From d5208ee5dd5fe56eba55f45402b231841236fed5 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Fri, 7 Jun 2024 08:52:30 -0400 Subject: [PATCH 0001/1099] Add `isodate` as available placeholder for auto-comment Related issue: https://github.com/uBlockOrigin/uBlock-issues/issues/3265 --- src/js/storage.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/js/storage.js b/src/js/storage.js index dfd9695c3218f..b26ff00c6462c 100644 --- a/src/js/storage.js +++ b/src/js/storage.js @@ -607,6 +607,7 @@ onBroadcast(msg => { const url = new URL(options.docURL); comment = '! ' + this.hiddenSettings.autoCommentFilterTemplate + .replace('{{isodate}}', d.toISOString().split('T')[0]) .replace('{{date}}', d.toLocaleDateString(undefined, { dateStyle: 'medium' })) .replace('{{time}}', d.toLocaleTimeString()) .replace('{{hostname}}', url.hostname) From 59b158217bd4b5779514a2b8d85484adca044074 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Fri, 7 Jun 2024 08:54:25 -0400 Subject: [PATCH 0002/1099] Update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index b7d8d3d6ba7b6..46c32f0d032fb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,4 @@ +- [Add isodate as available placeholder for auto-comment](https://github.com/gorhill/uBlock/commit/d5208ee5dd) - [Improve `trusted-replace-outbound-text` scriptlet](https://github.com/gorhill/uBlock/commit/fa6740a059) - [Classify generic cosmetic filters with comma as highly generic](https://github.com/gorhill/uBlock/commit/8f81833efc) - [Raise max buffer size for response body filtering](https://github.com/gorhill/uBlock/commit/82a3992896) From f10a17a6dc329bd6c2e4722a7e7f0f6f1a178dee Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Fri, 7 Jun 2024 08:55:03 -0400 Subject: [PATCH 0003/1099] New revision for dev build --- dist/version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dist/version b/dist/version index 33894b4bb88e9..1aa4c8a329030 100644 --- a/dist/version +++ b/dist/version @@ -1 +1 @@ -1.58.1.3 \ No newline at end of file +1.58.1.4 \ No newline at end of file From 79c2eec802508958e8b499490c3efd351f6c20a3 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Fri, 7 Jun 2024 09:05:45 -0400 Subject: [PATCH 0004/1099] Make Firefox dev build auto-update --- dist/firefox/updates.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dist/firefox/updates.json b/dist/firefox/updates.json index 66ccea57e3567..02f54020d1c66 100644 --- a/dist/firefox/updates.json +++ b/dist/firefox/updates.json @@ -3,9 +3,9 @@ "uBlock0@raymondhill.net": { "updates": [ { - "version": "1.58.1.3", + "version": "1.58.1.4", "browser_specific_settings": { "gecko": { "strict_min_version": "78.0" } }, - "update_link": "https://github.com/gorhill/uBlock/releases/download/1.58.1b3/uBlock0_1.58.1b3.firefox.signed.xpi" + "update_link": "https://github.com/gorhill/uBlock/releases/download/1.58.1b4/uBlock0_1.58.1b4.firefox.signed.xpi" } ] } From 91ee5bdeae6981cc20038b6610ab5c16b58623cf Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Tue, 11 Jun 2024 07:44:43 -0400 Subject: [PATCH 0005/1099] Improve `prevent-addEventListener` scriptlet Related issue: https://github.com/uBlockOrigin/uBlock-issues/issues/3061#issuecomment-2159662039 --- assets/resources/scriptlets.js | 2 ++ platform/mv3/extension/_locales/zh_CN/messages.json | 2 +- src/_locales/be/messages.json | 2 +- src/_locales/eu/messages.json | 2 +- 4 files changed, 5 insertions(+), 3 deletions(-) diff --git a/assets/resources/scriptlets.js b/assets/resources/scriptlets.js index 54adfb623376a..da232b70affd1 100644 --- a/assets/resources/scriptlets.js +++ b/assets/resources/scriptlets.js @@ -1642,6 +1642,8 @@ function addEventListenerDefuser( const debug = shouldDebug(extraArgs); const targetSelector = extraArgs.elements || undefined; const elementMatches = elem => { + if ( targetSelector === 'window' ) { return elem === window; } + if ( targetSelector === 'document' ) { return elem === document; } if ( elem && elem.matches && elem.matches(targetSelector) ) { return true; } const elems = Array.from(document.querySelectorAll(targetSelector)); return elems.includes(elem); diff --git a/platform/mv3/extension/_locales/zh_CN/messages.json b/platform/mv3/extension/_locales/zh_CN/messages.json index ffbbf9c03badb..9195970cb1b11 100644 --- a/platform/mv3/extension/_locales/zh_CN/messages.json +++ b/platform/mv3/extension/_locales/zh_CN/messages.json @@ -156,7 +156,7 @@ "description": "Label for a checkbox in the options page" }, "showBlockedCountLabel": { - "message": "在工具栏图标上显示已拦截的请求数", + "message": "在工具栏图标上显示拦截请求数", "description": "Label for a checkbox in the options page" } } diff --git a/src/_locales/be/messages.json b/src/_locales/be/messages.json index bb7a458505742..ca1ee35bc364d 100644 --- a/src/_locales/be/messages.json +++ b/src/_locales/be/messages.json @@ -4,7 +4,7 @@ "description": "extension name." }, "extShortDesc": { - "message": "Нарэшце, эфектыўны блакіроўшчык. Не нагружае працэсар і памяць.", + "message": "Нарэшце, эфектыўны блакавальнік. Не нагружае працэсар і памяць.", "description": "this will be in the Chrome web store: must be 132 characters or less" }, "dashboardName": { diff --git a/src/_locales/eu/messages.json b/src/_locales/eu/messages.json index b87b777e765fc..5cf4565fbe69f 100644 --- a/src/_locales/eu/messages.json +++ b/src/_locales/eu/messages.json @@ -540,7 +540,7 @@ "description": "Warning against copy-pasting filters from random sources" }, "1pEnableMyFiltersLabel": { - "message": "Enable my custom filters", + "message": "Aktibatu nire filtro pertsonalitsatuak", "description": "Label for the checkbox use to enable/disable 'My filters' list" }, "1pTrustMyFiltersLabel": { From b24793bc02e9ff9c3656fb389fcf1388ad591fc1 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Tue, 11 Jun 2024 07:47:26 -0400 Subject: [PATCH 0006/1099] Import translation work from Crowdin --- platform/mv3/description/webstore.eu.txt | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/platform/mv3/description/webstore.eu.txt b/platform/mv3/description/webstore.eu.txt index a15101de8214a..8f15a42a9bec0 100644 --- a/platform/mv3/description/webstore.eu.txt +++ b/platform/mv3/description/webstore.eu.txt @@ -3,13 +3,13 @@ uBO Lite (uBOL) irakargarri blokeatzailea de, baimen gutxiekin eta MV3ean basatu The default ruleset corresponds to uBlock Origin's default filterset: UblockOrigin-eko filtro lista -- EasyList +ZerrendaErraza PribazitateaErraza -- Peter Lowe’s Ad and tracking server list +Peter Lowe-ren Ad and tracker zerrenda -You can add more rulesets by visiting the options page -- click the _Cogs_ icon in the popup panel. +Arau gehiago gehitu ditzakezu aukeren orrialdea bisitatuz. Sakatu _Cogs_ ikonoa popup panelean. -uBOL is entirely declarative, meaning there is no need for a permanent uBOL process for the filtering to occur, and CSS/JS injection-based content filtering is performed reliably by the browser itself rather than by the extension. This means that uBOL itself does not consume CPU/memory resources while content blocking is ongoing -- uBOL's service worker process is required _only_ when you interact with the popup panel or the option pages. +uBOL is entirely declarative, meaning there is no need for a permanent uBOL process for the filtering to occur, and CSS/JS injection-based content filtering is performed reliably by the browser itself rather than by the extension. Horrek esan nahi du uBOLek berak ez duela CPU/memoria baliabiderik kontsumitzen edukien blokeoa martxan dagoen bitartean... uBOLren zerbitzuko langileen prozesua _only_ behar da popup panelarekin edo aukera orriekin elkarreragiten denean. uBOL does not require broad "read and modify data" permission at install time, hence its limited capabilities out of the box compared to uBlock Origin or other content blockers requiring broad "read and modify data" permissions at install time. @@ -25,6 +25,6 @@ You can set the default filtering mode from uBOL's options page. If you pick the Keep in mind this is still a work in progress, with these end goals: -- No broad host permissions at install time -- extended permissions are granted explicitly by the user on a per-site basis. +- Instalatzeko garaian, ez dago baimen zabalik. Erabiltzaileak esplizituki ematen ditu baimen zabalduak. - Entirely declarative for reliability and CPU/memory efficiency. From 3e35ea641421480abab6a097c3b2586a5986cd77 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Tue, 11 Jun 2024 15:36:16 -0400 Subject: [PATCH 0007/1099] Update changelog --- CHANGELOG.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 46c32f0d032fb..4d24f7f7caba7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,5 @@ -- [Add isodate as available placeholder for auto-comment](https://github.com/gorhill/uBlock/commit/d5208ee5dd) +- [Improve `prevent-addEventListener` scriptlet](https://github.com/gorhill/uBlock/commit/91ee5bdeae) +- [Add `isodate` as available placeholder for auto-comment](https://github.com/gorhill/uBlock/commit/d5208ee5dd) - [Improve `trusted-replace-outbound-text` scriptlet](https://github.com/gorhill/uBlock/commit/fa6740a059) - [Classify generic cosmetic filters with comma as highly generic](https://github.com/gorhill/uBlock/commit/8f81833efc) - [Raise max buffer size for response body filtering](https://github.com/gorhill/uBlock/commit/82a3992896) From b5819a29e90779f64c444c31e3a73d929dc90d55 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Tue, 11 Jun 2024 15:36:36 -0400 Subject: [PATCH 0008/1099] New revision for dev build --- dist/version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dist/version b/dist/version index 1aa4c8a329030..13f0a613ae0d0 100644 --- a/dist/version +++ b/dist/version @@ -1 +1 @@ -1.58.1.4 \ No newline at end of file +1.58.1.5 \ No newline at end of file From 441cf7c2b8b91fb437cafa0ecd54051301ecc723 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Tue, 11 Jun 2024 15:40:47 -0400 Subject: [PATCH 0009/1099] Make Firefox dev build auto-update --- dist/firefox/updates.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dist/firefox/updates.json b/dist/firefox/updates.json index 02f54020d1c66..adee00d9fd3a2 100644 --- a/dist/firefox/updates.json +++ b/dist/firefox/updates.json @@ -3,9 +3,9 @@ "uBlock0@raymondhill.net": { "updates": [ { - "version": "1.58.1.4", + "version": "1.58.1.5", "browser_specific_settings": { "gecko": { "strict_min_version": "78.0" } }, - "update_link": "https://github.com/gorhill/uBlock/releases/download/1.58.1b4/uBlock0_1.58.1b4.firefox.signed.xpi" + "update_link": "https://github.com/gorhill/uBlock/releases/download/1.58.1b5/uBlock0_1.58.1b5.firefox.signed.xpi" } ] } From 9072772f6134d80b4cfa4b90a10c624810a781fc Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Thu, 13 Jun 2024 09:32:30 -0400 Subject: [PATCH 0010/1099] Improve `trusted-replace-[fetch|xhr]-response` scriptlets Related discussion: https://github.com/uBlockOrigin/uBlock-discussions/discussions/831#discussioncomment-9750621 --- assets/resources/scriptlets.js | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/assets/resources/scriptlets.js b/assets/resources/scriptlets.js index da232b70affd1..71c98dcc2201a 100644 --- a/assets/resources/scriptlets.js +++ b/assets/resources/scriptlets.js @@ -1332,6 +1332,8 @@ function replaceFetchResponseFn( if ( pattern === '*' ) { pattern = '.*'; } const rePattern = safe.patternToRegex(pattern); const propNeedles = parsePropertiesToMatch(propsToMatch, 'url'); + const extraArgs = safe.getExtraArgs(Array.from(arguments), 4); + const reIncludes = extraArgs.includes ? safe.patternToRegex(extraArgs.includes) : null; self.fetch = new Proxy(self.fetch, { apply: function(target, thisArg, args) { const fetchPromise = Reflect.apply(target, thisArg, args); @@ -1361,6 +1363,9 @@ function replaceFetchResponseFn( return fetchPromise.then(responseBefore => { const response = responseBefore.clone(); return response.text().then(textBefore => { + if ( reIncludes && reIncludes.test(textBefore) === false ) { + return responseBefore; + } const textAfter = textBefore.replace(rePattern, replacement); const outcome = textAfter !== textBefore ? 'match' : 'nomatch'; if ( outcome === 'nomatch' ) { return responseBefore; } @@ -4365,6 +4370,8 @@ function trustedReplaceXhrResponse( if ( pattern === '*' ) { pattern = '.*'; } const rePattern = safe.patternToRegex(pattern); const propNeedles = parsePropertiesToMatch(propsToMatch, 'url'); + const extraArgs = safe.getExtraArgs(Array.from(arguments), 3); + const reIncludes = extraArgs.includes ? safe.patternToRegex(extraArgs.includes) : null; self.XMLHttpRequest = class extends self.XMLHttpRequest { open(method, url, ...args) { const outerXhr = this; @@ -4402,6 +4409,9 @@ function trustedReplaceXhrResponse( if ( typeof innerResponse !== 'string' ) { return (xhrDetails.response = innerResponse); } + if ( reIncludes && reIncludes.test(innerResponse) === false ) { + return (xhrDetails.response = innerResponse); + } const textBefore = innerResponse; const textAfter = textBefore.replace(rePattern, replacement); if ( textAfter !== textBefore ) { From 4611752f71e32eaa23c058ad602809add156a573 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Thu, 13 Jun 2024 09:39:07 -0400 Subject: [PATCH 0011/1099] Update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4d24f7f7caba7..a54360b74adaf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,4 @@ +- [Improve `trusted-replace-[fetch|xhr]-response` scriptlets](https://github.com/gorhill/uBlock/commit/9072772f61) - [Improve `prevent-addEventListener` scriptlet](https://github.com/gorhill/uBlock/commit/91ee5bdeae) - [Add `isodate` as available placeholder for auto-comment](https://github.com/gorhill/uBlock/commit/d5208ee5dd) - [Improve `trusted-replace-outbound-text` scriptlet](https://github.com/gorhill/uBlock/commit/fa6740a059) From f499cd27425e59250690f751a67a57ecd17f9703 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Thu, 13 Jun 2024 09:39:31 -0400 Subject: [PATCH 0012/1099] New revision for dev build --- dist/version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dist/version b/dist/version index 13f0a613ae0d0..fed0d5c50e183 100644 --- a/dist/version +++ b/dist/version @@ -1 +1 @@ -1.58.1.5 \ No newline at end of file +1.58.1.6 \ No newline at end of file From 62fdd8b4a48170f783e760ae3f64152a8a11bb47 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Thu, 13 Jun 2024 09:45:44 -0400 Subject: [PATCH 0013/1099] Make Firefox dev build auto-update --- dist/firefox/updates.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dist/firefox/updates.json b/dist/firefox/updates.json index adee00d9fd3a2..1d1b3a3c0d23c 100644 --- a/dist/firefox/updates.json +++ b/dist/firefox/updates.json @@ -3,9 +3,9 @@ "uBlock0@raymondhill.net": { "updates": [ { - "version": "1.58.1.5", + "version": "1.58.1.6", "browser_specific_settings": { "gecko": { "strict_min_version": "78.0" } }, - "update_link": "https://github.com/gorhill/uBlock/releases/download/1.58.1b5/uBlock0_1.58.1b5.firefox.signed.xpi" + "update_link": "https://github.com/gorhill/uBlock/releases/download/1.58.1b6/uBlock0_1.58.1b6.firefox.signed.xpi" } ] } From bdb99862ce93d61e9202ee1bad656276e4a9ee5a Mon Sep 17 00:00:00 2001 From: Fanboynz Date: Fri, 14 Jun 2024 11:32:11 -0700 Subject: [PATCH 0014/1099] Add hide and hidden to set-cookie (#3918) --- assets/resources/scriptlets.js | 1 + 1 file changed, 1 insertion(+) diff --git a/assets/resources/scriptlets.js b/assets/resources/scriptlets.js index 71c98dcc2201a..41cd83f1db9b9 100644 --- a/assets/resources/scriptlets.js +++ b/assets/resources/scriptlets.js @@ -3785,6 +3785,7 @@ function setCookie( 'yes', 'y', 'no', 'n', 'necessary', 'required', 'approved', 'disapproved', + 'hide', 'hidden', ]; const normalized = value.toLowerCase(); const match = /^("?)(.+)\1$/.exec(normalized); From cb0f65e035e4cd40b0127079ff1fca76ee964fa0 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Tue, 18 Jun 2024 10:01:27 -0400 Subject: [PATCH 0015/1099] Improve `trusted-replace-node-text` scriptlet Related discussion: https://github.com/uBlockOrigin/uAssets/discussions/24143 --- assets/resources/scriptlets.js | 1 + 1 file changed, 1 insertion(+) diff --git a/assets/resources/scriptlets.js b/assets/resources/scriptlets.js index 41cd83f1db9b9..dacfe7f50f210 100644 --- a/assets/resources/scriptlets.js +++ b/assets/resources/scriptlets.js @@ -764,6 +764,7 @@ function replaceNodeTextFn( count += 1; if ( node === null ) { break; } if ( reNodeName.test(node.nodeName) === false ) { continue; } + if ( node === document.currentScript ) { continue; } if ( handleNode(node) ) { continue; } stop(); break; } From 58a9838e41bdb722dad6e6bc11300fd89db56287 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Tue, 18 Jun 2024 10:10:45 -0400 Subject: [PATCH 0016/1099] Update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index a54360b74adaf..7fe32420b5c6c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,4 @@ +- [Improve `trusted-replace-node-text` scriptlet](https://github.com/gorhill/uBlock/commit/cb0f65e035) - [Improve `trusted-replace-[fetch|xhr]-response` scriptlets](https://github.com/gorhill/uBlock/commit/9072772f61) - [Improve `prevent-addEventListener` scriptlet](https://github.com/gorhill/uBlock/commit/91ee5bdeae) - [Add `isodate` as available placeholder for auto-comment](https://github.com/gorhill/uBlock/commit/d5208ee5dd) From 1df4ea45067efbdaea3f0e9436fb1ecdeefd0c61 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Tue, 18 Jun 2024 10:11:03 -0400 Subject: [PATCH 0017/1099] New revision for dev build --- dist/version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dist/version b/dist/version index fed0d5c50e183..df35a567cd2df 100644 --- a/dist/version +++ b/dist/version @@ -1 +1 @@ -1.58.1.6 \ No newline at end of file +1.58.1.7 \ No newline at end of file From 8e3eaf1cc807b787a35025bf47843142f69a2fcb Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Tue, 18 Jun 2024 10:15:54 -0400 Subject: [PATCH 0018/1099] Make Firefox dev build auto-update --- dist/firefox/updates.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dist/firefox/updates.json b/dist/firefox/updates.json index 1d1b3a3c0d23c..d70356242eed7 100644 --- a/dist/firefox/updates.json +++ b/dist/firefox/updates.json @@ -3,9 +3,9 @@ "uBlock0@raymondhill.net": { "updates": [ { - "version": "1.58.1.6", + "version": "1.58.1.7", "browser_specific_settings": { "gecko": { "strict_min_version": "78.0" } }, - "update_link": "https://github.com/gorhill/uBlock/releases/download/1.58.1b6/uBlock0_1.58.1b6.firefox.signed.xpi" + "update_link": "https://github.com/gorhill/uBlock/releases/download/1.58.1b7/uBlock0_1.58.1b7.firefox.signed.xpi" } ] } From b4d8750f445cfa06bebd184a6f3cdb4d73148e72 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Wed, 19 Jun 2024 08:48:54 -0400 Subject: [PATCH 0019/1099] Improve `set-cookie` scriptlet Related feedback: https://github.com/uBlockOrigin/uBlock-issues/issues/3178#issuecomment-2178502856 --- assets/resources/scriptlets.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/assets/resources/scriptlets.js b/assets/resources/scriptlets.js index dacfe7f50f210..2781fad3b0191 100644 --- a/assets/resources/scriptlets.js +++ b/assets/resources/scriptlets.js @@ -1009,6 +1009,8 @@ function setCookieFn( cookieParts.push(`; domain=${options.domain}`); } cookieParts.push('; Secure'); + } else if ( /^__(Host|Secure)-/.test(name) ) { + cookieParts.push('; Secure'); } try { From 297167755fd1fafd71ba9112f49558570c6cadfb Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Wed, 19 Jun 2024 08:51:46 -0400 Subject: [PATCH 0020/1099] Update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7fe32420b5c6c..0a7e40f5966ec 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,4 @@ +- [Improve `set-cookie` scriptlet](https://github.com/gorhill/uBlock/commit/b4d8750f44) - [Improve `trusted-replace-node-text` scriptlet](https://github.com/gorhill/uBlock/commit/cb0f65e035) - [Improve `trusted-replace-[fetch|xhr]-response` scriptlets](https://github.com/gorhill/uBlock/commit/9072772f61) - [Improve `prevent-addEventListener` scriptlet](https://github.com/gorhill/uBlock/commit/91ee5bdeae) From cc860a67f80696e50aa23610e512b9f88e78d188 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Wed, 19 Jun 2024 08:52:07 -0400 Subject: [PATCH 0021/1099] New revision for dev build --- dist/version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dist/version b/dist/version index df35a567cd2df..d8992862b105a 100644 --- a/dist/version +++ b/dist/version @@ -1 +1 @@ -1.58.1.7 \ No newline at end of file +1.58.1.8 \ No newline at end of file From 7be7e0b8709d87f3e32fbf49063ed04ea05dc4c3 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Wed, 19 Jun 2024 10:16:19 -0400 Subject: [PATCH 0022/1099] Make Firefox dev build auto-update --- dist/firefox/updates.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dist/firefox/updates.json b/dist/firefox/updates.json index d70356242eed7..9bc0dbc777640 100644 --- a/dist/firefox/updates.json +++ b/dist/firefox/updates.json @@ -3,9 +3,9 @@ "uBlock0@raymondhill.net": { "updates": [ { - "version": "1.58.1.7", + "version": "1.58.1.8", "browser_specific_settings": { "gecko": { "strict_min_version": "78.0" } }, - "update_link": "https://github.com/gorhill/uBlock/releases/download/1.58.1b7/uBlock0_1.58.1b7.firefox.signed.xpi" + "update_link": "https://github.com/gorhill/uBlock/releases/download/1.58.1b8/uBlock0_1.58.1b8.firefox.signed.xpi" } ] } From aca7674bacb9858b375c55f996d10dfabebbdb5e Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Wed, 19 Jun 2024 19:06:53 -0400 Subject: [PATCH 0023/1099] Add `:matches-prop()` pseudo CSS operator `subject:matches-prop(arg)` Description: Allows to select an element by a property name (or chain of properties), and optionally the property value. Chainable: Yes. `subject`: Can be a plain CSS selector, or a procedural cosmetic filter. `arg`: A declaration in the form `chain=value`, where `chain` is a dot- separated string for the target property, and `value` is the optional property value to match. `value` can be literal text or literal regular expression. When no `value` is declared, the operator only tests for the presence of the target property Example: example.org##div:matches-prop(imanad) example.org##img:matches-prop(naturalWidth=160) --- src/js/contentscript-extra.js | 21 ++ src/js/static-filtering-parser.js | 509 +++++++++++++++--------------- 2 files changed, 276 insertions(+), 254 deletions(-) diff --git a/src/js/contentscript-extra.js b/src/js/contentscript-extra.js index 0c3104d91bf11..4d10bb8782464 100644 --- a/src/js/contentscript-extra.js +++ b/src/js/contentscript-extra.js @@ -173,6 +173,26 @@ class PSelectorMatchesPathTask extends PSelectorTask { } } +class PSelectorMatchesPropTask extends PSelectorTask { + constructor(task) { + super(); + this.props = task[1].attr.split('.'); + this.reValue = task[1].value !== '' + ? regexFromString(task[1].value, true) + : null; + } + transpose(node, output) { + let value = node; + for ( const prop of this.props ) { + if ( value === undefined ) { return; } + if ( value === null ) { return; } + value = value[prop]; + } + if ( this.reValue !== null && this.reValue.test(value) === false ) { return; } + output.push(node); + } +} + class PSelectorMinTextLengthTask extends PSelectorTask { constructor(task) { super(); @@ -461,6 +481,7 @@ PSelector.prototype.operatorToTaskMap = new Map([ [ 'matches-css-before', PSelectorMatchesCSSBeforeTask ], [ 'matches-media', PSelectorMatchesMediaTask ], [ 'matches-path', PSelectorMatchesPathTask ], + [ 'matches-prop', PSelectorMatchesPropTask ], [ 'min-text-length', PSelectorMinTextLengthTask ], [ 'not', PSelectorIfNotTask ], [ 'others', PSelectorOthersTask ], diff --git a/src/js/static-filtering-parser.js b/src/js/static-filtering-parser.js index 48c5f62e7aaf1..e89f7b3a30ce5 100644 --- a/src/js/static-filtering-parser.js +++ b/src/js/static-filtering-parser.js @@ -19,12 +19,10 @@ Home: https://github.com/gorhill/uBlock */ -'use strict'; - /******************************************************************************/ -import Regex from '../lib/regexanalyzer/regex.js'; import * as cssTree from '../lib/csstree/css-tree.js'; +import Regex from '../lib/regexanalyzer/regex.js'; /******************************************************************************* * @@ -781,21 +779,21 @@ class DomainListIterator { let ready = false; while ( node !== 0 ) { switch ( this.parser.getNodeType(node) ) { - case NODE_TYPE_OPTION_VALUE_DOMAIN_RAW: - this.item.hn = ''; - this.item.not = false; - this.item.bad = this.parser.getNodeFlags(node, NODE_FLAG_ERROR) !== 0; - break; - case NODE_TYPE_OPTION_VALUE_NOT: - this.item.not = true; - break; - case NODE_TYPE_OPTION_VALUE_DOMAIN: - this.item.hn = this.parser.getNodeTransform(node); - this.value = this.item; - ready = true; - break; - default: - break; + case NODE_TYPE_OPTION_VALUE_DOMAIN_RAW: + this.item.hn = ''; + this.item.not = false; + this.item.bad = this.parser.getNodeFlags(node, NODE_FLAG_ERROR) !== 0; + break; + case NODE_TYPE_OPTION_VALUE_NOT: + this.item.not = true; + break; + case NODE_TYPE_OPTION_VALUE_DOMAIN: + this.item.hn = this.parser.getNodeTransform(node); + this.value = this.item; + ready = true; + break; + default: + break; } node = this.walker.next(); if ( ready ) { return this; } @@ -859,17 +857,17 @@ export class AstFilterParser { this.reInlineComment = /(?:\s+#).*?$/; this.reNetException = /^@@/; this.reNetAnchor = /(?:)\$[^,\w~]/; - this.reHnAnchoredPlainAscii = /^\|\|[0-9a-z%&,\-.\/:;=?_]+$/; + this.reHnAnchoredPlainAscii = /^\|\|[0-9a-z%&,\-./:;=?_]+$/; this.reHnAnchoredHostnameAscii = /^\|\|(?:[\da-z][\da-z_-]*\.)*[\da-z_-]*[\da-z]\^$/; this.reHnAnchoredHostnameUnicode = /^\|\|(?:[\p{L}\p{N}][\p{L}\p{N}\u{2d}]*\.)*[\p{L}\p{N}\u{2d}]*[\p{L}\p{N}]\^$/u; this.reHn3pAnchoredHostnameAscii = /^\|\|(?:[\da-z][\da-z_-]*\.)*[\da-z_-]*[\da-z]\^\$third-party$/; - this.rePlainAscii = /^[0-9a-z%&\-.\/:;=?_]{2,}$/; + this.rePlainAscii = /^[0-9a-z%&\-./:;=?_]{2,}$/; this.reNetHosts1 = /^127\.0\.0\.1 (?:[\da-z][\da-z_-]*\.)+[\da-z-]*[a-z]$/; this.reNetHosts2 = /^0\.0\.0\.0 (?:[\da-z][\da-z_-]*\.)+[\da-z-]*[a-z]$/; this.rePlainGenericCosmetic = /^##[.#][A-Za-z_][\w-]*$/; this.reHostnameAscii = /^(?:[\da-z][\da-z_-]*\.)*[\da-z][\da-z-]*[\da-z]$/; this.rePlainEntity = /^(?:[\da-z][\da-z_-]*\.)+\*$/; - this.reHostsSink = /^[\w%.:\[\]-]+\s+/; + this.reHostsSink = /^[\w%.:[\]-]+\s+/; this.reHostsRedirect = /(?:0\.0\.0\.0|broadcasthost|local|localhost(?:\.localdomain)?|ip6-\w+)(?:[^\w.-]|$)/; this.reNetOptionComma = /,(?:~?[13a-z-]+(?:=.*?)?|_+)(?:,|$)/; this.rePointlessLeftAnchor = /^\|\|?\*+/; @@ -886,8 +884,8 @@ export class AstFilterParser { this.rePreparseDirectiveIf = /^!#if /; this.rePreparseDirectiveAny = /^!#(?:else|endif|if |include )/; this.reURL = /\bhttps?:\/\/\S+/; - this.reHasPatternSpecialChars = /[\*\^]/; - this.rePatternAllSpecialChars = /[\*\^]+|[^\x00-\x7f]+/g; + this.reHasPatternSpecialChars = /[*^]/; + this.rePatternAllSpecialChars = /[*^]+|[^\x00-\x7f]+/g; // https://github.com/uBlockOrigin/uBlock-issues/issues/1146 // From https://codemirror.net/doc/manual.html#option_specialChars this.reHasInvalidChar = /[\x00-\x1F\x7F-\x9F\xAD\u061C\u200B-\u200F\u2028\u2029\uFEFF\uFFF9-\uFFFC]/; @@ -1315,218 +1313,218 @@ export class AstFilterParser { const hasValue = (flags & NODE_FLAG_OPTION_HAS_VALUE) !== 0; bad = false; realBad = false; switch ( type ) { - case NODE_TYPE_NET_OPTION_NAME_ALL: - realBad = isNegated || hasValue || modifierType !== 0; - break; - case NODE_TYPE_NET_OPTION_NAME_1P: - case NODE_TYPE_NET_OPTION_NAME_3P: - realBad = hasValue; - break; - case NODE_TYPE_NET_OPTION_NAME_BADFILTER: - badfilter = true; - /* falls through */ - case NODE_TYPE_NET_OPTION_NAME_NOOP: - realBad = isNegated || hasValue; - break; - case NODE_TYPE_NET_OPTION_NAME_CSS: - case NODE_TYPE_NET_OPTION_NAME_FONT: - case NODE_TYPE_NET_OPTION_NAME_IMAGE: - case NODE_TYPE_NET_OPTION_NAME_MEDIA: - case NODE_TYPE_NET_OPTION_NAME_OBJECT: - case NODE_TYPE_NET_OPTION_NAME_OTHER: - case NODE_TYPE_NET_OPTION_NAME_SCRIPT: - case NODE_TYPE_NET_OPTION_NAME_XHR: - realBad = hasValue; - if ( realBad ) { break; } - requestTypeCount += 1; - break; - case NODE_TYPE_NET_OPTION_NAME_CNAME: - realBad = isException === false || isNegated || hasValue; - if ( realBad ) { break; } - modifierType = type; - break; - case NODE_TYPE_NET_OPTION_NAME_CSP: - realBad = (hasValue || isException) === false || - modifierType !== 0 || - this.reBadCSP.test( - this.getNetOptionValue(NODE_TYPE_NET_OPTION_NAME_CSP) - ); - if ( realBad ) { break; } - modifierType = type; - break; - case NODE_TYPE_NET_OPTION_NAME_DENYALLOW: - realBad = isNegated || hasValue === false || - this.getBranchFromType(NODE_TYPE_NET_OPTION_NAME_FROM) === 0; - break; - case NODE_TYPE_NET_OPTION_NAME_DOC: - case NODE_TYPE_NET_OPTION_NAME_FRAME: - realBad = hasValue; - if ( realBad ) { break; } - docTypeCount += 1; - break; - case NODE_TYPE_NET_OPTION_NAME_EHIDE: - case NODE_TYPE_NET_OPTION_NAME_GHIDE: - case NODE_TYPE_NET_OPTION_NAME_SHIDE: - realBad = isNegated || hasValue || modifierType !== 0; - if ( realBad ) { break; } - behaviorTypeCount += 1; - unredirectableTypeCount += 1; - break; - case NODE_TYPE_NET_OPTION_NAME_EMPTY: - case NODE_TYPE_NET_OPTION_NAME_MP4: - realBad = isNegated || hasValue || modifierType !== 0; - if ( realBad ) { break; } - modifierType = type; - break; - case NODE_TYPE_NET_OPTION_NAME_FROM: - case NODE_TYPE_NET_OPTION_NAME_METHOD: - case NODE_TYPE_NET_OPTION_NAME_TO: - realBad = isNegated || hasValue === false; - break; - case NODE_TYPE_NET_OPTION_NAME_GENERICBLOCK: - bad = true; - realBad = isException === false || isNegated || hasValue; - break; - case NODE_TYPE_NET_OPTION_NAME_HEADER: - realBad = isNegated || hasValue === false; - break; - case NODE_TYPE_NET_OPTION_NAME_IMPORTANT: - realBad = isException || isNegated || hasValue; - break; - case NODE_TYPE_NET_OPTION_NAME_INLINEFONT: - case NODE_TYPE_NET_OPTION_NAME_INLINESCRIPT: - realBad = hasValue; - if ( realBad ) { break; } - modifierType = type; - unredirectableTypeCount += 1; - break; - case NODE_TYPE_NET_OPTION_NAME_MATCHCASE: - realBad = this.isRegexPattern() === false; - break; - case NODE_TYPE_NET_OPTION_NAME_PERMISSIONS: - realBad = modifierType !== 0 || - (hasValue || isException) === false || - this.reBadPP.test( - this.getNetOptionValue(NODE_TYPE_NET_OPTION_NAME_PERMISSIONS) - ); - if ( realBad ) { break; } - modifierType = type; - break; - case NODE_TYPE_NET_OPTION_NAME_PING: - case NODE_TYPE_NET_OPTION_NAME_WEBSOCKET: - realBad = hasValue; - if ( realBad ) { break; } - requestTypeCount += 1; - unredirectableTypeCount += 1; - break; - case NODE_TYPE_NET_OPTION_NAME_POPUNDER: - case NODE_TYPE_NET_OPTION_NAME_POPUP: - realBad = hasValue; - if ( realBad ) { break; } - abstractTypeCount += 1; - unredirectableTypeCount += 1; - break; - case NODE_TYPE_NET_OPTION_NAME_REDIRECT: - case NODE_TYPE_NET_OPTION_NAME_REDIRECTRULE: - case NODE_TYPE_NET_OPTION_NAME_REPLACE: - case NODE_TYPE_NET_OPTION_NAME_URLTRANSFORM: - realBad = isNegated || (isException || hasValue) === false || - modifierType !== 0; - if ( realBad ) { break; } - modifierType = type; - break; - case NODE_TYPE_NET_OPTION_NAME_REMOVEPARAM: - realBad = isNegated || modifierType !== 0; - if ( realBad ) { break; } - modifierType = type; - break; - case NODE_TYPE_NET_OPTION_NAME_STRICT1P: - case NODE_TYPE_NET_OPTION_NAME_STRICT3P: - realBad = isNegated || hasValue; - break; - case NODE_TYPE_NET_OPTION_NAME_UNKNOWN: - this.astError = AST_ERROR_OPTION_UNKNOWN; - realBad = true; - break; - case NODE_TYPE_NET_OPTION_NAME_WEBRTC: - realBad = true; - break; - case NODE_TYPE_NET_PATTERN_RAW: - realBad = this.hasOptions() === false && - this.getNetPattern().length <= 1; - break; - default: - break; - } - if ( bad || realBad ) { - this.addNodeFlags(targetNode, NODE_FLAG_ERROR); - } - if ( realBad ) { - this.addFlags(AST_FLAG_HAS_ERROR); - } - } - const requiresTrustedSource = ( ) => - this.options.trustedSource !== true && - isException === false && badfilter === false; - switch ( modifierType ) { + case NODE_TYPE_NET_OPTION_NAME_ALL: + realBad = isNegated || hasValue || modifierType !== 0; + break; + case NODE_TYPE_NET_OPTION_NAME_1P: + case NODE_TYPE_NET_OPTION_NAME_3P: + realBad = hasValue; + break; + case NODE_TYPE_NET_OPTION_NAME_BADFILTER: + badfilter = true; + /* falls through */ + case NODE_TYPE_NET_OPTION_NAME_NOOP: + realBad = isNegated || hasValue; + break; + case NODE_TYPE_NET_OPTION_NAME_CSS: + case NODE_TYPE_NET_OPTION_NAME_FONT: + case NODE_TYPE_NET_OPTION_NAME_IMAGE: + case NODE_TYPE_NET_OPTION_NAME_MEDIA: + case NODE_TYPE_NET_OPTION_NAME_OBJECT: + case NODE_TYPE_NET_OPTION_NAME_OTHER: + case NODE_TYPE_NET_OPTION_NAME_SCRIPT: + case NODE_TYPE_NET_OPTION_NAME_XHR: + realBad = hasValue; + if ( realBad ) { break; } + requestTypeCount += 1; + break; case NODE_TYPE_NET_OPTION_NAME_CNAME: - realBad = abstractTypeCount || behaviorTypeCount || requestTypeCount; + realBad = isException === false || isNegated || hasValue; + if ( realBad ) { break; } + modifierType = type; break; case NODE_TYPE_NET_OPTION_NAME_CSP: - case NODE_TYPE_NET_OPTION_NAME_PERMISSIONS: - realBad = abstractTypeCount || behaviorTypeCount || requestTypeCount; + realBad = (hasValue || isException) === false || + modifierType !== 0 || + this.reBadCSP.test( + this.getNetOptionValue(NODE_TYPE_NET_OPTION_NAME_CSP) + ); + if ( realBad ) { break; } + modifierType = type; + break; + case NODE_TYPE_NET_OPTION_NAME_DENYALLOW: + realBad = isNegated || hasValue === false || + this.getBranchFromType(NODE_TYPE_NET_OPTION_NAME_FROM) === 0; + break; + case NODE_TYPE_NET_OPTION_NAME_DOC: + case NODE_TYPE_NET_OPTION_NAME_FRAME: + realBad = hasValue; + if ( realBad ) { break; } + docTypeCount += 1; + break; + case NODE_TYPE_NET_OPTION_NAME_EHIDE: + case NODE_TYPE_NET_OPTION_NAME_GHIDE: + case NODE_TYPE_NET_OPTION_NAME_SHIDE: + realBad = isNegated || hasValue || modifierType !== 0; + if ( realBad ) { break; } + behaviorTypeCount += 1; + unredirectableTypeCount += 1; + break; + case NODE_TYPE_NET_OPTION_NAME_EMPTY: + case NODE_TYPE_NET_OPTION_NAME_MP4: + realBad = isNegated || hasValue || modifierType !== 0; + if ( realBad ) { break; } + modifierType = type; + break; + case NODE_TYPE_NET_OPTION_NAME_FROM: + case NODE_TYPE_NET_OPTION_NAME_METHOD: + case NODE_TYPE_NET_OPTION_NAME_TO: + realBad = isNegated || hasValue === false; + break; + case NODE_TYPE_NET_OPTION_NAME_GENERICBLOCK: + bad = true; + realBad = isException === false || isNegated || hasValue; + break; + case NODE_TYPE_NET_OPTION_NAME_HEADER: + realBad = isNegated || hasValue === false; + break; + case NODE_TYPE_NET_OPTION_NAME_IMPORTANT: + realBad = isException || isNegated || hasValue; break; case NODE_TYPE_NET_OPTION_NAME_INLINEFONT: case NODE_TYPE_NET_OPTION_NAME_INLINESCRIPT: - realBad = behaviorTypeCount; + realBad = hasValue; + if ( realBad ) { break; } + modifierType = type; + unredirectableTypeCount += 1; break; - case NODE_TYPE_NET_OPTION_NAME_EMPTY: - realBad = abstractTypeCount || behaviorTypeCount; + case NODE_TYPE_NET_OPTION_NAME_MATCHCASE: + realBad = this.isRegexPattern() === false; break; - case NODE_TYPE_NET_OPTION_NAME_MEDIA: - case NODE_TYPE_NET_OPTION_NAME_MP4: - realBad = abstractTypeCount || behaviorTypeCount || docTypeCount || requestTypeCount; + case NODE_TYPE_NET_OPTION_NAME_PERMISSIONS: + realBad = modifierType !== 0 || + (hasValue || isException) === false || + this.reBadPP.test( + this.getNetOptionValue(NODE_TYPE_NET_OPTION_NAME_PERMISSIONS) + ); + if ( realBad ) { break; } + modifierType = type; break; - case NODE_TYPE_NET_OPTION_NAME_REDIRECT: - case NODE_TYPE_NET_OPTION_NAME_REDIRECTRULE: { - realBad = abstractTypeCount || behaviorTypeCount || unredirectableTypeCount; + case NODE_TYPE_NET_OPTION_NAME_PING: + case NODE_TYPE_NET_OPTION_NAME_WEBSOCKET: + realBad = hasValue; + if ( realBad ) { break; } + requestTypeCount += 1; + unredirectableTypeCount += 1; break; - } - case NODE_TYPE_NET_OPTION_NAME_REPLACE: { - realBad = abstractTypeCount || behaviorTypeCount || unredirectableTypeCount; + case NODE_TYPE_NET_OPTION_NAME_POPUNDER: + case NODE_TYPE_NET_OPTION_NAME_POPUP: + realBad = hasValue; if ( realBad ) { break; } - if ( requiresTrustedSource() ) { - this.astError = AST_ERROR_UNTRUSTED_SOURCE; - realBad = true; - break; - } - const value = this.getNetOptionValue(NODE_TYPE_NET_OPTION_NAME_REPLACE); - if ( parseReplaceValue(value) === undefined ) { - this.astError = AST_ERROR_OPTION_BADVALUE; - realBad = true; - } + abstractTypeCount += 1; + unredirectableTypeCount += 1; break; - } - case NODE_TYPE_NET_OPTION_NAME_URLTRANSFORM: { - realBad = abstractTypeCount || behaviorTypeCount || unredirectableTypeCount; + case NODE_TYPE_NET_OPTION_NAME_REDIRECT: + case NODE_TYPE_NET_OPTION_NAME_REDIRECTRULE: + case NODE_TYPE_NET_OPTION_NAME_REPLACE: + case NODE_TYPE_NET_OPTION_NAME_URLTRANSFORM: + realBad = isNegated || (isException || hasValue) === false || + modifierType !== 0; if ( realBad ) { break; } - if ( requiresTrustedSource() ) { - this.astError = AST_ERROR_UNTRUSTED_SOURCE; - realBad = true; - break; - } - const value = this.getNetOptionValue(NODE_TYPE_NET_OPTION_NAME_URLTRANSFORM); - if ( value !== '' && parseReplaceValue(value) === undefined ) { - this.astError = AST_ERROR_OPTION_BADVALUE; - realBad = true; - } + modifierType = type; break; - } case NODE_TYPE_NET_OPTION_NAME_REMOVEPARAM: - realBad = abstractTypeCount || behaviorTypeCount; + realBad = isNegated || modifierType !== 0; + if ( realBad ) { break; } + modifierType = type; + break; + case NODE_TYPE_NET_OPTION_NAME_STRICT1P: + case NODE_TYPE_NET_OPTION_NAME_STRICT3P: + realBad = isNegated || hasValue; + break; + case NODE_TYPE_NET_OPTION_NAME_UNKNOWN: + this.astError = AST_ERROR_OPTION_UNKNOWN; + realBad = true; + break; + case NODE_TYPE_NET_OPTION_NAME_WEBRTC: + realBad = true; + break; + case NODE_TYPE_NET_PATTERN_RAW: + realBad = this.hasOptions() === false && + this.getNetPattern().length <= 1; break; default: break; + } + if ( bad || realBad ) { + this.addNodeFlags(targetNode, NODE_FLAG_ERROR); + } + if ( realBad ) { + this.addFlags(AST_FLAG_HAS_ERROR); + } + } + const requiresTrustedSource = ( ) => + this.options.trustedSource !== true && + isException === false && badfilter === false; + switch ( modifierType ) { + case NODE_TYPE_NET_OPTION_NAME_CNAME: + realBad = abstractTypeCount || behaviorTypeCount || requestTypeCount; + break; + case NODE_TYPE_NET_OPTION_NAME_CSP: + case NODE_TYPE_NET_OPTION_NAME_PERMISSIONS: + realBad = abstractTypeCount || behaviorTypeCount || requestTypeCount; + break; + case NODE_TYPE_NET_OPTION_NAME_INLINEFONT: + case NODE_TYPE_NET_OPTION_NAME_INLINESCRIPT: + realBad = behaviorTypeCount; + break; + case NODE_TYPE_NET_OPTION_NAME_EMPTY: + realBad = abstractTypeCount || behaviorTypeCount; + break; + case NODE_TYPE_NET_OPTION_NAME_MEDIA: + case NODE_TYPE_NET_OPTION_NAME_MP4: + realBad = abstractTypeCount || behaviorTypeCount || docTypeCount || requestTypeCount; + break; + case NODE_TYPE_NET_OPTION_NAME_REDIRECT: + case NODE_TYPE_NET_OPTION_NAME_REDIRECTRULE: { + realBad = abstractTypeCount || behaviorTypeCount || unredirectableTypeCount; + break; + } + case NODE_TYPE_NET_OPTION_NAME_REPLACE: { + realBad = abstractTypeCount || behaviorTypeCount || unredirectableTypeCount; + if ( realBad ) { break; } + if ( requiresTrustedSource() ) { + this.astError = AST_ERROR_UNTRUSTED_SOURCE; + realBad = true; + break; + } + const value = this.getNetOptionValue(NODE_TYPE_NET_OPTION_NAME_REPLACE); + if ( parseReplaceValue(value) === undefined ) { + this.astError = AST_ERROR_OPTION_BADVALUE; + realBad = true; + } + break; + } + case NODE_TYPE_NET_OPTION_NAME_URLTRANSFORM: { + realBad = abstractTypeCount || behaviorTypeCount || unredirectableTypeCount; + if ( realBad ) { break; } + if ( requiresTrustedSource() ) { + this.astError = AST_ERROR_UNTRUSTED_SOURCE; + realBad = true; + break; + } + const value = this.getNetOptionValue(NODE_TYPE_NET_OPTION_NAME_URLTRANSFORM); + if ( value !== '' && parseReplaceValue(value) === undefined ) { + this.astError = AST_ERROR_OPTION_BADVALUE; + realBad = true; + } + break; + } + case NODE_TYPE_NET_OPTION_NAME_REMOVEPARAM: + realBad = abstractTypeCount || behaviorTypeCount; + break; + default: + break; } if ( realBad ) { const targetNode = this.getBranchFromType(modifierType); @@ -2061,15 +2059,15 @@ export class AstFilterParser { parentBeg + optionEnd ); switch ( nodeOptionType ) { - case NODE_TYPE_NET_OPTION_NAME_DENYALLOW: - this.linkDown(next, this.parseDomainList(next, '|'), 0b00000); - break; - case NODE_TYPE_NET_OPTION_NAME_FROM: - case NODE_TYPE_NET_OPTION_NAME_TO: - this.linkDown(next, this.parseDomainList(next, '|', 0b11010)); - break; - default: - break; + case NODE_TYPE_NET_OPTION_NAME_DENYALLOW: + this.linkDown(next, this.parseDomainList(next, '|'), 0b00000); + break; + case NODE_TYPE_NET_OPTION_NAME_FROM: + case NODE_TYPE_NET_OPTION_NAME_TO: + this.linkDown(next, this.parseDomainList(next, '|', 0b11010)); + break; + default: + break; } this.linkRight(prev, next); return this.throwHeadNode(head); @@ -2284,27 +2282,27 @@ export class AstFilterParser { if ( (flags & NODE_FLAG_ERROR) !== 0 ) { continue; } realBad = false; switch ( type ) { - case NODE_TYPE_EXT_PATTERN_RESPONSEHEADER: { - const pattern = this.getNodeString(targetNode); - realBad = - pattern !== '' && removableHTTPHeaders.has(pattern) === false || - pattern === '' && isException === false; - break; - } - case NODE_TYPE_EXT_PATTERN_SCRIPTLET_TOKEN: { - if ( this.interactive !== true ) { break; } - if ( isException ) { break; } - const { trustedSource, trustedScriptletTokens } = this.options; - if ( trustedScriptletTokens instanceof Set === false ) { break; } - const token = this.getNodeString(targetNode); - if ( trustedScriptletTokens.has(token) && trustedSource !== true ) { - this.astError = AST_ERROR_UNTRUSTED_SOURCE; - realBad = true; - } - break; + case NODE_TYPE_EXT_PATTERN_RESPONSEHEADER: { + const pattern = this.getNodeString(targetNode); + realBad = + pattern !== '' && removableHTTPHeaders.has(pattern) === false || + pattern === '' && isException === false; + break; + } + case NODE_TYPE_EXT_PATTERN_SCRIPTLET_TOKEN: { + if ( this.interactive !== true ) { break; } + if ( isException ) { break; } + const { trustedSource, trustedScriptletTokens } = this.options; + if ( trustedScriptletTokens instanceof Set === false ) { break; } + const token = this.getNodeString(targetNode); + if ( trustedScriptletTokens.has(token) && trustedSource !== true ) { + this.astError = AST_ERROR_UNTRUSTED_SOURCE; + realBad = true; } - default: - break; + break; + } + default: + break; } if ( realBad ) { this.addNodeFlags(targetNode, NODE_FLAG_ERROR); @@ -2420,7 +2418,7 @@ export class AstFilterParser { parentBeg + argsEnd ); this.linkDown(next, this.parseExtPatternScriptletArglist(next)); - prev = this.linkRight(prev, next); + this.linkRight(prev, next); return this.throwHeadNode(head); } @@ -2474,12 +2472,12 @@ export class AstFilterParser { const walker = this.getWalker(root); for ( let node = walker.next(); node !== 0; node = walker.next() ) { switch ( this.getNodeType(node) ) { - case NODE_TYPE_EXT_PATTERN_SCRIPTLET_TOKEN: - case NODE_TYPE_EXT_PATTERN_SCRIPTLET_ARG: - args.push(this.getNodeTransform(node)); - break; - default: - break; + case NODE_TYPE_EXT_PATTERN_SCRIPTLET_TOKEN: + case NODE_TYPE_EXT_PATTERN_SCRIPTLET_ARG: + args.push(this.getNodeTransform(node)); + break; + default: + break; } } walker.dispose(); @@ -3206,6 +3204,7 @@ class ExtSelectorCompiler { 'matches-css-before', 'matches-media', 'matches-path', + 'matches-prop', 'min-text-length', 'others', 'shadow', @@ -3842,6 +3841,7 @@ class ExtSelectorCompiler { case 'if-not': return this.compileSelector(arg); case 'matches-attr': + case 'matches-prop': return this.compileMatchAttrArgument(arg); case 'matches-css': return this.compileCSSDeclaration(arg); @@ -4037,7 +4037,7 @@ class ExtSelectorCompiler { compileAttrList(s) { if ( s === '' ) { return s; } - const attrs = s.split('\s*,\s*'); + const attrs = s.split(/\s*,\s*/); const out = []; for ( const attr of attrs ) { if ( attr !== '' ) { @@ -4075,6 +4075,7 @@ export const proceduralOperatorTokens = new Map([ [ 'matches-css', 0b11 ], [ 'matches-media', 0b11 ], [ 'matches-path', 0b11 ], + [ 'matches-prop', 0b11 ], [ 'min-text-length', 0b01 ], [ 'not', 0b01 ], [ 'nth-ancestor', 0b00 ], From ad3bdba7c922128a6046ed6e3b866a383d910fe6 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Wed, 19 Jun 2024 19:22:04 -0400 Subject: [PATCH 0024/1099] Update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0a7e40f5966ec..887521f3e136e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,4 @@ +- [Add `:matches-prop()` pseudo CSS operator](https://github.com/gorhill/uBlock/commit/aca7674bac) - [Improve `set-cookie` scriptlet](https://github.com/gorhill/uBlock/commit/b4d8750f44) - [Improve `trusted-replace-node-text` scriptlet](https://github.com/gorhill/uBlock/commit/cb0f65e035) - [Improve `trusted-replace-[fetch|xhr]-response` scriptlets](https://github.com/gorhill/uBlock/commit/9072772f61) From 459060f56455c946e251ee08b7145e90f20b9514 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Wed, 19 Jun 2024 19:22:28 -0400 Subject: [PATCH 0025/1099] New revision for dev build --- dist/version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dist/version b/dist/version index d8992862b105a..b8e2859e134f5 100644 --- a/dist/version +++ b/dist/version @@ -1 +1 @@ -1.58.1.8 \ No newline at end of file +1.58.1.9 \ No newline at end of file From aaceabeba1b54d07c5ec7045193821a3f4bd5b8e Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Wed, 19 Jun 2024 19:31:19 -0400 Subject: [PATCH 0026/1099] Make Firefox dev build auto-update --- dist/firefox/updates.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dist/firefox/updates.json b/dist/firefox/updates.json index 9bc0dbc777640..6d73ed717721b 100644 --- a/dist/firefox/updates.json +++ b/dist/firefox/updates.json @@ -3,9 +3,9 @@ "uBlock0@raymondhill.net": { "updates": [ { - "version": "1.58.1.8", + "version": "1.58.1.9", "browser_specific_settings": { "gecko": { "strict_min_version": "78.0" } }, - "update_link": "https://github.com/gorhill/uBlock/releases/download/1.58.1b8/uBlock0_1.58.1b8.firefox.signed.xpi" + "update_link": "https://github.com/gorhill/uBlock/releases/download/1.58.1b9/uBlock0_1.58.1b9.firefox.signed.xpi" } ] } From 83aee4a516524e95712280c1f34375960f59469a Mon Sep 17 00:00:00 2001 From: scripthunter7 Date: Thu, 20 Jun 2024 10:48:09 +0200 Subject: [PATCH 0027/1099] Update subscription URL for hufilter --- assets/assets.dev.json | 2 +- assets/assets.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/assets/assets.dev.json b/assets/assets.dev.json index 354087f59c147..ab6ecfd6505de 100644 --- a/assets/assets.dev.json +++ b/assets/assets.dev.json @@ -634,7 +634,7 @@ "title": "🇭🇺hu: hufilter", "tags": "ads hungarian", "lang": "hu", - "contentURL": "https://raw.githubusercontent.com/hufilter/hufilter/master/hufilter-ublock.txt", + "contentURL": "https://cdn.jsdelivr.net/gh/hufilter/hufilter@gh-pages/hufilter-ublock.txt", "supportURL": "https://github.com/hufilter/hufilter" }, "IDN-0": { diff --git a/assets/assets.json b/assets/assets.json index 44ccc1bad8fde..bd8fd84550dce 100644 --- a/assets/assets.json +++ b/assets/assets.json @@ -634,7 +634,7 @@ "title": "🇭🇺hu: hufilter", "tags": "ads hungarian", "lang": "hu", - "contentURL": "https://raw.githubusercontent.com/hufilter/hufilter/master/hufilter-ublock.txt", + "contentURL": "https://cdn.jsdelivr.net/gh/hufilter/hufilter@gh-pages/hufilter-ublock.txt", "supportURL": "https://github.com/hufilter/hufilter" }, "IDN-0": { From 47b985a0563d8e75420ff89de65c6ab45fb0b01b Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Thu, 20 Jun 2024 09:21:43 -0400 Subject: [PATCH 0028/1099] Fix `:matches-prop()` operator when no value provided --- src/js/contentscript-extra.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/js/contentscript-extra.js b/src/js/contentscript-extra.js index 4d10bb8782464..21feb1d390b32 100644 --- a/src/js/contentscript-extra.js +++ b/src/js/contentscript-extra.js @@ -188,7 +188,11 @@ class PSelectorMatchesPropTask extends PSelectorTask { if ( value === null ) { return; } value = value[prop]; } - if ( this.reValue !== null && this.reValue.test(value) === false ) { return; } + if ( this.reValue === null ) { + if ( value === undefined ) { return; } + } else if ( this.reValue.test(value) === false ) { + return; + } output.push(node); } } From 533c5c7c734fc6121c0fb15b8321d6397cd7e8f8 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Thu, 20 Jun 2024 20:59:21 -0400 Subject: [PATCH 0029/1099] New revision for dev build --- dist/version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dist/version b/dist/version index b8e2859e134f5..8aac1264dbb66 100644 --- a/dist/version +++ b/dist/version @@ -1 +1 @@ -1.58.1.9 \ No newline at end of file +1.58.1.10 \ No newline at end of file From 5dd59889b9eae9ef9fb64761dc4f2dd086d448ac Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Thu, 20 Jun 2024 21:06:09 -0400 Subject: [PATCH 0030/1099] Make Firefox dev build auto-update --- dist/firefox/updates.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dist/firefox/updates.json b/dist/firefox/updates.json index 6d73ed717721b..730d239ac4dcb 100644 --- a/dist/firefox/updates.json +++ b/dist/firefox/updates.json @@ -3,9 +3,9 @@ "uBlock0@raymondhill.net": { "updates": [ { - "version": "1.58.1.9", + "version": "1.58.1.10", "browser_specific_settings": { "gecko": { "strict_min_version": "78.0" } }, - "update_link": "https://github.com/gorhill/uBlock/releases/download/1.58.1b9/uBlock0_1.58.1b9.firefox.signed.xpi" + "update_link": "https://github.com/gorhill/uBlock/releases/download/1.58.1b10/uBlock0_1.58.1b10.firefox.signed.xpi" } ] } From 5c69159b36166f3b6cbc42735900eb5eb40558ba Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sat, 22 Jun 2024 11:06:19 -0400 Subject: [PATCH 0031/1099] [mv3] Inject procedural cosmetic filtering script earlier Related issue: https://github.com/uBlockOrigin/uBOL-home/issues/139 --- platform/mv3/extension/js/scripting-manager.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform/mv3/extension/js/scripting-manager.js b/platform/mv3/extension/js/scripting-manager.js index e6eebf29f7b9b..10175fc92e952 100644 --- a/platform/mv3/extension/js/scripting-manager.js +++ b/platform/mv3/extension/js/scripting-manager.js @@ -276,7 +276,7 @@ function registerProcedural(context) { allFrames: true, matches, excludeMatches, - runAt: 'document_end', + runAt: 'document_start', }; // register From 88065d0091cb3c69861501dcdf81ec8dff09e433 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sat, 22 Jun 2024 11:09:57 -0400 Subject: [PATCH 0032/1099] [mv3] Bring procdural cosmetic filtering code up to date with uBO --- .../extension/js/scripting/css-procedural.js | 57 ++++++++++++++----- 1 file changed, 42 insertions(+), 15 deletions(-) diff --git a/platform/mv3/extension/js/scripting/css-procedural.js b/platform/mv3/extension/js/scripting/css-procedural.js index 7f50f80989d32..9f6f039d770ab 100644 --- a/platform/mv3/extension/js/scripting/css-procedural.js +++ b/platform/mv3/extension/js/scripting/css-procedural.js @@ -21,8 +21,6 @@ /* jshint esversion:11 */ -'use strict'; - /******************************************************************************/ // Important! @@ -112,18 +110,21 @@ const uBOL_injectCSS = (css, count = 10) => { }; const nonVisualElements = { + head: true, + link: true, + meta: true, script: true, style: true, }; const regexFromString = (s, exact = false) => { if ( s === '' ) { return /^/; } - const match = /^\/(.+)\/([i]?)$/.exec(s); + const match = /^\/(.+)\/([imu]*)$/.exec(s); if ( match !== null ) { return new RegExp(match[1], match[2] || undefined); } const reStr = s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); - return new RegExp(exact ? `^${reStr}$` : reStr, 'i'); + return new RegExp(exact ? `^${reStr}$` : reStr); }; /******************************************************************************/ @@ -269,6 +270,32 @@ class PSelectorMatchesPathTask extends PSelectorTask { /******************************************************************************/ +class PSelectorMatchesPropTask extends PSelectorTask { + constructor(task) { + super(); + this.props = task[1].attr.split('.'); + this.reValue = task[1].value !== '' + ? regexFromString(task[1].value, true) + : null; + } + transpose(node, output) { + let value = node; + for ( const prop of this.props ) { + if ( value === undefined ) { return; } + if ( value === null ) { return; } + value = value[prop]; + } + if ( this.reValue === null ) { + if ( value === undefined ) { return; } + } else if ( this.reValue.test(value) === false ) { + return; + } + output.push(node); + } +} + +/******************************************************************************/ + class PSelectorMinTextLengthTask extends PSelectorTask { constructor(task) { super(); @@ -295,28 +322,27 @@ class PSelectorOthersTask extends PSelectorTask { const toKeep = new Set(this.targets); const toDiscard = new Set(); const body = document.body; + const head = document.head; let discard = null; for ( let keep of this.targets ) { - while ( keep !== null && keep !== body ) { + while ( keep !== null && keep !== body && keep !== head ) { toKeep.add(keep); toDiscard.delete(keep); discard = keep.previousElementSibling; while ( discard !== null ) { - if ( - nonVisualElements[discard.localName] !== true && - toKeep.has(discard) === false - ) { - toDiscard.add(discard); + if ( nonVisualElements[discard.localName] !== true ) { + if ( toKeep.has(discard) === false ) { + toDiscard.add(discard); + } } discard = discard.previousElementSibling; } discard = keep.nextElementSibling; while ( discard !== null ) { - if ( - nonVisualElements[discard.localName] !== true && - toKeep.has(discard) === false - ) { - toDiscard.add(discard); + if ( nonVisualElements[discard.localName] !== true ) { + if ( toKeep.has(discard) === false ) { + toDiscard.add(discard); + } } discard = discard.nextElementSibling; } @@ -570,6 +596,7 @@ PSelector.prototype.operatorToTaskMap = new Map([ [ 'matches-css-before', PSelectorMatchesCSSBeforeTask ], [ 'matches-media', PSelectorMatchesMediaTask ], [ 'matches-path', PSelectorMatchesPathTask ], + [ 'matches-prop', PSelectorMatchesPropTask ], [ 'min-text-length', PSelectorMinTextLengthTask ], [ 'not', PSelectorIfNotTask ], [ 'others', PSelectorOthersTask ], From 8eb3b19c69e5960da2868095d82dc9403302c478 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Tue, 25 Jun 2024 09:08:46 -0400 Subject: [PATCH 0033/1099] Improve logging in `prevent-addEventListener` scriptlet Related feedback: https://github.com/uBlockOrigin/uAssets/discussions/17907#discussioncomment-9871079 --- assets/resources/scriptlets.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/assets/resources/scriptlets.js b/assets/resources/scriptlets.js index 2781fad3b0191..18cdfadb55958 100644 --- a/assets/resources/scriptlets.js +++ b/assets/resources/scriptlets.js @@ -1661,7 +1661,9 @@ function addEventListenerDefuser( if ( elem instanceof Document ) { return 'document'; } if ( elem instanceof Element === false ) { return '?'; } const parts = []; - if ( elem.id !== '' ) { parts.push(`#${CSS.escape(elem.id)}`); } + // https://github.com/uBlockOrigin/uAssets/discussions/17907#discussioncomment-9871079 + const id = String(elem.id); + if ( id !== '' ) { parts.push(`#${CSS.escape(id)}`); } for ( let i = 0; i < elem.classList.length; i++ ) { parts.push(`.${CSS.escape(elem.classList.item(i))}`); } From 8447fc5d170fa76129d947acd0d0e174581e1314 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Tue, 25 Jun 2024 09:59:07 -0400 Subject: [PATCH 0034/1099] New revision for dev build --- dist/version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dist/version b/dist/version index 8aac1264dbb66..67372adde24f3 100644 --- a/dist/version +++ b/dist/version @@ -1 +1 @@ -1.58.1.10 \ No newline at end of file +1.58.1.11 \ No newline at end of file From 6b349ca0ef76401b376d8eb487ffd3b20837bfb0 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Tue, 25 Jun 2024 10:05:54 -0400 Subject: [PATCH 0035/1099] Make Firefox dev build auto-update --- dist/firefox/updates.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dist/firefox/updates.json b/dist/firefox/updates.json index 730d239ac4dcb..d5628408f488c 100644 --- a/dist/firefox/updates.json +++ b/dist/firefox/updates.json @@ -3,9 +3,9 @@ "uBlock0@raymondhill.net": { "updates": [ { - "version": "1.58.1.10", + "version": "1.58.1.11", "browser_specific_settings": { "gecko": { "strict_min_version": "78.0" } }, - "update_link": "https://github.com/gorhill/uBlock/releases/download/1.58.1b10/uBlock0_1.58.1b10.firefox.signed.xpi" + "update_link": "https://github.com/gorhill/uBlock/releases/download/1.58.1b11/uBlock0_1.58.1b11.firefox.signed.xpi" } ] } From 896737d098d329af1b44f6a93a145872a9086f96 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Wed, 26 Jun 2024 11:00:50 -0400 Subject: [PATCH 0036/1099] Fix race condition when loading redirect/scriptlet resources Related feedback: https://github.com/uBlockOrigin/uAssets/issues/23806#issuecomment-2190491767 --- src/js/storage.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/js/storage.js b/src/js/storage.js index b26ff00c6462c..1163483220996 100644 --- a/src/js/storage.js +++ b/src/js/storage.js @@ -1008,12 +1008,12 @@ onBroadcast(msg => { ubolog('loadFilterLists() Start'); t0 = Date.now(); loadedListKeys.length = 0; - loadingPromise = Promise.all([ - this.getAvailableLists().then(lists => onFilterListsReady(lists)), - this.loadRedirectResources().then(( ) => { - ubolog(`loadFilterLists() Redirects/scriptlets ready at ${elapsed()}`); - }), - ]).then(( ) => { + loadingPromise = this.loadRedirectResources().then(( ) => { + ubolog(`loadFilterLists() Redirects/scriptlets ready at ${elapsed()}`); + return this.getAvailableLists(); + }).then(lists => { + return onFilterListsReady(lists) + }).then(( ) => { onDone(); }); return loadingPromise; From 57ed2937606fe6c9876efb218724d01dc584498f Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Fri, 28 Jun 2024 13:39:38 -0400 Subject: [PATCH 0037/1099] [mv3] Fix bad test re. managed storage --- platform/mv3/extension/js/ext.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform/mv3/extension/js/ext.js b/platform/mv3/extension/js/ext.js index 185fa9c92745d..0126c1894f7ad 100644 --- a/platform/mv3/extension/js/ext.js +++ b/platform/mv3/extension/js/ext.js @@ -107,7 +107,7 @@ export async function sessionWrite(key, value) { export async function adminRead(key) { if ( browser.storage instanceof Object === false ) { return; } - if ( browser.storage.local instanceof Object === false ) { return; } + if ( browser.storage.managed instanceof Object === false ) { return; } try { const bin = await browser.storage.managed.get(key); if ( bin instanceof Object === false ) { return; } From c154aaa69c6a2a4009d43923f1097c6298062b82 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Fri, 28 Jun 2024 13:40:19 -0400 Subject: [PATCH 0038/1099] Fix bad serialization of Date objects Related issue: https://github.com/uBlockOrigin/uBlock-issues/issues/3283 --- src/js/s14e-serializer.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/js/s14e-serializer.js b/src/js/s14e-serializer.js index 0c9200ebcaa14..8ef715a23ac10 100644 --- a/src/js/s14e-serializer.js +++ b/src/js/s14e-serializer.js @@ -592,7 +592,8 @@ const _serialize = data => { return; } if ( xtypeInt === I_DATE ) { - writeBuffer.push(C_DATE + _serialize(data.getTime())); + writeBuffer.push(C_DATE); + _serialize(data.getTime()); return; } // Reference to composite types From 1d7feb83a23bd3c9c70612f8ea2205eba0fde0b6 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Fri, 5 Jul 2024 10:01:28 -0400 Subject: [PATCH 0039/1099] [firefox] Add entry for 128px icon Related issue: https://github.com/uBlockOrigin/uBlock-issues/issues/3285 --- platform/firefox/manifest.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/platform/firefox/manifest.json b/platform/firefox/manifest.json index aa49d395a9291..bd347e5eeadd8 100644 --- a/platform/firefox/manifest.json +++ b/platform/firefox/manifest.json @@ -99,7 +99,8 @@ "32": "img/ublock.svg", "48": "img/ublock.svg", "64": "img/ublock.svg", - "96": "img/ublock.svg" + "96": "img/ublock.svg", + "128": "img/ublock.svg" }, "manifest_version": 2, "name": "uBlock Origin", From 8c5918dec78f7217103eace03ec6f2c376224b90 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Fri, 5 Jul 2024 10:11:27 -0400 Subject: [PATCH 0040/1099] Prevent resizing of title bar in popup menu Related issue: https://github.com/uBlockOrigin/uBlock-issues/issues/3284 --- src/popup-fenix.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/popup-fenix.html b/src/popup-fenix.html index 9abf3ddbab9a3..7c43e980ace09 100644 --- a/src/popup-fenix.html +++ b/src/popup-fenix.html @@ -16,7 +16,7 @@
-
­
+
­ 
lock From 9569969b55639dcd30c53a99212f2feeb1f20a71 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Fri, 5 Jul 2024 10:20:33 -0400 Subject: [PATCH 0041/1099] Fix distance calculation in picker Related issue: https://github.com/uBlockOrigin/uBlock-issues/issues/3279 --- src/js/epicker-ui.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/js/epicker-ui.js b/src/js/epicker-ui.js index 981602b66f354..d2b14df65dce0 100644 --- a/src/js/epicker-ui.js +++ b/src/js/epicker-ui.js @@ -386,7 +386,7 @@ const onSvgTouch = (( ) => { const stopY = ev.changedTouches[0].screenY; const angle = Math.abs(Math.atan2(stopY - startY, stopX - startX)); const distance = Math.sqrt( - Math.pow(stopX - startX, 2), + Math.pow(stopX - startX, 2) + Math.pow(stopY - startY, 2) ); // Interpret touch events as a tap if: From efc16c7069e88625758bacf63e2b5898a548c91f Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Fri, 5 Jul 2024 10:34:02 -0400 Subject: [PATCH 0042/1099] Update changelog --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 887521f3e136e..5a0b4497d8a1c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +- [Fix distance calculation in picker](https://github.com/gorhill/uBlock/commit/9569969b55) +- [Fix bad serialization of Date objects](https://github.com/gorhill/uBlock/commit/c154aaa69c) +- [Fix race condition when loading redirect/scriptlet resources](https://github.com/gorhill/uBlock/commit/896737d098) +- [Improve logging in `prevent-addEventListener` scriptlet](https://github.com/gorhill/uBlock/commit/8eb3b19c69) - [Add `:matches-prop()` pseudo CSS operator](https://github.com/gorhill/uBlock/commit/aca7674bac) - [Improve `set-cookie` scriptlet](https://github.com/gorhill/uBlock/commit/b4d8750f44) - [Improve `trusted-replace-node-text` scriptlet](https://github.com/gorhill/uBlock/commit/cb0f65e035) From 7343035a170f3d8eb6ca7c6735235fbc55a51492 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Fri, 5 Jul 2024 10:34:26 -0400 Subject: [PATCH 0043/1099] New revision for dev build --- dist/version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dist/version b/dist/version index 67372adde24f3..2e21c3e9f0304 100644 --- a/dist/version +++ b/dist/version @@ -1 +1 @@ -1.58.1.11 \ No newline at end of file +1.58.1.12 \ No newline at end of file From 966a2332197ed1c3ff9295699bcd42cde0549c64 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Fri, 5 Jul 2024 10:41:17 -0400 Subject: [PATCH 0044/1099] Make Firefox dev build auto-update --- dist/firefox/updates.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dist/firefox/updates.json b/dist/firefox/updates.json index d5628408f488c..cb8dce834d762 100644 --- a/dist/firefox/updates.json +++ b/dist/firefox/updates.json @@ -3,9 +3,9 @@ "uBlock0@raymondhill.net": { "updates": [ { - "version": "1.58.1.11", + "version": "1.58.1.12", "browser_specific_settings": { "gecko": { "strict_min_version": "78.0" } }, - "update_link": "https://github.com/gorhill/uBlock/releases/download/1.58.1b11/uBlock0_1.58.1b11.firefox.signed.xpi" + "update_link": "https://github.com/gorhill/uBlock/releases/download/1.58.1b12/uBlock0_1.58.1b12.firefox.signed.xpi" } ] } From 37d31a82d8d55a33e1c1ed86678183e51d209b23 Mon Sep 17 00:00:00 2001 From: Fanboynz Date: Mon, 8 Jul 2024 01:25:34 +1200 Subject: [PATCH 0045/1099] Add essential and nonessential to set-cookie (#3919) --- assets/resources/scriptlets.js | 1 + 1 file changed, 1 insertion(+) diff --git a/assets/resources/scriptlets.js b/assets/resources/scriptlets.js index 18cdfadb55958..7a967d394c212 100644 --- a/assets/resources/scriptlets.js +++ b/assets/resources/scriptlets.js @@ -3791,6 +3791,7 @@ function setCookie( 'necessary', 'required', 'approved', 'disapproved', 'hide', 'hidden', + 'essential', 'nonessential', ]; const normalized = value.toLowerCase(); const match = /^("?)(.+)\1$/.exec(normalized); From a3576ea6519dc08e5244dafc296dc8ac31b07655 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sun, 7 Jul 2024 11:18:20 -0400 Subject: [PATCH 0046/1099] Add support for `$currentISODate$` in `trusted-set-cookie` scriptlet Related discussion: https://github.com/uBlockOrigin/uAssets/discussions/20789#discussioncomment-9979107 --- assets/resources/scriptlets.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/assets/resources/scriptlets.js b/assets/resources/scriptlets.js index 7a967d394c212..cc0f7033bf3e9 100644 --- a/assets/resources/scriptlets.js +++ b/assets/resources/scriptlets.js @@ -4248,6 +4248,9 @@ function trustedSetCookie( if ( value.includes('$currentDate$') ) { value = value.replaceAll('$currentDate$', time.toUTCString()); } + if ( value.includes('$currentISODate$') ) { + value = value.replaceAll('$currentISODate$', time.toISOString()); + } let expires = ''; if ( offsetExpiresSec !== '' ) { From 97d11c03c20bdc15877c342c404f179ca5c63ff6 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Tue, 9 Jul 2024 13:03:50 -0400 Subject: [PATCH 0047/1099] Add `trusted-suppress-native-method` scriptlet Reference: https://github.com/AdguardTeam/Scriptlets/blob/5a92d79489/wiki/about-trusted-scriptlets.md#trusted-suppress-native-method This is a first draft version, see code comments for details. --- assets/resources/scriptlets.js | 100 +++++++++++++++++++++++++++++++-- 1 file changed, 95 insertions(+), 5 deletions(-) diff --git a/assets/resources/scriptlets.js b/assets/resources/scriptlets.js index cc0f7033bf3e9..80c251fb58f5d 100644 --- a/assets/resources/scriptlets.js +++ b/assets/resources/scriptlets.js @@ -4756,24 +4756,29 @@ builtinScriptlets.push({ }); function trustedReplaceArgument( propChain = '', - argpos = '', + argposRaw = '', argraw = '' ) { if ( propChain === '' ) { return; } - if ( argpos === '' ) { return; } - if ( argraw === '' ) { return; } const safe = safeSelf(); - const logPrefix = safe.makeLogPrefix('trusted-replace-argument', propChain, argpos, argraw); + const logPrefix = safe.makeLogPrefix('trusted-replace-argument', propChain, argposRaw, argraw); + const argpos = parseInt(argposRaw, 10) || 0; const extraArgs = safe.getExtraArgs(Array.from(arguments), 3); const normalValue = validateConstantFn(true, argraw); const reCondition = extraArgs.condition ? safe.patternToRegex(extraArgs.condition) : /^/; const reflector = proxyApplyFn(propChain, function(...args) { + if ( argposRaw === '' ) { + safe.uboLog(logPrefix, `Arguments:\n${args.join('\n')}`); + return reflector(...args); + } const arglist = args[args.length-1]; if ( Array.isArray(arglist) === false ) { return reflector(...args); } const argBefore = arglist[argpos]; - if ( reCondition.test(argBefore) === false ) { return reflector(...args); } + if ( safe.RegExp_test.call(reCondition, argBefore) === false ) { + return reflector(...args); + } arglist[argpos] = normalValue; safe.uboLog(logPrefix, `Replaced argument:\nBefore: ${JSON.stringify(argBefore)}\nAfter: ${normalValue}`); return reflector(...args); @@ -4830,4 +4835,89 @@ function trustedReplaceOutboundText( }); } +/******************************************************************************* + * + * Reference: + * https://github.com/AdguardTeam/Scriptlets/blob/5a92d79489/wiki/about-trusted-scriptlets.md#trusted-suppress-native-method + * + * This is a first version with current limitations: + * - Does not support matching arguments which are object or array + * - Does not support `stack` parameter + * + * If `signatureStr` parameter is not declared, the scriptlet will log all calls + * to `methodPath` along with the arguments passed and will not prevent the + * trapped method. + * + * */ + +builtinScriptlets.push({ + name: 'trusted-suppress-native-method.js', + requiresTrust: true, + fn: trustedSuppressNativeMethod, + dependencies: [ + 'proxy-apply.fn', + 'safe-self.fn', + ], +}); +function trustedSuppressNativeMethod( + methodPath = '', + signature = '', + how = '', + stack = '' +) { + if ( methodPath === '' ) { return; } + if ( stack !== '' ) { return; } + const safe = safeSelf(); + const logPrefix = safe.makeLogPrefix('trusted-suppress-native-method', methodPath, signature, how); + const signatureArgs = signature.split(/\s*\|\s*/).map(v => { + if ( /^".*"$/.test(v) ) { + return { type: 'pattern', re: safe.patternToRegex(v.slice(1, -1)) }; + } + if ( v === 'false' ) { + return { type: 'exact', value: false }; + } + if ( v === 'true' ) { + return { type: 'exact', value: true }; + } + if ( v === 'null' ) { + return { type: 'exact', value: null }; + } + if ( v === 'undefined' ) { + return { type: 'exact', value: undefined }; + } + }); + const reflector = proxyApplyFn(methodPath, function(...args) { + if ( signature === '' ) { + safe.uboLog(logPrefix, `Arguments:\n${args.join('\n')}`); + return reflector(...args); + } + const arglist = args[args.length-1]; + if ( Array.isArray(arglist) === false ) { + return reflector(...args); + } + if ( arglist.length < signatureArgs.length ) { + return reflector(...args); + } + for ( let i = 0; i < signatureArgs.length; i++ ) { + const signatureArg = signatureArgs[i]; + if ( signatureArg === undefined ) { continue; } + const targetArg = arglist[i]; + if ( signatureArg.type === 'exact' ) { + if ( targetArg !== signatureArg.value ) { + return reflector(...args); + } + } + if ( signatureArg.type === 'pattern' ) { + if ( safe.RegExp_test.call(signatureArg.re, targetArg) === false ) { + return reflector(...args); + } + } + } + safe.uboLog(logPrefix, `Suppressed:\n${args.join('\n')}`); + if ( how === 'abort' ) { + throw new ReferenceError(); + } + }); +} + /******************************************************************************/ From 25f8c03fde49990e926ba6832fc05cef9fbdaa0d Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Tue, 9 Jul 2024 13:06:31 -0400 Subject: [PATCH 0048/1099] Update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5a0b4497d8a1c..d8b94bce1604f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,4 @@ +- [Add `trusted-suppress-native-method` scriptlet](https://github.com/gorhill/uBlock/commit/97d11c03c2) - [Fix distance calculation in picker](https://github.com/gorhill/uBlock/commit/9569969b55) - [Fix bad serialization of Date objects](https://github.com/gorhill/uBlock/commit/c154aaa69c) - [Fix race condition when loading redirect/scriptlet resources](https://github.com/gorhill/uBlock/commit/896737d098) From 2bdbac1b84e6615a80ca2100f94ef594ac599030 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Tue, 9 Jul 2024 13:16:17 -0400 Subject: [PATCH 0049/1099] Import translation work from https://crowdin.com/project/ublock --- platform/mv3/description/webstore.eu.txt | 6 +-- platform/mv3/description/webstore.id.txt | 2 +- platform/mv3/description/webstore.it.txt | 18 ++++---- platform/mv3/description/webstore.kn.txt | 8 ++-- .../mv3/extension/_locales/it/messages.json | 34 +++++++-------- .../mv3/extension/_locales/kn/messages.json | 8 ++-- .../mv3/extension/_locales/ro/messages.json | 4 +- src/_locales/it/messages.json | 42 +++++++++---------- src/_locales/kn/messages.json | 26 ++++++------ src/_locales/nb/messages.json | 4 +- 10 files changed, 76 insertions(+), 76 deletions(-) diff --git a/platform/mv3/description/webstore.eu.txt b/platform/mv3/description/webstore.eu.txt index 8f15a42a9bec0..f24ed1d5fe65e 100644 --- a/platform/mv3/description/webstore.eu.txt +++ b/platform/mv3/description/webstore.eu.txt @@ -1,6 +1,6 @@ uBO Lite (uBOL) irakargarri blokeatzailea de, baimen gutxiekin eta MV3ean basatua -The default ruleset corresponds to uBlock Origin's default filterset: +Lehenespenez, iragazki-zerrenda hauek ditu konfiguratuta: UblockOrigin-eko filtro lista ZerrendaErraza @@ -17,9 +17,9 @@ However, uBOL allows you to *explicitly* grant extended permissions on specific Leku jakin batean baimenak emateko, ireki panel emergentea eta aukeratu goiko iragazteko modu bat, optimo edo oso gisa. -The browser will then warn you about the effects of granting the additional permissions requested by the extension on the current site, and you will have to tell the browser whether you accept or decline the request. +Nabigatzaileak orduan jakinaraziko dizu zer ondorio dituen luzapenak eskatutako baimen gehigarriak emateak egungo gunean, eta nabigatzaileari esan beharko diozu eskaera onartzen duzun edo uko egiten diozun. -If you accept uBOL's request for additional permissions on the current site, it will be able to better filter content for the current site. +Baimen gehigarrien eskaera onartzen baduzu, oraingo gunean edukiak hobeto iragazi ahal izango ditu. You can set the default filtering mode from uBOL's options page. If you pick the Optimal or Complete mode as the default one, you will need to grant uBOL the permission to read and modify data on all websites. diff --git a/platform/mv3/description/webstore.id.txt b/platform/mv3/description/webstore.id.txt index 33c86b66979cc..8d6c7b8b15376 100644 --- a/platform/mv3/description/webstore.id.txt +++ b/platform/mv3/description/webstore.id.txt @@ -7,7 +7,7 @@ Kumpulan aturan bawaan sesuai dengan kumpulan penyaringan bawaan uBlock Origin: - EasyPrivacy - Daftar server iklan dan pelacak Peter Lowe -kamu dapat menambahkan rulesets di halaman opsi -- klik ikon _Cogs_ di panel popup. +Anda dapat menambahkan ruleset dengan mengunjungi halaman options -- klik ikon _Cogs_ di panel popup. uBOL sepenuhnya deklaratif, yang mana tidak membutuhkan proses permanen uBOL agar penyaringan dapat terjadi, dan penyaringan konten berbasis injeksi CSS/JS dilakukan sepenuhnya oleh peramban itu sendiri ketimbang oleh ekstensi. Ini berarti bahwa uBOL sendiri tidak mengkonsumsi sumber daya CPU/memori selama melakukan pemblokiran konten -- proses pekerja layanan uBOL dibutuhkan _hanya_ ketika Anda berinteraksi dengan panel popup atau opsi halaman. diff --git a/platform/mv3/description/webstore.it.txt b/platform/mv3/description/webstore.it.txt index 0aa86f4f846a6..03f1d7122c32a 100644 --- a/platform/mv3/description/webstore.it.txt +++ b/platform/mv3/description/webstore.it.txt @@ -1,30 +1,30 @@ uBO Lite (uBOL) è un sistema per bloccare contenuti che *non richiede autorizzazioni* basato su MV3. -L'insieme di regole predefinite corrisponde a quello di uBlock Origin: +L'insieme di regole predefinito corrisponde all'insieme di filtri predefinito di uBlock Origin: -- Elenco dei filtri integrati in uBlock Origin +- Liste dei filtri integrati di uBlock Origin - EasyList - EasyPrivacy -- Elenco dei server di tracciatura e pubblicità di Peter Lowe +- Elenco dei server pubblicitari e di tracciamento di Peter Lowe -Puoi aggiungere altre regole nella pagina delle opzioni. Clicca sull'icona _Ingranaggi_ nel pannello a comparsa. +Puoi aggiungere altri insiemi di regole visitando la pagina delle opzioni: clicca sull'icona con gli _Ingranaggi_ nel pannello a comparsa. -uBOL è interamente dichiarativo ovvero non è necessario un processo uBOL permanente per eseguire il filtraggio e il filtraggio dei contenuti CSS/JS inietattai viene eseguito in modo affidabile dal browser stesso piuttosto che dall'estensione. Ciò significa che lo stesso uBOL non consuma risorse di CPU/memoria mentre il blocco dei contenuti è in corso: il processo di lavoro di servizio di uBOL è richiesto _solo_ quando interagisci con il pannello popup o le pagine delle opzioni. +uBOL è interamente dichiarativo, ovvero non è necessario che ci sia un processo di uBOL permanente per poter eseguire il filtraggio; e il filtraggio dei contenuti basato sull'iniezione di elementi CSS/JS viene eseguito in modo affidabile dal browser stesso piuttosto che dall'estensione. Ciò significa che lo stesso uBOL non consuma risorse di CPU o memoria mentre il blocco dei contenuti viene eseguito: il processo che esegue il servizio di uBOL è richiesto _solamente_ quando interagisci con il pannello a comparsa o con le pagine delle opzioni. uBOL non richiede un'ampia autorizzazione di "lettura e modifica dei dati" al momento dell'installazione, da qui le sue capacità limitate rispetto a uBlock Origin o ad altre estensioni che richiedono ampie autorizzazioni di "lettura e modifica dei dati" al momento dell'installazione. Tuttavia, uBOL consente di concedere *esplicitamente* permessi estesi a siti specifici di vostra scelta, in modo da poter filtrare meglio tali siti utilizzando il filtraggio cosmetico e le iniezioni di scriptlet. -Per concedere autorizzazioni estese su un determinato sito, apri il pannello popup e scegli una modalità di filtraggio più restrittiva come Ottimale o Completa. +Per concedere autorizzazioni estese su un determinato sito, apri il pannello popup e scegli una modalità di filtraggio più elevata come Ottimale o Completa. -Il browser ti avviserà degli effetti della concessione delle autorizzazioni aggiuntive richieste dall'estensione sul sito corrente e dovrai comunicare al browser se accetti o rifiuti la richiesta. +Il browser quindi ti avviserà degli effetti della concessione delle autorizzazioni aggiuntive richieste dall'estensione sul sito corrente e dovrai comunicare al browser se accetti o rifiuti la richiesta. -Se accetti la richiesta di uBOL per ulteriori autorizzazioni sul sito corrente, sarà in grado di filtrare meglio i contenuti per il sito corrente. +Se accetti la richiesta di uBOL per autorizzazioni aggiuntive sul sito corrente, esso sarà in grado di filtrare meglio il contenuto per il sito corrente. Puoi impostare la modalità di filtraggio predefinita dalla pagina delle opzioni di uBOL. Se scegli come predefinita la modalità Ottimale o Completa, dovrai concedere a uBOL il permesso di leggere e modificare i dati di tutti i siti web. Tieni presente che questo è ancora un work in progress, con questi obiettivi finali: -- Nessuna autorizzazione host ampia al momento dell'installazione: le autorizzazioni estese vengono concesse esplicitamente dall'utente in base al sito. +- Nessuna autorizzazione estesa degli host al momento dell'installazione; le autorizzazioni estese vengono concesse esplicitamente dall'utente in base al sito. - Interamente dichiarativo per l'affidabilità e l'efficienza della CPU/memoria. diff --git a/platform/mv3/description/webstore.kn.txt b/platform/mv3/description/webstore.kn.txt index e03fa801ee7f0..535ab9a320dc5 100644 --- a/platform/mv3/description/webstore.kn.txt +++ b/platform/mv3/description/webstore.kn.txt @@ -9,9 +9,9 @@ The default ruleset corresponds to uBlock Origin's default filterset: You can add more rulesets by visiting the options page -- click the _Cogs_ icon in the popup panel. -uBOL is entirely declarative, meaning there is no need for a permanent uBOL process for the filtering to occur, and CSS/JS injection-based content filtering is performed reliably by the browser itself rather than by the extension. This means that uBOL itself does not consume CPU/memory resources while content blocking is ongoing -- uBOL's service worker process is required _only_ when you interact with the popup panel or the option pages. +uBOL is entirely declarative, meaning there is no need for a permanent uBOL process for the filtering to occur, and CSS/JS injection-based content filtering is performed reliably by the browser itself rather than by the extension. ಇದರರ್ಥ ವಿಷಯ ನಿರ್ಬಂಧಿಸುವಿಕೆಯು ನಡೆಯುತ್ತಿರುವಾಗ uBOL ಸ್ವತಃ CPU/ಮೆಮೊರಿ ಸಂಪನ್ಮೂಲಗಳನ್ನು ಬಳಸುವುದಿಲ್ಲ -- ನೀವು ಪಾಪ್ಅಪ್ ಪ್ಯಾನೆಲ್ ಅಥವಾ ಆಯ್ಕೆಯ ಪುಟಗಳೊಂದಿಗೆ ಸಂವಹನ ನಡೆಸಿದಾಗ uBOL ನ ಸೇವಾ ವರ್ಕರ್ ಪ್ರಕ್ರಿಯೆಯು _ಮಾತ್ರಾ_ ಅಗತ್ಯವಿದೆ. -uBOL does not require broad "read and modify data" permission at install time, hence its limited capabilities out of the box compared to uBlock Origin or other content blockers requiring broad "read and modify data" permissions at install time. +uBOL ಗೆ ಅನುಸ್ಥಾಪನೆಯ ಸಮಯದಲ್ಲಿ ವಿಶಾಲವಾದ "ಡೇಟಾವನ್ನು ಓದಲು ಮತ್ತು ಮಾರ್ಪಡಿಸಲು" ಅನುಮತಿಯ ಅಗತ್ಯವಿರುವುದಿಲ್ಲ, ಆದ್ದರಿಂದ uBlock ಮೂಲಕ್ಕೆ ಹೋಲಿಸಿದರೆ ಅದರ ಸೀಮಿತ ಸಾಮರ್ಥ್ಯಗಳು ಅಥವಾ ಅನುಸ್ಥಾಪನೆಯ ಸಮಯದಲ್ಲಿ ವಿಶಾಲವಾದ "ಡೇಟಾವನ್ನು ಓದಲು ಮತ್ತು ಮಾರ್ಪಡಿಸಲು" ಅನುಮತಿಗಳ ಅಗತ್ಯವಿರುವ ಇತರ ವಿಷಯ ಬ್ಲಾಕರ್‌ಗಳಿಗೆ ಹೋಲಿಸಿದರೆ. However, uBOL allows you to *explicitly* grant extended permissions on specific sites of your choice so that it can better filter on those sites using cosmetic filtering and scriptlet injections. @@ -21,9 +21,9 @@ The browser will then warn you about the effects of granting the additional perm If you accept uBOL's request for additional permissions on the current site, it will be able to better filter content for the current site. -You can set the default filtering mode from uBOL's options page. If you pick the Optimal or Complete mode as the default one, you will need to grant uBOL the permission to read and modify data on all websites. +You can set the default filtering mode from uBOL's options page. ನೀವು ಆಪ್ಟಿಮಲ್ ಅಥವಾ ಕಂಪ್ಲೀಟ್ ಮೋಡ್ ಅನ್ನು ಡಿಫಾಲ್ಟ್ ಆಗಿ ಆರಿಸಿದರೆ, ಎಲ್ಲಾ ವೆಬ್‌ಸೈಟ್‌ಗಳಲ್ಲಿನ ಡೇಟಾವನ್ನು ಓದಲು ಮತ್ತು ಮಾರ್ಪಡಿಸಲು ನೀವು uBOL ಗೆ ಅನುಮತಿಯನ್ನು ನೀಡಬೇಕಾಗುತ್ತದೆ. -Keep in mind this is still a work in progress, with these end goals: +ಈ ಅಂತಿಮ ಗುರಿಗಳೊಂದಿಗೆ ಇದು ಇನ್ನೂ ಪ್ರಗತಿಯಲ್ಲಿದೆ ಎಂಬುದನ್ನು ನೆನಪಿನಲ್ಲಿಡಿ: - No broad host permissions at install time -- extended permissions are granted explicitly by the user on a per-site basis. diff --git a/platform/mv3/extension/_locales/it/messages.json b/platform/mv3/extension/_locales/it/messages.json index beea941700736..cf3082df674f6 100644 --- a/platform/mv3/extension/_locales/it/messages.json +++ b/platform/mv3/extension/_locales/it/messages.json @@ -8,7 +8,7 @@ "description": "this will be in the Chrome web store: must be 132 characters or less" }, "perRulesetStats": { - "message": "{{ruleCount}} Regole, convertite da {{filterCount}} filtri di rete", + "message": "{{ruleCount}} regole, convertite da {{filterCount}} filtri di rete", "description": "Appears aside each filter list in the _3rd-party filters_ pane" }, "dashboardName": { @@ -60,15 +60,15 @@ "description": "Header for a ruleset section in 'Filter lists pane'" }, "3pGroupAnnoyances": { - "message": "Disturbi", + "message": "Elementi fastidiosi", "description": "Header for a ruleset section in 'Filter lists pane'" }, "3pGroupMisc": { - "message": "Generici", + "message": "Varie", "description": "Header for a ruleset section in 'Filter lists pane'" }, "3pGroupRegions": { - "message": "Lingue e regioni", + "message": "Regioni, lingue", "description": "Header for a ruleset section in 'Filter lists pane'" }, "aboutChangelog": { @@ -92,27 +92,27 @@ "description": "Link text to translations repo" }, "aboutFilterLists": { - "message": "Elenco filtri", + "message": "Liste dei filtri", "description": "Link text to uBO's own filter lists repo" }, "aboutDependencies": { - "message": "Dipendenze esterne (in linea con la GPL v3):", + "message": "Dipendenze esterne (GPLv3-compatibili):", "description": "Shown in the About pane" }, "firstRunSectionLabel": { - "message": "Ciao", + "message": "Benvenuto", "description": "The header text for the welcome message section" }, "firstRunDescription": { - "message": "Hai installato uBO Lite. Scegli il filtro predefinito da usare ovunque.\n\nL'impostazione di fabbrica prevede la modalità Basilare che non richiede alcun tipo di autorizzazione. Puoi decidere di attivare il filtro avanzato se vuoi concedere a uBO Lite l'autorizzazione a rielaborare i dati di tutti i siti web che visiti, in modo da avere un sistema di filtraggio con altre funzioni in più.", + "message": "Hai appena installato uBO Lite. Qui puoi scegliere la modalità di filtraggio predefinita da utilizzare su tutti i siti web.\n\nPer impostazione predefinita, viene selezionata la modalità Basilare, perché non richiede l'autorizzazione per leggere e modificare i dati. Se ti fidi di uBO Lite, puoi concedergli un'ampia autorizzazione per leggere e modificare i dati su tutti i siti web, in modo da abilitare capacitità di filtraggio più avanzate per tutti i siti web in modo predefinito.", "description": "Descriptive text shown at first install time only " }, "defaultFilteringModeSectionLabel": { - "message": "Filtro predefinito", + "message": "Modalità di filtraggio predefinita", "description": "The header text for the default filtering mode section" }, "defaultFilteringModeDescription": { - "message": "Il filtraggio predefinito può essere sostituito dal filtraggio personalizzato. Puoi regolare il filtraggio per ogni singolo sito web al fine di ottenere il risultato migliore. Ognuno dei filtraggi presenta vantaggi e svantaggi.", + "message": "La modalità di filtraggio predefinita sarà sovrascritta dalle modalità di filtraggio per sito web. È possibile regolare la modalità di filtraggio su un determinato sito web in base alla modalità che funziona meglio su quel sito. Ogni modalità presenta vantaggi e svantaggi.", "description": "This describes the default filtering mode setting" }, "filteringMode0Name": { @@ -120,7 +120,7 @@ "description": "Name of blocking mode 0" }, "filteringMode1Name": { - "message": "di base", + "message": "basilare", "description": "Name of blocking mode 1" }, "filteringMode2Name": { @@ -132,19 +132,19 @@ "description": "Name of blocking mode 3" }, "basicFilteringModeDescription": { - "message": "Filtro di rete basilare basato su un elenco selezionato di filtri.\n\nNon servono autorizzazioni per rielaborare i dati dei siti web.", + "message": "Filtraggio di rete di base da liste di filtri selezionate.\n\nNon richiede l'autorizzazione per leggere e modificare i dati sui siti web.", "description": "This describes the 'basic' filtering mode" }, "optimalFilteringModeDescription": { - "message": "Filtro di rete avanzato in aggiunta al filtro avanzato basato su un elenco selezionato di filtri.\n\nRichiede delle autorizzazioni specifiche per rielaborare i dati dei siti web.", + "message": "Filtraggio di rete avanzato più un filtraggio esteso specifico da liste di filtri selezionate.\n\nRichiede un'ampia autorizzazione per leggere e modificare i dati su tutti i siti web.", "description": "This describes the 'optimal' filtering mode" }, "completeFilteringModeDescription": { - "message": "Filtro avanzato di rete in aggiunta al filtro esteso basato su un elenco selezionato di filtri.\n\nRichiede autorizzazioni estese per leggere e modificare i dati da ogni sito web.\n\nIl filtro esteso richiede più memoria e un maggiore impegno del processore.", + "message": "Filtraggio di rete avanzato più un filtraggio esteso specifico e generico da liste di filtri selezionate.\n\nRichiede un'ampia autorizzazione per leggere e modificare i dati su tutti i siti web.\n\nIl filtraggio esteso generico può causare un maggiore utilizzo delle risorse della pagina web.", "description": "This describes the 'complete' filtering mode" }, "noFilteringModeDescription": { - "message": "Elenco degli host per i quali non viene effettuato nessun filtraggio", + "message": "Lista dei nomi host per i quali non verrà effettuato alcun filtraggio.", "description": "A short description for the editable field which lists trusted sites" }, "behaviorSectionLabel": { @@ -152,11 +152,11 @@ "description": "The header text for the 'Behavior' section" }, "autoReloadLabel": { - "message": "Ricarica la pagina quando scegli un altro metodo di filtraggio", + "message": "Ricarica la pagina automaticamente quando cambi la modalità di filtraggio", "description": "Label for a checkbox in the options page" }, "showBlockedCountLabel": { - "message": "Mostra il numero di richieste bloccate sull'icona nella barra degli strumenti", + "message": "Mostra il numero delle richieste bloccate sull'icona nella barra degli strumenti", "description": "Label for a checkbox in the options page" } } diff --git a/platform/mv3/extension/_locales/kn/messages.json b/platform/mv3/extension/_locales/kn/messages.json index 30c578f94ffdd..7fc46b9e67b1d 100644 --- a/platform/mv3/extension/_locales/kn/messages.json +++ b/platform/mv3/extension/_locales/kn/messages.json @@ -28,7 +28,7 @@ "description": "Link to privacy policy on GitHub (English)" }, "popupFilteringModeLabel": { - "message": "filtering mode", + "message": "ಫಿಲ್ಟರಿಂಗ್ ಮೋಡ್", "description": "Label in the popup panel for the current filtering mode" }, "popupTipDashboard": { @@ -104,7 +104,7 @@ "description": "The header text for the welcome message section" }, "firstRunDescription": { - "message": "You have just installed uBO Lite. Here you can choose the default filtering mode to use on all websites.\n\nBy default, Basic mode is selected because it does not require the permission to read and modify data. If you trust uBO Lite, you can give it broad permission to read and modify data on all websites in order to enable more advanced filtering capabilities for all websites by default.", + "message": "ನೀವು ಈಗಷ್ಟೇ uBO Lite ಅನ್ನು ಸ್ಥಾಪಿಸಿರುವಿರಿ. ಇಲ್ಲಿ ನೀವು ಎಲ್ಲಾ ವೆಬ್‌ಸೈಟ್‌ಗಳಲ್ಲಿ ಬಳಸಲು ಡಿಫಾಲ್ಟ್ ಫಿಲ್ಟರಿಂಗ್ ಮೋಡ್ ಅನ್ನು ಆಯ್ಕೆ ಮಾಡಬಹುದು.\n\nಪೂರ್ವನಿಯೋಜಿತವಾಗಿ, ಸಾಮಾನ್ಯ ಮೋಡ್ ಅನ್ನು ಆಯ್ಕೆಮಾಡಲಾಗಿದೆ ಏಕೆಂದರೆ ಇದು ಡೇಟಾವನ್ನು ಓದಲು ಮತ್ತು ಮಾರ್ಪಡಿಸಲು ಅನುಮತಿಯ ಅಗತ್ಯವಿಲ್ಲ. ನೀವು uBO Lite ಅನ್ನು ನಂಬುತ್ತಿದ್ದರೆ, ಡೀಫಾಲ್ಟ್ ಆಗಿ ಎಲ್ಲಾ ವೆಬ್‌ಸೈಟ್‌ಗಳಿಗೆ ಹೆಚ್ಚು ಸುಧಾರಿತ ಫಿಲ್ಟರಿಂಗ್ ಸಾಮರ್ಥ್ಯಗಳನ್ನು ಸಕ್ರಿಯಗೊಳಿಸಲು ಎಲ್ಲಾ ವೆಬ್‌ಸೈಟ್‌ಗಳಲ್ಲಿನ ಡೇಟಾವನ್ನು ಓದಲು ಮತ್ತು ಮಾರ್ಪಡಿಸಲು ನೀವು ವಿಶಾಲವಾದ ಅನುಮತಿಯನ್ನು ನೀಡಬಹುದು.", "description": "Descriptive text shown at first install time only " }, "defaultFilteringModeSectionLabel": { @@ -124,7 +124,7 @@ "description": "Name of blocking mode 1" }, "filteringMode2Name": { - "message": "optimal", + "message": "ಸೂಕ್ತ", "description": "Name of blocking mode 2" }, "filteringMode3Name": { @@ -148,7 +148,7 @@ "description": "A short description for the editable field which lists trusted sites" }, "behaviorSectionLabel": { - "message": "Behavior", + "message": "ನಡವಳಿಕೆ", "description": "The header text for the 'Behavior' section" }, "autoReloadLabel": { diff --git a/platform/mv3/extension/_locales/ro/messages.json b/platform/mv3/extension/_locales/ro/messages.json index 70f9bf60ed171..ec8228953b77f 100644 --- a/platform/mv3/extension/_locales/ro/messages.json +++ b/platform/mv3/extension/_locales/ro/messages.json @@ -8,7 +8,7 @@ "description": "this will be in the Chrome web store: must be 132 characters or less" }, "perRulesetStats": { - "message": "{{ruleCount}} de reguli convertite din {{filterCount}} filtre de rețea", + "message": "{{ruleCount}} de reguli, convertite din {{filterCount}} filtre de rețea", "description": "Appears aside each filter list in the _3rd-party filters_ pane" }, "dashboardName": { @@ -156,7 +156,7 @@ "description": "Label for a checkbox in the options page" }, "showBlockedCountLabel": { - "message": "Show the number of blocked requests on the toolbar icon", + "message": "Arată numărul cererilor blocate pe simbolul extensiei", "description": "Label for a checkbox in the options page" } } diff --git a/src/_locales/it/messages.json b/src/_locales/it/messages.json index c87a90ff97181..726c6085ae0d1 100644 --- a/src/_locales/it/messages.json +++ b/src/_locales/it/messages.json @@ -380,7 +380,7 @@ "description": "" }, "settingsNoLargeMediaPrompt": { - "message": "Blocca elementi multimediali maggiori di {{input}} KB", + "message": "Blocca gli elementi multimediali di dimensioni maggiori di {{input}} KB", "description": "" }, "settingsNoRemoteFontsPrompt": { @@ -404,7 +404,7 @@ "description": "Section for controlling advanced-user settings" }, "settingsAdvancedSynopsis": { - "message": "Caratteristiche adatte solo ad utenti tecnici.", + "message": "Funzionalità adatte solo per gli utenti tecnici.", "description": "Description of section controlling advanced-user settings" }, "settingsAdvancedUserSettings": { @@ -440,11 +440,11 @@ "description": "A button in the in the _3rd-party filters_ pane" }, "3pParseAllABPHideFiltersPrompt1": { - "message": "Analizza e applica filtri cosmetici", + "message": "Analizza e applica i filtri cosmetici", "description": "English: Parse and enforce Adblock+ element hiding filters." }, "3pParseAllABPHideFiltersInfo": { - "message": "\nI filtri cosmetici servono a nascondere gli elementi in una pagina web che sono considerati un fastidio visivo, e che non possono essere bloccati dai motori di filtraggio basati sulla richiesta di rete.", + "message": "\nI filtri cosmetici servono a nascondere gli elementi in una pagina web che sono considerati visivamente fastidiosi e che non possono essere bloccati dai motori di filtraggio basati sulle richieste di rete.", "description": "Describes the purpose of the 'Parse and enforce cosmetic filters' feature." }, "3pIgnoreGenericCosmeticFilters": { @@ -456,7 +456,7 @@ "description": "Describes the purpose of the 'Ignore generic cosmetic filters' feature." }, "3pSuspendUntilListsAreLoaded": { - "message": "Sospendi l'attività di rete finché non vengono caricate tutte le liste filtri", + "message": "Sospendi l'attività di rete finché tutte le liste dei filtri sono caricate", "description": "A checkbox in the 'Filter lists' pane" }, "3pListsOfBlockedHostsHeader": { @@ -480,15 +480,15 @@ "description": "Filter lists section name" }, "3pGroupMalware": { - "message": "Domini con Malware", + "message": "Protezione dai malware, sicurezza", "description": "Filter lists section name" }, "3pGroupSocial": { - "message": "Widget sociali", + "message": "Widget dei social", "description": "Filter lists section name" }, "3pGroupCookies": { - "message": "Avviso sui cookie", + "message": "Avvisi sui cookie", "description": "Filter lists section name" }, "3pGroupAnnoyances": { @@ -512,7 +512,7 @@ "description": "The label for the checkbox used to import external filter lists" }, "3pExternalListsHint": { - "message": "Un URL per riga. URL non validi verranno silenziosamente ignorati.", + "message": "Un URL per riga. Gli URL non validi saranno silenziosamente ignorati.", "description": "Short information about how to use the textarea to import external filter lists by URL" }, "3pExternalListObsolete": { @@ -536,7 +536,7 @@ "description": "used as a tooltip for error icon beside a list" }, "1pTrustWarning": { - "message": "Non aggiungere filtri da fonti non attendibili.", + "message": "Non aggiungere filtri da fonti non affidabili.", "description": "Warning against copy-pasting filters from random sources" }, "1pEnableMyFiltersLabel": { @@ -688,7 +688,7 @@ "description": "Tooltip for the popup panel button in the logger page" }, "loggerInfoTip": { - "message": "uBlock Origin Wiki: Registro", + "message": "Wiki di uBlock Origin: Il registro", "description": "Tooltip for the top-right info label in the logger page" }, "loggerClearTip": { @@ -836,7 +836,7 @@ "description": "Below this sentence, the filter list(s) in which the filter was found" }, "loggerStaticFilteringFinderSentence2": { - "message": "Non è stato possibile trovare il filtro statico in nessuno degli elenchi di filtri attualmente abilitati", + "message": "Non è stato possibile trovare il filtro statico in alcuna delle liste dei filtri attualmente abilitate", "description": "Message to show when a filter cannot be found in any filter lists" }, "loggerSettingDiscardPrompt": { @@ -932,7 +932,7 @@ "description": "First paragraph of 'Filter issues' section in Support pane" }, "supportS3P2": { - "message": "Importante: Evita di utilizzare altre estensioni simili insieme a uBlock Origin, poiché ciò potrebbe causare problemi di filtro su specifici siti.", + "message": "Importante: Evita di utilizzare altre estensioni simili insieme a uBlock Origin, poiché ciò potrebbe causare problemi di filtraggio su specifici siti.", "description": "Second paragraph of 'Filter issues' section in Support pane" }, "supportS3P3": { @@ -952,7 +952,7 @@ "description": "Header of 'Troubleshooting Information' section in Support pane" }, "supportS5P1": { - "message": "Di seguito sono riportate le informazioni tecniche che potrebbero essere utili quando i volontari stanno cercando di aiutarti a risolvere un problema.", + "message": "Di seguito sono riportate informazioni tecniche che potrebbero essere utili quando i volontari cercheranno di aiutarti a risolvere un problema.", "description": "First paragraph of 'Troubleshooting Information' section in Support pane" }, "supportS5P2": { @@ -1024,11 +1024,11 @@ "description": "Text for 'Unredact' button" }, "aboutPrivacyPolicy": { - "message": "Codice di condotta sulla privacy", + "message": "Informativa sulla privacy", "description": "Link to privacy policy on GitHub (English)" }, "aboutChangelog": { - "message": "Changelog", + "message": "Registro delle modifiche", "description": "" }, "aboutCode": { @@ -1072,7 +1072,7 @@ "description": "English: my-ublock-backup_{{datetime}}.txt" }, "aboutRestoreDataButton": { - "message": "Ripristina configurazione", + "message": "Ripristina dal file...", "description": "English: Restore from file..." }, "aboutResetDataButton": { @@ -1168,7 +1168,7 @@ "description": "English: Close this window" }, "docblockedDontWarn": { - "message": "Non avvisarmi più per questo sito", + "message": "Non avvisarmi di nuovo riguardo questo sito", "description": "Label for checkbox in document-blocked page" }, "docblockedProceed": { @@ -1240,7 +1240,7 @@ "description": "A context menu entry, present when large media elements have been blocked on the current site" }, "contextMenuViewSource": { - "message": "Visualizza origine...", + "message": "Visualizza il codice sorgente…", "description": "A context menu entry, to view the source code of the target resource" }, "shortcutCapturePlaceholder": { @@ -1260,7 +1260,7 @@ "description": "Label for buttons used to select all text in editor" }, "toggleCosmeticFiltering": { - "message": "Attiva/Disattiva filtri cosmetici", + "message": "Attiva/Disattiva il filtraggio cosmetico", "description": "Label for keyboard shortcut used to toggle cosmetic filtering" }, "toggleJavascript": { @@ -1296,7 +1296,7 @@ "description": "Summary of number of errors as reported by the linter " }, "unprocessedRequestTooltip": { - "message": "Non è stato possibile filtrare correttamente all'avvio del browser.\nRicaricare la pagina per garantire un filtraggio corretto", + "message": "Non è stato effettuato correttamente il filtraggio all'avvio del browser.\nRicarica la pagina per garantire un filtraggio adeguato.", "description": "A warning which will appear in the popup panel if needed" }, "dummy": { diff --git a/src/_locales/kn/messages.json b/src/_locales/kn/messages.json index 7ae8e6ff84e0a..eed3253f2c98b 100644 --- a/src/_locales/kn/messages.json +++ b/src/_locales/kn/messages.json @@ -72,7 +72,7 @@ "description": "English: Click: disable/enable uBlock₀ for this site.\n\nCtrl+click: disable uBlock₀ only on this page." }, "popupPowerSwitchInfo1": { - "message": "Click to disable uBlock₀ for this site.\n\nCtrl+click to disable uBlock₀ only on this page.", + "message": "ಈ ಸೈಟ್‌ಗಾಗಿ uBlock₀ ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಲು ಕ್ಲಿಕ್ ಮಾಡಿ.\n\nಈ ಪುಟದಲ್ಲಿ ಮಾತ್ರ uBlock₀ ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಲು Ctrl+ಕ್ಲಿಕ್ ಮಾಡಿ.", "description": "Message to be read by screen readers" }, "popupPowerSwitchInfo2": { @@ -116,7 +116,7 @@ "description": "English: Click to open the dashboard" }, "popupTipZapper": { - "message": "Enter element zapper mode", + "message": "Element zapper ಮೋಡ್ ಅನ್ನು ಬಳಸಿ", "description": "Tooltip for the element-zapper icon in the popup panel" }, "popupTipPicker": { @@ -136,15 +136,15 @@ "description": "Tooltip for the no-popups per-site switch" }, "popupTipNoPopups1": { - "message": "Click to block all popups on this site", + "message": "ಈ ಸೈಟ್‌ನಲ್ಲಿ ಎಲ್ಲಾ ಪಾಪ್‌ಅಪ್‌ಗಳನ್ನು ನಿರ್ಬಂಧಿಸಲು ಕ್ಲಿಕ್ ಮಾಡಿ", "description": "Tooltip for the no-popups per-site switch" }, "popupTipNoPopups2": { - "message": "Click to no longer block all popups on this site", + "message": "ಈ ಸೈಟ್‌ನಲ್ಲಿ ಇನ್ನು ಮುಂದೆ ಎಲ್ಲಾ ಪಾಪ್‌ಅಪ್‌ಗಳನ್ನು ನಿರ್ಬಂಧಿಸದಿರಲು ಕ್ಲಿಕ್ ಮಾಡಿ", "description": "Tooltip for the no-popups per-site switch" }, "popupTipNoLargeMedia": { - "message": "Toggle the blocking of large media elements for this site", + "message": "ಈ ಸೈಟ್‌ಗಾಗಿ ದೊಡ್ಡ ಮಾಧ್ಯಮ ಅಂಶಗಳ ನಿರ್ಬಂಧಿಸುವಿಕೆಯನ್ನು ಸಕ್ರಿಯಗೊಳಿಸಲು ಕ್ಲಿಕ್ ಮಾಡಿ", "description": "Tooltip for the no-large-media per-site switch" }, "popupTipNoLargeMedia1": { @@ -188,15 +188,15 @@ "description": "Tooltip for the no-scripting per-site switch" }, "popupNoPopups_v2": { - "message": "Pop-up windows", + "message": "ಪಾಪ್-ಅಪ್ ವಿಂಡೋ", "description": "Caption for the no-popups per-site switch" }, "popupNoLargeMedia_v2": { - "message": "Large media elements", + "message": "ದೊಡ್ಡ ಮಾಧ್ಯಮ ಅಂಶಗಳು", "description": "Caption for the no-large-media per-site switch" }, "popupNoCosmeticFiltering_v2": { - "message": "Cosmetic filtering", + "message": "ಕಾಸ್ಮೆಟಿಕ್ ಫಿಲ್ಟರಿಂಗ್", "description": "Caption for the no-cosmetic-filtering per-site switch" }, "popupNoRemoteFonts_v2": { @@ -224,11 +224,11 @@ "description": "Tooltip when hovering the top-most cell of the local-rules column." }, "popupTipSaveRules": { - "message": "Click to make your changes permanent.", + "message": "ಶಾಶ್ವತ ಬದಲಾವಣೆಗಳನ್ನು ಮಾಡಲು ಕ್ಲಿಕ್ ಮಾಡಿ", "description": "Tooltip when hovering over the padlock in the dynamic filtering pane." }, "popupTipRevertRules": { - "message": "Click to revert your changes.", + "message": "ನಿಮ್ಮ ಬದಲಾವಣೆಗಳನ್ನು ಹಿಂತಿರುಗಿಸಲು ಕ್ಲಿಕ್ ಮಾಡಿ", "description": "Tooltip when hovering over the eraser in the dynamic filtering pane." }, "popupAnyRulePrompt": { @@ -864,7 +864,7 @@ "description": "Logger settings: a sentence to describe the purpose of the checkboxes below" }, "loggerSettingHideColumnTime": { - "message": "{{input}} Time", + "message": "{{input}} ಗಂಟೆ", "description": "A label for the time column" }, "loggerSettingHideColumnFilter": { @@ -1144,7 +1144,7 @@ "description": "Used as a title for the document-blocked page" }, "docblockedPrompt1": { - "message": "uBlock Origin has prevented the following page from loading:", + "message": "uBlock Origin ಈ ಕೆಳಗಿನ ಪುಟವನ್ನು ಲೋಡ್ ಮಾಡುವುದನ್ನು ತಡೆಯುತ್ತದೆ:", "description": "Used in the strict-blocking page" }, "docblockedPrompt2": { @@ -1272,7 +1272,7 @@ "description": "Label for keyboard shortcut used to relax blocking mode" }, "storageUsed": { - "message": "Storage used: {{value}} {{unit}}", + "message": "ಉಪಯೊಗಿಸುತಿರುವ ಸಂಗ್ರಹಣೆ: {{value}} {{unit}}", "description": " In Setting pane, renders as (example): Storage used: 13.2 MB" }, "KB": { diff --git a/src/_locales/nb/messages.json b/src/_locales/nb/messages.json index 6451d65426f51..5baaf48f039c0 100644 --- a/src/_locales/nb/messages.json +++ b/src/_locales/nb/messages.json @@ -484,11 +484,11 @@ "description": "Filter lists section name" }, "3pGroupSocial": { - "message": "Sosiale medie-widgeter", + "message": "Sosiale mediers moduler", "description": "Filter lists section name" }, "3pGroupCookies": { - "message": "Cookie meldinger", + "message": "Infokapselmeldinger", "description": "Filter lists section name" }, "3pGroupAnnoyances": { From 462a4e0304e1c87ea88bc4acb9dad3817dedd6b6 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Tue, 9 Jul 2024 13:16:54 -0400 Subject: [PATCH 0050/1099] New revision for release candidate --- dist/version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dist/version b/dist/version index 2e21c3e9f0304..9c665a56e3e9a 100644 --- a/dist/version +++ b/dist/version @@ -1 +1 @@ -1.58.1.12 \ No newline at end of file +1.58.1.100 \ No newline at end of file From e3b71a586183b0a8b984d93bcd5be58ab109f462 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Tue, 9 Jul 2024 13:25:30 -0400 Subject: [PATCH 0051/1099] Make Firefox dev build auto-update --- dist/firefox/updates.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dist/firefox/updates.json b/dist/firefox/updates.json index cb8dce834d762..4a93bdcf5dffd 100644 --- a/dist/firefox/updates.json +++ b/dist/firefox/updates.json @@ -3,9 +3,9 @@ "uBlock0@raymondhill.net": { "updates": [ { - "version": "1.58.1.12", + "version": "1.58.1.100", "browser_specific_settings": { "gecko": { "strict_min_version": "78.0" } }, - "update_link": "https://github.com/gorhill/uBlock/releases/download/1.58.1b12/uBlock0_1.58.1b12.firefox.signed.xpi" + "update_link": "https://github.com/gorhill/uBlock/releases/download/1.58.1rc0/uBlock0_1.58.1rc0.firefox.signed.xpi" } ] } From b98ef8141a27b1f6509be1af580d8fe11869ae0f Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Wed, 10 Jul 2024 10:09:59 -0400 Subject: [PATCH 0052/1099] Fix CSP injection in non-document resources Related feedback: https://github.com/uBlockOrigin/uBlock-issues/issues/229#issuecomment-2220354261 https://old.reddit.com/r/uBlockOrigin/comments/1dz6du7/ Regression from: https://github.com/gorhill/uBlock/commit/7c3e060c01 --- src/js/traffic.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/js/traffic.js b/src/js/traffic.js index f28f57fbbb366..f8e98b8988311 100644 --- a/src/js/traffic.js +++ b/src/js/traffic.js @@ -945,6 +945,10 @@ const bodyFilterer = (( ) => { /******************************************************************************/ const injectCSP = function(fctxt, pageStore, responseHeaders) { + // https://github.com/uBlockOrigin/uBlock-issues/issues/229#issuecomment-2220354261 + // Inject CSP in document resource only + if ( fctxt.isDocument() === false ) { return; } + const cspSubsets = []; const requestType = fctxt.type; From 6fb697b490a98b14ee9581b2d96a3937a666e759 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Wed, 10 Jul 2024 10:16:17 -0400 Subject: [PATCH 0053/1099] Update changelog --- CHANGELOG.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d8b94bce1604f..c9ee9002e224e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,7 @@ +- [Fix CSP injection in non-document resources](https://github.com/gorhill/uBlock/commit/b98ef8141a) - [Add `trusted-suppress-native-method` scriptlet](https://github.com/gorhill/uBlock/commit/97d11c03c2) +- [Add support for `$currentISODate$` in `trusted-set-cookie` scriptlet](https://github.com/gorhill/uBlock/commit/a3576ea651) +- [Add `essential` and `nonessential` to set-cookie](https://github.com/gorhill/uBlock/commit/37d31a82d8) (by @ryanbr) - [Fix distance calculation in picker](https://github.com/gorhill/uBlock/commit/9569969b55) - [Fix bad serialization of Date objects](https://github.com/gorhill/uBlock/commit/c154aaa69c) - [Fix race condition when loading redirect/scriptlet resources](https://github.com/gorhill/uBlock/commit/896737d098) @@ -6,7 +9,7 @@ - [Add `:matches-prop()` pseudo CSS operator](https://github.com/gorhill/uBlock/commit/aca7674bac) - [Improve `set-cookie` scriptlet](https://github.com/gorhill/uBlock/commit/b4d8750f44) - [Improve `trusted-replace-node-text` scriptlet](https://github.com/gorhill/uBlock/commit/cb0f65e035) -- [Improve `trusted-replace-[fetch|xhr]-response` scriptlets](https://github.com/gorhill/uBlock/commit/9072772f61) +- [Improve `trusted-replace-(fetch|xhr)-response` scriptlets](https://github.com/gorhill/uBlock/commit/9072772f61) - [Improve `prevent-addEventListener` scriptlet](https://github.com/gorhill/uBlock/commit/91ee5bdeae) - [Add `isodate` as available placeholder for auto-comment](https://github.com/gorhill/uBlock/commit/d5208ee5dd) - [Improve `trusted-replace-outbound-text` scriptlet](https://github.com/gorhill/uBlock/commit/fa6740a059) From 282f4f5ef4c9c83ab8fb786ffc8d072a4ade7f97 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Wed, 10 Jul 2024 10:28:29 -0400 Subject: [PATCH 0054/1099] New revision for release candidate --- dist/version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dist/version b/dist/version index 9c665a56e3e9a..f537e472ff8fc 100644 --- a/dist/version +++ b/dist/version @@ -1 +1 @@ -1.58.1.100 \ No newline at end of file +1.58.1.101 \ No newline at end of file From 9c1252ef9c537de4a821ff5167201707c87cdb7e Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Wed, 10 Jul 2024 10:35:31 -0400 Subject: [PATCH 0055/1099] Make Firefox dev build auto-update --- dist/firefox/updates.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dist/firefox/updates.json b/dist/firefox/updates.json index 4a93bdcf5dffd..197cf0b3e2751 100644 --- a/dist/firefox/updates.json +++ b/dist/firefox/updates.json @@ -3,9 +3,9 @@ "uBlock0@raymondhill.net": { "updates": [ { - "version": "1.58.1.100", + "version": "1.58.1.101", "browser_specific_settings": { "gecko": { "strict_min_version": "78.0" } }, - "update_link": "https://github.com/gorhill/uBlock/releases/download/1.58.1rc0/uBlock0_1.58.1rc0.firefox.signed.xpi" + "update_link": "https://github.com/gorhill/uBlock/releases/download/1.58.1rc1/uBlock0_1.58.1rc1.firefox.signed.xpi" } ] } From c90f4933df193f90624dff6a7da75c4c6f88ef49 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Wed, 10 Jul 2024 11:41:55 -0400 Subject: [PATCH 0056/1099] Fix CSP/PP header injection in non-document resources Related feedback: https://github.com/uBlockOrigin/uBlock-issues/issues/229#issuecomment-2220354261 https://old.reddit.com/r/uBlockOrigin/comments/1dz6du7/ Regression from: https://github.com/gorhill/uBlock/commit/7c3e060c01 --- src/js/traffic.js | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/src/js/traffic.js b/src/js/traffic.js index f8e98b8988311..4777a847004e0 100644 --- a/src/js/traffic.js +++ b/src/js/traffic.js @@ -553,11 +553,16 @@ const onHeadersReceived = function(details) { if ( httpheaderFilteringEngine.apply(fctxt, responseHeaders) === true ) { modifiedHeaders = true; } - if ( injectCSP(fctxt, pageStore, responseHeaders) === true ) { - modifiedHeaders = true; - } - if ( injectPP(fctxt, pageStore, responseHeaders) === true ) { - modifiedHeaders = true; + + // https://github.com/uBlockOrigin/uBlock-issues/issues/229#issuecomment-2220354261 + // Inject CSP/PP in document resource only + if ( fctxt.isDocument() ) { + if ( injectCSP(fctxt, pageStore, responseHeaders) === true ) { + modifiedHeaders = true; + } + if ( injectPP(fctxt, pageStore, responseHeaders) === true ) { + modifiedHeaders = true; + } } // https://bugzilla.mozilla.org/show_bug.cgi?id=1376932 @@ -945,10 +950,6 @@ const bodyFilterer = (( ) => { /******************************************************************************/ const injectCSP = function(fctxt, pageStore, responseHeaders) { - // https://github.com/uBlockOrigin/uBlock-issues/issues/229#issuecomment-2220354261 - // Inject CSP in document resource only - if ( fctxt.isDocument() === false ) { return; } - const cspSubsets = []; const requestType = fctxt.type; From 60a7812d7eb7fa69fa8f46bd2c468c0e8a2ffe66 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Wed, 10 Jul 2024 11:45:25 -0400 Subject: [PATCH 0057/1099] Update changelog --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c9ee9002e224e..8abfe8134872d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,4 @@ -- [Fix CSP injection in non-document resources](https://github.com/gorhill/uBlock/commit/b98ef8141a) +- [Fix CSP/PP header injection in non-document resources](https://github.com/gorhill/uBlock/commit/c90f4933df) - [Add `trusted-suppress-native-method` scriptlet](https://github.com/gorhill/uBlock/commit/97d11c03c2) - [Add support for `$currentISODate$` in `trusted-set-cookie` scriptlet](https://github.com/gorhill/uBlock/commit/a3576ea651) - [Add `essential` and `nonessential` to set-cookie](https://github.com/gorhill/uBlock/commit/37d31a82d8) (by @ryanbr) From dea9e77e2765a29c55a2cfe9e82614ae232cbb48 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Wed, 10 Jul 2024 11:46:00 -0400 Subject: [PATCH 0058/1099] New revision for release candidate --- dist/version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dist/version b/dist/version index f537e472ff8fc..68feaf13eeb88 100644 --- a/dist/version +++ b/dist/version @@ -1 +1 @@ -1.58.1.101 \ No newline at end of file +1.58.1.102 \ No newline at end of file From 569c8cbe04e2ef9f8f58f5e1b9c68f3bc707a7e3 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Wed, 10 Jul 2024 11:50:30 -0400 Subject: [PATCH 0059/1099] Make Firefox dev build auto-update --- dist/firefox/updates.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dist/firefox/updates.json b/dist/firefox/updates.json index 197cf0b3e2751..93df6326aa848 100644 --- a/dist/firefox/updates.json +++ b/dist/firefox/updates.json @@ -3,9 +3,9 @@ "uBlock0@raymondhill.net": { "updates": [ { - "version": "1.58.1.101", + "version": "1.58.1.102", "browser_specific_settings": { "gecko": { "strict_min_version": "78.0" } }, - "update_link": "https://github.com/gorhill/uBlock/releases/download/1.58.1rc1/uBlock0_1.58.1rc1.firefox.signed.xpi" + "update_link": "https://github.com/gorhill/uBlock/releases/download/1.58.1rc2/uBlock0_1.58.1rc2.firefox.signed.xpi" } ] } From 66e3a1ad47162a89709e99072e5eaecdd83cb633 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Wed, 10 Jul 2024 12:43:36 -0400 Subject: [PATCH 0060/1099] Improve `href-sanitizer` scriptlet Related issue: https://github.com/uBlockOrigin/uBlock-issues/issues/3297 Mind that the sanitized URL can have Unicode characters beyond ASCII. --- assets/resources/scriptlets.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets/resources/scriptlets.js b/assets/resources/scriptlets.js index 80c251fb58f5d..d3aedadc20f6c 100644 --- a/assets/resources/scriptlets.js +++ b/assets/resources/scriptlets.js @@ -3477,7 +3477,7 @@ function hrefSanitizer( }; const validateURL = text => { if ( text === '' ) { return ''; } - if ( /[^\x21-\x7e]/.test(text) ) { return ''; } + if ( /[\x00-\x20\x7f]/.test(text) ) { return ''; } try { const url = new URL(text, document.location); return url.href; From 7fbf792ba0db1edc4d33c0d6ba50f7e1bbde8567 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Thu, 11 Jul 2024 11:04:07 -0400 Subject: [PATCH 0061/1099] Update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8abfe8134872d..fb83f330008fa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,4 @@ +- [Improve `href-sanitizer` scriptlet](https://github.com/gorhill/uBlock/commit/66e3a1ad47) - [Fix CSP/PP header injection in non-document resources](https://github.com/gorhill/uBlock/commit/c90f4933df) - [Add `trusted-suppress-native-method` scriptlet](https://github.com/gorhill/uBlock/commit/97d11c03c2) - [Add support for `$currentISODate$` in `trusted-set-cookie` scriptlet](https://github.com/gorhill/uBlock/commit/a3576ea651) From 23b0e08715a2b100dbfd79b7227ac5e1343e0f47 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Thu, 11 Jul 2024 11:04:34 -0400 Subject: [PATCH 0062/1099] New revision for release candidate --- dist/version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dist/version b/dist/version index 68feaf13eeb88..5f0b49db6484c 100644 --- a/dist/version +++ b/dist/version @@ -1 +1 @@ -1.58.1.102 \ No newline at end of file +1.58.1.103 \ No newline at end of file From a54d416143b89f26417393fde76de078dc0b1dc8 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Thu, 11 Jul 2024 11:55:39 -0400 Subject: [PATCH 0063/1099] Make Firefox dev build auto-update --- dist/firefox/updates.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dist/firefox/updates.json b/dist/firefox/updates.json index 93df6326aa848..7873e2f4f5482 100644 --- a/dist/firefox/updates.json +++ b/dist/firefox/updates.json @@ -3,9 +3,9 @@ "uBlock0@raymondhill.net": { "updates": [ { - "version": "1.58.1.102", + "version": "1.58.1.103", "browser_specific_settings": { "gecko": { "strict_min_version": "78.0" } }, - "update_link": "https://github.com/gorhill/uBlock/releases/download/1.58.1rc2/uBlock0_1.58.1rc2.firefox.signed.xpi" + "update_link": "https://github.com/gorhill/uBlock/releases/download/1.58.1rc3/uBlock0_1.58.1rc3.firefox.signed.xpi" } ] } From e785b99338ea364ed1b618cbc2b471ff065b15f3 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sat, 13 Jul 2024 11:02:54 -0400 Subject: [PATCH 0064/1099] Improve `prevent-fetch` scriptlet Related discussion: https://github.com/uBlockOrigin/uBlock-discussions/discussions/848#discussioncomment-10027757 Added support for AdGuard's `responseType` parameter. Extended the meaning of that 3rd parameter to also be a JSON string with properties to set on the returned response instance. Currently supported properties: - `ok`, supported values: `false`, `true` - `type, supported values: `"basic"`, `"cors"`, `"opaque"` Reference: https://github.com/AdguardTeam/Scriptlets/blob/master/wiki/about-scriptlets.md#-%EF%B8%8F-prevent-fetch --- assets/resources/scriptlets.js | 51 +++++++++++++++++++--------------- 1 file changed, 29 insertions(+), 22 deletions(-) diff --git a/assets/resources/scriptlets.js b/assets/resources/scriptlets.js index d3aedadc20f6c..f753ba5a89684 100644 --- a/assets/resources/scriptlets.js +++ b/assets/resources/scriptlets.js @@ -57,6 +57,7 @@ function safeSelf() { 'Math_random': Math.random, 'Object': Object, 'Object_defineProperty': Object.defineProperty.bind(Object), + 'Object_defineProperties': Object.defineProperties.bind(Object), 'Object_fromEntries': Object.fromEntries.bind(Object), 'Object_getOwnPropertyDescriptor': Object.getOwnPropertyDescriptor.bind(Object), 'RegExp': self.RegExp, @@ -2067,11 +2068,11 @@ builtinScriptlets.push({ }); function noFetchIf( propsToMatch = '', - responseBody = '' + responseBody = '', + responseType = '' ) { - if ( typeof propsToMatch !== 'string' ) { return; } const safe = safeSelf(); - const logPrefix = safe.makeLogPrefix('prevent-fetch', propsToMatch, responseBody); + const logPrefix = safe.makeLogPrefix('prevent-fetch', propsToMatch, responseBody, responseType); const needles = []; for ( const condition of propsToMatch.split(/\s+/) ) { if ( condition === '' ) { continue; } @@ -2086,6 +2087,26 @@ function noFetchIf( } needles.push({ key, re: safe.patternToRegex(value) }); } + const validResponseProps = { + ok: [ false, true ], + type: [ 'basic', 'cors', 'opaque' ], + }; + let responseProps; + if ( /^\{.*\}$/.test(responseType) ) { + responseProps = {}; + try { + Object.entries(JSON.parse(responseType)).forEach(([ p, v ]) => { + if ( validResponseProps[p] === undefined ) { return; } + if ( validResponseProps[p].includes(v) === false ) { return; } + responseProps[p] = { value: v }; + }); + } + catch(ex) {} + } else if ( responseType !== '' ) { + if ( validResponseProps.type.includes(responseType) ) { + responseProps = { type: { value: responseType } }; + } + } self.fetch = new Proxy(self.fetch, { apply: function(target, thisArg, args) { const details = args[0] instanceof self.Request @@ -2123,17 +2144,6 @@ function noFetchIf( if ( proceed ) { return Reflect.apply(target, thisArg, args); } - let responseType = ''; - if ( details.mode === undefined || details.mode === 'cors' ) { - try { - const desURL = new URL(details.url); - responseType = desURL.origin !== document.location.origin - ? 'cors' - : 'basic'; - } catch(ex) { - safe.uboErr(logPrefix, `Error: ${ex}`); - } - } return generateContentFn(responseBody).then(text => { safe.uboLog(logPrefix, `Prevented with response "${text}"`); const response = new Response(text, { @@ -2142,14 +2152,11 @@ function noFetchIf( 'Content-Length': text.length, } }); - safe.Object_defineProperty(response, 'url', { - value: details.url - }); - if ( responseType !== '' ) { - safe.Object_defineProperty(response, 'type', { - value: responseType - }); - } + const props = Object.assign( + { url: { value: details.url } }, + responseProps + ); + safe.Object_defineProperties(response, props); return response; }); } From b9717555c32c09dcc3c74e63868fa3d1409316a2 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sat, 13 Jul 2024 11:09:53 -0400 Subject: [PATCH 0065/1099] Update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index fb83f330008fa..69451674bcb14 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,4 @@ +- [Improve `prevent-fetch` scriptlet](https://github.com/gorhill/uBlock/commit/e785b99338) - [Improve `href-sanitizer` scriptlet](https://github.com/gorhill/uBlock/commit/66e3a1ad47) - [Fix CSP/PP header injection in non-document resources](https://github.com/gorhill/uBlock/commit/c90f4933df) - [Add `trusted-suppress-native-method` scriptlet](https://github.com/gorhill/uBlock/commit/97d11c03c2) From 302ddad720cfae2b5ed0c1eb27ca7b12dfc1fcc6 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sat, 13 Jul 2024 11:10:16 -0400 Subject: [PATCH 0066/1099] New revision for release candidate --- dist/version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dist/version b/dist/version index 5f0b49db6484c..bbe1b5e2df417 100644 --- a/dist/version +++ b/dist/version @@ -1 +1 @@ -1.58.1.103 \ No newline at end of file +1.58.1.104 \ No newline at end of file From 324102cb659a23cacca71fd8c8f7d42b34722bb5 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sat, 13 Jul 2024 11:16:01 -0400 Subject: [PATCH 0067/1099] Make Firefox dev build auto-update --- dist/firefox/updates.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dist/firefox/updates.json b/dist/firefox/updates.json index 7873e2f4f5482..b81090881e13e 100644 --- a/dist/firefox/updates.json +++ b/dist/firefox/updates.json @@ -3,9 +3,9 @@ "uBlock0@raymondhill.net": { "updates": [ { - "version": "1.58.1.103", + "version": "1.58.1.104", "browser_specific_settings": { "gecko": { "strict_min_version": "78.0" } }, - "update_link": "https://github.com/gorhill/uBlock/releases/download/1.58.1rc3/uBlock0_1.58.1rc3.firefox.signed.xpi" + "update_link": "https://github.com/gorhill/uBlock/releases/download/1.58.1rc4/uBlock0_1.58.1rc4.firefox.signed.xpi" } ] } From 9ce30563613495f5b609211c017aed580847a9cf Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sun, 14 Jul 2024 12:15:53 -0400 Subject: [PATCH 0068/1099] Improve `prevent-fetch` scriptlet Add `statusText` as overridable property in response instance. Supported values are: `""`, `"Not Found"`. `statusText` defaults to `"OK"` when not overridden. --- assets/resources/scriptlets.js | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/assets/resources/scriptlets.js b/assets/resources/scriptlets.js index f753ba5a89684..ab98966a14f81 100644 --- a/assets/resources/scriptlets.js +++ b/assets/resources/scriptlets.js @@ -2089,11 +2089,13 @@ function noFetchIf( } const validResponseProps = { ok: [ false, true ], - type: [ 'basic', 'cors', 'opaque' ], + statusText: [ '', 'Not Found' ], + type: [ 'basic', 'cors', 'default', 'error', 'opaque' ], + }; + const responseProps = { + statusText: { value: 'OK' }, }; - let responseProps; if ( /^\{.*\}$/.test(responseType) ) { - responseProps = {}; try { Object.entries(JSON.parse(responseType)).forEach(([ p, v ]) => { if ( validResponseProps[p] === undefined ) { return; } @@ -2104,7 +2106,7 @@ function noFetchIf( catch(ex) {} } else if ( responseType !== '' ) { if ( validResponseProps.type.includes(responseType) ) { - responseProps = { type: { value: responseType } }; + responseProps.type = { value: responseType }; } } self.fetch = new Proxy(self.fetch, { @@ -2147,7 +2149,6 @@ function noFetchIf( return generateContentFn(responseBody).then(text => { safe.uboLog(logPrefix, `Prevented with response "${text}"`); const response = new Response(text, { - statusText: 'OK', headers: { 'Content-Length': text.length, } From 68e81f640c3476fa7c3a7d028fc7c82a5a40c17e Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sun, 14 Jul 2024 17:44:52 -0400 Subject: [PATCH 0069/1099] New revision for release candidate --- dist/version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dist/version b/dist/version index bbe1b5e2df417..cabaf898c1449 100644 --- a/dist/version +++ b/dist/version @@ -1 +1 @@ -1.58.1.104 \ No newline at end of file +1.58.1.105 \ No newline at end of file From fd054176a86c6dc4f6ca8d6debb3744b81e896be Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Mon, 15 Jul 2024 16:05:30 -0400 Subject: [PATCH 0070/1099] Make Firefox dev build auto-update --- dist/firefox/updates.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dist/firefox/updates.json b/dist/firefox/updates.json index b81090881e13e..e054b9642f560 100644 --- a/dist/firefox/updates.json +++ b/dist/firefox/updates.json @@ -3,9 +3,9 @@ "uBlock0@raymondhill.net": { "updates": [ { - "version": "1.58.1.104", + "version": "1.58.1.105", "browser_specific_settings": { "gecko": { "strict_min_version": "78.0" } }, - "update_link": "https://github.com/gorhill/uBlock/releases/download/1.58.1rc4/uBlock0_1.58.1rc4.firefox.signed.xpi" + "update_link": "https://github.com/gorhill/uBlock/releases/download/1.58.1rc5/uBlock0_1.58.1rc5.firefox.signed.xpi" } ] } From 77feb25c4d4d8a815da991b252ef2b1bbdbfd78c Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Wed, 17 Jul 2024 09:36:55 -0400 Subject: [PATCH 0071/1099] Improve `set-constant` scriptlet Related feedback: https://github.com/uBlockOrigin/uBlock-discussions/discussions/881#discussioncomment-10072370 --- assets/resources/scriptlets.js | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/assets/resources/scriptlets.js b/assets/resources/scriptlets.js index ab98966a14f81..9678724cc3b98 100644 --- a/assets/resources/scriptlets.js +++ b/assets/resources/scriptlets.js @@ -485,9 +485,8 @@ builtinScriptlets.push({ 'safe-self.fn', ], }); -function validateConstantFn(trusted, raw) { +function validateConstantFn(trusted, raw, extraArgs = {}) { const safe = safeSelf(); - const extraArgs = safe.getExtraArgs(Array.from(arguments), 2); let value; if ( raw === 'undefined' ) { value = undefined; @@ -587,7 +586,7 @@ function setConstantFn( }; if ( trappedProp === '' ) { return; } const thisScript = document.currentScript; - let normalValue = validateConstantFn(trusted, rawValue); + let normalValue = validateConstantFn(trusted, rawValue, extraArgs); if ( rawValue === 'noopFunc' || rawValue === 'trueFunc' || rawValue === 'falseFunc' ) { normalValue = cloakFunc(normalValue); } @@ -4772,7 +4771,7 @@ function trustedReplaceArgument( const logPrefix = safe.makeLogPrefix('trusted-replace-argument', propChain, argposRaw, argraw); const argpos = parseInt(argposRaw, 10) || 0; const extraArgs = safe.getExtraArgs(Array.from(arguments), 3); - const normalValue = validateConstantFn(true, argraw); + const normalValue = validateConstantFn(true, argraw, extraArgs); const reCondition = extraArgs.condition ? safe.patternToRegex(extraArgs.condition) : /^/; From b9ab889be54b6f82fb6e6bbe5f4f7a4f16bcd43c Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Wed, 17 Jul 2024 11:06:31 -0400 Subject: [PATCH 0072/1099] Update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 69451674bcb14..9ebb51689d551 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,4 @@ +- [Improve `set-constant` scriptlet](https://github.com/gorhill/uBlock/commit/77feb25c4d) - [Improve `prevent-fetch` scriptlet](https://github.com/gorhill/uBlock/commit/e785b99338) - [Improve `href-sanitizer` scriptlet](https://github.com/gorhill/uBlock/commit/66e3a1ad47) - [Fix CSP/PP header injection in non-document resources](https://github.com/gorhill/uBlock/commit/c90f4933df) From 59d9bbf0b29f9b6b44bfa242b1e331658f19ba5d Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Wed, 17 Jul 2024 11:06:48 -0400 Subject: [PATCH 0073/1099] New revision for release candidate --- dist/version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dist/version b/dist/version index cabaf898c1449..19fc7b2fc659d 100644 --- a/dist/version +++ b/dist/version @@ -1 +1 @@ -1.58.1.105 \ No newline at end of file +1.58.1.106 \ No newline at end of file From 152983f031deb11845b613eb881f4ac4cfb49c98 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Thu, 18 Jul 2024 09:19:15 -0400 Subject: [PATCH 0074/1099] Make Firefox dev build auto-update --- dist/firefox/updates.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dist/firefox/updates.json b/dist/firefox/updates.json index e054b9642f560..db3cd2435f8c2 100644 --- a/dist/firefox/updates.json +++ b/dist/firefox/updates.json @@ -3,9 +3,9 @@ "uBlock0@raymondhill.net": { "updates": [ { - "version": "1.58.1.105", + "version": "1.58.1.106", "browser_specific_settings": { "gecko": { "strict_min_version": "78.0" } }, - "update_link": "https://github.com/gorhill/uBlock/releases/download/1.58.1rc5/uBlock0_1.58.1rc5.firefox.signed.xpi" + "update_link": "https://github.com/gorhill/uBlock/releases/download/1.58.1rc6/uBlock0_1.58.1rc6.firefox.signed.xpi" } ] } From f2c4328a2e4b8d270ac27ca391f7fb0256139eae Mon Sep 17 00:00:00 2001 From: Fanboynz Date: Sat, 20 Jul 2024 00:01:38 +1200 Subject: [PATCH 0075/1099] Switch to updated Icelandic ABP List (#3920) --- assets/assets.dev.json | 4 ++-- assets/assets.json | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/assets/assets.dev.json b/assets/assets.dev.json index ab6ecfd6505de..a78998bf2b8a5 100644 --- a/assets/assets.dev.json +++ b/assets/assets.dev.json @@ -681,8 +681,8 @@ "title": "🇮🇸is: Icelandic ABP List", "tags": "ads icelandic", "lang": "is", - "contentURL": "https://adblock.gardar.net/is.abp.txt", - "supportURL": "https://adblock.gardar.net/" + "contentURL": "https://raw.githubusercontent.com/brave/adblock-lists/master/custom/is.txt", + "supportURL": "https://github.com/brave/adblock-lists/issues" }, "ISR-0": { "content": "filters", diff --git a/assets/assets.json b/assets/assets.json index bd8fd84550dce..454c954bb7439 100644 --- a/assets/assets.json +++ b/assets/assets.json @@ -681,8 +681,8 @@ "title": "🇮🇸is: Icelandic ABP List", "tags": "ads icelandic", "lang": "is", - "contentURL": "https://adblock.gardar.net/is.abp.txt", - "supportURL": "https://adblock.gardar.net/" + "contentURL": "https://raw.githubusercontent.com/brave/adblock-lists/master/custom/is.txt", + "supportURL": "https://github.com/brave/adblock-lists/issues" }, "ISR-0": { "content": "filters", From 8afd9e233d3c43f51d21e4e51d24e643089d9853 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sat, 20 Jul 2024 08:57:11 -0400 Subject: [PATCH 0076/1099] Improve `trusted-replace-node-text` scriptlet Related discussion: https://github.com/uBlockOrigin/uAssets/discussions/23769#discussioncomment-10102276 --- assets/resources/scriptlets.js | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/assets/resources/scriptlets.js b/assets/resources/scriptlets.js index 9678724cc3b98..fa1457f87fde8 100644 --- a/assets/resources/scriptlets.js +++ b/assets/resources/scriptlets.js @@ -714,7 +714,8 @@ function replaceNodeTextFn( const reNodeName = safe.patternToRegex(nodeName, 'i', true); const rePattern = safe.patternToRegex(pattern, 'gms'); const extraArgs = safe.getExtraArgs(Array.from(arguments), 3); - const reCondition = safe.patternToRegex(extraArgs.condition || '', 'ms'); + const reIncludes = safe.patternToRegex(extraArgs.includes || extraArgs.condition || '', 'ms'); + const reExcludes = safe.patternToRegex(extraArgs.excludes || '', 'ms'); const stop = (takeRecord = true) => { if ( takeRecord ) { handleMutations(observer.takeRecords()); @@ -727,8 +728,10 @@ function replaceNodeTextFn( let sedCount = extraArgs.sedCount || 0; const handleNode = node => { const before = node.textContent; - reCondition.lastIndex = 0; - if ( safe.RegExp_test.call(reCondition, before) === false ) { return true; } + reIncludes.lastIndex = 0; + if ( safe.RegExp_test.call(reIncludes, before) === false ) { return true; } + reExcludes.lastIndex = 0; + if ( safe.RegExp_test.call(reExcludes, before) ) { return true; } rePattern.lastIndex = 0; if ( safe.RegExp_test.call(rePattern, before) === false ) { return true; } rePattern.lastIndex = 0; From b871b00337cd0b142a759fb63ba148c35bf336f9 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sat, 20 Jul 2024 08:58:56 -0400 Subject: [PATCH 0077/1099] Update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9ebb51689d551..5dc86763ad714 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,4 @@ +- [Improve `trusted-replace-node-text` scriptlet](https://github.com/gorhill/uBlock/commit/8afd9e233d) - [Improve `set-constant` scriptlet](https://github.com/gorhill/uBlock/commit/77feb25c4d) - [Improve `prevent-fetch` scriptlet](https://github.com/gorhill/uBlock/commit/e785b99338) - [Improve `href-sanitizer` scriptlet](https://github.com/gorhill/uBlock/commit/66e3a1ad47) From 86d751b2ecddff92fc503d0eecff31f706a28e60 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sat, 20 Jul 2024 08:59:16 -0400 Subject: [PATCH 0078/1099] New revision for release candidate --- dist/version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dist/version b/dist/version index 19fc7b2fc659d..d587c6675c569 100644 --- a/dist/version +++ b/dist/version @@ -1 +1 @@ -1.58.1.106 \ No newline at end of file +1.58.1.107 \ No newline at end of file From db33eb9f41f41e9868c15fa3b2b1a6fd0a1a9682 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sat, 20 Jul 2024 09:06:03 -0400 Subject: [PATCH 0079/1099] Make Firefox dev build auto-update --- dist/firefox/updates.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dist/firefox/updates.json b/dist/firefox/updates.json index db3cd2435f8c2..dda8596ede1e8 100644 --- a/dist/firefox/updates.json +++ b/dist/firefox/updates.json @@ -3,9 +3,9 @@ "uBlock0@raymondhill.net": { "updates": [ { - "version": "1.58.1.106", + "version": "1.58.1.107", "browser_specific_settings": { "gecko": { "strict_min_version": "78.0" } }, - "update_link": "https://github.com/gorhill/uBlock/releases/download/1.58.1rc6/uBlock0_1.58.1rc6.firefox.signed.xpi" + "update_link": "https://github.com/gorhill/uBlock/releases/download/1.58.1rc7/uBlock0_1.58.1rc7.firefox.signed.xpi" } ] } From 62f87b0ea81ce7f7f5821702083b0a89a100975c Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sat, 20 Jul 2024 09:43:08 -0400 Subject: [PATCH 0080/1099] Rename parameters as per earlier commit Related commit: https://github.com/gorhill/uBlock/commit/8afd9e233d3c43f51d21e4e51d24e643089d9853 --- assets/resources/scriptlets.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/assets/resources/scriptlets.js b/assets/resources/scriptlets.js index fa1457f87fde8..8c9f8b007c8ca 100644 --- a/assets/resources/scriptlets.js +++ b/assets/resources/scriptlets.js @@ -3752,10 +3752,10 @@ builtinScriptlets.push({ }); function removeNodeText( nodeName, - condition, + includes, ...extraArgs ) { - replaceNodeTextFn(nodeName, '', '', 'condition', condition || '', ...extraArgs); + replaceNodeTextFn(nodeName, '', '', 'includes', includes || '', ...extraArgs); } /******************************************************************************* From c36f7822ef99922a69f65af34ee223e96f4945a5 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sat, 20 Jul 2024 09:44:22 -0400 Subject: [PATCH 0081/1099] New revision for release candidate --- dist/version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dist/version b/dist/version index d587c6675c569..ebf7a12d20135 100644 --- a/dist/version +++ b/dist/version @@ -1 +1 @@ -1.58.1.107 \ No newline at end of file +1.58.1.108 \ No newline at end of file From f1c45a280084e6054546d9cfc0fbc34339fe6a6e Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sat, 20 Jul 2024 09:50:55 -0400 Subject: [PATCH 0082/1099] Make Firefox dev build auto-update --- dist/firefox/updates.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dist/firefox/updates.json b/dist/firefox/updates.json index dda8596ede1e8..51546a2207fd3 100644 --- a/dist/firefox/updates.json +++ b/dist/firefox/updates.json @@ -3,9 +3,9 @@ "uBlock0@raymondhill.net": { "updates": [ { - "version": "1.58.1.107", + "version": "1.58.1.108", "browser_specific_settings": { "gecko": { "strict_min_version": "78.0" } }, - "update_link": "https://github.com/gorhill/uBlock/releases/download/1.58.1rc7/uBlock0_1.58.1rc7.firefox.signed.xpi" + "update_link": "https://github.com/gorhill/uBlock/releases/download/1.58.1rc8/uBlock0_1.58.1rc8.firefox.signed.xpi" } ] } From 14d90418b8208ddaf0ba464c694cd6f0d7058afe Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sun, 21 Jul 2024 08:34:18 -0400 Subject: [PATCH 0083/1099] Fix regression in `trusted-replace-node-text` scriptlet Related commit: https://github.com/gorhill/uBlock/commit/8afd9e233d3c43f51d21e4e51d24e643089d9853 --- assets/resources/scriptlets.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets/resources/scriptlets.js b/assets/resources/scriptlets.js index 8c9f8b007c8ca..f731a929f328c 100644 --- a/assets/resources/scriptlets.js +++ b/assets/resources/scriptlets.js @@ -715,7 +715,7 @@ function replaceNodeTextFn( const rePattern = safe.patternToRegex(pattern, 'gms'); const extraArgs = safe.getExtraArgs(Array.from(arguments), 3); const reIncludes = safe.patternToRegex(extraArgs.includes || extraArgs.condition || '', 'ms'); - const reExcludes = safe.patternToRegex(extraArgs.excludes || '', 'ms'); + const reExcludes = safe.patternToRegex(extraArgs.excludes || '.^', 'ms'); const stop = (takeRecord = true) => { if ( takeRecord ) { handleMutations(observer.takeRecords()); From fa2a0a9549329febd18ccacb1cb114ac5ca232a7 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sun, 21 Jul 2024 08:36:10 -0400 Subject: [PATCH 0084/1099] New revision for release candidate --- dist/version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dist/version b/dist/version index ebf7a12d20135..b6c997d9e524d 100644 --- a/dist/version +++ b/dist/version @@ -1 +1 @@ -1.58.1.108 \ No newline at end of file +1.58.1.109 \ No newline at end of file From ec568b614d54db9f9c930b7e0146c37bd0d9a08d Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sun, 21 Jul 2024 08:45:51 -0400 Subject: [PATCH 0085/1099] Make Firefox dev build auto-update --- dist/firefox/updates.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dist/firefox/updates.json b/dist/firefox/updates.json index 51546a2207fd3..800ac3a968897 100644 --- a/dist/firefox/updates.json +++ b/dist/firefox/updates.json @@ -3,9 +3,9 @@ "uBlock0@raymondhill.net": { "updates": [ { - "version": "1.58.1.108", + "version": "1.58.1.109", "browser_specific_settings": { "gecko": { "strict_min_version": "78.0" } }, - "update_link": "https://github.com/gorhill/uBlock/releases/download/1.58.1rc8/uBlock0_1.58.1rc8.firefox.signed.xpi" + "update_link": "https://github.com/gorhill/uBlock/releases/download/1.58.1rc9/uBlock0_1.58.1rc9.firefox.signed.xpi" } ] } From bf75dc2f9094a6ff8151624563bc3866a112aa6f Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Mon, 22 Jul 2024 09:51:40 -0400 Subject: [PATCH 0086/1099] Code review of `trusted-replace-node-text` scriptlet --- assets/resources/scriptlets.js | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/assets/resources/scriptlets.js b/assets/resources/scriptlets.js index f731a929f328c..55f512dde02a9 100644 --- a/assets/resources/scriptlets.js +++ b/assets/resources/scriptlets.js @@ -714,8 +714,12 @@ function replaceNodeTextFn( const reNodeName = safe.patternToRegex(nodeName, 'i', true); const rePattern = safe.patternToRegex(pattern, 'gms'); const extraArgs = safe.getExtraArgs(Array.from(arguments), 3); - const reIncludes = safe.patternToRegex(extraArgs.includes || extraArgs.condition || '', 'ms'); - const reExcludes = safe.patternToRegex(extraArgs.excludes || '.^', 'ms'); + const reIncludes = extraArgs.includes || extraArgs.condition + ? safe.patternToRegex(extraArgs.includes || extraArgs.condition, 'ms') + : null; + const reExcludes = extraArgs.excludes + ? safe.patternToRegex(extraArgs.excludes, 'ms') + : null; const stop = (takeRecord = true) => { if ( takeRecord ) { handleMutations(observer.takeRecords()); @@ -728,10 +732,14 @@ function replaceNodeTextFn( let sedCount = extraArgs.sedCount || 0; const handleNode = node => { const before = node.textContent; - reIncludes.lastIndex = 0; - if ( safe.RegExp_test.call(reIncludes, before) === false ) { return true; } - reExcludes.lastIndex = 0; - if ( safe.RegExp_test.call(reExcludes, before) ) { return true; } + if ( reIncludes ) { + reIncludes.lastIndex = 0; + if ( safe.RegExp_test.call(reIncludes, before) === false ) { return true; } + } + if ( reExcludes ) { + reExcludes.lastIndex = 0; + if ( safe.RegExp_test.call(reExcludes, before) ) { return true; } + } rePattern.lastIndex = 0; if ( safe.RegExp_test.call(rePattern, before) === false ) { return true; } rePattern.lastIndex = 0; From 5526b035bdc5974972e8067817dd52cd5460d16d Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Mon, 22 Jul 2024 10:32:45 -0400 Subject: [PATCH 0087/1099] New revision for release candidate --- dist/version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dist/version b/dist/version index b6c997d9e524d..726bf1384112a 100644 --- a/dist/version +++ b/dist/version @@ -1 +1 @@ -1.58.1.109 \ No newline at end of file +1.58.1.110 \ No newline at end of file From a54e3c5e3973e759a6c56cfa5eb1947a3234e197 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Mon, 22 Jul 2024 10:41:52 -0400 Subject: [PATCH 0088/1099] Make Firefox dev build auto-update --- dist/firefox/updates.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dist/firefox/updates.json b/dist/firefox/updates.json index 800ac3a968897..fd29665486240 100644 --- a/dist/firefox/updates.json +++ b/dist/firefox/updates.json @@ -3,9 +3,9 @@ "uBlock0@raymondhill.net": { "updates": [ { - "version": "1.58.1.109", + "version": "1.58.1.110", "browser_specific_settings": { "gecko": { "strict_min_version": "78.0" } }, - "update_link": "https://github.com/gorhill/uBlock/releases/download/1.58.1rc9/uBlock0_1.58.1rc9.firefox.signed.xpi" + "update_link": "https://github.com/gorhill/uBlock/releases/download/1.58.1rc10/uBlock0_1.58.1rc10.firefox.signed.xpi" } ] } From 84be9cde6d5ec3381a0a563796b92ed733c202a4 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Tue, 23 Jul 2024 09:19:16 -0400 Subject: [PATCH 0089/1099] Improve `href-sanitizer` scriptlet Support ability to recursively unwrap destination URL. Example: ...##+js(href-sanitizer, a.clickTracker, ?r?u) Related discussion: https://github.com/uBlockOrigin/uBlock-discussions/discussions/775#discussioncomment-10120835 --- assets/resources/scriptlets.js | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/assets/resources/scriptlets.js b/assets/resources/scriptlets.js index 55f512dde02a9..4cc2e938fc40d 100644 --- a/assets/resources/scriptlets.js +++ b/assets/resources/scriptlets.js @@ -3503,17 +3503,26 @@ function hrefSanitizer( } return ''; }; + const extractParam = (href, source) => { + if ( Boolean(source) === false ) { return href; } + const recursive = source.includes('?', 1); + const end = recursive ? source.indexOf('?', 1) : source.length; + try { + const url = new URL(href, document.location); + const value = url.searchParams.get(source.slice(1, end)); + if ( value === null ) { return href } + if ( recursive ) { return extractParam(value, source.slice(end)); } + return value; + } catch(x) { + } + return href; + }; const extractText = (elem, source) => { if ( /^\[.*\]$/.test(source) ) { return elem.getAttribute(source.slice(1,-1).trim()) || ''; } if ( source.startsWith('?') ) { - try { - const url = new URL(elem.href, document.location); - return url.searchParams.get(source.slice(1)) || ''; - } catch(x) { - } - return ''; + return extractParam(elem.href, source); } if ( source === 'text' ) { return elem.textContent From 45a1d61c87fbe40de57ca97c364eb3cfc3c8a267 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Tue, 23 Jul 2024 09:23:14 -0400 Subject: [PATCH 0090/1099] Update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5dc86763ad714..44692a4e23e6f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,4 @@ +- [Improve `href-sanitizer` scriptlet](https://github.com/gorhill/uBlock/commit/84be9cde6d) - [Improve `trusted-replace-node-text` scriptlet](https://github.com/gorhill/uBlock/commit/8afd9e233d) - [Improve `set-constant` scriptlet](https://github.com/gorhill/uBlock/commit/77feb25c4d) - [Improve `prevent-fetch` scriptlet](https://github.com/gorhill/uBlock/commit/e785b99338) From b7676e8f9fd4a8765efec991d8c3f2693b1a2e61 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Tue, 23 Jul 2024 09:23:31 -0400 Subject: [PATCH 0091/1099] New revision for release candidate --- dist/version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dist/version b/dist/version index 726bf1384112a..aff5d203cfe48 100644 --- a/dist/version +++ b/dist/version @@ -1 +1 @@ -1.58.1.110 \ No newline at end of file +1.58.1.111 \ No newline at end of file From ac34aa975b814ee169d7ce497b2d7e287f3e06b2 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Tue, 23 Jul 2024 09:40:31 -0400 Subject: [PATCH 0092/1099] Make Firefox dev build auto-update --- dist/firefox/updates.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dist/firefox/updates.json b/dist/firefox/updates.json index fd29665486240..6b426df1439eb 100644 --- a/dist/firefox/updates.json +++ b/dist/firefox/updates.json @@ -3,9 +3,9 @@ "uBlock0@raymondhill.net": { "updates": [ { - "version": "1.58.1.110", + "version": "1.58.1.111", "browser_specific_settings": { "gecko": { "strict_min_version": "78.0" } }, - "update_link": "https://github.com/gorhill/uBlock/releases/download/1.58.1rc10/uBlock0_1.58.1rc10.firefox.signed.xpi" + "update_link": "https://github.com/gorhill/uBlock/releases/download/1.58.1rc11/uBlock0_1.58.1rc11.firefox.signed.xpi" } ] } From 2a675785bc6a63dcb6f0a2847ba4fee67cdf12fd Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Wed, 24 Jul 2024 12:43:00 -0400 Subject: [PATCH 0093/1099] Update README.md --- README.md | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 23e90367a14f8..3a4fbd0927da6 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,6 @@ uBlock Origin (uBO)

Get uBlock Origin for Firefox -Get uBlock Origin for Chromium Get uBlock Origin for Microsoft Edge Get uBlock Origin for Opera Get uBlock Origin for Thunderbird @@ -31,6 +30,13 @@ uBlock Origin (uBO) *** +

+Get uBlock Origin for Chromium
+Important: Will uBO automatically transition to uBO Lite in the Chrome Web Store? (tldr: no) +

+ +*** + uBlock Origin (uBO) is a CPU and memory-efficient [wide-spectrum content blocker][Blocking] for Chromium and Firefox. It blocks ads, trackers, coin miners, popups, annoying anti-blockers, malware sites, etc., by default using [EasyList][EasyList], [EasyPrivacy][EasyPrivacy], [Peter Lowe's Blocklist][Peter Lowe's Blocklist], [Online Malicious URL Blocklist][Malicious Blocklist], and uBO [filter lists][uBO Filters]. There are many other lists available to block even more. Hosts files are also supported. uBO uses the EasyList filter syntax and [extends][Extended Syntax] the syntax to work with custom rules and filters. You may easily unselect any preselected filter lists if you think uBO blocks too much. For reference, Adblock Plus installs with only EasyList, ABP filters, and Acceptable Ads enabled by default. From 2dd591c1d73a967486970e2a789625462333fd81 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Wed, 24 Jul 2024 12:43:57 -0400 Subject: [PATCH 0094/1099] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 3a4fbd0927da6..79ffa966e181f 100644 --- a/README.md +++ b/README.md @@ -32,7 +32,7 @@ uBlock Origin (uBO)

Get uBlock Origin for Chromium
-Important: Will uBO automatically transition to uBO Lite in the Chrome Web Store? (tldr: no) +Important: Will uBO automatically transition to uBO Lite in the Chrome Web Store? (tldr: no)

*** From 76246f2c599c2f0a290dca490802d88ceba2d78c Mon Sep 17 00:00:00 2001 From: Fanboynz Date: Sun, 28 Jul 2024 00:26:59 +1200 Subject: [PATCH 0095/1099] Add dismissed to set-cookie (#3921) --- assets/resources/scriptlets.js | 1 + 1 file changed, 1 insertion(+) diff --git a/assets/resources/scriptlets.js b/assets/resources/scriptlets.js index 4cc2e938fc40d..c8af8f229775f 100644 --- a/assets/resources/scriptlets.js +++ b/assets/resources/scriptlets.js @@ -3819,6 +3819,7 @@ function setCookie( 'approved', 'disapproved', 'hide', 'hidden', 'essential', 'nonessential', + 'dismiss', 'dismissed', ]; const normalized = value.toLowerCase(); const match = /^("?)(.+)\1$/.exec(normalized); From ec633887dddd7cb98249959f81918e7cb325b8cb Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sun, 28 Jul 2024 10:44:17 -0400 Subject: [PATCH 0096/1099] Improve `abort-on-stack-trace` scriptlet Related issue: https://github.com/uBlockOrigin/uBlock-issues/issues/3314 --- assets/resources/scriptlets.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/assets/resources/scriptlets.js b/assets/resources/scriptlets.js index c8af8f229775f..6e9dcd60ea4b8 100644 --- a/assets/resources/scriptlets.js +++ b/assets/resources/scriptlets.js @@ -64,6 +64,7 @@ function safeSelf() { 'RegExp_test': self.RegExp.prototype.test, 'RegExp_exec': self.RegExp.prototype.exec, 'Request_clone': self.Request.prototype.clone, + 'String_fromCharCode': String.fromCharCode, 'XMLHttpRequest': self.XMLHttpRequest, 'addEventListener': self.EventTarget.prototype.addEventListener, 'removeEventListener': self.EventTarget.prototype.removeEventListener, @@ -211,7 +212,7 @@ builtinScriptlets.push({ function getExceptionToken() { const safe = safeSelf(); const token = - String.fromCharCode(Date.now() % 26 + 97) + + safe.String_fromCharCode(Date.now() % 26 + 97) + safe.Math_floor(safe.Math_random() * 982451653 + 982451653).toString(36); const oe = self.onerror; self.onerror = function(msg, ...args) { From c7b54af0a241963fc69e49f6ea8e2c583036be49 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Mon, 29 Jul 2024 14:54:46 -0400 Subject: [PATCH 0097/1099] [mv3] Add minimal ability to diagnose ruleset issue A new icon has been added to the popup panel, to open a popup window with a terse list of DNR events for the current tab, in reverse chronological order (most recent DNR event appears at the top). The new ability is available only when the extension is sideloaded, as per `declarativeNetRequestFeedback` documentation. Ref: https://developer.chrome.com/docs/extensions/reference/api/declarativeNetRequest#event-onRuleMatchedDebug Purposefully minimal, so as to have something rather than nothing when having to diagnose filtering issue with the DNR API. Example: https://github.com/uBlockOrigin/uBOL-home/issues/156 The content of the popup window does not dynamically update, force a refresh (F5) to get the most recent DNR events. This might be improved in the future. The DNR event buffer is not persisted, so the buffer is empty when service worker is restarted. This might be improved in the future by using session storage API. There is no output filtering ability in this first draft. This might be improved in the future. DNR rules are reported. The filter from which a DNR rule originates is not reported. Given that the rulesets are optimized after conversion from original filter lists to reduce the DNR rule count, this is unlikely to ever be possible. --- platform/mv3/extension/css/matched-rules.css | 28 ++++ platform/mv3/extension/js/background.js | 31 +++-- platform/mv3/extension/js/dashboard.js | 4 +- platform/mv3/extension/js/debug.js | 124 ++++++++++++++++++ platform/mv3/extension/js/ext.js | 7 +- platform/mv3/extension/js/matched-rules.js | 48 +++++++ platform/mv3/extension/js/popup.js | 23 +++- platform/mv3/extension/js/ruleset-manager.js | 8 +- .../mv3/extension/js/scripting-manager.js | 15 +-- platform/mv3/extension/js/utils.js | 30 +---- platform/mv3/extension/matched-rules.html | 33 +++++ platform/mv3/extension/popup.html | 2 +- src/js/dom.js | 12 +- tools/make-mv3.sh | 14 +- 14 files changed, 300 insertions(+), 79 deletions(-) create mode 100644 platform/mv3/extension/css/matched-rules.css create mode 100644 platform/mv3/extension/js/debug.js create mode 100644 platform/mv3/extension/js/matched-rules.js create mode 100644 platform/mv3/extension/matched-rules.html diff --git a/platform/mv3/extension/css/matched-rules.css b/platform/mv3/extension/css/matched-rules.css new file mode 100644 index 0000000000000..9db4d9c73f0fa --- /dev/null +++ b/platform/mv3/extension/css/matched-rules.css @@ -0,0 +1,28 @@ + +#matchedEntries { + display: flex; + flex-direction: column; + font-family: monospace; + font-size: small; + white-space: pre-wrap; + word-break: break-all; +} + +.matchInfo { + display: flex; + flex-wrap: nowrap; +} + +.matchInfo:nth-of-type(2n) { + background-color: lightgray; +} + +.requestInfo { + border-inline-end: 1px dotted black; + padding-inline-end: 0.5em; + width: 25vw; +} + +.ruleInfo { + padding-inline-start: 0.5em; +} diff --git a/platform/mv3/extension/js/background.js b/platform/mv3/extension/js/background.js index e65775e9db7f8..17601b114995a 100644 --- a/platform/mv3/extension/js/background.js +++ b/platform/mv3/extension/js/background.js @@ -19,8 +19,6 @@ Home: https://github.com/gorhill/uBlock */ -/******************************************************************************/ - import { adminRead, browser, @@ -28,13 +26,9 @@ import { localRead, localWrite, runtime, sessionRead, sessionWrite, + windows, } from './ext.js'; -import { - broadcastMessage, - ubolLog, -} from './utils.js'; - import { defaultRulesetsFromLanguage, enableRulesets, @@ -54,8 +48,13 @@ import { } from './mode-manager.js'; import { - registerInjectables, -} from './scripting-manager.js'; + getMatchedRules, + isSideloaded, + ubolLog, +} from './debug.js'; + +import { broadcastMessage } from './utils.js'; +import { registerInjectables } from './scripting-manager.js'; /******************************************************************************/ @@ -253,6 +252,7 @@ function onMessage(request, sender, callback) { hasOmnipotence: results[1], hasGreatPowers: results[2], rulesetDetails: results[3], + isSideloaded, }); }); return true; @@ -308,6 +308,19 @@ function onMessage(request, sender, callback) { }); return true; + case 'getMatchedRules': + getMatchedRules(request.tabId).then(entries => { + callback(entries); + }); + return true; + + case 'showMatchedRules': + windows.create({ + type: 'popup', + url: `/matched-rules.html?tab=${request.tabId}`, + }); + break; + default: break; } diff --git a/platform/mv3/extension/js/dashboard.js b/platform/mv3/extension/js/dashboard.js index 363f30173fa16..b8c240d5c3c43 100644 --- a/platform/mv3/extension/js/dashboard.js +++ b/platform/mv3/extension/js/dashboard.js @@ -19,10 +19,8 @@ Home: https://github.com/gorhill/uBlock */ -'use strict'; - -import { runtime } from './ext.js'; import { dom } from './dom.js'; +import { runtime } from './ext.js'; /******************************************************************************/ diff --git a/platform/mv3/extension/js/debug.js b/platform/mv3/extension/js/debug.js new file mode 100644 index 0000000000000..a5f1a852f9e62 --- /dev/null +++ b/platform/mv3/extension/js/debug.js @@ -0,0 +1,124 @@ +/******************************************************************************* + + uBlock Origin Lite - a comprehensive, MV3-compliant content blocker + Copyright (C) 2024-present Raymond Hill + + 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/}. + + Home: https://github.com/gorhill/uBlock +*/ + +import { browser, dnr } from './ext.js'; + +/******************************************************************************/ + +export const isSideloaded = (( ) => { + if ( dnr.onRuleMatchedDebug instanceof Object === false ) { return false; } + const { id } = browser.runtime; + // https://addons.mozilla.org/en-US/firefox/addon/ublock-origin-lite/ + if ( id === 'uBOLite@raymondhill.net' ) { return false; } + // https://chromewebstore.google.com/detail/ddkjiahejlhfcafbddmgiahcphecmpfh + if ( id === 'ddkjiahejlhfcafbddmgiahcphecmpfh' ) { return false; } + // https://microsoftedge.microsoft.com/addons/detail/cimighlppcgcoapaliogpjjdehbnofhn + if ( id === 'cimighlppcgcoapaliogpjjdehbnofhn' ) { return false; } + return true; +})(); + +/******************************************************************************/ + +export const ubolLog = (...args) => { + // Do not pollute dev console in stable releases. + if ( isSideloaded !== true ) { return; } + console.info('[uBOL]', ...args); +}; + +/******************************************************************************/ + +export const getMatchedRules = (( ) => { + const noopFn = ( ) => Promise.resolve([]); + if ( isSideloaded !== true ) { return noopFn; } + if ( dnr.onRuleMatchedDebug instanceof Object === false ) { return noopFn; } + + const rulesets = new Map(); + const bufferSize = 256; + const matchedRules = new Array(bufferSize); + matchedRules.fill(null); + let writePtr = 0; + + const pruneLongLists = list => { + if ( list.length <= 21 ) { return list; } + return [ ...list.slice(0, 10), '...', ...list.slice(-10) ]; + + }; + + const getRuleset = async rulesetId => { + if ( rulesets.has(rulesetId) ) { + return rulesets.get(rulesetId); + } + let rules; + if ( rulesetId === dnr.DYNAMIC_RULESET_ID ) { + rules = await dnr.getDynamicRules().catch(( ) => undefined); + } else { + const response = await fetch(`/rulesets/main/${rulesetId}.json`).catch(( ) => undefined); + if ( response === undefined ) { return; } + rules = await response.json().catch(( ) => undefined); + } + if ( Array.isArray(rules) === false ) { return; } + const ruleset = new Map(); + for ( const rule of rules ) { + const condition = rule.condition; + if ( condition ) { + if ( condition.requestDomains ) { + condition.requestDomains = pruneLongLists(condition.requestDomains); + } + if ( condition.initiatorDomains ) { + condition.initiatorDomains = pruneLongLists(condition.initiatorDomains); + } + } + const ruleId = rule.id; + rule.id = `${rulesetId}-${ruleId}`; + ruleset.set(ruleId, rule); + } + rulesets.set(rulesetId, ruleset); + return ruleset; + }; + + const getRuleDetails = async ruleInfo => { + const { rulesetId, ruleId } = ruleInfo.rule; + const ruleset = await getRuleset(rulesetId); + if ( ruleset === undefined ) { return; } + return { request: ruleInfo.request, rule: ruleset.get(ruleId) }; + }; + + dnr.onRuleMatchedDebug.addListener(ruleInfo => { + matchedRules[writePtr] = ruleInfo; + writePtr = (writePtr + 1) % bufferSize; + }); + + return async tabId => { + const promises = []; + for ( let i = 0; i < bufferSize; i++ ) { + const j = (writePtr + i) % bufferSize; + const ruleInfo = matchedRules[j]; + if ( ruleInfo === null ) { continue; } + if ( ruleInfo.request.tabId !== tabId ) { continue; } + const promise = getRuleDetails(ruleInfo); + if ( promise === undefined ) { continue; } + promises.unshift(promise); + } + return Promise.all(promises); + }; +})(); + +/******************************************************************************/ diff --git a/platform/mv3/extension/js/ext.js b/platform/mv3/extension/js/ext.js index 0126c1894f7ad..94d5118a211b5 100644 --- a/platform/mv3/extension/js/ext.js +++ b/platform/mv3/extension/js/ext.js @@ -19,12 +19,6 @@ Home: https://github.com/gorhill/uBlock */ -/* jshint esversion:11 */ - -'use strict'; - -/******************************************************************************/ - export const browser = self.browser instanceof Object && self.browser instanceof Element === false @@ -34,6 +28,7 @@ export const browser = export const dnr = browser.declarativeNetRequest; export const i18n = browser.i18n; export const runtime = browser.runtime; +export const windows = browser.windows; /******************************************************************************/ diff --git a/platform/mv3/extension/js/matched-rules.js b/platform/mv3/extension/js/matched-rules.js new file mode 100644 index 0000000000000..75a17caf4f7c6 --- /dev/null +++ b/platform/mv3/extension/js/matched-rules.js @@ -0,0 +1,48 @@ +/******************************************************************************* + + uBlock Origin Lite - a comprehensive, MV3-compliant content blocker + Copyright (C) 2014-present Raymond Hill + + 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/}. + + Home: https://github.com/gorhill/uBlock +*/ + +import { dom, qs$ } from './dom.js'; +import { sendMessage } from './ext.js'; + +/******************************************************************************/ + +const url = new URL(document.location.href); +const tabId = parseInt(url.searchParams.get('tab'), 10) || 0; + +const entries = await sendMessage({ + what: 'getMatchedRules', + tabId, +}); + +const fragment = new DocumentFragment(); +const template = qs$('#matchInfo'); +for ( const entry of (entries || []) ) { + if ( entry instanceof Object === false ) { continue; } + const row = template.content.cloneNode(true); + qs$(row, '.requestInfo').textContent = JSON.stringify(entry.request, null, 2); + qs$(row, '.ruleInfo').textContent = JSON.stringify(entry.rule, null, 2); + fragment.append(row); +} + +dom.empty('#matchedEntries'); +qs$('#matchedEntries').append(fragment); + +/******************************************************************************/ diff --git a/platform/mv3/extension/js/popup.js b/platform/mv3/extension/js/popup.js index 29b993b24aa39..712dbbdf2ed3b 100644 --- a/platform/mv3/extension/js/popup.js +++ b/platform/mv3/extension/js/popup.js @@ -19,17 +19,11 @@ Home: https://github.com/gorhill/uBlock */ -/* jshint esversion:11 */ - -'use strict'; - -/******************************************************************************/ - import { browser, + localRead, localWrite, runtime, sendMessage, - localRead, localWrite, } from './ext.js'; import { dom, qs$ } from './dom.js'; @@ -271,6 +265,15 @@ dom.on('[data-i18n-title="popupTipDashboard"]', 'click', ev => { runtime.openOptionsPage(); }); +dom.on('#showMatchedRules', 'click', ev => { + if ( ev.isTrusted !== true ) { return; } + if ( ev.button !== 0 ) { return; } + sendMessage({ + what: 'showMatchedRules', + tabId: currentTab.id, + }); +}); + /******************************************************************************/ async function init() { @@ -303,6 +306,12 @@ async function init() { dom.text('#hostname', punycode.toUnicode(tabHostname)); + dom.cl.toggle('#showMatchedRules', 'enabled', + popupPanelData.isSideloaded === true && + typeof currentTab.id === 'number' && + isNaN(currentTab.id) === false + ); + const parent = qs$('#rulesetStats'); for ( const details of popupPanelData.rulesetDetails || [] ) { const div = dom.clone('#templates .rulesetDetails'); diff --git a/platform/mv3/extension/js/ruleset-manager.js b/platform/mv3/extension/js/ruleset-manager.js index a484e1d21d17d..749a83c708c0a 100644 --- a/platform/mv3/extension/js/ruleset-manager.js +++ b/platform/mv3/extension/js/ruleset-manager.js @@ -19,15 +19,9 @@ Home: https://github.com/gorhill/uBlock */ -/* jshint esversion:11 */ - -'use strict'; - -/******************************************************************************/ - import { browser, dnr, i18n } from './ext.js'; import { fetchJSON } from './fetch.js'; -import { ubolLog } from './utils.js'; +import { ubolLog } from './debug.js'; /******************************************************************************/ diff --git a/platform/mv3/extension/js/scripting-manager.js b/platform/mv3/extension/js/scripting-manager.js index 10175fc92e952..6502cbc0aea1c 100644 --- a/platform/mv3/extension/js/scripting-manager.js +++ b/platform/mv3/extension/js/scripting-manager.js @@ -19,18 +19,13 @@ Home: https://github.com/gorhill/uBlock */ -/* jshint esversion:11 */ - -'use strict'; - -/******************************************************************************/ +import * as ut from './utils.js'; import { browser } from './ext.js'; import { fetchJSON } from './fetch.js'; -import { getFilteringModeDetails } from './mode-manager.js'; import { getEnabledRulesetsDetails } from './ruleset-manager.js'; - -import * as ut from './utils.js'; +import { getFilteringModeDetails } from './mode-manager.js'; +import { ubolLog } from './debug.js'; /******************************************************************************/ @@ -542,13 +537,13 @@ async function registerInjectables(origins) { toRemove.push(...Array.from(before.keys())); if ( toRemove.length !== 0 ) { - ut.ubolLog(`Unregistered ${toRemove} content (css/js)`); + ubolLog(`Unregistered ${toRemove} content (css/js)`); await browser.scripting.unregisterContentScripts({ ids: toRemove }) .catch(reason => { console.info(reason); }); } if ( toAdd.length !== 0 ) { - ut.ubolLog(`Registered ${toAdd.map(v => v.id)} content (css/js)`); + ubolLog(`Registered ${toAdd.map(v => v.id)} content (css/js)`); await browser.scripting.registerContentScripts(toAdd) .catch(reason => { console.info(reason); }); } diff --git a/platform/mv3/extension/js/utils.js b/platform/mv3/extension/js/utils.js index cadeaea0b64c3..cdfc98b25d12d 100644 --- a/platform/mv3/extension/js/utils.js +++ b/platform/mv3/extension/js/utils.js @@ -19,14 +19,6 @@ Home: https://github.com/gorhill/uBlock */ -/* jshint esversion:11 */ - -'use strict'; - -/******************************************************************************/ - -import { browser } from './ext.js'; - /******************************************************************************/ function parsedURLromOrigin(origin) { @@ -114,7 +106,7 @@ const hostnamesFromMatches = origins => { out.push('all-urls'); continue; } - const match = /^\*:\/\/(?:\*\.)?([^\/]+)\/\*/.exec(origin); + const match = /^\*:\/\/(?:\*\.)?([^/]+)\/\*/.exec(origin); if ( match === null ) { continue; } out.push(match[1]); } @@ -130,25 +122,6 @@ export const broadcastMessage = message => { /******************************************************************************/ -const ubolLog = (...args) => { - // Do not pollute dev console in stable releases. - if ( shouldLog !== true ) { return; } - console.info('[uBOL]', ...args); -}; - -const shouldLog = (( ) => { - const { id } = browser.runtime; - // https://addons.mozilla.org/en-US/firefox/addon/ublock-origin-lite/ - if ( id === 'uBOLite@raymondhill.net' ) { return false; } - // https://chromewebstore.google.com/detail/ddkjiahejlhfcafbddmgiahcphecmpfh - if ( id === 'ddkjiahejlhfcafbddmgiahcphecmpfh' ) { return false; } - // https://microsoftedge.microsoft.com/addons/detail/cimighlppcgcoapaliogpjjdehbnofhn - if ( id === 'cimighlppcgcoapaliogpjjdehbnofhn' ) { return false; } - return true; -})(); - -/******************************************************************************/ - export { parsedURLromOrigin, toBroaderHostname, @@ -158,5 +131,4 @@ export { subtractHostnameIters, matchesFromHostnames, hostnamesFromMatches, - ubolLog, }; diff --git a/platform/mv3/extension/matched-rules.html b/platform/mv3/extension/matched-rules.html new file mode 100644 index 0000000000000..ec392ca2338ab --- /dev/null +++ b/platform/mv3/extension/matched-rules.html @@ -0,0 +1,33 @@ + + + + + +Matched rules + + + + + + + + + + + +
+
+ + + + + + + + + diff --git a/platform/mv3/extension/popup.html b/platform/mv3/extension/popup.html index 7492469bd0290..990f6590af438 100644 --- a/platform/mv3/extension/popup.html +++ b/platform/mv3/extension/popup.html @@ -29,7 +29,7 @@ - + list-altShow matched rules cogs
diff --git a/src/js/dom.js b/src/js/dom.js index 5c4d19479f80d..241af9d525b1b 100644 --- a/src/js/dom.js +++ b/src/js/dom.js @@ -19,10 +19,6 @@ Home: https://github.com/gorhill/uBlock */ -/* jshint esversion:11 */ - -'use strict'; - /******************************************************************************/ const normalizeTarget = target => { @@ -113,6 +109,14 @@ class dom { } } + static empty(target) { + for ( const elem of normalizeTarget(target) ) { + while ( elem.firstElementChild !== null ) { + elem.firstElementChild.remove(); + } + } + } + // target, type, callback, [options] // target, type, subtarget, callback, [options] diff --git a/tools/make-mv3.sh b/tools/make-mv3.sh index 13f82f4db33c5..a4c20c48e5737 100755 --- a/tools/make-mv3.sh +++ b/tools/make-mv3.sh @@ -128,11 +128,19 @@ fi echo "*** uBOLite.mv3: extension ready" echo "Extension location: $DES/" -# Local build: use a different extension id than the official one -if [ -z "$TAGNAME" ] && [ "$PLATFORM" = "firefox" ]; then +# Local build +if [ -z "$TAGNAME" ]; then + # Enable DNR rule debugging tmp=$(mktemp) - jq '.browser_specific_settings.gecko.id = "uBOLite.dev@raymondhill.net"' "$DES/manifest.json" > "$tmp" \ + jq '.permissions += ["declarativeNetRequestFeedback"]' \ + "$DES/manifest.json" > "$tmp" \ && mv "$tmp" "$DES/manifest.json" + # Use a different extension id than the official one + if [ "$PLATFORM" = "firefox" ]; then + tmp=$(mktemp) + jq '.browser_specific_settings.gecko.id = "uBOLite.dev@raymondhill.net"' "$DES/manifest.json" > "$tmp" \ + && mv "$tmp" "$DES/manifest.json" + fi fi if [ "$FULL" = "yes" ]; then From 30e0d016569d0e60082b1909123c88f0aaf4ef01 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Mon, 29 Jul 2024 15:50:38 -0400 Subject: [PATCH 0098/1099] New revision for stable release --- dist/version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dist/version b/dist/version index aff5d203cfe48..43eb9bc68ddfd 100644 --- a/dist/version +++ b/dist/version @@ -1 +1 @@ -1.58.1.111 \ No newline at end of file +1.59.0 \ No newline at end of file From a0de43aba933bc7bd79a32a2f2b6b87bf2ca5f18 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Mon, 29 Jul 2024 16:05:15 -0400 Subject: [PATCH 0099/1099] Import translation work from https://crowdin.com/project/ublock --- platform/mv3/description/webstore.be.txt | 2 +- platform/mv3/description/webstore.br_FR.txt | 12 ++-- platform/mv3/description/webstore.eu.txt | 12 ++-- platform/mv3/description/webstore.ur.txt | 6 +- .../mv3/extension/_locales/bs/messages.json | 2 +- .../mv3/extension/_locales/ka/messages.json | 2 +- .../mv3/extension/_locales/lt/messages.json | 2 +- .../mv3/extension/_locales/ms/messages.json | 2 +- .../mv3/extension/_locales/tr/messages.json | 2 +- .../mv3/extension/_locales/ur/messages.json | 60 +++++++++---------- src/_locales/br_FR/messages.json | 4 +- src/_locales/bs/messages.json | 10 ++-- src/_locales/lt/messages.json | 2 +- src/_locales/ms/messages.json | 18 +++--- src/_locales/sv/messages.json | 4 +- src/_locales/tr/messages.json | 8 +-- src/_locales/zh_TW/messages.json | 34 +++++------ 17 files changed, 91 insertions(+), 91 deletions(-) diff --git a/platform/mv3/description/webstore.be.txt b/platform/mv3/description/webstore.be.txt index d5a2e9812c0df..80b06a4940748 100644 --- a/platform/mv3/description/webstore.be.txt +++ b/platform/mv3/description/webstore.be.txt @@ -1,4 +1,4 @@ -uBO Lite (uBOL) is a *permission-less* MV3-based content blocker. +uBO Лайт (uBOL) гэта блакіроўшчык кантэнту з меншымі патрабаваннямі да дазволаў заснаваны на MV3 The default ruleset corresponds to uBlock Origin's default filterset: diff --git a/platform/mv3/description/webstore.br_FR.txt b/platform/mv3/description/webstore.br_FR.txt index 03354935ec791..5cf05df10d91a 100644 --- a/platform/mv3/description/webstore.br_FR.txt +++ b/platform/mv3/description/webstore.br_FR.txt @@ -5,19 +5,19 @@ Ar reolennoù dre ziouer a glot gant silañ dre ziouer uBlock Origin: - Rolloù siloù genidik a uBlock Origin - EasyList - EasyPrivacy -- Peter Lowe’s Ad and tracking server list +- Roll bruderezh ha servijerioù heuliañ Peter Lowe Tu zo deoc'h ouzhpennañ reolennoù all en arventennoù -- klikit war an ikon _kendentadur_ er banell popup. -uBOL is entirely declarative, meaning there is no need for a permanent uBOL process for the filtering to occur, and CSS/JS injection-based content filtering is performed reliably by the browser itself rather than by the extension. This means that uBOL itself does not consume CPU/memory resources while content blocking is ongoing -- uBOL's service worker process is required _only_ when you interact with the popup panel or the option pages. +Disklêriañ a ra uBOL penn-da-benn, da lavaret eo n'eus ket ezhomm eus un argerzh uBOL padus evit ma c'hoarvezfe ar silañ, ha silañ endalc'hadoù diazezet war enlakaat CSS/JS a vez graet en un doare fizius gant ar merdeer e-unan kentoc'h eget gant an astenn. Kement-se a dalvez ne vez ket gounezet gant uBOL e-unan arc'hwelioù CPU/memor e-pad ma vez stanket an endalc'hadoù -- ezhomm zo eus argerzh al labourer servij uBOL _nemet_ pa vez etregweredet gant ar banell digeriñ pe ar pajennoù dibarzhioù. -uBOL does not require broad "read and modify data" permission at install time, hence its limited capabilities out of the box compared to uBlock Origin or other content blockers requiring broad "read and modify data" permissions at install time. +UBOL n'en deus ket ezhomm eus aotreoù ledan "lenn ha kemmañ roadennoù" e-pad ar staliañ, setu perak e c'halloudoù bevennet e-keñver uBlock Origin pe stankerien endalc'hadoù all a c'houlenn aotreoù ledan "lenn ha kemmañ roadennoù" e-pad ar staliañ. Koulskoude ez eus tu deoc'h reiñ *sklaer* aotreoù ouzhpenn da uBOL el lec'hiennoù ma fell deoc'h, mod-se e vint silet gwelloc'h en ur implij siloù kened hag ensinkladurioù scriplet. Evit reiñ aotreoù ouzhpenn da uBOL en ul lec'hienn bennak, n'ho peus nemet digeriñ ar prenestr pop-up ha diuzañ ul live silañ uheloc'h evel ar mod Gwellañ pe ar mod Klok -The browser will then warn you about the effects of granting the additional permissions requested by the extension on the current site, and you will have to tell the browser whether you accept or decline the request. +Goude-se e vo kelaouet ac'hanoc'h gant ar merdeer diwar-benn efedoù reiñ an aotreoù ouzhpenn goulennet gant an astenn war al lec'hienn bremanel, ha ret e vo deoc'h lâret d'ar merdeer hag-eñ e vo degemeret pe nac'het ar goulenn ganeoc'h. Ma asantit da reiñ muioc'h a aotreoù da uBOL war ar bajenn-mañ e vo silet gwelloc'h. @@ -25,6 +25,6 @@ Gallout a rit termeniñ ar mod silañ dre ziouer e pajenn arventennoù uBOL. Ma Dalc'hit soñj ez eo uBOL ur raktres war ober c'hoazh hag a zo e bal: -- No broad host permissions at install time -- extended permissions are granted explicitly by the user on a per-site basis. +- Aotreoù ostiz ledan ebet e-pad ar staliañ -- aotreoù astennet a vez roet splann gant an implijer dre lec'hienn. -- Entirely declarative for reliability and CPU/memory efficiency. +- Disklêriañ penn-da-benn evit ar fiziañs hag an efedusted CPU/memor. diff --git a/platform/mv3/description/webstore.eu.txt b/platform/mv3/description/webstore.eu.txt index f24ed1d5fe65e..f32691b857e2b 100644 --- a/platform/mv3/description/webstore.eu.txt +++ b/platform/mv3/description/webstore.eu.txt @@ -9,11 +9,11 @@ Peter Lowe-ren Ad and tracker zerrenda Arau gehiago gehitu ditzakezu aukeren orrialdea bisitatuz. Sakatu _Cogs_ ikonoa popup panelean. -uBOL is entirely declarative, meaning there is no need for a permanent uBOL process for the filtering to occur, and CSS/JS injection-based content filtering is performed reliably by the browser itself rather than by the extension. Horrek esan nahi du uBOLek berak ez duela CPU/memoria baliabiderik kontsumitzen edukien blokeoa martxan dagoen bitartean... uBOLren zerbitzuko langileen prozesua _only_ behar da popup panelarekin edo aukera orriekin elkarreragiten denean. +uBOL guztiz deklaratiboa da, hau da, ez dago uBOL prozesu iraunkor baten beharrik iragazketa gertatzeko, eta CSS/JS injekzioan oinarritutako edukien iragazketa nabigatzaileak berak egiten du fidagarritasunez, luzapenaren arabera beharrean. Horrek esan nahi du uBOLek berak ez duela CPU/memoria baliabiderik kontsumitzen edukien blokeoa martxan dagoen bitartean... uBOLren zerbitzuko langileen prozesua _only_ behar da popup panelarekin edo aukera orriekin elkarreragiten denean. -uBOL does not require broad "read and modify data" permission at install time, hence its limited capabilities out of the box compared to uBlock Origin or other content blockers requiring broad "read and modify data" permissions at install time. +uBOLek ez du "irakurtzeko eta aldatzeko datuak" baimen zabalik behar instalazio-unean, horregatik bere gaitasun mugatuak uBlock Origin-ekin alderatuta edo beste eduki-blokeatzaile batzuekin alderatuta, "irakurtzeko eta aldatzeko datuak" baimen zabalak behar dituzten instalazio garaian. -However, uBOL allows you to *explicitly* grant extended permissions on specific sites of your choice so that it can better filter on those sites using cosmetic filtering and scriptlet injections. +Hala ere, uBOLek aukera ematen dizu *esplizituki* baimen hedatuak zure aukeratutako gune espezifikoetan, gune horietan hobeto iragazteko iragazte kosmetikoak eta scriptlet injekzioak erabiliz. Leku jakin batean baimenak emateko, ireki panel emergentea eta aukeratu goiko iragazteko modu bat, optimo edo oso gisa. @@ -21,10 +21,10 @@ Nabigatzaileak orduan jakinaraziko dizu zer ondorio dituen luzapenak eskatutako Baimen gehigarrien eskaera onartzen baduzu, oraingo gunean edukiak hobeto iragazi ahal izango ditu. -You can set the default filtering mode from uBOL's options page. If you pick the Optimal or Complete mode as the default one, you will need to grant uBOL the permission to read and modify data on all websites. +Iragazki modu lehenetsia uBOL-en aukeren orrialdetik ezar dezakezu. Modu hoberena edo osoa aukeratzen baduzu lehenetsitako moduan, uBOL-i webgune guztietako datuak irakurtzeko eta aldatzeko baimena eman beharko diozu. -Keep in mind this is still a work in progress, with these end goals: +Gogoan izan oraindik lan bat dela, azken helburu hauekin: - Instalatzeko garaian, ez dago baimen zabalik. Erabiltzaileak esplizituki ematen ditu baimen zabalduak. -- Entirely declarative for reliability and CPU/memory efficiency. +- Erabat deklaratiboa fidagarritasunagatik eta CPU/memoriaren eraginkortasunagatik. diff --git a/platform/mv3/description/webstore.ur.txt b/platform/mv3/description/webstore.ur.txt index e03fa801ee7f0..0175f6167ba20 100644 --- a/platform/mv3/description/webstore.ur.txt +++ b/platform/mv3/description/webstore.ur.txt @@ -1,8 +1,8 @@ -uBO Lite (uBOL) is a *permission-less* MV3-based content blocker. +uBO Lite (uBOL) ایک *اجازت سے کم* MV3 پر مبنی مواد بلاکر ہے۔ -The default ruleset corresponds to uBlock Origin's default filterset: +ڈیفالٹ رولسیٹ uBlock Origin کے ڈیفالٹ فلٹر سیٹ سے مساوی ہے: -- uBlock Origin's built-in filter lists +- یو بلاک اوریجن کی بلٹ ان فلٹر لسٹ - EasyList - EasyPrivacy - Peter Lowe’s Ad and tracking server list diff --git a/platform/mv3/extension/_locales/bs/messages.json b/platform/mv3/extension/_locales/bs/messages.json index ab768f6b72003..d221c1b43c10b 100644 --- a/platform/mv3/extension/_locales/bs/messages.json +++ b/platform/mv3/extension/_locales/bs/messages.json @@ -156,7 +156,7 @@ "description": "Label for a checkbox in the options page" }, "showBlockedCountLabel": { - "message": "Show the number of blocked requests on the toolbar icon", + "message": "Prikažite broj blokiranih zahtjeva na ikoni trake sa alatkama", "description": "Label for a checkbox in the options page" } } diff --git a/platform/mv3/extension/_locales/ka/messages.json b/platform/mv3/extension/_locales/ka/messages.json index c728583fa9ef8..f31582f7a4a55 100644 --- a/platform/mv3/extension/_locales/ka/messages.json +++ b/platform/mv3/extension/_locales/ka/messages.json @@ -156,7 +156,7 @@ "description": "Label for a checkbox in the options page" }, "showBlockedCountLabel": { - "message": "Show the number of blocked requests on the toolbar icon", + "message": "აჩვენეთ დაბლოკილი მოთხოვნების რაოდენობა ხელსაწყოთა ზოლის ხატულაზე", "description": "Label for a checkbox in the options page" } } diff --git a/platform/mv3/extension/_locales/lt/messages.json b/platform/mv3/extension/_locales/lt/messages.json index 3f0bad82bbc7b..52c96b3ae207a 100644 --- a/platform/mv3/extension/_locales/lt/messages.json +++ b/platform/mv3/extension/_locales/lt/messages.json @@ -148,7 +148,7 @@ "description": "A short description for the editable field which lists trusted sites" }, "behaviorSectionLabel": { - "message": "Behavior", + "message": "Elgsena", "description": "The header text for the 'Behavior' section" }, "autoReloadLabel": { diff --git a/platform/mv3/extension/_locales/ms/messages.json b/platform/mv3/extension/_locales/ms/messages.json index 817a81f39af9d..c9147fe236ec9 100644 --- a/platform/mv3/extension/_locales/ms/messages.json +++ b/platform/mv3/extension/_locales/ms/messages.json @@ -156,7 +156,7 @@ "description": "Label for a checkbox in the options page" }, "showBlockedCountLabel": { - "message": "Show the number of blocked requests on the toolbar icon", + "message": "Tunjukkan bilangan permintaan yang disekat pada ikon bar alat", "description": "Label for a checkbox in the options page" } } diff --git a/platform/mv3/extension/_locales/tr/messages.json b/platform/mv3/extension/_locales/tr/messages.json index b4e410117aedf..a4bf19312686b 100644 --- a/platform/mv3/extension/_locales/tr/messages.json +++ b/platform/mv3/extension/_locales/tr/messages.json @@ -152,7 +152,7 @@ "description": "The header text for the 'Behavior' section" }, "autoReloadLabel": { - "message": "Filtreleme modu değiştirildiğinde sayfayı yenile", + "message": "Filtreleme modunu değiştirirken sayfayı otomatik olarak yenile", "description": "Label for a checkbox in the options page" }, "showBlockedCountLabel": { diff --git a/platform/mv3/extension/_locales/ur/messages.json b/platform/mv3/extension/_locales/ur/messages.json index 2705d513fd105..bb06300ced6cf 100644 --- a/platform/mv3/extension/_locales/ur/messages.json +++ b/platform/mv3/extension/_locales/ur/messages.json @@ -28,31 +28,31 @@ "description": "Link to privacy policy on GitHub (English)" }, "popupFilteringModeLabel": { - "message": "filtering mode", + "message": "فلٹرنگ موڈ", "description": "Label in the popup panel for the current filtering mode" }, "popupTipDashboard": { - "message": "Open the dashboard", + "message": "ڈیش بورڈ کھولیں۔", "description": "English: Click to open the dashboard" }, "popupMoreButton": { - "message": "More", + "message": "مزید", "description": "Label to be used to show popup panel sections" }, "popupLessButton": { - "message": "Less", + "message": "کم", "description": "Label to be used to hide popup panel sections" }, "3pGroupDefault": { - "message": "Default", + "message": "طے شدہ", "description": "Header for a ruleset section in 'Filter lists pane'" }, "3pGroupAds": { - "message": "Ads", + "message": "اشتہارات", "description": "Header for a ruleset section in 'Filter lists pane'" }, "3pGroupPrivacy": { - "message": "Privacy", + "message": "رازداری", "description": "Header for a ruleset section in 'Filter lists pane'" }, "3pGroupMalware": { @@ -60,31 +60,31 @@ "description": "Header for a ruleset section in 'Filter lists pane'" }, "3pGroupAnnoyances": { - "message": "Annoyances", + "message": "پریشانیاں", "description": "Header for a ruleset section in 'Filter lists pane'" }, "3pGroupMisc": { - "message": "Miscellaneous", + "message": "متفرق", "description": "Header for a ruleset section in 'Filter lists pane'" }, "3pGroupRegions": { - "message": "Regions, languages", + "message": "علاقے، زبانیں۔", "description": "Header for a ruleset section in 'Filter lists pane'" }, "aboutChangelog": { - "message": "Changelog", + "message": "چینج لاگ", "description": "" }, "aboutCode": { - "message": "Source code (GPLv3)", + "message": "ماخذ کوڈ (GPLv3)", "description": "English: Source code (GPLv3)" }, "aboutContributors": { - "message": "Contributors", + "message": "تعاون کرنے والے", "description": "English: Contributors" }, "aboutSourceCode": { - "message": "Source code", + "message": "سورس کوڈ", "description": "Link text to source code repo" }, "aboutTranslations": { @@ -92,39 +92,39 @@ "description": "Link text to translations repo" }, "aboutFilterLists": { - "message": "Filter lists", + "message": "فہرستوں کو فلٹر کریں۔", "description": "Link text to uBO's own filter lists repo" }, "aboutDependencies": { - "message": "External dependencies (GPLv3-compatible):", + "message": "بیرونی انحصار (GPLv3-مطابق):", "description": "Shown in the About pane" }, "firstRunSectionLabel": { - "message": "Welcome", + "message": "خوش آمدید", "description": "The header text for the welcome message section" }, "firstRunDescription": { - "message": "You have just installed uBO Lite. Here you can choose the default filtering mode to use on all websites.\n\nBy default, Basic mode is selected because it does not require the permission to read and modify data. If you trust uBO Lite, you can give it broad permission to read and modify data on all websites in order to enable more advanced filtering capabilities for all websites by default.", + "message": "آپ نے ابھی یو بی او لائٹ انسٹال کیا ہے۔ یہاں آپ تمام ویب سائٹس پر استعمال کرنے کے لیے ڈیفالٹ فلٹرنگ موڈ کا انتخاب کر سکتے ہیں۔\n\nپہلے سے طے شدہ طور پر، بنیادی موڈ کو منتخب کیا جاتا ہے کیونکہ اسے ڈیٹا کو پڑھنے اور اس میں ترمیم کرنے کی اجازت کی ضرورت نہیں ہوتی ہے۔ اگر آپ کو uBO Lite پر بھروسہ ہے، تو آپ اسے تمام ویب سائٹس پر ڈیٹا کو پڑھنے اور اس میں ترمیم کرنے کی وسیع اجازت دے سکتے ہیں تاکہ تمام ویب سائٹس کے لیے پہلے سے طے شدہ فلٹرنگ کی مزید جدید صلاحیتوں کو فعال کیا جا سکے۔", "description": "Descriptive text shown at first install time only " }, "defaultFilteringModeSectionLabel": { - "message": "Default filtering mode", + "message": "ڈیفالٹ فلٹرنگ موڈ", "description": "The header text for the default filtering mode section" }, "defaultFilteringModeDescription": { - "message": "The default filtering mode will be overridden by per-website filtering modes. You can adjust the filtering mode on any given website according to whichever mode works best on that website. Each mode has its advantages and disadvantages.", + "message": "ڈیفالٹ فلٹرنگ موڈ کو فی ویب سائٹ فلٹرنگ موڈز کے ذریعے اوور رائیڈ کر دیا جائے گا۔ آپ کسی بھی ویب سائٹ پر فلٹرنگ موڈ کو ایڈجسٹ کرسکتے ہیں اس کے مطابق جو بھی موڈ اس ویب سائٹ پر بہترین کام کرتا ہے۔ ہر موڈ کے اپنے فوائد اور نقصانات ہیں۔", "description": "This describes the default filtering mode setting" }, "filteringMode0Name": { - "message": "no filtering", + "message": "کوئی فلٹرنگ نہیں", "description": "Name of blocking mode 0" }, "filteringMode1Name": { - "message": "basic", + "message": "بنیادی", "description": "Name of blocking mode 1" }, "filteringMode2Name": { - "message": "optimal", + "message": "بہترین", "description": "Name of blocking mode 2" }, "filteringMode3Name": { @@ -132,31 +132,31 @@ "description": "Name of blocking mode 3" }, "basicFilteringModeDescription": { - "message": "Basic network filtering from selected filter lists.\n\nDoes not require permission to read and modify data on websites.", + "message": "منتخب فلٹر فہرستوں سے بنیادی نیٹ ورک فلٹرنگ۔\n\nویب سائٹس پر ڈیٹا کو پڑھنے اور اس میں ترمیم کرنے کے لیے اجازت کی ضرورت نہیں ہے۔", "description": "This describes the 'basic' filtering mode" }, "optimalFilteringModeDescription": { - "message": "Advanced network filtering plus specific extended filtering from selected filter lists.\n\nRequires broad permission to read and modify data on all websites.", + "message": "اعلی درجے کی نیٹ ورک فلٹرنگ کے علاوہ منتخب فلٹر فہرستوں سے مخصوص توسیعی فلٹرنگ۔\n\nتمام ویب سائٹس پر ڈیٹا کو پڑھنے اور اس میں ترمیم کرنے کے لیے وسیع اجازت درکار ہے۔", "description": "This describes the 'optimal' filtering mode" }, "completeFilteringModeDescription": { - "message": "Advanced network filtering plus specific and generic extended filtering from selected filter lists.\n\nRequires broad permission to read and modify data on all websites.\n\nGeneric extended filtering may cause higher webpage resources usage.", + "message": "اعلی درجے کی نیٹ ورک فلٹرنگ کے علاوہ منتخب فلٹر فہرستوں سے مخصوص اور عام توسیعی فلٹرنگ۔\n\nتمام ویب سائٹس پر ڈیٹا کو پڑھنے اور اس میں ترمیم کرنے کے لیے وسیع اجازت درکار ہے۔\n\nعام توسیع شدہ فلٹرنگ ویب پیج کے وسائل کے زیادہ استعمال کا سبب بن سکتی ہے۔", "description": "This describes the 'complete' filtering mode" }, "noFilteringModeDescription": { - "message": "List of hostnames for which no filtering will take place.", + "message": "میزبان ناموں کی فہرست جن کے لیے کوئی فلٹرنگ نہیں ہوگی۔", "description": "A short description for the editable field which lists trusted sites" }, "behaviorSectionLabel": { - "message": "Behavior", + "message": "رویہ", "description": "The header text for the 'Behavior' section" }, "autoReloadLabel": { - "message": "Automatically reload page when changing filtering mode", + "message": "فلٹرنگ موڈ تبدیل کرتے وقت صفحہ خودکار طور پر دوبارہ لوڈ کریں۔", "description": "Label for a checkbox in the options page" }, "showBlockedCountLabel": { - "message": "Show the number of blocked requests on the toolbar icon", + "message": "ٹول بار کے آئیکن پر بلاک شدہ درخواستوں کی تعداد دکھائیں۔", "description": "Label for a checkbox in the options page" } } diff --git a/src/_locales/br_FR/messages.json b/src/_locales/br_FR/messages.json index 2a27e9f019281..2722eb8de84fc 100644 --- a/src/_locales/br_FR/messages.json +++ b/src/_locales/br_FR/messages.json @@ -484,11 +484,11 @@ "description": "Filter lists section name" }, "3pGroupSocial": { - "message": "Social widgets", + "message": "Widgetoù sokial", "description": "Filter lists section name" }, "3pGroupCookies": { - "message": "Cookie notices", + "message": "Kemennadennoù diwar-benn an toupinoù", "description": "Filter lists section name" }, "3pGroupAnnoyances": { diff --git a/src/_locales/bs/messages.json b/src/_locales/bs/messages.json index d80ae9d7bb8ef..bd0810a6c6ab0 100644 --- a/src/_locales/bs/messages.json +++ b/src/_locales/bs/messages.json @@ -484,11 +484,11 @@ "description": "Filter lists section name" }, "3pGroupSocial": { - "message": "Social widgets", + "message": "Društveni widgeti", "description": "Filter lists section name" }, "3pGroupCookies": { - "message": "Cookie notices", + "message": "Obavještenja o kolačićima", "description": "Filter lists section name" }, "3pGroupAnnoyances": { @@ -540,11 +540,11 @@ "description": "Warning against copy-pasting filters from random sources" }, "1pEnableMyFiltersLabel": { - "message": "Enable my custom filters", + "message": "Omogući moje prilagođene filtere", "description": "Label for the checkbox use to enable/disable 'My filters' list" }, "1pTrustMyFiltersLabel": { - "message": "Allow custom filters requiring trust", + "message": "Dozvolite prilagođene filtere koji zahtijevaju povjerenje", "description": "Label for the checkbox use to trust the content of 'My filters' list" }, "1pImport": { @@ -1264,7 +1264,7 @@ "description": "Label for keyboard shortcut used to toggle cosmetic filtering" }, "toggleJavascript": { - "message": "Toggle JavaScript", + "message": "Uključi JavaScript", "description": "Label for keyboard shortcut used to toggle no-scripting switch" }, "relaxBlockingMode": { diff --git a/src/_locales/lt/messages.json b/src/_locales/lt/messages.json index a44db57fe1420..b7431aceaa93c 100644 --- a/src/_locales/lt/messages.json +++ b/src/_locales/lt/messages.json @@ -940,7 +940,7 @@ "description": "Third paragraph of 'Filter issues' section in Support pane" }, "supportS4H": { - "message": "Bug report", + "message": "Klaidos pranešimas", "description": "Header of 'Bug report' section in Support pane" }, "supportS4P1": { diff --git a/src/_locales/ms/messages.json b/src/_locales/ms/messages.json index ed5b060b45f94..5247e837f1f1c 100644 --- a/src/_locales/ms/messages.json +++ b/src/_locales/ms/messages.json @@ -8,7 +8,7 @@ "description": "this will be in the Chrome web store: must be 132 characters or less" }, "dashboardName": { - "message": "uBlock₀ — Papanmuka", + "message": "uBlock₀ — Papan pemuka", "description": "English: uBlock₀ — Dashboard" }, "dashboardUnsavedWarning": { @@ -124,7 +124,7 @@ "description": "English: Enter element picker mode" }, "popupTipLog": { - "message": "Membuka catatan", + "message": "Buka pengelog", "description": "Tooltip used for the logger icon in the panel" }, "popupTipReport": { @@ -188,7 +188,7 @@ "description": "Tooltip for the no-scripting per-site switch" }, "popupNoPopups_v2": { - "message": "Tetingkap pop timbul", + "message": "Tingkap timbul", "description": "Caption for the no-popups per-site switch" }, "popupNoLargeMedia_v2": { @@ -200,7 +200,7 @@ "description": "Caption for the no-cosmetic-filtering per-site switch" }, "popupNoRemoteFonts_v2": { - "message": "Fon terpencil", + "message": "Font tersendiri", "description": "Caption for the no-remote-fonts per-site switch" }, "popupNoScripting_v2": { @@ -484,11 +484,11 @@ "description": "Filter lists section name" }, "3pGroupSocial": { - "message": "Social widgets", + "message": "Widget sosial", "description": "Filter lists section name" }, "3pGroupCookies": { - "message": "Cookie notices", + "message": "Notis kuki", "description": "Filter lists section name" }, "3pGroupAnnoyances": { @@ -540,11 +540,11 @@ "description": "Warning against copy-pasting filters from random sources" }, "1pEnableMyFiltersLabel": { - "message": "Enable my custom filters", + "message": "Dayakan penapis tersuai saya", "description": "Label for the checkbox use to enable/disable 'My filters' list" }, "1pTrustMyFiltersLabel": { - "message": "Allow custom filters requiring trust", + "message": "Benarkan penapis tersuai yang memerlukan kepercayaan", "description": "Label for the checkbox use to trust the content of 'My filters' list" }, "1pImport": { @@ -1264,7 +1264,7 @@ "description": "Label for keyboard shortcut used to toggle cosmetic filtering" }, "toggleJavascript": { - "message": "Toggle JavaScript", + "message": "Togol JavaScript", "description": "Label for keyboard shortcut used to toggle no-scripting switch" }, "relaxBlockingMode": { diff --git a/src/_locales/sv/messages.json b/src/_locales/sv/messages.json index 37a8070e4e476..3abea889d2536 100644 --- a/src/_locales/sv/messages.json +++ b/src/_locales/sv/messages.json @@ -928,7 +928,7 @@ "description": "Header of 'Filter issues' section in Support pane" }, "supportS3P1": { - "message": "Rapportera filterproblem med specifika webbplatser till uBlockOrigin/uAssets problemhanteringssystemet. Kräver ett GitHub-konto.", + "message": "Rapportera filterproblem med specifika webbplatser till problemhanteringssystemet uBlockOrigin/uAssets. Kräver ett GitHub-konto.", "description": "First paragraph of 'Filter issues' section in Support pane" }, "supportS3P2": { @@ -944,7 +944,7 @@ "description": "Header of 'Bug report' section in Support pane" }, "supportS4P1": { - "message": "Rapportera problem med själva uBlock Origin till uBlockOrigin/uBlock-issue ärendehanteringssystemet. Kräver ett GitHub-konto.", + "message": "Rapportera problem med själva uBlock Origin till problemhanteringssystemet uBlockOrigin/uBlock-issue. Kräver ett GitHub-konto.", "description": "First paragraph of 'Bug report' section in Support pane" }, "supportS5H": { diff --git a/src/_locales/tr/messages.json b/src/_locales/tr/messages.json index 31f6aa55ca6ee..41ac3b3d47b5a 100644 --- a/src/_locales/tr/messages.json +++ b/src/_locales/tr/messages.json @@ -484,7 +484,7 @@ "description": "Filter lists section name" }, "3pGroupSocial": { - "message": "Sosyal gereçler", + "message": "Sosyal medya tuşları", "description": "Filter lists section name" }, "3pGroupCookies": { @@ -548,11 +548,11 @@ "description": "Label for the checkbox use to trust the content of 'My filters' list" }, "1pImport": { - "message": "İçe aktar ve ekle", + "message": "İçe aktar ve ekle…", "description": "Button in the 'My filters' pane" }, "1pExport": { - "message": "Dışa aktar", + "message": "Dışa aktar…", "description": "Button in the 'My filters' pane" }, "1pExportFilename": { @@ -636,7 +636,7 @@ "description": "Button in the 'Trusted sites' pane" }, "whitelistExport": { - "message": "Dışa aktar", + "message": "Dışa aktar…", "description": "Button in the 'Trusted sites' pane" }, "whitelistExportFilename": { diff --git a/src/_locales/zh_TW/messages.json b/src/_locales/zh_TW/messages.json index f07258b6a40cb..7c3bc17a10ce8 100644 --- a/src/_locales/zh_TW/messages.json +++ b/src/_locales/zh_TW/messages.json @@ -4,7 +4,7 @@ "description": "extension name." }, "extShortDesc": { - "message": "終於有套使用不多的 CPU 及記憶體資源的高效率阻擋器。", + "message": "終於,有款僅使用少量 CPU 及記憶體的高效率阻擋器。", "description": "this will be in the Chrome web store: must be 132 characters or less" }, "dashboardName": { @@ -12,7 +12,7 @@ "description": "English: uBlock₀ — Dashboard" }, "dashboardUnsavedWarning": { - "message": "警告!您有尚未儲存的變更", + "message": "警告!變更尚未儲存。", "description": "A warning in the dashboard when navigating away from unsaved changes" }, "dashboardUnsavedWarningStay": { @@ -40,7 +40,7 @@ "description": "appears as tab name in dashboard" }, "whitelistPageName": { - "message": "受信任網站", + "message": "白名單", "description": "appears as tab name in dashboard" }, "shortcutsPageName": { @@ -140,7 +140,7 @@ "description": "Tooltip for the no-popups per-site switch" }, "popupTipNoPopups2": { - "message": "點擊後將不再封鎖此網站的所有彈出式廣告", + "message": "點擊以停止阻擋此網站的所有彈出式視窗", "description": "Tooltip for the no-popups per-site switch" }, "popupTipNoLargeMedia": { @@ -176,7 +176,7 @@ "description": "Tooltip for the no-remote-fonts per-site switch" }, "popupTipNoRemoteFonts2": { - "message": "點擊以停止封鎖此網站的遠端字型", + "message": "點擊以解除封鎖此網站的遠端字體", "description": "Tooltip for the no-remote-fonts per-site switch" }, "popupTipNoScripting1": { @@ -276,7 +276,7 @@ "description": "Example of use: Version 1.26.4" }, "popup3pScriptFilter": { - "message": "腳本", + "message": "指令碼", "description": "Appears as an option to filter out firewall rows" }, "popup3pFrameFilter": { @@ -324,7 +324,7 @@ "description": "English: Show the number of blocked requests on the icon" }, "settingsTooltipsPrompt": { - "message": "關閉提示文字功能", + "message": "停用提示框", "description": "A checkbox in the Settings pane" }, "settingsContextMenuPrompt": { @@ -384,7 +384,7 @@ "description": "" }, "settingsNoRemoteFontsPrompt": { - "message": "封鎖遠端字型", + "message": "封鎖遠端字體", "description": "" }, "settingsNoScriptingPrompt": { @@ -444,7 +444,7 @@ "description": "English: Parse and enforce Adblock+ element hiding filters." }, "3pParseAllABPHideFiltersInfo": { - "message": "「元素隱藏過濾規則」用於隱藏網頁中礙眼,且不能被以網路請求為基礎之過濾引擎所阻擋的元素。", + "message": "「網頁元素過濾規則」用來隱藏網頁中被認為礙眼,且不能被以網路請求為基礎之過濾引擎所阻擋的元素。", "description": "Describes the purpose of the 'Parse and enforce cosmetic filters' feature." }, "3pIgnoreGenericCosmeticFilters": { @@ -452,7 +452,7 @@ "description": "This will cause uBO to ignore all generic cosmetic filters." }, "3pIgnoreGenericCosmeticFiltersInfo": { - "message": "「通用元素隱藏過濾規則」是會套用在所有網站上的元素隱藏過濾規則。啟用此選項會消除網頁處理此類規則時增加的額外記憶體與 CPU 使用量。\n\n建議在較低效能的裝置上啟用此選項。", + "message": "「通用元素隱藏過濾規則」是會套用在所有網站上的網頁元素過濾規則。啟用此選項會消除網頁處理此類規則時增加的額外記憶體與 CPU 使用量。\n\n建議在擁有較低效能的裝置上啟用此選項。", "description": "Describes the purpose of the 'Ignore generic cosmetic filters' feature." }, "3pSuspendUntilListsAreLoaded": { @@ -992,7 +992,7 @@ "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option2": { - "message": "含有覆蓋物或其他滋擾", + "message": "含有覆蓋物或其他滋擾物", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option3": { @@ -1000,7 +1000,7 @@ "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option4": { - "message": "有私穩相關問題", + "message": "有隱私權相關問題", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option5": { @@ -1008,7 +1008,7 @@ "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option6": { - "message": "會開啟不想要的分頁或視窗", + "message": "會開啟不需要的分頁或視窗", "description": "An entry in the widget used to select the type of issue" }, "supportS6Checkbox1": { @@ -1140,7 +1140,7 @@ "description": "Firefox-specific: appears as 'uBlock₀ (off)'" }, "docblockedTitle": { - "message": "頁面已阻擋", + "message": "已封鎖頁面", "description": "Used as a title for the document-blocked page" }, "docblockedPrompt1": { @@ -1208,7 +1208,7 @@ "description": "used as a prompt for the user to provide a custom device name" }, "advancedSettingsWarning": { - "message": "警告!修改進階設定時請自負風險。", + "message": "警告!修改進階設定時,請自負風險。", "description": "A warning to users at the top of 'Advanced settings' page" }, "genericSubmit": { @@ -1244,7 +1244,7 @@ "description": "A context menu entry, to view the source code of the target resource" }, "shortcutCapturePlaceholder": { - "message": "輸入快捷鍵", + "message": "鍵入快速鍵", "description": "Placeholder string for input field used to capture a keyboard shortcut" }, "genericMergeViewScrollLock": { @@ -1296,7 +1296,7 @@ "description": "Summary of number of errors as reported by the linter " }, "unprocessedRequestTooltip": { - "message": "無法在瀏覽器啟動的時候正確過濾頁面。請重新載入來確保過濾正確。", + "message": "無法在瀏覽器啟動時正確過濾頁面。請重新載入以確保過濾正常。", "description": "A warning which will appear in the popup panel if needed" }, "dummy": { From 0cc8b7864f9153339d9cfa6beaa21ef00e0544fc Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Thu, 1 Aug 2024 10:37:22 -0400 Subject: [PATCH 0100/1099] Ignore transient py stuff --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index dee00e98d3b96..1f064b847d71f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ *.bak *.pem +__pycache__/ /dist/build/ /tmp/ From 9562b19a935018dbe54cf7727205ffadecc435aa Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Thu, 1 Aug 2024 11:24:42 -0400 Subject: [PATCH 0101/1099] Fall back to "Basic" when removing `all-urls` from "No filtering" Related discussion: https://github.com/uBlockOrigin/uBOL-home/issues/156#issuecomment-2254576670 --- platform/mv3/extension/js/mode-manager.js | 37 ++++++++++++----------- 1 file changed, 19 insertions(+), 18 deletions(-) diff --git a/platform/mv3/extension/js/mode-manager.js b/platform/mv3/extension/js/mode-manager.js index e75dbeef58339..c6e7ddba651c3 100644 --- a/platform/mv3/extension/js/mode-manager.js +++ b/platform/mv3/extension/js/mode-manager.js @@ -19,18 +19,17 @@ Home: https://github.com/gorhill/uBlock */ -/* jshint esversion:11 */ - -'use strict'; - -/******************************************************************************/ +import { + TRUSTED_DIRECTIVE_BASE_RULE_ID, + getDynamicRules, +} from './ruleset-manager.js'; import { + adminRead, browser, dnr, - localRead, localWrite, localRemove, + localRead, localRemove, localWrite, sessionRead, sessionWrite, - adminRead, } from './ext.js'; import { @@ -40,11 +39,6 @@ import { toBroaderHostname, } from './utils.js'; -import { - TRUSTED_DIRECTIVE_BASE_RULE_ID, - getDynamicRules -} from './ruleset-manager.js'; - /******************************************************************************/ // 0: no filtering @@ -288,13 +282,15 @@ async function writeFilteringModeDetails(afterDetails) { async function filteringModesToDNR(modes) { const dynamicRuleMap = await getDynamicRules(); - const presentRule = dynamicRuleMap.get(TRUSTED_DIRECTIVE_BASE_RULE_ID+0); - const presentNone = new Set( - presentRule && presentRule.condition.requestDomains - ); - if ( eqSets(presentNone, modes.none) ) { return; } + const trustedRule = dynamicRuleMap.get(TRUSTED_DIRECTIVE_BASE_RULE_ID+0); + const trustedDomainSet = new Set(trustedRule?.condition.requestDomains); + if ( trustedDomainSet.size !== 0 ) { + if ( eqSets(trustedDomainSet, modes.none) ) { return; } + } else if ( trustedRule !== undefined ) { + if ( modes.none.has('all-urls') ) { return; } + } const removeRuleIds = []; - if ( presentRule !== undefined ) { + if ( trustedRule !== undefined ) { removeRuleIds.push(TRUSTED_DIRECTIVE_BASE_RULE_ID+0); removeRuleIds.push(TRUSTED_DIRECTIVE_BASE_RULE_ID+1); dynamicRuleMap.delete(TRUSTED_DIRECTIVE_BASE_RULE_ID+0); @@ -394,6 +390,11 @@ export async function setTrustedSites(hostnames) { const { none } = filteringModes; const hnSet = new Set(hostnames); let modified = false; + // Set default mode to Basic when removing No-filtering as default mode + if ( none.has('all-urls') && hnSet.has('all-urls') === false ) { + applyFilteringMode(filteringModes, 'all-urls', MODE_BASIC); + modified = true; + } for ( const hn of none ) { if ( hnSet.has(hn) ) { hnSet.delete(hn); From cc50accd4cd77f58821a2ac040b6ce8bb801663e Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Fri, 2 Aug 2024 10:58:03 -0400 Subject: [PATCH 0102/1099] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 79ffa966e181f..c068d9816438d 100644 --- a/README.md +++ b/README.md @@ -32,7 +32,7 @@ uBlock Origin (uBO)

Get uBlock Origin for Chromium
-Important: Will uBO automatically transition to uBO Lite in the Chrome Web Store? (tldr: no) +Important: About Chrome Web Store's "This extension may soon no longer be supported"

*** From 2d41dc89e5613a1ee80686155814c17f8d575102 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Fri, 2 Aug 2024 11:12:39 -0400 Subject: [PATCH 0103/1099] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index c068d9816438d..61a18f54a64dc 100644 --- a/README.md +++ b/README.md @@ -32,7 +32,7 @@ uBlock Origin (uBO)

Get uBlock Origin for Chromium
-Important: About Chrome Web Store's "This extension may soon no longer be supported" +IMPORTANT: About Chrome Web Store's "This extension may soon no longer be supported"

*** From 23a113715dab11936249ddf61151587d144030ac Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Fri, 2 Aug 2024 11:20:34 -0400 Subject: [PATCH 0104/1099] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 61a18f54a64dc..d91e592142693 100644 --- a/README.md +++ b/README.md @@ -32,7 +32,7 @@ uBlock Origin (uBO)

Get uBlock Origin for Chromium
-IMPORTANT: About Chrome Web Store's "This extension may soon no longer be supported" +IMPORTANT: About Google Chrome's "This extension may soon no longer be supported"

*** From d42329a3a348b2e4d5475012373e73493e73d7b1 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sat, 3 Aug 2024 12:03:22 -0400 Subject: [PATCH 0105/1099] Rephrase more accurately Related issue: https://github.com/uBlockOrigin/uBOL-home/issues/160 --- platform/mv3/description/webstore.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform/mv3/description/webstore.txt b/platform/mv3/description/webstore.txt index e03fa801ee7f0..7669a5939f98c 100644 --- a/platform/mv3/description/webstore.txt +++ b/platform/mv3/description/webstore.txt @@ -7,7 +7,7 @@ The default ruleset corresponds to uBlock Origin's default filterset: - EasyPrivacy - Peter Lowe’s Ad and tracking server list -You can add more rulesets by visiting the options page -- click the _Cogs_ icon in the popup panel. +You can enable more rulesets by visiting the options page -- click the _Cogs_ icon in the popup panel. uBOL is entirely declarative, meaning there is no need for a permanent uBOL process for the filtering to occur, and CSS/JS injection-based content filtering is performed reliably by the browser itself rather than by the extension. This means that uBOL itself does not consume CPU/memory resources while content blocking is ongoing -- uBOL's service worker process is required _only_ when you interact with the popup panel or the option pages. From 4f0d1301ab48e62afb3424609834c6353a0a5eee Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sat, 3 Aug 2024 20:09:21 -0400 Subject: [PATCH 0106/1099] Improve `trusted-replace-node-text` scriptlet Related discussion: https://github.com/brave/adblock-resources/pull/194 --- assets/resources/scriptlets.js | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/assets/resources/scriptlets.js b/assets/resources/scriptlets.js index 6e9dcd60ea4b8..c95412867332b 100644 --- a/assets/resources/scriptlets.js +++ b/assets/resources/scriptlets.js @@ -730,6 +730,18 @@ function replaceNodeTextFn( safe.uboLog(logPrefix, 'Quitting'); } }; + const textContentFactory = (( ) => { + const out = { createScript: s => s }; + const { trustedTypes: tt } = self; + if ( tt instanceof Object ) { + if ( typeof tt.getPropertyType === 'function' ) { + if ( tt.getPropertyType('script', 'textContent') === 'TrustedScript' ) { + return tt.createPolicy('uBO', out); + } + } + } + return out; + })(); let sedCount = extraArgs.sedCount || 0; const handleNode = node => { const before = node.textContent; @@ -747,7 +759,9 @@ function replaceNodeTextFn( const after = pattern !== '' ? before.replace(rePattern, replacement) : replacement; - node.textContent = after; + node.textContent = node.nodeName === 'SCRIPT' + ? textContentFactory.createScript(after) + : after; if ( safe.logLevel > 1 ) { safe.uboLog(logPrefix, `Text before:\n${before.trim()}`); } From de0a35e7cc4ba38a2167a2b0c57b2dc2664f9ce9 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sat, 3 Aug 2024 20:11:51 -0400 Subject: [PATCH 0107/1099] Update changelog --- CHANGELOG.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 44692a4e23e6f..1dddae8d0af72 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,11 @@ +- [Improve `trusted-replace-node-text` scriptlet](https://github.com/gorhill/uBlock/commit/4f0d1301ab) + +---------- + +# 1.59.0 + +## Fixes / changes + - [Improve `href-sanitizer` scriptlet](https://github.com/gorhill/uBlock/commit/84be9cde6d) - [Improve `trusted-replace-node-text` scriptlet](https://github.com/gorhill/uBlock/commit/8afd9e233d) - [Improve `set-constant` scriptlet](https://github.com/gorhill/uBlock/commit/77feb25c4d) From a577d5ff50de8bffc1c1847f6640c04f71a6b249 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sat, 3 Aug 2024 20:12:19 -0400 Subject: [PATCH 0108/1099] New revision for dev build --- dist/version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dist/version b/dist/version index 43eb9bc68ddfd..010d58db08ef5 100644 --- a/dist/version +++ b/dist/version @@ -1 +1 @@ -1.59.0 \ No newline at end of file +1.59.1.0 \ No newline at end of file From fea92ac110dac632d350fec4500c084ecc7848bf Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sat, 3 Aug 2024 20:21:23 -0400 Subject: [PATCH 0109/1099] Make Firefox dev build auto-update --- dist/firefox/updates.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dist/firefox/updates.json b/dist/firefox/updates.json index 6b426df1439eb..c6344aa21d8ed 100644 --- a/dist/firefox/updates.json +++ b/dist/firefox/updates.json @@ -3,9 +3,9 @@ "uBlock0@raymondhill.net": { "updates": [ { - "version": "1.58.1.111", + "version": "1.59.1.0", "browser_specific_settings": { "gecko": { "strict_min_version": "78.0" } }, - "update_link": "https://github.com/gorhill/uBlock/releases/download/1.58.1rc11/uBlock0_1.58.1rc11.firefox.signed.xpi" + "update_link": "https://github.com/gorhill/uBlock/releases/download/1.59.1b0/uBlock0_1.59.1b0.firefox.signed.xpi" } ] } From 3668445679b0996d5333d7ba7f5b4523ec0a9d95 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sun, 4 Aug 2024 00:15:40 -0400 Subject: [PATCH 0110/1099] Use random trusted-types policy name Related commit: https://github.com/gorhill/uBlock/commit/4f0d1301ab48e62afb3424609834c6353a0a5eee --- assets/resources/scriptlets.js | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/assets/resources/scriptlets.js b/assets/resources/scriptlets.js index c95412867332b..48f4958dd4768 100644 --- a/assets/resources/scriptlets.js +++ b/assets/resources/scriptlets.js @@ -203,17 +203,28 @@ function safeSelf() { /******************************************************************************/ builtinScriptlets.push({ - name: 'get-exception-token.fn', - fn: getExceptionToken, + name: 'get-random-token.fn', + fn: getRandomToken, dependencies: [ 'safe-self.fn', ], }); -function getExceptionToken() { +function getRandomToken() { const safe = safeSelf(); - const token = - safe.String_fromCharCode(Date.now() % 26 + 97) + + return safe.String_fromCharCode(Date.now() % 26 + 97) + safe.Math_floor(safe.Math_random() * 982451653 + 982451653).toString(36); +} +/******************************************************************************/ + +builtinScriptlets.push({ + name: 'get-exception-token.fn', + fn: getExceptionToken, + dependencies: [ + 'get-random-token.fn', + ], +}); +function getExceptionToken() { + const token = getRandomToken(); const oe = self.onerror; self.onerror = function(msg, ...args) { if ( typeof msg === 'string' && msg.includes(token) ) { return true; } @@ -701,6 +712,7 @@ builtinScriptlets.push({ name: 'replace-node-text.fn', fn: replaceNodeTextFn, dependencies: [ + 'get-random-token.fn', 'run-at.fn', 'safe-self.fn', ], @@ -736,7 +748,7 @@ function replaceNodeTextFn( if ( tt instanceof Object ) { if ( typeof tt.getPropertyType === 'function' ) { if ( tt.getPropertyType('script', 'textContent') === 'TrustedScript' ) { - return tt.createPolicy('uBO', out); + return tt.createPolicy(getRandomToken(), out); } } } From 417dab538c99adbf567279043d733fdad1465968 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sun, 4 Aug 2024 11:58:09 -0400 Subject: [PATCH 0111/1099] [mv3] Fix generating `allowAllRequests` rule when default mode is no-filtering --- platform/mv3/extension/js/mode-manager.js | 76 +++++++++++++++-------- 1 file changed, 50 insertions(+), 26 deletions(-) diff --git a/platform/mv3/extension/js/mode-manager.js b/platform/mv3/extension/js/mode-manager.js index c6e7ddba651c3..c3b9a01bb877b 100644 --- a/platform/mv3/extension/js/mode-manager.js +++ b/platform/mv3/extension/js/mode-manager.js @@ -74,6 +74,7 @@ const pruneHostnameFromSet = (hostname, hnSet) => { /******************************************************************************/ const eqSets = (setBefore, setAfter) => { + if ( setBefore.size !== setAfter.size ) { return false; } for ( const hn of setAfter ) { if ( setBefore.has(hn) === false ) { return false; } } @@ -283,23 +284,48 @@ async function writeFilteringModeDetails(afterDetails) { async function filteringModesToDNR(modes) { const dynamicRuleMap = await getDynamicRules(); const trustedRule = dynamicRuleMap.get(TRUSTED_DIRECTIVE_BASE_RULE_ID+0); - const trustedDomainSet = new Set(trustedRule?.condition.requestDomains); - if ( trustedDomainSet.size !== 0 ) { - if ( eqSets(trustedDomainSet, modes.none) ) { return; } - } else if ( trustedRule !== undefined ) { - if ( modes.none.has('all-urls') ) { return; } + const beforeRequestDomainSet = new Set(trustedRule?.condition.requestDomains); + const beforeExcludedRrequestDomainSet = new Set(trustedRule?.condition.excludedRequestDomains); + if ( trustedRule !== undefined && beforeRequestDomainSet.size === 0 ) { + beforeRequestDomainSet.add('all-urls'); + } else { + beforeExcludedRrequestDomainSet.add('all-urls'); } - const removeRuleIds = []; - if ( trustedRule !== undefined ) { - removeRuleIds.push(TRUSTED_DIRECTIVE_BASE_RULE_ID+0); - removeRuleIds.push(TRUSTED_DIRECTIVE_BASE_RULE_ID+1); - dynamicRuleMap.delete(TRUSTED_DIRECTIVE_BASE_RULE_ID+0); - dynamicRuleMap.delete(TRUSTED_DIRECTIVE_BASE_RULE_ID+1); + + const noneHostnames = new Set([ ...modes.none ]); + const notNoneHostnames = new Set([ ...modes.basic, ...modes.optimal, ...modes.complete ]); + let afterRequestDomainSet = new Set(); + let afterExcludedRequestDomainSet = new Set(); + if ( noneHostnames.has('all-urls') ) { + afterRequestDomainSet = new Set([ 'all-urls' ]); + afterExcludedRequestDomainSet = notNoneHostnames; + } else { + afterRequestDomainSet = noneHostnames; + afterExcludedRequestDomainSet = new Set([ 'all-urls' ]); } + + if ( eqSets(beforeRequestDomainSet, afterRequestDomainSet) ) { + if ( eqSets(beforeExcludedRrequestDomainSet, afterExcludedRequestDomainSet) ) { + return; + } + } + + const removeRuleIds = [ + TRUSTED_DIRECTIVE_BASE_RULE_ID+0, + TRUSTED_DIRECTIVE_BASE_RULE_ID+1, + ]; + dynamicRuleMap.delete(TRUSTED_DIRECTIVE_BASE_RULE_ID+0); + dynamicRuleMap.delete(TRUSTED_DIRECTIVE_BASE_RULE_ID+1); + + const allowEverywhere = afterRequestDomainSet.delete('all-urls'); + afterExcludedRequestDomainSet.delete('all-urls'); + const addRules = []; - const noneHostnames = [ ...modes.none ]; - const notNoneHostnames = [ ...modes.basic, ...modes.optimal, ...modes.complete ]; - if ( noneHostnames.length !== 0 ) { + if ( + allowEverywhere || + afterRequestDomainSet.size !== 0 || + afterExcludedRequestDomainSet.size !== 0 + ) { const rule0 = { id: TRUSTED_DIRECTIVE_BASE_RULE_ID+0, action: { type: 'allowAllRequests' }, @@ -308,10 +334,10 @@ async function filteringModesToDNR(modes) { }, priority: 100, }; - if ( modes.none.has('all-urls') === false ) { - rule0.condition.requestDomains = noneHostnames.slice(); - } else if ( notNoneHostnames.length !== 0 ) { - rule0.condition.excludedRequestDomains = notNoneHostnames.slice(); + if ( afterRequestDomainSet.size !== 0 ) { + rule0.condition.requestDomains = Array.from(afterRequestDomainSet); + } else if ( afterExcludedRequestDomainSet.size !== 0 ) { + rule0.condition.excludedRequestDomains = Array.from(afterExcludedRequestDomainSet); } addRules.push(rule0); dynamicRuleMap.set(TRUSTED_DIRECTIVE_BASE_RULE_ID+0, rule0); @@ -324,21 +350,19 @@ async function filteringModesToDNR(modes) { }, priority: 100, }; - if ( modes.none.has('all-urls') === false ) { - rule1.condition.initiatorDomains = noneHostnames.slice(); - } else if ( notNoneHostnames.length !== 0 ) { - rule1.condition.excludedInitiatorDomains = notNoneHostnames.slice(); + if ( rule0.condition.requestDomains ) { + rule1.condition.initiatorDomains = rule0.condition.requestDomains.slice(); + } else if ( rule0.condition.excludedRequestDomains ) { + rule1.condition.excludedInitiatorDomains = rule0.condition.excludedRequestDomains.slice(); } addRules.push(rule1); dynamicRuleMap.set(TRUSTED_DIRECTIVE_BASE_RULE_ID+1, rule1); } - const updateOptions = {}; + + const updateOptions = { removeRuleIds }; if ( addRules.length ) { updateOptions.addRules = addRules; } - if ( removeRuleIds.length ) { - updateOptions.removeRuleIds = removeRuleIds; - } await dnr.updateDynamicRules(updateOptions); } From fb037e97d0878db2f13eef0702a893d1420e7264 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Tue, 6 Aug 2024 14:47:03 -0400 Subject: [PATCH 0112/1099] [mv3] Improve `remove-attr.js` scriptlet Related issue: https://github.com/uBlockOrigin/uBOL-home/issues/166 --- assets/resources/scriptlets.js | 31 ++++++++++++++++++++++-------- platform/mv3/extension/js/popup.js | 4 +++- 2 files changed, 26 insertions(+), 9 deletions(-) diff --git a/assets/resources/scriptlets.js b/assets/resources/scriptlets.js index 48f4958dd4768..866b306678d1e 100644 --- a/assets/resources/scriptlets.js +++ b/assets/resources/scriptlets.js @@ -163,6 +163,12 @@ function safeSelf() { } return self.requestAnimationFrame(fn); }, + offIdle(id) { + if ( self.requestIdleCallback ) { + return self.cancelIdleCallback(id); + } + return self.cancelAnimationFrame(id); + } }; scriptletGlobals.safeSelf = safe; if ( scriptletGlobals.bcSecret === undefined ) { return safe; } @@ -258,7 +264,7 @@ builtinScriptlets.push({ function runAt(fn, when) { const intFromReadyState = state => { const targets = { - 'loading': 1, + 'loading': 1, 'asap': 1, 'interactive': 2, 'end': 2, '2': 2, 'complete': 3, 'idle': 3, '3': 3, }; @@ -2266,9 +2272,20 @@ function removeAttr( if ( safe.logLevel > 1 ) { safe.uboLog(logPrefix, `Target selector:\n\t${selector}`); } - let timer; + const asap = /\basap\b/.test(behavior); + let timerId; + const rmattrAsync = ( ) => { + if ( timerId !== undefined ) { return; } + timerId = safe.onIdle(( ) => { + timerId = undefined; + rmattr(); + }, { timeout: 17 }); + }; const rmattr = ( ) => { - timer = undefined; + if ( timerId !== undefined ) { + safe.offIdle(timerId); + timerId = undefined; + } try { const nodes = document.querySelectorAll(selector); for ( const node of nodes ) { @@ -2282,7 +2299,7 @@ function removeAttr( } }; const mutationHandler = mutations => { - if ( timer !== undefined ) { return; } + if ( timerId !== undefined ) { return; } let skip = true; for ( let i = 0; i < mutations.length && skip; i++ ) { const { type, addedNodes, removedNodes } = mutations[i]; @@ -2295,7 +2312,7 @@ function removeAttr( } } if ( skip ) { return; } - timer = safe.onIdle(rmattr, { timeout: 67 }); + asap ? rmattr() : rmattrAsync(); }; const start = ( ) => { rmattr(); @@ -2308,9 +2325,7 @@ function removeAttr( subtree: true, }); }; - runAt(( ) => { - start(); - }, /\bcomplete\b/.test(behavior) ? 'idle' : 'interactive'); + runAt(( ) => { start(); }, behavior.split(/\s+/)); } /******************************************************************************/ diff --git a/platform/mv3/extension/js/popup.js b/platform/mv3/extension/js/popup.js index 712dbbdf2ed3b..123c20bc73fa1 100644 --- a/platform/mv3/extension/js/popup.js +++ b/platform/mv3/extension/js/popup.js @@ -91,7 +91,9 @@ async function commitFilteringMode() { setFilteringMode(actualLevel); } if ( actualLevel !== beforeLevel && popupPanelData.autoReload ) { - browser.tabs.reload(currentTab.id); + self.setTimeout(( ) => { + browser.tabs.reload(currentTab.id); + }, 437); } } From ae6b53479d29b3915aff3d3b4fd3fd1174a4f472 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Tue, 6 Aug 2024 14:55:30 -0400 Subject: [PATCH 0113/1099] [mv3] Minor change to debug output --- platform/mv3/extension/js/debug.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform/mv3/extension/js/debug.js b/platform/mv3/extension/js/debug.js index a5f1a852f9e62..0674cd9446b88 100644 --- a/platform/mv3/extension/js/debug.js +++ b/platform/mv3/extension/js/debug.js @@ -87,7 +87,7 @@ export const getMatchedRules = (( ) => { } } const ruleId = rule.id; - rule.id = `${rulesetId}-${ruleId}`; + rule.id = `${rulesetId}/${ruleId}`; ruleset.set(ruleId, rule); } rulesets.set(rulesetId, ruleset); From 277977aa8a3342c8cb7cb88d584d26a11df83a12 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Tue, 6 Aug 2024 15:00:59 -0400 Subject: [PATCH 0114/1099] [mv3] Remove useless arguments --- platform/mv3/extension/js/scripting/css-procedural.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/platform/mv3/extension/js/scripting/css-procedural.js b/platform/mv3/extension/js/scripting/css-procedural.js index 9f6f039d770ab..58fd4d6d7efe6 100644 --- a/platform/mv3/extension/js/scripting/css-procedural.js +++ b/platform/mv3/extension/js/scripting/css-procedural.js @@ -243,7 +243,7 @@ class PSelectorMatchesMediaTask extends PSelectorTask { if ( this.mql.media === 'not all' ) { return; } this.mql.addEventListener('change', ( ) => { if ( proceduralFilterer instanceof Object === false ) { return; } - proceduralFilterer.onDOMChanged([ null ]); + proceduralFilterer.onDOMChanged(); }); } transpose(node, output) { @@ -487,7 +487,7 @@ class PSelectorWatchAttrs extends PSelectorTask { // TODO: Is it worth trying to re-apply only the current selector? handler() { if ( proceduralFilterer instanceof Object ) { - proceduralFilterer.onDOMChanged([ null ]); + proceduralFilterer.onDOMChanged(); } } transpose(node, output) { From 4697f18d0ec2c3840a4ca83d4faf217c5b8a627e Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Tue, 6 Aug 2024 16:50:07 -0400 Subject: [PATCH 0115/1099] Update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1dddae8d0af72..24b7c6b9a9d15 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,4 @@ +- [Improve `remove-attr.js` scriptlet](https://github.com/gorhill/uBlock/commit/fb037e97d0) - [Improve `trusted-replace-node-text` scriptlet](https://github.com/gorhill/uBlock/commit/4f0d1301ab) ---------- From fa285f0e972a8471f59ebffca6f63d0a6b37dabc Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Tue, 6 Aug 2024 16:50:33 -0400 Subject: [PATCH 0116/1099] New revision for dev build --- dist/version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dist/version b/dist/version index 010d58db08ef5..4c593fac9f49f 100644 --- a/dist/version +++ b/dist/version @@ -1 +1 @@ -1.59.1.0 \ No newline at end of file +1.59.1.1 \ No newline at end of file From f0dd466f99da8588ac686e9b6e6dc59509a49fa0 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Tue, 6 Aug 2024 16:55:50 -0400 Subject: [PATCH 0117/1099] Make Firefox dev build auto-update --- dist/firefox/updates.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dist/firefox/updates.json b/dist/firefox/updates.json index c6344aa21d8ed..c3bc0cd6412ed 100644 --- a/dist/firefox/updates.json +++ b/dist/firefox/updates.json @@ -3,9 +3,9 @@ "uBlock0@raymondhill.net": { "updates": [ { - "version": "1.59.1.0", + "version": "1.59.1.1", "browser_specific_settings": { "gecko": { "strict_min_version": "78.0" } }, - "update_link": "https://github.com/gorhill/uBlock/releases/download/1.59.1b0/uBlock0_1.59.1b0.firefox.signed.xpi" + "update_link": "https://github.com/gorhill/uBlock/releases/download/1.59.1b1/uBlock0_1.59.1b1.firefox.signed.xpi" } ] } From 665648ba9776dcf5804d823502de324b3eb82a07 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Wed, 7 Aug 2024 10:03:03 -0400 Subject: [PATCH 0118/1099] [mv3] Re-word some text for accuracy --- platform/mv3/extension/_locales/en/messages.json | 6 +++++- platform/mv3/extension/dashboard.html | 2 +- platform/mv3/make-rulesets.js | 2 +- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/platform/mv3/extension/_locales/en/messages.json b/platform/mv3/extension/_locales/en/messages.json index 878f3cc80b78c..9f281de19554b 100644 --- a/platform/mv3/extension/_locales/en/messages.json +++ b/platform/mv3/extension/_locales/en/messages.json @@ -144,9 +144,13 @@ "description": "This describes the 'complete' filtering mode" }, "noFilteringModeDescription": { - "message": "List of hostnames for which no filtering will take place.", + "message": "List of websites for which no filtering will take place.", "description": "A short description for the editable field which lists trusted sites" }, + "noFilteringModePlaceholder": { + "message": "[hostnames only]\nexample.com\ngames.example\n...", + "description": "Default text for in edit field" + }, "behaviorSectionLabel": { "message": "Behavior", "description": "The header text for the 'Behavior' section" diff --git a/platform/mv3/extension/dashboard.html b/platform/mv3/extension/dashboard.html index d2dc918d980db..1cf7582050951 100644 --- a/platform/mv3/extension/dashboard.html +++ b/platform/mv3/extension/dashboard.html @@ -92,7 +92,7 @@

_

-

+

diff --git a/platform/mv3/make-rulesets.js b/platform/mv3/make-rulesets.js index fd842db38eaa5..e0ab17135fa16 100644 --- a/platform/mv3/make-rulesets.js +++ b/platform/mv3/make-rulesets.js @@ -1278,7 +1278,7 @@ async function main() { // Handpicked rulesets from abroad await rulesetFromURLs({ id: 'stevenblack-hosts', - name: 'Steven Black\'s hosts file', + name: 'Steven Black’s Unified Hosts (adware + malware)', enabled: false, urls: [ 'https://raw.githubusercontent.com/StevenBlack/hosts/master/hosts' ], homeURL: 'https://github.com/StevenBlack/hosts#readme', From 1822d1503f48c44a5a44f8527bf9c60d003e6223 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Mon, 12 Aug 2024 10:54:11 -0400 Subject: [PATCH 0119/1099] Import translation work from https://crowdin.com/project/ublock --- platform/mv3/description/webstore.ar.txt | 2 +- platform/mv3/description/webstore.az.txt | 2 +- platform/mv3/description/webstore.be.txt | 2 +- platform/mv3/description/webstore.bg.txt | 2 +- platform/mv3/description/webstore.bn.txt | 2 +- platform/mv3/description/webstore.br_FR.txt | 2 +- platform/mv3/description/webstore.bs.txt | 2 +- platform/mv3/description/webstore.ca.txt | 2 +- platform/mv3/description/webstore.cs.txt | 2 +- platform/mv3/description/webstore.cv.txt | 2 +- platform/mv3/description/webstore.cy.txt | 2 +- platform/mv3/description/webstore.da.txt | 2 +- platform/mv3/description/webstore.de.txt | 2 +- platform/mv3/description/webstore.el.txt | 2 +- platform/mv3/description/webstore.en_GB.txt | 2 +- platform/mv3/description/webstore.eo.txt | 2 +- platform/mv3/description/webstore.es.txt | 2 +- platform/mv3/description/webstore.et.txt | 2 +- platform/mv3/description/webstore.eu.txt | 2 +- platform/mv3/description/webstore.fa.txt | 4 +- platform/mv3/description/webstore.fi.txt | 4 +- platform/mv3/description/webstore.fil.txt | 2 +- platform/mv3/description/webstore.fr.txt | 2 +- platform/mv3/description/webstore.fy.txt | 2 +- platform/mv3/description/webstore.gl.txt | 2 +- platform/mv3/description/webstore.gu.txt | 2 +- platform/mv3/description/webstore.he.txt | 2 +- platform/mv3/description/webstore.hi.txt | 2 +- platform/mv3/description/webstore.hr.txt | 2 +- platform/mv3/description/webstore.hu.txt | 18 +-- platform/mv3/description/webstore.hy.txt | 2 +- platform/mv3/description/webstore.id.txt | 2 +- platform/mv3/description/webstore.it.txt | 2 +- platform/mv3/description/webstore.ja.txt | 2 +- platform/mv3/description/webstore.ka.txt | 2 +- platform/mv3/description/webstore.kk.txt | 2 +- platform/mv3/description/webstore.kn.txt | 2 +- platform/mv3/description/webstore.ko.txt | 2 +- platform/mv3/description/webstore.lt.txt | 2 +- platform/mv3/description/webstore.lv.txt | 2 +- platform/mv3/description/webstore.mk.txt | 2 +- platform/mv3/description/webstore.ml.txt | 2 +- platform/mv3/description/webstore.mr.txt | 2 +- platform/mv3/description/webstore.ms.txt | 2 +- platform/mv3/description/webstore.nb.txt | 2 +- platform/mv3/description/webstore.nl.txt | 2 +- platform/mv3/description/webstore.oc.txt | 2 +- platform/mv3/description/webstore.pa.txt | 4 +- platform/mv3/description/webstore.pl.txt | 2 +- platform/mv3/description/webstore.pt_BR.txt | 2 +- platform/mv3/description/webstore.pt_PT.txt | 2 +- platform/mv3/description/webstore.ro.txt | 2 +- platform/mv3/description/webstore.ru.txt | 2 +- platform/mv3/description/webstore.si.txt | 2 +- platform/mv3/description/webstore.sk.txt | 2 +- platform/mv3/description/webstore.sl.txt | 2 +- platform/mv3/description/webstore.so.txt | 2 +- platform/mv3/description/webstore.sq.txt | 2 +- platform/mv3/description/webstore.sr.txt | 2 +- platform/mv3/description/webstore.sv.txt | 2 +- platform/mv3/description/webstore.sw.txt | 2 +- platform/mv3/description/webstore.ta.txt | 2 +- platform/mv3/description/webstore.te.txt | 2 +- platform/mv3/description/webstore.th.txt | 2 +- platform/mv3/description/webstore.tr.txt | 2 +- platform/mv3/description/webstore.uk.txt | 2 +- platform/mv3/description/webstore.ur.txt | 2 +- platform/mv3/description/webstore.vi.txt | 2 +- platform/mv3/description/webstore.zh_CN.txt | 2 +- platform/mv3/description/webstore.zh_TW.txt | 2 +- .../mv3/extension/_locales/ar/messages.json | 8 +- .../mv3/extension/_locales/az/messages.json | 6 +- .../mv3/extension/_locales/be/messages.json | 4 + .../mv3/extension/_locales/bg/messages.json | 4 + .../mv3/extension/_locales/bn/messages.json | 4 + .../extension/_locales/br_FR/messages.json | 4 + .../mv3/extension/_locales/bs/messages.json | 4 + .../mv3/extension/_locales/ca/messages.json | 4 + .../mv3/extension/_locales/cs/messages.json | 4 + .../mv3/extension/_locales/cv/messages.json | 6 +- .../mv3/extension/_locales/cy/messages.json | 6 +- .../mv3/extension/_locales/da/messages.json | 4 + .../mv3/extension/_locales/de/messages.json | 6 +- .../mv3/extension/_locales/el/messages.json | 4 + .../extension/_locales/en_GB/messages.json | 4 + .../mv3/extension/_locales/eo/messages.json | 6 +- .../mv3/extension/_locales/es/messages.json | 4 + .../mv3/extension/_locales/et/messages.json | 6 +- .../mv3/extension/_locales/eu/messages.json | 4 + .../mv3/extension/_locales/fa/messages.json | 6 +- .../mv3/extension/_locales/fi/messages.json | 4 + .../mv3/extension/_locales/fil/messages.json | 6 +- .../mv3/extension/_locales/fr/messages.json | 6 +- .../mv3/extension/_locales/fy/messages.json | 4 + .../mv3/extension/_locales/gl/messages.json | 4 + .../mv3/extension/_locales/gu/messages.json | 6 +- .../mv3/extension/_locales/he/messages.json | 4 + .../mv3/extension/_locales/hi/messages.json | 4 + .../mv3/extension/_locales/hr/messages.json | 4 + .../mv3/extension/_locales/hu/messages.json | 18 ++- .../mv3/extension/_locales/hy/messages.json | 4 + .../mv3/extension/_locales/id/messages.json | 4 + .../mv3/extension/_locales/it/messages.json | 4 + .../mv3/extension/_locales/ja/messages.json | 4 + .../mv3/extension/_locales/ka/messages.json | 4 + .../mv3/extension/_locales/kk/messages.json | 6 +- .../mv3/extension/_locales/kn/messages.json | 6 +- .../mv3/extension/_locales/ko/messages.json | 4 + .../mv3/extension/_locales/lt/messages.json | 6 +- .../mv3/extension/_locales/lv/messages.json | 4 + .../mv3/extension/_locales/mk/messages.json | 6 +- .../mv3/extension/_locales/ml/messages.json | 4 + .../mv3/extension/_locales/mr/messages.json | 6 +- .../mv3/extension/_locales/ms/messages.json | 4 + .../mv3/extension/_locales/nb/messages.json | 4 + .../mv3/extension/_locales/nl/messages.json | 4 + .../mv3/extension/_locales/oc/messages.json | 6 +- .../mv3/extension/_locales/pa/messages.json | 4 + .../mv3/extension/_locales/pl/messages.json | 4 + .../extension/_locales/pt_BR/messages.json | 6 +- .../extension/_locales/pt_PT/messages.json | 4 + .../mv3/extension/_locales/ro/messages.json | 4 + .../mv3/extension/_locales/ru/messages.json | 4 + .../mv3/extension/_locales/si/messages.json | 6 +- .../mv3/extension/_locales/sk/messages.json | 4 + .../mv3/extension/_locales/sl/messages.json | 6 +- .../mv3/extension/_locales/so/messages.json | 6 +- .../mv3/extension/_locales/sq/messages.json | 6 +- .../mv3/extension/_locales/sr/messages.json | 4 + .../mv3/extension/_locales/sv/messages.json | 4 + .../mv3/extension/_locales/sw/messages.json | 6 +- .../mv3/extension/_locales/ta/messages.json | 6 +- .../mv3/extension/_locales/te/messages.json | 4 + .../mv3/extension/_locales/th/messages.json | 28 ++-- .../mv3/extension/_locales/tr/messages.json | 4 + .../mv3/extension/_locales/uk/messages.json | 4 + .../mv3/extension/_locales/ur/messages.json | 4 + .../mv3/extension/_locales/vi/messages.json | 4 + .../extension/_locales/zh_CN/messages.json | 4 + .../extension/_locales/zh_TW/messages.json | 4 + src/_locales/ca/messages.json | 2 +- src/_locales/el/messages.json | 8 +- src/_locales/fi/messages.json | 6 +- src/_locales/gl/messages.json | 16 +-- src/_locales/hu/messages.json | 124 +++++++++--------- src/_locales/pa/messages.json | 8 +- src/_locales/sr/messages.json | 4 +- 147 files changed, 489 insertions(+), 209 deletions(-) diff --git a/platform/mv3/description/webstore.ar.txt b/platform/mv3/description/webstore.ar.txt index 97675121f0c9d..9053a997ad161 100644 --- a/platform/mv3/description/webstore.ar.txt +++ b/platform/mv3/description/webstore.ar.txt @@ -7,7 +7,7 @@ - الخصوصية السهلة - قائمة خادم الإعلانات والتتبع لبيتر لوي -يمكنك إضافة المزيد من القواعد من خلال زيارة صفحة الخيارات ومن ثم أنقر على رمز _Cogs_ في اللوحة المنبثقة. +يمكنك تفعيل المزيد من مجموعات القواعد من خلال زيارة صفحة الخيارات - انقر على أيقونة _الترس_ في لوحة الإشعارات. uBOL صريح تمامًا، مما يعني أنه لا تحتاج إلى uBOL بشكل دائم لحدوث تصفية المحتوى، يتم إجراء تصفية المحتوى من خلال إضافة CSS/JS بشكل موثوق به بواسطة المتصفح نفسه بدلًا من الإضافة. هذا يعني أن uBOL نفسه لا يستهلك موارد وحدة المعالجة المركزية/الذاكرة أثناء استمراره في حظر المحتوى. عملية عامل الخدمة في uBOL مطلوبة _فقط_ عند التفاعل مع اللوحة المنبثقة أو صفحة الخيارات. diff --git a/platform/mv3/description/webstore.az.txt b/platform/mv3/description/webstore.az.txt index 8ff852cf786a6..61d4f83dca739 100644 --- a/platform/mv3/description/webstore.az.txt +++ b/platform/mv3/description/webstore.az.txt @@ -7,7 +7,7 @@ Defolt qaydalar dəsti uBlock Origin-in defolt filtr dəstinə uyğundur: - EasyPrivacy - Peter Lowe-un Reklam və izləyici server siyahısı -You can add more rulesets by visiting the options page -- click the _Cogs_ icon in the popup panel. +You can enable more rulesets by visiting the options page -- click the _Cogs_ icon in the popup panel. uBOL is entirely declarative, meaning there is no need for a permanent uBOL process for the filtering to occur, and CSS/JS injection-based content filtering is performed reliably by the browser itself rather than by the extension. This means that uBOL itself does not consume CPU/memory resources while content blocking is ongoing -- uBOL's service worker process is required _only_ when you interact with the popup panel or the option pages. diff --git a/platform/mv3/description/webstore.be.txt b/platform/mv3/description/webstore.be.txt index 80b06a4940748..c23daa3fb6dd8 100644 --- a/platform/mv3/description/webstore.be.txt +++ b/platform/mv3/description/webstore.be.txt @@ -7,7 +7,7 @@ The default ruleset corresponds to uBlock Origin's default filterset: - EasyPrivacy - Peter Lowe’s Ad and tracking server list -You can add more rulesets by visiting the options page -- click the _Cogs_ icon in the popup panel. +You can enable more rulesets by visiting the options page -- click the _Cogs_ icon in the popup panel. uBOL is entirely declarative, meaning there is no need for a permanent uBOL process for the filtering to occur, and CSS/JS injection-based content filtering is performed reliably by the browser itself rather than by the extension. This means that uBOL itself does not consume CPU/memory resources while content blocking is ongoing -- uBOL's service worker process is required _only_ when you interact with the popup panel or the option pages. diff --git a/platform/mv3/description/webstore.bg.txt b/platform/mv3/description/webstore.bg.txt index 6e58550d3cf46..3430ca651326e 100644 --- a/platform/mv3/description/webstore.bg.txt +++ b/platform/mv3/description/webstore.bg.txt @@ -7,7 +7,7 @@ uBO Lite (uBOL) е блокер за съдържание *без разреше - EasyPrivacy - Списък със сървъри на Peter Lowe за реклами и проследяване -Можете да добавите още набори от правила, като посетите страницата с опции – щракнете върху иконата _зъбно колело_ в изскачащия панел. +Можете да включите още набори от правила, като посетите страницата с опции – щракнете върху иконата „зъбно колело“ в изскачащия панел. uBOL е изцяло декларативен, което означава, че няма нужда от постоянен процес на uBOL за филтриране, а филтрирането на съдържание, базирано на инжектиране на CSS/JS, се извършва надеждно от самия браузър, а не от разширението. Това означава, че самият uBOL не консумира ресурси на процесора/паметта, докато тече блокирането на съдържанието – работният процес на услугата на uBOL е необходим _само_ когато взаимодействате с изскачащия панел или страниците с опции. diff --git a/platform/mv3/description/webstore.bn.txt b/platform/mv3/description/webstore.bn.txt index 83e739a19aebb..ba1fcbdc5e9d3 100644 --- a/platform/mv3/description/webstore.bn.txt +++ b/platform/mv3/description/webstore.bn.txt @@ -7,7 +7,7 @@ uBO Lite (uBOL) হল একটি *অনুমতি-হীন* MV3-ভিত - সহজ গোপনীয়তা - পিটার লো এর বিজ্ঞাপন এবং ট্র্যাকিং সার্ভার তালিকা -আপনি অপশন পেজে গিয়ে আরও নিয়ম সেট যোগ করতে পারেন -- পপআপ প্যানেলে _Cogs_ আইকনে ক্লিক করুন। +You can enable more rulesets by visiting the options page -- click the _Cogs_ icon in the popup panel. uBOL সম্পূর্ণরূপে ঘোষণামূলক, অর্থাৎ ফিল্টারিং করতে একটি স্থায়ী uBOL প্রক্রিয়ার প্রয়োজন নেই, এবং CSS/JS ইনজেকশন-ভিত্তিক বিষয়বস্তু ফিল্টারিং এক্সটেনশনের পরিবর্তে ব্রাউজার নিজেই নির্ভরযোগ্যভাবে এই কাজ করে থাকে। এর মানে হল যে কন্টেন্ট ব্লকিং চলমান থাকা অবস্থায় uBOL নিজেই CPU/মেমরি রিসোর্স ব্যবহার করে না -- uBOL-এর পরিষেবার প্রক্রিয়ার প্রয়োজন শুধুমাত্র_ যখন আপনি পপআপ প্যানেল বা অপশন পেজগুলির সাথে ইন্টারঅ্যাক্ট করেন। diff --git a/platform/mv3/description/webstore.br_FR.txt b/platform/mv3/description/webstore.br_FR.txt index 5cf05df10d91a..a768351889f18 100644 --- a/platform/mv3/description/webstore.br_FR.txt +++ b/platform/mv3/description/webstore.br_FR.txt @@ -7,7 +7,7 @@ Ar reolennoù dre ziouer a glot gant silañ dre ziouer uBlock Origin: - EasyPrivacy - Roll bruderezh ha servijerioù heuliañ Peter Lowe -Tu zo deoc'h ouzhpennañ reolennoù all en arventennoù -- klikit war an ikon _kendentadur_ er banell popup. +You can enable more rulesets by visiting the options page -- click the _Cogs_ icon in the popup panel. Disklêriañ a ra uBOL penn-da-benn, da lavaret eo n'eus ket ezhomm eus un argerzh uBOL padus evit ma c'hoarvezfe ar silañ, ha silañ endalc'hadoù diazezet war enlakaat CSS/JS a vez graet en un doare fizius gant ar merdeer e-unan kentoc'h eget gant an astenn. Kement-se a dalvez ne vez ket gounezet gant uBOL e-unan arc'hwelioù CPU/memor e-pad ma vez stanket an endalc'hadoù -- ezhomm zo eus argerzh al labourer servij uBOL _nemet_ pa vez etregweredet gant ar banell digeriñ pe ar pajennoù dibarzhioù. diff --git a/platform/mv3/description/webstore.bs.txt b/platform/mv3/description/webstore.bs.txt index 96ba7d2ef9c3b..f0636858a031a 100644 --- a/platform/mv3/description/webstore.bs.txt +++ b/platform/mv3/description/webstore.bs.txt @@ -7,7 +7,7 @@ Zadani skup pravila odgovara zadanom skupu filtera uBlock Origin: - EasyPrivacy - Oglas Peter Lowe i lista servera za praćenje -Možete dodati još skupova pravila tako što ćete posjetiti stranicu sa opcijama -- kliknite na ikonu _Cogs_ na iskačućoj ploči. +You can enable more rulesets by visiting the options page -- click the _Cogs_ icon in the popup panel. uBOL je potpuno deklarativno, što znači da nema potrebe za trajnim uBOL procesom da bi se filtriranje dogodilo, a filtriranje sadržaja zasnovano na CSS/JS injekcijama se pouzdano izvodi od strane samog pretraživača, a ne ekstenzije. To znači da sam uBOL ne troši CPU/memorijske resurse dok je blokiranje sadržaja u toku -- proces uBOL-a servisnog radnika je potreban _samo_ kada stupite u interakciju sa iskačućim panelom ili stranicama sa opcijama. diff --git a/platform/mv3/description/webstore.ca.txt b/platform/mv3/description/webstore.ca.txt index e75025530597a..1a5b9cd9aaa45 100644 --- a/platform/mv3/description/webstore.ca.txt +++ b/platform/mv3/description/webstore.ca.txt @@ -7,7 +7,7 @@ El conjunt de regles per defecte correspon al conjunt de filtres per defecte d'u - EasyPrivacy - Llista de servidors de seguiment i anuncis de Peter Lowe -Podeu afegir més conjunts de regles si visiteu la pàgina d'opcions: feu clic a la icona _Cogs_ al tauler emergent. +Podeu habilitar més conjunts de regles si visiteu la pàgina d'opcions: feu clic a la icona _Cogs_ al tauler emergent. L'uBOL és totalment declaratiu, és a dir, no cal un procés uBOL permanent perquè es produeixi el filtratge, i el filtratge de contingut basat en injecció CSS/JS es realitza de manera fiable pel propi navegador més que per l'extensió. Això vol dir que l'uBOL en si no consumeix recursos de CPU/memòria mentre el bloqueig de contingut està en curs; el procés de treballador de servei d'uBOL només es requereix quan interactueu amb el tauler emergent o les pàgines d'opcions. diff --git a/platform/mv3/description/webstore.cs.txt b/platform/mv3/description/webstore.cs.txt index 05ca248f754a1..e698d696b48e5 100644 --- a/platform/mv3/description/webstore.cs.txt +++ b/platform/mv3/description/webstore.cs.txt @@ -7,7 +7,7 @@ Výchozí sada pravidel koresponduje k výchozím sadám filtrů uBlock Origin: - EasyPrivacy - Peter Lowe’s Ad and tracking server list -Můžete přidat více sad pravidel navštívením stránky nastavení -- klikněte na ikonu ozubených kol ve vyskakovácím panelu. +Další sady pravidel můžete povolit na stránce nastavení - klikněte na ikonu _Ozubeného kolečka_ ve vyskakovacím panelu. uBOL je zcela deklarativní, což znamená, že pro filtrování není potřeba permanentní proces uBOL a filtrování obsahu založené na vstřikování CSS/JS je spolehlivě prováděno samotným prohlížečem, nikoli rozšířením. To znamená, že samotný uBOL nespotřebovává zdroje CPU/paměti, zatímco probíhá blokování obsahu – proces servisního pracovníka uBOL je vyžadován _pouze_ při interakci s vyskakovacím panelem nebo stránkami nastavení. diff --git a/platform/mv3/description/webstore.cv.txt b/platform/mv3/description/webstore.cv.txt index e03fa801ee7f0..7669a5939f98c 100644 --- a/platform/mv3/description/webstore.cv.txt +++ b/platform/mv3/description/webstore.cv.txt @@ -7,7 +7,7 @@ The default ruleset corresponds to uBlock Origin's default filterset: - EasyPrivacy - Peter Lowe’s Ad and tracking server list -You can add more rulesets by visiting the options page -- click the _Cogs_ icon in the popup panel. +You can enable more rulesets by visiting the options page -- click the _Cogs_ icon in the popup panel. uBOL is entirely declarative, meaning there is no need for a permanent uBOL process for the filtering to occur, and CSS/JS injection-based content filtering is performed reliably by the browser itself rather than by the extension. This means that uBOL itself does not consume CPU/memory resources while content blocking is ongoing -- uBOL's service worker process is required _only_ when you interact with the popup panel or the option pages. diff --git a/platform/mv3/description/webstore.cy.txt b/platform/mv3/description/webstore.cy.txt index e03fa801ee7f0..7669a5939f98c 100644 --- a/platform/mv3/description/webstore.cy.txt +++ b/platform/mv3/description/webstore.cy.txt @@ -7,7 +7,7 @@ The default ruleset corresponds to uBlock Origin's default filterset: - EasyPrivacy - Peter Lowe’s Ad and tracking server list -You can add more rulesets by visiting the options page -- click the _Cogs_ icon in the popup panel. +You can enable more rulesets by visiting the options page -- click the _Cogs_ icon in the popup panel. uBOL is entirely declarative, meaning there is no need for a permanent uBOL process for the filtering to occur, and CSS/JS injection-based content filtering is performed reliably by the browser itself rather than by the extension. This means that uBOL itself does not consume CPU/memory resources while content blocking is ongoing -- uBOL's service worker process is required _only_ when you interact with the popup panel or the option pages. diff --git a/platform/mv3/description/webstore.da.txt b/platform/mv3/description/webstore.da.txt index 1d3218873cb5a..34a84077d1281 100644 --- a/platform/mv3/description/webstore.da.txt +++ b/platform/mv3/description/webstore.da.txt @@ -7,7 +7,7 @@ Standardregelsættet svarer til uBlock Origins standardfiltersæt: - EasyPrivacy - Peter Lowe’s Ad and tracking server list -Flere regelsæt kan tilføjes ved at gå til indstillingssiden -- klik på ikonet _Cogs_ i pop op-panelet. +Flere regelsæt kan aktiveres ved at gå til indstillingssiden -- klik på ikonet _Tandhjul_ i pop op-panelet. uBOL er fuldstændig deklarativ, hvilket betyder, at ingen permanent uBOL-proces behøves for at filtreringen kan finde sted, og CSS/JS-injektionsbaseret indholdsfiltrering udføres pålideligt af browseren selv i stedet for af udvidelsen. Dette betyder, at uBOL ikke selv forbruger CPU-/hukommelsesressourcer under indholdsblokeringen -- uBOLs tjenestearbejdsproces er _kun_ nødvendig under interaktion med pop op-panelet eller indstillingssiderne. diff --git a/platform/mv3/description/webstore.de.txt b/platform/mv3/description/webstore.de.txt index 807ec84c1d918..df224daac5367 100644 --- a/platform/mv3/description/webstore.de.txt +++ b/platform/mv3/description/webstore.de.txt @@ -7,7 +7,7 @@ Die Standardregeln entsprechen den Standardfiltern von uBlock Origin: - EasyPrivacy - Peter Lowe’s Ad and tracking server list -Sie können weitere Regeln hinzufügen, indem Sie die Optionen aufrufen — klicken Sie dazu im Pop-up-Fenster auf das Symbol mit den _Zahnrädern_. +Sie können weitere Regeln aktivieren, indem Sie die Einstellungen aufrufen — klicken Sie dazu im Pop-up-Fenster auf das Symbol mit den _Zahnrädern_. uBOL ist vollständig deklarativ, d. h. es ist kein dauerhafter uBOL-Prozess für das Filtern erforderlich, und die auf CSS/JS-Injektion basierende Inhaltsfilterung wird zuverlässig vom Browser selbst und nicht von der Erweiterung durchgeführt. Das bedeutet, dass uBOL selbst keine CPU-/Speicherressourcen verbraucht, während der Inhalt blockiert wird — der uBOL-Service-Worker-Prozess wird _nur_ benötigt, wenn Sie mit dem Pop-up-Fenster oder den Optionen interagieren. diff --git a/platform/mv3/description/webstore.el.txt b/platform/mv3/description/webstore.el.txt index 32d043db89b31..ae5aed364399c 100644 --- a/platform/mv3/description/webstore.el.txt +++ b/platform/mv3/description/webstore.el.txt @@ -7,7 +7,7 @@ - EasyPrivacy - Peter Lowe’s Ad and tracking server list -Μπορείτε να προσθέσετε περισσότερα σύνολα κανόνων μεταβαίνοντας στη σελίδα επιλογών -- κάντε κλικ στο εικονίδιο _Cogs_ στον αναδυόμενο πίνακα. +You can enable more rulesets by visiting the options page -- click the _Cogs_ icon in the popup panel. Το uBOL είναι εξ'ολοκλήρου δηλωτικό, πράγμα που σημαίνει ότι δεν υπάρχει ανάγκη για μόνιμη διαδικασία uBOL για να πραγματοποιηθεί το φιλτράρισμα, και το φιλτράρισμα περιεχομένου που βασίζεται σε έγχυση CSS/JS εκτελείται αξιόπιστα από το ίδιο το πρόγραμμα περιήγησης και όχι από την επέκταση. Αυτό σημαίνει ότι το ίδιο το uBOL δεν καταναλώνει πόρους CPU/μνήμης ενώ ο αποκλεισμός περιεχομένου είναι σε εξέλιξη -- η διαδικασία του service worker του uBOL απαιτείται _μόνο_ όταν αλληλεπιδράτε με τον αναδυόμενο πίνακα ή τις σελίδες επιλογών. diff --git a/platform/mv3/description/webstore.en_GB.txt b/platform/mv3/description/webstore.en_GB.txt index e03fa801ee7f0..7669a5939f98c 100644 --- a/platform/mv3/description/webstore.en_GB.txt +++ b/platform/mv3/description/webstore.en_GB.txt @@ -7,7 +7,7 @@ The default ruleset corresponds to uBlock Origin's default filterset: - EasyPrivacy - Peter Lowe’s Ad and tracking server list -You can add more rulesets by visiting the options page -- click the _Cogs_ icon in the popup panel. +You can enable more rulesets by visiting the options page -- click the _Cogs_ icon in the popup panel. uBOL is entirely declarative, meaning there is no need for a permanent uBOL process for the filtering to occur, and CSS/JS injection-based content filtering is performed reliably by the browser itself rather than by the extension. This means that uBOL itself does not consume CPU/memory resources while content blocking is ongoing -- uBOL's service worker process is required _only_ when you interact with the popup panel or the option pages. diff --git a/platform/mv3/description/webstore.eo.txt b/platform/mv3/description/webstore.eo.txt index e03fa801ee7f0..7669a5939f98c 100644 --- a/platform/mv3/description/webstore.eo.txt +++ b/platform/mv3/description/webstore.eo.txt @@ -7,7 +7,7 @@ The default ruleset corresponds to uBlock Origin's default filterset: - EasyPrivacy - Peter Lowe’s Ad and tracking server list -You can add more rulesets by visiting the options page -- click the _Cogs_ icon in the popup panel. +You can enable more rulesets by visiting the options page -- click the _Cogs_ icon in the popup panel. uBOL is entirely declarative, meaning there is no need for a permanent uBOL process for the filtering to occur, and CSS/JS injection-based content filtering is performed reliably by the browser itself rather than by the extension. This means that uBOL itself does not consume CPU/memory resources while content blocking is ongoing -- uBOL's service worker process is required _only_ when you interact with the popup panel or the option pages. diff --git a/platform/mv3/description/webstore.es.txt b/platform/mv3/description/webstore.es.txt index 2ea6cfff29121..b89c567215bb5 100644 --- a/platform/mv3/description/webstore.es.txt +++ b/platform/mv3/description/webstore.es.txt @@ -7,7 +7,7 @@ Por defecto ya trae configuradas las siguientes listas de filtros: - EasyPrivacy - Peter Lowe’s Ad and tracking server list -Puedes añadir más conjuntos de reglas visitando la página de opciones, haz clic en el icono de _engranaje_ del panel emergente. +Puedes activar más ruletas visitando la página de opciones --pulsa el icono _Cogs_ en la ventana del panel uBOL es completamente declarativo, lo que significa que no hay necesidad de un proceso uBOL permanente para que se produzca el filtrado, y el filtrado de contenido basado en la inyección de CSS/JS se realiza de forma confiable por el propio navegador en lugar de la extensión. Esto significa que uBOL en sí mismo no consume recursos de CPU/memoria mientras el bloqueo de contenido está en curso, el proceso service worker de uBOL se requiere _solo_ cuando se interactúa con el panel emergente o las páginas de opciones. diff --git a/platform/mv3/description/webstore.et.txt b/platform/mv3/description/webstore.et.txt index 520e4ce134edc..0f81da1126305 100644 --- a/platform/mv3/description/webstore.et.txt +++ b/platform/mv3/description/webstore.et.txt @@ -7,7 +7,7 @@ Tavaline reeglitekogum vastab uBlock Origini tavalisele filtritekogumile: - EasyPrivacy Peter Lowe'i reklaamide ja jälitusserverite loend -Reeglitekogumeid saate lisada valikute lehelt ehk avanenud paneelis klõpsake _Cogs_ ikooni. +Rohkem reegleid valikutest ehk toksake _Cogs_ ikooni hüpikpaneelis. uBOL on läbinisti deklaratiivne ehk filtreerimiseks pole vaja kogu aeg töötavat uBOLi protsessi ja CSS/JS süstipõhist sisu filtreerib tegelikult brauser, mitte laiendus. Teisisõnu, uBOL ei kasuta sisu tõkestamisel protsessori/mälu ressursse. uBOLi teenuse toimimise protsessi on vaja _vaid_ juhul, kui kasutate hüpikpaneeli või valikute lehekülgi. diff --git a/platform/mv3/description/webstore.eu.txt b/platform/mv3/description/webstore.eu.txt index f32691b857e2b..c0ebbdfb27e0d 100644 --- a/platform/mv3/description/webstore.eu.txt +++ b/platform/mv3/description/webstore.eu.txt @@ -7,7 +7,7 @@ ZerrendaErraza PribazitateaErraza Peter Lowe-ren Ad and tracker zerrenda -Arau gehiago gehitu ditzakezu aukeren orrialdea bisitatuz. Sakatu _Cogs_ ikonoa popup panelean. +Ruleta gehiago aktibatu ahal duzu aukerak orria --klikatu _Cogs_ ikonoa panelearen lehioan uBOL guztiz deklaratiboa da, hau da, ez dago uBOL prozesu iraunkor baten beharrik iragazketa gertatzeko, eta CSS/JS injekzioan oinarritutako edukien iragazketa nabigatzaileak berak egiten du fidagarritasunez, luzapenaren arabera beharrean. Horrek esan nahi du uBOLek berak ez duela CPU/memoria baliabiderik kontsumitzen edukien blokeoa martxan dagoen bitartean... uBOLren zerbitzuko langileen prozesua _only_ behar da popup panelarekin edo aukera orriekin elkarreragiten denean. diff --git a/platform/mv3/description/webstore.fa.txt b/platform/mv3/description/webstore.fa.txt index e03fa801ee7f0..b85b55d27092c 100644 --- a/platform/mv3/description/webstore.fa.txt +++ b/platform/mv3/description/webstore.fa.txt @@ -1,13 +1,13 @@ uBO Lite (uBOL) is a *permission-less* MV3-based content blocker. -The default ruleset corresponds to uBlock Origin's default filterset: +مجموعه قوانین پیش فرض آن مطابق با مجموعه قوانین پیش فرض uBlock Origin است: - uBlock Origin's built-in filter lists - EasyList - EasyPrivacy - Peter Lowe’s Ad and tracking server list -You can add more rulesets by visiting the options page -- click the _Cogs_ icon in the popup panel. +You can enable more rulesets by visiting the options page -- click the _Cogs_ icon in the popup panel. uBOL is entirely declarative, meaning there is no need for a permanent uBOL process for the filtering to occur, and CSS/JS injection-based content filtering is performed reliably by the browser itself rather than by the extension. This means that uBOL itself does not consume CPU/memory resources while content blocking is ongoing -- uBOL's service worker process is required _only_ when you interact with the popup panel or the option pages. diff --git a/platform/mv3/description/webstore.fi.txt b/platform/mv3/description/webstore.fi.txt index 7e5693c715911..672396d3a9f7b 100644 --- a/platform/mv3/description/webstore.fi.txt +++ b/platform/mv3/description/webstore.fi.txt @@ -7,7 +7,7 @@ Oletusarvoiset sääntömääritykset vastaavat uBlock Origin -laajennuksen olet - EasyPrivacy - Peter Lowe’s Ad and tracking server list -Voit lisätä sääntömäärityksiä asetussivulta -- paina ponnahduspaneelin _Rataskuvaketta_. +Voit aktivoida lisää sääntömäärityksiä laajennuksen asetuksista – paina ponnahduspaneelin _Ratas_-kuvaketta. uBOL on täysin deklaratiivinen, eli suodatus ei edellytä pysyvää uBOL-prosessia ja CSS-/JS-koodin manipulointiin perustuva sisällönsuodatuksen suorittaa laajennusprosessin sijaan luotettavasti selainsovellus. Tämän ansiosta itse uBOL ei kuormita prosessoria tai keskusmuistia sisällöneston tapahtuessa -- uBOL:n työprosessia tarvitaan _ainoastaan_ ponnahduspaneelia ja asetussivuja käytettäessä. @@ -15,7 +15,7 @@ uBOL ei edellytä laajan tietojen luku- ja muokkausoikeuden myöntämistä asenn On kuitenkin mahdollista myöntää *yksinomaisesti* uBOL:lle laajennetut käyttöoikeudet sivustokohtaisesti niiden suodatuksen tehostamiseksi kosmeettisella suodatuksella ja scriplet-injektoinnilla. -Laajemmat oikeudet myönnetään avoimelle sivustolle avaamalla ponnahduspaneeli ja valitsemalla korkeampi suodatustaso, kuten Optimaalinen tai Täysi. +Laajemmat oikeudet myönnetään avoimelle sivustolle avaamalla ponnahduspaneeli ja valitsemalla korkeampi suodatustaso, kuten "Optimaalinen" tai "Täysi". Tällöin selain varoittaa laajennuksen avoimelle sivustolle pyytämien käyttöoikeuksien seurauksista ja pyytää hyväksymään tai hylkäämään pyynnön. diff --git a/platform/mv3/description/webstore.fil.txt b/platform/mv3/description/webstore.fil.txt index 0bb5710c6e233..b859f25bf3182 100644 --- a/platform/mv3/description/webstore.fil.txt +++ b/platform/mv3/description/webstore.fil.txt @@ -7,7 +7,7 @@ Tulad ng uBlock Origin, ito rin ang mga default na listahan ng mga filter: - EasyPrivacy - Listahan ni Peter Lowe sa mga ad at tracking server (Peter Lowe’s Ad and tracking server list) -Makakapagdagdag ka ng higit pang mga patakaran sa pahina ng mga opsyon -- pindutin ang icon ng _gulong_ sa popup panel. +You can enable more rulesets by visiting the options page -- click the _Cogs_ icon in the popup panel. Deklaratibo lamang ang uBOL, kaya hindi nito kailangan ng permanenteng proseso upang mag-filter, at mainam na ginagawa ng browser mismo imbes na ekstensyon ang pagfi-filter sa content na nakabase sa CSS o JS. Ibig-sabihin, hindi kumokonsyumo ng CPU o memorya ang uBOL habang nanghaharang -- ang proseso ng trabahante ng serbisyo ay kailangan _lang_ kung nasa popup panel o pahina ng opsyon ka. diff --git a/platform/mv3/description/webstore.fr.txt b/platform/mv3/description/webstore.fr.txt index 12b9d01d1f4f8..cdf03fc490487 100644 --- a/platform/mv3/description/webstore.fr.txt +++ b/platform/mv3/description/webstore.fr.txt @@ -7,7 +7,7 @@ Les règles par défaut correspondent au filtrage par défaut d'uBlock Origin : - EasyPrivacy - La liste anti-serveurs pub et pistage de Peter Lowe -Vous pouvez ajouter plus de règles en consultant la page des paramètres -- Cliquez sur l'_Engrenage_ dans le panneau pop-up. +Vous pouvez ajouter plus de règles en consultant la page des paramètres -- Cliquez sur l'icône de l'_Engrenage_ dans le panneau pop-up. uBOL est entièrement déclarative, c'est-à-dire qu'il n'y a pas besoin d'un processus uBOL permanent pour filtrer, et le filtrage basé sur l'injection CSS/JavaScript se fait en toute fiabilité par le navigateur lui-même. Cela veut dire qu'en soi, uBOL ne consomme pas de ressources processeur/mémoire pendant le blocage de contenu -- l'agent de service d'uBOL n'est sollicité _que_ quand vous interagissez avec le panneau pop-up ou la page des paramètres. diff --git a/platform/mv3/description/webstore.fy.txt b/platform/mv3/description/webstore.fy.txt index 63fa94a819b56..2f070096fc3ce 100644 --- a/platform/mv3/description/webstore.fy.txt +++ b/platform/mv3/description/webstore.fy.txt @@ -7,7 +7,7 @@ De standert regelset komt oerien mei de standert filterset fan uBlock Origin: - EasyPrivacy - Peter Lowe’s Ad and tracking-serverlist -Jo kinne mear regelsets tafoegje troch de opsjesside te besykjen – klik op it _tântsjilpiktogram_ yn it pop-uppaniel. +Jo kinne mear regelsets ynskeakelje troch de opsjesside te besykjen – klik op it _tântsjilpiktogram_ yn it pop-uppaniel. uBOL is folslein deklaratyf, wat betsjut dat in permanint uBOL-proses foar de filtering net nedich is, en ynhâldsfiltering op basis fan CSS/JS-ynjeksje op in betroubere manier troch de browser sels útfierd wurdt yn stee fan de útwreiding. Dit betsjut dat uBOL sels gjin CPU-/ûnthâldboarnen brûkt wylst ynhâldsblokkearring aktyf is – it serviceworker-proses fan uBOL is _allinnich_ fereaske as jo mei it pop-uppaniel of de opsjessiden wurkje. diff --git a/platform/mv3/description/webstore.gl.txt b/platform/mv3/description/webstore.gl.txt index a82432f3762c8..6dca35b6e6fbd 100644 --- a/platform/mv3/description/webstore.gl.txt +++ b/platform/mv3/description/webstore.gl.txt @@ -7,7 +7,7 @@ O conxunto de regras predeterminado corresponde ao conxunto de filtros predeterm - EasyPrivacy Lista de servidores de seguimento e anuncios de Peter Lowe -Podes engadir máis conxuntos de regras visitando a páxina de opcións: fai clic na icona _Cogs_ no panel emerxente. +Podes activar máis grupos de regras indo á páxina de opcións -- preme na roda dentada no panel emerxente. uBOL é totalmente declarativo, o que significa que non é necesario un proceso permanente de uBOL para que se produza o filtrado e o filtrado de contido baseado en inxección de CSS/JS realízao de forma fiable o propio navegador en lugar da extensión. Isto significa que o propio uBOL non consume recursos de CPU/memoria mentres o bloqueo de contido está en curso -- o proceso do traballador do servizo de uBOL é necesario _só_ cando interactúas co panel emerxente ou coas páxinas de opcións. diff --git a/platform/mv3/description/webstore.gu.txt b/platform/mv3/description/webstore.gu.txt index e03fa801ee7f0..7669a5939f98c 100644 --- a/platform/mv3/description/webstore.gu.txt +++ b/platform/mv3/description/webstore.gu.txt @@ -7,7 +7,7 @@ The default ruleset corresponds to uBlock Origin's default filterset: - EasyPrivacy - Peter Lowe’s Ad and tracking server list -You can add more rulesets by visiting the options page -- click the _Cogs_ icon in the popup panel. +You can enable more rulesets by visiting the options page -- click the _Cogs_ icon in the popup panel. uBOL is entirely declarative, meaning there is no need for a permanent uBOL process for the filtering to occur, and CSS/JS injection-based content filtering is performed reliably by the browser itself rather than by the extension. This means that uBOL itself does not consume CPU/memory resources while content blocking is ongoing -- uBOL's service worker process is required _only_ when you interact with the popup panel or the option pages. diff --git a/platform/mv3/description/webstore.he.txt b/platform/mv3/description/webstore.he.txt index d931b3dae4271..e9d1100485df9 100644 --- a/platform/mv3/description/webstore.he.txt +++ b/platform/mv3/description/webstore.he.txt @@ -7,7 +7,7 @@ uBO Lite (uBOL) הוא חוסם תוכן *ללא הרשאות* מבוסס MV3. - EasyPrivacy - רשימת שרתי מודעות ומעקב של פיטר לואו -ניתן להוסיף ערכות כללים נוספות מעמוד האפשרויות –על ידי הקשה על סמל _Cogs_ בלוח הצץ. +ניתן לאפשר קבוצות חוקים נוספות בדף האפשרויות - עם לחיצה על סמליל _גלגלי השיניים_ בחלונית הקופצת. uBOL הוא הכרזתי לחלוטין, כלומר אין צורך בתהליך uBOL קבוע כדי שהסינון יתרחש, וסינון תוכן מבוסס הזרקת CSS/JS מבוצע באופן אמין על ידי הדפדפן עצמו ולא על ידי ההרחבה. המשמעות היא ש־uBOL עצמו לא צורכך משאבי מעבד/זיכרון בזמן שחסימת התוכן מתרחשת – תהליך ה־service worker של uBOL נדרש _אך ורק_ בזמן הידוד עם החלון הקופץ או עם עמוד האפשרויות. diff --git a/platform/mv3/description/webstore.hi.txt b/platform/mv3/description/webstore.hi.txt index 7b361bc8c5bed..1f8ba632744af 100644 --- a/platform/mv3/description/webstore.hi.txt +++ b/platform/mv3/description/webstore.hi.txt @@ -7,7 +7,7 @@ uBO Lite (uBOL) एक *अनुमति-रहित* MV3-आधारित - EasyPrivacy - Peter Lowe की विज्ञापन एवं ट्रैकिंग सर्वर सू‍ची -आप विकल्प पृष्ठ पर जाकर और अधिक रूलसेट जोड़ सकते हैं -- पॉपअप पैनल में _Cogs_ आइकन पर क्लिक करें। +आप विकल्प पृष्ठ पर जाकर और अधिक नियम-सेट सक्षम कर सकते हैं -- पॉपअप पैनल में _Cogs_ आइकन पर क्लिक करें। uBOL पूरी तरह से वर्णनात्मक है, जिसका यह अर्थ है कि फ़िल्टरिंग के लिए एक स्थायी uBOL प्रक्रिया की कोई आवश्यकता नहीं है, और CSS/JS इंजेक्शन-आधारित कन्टेन्ट फ़िल्टरिंग एक्सटेंशन के बजाय ब्राउज़र द्वारा विश्वसनीय रूप से की जाती है। इसका यह अर्थ है कि कन्टेन्ट ब्लॉक करते समय uBOL द्वारा सीपीयू/मेमोरी संसाधनों का उपभोग स्वयं नहीं किया जाता है -- uBOL की सर्विस प्रोसेस की आवश्यकता _केवल_ तब होती है जब आप पॉपअप पैनल या विकल्प पृष्ठों पर कोई अंत:क्रिया करते हैं। diff --git a/platform/mv3/description/webstore.hr.txt b/platform/mv3/description/webstore.hr.txt index 898bc2002f3f0..0addfd46d3509 100644 --- a/platform/mv3/description/webstore.hr.txt +++ b/platform/mv3/description/webstore.hr.txt @@ -7,7 +7,7 @@ Zadana lista pravila odgovara uBlock Origin-ovoj zadanoj listi filtera: - EasyPrivacy - Peter Lowe-ova lista oglasa i pratećih servera -Možete dodati više skupova pravila tako što ćete posjetiti stranicu s opcijama -- kliknite ikonu _zupčanika_ na skočnoj ploči. +Možete omogućiti više skupova pravila tako što ćete posjetiti stranicu s opcijama -- kliknite ikonu _Cogs_ na skočnoj ploči. uBOL je u potpunosti deklarativan, što znači da nema potrebe za trajnim uBOL procesom za filtriranje, a filtriranje sadržaja temeljeno na ubacivanju CSS/JS pouzdano izvodi sam preglednik, a ne ekstenzija. To znači da sam uBOL ne troši CPU/memorijske resurse dok je blokiranje sadržaja u tijeku -- uBOL-ov servisni radni proces potreban je _samo_ kada komunicirate s skočnom pločom ili stranicama s opcijama. diff --git a/platform/mv3/description/webstore.hu.txt b/platform/mv3/description/webstore.hu.txt index 4d54e2f1f1aa5..bc2d5f2e77fba 100644 --- a/platform/mv3/description/webstore.hu.txt +++ b/platform/mv3/description/webstore.hu.txt @@ -1,4 +1,4 @@ -Az uBO Lite (uBOL) egy *engedélyt nem igénylő* MV3-alapú tartalomblokkoló. +A uBO Lite (uBOL) egy *engedélyt nem igénylő* MV3-alapú tartalomblokkoló. Az alapértelmezett szabálykészlet megfelel a uBlock Origin alapértelmezett szűrőkészletének: @@ -7,15 +7,15 @@ Az alapértelmezett szabálykészlet megfelel a uBlock Origin alapértelmezett s - EasyPrivacy - Peter Lowe hirdetési és nyomkövető-kiszolgálókat tartalmazó listája -További szabályokat adhat hozzá a beállítások oldalon – kattintson a _Fogaskerekek_ ikonra a felugró panelen. +További szabályokat engedélyezhet a beállítások oldalon – kattintson a _Fogaskerekek_ ikonra a felugró panelen. -Az uBOL teljes mértékben deklaratív, vagyis nincs szükség állandó uBOL folyamatra a szűréshez, és a CSS/JS injektálás-alapú tartalomszűrést maga a böngésző végzi megbízhatóan, nem pedig a kiegészítő. Ez azt jelenti, hogy az uBOL maga nem fogyaszt CPU/memória erőforrásokat, amíg a tartalom blokkolása folyamatban van – az uBOL service worker folyamatára _csak_ akkor van szükség, amikor az felugró panellel vagy a beállítási oldalakkal érintkezik. +A uBOL teljes mértékben deklaratív, vagyis nincs szükség állandó uBOL folyamatra a szűréshez, és a CSS/JS injektálás-alapú tartalomszűrést maga a böngésző végzi megbízhatóan, nem pedig a kiegészítő. Ez azt jelenti, hogy az uBOL maga nem fogyaszt CPU/memória erőforrásokat, amíg a tartalom blokkolása folyamatban van – az uBOL service worker folyamatára _csak_ akkor van szükség, amikor az felugró panellel vagy a beállítási oldalakkal érintkezik. -Az uBOL nem igényel széles körű „adatok módosítása és olvasása” engedélyt a telepítéskor, ezért korlátozott képességei vannak az uBlock Originhez vagy más tartalomblokkolókhoz képest, amelyek széles körű „adatok olvasása és módosítása” engedélyeket igényelnek a telepítésükkor. +A uBOL nem igényel széles körű „adatok módosítása és olvasása” engedélyt a telepítéskor, ezért korlátozott képességei vannak a uBlock Originhez vagy más tartalomblokkolókhoz képest, amelyek széles körű „adatok olvasása és módosítása” engedélyeket igényelnek a telepítésükkor. -Az uBOL azonban lehetővé teszi, hogy *kifejezetten* kiterjesztett engedélyeket adjon az Ön által választott bizonyos webhelyekhez, hogy jobban szűrhessen ezeken a webhelyeken kozmetikai szűréssel és szkriptlet-injekciókkal. +Az uBOL azonban lehetővé teszi, hogy *kifejezetten* kiterjesztett engedélyeket adjon az Ön által választott bizonyos webhelyeknél, hogy jobban szűrhessen ezeken a webhelyeken kozmetikai szűréssel és parancsfájlbeillesztéssel. -Ha kiterjesztett engedélyeket szeretne adni egy adott webhelyen, nyissa meg az előugró panelt, és válasszon magasabb szűrési módot, például Optimális vagy Teljes. +Ha kiterjesztett engedélyeket szeretne adni egy adott webhelyen, nyissa meg a felugró panelt, és válasszon magasabb szűrési módot, mint az Optimális vagy a Teljes. A böngésző ekkor figyelmezteti Önt a bővítmény által kért további engedélyek megadásának hatásaira az aktuális webhelyen, és közölnie kell a böngészővel, hogy elfogadja-e vagy elutasítja a kérést. @@ -23,8 +23,8 @@ Ha elfogadja az uBOL további engedélyekre vonatkozó kérését az aktuális w Az alapértelmezett szűrési módot az uBOL beállítási oldalán állíthatja be. Ha az Optimális vagy a Teljes módot választja alapértelmezettként, akkor az uBOL-nak engedélyt kell adnia az adatok olvasására és módosítására az összes webhelyen. -Ne feledje, hogy ez még folyamatban van, a következő célokkal: +Ne feledje, hogy ez még egy folyamatban lévő fejlesztés, a következő célokkal: -- Nincsenek széles körű gazdagép-engedélyek a telepítés során – a kiterjesztett engedélyeket a felhasználó kifejezetten webhelyenként adja meg. +- Nincsenek széles körű kiszolgálókra vonatkozó engedélyek a telepítés során – a kiterjesztett engedélyeket a felhasználó kifejezetten webhelyenként adja meg. -- Teljesen deklaratív a nagyobb megbízhatóság illetve CPU- és memóriahatékonyság érdekében. +- Teljesen deklaratív a nagyobb megbízhatóság, illetve CPU- és memóriahatékonyság érdekében. diff --git a/platform/mv3/description/webstore.hy.txt b/platform/mv3/description/webstore.hy.txt index c847ac69df042..cf9ca080ecd5c 100644 --- a/platform/mv3/description/webstore.hy.txt +++ b/platform/mv3/description/webstore.hy.txt @@ -7,7 +7,7 @@ uBO Lite (uBOL)-ը բովանդակության արգելափակիչ է, որ - EasyPrivacy - Peter Lowe-ի գովազդային և հետագծող սպասարկիչների ցուցակ -Դուք կարող եք ավելացնել ուրիշ կանոններ՝ այցելելով ընտրանքների էջը. կտտացրեք Ժանանվակի_պատկերակին դուրս լողացող վահանակում։ +You can enable more rulesets by visiting the options page -- click the _Cogs_ icon in the popup panel. uBOL-ն ամբողջությամբ դեկլարատիվ է, այսինքն՝ զտման համար անընդհատ կատարվող uBOL գործընթացի կարիք չկա, իսկ CSS/JS արմատավորման վրա հիմնված բովանդակության զտումը հուսալիորեն իրականացվում է զննիչի կողմից, այլ ոչ թե ընդլայնման միջոցով։ Սա նշանակում է, որ uBOL հավելումը չի սպառում մշակիչի/հիշողության որևէ ռեսուրս, երբ տեղի է ունենում գովազդի արգելափակումը. uBOL աշխատանքային գործընթացն աշխատում է _միայն_ երբ Դուք փոփոխություններ եք կատարում դուրս լողացող վահանակում կամ ընտրանքների էջում։ diff --git a/platform/mv3/description/webstore.id.txt b/platform/mv3/description/webstore.id.txt index 8d6c7b8b15376..b9ba140a727ec 100644 --- a/platform/mv3/description/webstore.id.txt +++ b/platform/mv3/description/webstore.id.txt @@ -7,7 +7,7 @@ Kumpulan aturan bawaan sesuai dengan kumpulan penyaringan bawaan uBlock Origin: - EasyPrivacy - Daftar server iklan dan pelacak Peter Lowe -Anda dapat menambahkan ruleset dengan mengunjungi halaman options -- klik ikon _Cogs_ di panel popup. +You can enable more rulesets by visiting the options page -- click the _Cogs_ icon in the popup panel. uBOL sepenuhnya deklaratif, yang mana tidak membutuhkan proses permanen uBOL agar penyaringan dapat terjadi, dan penyaringan konten berbasis injeksi CSS/JS dilakukan sepenuhnya oleh peramban itu sendiri ketimbang oleh ekstensi. Ini berarti bahwa uBOL sendiri tidak mengkonsumsi sumber daya CPU/memori selama melakukan pemblokiran konten -- proses pekerja layanan uBOL dibutuhkan _hanya_ ketika Anda berinteraksi dengan panel popup atau opsi halaman. diff --git a/platform/mv3/description/webstore.it.txt b/platform/mv3/description/webstore.it.txt index 03f1d7122c32a..edf67f69a8798 100644 --- a/platform/mv3/description/webstore.it.txt +++ b/platform/mv3/description/webstore.it.txt @@ -7,7 +7,7 @@ L'insieme di regole predefinito corrisponde all'insieme di filtri predefinito di - EasyPrivacy - Elenco dei server pubblicitari e di tracciamento di Peter Lowe -Puoi aggiungere altri insiemi di regole visitando la pagina delle opzioni: clicca sull'icona con gli _Ingranaggi_ nel pannello a comparsa. +Puoi abilitare altri set di regole visitando la pagina delle opzioni: clicca sull'icona _Cogs_ nel pannello a comparsa. uBOL è interamente dichiarativo, ovvero non è necessario che ci sia un processo di uBOL permanente per poter eseguire il filtraggio; e il filtraggio dei contenuti basato sull'iniezione di elementi CSS/JS viene eseguito in modo affidabile dal browser stesso piuttosto che dall'estensione. Ciò significa che lo stesso uBOL non consuma risorse di CPU o memoria mentre il blocco dei contenuti viene eseguito: il processo che esegue il servizio di uBOL è richiesto _solamente_ quando interagisci con il pannello a comparsa o con le pagine delle opzioni. diff --git a/platform/mv3/description/webstore.ja.txt b/platform/mv3/description/webstore.ja.txt index 5ae015f74dd89..c6ef50e37d00b 100644 --- a/platform/mv3/description/webstore.ja.txt +++ b/platform/mv3/description/webstore.ja.txt @@ -7,7 +7,7 @@ uBO Lite (uBOL) は権限を必要としない MV3 ベースのコンテンツ - EasyPrivacy - Peter Lowe’s Ad and tracking server list -オプションページでルールセットを追加できます -- ポップアップ パネルの「歯車」アイコンをクリックします。 +オプションページでルールセットを有効化できます。ポップアップパネルの「歯車」アイコンをクリックしてください。 uBOL は完全に宣言的です。つまり、フィルタリングを行うための恒久的な uBOL プロセスは必要なく、CSS/JS インジェクション ベースのコンテンツフィルタリングは拡張機能ではなくブラウザによって、確実に実行されます。 これは uBOL がコンテンツブロッキングの際に CPU、メモリを消費しないことを意味します。uBOL のサービス ワーカーは ポップアップ パネルや設定ページでのみ必要とされます。 diff --git a/platform/mv3/description/webstore.ka.txt b/platform/mv3/description/webstore.ka.txt index ffcc9851bd291..7fb68dd2508b8 100644 --- a/platform/mv3/description/webstore.ka.txt +++ b/platform/mv3/description/webstore.ka.txt @@ -7,7 +7,7 @@ uBO Lite (uBOL) *ნებართვებისგან თავისუ - EasyPrivacy - Peter Lowe – სარეკლამო სერვერების სია -შეგიძლიათ სხვა კრებულებიც დაამატეთ პარამეტრების გვერდიდან -- დაწკაპეთ _Cogs_ ხატულაზე ამომხტომ არეში. +You can enable more rulesets by visiting the options page -- click the _Cogs_ icon in the popup panel. uBOL სრულად დეკლარაციულია, ანუ არაა საჭირო მუდმივად იყოს გაშვებული uBOL-პროცესი გასაფილტრად, CSS/JS ჩანაცვლებით შიგთავსის გაფილტვრას თავად ბრაუზერი უზრუნველყოფს ნაცვლად გაფართოებისა, რაც მეტად საიმედოა. შესაბამისად, uBOL თავად არ დატვირთავს პროცესორს/ოპერატიულს შიგთავსის შეზღუდვის დროს -- uBOL-ის შუამავალი მომსახურე პროცესი საჭიროა _მხოლოდ_ მაშინ, როცა ამომხტომ არესთან ურთიერთქმედებთ ან ცვლით პარამეტრებს. diff --git a/platform/mv3/description/webstore.kk.txt b/platform/mv3/description/webstore.kk.txt index e03fa801ee7f0..7669a5939f98c 100644 --- a/platform/mv3/description/webstore.kk.txt +++ b/platform/mv3/description/webstore.kk.txt @@ -7,7 +7,7 @@ The default ruleset corresponds to uBlock Origin's default filterset: - EasyPrivacy - Peter Lowe’s Ad and tracking server list -You can add more rulesets by visiting the options page -- click the _Cogs_ icon in the popup panel. +You can enable more rulesets by visiting the options page -- click the _Cogs_ icon in the popup panel. uBOL is entirely declarative, meaning there is no need for a permanent uBOL process for the filtering to occur, and CSS/JS injection-based content filtering is performed reliably by the browser itself rather than by the extension. This means that uBOL itself does not consume CPU/memory resources while content blocking is ongoing -- uBOL's service worker process is required _only_ when you interact with the popup panel or the option pages. diff --git a/platform/mv3/description/webstore.kn.txt b/platform/mv3/description/webstore.kn.txt index 535ab9a320dc5..3a7ef1f211ced 100644 --- a/platform/mv3/description/webstore.kn.txt +++ b/platform/mv3/description/webstore.kn.txt @@ -7,7 +7,7 @@ The default ruleset corresponds to uBlock Origin's default filterset: - EasyPrivacy - Peter Lowe’s Ad and tracking server list -You can add more rulesets by visiting the options page -- click the _Cogs_ icon in the popup panel. +You can enable more rulesets by visiting the options page -- click the _Cogs_ icon in the popup panel. uBOL is entirely declarative, meaning there is no need for a permanent uBOL process for the filtering to occur, and CSS/JS injection-based content filtering is performed reliably by the browser itself rather than by the extension. ಇದರರ್ಥ ವಿಷಯ ನಿರ್ಬಂಧಿಸುವಿಕೆಯು ನಡೆಯುತ್ತಿರುವಾಗ uBOL ಸ್ವತಃ CPU/ಮೆಮೊರಿ ಸಂಪನ್ಮೂಲಗಳನ್ನು ಬಳಸುವುದಿಲ್ಲ -- ನೀವು ಪಾಪ್ಅಪ್ ಪ್ಯಾನೆಲ್ ಅಥವಾ ಆಯ್ಕೆಯ ಪುಟಗಳೊಂದಿಗೆ ಸಂವಹನ ನಡೆಸಿದಾಗ uBOL ನ ಸೇವಾ ವರ್ಕರ್ ಪ್ರಕ್ರಿಯೆಯು _ಮಾತ್ರಾ_ ಅಗತ್ಯವಿದೆ. diff --git a/platform/mv3/description/webstore.ko.txt b/platform/mv3/description/webstore.ko.txt index c23291c36032e..37bf49d9252ff 100644 --- a/platform/mv3/description/webstore.ko.txt +++ b/platform/mv3/description/webstore.ko.txt @@ -7,7 +7,7 @@ uBO Lite(uBOL)는 *적은 권한을 요구하는* MV3 기반 콘텐츠 차단기 - EasyPrivacy - Peter Lowe’s Ad and tracking server list -설정 페이지에서 규칙 목록을 더욱 추가할 수 있습니다. 팝업 창의 _Cogs_ 아이콘을 누르세요. +You can enable more rulesets by visiting the options page -- click the _Cogs_ icon in the popup panel. uBOL은 완전히 선언적이라 필터링 중 영구적으로 실행되는 uBOL 프로세스를 필요로 하지 않으며, CSS/JS 주입 기반 콘텐츠 필터링이 확장 프로그램이 아닌 브라우저 자체에서 더욱 안정적으로 동작합니다. 즉 uBOL 자체는 콘텐츠 차단을 하는 동안 CPU/메모리 리소스를 소비하지 않습니다. uBOL 서비스워커 프로세스는 사용자가 팝업 창이나 설정을 열었을 _때에만_ 동작합니다. diff --git a/platform/mv3/description/webstore.lt.txt b/platform/mv3/description/webstore.lt.txt index e03fa801ee7f0..7669a5939f98c 100644 --- a/platform/mv3/description/webstore.lt.txt +++ b/platform/mv3/description/webstore.lt.txt @@ -7,7 +7,7 @@ The default ruleset corresponds to uBlock Origin's default filterset: - EasyPrivacy - Peter Lowe’s Ad and tracking server list -You can add more rulesets by visiting the options page -- click the _Cogs_ icon in the popup panel. +You can enable more rulesets by visiting the options page -- click the _Cogs_ icon in the popup panel. uBOL is entirely declarative, meaning there is no need for a permanent uBOL process for the filtering to occur, and CSS/JS injection-based content filtering is performed reliably by the browser itself rather than by the extension. This means that uBOL itself does not consume CPU/memory resources while content blocking is ongoing -- uBOL's service worker process is required _only_ when you interact with the popup panel or the option pages. diff --git a/platform/mv3/description/webstore.lv.txt b/platform/mv3/description/webstore.lv.txt index 078cf7f4f9b48..b2b17768b8fad 100644 --- a/platform/mv3/description/webstore.lv.txt +++ b/platform/mv3/description/webstore.lv.txt @@ -7,7 +7,7 @@ Noklusējuma nosacījumu kopa atbilst uBokc Origin noklusējuma aizturēšanas k - EasyPrivacy - Pētera Lova (Peter Lowe) reklāmu un izsakošanas serveru saraksts -Vairāk nosacījumu kopu var pievienot iestatījumu sadaļā -- jāklikšķina _Zobratu_ ikona uznirstošajā logā. +Vairāk nosacījumu kopu var iespējot iestatījumu sadaļā -- jāklikšķina _Zobratu_ ikona uznirstošajā logā. uBOL ir pilnībā vispārīgs, kas nozīmē, ka nav nepieciešamības pēc pastāvīga uBOL procesa, lai notiktu aizturēšana, un uz CSS/JS ievietošanu balstīta satura aizturēšanu uzticami veic pārlūks, nevis paplašinājums. Tas nozīmē, ka uBOL pats par sevi neizmanto procesoru un atmiņu, kamēr satura aizturēšana ir notiekoša -- uBOL pakalpojuma strādņa process ir nepieciešams _tikai_ tad, kad notiek mijiedarbība ar uznirstošo logu vai iestatījumu sadaļām. diff --git a/platform/mv3/description/webstore.mk.txt b/platform/mv3/description/webstore.mk.txt index e03fa801ee7f0..7669a5939f98c 100644 --- a/platform/mv3/description/webstore.mk.txt +++ b/platform/mv3/description/webstore.mk.txt @@ -7,7 +7,7 @@ The default ruleset corresponds to uBlock Origin's default filterset: - EasyPrivacy - Peter Lowe’s Ad and tracking server list -You can add more rulesets by visiting the options page -- click the _Cogs_ icon in the popup panel. +You can enable more rulesets by visiting the options page -- click the _Cogs_ icon in the popup panel. uBOL is entirely declarative, meaning there is no need for a permanent uBOL process for the filtering to occur, and CSS/JS injection-based content filtering is performed reliably by the browser itself rather than by the extension. This means that uBOL itself does not consume CPU/memory resources while content blocking is ongoing -- uBOL's service worker process is required _only_ when you interact with the popup panel or the option pages. diff --git a/platform/mv3/description/webstore.ml.txt b/platform/mv3/description/webstore.ml.txt index 9237bc276067f..547aa6234890f 100644 --- a/platform/mv3/description/webstore.ml.txt +++ b/platform/mv3/description/webstore.ml.txt @@ -7,7 +7,7 @@ uBO Lite (uBOL) ഒരു *അനുമതി-കുറവ്* MV3 അടിസ - ഈസി സ്വകാര്യത - പീറ്റർ ലോവിന്റെ പരസ്യവും ട്രാക്കിംഗ് സെർവർ ലിസ്റ്റും -ഓപ്ഷനുകൾ പേജ് സന്ദർശിച്ച് നിങ്ങൾക്ക് കൂടുതൽ നിയമങ്ങൾ ചേർക്കാൻ കഴിയും -- പോപ്പ്അപ്പ് പാനലിലെ _Cogs_ ഐക്കണിൽ ക്ലിക്കുചെയ്യുക. +You can enable more rulesets by visiting the options page -- click the _Cogs_ icon in the popup panel. uBOL പൂർണ്ണമായും ഡിക്ലറേറ്റീവ് ആണ്, അതായത് ഫിൽട്ടറിംഗ് സംഭവിക്കുന്നതിന് ഒരു സ്ഥിരമായ uBOL പ്രക്രിയയുടെ ആവശ്യമില്ല, കൂടാതെ CSS/JS ഇഞ്ചക്ഷൻ അടിസ്ഥാനമാക്കിയുള്ള ഉള്ളടക്ക ഫിൽട്ടറിംഗ്, എക്സ്റ്റൻഷനേക്കാൾ വിശ്വസനീയമായി ബ്രൗസർ തന്നെ നിർവഹിക്കുന്നു. ഉള്ളടക്കം തടയൽ നടന്നുകൊണ്ടിരിക്കുമ്പോൾ uBOL തന്നെ CPU/മെമ്മറി ഉറവിടങ്ങൾ ഉപയോഗിക്കില്ല എന്നാണ് ഇതിനർത്ഥം -- നിങ്ങൾ പോപ്പ്അപ്പ് പാനലുമായോ ഓപ്‌ഷൻ പേജുകളുമായോ സംവദിക്കുമ്പോൾ _only_ uBOL-ന്റെ സേവന വർക്കർ പ്രോസസ്സ് ആവശ്യമാണ്. diff --git a/platform/mv3/description/webstore.mr.txt b/platform/mv3/description/webstore.mr.txt index e03fa801ee7f0..7669a5939f98c 100644 --- a/platform/mv3/description/webstore.mr.txt +++ b/platform/mv3/description/webstore.mr.txt @@ -7,7 +7,7 @@ The default ruleset corresponds to uBlock Origin's default filterset: - EasyPrivacy - Peter Lowe’s Ad and tracking server list -You can add more rulesets by visiting the options page -- click the _Cogs_ icon in the popup panel. +You can enable more rulesets by visiting the options page -- click the _Cogs_ icon in the popup panel. uBOL is entirely declarative, meaning there is no need for a permanent uBOL process for the filtering to occur, and CSS/JS injection-based content filtering is performed reliably by the browser itself rather than by the extension. This means that uBOL itself does not consume CPU/memory resources while content blocking is ongoing -- uBOL's service worker process is required _only_ when you interact with the popup panel or the option pages. diff --git a/platform/mv3/description/webstore.ms.txt b/platform/mv3/description/webstore.ms.txt index 2a48cf613a824..07948a0608641 100644 --- a/platform/mv3/description/webstore.ms.txt +++ b/platform/mv3/description/webstore.ms.txt @@ -7,7 +7,7 @@ Set peraturan lalai sepadan dengan set penapis lalai uBlock Origin: - EasyPrivacy - Senarai pelayan iklan dan penjejakan 'Peter Lowe' -Anda boleh menambah lebih banyak set peraturan dengan melawat halaman pilihan -- klik the _Cogs_ icon pada panel timbul. +You can enable more rulesets by visiting the options page -- click the _Cogs_ icon in the popup panel. uBOL adalah deklaratif sepenuhnya, bermakna tidak ada keperluan untuk proses uBOL kekal untuk penapisan berlaku, dan penapisan kandungan berasaskan suntikan CSS/JS dilakukan sepenuhnya oleh penyemak imbas itu sendiri dan bukannya oleh sambungan. Ini bermakna uBOL sendiri tidak menggunakan sumber CPU/memori semasa penyekatan kandungan sedang berjalan -- proses pekerja perkhidmatan uBOL diperlukan _hanya_ apabila anda berinteraksi dengan panel timbul atau halaman pilihan. diff --git a/platform/mv3/description/webstore.nb.txt b/platform/mv3/description/webstore.nb.txt index c45f9a527c080..756166aba704d 100644 --- a/platform/mv3/description/webstore.nb.txt +++ b/platform/mv3/description/webstore.nb.txt @@ -7,7 +7,7 @@ Standardregelsettet tilsvarer standardfiltersettet til uBlock Origin: - EasyPrivacy - Peter Lowe’s Ad and tracking server list -Du kan legge til flere regelsett ved å gå til innstillingssiden -- klikk _Tannhjul_-ikonet i oppsprettspanelet. +You can enable more rulesets by visiting the options page -- click the _Cogs_ icon in the popup panel. uBOL er fullstendig deklarativ, noe som betyr at det ikke er behov for en permanent uBOL-prosess for at filtreringen skal skje, og CSS/JS-injeksjonsbasert innholdsfiltrering utføres pålitelig av nettleseren selv i stedet for av utvidelsen. Dette betyr at uBOL selv ikke bruker CPU/minneressurser mens innholdsblokkering pågår -- uBOL's service worker-prosess kreves _bare_ når du samhandler med oppsprettspanelet eller innstillingssidene. diff --git a/platform/mv3/description/webstore.nl.txt b/platform/mv3/description/webstore.nl.txt index ecdf0c3465e51..f94d6521586a9 100644 --- a/platform/mv3/description/webstore.nl.txt +++ b/platform/mv3/description/webstore.nl.txt @@ -7,7 +7,7 @@ De standaard regelset komt overeen met de standaard filterset van uBlock Origin: - EasyPrivacy - Peter Lowe’s Ad and tracking server list -U kunt meer regelsets toevoegen door de optiespagina te bezoeken -- klik op het _tandwielpictogram_ in het pop-uppaneel. +U kunt meer regelsets inschakelen door de optiespagina te bezoeken -- klik hiervoor op het _tandwielpictogram_ in het pop-uppaneel. uBOL is volledig declaratief, wat betekent dat er geen permanent uBOL-proces voor de filtering nodig is, en inhoudsfiltering op basis van CSS/JS-injectie op een betrouwbare manier door de browser zelf wordt uitgevoerd in plaats van door de extensie. Dit betekent dat uBOL zelf geen CPU-/geheugenbronnen gebruikt terwijl inhoudsblokkering actief is -- het serviceworker-proces van uBOL is _alleen_ vereist als u met het pop-uppaneel of de optiespagina’s werkt. diff --git a/platform/mv3/description/webstore.oc.txt b/platform/mv3/description/webstore.oc.txt index e03fa801ee7f0..7669a5939f98c 100644 --- a/platform/mv3/description/webstore.oc.txt +++ b/platform/mv3/description/webstore.oc.txt @@ -7,7 +7,7 @@ The default ruleset corresponds to uBlock Origin's default filterset: - EasyPrivacy - Peter Lowe’s Ad and tracking server list -You can add more rulesets by visiting the options page -- click the _Cogs_ icon in the popup panel. +You can enable more rulesets by visiting the options page -- click the _Cogs_ icon in the popup panel. uBOL is entirely declarative, meaning there is no need for a permanent uBOL process for the filtering to occur, and CSS/JS injection-based content filtering is performed reliably by the browser itself rather than by the extension. This means that uBOL itself does not consume CPU/memory resources while content blocking is ongoing -- uBOL's service worker process is required _only_ when you interact with the popup panel or the option pages. diff --git a/platform/mv3/description/webstore.pa.txt b/platform/mv3/description/webstore.pa.txt index b413fe39a4862..bcd747436bbfe 100644 --- a/platform/mv3/description/webstore.pa.txt +++ b/platform/mv3/description/webstore.pa.txt @@ -7,7 +7,7 @@ uBO Lite (uBOL) ਇੱਕ *ਬਿਨਾਂ ਇਜਾਜ਼ਤਾਂ* ਵਾਲਾ M -ਸੌਖੀ ਪਰਦੇਦਾਰੀ - Peter Lowe ਦੀ ਇਸ਼ਤਿਹਾਰ ਅਤੇ ਟਰੈਕਿੰਗ ਸਰਵਰ ਸੂਚੀ -You can add more rulesets by visiting the options page -- click the _Cogs_ icon in the popup panel. +ਤੁਸੀਂ ਚੋਣਾਂ ਸਫ਼ੇ ਨੂੰ ਖੋਲ੍ਹ ਕੇ ਹੋਰ ਰੂਲ-ਸੈੱਟ ਸਮਰੱਥ ਕਰ ਕਦੇ ਹੋ -- ਪੌਪ-ਅੱਪ ਪੈਨਲ ਵਿੱਚ _Cogs_ icon ਨੂੰ ਕਲਿੱਕ ਕਰੋ। uBOL is entirely declarative, meaning there is no need for a permanent uBOL process for the filtering to occur, and CSS/JS injection-based content filtering is performed reliably by the browser itself rather than by the extension. This means that uBOL itself does not consume CPU/memory resources while content blocking is ongoing -- uBOL's service worker process is required _only_ when you interact with the popup panel or the option pages. @@ -19,7 +19,7 @@ To grant extended permissions on a given site, open the popup panel and pick a h The browser will then warn you about the effects of granting the additional permissions requested by the extension on the current site, and you will have to tell the browser whether you accept or decline the request. -If you accept uBOL's request for additional permissions on the current site, it will be able to better filter content for the current site. +ਜੇ ਤੁਸੀਂ ਮੌਜੂਦਾ ਸਾਈਟਾਂ ਉੱਤੇ ਹੋਰ ਇਜਾਜਤਾਂ ਲਈ uBOL ਦੀ ਬੇਨਤੀ ਨੂੰ ਮਨਜ਼ੂਰ ਕੀਤਾ ਤਾਂ ਇਹ ਮੌਜੂਦਾ ਸਾਈਟ ਬਾਰੇ ਵਧੀਆ ਫਿਲਟਰ ਸਮੱਗਰੀ ਨੂੰ ਸਮਰੱਥ ਕਰੇਗੀ। You can set the default filtering mode from uBOL's options page. If you pick the Optimal or Complete mode as the default one, you will need to grant uBOL the permission to read and modify data on all websites. diff --git a/platform/mv3/description/webstore.pl.txt b/platform/mv3/description/webstore.pl.txt index 4f5c5247c872f..a6a17bc2efc8c 100644 --- a/platform/mv3/description/webstore.pl.txt +++ b/platform/mv3/description/webstore.pl.txt @@ -7,7 +7,7 @@ Domyślny zestaw reguł odpowiada domyślnemu zestawowi filtrów uBlock Origin: – EasyPrivacy – lista serwerów śledzących i reklam Petera Lowe'a -Możesz dodać więcej zestawów reguł, odwiedzając stronę opcji – kliknij ikonę _koła zębatego_ w wyskakującym panelu. +Możesz włączyć więcej zestawów reguł, odwiedzając stronę opcji – kliknij ikonę _koła zębatego_ w wyskakującym panelu. uBOL jest całkowicie deklaratywny, co oznacza, że nie jest potrzebny stały proces uBOL w celu filtrowania, a filtrowanie treści oparte na wstrzykiwaniu CSS/JS jest wykonywane niezawodnie przez samą przeglądarkę, a nie przez rozszerzenie. Oznacza to, że sam uBOL nie zużywa zasobów procesora/pamięci, gdy trwa blokowanie treści – proces Service Worker uBOL jest wymagany _tylko_ podczas interakcji z panelem wyskakującym lub stronami opcji. diff --git a/platform/mv3/description/webstore.pt_BR.txt b/platform/mv3/description/webstore.pt_BR.txt index 884ec649a4499..1478cb2af743e 100644 --- a/platform/mv3/description/webstore.pt_BR.txt +++ b/platform/mv3/description/webstore.pt_BR.txt @@ -7,7 +7,7 @@ O conjunto de regras padrão corresponde ao conjunto de filtros padrão do uBloc - EasyPrivacy - Lista de servidores de anúncios e rastreadores do Peter Lowe -Você pode adicionar mais conjuntos de regras visitando a página das opções -- clique no ícone _Engrenagens_ no painel do pop-up. +Você pode ativar mais conjuntos de regras visitando a página das opções — clique no ícone da _Engranagem_ no painel do popup. O uBOL é totalmente declarativo, significando que não há necessidade de um processo permanente do uBOL para a filtragem ocorrer e a filtragem de conteúdo baseada em injeção do CSS/JS é realizada confiavelmente pelo próprio navegador ao invés da extensão. Isto significa que o próprio uBOL não consome recursos de CPU/memória enquanto o bloqueio de conteúdo está em andamento -- o processo do service worker do uBOL _só_ é necessário quando você interage com o painel do pop-up ou as páginas das opções. diff --git a/platform/mv3/description/webstore.pt_PT.txt b/platform/mv3/description/webstore.pt_PT.txt index fcd3823801406..56560c1d7515d 100644 --- a/platform/mv3/description/webstore.pt_PT.txt +++ b/platform/mv3/description/webstore.pt_PT.txt @@ -7,7 +7,7 @@ O conjunto de regras padrão corresponde ao conjunto de filtros padrão do uBloc - EasyPrivacy - Peter Lowe’s Ad and tracking server list -Pode adicionar mais conjuntos de regras visitando a página de opções -- clique no ícone _Cogs_ na janela flutuante. +Pode ativar mais conjuntos de regras visitando a página de opções -- clique no ícone _Cogs_ na janela flutuante. O uBOL é totalmente declarativo, o que elimina a necessidade de um processo ativo constante para a filtragem ocorrer. A injeção de CSS e JS para filtragem de conteúdo é efetuada de maneira confiável pelo navegador, não dependendo da extensão. Isso significa que o uBOL por si só não gasta recursos de CPU/memória enquanto o bloqueio de conteúdo está a acontecer -- o processo do trabalhador de serviço do uBOL é necessário apenas quando se interage com a janela flutuante ou as páginas de opções. diff --git a/platform/mv3/description/webstore.ro.txt b/platform/mv3/description/webstore.ro.txt index 6e1f05d38f5f0..61c81d41a5d24 100644 --- a/platform/mv3/description/webstore.ro.txt +++ b/platform/mv3/description/webstore.ro.txt @@ -7,7 +7,7 @@ Listele de filtre încorporate de uBlock Origin - EasyPrivacy - Oglas Peter Lowe i lista servera za praćenje -Puteți adăuga mai multe seturi de reguli vizitând pagina de opțiuni -- făcând clic pe pictograma _Cogs_ din panoul pop-up +You can enable more rulesets by visiting the options page -- click the _Cogs_ icon in the popup panel. uBOL este în întregime declarativ, ceea ce înseamnă că nu este nevoie de un proces uBOL permanent pentru ca filtrarea să aibă loc, iar filtrarea conținutului pe bază de injecție CSS/JS este realizată în mod sigur de browser în sine, mai degrabă decât de extensie. Aceasta înseamnă că uBOL în sine nu consumă resurse CPU/memorie în timp ce blocarea conținutului este în desfășurare -- procesul de lucru al serviciului uBOL este necesar _doar_ atunci când interacționați cu panoul pop-up sau cu paginile de opțiuni. diff --git a/platform/mv3/description/webstore.ru.txt b/platform/mv3/description/webstore.ru.txt index 637d7cb444841..4abdcf400e533 100644 --- a/platform/mv3/description/webstore.ru.txt +++ b/platform/mv3/description/webstore.ru.txt @@ -7,7 +7,7 @@ uBO Lite (uBOL) — это блокировщик содержимого, *не - EasyPrivacy - Список рекламных и отслеживающих серверов от Peter Lowe -Вы можете добавить больше правил, посетив страницу настроек -- нажмите на значок_Шестеренок на всплывающей панели. +Вы можете активировать больше списков правил на странице настроек -- нажмите на значок _Шестерёнки_ на всплывающей панели. uBOL - полностью декларативный, т.е. для фильтрации не нужен постоянно выполняющийся uBOL процесс, а фильтрация контента, основанная на внедрении CSS/JS, производится непосредственно браузером. Это значит, что дополнение uBOL не расходует ресурсы ЦПУ/памяти, когда происходит блокировка рекламы -- служебный процесс uBOL запускается, _только_ когда вы вносите изменения на всплывающей панели или странице настроек. diff --git a/platform/mv3/description/webstore.si.txt b/platform/mv3/description/webstore.si.txt index e03fa801ee7f0..7669a5939f98c 100644 --- a/platform/mv3/description/webstore.si.txt +++ b/platform/mv3/description/webstore.si.txt @@ -7,7 +7,7 @@ The default ruleset corresponds to uBlock Origin's default filterset: - EasyPrivacy - Peter Lowe’s Ad and tracking server list -You can add more rulesets by visiting the options page -- click the _Cogs_ icon in the popup panel. +You can enable more rulesets by visiting the options page -- click the _Cogs_ icon in the popup panel. uBOL is entirely declarative, meaning there is no need for a permanent uBOL process for the filtering to occur, and CSS/JS injection-based content filtering is performed reliably by the browser itself rather than by the extension. This means that uBOL itself does not consume CPU/memory resources while content blocking is ongoing -- uBOL's service worker process is required _only_ when you interact with the popup panel or the option pages. diff --git a/platform/mv3/description/webstore.sk.txt b/platform/mv3/description/webstore.sk.txt index 686e41f918185..1b2ef413a7b7f 100644 --- a/platform/mv3/description/webstore.sk.txt +++ b/platform/mv3/description/webstore.sk.txt @@ -7,7 +7,7 @@ Predvolený súbor pravidiel zodpovedá predvolenému súboru filtrov uBlock Ori - EasyPrivacy - Zoznam reklamných a sledovacích serverov Petra Lowea -Ďalšie súbory pravidiel môžete pridať na stránke s možnosťami – kliknite na ikonu _súkolesia_ vo vyskakovacom paneli. +Ďalšie súbory pravidiel môžete povoliť na stránke s možnosťami – kliknite na ikonu _súkolesia_ vo vyskakovacom paneli. uBOL je úplne deklaratívny, čo znamená, že na filtrovanie nie je potrebný trvalý proces uBOL a filtrovanie obsahu založené na injektovaní CSS/JS spoľahlivo vykonáva samotný prehliadač, a nie rozšírenie. To znamená, že samotný uBOL nespotrebúva zdroje CPU/pamäte, kým prebieha blokovanie obsahu -- proces uBOL Service Worker je potrebný _len_ pri interakcii s vyskakovacím panelom alebo stránkami možností. diff --git a/platform/mv3/description/webstore.sl.txt b/platform/mv3/description/webstore.sl.txt index e03fa801ee7f0..7669a5939f98c 100644 --- a/platform/mv3/description/webstore.sl.txt +++ b/platform/mv3/description/webstore.sl.txt @@ -7,7 +7,7 @@ The default ruleset corresponds to uBlock Origin's default filterset: - EasyPrivacy - Peter Lowe’s Ad and tracking server list -You can add more rulesets by visiting the options page -- click the _Cogs_ icon in the popup panel. +You can enable more rulesets by visiting the options page -- click the _Cogs_ icon in the popup panel. uBOL is entirely declarative, meaning there is no need for a permanent uBOL process for the filtering to occur, and CSS/JS injection-based content filtering is performed reliably by the browser itself rather than by the extension. This means that uBOL itself does not consume CPU/memory resources while content blocking is ongoing -- uBOL's service worker process is required _only_ when you interact with the popup panel or the option pages. diff --git a/platform/mv3/description/webstore.so.txt b/platform/mv3/description/webstore.so.txt index e03fa801ee7f0..7669a5939f98c 100644 --- a/platform/mv3/description/webstore.so.txt +++ b/platform/mv3/description/webstore.so.txt @@ -7,7 +7,7 @@ The default ruleset corresponds to uBlock Origin's default filterset: - EasyPrivacy - Peter Lowe’s Ad and tracking server list -You can add more rulesets by visiting the options page -- click the _Cogs_ icon in the popup panel. +You can enable more rulesets by visiting the options page -- click the _Cogs_ icon in the popup panel. uBOL is entirely declarative, meaning there is no need for a permanent uBOL process for the filtering to occur, and CSS/JS injection-based content filtering is performed reliably by the browser itself rather than by the extension. This means that uBOL itself does not consume CPU/memory resources while content blocking is ongoing -- uBOL's service worker process is required _only_ when you interact with the popup panel or the option pages. diff --git a/platform/mv3/description/webstore.sq.txt b/platform/mv3/description/webstore.sq.txt index 6b087c3c404ac..197520dde7b87 100644 --- a/platform/mv3/description/webstore.sq.txt +++ b/platform/mv3/description/webstore.sq.txt @@ -7,7 +7,7 @@ Rregullat e tij janë të barasvlershme me filtrat standardë që përdor uBlock - EasyPrivacy - Lista e Peter Lowe për reklamat dhe gjurmuesit -Në faqen e opsioneve mund të shtoni rregulla të tjera – klikoni ikonën e _ingranazhit_ në panelin modal. +Në faqen e opsioneve mund të aktivizoni rregulla të tjera – klikoni ikonën e _ingranazhit_ në panelin modal. uBOL është tërësisht deklarativ, domethënë filtrimi ndodh pa qenë nevoja që procesi i uBOL të vijojë vazhdimisht në sfond, ndërsa injektimi i filtrave CSS/JS te materialet kryhet me saktësi nga vetë shfletuesi. Pra, uBOL i bllokon materialet pa konsumuar resurset e procesorit/memories – asetet e uBOL nevojiten _vetëm_ kur ndërveproni me panelin modal ose faqen e opsioneve të tij. diff --git a/platform/mv3/description/webstore.sr.txt b/platform/mv3/description/webstore.sr.txt index 065d6c0249db2..da4d7dd90cfa1 100644 --- a/platform/mv3/description/webstore.sr.txt +++ b/platform/mv3/description/webstore.sr.txt @@ -7,7 +7,7 @@ uBO Lite (uBOL) је блокатор садржаја *без дозвола*, - EasyPrivacy - Peter Lowe’s Ad and tracking server list -Можете додати још скупова правила тако што ћете посетити страницу са опцијама - кликните на иконицу зупчаника у искачућем панелу. +Можете омогућити још скупова правила тако што ћете посетити страницу са опцијама -- кликните на иконицу зупчаника у искачућем панелу. uBOL је потпуно декларативан, што значи да нема потребе за трајним uBOL процесом да би дошло до филтрирања, а филтрирање садржаја засновано на убацивању CSS/JS се обавља поуздано од стране самог прегледача, а не проширења. То значи да сам uBOL не троши CPU/меморијске ресурсе док је блокирање садржаја у току -- сервисни радни процес uBOL-а је потребан _само_ када ступите у интеракцију са искачућим панелом или страницама опција. diff --git a/platform/mv3/description/webstore.sv.txt b/platform/mv3/description/webstore.sv.txt index 0355bd0ae45b3..50266fdc13d30 100644 --- a/platform/mv3/description/webstore.sv.txt +++ b/platform/mv3/description/webstore.sv.txt @@ -7,7 +7,7 @@ Standardregeluppsättningen motsvarar uBlock Origins standardfilteruppsättning: - EasyPrivacy - Peter Lowes reklam- och spårningsserverlista -Du kan lägga till fler regeluppsättningar genom att besöka alternativsidan -- klicka på ikonen _Kugghjulet_ i popup-panelen. +Du kan lägga till fler regeluppsättningar i alternativ -- klicka på _Kugghjulet_ i popup-panelen. uBOL är helt deklarativt, vilket innebär att det inte finns något behov av en permanent uBOL-process för att filtreringen ska ske och CSS/JS-injektionsbaserad innehållsfiltrering utförs på ett tillförlitligt sätt av webbläsaren själv snarare än av tillägget. Detta innebär att uBOL själv inte förbrukar CPU/minnesresurser medan innehållsblockering pågår -- uBOLs serviceworkerprocess krävs _endast_ när du interagerar med popup-panelen eller alternativsidorna. diff --git a/platform/mv3/description/webstore.sw.txt b/platform/mv3/description/webstore.sw.txt index e03fa801ee7f0..7669a5939f98c 100644 --- a/platform/mv3/description/webstore.sw.txt +++ b/platform/mv3/description/webstore.sw.txt @@ -7,7 +7,7 @@ The default ruleset corresponds to uBlock Origin's default filterset: - EasyPrivacy - Peter Lowe’s Ad and tracking server list -You can add more rulesets by visiting the options page -- click the _Cogs_ icon in the popup panel. +You can enable more rulesets by visiting the options page -- click the _Cogs_ icon in the popup panel. uBOL is entirely declarative, meaning there is no need for a permanent uBOL process for the filtering to occur, and CSS/JS injection-based content filtering is performed reliably by the browser itself rather than by the extension. This means that uBOL itself does not consume CPU/memory resources while content blocking is ongoing -- uBOL's service worker process is required _only_ when you interact with the popup panel or the option pages. diff --git a/platform/mv3/description/webstore.ta.txt b/platform/mv3/description/webstore.ta.txt index e03fa801ee7f0..7669a5939f98c 100644 --- a/platform/mv3/description/webstore.ta.txt +++ b/platform/mv3/description/webstore.ta.txt @@ -7,7 +7,7 @@ The default ruleset corresponds to uBlock Origin's default filterset: - EasyPrivacy - Peter Lowe’s Ad and tracking server list -You can add more rulesets by visiting the options page -- click the _Cogs_ icon in the popup panel. +You can enable more rulesets by visiting the options page -- click the _Cogs_ icon in the popup panel. uBOL is entirely declarative, meaning there is no need for a permanent uBOL process for the filtering to occur, and CSS/JS injection-based content filtering is performed reliably by the browser itself rather than by the extension. This means that uBOL itself does not consume CPU/memory resources while content blocking is ongoing -- uBOL's service worker process is required _only_ when you interact with the popup panel or the option pages. diff --git a/platform/mv3/description/webstore.te.txt b/platform/mv3/description/webstore.te.txt index 2f0c87d02703a..f14c2563da3c5 100644 --- a/platform/mv3/description/webstore.te.txt +++ b/platform/mv3/description/webstore.te.txt @@ -7,7 +7,7 @@ The default ruleset corresponds to uBlock Origin's default filterset: - EasyPrivacy - Peter Lowe’s Ad and tracking server list -You can add more rulesets by visiting the options page -- click the _Cogs_ icon in the popup panel. +You can enable more rulesets by visiting the options page -- click the _Cogs_ icon in the popup panel. uBOL is entirely declarative, meaning there is no need for a permanent uBOL process for the filtering to occur, and CSS/JS injection-based content filtering is performed reliably by the browser itself rather than by the extension. This means that uBOL itself does not consume CPU/memory resources while content blocking is ongoing -- uBOL's service worker process is required _only_ when you interact with the popup panel or the option pages. diff --git a/platform/mv3/description/webstore.th.txt b/platform/mv3/description/webstore.th.txt index e03fa801ee7f0..7669a5939f98c 100644 --- a/platform/mv3/description/webstore.th.txt +++ b/platform/mv3/description/webstore.th.txt @@ -7,7 +7,7 @@ The default ruleset corresponds to uBlock Origin's default filterset: - EasyPrivacy - Peter Lowe’s Ad and tracking server list -You can add more rulesets by visiting the options page -- click the _Cogs_ icon in the popup panel. +You can enable more rulesets by visiting the options page -- click the _Cogs_ icon in the popup panel. uBOL is entirely declarative, meaning there is no need for a permanent uBOL process for the filtering to occur, and CSS/JS injection-based content filtering is performed reliably by the browser itself rather than by the extension. This means that uBOL itself does not consume CPU/memory resources while content blocking is ongoing -- uBOL's service worker process is required _only_ when you interact with the popup panel or the option pages. diff --git a/platform/mv3/description/webstore.tr.txt b/platform/mv3/description/webstore.tr.txt index 125ca901419c5..05bc267cfd5b7 100644 --- a/platform/mv3/description/webstore.tr.txt +++ b/platform/mv3/description/webstore.tr.txt @@ -7,7 +7,7 @@ Varsayılan kural seti, uBlock Origin'in varsayılan filtre setine karşılık g - EasyPrivacy - Peter Lowe'un Reklam ve izleme sunucusu listesi -Seçenekler sayfasını ziyaret ederek daha fazla kural seti ekleyebilirsiniz -- açılan paneldeki _Cogs_ simgesine tıklayın. +Seçenekler ekranına uğrayarak daha çok kuralı uygulatabilirsiniz, bunun için açılır paneldeki _dişli_ simgesine tıklayın. uBOL tamamen bildirimseldir, yani filtrelemenin gerçekleşmesi için kalıcı bir uBOL işlemine gerek yoktur, içerik filtreleme eklenti yerine tarayıcının kendisi tarafından CSS/JS yerleştirerek gerçekleştirilir. Bu, içerik engelleme devam ederken uBOL'nin kendisinin CPU/bellek kaynaklarını tüketmediği anlamına gelir -- uBOL'un hizmet çalışanı işlemi, _only_ açılan panel veya seçenek sayfalarıyla etkileşim kurduğunuzda gereklidir. diff --git a/platform/mv3/description/webstore.uk.txt b/platform/mv3/description/webstore.uk.txt index 799bdec1c77aa..9fbd578480a61 100644 --- a/platform/mv3/description/webstore.uk.txt +++ b/platform/mv3/description/webstore.uk.txt @@ -7,7 +7,7 @@ uBO Lite (uBOL) - це блокувальник вмісту на основі M - EasyPrivacy - Список серверів реклами та стеження від Peter Lowe -Ви можете додати більше наборів правил, перейшовши на сторінку налаштувань — натисніть на піктограму _Шестерень_ на спливній панелі. +Ви можете ввімкнути більше наборів правил, перейшовши на сторінку налаштувань — натисніть на піктограму _Шестерень_ на спливній панелі. uBOL повністю декларативний, тобто немає необхідності в постійному процесі uBOL для здійснення фільтрації, а фільтрація вмісту на основі CSS/JS-ін'єкцій надійно виконується самим браузером, а не розширенням. Це означає, що сам uBOL не споживає ресурси процесора/пам'яті під час блокування вмісту — службовий робочий процес uBOL потрібен _лише_ під час взаємодії зі спливною панеллю або сторінками опцій. diff --git a/platform/mv3/description/webstore.ur.txt b/platform/mv3/description/webstore.ur.txt index 0175f6167ba20..5aa5b35b240c1 100644 --- a/platform/mv3/description/webstore.ur.txt +++ b/platform/mv3/description/webstore.ur.txt @@ -7,7 +7,7 @@ uBO Lite (uBOL) ایک *اجازت سے کم* MV3 پر مبنی مواد بلا - EasyPrivacy - Peter Lowe’s Ad and tracking server list -You can add more rulesets by visiting the options page -- click the _Cogs_ icon in the popup panel. +You can enable more rulesets by visiting the options page -- click the _Cogs_ icon in the popup panel. uBOL is entirely declarative, meaning there is no need for a permanent uBOL process for the filtering to occur, and CSS/JS injection-based content filtering is performed reliably by the browser itself rather than by the extension. This means that uBOL itself does not consume CPU/memory resources while content blocking is ongoing -- uBOL's service worker process is required _only_ when you interact with the popup panel or the option pages. diff --git a/platform/mv3/description/webstore.vi.txt b/platform/mv3/description/webstore.vi.txt index 70d6d591f7ef3..249df6a5e8e37 100644 --- a/platform/mv3/description/webstore.vi.txt +++ b/platform/mv3/description/webstore.vi.txt @@ -7,7 +7,7 @@ Bộ quy tắc mặc định tương tự bộ lọc của uBlock Origin: - EasyPrivacy - Danh sách máy chủ chạy quảng cáo và trình theo dõi của Pete Lowe -Bạn có thể tự thêm quy tắc mới ở trang cài đặt -- click vào icon _Bánh răng_ ở trong cửa sổ popup. +You can enable more rulesets by visiting the options page -- click the _Cogs_ icon in the popup panel. uBOL mang tính khai báo hoàn toàn, vì vậy uBOL sẽ không cần phải liên tục chạy để chặn nội dung. Thay vào đó, chính trình duyệt sẽ thực hiện lọc nội dung bằng cách sử dụng công cụ chèn CSS/JS hiệu quả hơn có sẵn của nó. Điều này cũng đồng thời có nghĩa là uBOL sẽ không tiêu tốn tài nguyên CPU/bộ nhớ của bạn để chặn nội dung. uBOL sẽ chỉ chạy _khi và chỉ khi_ bạn đang xem cửa sổ popup của uBOL, hoặc bạn đang cấu hình uBOL ở trang cài đặt. diff --git a/platform/mv3/description/webstore.zh_CN.txt b/platform/mv3/description/webstore.zh_CN.txt index b307f534d48bf..413f499ea0d50 100644 --- a/platform/mv3/description/webstore.zh_CN.txt +++ b/platform/mv3/description/webstore.zh_CN.txt @@ -7,7 +7,7 @@ uBO Lite (uBOL) 是一个基于最新浏览器扩展接口(Manifest Version 3 - EasyPrivacy - Peter Lowe’s Ad and tracking server list -您可以通过设置页面添加更多过滤规则列表——点击弹出面板的“齿轮”图标。 +You can enable more rulesets by visiting the options page -- click the _Cogs_ icon in the popup panel. uBOL 的过滤规则是完全声明式的,并不需要固定保留一个 uBOL 扩展进程,基于 CSS/JS 注入的内容过滤更是交由浏览器进行调度,比起扩展本身更为可靠。 这也即是说当内容被过滤时 uBOL 自身并不占用额外 CPU 和内存资源,只有在您打开弹出面板或是设置页面时才会生成 uBOL 扩展进程。 diff --git a/platform/mv3/description/webstore.zh_TW.txt b/platform/mv3/description/webstore.zh_TW.txt index 1add144ac7cf5..8af192573f3b8 100644 --- a/platform/mv3/description/webstore.zh_TW.txt +++ b/platform/mv3/description/webstore.zh_TW.txt @@ -7,7 +7,7 @@ uBO Lite (uBOL) 是款以 MV3 為基礎的「免權限」內容阻擋器。 - EasyPrivacy - Peter Lowe’s Ad and tracking server list -您可以前往選項頁面(按下彈出面板的 **齒輪** 按鈕)新增更多規則集。 +您可以前往選項頁面(按下彈出面板的 **齒輪** 按鈕)啟用更多規則集。 uBOL 是完全宣告式的,意即過濾過程中不需要持續性的 uBOL 處理程序參與,且以 CSS/JS 注入為基礎進行的內容過濾由可靠的瀏覽器執行,而非是擴充功能。 這就代表 uBOL 在內容阻擋過程不會佔用 CPU 和記憶體資源——除了和彈出面板或選項頁面互動的場景外,都不需要 uBOL 的 Service Worker 程序。 diff --git a/platform/mv3/extension/_locales/ar/messages.json b/platform/mv3/extension/_locales/ar/messages.json index 6b6b476ca35f0..7aa0a40b9cce3 100644 --- a/platform/mv3/extension/_locales/ar/messages.json +++ b/platform/mv3/extension/_locales/ar/messages.json @@ -4,11 +4,11 @@ "description": "extension name." }, "extShortDesc": { - "message": "حاجب محتوى و بأقل التراخيص المسبقة. يحجب الإعلانات والمتتبعات والمعدنات والكثير فوراً عند التثبيت.", + "message": "أداة لحظر المحتوى بدون إذن. يحظر الإعلانات وأدوات التتبع وأدوات التعدين وغيرها فور التثبيت.", "description": "this will be in the Chrome web store: must be 132 characters or less" }, "perRulesetStats": { - "message": "{{ruleCount}} شرط محولة من {{filterCount}} مصفيّات الشبكة ", + "message": "{{ruleCount}} قواعد، محولة من {{filterCount}} مرشحات الشبكة", "description": "Appears aside each filter list in the _3rd-party filters_ pane" }, "dashboardName": { @@ -147,6 +147,10 @@ "message": "قائمة بأسماء المضيفين التي لن تتم أي تصفية لها", "description": "A short description for the editable field which lists trusted sites" }, + "noFilteringModePlaceholder": { + "message": "[hostnames only]\nexample.com\ngames.example\n...", + "description": "Default text for in edit field" + }, "behaviorSectionLabel": { "message": "السلوك", "description": "The header text for the 'Behavior' section" diff --git a/platform/mv3/extension/_locales/az/messages.json b/platform/mv3/extension/_locales/az/messages.json index deef8d2510f1b..5f8c5ae1aaf27 100644 --- a/platform/mv3/extension/_locales/az/messages.json +++ b/platform/mv3/extension/_locales/az/messages.json @@ -144,9 +144,13 @@ "description": "This describes the 'complete' filtering mode" }, "noFilteringModeDescription": { - "message": "List of hostnames for which no filtering will take place.", + "message": "List of websites for which no filtering will take place.", "description": "A short description for the editable field which lists trusted sites" }, + "noFilteringModePlaceholder": { + "message": "[hostnames only]\nexample.com\ngames.example\n...", + "description": "Default text for in edit field" + }, "behaviorSectionLabel": { "message": "Behavior", "description": "The header text for the 'Behavior' section" diff --git a/platform/mv3/extension/_locales/be/messages.json b/platform/mv3/extension/_locales/be/messages.json index e230e07e93238..d15851fd1aad0 100644 --- a/platform/mv3/extension/_locales/be/messages.json +++ b/platform/mv3/extension/_locales/be/messages.json @@ -147,6 +147,10 @@ "message": "Спіс назваў хостаў, для якіх не будзе праводзіцца фільтраванне", "description": "A short description for the editable field which lists trusted sites" }, + "noFilteringModePlaceholder": { + "message": "[толькі назвы хостаў]\nexample.com\ngames.example\n...", + "description": "Default text for in edit field" + }, "behaviorSectionLabel": { "message": "Паводзіны", "description": "The header text for the 'Behavior' section" diff --git a/platform/mv3/extension/_locales/bg/messages.json b/platform/mv3/extension/_locales/bg/messages.json index f4606dd26de7d..6ac57860653fc 100644 --- a/platform/mv3/extension/_locales/bg/messages.json +++ b/platform/mv3/extension/_locales/bg/messages.json @@ -147,6 +147,10 @@ "message": "Списък с имена на хостове, за които няма да се извършва филтриране", "description": "A short description for the editable field which lists trusted sites" }, + "noFilteringModePlaceholder": { + "message": "[само имена на хостове]\nprimer.com\nigri.primer\n...", + "description": "Default text for in edit field" + }, "behaviorSectionLabel": { "message": "Поведение", "description": "The header text for the 'Behavior' section" diff --git a/platform/mv3/extension/_locales/bn/messages.json b/platform/mv3/extension/_locales/bn/messages.json index d706ab1c8592c..f987c1850a676 100644 --- a/platform/mv3/extension/_locales/bn/messages.json +++ b/platform/mv3/extension/_locales/bn/messages.json @@ -147,6 +147,10 @@ "message": "হোস্টনেমের তালিকা যেগুলোর কোনো ফিল্টারিং হবে না", "description": "A short description for the editable field which lists trusted sites" }, + "noFilteringModePlaceholder": { + "message": "[hostnames only]\nexample.com\ngames.example\n...", + "description": "Default text for in edit field" + }, "behaviorSectionLabel": { "message": "আচরণ", "description": "The header text for the 'Behavior' section" diff --git a/platform/mv3/extension/_locales/br_FR/messages.json b/platform/mv3/extension/_locales/br_FR/messages.json index 30395963560de..dce348fb2e778 100644 --- a/platform/mv3/extension/_locales/br_FR/messages.json +++ b/platform/mv3/extension/_locales/br_FR/messages.json @@ -147,6 +147,10 @@ "message": "Roll anvioù domanioù ha ne vint ket silet.", "description": "A short description for the editable field which lists trusted sites" }, + "noFilteringModePlaceholder": { + "message": "[anvioù herberc'hierien hepken]\nskouer.com\nchoariou.skouer\n...", + "description": "Default text for in edit field" + }, "behaviorSectionLabel": { "message": "Emzalc'h", "description": "The header text for the 'Behavior' section" diff --git a/platform/mv3/extension/_locales/bs/messages.json b/platform/mv3/extension/_locales/bs/messages.json index d221c1b43c10b..6f7f257782a20 100644 --- a/platform/mv3/extension/_locales/bs/messages.json +++ b/platform/mv3/extension/_locales/bs/messages.json @@ -147,6 +147,10 @@ "message": "Lista imena hostova za koja se neće vršiti filtriranje", "description": "A short description for the editable field which lists trusted sites" }, + "noFilteringModePlaceholder": { + "message": "[hostnames only]\nexample.com\ngames.example\n...", + "description": "Default text for in edit field" + }, "behaviorSectionLabel": { "message": "Ponašanje", "description": "The header text for the 'Behavior' section" diff --git a/platform/mv3/extension/_locales/ca/messages.json b/platform/mv3/extension/_locales/ca/messages.json index 5e1f1eb9c7d60..ea727755353db 100644 --- a/platform/mv3/extension/_locales/ca/messages.json +++ b/platform/mv3/extension/_locales/ca/messages.json @@ -147,6 +147,10 @@ "message": "Llistat de noms d'amfitrió als quals no s'aplicarà cap filtre", "description": "A short description for the editable field which lists trusted sites" }, + "noFilteringModePlaceholder": { + "message": "[només noms d'amfitrió]\nexemple.com\njocs.exemple\n...", + "description": "Default text for in edit field" + }, "behaviorSectionLabel": { "message": "Comportament", "description": "The header text for the 'Behavior' section" diff --git a/platform/mv3/extension/_locales/cs/messages.json b/platform/mv3/extension/_locales/cs/messages.json index db051f05d1ad1..6daf4a73a9f51 100644 --- a/platform/mv3/extension/_locales/cs/messages.json +++ b/platform/mv3/extension/_locales/cs/messages.json @@ -147,6 +147,10 @@ "message": "Seznam názvů hostitelů, pro které nebude probíhat žádné filtrování", "description": "A short description for the editable field which lists trusted sites" }, + "noFilteringModePlaceholder": { + "message": "[pouze názvy hostitelů]\nexample.com\ngames.example\n...", + "description": "Default text for in edit field" + }, "behaviorSectionLabel": { "message": "Chování", "description": "The header text for the 'Behavior' section" diff --git a/platform/mv3/extension/_locales/cv/messages.json b/platform/mv3/extension/_locales/cv/messages.json index 4bbb5e389bf08..30c719d52722d 100644 --- a/platform/mv3/extension/_locales/cv/messages.json +++ b/platform/mv3/extension/_locales/cv/messages.json @@ -144,9 +144,13 @@ "description": "This describes the 'complete' filtering mode" }, "noFilteringModeDescription": { - "message": "List of hostnames for which no filtering will take place.", + "message": "List of websites for which no filtering will take place.", "description": "A short description for the editable field which lists trusted sites" }, + "noFilteringModePlaceholder": { + "message": "[hostnames only]\nexample.com\ngames.example\n...", + "description": "Default text for in edit field" + }, "behaviorSectionLabel": { "message": "Behavior", "description": "The header text for the 'Behavior' section" diff --git a/platform/mv3/extension/_locales/cy/messages.json b/platform/mv3/extension/_locales/cy/messages.json index c9d0e36583d83..56a6d66e186bf 100644 --- a/platform/mv3/extension/_locales/cy/messages.json +++ b/platform/mv3/extension/_locales/cy/messages.json @@ -144,9 +144,13 @@ "description": "This describes the 'complete' filtering mode" }, "noFilteringModeDescription": { - "message": "List of hostnames for which no filtering will take place.", + "message": "List of websites for which no filtering will take place.", "description": "A short description for the editable field which lists trusted sites" }, + "noFilteringModePlaceholder": { + "message": "[hostnames only]\nexample.com\ngames.example\n...", + "description": "Default text for in edit field" + }, "behaviorSectionLabel": { "message": "Ymddygiad", "description": "The header text for the 'Behavior' section" diff --git a/platform/mv3/extension/_locales/da/messages.json b/platform/mv3/extension/_locales/da/messages.json index ae494fe74a254..303d3bd3810ee 100644 --- a/platform/mv3/extension/_locales/da/messages.json +++ b/platform/mv3/extension/_locales/da/messages.json @@ -147,6 +147,10 @@ "message": "Liste over værtsnavne, for hvilke ingen filtrering vil ske.", "description": "A short description for the editable field which lists trusted sites" }, + "noFilteringModePlaceholder": { + "message": "[kun værtsnavne]\neksempel.dk\nspil.eksempel\n...", + "description": "Default text for in edit field" + }, "behaviorSectionLabel": { "message": "Adfærd", "description": "The header text for the 'Behavior' section" diff --git a/platform/mv3/extension/_locales/de/messages.json b/platform/mv3/extension/_locales/de/messages.json index 13829f3e919fb..7e8e072ca80f5 100644 --- a/platform/mv3/extension/_locales/de/messages.json +++ b/platform/mv3/extension/_locales/de/messages.json @@ -144,9 +144,13 @@ "description": "This describes the 'complete' filtering mode" }, "noFilteringModeDescription": { - "message": "Liste der Hostnamen, für die nicht gefiltert wird.", + "message": "Liste der Websites, für die nicht gefiltert wird.", "description": "A short description for the editable field which lists trusted sites" }, + "noFilteringModePlaceholder": { + "message": "[nur Hostnamen]\nexample.com\ngames.example\n…", + "description": "Default text for in edit field" + }, "behaviorSectionLabel": { "message": "Verhalten", "description": "The header text for the 'Behavior' section" diff --git a/platform/mv3/extension/_locales/el/messages.json b/platform/mv3/extension/_locales/el/messages.json index 8e3bc5ed1fe52..484b2f4ae268b 100644 --- a/platform/mv3/extension/_locales/el/messages.json +++ b/platform/mv3/extension/_locales/el/messages.json @@ -147,6 +147,10 @@ "message": "Λίστα των hostnames για τα οποία δεν θα πραγματοποιηθεί φιλτράρισμα", "description": "A short description for the editable field which lists trusted sites" }, + "noFilteringModePlaceholder": { + "message": "[hostnames only]\nexample.com\ngames.example\n...", + "description": "Default text for in edit field" + }, "behaviorSectionLabel": { "message": "Συμπεριφορά", "description": "The header text for the 'Behavior' section" diff --git a/platform/mv3/extension/_locales/en_GB/messages.json b/platform/mv3/extension/_locales/en_GB/messages.json index 884d7c1960243..42cde830929ad 100644 --- a/platform/mv3/extension/_locales/en_GB/messages.json +++ b/platform/mv3/extension/_locales/en_GB/messages.json @@ -147,6 +147,10 @@ "message": "List of hostnames for which no filtering will take place", "description": "A short description for the editable field which lists trusted sites" }, + "noFilteringModePlaceholder": { + "message": "[hostnames only]\nexample.com\ngames.example\n...", + "description": "Default text for in edit field" + }, "behaviorSectionLabel": { "message": "Behaviour", "description": "The header text for the 'Behavior' section" diff --git a/platform/mv3/extension/_locales/eo/messages.json b/platform/mv3/extension/_locales/eo/messages.json index ba5cfde43e504..4481c48f28e8e 100644 --- a/platform/mv3/extension/_locales/eo/messages.json +++ b/platform/mv3/extension/_locales/eo/messages.json @@ -144,9 +144,13 @@ "description": "This describes the 'complete' filtering mode" }, "noFilteringModeDescription": { - "message": "List of hostnames for which no filtering will take place.", + "message": "List of websites for which no filtering will take place.", "description": "A short description for the editable field which lists trusted sites" }, + "noFilteringModePlaceholder": { + "message": "[hostnames only]\nexample.com\ngames.example\n...", + "description": "Default text for in edit field" + }, "behaviorSectionLabel": { "message": "Behavior", "description": "The header text for the 'Behavior' section" diff --git a/platform/mv3/extension/_locales/es/messages.json b/platform/mv3/extension/_locales/es/messages.json index 53ff70b95a657..2d3cd2fd6c68d 100644 --- a/platform/mv3/extension/_locales/es/messages.json +++ b/platform/mv3/extension/_locales/es/messages.json @@ -147,6 +147,10 @@ "message": "Lista de nombres de dominio para los cuales no se realizará ningún filtrado.", "description": "A short description for the editable field which lists trusted sites" }, + "noFilteringModePlaceholder": { + "message": "[solo nombres de hospedador]\nejemplo.com\njuegos.ejemplo\n...", + "description": "Default text for in edit field" + }, "behaviorSectionLabel": { "message": "Comportamiento", "description": "The header text for the 'Behavior' section" diff --git a/platform/mv3/extension/_locales/et/messages.json b/platform/mv3/extension/_locales/et/messages.json index 69f6aa21932e4..bdbf31a3afafd 100644 --- a/platform/mv3/extension/_locales/et/messages.json +++ b/platform/mv3/extension/_locales/et/messages.json @@ -144,9 +144,13 @@ "description": "This describes the 'complete' filtering mode" }, "noFilteringModeDescription": { - "message": "Hostinimede loend, kus filtreerimine keelatakse", + "message": "Veebilehtede loend, kus filtreid ei kasutata.", "description": "A short description for the editable field which lists trusted sites" }, + "noFilteringModePlaceholder": { + "message": "[ainult hostinimed]\nnäide.com\nmängud.näide\n...", + "description": "Default text for in edit field" + }, "behaviorSectionLabel": { "message": "Käitumine", "description": "The header text for the 'Behavior' section" diff --git a/platform/mv3/extension/_locales/eu/messages.json b/platform/mv3/extension/_locales/eu/messages.json index 353f3b79c3eed..ac5c05b345274 100644 --- a/platform/mv3/extension/_locales/eu/messages.json +++ b/platform/mv3/extension/_locales/eu/messages.json @@ -147,6 +147,10 @@ "message": "Filtrorik ezarriko ez zaien zerbitzarien izenak", "description": "A short description for the editable field which lists trusted sites" }, + "noFilteringModePlaceholder": { + "message": "[hostnames only]\nexample.com\ngames.example\n...", + "description": "Default text for in edit field" + }, "behaviorSectionLabel": { "message": "portaera or jokaera", "description": "The header text for the 'Behavior' section" diff --git a/platform/mv3/extension/_locales/fa/messages.json b/platform/mv3/extension/_locales/fa/messages.json index a18a6ed3a7cb3..169540c053e57 100644 --- a/platform/mv3/extension/_locales/fa/messages.json +++ b/platform/mv3/extension/_locales/fa/messages.json @@ -144,9 +144,13 @@ "description": "This describes the 'complete' filtering mode" }, "noFilteringModeDescription": { - "message": "List of hostnames for which no filtering will take place.", + "message": "List of websites for which no filtering will take place.", "description": "A short description for the editable field which lists trusted sites" }, + "noFilteringModePlaceholder": { + "message": "[hostnames only]\nexample.com\ngames.example\n...", + "description": "Default text for in edit field" + }, "behaviorSectionLabel": { "message": "Behavior", "description": "The header text for the 'Behavior' section" diff --git a/platform/mv3/extension/_locales/fi/messages.json b/platform/mv3/extension/_locales/fi/messages.json index 05d42117aa0ed..5e8ef6de8dc4b 100644 --- a/platform/mv3/extension/_locales/fi/messages.json +++ b/platform/mv3/extension/_locales/fi/messages.json @@ -147,6 +147,10 @@ "message": "Listaus osotteista, joita ei suodateta.", "description": "A short description for the editable field which lists trusted sites" }, + "noFilteringModePlaceholder": { + "message": "[vain isäntänimiä]\nesimerkki.fi\npelit.esimerkki\n...", + "description": "Default text for in edit field" + }, "behaviorSectionLabel": { "message": "Toiminta", "description": "The header text for the 'Behavior' section" diff --git a/platform/mv3/extension/_locales/fil/messages.json b/platform/mv3/extension/_locales/fil/messages.json index fe5daa48fba7e..8b48cf12d2ac3 100644 --- a/platform/mv3/extension/_locales/fil/messages.json +++ b/platform/mv3/extension/_locales/fil/messages.json @@ -144,9 +144,13 @@ "description": "This describes the 'complete' filtering mode" }, "noFilteringModeDescription": { - "message": "List of hostnames for which no filtering will take place.", + "message": "List of websites for which no filtering will take place.", "description": "A short description for the editable field which lists trusted sites" }, + "noFilteringModePlaceholder": { + "message": "[hostnames only]\nexample.com\ngames.example\n...", + "description": "Default text for in edit field" + }, "behaviorSectionLabel": { "message": "Ugali", "description": "The header text for the 'Behavior' section" diff --git a/platform/mv3/extension/_locales/fr/messages.json b/platform/mv3/extension/_locales/fr/messages.json index 0152c8e9139c3..a0c557d0ea923 100644 --- a/platform/mv3/extension/_locales/fr/messages.json +++ b/platform/mv3/extension/_locales/fr/messages.json @@ -144,9 +144,13 @@ "description": "This describes the 'complete' filtering mode" }, "noFilteringModeDescription": { - "message": "Liste des noms de domaine qu'uBO Lite ne devra pas filtrer", + "message": "Liste des noms de domaine pour lesquels aucun filtrage n'aura lieu.", "description": "A short description for the editable field which lists trusted sites" }, + "noFilteringModePlaceholder": { + "message": "[noms de domaine uniquement]\nexemple.com\njeux.exemple\n...", + "description": "Default text for in edit field" + }, "behaviorSectionLabel": { "message": "Comportement", "description": "The header text for the 'Behavior' section" diff --git a/platform/mv3/extension/_locales/fy/messages.json b/platform/mv3/extension/_locales/fy/messages.json index f583e4b49e1c1..634f28c88f3de 100644 --- a/platform/mv3/extension/_locales/fy/messages.json +++ b/platform/mv3/extension/_locales/fy/messages.json @@ -147,6 +147,10 @@ "message": "List fan hostnammen wêrfoar gjin filtering plakfynt.", "description": "A short description for the editable field which lists trusted sites" }, + "noFilteringModePlaceholder": { + "message": "[allinnich hostnammen]\nexample.com\ngames.example\n…", + "description": "Default text for in edit field" + }, "behaviorSectionLabel": { "message": "Gedrach", "description": "The header text for the 'Behavior' section" diff --git a/platform/mv3/extension/_locales/gl/messages.json b/platform/mv3/extension/_locales/gl/messages.json index 760c65c899a85..1dcc3844d46da 100644 --- a/platform/mv3/extension/_locales/gl/messages.json +++ b/platform/mv3/extension/_locales/gl/messages.json @@ -147,6 +147,10 @@ "message": "Lista de nomes de host para os que non se fará filtrado", "description": "A short description for the editable field which lists trusted sites" }, + "noFilteringModePlaceholder": { + "message": "[só nomes de servidor]\nexample.com\ngames.example\n...", + "description": "Default text for in edit field" + }, "behaviorSectionLabel": { "message": "Comportamento", "description": "The header text for the 'Behavior' section" diff --git a/platform/mv3/extension/_locales/gu/messages.json b/platform/mv3/extension/_locales/gu/messages.json index 4bbb5e389bf08..30c719d52722d 100644 --- a/platform/mv3/extension/_locales/gu/messages.json +++ b/platform/mv3/extension/_locales/gu/messages.json @@ -144,9 +144,13 @@ "description": "This describes the 'complete' filtering mode" }, "noFilteringModeDescription": { - "message": "List of hostnames for which no filtering will take place.", + "message": "List of websites for which no filtering will take place.", "description": "A short description for the editable field which lists trusted sites" }, + "noFilteringModePlaceholder": { + "message": "[hostnames only]\nexample.com\ngames.example\n...", + "description": "Default text for in edit field" + }, "behaviorSectionLabel": { "message": "Behavior", "description": "The header text for the 'Behavior' section" diff --git a/platform/mv3/extension/_locales/he/messages.json b/platform/mv3/extension/_locales/he/messages.json index beb73f27377fd..d76b50ebb83d1 100644 --- a/platform/mv3/extension/_locales/he/messages.json +++ b/platform/mv3/extension/_locales/he/messages.json @@ -147,6 +147,10 @@ "message": "רשימה של שמות אתרים שלא יתבצע עליהם סינון", "description": "A short description for the editable field which lists trusted sites" }, + "noFilteringModePlaceholder": { + "message": "[שמות אתרים בלבד]\nexample.com\ngames.example\n...", + "description": "Default text for in edit field" + }, "behaviorSectionLabel": { "message": "התנהגות", "description": "The header text for the 'Behavior' section" diff --git a/platform/mv3/extension/_locales/hi/messages.json b/platform/mv3/extension/_locales/hi/messages.json index 244acb89cb7b6..0cfd586195cc7 100644 --- a/platform/mv3/extension/_locales/hi/messages.json +++ b/platform/mv3/extension/_locales/hi/messages.json @@ -147,6 +147,10 @@ "message": "होस्टनामों की सूची जिनके लिए कोई फ़िल्टरिंग नहीं होगी", "description": "A short description for the editable field which lists trusted sites" }, + "noFilteringModePlaceholder": { + "message": "[केवल होस्ट का नाम]\nexample.com\ngames.example\n...", + "description": "Default text for in edit field" + }, "behaviorSectionLabel": { "message": "व्यवहार", "description": "The header text for the 'Behavior' section" diff --git a/platform/mv3/extension/_locales/hr/messages.json b/platform/mv3/extension/_locales/hr/messages.json index 2ae0291a129f3..5ca15ed148feb 100644 --- a/platform/mv3/extension/_locales/hr/messages.json +++ b/platform/mv3/extension/_locales/hr/messages.json @@ -147,6 +147,10 @@ "message": "Popis naziva hostova za koje se neće izvršiti filtriranje.", "description": "A short description for the editable field which lists trusted sites" }, + "noFilteringModePlaceholder": { + "message": "[samo nazivi hostova]\nexample.com\ngames.example", + "description": "Default text for in edit field" + }, "behaviorSectionLabel": { "message": "Ponašanje", "description": "The header text for the 'Behavior' section" diff --git a/platform/mv3/extension/_locales/hu/messages.json b/platform/mv3/extension/_locales/hu/messages.json index 3ec2b0e2ece25..a27467b005740 100644 --- a/platform/mv3/extension/_locales/hu/messages.json +++ b/platform/mv3/extension/_locales/hu/messages.json @@ -4,7 +4,7 @@ "description": "extension name." }, "extShortDesc": { - "message": "Kísérleti, engedélyt nem igénylő nélküli tartalomblokkoló. A telepítés után azonnal blokkolja a hirdetéseket, nyomkövetőket, bányászokat és egyebeket.", + "message": "Kísérleti, engedélyeket nem igénylő nélküli tartalomblokkoló. A telepítés után azonnal blokkolja a hirdetéseket, nyomkövetőket, bányászprogramokat és egyebeket.", "description": "this will be in the Chrome web store: must be 132 characters or less" }, "perRulesetStats": { @@ -100,15 +100,15 @@ "description": "Shown in the About pane" }, "firstRunSectionLabel": { - "message": "Üdvözlünk", + "message": "Üdvözöljük", "description": "The header text for the welcome message section" }, "firstRunDescription": { - "message": "Alapértelmezés szerint az Alap mód van kiválasztva, mert nem igényel engedélyt az adatok olvasásához és módosításához. Ha megbízik az uBO Lite-ban, széles körű engedélyt adhat neki az adatok olvasására és módosítására az összes webhelyen, hogy alapértelmezés szerint fejlettebb szűrési lehetőségeket tegyen lehetővé minden webhelyen.", + "message": "Most telepítette a uBO Lite-ot. Itt választhatja ki az összes weboldalon használandó alapértelmezett szűrési módot.\n\nAlapértelmezés szerint az Alapvetú mód van kiválasztva, mert nem igényel engedélyt az adatok olvasásához és módosításához. Ha megbízik az uBO Lite-ban, széles körű engedélyt adhat neki az adatok olvasására és módosítására az összes webhelyen, hogy alapértelmezés szerint fejlettebb szűrési lehetőségeket tegyen lehetővé minden webhelyen.", "description": "Descriptive text shown at first install time only " }, "defaultFilteringModeSectionLabel": { - "message": "Alapértelmezett szűrő", + "message": "Alapértelmezett szűrési mód", "description": "The header text for the default filtering mode section" }, "defaultFilteringModeDescription": { @@ -116,11 +116,11 @@ "description": "This describes the default filtering mode setting" }, "filteringMode0Name": { - "message": "Nincs szűrés", + "message": "nincs szűrés", "description": "Name of blocking mode 0" }, "filteringMode1Name": { - "message": "alapok", + "message": "alapvető", "description": "Name of blocking mode 1" }, "filteringMode2Name": { @@ -144,9 +144,13 @@ "description": "This describes the 'complete' filtering mode" }, "noFilteringModeDescription": { - "message": "Azon hosztnevek listája, amelyek esetében nem történik szűrés", + "message": "Azon gépnevek listája, amelyek esetében nem történik szűrés.", "description": "A short description for the editable field which lists trusted sites" }, + "noFilteringModePlaceholder": { + "message": "[hostnames only]\nexample.com\ngames.example\n...", + "description": "Default text for in edit field" + }, "behaviorSectionLabel": { "message": "Viselkedés", "description": "The header text for the 'Behavior' section" diff --git a/platform/mv3/extension/_locales/hy/messages.json b/platform/mv3/extension/_locales/hy/messages.json index 6de0ec8a17e86..36cd1cc8e60ac 100644 --- a/platform/mv3/extension/_locales/hy/messages.json +++ b/platform/mv3/extension/_locales/hy/messages.json @@ -147,6 +147,10 @@ "message": "Հյուրերի անունների ցանկ, որոնց համար զտում չի իրականացվի", "description": "A short description for the editable field which lists trusted sites" }, + "noFilteringModePlaceholder": { + "message": "[hostnames only]\nexample.com\ngames.example\n...", + "description": "Default text for in edit field" + }, "behaviorSectionLabel": { "message": "Վարք", "description": "The header text for the 'Behavior' section" diff --git a/platform/mv3/extension/_locales/id/messages.json b/platform/mv3/extension/_locales/id/messages.json index 421f091afb516..8041d111ad497 100644 --- a/platform/mv3/extension/_locales/id/messages.json +++ b/platform/mv3/extension/_locales/id/messages.json @@ -147,6 +147,10 @@ "message": "Daftar nama host yang tidak akan difilter.", "description": "A short description for the editable field which lists trusted sites" }, + "noFilteringModePlaceholder": { + "message": "[hostnames only]\nexample.com\ngames.example\n...", + "description": "Default text for in edit field" + }, "behaviorSectionLabel": { "message": "Perilaku", "description": "The header text for the 'Behavior' section" diff --git a/platform/mv3/extension/_locales/it/messages.json b/platform/mv3/extension/_locales/it/messages.json index cf3082df674f6..e504d5b206d85 100644 --- a/platform/mv3/extension/_locales/it/messages.json +++ b/platform/mv3/extension/_locales/it/messages.json @@ -147,6 +147,10 @@ "message": "Lista dei nomi host per i quali non verrà effettuato alcun filtraggio.", "description": "A short description for the editable field which lists trusted sites" }, + "noFilteringModePlaceholder": { + "message": "[hostnames only]\nexample.com\ngames.example\n...", + "description": "Default text for in edit field" + }, "behaviorSectionLabel": { "message": "Comportamento", "description": "The header text for the 'Behavior' section" diff --git a/platform/mv3/extension/_locales/ja/messages.json b/platform/mv3/extension/_locales/ja/messages.json index 28ad8753b9d6f..53ad766bb031a 100644 --- a/platform/mv3/extension/_locales/ja/messages.json +++ b/platform/mv3/extension/_locales/ja/messages.json @@ -147,6 +147,10 @@ "message": "フィルタリングを行わないホスト名のリスト", "description": "A short description for the editable field which lists trusted sites" }, + "noFilteringModePlaceholder": { + "message": "[ホスト名のみ]\nexample.com\ngames.example\n...", + "description": "Default text for in edit field" + }, "behaviorSectionLabel": { "message": "動作", "description": "The header text for the 'Behavior' section" diff --git a/platform/mv3/extension/_locales/ka/messages.json b/platform/mv3/extension/_locales/ka/messages.json index f31582f7a4a55..a0550c66e1a05 100644 --- a/platform/mv3/extension/_locales/ka/messages.json +++ b/platform/mv3/extension/_locales/ka/messages.json @@ -147,6 +147,10 @@ "message": "სია მისამართებისა, რომლებზეც ფილტრები არ იმოქმედებს", "description": "A short description for the editable field which lists trusted sites" }, + "noFilteringModePlaceholder": { + "message": "[hostnames only]\nexample.com\ngames.example\n...", + "description": "Default text for in edit field" + }, "behaviorSectionLabel": { "message": "მოქმედება", "description": "The header text for the 'Behavior' section" diff --git a/platform/mv3/extension/_locales/kk/messages.json b/platform/mv3/extension/_locales/kk/messages.json index 4bbb5e389bf08..30c719d52722d 100644 --- a/platform/mv3/extension/_locales/kk/messages.json +++ b/platform/mv3/extension/_locales/kk/messages.json @@ -144,9 +144,13 @@ "description": "This describes the 'complete' filtering mode" }, "noFilteringModeDescription": { - "message": "List of hostnames for which no filtering will take place.", + "message": "List of websites for which no filtering will take place.", "description": "A short description for the editable field which lists trusted sites" }, + "noFilteringModePlaceholder": { + "message": "[hostnames only]\nexample.com\ngames.example\n...", + "description": "Default text for in edit field" + }, "behaviorSectionLabel": { "message": "Behavior", "description": "The header text for the 'Behavior' section" diff --git a/platform/mv3/extension/_locales/kn/messages.json b/platform/mv3/extension/_locales/kn/messages.json index 7fc46b9e67b1d..c1261b1651f77 100644 --- a/platform/mv3/extension/_locales/kn/messages.json +++ b/platform/mv3/extension/_locales/kn/messages.json @@ -144,9 +144,13 @@ "description": "This describes the 'complete' filtering mode" }, "noFilteringModeDescription": { - "message": "List of hostnames for which no filtering will take place.", + "message": "List of websites for which no filtering will take place.", "description": "A short description for the editable field which lists trusted sites" }, + "noFilteringModePlaceholder": { + "message": "[hostnames only]\nexample.com\ngames.example\n...", + "description": "Default text for in edit field" + }, "behaviorSectionLabel": { "message": "ನಡವಳಿಕೆ", "description": "The header text for the 'Behavior' section" diff --git a/platform/mv3/extension/_locales/ko/messages.json b/platform/mv3/extension/_locales/ko/messages.json index d066b63af68a8..d7de8ce1654d5 100644 --- a/platform/mv3/extension/_locales/ko/messages.json +++ b/platform/mv3/extension/_locales/ko/messages.json @@ -147,6 +147,10 @@ "message": "필터링을 비활성화할 호스트 이름 목록", "description": "A short description for the editable field which lists trusted sites" }, + "noFilteringModePlaceholder": { + "message": "[hostnames only]\nexample.com\ngames.example\n...", + "description": "Default text for in edit field" + }, "behaviorSectionLabel": { "message": "동작", "description": "The header text for the 'Behavior' section" diff --git a/platform/mv3/extension/_locales/lt/messages.json b/platform/mv3/extension/_locales/lt/messages.json index 52c96b3ae207a..497cd30837082 100644 --- a/platform/mv3/extension/_locales/lt/messages.json +++ b/platform/mv3/extension/_locales/lt/messages.json @@ -144,9 +144,13 @@ "description": "This describes the 'complete' filtering mode" }, "noFilteringModeDescription": { - "message": "List of hostnames for which no filtering will take place.", + "message": "List of websites for which no filtering will take place.", "description": "A short description for the editable field which lists trusted sites" }, + "noFilteringModePlaceholder": { + "message": "[hostnames only]\nexample.com\ngames.example\n...", + "description": "Default text for in edit field" + }, "behaviorSectionLabel": { "message": "Elgsena", "description": "The header text for the 'Behavior' section" diff --git a/platform/mv3/extension/_locales/lv/messages.json b/platform/mv3/extension/_locales/lv/messages.json index a37ba6dda83c1..4514c75b4f19a 100644 --- a/platform/mv3/extension/_locales/lv/messages.json +++ b/platform/mv3/extension/_locales/lv/messages.json @@ -147,6 +147,10 @@ "message": "Saraksts ar saimniekdatoru nosaukumiem, kuriem netiks pielietota aizturēšana", "description": "A short description for the editable field which lists trusted sites" }, + "noFilteringModePlaceholder": { + "message": "[tikai saimniekdatoru nosaukumi]\nexample.com\ngames.example\n...", + "description": "Default text for in edit field" + }, "behaviorSectionLabel": { "message": "Uzvedība", "description": "The header text for the 'Behavior' section" diff --git a/platform/mv3/extension/_locales/mk/messages.json b/platform/mv3/extension/_locales/mk/messages.json index cb7631b2bbf95..73d13cc15b35b 100644 --- a/platform/mv3/extension/_locales/mk/messages.json +++ b/platform/mv3/extension/_locales/mk/messages.json @@ -144,9 +144,13 @@ "description": "This describes the 'complete' filtering mode" }, "noFilteringModeDescription": { - "message": "List of hostnames for which no filtering will take place.", + "message": "List of websites for which no filtering will take place.", "description": "A short description for the editable field which lists trusted sites" }, + "noFilteringModePlaceholder": { + "message": "[hostnames only]\nexample.com\ngames.example\n...", + "description": "Default text for in edit field" + }, "behaviorSectionLabel": { "message": "Behavior", "description": "The header text for the 'Behavior' section" diff --git a/platform/mv3/extension/_locales/ml/messages.json b/platform/mv3/extension/_locales/ml/messages.json index 2bffd1a1ffc61..42db083577715 100644 --- a/platform/mv3/extension/_locales/ml/messages.json +++ b/platform/mv3/extension/_locales/ml/messages.json @@ -147,6 +147,10 @@ "message": "ഫിൽട്ടറിംഗ് നടക്കാത്ത ഹോസ്റ്റ് നെയിമുകളുടെ ലിസ്റ്റ്", "description": "A short description for the editable field which lists trusted sites" }, + "noFilteringModePlaceholder": { + "message": "[hostnames only]\nexample.com\ngames.example\n...", + "description": "Default text for in edit field" + }, "behaviorSectionLabel": { "message": "പെരുമാറ്റം", "description": "The header text for the 'Behavior' section" diff --git a/platform/mv3/extension/_locales/mr/messages.json b/platform/mv3/extension/_locales/mr/messages.json index 4bbb5e389bf08..30c719d52722d 100644 --- a/platform/mv3/extension/_locales/mr/messages.json +++ b/platform/mv3/extension/_locales/mr/messages.json @@ -144,9 +144,13 @@ "description": "This describes the 'complete' filtering mode" }, "noFilteringModeDescription": { - "message": "List of hostnames for which no filtering will take place.", + "message": "List of websites for which no filtering will take place.", "description": "A short description for the editable field which lists trusted sites" }, + "noFilteringModePlaceholder": { + "message": "[hostnames only]\nexample.com\ngames.example\n...", + "description": "Default text for in edit field" + }, "behaviorSectionLabel": { "message": "Behavior", "description": "The header text for the 'Behavior' section" diff --git a/platform/mv3/extension/_locales/ms/messages.json b/platform/mv3/extension/_locales/ms/messages.json index c9147fe236ec9..71f193001665a 100644 --- a/platform/mv3/extension/_locales/ms/messages.json +++ b/platform/mv3/extension/_locales/ms/messages.json @@ -147,6 +147,10 @@ "message": "Senarai nama hos yang tiada penapisan akan berlaku", "description": "A short description for the editable field which lists trusted sites" }, + "noFilteringModePlaceholder": { + "message": "[hostnames only]\nexample.com\ngames.example\n...", + "description": "Default text for in edit field" + }, "behaviorSectionLabel": { "message": "Tingkah laku", "description": "The header text for the 'Behavior' section" diff --git a/platform/mv3/extension/_locales/nb/messages.json b/platform/mv3/extension/_locales/nb/messages.json index ed850b77db4ad..44234c059857e 100644 --- a/platform/mv3/extension/_locales/nb/messages.json +++ b/platform/mv3/extension/_locales/nb/messages.json @@ -147,6 +147,10 @@ "message": "Liste over vertsnavn der ingen filtrering vil finne sted", "description": "A short description for the editable field which lists trusted sites" }, + "noFilteringModePlaceholder": { + "message": "[hostnames only]\nexample.com\ngames.example\n...", + "description": "Default text for in edit field" + }, "behaviorSectionLabel": { "message": "Virkemåte", "description": "The header text for the 'Behavior' section" diff --git a/platform/mv3/extension/_locales/nl/messages.json b/platform/mv3/extension/_locales/nl/messages.json index d73b801d6118f..a32f81e591895 100644 --- a/platform/mv3/extension/_locales/nl/messages.json +++ b/platform/mv3/extension/_locales/nl/messages.json @@ -147,6 +147,10 @@ "message": "Lijst van hostnamen waarvoor geen filtering plaatsvindt.", "description": "A short description for the editable field which lists trusted sites" }, + "noFilteringModePlaceholder": { + "message": "[alleen hostnamen]\nexample.com\ngames.example\n...", + "description": "Default text for in edit field" + }, "behaviorSectionLabel": { "message": "Gedrag", "description": "The header text for the 'Behavior' section" diff --git a/platform/mv3/extension/_locales/oc/messages.json b/platform/mv3/extension/_locales/oc/messages.json index 4bbb5e389bf08..30c719d52722d 100644 --- a/platform/mv3/extension/_locales/oc/messages.json +++ b/platform/mv3/extension/_locales/oc/messages.json @@ -144,9 +144,13 @@ "description": "This describes the 'complete' filtering mode" }, "noFilteringModeDescription": { - "message": "List of hostnames for which no filtering will take place.", + "message": "List of websites for which no filtering will take place.", "description": "A short description for the editable field which lists trusted sites" }, + "noFilteringModePlaceholder": { + "message": "[hostnames only]\nexample.com\ngames.example\n...", + "description": "Default text for in edit field" + }, "behaviorSectionLabel": { "message": "Behavior", "description": "The header text for the 'Behavior' section" diff --git a/platform/mv3/extension/_locales/pa/messages.json b/platform/mv3/extension/_locales/pa/messages.json index 868cc48068375..aff262e364ead 100644 --- a/platform/mv3/extension/_locales/pa/messages.json +++ b/platform/mv3/extension/_locales/pa/messages.json @@ -147,6 +147,10 @@ "message": "ਹੋਸਟ-ਨਾਵਾਂ ਦੀ ਸੂਚੀ, ਜਿਨ੍ਹਾਂ ਲਈ ਕੋਈ ਫਿਲਟਰ ਨਹੀਂ ਕੀਤਾ ਜਾਵੇਗਾ", "description": "A short description for the editable field which lists trusted sites" }, + "noFilteringModePlaceholder": { + "message": "[hostnames only]\nexample.com\ngames.example\n...", + "description": "Default text for in edit field" + }, "behaviorSectionLabel": { "message": "ਰਵੱਈਆ", "description": "The header text for the 'Behavior' section" diff --git a/platform/mv3/extension/_locales/pl/messages.json b/platform/mv3/extension/_locales/pl/messages.json index 1c7b89f236beb..61a1b8eb16fe5 100644 --- a/platform/mv3/extension/_locales/pl/messages.json +++ b/platform/mv3/extension/_locales/pl/messages.json @@ -147,6 +147,10 @@ "message": "Lista nazw hostów, dla których nie będzie stosowane żadne filtrowanie", "description": "A short description for the editable field which lists trusted sites" }, + "noFilteringModePlaceholder": { + "message": "[tylko nazwy hostów]\nexample.com\ngry.przykład\n...", + "description": "Default text for in edit field" + }, "behaviorSectionLabel": { "message": "Zachowanie", "description": "The header text for the 'Behavior' section" diff --git a/platform/mv3/extension/_locales/pt_BR/messages.json b/platform/mv3/extension/_locales/pt_BR/messages.json index e6265efab5275..671006eacf313 100644 --- a/platform/mv3/extension/_locales/pt_BR/messages.json +++ b/platform/mv3/extension/_locales/pt_BR/messages.json @@ -147,6 +147,10 @@ "message": "Lista de nomes dos hospedeiros para os quais nenhuma filtragem acontecerá.", "description": "A short description for the editable field which lists trusted sites" }, + "noFilteringModePlaceholder": { + "message": "[só nomes de hospedeiros]\nexemplo.com\njogos.exemplo", + "description": "Default text for in edit field" + }, "behaviorSectionLabel": { "message": "Comportamento", "description": "The header text for the 'Behavior' section" @@ -156,7 +160,7 @@ "description": "Label for a checkbox in the options page" }, "showBlockedCountLabel": { - "message": "Mostrar o número de solicitações bloqueadas no ícone da barra de ferramentas", + "message": "Mostrar o número de requisições bloqueadas no ícone da barra de ferramentas", "description": "Label for a checkbox in the options page" } } diff --git a/platform/mv3/extension/_locales/pt_PT/messages.json b/platform/mv3/extension/_locales/pt_PT/messages.json index 511db27ff621a..2f637e25713e8 100644 --- a/platform/mv3/extension/_locales/pt_PT/messages.json +++ b/platform/mv3/extension/_locales/pt_PT/messages.json @@ -147,6 +147,10 @@ "message": "Lista de nomes de anfitriões para os quais não será efetuada qualquer filtragem.", "description": "A short description for the editable field which lists trusted sites" }, + "noFilteringModePlaceholder": { + "message": "[hostnames only]\nexemplo.com\njogos.exemplo\n...", + "description": "Default text for in edit field" + }, "behaviorSectionLabel": { "message": "Comportamento", "description": "The header text for the 'Behavior' section" diff --git a/platform/mv3/extension/_locales/ro/messages.json b/platform/mv3/extension/_locales/ro/messages.json index ec8228953b77f..c953b4626e9e1 100644 --- a/platform/mv3/extension/_locales/ro/messages.json +++ b/platform/mv3/extension/_locales/ro/messages.json @@ -147,6 +147,10 @@ "message": "Lista numelor mașinilor pentru care nu se va face filtrare", "description": "A short description for the editable field which lists trusted sites" }, + "noFilteringModePlaceholder": { + "message": "[hostnames only]\nexample.com\ngames.example\n...", + "description": "Default text for in edit field" + }, "behaviorSectionLabel": { "message": "Comportament", "description": "The header text for the 'Behavior' section" diff --git a/platform/mv3/extension/_locales/ru/messages.json b/platform/mv3/extension/_locales/ru/messages.json index f13511e62cf28..63fee1591a597 100644 --- a/platform/mv3/extension/_locales/ru/messages.json +++ b/platform/mv3/extension/_locales/ru/messages.json @@ -147,6 +147,10 @@ "message": "Список имён хостов, для которых не будет производиться фильтрация.", "description": "A short description for the editable field which lists trusted sites" }, + "noFilteringModePlaceholder": { + "message": "[только имена хостов]\nexample.com\ngames.example\n...", + "description": "Default text for in edit field" + }, "behaviorSectionLabel": { "message": "Поведение", "description": "The header text for the 'Behavior' section" diff --git a/platform/mv3/extension/_locales/si/messages.json b/platform/mv3/extension/_locales/si/messages.json index 4bbb5e389bf08..30c719d52722d 100644 --- a/platform/mv3/extension/_locales/si/messages.json +++ b/platform/mv3/extension/_locales/si/messages.json @@ -144,9 +144,13 @@ "description": "This describes the 'complete' filtering mode" }, "noFilteringModeDescription": { - "message": "List of hostnames for which no filtering will take place.", + "message": "List of websites for which no filtering will take place.", "description": "A short description for the editable field which lists trusted sites" }, + "noFilteringModePlaceholder": { + "message": "[hostnames only]\nexample.com\ngames.example\n...", + "description": "Default text for in edit field" + }, "behaviorSectionLabel": { "message": "Behavior", "description": "The header text for the 'Behavior' section" diff --git a/platform/mv3/extension/_locales/sk/messages.json b/platform/mv3/extension/_locales/sk/messages.json index 91ebfc926a1c6..b4ad74ba2fecc 100644 --- a/platform/mv3/extension/_locales/sk/messages.json +++ b/platform/mv3/extension/_locales/sk/messages.json @@ -147,6 +147,10 @@ "message": "Zoznam názvov hostiteľov s vylúčeným filtrovaním", "description": "A short description for the editable field which lists trusted sites" }, + "noFilteringModePlaceholder": { + "message": "[len názvy hostiteľov]\nexample.com\ngames.example\n...", + "description": "Default text for in edit field" + }, "behaviorSectionLabel": { "message": "Správanie", "description": "The header text for the 'Behavior' section" diff --git a/platform/mv3/extension/_locales/sl/messages.json b/platform/mv3/extension/_locales/sl/messages.json index 4bbb5e389bf08..30c719d52722d 100644 --- a/platform/mv3/extension/_locales/sl/messages.json +++ b/platform/mv3/extension/_locales/sl/messages.json @@ -144,9 +144,13 @@ "description": "This describes the 'complete' filtering mode" }, "noFilteringModeDescription": { - "message": "List of hostnames for which no filtering will take place.", + "message": "List of websites for which no filtering will take place.", "description": "A short description for the editable field which lists trusted sites" }, + "noFilteringModePlaceholder": { + "message": "[hostnames only]\nexample.com\ngames.example\n...", + "description": "Default text for in edit field" + }, "behaviorSectionLabel": { "message": "Behavior", "description": "The header text for the 'Behavior' section" diff --git a/platform/mv3/extension/_locales/so/messages.json b/platform/mv3/extension/_locales/so/messages.json index 4bbb5e389bf08..30c719d52722d 100644 --- a/platform/mv3/extension/_locales/so/messages.json +++ b/platform/mv3/extension/_locales/so/messages.json @@ -144,9 +144,13 @@ "description": "This describes the 'complete' filtering mode" }, "noFilteringModeDescription": { - "message": "List of hostnames for which no filtering will take place.", + "message": "List of websites for which no filtering will take place.", "description": "A short description for the editable field which lists trusted sites" }, + "noFilteringModePlaceholder": { + "message": "[hostnames only]\nexample.com\ngames.example\n...", + "description": "Default text for in edit field" + }, "behaviorSectionLabel": { "message": "Behavior", "description": "The header text for the 'Behavior' section" diff --git a/platform/mv3/extension/_locales/sq/messages.json b/platform/mv3/extension/_locales/sq/messages.json index 7b8c0748da091..986b72a0479ec 100644 --- a/platform/mv3/extension/_locales/sq/messages.json +++ b/platform/mv3/extension/_locales/sq/messages.json @@ -144,9 +144,13 @@ "description": "This describes the 'complete' filtering mode" }, "noFilteringModeDescription": { - "message": "Emrat e hosteve, që nuk do të kalojnë në filtër.", + "message": "Emrat e hosteve që nuk do të kalojnë në filtër", "description": "A short description for the editable field which lists trusted sites" }, + "noFilteringModePlaceholder": { + "message": "[hostnames only]\nexample.com\ngames.example\n...", + "description": "Default text for in edit field" + }, "behaviorSectionLabel": { "message": "Sjellja", "description": "The header text for the 'Behavior' section" diff --git a/platform/mv3/extension/_locales/sr/messages.json b/platform/mv3/extension/_locales/sr/messages.json index 5680a04dc5823..c33af6db43976 100644 --- a/platform/mv3/extension/_locales/sr/messages.json +++ b/platform/mv3/extension/_locales/sr/messages.json @@ -147,6 +147,10 @@ "message": "Листа имена хостова за које се неће вршити филтрирање", "description": "A short description for the editable field which lists trusted sites" }, + "noFilteringModePlaceholder": { + "message": "[само имена хостова]\nexample.com\ngames.example\n...", + "description": "Default text for in edit field" + }, "behaviorSectionLabel": { "message": "Понашање", "description": "The header text for the 'Behavior' section" diff --git a/platform/mv3/extension/_locales/sv/messages.json b/platform/mv3/extension/_locales/sv/messages.json index 5dcbfc98a8a3f..4fb9b47a26a57 100644 --- a/platform/mv3/extension/_locales/sv/messages.json +++ b/platform/mv3/extension/_locales/sv/messages.json @@ -147,6 +147,10 @@ "message": "Lista över värdnamn som inte kommer att filtreras", "description": "A short description for the editable field which lists trusted sites" }, + "noFilteringModePlaceholder": { + "message": "[endast värdnamn]\nexempel.se\nspel.exempel\n...", + "description": "Default text for in edit field" + }, "behaviorSectionLabel": { "message": "Beteende", "description": "The header text for the 'Behavior' section" diff --git a/platform/mv3/extension/_locales/sw/messages.json b/platform/mv3/extension/_locales/sw/messages.json index 4bbb5e389bf08..30c719d52722d 100644 --- a/platform/mv3/extension/_locales/sw/messages.json +++ b/platform/mv3/extension/_locales/sw/messages.json @@ -144,9 +144,13 @@ "description": "This describes the 'complete' filtering mode" }, "noFilteringModeDescription": { - "message": "List of hostnames for which no filtering will take place.", + "message": "List of websites for which no filtering will take place.", "description": "A short description for the editable field which lists trusted sites" }, + "noFilteringModePlaceholder": { + "message": "[hostnames only]\nexample.com\ngames.example\n...", + "description": "Default text for in edit field" + }, "behaviorSectionLabel": { "message": "Behavior", "description": "The header text for the 'Behavior' section" diff --git a/platform/mv3/extension/_locales/ta/messages.json b/platform/mv3/extension/_locales/ta/messages.json index 4bbb5e389bf08..30c719d52722d 100644 --- a/platform/mv3/extension/_locales/ta/messages.json +++ b/platform/mv3/extension/_locales/ta/messages.json @@ -144,9 +144,13 @@ "description": "This describes the 'complete' filtering mode" }, "noFilteringModeDescription": { - "message": "List of hostnames for which no filtering will take place.", + "message": "List of websites for which no filtering will take place.", "description": "A short description for the editable field which lists trusted sites" }, + "noFilteringModePlaceholder": { + "message": "[hostnames only]\nexample.com\ngames.example\n...", + "description": "Default text for in edit field" + }, "behaviorSectionLabel": { "message": "Behavior", "description": "The header text for the 'Behavior' section" diff --git a/platform/mv3/extension/_locales/te/messages.json b/platform/mv3/extension/_locales/te/messages.json index 00008c2bf2ac7..8437838c64a0c 100644 --- a/platform/mv3/extension/_locales/te/messages.json +++ b/platform/mv3/extension/_locales/te/messages.json @@ -147,6 +147,10 @@ "message": "వడపోత జరగని హోస్ట్ పేర్ల జాబితా", "description": "A short description for the editable field which lists trusted sites" }, + "noFilteringModePlaceholder": { + "message": "[hostnames only]\nexample.com\ngames.example\n...", + "description": "Default text for in edit field" + }, "behaviorSectionLabel": { "message": "ప్రవర్తన", "description": "The header text for the 'Behavior' section" diff --git a/platform/mv3/extension/_locales/th/messages.json b/platform/mv3/extension/_locales/th/messages.json index 8d523ba56d2de..fee5db38e9fc4 100644 --- a/platform/mv3/extension/_locales/th/messages.json +++ b/platform/mv3/extension/_locales/th/messages.json @@ -16,15 +16,15 @@ "description": "English: uBO Lite — Dashboard" }, "settingsPageName": { - "message": "Settings", + "message": "การตั้งค่า", "description": "appears as tab name in dashboard" }, "aboutPageName": { - "message": "About", + "message": "เกี่ยวกับเรา", "description": "appears as tab name in dashboard" }, "aboutPrivacyPolicy": { - "message": "Privacy policy", + "message": "นโยบายความเป็นส่วนตัว", "description": "Link to privacy policy on GitHub (English)" }, "popupFilteringModeLabel": { @@ -72,7 +72,7 @@ "description": "Header for a ruleset section in 'Filter lists pane'" }, "aboutChangelog": { - "message": "Changelog", + "message": "บันทึการเปลี่ยนแปลง", "description": "" }, "aboutCode": { @@ -100,15 +100,15 @@ "description": "Shown in the About pane" }, "firstRunSectionLabel": { - "message": "Welcome", + "message": "ยินดีต้อนรับ", "description": "The header text for the welcome message section" }, "firstRunDescription": { - "message": "You have just installed uBO Lite. Here you can choose the default filtering mode to use on all websites.\n\nBy default, Basic mode is selected because it does not require the permission to read and modify data. If you trust uBO Lite, you can give it broad permission to read and modify data on all websites in order to enable more advanced filtering capabilities for all websites by default.", + "message": "คุณเพิ่งติดตั้ง uBO Lite คุณสามารถเลือกโหมดการกรอง (ค่าเริ่มต้น) ที่จะใช้กับเว็บไซต์ทั้งหมดได้ที่นี่\n\nตามค่าเริ่มต้น โหมด พื้นฐาน จะถูกเลือกเนื่องจากไม่จำเป็นต้องมีสิทธิ์ในการอ่านและแก้ไขข้อมูล หากคุณไว้วางใจ uBO Lite คุณสามารถให้สิทธิ์ในการอ่านและแก้ไขข้อมูลบนเว็บไซต์ทั้งหมดได้ เพื่อเปิดใช้งานคุณสมบัติการกรองข้อมูลขั้นสูงสำหรับเว็บไซต์ทั้งหมดตามค่าเริ่มต้น", "description": "Descriptive text shown at first install time only " }, "defaultFilteringModeSectionLabel": { - "message": "Default filtering mode", + "message": "โหมดการกรองเริ่มต้น", "description": "The header text for the default filtering mode section" }, "defaultFilteringModeDescription": { @@ -120,7 +120,7 @@ "description": "Name of blocking mode 0" }, "filteringMode1Name": { - "message": "basic", + "message": "พื้นฐาน", "description": "Name of blocking mode 1" }, "filteringMode2Name": { @@ -144,19 +144,23 @@ "description": "This describes the 'complete' filtering mode" }, "noFilteringModeDescription": { - "message": "List of hostnames for which no filtering will take place.", + "message": "List of websites for which no filtering will take place.", "description": "A short description for the editable field which lists trusted sites" }, + "noFilteringModePlaceholder": { + "message": "[hostnames only]\nexample.com\ngames.example\n...", + "description": "Default text for in edit field" + }, "behaviorSectionLabel": { - "message": "Behavior", + "message": "ลักษณะการทำงาน", "description": "The header text for the 'Behavior' section" }, "autoReloadLabel": { - "message": "Automatically reload page when changing filtering mode", + "message": "โหลดหน้าเว็บใหม่อัตโนมัติเมื่อเปลี่ยนโหมดการกรอง", "description": "Label for a checkbox in the options page" }, "showBlockedCountLabel": { - "message": "Show the number of blocked requests on the toolbar icon", + "message": "แสดงจำนวนคำขอที่ถูกปิดกั้นบนไอคอนแถบเครื่องมือ", "description": "Label for a checkbox in the options page" } } diff --git a/platform/mv3/extension/_locales/tr/messages.json b/platform/mv3/extension/_locales/tr/messages.json index a4bf19312686b..5cf18a7020d51 100644 --- a/platform/mv3/extension/_locales/tr/messages.json +++ b/platform/mv3/extension/_locales/tr/messages.json @@ -147,6 +147,10 @@ "message": "Filtreleme yapılmayacak alan alarının listesi", "description": "A short description for the editable field which lists trusted sites" }, + "noFilteringModePlaceholder": { + "message": "[sadece ana bilgisayar adları]\nörnek.com\noyunlar.site\n…", + "description": "Default text for in edit field" + }, "behaviorSectionLabel": { "message": "Davranış", "description": "The header text for the 'Behavior' section" diff --git a/platform/mv3/extension/_locales/uk/messages.json b/platform/mv3/extension/_locales/uk/messages.json index 5bf1a04b50d6e..9a1a054b0f274 100644 --- a/platform/mv3/extension/_locales/uk/messages.json +++ b/platform/mv3/extension/_locales/uk/messages.json @@ -147,6 +147,10 @@ "message": "Список імен хостів, для яких буде відбуватись фільтрування", "description": "A short description for the editable field which lists trusted sites" }, + "noFilteringModePlaceholder": { + "message": "[тільки імена доменів]\nexample.com\ngames.example\n...", + "description": "Default text for in edit field" + }, "behaviorSectionLabel": { "message": "Поведінка", "description": "The header text for the 'Behavior' section" diff --git a/platform/mv3/extension/_locales/ur/messages.json b/platform/mv3/extension/_locales/ur/messages.json index bb06300ced6cf..92ba9e9bc7b5f 100644 --- a/platform/mv3/extension/_locales/ur/messages.json +++ b/platform/mv3/extension/_locales/ur/messages.json @@ -147,6 +147,10 @@ "message": "میزبان ناموں کی فہرست جن کے لیے کوئی فلٹرنگ نہیں ہوگی۔", "description": "A short description for the editable field which lists trusted sites" }, + "noFilteringModePlaceholder": { + "message": "[hostnames only]\nexample.com\ngames.example\n...", + "description": "Default text for in edit field" + }, "behaviorSectionLabel": { "message": "رویہ", "description": "The header text for the 'Behavior' section" diff --git a/platform/mv3/extension/_locales/vi/messages.json b/platform/mv3/extension/_locales/vi/messages.json index 9f796fc5d5ffe..0a9042b6f3652 100644 --- a/platform/mv3/extension/_locales/vi/messages.json +++ b/platform/mv3/extension/_locales/vi/messages.json @@ -147,6 +147,10 @@ "message": "Danh sách tên máy chủ sẽ không được lọc", "description": "A short description for the editable field which lists trusted sites" }, + "noFilteringModePlaceholder": { + "message": "[hostnames only]\nexample.com\ngames.example\n...", + "description": "Default text for in edit field" + }, "behaviorSectionLabel": { "message": "Hành vi", "description": "The header text for the 'Behavior' section" diff --git a/platform/mv3/extension/_locales/zh_CN/messages.json b/platform/mv3/extension/_locales/zh_CN/messages.json index 9195970cb1b11..26f88f655f6d5 100644 --- a/platform/mv3/extension/_locales/zh_CN/messages.json +++ b/platform/mv3/extension/_locales/zh_CN/messages.json @@ -147,6 +147,10 @@ "message": "不进行过滤的主机名列表", "description": "A short description for the editable field which lists trusted sites" }, + "noFilteringModePlaceholder": { + "message": "[hostnames only]\nexample.com\ngames.example\n...", + "description": "Default text for in edit field" + }, "behaviorSectionLabel": { "message": "操作设置", "description": "The header text for the 'Behavior' section" diff --git a/platform/mv3/extension/_locales/zh_TW/messages.json b/platform/mv3/extension/_locales/zh_TW/messages.json index 99ebc6263ea99..d79f5d0ea4356 100644 --- a/platform/mv3/extension/_locales/zh_TW/messages.json +++ b/platform/mv3/extension/_locales/zh_TW/messages.json @@ -147,6 +147,10 @@ "message": "不進行過濾的主機名稱列表", "description": "A short description for the editable field which lists trusted sites" }, + "noFilteringModePlaceholder": { + "message": "[僅主機名稱]\nexample.com\ngames.example\n...", + "description": "Default text for in edit field" + }, "behaviorSectionLabel": { "message": "行為", "description": "The header text for the 'Behavior' section" diff --git a/src/_locales/ca/messages.json b/src/_locales/ca/messages.json index 97ade2de31ecf..0ba1ccc1e0beb 100644 --- a/src/_locales/ca/messages.json +++ b/src/_locales/ca/messages.json @@ -1264,7 +1264,7 @@ "description": "Label for keyboard shortcut used to toggle cosmetic filtering" }, "toggleJavascript": { - "message": "Canviar l'estat de JavaScript", + "message": "Commuta el JavaScript", "description": "Label for keyboard shortcut used to toggle no-scripting switch" }, "relaxBlockingMode": { diff --git a/src/_locales/el/messages.json b/src/_locales/el/messages.json index ea6a4a6662fe9..73b67df631e91 100644 --- a/src/_locales/el/messages.json +++ b/src/_locales/el/messages.json @@ -40,7 +40,7 @@ "description": "appears as tab name in dashboard" }, "whitelistPageName": { - "message": "Έμπιστες τοποθεσίες", + "message": "Αξιόπιστοι ιστότοποι", "description": "appears as tab name in dashboard" }, "shortcutsPageName": { @@ -184,7 +184,7 @@ "description": "Tooltip for the no-scripting per-site switch" }, "popupTipNoScripting2": { - "message": "Κάντε κλικ για να μην απενεργοποιείται πλέον η JavaScript σε αυτή την τοποθεσία", + "message": "Κάντε κλικ για να μην απενεργοποιήσετε πλέον τη JavaScript σε αυτόν τον ιστότοπο", "description": "Tooltip for the no-scripting per-site switch" }, "popupNoPopups_v2": { @@ -196,7 +196,7 @@ "description": "Caption for the no-large-media per-site switch" }, "popupNoCosmeticFiltering_v2": { - "message": "Καλλωπιστικά φίλτρα", + "message": "Κοσμητικά φίλτρα", "description": "Caption for the no-cosmetic-filtering per-site switch" }, "popupNoRemoteFonts_v2": { @@ -224,7 +224,7 @@ "description": "Tooltip when hovering the top-most cell of the local-rules column." }, "popupTipSaveRules": { - "message": "Πατήστε για να κάνετε τις αλλαγές σας μόνιμες.", + "message": "Κάντε κλικ για να μονιμοποιήσετε τις αλλαγές σας.", "description": "Tooltip when hovering over the padlock in the dynamic filtering pane." }, "popupTipRevertRules": { diff --git a/src/_locales/fi/messages.json b/src/_locales/fi/messages.json index da52acf6d3516..1f3677df2940c 100644 --- a/src/_locales/fi/messages.json +++ b/src/_locales/fi/messages.json @@ -540,11 +540,11 @@ "description": "Warning against copy-pasting filters from random sources" }, "1pEnableMyFiltersLabel": { - "message": "Ota omat suodattimet käyttöön", + "message": "Käytä omia suodattimia", "description": "Label for the checkbox use to enable/disable 'My filters' list" }, "1pTrustMyFiltersLabel": { - "message": "Allow custom filters requiring trust", + "message": "Salli omat suodattimet, joihin luotat", "description": "Label for the checkbox use to trust the content of 'My filters' list" }, "1pImport": { @@ -1264,7 +1264,7 @@ "description": "Label for keyboard shortcut used to toggle cosmetic filtering" }, "toggleJavascript": { - "message": "Kytke JavaScript", + "message": "Kytke JavaScript käyttöön/pois käytöstä", "description": "Label for keyboard shortcut used to toggle no-scripting switch" }, "relaxBlockingMode": { diff --git a/src/_locales/gl/messages.json b/src/_locales/gl/messages.json index 291dc091324a8..0da46340cd70c 100644 --- a/src/_locales/gl/messages.json +++ b/src/_locales/gl/messages.json @@ -12,7 +12,7 @@ "description": "English: uBlock₀ — Dashboard" }, "dashboardUnsavedWarning": { - "message": "Aviso! ten cambios sen gardar", + "message": "Aviso! Non gardaches os cambios", "description": "A warning in the dashboard when navigating away from unsaved changes" }, "dashboardUnsavedWarningStay": { @@ -40,7 +40,7 @@ "description": "appears as tab name in dashboard" }, "whitelistPageName": { - "message": "Lista branca", + "message": "Sitios de confianza", "description": "appears as tab name in dashboard" }, "shortcutsPageName": { @@ -336,7 +336,7 @@ "description": "English: Color-blind friendly" }, "settingsAppearance": { - "message": "Aspecto", + "message": "Aparencia", "description": "Section for controlling user interface appearance" }, "settingsThemeLabel": { @@ -480,7 +480,7 @@ "description": "Filter lists section name" }, "3pGroupMalware": { - "message": "Dominios de malware", + "message": "Protección contra malware, seguridade", "description": "Filter lists section name" }, "3pGroupSocial": { @@ -596,7 +596,7 @@ "description": "" }, "rulesExport": { - "message": "Exportar a un arquivo…", + "message": "Exportar a un ficheiro…", "description": "Button in the 'My rules' pane" }, "rulesDefaultFileName": { @@ -632,15 +632,15 @@ "description": "A concise description of the 'Trusted sites' pane." }, "whitelistImport": { - "message": "Importar e anexar", + "message": "Importar e engadir…", "description": "Button in the 'Trusted sites' pane" }, "whitelistExport": { - "message": "Exportar", + "message": "Exportar…", "description": "Button in the 'Trusted sites' pane" }, "whitelistExportFilename": { - "message": "o-meu-ublock-lista-branca_{{datetime}}.txt", + "message": "o-meu-ublock-webs-confiables_{{datetime}}.txt", "description": "The default filename to use for import/export purpose" }, "whitelistApply": { diff --git a/src/_locales/hu/messages.json b/src/_locales/hu/messages.json index b41a850395026..39d020f645591 100644 --- a/src/_locales/hu/messages.json +++ b/src/_locales/hu/messages.json @@ -12,7 +12,7 @@ "description": "English: uBlock₀ — Dashboard" }, "dashboardUnsavedWarning": { - "message": "Figyelem! Neked még vannak nem mentett változtatásaid", + "message": "Figyelem! Mentetlen változtatásai vannak.", "description": "A warning in the dashboard when navigating away from unsaved changes" }, "dashboardUnsavedWarningStay": { @@ -108,7 +108,7 @@ "description": "For the new mobile-friendly popup design" }, "popupDomainsConnected_v2": { - "message": "Csatlakoztatott domain-ek", + "message": "Kapcsolódott domainek", "description": "For the new mobile-friendly popup design" }, "popupTipDashboard": { @@ -200,7 +200,7 @@ "description": "Caption for the no-cosmetic-filtering per-site switch" }, "popupNoRemoteFonts_v2": { - "message": "Távoli betűtípusok", + "message": "Távoli betűkészletek", "description": "Caption for the no-remote-fonts per-site switch" }, "popupNoScripting_v2": { @@ -276,7 +276,7 @@ "description": "Example of use: Version 1.26.4" }, "popup3pScriptFilter": { - "message": "szkript", + "message": "parancsfájl", "description": "Appears as an option to filter out firewall rows" }, "popup3pFrameFilter": { @@ -312,7 +312,7 @@ "description": "English: Click, Ctrl-click" }, "pickerContextMenuEntry": { - "message": "Elem blokkolása", + "message": "Elem blokkolása…", "description": "An entry in the browser's contextual menu" }, "settingsCollapseBlockedPrompt": { @@ -344,7 +344,7 @@ "description": "Label for checkbox to enable a custom dark theme" }, "settingsThemeAccent0Label": { - "message": "Egyedi akcentus szín", + "message": "Egyéni kiemelőszín", "description": "Label for checkbox to pick an accent color" }, "settingsCloudStorageEnabledPrompt": { @@ -400,11 +400,11 @@ "description": "background information: https://github.com/uBlockOrigin/uBlock-issues/issues/1513" }, "settingsAdvanced": { - "message": "Haladó", + "message": "Speciális", "description": "Section for controlling advanced-user settings" }, "settingsAdvancedSynopsis": { - "message": "Funkciók csak haladó felhasználóknak ", + "message": "Funkciók csak hozzáértő felhasználóknak", "description": "Description of section controlling advanced-user settings" }, "settingsAdvancedUserSettings": { @@ -444,7 +444,7 @@ "description": "English: Parse and enforce Adblock+ element hiding filters." }, "3pParseAllABPHideFiltersInfo": { - "message": "

Ez a beállítás engedélyezi az Adblock Plus-típusú “elemelrejtő” szűrőket. Ezek kizárólag kozmetikai célokat szolgálnak; elrejtik egy webhely azon elemeit, amelyek vizuálisan zavaróak, de a hálózati lekérések alapján nem szűrhetők ki.

A beállítás engedélyezése megnöveli a uBlock₀ memóriahasználatát.

", + "message": "A kozmetikai célú szűrők elrejtik a webhely azon elemeit, amelyek vizuálisan zavaróak, de a hálózati kérések alapján nem szűrhetők ki.", "description": "Describes the purpose of the 'Parse and enforce cosmetic filters' feature." }, "3pIgnoreGenericCosmeticFilters": { @@ -484,7 +484,7 @@ "description": "Filter lists section name" }, "3pGroupSocial": { - "message": "Közösségi widgetek", + "message": "Közösségi média felületi elemei", "description": "Filter lists section name" }, "3pGroupCookies": { @@ -536,15 +536,15 @@ "description": "used as a tooltip for error icon beside a list" }, "1pTrustWarning": { - "message": "Ne adj hozzá szűrőket megbízhatatlan forrásokból.", + "message": "Ne adjon hozzá megbízhatatlan forrásokból származó szűrőket.", "description": "Warning against copy-pasting filters from random sources" }, "1pEnableMyFiltersLabel": { - "message": "Egyedi szűrőim engedélyezése", + "message": "Egyéni szűrők engedélyezése", "description": "Label for the checkbox use to enable/disable 'My filters' list" }, "1pTrustMyFiltersLabel": { - "message": "Megbízhatóságot igénylő egyéni szűrők engedélyezése", + "message": "Bizalmas egyéni szűrők engedélyezése", "description": "Label for the checkbox use to trust the content of 'My filters' list" }, "1pImport": { @@ -640,7 +640,7 @@ "description": "Button in the 'Trusted sites' pane" }, "whitelistExportFilename": { - "message": "ublock-feherlistam_{{datetime}}.txt", + "message": "ublock-megbizhato-oldalak_{{datetime}}.txt", "description": "The default filename to use for import/export purpose" }, "whitelistApply": { @@ -704,7 +704,7 @@ "description": "Tooltip for the play button in the logger page" }, "loggerRowFiltererButtonTip": { - "message": "Naplózó szűrőjének kapcsolása", + "message": "Naplózó szűrése be/ki", "description": "Tooltip for the row filterer button in the logger page" }, "logFilterPrompt": { @@ -712,7 +712,7 @@ "description": "Placeholder string for logger output filtering input field" }, "loggerRowFiltererBuiltinTip": { - "message": "Naplózó szűrő beállítások", + "message": "Naplózó szűrőbeállításai", "description": "Tooltip for the button to bring up logger output filtering options" }, "loggerRowFiltererBuiltinNot": { @@ -732,7 +732,7 @@ "description": "A keyword in the built-in row filtering expression" }, "loggerRowFiltererBuiltinModified": { - "message": "módosított", + "message": "módosítva", "description": "A keyword in the built-in row filtering expression" }, "loggerRowFiltererBuiltin1p": { @@ -752,7 +752,7 @@ "description": "Label to identify a filter field" }, "loggerEntryDetailsFilterList": { - "message": "Szűrő lista", + "message": "Szűrőlista", "description": "Label to identify a filter list field" }, "loggerEntryDetailsRule": { @@ -760,15 +760,15 @@ "description": "Label to identify a rule field" }, "loggerEntryDetailsContext": { - "message": "Kontextus", + "message": "Környezet", "description": "Label to identify a context field (typically a hostname)" }, "loggerEntryDetailsRootContext": { - "message": "Gyökér kontextus", + "message": "Gyökérkörnyezet", "description": "Label to identify a root context field (typically a hostname)" }, "loggerEntryDetailsPartyness": { - "message": "Partyness", + "message": "Fél", "description": "Label to identify a field providing partyness information" }, "loggerEntryDetailsType": { @@ -780,7 +780,7 @@ "description": "Label to identify the URL of an entry" }, "loggerURLFilteringHeader": { - "message": "Dinamikus URL szűrő", + "message": "Webcímszabály", "description": "Small header to identify the dynamic URL filtering section" }, "loggerURLFilteringContextLabel": { @@ -792,7 +792,7 @@ "description": "Label for the type selector" }, "loggerStaticFilteringHeader": { - "message": "Statikus szűrés", + "message": "Statikus szűrő", "description": "Small header to identify the static filtering section" }, "loggerStaticFilteringSentence": { @@ -840,23 +840,23 @@ "description": "Message to show when a filter cannot be found in any filter lists" }, "loggerSettingDiscardPrompt": { - "message": "A naplózó bejegyzések, amelyek nem felelnek meg az alábbi három feltételnek, automatikusan eldobásra kerülnek:", + "message": "Azok a naplóbejegyzések, amelyek nem felelnek meg az alábbi három feltételnek, automatikusan eldobásra kerülnek:", "description": "Logger setting: A sentence to describe the purpose of the settings below" }, "loggerSettingPerEntryMaxAge": { - "message": "A bejegyzések megőrzése az utolsó {{input}} percből", + "message": "Bejegyzések megőrzése az utolsó {{input}} percből", "description": "A logger setting" }, "loggerSettingPerTabMaxLoads": { - "message": "Tartsa meg a legtöbb {{input}} lapot betöltéskor laponként", + "message": "Laponként legfeljebb {{input}} oldalbetöltés megtartása", "description": "A logger setting" }, "loggerSettingPerTabMaxEntries": { - "message": "Tartsa meg a legtöbb {{input}} bejegyzést laponként", + "message": "Laponként legfeljebb {{input}} bejegyzés megtartása", "description": "A logger setting" }, "loggerSettingPerEntryLineCount": { - "message": "Használja a {{input}} sorokat egy bejegyzésre függőlegesen bővített módban", + "message": "Bejegyzésenként {{input}} sor használata a függőlegesen bővített módban", "description": "A logger setting" }, "loggerSettingHideColumnsPrompt": { @@ -872,11 +872,11 @@ "description": "A label for the filter or rule column" }, "loggerSettingHideColumnContext": { - "message": "{{input}} Kontextus", + "message": "{{input}} Környezet", "description": "A label for the context column" }, "loggerSettingHideColumnPartyness": { - "message": "{{input}} Partyness", + "message": "{{input}} Fél", "description": "A label for the partyness column" }, "loggerExportFormatList": { @@ -888,7 +888,7 @@ "description": "Label for radio-button to pick export format" }, "loggerExportEncodePlain": { - "message": "Sík", + "message": "Egyszerű", "description": "Label for radio-button to pick export text format" }, "loggerExportEncodeMarkdown": { @@ -912,7 +912,7 @@ "description": "Header of 'Documentation' section in Support pane" }, "supportS1P1": { - "message": "Az uBlock Origin további funkcióihoz látogassa meg a dokumentációt itt: uBlock/wiki.", + "message": "A uBlock Origin további funkcióihoz olvassa el a dokumentációt itt: uBlock/wiki.", "description": "First paragraph of 'Documentation' section in Support pane" }, "supportS2H": { @@ -928,15 +928,15 @@ "description": "Header of 'Filter issues' section in Support pane" }, "supportS3P1": { - "message": "Adott weboldalra vonatkozó szűrőhibákat jelentse a uBlockOrigin/uAssets hibakövetőn. GitHub-fiók szükséges.", + "message": "Az adott weboldalra vonatkozó szűrőhibákat jelentse a uBlockOrigin/uAssets hibakövetőben. GitHub-fiók szükséges.", "description": "First paragraph of 'Filter issues' section in Support pane" }, "supportS3P2": { - "message": "Fontos: Ne használjon hasonló célú reklámblokkolókat az uBlock Originnal egyszerre, mert ez szűrőhibákat okozhat bizonyos weboldalakon.", + "message": "Fontos: Ne használjon hasonló célú reklámblokkolókat a uBlock Originnel egyidőben, mert ez szűrőhibákat okozhat bizonyos weboldalakon.", "description": "Second paragraph of 'Filter issues' section in Support pane" }, "supportS3P3": { - "message": "Tippek: Bizonyosodjon meg arról, hogy a legfrissebb szűrőlistákat használja. A napló a legfontosabb eszköz a szűrőkkel kapcsolatos hibák diagnózisában.", + "message": "Tippek: Bizonyosodjon meg arról, hogy a legfrissebb szűrőlistákat használja. A naplózó a legfontosabb eszköz a szűrőkkel kapcsolatos hibák megállapításában.", "description": "Third paragraph of 'Filter issues' section in Support pane" }, "supportS4H": { @@ -944,19 +944,19 @@ "description": "Header of 'Bug report' section in Support pane" }, "supportS4P1": { - "message": "Az uBlock Origin hibáit jelentse a uBlockOrigin/uBlock-issue hibakövetőn. GitHub-fiók szükséges.", + "message": "A uBlock Origin hibáit jelentse a uBlockOrigin/uBlock-issue hibakövetőben. GitHub-fiók szükséges.", "description": "First paragraph of 'Bug report' section in Support pane" }, "supportS5H": { - "message": "Hibakeresési információ", + "message": "Hibakeresési információk", "description": "Header of 'Troubleshooting Information' section in Support pane" }, "supportS5P1": { - "message": "Az alább található technikai információk segíthetnek önkénteseinknek megoldani a problémáját.", + "message": "Az alábbi műszaki információk segíthetnek önkénteseinknek megoldani a problémáját.", "description": "First paragraph of 'Troubleshooting Information' section in Support pane" }, "supportS5P2": { - "message": "Fontos: Az esetlegesen személyes vagy érzékeny adatok alapértelmezésből rejtve vannak. Az elrejtett információk megnehezíthetik a probléma megoldását.", + "message": "Fontos: Az esetleges személyes vagy érzékeny adatok alapértelmezés szerint eltávolításra kerülnek. A hiányzó információk megnehezíthetik a probléma megoldását.", "description": "Second paragraph of 'Troubleshooting Information' section in Support pane" }, "supportS6H": { @@ -968,7 +968,7 @@ "description": "A paragraph in the filter issue reporter section" }, "supportS6P2S1": { - "message": "A szűrőlisták naponta frissülnek. Ügyeljen arra, hogy az Ön problémája ne szerepeljen már a legfrissebb szűrőlistákon.", + "message": "A szűrőlisták naponta frissülnek. Győződjön meg róla, hogy a problémáját nem oldják meg a legfrissebb szűrőlisták.", "description": "A paragraph in the filter issue reporter section" }, "supportS6P2S2": { @@ -976,19 +976,19 @@ "description": "A paragraph in the filter issue reporter section" }, "supportS6URL": { - "message": "A weboldal címe:", + "message": "A weboldal címe:", "description": "Label for the URL of the page" }, "supportS6Select1": { - "message": "A weboldal...", + "message": "A weboldal…", "description": "Label for widget to select type of issue" }, "supportS6Select1Option0": { - "message": "-- Válasszon egy opciót --", + "message": "-- Válasszon egy bejegyzést --", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option1": { - "message": "Hirdetéseket vagy hirdetés maradványokat jelenít meg", + "message": "Hirdetéseket vagy azok maradványait jelenít meg", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option2": { @@ -996,7 +996,7 @@ "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option3": { - "message": "Érzékeli a uBlock Origin-t", + "message": "Észleli a uBlock Origint", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option4": { @@ -1004,7 +1004,7 @@ "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option5": { - "message": "Hibásan működik, amikor az uBlock Origin be van kapcsolva", + "message": "Hibásan működik, amikor a uBlock Origin be van kapcsolva", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option6": { @@ -1024,7 +1024,7 @@ "description": "Text for 'Unredact' button" }, "aboutPrivacyPolicy": { - "message": "Adatvédelmi szabályzat", + "message": "Adatvédelmi irányelvek", "description": "Link to privacy policy on GitHub (English)" }, "aboutChangelog": { @@ -1048,7 +1048,7 @@ "description": "Link text to translations repo" }, "aboutFilterLists": { - "message": "Szűrő listák", + "message": "Szűrőlisták", "description": "Link text to uBO's own filter lists repo" }, "aboutDependencies": { @@ -1056,15 +1056,15 @@ "description": "Shown in the About pane" }, "aboutCDNs": { - "message": "Az uBO saját szűrőlistáit a következő ingyenes CDN-ek (angol) szolgáltatják:", + "message": "A uBO saját szűrőlistáit a következő ingyenes CDN-ek (angol) szolgáltatják:", "description": "Shown in the About pane" }, "aboutCDNsInfo": { - "message": "A szűrőlisták frissítéséhez egy véletlenszerűen kiválasztott CDN-t használ a bővítmény", + "message": "A szűrőlisták frissítéséhez egy véletlenszerűen kiválasztott CDN használatos.", "description": "Shown in the About pane" }, "aboutBackupDataButton": { - "message": "Biztonsági mentés fájlba", + "message": "Biztonsági mentés fájlba…", "description": "Text for button to create a backup of all settings" }, "aboutBackupFilename": { @@ -1076,7 +1076,7 @@ "description": "English: Restore from file..." }, "aboutResetDataButton": { - "message": "Alapértelmezett beállítások visszaállítása...", + "message": "Alapértelmezett beállítások visszaállítása…", "description": "English: Reset to default settings..." }, "aboutRestoreDataConfirm": { @@ -1096,7 +1096,7 @@ "description": "English: Network error: {{msg}}" }, "subscriberConfirm": { - "message": "uBlock₀: Hozzáadja a következő URL-t a saját szűrő listákhoz?\n\nNév: \"{{title}}\"\nURL: \"{{url}}\"", + "message": "Hozzáadja a következő webcímet a saját szűrőlistáihoz?\n\nNév: „{{title}}”\nWebcím: {{url}}", "description": "No longer used" }, "subscribeButton": { @@ -1228,11 +1228,11 @@ "description": "" }, "contextMenuBlockElementInFrame": { - "message": "Elem blokkolása a keretben", + "message": "Elem blokkolása a keretben…", "description": "An entry in the browser's contextual menu" }, "contextMenuSubscribeToList": { - "message": "Feliratkozás szűrőlistára...", + "message": "Feliratkozás szűrőlistára…", "description": "An entry in the browser's contextual menu" }, "contextMenuTemporarilyAllowLargeMediaElements": { @@ -1248,7 +1248,7 @@ "description": "Placeholder string for input field used to capture a keyboard shortcut" }, "genericMergeViewScrollLock": { - "message": "Zárolt görgetés kapcsolása", + "message": "Zárolt görgetés be/ki", "description": "Tooltip for the button used to lock scrolling between the views in the 'My rules' pane" }, "genericCopyToClipboard": { @@ -1256,7 +1256,7 @@ "description": "Label for buttons used to copy something to the clipboard" }, "genericSelectAll": { - "message": "Mindent kijelöl", + "message": "Összes kijelölése", "description": "Label for buttons used to select all text in editor" }, "toggleCosmeticFiltering": { @@ -1264,19 +1264,19 @@ "description": "Label for keyboard shortcut used to toggle cosmetic filtering" }, "toggleJavascript": { - "message": "Javascript ki/bekapcsolása", + "message": "Javascript be/ki", "description": "Label for keyboard shortcut used to toggle no-scripting switch" }, "relaxBlockingMode": { - "message": "Relaxáló blokkolási mód", + "message": "Blokkolási mód lazítása", "description": "Label for keyboard shortcut used to relax blocking mode" }, "storageUsed": { - "message": "Használt tárolás: {{value}} {{unit}}", + "message": "Használt tárhely: {{value}} {{unit}}", "description": " In Setting pane, renders as (example): Storage used: 13.2 MB" }, "KB": { - "message": "KB", + "message": "kB", "description": "short for 'kilobytes'" }, "MB": { @@ -1288,7 +1288,7 @@ "description": "short for 'gigabytes'" }, "clickToLoad": { - "message": "Kattints a betöltéshez", + "message": "Kattintson a betöltéshez", "description": "Message used in frame placeholders" }, "linterMainReport": { diff --git a/src/_locales/pa/messages.json b/src/_locales/pa/messages.json index b58a1203bc44b..584a23d7e7caf 100644 --- a/src/_locales/pa/messages.json +++ b/src/_locales/pa/messages.json @@ -488,7 +488,7 @@ "description": "Filter lists section name" }, "3pGroupCookies": { - "message": "Cookie notices", + "message": "ਕੂਕੀਜ਼ ਨੋਟਿਸ", "description": "Filter lists section name" }, "3pGroupAnnoyances": { @@ -540,7 +540,7 @@ "description": "Warning against copy-pasting filters from random sources" }, "1pEnableMyFiltersLabel": { - "message": "Enable my custom filters", + "message": "ਮੇਰੇ ਕਸਟਮ ਫਿਲਟਰ ਸਮਰੱਥ ਕਰੋ", "description": "Label for the checkbox use to enable/disable 'My filters' list" }, "1pTrustMyFiltersLabel": { @@ -920,7 +920,7 @@ "description": "Header of 'Questions and support' section in Support pane" }, "supportS2P1": { - "message": "Answers to questions and other kinds of help support is provided on the subreddit /r/uBlockOrigin.", + "message": "ਸਵਾਲਾਂ ਦੇ ਜਵਾਬ ਅਤੇ ਹੋਰ ਕਿਸੇ ਵੀ ਕਿਸਮ ਦੀ ਮਦਦ subreddit /r/uBlockOrigin ਉੱਤੇ ਦਿੱਤੀ ਜਾਂਦੀ ਹੈ।", "description": "First paragraph of 'Questions and support' section in Support pane" }, "supportS3H": { @@ -1068,7 +1068,7 @@ "description": "Text for button to create a backup of all settings" }, "aboutBackupFilename": { - "message": "my-ublock-backup_{{datetime}}.txt", + "message": "ਮੇਰਾ-ublock-ਬੈਕ-ਅੱਪ_{{datetime}}.txt", "description": "English: my-ublock-backup_{{datetime}}.txt" }, "aboutRestoreDataButton": { diff --git a/src/_locales/sr/messages.json b/src/_locales/sr/messages.json index 35e56fc77a59b..f17e861e0d097 100644 --- a/src/_locales/sr/messages.json +++ b/src/_locales/sr/messages.json @@ -548,11 +548,11 @@ "description": "Label for the checkbox use to trust the content of 'My filters' list" }, "1pImport": { - "message": "Увези и додај", + "message": "Увези и додај…", "description": "Button in the 'My filters' pane" }, "1pExport": { - "message": "Извези", + "message": "Извези…", "description": "Button in the 'My filters' pane" }, "1pExportFilename": { From db3dc69bcc500ca273e70bb8fa000ad29ccfc586 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Mon, 12 Aug 2024 13:45:46 -0400 Subject: [PATCH 0120/1099] Improve `href-sanitizer` sciptlet Tolerate unexpected spaces in extracted URL parameters. Related feedback: https://github.com/uBlockOrigin/uBlock-issues/issues/3297#issuecomment-2283806183 --- assets/resources/scriptlets.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/assets/resources/scriptlets.js b/assets/resources/scriptlets.js index 866b306678d1e..8cbeb386a70e9 100644 --- a/assets/resources/scriptlets.js +++ b/assets/resources/scriptlets.js @@ -3551,9 +3551,12 @@ function hrefSanitizer( const end = recursive ? source.indexOf('?', 1) : source.length; try { const url = new URL(href, document.location); - const value = url.searchParams.get(source.slice(1, end)); + let value = url.searchParams.get(source.slice(1, end)); if ( value === null ) { return href } if ( recursive ) { return extractParam(value, source.slice(end)); } + if ( value.includes(' ') ) { + value = value.replace(/ /g, '%20'); + } return value; } catch(x) { } From f919218e275b3a08e07b00ea7e3bdcabb8aa8250 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Mon, 12 Aug 2024 14:11:49 -0400 Subject: [PATCH 0121/1099] Update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 24b7c6b9a9d15..7317f4d7f5408 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,4 @@ +- [Improve `href-sanitizer` sciptlet](https://github.com/gorhill/uBlock/commit/db3dc69bcc) - [Improve `remove-attr.js` scriptlet](https://github.com/gorhill/uBlock/commit/fb037e97d0) - [Improve `trusted-replace-node-text` scriptlet](https://github.com/gorhill/uBlock/commit/4f0d1301ab) From 0ec2c1f54caadf198100fd3c8e745c0960f54e55 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Mon, 12 Aug 2024 14:12:55 -0400 Subject: [PATCH 0122/1099] New revision for dev build --- dist/version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dist/version b/dist/version index 4c593fac9f49f..8eaa884f833ba 100644 --- a/dist/version +++ b/dist/version @@ -1 +1 @@ -1.59.1.1 \ No newline at end of file +1.59.1.2 \ No newline at end of file From 415f9b87434ddaa98f2e9e16340601af3d342ee5 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Mon, 12 Aug 2024 14:20:54 -0400 Subject: [PATCH 0123/1099] Make Firefox dev build auto-update --- dist/firefox/updates.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dist/firefox/updates.json b/dist/firefox/updates.json index c3bc0cd6412ed..8553569b3ceb7 100644 --- a/dist/firefox/updates.json +++ b/dist/firefox/updates.json @@ -3,9 +3,9 @@ "uBlock0@raymondhill.net": { "updates": [ { - "version": "1.59.1.1", + "version": "1.59.1.2", "browser_specific_settings": { "gecko": { "strict_min_version": "78.0" } }, - "update_link": "https://github.com/gorhill/uBlock/releases/download/1.59.1b1/uBlock0_1.59.1b1.firefox.signed.xpi" + "update_link": "https://github.com/gorhill/uBlock/releases/download/1.59.1b2/uBlock0_1.59.1b2.firefox.signed.xpi" } ] } From 9ced01ebf7f0c4a8961b572f5c14c2c3330f1131 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Mon, 12 Aug 2024 14:24:00 -0400 Subject: [PATCH 0124/1099] Add publish script for stable version on Chromium --- dist/chromium/publish-stable.py | 190 ++++++++++++++++++++++++++++++++ 1 file changed, 190 insertions(+) create mode 100755 dist/chromium/publish-stable.py diff --git a/dist/chromium/publish-stable.py b/dist/chromium/publish-stable.py new file mode 100755 index 0000000000000..d1adc14740783 --- /dev/null +++ b/dist/chromium/publish-stable.py @@ -0,0 +1,190 @@ +#!/usr/bin/env python3 + +import datetime +import json +import os +import re +import requests +import shutil +import subprocess +import sys +import tempfile +import time +import zipfile + +from string import Template + +# - Download target (raw) uBlock0.chromium.zip from GitHub +# - This is referred to as "raw" package +# - This will fail if not a dev build +# - Upload uBlock0.chromium.zip to Chrome store +# - Publish uBlock0.chromium.zip to Chrome store + +# Find path to project root +projdir = os.path.split(os.path.abspath(__file__))[0] +while not os.path.isdir(os.path.join(projdir, '.git')): + projdir = os.path.normpath(os.path.join(projdir, '..')) + +# We need a version string to work with +if len(sys.argv) >= 2 and sys.argv[1]: + version = sys.argv[1] +else: + version = input('Github release version: ') +version.strip() +if not re.search('^\d+\.\d+\.\d+$', version): + print('Error: Invalid version string.') + exit(1) + +cs_extension_id = 'cjpalhdlnbpafiamejdnhcphjbkeiagm' +tmpdir = tempfile.TemporaryDirectory() +raw_zip_filename = 'uBlock0_' + version + '.chromium.zip' +raw_zip_filepath = os.path.join(tmpdir.name, raw_zip_filename) +github_owner = 'gorhill' +github_repo = 'uBlock' + +# Load/save auth secrets +# The tmp directory is excluded from git +ubo_secrets = dict() +ubo_secrets_filename = os.path.join(projdir, 'tmp', 'ubo_secrets') +if os.path.isfile(ubo_secrets_filename): + with open(ubo_secrets_filename) as f: + ubo_secrets = json.load(f) + +def input_secret(prompt, token): + if token in ubo_secrets: + prompt += ' ✔' + prompt += ': ' + value = input(prompt).strip() + if len(value) == 0: + if token not in ubo_secrets: + print('Token error:', token) + exit(1) + value = ubo_secrets[token] + elif token not in ubo_secrets or value != ubo_secrets[token]: + ubo_secrets[token] = value + exists = os.path.isfile(ubo_secrets_filename) + with open(ubo_secrets_filename, 'w') as f: + json.dump(ubo_secrets, f, indent=2) + if not exists: + os.chmod(ubo_secrets_filename, 0o600) + return value + + +# GitHub API token +github_token = input_secret('Github token', 'github_token') +github_auth = 'token ' + github_token + +# +# Get metadata from GitHub about the release +# + +# https://developer.github.com/v3/repos/releases/#get-a-single-release +print('Downloading release info from GitHub...') +release_info_url = 'https://api.github.com/repos/{0}/{1}/releases/tags/{2}'.format(github_owner, github_repo, version) +headers = { 'Authorization': github_auth, } +response = requests.get(release_info_url, headers=headers) +if response.status_code != 200: + print('Error: Release not found: {0}'.format(response.status_code)) + exit(1) +release_info = response.json() + +# +# Extract URL to raw package from metadata +# + +# Find url for uBlock0.chromium.zip +raw_zip_url = '' +for asset in release_info['assets']: + if asset['name'] == raw_zip_filename: + raw_zip_url = asset['url'] +if len(raw_zip_url) == 0: + print('Error: Release asset URL not found') + exit(1) + +# +# Download raw package from GitHub +# + +# https://developer.github.com/v3/repos/releases/#get-a-single-release-asset +print('Downloading raw zip package from GitHub...') +headers = { + 'Authorization': github_auth, + 'Accept': 'application/octet-stream', +} +response = requests.get(raw_zip_url, headers=headers) +# Redirections are transparently handled: +# http://docs.python-requests.org/en/master/user/quickstart/#redirection-and-history +if response.status_code != 200: + print('Error: Downloading raw package failed -- server error {0}'.format(response.status_code)) + exit(1) +with open(raw_zip_filepath, 'wb') as f: + f.write(response.content) +print('Downloaded raw package saved as {0}'.format(raw_zip_filepath)) + +# +# Upload to Chrome store +# + +# Auth tokens +cs_id = input_secret('Chrome store id', 'cs_id') +cs_secret = input_secret('Chrome store secret', 'cs_secret') +cs_refresh = input_secret('Chrome store refresh token', 'cs_refresh') + +print('Uploading to Chrome store...') +with open(raw_zip_filepath, 'rb') as f: + print('Generating access token...') + auth_url = 'https://accounts.google.com/o/oauth2/token' + auth_payload = { + 'client_id': cs_id, + 'client_secret': cs_secret, + 'grant_type': 'refresh_token', + 'refresh_token': cs_refresh, + } + auth_response = requests.post(auth_url, data=auth_payload) + if auth_response.status_code != 200: + print('Error: Auth failed -- server error {0}'.format(auth_response.status_code)) + print(auth_response.text) + exit(1) + response_dict = auth_response.json() + if 'access_token' not in response_dict: + print('Error: Auth failed -- no access token') + exit(1) + # Prepare access token + cs_auth = 'Bearer ' + response_dict['access_token'] + headers = { + 'Authorization': cs_auth, + 'x-goog-api-version': '2', + } + # Upload + print('Uploading package...') + upload_url = 'https://www.googleapis.com/upload/chromewebstore/v1.1/items/{0}'.format(cs_extension_id) + upload_response = requests.put(upload_url, headers=headers, data=f) + f.close() + if upload_response.status_code != 200: + print('Upload failed -- server error {0}'.format(upload_response.status_code)) + print(upload_response.text) + exit(1) + response_dict = upload_response.json(); + if 'uploadState' not in response_dict or response_dict['uploadState'] != 'SUCCESS': + print('Upload failed -- server error {0}'.format(response_dict['uploadState'])) + exit(1) + print('Upload succeeded.') + # Publish + print('Publishing package...') + publish_url = 'https://www.googleapis.com/chromewebstore/v1.1/items/{0}/publish'.format(cs_extension_id) + headers = { + 'Authorization': cs_auth, + 'x-goog-api-version': '2', + 'Content-Length': '0', + } + publish_response = requests.post(publish_url, headers=headers) + if publish_response.status_code != 200: + print('Error: Chrome store publishing failed -- server error {0}'.format(publish_response.status_code)) + exit(1) + response_dict = publish_response.json(); + if 'status' not in response_dict or response_dict['status'][0] != 'OK': + print('Publishing failed -- server error {0}'.format(response_dict['status'])) + exit(1) + print('Publishing succeeded.') + +print('All done.') From 56dfdd256853c1cab9910843ab48e99406950dad Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Fri, 16 Aug 2024 11:49:16 -0400 Subject: [PATCH 0125/1099] Improve various scriptlets Specifically, improve proxying of native methods. --- assets/resources/scriptlets.js | 268 ++++++++++++++++----------------- 1 file changed, 126 insertions(+), 142 deletions(-) diff --git a/assets/resources/scriptlets.js b/assets/resources/scriptlets.js index 8cbeb386a70e9..6b08f72373c85 100644 --- a/assets/resources/scriptlets.js +++ b/assets/resources/scriptlets.js @@ -1446,9 +1446,6 @@ function replaceFetchResponseFn( builtinScriptlets.push({ name: 'proxy-apply.fn', fn: proxyApplyFn, - dependencies: [ - 'safe-self.fn', - ], }); function proxyApplyFn( target = '', @@ -1465,12 +1462,28 @@ function proxyApplyFn( } const fn = context[prop]; if ( typeof fn !== 'function' ) { return; } + const fnname = fn.name; + const toString = (function toString() { + return `function ${fnname}() { [native code] }`; + }).bind(null); if ( fn.prototype && fn.prototype.constructor === fn ) { - context[prop] = new Proxy(fn, { construct: handler }); - return (...args) => { return Reflect.construct(...args); }; + context[prop] = new Proxy(fn, { + construct: handler, + get(target, prop, receiver) { + if ( prop === 'toString' ) { return toString; } + return Reflect.get(target, prop, receiver); + }, + }); + return (...args) => Reflect.construct(...args); } - context[prop] = new Proxy(fn, { apply: handler }); - return (...args) => { return Reflect.apply(...args); }; + context[prop] = new Proxy(fn, { + apply: handler, + get(target, prop, receiver) { + if ( prop === 'toString' ) { return toString; } + return Reflect.get(target, prop, receiver); + }, + }); + return (...args) => Reflect.apply(...args); } /******************************************************************************* @@ -1676,6 +1689,7 @@ builtinScriptlets.push({ ], fn: addEventListenerDefuser, dependencies: [ + 'proxy-apply.fn', 'run-at.fn', 'safe-self.fn', 'should-debug.fn', @@ -1732,44 +1746,29 @@ function addEventListenerDefuser( } return matchesBoth; }; - const trapEddEventListeners = ( ) => { - const eventListenerHandler = { - apply: function(target, thisArg, args) { - let t, h; - try { - t = String(args[0]); - if ( typeof args[1] === 'function' ) { - h = String(safe.Function_toString(args[1])); - } else if ( typeof args[1] === 'object' && args[1] !== null ) { - if ( typeof args[1].handleEvent === 'function' ) { - h = String(safe.Function_toString(args[1].handleEvent)); - } - } else { - h = String(args[1]); + runAt(( ) => { + proxyApplyFn('EventTarget.prototype.addEventListener', function(target, thisArg, args) { + let t, h; + try { + t = String(args[0]); + if ( typeof args[1] === 'function' ) { + h = String(safe.Function_toString(args[1])); + } else if ( typeof args[1] === 'object' && args[1] !== null ) { + if ( typeof args[1].handleEvent === 'function' ) { + h = String(safe.Function_toString(args[1].handleEvent)); } - } catch(ex) { - } - if ( type === '' && pattern === '' ) { - safe.uboLog(logPrefix, `Called: ${t}\n${h}\n${elementDetails(thisArg)}`); - } else if ( shouldPrevent(thisArg, t, h) ) { - return safe.uboLog(logPrefix, `Prevented: ${t}\n${h}\n${elementDetails(thisArg)}`); - } - return Reflect.apply(target, thisArg, args); - }, - get(target, prop, receiver) { - if ( prop === 'toString' ) { - return target.toString.bind(target); + } else { + h = String(args[1]); } - return Reflect.get(target, prop, receiver); - }, - }; - self.EventTarget.prototype.addEventListener = new Proxy( - self.EventTarget.prototype.addEventListener, - eventListenerHandler - ); - }; - runAt(( ) => { - trapEddEventListeners(); + } catch(ex) { + } + if ( type === '' && pattern === '' ) { + safe.uboLog(logPrefix, `Called: ${t}\n${h}\n${elementDetails(thisArg)}`); + } else if ( shouldPrevent(thisArg, t, h) ) { + return safe.uboLog(logPrefix, `Prevented: ${t}\n${h}\n${elementDetails(thisArg)}`); + } + return Reflect.apply(target, thisArg, args); + }); }, extraArgs.runAt); } @@ -2475,6 +2474,7 @@ builtinScriptlets.push({ ], fn: noSetIntervalIf, dependencies: [ + 'proxy-apply.fn', 'safe-self.fn', ], }); @@ -2495,35 +2495,27 @@ function noSetIntervalIf( delay = parseInt(delay, 10); } const reNeedle = safe.patternToRegex(needle); - self.setInterval = new Proxy(self.setInterval, { - apply: function(target, thisArg, args) { - const a = args[0] instanceof Function - ? String(safe.Function_toString(args[0])) - : String(args[0]); - const b = args[1]; - if ( needle === '' && delay === undefined ) { - safe.uboLog(logPrefix, `Called:\n${a}\n${b}`); - return Reflect.apply(target, thisArg, args); - } - let defuse; - if ( needle !== '' ) { - defuse = reNeedle.test(a) !== needleNot; - } - if ( defuse !== false && delay !== undefined ) { - defuse = (b === delay || isNaN(b) && isNaN(delay) ) !== delayNot; - } - if ( defuse ) { - args[0] = function(){}; - safe.uboLog(logPrefix, `Prevented:\n${a}\n${b}`); - } + proxyApplyFn('setInterval', function setInterval(target, thisArg, args) { + const a = args[0] instanceof Function + ? String(safe.Function_toString(args[0])) + : String(args[0]); + const b = args[1]; + if ( needle === '' && delay === undefined ) { + safe.uboLog(logPrefix, `Called:\n${a}\n${b}`); return Reflect.apply(target, thisArg, args); - }, - get(target, prop, receiver) { - if ( prop === 'toString' ) { - return target.toString.bind(target); - } - return Reflect.get(target, prop, receiver); - }, + } + let defuse; + if ( needle !== '' ) { + defuse = reNeedle.test(a) !== needleNot; + } + if ( defuse !== false && delay !== undefined ) { + defuse = (b === delay || isNaN(b) && isNaN(delay) ) !== delayNot; + } + if ( defuse ) { + args[0] = function(){}; + safe.uboLog(logPrefix, `Prevented:\n${a}\n${b}`); + } + return Reflect.apply(target, thisArg, args); }); } @@ -2538,6 +2530,7 @@ builtinScriptlets.push({ ], fn: noSetTimeoutIf, dependencies: [ + 'proxy-apply.fn', 'safe-self.fn', ], }); @@ -2558,35 +2551,27 @@ function noSetTimeoutIf( delay = parseInt(delay, 10); } const reNeedle = safe.patternToRegex(needle); - self.setTimeout = new Proxy(self.setTimeout, { - apply: function(target, thisArg, args) { - const a = args[0] instanceof Function - ? String(safe.Function_toString(args[0])) - : String(args[0]); - const b = args[1]; - if ( needle === '' && delay === undefined ) { - safe.uboLog(logPrefix, `Called:\n${a}\n${b}`); - return Reflect.apply(target, thisArg, args); - } - let defuse; - if ( needle !== '' ) { - defuse = reNeedle.test(a) !== needleNot; - } - if ( defuse !== false && delay !== undefined ) { - defuse = (b === delay || isNaN(b) && isNaN(delay) ) !== delayNot; - } - if ( defuse ) { - args[0] = function(){}; - safe.uboLog(logPrefix, `Prevented:\n${a}\n${b}`); - } + proxyApplyFn('setTimeout', function setTimeout(target, thisArg, args) { + const a = args[0] instanceof Function + ? String(safe.Function_toString(args[0])) + : String(args[0]); + const b = args[1]; + if ( needle === '' && delay === undefined ) { + safe.uboLog(logPrefix, `Called:\n${a}\n${b}`); return Reflect.apply(target, thisArg, args); - }, - get(target, prop, receiver) { - if ( prop === 'toString' ) { - return target.toString.bind(target); - } - return Reflect.get(target, prop, receiver); - }, + } + let defuse; + if ( needle !== '' ) { + defuse = reNeedle.test(a) !== needleNot; + } + if ( defuse !== false && delay !== undefined ) { + defuse = (b === delay || isNaN(b) && isNaN(delay) ) !== delayNot; + } + if ( defuse ) { + args[0] = function(){}; + safe.uboLog(logPrefix, `Prevented:\n${a}\n${b}`); + } + return Reflect.apply(target, thisArg, args); }); } @@ -2815,6 +2800,7 @@ builtinScriptlets.push({ ], fn: noWindowOpenIf, dependencies: [ + 'proxy-apply.fn', 'safe-self.fn', ], }); @@ -2845,51 +2831,49 @@ function noWindowOpenIf( setTimeout(( ) => { decoyElem.remove(); }, autoRemoveAfter * 1000); return decoyElem; }; - window.open = new Proxy(window.open, { - apply: function(target, thisArg, args) { - const haystack = args.join(' '); - if ( rePattern.test(haystack) !== targetMatchResult ) { - if ( safe.logLevel > 1 ) { - safe.uboLog(logPrefix, `Allowed (${args.join(', ')})`); - } - return Reflect.apply(target, thisArg, args); - } - safe.uboLog(logPrefix, `Prevented (${args.join(', ')})`); - if ( autoRemoveAfter < 0 ) { return null; } - const decoyElem = decoy === 'obj' - ? createDecoy('object', 'data', ...args) - : createDecoy('iframe', 'src', ...args); - let popup = decoyElem.contentWindow; - if ( typeof popup === 'object' && popup !== null ) { - Object.defineProperty(popup, 'closed', { value: false }); - } else { - const noopFunc = (function(){}).bind(self); - popup = new Proxy(self, { - get: function(target, prop) { - if ( prop === 'closed' ) { return false; } - const r = Reflect.get(...arguments); - if ( typeof r === 'function' ) { return noopFunc; } - return target[prop]; - }, - set: function() { - return Reflect.set(...arguments); - }, - }); - } - if ( safe.logLevel !== 0 ) { - popup = new Proxy(popup, { - get: function(target, prop) { - safe.uboLog(logPrefix, 'window.open / get', prop, '===', target[prop]); - return Reflect.get(...arguments); - }, - set: function(target, prop, value) { - safe.uboLog(logPrefix, 'window.open / set', prop, '=', value); - return Reflect.set(...arguments); - }, - }); + proxyApplyFn('open', function open(target, thisArg, args) { + const haystack = args.join(' '); + if ( rePattern.test(haystack) !== targetMatchResult ) { + if ( safe.logLevel > 1 ) { + safe.uboLog(logPrefix, `Allowed (${args.join(', ')})`); } - return popup; + return Reflect.apply(target, thisArg, args); + } + safe.uboLog(logPrefix, `Prevented (${args.join(', ')})`); + if ( autoRemoveAfter < 0 ) { return null; } + const decoyElem = decoy === 'obj' + ? createDecoy('object', 'data', ...args) + : createDecoy('iframe', 'src', ...args); + let popup = decoyElem.contentWindow; + if ( typeof popup === 'object' && popup !== null ) { + Object.defineProperty(popup, 'closed', { value: false }); + } else { + const noopFunc = function open(){}; + popup = new Proxy(self, { + get: function(target, prop) { + if ( prop === 'closed' ) { return false; } + const r = Reflect.get(...arguments); + if ( typeof r === 'function' ) { return noopFunc; } + return target[prop]; + }, + set: function() { + return Reflect.set(...arguments); + }, + }); + } + if ( safe.logLevel !== 0 ) { + popup = new Proxy(popup, { + get: function(target, prop) { + safe.uboLog(logPrefix, 'window.open / get', prop, '===', target[prop]); + return Reflect.get(...arguments); + }, + set: function(target, prop, value) { + safe.uboLog(logPrefix, 'window.open / set', prop, '=', value); + return Reflect.set(...arguments); + }, + }); } + return popup; }); } From cf5e781c915f2b08a3bd04cbc82e9b97701f193e Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Fri, 16 Aug 2024 11:55:53 -0400 Subject: [PATCH 0126/1099] Update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7317f4d7f5408..f8d78c9ab18dc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,4 @@ +- [Improve various scriptlets](https://github.com/gorhill/uBlock/commit/56dfdd2568) - [Improve `href-sanitizer` sciptlet](https://github.com/gorhill/uBlock/commit/db3dc69bcc) - [Improve `remove-attr.js` scriptlet](https://github.com/gorhill/uBlock/commit/fb037e97d0) - [Improve `trusted-replace-node-text` scriptlet](https://github.com/gorhill/uBlock/commit/4f0d1301ab) From 4520a8e611cf151b10f789994dd2ae4226fe1a20 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Fri, 16 Aug 2024 11:56:20 -0400 Subject: [PATCH 0127/1099] New revision for dev build --- dist/version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dist/version b/dist/version index 8eaa884f833ba..e95c3036d1206 100644 --- a/dist/version +++ b/dist/version @@ -1 +1 @@ -1.59.1.2 \ No newline at end of file +1.59.1.3 \ No newline at end of file From 8de454ccca4e64c1e5486362a56ec0f745faf560 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Fri, 16 Aug 2024 12:11:18 -0400 Subject: [PATCH 0128/1099] Make Firefox dev build auto-update --- dist/firefox/updates.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dist/firefox/updates.json b/dist/firefox/updates.json index 8553569b3ceb7..b4f9d0ec4de8a 100644 --- a/dist/firefox/updates.json +++ b/dist/firefox/updates.json @@ -3,9 +3,9 @@ "uBlock0@raymondhill.net": { "updates": [ { - "version": "1.59.1.2", + "version": "1.59.1.3", "browser_specific_settings": { "gecko": { "strict_min_version": "78.0" } }, - "update_link": "https://github.com/gorhill/uBlock/releases/download/1.59.1b2/uBlock0_1.59.1b2.firefox.signed.xpi" + "update_link": "https://github.com/gorhill/uBlock/releases/download/1.59.1b3/uBlock0_1.59.1b3.firefox.signed.xpi" } ] } From 1cb660b94e0adb6d4e15e91e3e4011279393efd9 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sat, 17 Aug 2024 10:11:18 -0400 Subject: [PATCH 0129/1099] Fix plain exceptions not overriding block filters using `header=` option Related issue: https://github.com/uBlockOrigin/uBlock-issues/issues/3347 --- src/js/static-net-filtering.js | 137 +++++++++++++++------------------ 1 file changed, 64 insertions(+), 73 deletions(-) diff --git a/src/js/static-net-filtering.js b/src/js/static-net-filtering.js index 9a252fdce33cd..96aaff79ef7a7 100644 --- a/src/js/static-net-filtering.js +++ b/src/js/static-net-filtering.js @@ -19,28 +19,15 @@ Home: https://github.com/gorhill/uBlock */ -/* globals vAPI */ - -'use strict'; +import * as sfp from './static-filtering-parser.js'; -/******************************************************************************/ +import { domainFromHostname, hostnameFromNetworkURL } from './uri-utils.js'; +import { dropTask, queueTask } from './tasks.js'; -import { queueTask, dropTask } from './tasks.js'; import BidiTrieContainer from './biditrie.js'; -import HNTrieContainer from './hntrie.js'; import { CompiledListReader } from './static-filtering-io.js'; -import * as sfp from './static-filtering-parser.js'; - -import { - domainFromHostname, - hostnameFromNetworkURL, -} from './uri-utils.js'; - -// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import#browser_compatibility -// -// This import would be best done dynamically, but since dynamic imports are -// not supported by older browsers, for now a static import is necessary. import { FilteringContext } from './filtering-context.js'; +import HNTrieContainer from './hntrie.js'; /******************************************************************************/ @@ -3423,63 +3410,63 @@ class FilterCompiler { processOptionWithValue(parser, id) { switch ( id ) { - case sfp.NODE_TYPE_NET_OPTION_NAME_CSP: - if ( this.processCspOption(parser.getNetOptionValue(id)) === false ) { return false; } - break; - case sfp.NODE_TYPE_NET_OPTION_NAME_DENYALLOW: - this.denyallowOpt = this.processHostnameList( - parser.getNetFilterDenyallowOptionIterator(), - ); - if ( this.denyallowOpt === '' ) { return false; } - this.optionUnitBits |= DENYALLOW_BIT; - break; - case sfp.NODE_TYPE_NET_OPTION_NAME_FROM: - this.fromDomainOpt = this.processHostnameList( - parser.getNetFilterFromOptionIterator(), - this.fromDomainOptList - ); - if ( this.fromDomainOpt === '' ) { return false; } - this.optionUnitBits |= FROM_BIT; - break; - case sfp.NODE_TYPE_NET_OPTION_NAME_HEADER: { - this.headerOpt = parser.getNetOptionValue(id) || ''; - this.optionUnitBits |= HEADER_BIT; - break; + case sfp.NODE_TYPE_NET_OPTION_NAME_CSP: + if ( this.processCspOption(parser.getNetOptionValue(id)) === false ) { return false; } + break; + case sfp.NODE_TYPE_NET_OPTION_NAME_DENYALLOW: + this.denyallowOpt = this.processHostnameList( + parser.getNetFilterDenyallowOptionIterator(), + ); + if ( this.denyallowOpt === '' ) { return false; } + this.optionUnitBits |= DENYALLOW_BIT; + break; + case sfp.NODE_TYPE_NET_OPTION_NAME_FROM: + this.fromDomainOpt = this.processHostnameList( + parser.getNetFilterFromOptionIterator(), + this.fromDomainOptList + ); + if ( this.fromDomainOpt === '' ) { return false; } + this.optionUnitBits |= FROM_BIT; + break; + case sfp.NODE_TYPE_NET_OPTION_NAME_HEADER: { + this.headerOpt = parser.getNetOptionValue(id) || ''; + this.optionUnitBits |= HEADER_BIT; + break; + } + case sfp.NODE_TYPE_NET_OPTION_NAME_METHOD: + this.processMethodOption(parser.getNetOptionValue(id)); + this.optionUnitBits |= METHOD_BIT; + break; + case sfp.NODE_TYPE_NET_OPTION_NAME_PERMISSIONS: + case sfp.NODE_TYPE_NET_OPTION_NAME_REDIRECTRULE: + case sfp.NODE_TYPE_NET_OPTION_NAME_REMOVEPARAM: + case sfp.NODE_TYPE_NET_OPTION_NAME_REPLACE: + case sfp.NODE_TYPE_NET_OPTION_NAME_URLTRANSFORM: + if ( this.processModifierOption(id, parser.getNetOptionValue(id)) === false ) { + return false; } - case sfp.NODE_TYPE_NET_OPTION_NAME_METHOD: - this.processMethodOption(parser.getNetOptionValue(id)); - this.optionUnitBits |= METHOD_BIT; - break; - case sfp.NODE_TYPE_NET_OPTION_NAME_PERMISSIONS: - case sfp.NODE_TYPE_NET_OPTION_NAME_REDIRECTRULE: - case sfp.NODE_TYPE_NET_OPTION_NAME_REMOVEPARAM: - case sfp.NODE_TYPE_NET_OPTION_NAME_REPLACE: - case sfp.NODE_TYPE_NET_OPTION_NAME_URLTRANSFORM: - if ( this.processModifierOption(id, parser.getNetOptionValue(id)) === false ) { - return false; - } - this.optionUnitBits |= MODIFY_BIT; - break; - case sfp.NODE_TYPE_NET_OPTION_NAME_REDIRECT: { - const actualId = this.action === ALLOW_REALM - ? sfp.NODE_TYPE_NET_OPTION_NAME_REDIRECTRULE - : id; - if ( this.processModifierOption(actualId, parser.getNetOptionValue(id)) === false ) { - return false; - } - this.optionUnitBits |= MODIFY_BIT; - break; + this.optionUnitBits |= MODIFY_BIT; + break; + case sfp.NODE_TYPE_NET_OPTION_NAME_REDIRECT: { + const actualId = this.action === ALLOW_REALM + ? sfp.NODE_TYPE_NET_OPTION_NAME_REDIRECTRULE + : id; + if ( this.processModifierOption(actualId, parser.getNetOptionValue(id)) === false ) { + return false; } - case sfp.NODE_TYPE_NET_OPTION_NAME_TO: - this.toDomainOpt = this.processHostnameList( - parser.getNetFilterToOptionIterator(), - this.toDomainOptList - ); - if ( this.toDomainOpt === '' ) { return false; } - this.optionUnitBits |= TO_BIT; - break; - default: - break; + this.optionUnitBits |= MODIFY_BIT; + break; + } + case sfp.NODE_TYPE_NET_OPTION_NAME_TO: + this.toDomainOpt = this.processHostnameList( + parser.getNetFilterToOptionIterator(), + this.toDomainOptList + ); + if ( this.toDomainOpt === '' ) { return false; } + this.optionUnitBits |= TO_BIT; + break; + default: + break; } return true; } @@ -3798,7 +3785,7 @@ class FilterCompiler { isJustOrigin() { if ( this.optionUnitBits !== FROM_BIT ) { return false; } if ( this.isRegex ) { return false; } - if ( /[\/~]/.test(this.fromDomainOpt) ) { return false; } + if ( /[/~]/.test(this.fromDomainOpt) ) { return false; } if ( this.pattern === '*' ) { return true; } if ( this.anchor !== 0b010 ) { return false; } if ( /^(?:http[s*]?:(?:\/\/)?)$/.test(this.pattern) ) { return true; } @@ -5156,6 +5143,10 @@ StaticNetFilteringEngine.prototype.matchHeaders = function(fctxt, headers) { if ( r !== 0 && $isBlockImportant !== true ) { if ( this.realmMatchString(HEADERS_REALM | ALLOW_REALM, typeBits, partyBits) ) { r = 2; + } else if ( this.realmMatchString(ALLOW_REALM, typeBits, partyBits) ) { + r = 2; + } + if ( r === 2 ) { if ( this.realmMatchString(HEADERS_REALM | BLOCKIMPORTANT_REALM, typeBits, partyBits) ) { r = 1; } From e73eb23c9047ec79945a27b7ac3810097485d525 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sat, 17 Aug 2024 10:42:17 -0400 Subject: [PATCH 0130/1099] [mv3] Don't filter out tabless requests in "logger" --- platform/mv3/extension/js/debug.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/platform/mv3/extension/js/debug.js b/platform/mv3/extension/js/debug.js index 0674cd9446b88..9488260d856c2 100644 --- a/platform/mv3/extension/js/debug.js +++ b/platform/mv3/extension/js/debug.js @@ -112,7 +112,9 @@ export const getMatchedRules = (( ) => { const j = (writePtr + i) % bufferSize; const ruleInfo = matchedRules[j]; if ( ruleInfo === null ) { continue; } - if ( ruleInfo.request.tabId !== tabId ) { continue; } + if ( ruleInfo.request.tabId !== -1 ) { + if ( ruleInfo.request.tabId !== tabId ) { continue; } + } const promise = getRuleDetails(ruleInfo); if ( promise === undefined ) { continue; } promises.unshift(promise); From 6891037758354c809953a549017f654437ed38c7 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sat, 17 Aug 2024 11:00:42 -0400 Subject: [PATCH 0131/1099] [mv3] Fix exception filters not overriding redirect filters Related issue: https://github.com/uBlockOrigin/uBOL-home/issues/185 --- src/js/static-net-filtering.js | 37 +++++++++++++++++++++------------- 1 file changed, 23 insertions(+), 14 deletions(-) diff --git a/src/js/static-net-filtering.js b/src/js/static-net-filtering.js index 96aaff79ef7a7..84d04478d3dd9 100644 --- a/src/js/static-net-filtering.js +++ b/src/js/static-net-filtering.js @@ -752,7 +752,7 @@ class FilterImportant { } static dnrFromCompiled(args, rule) { - rule.priority = (rule.priority || 1) + 10; + rule.priority = (rule.priority || 1) + 30; } static keyFromArgs() { @@ -4312,14 +4312,22 @@ StaticNetFilteringEngine.prototype.dnrFromCompiled = function(op, context, ...ar } } + // Priority: + // Block: 0 (default priority) + // Redirect: 1-9 + // Excepted redirect: 11-19 + // Allow: 20 + // Block important: 30 + // Redirect important: 31-39 + const realms = new Map([ - [ BLOCK_REALM, 'block' ], - [ ALLOW_REALM, 'allow' ], - [ REDIRECT_REALM, 'redirect' ], - [ REMOVEPARAM_REALM, 'removeparam' ], - [ CSP_REALM, 'csp' ], - [ PERMISSIONS_REALM, 'permissions' ], - [ URLTRANSFORM_REALM, 'uritransform' ], + [ BLOCK_REALM, { type: 'block', priority: 0 } ], + [ ALLOW_REALM, { type: 'allow', priority: 20 } ], + [ REDIRECT_REALM, { type: 'redirect', priority: 1 } ], + [ REMOVEPARAM_REALM, { type: 'removeparam', priority: 0 } ], + [ CSP_REALM, { type: 'csp', priority: 0 } ], + [ PERMISSIONS_REALM, { type: 'permissions', priority: 0 } ], + [ URLTRANSFORM_REALM, { type: 'uritransform', priority: 0 } ], ]); const partyness = new Map([ [ ANYPARTY_REALM, '' ], @@ -4342,7 +4350,7 @@ StaticNetFilteringEngine.prototype.dnrFromCompiled = function(op, context, ...ar 'other', ]); const ruleset = []; - for ( const [ realmBits, realmName ] of realms ) { + for ( const [ realmBits, realmDetails ] of realms ) { for ( const [ partyBits, partyName ] of partyness ) { for ( const typeName in typeNameToTypeValue ) { if ( types.has(typeName) === false ) { continue; } @@ -4353,7 +4361,10 @@ StaticNetFilteringEngine.prototype.dnrFromCompiled = function(op, context, ...ar for ( const rules of bucket.values() ) { for ( const rule of rules ) { rule.action = rule.action || {}; - rule.action.type = realmName; + rule.action.type = realmDetails.type; + if ( realmDetails.priority !== 0 ) { + rule.priority = (rule.priority || 0) + realmDetails.priority; + } if ( partyName !== '' ) { rule.condition = rule.condition || {}; rule.condition.domainType = partyName; @@ -4449,12 +4460,11 @@ StaticNetFilteringEngine.prototype.dnrFromCompiled = function(op, context, ...ar } break; case 'redirect-rule': { - let priority = rule.priority || 1; let token = rule.__modifierValue; if ( token !== '' ) { const match = /:(\d+)$/.exec(token); if ( match !== null ) { - priority += parseInt(match[1], 10); + rule.priority = Math.min(rule.priority + parseInt(match[1], 10), 9); token = token.slice(0, match.index); } } @@ -4466,10 +4476,9 @@ StaticNetFilteringEngine.prototype.dnrFromCompiled = function(op, context, ...ar const extensionPath = resource || token; rule.action.type = 'redirect'; rule.action.redirect = { extensionPath }; - rule.priority = priority + 1; } else { rule.action.type = 'block'; - rule.priority = priority + 2; + rule.priority += 10; } break; } From cb452bc21c79a896fd073afbb83cbcfce867646f Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sat, 17 Aug 2024 12:32:31 -0400 Subject: [PATCH 0132/1099] Update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index f8d78c9ab18dc..2f56598079b86 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,4 @@ +- [Fix plain exceptions not overriding block filters using `header=` option](https://github.com/gorhill/uBlock/commit/1cb660b94e) - [Improve various scriptlets](https://github.com/gorhill/uBlock/commit/56dfdd2568) - [Improve `href-sanitizer` sciptlet](https://github.com/gorhill/uBlock/commit/db3dc69bcc) - [Improve `remove-attr.js` scriptlet](https://github.com/gorhill/uBlock/commit/fb037e97d0) From a6cc75109d108106f27b3637af475fc304f86f70 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sat, 17 Aug 2024 12:32:51 -0400 Subject: [PATCH 0133/1099] New revision for dev build --- dist/version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dist/version b/dist/version index e95c3036d1206..a11a4871783ba 100644 --- a/dist/version +++ b/dist/version @@ -1 +1 @@ -1.59.1.3 \ No newline at end of file +1.59.1.4 \ No newline at end of file From 2c60b331e39e96114386e568d028240b37cdeefc Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sat, 17 Aug 2024 12:50:45 -0400 Subject: [PATCH 0134/1099] Make Firefox dev build auto-update --- dist/firefox/updates.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dist/firefox/updates.json b/dist/firefox/updates.json index b4f9d0ec4de8a..c193c382f807c 100644 --- a/dist/firefox/updates.json +++ b/dist/firefox/updates.json @@ -3,9 +3,9 @@ "uBlock0@raymondhill.net": { "updates": [ { - "version": "1.59.1.3", + "version": "1.59.1.4", "browser_specific_settings": { "gecko": { "strict_min_version": "78.0" } }, - "update_link": "https://github.com/gorhill/uBlock/releases/download/1.59.1b3/uBlock0_1.59.1b3.firefox.signed.xpi" + "update_link": "https://github.com/gorhill/uBlock/releases/download/1.59.1b4/uBlock0_1.59.1b4.firefox.signed.xpi" } ] } From 5287f6e0296f6561307d623e9b0cf2f7f7dccc49 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sun, 18 Aug 2024 09:12:54 -0400 Subject: [PATCH 0135/1099] Code review of scriptlet helper --- assets/resources/scriptlets.js | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/assets/resources/scriptlets.js b/assets/resources/scriptlets.js index 6b08f72373c85..96042aba4464c 100644 --- a/assets/resources/scriptlets.js +++ b/assets/resources/scriptlets.js @@ -1462,10 +1462,8 @@ function proxyApplyFn( } const fn = context[prop]; if ( typeof fn !== 'function' ) { return; } - const fnname = fn.name; - const toString = (function toString() { - return `function ${fnname}() { [native code] }`; - }).bind(null); + const fnStr = fn.toString(); + const toString = (function toString() { return fnStr; }).bind(null); if ( fn.prototype && fn.prototype.constructor === fn ) { context[prop] = new Proxy(fn, { construct: handler, From 41c2258f9133cdea511a5bcd8d592682f7b17cca Mon Sep 17 00:00:00 2001 From: Fanboynz Date: Mon, 19 Aug 2024 01:14:44 +1200 Subject: [PATCH 0136/1099] Add allowed/denied to set-local-storage (#3922) --- assets/resources/scriptlets.js | 1 + 1 file changed, 1 insertion(+) diff --git a/assets/resources/scriptlets.js b/assets/resources/scriptlets.js index 96042aba4464c..18ff58d60ae28 100644 --- a/assets/resources/scriptlets.js +++ b/assets/resources/scriptlets.js @@ -1102,6 +1102,7 @@ function setLocalStorageItemFn( 'yes', 'no', 'accept', 'reject', 'accepted', 'rejected', + 'allowed', 'denied', '{}', '[]', '""', '$remove$', ]; From 34047daa06e4255b7347f312770b9634d481e09e Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Mon, 19 Aug 2024 12:02:00 -0400 Subject: [PATCH 0137/1099] Import translation work from https://crowdin.com/project/ublock --- platform/mv3/description/webstore.es.txt | 2 +- platform/mv3/extension/_locales/ar/messages.json | 2 +- platform/mv3/extension/_locales/az/messages.json | 2 +- platform/mv3/extension/_locales/es/messages.json | 4 ++-- platform/mv3/extension/_locales/fa/messages.json | 2 +- platform/mv3/extension/_locales/hu/messages.json | 2 +- platform/mv3/extension/_locales/hy/messages.json | 2 +- platform/mv3/extension/_locales/it/messages.json | 4 ++-- platform/mv3/extension/_locales/pt_BR/messages.json | 2 +- platform/mv3/extension/_locales/pt_PT/messages.json | 2 +- platform/mv3/extension/_locales/si/messages.json | 4 ++-- platform/mv3/extension/_locales/sv/messages.json | 2 +- platform/mv3/extension/_locales/tr/messages.json | 2 +- src/_locales/br_FR/messages.json | 10 +++++----- 14 files changed, 21 insertions(+), 21 deletions(-) diff --git a/platform/mv3/description/webstore.es.txt b/platform/mv3/description/webstore.es.txt index b89c567215bb5..e0cbfb8bd4204 100644 --- a/platform/mv3/description/webstore.es.txt +++ b/platform/mv3/description/webstore.es.txt @@ -7,7 +7,7 @@ Por defecto ya trae configuradas las siguientes listas de filtros: - EasyPrivacy - Peter Lowe’s Ad and tracking server list -Puedes activar más ruletas visitando la página de opciones --pulsa el icono _Cogs_ en la ventana del panel +Puedes habilitar más conjuntos de reglas visitando la página de opciones, haz clic en el icono de _engranaje_ del panel emergente. uBOL es completamente declarativo, lo que significa que no hay necesidad de un proceso uBOL permanente para que se produzca el filtrado, y el filtrado de contenido basado en la inyección de CSS/JS se realiza de forma confiable por el propio navegador en lugar de la extensión. Esto significa que uBOL en sí mismo no consume recursos de CPU/memoria mientras el bloqueo de contenido está en curso, el proceso service worker de uBOL se requiere _solo_ cuando se interactúa con el panel emergente o las páginas de opciones. diff --git a/platform/mv3/extension/_locales/ar/messages.json b/platform/mv3/extension/_locales/ar/messages.json index 7aa0a40b9cce3..a25dfab4de557 100644 --- a/platform/mv3/extension/_locales/ar/messages.json +++ b/platform/mv3/extension/_locales/ar/messages.json @@ -148,7 +148,7 @@ "description": "A short description for the editable field which lists trusted sites" }, "noFilteringModePlaceholder": { - "message": "[hostnames only]\nexample.com\ngames.example\n...", + "message": "[أسماء النطاقات الرئيسية فقط]\nexample.com\ngames.example", "description": "Default text for in edit field" }, "behaviorSectionLabel": { diff --git a/platform/mv3/extension/_locales/az/messages.json b/platform/mv3/extension/_locales/az/messages.json index 5f8c5ae1aaf27..3f397e146f9ea 100644 --- a/platform/mv3/extension/_locales/az/messages.json +++ b/platform/mv3/extension/_locales/az/messages.json @@ -4,7 +4,7 @@ "description": "extension name." }, "extShortDesc": { - "message": "İcazəyə ehtiyac duymayan məzmun əngəlləyicisi. Reklamları, izləyiciləri, maynerləri və daha çoxunu quraşdırmadan dərhal sonra əngəlləyir.", + "message": "İcazəyə ehtiyac duymayan məzmun əngəlləyicisi. Reklamları, izləyiciləri, və daha çoxunu quraşdırmadan dərhal sonra əngəlləyir.", "description": "this will be in the Chrome web store: must be 132 characters or less" }, "perRulesetStats": { diff --git a/platform/mv3/extension/_locales/es/messages.json b/platform/mv3/extension/_locales/es/messages.json index 2d3cd2fd6c68d..f4ff120d131d7 100644 --- a/platform/mv3/extension/_locales/es/messages.json +++ b/platform/mv3/extension/_locales/es/messages.json @@ -144,11 +144,11 @@ "description": "This describes the 'complete' filtering mode" }, "noFilteringModeDescription": { - "message": "Lista de nombres de dominio para los cuales no se realizará ningún filtrado.", + "message": "Lista de sitios web para los cuales no se realizará ningún filtrado.", "description": "A short description for the editable field which lists trusted sites" }, "noFilteringModePlaceholder": { - "message": "[solo nombres de hospedador]\nejemplo.com\njuegos.ejemplo\n...", + "message": "[solo nombres de dominio]\nejemplo.com\njuegos.ejemplo\n...", "description": "Default text for in edit field" }, "behaviorSectionLabel": { diff --git a/platform/mv3/extension/_locales/fa/messages.json b/platform/mv3/extension/_locales/fa/messages.json index 169540c053e57..ba2d878aa6078 100644 --- a/platform/mv3/extension/_locales/fa/messages.json +++ b/platform/mv3/extension/_locales/fa/messages.json @@ -4,7 +4,7 @@ "description": "extension name." }, "extShortDesc": { - "message": "مسدود کننده محتوا بدون نیاز به مجوز که بلافاصله پس از نصب، تبلیغات، ردیاب ها، ابزار‌های استخراج ارز دیجیتال و موارد دیگر را مسدود می کند.", + "message": "یک مسدود کننده محتوای بدون مجوز که بلافاصله پس از نصب، تبلیغات، ردیاب ها، ابزارهای استخراج و موارد دیگر را مسدود می کند.", "description": "this will be in the Chrome web store: must be 132 characters or less" }, "perRulesetStats": { diff --git a/platform/mv3/extension/_locales/hu/messages.json b/platform/mv3/extension/_locales/hu/messages.json index a27467b005740..bb044590e905f 100644 --- a/platform/mv3/extension/_locales/hu/messages.json +++ b/platform/mv3/extension/_locales/hu/messages.json @@ -4,7 +4,7 @@ "description": "extension name." }, "extShortDesc": { - "message": "Kísérleti, engedélyeket nem igénylő nélküli tartalomblokkoló. A telepítés után azonnal blokkolja a hirdetéseket, nyomkövetőket, bányászprogramokat és egyebeket.", + "message": "Engedélyt nem igénylő tartalomblokkoló. A telepítés után azonnal blokkolja a hirdetéseket, nyomkövetőket, bányászokat és egyebeket.", "description": "this will be in the Chrome web store: must be 132 characters or less" }, "perRulesetStats": { diff --git a/platform/mv3/extension/_locales/hy/messages.json b/platform/mv3/extension/_locales/hy/messages.json index 36cd1cc8e60ac..5a50db68ace07 100644 --- a/platform/mv3/extension/_locales/hy/messages.json +++ b/platform/mv3/extension/_locales/hy/messages.json @@ -4,7 +4,7 @@ "description": "extension name." }, "extShortDesc": { - "message": "Փորձարարական բովանդակության արգելափակիչ, որը չի պահանջում թույլտվություններ։ Արգելափակում է ազդերը, հետագծիչները, մայներները և շատ ավելին։", + "message": "Փբովանդակության արգելափակիչ, որը չի պահանջում թույլտվություններ։ Արգելափակում է ազդերը, հետագծիչները, մայներները և շատ ավելին։", "description": "this will be in the Chrome web store: must be 132 characters or less" }, "perRulesetStats": { diff --git a/platform/mv3/extension/_locales/it/messages.json b/platform/mv3/extension/_locales/it/messages.json index e504d5b206d85..dc7fbd930783d 100644 --- a/platform/mv3/extension/_locales/it/messages.json +++ b/platform/mv3/extension/_locales/it/messages.json @@ -16,7 +16,7 @@ "description": "English: uBO Lite — Dashboard" }, "settingsPageName": { - "message": "Opzioni", + "message": "Impostazioni", "description": "appears as tab name in dashboard" }, "aboutPageName": { @@ -148,7 +148,7 @@ "description": "A short description for the editable field which lists trusted sites" }, "noFilteringModePlaceholder": { - "message": "[hostnames only]\nexample.com\ngames.example\n...", + "message": "[solo nomi di host]\nesempio.com\ngiochi.esempio\n...", "description": "Default text for in edit field" }, "behaviorSectionLabel": { diff --git a/platform/mv3/extension/_locales/pt_BR/messages.json b/platform/mv3/extension/_locales/pt_BR/messages.json index 671006eacf313..e6533c12712ff 100644 --- a/platform/mv3/extension/_locales/pt_BR/messages.json +++ b/platform/mv3/extension/_locales/pt_BR/messages.json @@ -4,7 +4,7 @@ "description": "extension name." }, "extShortDesc": { - "message": "Um bloqueador de conteúdo com menos permissões - Bloqueie anúncios, rastreadores, mineradores e mais imediatamente após a instalação.", + "message": "Um bloqueador de conteúdo com menos permissões - Bloqueie anúncios, rastreadores, mineradores e mais imediatamente após a instalação", "description": "this will be in the Chrome web store: must be 132 characters or less" }, "perRulesetStats": { diff --git a/platform/mv3/extension/_locales/pt_PT/messages.json b/platform/mv3/extension/_locales/pt_PT/messages.json index 2f637e25713e8..52917d37ecb71 100644 --- a/platform/mv3/extension/_locales/pt_PT/messages.json +++ b/platform/mv3/extension/_locales/pt_PT/messages.json @@ -4,7 +4,7 @@ "description": "extension name." }, "extShortDesc": { - "message": "Um bloqueador de conteúdo sem permissões. Bloqueia anúncios, rastreadores, mineradores de criptomoedas e muito mais, imediatamente após a instalação.", + "message": "Um bloqueador de conteúdo sem permissões. Bloqueia anúncios, rastreadores e muito mais, imediatamente após a instalação.", "description": "this will be in the Chrome web store: must be 132 characters or less" }, "perRulesetStats": { diff --git a/platform/mv3/extension/_locales/si/messages.json b/platform/mv3/extension/_locales/si/messages.json index 30c719d52722d..bc0e73fc0ddc2 100644 --- a/platform/mv3/extension/_locales/si/messages.json +++ b/platform/mv3/extension/_locales/si/messages.json @@ -16,11 +16,11 @@ "description": "English: uBO Lite — Dashboard" }, "settingsPageName": { - "message": "Settings", + "message": "සැකසුම්", "description": "appears as tab name in dashboard" }, "aboutPageName": { - "message": "About", + "message": "පිළිබඳව", "description": "appears as tab name in dashboard" }, "aboutPrivacyPolicy": { diff --git a/platform/mv3/extension/_locales/sv/messages.json b/platform/mv3/extension/_locales/sv/messages.json index 4fb9b47a26a57..c95ce8d24db00 100644 --- a/platform/mv3/extension/_locales/sv/messages.json +++ b/platform/mv3/extension/_locales/sv/messages.json @@ -4,7 +4,7 @@ "description": "extension name." }, "extShortDesc": { - "message": "En behörighetslös innehållsblockerare. Blockerar annonser, spårare, miners och mer omedelbart efter installationen.", + "message": "En behörighetslös innehållsblockerare. Blockerar annonser, spårare, miners och mer omedelbart efter installationen.", "description": "this will be in the Chrome web store: must be 132 characters or less" }, "perRulesetStats": { diff --git a/platform/mv3/extension/_locales/tr/messages.json b/platform/mv3/extension/_locales/tr/messages.json index 5cf18a7020d51..06e259997e21f 100644 --- a/platform/mv3/extension/_locales/tr/messages.json +++ b/platform/mv3/extension/_locales/tr/messages.json @@ -4,7 +4,7 @@ "description": "extension name." }, "extShortDesc": { - "message": "İzin gerektirmeyen içerik engelleyicisi. Kurulumdan hemen sonra reklamları, izleyicileri, kripto madencilerini, ve daha fazlasını engeller.", + "message": "İzin gerektirmeyen içerik engelleyicisi. Kurulumdan hemen sonra reklamları, izleyicileri, ve daha fazlasını engeller.", "description": "this will be in the Chrome web store: must be 132 characters or less" }, "perRulesetStats": { diff --git a/src/_locales/br_FR/messages.json b/src/_locales/br_FR/messages.json index 2722eb8de84fc..e0005b74baf74 100644 --- a/src/_locales/br_FR/messages.json +++ b/src/_locales/br_FR/messages.json @@ -156,15 +156,15 @@ "description": "Tooltip for the no-large-media per-site switch" }, "popupTipNoCosmeticFiltering": { - "message": "Lazhañ/enaouiñ ar silañ kenedel war al lec'hienn-mañ", + "message": "Lazhañ/enaouiñ ar siloù kenedel war al lec'hienn-mañ", "description": "Tooltip for the no-cosmetic-filtering per-site switch" }, "popupTipNoCosmeticFiltering1": { - "message": "Lazhañ ar silañ kenedel war al lec'hienn-mañ", + "message": "Lazhañ ar siloù kenedel war al lec'hienn-mañ", "description": "Tooltip for the no-cosmetic-filtering per-site switch" }, "popupTipNoCosmeticFiltering2": { - "message": "Enaouiñ ar silañ kenedel war al lec'hienn-mañ", + "message": "Enaouiñ ar siloù kenedel war al lec'hienn-mañ", "description": "Tooltip for the no-cosmetic-filtering per-site switch" }, "popupTipNoRemoteFonts": { @@ -376,7 +376,7 @@ "description": "" }, "settingsNoCosmeticFilteringPrompt": { - "message": "Lazhañ ar silañ kenedel", + "message": "Lazhañ ar siloù kenedel", "description": "" }, "settingsNoLargeMediaPrompt": { @@ -1260,7 +1260,7 @@ "description": "Label for buttons used to select all text in editor" }, "toggleCosmeticFiltering": { - "message": "Enaouiñ ar silañ kenedel", + "message": "Enaouiñ ar siloù kenedel", "description": "Label for keyboard shortcut used to toggle cosmetic filtering" }, "toggleJavascript": { From 3e2171f550e0582dfa0419d59b563a4f22f952f6 Mon Sep 17 00:00:00 2001 From: Fanboynz Date: Tue, 20 Aug 2024 06:40:33 +1200 Subject: [PATCH 0138/1099] Add checked/unchecked to set-cookie (#3923) --- assets/resources/scriptlets.js | 1 + 1 file changed, 1 insertion(+) diff --git a/assets/resources/scriptlets.js b/assets/resources/scriptlets.js index 18ff58d60ae28..f59eb8ed387bc 100644 --- a/assets/resources/scriptlets.js +++ b/assets/resources/scriptlets.js @@ -3848,6 +3848,7 @@ function setCookie( 'hide', 'hidden', 'essential', 'nonessential', 'dismiss', 'dismissed', + 'checked', 'unchecked', ]; const normalized = value.toLowerCase(); const match = /^("?)(.+)\1$/.exec(normalized); From 79e10323adf3816410915a2583bf3a712a966720 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Mon, 19 Aug 2024 14:56:15 -0400 Subject: [PATCH 0139/1099] Use helper function to lookup safe cookie values This helper function is now used by `set-cookie` and `set-local-storage-item` scriptlets, so changes in the helper function will benefit both scriptlets. --- assets/resources/scriptlets.js | 56 +++++++++++++++++++--------------- 1 file changed, 31 insertions(+), 25 deletions(-) diff --git a/assets/resources/scriptlets.js b/assets/resources/scriptlets.js index f59eb8ed387bc..8acbb98940d84 100644 --- a/assets/resources/scriptlets.js +++ b/assets/resources/scriptlets.js @@ -954,6 +954,33 @@ function objectFindOwnerFn( /******************************************************************************/ +builtinScriptlets.push({ + name: 'get-safe-cookie-values.fn', + fn: getSafeCookieValuesFn, +}); +function getSafeCookieValuesFn() { + return [ + 'accept', 'reject', + 'accepted', 'rejected', 'notaccepted', + 'allow', 'disallow', 'deny', + 'allowed', 'denied', + 'approved', 'disapproved', + 'checked', 'unchecked', + 'dismiss', 'dismissed', + 'enable', 'disable', + 'enabled', 'disabled', + 'essential', 'nonessential', + 'hide', 'hidden', + 'necessary', 'required', + 'ok', + 'on', 'off', + 'true', 't', 'false', 'f', + 'yes', 'y', 'no', 'n', + ]; +} + +/******************************************************************************/ + builtinScriptlets.push({ name: 'get-all-cookies.fn', fn: getAllCookiesFn, @@ -1076,6 +1103,7 @@ builtinScriptlets.push({ name: 'set-local-storage-item.fn', fn: setLocalStorageItemFn, dependencies: [ + 'get-safe-cookie-values.fn', 'safe-self.fn', ], }); @@ -1097,14 +1125,9 @@ function setLocalStorageItemFn( const trustedValues = [ '', 'undefined', 'null', - 'false', 'true', - 'on', 'off', - 'yes', 'no', - 'accept', 'reject', - 'accepted', 'rejected', - 'allowed', 'denied', '{}', '[]', '""', '$remove$', + ...getSafeCookieValuesFn(), ]; if ( trusted ) { @@ -3819,6 +3842,7 @@ builtinScriptlets.push({ fn: setCookie, world: 'ISOLATED', dependencies: [ + 'get-safe-cookie-values.fn', 'safe-self.fn', 'set-cookie.fn', ], @@ -3831,28 +3855,10 @@ function setCookie( if ( name === '' ) { return; } const safe = safeSelf(); const logPrefix = safe.makeLogPrefix('set-cookie', name, value, path); - - const validValues = [ - 'accept', 'reject', - 'accepted', 'rejected', 'notaccepted', - 'allow', 'deny', - 'allowed', 'disallow', - 'enable', 'disable', - 'enabled', 'disabled', - 'ok', - 'on', 'off', - 'true', 't', 'false', 'f', - 'yes', 'y', 'no', 'n', - 'necessary', 'required', - 'approved', 'disapproved', - 'hide', 'hidden', - 'essential', 'nonessential', - 'dismiss', 'dismissed', - 'checked', 'unchecked', - ]; const normalized = value.toLowerCase(); const match = /^("?)(.+)\1$/.exec(normalized); const unquoted = match && match[2] || normalized; + const validValues = getSafeCookieValuesFn(); if ( validValues.includes(unquoted) === false ) { if ( /^\d+$/.test(unquoted) === false ) { return; } const n = parseInt(value, 10); From 5f14716954b11eadcc1c0aa7829fc79261d8c162 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Mon, 19 Aug 2024 15:01:18 -0400 Subject: [PATCH 0140/1099] Update changelog --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2f56598079b86..9f8d6367e91d3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,6 @@ +- [Use helper function to lookup safe cookie values](https://github.com/gorhill/uBlock/commit/79e10323ad) +- [Add `checked`/`unchecked` to `set-cookie`](https://github.com/gorhill/uBlock/commit/3e2171f550) (by @ryanbr) +- [Add `allowed`/`denied` to `set-local-storage-item`](https://github.com/gorhill/uBlock/commit/41c2258f91) - [Fix plain exceptions not overriding block filters using `header=` option](https://github.com/gorhill/uBlock/commit/1cb660b94e) - [Improve various scriptlets](https://github.com/gorhill/uBlock/commit/56dfdd2568) - [Improve `href-sanitizer` sciptlet](https://github.com/gorhill/uBlock/commit/db3dc69bcc) From 40c3d4bccac4d149a8d1bc3dbf4f2d14d958e981 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Mon, 19 Aug 2024 15:01:37 -0400 Subject: [PATCH 0141/1099] New revision for dev build --- dist/version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dist/version b/dist/version index a11a4871783ba..59d28f274b6ef 100644 --- a/dist/version +++ b/dist/version @@ -1 +1 @@ -1.59.1.4 \ No newline at end of file +1.59.1.5 \ No newline at end of file From 378be82dec0660e15758b6588f75428d7ea6b07f Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Mon, 19 Aug 2024 15:15:45 -0400 Subject: [PATCH 0142/1099] Make Firefox dev build auto-update --- dist/firefox/updates.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dist/firefox/updates.json b/dist/firefox/updates.json index c193c382f807c..a008bf43f72bb 100644 --- a/dist/firefox/updates.json +++ b/dist/firefox/updates.json @@ -3,9 +3,9 @@ "uBlock0@raymondhill.net": { "updates": [ { - "version": "1.59.1.4", + "version": "1.59.1.5", "browser_specific_settings": { "gecko": { "strict_min_version": "78.0" } }, - "update_link": "https://github.com/gorhill/uBlock/releases/download/1.59.1b4/uBlock0_1.59.1b4.firefox.signed.xpi" + "update_link": "https://github.com/gorhill/uBlock/releases/download/1.59.1b5/uBlock0_1.59.1b5.firefox.signed.xpi" } ] } From 24756e83402234a8ed9c21dfba93eda98d8fd465 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Mon, 19 Aug 2024 17:56:54 -0400 Subject: [PATCH 0143/1099] Update changelog --- CHANGELOG.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9f8d6367e91d3..3508c264035a8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,9 +1,9 @@ - [Use helper function to lookup safe cookie values](https://github.com/gorhill/uBlock/commit/79e10323ad) - [Add `checked`/`unchecked` to `set-cookie`](https://github.com/gorhill/uBlock/commit/3e2171f550) (by @ryanbr) -- [Add `allowed`/`denied` to `set-local-storage-item`](https://github.com/gorhill/uBlock/commit/41c2258f91) +- [Add `allowed`/`denied` to `set-local-storage-item`](https://github.com/gorhill/uBlock/commit/41c2258f91) (by @ryanbr) - [Fix plain exceptions not overriding block filters using `header=` option](https://github.com/gorhill/uBlock/commit/1cb660b94e) - [Improve various scriptlets](https://github.com/gorhill/uBlock/commit/56dfdd2568) -- [Improve `href-sanitizer` sciptlet](https://github.com/gorhill/uBlock/commit/db3dc69bcc) +- [Improve `href-sanitizer` scriptlet](https://github.com/gorhill/uBlock/commit/db3dc69bcc) - [Improve `remove-attr.js` scriptlet](https://github.com/gorhill/uBlock/commit/fb037e97d0) - [Improve `trusted-replace-node-text` scriptlet](https://github.com/gorhill/uBlock/commit/4f0d1301ab) From 520f81fcca77523f7b8ce980a9f523ff4c7b7de0 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Tue, 20 Aug 2024 08:18:03 -0400 Subject: [PATCH 0144/1099] [mv3] Fix injection of scriptlets into embedded contexts --- platform/mv3/chromium/manifest.json | 2 +- .../mv3/extension/js/scripting-manager.js | 1 + platform/mv3/scriptlets/scriptlet.template.js | 20 ++++++++++++++----- 3 files changed, 17 insertions(+), 6 deletions(-) diff --git a/platform/mv3/chromium/manifest.json b/platform/mv3/chromium/manifest.json index b3e77ccc24ab5..49f1381b2c679 100644 --- a/platform/mv3/chromium/manifest.json +++ b/platform/mv3/chromium/manifest.json @@ -25,7 +25,7 @@ "128": "img/icon_128.png" }, "manifest_version": 3, - "minimum_chrome_version": "118.0", + "minimum_chrome_version": "119.0", "name": "__MSG_extName__", "options_page": "dashboard.html", "optional_host_permissions": [ diff --git a/platform/mv3/extension/js/scripting-manager.js b/platform/mv3/extension/js/scripting-manager.js index 6502cbc0aea1c..814343ebe7500 100644 --- a/platform/mv3/extension/js/scripting-manager.js +++ b/platform/mv3/extension/js/scripting-manager.js @@ -473,6 +473,7 @@ function registerScriptlet(context, scriptletDetails) { // `MAIN` world not yet supported in Firefox if ( isGecko === false ) { directive.world = 'MAIN'; + directive.matchOriginAsFallback = true; } // register diff --git a/platform/mv3/scriptlets/scriptlet.template.js b/platform/mv3/scriptlets/scriptlet.template.js index f5a47483b5ec8..9ce165d094d8e 100644 --- a/platform/mv3/scriptlets/scriptlet.template.js +++ b/platform/mv3/scriptlets/scriptlet.template.js @@ -20,11 +20,9 @@ */ -/* jshint esversion:11 */ +/* eslint-disable indent */ /* global cloneInto */ -'use strict'; - // ruleset: $rulesetId$ /******************************************************************************/ @@ -40,7 +38,7 @@ // Start of code to inject const uBOL_$scriptletName$ = function() { -const scriptletGlobals = {}; // jshint ignore: line +const scriptletGlobals = {}; // eslint-disable-line const argsList = self.$argsList$; @@ -57,7 +55,19 @@ function $scriptletName$(){} /******************************************************************************/ const hnParts = []; -try { hnParts.push(...document.location.hostname.split('.')); } +try { + let origin = document.location.origin; + if ( origin === 'null' ) { + const origins = document.location.ancestorOrigins; + for ( let i = 0; i < origins.length; i++ ) { + origin = origins[i]; + if ( origin !== 'null' ) { break; } + } + } + const pos = origin.lastIndexOf('://'); + if ( pos === -1 ) { return; } + hnParts.push(...origin.slice(pos+3).split('.')); +} catch(ex) { } const hnpartslen = hnParts.length; if ( hnpartslen === 0 ) { return; } From 09fef54959742d6d4da412745c4b1e395399557f Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Wed, 21 Aug 2024 10:56:18 -0400 Subject: [PATCH 0145/1099] [mv3] Ensure `redirect` has priority over`block` Related issue: https://github.com/uBlockOrigin/uBOL-home/issues/187#issuecomment-2301450418 --- src/js/static-net-filtering.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/js/static-net-filtering.js b/src/js/static-net-filtering.js index 84d04478d3dd9..d7edc01fb35d8 100644 --- a/src/js/static-net-filtering.js +++ b/src/js/static-net-filtering.js @@ -752,7 +752,7 @@ class FilterImportant { } static dnrFromCompiled(args, rule) { - rule.priority = (rule.priority || 1) + 30; + rule.priority = (rule.priority || 0) + 30; } static keyFromArgs() { @@ -4313,17 +4313,17 @@ StaticNetFilteringEngine.prototype.dnrFromCompiled = function(op, context, ...ar } // Priority: - // Block: 0 (default priority) - // Redirect: 1-9 - // Excepted redirect: 11-19 + // Block: 1 (default priority) + // Redirect: 2-9 + // Excepted redirect: 12-19 // Allow: 20 // Block important: 30 - // Redirect important: 31-39 + // Redirect important: 32-39 const realms = new Map([ [ BLOCK_REALM, { type: 'block', priority: 0 } ], [ ALLOW_REALM, { type: 'allow', priority: 20 } ], - [ REDIRECT_REALM, { type: 'redirect', priority: 1 } ], + [ REDIRECT_REALM, { type: 'redirect', priority: 2 } ], [ REMOVEPARAM_REALM, { type: 'removeparam', priority: 0 } ], [ CSP_REALM, { type: 'csp', priority: 0 } ], [ PERMISSIONS_REALM, { type: 'permissions', priority: 0 } ], From 3360d3e3e1a248338e5c6b98e4a9de9485b45693 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Wed, 21 Aug 2024 11:52:39 -0400 Subject: [PATCH 0146/1099] [mv3] Remove unused file --- platform/mv3/ubo-version | 1 - 1 file changed, 1 deletion(-) delete mode 100644 platform/mv3/ubo-version diff --git a/platform/mv3/ubo-version b/platform/mv3/ubo-version deleted file mode 100644 index 4437cefd15cb1..0000000000000 --- a/platform/mv3/ubo-version +++ /dev/null @@ -1 +0,0 @@ -https://github.com/gorhill/uBlock/tree/4b83101ab9270a5403d66af4ebe08d251ac372ca From 0a048eb64e706c47f5fd49a5cd971ce29ef8b0f4 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Thu, 22 Aug 2024 12:36:31 -0400 Subject: [PATCH 0147/1099] Address eslint warnings --- platform/mv3/make-rulesets.js | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/platform/mv3/make-rulesets.js b/platform/mv3/make-rulesets.js index e0ab17135fa16..c85a11a5869f0 100644 --- a/platform/mv3/make-rulesets.js +++ b/platform/mv3/make-rulesets.js @@ -19,19 +19,17 @@ Home: https://github.com/gorhill/uBlock */ -'use strict'; +import * as makeScriptlet from './make-scriptlets.js'; +import * as sfp from './js/static-filtering-parser.js'; -/******************************************************************************/ +import { createHash, randomBytes } from 'crypto'; +import { dnrRulesetFromRawLists } from './js/static-dnr-filtering.js'; import fs from 'fs/promises'; import https from 'https'; import path from 'path'; import process from 'process'; -import { createHash, randomBytes } from 'crypto'; import redirectResourcesMap from './js/redirect-resources.js'; -import { dnrRulesetFromRawLists } from './js/static-dnr-filtering.js'; -import * as sfp from './js/static-filtering-parser.js'; -import * as makeScriptlet from './make-scriptlets.js'; import { safeReplace } from './safe-replace.js'; /******************************************************************************/ @@ -106,8 +104,7 @@ const log = (text, silent = false) => { const urlToFileName = url => { return url .replace(/^https?:\/\//, '') - .replace(/\//g, '_') - ; + .replace(/\//g, '_'); }; const fetchText = (url, cacheDir) => { @@ -365,8 +362,7 @@ async function processNetworkFilters(assetDetails, network) { log(plainGood .filter(rule => Array.isArray(rule._warning)) .map(rule => rule._warning.map(v => `\t\t${v}`)) - .join('\n'), - true + .join('\n'), true ); const regexes = rules.filter(rule => isGood(rule) && isRegex(rule)); From 59a9a43a83202111ef2a786e1d4c3638185f99ec Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sat, 24 Aug 2024 12:11:35 -0400 Subject: [PATCH 0148/1099] Add noop resources for redirect purpose Related discussion: https://github.com/uBlockOrigin/uAssets/issues/25014#issuecomment-2307982886 --- src/js/redirect-resources.js | 18 ++++++++++++++---- src/web_accessible_resources/noop-vast2.xml | 1 + src/web_accessible_resources/noop-vast3.xml | 1 + src/web_accessible_resources/noop-vast4.xml | 1 + .../{noop-vmap1.0.xml => noop-vmap1.xml} | 0 5 files changed, 17 insertions(+), 4 deletions(-) create mode 100644 src/web_accessible_resources/noop-vast2.xml create mode 100644 src/web_accessible_resources/noop-vast3.xml create mode 100644 src/web_accessible_resources/noop-vast4.xml rename src/web_accessible_resources/{noop-vmap1.0.xml => noop-vmap1.xml} (100%) diff --git a/src/js/redirect-resources.js b/src/js/redirect-resources.js index b8577e3701fcf..b1a24c61f42f4 100644 --- a/src/js/redirect-resources.js +++ b/src/js/redirect-resources.js @@ -19,8 +19,6 @@ Home: https://github.com/gorhill/uBlock */ -'use strict'; - /******************************************************************************/ // The resources referenced below are found in ./web_accessible_resources/ @@ -159,8 +157,20 @@ export default new Map([ alias: 'nooptext', data: 'text', } ], - [ 'noop-vmap1.0.xml', { - alias: 'noopvmap-1.0', + [ 'noop-vast2.xml', { + alias: 'noopvast-2.0', + data: 'text', + } ], + [ 'noop-vast3.xml', { + alias: 'noopvast-3.0', + data: 'text', + } ], + [ 'noop-vast4.xml', { + alias: 'noopvast-4.0', + data: 'text', + } ], + [ 'noop-vmap1.xml', { + alias: [ 'noop-vmap1.0.xml', 'noopvmap-1.0' ], data: 'text', } ], [ 'outbrain-widget.js', { diff --git a/src/web_accessible_resources/noop-vast2.xml b/src/web_accessible_resources/noop-vast2.xml new file mode 100644 index 0000000000000..9bbe8d3cacde1 --- /dev/null +++ b/src/web_accessible_resources/noop-vast2.xml @@ -0,0 +1 @@ + diff --git a/src/web_accessible_resources/noop-vast3.xml b/src/web_accessible_resources/noop-vast3.xml new file mode 100644 index 0000000000000..cc1185f288234 --- /dev/null +++ b/src/web_accessible_resources/noop-vast3.xml @@ -0,0 +1 @@ + diff --git a/src/web_accessible_resources/noop-vast4.xml b/src/web_accessible_resources/noop-vast4.xml new file mode 100644 index 0000000000000..bd408f56bb932 --- /dev/null +++ b/src/web_accessible_resources/noop-vast4.xml @@ -0,0 +1 @@ + diff --git a/src/web_accessible_resources/noop-vmap1.0.xml b/src/web_accessible_resources/noop-vmap1.xml similarity index 100% rename from src/web_accessible_resources/noop-vmap1.0.xml rename to src/web_accessible_resources/noop-vmap1.xml From 794e6ca0b5ad1c3a9cbb70cea8756140092144c1 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sat, 24 Aug 2024 12:14:07 -0400 Subject: [PATCH 0149/1099] Update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3508c264035a8..5060e90e67fd3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,4 @@ +- [Add noop resources for redirect purpose](https://github.com/gorhill/uBlock/commit/59a9a43a83) - [Use helper function to lookup safe cookie values](https://github.com/gorhill/uBlock/commit/79e10323ad) - [Add `checked`/`unchecked` to `set-cookie`](https://github.com/gorhill/uBlock/commit/3e2171f550) (by @ryanbr) - [Add `allowed`/`denied` to `set-local-storage-item`](https://github.com/gorhill/uBlock/commit/41c2258f91) (by @ryanbr) From 6991d9fdd7a9b347d305821f0435aa3bd98c6cad Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sat, 24 Aug 2024 12:14:27 -0400 Subject: [PATCH 0150/1099] New revision for dev build --- dist/version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dist/version b/dist/version index 59d28f274b6ef..0739b6790ad25 100644 --- a/dist/version +++ b/dist/version @@ -1 +1 @@ -1.59.1.5 \ No newline at end of file +1.59.1.6 \ No newline at end of file From 26b2ab8bb5fc572a64e75d58f8d4d6388d9909c5 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sat, 24 Aug 2024 12:36:21 -0400 Subject: [PATCH 0151/1099] Make Firefox dev build auto-update --- dist/firefox/updates.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dist/firefox/updates.json b/dist/firefox/updates.json index a008bf43f72bb..bf1454c9771d2 100644 --- a/dist/firefox/updates.json +++ b/dist/firefox/updates.json @@ -3,9 +3,9 @@ "uBlock0@raymondhill.net": { "updates": [ { - "version": "1.59.1.5", + "version": "1.59.1.6", "browser_specific_settings": { "gecko": { "strict_min_version": "78.0" } }, - "update_link": "https://github.com/gorhill/uBlock/releases/download/1.59.1b5/uBlock0_1.59.1b5.firefox.signed.xpi" + "update_link": "https://github.com/gorhill/uBlock/releases/download/1.59.1b6/uBlock0_1.59.1b6.firefox.signed.xpi" } ] } From 3a249f395cc44272d36757829c51a6d6fb863fc0 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Mon, 26 Aug 2024 14:28:16 -0400 Subject: [PATCH 0152/1099] Improve `prevent-xhr` scriptlet As per filter list maintainers feedback. --- assets/resources/scriptlets.js | 81 ++++++++++++++++++++++++++-------- 1 file changed, 63 insertions(+), 18 deletions(-) diff --git a/assets/resources/scriptlets.js b/assets/resources/scriptlets.js index 8acbb98940d84..0272ecce39b27 100644 --- a/assets/resources/scriptlets.js +++ b/assets/resources/scriptlets.js @@ -2695,6 +2695,13 @@ function noXhrIf( 'content-type': '', 'content-length': '', }; + const safeDispatchEvent = (xhr, type) => { + try { + xhr.dispatchEvent(new Event(type)); + } catch(_) { + } + }; + const XHRBefore = XMLHttpRequest.prototype; self.XMLHttpRequest = class extends self.XMLHttpRequest { open(method, url, ...args) { xhrInstances.delete(this); @@ -2721,27 +2728,26 @@ function noXhrIf( let promise = Promise.resolve({ xhr: this, directive, - props: { - readyState: { value: 4 }, + response: { response: { value: '' }, responseText: { value: '' }, responseXML: { value: null }, responseURL: { value: haystack.url }, - status: { value: 200 }, - statusText: { value: 'OK' }, - }, + } }); switch ( this.responseType ) { case 'arraybuffer': promise = promise.then(details => { - details.props.response.value = new ArrayBuffer(0); + const response = details.response; + response.response.value = new ArrayBuffer(0); return details; }); haystack.headers['content-type'] = 'application/octet-stream'; break; case 'blob': promise = promise.then(details => { - details.props.response.value = new Blob([]); + const response = details.response; + response.response.value = new Blob([]); return details; }); haystack.headers['content-type'] = 'application/octet-stream'; @@ -2750,8 +2756,9 @@ function noXhrIf( promise = promise.then(details => { const parser = new DOMParser(); const doc = parser.parseFromString('', 'text/html'); - details.props.response.value = doc; - details.props.responseXML.value = doc; + const response = details.response; + response.response.value = doc; + response.responseXML.value = doc; return details; }); haystack.headers['content-type'] = 'text/html'; @@ -2759,8 +2766,9 @@ function noXhrIf( } case 'json': promise = promise.then(details => { - details.props.response.value = {}; - details.props.responseText.value = '{}'; + const response = details.response; + response.response.value = {}; + response.responseText.value = '{}'; return details; }); haystack.headers['content-type'] = 'application/json'; @@ -2769,8 +2777,9 @@ function noXhrIf( if ( directive === '' ) { break; } promise = promise.then(details => { return generateContentFn(details.directive).then(text => { - details.props.response.value = text; - details.props.responseText.value = text; + const response = details.response; + response.response.value = text; + response.responseText.value = text; return details; }); }); @@ -2778,11 +2787,35 @@ function noXhrIf( break; } promise.then(details => { - haystack.headers['content-length'] = `${details.props.response.value}`.length; - Object.defineProperties(details.xhr, details.props); - details.xhr.dispatchEvent(new Event('readystatechange')); - details.xhr.dispatchEvent(new Event('load')); - details.xhr.dispatchEvent(new Event('loadend')); + Object.defineProperties(details.xhr, { + readyState: { value: 1, configurable: true }, + }); + safeDispatchEvent(details.xhr, 'readystatechange'); + return details; + }).then(details => { + const response = details.response; + haystack.headers['content-length'] = `${response.response.value}`.length; + Object.defineProperties(details.xhr, { + readyState: { value: 2, configurable: true }, + status: { value: 200 }, + statusText: { value: 'OK' }, + }); + safeDispatchEvent(details.xhr, 'readystatechange'); + return details; + }).then(details => { + Object.defineProperties(details.xhr, { + readyState: { value: 3, configurable: true }, + }); + Object.defineProperties(details.xhr, details.response); + safeDispatchEvent(details.xhr, 'readystatechange'); + return details; + }).then(details => { + Object.defineProperties(details.xhr, { + readyState: { value: 4 }, + }); + safeDispatchEvent(details.xhr, 'readystatechange'); + safeDispatchEvent(details.xhr, 'load'); + safeDispatchEvent(details.xhr, 'loadend'); safe.uboLog(logPrefix, `Prevented with response:\n${details.xhr.response}`); }); } @@ -2809,6 +2842,18 @@ function noXhrIf( return out.join('\r\n'); } }; + self.XMLHttpRequest.prototype.open.toString = function() { + return XHRBefore.open.toString(); + }; + self.XMLHttpRequest.prototype.send.toString = function() { + return XHRBefore.send.toString(); + }; + self.XMLHttpRequest.prototype.getResponseHeader.toString = function() { + return XHRBefore.getResponseHeader.toString(); + }; + self.XMLHttpRequest.prototype.getAllResponseHeaders.toString = function() { + return XHRBefore.getAllResponseHeaders.toString(); + }; } /******************************************************************************/ From bada70af83b9e430af911d1f85e6afdf14904d4e Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Mon, 26 Aug 2024 14:30:27 -0400 Subject: [PATCH 0153/1099] Update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5060e90e67fd3..4cde65fbbe8cf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,4 @@ +- [Improve `prevent-xhr` scriptlet](https://github.com/gorhill/uBlock/commit/3a249f395c) - [Add noop resources for redirect purpose](https://github.com/gorhill/uBlock/commit/59a9a43a83) - [Use helper function to lookup safe cookie values](https://github.com/gorhill/uBlock/commit/79e10323ad) - [Add `checked`/`unchecked` to `set-cookie`](https://github.com/gorhill/uBlock/commit/3e2171f550) (by @ryanbr) From f306479f5a8d28832a1804122740fa403154f090 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Mon, 26 Aug 2024 14:30:45 -0400 Subject: [PATCH 0154/1099] New revision for dev build --- dist/version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dist/version b/dist/version index 0739b6790ad25..054eaa8a4b990 100644 --- a/dist/version +++ b/dist/version @@ -1 +1 @@ -1.59.1.6 \ No newline at end of file +1.59.1.7 \ No newline at end of file From f5f042a6f0c39ee92da06775a5ebff9da1be9c5a Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Mon, 26 Aug 2024 14:50:41 -0400 Subject: [PATCH 0155/1099] Make Firefox dev build auto-update --- dist/firefox/updates.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dist/firefox/updates.json b/dist/firefox/updates.json index bf1454c9771d2..eb0ddf28e87b4 100644 --- a/dist/firefox/updates.json +++ b/dist/firefox/updates.json @@ -3,9 +3,9 @@ "uBlock0@raymondhill.net": { "updates": [ { - "version": "1.59.1.6", + "version": "1.59.1.7", "browser_specific_settings": { "gecko": { "strict_min_version": "78.0" } }, - "update_link": "https://github.com/gorhill/uBlock/releases/download/1.59.1b6/uBlock0_1.59.1b6.firefox.signed.xpi" + "update_link": "https://github.com/gorhill/uBlock/releases/download/1.59.1b7/uBlock0_1.59.1b7.firefox.signed.xpi" } ] } From 0dcb9856013eba719d1b2bbc01b25039696593cf Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Tue, 27 Aug 2024 12:49:35 -0400 Subject: [PATCH 0156/1099] Improve `trusted-replace-outbound-text` scriptlet When the replacement starts with `json:`, it will be first decoded using JSON.parse(). Example: example.com##+js(trusted-replace-outbound-text, somefn, json:"ok") The doublequotes are required since this is what JSON.parse() expects as a valid JSON string. --- assets/resources/scriptlets.js | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/assets/resources/scriptlets.js b/assets/resources/scriptlets.js index 0272ecce39b27..f450b88a9b903 100644 --- a/assets/resources/scriptlets.js +++ b/assets/resources/scriptlets.js @@ -4906,14 +4906,17 @@ builtinScriptlets.push({ }); function trustedReplaceOutboundText( propChain = '', - pattern = '', - replacement = '', + rawPattern = '', + rawReplacement = '', ...args ) { if ( propChain === '' ) { return; } const safe = safeSelf(); - const logPrefix = safe.makeLogPrefix('trusted-replace-outbound-text', propChain, pattern, replacement, ...args); - const rePattern = safe.patternToRegex(pattern); + const logPrefix = safe.makeLogPrefix('trusted-replace-outbound-text', propChain, rawPattern, rawReplacement, ...args); + const rePattern = safe.patternToRegex(rawPattern); + const replacement = rawReplacement.startsWith('json:') + ? safe.JSON_parse(rawReplacement.slice(5)) + : rawReplacement; const extraArgs = safe.getExtraArgs(args); const reCondition = safe.patternToRegex(extraArgs.condition || ''); const reflector = proxyApplyFn(propChain, function(...args) { @@ -4923,7 +4926,7 @@ function trustedReplaceOutboundText( try { textBefore = self.atob(encodedTextBefore); } catch(ex) { return encodedTextBefore; } } - if ( pattern === '' ) { + if ( rawPattern === '' ) { safe.uboLog(logPrefix, 'Decoded outbound text:\n', textBefore); return encodedTextBefore; } From 1e14a2176b3d8ecf19ae94bc4b6304522eb7069a Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Tue, 27 Aug 2024 12:54:19 -0400 Subject: [PATCH 0157/1099] Update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4cde65fbbe8cf..29c7b0fd3c08b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,4 @@ +- [Improve `trusted-replace-outbound-text` scriptlet](https://github.com/gorhill/uBlock/commit/0dcb985601) - [Improve `prevent-xhr` scriptlet](https://github.com/gorhill/uBlock/commit/3a249f395c) - [Add noop resources for redirect purpose](https://github.com/gorhill/uBlock/commit/59a9a43a83) - [Use helper function to lookup safe cookie values](https://github.com/gorhill/uBlock/commit/79e10323ad) From d24bf002e26a503ca6dd0eb07f9ea7a6db21adcc Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Tue, 27 Aug 2024 12:54:35 -0400 Subject: [PATCH 0158/1099] New revision for dev build --- dist/version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dist/version b/dist/version index 054eaa8a4b990..ac401f9e36a9d 100644 --- a/dist/version +++ b/dist/version @@ -1 +1 @@ -1.59.1.7 \ No newline at end of file +1.59.1.8 \ No newline at end of file From b1f28b4ce06a2c89ba688a63fd296af80ec378e5 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Tue, 27 Aug 2024 13:01:04 -0400 Subject: [PATCH 0159/1099] Make Firefox dev build auto-update --- dist/firefox/updates.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dist/firefox/updates.json b/dist/firefox/updates.json index eb0ddf28e87b4..cd8c521d143b6 100644 --- a/dist/firefox/updates.json +++ b/dist/firefox/updates.json @@ -3,9 +3,9 @@ "uBlock0@raymondhill.net": { "updates": [ { - "version": "1.59.1.7", + "version": "1.59.1.8", "browser_specific_settings": { "gecko": { "strict_min_version": "78.0" } }, - "update_link": "https://github.com/gorhill/uBlock/releases/download/1.59.1b7/uBlock0_1.59.1b7.firefox.signed.xpi" + "update_link": "https://github.com/gorhill/uBlock/releases/download/1.59.1b8/uBlock0_1.59.1b8.firefox.signed.xpi" } ] } From ae5dc6299e513cb75c601fcebadff3a7235de6a4 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Thu, 29 Aug 2024 13:47:48 -0400 Subject: [PATCH 0160/1099] Improve `validate-constant` scriptlet helper Add support for `json:`-prefixed values. --- assets/resources/scriptlets.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/assets/resources/scriptlets.js b/assets/resources/scriptlets.js index f450b88a9b903..a3196208d1f5e 100644 --- a/assets/resources/scriptlets.js +++ b/assets/resources/scriptlets.js @@ -531,7 +531,9 @@ function validateConstantFn(trusted, raw, extraArgs = {}) { if ( isNaN(raw) ) { return; } if ( Math.abs(raw) > 0x7FFF ) { return; } } else if ( trusted ) { - if ( raw.startsWith('{') && raw.endsWith('}') ) { + if ( raw.startsWith('json:') ) { + try { value = safe.JSON_parse(raw.slice(5)); } catch(ex) { return; } + } else if ( raw.startsWith('{') && raw.endsWith('}') ) { try { value = safe.JSON_parse(raw).value; } catch(ex) { return; } } } else { From 7f11d6216eb866fdbec4c13d9766b76432908f4f Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Fri, 30 Aug 2024 10:25:39 -0400 Subject: [PATCH 0161/1099] Improve `prevent-window-open` scriptlet As discussed with filter list maintainers. --- assets/resources/scriptlets.js | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/assets/resources/scriptlets.js b/assets/resources/scriptlets.js index a3196208d1f5e..9d703997c9c4c 100644 --- a/assets/resources/scriptlets.js +++ b/assets/resources/scriptlets.js @@ -2885,10 +2885,8 @@ function noWindowOpenIf( pattern = pattern.slice(1); } const rePattern = safe.patternToRegex(pattern); - let autoRemoveAfter = parseInt(delay); - if ( isNaN(autoRemoveAfter) ) { - autoRemoveAfter = -1; - } + const autoRemoveAfter = parseInt(delay, 10) || 0; + const setTimeout = self.setTimeout; const createDecoy = function(tag, urlProp, url) { const decoyElem = document.createElement(tag); decoyElem[urlProp] = url; @@ -2909,7 +2907,13 @@ function noWindowOpenIf( return Reflect.apply(target, thisArg, args); } safe.uboLog(logPrefix, `Prevented (${args.join(', ')})`); - if ( autoRemoveAfter < 0 ) { return null; } + if ( delay === '' ) { return null; } + if ( decoy === 'blank' ) { + args[0] = 'about:blank'; + const r = Reflect.apply(target, thisArg, args); + setTimeout(( ) => { r.close(); }, autoRemoveAfter); + return r; + } const decoyElem = decoy === 'obj' ? createDecoy('object', 'data', ...args) : createDecoy('iframe', 'src', ...args); From 63166ca88239d26f92ae586cd53bb5625ce34a84 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Fri, 30 Aug 2024 10:28:23 -0400 Subject: [PATCH 0162/1099] Update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 29c7b0fd3c08b..b19d6a684b36b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,4 @@ +- [Improve `prevent-window-open` scriptlet](https://github.com/gorhill/uBlock/commit/7f11d6216e) - [Improve `trusted-replace-outbound-text` scriptlet](https://github.com/gorhill/uBlock/commit/0dcb985601) - [Improve `prevent-xhr` scriptlet](https://github.com/gorhill/uBlock/commit/3a249f395c) - [Add noop resources for redirect purpose](https://github.com/gorhill/uBlock/commit/59a9a43a83) From 11e0f08c9a9b298c4c8fb7c47cea98a4154a4b18 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Fri, 30 Aug 2024 10:28:59 -0400 Subject: [PATCH 0163/1099] New revision for dev build --- dist/version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dist/version b/dist/version index ac401f9e36a9d..b5b7973072213 100644 --- a/dist/version +++ b/dist/version @@ -1 +1 @@ -1.59.1.8 \ No newline at end of file +1.59.1.9 \ No newline at end of file From 66cf6f0a14efd5e1f30ba019b506568844ed26cd Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Fri, 30 Aug 2024 10:56:51 -0400 Subject: [PATCH 0164/1099] Make Firefox dev build auto-update --- dist/firefox/updates.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dist/firefox/updates.json b/dist/firefox/updates.json index cd8c521d143b6..4b3932642a390 100644 --- a/dist/firefox/updates.json +++ b/dist/firefox/updates.json @@ -3,9 +3,9 @@ "uBlock0@raymondhill.net": { "updates": [ { - "version": "1.59.1.8", + "version": "1.59.1.9", "browser_specific_settings": { "gecko": { "strict_min_version": "78.0" } }, - "update_link": "https://github.com/gorhill/uBlock/releases/download/1.59.1b8/uBlock0_1.59.1b8.firefox.signed.xpi" + "update_link": "https://github.com/gorhill/uBlock/releases/download/1.59.1b9/uBlock0_1.59.1b9.firefox.signed.xpi" } ] } From f552f655cbdaf89e4f74a47772a4d896f12bb473 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sat, 31 Aug 2024 12:36:20 -0400 Subject: [PATCH 0165/1099] Fix `prevent-window-open` for when logger is open Related discussion: https://github.com/uBlockOrigin/uBlock-discussions/discussions/906 --- assets/resources/scriptlets.js | 32 ++++++++++++++++++-------------- 1 file changed, 18 insertions(+), 14 deletions(-) diff --git a/assets/resources/scriptlets.js b/assets/resources/scriptlets.js index 9d703997c9c4c..836a9ef2c95cc 100644 --- a/assets/resources/scriptlets.js +++ b/assets/resources/scriptlets.js @@ -2885,7 +2885,7 @@ function noWindowOpenIf( pattern = pattern.slice(1); } const rePattern = safe.patternToRegex(pattern); - const autoRemoveAfter = parseInt(delay, 10) || 0; + const autoRemoveAfter = (parseFloat(delay) || 0) * 1000; const setTimeout = self.setTimeout; const createDecoy = function(tag, urlProp, url) { const decoyElem = document.createElement(tag); @@ -2895,9 +2895,10 @@ function noWindowOpenIf( decoyElem.style.setProperty('top','-1px', 'important'); decoyElem.style.setProperty('width','1px', 'important'); document.body.appendChild(decoyElem); - setTimeout(( ) => { decoyElem.remove(); }, autoRemoveAfter * 1000); + setTimeout(( ) => { decoyElem.remove(); }, autoRemoveAfter); return decoyElem; }; + const noopFunc = function(){}; proxyApplyFn('open', function open(target, thisArg, args) { const haystack = args.join(' '); if ( rePattern.test(haystack) !== targetMatchResult ) { @@ -2921,28 +2922,31 @@ function noWindowOpenIf( if ( typeof popup === 'object' && popup !== null ) { Object.defineProperty(popup, 'closed', { value: false }); } else { - const noopFunc = function open(){}; popup = new Proxy(self, { - get: function(target, prop) { + get: function(target, prop, ...args) { if ( prop === 'closed' ) { return false; } - const r = Reflect.get(...arguments); + const r = Reflect.get(target, prop, ...args); if ( typeof r === 'function' ) { return noopFunc; } - return target[prop]; + return r; }, - set: function() { - return Reflect.set(...arguments); + set: function(...args) { + return Reflect.set(...args); }, }); } if ( safe.logLevel !== 0 ) { popup = new Proxy(popup, { - get: function(target, prop) { - safe.uboLog(logPrefix, 'window.open / get', prop, '===', target[prop]); - return Reflect.get(...arguments); + get: function(target, prop, ...args) { + const r = Reflect.get(target, prop, ...args); + safe.uboLog(logPrefix, `popup / get ${prop} === ${r}`); + if ( typeof r === 'function' ) { + return (...args) => { return r.call(target, ...args); }; + } + return r; }, - set: function(target, prop, value) { - safe.uboLog(logPrefix, 'window.open / set', prop, '=', value); - return Reflect.set(...arguments); + set: function(target, prop, value, ...args) { + safe.uboLog(logPrefix, `popup / set ${prop} = ${value}`); + return Reflect.set(target, prop, value, ...args); }, }); } From 17183f7de58d554c2b689e85a7347a3f2cd13385 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sat, 31 Aug 2024 12:47:38 -0400 Subject: [PATCH 0166/1099] Update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index b19d6a684b36b..edbccb68ad593 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,4 @@ +- [Fix `prevent-window-open` for when logger is open](https://github.com/gorhill/uBlock/commit/f552f655cb) - [Improve `prevent-window-open` scriptlet](https://github.com/gorhill/uBlock/commit/7f11d6216e) - [Improve `trusted-replace-outbound-text` scriptlet](https://github.com/gorhill/uBlock/commit/0dcb985601) - [Improve `prevent-xhr` scriptlet](https://github.com/gorhill/uBlock/commit/3a249f395c) From 73e0cc71631487705a98f26fda98c18b6a1865f4 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sat, 31 Aug 2024 12:48:12 -0400 Subject: [PATCH 0167/1099] New revision for dev build --- dist/version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dist/version b/dist/version index b5b7973072213..55d7f586f3070 100644 --- a/dist/version +++ b/dist/version @@ -1 +1 @@ -1.59.1.9 \ No newline at end of file +1.59.1.10 \ No newline at end of file From eef99e9db69bca8eda238309760e7b7d5be2ca2d Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sat, 31 Aug 2024 13:11:29 -0400 Subject: [PATCH 0168/1099] Make Firefox dev build auto-update --- dist/firefox/updates.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dist/firefox/updates.json b/dist/firefox/updates.json index 4b3932642a390..d667b8e5585ef 100644 --- a/dist/firefox/updates.json +++ b/dist/firefox/updates.json @@ -3,9 +3,9 @@ "uBlock0@raymondhill.net": { "updates": [ { - "version": "1.59.1.9", + "version": "1.59.1.10", "browser_specific_settings": { "gecko": { "strict_min_version": "78.0" } }, - "update_link": "https://github.com/gorhill/uBlock/releases/download/1.59.1b9/uBlock0_1.59.1b9.firefox.signed.xpi" + "update_link": "https://github.com/gorhill/uBlock/releases/download/1.59.1b10/uBlock0_1.59.1b10.firefox.signed.xpi" } ] } From b7ed3b45ed18d28b99a3d162b8794467a671b0a8 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Mon, 2 Sep 2024 19:32:56 -0400 Subject: [PATCH 0169/1099] Add ability to directly evaluate static network filtering engine Related issue: https://github.com/uBlockOrigin/uBlock-issues/issues/3362 There used to be a way to test URL against the network filtering engine, but this was removed in a distant past during refactoring. The ability has been brought back through uBO's own developer tools, accessible through the _More_ button in the _Support_ pane in the dashboard. To query the static network filtering engine, enter the following in the text editor: snfe?url-to-test [type] [url-of-context] `snfe?` is a prompt indicating the intent to query the static network filtering engine. At a minimum there must be a URL to test. Optionally the type of the resource to match, default to `xhr` if none specified. Also optionally, the context from within which the request is made. Example: Enter: snfe?https://www.google-analytics.com/analytics.js Result: url: https://www.google-analytics.com/analytics.js blocked: ||google-analytics.com^ Enter: snfe?https://www.google-analytics.com/analytics.js script Result: url: https://www.google-analytics.com/analytics.js type: script blocked: ||google-analytics.com^ modified: ||google-analytics.com/analytics.js$script,redirect-rule=google-analytics_analytics.js:5 Enter: snfe?https://example.com/ Result: url: https://example.com/ not blocked Enter: snfe?https://example.com/ ping Result: url: https://example.com/ type: ping blocked: *$ping,3p --- .eslintrc.yml | 1 + src/js/benchmarks.js | 50 +++++++++---------- src/js/devtools.js | 43 +++++++++++++++- src/js/messaging.js | 90 ++++++++++++++++++---------------- src/js/static-net-filtering.js | 65 +++++++++++++++++++++--- 5 files changed, 171 insertions(+), 78 deletions(-) diff --git a/.eslintrc.yml b/.eslintrc.yml index e9ff21ec0234c..470a195bd90e7 100644 --- a/.eslintrc.yml +++ b/.eslintrc.yml @@ -12,6 +12,7 @@ rules: - error - 4 - ignoredNodes: + - Program > BlockStatement - Program > IfStatement > BlockStatement - Program > ExpressionStatement > CallExpression > ArrowFunctionExpression > BlockStatement - Program > ExpressionStatement > CallExpression > FunctionExpression > BlockStatement diff --git a/src/js/benchmarks.js b/src/js/benchmarks.js index 9fdc6ec104538..93d099bd448dc 100644 --- a/src/js/benchmarks.js +++ b/src/js/benchmarks.js @@ -19,26 +19,22 @@ Home: https://github.com/gorhill/uBlock */ -'use strict'; - -/******************************************************************************/ - -import cosmeticFilteringEngine from './cosmetic-filtering.js'; -import io from './assets.js'; -import scriptletFilteringEngine from './scriptlet-filtering.js'; -import staticNetFilteringEngine from './static-net-filtering.js'; -import µb from './background.js'; -import webRequest from './traffic.js'; -import { FilteringContext } from './filtering-context.js'; -import { LineIterator } from './text-utils.js'; -import { sessionFirewall } from './filtering-engines.js'; - import { domainFromHostname, entityFromDomain, hostnameFromURI, } from './uri-utils.js'; +import { FilteringContext } from './filtering-context.js'; +import { LineIterator } from './text-utils.js'; +import cosmeticFilteringEngine from './cosmetic-filtering.js'; +import io from './assets.js'; +import scriptletFilteringEngine from './scriptlet-filtering.js'; +import { sessionFirewall } from './filtering-engines.js'; +import { default as sfne } from './static-net-filtering.js'; +import webRequest from './traffic.js'; +import µb from './background.js'; + /******************************************************************************/ // The requests.json.gz file can be downloaded from: @@ -155,13 +151,13 @@ export async function benchmarkStaticNetFiltering(options = {}) { fctxt.setURL(request.url); fctxt.setDocOriginFromURL(request.frameUrl); fctxt.setType(request.cpt); - const r = staticNetFilteringEngine.matchRequest(fctxt); + const r = sfne.matchRequest(fctxt); console.info(`Result=${r}:`); console.info(`\ttype=${fctxt.type}`); console.info(`\turl=${fctxt.url}`); console.info(`\tdocOrigin=${fctxt.getDocOrigin()}`); if ( r !== 0 ) { - console.info(staticNetFilteringEngine.toLogData()); + console.info(sfne.toLogData()); } return; } @@ -180,34 +176,34 @@ export async function benchmarkStaticNetFiltering(options = {}) { fctxt.setURL(request.url); fctxt.setDocOriginFromURL(request.frameUrl); fctxt.setType(request.cpt); - staticNetFilteringEngine.redirectURL = undefined; - const r = staticNetFilteringEngine.matchRequest(fctxt); + sfne.redirectURL = undefined; + const r = sfne.matchRequest(fctxt); matchCount += 1; if ( r === 1 ) { blockCount += 1; } else if ( r === 2 ) { allowCount += 1; } if ( r !== 1 ) { - if ( staticNetFilteringEngine.transformRequest(fctxt) ) { + if ( sfne.transformRequest(fctxt) ) { redirectCount += 1; } - if ( fctxt.redirectURL !== undefined && staticNetFilteringEngine.hasQuery(fctxt) ) { - if ( staticNetFilteringEngine.filterQuery(fctxt, 'removeparam') ) { + if ( fctxt.redirectURL !== undefined && sfne.hasQuery(fctxt) ) { + if ( sfne.filterQuery(fctxt, 'removeparam') ) { removeparamCount += 1; } } if ( fctxt.type === 'main_frame' || fctxt.type === 'sub_frame' ) { - if ( staticNetFilteringEngine.matchAndFetchModifiers(fctxt, 'csp') ) { + if ( sfne.matchAndFetchModifiers(fctxt, 'csp') ) { cspCount += 1; } - if ( staticNetFilteringEngine.matchAndFetchModifiers(fctxt, 'permissions') ) { + if ( sfne.matchAndFetchModifiers(fctxt, 'permissions') ) { permissionsCount += 1; } } - staticNetFilteringEngine.matchHeaders(fctxt, []); - if ( staticNetFilteringEngine.matchAndFetchModifiers(fctxt, 'replace') ) { + sfne.matchHeaders(fctxt, []); + if ( sfne.matchAndFetchModifiers(fctxt, 'replace') ) { replaceCount += 1; } } else if ( redirectEngine !== undefined ) { - if ( staticNetFilteringEngine.redirectRequest(redirectEngine, fctxt) ) { + if ( sfne.redirectRequest(redirectEngine, fctxt) ) { redirectCount += 1; } } @@ -254,7 +250,7 @@ export async function tokenHistogramsfunction() { fctxt.setURL(request.url); fctxt.setDocOriginFromURL(request.frameUrl); fctxt.setType(request.cpt); - const r = staticNetFilteringEngine.matchRequest(fctxt); + const r = sfne.matchRequest(fctxt); for ( let [ keyword ] of request.url.toLowerCase().matchAll(reTokens) ) { const token = keyword.slice(0, 7); if ( r === 0 ) { diff --git a/src/js/devtools.js b/src/js/devtools.js index 0763b0be83b6c..629cd36b17d10 100644 --- a/src/js/devtools.js +++ b/src/js/devtools.js @@ -21,8 +21,6 @@ /* global CodeMirror, uBlockDashboard */ -'use strict'; - import { dom, qs$ } from './dom.js'; /******************************************************************************/ @@ -212,3 +210,44 @@ vAPI.messaging.send('dashboard', { }); /******************************************************************************/ + +async function snfeQuery(lineNo, query) { + const doc = cmEditor.getDoc(); + const lineHandle = doc.getLineHandle(lineNo) + const result = await vAPI.messaging.send('devTools', { + what: 'snfeQuery', + query + }); + if ( typeof result !== 'string' ) { return; } + cmEditor.startOperation(); + const nextLineNo = doc.getLineNumber(lineHandle) + 1; + doc.replaceRange(`${result}\n`, { line: nextLineNo, ch: 0 }); + cmEditor.endOperation(); +} + +cmEditor.on('beforeChange', (cm, details) => { + if ( details.origin !== '+input' ) { return; } + if ( details.text.length !== 2 ) { return; } + if ( details.text[1] !== '' ) { return; } + const lineNo = details.from.line; + const line = cm.getLine(lineNo); + if ( details.from.ch !== line.length ) { return; } + if ( line.startsWith('snfe?') === false ) { return; } + const fields = line.slice(5).split(/\s+/); + const query = {}; + for ( const field of fields ) { + if ( /[/.]/.test(field) ) { + if ( query.url === undefined ) { + query.url = field; + } else if ( query.from === undefined ) { + query.from = field; + } + } else if ( query.type === undefined ) { + query.type = field; + } + } + if ( query.url === undefined ) { return; } + snfeQuery(lineNo, query); +}); + +/******************************************************************************/ diff --git a/src/js/messaging.js b/src/js/messaging.js index 5f39af4f441dd..ebfc5c7d44fed 100644 --- a/src/js/messaging.js +++ b/src/js/messaging.js @@ -19,53 +19,50 @@ Home: https://github.com/gorhill/uBlock */ -/* globals browser */ - -'use strict'; +import * as s14e from './s14e-serializer.js'; +import * as sfp from './static-filtering-parser.js'; -/******************************************************************************/ +import { + domainFromHostname, + domainFromURI, + entityFromDomain, + hostnameFromURI, + isNetworkURI, +} from './uri-utils.js'; -import publicSuffixList from '../lib/publicsuffixlist/publicsuffixlist.js'; -import punycode from '../lib/punycode.js'; +import { + permanentFirewall, + permanentSwitches, + permanentURLFiltering, + sessionFirewall, + sessionSwitches, + sessionURLFiltering, +} from './filtering-engines.js'; -import { filteringBehaviorChanged } from './broadcast.js'; import cacheStorage from './cachestorage.js'; import cosmeticFilteringEngine from './cosmetic-filtering.js'; +import { denseBase64 } from './base64-custom.js'; +import { dnrRulesetFromRawLists } from './static-dnr-filtering.js'; +import { filteringBehaviorChanged } from './broadcast.js'; import htmlFilteringEngine from './html-filtering.js'; +import { i18n$ } from './i18n.js'; +import io from './assets.js'; import logger from './logger.js'; import lz4Codec from './lz4.js'; -import io from './assets.js'; +import publicSuffixList from '../lib/publicsuffixlist/publicsuffixlist.js'; +import punycode from '../lib/punycode.js'; +import { redirectEngine } from './redirect-engine.js'; import scriptletFilteringEngine from './scriptlet-filtering.js'; import staticFilteringReverseLookup from './reverselookup.js'; import staticNetFilteringEngine from './static-net-filtering.js'; -import µb from './background.js'; import webRequest from './traffic.js'; -import { denseBase64 } from './base64-custom.js'; -import { dnrRulesetFromRawLists } from './static-dnr-filtering.js'; -import { i18n$ } from './i18n.js'; -import { redirectEngine } from './redirect-engine.js'; -import * as sfp from './static-filtering-parser.js'; -import * as s14e from './s14e-serializer.js'; - -import { - permanentFirewall, - sessionFirewall, - permanentSwitches, - sessionSwitches, - permanentURLFiltering, - sessionURLFiltering, -} from './filtering-engines.js'; - -import { - domainFromHostname, - domainFromURI, - entityFromDomain, - hostnameFromURI, - isNetworkURI, -} from './uri-utils.js'; +import µb from './background.js'; /******************************************************************************/ +const hasOwnProperty = (o, p) => + Object.prototype.hasOwnProperty.call(o, p); + // https://github.com/uBlockOrigin/uBlock-issues/issues/710 // Listeners have a name and a "privileged" status. // The nameless default handler is always deemed "privileged". @@ -807,7 +804,7 @@ const onMessage = function(request, sender, callback) { }); break; - case 'shouldRenderNoscriptTags': + case 'shouldRenderNoscriptTags': { if ( pageStore === null ) { break; } const fctxt = µb.filteringContext.fromTabId(sender.tabId); if ( pageStore.filterScripting(fctxt, undefined) ) { @@ -818,7 +815,7 @@ const onMessage = function(request, sender, callback) { }); } break; - + } case 'retrieveGenericCosmeticSelectors': request.tabId = sender.tabId; request.frameId = sender.frameId; @@ -1098,7 +1095,7 @@ const restoreUserData = async function(request) { // Discard unknown setting or setting with default value. for ( const key in hiddenSettings ) { if ( - µb.hiddenSettingsDefault.hasOwnProperty(key) === false || + hasOwnProperty(µb.hiddenSettingsDefault, key) === false || hiddenSettings[key] === µb.hiddenSettingsDefault[key] ) { delete hiddenSettings[key]; @@ -1128,7 +1125,7 @@ const restoreUserData = async function(request) { }); µb.saveUserFilters(userData.userFilters); if ( Array.isArray(userData.selectedFilterLists) ) { - await µb.saveSelectedFilterLists(userData.selectedFilterLists); + await µb.saveSelectedFilterLists(userData.selectedFilterLists); } vAPI.app.restart(); @@ -1150,7 +1147,7 @@ const resetUserData = async function() { // Filter lists const prepListEntries = function(entries) { for ( const k in entries ) { - if ( entries.hasOwnProperty(k) === false ) { continue; } + if ( hasOwnProperty(entries, k) === false ) { continue; } const entry = entries[k]; if ( typeof entry.supportURL === 'string' && entry.supportURL !== '' ) { entry.supportName = hostnameFromURI(entry.supportURL); @@ -1338,7 +1335,7 @@ const getSupportData = async function() { let addedListset = {}; let removedListset = {}; for ( const listKey in lists ) { - if ( lists.hasOwnProperty(listKey) === false ) { continue; } + if ( hasOwnProperty(lists, listKey) === false ) { continue; } const list = lists[listKey]; if ( list.content !== 'filters' ) { continue; } const used = µb.selectedFilterLists.includes(listKey); @@ -1755,7 +1752,7 @@ const onMessage = (request, sender, callback) => { // Sync let response; switch ( request.what ) { - case 'getInspectorArgs': + case 'getInspectorArgs': { const bc = new globalThis.BroadcastChannel('contentInspectorChannel'); bc.postMessage({ what: 'contentInspectorChannel', @@ -1768,6 +1765,7 @@ const onMessage = (request, sender, callback) => { ), }; break; + } default: return vAPI.messaging.UNHANDLED; } @@ -2004,6 +2002,12 @@ const onMessage = function(request, sender, callback) { response = staticNetFilteringEngine.dump(); break; + case 'snfeQuery': + response = staticNetFilteringEngine.test( + Object.assign({ redirectEngine }, request.query) + ); + break; + case 'cfeDump': response = cosmeticFilteringEngine.dump(); break; @@ -2181,7 +2185,7 @@ const onMessage = function(request, sender, callback) { } break; - case 'subscribeTo': + case 'subscribeTo': { // https://github.com/uBlockOrigin/uBlock-issues/issues/1797 if ( /^(file|https?):\/\//.test(request.location) === false ) { break; } const url = encodeURIComponent(request.location); @@ -2194,8 +2198,8 @@ const onMessage = function(request, sender, callback) { select: true, }); break; - - case 'updateLists': + } + case 'updateLists': { const listkeys = request.listkeys.split(',').filter(s => s !== ''); if ( listkeys.length === 0 ) { return; } if ( listkeys.includes('all') ) { @@ -2211,7 +2215,7 @@ const onMessage = function(request, sender, callback) { }); µb.scheduleAssetUpdater({ now: true, fetchDelay: 100, auto: request.auto }); break; - + } default: return vAPI.messaging.UNHANDLED; } diff --git a/src/js/static-net-filtering.js b/src/js/static-net-filtering.js index d7edc01fb35d8..f30af31bea003 100644 --- a/src/js/static-net-filtering.js +++ b/src/js/static-net-filtering.js @@ -5384,17 +5384,70 @@ StaticNetFilteringEngine.prototype.enableWASM = function(wasmModuleFetcher, path /******************************************************************************/ -StaticNetFilteringEngine.prototype.test = async function(docURL, type, url) { +StaticNetFilteringEngine.prototype.test = function(details) { + const { url, type, from, redirectEngine } = details; + if ( url === undefined ) { return; } const fctxt = new FilteringContext(); - fctxt.setDocOriginFromURL(docURL); - fctxt.setType(type); fctxt.setURL(url); + fctxt.setType(type || ''); + fctxt.setDocOriginFromURL(from || ''); const r = this.matchRequest(fctxt); - console.info(`${r}`); + const out = [ `url: ${url}` ]; + if ( type ) { + out.push(`type: ${type}`); + } + if ( from ) { + out.push(`context: ${from}`); + } if ( r !== 0 ) { - console.info(this.toLogData()); + const logdata = this.toLogData(); + if ( r === 1 ) { + out.push(`blocked: ${logdata.raw}`); + } else if ( r === 2 ) { + out.push(`unblocked: ${logdata.raw}`); + } + } else { + out.push('not blocked'); } -}; + if ( r !== 1 ) { + const entries = this.transformRequest(fctxt); + if ( entries ) { + for ( const entry of entries ) { + out.push(`modified: ${entry.logData().raw}`); + } + } + if ( fctxt.redirectURL !== undefined && this.hasQuery(fctxt) ) { + const entries = this.filterQuery(fctxt, 'removeparam'); + if ( entries ) { + for ( const entry of entries ) { + out.push(`modified: ${entry.logData().raw}`); + } + } + } + if ( fctxt.type === 'main_frame' || fctxt.type === 'sub_frame' ) { + const csps = this.matchAndFetchModifiers(fctxt, 'csp'); + if ( csps ) { + for ( const csp of csps ) { + out.push(`modified: ${csp.logData().raw}`); + } + } + const pps = this.matchAndFetchModifiers(fctxt, 'permissions'); + if ( pps ) { + for ( const pp of pps ) { + out.push(`modified: ${pp.logData().raw}`); + } + } + } + } else if ( redirectEngine ) { + const redirects = this.redirectRequest(redirectEngine, fctxt); + if ( redirects ) { + for ( const redirect of redirects ) { + out.push(`modified: ${redirect.logData().raw}`); + } + } + } + return out.join('\n'); +} /******************************************************************************/ From 969d3cb40ba974fc993ede15cfb8d034fc1c3f00 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Mon, 2 Sep 2024 19:49:31 -0400 Subject: [PATCH 0170/1099] Update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index edbccb68ad593..2ec4cc5e002eb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,4 @@ +- [Add ability to directly evaluate static network filtering engine](https://github.com/gorhill/uBlock/commit/b7ed3b45ed) - [Fix `prevent-window-open` for when logger is open](https://github.com/gorhill/uBlock/commit/f552f655cb) - [Improve `prevent-window-open` scriptlet](https://github.com/gorhill/uBlock/commit/7f11d6216e) - [Improve `trusted-replace-outbound-text` scriptlet](https://github.com/gorhill/uBlock/commit/0dcb985601) From 4310732b98f73659a688dbe84bf48520a126a12e Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Mon, 2 Sep 2024 19:49:49 -0400 Subject: [PATCH 0171/1099] New revision for dev build --- dist/version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dist/version b/dist/version index 55d7f586f3070..269e7c5474c30 100644 --- a/dist/version +++ b/dist/version @@ -1 +1 @@ -1.59.1.10 \ No newline at end of file +1.59.1.11 \ No newline at end of file From 856dc419b5dc0a827adcd5091bd636ff1b39d728 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Mon, 2 Sep 2024 19:55:53 -0400 Subject: [PATCH 0172/1099] Make Firefox dev build auto-update --- dist/firefox/updates.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dist/firefox/updates.json b/dist/firefox/updates.json index d667b8e5585ef..9a3eaa69d9a00 100644 --- a/dist/firefox/updates.json +++ b/dist/firefox/updates.json @@ -3,9 +3,9 @@ "uBlock0@raymondhill.net": { "updates": [ { - "version": "1.59.1.10", + "version": "1.59.1.11", "browser_specific_settings": { "gecko": { "strict_min_version": "78.0" } }, - "update_link": "https://github.com/gorhill/uBlock/releases/download/1.59.1b10/uBlock0_1.59.1b10.firefox.signed.xpi" + "update_link": "https://github.com/gorhill/uBlock/releases/download/1.59.1b11/uBlock0_1.59.1b11.firefox.signed.xpi" } ] } From 91125d29cfa6eab89eb9f2ed19dc535dcf3f8d73 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Tue, 3 Sep 2024 10:14:15 -0400 Subject: [PATCH 0173/1099] Add support for `application/dash+xml` in `replace=` option Related feedback: https://github.com/uBlockOrigin/uAssets/issues/25164#issuecomment-2326358453 --- src/js/traffic.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/js/traffic.js b/src/js/traffic.js index 4777a847004e0..38ba7264b85d8 100644 --- a/src/js/traffic.js +++ b/src/js/traffic.js @@ -680,6 +680,7 @@ const bodyFilterer = (( ) => { const sessions = new Map(); const reContentTypeCharset = /charset=['"]?([^'" ]+)/i; const otherValidMimes = new Set([ + 'application/dash+xml', 'application/javascript', 'application/json', 'application/mpegurl', From c8307f58a3c0cc29ad0bfb4263cadea4ebf0ee1b Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Tue, 3 Sep 2024 11:15:16 -0400 Subject: [PATCH 0174/1099] Improve `xml-prune` scriptlet Related feedback: https://github.com/uBlockOrigin/uAssets/issues/25164#issuecomment-2326358453 --- assets/resources/scriptlets.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/assets/resources/scriptlets.js b/assets/resources/scriptlets.js index 836a9ef2c95cc..07260358c37e3 100644 --- a/assets/resources/scriptlets.js +++ b/assets/resources/scriptlets.js @@ -3347,6 +3347,9 @@ function xmlPrune( const serializer = new XMLSerializer(); const textout = serializer.serializeToString(thisArg.responseXML); Object.defineProperty(thisArg, 'responseText', { value: textout }); + if ( typeof thisArg.response === 'string' ) { + Object.defineProperty(thisArg, 'response', { value: textout }); + } return; } if ( From 6e426aeac3c9e8cdaa3ec604532b510e0e8838af Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Tue, 3 Sep 2024 11:18:52 -0400 Subject: [PATCH 0175/1099] Update changelog --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2ec4cc5e002eb..f84bb4f63fdd5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,5 @@ +- [Improve `xml-prune` scriptlet](https://github.com/gorhill/uBlock/commit/c8307f58a3) +- [Add support for `application/dash+xml` in `replace=` option](https://github.com/gorhill/uBlock/commit/91125d29cf) - [Add ability to directly evaluate static network filtering engine](https://github.com/gorhill/uBlock/commit/b7ed3b45ed) - [Fix `prevent-window-open` for when logger is open](https://github.com/gorhill/uBlock/commit/f552f655cb) - [Improve `prevent-window-open` scriptlet](https://github.com/gorhill/uBlock/commit/7f11d6216e) From 08ed4b4ed81485fcceadb490f44044701310c0fe Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Tue, 3 Sep 2024 11:19:13 -0400 Subject: [PATCH 0176/1099] New revision for dev build --- dist/version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dist/version b/dist/version index 269e7c5474c30..9f041eba635fd 100644 --- a/dist/version +++ b/dist/version @@ -1 +1 @@ -1.59.1.11 \ No newline at end of file +1.59.1.12 \ No newline at end of file From 22fb9c4d63f1198551c03376e13202aacbab1894 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Tue, 3 Sep 2024 12:01:04 -0400 Subject: [PATCH 0177/1099] Make Firefox dev build auto-update --- dist/firefox/updates.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dist/firefox/updates.json b/dist/firefox/updates.json index 9a3eaa69d9a00..040211953b0ac 100644 --- a/dist/firefox/updates.json +++ b/dist/firefox/updates.json @@ -3,9 +3,9 @@ "uBlock0@raymondhill.net": { "updates": [ { - "version": "1.59.1.11", + "version": "1.59.1.12", "browser_specific_settings": { "gecko": { "strict_min_version": "78.0" } }, - "update_link": "https://github.com/gorhill/uBlock/releases/download/1.59.1b11/uBlock0_1.59.1b11.firefox.signed.xpi" + "update_link": "https://github.com/gorhill/uBlock/releases/download/1.59.1b12/uBlock0_1.59.1b12.firefox.signed.xpi" } ] } From 89f02098fd5b3564077c8e95ed9e5c95ff10f4d3 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Wed, 4 Sep 2024 11:28:45 -0400 Subject: [PATCH 0178/1099] Apply CSP/PP injections to `object` resources Related feedback: https://old.reddit.com/r/uBlockOrigin/comments/1f84tc5/ --- src/js/background.js | 2 +- src/js/filtering-context.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/js/background.js b/src/js/background.js index edeac08935de9..4cd10eeff4a1b 100644 --- a/src/js/background.js +++ b/src/js/background.js @@ -346,7 +346,7 @@ const µBlock = { // jshint ignore:line this.setDocOrigin(origin).setTabOrigin(origin); return this; } - const origin = (this.itype & this.FRAME_ANY) !== 0 + const origin = this.isDocument() ? originFromURI(this.url) : this.tabOrigin; this.setDocOrigin(origin).setTabOrigin(origin); diff --git a/src/js/filtering-context.js b/src/js/filtering-context.js index 6642050712904..3da9d158f0a4c 100644 --- a/src/js/filtering-context.js +++ b/src/js/filtering-context.js @@ -56,7 +56,7 @@ export const XMLHTTPREQUEST = 1 << 13; export const INLINE_FONT = 1 << 14; export const INLINE_SCRIPT = 1 << 15; export const OTHER = 1 << 16; -export const FRAME_ANY = MAIN_FRAME | SUB_FRAME; +export const FRAME_ANY = MAIN_FRAME | SUB_FRAME | OBJECT; export const FONT_ANY = FONT | INLINE_FONT; export const INLINE_ANY = INLINE_FONT | INLINE_SCRIPT; export const PING_ANY = BEACON | CSP_REPORT | PING; From e8202af11d0d3b6b5c1b345679eaae47f7d030c1 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Wed, 4 Sep 2024 11:32:26 -0400 Subject: [PATCH 0179/1099] Improve `prevent-fetch` scriptlet - Add support for negated matches - Log caller's arguments when verbose logging is enabled --- assets/resources/scriptlets.js | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/assets/resources/scriptlets.js b/assets/resources/scriptlets.js index 07260358c37e3..5a27d810b63d1 100644 --- a/assets/resources/scriptlets.js +++ b/assets/resources/scriptlets.js @@ -97,7 +97,7 @@ function safeSelf() { }, initPattern(pattern, options = {}) { if ( pattern === '' ) { - return { matchAll: true }; + return { matchAll: true, expect: true }; } const expect = (options.canNegate !== true || pattern.startsWith('!') === false); if ( expect === false ) { @@ -2151,7 +2151,7 @@ function noFetchIf( key = 'url'; value = condition; } - needles.push({ key, re: safe.patternToRegex(value) }); + needles.push({ key, pattern: safe.initPattern(value, { canNegate: true }) }); } const validResponseProps = { ok: [ false, true ], @@ -2180,6 +2180,9 @@ function noFetchIf( const details = args[0] instanceof self.Request ? args[0] : Object.assign({ url: args[0] }, args[1]); + if ( safe.logLevel > 1 ) { + safe.uboLog(logPrefix, `apply:\n\t${Object.entries(details).map(a => `${a[0]}: ${a[1]}`).join('\n\t')}`); + } let proceed = true; try { const props = new Map(); @@ -2198,10 +2201,10 @@ function noFetchIf( return Reflect.apply(target, thisArg, args); } proceed = needles.length === 0; - for ( const { key, re } of needles ) { + for ( const { key, pattern } of needles ) { if ( - props.has(key) === false || - re.test(props.get(key)) === false + pattern.expect && props.has(key) === false || + safe.testPattern(pattern, props.get(key)) === false ) { proceed = true; break; From f4a75ccd80e0193674e0504a7de15fbf26b5b82c Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Wed, 4 Sep 2024 11:37:14 -0400 Subject: [PATCH 0180/1099] Update changelog --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index f84bb4f63fdd5..572ee92009721 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,5 @@ +- [Improve `prevent-fetch` scriptlet](https://github.com/gorhill/uBlock/commit/e8202af11d) +- [Apply CSP/PP injections to `object` resources](https://github.com/gorhill/uBlock/commit/89f02098fd) - [Improve `xml-prune` scriptlet](https://github.com/gorhill/uBlock/commit/c8307f58a3) - [Add support for `application/dash+xml` in `replace=` option](https://github.com/gorhill/uBlock/commit/91125d29cf) - [Add ability to directly evaluate static network filtering engine](https://github.com/gorhill/uBlock/commit/b7ed3b45ed) From ae9acbf521fd44114a90aef76565b863682a228e Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Wed, 4 Sep 2024 11:37:35 -0400 Subject: [PATCH 0181/1099] New revision for dev build --- dist/version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dist/version b/dist/version index 9f041eba635fd..3f55aac89412a 100644 --- a/dist/version +++ b/dist/version @@ -1 +1 @@ -1.59.1.12 \ No newline at end of file +1.59.1.13 \ No newline at end of file From 1dc09b6217ec979fa982c12cef28c175e3133dbf Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Wed, 4 Sep 2024 11:56:02 -0400 Subject: [PATCH 0182/1099] Make Firefox dev build auto-update --- dist/firefox/updates.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dist/firefox/updates.json b/dist/firefox/updates.json index 040211953b0ac..e772fa745fc9b 100644 --- a/dist/firefox/updates.json +++ b/dist/firefox/updates.json @@ -3,9 +3,9 @@ "uBlock0@raymondhill.net": { "updates": [ { - "version": "1.59.1.12", + "version": "1.59.1.13", "browser_specific_settings": { "gecko": { "strict_min_version": "78.0" } }, - "update_link": "https://github.com/gorhill/uBlock/releases/download/1.59.1b12/uBlock0_1.59.1b12.firefox.signed.xpi" + "update_link": "https://github.com/gorhill/uBlock/releases/download/1.59.1b13/uBlock0_1.59.1b13.firefox.signed.xpi" } ] } From 60a009c530424a0bced0cd77e60f69706df01f29 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Thu, 5 Sep 2024 11:17:36 -0400 Subject: [PATCH 0183/1099] Further improve `prevent-fetch` scriptlet --- assets/resources/scriptlets.js | 98 +++++++++++++++++----------------- 1 file changed, 48 insertions(+), 50 deletions(-) diff --git a/assets/resources/scriptlets.js b/assets/resources/scriptlets.js index 5a27d810b63d1..3ccb1b0215280 100644 --- a/assets/resources/scriptlets.js +++ b/assets/resources/scriptlets.js @@ -2129,6 +2129,7 @@ builtinScriptlets.push({ fn: noFetchIf, dependencies: [ 'generate-content.fn', + 'proxy-apply.fn', 'safe-self.fn', ], }); @@ -2175,61 +2176,58 @@ function noFetchIf( responseProps.type = { value: responseType }; } } - self.fetch = new Proxy(self.fetch, { - apply: function(target, thisArg, args) { - const details = args[0] instanceof self.Request - ? args[0] - : Object.assign({ url: args[0] }, args[1]); - if ( safe.logLevel > 1 ) { - safe.uboLog(logPrefix, `apply:\n\t${Object.entries(details).map(a => `${a[0]}: ${a[1]}`).join('\n\t')}`); - } - let proceed = true; - try { - const props = new Map(); - for ( const prop in details ) { - let v = details[prop]; - if ( typeof v !== 'string' ) { - try { v = safe.JSON_stringify(v); } - catch(ex) { } - } - if ( typeof v !== 'string' ) { continue; } - props.set(prop, v); - } - if ( propsToMatch === '' && responseBody === '' ) { - const out = Array.from(props).map(a => `${a[0]}:${a[1]}`); - safe.uboLog(logPrefix, `Called: ${out.join('\n')}`); - return Reflect.apply(target, thisArg, args); - } - proceed = needles.length === 0; - for ( const { key, pattern } of needles ) { - if ( - pattern.expect && props.has(key) === false || - safe.testPattern(pattern, props.get(key)) === false - ) { - proceed = true; - break; - } + proxyApplyFn('fetch', function fetch(target, thisArg, args) { + const details = args[0] instanceof self.Request + ? args[0] + : Object.assign({ url: args[0] }, args[1]); + let proceed = true; + try { + const props = new Map(); + for ( const prop in details ) { + let v = details[prop]; + if ( typeof v !== 'string' ) { + try { v = safe.JSON_stringify(v); } + catch(ex) { } } - } catch(ex) { + if ( typeof v !== 'string' ) { continue; } + props.set(prop, v); } - if ( proceed ) { + if ( safe.logLevel > 1 || propsToMatch === '' && responseBody === '' ) { + const out = Array.from(props).map(a => `${a[0]}:${a[1]}`); + safe.uboLog(logPrefix, `Called: ${out.join('\n')}`); + } + if ( propsToMatch === '' && responseBody === '' ) { return Reflect.apply(target, thisArg, args); } - return generateContentFn(responseBody).then(text => { - safe.uboLog(logPrefix, `Prevented with response "${text}"`); - const response = new Response(text, { - headers: { - 'Content-Length': text.length, - } - }); - const props = Object.assign( - { url: { value: details.url } }, - responseProps - ); - safe.Object_defineProperties(response, props); - return response; - }); + proceed = needles.length === 0; + for ( const { key, pattern } of needles ) { + if ( + pattern.expect && props.has(key) === false || + safe.testPattern(pattern, props.get(key)) === false + ) { + proceed = true; + break; + } + } + } catch(ex) { } + if ( proceed ) { + return Reflect.apply(target, thisArg, args); + } + return generateContentFn(responseBody).then(text => { + safe.uboLog(logPrefix, `Prevented with response "${text}"`); + const response = new Response(text, { + headers: { + 'Content-Length': text.length, + } + }); + const props = Object.assign( + { url: { value: details.url } }, + responseProps + ); + safe.Object_defineProperties(response, props); + return response; + }); }); } From 8631b955bfb419e744af4ca89f87f169c682dc58 Mon Sep 17 00:00:00 2001 From: Imre Eilertsen Date: Thu, 5 Sep 2024 20:49:32 +0200 Subject: [PATCH 0184/1099] Maintenance for the Serbo-Croatian Filters section (#3924) * Maintenance for the Serbo-Croatian Filters section * Implemented the request to move to contentURLs I sure I hope I understood the request correctly, or things would get slightly awkward for me. --- assets/assets.json | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/assets/assets.json b/assets/assets.json index 454c954bb7439..8665c36e8a2bc 100644 --- a/assets/assets.json +++ b/assets/assets.json @@ -622,9 +622,13 @@ "group": "regions", "off": true, "title": "🇭🇷hr 🇷🇸rs: Dandelion Sprout's Serbo-Croatian filters", - "tags": "ads croatian serbian", - "lang": "hr sr", - "contentURL": "https://raw.githubusercontent.com/DandelionSprout/adfilt/master/SerboCroatianList.txt", + "tags": "ads croatian serbian bosnian", + "lang": "bs hr sr", + "contentURL": [ + "https://raw.githubusercontent.com/DandelionSprout/adfilt/master/SerboCroatianList.txt", + "https://cdn.jsdelivr.net/gh/DandelionSprout/adfilt@master/SerboCroatianList.txt", + "https://cdn.statically.io/gl/DandelionSprout/adfilt/master/SerboCroatianList.txt" + ], "supportURL": "https://github.com/DandelionSprout/adfilt#readme" }, "HUN-0": { @@ -778,9 +782,7 @@ "tags": "ads norwegian danish icelandic", "lang": "nb nn no da is", "contentURL": [ - "https://raw.githubusercontent.com/DandelionSprout/adfilt/master/NorwegianList.txt" - ], - "cdnURLs": [ + "https://raw.githubusercontent.com/DandelionSprout/adfilt/master/NorwegianList.txt", "https://cdn.jsdelivr.net/gh/DandelionSprout/adfilt@master/NorwegianList.txt", "https://cdn.statically.io/gl/DandelionSprout/adfilt/master/NorwegianList.txt" ], From 901b4ad061ba23867c75e35ed9c284a6aacc817b Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Thu, 5 Sep 2024 14:51:47 -0400 Subject: [PATCH 0185/1099] Import changes from https://github.com/gorhill/uBlock/pull/3924 --- assets/assets.dev.json | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/assets/assets.dev.json b/assets/assets.dev.json index a78998bf2b8a5..aa54da8290962 100644 --- a/assets/assets.dev.json +++ b/assets/assets.dev.json @@ -622,9 +622,13 @@ "group": "regions", "off": true, "title": "🇭🇷hr 🇷🇸rs: Dandelion Sprout's Serbo-Croatian filters", - "tags": "ads croatian serbian", - "lang": "hr sr", - "contentURL": "https://raw.githubusercontent.com/DandelionSprout/adfilt/master/SerboCroatianList.txt", + "tags": "ads croatian serbian bosnian", + "lang": "bs hr sr", + "contentURL": [ + "https://raw.githubusercontent.com/DandelionSprout/adfilt/master/SerboCroatianList.txt", + "https://cdn.jsdelivr.net/gh/DandelionSprout/adfilt@master/SerboCroatianList.txt", + "https://cdn.statically.io/gl/DandelionSprout/adfilt/master/SerboCroatianList.txt" + ], "supportURL": "https://github.com/DandelionSprout/adfilt#readme" }, "HUN-0": { @@ -778,9 +782,7 @@ "tags": "ads norwegian danish icelandic", "lang": "nb nn no da is", "contentURL": [ - "https://raw.githubusercontent.com/DandelionSprout/adfilt/master/NorwegianList.txt" - ], - "cdnURLs": [ + "https://raw.githubusercontent.com/DandelionSprout/adfilt/master/NorwegianList.txt", "https://cdn.jsdelivr.net/gh/DandelionSprout/adfilt@master/NorwegianList.txt", "https://cdn.statically.io/gl/DandelionSprout/adfilt/master/NorwegianList.txt" ], From 8981d3e7fe3c05fe2431a06cef0d71dd23210eff Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Thu, 5 Sep 2024 14:52:19 -0400 Subject: [PATCH 0186/1099] New revision for dev build --- dist/version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dist/version b/dist/version index 3f55aac89412a..4a1a7010ab022 100644 --- a/dist/version +++ b/dist/version @@ -1 +1 @@ -1.59.1.13 \ No newline at end of file +1.59.1.14 \ No newline at end of file From 08e5bffc764c21b1b37e043a165599f96459953f Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Thu, 5 Sep 2024 15:01:24 -0400 Subject: [PATCH 0187/1099] Make Firefox dev build auto-update --- dist/firefox/updates.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dist/firefox/updates.json b/dist/firefox/updates.json index e772fa745fc9b..89e8f3d91f137 100644 --- a/dist/firefox/updates.json +++ b/dist/firefox/updates.json @@ -3,9 +3,9 @@ "uBlock0@raymondhill.net": { "updates": [ { - "version": "1.59.1.13", + "version": "1.59.1.14", "browser_specific_settings": { "gecko": { "strict_min_version": "78.0" } }, - "update_link": "https://github.com/gorhill/uBlock/releases/download/1.59.1b13/uBlock0_1.59.1b13.firefox.signed.xpi" + "update_link": "https://github.com/gorhill/uBlock/releases/download/1.59.1b14/uBlock0_1.59.1b14.firefox.signed.xpi" } ] } From bec5d53ce0fc68440e657cdf78a39e2fc74d9ea0 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Fri, 6 Sep 2024 09:31:17 -0400 Subject: [PATCH 0188/1099] [mv3] Attempt at mitigation for when "internal error" occurs Related issue: https://github.com/uBlockOrigin/uBOL-home/issues/199 --- platform/mv3/extension/js/background.js | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/platform/mv3/extension/js/background.js b/platform/mv3/extension/js/background.js index 17601b114995a..f0f6cb6cdfd69 100644 --- a/platform/mv3/extension/js/background.js +++ b/platform/mv3/extension/js/background.js @@ -386,8 +386,17 @@ async function start() { } } +// https://github.com/uBlockOrigin/uBOL-home/issues/199 +// Force a restart of the extension once when an "internal error" occurs try { start(); + localWrite({ goodStart: true }); } catch(reason) { console.trace(reason); + localRead.get('goodStart').then((bin = {}) => { + if ( bin.goodStart !== true ) { return; } + localWrite({ goodStart: false }).then(( ) => { + runtime.reload(); + }); + }); } From 1b464f75cc6b036d520c04944c5c522b5671b72d Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sat, 7 Sep 2024 16:47:43 -0400 Subject: [PATCH 0189/1099] Fix spurious browser error at the console --- platform/common/vapi-background.js | 1 + 1 file changed, 1 insertion(+) diff --git a/platform/common/vapi-background.js b/platform/common/vapi-background.js index 283ffe6efd275..26507982a51e9 100644 --- a/platform/common/vapi-background.js +++ b/platform/common/vapi-background.js @@ -958,6 +958,7 @@ vAPI.messaging = { onPortDisconnect: function(port) { this.ports.delete(port.name); + void browser.runtime.lastError; }, // https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/runtime/Port From 20115697e5e7265b6045b11055ef45d0470e157e Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sun, 8 Sep 2024 10:01:13 -0400 Subject: [PATCH 0190/1099] Add ability to quote static network option values For the sake of convenience for filter list maintainers, this commit add ability to quote static network option values, so as to avoid the need to escape commas when parser ambiguity arises. The quotes can be `"`, `'`, or backticks. Example, the following filter requires escaping commas: example.com$xhr,replace=/"loremIpsum.*?([A-Z]"\}|"\}{2\,4})\}\]\,//,1p Can be now rewritten with no need to escape when using quotes: example.com$xhr,replace='/"loremIpsum.*?([A-Z]"\}|"\}{2,4})\}\],//',1p --- src/js/codemirror/ubo-static-filtering.js | 1 + src/js/static-filtering-parser.js | 105 ++++++++++++++-------- src/js/static-net-filtering.js | 12 ++- 3 files changed, 81 insertions(+), 37 deletions(-) diff --git a/src/js/codemirror/ubo-static-filtering.js b/src/js/codemirror/ubo-static-filtering.js index 2aaf85b3df6ea..386ed52f3aa93 100644 --- a/src/js/codemirror/ubo-static-filtering.js +++ b/src/js/codemirror/ubo-static-filtering.js @@ -189,6 +189,7 @@ const uBOStaticFilteringMode = (( ) => { mode.lastNetOptionType = nodeType; return 'def'; case sfp.NODE_TYPE_NET_OPTION_ASSIGN: + case sfp.NODE_TYPE_NET_OPTION_QUOTE: return 'def'; case sfp.NODE_TYPE_NET_OPTION_VALUE: if ( mode.astWalker.canGoDown() ) { break; } diff --git a/src/js/static-filtering-parser.js b/src/js/static-filtering-parser.js index e89f7b3a30ce5..b0a4a15d1a87b 100644 --- a/src/js/static-filtering-parser.js +++ b/src/js/static-filtering-parser.js @@ -195,6 +195,7 @@ export const NODE_TYPE_NET_OPTION_NAME_XHR = iota++; export const NODE_TYPE_NET_OPTION_NAME_WEBRTC = iota++; export const NODE_TYPE_NET_OPTION_NAME_WEBSOCKET = iota++; export const NODE_TYPE_NET_OPTION_ASSIGN = iota++; +export const NODE_TYPE_NET_OPTION_QUOTE = iota++; export const NODE_TYPE_NET_OPTION_VALUE = iota++; export const NODE_TYPE_OPTION_VALUE_DOMAIN_LIST = iota++; export const NODE_TYPE_OPTION_VALUE_DOMAIN_RAW = iota++; @@ -896,7 +897,9 @@ export class AstFilterParser { this.reGoodRegexToken = /[^\x01%0-9A-Za-z][%0-9A-Za-z]{7,}|[^\x01%0-9A-Za-z][%0-9A-Za-z]{1,6}[^\x01%0-9A-Za-z]/; this.reBadCSP = /(?:^|[;,])\s*report-(?:to|uri)\b/i; this.reBadPP = /(?:^|[;,])\s*report-to\b/i; + this.reNetOption = /^(~?)([13a-z_-]+)(=?)/; this.reNoopOption = /^_+$/; + this.netOptionValueParser = new ArgListParser(','); this.scriptletArgListParser = new ArgListParser(','); } @@ -1959,16 +1962,17 @@ export class AstFilterParser { const head = this.allocHeadNode(); let prev = head, next = 0; let optionBeg = 0, optionEnd = 0; - let emptyOption = false, badComma = false; while ( optionBeg !== optionsEnd ) { - optionEnd = this.endOfNetOption(s, optionBeg); next = this.allocTypedNode( NODE_TYPE_NET_OPTION_RAW, parentBeg + optionBeg, - parentBeg + optionEnd + parentBeg + optionsEnd // open ended ); - emptyOption = optionEnd === optionBeg; - this.linkDown(next, this.parseNetOption(next)); + const { node: down, len: optionLen } = this.parseNetOption(next); + // set next's end to down's end + optionEnd += optionLen; + this.nodes[next+NODE_END_INDEX] = parentBeg + optionEnd; + this.linkDown(next, down); prev = this.linkRight(prev, next); if ( optionEnd === optionsEnd ) { break; } optionBeg = optionEnd + 1; @@ -1977,12 +1981,12 @@ export class AstFilterParser { parentBeg + optionEnd, parentBeg + optionBeg ); - badComma = optionBeg === optionsEnd; - prev = this.linkRight(prev, next); - if ( emptyOption || badComma ) { + if ( optionLen === 0 || optionBeg === optionsEnd ) { this.addNodeFlags(next, NODE_FLAG_ERROR); this.addFlags(AST_FLAG_HAS_ERROR); } + prev = this.linkRight(prev, next); + optionEnd = optionBeg; } this.linkRight(prev, this.allocSentinelNode(NODE_TYPE_NET_OPTION_SENTINEL, parentEnd) @@ -1990,19 +1994,21 @@ export class AstFilterParser { return this.throwHeadNode(head); } - endOfNetOption(s, beg) { - const match = this.reNetOptionComma.exec(s.slice(beg)); - return match !== null ? beg + match.index : s.length; - } - parseNetOption(parent) { const parentBeg = this.nodes[parent+NODE_BEG_INDEX]; const s = this.getNodeString(parent); - const optionEnd = s.length; + const match = this.reNetOption.exec(s) || []; + if ( match.length === 0 ) { + this.addNodeFlags(parent, NODE_FLAG_ERROR); + this.addFlags(AST_FLAG_HAS_ERROR); + this.astError = AST_ERROR_OPTION_UNKNOWN; + return { node: 0, len: s.length }; + } const head = this.allocHeadNode(); let prev = head, next = 0; - let nameBeg = 0; - if ( s.charCodeAt(0) === 0x7E ) { + const matchEnd = match && match[0].length || 0; + const negated = match[1] === '~'; + if ( negated ) { this.addNodeFlags(parent, NODE_FLAG_IS_NEGATED); next = this.allocTypedNode( NODE_TYPE_NET_OPTION_NAME_NOT, @@ -2010,11 +2016,11 @@ export class AstFilterParser { parentBeg+1 ); prev = this.linkRight(prev, next); - nameBeg += 1; } - const equalPos = s.indexOf('='); - const nameEnd = equalPos !== -1 ? equalPos : s.length; - const name = s.slice(nameBeg, nameEnd); + const nameBeg = negated ? 1 : 0; + const assigned = match[3] === '='; + const nameEnd = matchEnd - (assigned ? 1 : 0); + const name = match[2] || ''; let nodeOptionType = nodeTypeFromOptionName.get(name); if ( nodeOptionType === undefined ) { nodeOptionType = this.reNoopOption.test(name) @@ -2037,27 +2043,43 @@ export class AstFilterParser { this.addNodeToRegister(nodeOptionType, parent); } prev = this.linkRight(prev, next); - if ( equalPos === -1 ) { - return this.throwHeadNode(head); + if ( assigned === false ) { + return { node: this.throwHeadNode(head), len: matchEnd }; } - const valueBeg = equalPos + 1; next = this.allocTypedNode( NODE_TYPE_NET_OPTION_ASSIGN, - parentBeg + equalPos, - parentBeg + valueBeg + parentBeg + matchEnd - 1, + parentBeg + matchEnd ); prev = this.linkRight(prev, next); - if ( (equalPos+1) === optionEnd ) { - this.addNodeFlags(parent, NODE_FLAG_ERROR); - this.addFlags(AST_FLAG_HAS_ERROR); - return this.throwHeadNode(head); - } this.addNodeFlags(parent, NODE_FLAG_OPTION_HAS_VALUE); + const details = this.netOptionValueParser.nextArg(s, matchEnd); + if ( details.quoteBeg !== details.argBeg ) { + next = this.allocTypedNode( + NODE_TYPE_NET_OPTION_QUOTE, + parentBeg + details.quoteBeg, + parentBeg + details.argBeg + ); + prev = this.linkRight(prev, next); + } else { + const argEnd = this.endOfNetOption(s, matchEnd); + if ( argEnd !== details.argEnd ) { + details.argEnd = details.quoteEnd = argEnd; + } + } next = this.allocTypedNode( NODE_TYPE_NET_OPTION_VALUE, - parentBeg + valueBeg, - parentBeg + optionEnd + parentBeg + details.argBeg, + parentBeg + details.argEnd ); + if ( details.argBeg === details.argEnd ) { + this.addNodeFlags(parent, NODE_FLAG_ERROR); + this.addFlags(AST_FLAG_HAS_ERROR); + this.astError = AST_ERROR_OPTION_BADVALUE; + } else if ( details.transform ) { + const arg = s.slice(details.argBeg, details.argEnd); + this.setNodeTransform(next, this.netOptionValueParser.normalizeArg(arg)); + } switch ( nodeOptionType ) { case NODE_TYPE_NET_OPTION_NAME_DENYALLOW: this.linkDown(next, this.parseDomainList(next, '|'), 0b00000); @@ -2069,8 +2091,21 @@ export class AstFilterParser { default: break; } - this.linkRight(prev, next); - return this.throwHeadNode(head); + prev = this.linkRight(prev, next); + if ( details.quoteEnd !== details.argEnd ) { + next = this.allocTypedNode( + NODE_TYPE_NET_OPTION_QUOTE, + parentBeg + details.argEnd, + parentBeg + details.quoteEnd + ); + this.linkRight(prev, next); + } + return { node: this.throwHeadNode(head), len: details.quoteEnd }; + } + + endOfNetOption(s, beg) { + const match = this.reNetOptionComma.exec(s.slice(beg)); + return match !== null ? beg + match.index : s.length; } getNetOptionValue(type) { @@ -3086,8 +3121,8 @@ export const netOptionTokenDescriptors = new Map([ /* synonym */ [ 'rewrite', { mustAssign: true } ], [ 'redirect-rule', { mustAssign: true } ], [ 'removeparam', { } ], - [ 'replace', { mustAssign: true } ], /* synonym */ [ 'queryprune', { } ], + [ 'replace', { mustAssign: true } ], [ 'script', { canNegate: true } ], [ 'shide', { } ], /* synonym */ [ 'specifichide', { } ], diff --git a/src/js/static-net-filtering.js b/src/js/static-net-filtering.js index f30af31bea003..eaa2c7131bede 100644 --- a/src/js/static-net-filtering.js +++ b/src/js/static-net-filtering.js @@ -409,6 +409,14 @@ class LogData { isPureHostname() { return this.tokenHash === DOT_TOKEN_HASH; } + + static requote(s) { + if ( /^(["'`]).+\1$|,/.test(s) === false ) { return s; } + if ( s.includes("'") === false ) { return `'${s}'`; } + if ( s.includes('"') === false ) { return `"${s}"`; } + if ( s.includes('`') === false ) { return `\`${s}\``; } + return `'${s.replace(/'/g, "\\'")}'`; + } } /******************************************************************************/ @@ -2128,7 +2136,7 @@ class FilterModifier { let opt = modifierNameFromType.get(filterData[idata+2]); const refs = filterRefs[filterData[idata+3]]; if ( refs.value !== '' ) { - opt += `=${refs.value}`; + opt += `=${LogData.requote(refs.value)}`; } details.options.push(opt); } @@ -2947,7 +2955,7 @@ class FilterOnHeaders { const headerOpt = filterRefs[irefs].headerOpt; let opt = 'header'; if ( headerOpt !== '' ) { - opt += `=${headerOpt}`; + opt += `=${LogData.requote(headerOpt)}`; } details.options.push(opt); } From c6dedd253ffbf649729a38998033c75993489859 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Mon, 9 Sep 2024 09:35:23 -0400 Subject: [PATCH 0191/1099] New static network filter option `ipaddress=` The purpose is to block according to the ip address of a network request. In the current implementation, the filter option can only be enforced at onHeadersReceived time. The new filter option cannot be enforced in Chromium-based browsers since the ip address of network requests is available only at onResponseStarted time, which is not blocking. The value assigned to `ipaddress` can either be a plain string which must match exactly a given ip address, or a regex which will be matched against the ip address. The `ipaddress` option can only be enforced when the extension framework does provide a valid ip address in a onHeadersReceived listener. For instance, cached resources do not have a valid ip address and thus can't be a match to `ipaddress` option. Example: *$script,ipaddress=93.184.215.14 --- src/js/background.js | 1 + src/js/filtering-context.js | 109 ++++++++++++-------- src/js/static-filtering-parser.js | 19 +++- src/js/static-net-filtering.js | 165 +++++++++++++++++++----------- 4 files changed, 194 insertions(+), 100 deletions(-) diff --git a/src/js/background.js b/src/js/background.js index 4cd10eeff4a1b..2a5c2366e2982 100644 --- a/src/js/background.js +++ b/src/js/background.js @@ -307,6 +307,7 @@ const µBlock = { // jshint ignore:line this.setMethod(details.method); this.setURL(details.url); this.aliasURL = details.aliasURL || undefined; + this.ipaddress = details.ip || undefined; this.redirectURL = undefined; this.filter = undefined; if ( this.itype !== this.SUB_FRAME ) { diff --git a/src/js/filtering-context.js b/src/js/filtering-context.js index 3da9d158f0a4c..b1d7d7033dac5 100644 --- a/src/js/filtering-context.js +++ b/src/js/filtering-context.js @@ -19,13 +19,9 @@ Home: https://github.com/gorhill/uBlock */ -'use strict'; - -/******************************************************************************/ - import { - hostnameFromURI, domainFromHostname, + hostnameFromURI, originFromURI, } from './uri-utils.js'; @@ -140,6 +136,7 @@ export const FilteringContext = class { this.stype = undefined; this.url = undefined; this.aliasURL = undefined; + this.ipaddress = undefined; this.hostname = undefined; this.domain = undefined; this.docId = -1; @@ -418,42 +415,72 @@ export const FilteringContext = class { static getMethodName(a) { return methodBitToStrMap.get(a) || ''; } -}; - -/******************************************************************************/ -FilteringContext.prototype.BEACON = FilteringContext.BEACON = BEACON; -FilteringContext.prototype.CSP_REPORT = FilteringContext.CSP_REPORT = CSP_REPORT; -FilteringContext.prototype.FONT = FilteringContext.FONT = FONT; -FilteringContext.prototype.IMAGE = FilteringContext.IMAGE = IMAGE; -FilteringContext.prototype.IMAGESET = FilteringContext.IMAGESET = IMAGESET; -FilteringContext.prototype.MAIN_FRAME = FilteringContext.MAIN_FRAME = MAIN_FRAME; -FilteringContext.prototype.MEDIA = FilteringContext.MEDIA = MEDIA; -FilteringContext.prototype.OBJECT = FilteringContext.OBJECT = OBJECT; -FilteringContext.prototype.OBJECT_SUBREQUEST = FilteringContext.OBJECT_SUBREQUEST = OBJECT_SUBREQUEST; -FilteringContext.prototype.PING = FilteringContext.PING = PING; -FilteringContext.prototype.SCRIPT = FilteringContext.SCRIPT = SCRIPT; -FilteringContext.prototype.STYLESHEET = FilteringContext.STYLESHEET = STYLESHEET; -FilteringContext.prototype.SUB_FRAME = FilteringContext.SUB_FRAME = SUB_FRAME; -FilteringContext.prototype.WEBSOCKET = FilteringContext.WEBSOCKET = WEBSOCKET; -FilteringContext.prototype.XMLHTTPREQUEST = FilteringContext.XMLHTTPREQUEST = XMLHTTPREQUEST; -FilteringContext.prototype.INLINE_FONT = FilteringContext.INLINE_FONT = INLINE_FONT; -FilteringContext.prototype.INLINE_SCRIPT = FilteringContext.INLINE_SCRIPT = INLINE_SCRIPT; -FilteringContext.prototype.OTHER = FilteringContext.OTHER = OTHER; -FilteringContext.prototype.FRAME_ANY = FilteringContext.FRAME_ANY = FRAME_ANY; -FilteringContext.prototype.FONT_ANY = FilteringContext.FONT_ANY = FONT_ANY; -FilteringContext.prototype.INLINE_ANY = FilteringContext.INLINE_ANY = INLINE_ANY; -FilteringContext.prototype.PING_ANY = FilteringContext.PING_ANY = PING_ANY; -FilteringContext.prototype.SCRIPT_ANY = FilteringContext.SCRIPT_ANY = SCRIPT_ANY; - -FilteringContext.prototype.METHOD_NONE = FilteringContext.METHOD_NONE = METHOD_NONE; -FilteringContext.prototype.METHOD_CONNECT = FilteringContext.METHOD_CONNECT = METHOD_CONNECT; -FilteringContext.prototype.METHOD_DELETE = FilteringContext.METHOD_DELETE = METHOD_DELETE; -FilteringContext.prototype.METHOD_GET = FilteringContext.METHOD_GET = METHOD_GET; -FilteringContext.prototype.METHOD_HEAD = FilteringContext.METHOD_HEAD = METHOD_HEAD; -FilteringContext.prototype.METHOD_OPTIONS = FilteringContext.METHOD_OPTIONS = METHOD_OPTIONS; -FilteringContext.prototype.METHOD_PATCH = FilteringContext.METHOD_PATCH = METHOD_PATCH; -FilteringContext.prototype.METHOD_POST = FilteringContext.METHOD_POST = METHOD_POST; -FilteringContext.prototype.METHOD_PUT = FilteringContext.METHOD_PUT = METHOD_PUT; + BEACON = BEACON; + CSP_REPORT = CSP_REPORT; + FONT = FONT; + IMAGE = IMAGE; + IMAGESET = IMAGESET; + MAIN_FRAME = MAIN_FRAME; + MEDIA = MEDIA; + OBJECT = OBJECT; + OBJECT_SUBREQUEST = OBJECT_SUBREQUEST; + PING = PING; + SCRIPT = SCRIPT; + STYLESHEET = STYLESHEET; + SUB_FRAME = SUB_FRAME; + WEBSOCKET = WEBSOCKET; + XMLHTTPREQUEST = XMLHTTPREQUEST; + INLINE_FONT = INLINE_FONT; + INLINE_SCRIPT = INLINE_SCRIPT; + OTHER = OTHER; + FRAME_ANY = FRAME_ANY; + FONT_ANY = FONT_ANY; + INLINE_ANY = INLINE_ANY; + PING_ANY = PING_ANY; + SCRIPT_ANY = SCRIPT_ANY; + METHOD_NONE = METHOD_NONE; + METHOD_CONNECT = METHOD_CONNECT; + METHOD_DELETE = METHOD_DELETE; + METHOD_GET = METHOD_GET; + METHOD_HEAD = METHOD_HEAD; + METHOD_OPTIONS = METHOD_OPTIONS; + METHOD_PATCH = METHOD_PATCH; + METHOD_POST = METHOD_POST; + METHOD_PUT = METHOD_PUT; + + static BEACON = BEACON; + static CSP_REPORT = CSP_REPORT; + static FONT = FONT; + static IMAGE = IMAGE; + static IMAGESET = IMAGESET; + static MAIN_FRAME = MAIN_FRAME; + static MEDIA = MEDIA; + static OBJECT = OBJECT; + static OBJECT_SUBREQUEST = OBJECT_SUBREQUEST; + static PING = PING; + static SCRIPT = SCRIPT; + static STYLESHEET = STYLESHEET; + static SUB_FRAME = SUB_FRAME; + static WEBSOCKET = WEBSOCKET; + static XMLHTTPREQUEST = XMLHTTPREQUEST; + static INLINE_FONT = INLINE_FONT; + static INLINE_SCRIPT = INLINE_SCRIPT; + static OTHER = OTHER; + static FRAME_ANY = FRAME_ANY; + static FONT_ANY = FONT_ANY; + static INLINE_ANY = INLINE_ANY; + static PING_ANY = PING_ANY; + static SCRIPT_ANY = SCRIPT_ANY; + static METHOD_NONE = METHOD_NONE; + static METHOD_CONNECT = METHOD_CONNECT; + static METHOD_DELETE = METHOD_DELETE; + static METHOD_GET = METHOD_GET; + static METHOD_HEAD = METHOD_HEAD; + static METHOD_OPTIONS = METHOD_OPTIONS; + static METHOD_PATCH = METHOD_PATCH; + static METHOD_POST = METHOD_POST; + static METHOD_PUT = METHOD_PUT; +}; /******************************************************************************/ diff --git a/src/js/static-filtering-parser.js b/src/js/static-filtering-parser.js index b0a4a15d1a87b..d3520b6e555c3 100644 --- a/src/js/static-filtering-parser.js +++ b/src/js/static-filtering-parser.js @@ -172,6 +172,7 @@ export const NODE_TYPE_NET_OPTION_NAME_IMAGE = iota++; export const NODE_TYPE_NET_OPTION_NAME_IMPORTANT = iota++; export const NODE_TYPE_NET_OPTION_NAME_INLINEFONT = iota++; export const NODE_TYPE_NET_OPTION_NAME_INLINESCRIPT = iota++; +export const NODE_TYPE_NET_OPTION_NAME_IPADDRESS = iota++; export const NODE_TYPE_NET_OPTION_NAME_MATCHCASE = iota++; export const NODE_TYPE_NET_OPTION_NAME_MEDIA = iota++; export const NODE_TYPE_NET_OPTION_NAME_METHOD = iota++; @@ -249,6 +250,7 @@ export const nodeTypeFromOptionName = new Map([ [ 'important', NODE_TYPE_NET_OPTION_NAME_IMPORTANT ], [ 'inline-font', NODE_TYPE_NET_OPTION_NAME_INLINEFONT ], [ 'inline-script', NODE_TYPE_NET_OPTION_NAME_INLINESCRIPT ], + [ 'ipaddress', NODE_TYPE_NET_OPTION_NAME_IPADDRESS ], [ 'match-case', NODE_TYPE_NET_OPTION_NAME_MATCHCASE ], [ 'media', NODE_TYPE_NET_OPTION_NAME_MEDIA ], [ 'method', NODE_TYPE_NET_OPTION_NAME_METHOD ], @@ -1401,6 +1403,14 @@ export class AstFilterParser { modifierType = type; unredirectableTypeCount += 1; break; + case NODE_TYPE_NET_OPTION_NAME_IPADDRESS: { + const value = this.getNetOptionValue(NODE_TYPE_NET_OPTION_NAME_IPADDRESS); + if ( /^\/.+\/$/.test(value) ) { + try { void new RegExp(value); } + catch(_) { realBad = true; } + } + break; + } case NODE_TYPE_NET_OPTION_NAME_MATCHCASE: realBad = this.isRegexPattern() === false; break; @@ -3104,6 +3114,7 @@ export const netOptionTokenDescriptors = new Map([ [ 'important', { blockOnly: true } ], [ 'inline-font', { canNegate: true } ], [ 'inline-script', { canNegate: true } ], + [ 'ipaddress', { mustAssign: true } ], [ 'match-case', { } ], [ 'media', { canNegate: true } ], [ 'method', { mustAssign: true } ], @@ -4324,6 +4335,7 @@ export const utils = (( ) => { [ 'env_safari', 'safari' ], [ 'cap_html_filtering', 'html_filtering' ], [ 'cap_user_stylesheet', 'user_stylesheet' ], + [ 'cap_ipaddress', 'ipaddress' ], [ 'false', 'false' ], // Hoping ABP-only list maintainers can at least make use of it to // help non-ABP content blockers better deal with filters benefiting @@ -4358,8 +4370,11 @@ export const utils = (( ) => { static evaluateExprToken(token, env = []) { const not = token.charCodeAt(0) === 0x21 /* ! */; if ( not ) { token = token.slice(1); } - const state = preparserTokens.get(token); - if ( state === undefined ) { return; } + let state = preparserTokens.get(token); + if ( state === undefined ) { + if ( token.startsWith('cap_') === false ) { return; } + state = 'false'; + } return state === 'false' && not || env.includes(state) !== not; } diff --git a/src/js/static-net-filtering.js b/src/js/static-net-filtering.js index eaa2c7131bede..e8f6de911dd6d 100644 --- a/src/js/static-net-filtering.js +++ b/src/js/static-net-filtering.js @@ -242,6 +242,7 @@ let $requestTypeValue = 0; let $requestURL = ''; let $requestURLRaw = ''; let $requestHostname = ''; +let $requestAddress = ''; let $docHostname = ''; let $docDomain = ''; let $tokenBeg = 0; @@ -702,6 +703,8 @@ const dnrAddRuleWarning = (rule, msg) => { FilterNotType FilterStrictParty FilterModifier + FilterOnHeaders + FilterIPAddress Collection: FilterCollection @@ -1234,7 +1237,7 @@ class FilterRegex { return [ FilterRegex.fid, details.pattern, - details.patternMatchCase ? 1 : 0 + details.optionValues.has('match-case') ? 1 : 0, ]; } @@ -2075,7 +2078,7 @@ const compileToDomainOpt = (...args) => { class FilterDenyAllow extends FilterToDomainMissSet { static compile(details) { - return super.compile(details.denyallowOpt, 0b01); + return super.compile(details.optionValues.get('denyallow'), 0b01); } static logData(idata, details) { @@ -2937,12 +2940,12 @@ class FilterOnHeaders { } static compile(details) { - return [ FilterOnHeaders.fid, details.headerOpt ]; + return [ FilterOnHeaders.fid, details.optionValues.get('header') ]; } static fromCompiled(args) { return filterDataAlloc( - args[0], // fid + args[0], // fid filterRefAdd({ headerOpt: args[1], $parsed: null, @@ -2963,6 +2966,41 @@ class FilterOnHeaders { registerFilterClass(FilterOnHeaders); +/******************************************************************************/ + +class FilterIPAddress { + static match(idata) { + const details = filterRefs[filterData[idata+1]]; + if ( details.isRegex === false ) { + return $requestAddress === details.pattern; + } + if ( details.$re === undefined ) { + details.$re = new RegExp(details.pattern.slice(1, -1)); + } + return details.$re.test($requestAddress); + } + + static compile(details) { + return [ FilterIPAddress.fid, details.optionValues.get('ipaddress') ]; + } + + static fromCompiled(args) { + const pattern = args[1]; + const details = { + pattern, + isRegex: pattern.startsWith('/') && pattern.endsWith('/'), + }; + return filterDataAlloc(args[0], filterRefAdd(details)); + } + + static logData(idata, details) { + const irefs = filterData[idata+1]; + details.options.push(`ipaddress=${LogData.requote(filterRefs[irefs].pattern)}`); + } +} + +registerFilterClass(FilterIPAddress); + /******************************************************************************/ /******************************************************************************/ @@ -3146,8 +3184,7 @@ class FilterCompiler { return Object.assign(this, other); } this.reToken = /[%0-9A-Za-z]+/g; - this.fromDomainOptList = []; - this.toDomainOptList = []; + this.optionValues = new Map(); this.tokenIdToNormalizedType = new Map([ [ sfp.NODE_TYPE_NET_OPTION_NAME_CNAME, bitFromType('cname') ], [ sfp.NODE_TYPE_NET_OPTION_NAME_CSS, bitFromType('stylesheet') ], @@ -3304,13 +3341,9 @@ class FilterCompiler { this.modifyType = undefined; this.modifyValue = undefined; this.pattern = ''; - this.patternMatchCase = false; this.party = ANYPARTY_REALM; this.optionUnitBits = 0; - this.fromDomainOpt = ''; - this.toDomainOpt = ''; - this.denyallowOpt = ''; - this.headerOpt = undefined; + this.optionValues.clear(); this.isPureHostname = false; this.isGeneric = false; this.isRegex = false; @@ -3322,8 +3355,7 @@ class FilterCompiler { this.notTypeBits = 0; this.methodBits = 0; this.notMethodBits = 0; - this.wildcardPos = -1; - this.caretPos = -1; + this.responseHeadersRealm = false; return this; } @@ -3421,26 +3453,36 @@ class FilterCompiler { case sfp.NODE_TYPE_NET_OPTION_NAME_CSP: if ( this.processCspOption(parser.getNetOptionValue(id)) === false ) { return false; } break; - case sfp.NODE_TYPE_NET_OPTION_NAME_DENYALLOW: - this.denyallowOpt = this.processHostnameList( - parser.getNetFilterDenyallowOptionIterator(), + case sfp.NODE_TYPE_NET_OPTION_NAME_DENYALLOW: { + const value = this.processHostnameList( + parser.getNetFilterDenyallowOptionIterator() ); - if ( this.denyallowOpt === '' ) { return false; } + if ( value === '' ) { return false; } + this.optionValues.set('denyallow', value); this.optionUnitBits |= DENYALLOW_BIT; break; - case sfp.NODE_TYPE_NET_OPTION_NAME_FROM: - this.fromDomainOpt = this.processHostnameList( - parser.getNetFilterFromOptionIterator(), - this.fromDomainOptList - ); - if ( this.fromDomainOpt === '' ) { return false; } + } + case sfp.NODE_TYPE_NET_OPTION_NAME_FROM: { + const iter = parser.getNetFilterFromOptionIterator(); + const list = []; + const value = this.processHostnameList(iter, list); + if ( value === '' ) { return false; } + this.optionValues.set('from', value); + this.optionValues.set('fromList', list); this.optionUnitBits |= FROM_BIT; break; + } case sfp.NODE_TYPE_NET_OPTION_NAME_HEADER: { - this.headerOpt = parser.getNetOptionValue(id) || ''; + this.optionValues.set('header', parser.getNetOptionValue(id) || ''); this.optionUnitBits |= HEADER_BIT; + this.responseHeadersRealm = true; break; } + case sfp.NODE_TYPE_NET_OPTION_NAME_IPADDRESS: + this.optionValues.set('ipaddress', parser.getNetOptionValue(id) || ''); + this.optionUnitBits |= IPADDRESS_BIT; + this.responseHeadersRealm = true; + break; case sfp.NODE_TYPE_NET_OPTION_NAME_METHOD: this.processMethodOption(parser.getNetOptionValue(id)); this.optionUnitBits |= METHOD_BIT; @@ -3465,14 +3507,16 @@ class FilterCompiler { this.optionUnitBits |= MODIFY_BIT; break; } - case sfp.NODE_TYPE_NET_OPTION_NAME_TO: - this.toDomainOpt = this.processHostnameList( - parser.getNetFilterToOptionIterator(), - this.toDomainOptList - ); - if ( this.toDomainOpt === '' ) { return false; } + case sfp.NODE_TYPE_NET_OPTION_NAME_TO: { + const iter = parser.getNetFilterToOptionIterator(); + const list = []; + const value = this.processHostnameList(iter, list); + if ( value === '' ) { return false; } + this.optionValues.set('to', value); + this.optionValues.set('toList', list); this.optionUnitBits |= TO_BIT; break; + } default: break; } @@ -3558,6 +3602,7 @@ class FilterCompiler { case sfp.NODE_TYPE_NET_OPTION_NAME_DENYALLOW: case sfp.NODE_TYPE_NET_OPTION_NAME_FROM: case sfp.NODE_TYPE_NET_OPTION_NAME_HEADER: + case sfp.NODE_TYPE_NET_OPTION_NAME_IPADDRESS: case sfp.NODE_TYPE_NET_OPTION_NAME_METHOD: case sfp.NODE_TYPE_NET_OPTION_NAME_PERMISSIONS: case sfp.NODE_TYPE_NET_OPTION_NAME_REDIRECT: @@ -3591,7 +3636,7 @@ class FilterCompiler { this.action = BLOCKIMPORTANT_REALM; break; case sfp.NODE_TYPE_NET_OPTION_NAME_MATCHCASE: - this.patternMatchCase = true; + this.optionValues.set('match-case', true); break; case sfp.NODE_TYPE_NET_OPTION_NAME_MP4: { const id = this.action === ALLOW_REALM @@ -3661,11 +3706,6 @@ class FilterCompiler { return this.FILTER_OK; } - if ( this.isGeneric ) { - this.wildcardPos = this.pattern.indexOf('*'); - this.caretPos = this.pattern.indexOf('^'); - } - if ( this.pattern.length > 1024 ) { return this.FILTER_UNSUPPORTED; } @@ -3793,7 +3833,7 @@ class FilterCompiler { isJustOrigin() { if ( this.optionUnitBits !== FROM_BIT ) { return false; } if ( this.isRegex ) { return false; } - if ( /[/~]/.test(this.fromDomainOpt) ) { return false; } + if ( /[/~]/.test(this.optionValues.get('from')) ) { return false; } if ( this.pattern === '*' ) { return true; } if ( this.anchor !== 0b010 ) { return false; } if ( /^(?:http[s*]?:(?:\/\/)?)$/.test(this.pattern) ) { return true; } @@ -3870,7 +3910,7 @@ class FilterCompiler { } else /* 'http:' */ { this.tokenHash = ANY_HTTP_TOKEN_HASH; } - for ( const hn of this.fromDomainOptList ) { + for ( const hn of this.optionValues.get('fromList') ) { this.compileToAtomicFilter(hn, writer); } return; @@ -3911,31 +3951,36 @@ class FilterCompiler { } // Origin - if ( this.fromDomainOpt !== '' ) { + if ( this.optionValues.has('from') ) { compileFromDomainOpt( - this.fromDomainOptList, + this.optionValues.get('fromList'), units.length !== 0 && patternClass.isSlow === true, units ); } // Destination - if ( this.toDomainOpt !== '' ) { + if ( this.optionValues.has('to') ) { compileToDomainOpt( - this.toDomainOptList, + this.optionValues.get('toList'), units.length !== 0 && patternClass.isSlow === true, units ); } // Deny-allow - if ( this.denyallowOpt !== '' ) { + if ( this.optionValues.has('denyallow') ) { units.push(FilterDenyAllow.compile(this)); } // Header - if ( this.headerOpt !== undefined ) { - units.push(FilterOnHeaders.compile(this)); + if ( this.responseHeadersRealm ) { + if ( this.optionValues.has('ipaddress') ) { + units.push(FilterIPAddress.compile(this)); + } + if ( this.optionValues.has('header') ) { + units.push(FilterOnHeaders.compile(this)); + } this.action |= HEADERS_REALM; } @@ -3977,12 +4022,13 @@ class FilterCompiler { units.push(FilterPatternGeneric.compile(this)); return FilterPatternGeneric; } - if ( this.wildcardPos === -1 ) { - if ( this.caretPos === -1 ) { + if ( this.pattern.includes('*') === false ) { + const caretPos = this.pattern.indexOf('^'); + if ( caretPos === -1 ) { units.push(FilterPatternPlain.compile(this)); return FilterPatternPlain; } - if ( this.caretPos === (this.pattern.length - 1) ) { + if ( caretPos === (this.pattern.length - 1) ) { this.pattern = this.pattern.slice(0, -1); units.push(FilterPatternPlain.compile(this)); units.push(FilterTrailingSeparator.compile()); @@ -4027,15 +4073,16 @@ class FilterCompiler { } // These are to quickly test whether a filter is composite -const FROM_BIT = 0b000000001; -const TO_BIT = 0b000000010; -const DENYALLOW_BIT = 0b000000100; -const HEADER_BIT = 0b000001000; -const STRICT_PARTY_BIT = 0b000010000; -const MODIFY_BIT = 0b000100000; -const NOT_TYPE_BIT = 0b001000000; -const IMPORTANT_BIT = 0b010000000; -const METHOD_BIT = 0b100000000; +const FROM_BIT = 0b0000000001; +const TO_BIT = 0b0000000010; +const DENYALLOW_BIT = 0b0000000100; +const HEADER_BIT = 0b0000001000; +const STRICT_PARTY_BIT = 0b0000010000; +const MODIFY_BIT = 0b0000100000; +const NOT_TYPE_BIT = 0b0001000000; +const IMPORTANT_BIT = 0b0010000000; +const METHOD_BIT = 0b0100000000; +const IPADDRESS_BIT = 0b1000000000; FilterCompiler.prototype.FILTER_OK = 0; FilterCompiler.prototype.FILTER_INVALID = 1; @@ -4751,6 +4798,7 @@ StaticNetFilteringEngine.prototype.matchAndFetchModifiers = function( $requestHostname = fctxt.getHostname(); $requestMethodBit = fctxt.method || 0; $requestTypeValue = (typeBits & TypeBitsMask) >>> TypeBitsOffset; + $requestAddress = fctxt.ipaddress || ''; const modifierType = modifierTypeFromName.get(modifierName); const modifierBits = modifierBitsFromType.get(modifierType); @@ -5048,6 +5096,7 @@ StaticNetFilteringEngine.prototype.matchRequestReverse = function(type, url) { $requestURLRaw = url; $requestMethodBit = 0; $requestTypeValue = (typeBits & TypeBitsMask) >>> TypeBitsOffset; + $requestAddress = ''; $isBlockImportant = false; this.$filterUnit = 0; @@ -5116,6 +5165,7 @@ StaticNetFilteringEngine.prototype.matchRequest = function(fctxt, modifiers = 0) $requestHostname = fctxt.getHostname(); $requestMethodBit = fctxt.method || 0; $requestTypeValue = (typeBits & TypeBitsMask) >>> TypeBitsOffset; + $requestAddress = fctxt.ipaddress || ''; $isBlockImportant = false; // Evaluate block realm before allow realm, and allow realm before @@ -5151,6 +5201,7 @@ StaticNetFilteringEngine.prototype.matchHeaders = function(fctxt, headers) { $requestHostname = fctxt.getHostname(); $requestMethodBit = fctxt.method || 0; $requestTypeValue = (typeBits & TypeBitsMask) >>> TypeBitsOffset; + $requestAddress = fctxt.ipaddress || ''; $httpHeaders.init(headers); let r = 0; From 2cb2ee84469febffc61256910ffdf64410bd53a4 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Mon, 9 Sep 2024 09:51:24 -0400 Subject: [PATCH 0192/1099] Update changelog --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 572ee92009721..66d42b2fa9124 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,5 @@ +- [New static network filter option `ipaddress=`](https://github.com/gorhill/uBlock/commit/c6dedd253f) +- [Add ability to quote static network option values](https://github.com/gorhill/uBlock/commit/20115697e5) - [Improve `prevent-fetch` scriptlet](https://github.com/gorhill/uBlock/commit/e8202af11d) - [Apply CSP/PP injections to `object` resources](https://github.com/gorhill/uBlock/commit/89f02098fd) - [Improve `xml-prune` scriptlet](https://github.com/gorhill/uBlock/commit/c8307f58a3) From 7732df1dbda4a65bb330763f4679ffb45d0298f9 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Mon, 9 Sep 2024 09:51:45 -0400 Subject: [PATCH 0193/1099] New revision for dev build --- dist/version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dist/version b/dist/version index 4a1a7010ab022..b283f756e5297 100644 --- a/dist/version +++ b/dist/version @@ -1 +1 @@ -1.59.1.14 \ No newline at end of file +1.59.1.15 \ No newline at end of file From 41c96690a5168dcbe4bbb637626a6555f2c32be8 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Mon, 9 Sep 2024 10:06:34 -0400 Subject: [PATCH 0194/1099] Make Firefox dev build auto-update --- dist/firefox/updates.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dist/firefox/updates.json b/dist/firefox/updates.json index 89e8f3d91f137..04ab41e136b83 100644 --- a/dist/firefox/updates.json +++ b/dist/firefox/updates.json @@ -3,9 +3,9 @@ "uBlock0@raymondhill.net": { "updates": [ { - "version": "1.59.1.14", + "version": "1.59.1.15", "browser_specific_settings": { "gecko": { "strict_min_version": "78.0" } }, - "update_link": "https://github.com/gorhill/uBlock/releases/download/1.59.1b14/uBlock0_1.59.1b14.firefox.signed.xpi" + "update_link": "https://github.com/gorhill/uBlock/releases/download/1.59.1b15/uBlock0_1.59.1b15.firefox.signed.xpi" } ] } From 52dee3532525fe6ec6b8d773ae10023f681c9cd2 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Mon, 9 Sep 2024 10:54:47 -0400 Subject: [PATCH 0195/1099] Properly reflect whether `ipaddress=` is supported --- platform/common/vapi-common.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/platform/common/vapi-common.js b/platform/common/vapi-common.js index b2b047bc05a8b..367def90a7b7f 100644 --- a/platform/common/vapi-common.js +++ b/platform/common/vapi-common.js @@ -181,7 +181,8 @@ vAPI.webextFlavor = { if ( browser.runtime.getURL('').startsWith('moz-extension://') ) { soup.add('firefox') .add('user_stylesheet') - .add('html_filtering'); + .add('html_filtering') + .add('ipaddress'); const match = /Firefox\/(\d+)/.exec(ua); flavor.major = match && parseInt(match[1], 10) || 115; } else { From faf1b15f8d3d7bf20d65b85b410ce64f1b21d1ad Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Mon, 9 Sep 2024 10:55:57 -0400 Subject: [PATCH 0196/1099] New revision for dev build --- dist/version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dist/version b/dist/version index b283f756e5297..0e42031b3d95e 100644 --- a/dist/version +++ b/dist/version @@ -1 +1 @@ -1.59.1.15 \ No newline at end of file +1.59.1.16 \ No newline at end of file From 839857dd4b92620f56e5ca2da4deacf8b31e812e Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Mon, 9 Sep 2024 11:15:41 -0400 Subject: [PATCH 0197/1099] Make Firefox dev build auto-update --- dist/firefox/updates.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dist/firefox/updates.json b/dist/firefox/updates.json index 04ab41e136b83..7603b4da35ca4 100644 --- a/dist/firefox/updates.json +++ b/dist/firefox/updates.json @@ -3,9 +3,9 @@ "uBlock0@raymondhill.net": { "updates": [ { - "version": "1.59.1.15", + "version": "1.59.1.16", "browser_specific_settings": { "gecko": { "strict_min_version": "78.0" } }, - "update_link": "https://github.com/gorhill/uBlock/releases/download/1.59.1b15/uBlock0_1.59.1b15.firefox.signed.xpi" + "update_link": "https://github.com/gorhill/uBlock/releases/download/1.59.1b16/uBlock0_1.59.1b16.firefox.signed.xpi" } ] } From 030d7334e4ad832441178ecc5161e8caa58666a6 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Tue, 10 Sep 2024 11:11:11 -0400 Subject: [PATCH 0198/1099] Add support for `lan`/`loopback` values to `ipaddress=` option Related issue: https://github.com/uBlockOrigin/uBlock-issues/issues/1070 --- src/js/static-net-filtering.js | 59 +++++++++++++++++++++++++++++++--- 1 file changed, 54 insertions(+), 5 deletions(-) diff --git a/src/js/static-net-filtering.js b/src/js/static-net-filtering.js index e8f6de911dd6d..967e8768756b0 100644 --- a/src/js/static-net-filtering.js +++ b/src/js/static-net-filtering.js @@ -2970,14 +2970,63 @@ registerFilterClass(FilterOnHeaders); class FilterIPAddress { static match(idata) { + const ipaddr = $requestAddress; const details = filterRefs[filterData[idata+1]]; - if ( details.isRegex === false ) { - return $requestAddress === details.pattern; + if ( details.isRegex ) { + if ( details.$re === undefined ) { + details.$re = new RegExp(details.pattern.slice(1, -1)); + } + return details.$re.test(ipaddr); + } + if ( ipaddr === '' ) { return false; } + if ( details.pattern === 'lan' ) { + return this.isLAN(ipaddr); + } + if ( details.pattern === 'loopback' ) { + return this.isLoopback(ipaddr); + } + return ipaddr.startsWith(details.pattern); + } + + // https://github.com/uBlockOrigin/uAssets/blob/master/filters/lan-block.txt + // https://en.wikipedia.org/wiki/Reserved_IP_addresses + // `ipaddr` is assumed well-formed + static isLAN(ipaddr) { + const c0 = ipaddr.charCodeAt(0); + // ipv4 + if ( c0 === 0x30 /* 0 */ ) { + return ipaddr.startsWith('0.'); + } + if ( c0 === 0x31 /* 1 */ ) { + if ( ipaddr.startsWith('10.') ) { return true; } + if ( ipaddr.startsWith('127.') ) { return true; } + if ( ipaddr.startsWith('169.254.') ) { return true; } + if ( ipaddr.startsWith('172.') ) { + const v = parseInt(ipaddr.slice(4), 10); + return v >= 16 && v <= 31; + } + return ipaddr.startsWith('192.168.'); + } + if ( c0 !== 0x5B /* [ */ ) { return false; } + // ipv6 + const c1 = ipaddr.charCodeAt(1); + if ( c1 === 0x3A /* : */ ) { + if ( ipaddr.startsWith('[::') === false ) { return false; } + if ( ipaddr === '[::]' || ipaddr === '[::1]' ) { return true; } + if ( ipaddr.startsWith('[::ffff:') === false ) { return false; } + return /^\[::ffff:(7f\w{2}|a\w{2}|a9fe|c0a8):\w+\]$/.test(ipaddr); + } + if ( c1 === 0x36 /* 6 */ ) { + return ipaddr.startsWith('[64:ff9b:'); } - if ( details.$re === undefined ) { - details.$re = new RegExp(details.pattern.slice(1, -1)); + if ( c1 === 0x66 /* f */ ) { + return /^\[f[cd]\w{2}:/.test(ipaddr); } - return details.$re.test($requestAddress); + return false; + } + + static isLoopback(ipaddr) { + return ipaddr === '127.0.0.1' || ipaddr === '[::1]'; } static compile(details) { From 401d2e8ea9f31720a1ea05943be37e4275182a17 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Tue, 10 Sep 2024 11:50:09 -0400 Subject: [PATCH 0199/1099] [mv3] Mind unsupported `header=`/`ipaddress` options in DNR API Properly report unsupported `header=` and `ipaddress=` option in log file. `header=` support may become possible soon with Chromium 128 introducing blocking according to response headers content. --- src/js/static-net-filtering.js | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/js/static-net-filtering.js b/src/js/static-net-filtering.js index 967e8768756b0..0523217773114 100644 --- a/src/js/static-net-filtering.js +++ b/src/js/static-net-filtering.js @@ -2905,7 +2905,7 @@ class FilterStrictParty { static dnrFromCompiled(args, rule) { const partyness = args[1] === 0 ? 1 : 3; - dnrAddRuleError(rule, `FilterStrictParty: Strict partyness strict${partyness}p not supported`); + dnrAddRuleError(rule, `strict${partyness}p not supported`); } static keyFromArgs(args) { @@ -2953,6 +2953,10 @@ class FilterOnHeaders { ); } + static dnrFromCompiled(args, rule) { + dnrAddRuleError(rule, `header="${args[1]}" not supported`); + } + static logData(idata, details) { const irefs = filterData[idata+1]; const headerOpt = filterRefs[irefs].headerOpt; @@ -3042,6 +3046,10 @@ class FilterIPAddress { return filterDataAlloc(args[0], filterRefAdd(details)); } + static dnrFromCompiled(args, rule) { + dnrAddRuleError(rule, `"ipaddress=${args[1]}" not supported`); + } + static logData(idata, details) { const irefs = filterData[idata+1]; details.options.push(`ipaddress=${LogData.requote(filterRefs[irefs].pattern)}`); @@ -4432,6 +4440,7 @@ StaticNetFilteringEngine.prototype.dnrFromCompiled = function(op, context, ...ar [ CSP_REALM, { type: 'csp', priority: 0 } ], [ PERMISSIONS_REALM, { type: 'permissions', priority: 0 } ], [ URLTRANSFORM_REALM, { type: 'uritransform', priority: 0 } ], + [ HEADERS_REALM, { type: 'block', priority: 0 } ], ]); const partyness = new Map([ [ ANYPARTY_REALM, '' ], From 185580d23f24ecf3aded75b06ce919fa95960b25 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Tue, 10 Sep 2024 11:55:53 -0400 Subject: [PATCH 0200/1099] Update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 66d42b2fa9124..6a061ab35e6e5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,4 @@ +- [Add support for `lan`/`loopback` values to `ipaddress=` option](https://github.com/gorhill/uBlock/commit/030d7334e4) - [New static network filter option `ipaddress=`](https://github.com/gorhill/uBlock/commit/c6dedd253f) - [Add ability to quote static network option values](https://github.com/gorhill/uBlock/commit/20115697e5) - [Improve `prevent-fetch` scriptlet](https://github.com/gorhill/uBlock/commit/e8202af11d) From 0ae02788b20d22cac0ad7623862c9f54c667e8ff Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Tue, 10 Sep 2024 11:56:13 -0400 Subject: [PATCH 0201/1099] New revision for dev build --- dist/version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dist/version b/dist/version index 0e42031b3d95e..31b41e046d12c 100644 --- a/dist/version +++ b/dist/version @@ -1 +1 @@ -1.59.1.16 \ No newline at end of file +1.59.1.17 \ No newline at end of file From 09ccfc8cfb29cc84c4a26d2e1b39f017f36e46eb Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Tue, 10 Sep 2024 12:11:03 -0400 Subject: [PATCH 0202/1099] Make Firefox dev build auto-update --- dist/firefox/updates.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dist/firefox/updates.json b/dist/firefox/updates.json index 7603b4da35ca4..31eb215b13f42 100644 --- a/dist/firefox/updates.json +++ b/dist/firefox/updates.json @@ -3,9 +3,9 @@ "uBlock0@raymondhill.net": { "updates": [ { - "version": "1.59.1.16", + "version": "1.59.1.17", "browser_specific_settings": { "gecko": { "strict_min_version": "78.0" } }, - "update_link": "https://github.com/gorhill/uBlock/releases/download/1.59.1b16/uBlock0_1.59.1b16.firefox.signed.xpi" + "update_link": "https://github.com/gorhill/uBlock/releases/download/1.59.1b17/uBlock0_1.59.1b17.firefox.signed.xpi" } ] } From d5f14ffa32a3b0ae3662bd8067dc937dc116d2cc Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Tue, 10 Sep 2024 14:58:40 -0400 Subject: [PATCH 0203/1099] Avoid using dns.resolve() for proxied DNS resolution Related issue: https://github.com/uBlockOrigin/uBlock-issues/issues/1743 --- platform/firefox/vapi-background-ext.js | 47 ++----------------------- src/js/background.js | 1 - src/js/storage.js | 1 - 3 files changed, 2 insertions(+), 47 deletions(-) diff --git a/platform/firefox/vapi-background-ext.js b/platform/firefox/vapi-background-ext.js index 8ecefc9a8e197..1bb15d38b30e2 100644 --- a/platform/firefox/vapi-background-ext.js +++ b/platform/firefox/vapi-background-ext.js @@ -19,12 +19,6 @@ Home: https://github.com/gorhill/uBlock */ -/* globals browser */ - -'use strict'; - -/******************************************************************************/ - import { domainFromHostname, hostnameFromNetworkURL, @@ -34,24 +28,6 @@ import { // Canonical name-uncloaking feature. let cnameUncloakEnabled = browser.dns instanceof Object; -let cnameUncloakProxied = false; - -// https://github.com/uBlockOrigin/uBlock-issues/issues/911 -// We detect here whether network requests are proxied, and if so, -// de-aliasing of hostnames will be disabled to avoid possible -// DNS leaks. -const proxyDetector = function(details) { - if ( details.proxyInfo instanceof Object ) { - cnameUncloakEnabled = false; - proxyDetectorTryCount = 0; - } - if ( proxyDetectorTryCount === 0 ) { - browser.webRequest.onHeadersReceived.removeListener(proxyDetector); - return; - } - proxyDetectorTryCount -= 1; -}; -let proxyDetectorTryCount = 0; // Related issues: // - https://github.com/gorhill/uBlock/issues/1327 @@ -81,9 +57,6 @@ vAPI.Net = class extends vAPI.Net { this.canUncloakCnames && options.cnameUncloakEnabled !== false; } - if ( 'cnameUncloakProxied' in options ) { - cnameUncloakProxied = options.cnameUncloakProxied === true; - } if ( 'cnameIgnoreList' in options ) { this.cnameIgnoreList = this.regexFromStrList(options.cnameIgnoreList); @@ -108,23 +81,6 @@ vAPI.Net = class extends vAPI.Net { } this.cnames.clear(); this.cnames.set('', null); this.cnameFlushTime = Date.now() + this.cnameMaxTTL * 60000; - // https://github.com/uBlockOrigin/uBlock-issues/issues/911 - // Install/remove proxy detector. - if ( vAPI.webextFlavor.major < 80 ) { - const wrohr = browser.webRequest.onHeadersReceived; - if ( cnameUncloakEnabled === false || cnameUncloakProxied ) { - if ( wrohr.hasListener(proxyDetector) ) { - wrohr.removeListener(proxyDetector); - } - } else if ( wrohr.hasListener(proxyDetector) === false ) { - wrohr.addListener( - proxyDetector, - { urls: [ '*://*/*' ] }, - [ 'blocking' ] - ); - } - proxyDetectorTryCount = 32; - } } normalizeDetails(details) { const type = details.type; @@ -236,7 +192,7 @@ vAPI.Net = class extends vAPI.Net { return /^./; } return new RegExp( - '(?:^|\.)(?:' + + '(?:^|\\.)(?:' + list.trim() .split(/\s+/) .map(a => a.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')) @@ -261,6 +217,7 @@ vAPI.Net = class extends vAPI.Net { if ( cnRecord !== undefined ) { return this.processCanonicalName(hn, cnRecord, details); } + if ( details.proxyInfo && details.proxyInfo.proxyDNS ) { return; } const documentUrl = details.documentUrl || details.url; const isRootDocument = this.cnameIgnoreRootDocument && hn === hostnameFromNetworkURL(documentUrl); diff --git a/src/js/background.js b/src/js/background.js index 2a5c2366e2982..a596710914fe9 100644 --- a/src/js/background.js +++ b/src/js/background.js @@ -61,7 +61,6 @@ const hiddenSettingsDefault = { cnameIgnoreRootDocument: true, cnameMaxTTL: 120, cnameReplayFullURL: false, - cnameUncloakProxied: false, consoleLogLevel: 'unset', debugAssetsJson: false, debugScriptlets: false, diff --git a/src/js/storage.js b/src/js/storage.js index 1163483220996..77ec90b7ddfdc 100644 --- a/src/js/storage.js +++ b/src/js/storage.js @@ -319,7 +319,6 @@ onBroadcast(msg => { cnameIgnoreRootDocument: µbhs.cnameIgnoreRootDocument, cnameMaxTTL: µbhs.cnameMaxTTL, cnameReplayFullURL: µbhs.cnameReplayFullURL, - cnameUncloakProxied: µbhs.cnameUncloakProxied, }); }); From c19497db3315fd1766e113b1205ea37e05753d0f Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Tue, 10 Sep 2024 15:13:28 -0400 Subject: [PATCH 0204/1099] Update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6a061ab35e6e5..ac7e1ddc34ad6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,4 @@ +- [Avoid using dns.resolve() for proxied DNS resolution](https://github.com/gorhill/uBlock/commit/d5f14ffa32) - [Add support for `lan`/`loopback` values to `ipaddress=` option](https://github.com/gorhill/uBlock/commit/030d7334e4) - [New static network filter option `ipaddress=`](https://github.com/gorhill/uBlock/commit/c6dedd253f) - [Add ability to quote static network option values](https://github.com/gorhill/uBlock/commit/20115697e5) From 099b9852cd1630c7536800a5f25b8b6be0636e7b Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Wed, 11 Sep 2024 09:56:44 -0400 Subject: [PATCH 0205/1099] Code review for `ipaddress=` filter option If an IP address can be extracted from the hostname portion of a URL, the IP address matching will be performed at onBeforeRequest() time. Regardless, IP address matching will subsequently always be performed at onHeadersReceived() time as the request details at that point contain a reliable IP address value on supported platforms (Firefox- only as of now). The `cap_ipaddress` now evaluates to `true` in Chromium-based browsers. Even though these browsers are unable to provide reliable IP address value at onHeadersReceived() time, they can still perform IP address matching for IP address extracted from hostname portion of a URL. --- platform/common/vapi-common.js | 4 +- src/js/background.js | 2 +- src/js/filtering-context.js | 29 +++++++++++- src/js/static-filtering-parser.js | 1 + src/js/static-net-filtering.js | 77 ++++++++++++++++--------------- 5 files changed, 72 insertions(+), 41 deletions(-) diff --git a/platform/common/vapi-common.js b/platform/common/vapi-common.js index 367def90a7b7f..1cf98242f3449 100644 --- a/platform/common/vapi-common.js +++ b/platform/common/vapi-common.js @@ -163,6 +163,7 @@ vAPI.webextFlavor = { // This is always true. soup.add('ublock').add('webext'); + soup.add('ipaddress'); // Whether this is a dev build. if ( /^\d+\.\d+\.\d+\D/.test(browser.runtime.getManifest().version) ) { @@ -181,8 +182,7 @@ vAPI.webextFlavor = { if ( browser.runtime.getURL('').startsWith('moz-extension://') ) { soup.add('firefox') .add('user_stylesheet') - .add('html_filtering') - .add('ipaddress'); + .add('html_filtering'); const match = /Firefox\/(\d+)/.exec(ua); flavor.major = match && parseInt(match[1], 10) || 115; } else { diff --git a/src/js/background.js b/src/js/background.js index a596710914fe9..a68487f6c6a72 100644 --- a/src/js/background.js +++ b/src/js/background.js @@ -305,8 +305,8 @@ const µBlock = { // jshint ignore:line this.realm = ''; this.setMethod(details.method); this.setURL(details.url); + this.setIPAddress(details.ip); this.aliasURL = details.aliasURL || undefined; - this.ipaddress = details.ip || undefined; this.redirectURL = undefined; this.filter = undefined; if ( this.itype !== this.SUB_FRAME ) { diff --git a/src/js/filtering-context.js b/src/js/filtering-context.js index b1d7d7033dac5..0bbd7decfb3f1 100644 --- a/src/js/filtering-context.js +++ b/src/js/filtering-context.js @@ -122,6 +122,8 @@ const methodBitToStrMap = new Map([ [ METHOD_PUT, 'put' ], ]); +const reIPv4 = /^\d+\.\d+\.\d+\.\d+$/; + /******************************************************************************/ export const FilteringContext = class { @@ -136,9 +138,9 @@ export const FilteringContext = class { this.stype = undefined; this.url = undefined; this.aliasURL = undefined; - this.ipaddress = undefined; this.hostname = undefined; this.domain = undefined; + this.ipaddress = undefined; this.docId = -1; this.frameId = -1; this.docOrigin = undefined; @@ -176,6 +178,7 @@ export const FilteringContext = class { this.url = other.url; this.hostname = other.hostname; this.domain = other.domain; + this.ipaddress = other.ipaddress; this.docId = other.docId; this.frameId = other.frameId; this.docOrigin = other.docOrigin; @@ -213,7 +216,7 @@ export const FilteringContext = class { setURL(a) { if ( a !== this.url ) { - this.hostname = this.domain = undefined; + this.hostname = this.domain = this.ipaddress = undefined; this.url = a; } return this; @@ -246,6 +249,28 @@ export const FilteringContext = class { return this; } + getIPAddress() { + if ( this.ipaddress !== undefined ) { + return this.ipaddress; + } + const ipaddr = this.getHostname(); + const c0 = ipaddr.charCodeAt(0); + if ( c0 === 0x5B /* [ */ ) { + return (this.ipaddress = ipaddr.slice(1, -1)); + } else if ( c0 >= 0x30 && c0 <= 0x39 ) { + if ( reIPv4.test(ipaddr) ) { + return (this.ipaddress = ipaddr); + } + } + return (this.ipaddress = ''); + } + + // Must always be called *after* setURL() + setIPAddress(ipaddr) { + this.ipaddress = ipaddr || undefined; + return this; + } + getDocOrigin() { if ( this.docOrigin === undefined ) { this.docOrigin = this.tabOrigin; diff --git a/src/js/static-filtering-parser.js b/src/js/static-filtering-parser.js index d3520b6e555c3..3d8e82fd64271 100644 --- a/src/js/static-filtering-parser.js +++ b/src/js/static-filtering-parser.js @@ -578,6 +578,7 @@ export const preparserIfTokens = new Set([ 'env_mv3', 'env_safari', 'cap_html_filtering', + 'cap_ipaddress', 'cap_user_stylesheet', 'false', 'ext_abp', diff --git a/src/js/static-net-filtering.js b/src/js/static-net-filtering.js index 0523217773114..780015209f68b 100644 --- a/src/js/static-net-filtering.js +++ b/src/js/static-net-filtering.js @@ -2973,6 +2973,9 @@ registerFilterClass(FilterOnHeaders); /******************************************************************************/ class FilterIPAddress { + static reIPv6IPv4lan = /^::ffff:(7f\w{2}|a\w{2}|a9fe|c0a8):\w+$/; + static reIPv6local = /^f[cd]\w{2}:/; + static match(idata) { const ipaddr = $requestAddress; const details = filterRefs[filterData[idata+1]]; @@ -3011,26 +3014,26 @@ class FilterIPAddress { } return ipaddr.startsWith('192.168.'); } - if ( c0 !== 0x5B /* [ */ ) { return false; } // ipv6 - const c1 = ipaddr.charCodeAt(1); - if ( c1 === 0x3A /* : */ ) { - if ( ipaddr.startsWith('[::') === false ) { return false; } - if ( ipaddr === '[::]' || ipaddr === '[::1]' ) { return true; } - if ( ipaddr.startsWith('[::ffff:') === false ) { return false; } - return /^\[::ffff:(7f\w{2}|a\w{2}|a9fe|c0a8):\w+\]$/.test(ipaddr); - } - if ( c1 === 0x36 /* 6 */ ) { - return ipaddr.startsWith('[64:ff9b:'); - } - if ( c1 === 0x66 /* f */ ) { - return /^\[f[cd]\w{2}:/.test(ipaddr); + if ( c0 === 0x3A /* : */ ) { + if ( ipaddr.startsWith('::') === false ) { return false; } + if ( ipaddr === '::' || ipaddr === '::1' ) { return true; } + if ( ipaddr.startsWith('::ffff:') === false ) { return false; } + return this.reIPv6IPv4lan.test(ipaddr); + } + if ( ipaddr.includes(':') ) { + if ( c0 === 0x36 /* 6 */ ) { + return ipaddr.startsWith('64:ff9b:'); + } + if ( c0 === 0x66 /* f */ ) { + return this.reIPv6local.test(ipaddr); + } } return false; } static isLoopback(ipaddr) { - return ipaddr === '127.0.0.1' || ipaddr === '[::1]'; + return ipaddr === '127.0.0.1' || ipaddr === '::1'; } static compile(details) { @@ -3412,7 +3415,6 @@ class FilterCompiler { this.notTypeBits = 0; this.methodBits = 0; this.notMethodBits = 0; - this.responseHeadersRealm = false; return this; } @@ -3532,13 +3534,11 @@ class FilterCompiler { case sfp.NODE_TYPE_NET_OPTION_NAME_HEADER: { this.optionValues.set('header', parser.getNetOptionValue(id) || ''); this.optionUnitBits |= HEADER_BIT; - this.responseHeadersRealm = true; break; } case sfp.NODE_TYPE_NET_OPTION_NAME_IPADDRESS: this.optionValues.set('ipaddress', parser.getNetOptionValue(id) || ''); this.optionUnitBits |= IPADDRESS_BIT; - this.responseHeadersRealm = true; break; case sfp.NODE_TYPE_NET_OPTION_NAME_METHOD: this.processMethodOption(parser.getNetOptionValue(id)); @@ -4008,7 +4008,7 @@ class FilterCompiler { } // Origin - if ( this.optionValues.has('from') ) { + if ( (this.optionUnitBits & FROM_BIT) !== 0 ) { compileFromDomainOpt( this.optionValues.get('fromList'), units.length !== 0 && patternClass.isSlow === true, @@ -4017,7 +4017,7 @@ class FilterCompiler { } // Destination - if ( this.optionValues.has('to') ) { + if ( (this.optionUnitBits & TO_BIT) !== 0 ) { compileToDomainOpt( this.optionValues.get('toList'), units.length !== 0 && patternClass.isSlow === true, @@ -4026,18 +4026,18 @@ class FilterCompiler { } // Deny-allow - if ( this.optionValues.has('denyallow') ) { + if ( (this.optionUnitBits & DENYALLOW_BIT) !== 0 ) { units.push(FilterDenyAllow.compile(this)); } + // IP address + if ( (this.optionUnitBits & IPADDRESS_BIT) !== 0 ) { + units.push(FilterIPAddress.compile(this)); + } + // Header - if ( this.responseHeadersRealm ) { - if ( this.optionValues.has('ipaddress') ) { - units.push(FilterIPAddress.compile(this)); - } - if ( this.optionValues.has('header') ) { - units.push(FilterOnHeaders.compile(this)); - } + if ( (this.optionUnitBits & HEADER_BIT) !== 0 ) { + units.push(FilterOnHeaders.compile(this)); this.action |= HEADERS_REALM; } @@ -4058,12 +4058,17 @@ class FilterCompiler { modifierBitsFromType.get(this.modifyType); } - this.compileToAtomicFilter( - units.length === 1 - ? units[0] - : FilterCompositeAll.compile(units), - writer - ); + const fdata = units.length === 1 + ? units[0] + : FilterCompositeAll.compile(units); + + this.compileToAtomicFilter(fdata, writer); + + if ( (this.optionUnitBits & IPADDRESS_BIT) !== 0 ) { + if ( (this.action & HEADERS_REALM) !== 0 ) { return; } + this.action |= HEADERS_REALM; + this.compileToAtomicFilter(fdata, writer); + } } compilePattern(units) { @@ -4856,7 +4861,7 @@ StaticNetFilteringEngine.prototype.matchAndFetchModifiers = function( $requestHostname = fctxt.getHostname(); $requestMethodBit = fctxt.method || 0; $requestTypeValue = (typeBits & TypeBitsMask) >>> TypeBitsOffset; - $requestAddress = fctxt.ipaddress || ''; + $requestAddress = fctxt.getIPAddress(); const modifierType = modifierTypeFromName.get(modifierName); const modifierBits = modifierBitsFromType.get(modifierType); @@ -5223,7 +5228,7 @@ StaticNetFilteringEngine.prototype.matchRequest = function(fctxt, modifiers = 0) $requestHostname = fctxt.getHostname(); $requestMethodBit = fctxt.method || 0; $requestTypeValue = (typeBits & TypeBitsMask) >>> TypeBitsOffset; - $requestAddress = fctxt.ipaddress || ''; + $requestAddress = fctxt.getIPAddress(); $isBlockImportant = false; // Evaluate block realm before allow realm, and allow realm before @@ -5259,7 +5264,7 @@ StaticNetFilteringEngine.prototype.matchHeaders = function(fctxt, headers) { $requestHostname = fctxt.getHostname(); $requestMethodBit = fctxt.method || 0; $requestTypeValue = (typeBits & TypeBitsMask) >>> TypeBitsOffset; - $requestAddress = fctxt.ipaddress || ''; + $requestAddress = fctxt.getIPAddress(); $httpHeaders.init(headers); let r = 0; From 0e2f04eb2bc61ae2fbc417bfd129975091cbc31b Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Wed, 11 Sep 2024 10:21:50 -0400 Subject: [PATCH 0206/1099] New revision for dev build --- dist/version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dist/version b/dist/version index 31b41e046d12c..b3939ab3af9cf 100644 --- a/dist/version +++ b/dist/version @@ -1 +1 @@ -1.59.1.17 \ No newline at end of file +1.59.1.18 \ No newline at end of file From 44b6519db1c335caa7c272296a986feeab329377 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Wed, 11 Sep 2024 10:41:19 -0400 Subject: [PATCH 0207/1099] Make Firefox dev build auto-update --- dist/firefox/updates.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dist/firefox/updates.json b/dist/firefox/updates.json index 31eb215b13f42..c1705781c285b 100644 --- a/dist/firefox/updates.json +++ b/dist/firefox/updates.json @@ -3,9 +3,9 @@ "uBlock0@raymondhill.net": { "updates": [ { - "version": "1.59.1.17", + "version": "1.59.1.18", "browser_specific_settings": { "gecko": { "strict_min_version": "78.0" } }, - "update_link": "https://github.com/gorhill/uBlock/releases/download/1.59.1b17/uBlock0_1.59.1b17.firefox.signed.xpi" + "update_link": "https://github.com/gorhill/uBlock/releases/download/1.59.1b18/uBlock0_1.59.1b18.firefox.signed.xpi" } ] } From 6acf97bf5143543c036c38a82160e5f8efe8b3f1 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Thu, 12 Sep 2024 11:19:57 -0400 Subject: [PATCH 0208/1099] Rewrite cname uncloaking code to account for new `ipaddress=` option This commit makes the DNS resolution code better suited for both filtering on cname and ip address. The change allows early availability of ip address so that `ipaddress=` option can be matched at onBeforeRequest time. As a result, it is now possible to block root document using `ipaddress=` option -- so long as an ip address can be extracted before first onBeforeRequest() call. Related issue: https://github.com/uBlockOrigin/uBlock-issues/issues/2792 Caveat ------ the ip address used is the first one among the list of ip addresses returned by dns.resolve() method. There is no way for uBO to know which exact ip address will be used by the browser when sending the request, so this is at most a best guess. The exact IP address used by the browser is available at onHeadersReceived time, and uBO will also filter according to this value, but by then the network request has already been sent to the remote server. Possibly a future improvement would make available the whole list of ip addresses to the filtering engine, but even then it's impossible to know with certainty which ip address will ultimately be used by the browser -- it is entirely possible that the ip address used by the browser might not be in the list received through dns.resolve(). --- platform/chromium/manifest.json | 2 +- platform/firefox/vapi-background-ext.js | 244 +++++++++++++++--------- platform/opera/manifest.json | 2 +- src/js/background.js | 1 - 4 files changed, 153 insertions(+), 96 deletions(-) diff --git a/platform/chromium/manifest.json b/platform/chromium/manifest.json index 637b26764bcdc..ba860b6e36bd7 100644 --- a/platform/chromium/manifest.json +++ b/platform/chromium/manifest.json @@ -89,7 +89,7 @@ }, "incognito": "split", "manifest_version": 2, - "minimum_chrome_version": "73.0", + "minimum_chrome_version": "80.0", "name": "uBlock Origin", "options_ui": { "page": "dashboard.html", diff --git a/platform/firefox/vapi-background-ext.js b/platform/firefox/vapi-background-ext.js index 1bb15d38b30e2..d3abf2749586b 100644 --- a/platform/firefox/vapi-background-ext.js +++ b/platform/firefox/vapi-background-ext.js @@ -26,8 +26,10 @@ import { /******************************************************************************/ -// Canonical name-uncloaking feature. -let cnameUncloakEnabled = browser.dns instanceof Object; +const dnsAPI = browser.dns; + +const isPromise = o => o instanceof Promise; +const reIPv4 = /^\d+\.\d+\.\d+\.\d+$/ // Related issues: // - https://github.com/gorhill/uBlock/issues/1327 @@ -40,21 +42,24 @@ vAPI.Net = class extends vAPI.Net { constructor() { super(); this.pendingRequests = []; - this.canUncloakCnames = browser.dns instanceof Object; - this.cnames = new Map([ [ '', null ] ]); + this.dnsList = []; // ring buffer + this.dnsWritePtr = 0; // next write pointer in ring buffer + this.dnsMaxCount = 256; // max size of ring buffer + this.dnsDict = new Map(); // hn to index in ring buffer + this.dnsEntryTTL = 60000; // delay after which an entry is obsolete + this.canUncloakCnames = true; + this.cnameUncloakEnabled = true; this.cnameIgnoreList = null; this.cnameIgnore1stParty = true; this.cnameIgnoreExceptions = true; this.cnameIgnoreRootDocument = true; - this.cnameMaxTTL = 120; this.cnameReplayFullURL = false; - this.cnameFlushTime = Date.now() + this.cnameMaxTTL * 60000; } + setOptions(options) { super.setOptions(options); if ( 'cnameUncloakEnabled' in options ) { - cnameUncloakEnabled = - this.canUncloakCnames && + this.cnameUncloakEnabled = options.cnameUncloakEnabled !== false; } if ( 'cnameIgnoreList' in options ) { @@ -73,15 +78,13 @@ vAPI.Net = class extends vAPI.Net { this.cnameIgnoreRootDocument = options.cnameIgnoreRootDocument !== false; } - if ( 'cnameMaxTTL' in options ) { - this.cnameMaxTTL = options.cnameMaxTTL || 120; - } if ( 'cnameReplayFullURL' in options ) { this.cnameReplayFullURL = options.cnameReplayFullURL === true; } - this.cnames.clear(); this.cnames.set('', null); - this.cnameFlushTime = Date.now() + this.cnameMaxTTL * 60000; + this.dnsList.fill(null); + this.dnsDict.clear(); } + normalizeDetails(details) { const type = details.type; @@ -104,6 +107,7 @@ vAPI.Net = class extends vAPI.Net { } } } + denormalizeTypes(types) { if ( types.length === 0 ) { return Array.from(this.validTypes); @@ -122,75 +126,19 @@ vAPI.Net = class extends vAPI.Net { } return Array.from(out); } + canonicalNameFromHostname(hn) { - const cnRecord = this.cnames.get(hn); - if ( cnRecord !== undefined && cnRecord !== null ) { - return cnRecord.cname; - } - } - processCanonicalName(hn, cnRecord, details) { - if ( cnRecord === null ) { return; } - if ( cnRecord.isRootDocument ) { return; } - const hnBeg = details.url.indexOf(hn); - if ( hnBeg === -1 ) { return; } - const oldURL = details.url; - let newURL = oldURL.slice(0, hnBeg) + cnRecord.cname; - const hnEnd = hnBeg + hn.length; - if ( this.cnameReplayFullURL ) { - newURL += oldURL.slice(hnEnd); - } else { - const pathBeg = oldURL.indexOf('/', hnEnd); - if ( pathBeg !== -1 ) { - newURL += oldURL.slice(hnEnd, pathBeg + 1); - } - } - details.url = newURL; - details.aliasURL = oldURL; - return super.onBeforeSuspendableRequest(details); - } - recordCanonicalName(hn, record, isRootDocument) { - if ( (this.cnames.size & 0b111111) === 0 ) { - const now = Date.now(); - if ( now >= this.cnameFlushTime ) { - this.cnames.clear(); this.cnames.set('', null); - this.cnameFlushTime = now + this.cnameMaxTTL * 60000; - } - } - let cname = - typeof record.canonicalName === 'string' && - record.canonicalName !== hn - ? record.canonicalName - : ''; - if ( - cname !== '' && - this.cnameIgnore1stParty && - domainFromHostname(cname) === domainFromHostname(hn) - ) { - cname = ''; - } - if ( - cname !== '' && - this.cnameIgnoreList !== null && - this.cnameIgnoreList.test(cname) - ) { - cname = ''; - } - const cnRecord = cname !== '' ? { cname, isRootDocument } : null; - this.cnames.set(hn, cnRecord); - return cnRecord; + if ( hn === '' ) { return; } + const dnsEntry = this.dnsFromCache(hn); + if ( isPromise(dnsEntry) ) { return; } + return dnsEntry?.cname; } + regexFromStrList(list) { - if ( - typeof list !== 'string' || - list.length === 0 || - list === 'unset' || - browser.dns instanceof Object === false - ) { + if ( typeof list !== 'string' || list.length === 0 || list === 'unset' ) { return null; } - if ( list === '*' ) { - return /^./; - } + if ( list === '*' ) { return /^./; } return new RegExp( '(?:^|\\.)(?:' + list.trim() @@ -200,9 +148,14 @@ vAPI.Net = class extends vAPI.Net { ')$' ); } + onBeforeSuspendableRequest(details) { + const hn = hostnameFromNetworkURL(details.url); + const dnsEntry = this.dnsFromCache(hn); + if ( dnsEntry?.ip ) { + details.ip = dnsEntry.ip; + } const r = super.onBeforeSuspendableRequest(details); - if ( cnameUncloakEnabled === false ) { return r; } if ( r !== undefined ) { if ( r.cancel === true || @@ -212,25 +165,128 @@ vAPI.Net = class extends vAPI.Net { return r; } } - const hn = hostnameFromNetworkURL(details.url); - const cnRecord = this.cnames.get(hn); - if ( cnRecord !== undefined ) { - return this.processCanonicalName(hn, cnRecord, details); + if ( dnsEntry !== undefined ) { + if ( isPromise(dnsEntry) === false ) { + return this.onAfterDNSResolution(hn, details, dnsEntry); + } } - if ( details.proxyInfo && details.proxyInfo.proxyDNS ) { return; } - const documentUrl = details.documentUrl || details.url; - const isRootDocument = this.cnameIgnoreRootDocument && - hn === hostnameFromNetworkURL(documentUrl); - return browser.dns.resolve(hn, [ 'canonical_name' ]).then( - rec => { - const cnRecord = this.recordCanonicalName(hn, rec, isRootDocument); - return this.processCanonicalName(hn, cnRecord, details); - }, - ( ) => { - this.cnames.set(hn, null); + if ( this.dnsShouldResolve(hn) === false ) { return; } + if ( details.proxyInfo?.proxyDNS ) { return; } + const promise = dnsEntry || this.dnsResolve(hn, details); + return promise.then(( ) => this.onAfterDNSResolution(hn, details)); + } + + onAfterDNSResolution(hn, details, dnsEntry) { + if ( dnsEntry === undefined ) { + dnsEntry = this.dnsFromCache(hn); + if ( dnsEntry === undefined || isPromise(dnsEntry) ) { return; } + } + let proceed = false; + if ( dnsEntry.cname && this.cnameUncloakEnabled ) { + const newURL = this.uncloakURL(hn, dnsEntry, details); + if ( newURL ) { + details.aliasURL = details.url; + details.url = newURL; + proceed = true; } + } + if ( dnsEntry.ip && details.ip !== dnsEntry.ip ) { + details.ip = dnsEntry.ip + proceed = true; + } + if ( proceed === false ) { return; } + // Must call method on base class + return super.onBeforeSuspendableRequest(details); + } + + dnsToCache(hn, record, details) { + const i = this.dnsDict.get(hn); + if ( i === undefined ) { return; } + const dnsEntry = { + hn, + until: Date.now() + this.dnsEntryTTL, + }; + if ( record ) { + const cname = this.cnameFromRecord(hn, record, details); + if ( cname ) { dnsEntry.cname = cname; } + const ip = this.ipFromRecord(record); + if ( ip ) { dnsEntry.ip = ip; } + } + this.dnsList[i] = dnsEntry; + return dnsEntry; + } + + dnsFromCache(hn) { + const i = this.dnsDict.get(hn); + if ( i === undefined ) { return; } + const dnsEntry = this.dnsList[i]; + if ( dnsEntry === null ) { return; } + if ( isPromise(dnsEntry) ) { return dnsEntry; } + if ( dnsEntry.hn !== hn ) { return; } + if ( dnsEntry.until >= Date.now() ) { return dnsEntry; } + this.dnsList[i] = null; + this.dnsDict.delete(hn) + } + + dnsShouldResolve(hn) { + if ( hn === '' ) { return false; } + const c0 = hn.charCodeAt(0); + if ( c0 === 0x5B /* [ */ ) { return false; } + if ( c0 > 0x39 /* 9 */ ) { return true; } + return reIPv4.test(hn) === false; + } + + dnsResolve(hn, details) { + const i = this.dnsWritePtr++; + this.dnsWritePtr %= this.dnsMaxCount; + this.dnsDict.set(hn, i); + const promise = dnsAPI.resolve(hn, [ 'canonical_name' ]).then( + rec => this.dnsToCache(hn, rec, details), + ( ) => this.dnsToCache(hn) ); + return (this.dnsList[i] = promise); } + + cnameFromRecord(hn, record, details) { + const cn = record.canonicalName; + if ( cn === undefined ) { return; } + if ( cn === hn ) { return; } + if ( this.cnameIgnore1stParty ) { + if ( domainFromHostname(cn) === domainFromHostname(hn) ) { return; } + } + if ( this.cnameIgnoreList !== null ) { + if ( this.cnameIgnoreList.test(cn) === false ) { return; } + } + if ( this.cnameIgnoreRootDocument ) { + const origin = hostnameFromNetworkURL(details.documentUrl || details.url); + if ( hn === origin ) { return; } + } + return cn; + } + + uncloakURL(hn, dnsEntry, details) { + const hnBeg = details.url.indexOf(hn); + if ( hnBeg === -1 ) { return; } + const oldURL = details.url; + const newURL = oldURL.slice(0, hnBeg) + dnsEntry.cname; + const hnEnd = hnBeg + hn.length; + if ( this.cnameReplayFullURL ) { + return newURL + oldURL.slice(hnEnd); + } + const pathBeg = oldURL.indexOf('/', hnEnd); + if ( pathBeg !== -1 ) { + return newURL + oldURL.slice(hnEnd, pathBeg + 1); + } + return newURL; + } + + ipFromRecord(record) { + const { addresses } = record; + if ( Array.isArray(addresses) === false ) { return; } + if ( addresses.length === 0 ) { return; } + return addresses[0]; + } + suspendOneRequest(details) { const pending = { details: Object.assign({}, details), @@ -243,6 +299,7 @@ vAPI.Net = class extends vAPI.Net { this.pendingRequests.push(pending); return pending.promise; } + unsuspendAllRequests(discard = false) { const pendingRequests = this.pendingRequests; this.pendingRequests = []; @@ -254,6 +311,7 @@ vAPI.Net = class extends vAPI.Net { ); } } + static canSuspend() { return true; } diff --git a/platform/opera/manifest.json b/platform/opera/manifest.json index 196c171726670..38015c3959979 100644 --- a/platform/opera/manifest.json +++ b/platform/opera/manifest.json @@ -88,7 +88,7 @@ }, "incognito": "split", "manifest_version": 2, - "minimum_opera_version": "60.0", + "minimum_opera_version": "67.0", "name": "uBlock Origin", "options_page": "dashboard.html", "permissions": [ diff --git a/src/js/background.js b/src/js/background.js index a68487f6c6a72..939c559c32167 100644 --- a/src/js/background.js +++ b/src/js/background.js @@ -59,7 +59,6 @@ const hiddenSettingsDefault = { cnameIgnore1stParty: true, cnameIgnoreExceptions: true, cnameIgnoreRootDocument: true, - cnameMaxTTL: 120, cnameReplayFullURL: false, consoleLogLevel: 'unset', debugAssetsJson: false, From 71f07b18aebabcb985f15c3789c4a1b851dca1fd Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Thu, 12 Sep 2024 11:41:26 -0400 Subject: [PATCH 0209/1099] Update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index ac7e1ddc34ad6..5cb5d42cbc4ba 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,4 @@ +- [Rewrite cname uncloaking code to account for new `ipaddress=` option](https://github.com/gorhill/uBlock/commit/6acf97bf51) - [Avoid using dns.resolve() for proxied DNS resolution](https://github.com/gorhill/uBlock/commit/d5f14ffa32) - [Add support for `lan`/`loopback` values to `ipaddress=` option](https://github.com/gorhill/uBlock/commit/030d7334e4) - [New static network filter option `ipaddress=`](https://github.com/gorhill/uBlock/commit/c6dedd253f) From 9b967eccc84cb938fffc9b602c566cb028f6c4de Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Thu, 12 Sep 2024 11:42:03 -0400 Subject: [PATCH 0210/1099] New revision for dev build --- dist/version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dist/version b/dist/version index b3939ab3af9cf..d9155e0cf75ab 100644 --- a/dist/version +++ b/dist/version @@ -1 +1 @@ -1.59.1.18 \ No newline at end of file +1.59.1.19 \ No newline at end of file From 671b0c540ca75866792697bb0555df44b03da945 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Thu, 12 Sep 2024 12:06:26 -0400 Subject: [PATCH 0211/1099] Make Firefox dev build auto-update --- dist/firefox/updates.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dist/firefox/updates.json b/dist/firefox/updates.json index c1705781c285b..5a4347f7cc7d3 100644 --- a/dist/firefox/updates.json +++ b/dist/firefox/updates.json @@ -3,9 +3,9 @@ "uBlock0@raymondhill.net": { "updates": [ { - "version": "1.59.1.18", + "version": "1.59.1.19", "browser_specific_settings": { "gecko": { "strict_min_version": "78.0" } }, - "update_link": "https://github.com/gorhill/uBlock/releases/download/1.59.1b18/uBlock0_1.59.1b18.firefox.signed.xpi" + "update_link": "https://github.com/gorhill/uBlock/releases/download/1.59.1b19/uBlock0_1.59.1b19.firefox.signed.xpi" } ] } From 8fadfb2c5eab528e22a134c552fc2eb07c3da9de Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Thu, 12 Sep 2024 15:40:24 -0400 Subject: [PATCH 0212/1099] Import translation work from https://crowdin.com/project/ublock --- platform/mv3/description/webstore.id.txt | 2 +- platform/mv3/description/webstore.ja.txt | 2 +- platform/mv3/extension/_locales/bg/messages.json | 2 +- platform/mv3/extension/_locales/eu/messages.json | 2 +- platform/mv3/extension/_locales/id/messages.json | 2 +- platform/mv3/extension/_locales/pt_PT/messages.json | 4 ++-- platform/mv3/extension/_locales/ro/messages.json | 2 +- platform/mv3/extension/_locales/sq/messages.json | 4 ++-- platform/mv3/extension/_locales/vi/messages.json | 2 +- src/_locales/fi/messages.json | 4 ++-- 10 files changed, 13 insertions(+), 13 deletions(-) diff --git a/platform/mv3/description/webstore.id.txt b/platform/mv3/description/webstore.id.txt index b9ba140a727ec..92e0b9852bd34 100644 --- a/platform/mv3/description/webstore.id.txt +++ b/platform/mv3/description/webstore.id.txt @@ -7,7 +7,7 @@ Kumpulan aturan bawaan sesuai dengan kumpulan penyaringan bawaan uBlock Origin: - EasyPrivacy - Daftar server iklan dan pelacak Peter Lowe -You can enable more rulesets by visiting the options page -- click the _Cogs_ icon in the popup panel. +Anda dapat mengaktifkan lebih banyak kumpulan pengaturan dengan mengunjungi halaman opsi - klik ikon _Cogs_ pada panel popup. uBOL sepenuhnya deklaratif, yang mana tidak membutuhkan proses permanen uBOL agar penyaringan dapat terjadi, dan penyaringan konten berbasis injeksi CSS/JS dilakukan sepenuhnya oleh peramban itu sendiri ketimbang oleh ekstensi. Ini berarti bahwa uBOL sendiri tidak mengkonsumsi sumber daya CPU/memori selama melakukan pemblokiran konten -- proses pekerja layanan uBOL dibutuhkan _hanya_ ketika Anda berinteraksi dengan panel popup atau opsi halaman. diff --git a/platform/mv3/description/webstore.ja.txt b/platform/mv3/description/webstore.ja.txt index c6ef50e37d00b..4271d4a7d3562 100644 --- a/platform/mv3/description/webstore.ja.txt +++ b/platform/mv3/description/webstore.ja.txt @@ -19,7 +19,7 @@ uBOL はインストール時に広範な「データの読み取りと変更」 ブラウザは、現在のサイトで拡張機能によってリクエストされた追加の権限を付与することによってもたらされる影響について警告します。承認または拒否することができます。 -閲覧中のサイトに対するuBOLの追加的な権限要求リクエストを承認すると、そのサイトへのコンテンツフィルタリングの品質をあげることができます。 +現在のサイトでの uBOL のリクエストを承認すると、現在のサイトにより良いフィルターを適用できるようになります。 uBOL の設定ページで既定のフィルタリングモードを設定できます。 「最適」または「完全」を規定のフィルタリング モードに設定した場合、すべてのWebサイトで「データの読み取りと変更」権限を付与する必要があります。 diff --git a/platform/mv3/extension/_locales/bg/messages.json b/platform/mv3/extension/_locales/bg/messages.json index 6ac57860653fc..0d35e5ec11f44 100644 --- a/platform/mv3/extension/_locales/bg/messages.json +++ b/platform/mv3/extension/_locales/bg/messages.json @@ -148,7 +148,7 @@ "description": "A short description for the editable field which lists trusted sites" }, "noFilteringModePlaceholder": { - "message": "[само имена на хостове]\nprimer.com\nigri.primer\n...", + "message": "[само имена на хостове]\nexample.com\ngames.example\n...", "description": "Default text for in edit field" }, "behaviorSectionLabel": { diff --git a/platform/mv3/extension/_locales/eu/messages.json b/platform/mv3/extension/_locales/eu/messages.json index ac5c05b345274..204ddba5bc649 100644 --- a/platform/mv3/extension/_locales/eu/messages.json +++ b/platform/mv3/extension/_locales/eu/messages.json @@ -148,7 +148,7 @@ "description": "A short description for the editable field which lists trusted sites" }, "noFilteringModePlaceholder": { - "message": "[hostnames only]\nexample.com\ngames.example\n...", + "message": "[zerbitzari izenak bakarrik]\nadibidea.eus\nexample.com\ngames.example", "description": "Default text for in edit field" }, "behaviorSectionLabel": { diff --git a/platform/mv3/extension/_locales/id/messages.json b/platform/mv3/extension/_locales/id/messages.json index 8041d111ad497..7907760e09ecd 100644 --- a/platform/mv3/extension/_locales/id/messages.json +++ b/platform/mv3/extension/_locales/id/messages.json @@ -148,7 +148,7 @@ "description": "A short description for the editable field which lists trusted sites" }, "noFilteringModePlaceholder": { - "message": "[hostnames only]\nexample.com\ngames.example\n...", + "message": "[hanya nama host]\ncontoh.com\npermainan.contoh\n...", "description": "Default text for in edit field" }, "behaviorSectionLabel": { diff --git a/platform/mv3/extension/_locales/pt_PT/messages.json b/platform/mv3/extension/_locales/pt_PT/messages.json index 52917d37ecb71..e7f1ac7a71a37 100644 --- a/platform/mv3/extension/_locales/pt_PT/messages.json +++ b/platform/mv3/extension/_locales/pt_PT/messages.json @@ -144,11 +144,11 @@ "description": "This describes the 'complete' filtering mode" }, "noFilteringModeDescription": { - "message": "Lista de nomes de anfitriões para os quais não será efetuada qualquer filtragem.", + "message": "Lista de sítios ‘web’ para os quais não será efetuada qualquer filtragem.", "description": "A short description for the editable field which lists trusted sites" }, "noFilteringModePlaceholder": { - "message": "[hostnames only]\nexemplo.com\njogos.exemplo\n...", + "message": "[apenas nomes de anfitriões]\nexemplo.com\njogos.exemplo\n...", "description": "Default text for in edit field" }, "behaviorSectionLabel": { diff --git a/platform/mv3/extension/_locales/ro/messages.json b/platform/mv3/extension/_locales/ro/messages.json index c953b4626e9e1..4502d6086b52a 100644 --- a/platform/mv3/extension/_locales/ro/messages.json +++ b/platform/mv3/extension/_locales/ro/messages.json @@ -144,7 +144,7 @@ "description": "This describes the 'complete' filtering mode" }, "noFilteringModeDescription": { - "message": "Lista numelor mașinilor pentru care nu se va face filtrare", + "message": "Lista numelor site-urilor pentru care nu se va face filtrare", "description": "A short description for the editable field which lists trusted sites" }, "noFilteringModePlaceholder": { diff --git a/platform/mv3/extension/_locales/sq/messages.json b/platform/mv3/extension/_locales/sq/messages.json index 986b72a0479ec..d7ec67c9dfe6d 100644 --- a/platform/mv3/extension/_locales/sq/messages.json +++ b/platform/mv3/extension/_locales/sq/messages.json @@ -144,11 +144,11 @@ "description": "This describes the 'complete' filtering mode" }, "noFilteringModeDescription": { - "message": "Emrat e hosteve që nuk do të kalojnë në filtër", + "message": "Lista e uebsajteve që nuk do të kalojnë në filtër.", "description": "A short description for the editable field which lists trusted sites" }, "noFilteringModePlaceholder": { - "message": "[hostnames only]\nexample.com\ngames.example\n...", + "message": "[vetëm emrat e hosteve]\nshembull.com\nlojera.shembull\n...", "description": "Default text for in edit field" }, "behaviorSectionLabel": { diff --git a/platform/mv3/extension/_locales/vi/messages.json b/platform/mv3/extension/_locales/vi/messages.json index 0a9042b6f3652..9a612c32deee0 100644 --- a/platform/mv3/extension/_locales/vi/messages.json +++ b/platform/mv3/extension/_locales/vi/messages.json @@ -148,7 +148,7 @@ "description": "A short description for the editable field which lists trusted sites" }, "noFilteringModePlaceholder": { - "message": "[hostnames only]\nexample.com\ngames.example\n...", + "message": "[hostnames only]\nexample.com\ngames.example", "description": "Default text for in edit field" }, "behaviorSectionLabel": { diff --git a/src/_locales/fi/messages.json b/src/_locales/fi/messages.json index 1f3677df2940c..881e7f6e1e637 100644 --- a/src/_locales/fi/messages.json +++ b/src/_locales/fi/messages.json @@ -544,7 +544,7 @@ "description": "Label for the checkbox use to enable/disable 'My filters' list" }, "1pTrustMyFiltersLabel": { - "message": "Salli omat suodattimet, joihin luotat", + "message": "Salli luottamusta edellyttävät omat suodattimet", "description": "Label for the checkbox use to trust the content of 'My filters' list" }, "1pImport": { @@ -1264,7 +1264,7 @@ "description": "Label for keyboard shortcut used to toggle cosmetic filtering" }, "toggleJavascript": { - "message": "Kytke JavaScript käyttöön/pois käytöstä", + "message": "Kytke JavaScript", "description": "Label for keyboard shortcut used to toggle no-scripting switch" }, "relaxBlockingMode": { From 73ee3ffe9259f23f97006ef01c588584f90875f9 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Fri, 13 Sep 2024 10:00:41 -0400 Subject: [PATCH 0213/1099] Code review of DNS-related code Related commit: https://github.com/gorhill/uBlock/commit/6acf97bf5143543c036c38a82160e5f8efe8b3f1 --- platform/firefox/vapi-background-ext.js | 72 +++++++++++++++---------- 1 file changed, 44 insertions(+), 28 deletions(-) diff --git a/platform/firefox/vapi-background-ext.js b/platform/firefox/vapi-background-ext.js index d3abf2749586b..acc65da325930 100644 --- a/platform/firefox/vapi-background-ext.js +++ b/platform/firefox/vapi-background-ext.js @@ -27,10 +27,13 @@ import { /******************************************************************************/ const dnsAPI = browser.dns; - const isPromise = o => o instanceof Promise; +const isResolvedObject = o => o instanceof Object && + o instanceof Promise === false; const reIPv4 = /^\d+\.\d+\.\d+\.\d+$/ +/******************************************************************************/ + // Related issues: // - https://github.com/gorhill/uBlock/issues/1327 // - https://github.com/uBlockOrigin/uBlock-issues/issues/128 @@ -87,12 +90,10 @@ vAPI.Net = class extends vAPI.Net { normalizeDetails(details) { const type = details.type; - if ( type === 'imageset' ) { details.type = 'image'; return; } - // https://github.com/uBlockOrigin/uBlock-issues/issues/345 // Re-categorize an embedded object as a `sub_frame` if its // content type is that of a HTML document. @@ -130,8 +131,8 @@ vAPI.Net = class extends vAPI.Net { canonicalNameFromHostname(hn) { if ( hn === '' ) { return; } const dnsEntry = this.dnsFromCache(hn); - if ( isPromise(dnsEntry) ) { return; } - return dnsEntry?.cname; + if ( isResolvedObject(dnsEntry) === false ) { return; } + return dnsEntry.cname; } regexFromStrList(list) { @@ -152,7 +153,7 @@ vAPI.Net = class extends vAPI.Net { onBeforeSuspendableRequest(details) { const hn = hostnameFromNetworkURL(details.url); const dnsEntry = this.dnsFromCache(hn); - if ( dnsEntry?.ip ) { + if ( isResolvedObject(dnsEntry) && dnsEntry.ip ) { details.ip = dnsEntry.ip; } const r = super.onBeforeSuspendableRequest(details); @@ -165,10 +166,8 @@ vAPI.Net = class extends vAPI.Net { return r; } } - if ( dnsEntry !== undefined ) { - if ( isPromise(dnsEntry) === false ) { - return this.onAfterDNSResolution(hn, details, dnsEntry); - } + if ( isResolvedObject(dnsEntry) ) { + return this.onAfterDNSResolution(hn, details, dnsEntry); } if ( this.dnsShouldResolve(hn) === false ) { return; } if ( details.proxyInfo?.proxyDNS ) { return; } @@ -179,7 +178,7 @@ vAPI.Net = class extends vAPI.Net { onAfterDNSResolution(hn, details, dnsEntry) { if ( dnsEntry === undefined ) { dnsEntry = this.dnsFromCache(hn); - if ( dnsEntry === undefined || isPromise(dnsEntry) ) { return; } + if ( isResolvedObject(dnsEntry) === false ) { return; } } let proceed = false; if ( dnsEntry.cname && this.cnameUncloakEnabled ) { @@ -200,32 +199,51 @@ vAPI.Net = class extends vAPI.Net { } dnsToCache(hn, record, details) { - const i = this.dnsDict.get(hn); - if ( i === undefined ) { return; } - const dnsEntry = { - hn, - until: Date.now() + this.dnsEntryTTL, - }; + const dnsEntry = { hn, until: Date.now() + this.dnsEntryTTL }; if ( record ) { const cname = this.cnameFromRecord(hn, record, details); if ( cname ) { dnsEntry.cname = cname; } const ip = this.ipFromRecord(record); if ( ip ) { dnsEntry.ip = ip; } } - this.dnsList[i] = dnsEntry; + this.dnsSetCache(-1, hn, dnsEntry); return dnsEntry; } dnsFromCache(hn) { const i = this.dnsDict.get(hn); if ( i === undefined ) { return; } + if ( isPromise(i) ) { return i; } const dnsEntry = this.dnsList[i]; - if ( dnsEntry === null ) { return; } - if ( isPromise(dnsEntry) ) { return dnsEntry; } - if ( dnsEntry.hn !== hn ) { return; } - if ( dnsEntry.until >= Date.now() ) { return dnsEntry; } - this.dnsList[i] = null; - this.dnsDict.delete(hn) + if ( dnsEntry !== null && dnsEntry.hn === hn ) { + if ( dnsEntry.until >= Date.now() ) { + return dnsEntry; + } + } + this.dnsSetCache(i); + } + + dnsSetCache(i, hn, after) { + if ( i < 0 ) { + const j = this.dnsDict.get(hn); + if ( typeof j === 'number' ) { + this.dnsList[j] = after; + return; + } + i = this.dnsWritePtr++; + this.dnsWritePtr %= this.dnsMaxCount; + } + const before = this.dnsList[i]; + if ( before ) { + this.dnsDict.delete(before.hn); + } + if ( after ) { + this.dnsDict.set(hn, i); + this.dnsList[i] = after; + } else { + if ( hn ) { this.dnsDict.delete(hn); } + this.dnsList[i] = null; + } } dnsShouldResolve(hn) { @@ -237,14 +255,12 @@ vAPI.Net = class extends vAPI.Net { } dnsResolve(hn, details) { - const i = this.dnsWritePtr++; - this.dnsWritePtr %= this.dnsMaxCount; - this.dnsDict.set(hn, i); const promise = dnsAPI.resolve(hn, [ 'canonical_name' ]).then( rec => this.dnsToCache(hn, rec, details), ( ) => this.dnsToCache(hn) ); - return (this.dnsList[i] = promise); + this.dnsDict.set(hn, promise); + return promise; } cnameFromRecord(hn, record, details) { From 63e5611877e2cefb92e2300a2e027ff4506f6fea Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Fri, 13 Sep 2024 10:04:13 -0400 Subject: [PATCH 0214/1099] New revision for dev build --- dist/version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dist/version b/dist/version index d9155e0cf75ab..4058c0b4b7223 100644 --- a/dist/version +++ b/dist/version @@ -1 +1 @@ -1.59.1.19 \ No newline at end of file +1.59.1.20 \ No newline at end of file From d1db02b04c9eabc05f5a5f56f99fa882f10bc6ce Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Fri, 13 Sep 2024 10:11:27 -0400 Subject: [PATCH 0215/1099] Make Firefox dev build auto-update --- dist/firefox/updates.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dist/firefox/updates.json b/dist/firefox/updates.json index 5a4347f7cc7d3..3d1492ec3e0b6 100644 --- a/dist/firefox/updates.json +++ b/dist/firefox/updates.json @@ -3,9 +3,9 @@ "uBlock0@raymondhill.net": { "updates": [ { - "version": "1.59.1.19", + "version": "1.59.1.20", "browser_specific_settings": { "gecko": { "strict_min_version": "78.0" } }, - "update_link": "https://github.com/gorhill/uBlock/releases/download/1.59.1b19/uBlock0_1.59.1b19.firefox.signed.xpi" + "update_link": "https://github.com/gorhill/uBlock/releases/download/1.59.1b20/uBlock0_1.59.1b20.firefox.signed.xpi" } ] } From 93042eced4e4fabc18d76d92f6a16362965593cf Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Fri, 13 Sep 2024 11:20:37 -0400 Subject: [PATCH 0216/1099] Use dummy dns API when it's not present --- platform/firefox/vapi-background-ext.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/platform/firefox/vapi-background-ext.js b/platform/firefox/vapi-background-ext.js index acc65da325930..5d3ff8c5dfbdd 100644 --- a/platform/firefox/vapi-background-ext.js +++ b/platform/firefox/vapi-background-ext.js @@ -26,7 +26,12 @@ import { /******************************************************************************/ -const dnsAPI = browser.dns; +const dnsAPI = browser.dns || { + resolve() { + return Promise.resolve(); + } +}; + const isPromise = o => o instanceof Promise; const isResolvedObject = o => o instanceof Object && o instanceof Promise === false; From 5e6f78a902be689f675fd462817c43e0381970be Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sat, 14 Sep 2024 00:10:34 -0400 Subject: [PATCH 0217/1099] Fix regression re. `mp4` filter option Related issue: https://github.com/uBlockOrigin/uBlock-issues/issues/3375 --- src/js/static-filtering-parser.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/js/static-filtering-parser.js b/src/js/static-filtering-parser.js index 3d8e82fd64271..ee3318240dd41 100644 --- a/src/js/static-filtering-parser.js +++ b/src/js/static-filtering-parser.js @@ -900,7 +900,7 @@ export class AstFilterParser { this.reGoodRegexToken = /[^\x01%0-9A-Za-z][%0-9A-Za-z]{7,}|[^\x01%0-9A-Za-z][%0-9A-Za-z]{1,6}[^\x01%0-9A-Za-z]/; this.reBadCSP = /(?:^|[;,])\s*report-(?:to|uri)\b/i; this.reBadPP = /(?:^|[;,])\s*report-to\b/i; - this.reNetOption = /^(~?)([13a-z_-]+)(=?)/; + this.reNetOption = /^(~?)([134a-z_-]+)(=?)/; this.reNoopOption = /^_+$/; this.netOptionValueParser = new ArgListParser(','); this.scriptletArgListParser = new ArgListParser(','); From 42700a6f76737cf006ae88f19a3e870a279801fb Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sat, 14 Sep 2024 00:12:00 -0400 Subject: [PATCH 0218/1099] New revision for dev build --- dist/version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dist/version b/dist/version index 4058c0b4b7223..1a627ff03ae93 100644 --- a/dist/version +++ b/dist/version @@ -1 +1 @@ -1.59.1.20 \ No newline at end of file +1.59.1.21 \ No newline at end of file From f9ab4b75041815e6e5690d80851189ae3dc660d0 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sat, 14 Sep 2024 00:15:41 -0400 Subject: [PATCH 0219/1099] Make Firefox dev build auto-update --- dist/firefox/updates.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dist/firefox/updates.json b/dist/firefox/updates.json index 3d1492ec3e0b6..72ea093e8c640 100644 --- a/dist/firefox/updates.json +++ b/dist/firefox/updates.json @@ -3,9 +3,9 @@ "uBlock0@raymondhill.net": { "updates": [ { - "version": "1.59.1.20", + "version": "1.59.1.21", "browser_specific_settings": { "gecko": { "strict_min_version": "78.0" } }, - "update_link": "https://github.com/gorhill/uBlock/releases/download/1.59.1b20/uBlock0_1.59.1b20.firefox.signed.xpi" + "update_link": "https://github.com/gorhill/uBlock/releases/download/1.59.1b21/uBlock0_1.59.1b21.firefox.signed.xpi" } ] } From 41d49921c8a9061ced09ac84ef3a4bc4162f5c01 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sat, 14 Sep 2024 09:20:54 -0400 Subject: [PATCH 0220/1099] Minor code review --- src/js/filtering-context.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/js/filtering-context.js b/src/js/filtering-context.js index 0bbd7decfb3f1..933785f55114a 100644 --- a/src/js/filtering-context.js +++ b/src/js/filtering-context.js @@ -257,7 +257,7 @@ export const FilteringContext = class { const c0 = ipaddr.charCodeAt(0); if ( c0 === 0x5B /* [ */ ) { return (this.ipaddress = ipaddr.slice(1, -1)); - } else if ( c0 >= 0x30 && c0 <= 0x39 ) { + } else if ( c0 <= 0x39 && c0 >= 0x30 ) { if ( reIPv4.test(ipaddr) ) { return (this.ipaddress = ipaddr); } From f936dfa6486a19899d414d725e94c21a14a5cbfb Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sat, 14 Sep 2024 09:31:22 -0400 Subject: [PATCH 0221/1099] Fix potential failure to unregister scriptlet In Firefox-specific contentScripts API used to register scriptlets. This could potentially occurs when there are registrations pending during a reload of filter lists. --- src/js/scriptlet-filtering.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/js/scriptlet-filtering.js b/src/js/scriptlet-filtering.js index 98f2a64a39a5d..e221a4197c0d0 100644 --- a/src/js/scriptlet-filtering.js +++ b/src/js/scriptlet-filtering.js @@ -61,6 +61,7 @@ const contentScriptRegisterer = new (class { runAt: 'document_start', }).then(handle => { this.hostnameToDetails.set(hostname, { handle, code }); + return handle; }).catch(( ) => { this.hostnameToDetails.delete(hostname); }); @@ -94,7 +95,9 @@ const contentScriptRegisterer = new (class { } unregisterHandle(handle) { if ( handle instanceof Promise ) { - handle.then(handle => { handle.unregister(); }); + handle.then(handle => { + if ( handle ) { handle.unregister(); } + }); } else { handle.unregister(); } From c265e849e070edf49a73f14007bf35f2a8a34656 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sat, 14 Sep 2024 11:50:10 -0400 Subject: [PATCH 0222/1099] Fetch and cache cname of collated hostnames in page store Related feedback: https://github.com/uBlockOrigin/uBlock-issues/discussions/3376 --- src/js/messaging.js | 5 ++--- src/js/pagestore.js | 1 + 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/js/messaging.js b/src/js/messaging.js index ebfc5c7d44fed..0142a1261ea44 100644 --- a/src/js/messaging.js +++ b/src/js/messaging.js @@ -288,9 +288,8 @@ const getHostnameDict = function(hostnameDetailsMap, out) { const cnMap = []; const createDictEntry = (domain, hostname, details) => { - const cname = vAPI.net.canonicalNameFromHostname(hostname); - if ( cname !== undefined ) { - cnMap.push([ cname, hostname ]); + if ( details.cname ) { + cnMap.push([ details.cname, hostname ]); } hnDict[hostname] = { domain, counts: details.counts }; }; diff --git a/src/js/pagestore.js b/src/js/pagestore.js index 227352d5d97e6..d6f52b8cd12ea 100644 --- a/src/js/pagestore.js +++ b/src/js/pagestore.js @@ -312,6 +312,7 @@ const HostnameDetails = class { } init(hostname) { this.hostname = hostname; + this.cname = vAPI.net.canonicalNameFromHostname(hostname); this.counts.reset(); } dispose() { From 4b285c059329fe2dc4e13a5473d813bb3f159649 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sat, 14 Sep 2024 12:13:47 -0400 Subject: [PATCH 0223/1099] Dynamically load DNR conversion module on-demand DNR conversion module is a dev tool, no point loading it by default. This was done this way because in a distant past uBO had to be compatible with browsers not supporting dynamically loaded modules. Currently all supported browser versions support dynamically loaded modules. --- src/js/devtools.js | 97 +++++++++++++++++++++++++++++++++++++++++++- src/js/messaging.js | 99 +++++---------------------------------------- 2 files changed, 105 insertions(+), 91 deletions(-) diff --git a/src/js/devtools.js b/src/js/devtools.js index 629cd36b17d10..92cd078940f80 100644 --- a/src/js/devtools.js +++ b/src/js/devtools.js @@ -21,6 +21,7 @@ /* global CodeMirror, uBlockDashboard */ +import * as s14e from './s14e-serializer.js'; import { dom, qs$ } from './dom.js'; /******************************************************************************/ @@ -79,6 +80,100 @@ function log(text) { cmEditor.replaceRange(text.trim() + '\n\n', { line: 0, ch: 0 }); } +/******************************************************************************/ + +function toDNRText(raw) { + const result = s14e.deserialize(raw); + if ( typeof result === 'string' ) { return result; } + const { network } = result; + const replacer = (k, v) => { + if ( k.startsWith('__') ) { return; } + if ( Array.isArray(v) ) { + return v.sort(); + } + if ( v instanceof Object ) { + const sorted = {}; + for ( const kk of Object.keys(v).sort() ) { + sorted[kk] = v[kk]; + } + return sorted; + } + return v; + }; + const isUnsupported = rule => + rule._error !== undefined; + const isRegex = rule => + rule.condition !== undefined && + rule.condition.regexFilter !== undefined; + const isRedirect = rule => + rule.action !== undefined && + rule.action.type === 'redirect' && + rule.action.redirect.extensionPath !== undefined; + const isCsp = rule => + rule.action !== undefined && + rule.action.type === 'modifyHeaders'; + const isRemoveparam = rule => + rule.action !== undefined && + rule.action.type === 'redirect' && + rule.action.redirect.transform !== undefined; + const { ruleset } = network; + const good = ruleset.filter(rule => + isUnsupported(rule) === false && + isRegex(rule) === false && + isRedirect(rule) === false && + isCsp(rule) === false && + isRemoveparam(rule) === false + ); + const unsupported = ruleset.filter(rule => + isUnsupported(rule) + ); + const regexes = ruleset.filter(rule => + isUnsupported(rule) === false && + isRegex(rule) && + isRedirect(rule) === false && + isCsp(rule) === false && + isRemoveparam(rule) === false + ); + const redirects = ruleset.filter(rule => + isUnsupported(rule) === false && + isRedirect(rule) + ); + const headers = ruleset.filter(rule => + isUnsupported(rule) === false && + isCsp(rule) + ); + const removeparams = ruleset.filter(rule => + isUnsupported(rule) === false && + isRemoveparam(rule) + ); + const out = [ + `dnrRulesetFromRawLists(${JSON.stringify(result.listNames, null, 2)})`, + `Run time: ${result.runtime} ms`, + `Filters count: ${network.filterCount}`, + `Accepted filter count: ${network.acceptedFilterCount}`, + `Rejected filter count: ${network.rejectedFilterCount}`, + `Un-DNR-able filter count: ${unsupported.length}`, + `Resulting DNR rule count: ${ruleset.length}`, + ]; + out.push(`+ Good filters (${good.length}): ${JSON.stringify(good, replacer, 2)}`); + out.push(`+ Regex-based filters (${regexes.length}): ${JSON.stringify(regexes, replacer, 2)}`); + out.push(`+ 'redirect=' filters (${redirects.length}): ${JSON.stringify(redirects, replacer, 2)}`); + out.push(`+ 'csp=' filters (${headers.length}): ${JSON.stringify(headers, replacer, 2)}`); + out.push(`+ 'removeparam=' filters (${removeparams.length}): ${JSON.stringify(removeparams, replacer, 2)}`); + out.push(`+ Unsupported filters (${unsupported.length}): ${JSON.stringify(unsupported, replacer, 2)}`); + out.push(`+ generichide exclusions (${network.generichideExclusions.length}): ${JSON.stringify(network.generichideExclusions, replacer, 2)}`); + if ( result.specificCosmetic ) { + out.push(`+ Cosmetic filters: ${result.specificCosmetic.size}`); + for ( const details of result.specificCosmetic ) { + out.push(` ${JSON.stringify(details)}`); + } + } else { + out.push(' Cosmetic filters: 0'); + } + return out.join('\n'); +} + + /******************************************************************************/ dom.on('#console-clear', 'click', ( ) => { @@ -146,7 +241,7 @@ dom.on('#snfe-todnr', 'click', ev => { vAPI.messaging.send('devTools', { what: 'snfeToDNR', }).then(result => { - log(result); + log(toDNRText(result)); dom.attr(button, 'disabled', null); }); }); diff --git a/src/js/messaging.js b/src/js/messaging.js index 0142a1261ea44..d35a1347e782e 100644 --- a/src/js/messaging.js +++ b/src/js/messaging.js @@ -42,7 +42,6 @@ import { import cacheStorage from './cachestorage.js'; import cosmeticFilteringEngine from './cosmetic-filtering.js'; import { denseBase64 } from './base64-custom.js'; -import { dnrRulesetFromRawLists } from './static-dnr-filtering.js'; import { filteringBehaviorChanged } from './broadcast.js'; import htmlFilteringEngine from './html-filtering.js'; import { i18n$ } from './i18n.js'; @@ -1897,95 +1896,15 @@ const onMessage = function(request, sender, callback) { ), env: vAPI.webextFlavor.env, }; - const t0 = Date.now(); - dnrRulesetFromRawLists(listPromises, options).then(result => { - const { network } = result; - const replacer = (k, v) => { - if ( k.startsWith('__') ) { return; } - if ( Array.isArray(v) ) { - return v.sort(); - } - if ( v instanceof Object ) { - const sorted = {}; - for ( const kk of Object.keys(v).sort() ) { - sorted[kk] = v[kk]; - } - return sorted; - } - return v; - }; - const isUnsupported = rule => - rule._error !== undefined; - const isRegex = rule => - rule.condition !== undefined && - rule.condition.regexFilter !== undefined; - const isRedirect = rule => - rule.action !== undefined && - rule.action.type === 'redirect' && - rule.action.redirect.extensionPath !== undefined; - const isCsp = rule => - rule.action !== undefined && - rule.action.type === 'modifyHeaders'; - const isRemoveparam = rule => - rule.action !== undefined && - rule.action.type === 'redirect' && - rule.action.redirect.transform !== undefined; - const runtime = Date.now() - t0; - const { ruleset } = network; - const good = ruleset.filter(rule => - isUnsupported(rule) === false && - isRegex(rule) === false && - isRedirect(rule) === false && - isCsp(rule) === false && - isRemoveparam(rule) === false - ); - const unsupported = ruleset.filter(rule => - isUnsupported(rule) - ); - const regexes = ruleset.filter(rule => - isUnsupported(rule) === false && - isRegex(rule) && - isRedirect(rule) === false && - isCsp(rule) === false && - isRemoveparam(rule) === false - ); - const redirects = ruleset.filter(rule => - isUnsupported(rule) === false && - isRedirect(rule) - ); - const headers = ruleset.filter(rule => - isUnsupported(rule) === false && - isCsp(rule) - ); - const removeparams = ruleset.filter(rule => - isUnsupported(rule) === false && - isRemoveparam(rule) - ); - const out = [ - `dnrRulesetFromRawLists(${JSON.stringify(listNames, null, 2)})`, - `Run time: ${runtime} ms`, - `Filters count: ${network.filterCount}`, - `Accepted filter count: ${network.acceptedFilterCount}`, - `Rejected filter count: ${network.rejectedFilterCount}`, - `Un-DNR-able filter count: ${unsupported.length}`, - `Resulting DNR rule count: ${ruleset.length}`, - ]; - out.push(`+ Good filters (${good.length}): ${JSON.stringify(good, replacer, 2)}`); - out.push(`+ Regex-based filters (${regexes.length}): ${JSON.stringify(regexes, replacer, 2)}`); - out.push(`+ 'redirect=' filters (${redirects.length}): ${JSON.stringify(redirects, replacer, 2)}`); - out.push(`+ 'csp=' filters (${headers.length}): ${JSON.stringify(headers, replacer, 2)}`); - out.push(`+ 'removeparam=' filters (${removeparams.length}): ${JSON.stringify(removeparams, replacer, 2)}`); - out.push(`+ Unsupported filters (${unsupported.length}): ${JSON.stringify(unsupported, replacer, 2)}`); - out.push(`+ generichide exclusions (${network.generichideExclusions.length}): ${JSON.stringify(network.generichideExclusions, replacer, 2)}`); - if ( result.specificCosmetic ) { - out.push(`+ Cosmetic filters: ${result.specificCosmetic.size}`); - for ( const details of result.specificCosmetic ) { - out.push(` ${JSON.stringify(details)}`); - } - } else { - out.push(' Cosmetic filters: 0'); - } - callback(out.join('\n')); + import('./static-dnr-filtering.js').then(module => { + const t0 = Date.now(); + module.dnrRulesetFromRawLists(listPromises, options).then(dnrdata => { + dnrdata.listNames = listNames; + dnrdata.runtime = Date.now() - t0; + callback(s14e.serialize(dnrdata)); + }) + }).catch(reason => { + callback(reason); }); return; } From 266ec4894b09e3686225f044daa29fe94b0b14cf Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sun, 15 Sep 2024 09:17:19 -0400 Subject: [PATCH 0224/1099] New static network filter option `urlskip=` Related issue: https://github.com/uBlockOrigin/uBlock-issues/issues/3206 The main purpose is to bypass URLs designed to track whether a user visited a specific URL, typically used in click-tracking links. The `urlskip=` option ... - ... is valid only when used in a trusted filter list - ... is enforced only on top documents - ... is enforced on both blocked and non-blocked documents - ... is a modifier, i.e. it cannot be used along with other modifier options in a single filter The syntax is `urlskip=[steps]`, where steps is a space-separated list of extraction directives detailing what action to perform on the current URL. The only supported directive in this first commit is `?name`, which purpose is to extract the value of a named URL parameter and use the result as the new URL. Example: ||example.com/path/to/tracker$urlskip=?url The above filter will cause navigation to https://example.com/path/to/tracker?url=https://example.org/ to automatically bypass navigation to `example.com` and navigate directly to https://example.org/ It is possible to recursively extract URL parameters by using more than one directive, example: ||example.com/path/to/tracker$urlskip=?url ?to More extraction capabilities may be added in the future. --- src/js/filtering-context.js | 3 + src/js/logger-ui.js | 2 +- src/js/pagestore.js | 32 ++-- src/js/static-filtering-parser.js | 19 +++ src/js/static-net-filtering.js | 275 ++++++++++++++++++------------ src/js/traffic.js | 11 +- 6 files changed, 215 insertions(+), 127 deletions(-) diff --git a/src/js/filtering-context.js b/src/js/filtering-context.js index 933785f55114a..a24cdabc5da0b 100644 --- a/src/js/filtering-context.js +++ b/src/js/filtering-context.js @@ -163,6 +163,9 @@ export const FilteringContext = class { this.stype = a; } + isRootDocument() { + return (this.itype & MAIN_FRAME) !== 0; + } isDocument() { return (this.itype & FRAME_ANY) !== 0; } diff --git a/src/js/logger-ui.js b/src/js/logger-ui.js index db95f2e114aba..e76586a50f854 100644 --- a/src/js/logger-ui.js +++ b/src/js/logger-ui.js @@ -331,7 +331,7 @@ const processLoggerEntries = function(response) { parsed.type === 'main_frame' && parsed.aliased === false && ( parsed.filter === undefined || - parsed.filter.modifier !== true + parsed.filter.modifier !== true && parsed.filter.source !== 'redirect' ) ) { const separator = createLogSeparator(parsed, unboxed.url); diff --git a/src/js/pagestore.js b/src/js/pagestore.js index d6f52b8cd12ea..e201496366e3f 100644 --- a/src/js/pagestore.js +++ b/src/js/pagestore.js @@ -933,19 +933,14 @@ const PageStore = class { } redirectNonBlockedRequest(fctxt) { - const transformDirectives = staticNetFilteringEngine.transformRequest(fctxt); - const pruneDirectives = fctxt.redirectURL === undefined && - staticNetFilteringEngine.hasQuery(fctxt) && - staticNetFilteringEngine.filterQuery(fctxt) || - undefined; - if ( transformDirectives === undefined && pruneDirectives === undefined ) { return; } - if ( logger.enabled !== true ) { return; } - if ( transformDirectives !== undefined ) { - fctxt.pushFilters(transformDirectives.map(a => a.logData())); - } - if ( pruneDirectives !== undefined ) { - fctxt.pushFilters(pruneDirectives.map(a => a.logData())); + const directives = []; + staticNetFilteringEngine.transformRequest(fctxt, directives); + if ( staticNetFilteringEngine.hasQuery(fctxt) ) { + staticNetFilteringEngine.filterQuery(fctxt, directives); } + if ( directives.length === 0 ) { return; } + if ( logger.enabled !== true ) { return; } + fctxt.pushFilters(directives.map(a => a.logData())); if ( fctxt.redirectURL === undefined ) { return; } fctxt.pushFilter({ source: 'redirect', @@ -953,6 +948,19 @@ const PageStore = class { }); } + skipMainDocument(fctxt) { + const directives = staticNetFilteringEngine.urlSkip(fctxt); + if ( directives === undefined ) { return; } + if ( logger.enabled !== true ) { return; } + fctxt.pushFilters(directives.map(a => a.logData())); + if ( fctxt.redirectURL !== undefined ) { + fctxt.pushFilter({ + source: 'redirect', + raw: fctxt.redirectURL + }); + } + } + filterCSPReport(fctxt) { if ( sessionSwitches.evaluateZ( diff --git a/src/js/static-filtering-parser.js b/src/js/static-filtering-parser.js index ee3318240dd41..db5b2bd132e03 100644 --- a/src/js/static-filtering-parser.js +++ b/src/js/static-filtering-parser.js @@ -191,6 +191,7 @@ export const NODE_TYPE_NET_OPTION_NAME_REPLACE = iota++; export const NODE_TYPE_NET_OPTION_NAME_SCRIPT = iota++; export const NODE_TYPE_NET_OPTION_NAME_SHIDE = iota++; export const NODE_TYPE_NET_OPTION_NAME_TO = iota++; +export const NODE_TYPE_NET_OPTION_NAME_URLSKIP = iota++; export const NODE_TYPE_NET_OPTION_NAME_URLTRANSFORM = iota++; export const NODE_TYPE_NET_OPTION_NAME_XHR = iota++; export const NODE_TYPE_NET_OPTION_NAME_WEBRTC = iota++; @@ -274,6 +275,7 @@ export const nodeTypeFromOptionName = new Map([ [ 'shide', NODE_TYPE_NET_OPTION_NAME_SHIDE ], /* synonym */ [ 'specifichide', NODE_TYPE_NET_OPTION_NAME_SHIDE ], [ 'to', NODE_TYPE_NET_OPTION_NAME_TO ], + [ 'urlskip', NODE_TYPE_NET_OPTION_NAME_URLSKIP ], [ 'uritransform', NODE_TYPE_NET_OPTION_NAME_URLTRANSFORM ], [ 'xhr', NODE_TYPE_NET_OPTION_NAME_XHR ], /* synonym */ [ 'xmlhttprequest', NODE_TYPE_NET_OPTION_NAME_XHR ], @@ -1441,6 +1443,7 @@ export class AstFilterParser { case NODE_TYPE_NET_OPTION_NAME_REDIRECT: case NODE_TYPE_NET_OPTION_NAME_REDIRECTRULE: case NODE_TYPE_NET_OPTION_NAME_REPLACE: + case NODE_TYPE_NET_OPTION_NAME_URLSKIP: case NODE_TYPE_NET_OPTION_NAME_URLTRANSFORM: realBad = isNegated || (isException || hasValue) === false || modifierType !== 0; @@ -1519,6 +1522,21 @@ export class AstFilterParser { } break; } + case NODE_TYPE_NET_OPTION_NAME_URLSKIP: { + realBad = abstractTypeCount || behaviorTypeCount || unredirectableTypeCount; + if ( realBad ) { break; } + if ( requiresTrustedSource() ) { + this.astError = AST_ERROR_UNTRUSTED_SOURCE; + realBad = true; + break; + } + const value = this.getNetOptionValue(NODE_TYPE_NET_OPTION_NAME_URLSKIP); + if ( value.startsWith('?') === false || value.length < 2 ) { + this.astError = AST_ERROR_OPTION_BADVALUE; + realBad = true; + } + break; + } case NODE_TYPE_NET_OPTION_NAME_URLTRANSFORM: { realBad = abstractTypeCount || behaviorTypeCount || unredirectableTypeCount; if ( realBad ) { break; } @@ -3139,6 +3157,7 @@ export const netOptionTokenDescriptors = new Map([ [ 'shide', { } ], /* synonym */ [ 'specifichide', { } ], [ 'to', { mustAssign: true } ], + [ 'urlskip', { mustAssign: true } ], [ 'uritransform', { mustAssign: true } ], [ 'xhr', { canNegate: true } ], /* synonym */ [ 'xmlhttprequest', { canNegate: true } ], diff --git a/src/js/static-net-filtering.js b/src/js/static-net-filtering.js index 780015209f68b..87216156f7018 100644 --- a/src/js/static-net-filtering.js +++ b/src/js/static-net-filtering.js @@ -43,97 +43,99 @@ const keyvalStore = typeof vAPI !== 'undefined' /******************************************************************************/ -// 0fedcba9876543210 -// ||||||| | || | -// ||||||| | || | -// ||||||| | || | -// ||||||| | || | -// ||||||| | || +---- bit 0- 1: block=0, allow=1, block important=2 -// ||||||| | |+------ bit 2: unused -// ||||||| | +------- bit 3- 4: party [0-3] -// ||||||| +--------- bit 5- 9: type [0-31] -// ||||||+-------------- bit 10: headers-based filters -// |||||+--------------- bit 11: redirect filters -// ||||+---------------- bit 12: removeparam filters -// |||+----------------- bit 13: csp filters -// ||+------------------ bit 14: permissions filters -// |+------------------- bit 15: uritransform filters -// +-------------------- bit 16: replace filters -// TODO: bit 11-16 can be converted into 3-bit value, as these options are not +// 10fedcba9876543210 +// |||||||| | || | +// |||||||| | || | +// |||||||| | || | +// |||||||| | || | +// |||||||| | || +---- bit 0- 1: block=0, allow=1, block important=2 +// |||||||| | |+------ bit 2: unused +// |||||||| | +------- bit 3- 4: party [0-3] +// |||||||| +--------- bit 5- 9: type [0-31] +// |||||||+-------------- bit 10: headers-based filters +// ||||||+--------------- bit 11: redirect filters +// |||||+---------------- bit 12: removeparam filters +// ||||+----------------- bit 13: csp filters +// |||+------------------ bit 14: permissions filters +// ||+------------------- bit 15: uritransform filters +// |+-------------------- bit 16: replace filters +// +--------------------- bit 17: urlskip filters +// TODO: bit 11-17 could be converted into 3-bit value, as these options are not // meant to be combined. -const RealmBitsMask = 0b00000000111; -const ActionBitsMask = 0b00000000011; -const TypeBitsMask = 0b01111100000; -const TypeBitsOffset = 5; - -const BLOCK_REALM = 0b00000000000000000; -const ALLOW_REALM = 0b00000000000000001; -const IMPORTANT_REALM = 0b00000000000000010; +const BLOCK_REALM = 0b0000_0000_0000_0000_0000; +const ALLOW_REALM = 0b0000_0000_0000_0000_0001; +const IMPORTANT_REALM = 0b0000_0000_0000_0000_0010; +const BLOCKALLOW_REALM = BLOCK_REALM | ALLOW_REALM | IMPORTANT_REALM; const BLOCKIMPORTANT_REALM = BLOCK_REALM | IMPORTANT_REALM; -const ANYPARTY_REALM = 0b00000000000000000; -const FIRSTPARTY_REALM = 0b00000000000001000; -const THIRDPARTY_REALM = 0b00000000000010000; +const ANYPARTY_REALM = 0b0000_0000_0000_0000_0000; +const FIRSTPARTY_REALM = 0b0000_0000_0000_0000_1000; +const THIRDPARTY_REALM = 0b0000_0000_0000_0001_0000; const ALLPARTIES_REALM = FIRSTPARTY_REALM | THIRDPARTY_REALM; -const HEADERS_REALM = 0b00000010000000000; -const REDIRECT_REALM = 0b00000100000000000; -const REMOVEPARAM_REALM = 0b00001000000000000; -const CSP_REALM = 0b00010000000000000; -const PERMISSIONS_REALM = 0b00100000000000000; -const URLTRANSFORM_REALM = 0b01000000000000000; -const REPLACE_REALM = 0b10000000000000000; +const TYPE_REALM = 0b0000_0000_0011_1110_0000; +const HEADERS_REALM = 0b0000_0000_0100_0000_0000; +const REDIRECT_REALM = 0b0000_0000_1000_0000_0000; +const REMOVEPARAM_REALM = 0b0000_0001_0000_0000_0000; +const CSP_REALM = 0b0000_0010_0000_0000_0000; +const PERMISSIONS_REALM = 0b0000_0100_0000_0000_0000; +const URLTRANSFORM_REALM = 0b0000_1000_0000_0000_0000; +const REPLACE_REALM = 0b0001_0000_0000_0000_0000; +const URLSKIP_REALM = 0b0010_0000_0000_0000_0000; const MODIFY_REALMS = REDIRECT_REALM | CSP_REALM | REMOVEPARAM_REALM | PERMISSIONS_REALM | - URLTRANSFORM_REALM | REPLACE_REALM; + URLTRANSFORM_REALM | REPLACE_REALM | + URLSKIP_REALM; + +const TYPE_REALM_OFFSET = 5; const typeNameToTypeValue = { - 'no_type': 0 << TypeBitsOffset, - 'stylesheet': 1 << TypeBitsOffset, - 'image': 2 << TypeBitsOffset, - 'object': 3 << TypeBitsOffset, - 'object_subrequest': 3 << TypeBitsOffset, - 'script': 4 << TypeBitsOffset, - 'fetch': 5 << TypeBitsOffset, - 'xmlhttprequest': 5 << TypeBitsOffset, - 'sub_frame': 6 << TypeBitsOffset, - 'font': 7 << TypeBitsOffset, - 'media': 8 << TypeBitsOffset, - 'websocket': 9 << TypeBitsOffset, - 'beacon': 10 << TypeBitsOffset, - 'ping': 10 << TypeBitsOffset, - 'other': 11 << TypeBitsOffset, - 'popup': 12 << TypeBitsOffset, // start of behavioral filtering - 'popunder': 13 << TypeBitsOffset, - 'main_frame': 14 << TypeBitsOffset, // start of 1p behavioral filtering - 'generichide': 15 << TypeBitsOffset, - 'specifichide': 16 << TypeBitsOffset, - 'inline-font': 17 << TypeBitsOffset, - 'inline-script': 18 << TypeBitsOffset, - 'cname': 19 << TypeBitsOffset, - 'webrtc': 20 << TypeBitsOffset, - 'unsupported': 21 << TypeBitsOffset, + 'no_type': 0 << TYPE_REALM_OFFSET, + 'stylesheet': 1 << TYPE_REALM_OFFSET, + 'image': 2 << TYPE_REALM_OFFSET, + 'object': 3 << TYPE_REALM_OFFSET, + 'object_subrequest': 3 << TYPE_REALM_OFFSET, + 'script': 4 << TYPE_REALM_OFFSET, + 'fetch': 5 << TYPE_REALM_OFFSET, + 'xmlhttprequest': 5 << TYPE_REALM_OFFSET, + 'sub_frame': 6 << TYPE_REALM_OFFSET, + 'font': 7 << TYPE_REALM_OFFSET, + 'media': 8 << TYPE_REALM_OFFSET, + 'websocket': 9 << TYPE_REALM_OFFSET, + 'beacon': 10 << TYPE_REALM_OFFSET, + 'ping': 10 << TYPE_REALM_OFFSET, + 'other': 11 << TYPE_REALM_OFFSET, + 'popup': 12 << TYPE_REALM_OFFSET, // start of behavioral filtering + 'popunder': 13 << TYPE_REALM_OFFSET, + 'main_frame': 14 << TYPE_REALM_OFFSET, // start of 1p behavioral filtering + 'generichide': 15 << TYPE_REALM_OFFSET, + 'specifichide': 16 << TYPE_REALM_OFFSET, + 'inline-font': 17 << TYPE_REALM_OFFSET, + 'inline-script': 18 << TYPE_REALM_OFFSET, + 'cname': 19 << TYPE_REALM_OFFSET, + 'webrtc': 20 << TYPE_REALM_OFFSET, + 'unsupported': 21 << TYPE_REALM_OFFSET, }; const otherTypeBitValue = typeNameToTypeValue.other; const bitFromType = type => - 1 << ((typeNameToTypeValue[type] >>> TypeBitsOffset) - 1); + 1 << ((typeNameToTypeValue[type] >>> TYPE_REALM_OFFSET) - 1); // All network request types to bitmap -// bring origin to 0 (from TypeBitsOffset -- see typeNameToTypeValue) +// bring origin to 0 (from TYPE_REALM_OFFSET -- see typeNameToTypeValue) // left-shift 1 by the above-calculated value // subtract 1 to set all type bits const allNetworkTypesBits = - (1 << (otherTypeBitValue >>> TypeBitsOffset)) - 1; + (1 << (otherTypeBitValue >>> TYPE_REALM_OFFSET)) - 1; const allTypesBits = allNetworkTypesBits | - 1 << (typeNameToTypeValue['popup'] >>> TypeBitsOffset) - 1 | - 1 << (typeNameToTypeValue['main_frame'] >>> TypeBitsOffset) - 1 | - 1 << (typeNameToTypeValue['inline-font'] >>> TypeBitsOffset) - 1 | - 1 << (typeNameToTypeValue['inline-script'] >>> TypeBitsOffset) - 1; + 1 << (typeNameToTypeValue['popup'] >>> TYPE_REALM_OFFSET) - 1 | + 1 << (typeNameToTypeValue['main_frame'] >>> TYPE_REALM_OFFSET) - 1 | + 1 << (typeNameToTypeValue['inline-font'] >>> TYPE_REALM_OFFSET) - 1 | + 1 << (typeNameToTypeValue['inline-script'] >>> TYPE_REALM_OFFSET) - 1; const unsupportedTypeBit = - 1 << (typeNameToTypeValue['unsupported'] >>> TypeBitsOffset) - 1; + 1 << (typeNameToTypeValue['unsupported'] >>> TYPE_REALM_OFFSET) - 1; const typeValueToTypeName = [ '', @@ -186,6 +188,7 @@ const MODIFIER_TYPE_CSP = 4; const MODIFIER_TYPE_PERMISSIONS = 5; const MODIFIER_TYPE_URLTRANSFORM = 6; const MODIFIER_TYPE_REPLACE = 7; +const MODIFIER_TYPE_URLSKIP = 8; const modifierBitsFromType = new Map([ [ MODIFIER_TYPE_REDIRECT, REDIRECT_REALM ], @@ -195,6 +198,7 @@ const modifierBitsFromType = new Map([ [ MODIFIER_TYPE_PERMISSIONS, PERMISSIONS_REALM ], [ MODIFIER_TYPE_URLTRANSFORM, URLTRANSFORM_REALM ], [ MODIFIER_TYPE_REPLACE, REPLACE_REALM ], + [ MODIFIER_TYPE_URLSKIP, URLSKIP_REALM ], ]); const modifierTypeFromName = new Map([ @@ -205,6 +209,7 @@ const modifierTypeFromName = new Map([ [ 'permissions', MODIFIER_TYPE_PERMISSIONS ], [ 'uritransform', MODIFIER_TYPE_URLTRANSFORM ], [ 'replace', MODIFIER_TYPE_REPLACE ], + [ 'urlskip', MODIFIER_TYPE_URLSKIP ], ]); const modifierNameFromType = new Map([ @@ -215,22 +220,23 @@ const modifierNameFromType = new Map([ [ MODIFIER_TYPE_PERMISSIONS, 'permissions' ], [ MODIFIER_TYPE_URLTRANSFORM, 'uritransform' ], [ MODIFIER_TYPE_REPLACE, 'replace' ], + [ MODIFIER_TYPE_URLSKIP, 'urlskip' ], ]); -//const typeValueFromCatBits = catBits => (catBits >>> TypeBitsOffset) & 0b11111; +//const typeValueFromCatBits = catBits => (catBits >>> TYPE_REALM_OFFSET) & 0b11111; const MAX_TOKEN_LENGTH = 7; // Four upper bits of token hash are reserved for built-in predefined // token hashes, which should never end up being used when tokenizing // any arbitrary string. -const NO_TOKEN_HASH = 0x50000000; -const DOT_TOKEN_HASH = 0x10000000; -const ANY_TOKEN_HASH = 0x20000000; -const ANY_HTTPS_TOKEN_HASH = 0x30000000; -const ANY_HTTP_TOKEN_HASH = 0x40000000; -const EMPTY_TOKEN_HASH = 0xF0000000; -const INVALID_TOKEN_HASH = 0xFFFFFFFF; +const NO_TOKEN_HASH = 0x5000_0000; +const DOT_TOKEN_HASH = 0x1000_0000; +const ANY_TOKEN_HASH = 0x2000_0000; +const ANY_HTTPS_TOKEN_HASH = 0x3000_0000; +const ANY_HTTP_TOKEN_HASH = 0x4000_0000; +const EMPTY_TOKEN_HASH = 0xF000_0000; +const INVALID_TOKEN_HASH = 0xFFFF_FFFF; /******************************************************************************/ @@ -374,9 +380,9 @@ class LogData { } else if ( (categoryBits & FIRSTPARTY_REALM) !== 0 ) { logData.options.unshift('1p'); } - const type = categoryBits & TypeBitsMask; + const type = categoryBits & TYPE_REALM; if ( type !== 0 ) { - logData.options.unshift(typeValueToTypeName[type >>> TypeBitsOffset]); + logData.options.unshift(typeValueToTypeName[type >>> TYPE_REALM_OFFSET]); } let raw = logData.pattern.join(''); if ( @@ -2163,7 +2169,7 @@ class FilterModifierResult { this.refs = filterRefs[filterData[imodifierunit+3]]; this.ireportedunit = env.iunit; this.th = env.th; - this.bits = (env.bits & ~RealmBitsMask) | filterData[imodifierunit+1]; + this.bits = (env.bits & ~BLOCKALLOW_REALM) | filterData[imodifierunit+1]; } get result() { @@ -3276,6 +3282,7 @@ class FilterCompiler { [ sfp.NODE_TYPE_NET_OPTION_NAME_REMOVEPARAM, MODIFIER_TYPE_REMOVEPARAM ], [ sfp.NODE_TYPE_NET_OPTION_NAME_URLTRANSFORM, MODIFIER_TYPE_URLTRANSFORM ], [ sfp.NODE_TYPE_NET_OPTION_NAME_REPLACE, MODIFIER_TYPE_REPLACE ], + [ sfp.NODE_TYPE_NET_OPTION_NAME_URLSKIP, MODIFIER_TYPE_URLSKIP ], ]); // These top 100 "bad tokens" are collated using the "miss" histogram // from tokenHistograms(). The "score" is their occurrence among the @@ -3548,6 +3555,7 @@ class FilterCompiler { case sfp.NODE_TYPE_NET_OPTION_NAME_REDIRECTRULE: case sfp.NODE_TYPE_NET_OPTION_NAME_REMOVEPARAM: case sfp.NODE_TYPE_NET_OPTION_NAME_REPLACE: + case sfp.NODE_TYPE_NET_OPTION_NAME_URLSKIP: case sfp.NODE_TYPE_NET_OPTION_NAME_URLTRANSFORM: if ( this.processModifierOption(id, parser.getNetOptionValue(id)) === false ) { return false; @@ -3667,6 +3675,7 @@ class FilterCompiler { case sfp.NODE_TYPE_NET_OPTION_NAME_REMOVEPARAM: case sfp.NODE_TYPE_NET_OPTION_NAME_REPLACE: case sfp.NODE_TYPE_NET_OPTION_NAME_TO: + case sfp.NODE_TYPE_NET_OPTION_NAME_URLSKIP: case sfp.NODE_TYPE_NET_OPTION_NAME_URLTRANSFORM: if ( this.processOptionWithValue(parser, type) === false ) { return this.FILTER_INVALID; @@ -4054,7 +4063,7 @@ class FilterCompiler { // IMPORTANT: the modifier unit MUST always appear first in a sequence if ( this.modifyType !== undefined ) { units.unshift(FilterModifier.compile(this)); - this.action = (this.action & ~ActionBitsMask) | + this.action = (this.action & ~BLOCKALLOW_REALM) | modifierBitsFromType.get(this.modifyType); } @@ -4123,7 +4132,7 @@ class FilterCompiler { do { if ( typeBits & 1 ) { writer.push([ - catBits | (bitOffset << TypeBitsOffset), + catBits | (bitOffset << TYPE_REALM_OFFSET), this.tokenHash, fdata ]); @@ -4286,7 +4295,7 @@ StaticNetFilteringEngine.prototype.freeze = function() { // the block-important realm should be checked when and only when // there is a matched exception filter, which important filters are // meant to override. - if ( (bits & ActionBitsMask) === BLOCKIMPORTANT_REALM ) { + if ( (bits & BLOCKALLOW_REALM) === BLOCKIMPORTANT_REALM ) { this.addFilterUnit( bits & ~IMPORTANT_REALM, tokenHash, @@ -4446,6 +4455,7 @@ StaticNetFilteringEngine.prototype.dnrFromCompiled = function(op, context, ...ar [ PERMISSIONS_REALM, { type: 'permissions', priority: 0 } ], [ URLTRANSFORM_REALM, { type: 'uritransform', priority: 0 } ], [ HEADERS_REALM, { type: 'block', priority: 0 } ], + [ URLSKIP_REALM, { type: 'urlskip', priority: 0 } ], ]); const partyness = new Map([ [ ANYPARTY_REALM, '' ], @@ -4860,7 +4870,7 @@ StaticNetFilteringEngine.prototype.matchAndFetchModifiers = function( $docDomain = fctxt.getDocDomain(); $requestHostname = fctxt.getHostname(); $requestMethodBit = fctxt.method || 0; - $requestTypeValue = (typeBits & TypeBitsMask) >>> TypeBitsOffset; + $requestTypeValue = (typeBits & TYPE_REALM) >>> TYPE_REALM_OFFSET; $requestAddress = fctxt.getIPAddress(); const modifierType = modifierTypeFromName.get(modifierName); @@ -4955,7 +4965,7 @@ StaticNetFilteringEngine.prototype.matchAndFetchModifiers = function( const toRemove = new Map(); for ( const result of results ) { - const actionBits = result.bits & ActionBitsMask; + const actionBits = result.bits & BLOCKALLOW_REALM; const modifyValue = result.value; if ( actionBits === BLOCKIMPORTANT_REALM ) { toAddImportant.set(modifyValue, result); @@ -5158,7 +5168,7 @@ StaticNetFilteringEngine.prototype.matchRequestReverse = function(type, url) { $requestURL = urlTokenizer.setURL(url); $requestURLRaw = url; $requestMethodBit = 0; - $requestTypeValue = (typeBits & TypeBitsMask) >>> TypeBitsOffset; + $requestTypeValue = (typeBits & TYPE_REALM) >>> TYPE_REALM_OFFSET; $requestAddress = ''; $isBlockImportant = false; this.$filterUnit = 0; @@ -5227,7 +5237,7 @@ StaticNetFilteringEngine.prototype.matchRequest = function(fctxt, modifiers = 0) $docDomain = fctxt.getDocDomain(); $requestHostname = fctxt.getHostname(); $requestMethodBit = fctxt.method || 0; - $requestTypeValue = (typeBits & TypeBitsMask) >>> TypeBitsOffset; + $requestTypeValue = (typeBits & TYPE_REALM) >>> TYPE_REALM_OFFSET; $requestAddress = fctxt.getIPAddress(); $isBlockImportant = false; @@ -5263,7 +5273,7 @@ StaticNetFilteringEngine.prototype.matchHeaders = function(fctxt, headers) { $docDomain = fctxt.getDocDomain(); $requestHostname = fctxt.getHostname(); $requestMethodBit = fctxt.method || 0; - $requestTypeValue = (typeBits & TypeBitsMask) >>> TypeBitsOffset; + $requestTypeValue = (typeBits & TYPE_REALM) >>> TYPE_REALM_OFFSET; $requestAddress = fctxt.getIPAddress(); $httpHeaders.init(headers); @@ -5309,11 +5319,35 @@ StaticNetFilteringEngine.prototype.redirectRequest = function(redirectEngine, fc return directives; }; -StaticNetFilteringEngine.prototype.transformRequest = function(fctxt) { +function parseRedirectRequestValue(directive) { + if ( directive.cache === null ) { + directive.cache = sfp.parseRedirectValue(directive.value); + } + return directive.cache; +} + +function compareRedirectRequests(redirectEngine, a, b) { + const { token: atok, priority: aint, bits: abits } = + parseRedirectRequestValue(a); + if ( redirectEngine.hasToken(atok) === false ) { return -1; } + const { token: btok, priority: bint, bits: bbits } = + parseRedirectRequestValue(b); + if ( redirectEngine.hasToken(btok) === false ) { return 1; } + if ( abits !== bbits ) { + if ( (abits & IMPORTANT_REALM) !== 0 ) { return 1; } + if ( (bbits & IMPORTANT_REALM) !== 0 ) { return -1; } + if ( (abits & ALLOW_REALM) !== 0 ) { return -1; } + if ( (bbits & ALLOW_REALM) !== 0 ) { return 1; } + } + return aint - bint; +} + +/******************************************************************************/ + +StaticNetFilteringEngine.prototype.transformRequest = function(fctxt, out = []) { const directives = this.matchAndFetchModifiers(fctxt, 'uritransform'); if ( directives === undefined ) { return; } const redirectURL = new URL(fctxt.url); - const out = []; for ( const directive of directives ) { if ( (directive.bits & ALLOW_REALM) !== 0 ) { out.push(directive); @@ -5345,27 +5379,47 @@ StaticNetFilteringEngine.prototype.transformRequest = function(fctxt) { return out; }; -function parseRedirectRequestValue(directive) { - if ( directive.cache === null ) { - directive.cache = sfp.parseRedirectValue(directive.value); +/******************************************************************************/ + +StaticNetFilteringEngine.prototype.urlSkip = function(fctxt, out = []) { + if ( fctxt.redirectURL !== undefined ) { return; } + const directives = this.matchAndFetchModifiers(fctxt, 'urlskip'); + if ( directives === undefined ) { return; } + for ( const directive of directives ) { + if ( (directive.bits & ALLOW_REALM) !== 0 ) { + out.push(directive); + continue; + } + const urlin = fctxt.url; + const value = directive.value; + const steps = value.includes(' ') && value.split(/ +/) || [ value ]; + const urlout = urlSkip(urlin, steps); + if ( urlout === undefined ) { continue; } + if ( urlout === urlin ) { continue; } + fctxt.redirectURL = urlout; + out.push(directive); + break; } - return directive.cache; -} + if ( out.length === 0 ) { return; } + return out; +}; -function compareRedirectRequests(redirectEngine, a, b) { - const { token: atok, priority: aint, bits: abits } = - parseRedirectRequestValue(a); - if ( redirectEngine.hasToken(atok) === false ) { return -1; } - const { token: btok, priority: bint, bits: bbits } = - parseRedirectRequestValue(b); - if ( redirectEngine.hasToken(btok) === false ) { return 1; } - if ( abits !== bbits ) { - if ( (abits & IMPORTANT_REALM) !== 0 ) { return 1; } - if ( (bbits & IMPORTANT_REALM) !== 0 ) { return -1; } - if ( (abits & ALLOW_REALM) !== 0 ) { return -1; } - if ( (bbits & ALLOW_REALM) !== 0 ) { return 1; } +function urlSkip(urlin, steps) { + try { + let urlout; + for ( const step of steps ) { + if ( step.startsWith('?') === false ) { return; } + urlout = (new URL(urlin)).searchParams.get(step.slice(1)); + if ( urlout === null ) { return; } + if ( urlout.includes(' ') ) { + urlout = urlout.replace(/ /g, '%20'); + } + urlin = urlout; + } + void new URL(urlout); + return urlout; + } catch(x) { } - return aint - bint; } /******************************************************************************/ @@ -5373,7 +5427,8 @@ function compareRedirectRequests(redirectEngine, a, b) { // https://github.com/uBlockOrigin/uBlock-issues/issues/1626 // Do not redirect when the number of query parameters does not change. -StaticNetFilteringEngine.prototype.filterQuery = function(fctxt) { +StaticNetFilteringEngine.prototype.filterQuery = function(fctxt, out = []) { + if ( fctxt.redirectURL !== undefined ) { return; } const directives = this.matchAndFetchModifiers(fctxt, 'removeparam'); if ( directives === undefined ) { return; } const url = fctxt.url; @@ -5396,7 +5451,6 @@ StaticNetFilteringEngine.prototype.filterQuery = function(fctxt) { } } const inParamCount = params.size; - const out = []; for ( const directive of directives ) { if ( params.size === 0 ) { break; } const isException = (directive.bits & ALLOW_REALM) !== 0; @@ -5664,6 +5718,7 @@ StaticNetFilteringEngine.prototype.dump = function() { [ PERMISSIONS_REALM, 'permissions' ], [ URLTRANSFORM_REALM, 'uritransform' ], [ REPLACE_REALM, 'replace' ], + [ URLSKIP_REALM, 'urlskip' ], ]); const partyness = new Map([ [ ANYPARTY_REALM, 'any-party' ], diff --git a/src/js/traffic.js b/src/js/traffic.js index 38ba7264b85d8..55538fa7e425e 100644 --- a/src/js/traffic.js +++ b/src/js/traffic.js @@ -188,17 +188,20 @@ const onBeforeRootFrameRequest = function(fctxt) { } if ( logger.enabled ) { - fctxt.setFilter(logData); + fctxt.setRealm('network').setFilter(logData); } // https://github.com/uBlockOrigin/uBlock-issues/issues/760 // Redirect non-blocked request? - if ( result !== 1 && trusted === false && pageStore !== null ) { - pageStore.redirectNonBlockedRequest(fctxt); + if ( trusted === false && pageStore !== null ) { + if ( result !== 1 ) { + pageStore.redirectNonBlockedRequest(fctxt); + } + pageStore.skipMainDocument(fctxt); } if ( logger.enabled ) { - fctxt.setRealm('network').toLogger(); + fctxt.toLogger(); } // Redirected From b79fe942e19752d14b0ae84cd83a359a749c67ea Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sun, 15 Sep 2024 09:45:33 -0400 Subject: [PATCH 0225/1099] Update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5cb5d42cbc4ba..5dacef83f2562 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,4 @@ +- [New static network filter option `urlskip=`](https://github.com/gorhill/uBlock/commit/266ec4894b) - [Rewrite cname uncloaking code to account for new `ipaddress=` option](https://github.com/gorhill/uBlock/commit/6acf97bf51) - [Avoid using dns.resolve() for proxied DNS resolution](https://github.com/gorhill/uBlock/commit/d5f14ffa32) - [Add support for `lan`/`loopback` values to `ipaddress=` option](https://github.com/gorhill/uBlock/commit/030d7334e4) From 096b4d9e81fe41dd1c553ac9c5d23819f8f884ab Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sun, 15 Sep 2024 09:46:06 -0400 Subject: [PATCH 0226/1099] New revision for dev build --- dist/version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dist/version b/dist/version index 1a627ff03ae93..a56296cd32544 100644 --- a/dist/version +++ b/dist/version @@ -1 +1 @@ -1.59.1.21 \ No newline at end of file +1.59.1.22 \ No newline at end of file From 5fda425059021603a17f610543bb43b5adfb992b Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sun, 15 Sep 2024 09:51:42 -0400 Subject: [PATCH 0227/1099] Make Firefox dev build auto-update --- dist/firefox/updates.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dist/firefox/updates.json b/dist/firefox/updates.json index 72ea093e8c640..d52f231b67443 100644 --- a/dist/firefox/updates.json +++ b/dist/firefox/updates.json @@ -3,9 +3,9 @@ "uBlock0@raymondhill.net": { "updates": [ { - "version": "1.59.1.21", + "version": "1.59.1.22", "browser_specific_settings": { "gecko": { "strict_min_version": "78.0" } }, - "update_link": "https://github.com/gorhill/uBlock/releases/download/1.59.1b21/uBlock0_1.59.1b21.firefox.signed.xpi" + "update_link": "https://github.com/gorhill/uBlock/releases/download/1.59.1b22/uBlock0_1.59.1b22.firefox.signed.xpi" } ] } From a9d8e9667569b94b7c734650e146dece711126e2 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sun, 15 Sep 2024 10:38:52 -0400 Subject: [PATCH 0228/1099] Update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5dacef83f2562..6a79906df5df0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ - [Add ability to directly evaluate static network filtering engine](https://github.com/gorhill/uBlock/commit/b7ed3b45ed) - [Fix `prevent-window-open` for when logger is open](https://github.com/gorhill/uBlock/commit/f552f655cb) - [Improve `prevent-window-open` scriptlet](https://github.com/gorhill/uBlock/commit/7f11d6216e) +- [Improve `validate-constant` scriptlet helper](https://github.com/gorhill/uBlock/commit/ae5dc6299e) - [Improve `trusted-replace-outbound-text` scriptlet](https://github.com/gorhill/uBlock/commit/0dcb985601) - [Improve `prevent-xhr` scriptlet](https://github.com/gorhill/uBlock/commit/3a249f395c) - [Add noop resources for redirect purpose](https://github.com/gorhill/uBlock/commit/59a9a43a83) From e18a3707c71ad5694dd67fc73b30515c5f2bd0b5 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sun, 15 Sep 2024 13:20:47 -0400 Subject: [PATCH 0229/1099] Add an entry in _Report_ page for badware/phishing category Related issue: https://github.com/uBlockOrigin/uBlock-issues/issues/3151 Entry: "Add an entry in the reporting tool for badware issues" --- src/_locales/en/messages.json | 4 ++++ src/support.html | 1 + 2 files changed, 5 insertions(+) diff --git a/src/_locales/en/messages.json b/src/_locales/en/messages.json index 92fb26065ab9d..433ff60772a69 100644 --- a/src/_locales/en/messages.json +++ b/src/_locales/en/messages.json @@ -1012,6 +1012,10 @@ "message": "Opens unwanted tabs or windows", "description": "An entry in the widget used to select the type of issue" }, + "supportS6Select1Option7": { + "message": "Leads to badware, phishing", + "description": "An entry in the widget used to select the type of issue" + }, "supportS6Checkbox1": { "message": "Label the web page as “NSFW” (“Not Safe For Work”)", "description": "A checkbox to use for NSFW sites" diff --git a/src/support.html b/src/support.html index 12329423fecf2..fedb42ef2f470 100644 --- a/src/support.html +++ b/src/support.html @@ -93,6 +93,7 @@

+

From b62aabda8530f42c479089ed325be233991ffee5 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sun, 15 Sep 2024 13:25:58 -0400 Subject: [PATCH 0230/1099] Import translation work from https://crowdin.com/project/ublock --- platform/mv3/extension/_locales/ar/messages.json | 2 +- src/_locales/ar/messages.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/platform/mv3/extension/_locales/ar/messages.json b/platform/mv3/extension/_locales/ar/messages.json index a25dfab4de557..404066efaa0a9 100644 --- a/platform/mv3/extension/_locales/ar/messages.json +++ b/platform/mv3/extension/_locales/ar/messages.json @@ -4,7 +4,7 @@ "description": "extension name." }, "extShortDesc": { - "message": "أداة لحظر المحتوى بدون إذن. يحظر الإعلانات وأدوات التتبع وأدوات التعدين وغيرها فور التثبيت.", + "message": "أداة لحظر المحتوى دون إذن. يحظر الإعلانات وأدوات التتبع وأدوات التعدين وغيرها فور التثبيت.", "description": "this will be in the Chrome web store: must be 132 characters or less" }, "perRulesetStats": { diff --git a/src/_locales/ar/messages.json b/src/_locales/ar/messages.json index b2c204d3abefd..b7ea3ee6fab5e 100644 --- a/src/_locales/ar/messages.json +++ b/src/_locales/ar/messages.json @@ -312,7 +312,7 @@ "description": "English: Click, Ctrl-click" }, "pickerContextMenuEntry": { - "message": "إحجب العنصر...", + "message": "حجب العنصر...", "description": "An entry in the browser's contextual menu" }, "settingsCollapseBlockedPrompt": { From ef1e134460e8ae871c245a08aa77fe0fa959afed Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sun, 15 Sep 2024 13:27:32 -0400 Subject: [PATCH 0231/1099] Import translation work from https://crowdin.com/project/ublock --- src/_locales/ar/messages.json | 4 ++++ src/_locales/az/messages.json | 4 ++++ src/_locales/be/messages.json | 4 ++++ src/_locales/bg/messages.json | 4 ++++ src/_locales/bn/messages.json | 4 ++++ src/_locales/br_FR/messages.json | 4 ++++ src/_locales/bs/messages.json | 4 ++++ src/_locales/ca/messages.json | 4 ++++ src/_locales/cs/messages.json | 4 ++++ src/_locales/cv/messages.json | 4 ++++ src/_locales/cy/messages.json | 4 ++++ src/_locales/da/messages.json | 4 ++++ src/_locales/de/messages.json | 4 ++++ src/_locales/el/messages.json | 4 ++++ src/_locales/en_GB/messages.json | 4 ++++ src/_locales/eo/messages.json | 4 ++++ src/_locales/es/messages.json | 4 ++++ src/_locales/et/messages.json | 4 ++++ src/_locales/eu/messages.json | 4 ++++ src/_locales/fa/messages.json | 4 ++++ src/_locales/fi/messages.json | 4 ++++ src/_locales/fil/messages.json | 4 ++++ src/_locales/fr/messages.json | 4 ++++ src/_locales/fy/messages.json | 4 ++++ src/_locales/gl/messages.json | 4 ++++ src/_locales/gu/messages.json | 4 ++++ src/_locales/he/messages.json | 4 ++++ src/_locales/hi/messages.json | 4 ++++ src/_locales/hr/messages.json | 4 ++++ src/_locales/hu/messages.json | 4 ++++ src/_locales/hy/messages.json | 4 ++++ src/_locales/id/messages.json | 4 ++++ src/_locales/it/messages.json | 4 ++++ src/_locales/ja/messages.json | 4 ++++ src/_locales/ka/messages.json | 4 ++++ src/_locales/kk/messages.json | 4 ++++ src/_locales/kn/messages.json | 4 ++++ src/_locales/ko/messages.json | 4 ++++ src/_locales/lt/messages.json | 4 ++++ src/_locales/lv/messages.json | 4 ++++ src/_locales/mk/messages.json | 4 ++++ src/_locales/ml/messages.json | 4 ++++ src/_locales/mr/messages.json | 4 ++++ src/_locales/ms/messages.json | 4 ++++ src/_locales/nb/messages.json | 4 ++++ src/_locales/nl/messages.json | 4 ++++ src/_locales/oc/messages.json | 4 ++++ src/_locales/pa/messages.json | 4 ++++ src/_locales/pl/messages.json | 4 ++++ src/_locales/pt_BR/messages.json | 4 ++++ src/_locales/pt_PT/messages.json | 4 ++++ src/_locales/ro/messages.json | 4 ++++ src/_locales/ru/messages.json | 4 ++++ src/_locales/si/messages.json | 4 ++++ src/_locales/sk/messages.json | 4 ++++ src/_locales/sl/messages.json | 4 ++++ src/_locales/so/messages.json | 4 ++++ src/_locales/sq/messages.json | 4 ++++ src/_locales/sr/messages.json | 4 ++++ src/_locales/sv/messages.json | 4 ++++ src/_locales/sw/messages.json | 4 ++++ src/_locales/ta/messages.json | 4 ++++ src/_locales/te/messages.json | 4 ++++ src/_locales/th/messages.json | 4 ++++ src/_locales/tr/messages.json | 4 ++++ src/_locales/uk/messages.json | 4 ++++ src/_locales/ur/messages.json | 4 ++++ src/_locales/vi/messages.json | 4 ++++ src/_locales/zh_CN/messages.json | 4 ++++ src/_locales/zh_TW/messages.json | 4 ++++ 70 files changed, 280 insertions(+) diff --git a/src/_locales/ar/messages.json b/src/_locales/ar/messages.json index b7ea3ee6fab5e..17bd55c0f86c1 100644 --- a/src/_locales/ar/messages.json +++ b/src/_locales/ar/messages.json @@ -1011,6 +1011,10 @@ "message": "افتح التبويبات أو النوافذ التي ليس مرغوبًا بها", "description": "An entry in the widget used to select the type of issue" }, + "supportS6Select1Option7": { + "message": "Leads to badware, phishing", + "description": "An entry in the widget used to select the type of issue" + }, "supportS6Checkbox1": { "message": "تسمية صفحة الويب باسم \"NSFW\" (\"ليس آمن للعمل\"\n", "description": "A checkbox to use for NSFW sites" diff --git a/src/_locales/az/messages.json b/src/_locales/az/messages.json index 1708ea47e5954..38540ecc79e74 100644 --- a/src/_locales/az/messages.json +++ b/src/_locales/az/messages.json @@ -1011,6 +1011,10 @@ "message": "Arzuolunmaz tab-vərəqələr və ya pəncərələr açır", "description": "An entry in the widget used to select the type of issue" }, + "supportS6Select1Option7": { + "message": "Leads to badware, phishing", + "description": "An entry in the widget used to select the type of issue" + }, "supportS6Checkbox1": { "message": "Veb-səhifəni uyğun olmayan (“NSFW”) olaraq işarələ (“Not Safe For Work”)", "description": "A checkbox to use for NSFW sites" diff --git a/src/_locales/be/messages.json b/src/_locales/be/messages.json index ca1ee35bc364d..6ae12101583fe 100644 --- a/src/_locales/be/messages.json +++ b/src/_locales/be/messages.json @@ -1011,6 +1011,10 @@ "message": "Адкрывае непажаданыя карткі або вокны", "description": "An entry in the widget used to select the type of issue" }, + "supportS6Select1Option7": { + "message": "Leads to badware, phishing", + "description": "An entry in the widget used to select the type of issue" + }, "supportS6Checkbox1": { "message": "Пазначыць вэб-старонку як “NSFW” (“Not Safe For Work”)", "description": "A checkbox to use for NSFW sites" diff --git a/src/_locales/bg/messages.json b/src/_locales/bg/messages.json index aaff8db583deb..98e3bd4a61606 100644 --- a/src/_locales/bg/messages.json +++ b/src/_locales/bg/messages.json @@ -1011,6 +1011,10 @@ "message": "Отваря нежелани раздели или прозорци", "description": "An entry in the widget used to select the type of issue" }, + "supportS6Select1Option7": { + "message": "Leads to badware, phishing", + "description": "An entry in the widget used to select the type of issue" + }, "supportS6Checkbox1": { "message": "Маркиране на уеб страницата като “NSFW” (“не е безопасна за работа”)", "description": "A checkbox to use for NSFW sites" diff --git a/src/_locales/bn/messages.json b/src/_locales/bn/messages.json index de82ca4dee471..0c3b776afa454 100644 --- a/src/_locales/bn/messages.json +++ b/src/_locales/bn/messages.json @@ -1011,6 +1011,10 @@ "message": "অবাঞ্ছিত ট্যাব বা উইন্ডো খোলে", "description": "An entry in the widget used to select the type of issue" }, + "supportS6Select1Option7": { + "message": "Leads to badware, phishing", + "description": "An entry in the widget used to select the type of issue" + }, "supportS6Checkbox1": { "message": "ওয়েব পৃষ্ঠাটিকে “NSFW” হিসাবে লেবেল করুন (“কাজের জন্য নিরাপদ নয়”)", "description": "A checkbox to use for NSFW sites" diff --git a/src/_locales/br_FR/messages.json b/src/_locales/br_FR/messages.json index e0005b74baf74..92c0b73ca0d75 100644 --- a/src/_locales/br_FR/messages.json +++ b/src/_locales/br_FR/messages.json @@ -1011,6 +1011,10 @@ "message": "Digeriñ a ra ivinelloù pe prenestroù noazus", "description": "An entry in the widget used to select the type of issue" }, + "supportS6Select1Option7": { + "message": "Leads to badware, phishing", + "description": "An entry in the widget used to select the type of issue" + }, "supportS6Checkbox1": { "message": "Merkañ ar bejenn evel \"NSFW\" (“Not Safe For Work”) hag a dalv ez eus danvez noazus pe kizidik enni", "description": "A checkbox to use for NSFW sites" diff --git a/src/_locales/bs/messages.json b/src/_locales/bs/messages.json index bd0810a6c6ab0..d5e299aab9bf6 100644 --- a/src/_locales/bs/messages.json +++ b/src/_locales/bs/messages.json @@ -1011,6 +1011,10 @@ "message": "Otvara neželjene kartice ili prozore", "description": "An entry in the widget used to select the type of issue" }, + "supportS6Select1Option7": { + "message": "Leads to badware, phishing", + "description": "An entry in the widget used to select the type of issue" + }, "supportS6Checkbox1": { "message": "Označite web stranicu kao “NSZP” (“Ne-Sigurna-Za-Posao”)", "description": "A checkbox to use for NSFW sites" diff --git a/src/_locales/ca/messages.json b/src/_locales/ca/messages.json index 0ba1ccc1e0beb..9ec5bda0cba6e 100644 --- a/src/_locales/ca/messages.json +++ b/src/_locales/ca/messages.json @@ -1011,6 +1011,10 @@ "message": "Obre pestanyes o finestres no desitjades", "description": "An entry in the widget used to select the type of issue" }, + "supportS6Select1Option7": { + "message": "Leads to badware, phishing", + "description": "An entry in the widget used to select the type of issue" + }, "supportS6Checkbox1": { "message": "Marca aquesta pàgina com a “NSFW” (“No segur per al treball”)", "description": "A checkbox to use for NSFW sites" diff --git a/src/_locales/cs/messages.json b/src/_locales/cs/messages.json index 025f0b59a868c..8fc18fd9623c4 100644 --- a/src/_locales/cs/messages.json +++ b/src/_locales/cs/messages.json @@ -1011,6 +1011,10 @@ "message": "Otevírá nechtěné karty nebo okna", "description": "An entry in the widget used to select the type of issue" }, + "supportS6Select1Option7": { + "message": "Leads to badware, phishing", + "description": "An entry in the widget used to select the type of issue" + }, "supportS6Checkbox1": { "message": "Označit webovou stránku jako “NSFW” (Není bezpečné pro práci”)", "description": "A checkbox to use for NSFW sites" diff --git a/src/_locales/cv/messages.json b/src/_locales/cv/messages.json index aec33ebe8dfc8..70ee6f7e21e16 100644 --- a/src/_locales/cv/messages.json +++ b/src/_locales/cv/messages.json @@ -1011,6 +1011,10 @@ "message": "Opens unwanted tabs or windows", "description": "An entry in the widget used to select the type of issue" }, + "supportS6Select1Option7": { + "message": "Leads to badware, phishing", + "description": "An entry in the widget used to select the type of issue" + }, "supportS6Checkbox1": { "message": "Label the web page as “NSFW” (“Not Safe For Work”)", "description": "A checkbox to use for NSFW sites" diff --git a/src/_locales/cy/messages.json b/src/_locales/cy/messages.json index 7152ef93ed9dd..813c625b3140b 100644 --- a/src/_locales/cy/messages.json +++ b/src/_locales/cy/messages.json @@ -1011,6 +1011,10 @@ "message": "Opens unwanted tabs or windows", "description": "An entry in the widget used to select the type of issue" }, + "supportS6Select1Option7": { + "message": "Leads to badware, phishing", + "description": "An entry in the widget used to select the type of issue" + }, "supportS6Checkbox1": { "message": "Label the web page as “NSFW” (“Not Safe For Work”)", "description": "A checkbox to use for NSFW sites" diff --git a/src/_locales/da/messages.json b/src/_locales/da/messages.json index 061e781a95026..ca5ee9491d69b 100644 --- a/src/_locales/da/messages.json +++ b/src/_locales/da/messages.json @@ -1011,6 +1011,10 @@ "message": "Åbner uønskede faner eller vinduer", "description": "An entry in the widget used to select the type of issue" }, + "supportS6Select1Option7": { + "message": "Leads to badware, phishing", + "description": "An entry in the widget used to select the type of issue" + }, "supportS6Checkbox1": { "message": "Markér websiden som “NSFW” (“Ikke sikker til arbejdsbrug”)", "description": "A checkbox to use for NSFW sites" diff --git a/src/_locales/de/messages.json b/src/_locales/de/messages.json index 473673525f79d..748653846b451 100644 --- a/src/_locales/de/messages.json +++ b/src/_locales/de/messages.json @@ -1011,6 +1011,10 @@ "message": "Öffnet unerwünschte Tabs oder Fenster", "description": "An entry in the widget used to select the type of issue" }, + "supportS6Select1Option7": { + "message": "Leads to badware, phishing", + "description": "An entry in the widget used to select the type of issue" + }, "supportS6Checkbox1": { "message": "Webseite als »NSFW« kennzeichnen (»Unpassend für den Arbeitsplatz«)", "description": "A checkbox to use for NSFW sites" diff --git a/src/_locales/el/messages.json b/src/_locales/el/messages.json index 73b67df631e91..1ef763e3b1e58 100644 --- a/src/_locales/el/messages.json +++ b/src/_locales/el/messages.json @@ -1011,6 +1011,10 @@ "message": "Ανοίγει ανεπιθύμητες καρτέλες ή παράθυρα", "description": "An entry in the widget used to select the type of issue" }, + "supportS6Select1Option7": { + "message": "Leads to badware, phishing", + "description": "An entry in the widget used to select the type of issue" + }, "supportS6Checkbox1": { "message": "Επισημάνετε την ιστοσελίδα ως “NSFW” (“Not Safe For Work”)", "description": "A checkbox to use for NSFW sites" diff --git a/src/_locales/en_GB/messages.json b/src/_locales/en_GB/messages.json index 8fd9b46001abc..69334adb0d506 100644 --- a/src/_locales/en_GB/messages.json +++ b/src/_locales/en_GB/messages.json @@ -1011,6 +1011,10 @@ "message": "Opens unwanted tabs or windows", "description": "An entry in the widget used to select the type of issue" }, + "supportS6Select1Option7": { + "message": "Leads to badware, phishing", + "description": "An entry in the widget used to select the type of issue" + }, "supportS6Checkbox1": { "message": "Label the web page as “NSFW” (“Not Safe For Work”)", "description": "A checkbox to use for NSFW sites" diff --git a/src/_locales/eo/messages.json b/src/_locales/eo/messages.json index 1d69a1fda396f..084841e499d9f 100644 --- a/src/_locales/eo/messages.json +++ b/src/_locales/eo/messages.json @@ -1011,6 +1011,10 @@ "message": "Malfermas nedeziratajn langetojn aŭ fenestrojn", "description": "An entry in the widget used to select the type of issue" }, + "supportS6Select1Option7": { + "message": "Leads to badware, phishing", + "description": "An entry in the widget used to select the type of issue" + }, "supportS6Checkbox1": { "message": "Marki la paĝon kiel «nelabortaŭgan»", "description": "A checkbox to use for NSFW sites" diff --git a/src/_locales/es/messages.json b/src/_locales/es/messages.json index 55bd2e95821f5..c88cc62193f60 100644 --- a/src/_locales/es/messages.json +++ b/src/_locales/es/messages.json @@ -1011,6 +1011,10 @@ "message": "Abre pestañas o ventanas no deseadas", "description": "An entry in the widget used to select the type of issue" }, + "supportS6Select1Option7": { + "message": "Leads to badware, phishing", + "description": "An entry in the widget used to select the type of issue" + }, "supportS6Checkbox1": { "message": "Etiquetar la página web como “NSFW” (“no es seguro/apropiado para el trabajo”)", "description": "A checkbox to use for NSFW sites" diff --git a/src/_locales/et/messages.json b/src/_locales/et/messages.json index 5ab8cc917fa23..63ac039bb19b2 100644 --- a/src/_locales/et/messages.json +++ b/src/_locales/et/messages.json @@ -1011,6 +1011,10 @@ "message": "Avab soovimatuid kaarte või aknaid", "description": "An entry in the widget used to select the type of issue" }, + "supportS6Select1Option7": { + "message": "Leads to badware, phishing", + "description": "An entry in the widget used to select the type of issue" + }, "supportS6Checkbox1": { "message": "Sildista veebileht kui „NSFW“ (“tööks sobimatu”)", "description": "A checkbox to use for NSFW sites" diff --git a/src/_locales/eu/messages.json b/src/_locales/eu/messages.json index 5cf4565fbe69f..302800d723aef 100644 --- a/src/_locales/eu/messages.json +++ b/src/_locales/eu/messages.json @@ -1011,6 +1011,10 @@ "message": "Ireki nahi ez diren erlaitzak edo leihoak", "description": "An entry in the widget used to select the type of issue" }, + "supportS6Select1Option7": { + "message": "Leads to badware, phishing", + "description": "An entry in the widget used to select the type of issue" + }, "supportS6Checkbox1": { "message": "Web orria «NSFW» moduan jarri (“Not Safe For Work” )", "description": "A checkbox to use for NSFW sites" diff --git a/src/_locales/fa/messages.json b/src/_locales/fa/messages.json index ef05c734625d9..23838c8ff4412 100644 --- a/src/_locales/fa/messages.json +++ b/src/_locales/fa/messages.json @@ -1011,6 +1011,10 @@ "message": "تب یا پنجره‌ی ناخواسته باز می‌کند", "description": "An entry in the widget used to select the type of issue" }, + "supportS6Select1Option7": { + "message": "Leads to badware, phishing", + "description": "An entry in the widget used to select the type of issue" + }, "supportS6Checkbox1": { "message": "علامت زدن صفحه به عنوان \"NSFW\" (\"نامناسب برای کار\")", "description": "A checkbox to use for NSFW sites" diff --git a/src/_locales/fi/messages.json b/src/_locales/fi/messages.json index 881e7f6e1e637..4d274bc102a57 100644 --- a/src/_locales/fi/messages.json +++ b/src/_locales/fi/messages.json @@ -1011,6 +1011,10 @@ "message": "Avaa ei-toivottuja välilehtiä tai ikkunoita", "description": "An entry in the widget used to select the type of issue" }, + "supportS6Select1Option7": { + "message": "Leads to badware, phishing", + "description": "An entry in the widget used to select the type of issue" + }, "supportS6Checkbox1": { "message": "Luokittele verkkosivu ns. työpaikalle sopimattomaksi, \"NSFW\" (\"Not Safe For Work\")", "description": "A checkbox to use for NSFW sites" diff --git a/src/_locales/fil/messages.json b/src/_locales/fil/messages.json index d38e062021d63..58305b46858be 100644 --- a/src/_locales/fil/messages.json +++ b/src/_locales/fil/messages.json @@ -1011,6 +1011,10 @@ "message": "Kung anu-anong mga tab o window ang binubuksan", "description": "An entry in the widget used to select the type of issue" }, + "supportS6Select1Option7": { + "message": "Leads to badware, phishing", + "description": "An entry in the widget used to select the type of issue" + }, "supportS6Checkbox1": { "message": "Markahan bilang hindi dapat tinitignan sa pook-trabahuan (“Not Safe For Work”)", "description": "A checkbox to use for NSFW sites" diff --git a/src/_locales/fr/messages.json b/src/_locales/fr/messages.json index fda258990d51f..3cf16c76e2e7a 100644 --- a/src/_locales/fr/messages.json +++ b/src/_locales/fr/messages.json @@ -1011,6 +1011,10 @@ "message": "ouvre des onglets ou fenêtres indésirables", "description": "An entry in the widget used to select the type of issue" }, + "supportS6Select1Option7": { + "message": "Leads to badware, phishing", + "description": "An entry in the widget used to select the type of issue" + }, "supportS6Checkbox1": { "message": "Marquer le site Web en tant que \"NSFW\" (\"Not Safe For Work\", c'est-à-dire pour public averti/inapproprié au travail)", "description": "A checkbox to use for NSFW sites" diff --git a/src/_locales/fy/messages.json b/src/_locales/fy/messages.json index ef97ec15d4891..729a7d126d440 100644 --- a/src/_locales/fy/messages.json +++ b/src/_locales/fy/messages.json @@ -1011,6 +1011,10 @@ "message": "Iepenet net-winske ljepblêden of finsters", "description": "An entry in the widget used to select the type of issue" }, + "supportS6Select1Option7": { + "message": "Leads to badware, phishing", + "description": "An entry in the widget used to select the type of issue" + }, "supportS6Checkbox1": { "message": "De webside labelje as ‘NSFW’ (‘Not Safe For Work’)", "description": "A checkbox to use for NSFW sites" diff --git a/src/_locales/gl/messages.json b/src/_locales/gl/messages.json index 0da46340cd70c..358fe4043b989 100644 --- a/src/_locales/gl/messages.json +++ b/src/_locales/gl/messages.json @@ -1011,6 +1011,10 @@ "message": "Abre ventás ou páxinas non desexadas", "description": "An entry in the widget used to select the type of issue" }, + "supportS6Select1Option7": { + "message": "Leads to badware, phishing", + "description": "An entry in the widget used to select the type of issue" + }, "supportS6Checkbox1": { "message": "Etiqueta a páxina como \"NSFW\" (\"Non é Segura No Traballo\")", "description": "A checkbox to use for NSFW sites" diff --git a/src/_locales/gu/messages.json b/src/_locales/gu/messages.json index 9db2f73145056..b2c8b432116e8 100644 --- a/src/_locales/gu/messages.json +++ b/src/_locales/gu/messages.json @@ -1011,6 +1011,10 @@ "message": "Opens unwanted tabs or windows", "description": "An entry in the widget used to select the type of issue" }, + "supportS6Select1Option7": { + "message": "Leads to badware, phishing", + "description": "An entry in the widget used to select the type of issue" + }, "supportS6Checkbox1": { "message": "Label the web page as “NSFW” (“Not Safe For Work”)", "description": "A checkbox to use for NSFW sites" diff --git a/src/_locales/he/messages.json b/src/_locales/he/messages.json index b036e932ece77..eb55156173ab1 100644 --- a/src/_locales/he/messages.json +++ b/src/_locales/he/messages.json @@ -1011,6 +1011,10 @@ "message": "נפתחים לשוניות או חלונות לא רצויים", "description": "An entry in the widget used to select the type of issue" }, + "supportS6Select1Option7": { + "message": "Leads to badware, phishing", + "description": "An entry in the widget used to select the type of issue" + }, "supportS6Checkbox1": { "message": "תיוג דף האינטרנט כ- לב\"ל ('לא בטוח לעבודה')", "description": "A checkbox to use for NSFW sites" diff --git a/src/_locales/hi/messages.json b/src/_locales/hi/messages.json index 4e2cf86c5182b..0380515188a5c 100644 --- a/src/_locales/hi/messages.json +++ b/src/_locales/hi/messages.json @@ -1011,6 +1011,10 @@ "message": "अवांछित टैब या विंडो खोलता है", "description": "An entry in the widget used to select the type of issue" }, + "supportS6Select1Option7": { + "message": "Leads to badware, phishing", + "description": "An entry in the widget used to select the type of issue" + }, "supportS6Checkbox1": { "message": "वेब पेज को इस रूप में लेबल करें “NSFW” (“काम करने के लिए सुरक्षित नहीं”)", "description": "A checkbox to use for NSFW sites" diff --git a/src/_locales/hr/messages.json b/src/_locales/hr/messages.json index bb5ed413611fd..59d07378a8bbe 100644 --- a/src/_locales/hr/messages.json +++ b/src/_locales/hr/messages.json @@ -1011,6 +1011,10 @@ "message": "Otvara neželjene kartice ili prozore", "description": "An entry in the widget used to select the type of issue" }, + "supportS6Select1Option7": { + "message": "Leads to badware, phishing", + "description": "An entry in the widget used to select the type of issue" + }, "supportS6Checkbox1": { "message": "Označite web stranicu kao “NSFW” (“nije sigurno za pregledavanje na poslu“)", "description": "A checkbox to use for NSFW sites" diff --git a/src/_locales/hu/messages.json b/src/_locales/hu/messages.json index 39d020f645591..98c4d065e5cdc 100644 --- a/src/_locales/hu/messages.json +++ b/src/_locales/hu/messages.json @@ -1011,6 +1011,10 @@ "message": "Kéretlen lapokat vagy ablakokat nyit meg", "description": "An entry in the widget used to select the type of issue" }, + "supportS6Select1Option7": { + "message": "Leads to badware, phishing", + "description": "An entry in the widget used to select the type of issue" + }, "supportS6Checkbox1": { "message": "A weboldal megjelölése felnőtt tartalomként", "description": "A checkbox to use for NSFW sites" diff --git a/src/_locales/hy/messages.json b/src/_locales/hy/messages.json index bc6d4c20a1472..7956bce8736f7 100644 --- a/src/_locales/hy/messages.json +++ b/src/_locales/hy/messages.json @@ -1011,6 +1011,10 @@ "message": "Բացում է անցանկալի ներդիրները կամ լուսամուտները", "description": "An entry in the widget used to select the type of issue" }, + "supportS6Select1Option7": { + "message": "Leads to badware, phishing", + "description": "An entry in the widget used to select the type of issue" + }, "supportS6Checkbox1": { "message": "Նշել վեբ էջը որպես «NSFW» («Աշխատանքի համար անվտանգ չէ»)", "description": "A checkbox to use for NSFW sites" diff --git a/src/_locales/id/messages.json b/src/_locales/id/messages.json index 2ac75d90f607f..1e3b55b7accc0 100644 --- a/src/_locales/id/messages.json +++ b/src/_locales/id/messages.json @@ -1011,6 +1011,10 @@ "message": "Membuka tab atau jendela yang tidak diinginkan", "description": "An entry in the widget used to select the type of issue" }, + "supportS6Select1Option7": { + "message": "Leads to badware, phishing", + "description": "An entry in the widget used to select the type of issue" + }, "supportS6Checkbox1": { "message": "Beri label halaman web sebagai “TAUSB” (“Tidak Aman Untuk Saat Bekerja”)", "description": "A checkbox to use for NSFW sites" diff --git a/src/_locales/it/messages.json b/src/_locales/it/messages.json index 726c6085ae0d1..cafc1e0e94458 100644 --- a/src/_locales/it/messages.json +++ b/src/_locales/it/messages.json @@ -1011,6 +1011,10 @@ "message": "Apre schede o finestre indesiderate", "description": "An entry in the widget used to select the type of issue" }, + "supportS6Select1Option7": { + "message": "Leads to badware, phishing", + "description": "An entry in the widget used to select the type of issue" + }, "supportS6Checkbox1": { "message": "Etichetta la pagina web come “NSFW” (“Not Safe For Work”)", "description": "A checkbox to use for NSFW sites" diff --git a/src/_locales/ja/messages.json b/src/_locales/ja/messages.json index add025fe64505..1a720ceb13d83 100644 --- a/src/_locales/ja/messages.json +++ b/src/_locales/ja/messages.json @@ -1011,6 +1011,10 @@ "message": "勝手にタブやウィンドウを開きます", "description": "An entry in the widget used to select the type of issue" }, + "supportS6Select1Option7": { + "message": "Leads to badware, phishing", + "description": "An entry in the widget used to select the type of issue" + }, "supportS6Checkbox1": { "message": "ウェブページに“NSFW” (閲覧注意、“Not Safe For Work”) とラベルをつける", "description": "A checkbox to use for NSFW sites" diff --git a/src/_locales/ka/messages.json b/src/_locales/ka/messages.json index da9ea635b7a7d..08b504bf58fce 100644 --- a/src/_locales/ka/messages.json +++ b/src/_locales/ka/messages.json @@ -1011,6 +1011,10 @@ "message": "ხსნის არასასურველ ჩანართებს ან ფანჯრებს", "description": "An entry in the widget used to select the type of issue" }, + "supportS6Select1Option7": { + "message": "Leads to badware, phishing", + "description": "An entry in the widget used to select the type of issue" + }, "supportS6Checkbox1": { "message": "გვერდი მოინიშნოს, როგორც „NSFW“ („შეუსაბამო“)", "description": "A checkbox to use for NSFW sites" diff --git a/src/_locales/kk/messages.json b/src/_locales/kk/messages.json index bd037e0a72ff5..55f5653a015ae 100644 --- a/src/_locales/kk/messages.json +++ b/src/_locales/kk/messages.json @@ -1011,6 +1011,10 @@ "message": "Opens unwanted tabs or windows", "description": "An entry in the widget used to select the type of issue" }, + "supportS6Select1Option7": { + "message": "Leads to badware, phishing", + "description": "An entry in the widget used to select the type of issue" + }, "supportS6Checkbox1": { "message": "Label the web page as “NSFW” (“Not Safe For Work”)", "description": "A checkbox to use for NSFW sites" diff --git a/src/_locales/kn/messages.json b/src/_locales/kn/messages.json index eed3253f2c98b..27374f2fca4e5 100644 --- a/src/_locales/kn/messages.json +++ b/src/_locales/kn/messages.json @@ -1011,6 +1011,10 @@ "message": "Opens unwanted tabs or windows", "description": "An entry in the widget used to select the type of issue" }, + "supportS6Select1Option7": { + "message": "Leads to badware, phishing", + "description": "An entry in the widget used to select the type of issue" + }, "supportS6Checkbox1": { "message": "Label the web page as “NSFW” (“Not Safe For Work”)", "description": "A checkbox to use for NSFW sites" diff --git a/src/_locales/ko/messages.json b/src/_locales/ko/messages.json index 486e54fc5ed76..1907260429fd3 100644 --- a/src/_locales/ko/messages.json +++ b/src/_locales/ko/messages.json @@ -1011,6 +1011,10 @@ "message": "원치 않는 탭이나 창을 엽니다", "description": "An entry in the widget used to select the type of issue" }, + "supportS6Select1Option7": { + "message": "Leads to badware, phishing", + "description": "An entry in the widget used to select the type of issue" + }, "supportS6Checkbox1": { "message": "웹페이지를 \"NSFW\" (“Not Safe For Work”)로 분류", "description": "A checkbox to use for NSFW sites" diff --git a/src/_locales/lt/messages.json b/src/_locales/lt/messages.json index b7431aceaa93c..5769d17566d35 100644 --- a/src/_locales/lt/messages.json +++ b/src/_locales/lt/messages.json @@ -1011,6 +1011,10 @@ "message": "Opens unwanted tabs or windows", "description": "An entry in the widget used to select the type of issue" }, + "supportS6Select1Option7": { + "message": "Leads to badware, phishing", + "description": "An entry in the widget used to select the type of issue" + }, "supportS6Checkbox1": { "message": "Label the web page as “NSFW” (“Not Safe For Work”)", "description": "A checkbox to use for NSFW sites" diff --git a/src/_locales/lv/messages.json b/src/_locales/lv/messages.json index c529a68246d4d..fc24c5e2bb0c0 100644 --- a/src/_locales/lv/messages.json +++ b/src/_locales/lv/messages.json @@ -1011,6 +1011,10 @@ "message": "Atver nevēlamas cilnes vai logus", "description": "An entry in the widget used to select the type of issue" }, + "supportS6Select1Option7": { + "message": "Leads to badware, phishing", + "description": "An entry in the widget used to select the type of issue" + }, "supportS6Checkbox1": { "message": "Iezīmēt tīmekļa lapu kā \"NSFW\" (\"Not Safe for Work (nav droša darbam))", "description": "A checkbox to use for NSFW sites" diff --git a/src/_locales/mk/messages.json b/src/_locales/mk/messages.json index 0a7777f9b04aa..2eedde6695197 100644 --- a/src/_locales/mk/messages.json +++ b/src/_locales/mk/messages.json @@ -1011,6 +1011,10 @@ "message": "Opens unwanted tabs or windows", "description": "An entry in the widget used to select the type of issue" }, + "supportS6Select1Option7": { + "message": "Leads to badware, phishing", + "description": "An entry in the widget used to select the type of issue" + }, "supportS6Checkbox1": { "message": "Label the web page as “NSFW” (“Not Safe For Work”)", "description": "A checkbox to use for NSFW sites" diff --git a/src/_locales/ml/messages.json b/src/_locales/ml/messages.json index b4c1b3a162a36..584f165d68b16 100644 --- a/src/_locales/ml/messages.json +++ b/src/_locales/ml/messages.json @@ -1011,6 +1011,10 @@ "message": "Opens unwanted tabs or windows", "description": "An entry in the widget used to select the type of issue" }, + "supportS6Select1Option7": { + "message": "Leads to badware, phishing", + "description": "An entry in the widget used to select the type of issue" + }, "supportS6Checkbox1": { "message": "Label the web page as “NSFW” (“Not Safe For Work”)", "description": "A checkbox to use for NSFW sites" diff --git a/src/_locales/mr/messages.json b/src/_locales/mr/messages.json index 6530b322480be..e0bc26d1ec638 100644 --- a/src/_locales/mr/messages.json +++ b/src/_locales/mr/messages.json @@ -1011,6 +1011,10 @@ "message": "Opens unwanted tabs or windows", "description": "An entry in the widget used to select the type of issue" }, + "supportS6Select1Option7": { + "message": "Leads to badware, phishing", + "description": "An entry in the widget used to select the type of issue" + }, "supportS6Checkbox1": { "message": "Label the web page as “NSFW” (“Not Safe For Work”)", "description": "A checkbox to use for NSFW sites" diff --git a/src/_locales/ms/messages.json b/src/_locales/ms/messages.json index 5247e837f1f1c..88e52805548bd 100644 --- a/src/_locales/ms/messages.json +++ b/src/_locales/ms/messages.json @@ -1011,6 +1011,10 @@ "message": "Membuka tab atau tetingkap yang tidak diingini", "description": "An entry in the widget used to select the type of issue" }, + "supportS6Select1Option7": { + "message": "Leads to badware, phishing", + "description": "An entry in the widget used to select the type of issue" + }, "supportS6Checkbox1": { "message": "Labelkan laman web itu sebagai \"NSFW\" (“Not Safe For Work”)", "description": "A checkbox to use for NSFW sites" diff --git a/src/_locales/nb/messages.json b/src/_locales/nb/messages.json index 5baaf48f039c0..634fcca615fe2 100644 --- a/src/_locales/nb/messages.json +++ b/src/_locales/nb/messages.json @@ -1011,6 +1011,10 @@ "message": "Åpner uønskede faner eller vinduer", "description": "An entry in the widget used to select the type of issue" }, + "supportS6Select1Option7": { + "message": "Leads to badware, phishing", + "description": "An entry in the widget used to select the type of issue" + }, "supportS6Checkbox1": { "message": "Merk nettsiden som “NSFW” (“Not Safe For Work”) (advarsel mot sider med upassende innhold)", "description": "A checkbox to use for NSFW sites" diff --git a/src/_locales/nl/messages.json b/src/_locales/nl/messages.json index 554544ccb5437..49b94078774c2 100644 --- a/src/_locales/nl/messages.json +++ b/src/_locales/nl/messages.json @@ -1011,6 +1011,10 @@ "message": "Opent ongewenste tabbladen of vensters", "description": "An entry in the widget used to select the type of issue" }, + "supportS6Select1Option7": { + "message": "Leads to badware, phishing", + "description": "An entry in the widget used to select the type of issue" + }, "supportS6Checkbox1": { "message": "De webpagina labelen als ‘NSFW’ (‘Not Safe For Work’)", "description": "A checkbox to use for NSFW sites" diff --git a/src/_locales/oc/messages.json b/src/_locales/oc/messages.json index 6d141a65722d6..8dd23f9833000 100644 --- a/src/_locales/oc/messages.json +++ b/src/_locales/oc/messages.json @@ -1011,6 +1011,10 @@ "message": "Opens unwanted tabs or windows", "description": "An entry in the widget used to select the type of issue" }, + "supportS6Select1Option7": { + "message": "Leads to badware, phishing", + "description": "An entry in the widget used to select the type of issue" + }, "supportS6Checkbox1": { "message": "Label the web page as “NSFW” (“Not Safe For Work”)", "description": "A checkbox to use for NSFW sites" diff --git a/src/_locales/pa/messages.json b/src/_locales/pa/messages.json index 584a23d7e7caf..e295df49b7e2e 100644 --- a/src/_locales/pa/messages.json +++ b/src/_locales/pa/messages.json @@ -1011,6 +1011,10 @@ "message": "ਬੇਲੋੜੀਆਂ ਟੈਬਾਂ ਜਾਂ ਵਿੰਡੋ ਖੋਲ੍ਹਦਾ ਹੈ", "description": "An entry in the widget used to select the type of issue" }, + "supportS6Select1Option7": { + "message": "Leads to badware, phishing", + "description": "An entry in the widget used to select the type of issue" + }, "supportS6Checkbox1": { "message": "Label the web page as “NSFW” (“Not Safe For Work”)", "description": "A checkbox to use for NSFW sites" diff --git a/src/_locales/pl/messages.json b/src/_locales/pl/messages.json index a4ce6344d7508..b63a8a218b48f 100644 --- a/src/_locales/pl/messages.json +++ b/src/_locales/pl/messages.json @@ -1011,6 +1011,10 @@ "message": "Otwiera niepożądane karty lub okna", "description": "An entry in the widget used to select the type of issue" }, + "supportS6Select1Option7": { + "message": "Leads to badware, phishing", + "description": "An entry in the widget used to select the type of issue" + }, "supportS6Checkbox1": { "message": "Oznacz stronę internetową jako „NSFW” („Not Safe For Work (nieodpowiednią w pracy)”)", "description": "A checkbox to use for NSFW sites" diff --git a/src/_locales/pt_BR/messages.json b/src/_locales/pt_BR/messages.json index 1704ab211707c..9b4d7c6f7ca38 100644 --- a/src/_locales/pt_BR/messages.json +++ b/src/_locales/pt_BR/messages.json @@ -1011,6 +1011,10 @@ "message": "Abre abas ou janelas indesejadas", "description": "An entry in the widget used to select the type of issue" }, + "supportS6Select1Option7": { + "message": "Leads to badware, phishing", + "description": "An entry in the widget used to select the type of issue" + }, "supportS6Checkbox1": { "message": "Rotula a página da web como \"NSFW\" (\"Não é Seguro no Trabalho\")", "description": "A checkbox to use for NSFW sites" diff --git a/src/_locales/pt_PT/messages.json b/src/_locales/pt_PT/messages.json index 743b8770b1c0d..882b2c278ea13 100644 --- a/src/_locales/pt_PT/messages.json +++ b/src/_locales/pt_PT/messages.json @@ -1011,6 +1011,10 @@ "message": "Abre separadores ou janelas indesejáveis", "description": "An entry in the widget used to select the type of issue" }, + "supportS6Select1Option7": { + "message": "Leads to badware, phishing", + "description": "An entry in the widget used to select the type of issue" + }, "supportS6Checkbox1": { "message": "Rotular a página web como “NSFW” (“Não seguro para o trabalho”)", "description": "A checkbox to use for NSFW sites" diff --git a/src/_locales/ro/messages.json b/src/_locales/ro/messages.json index 3881e7821a45a..ce4595d71c440 100644 --- a/src/_locales/ro/messages.json +++ b/src/_locales/ro/messages.json @@ -1011,6 +1011,10 @@ "message": "Deschide file sau ferestre nedorite", "description": "An entry in the widget used to select the type of issue" }, + "supportS6Select1Option7": { + "message": "Leads to badware, phishing", + "description": "An entry in the widget used to select the type of issue" + }, "supportS6Checkbox1": { "message": "Etichetează pagina ca fiind „NSFW” (“Not Safe For Work”)", "description": "A checkbox to use for NSFW sites" diff --git a/src/_locales/ru/messages.json b/src/_locales/ru/messages.json index 2d535dd32f159..fd4d542cb1e8d 100644 --- a/src/_locales/ru/messages.json +++ b/src/_locales/ru/messages.json @@ -1011,6 +1011,10 @@ "message": "Открываются нежелательные вкладки или окна", "description": "An entry in the widget used to select the type of issue" }, + "supportS6Select1Option7": { + "message": "Leads to badware, phishing", + "description": "An entry in the widget used to select the type of issue" + }, "supportS6Checkbox1": { "message": "Пометить веб-страницу как “NSFW” (“Not Safe For Work”)", "description": "A checkbox to use for NSFW sites" diff --git a/src/_locales/si/messages.json b/src/_locales/si/messages.json index afd9c19d6f97f..5b1819838b86a 100644 --- a/src/_locales/si/messages.json +++ b/src/_locales/si/messages.json @@ -1011,6 +1011,10 @@ "message": "Opens unwanted tabs or windows", "description": "An entry in the widget used to select the type of issue" }, + "supportS6Select1Option7": { + "message": "Leads to badware, phishing", + "description": "An entry in the widget used to select the type of issue" + }, "supportS6Checkbox1": { "message": "Label the web page as “NSFW” (“Not Safe For Work”)", "description": "A checkbox to use for NSFW sites" diff --git a/src/_locales/sk/messages.json b/src/_locales/sk/messages.json index ab778d9d25cf9..e1adf45e0fe9c 100644 --- a/src/_locales/sk/messages.json +++ b/src/_locales/sk/messages.json @@ -1011,6 +1011,10 @@ "message": "Otvára nechcené karty alebo okná", "description": "An entry in the widget used to select the type of issue" }, + "supportS6Select1Option7": { + "message": "Leads to badware, phishing", + "description": "An entry in the widget used to select the type of issue" + }, "supportS6Checkbox1": { "message": "Označiť webstránku ako “NSFW” (Nie je bezpečné pre prácu”)", "description": "A checkbox to use for NSFW sites" diff --git a/src/_locales/sl/messages.json b/src/_locales/sl/messages.json index bb2cfa2e156d5..703ce9f8732db 100644 --- a/src/_locales/sl/messages.json +++ b/src/_locales/sl/messages.json @@ -1011,6 +1011,10 @@ "message": "Opens unwanted tabs or windows", "description": "An entry in the widget used to select the type of issue" }, + "supportS6Select1Option7": { + "message": "Leads to badware, phishing", + "description": "An entry in the widget used to select the type of issue" + }, "supportS6Checkbox1": { "message": "Label the web page as “NSFW” (“Not Safe For Work”)", "description": "A checkbox to use for NSFW sites" diff --git a/src/_locales/so/messages.json b/src/_locales/so/messages.json index b8c9c13bcef31..4276bf8c7ac68 100644 --- a/src/_locales/so/messages.json +++ b/src/_locales/so/messages.json @@ -1011,6 +1011,10 @@ "message": "Opens unwanted tabs or windows", "description": "An entry in the widget used to select the type of issue" }, + "supportS6Select1Option7": { + "message": "Leads to badware, phishing", + "description": "An entry in the widget used to select the type of issue" + }, "supportS6Checkbox1": { "message": "Label the web page as “NSFW” (“Not Safe For Work”)", "description": "A checkbox to use for NSFW sites" diff --git a/src/_locales/sq/messages.json b/src/_locales/sq/messages.json index 3fc972c4b03cb..6cdf1664316fd 100644 --- a/src/_locales/sq/messages.json +++ b/src/_locales/sq/messages.json @@ -1011,6 +1011,10 @@ "message": "Hap skeda ose dritare të panevojshme", "description": "An entry in the widget used to select the type of issue" }, + "supportS6Select1Option7": { + "message": "Leads to badware, phishing", + "description": "An entry in the widget used to select the type of issue" + }, "supportS6Checkbox1": { "message": "Etiketoj faqen si “NSFW” (“E papërshtatshme për punë”)", "description": "A checkbox to use for NSFW sites" diff --git a/src/_locales/sr/messages.json b/src/_locales/sr/messages.json index f17e861e0d097..734925be9e56d 100644 --- a/src/_locales/sr/messages.json +++ b/src/_locales/sr/messages.json @@ -1011,6 +1011,10 @@ "message": "\nОтвара нежељене картице или прозоре", "description": "An entry in the widget used to select the type of issue" }, + "supportS6Select1Option7": { + "message": "Leads to badware, phishing", + "description": "An entry in the widget used to select the type of issue" + }, "supportS6Checkbox1": { "message": "Изначите веб страницу као „NSFW“ (“Није безбедна за рад”)", "description": "A checkbox to use for NSFW sites" diff --git a/src/_locales/sv/messages.json b/src/_locales/sv/messages.json index 3abea889d2536..c6b46530ee7ac 100644 --- a/src/_locales/sv/messages.json +++ b/src/_locales/sv/messages.json @@ -1011,6 +1011,10 @@ "message": "Öppnar oönskade flikar eller fönster", "description": "An entry in the widget used to select the type of issue" }, + "supportS6Select1Option7": { + "message": "Leads to badware, phishing", + "description": "An entry in the widget used to select the type of issue" + }, "supportS6Checkbox1": { "message": "Märk webbsidan som \"NSFW\" (“Inte lämplig på jobbet”)", "description": "A checkbox to use for NSFW sites" diff --git a/src/_locales/sw/messages.json b/src/_locales/sw/messages.json index ad3e42820fc88..ecb8214e3e0d6 100644 --- a/src/_locales/sw/messages.json +++ b/src/_locales/sw/messages.json @@ -1011,6 +1011,10 @@ "message": "Hufungua vichupo au vidirisha visivyotakikana", "description": "An entry in the widget used to select the type of issue" }, + "supportS6Select1Option7": { + "message": "Leads to badware, phishing", + "description": "An entry in the widget used to select the type of issue" + }, "supportS6Checkbox1": { "message": "Ipe tovuti lebo ya \"NSFW\" (\"Haifai Kazini\")", "description": "A checkbox to use for NSFW sites" diff --git a/src/_locales/ta/messages.json b/src/_locales/ta/messages.json index c76293554b4ca..fe1be27a9ef3c 100644 --- a/src/_locales/ta/messages.json +++ b/src/_locales/ta/messages.json @@ -1011,6 +1011,10 @@ "message": "தேவையற்ற தாவல்கள் அ சாளரங்களைத் திறக்கிறது", "description": "An entry in the widget used to select the type of issue" }, + "supportS6Select1Option7": { + "message": "Leads to badware, phishing", + "description": "An entry in the widget used to select the type of issue" + }, "supportS6Checkbox1": { "message": "Label the web page as “NSFW” (“Not Safe For Work”)", "description": "A checkbox to use for NSFW sites" diff --git a/src/_locales/te/messages.json b/src/_locales/te/messages.json index 0f4e085e0c91a..e4d15b99375c1 100644 --- a/src/_locales/te/messages.json +++ b/src/_locales/te/messages.json @@ -1011,6 +1011,10 @@ "message": "అనవసరమైన ట్యాబ్‌లు లేదా విండోలను తెరుస్తుంది", "description": "An entry in the widget used to select the type of issue" }, + "supportS6Select1Option7": { + "message": "Leads to badware, phishing", + "description": "An entry in the widget used to select the type of issue" + }, "supportS6Checkbox1": { "message": "వెబ్ పేజీని “NSFW”గా లేబుల్ చేయండి (“పని కోసం సురక్షితం కాదు”)", "description": "A checkbox to use for NSFW sites" diff --git a/src/_locales/th/messages.json b/src/_locales/th/messages.json index 268cd4da0366d..8bcdce36b5c7f 100644 --- a/src/_locales/th/messages.json +++ b/src/_locales/th/messages.json @@ -1011,6 +1011,10 @@ "message": "Opens unwanted tabs or windows", "description": "An entry in the widget used to select the type of issue" }, + "supportS6Select1Option7": { + "message": "Leads to badware, phishing", + "description": "An entry in the widget used to select the type of issue" + }, "supportS6Checkbox1": { "message": "Label the web page as “NSFW” (“Not Safe For Work”)", "description": "A checkbox to use for NSFW sites" diff --git a/src/_locales/tr/messages.json b/src/_locales/tr/messages.json index 41ac3b3d47b5a..a3c4ad513c734 100644 --- a/src/_locales/tr/messages.json +++ b/src/_locales/tr/messages.json @@ -1011,6 +1011,10 @@ "message": "İstenmeyen sekme veya pencereler açıyor", "description": "An entry in the widget used to select the type of issue" }, + "supportS6Select1Option7": { + "message": "Leads to badware, phishing", + "description": "An entry in the widget used to select the type of issue" + }, "supportS6Checkbox1": { "message": "Web sayfasını uygunsuz (“NSFW”) olarak etiketle (“Not Safe For Work”)", "description": "A checkbox to use for NSFW sites" diff --git a/src/_locales/uk/messages.json b/src/_locales/uk/messages.json index c78caea505588..0bb5e53cb8182 100644 --- a/src/_locales/uk/messages.json +++ b/src/_locales/uk/messages.json @@ -1011,6 +1011,10 @@ "message": "Відкриває небажані вкладки або вікна", "description": "An entry in the widget used to select the type of issue" }, + "supportS6Select1Option7": { + "message": "Leads to badware, phishing", + "description": "An entry in the widget used to select the type of issue" + }, "supportS6Checkbox1": { "message": "Позначити цю сторінку «NSFW» («Небезпечно для роботи»)", "description": "A checkbox to use for NSFW sites" diff --git a/src/_locales/ur/messages.json b/src/_locales/ur/messages.json index 6fbc6fcc824a5..4a608998bc2d0 100644 --- a/src/_locales/ur/messages.json +++ b/src/_locales/ur/messages.json @@ -1011,6 +1011,10 @@ "message": "Opens unwanted tabs or windows", "description": "An entry in the widget used to select the type of issue" }, + "supportS6Select1Option7": { + "message": "Leads to badware, phishing", + "description": "An entry in the widget used to select the type of issue" + }, "supportS6Checkbox1": { "message": "Label the web page as “NSFW” (“Not Safe For Work”)", "description": "A checkbox to use for NSFW sites" diff --git a/src/_locales/vi/messages.json b/src/_locales/vi/messages.json index 9c50188234059..db6393db4db32 100644 --- a/src/_locales/vi/messages.json +++ b/src/_locales/vi/messages.json @@ -1011,6 +1011,10 @@ "message": "Xuất hiện các tab và cửa sổ ngoài mong muốn", "description": "An entry in the widget used to select the type of issue" }, + "supportS6Select1Option7": { + "message": "Leads to badware, phishing", + "description": "An entry in the widget used to select the type of issue" + }, "supportS6Checkbox1": { "message": "Đánh dấu \"NFSW\" - Not Safe For Work cho trang web. Tìm hiểu về \"Not Safe For Work\"", "description": "A checkbox to use for NSFW sites" diff --git a/src/_locales/zh_CN/messages.json b/src/_locales/zh_CN/messages.json index 8e1acd8f3efd4..7004d01ff35ca 100644 --- a/src/_locales/zh_CN/messages.json +++ b/src/_locales/zh_CN/messages.json @@ -1011,6 +1011,10 @@ "message": "打开不需要的标签页或窗口", "description": "An entry in the widget used to select the type of issue" }, + "supportS6Select1Option7": { + "message": "Leads to badware, phishing", + "description": "An entry in the widget used to select the type of issue" + }, "supportS6Checkbox1": { "message": "将该网页标记为 “NSFW”(“工作场所不宜”)", "description": "A checkbox to use for NSFW sites" diff --git a/src/_locales/zh_TW/messages.json b/src/_locales/zh_TW/messages.json index 7c3bc17a10ce8..8fb32f4e43d9c 100644 --- a/src/_locales/zh_TW/messages.json +++ b/src/_locales/zh_TW/messages.json @@ -1011,6 +1011,10 @@ "message": "會開啟不需要的分頁或視窗", "description": "An entry in the widget used to select the type of issue" }, + "supportS6Select1Option7": { + "message": "Leads to badware, phishing", + "description": "An entry in the widget used to select the type of issue" + }, "supportS6Checkbox1": { "message": "將網頁標記為「NSFW」(工作場所不宜)", "description": "A checkbox to use for NSFW sites" From 547fae484274df79da5fbb32a29fe6f165303f9a Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Tue, 17 Sep 2024 09:09:19 -0400 Subject: [PATCH 0232/1099] Improve scriptlet helper `proxy-apply` Related issue: https://github.com/uBlockOrigin/uBlock-issues/issues/3378 --- assets/resources/scriptlets.js | 191 ++++++++++++++++++--------------- 1 file changed, 106 insertions(+), 85 deletions(-) diff --git a/assets/resources/scriptlets.js b/assets/resources/scriptlets.js index 3ccb1b0215280..b12580db40d78 100644 --- a/assets/resources/scriptlets.js +++ b/assets/resources/scriptlets.js @@ -1488,26 +1488,44 @@ function proxyApplyFn( } const fn = context[prop]; if ( typeof fn !== 'function' ) { return; } + if ( proxyApplyFn.CtorContext === undefined ) { + proxyApplyFn.CtorContext = class { + constructor(callFn, callArgs) { + this.callFn = callFn; + this.callArgs = callArgs; + } + reflect() { + return Reflect.construct(this.callFn, this.callArgs); + } + }; + proxyApplyFn.ApplyContext = class { + constructor(callFn, thisArg, callArgs) { + this.callFn = callFn; + this.thisArg = thisArg; + this.callArgs = callArgs; + } + reflect() { + return Reflect.apply(this.callFn, this.thisArg, this.callArgs); + } + }; + } const fnStr = fn.toString(); const toString = (function toString() { return fnStr; }).bind(null); - if ( fn.prototype && fn.prototype.constructor === fn ) { - context[prop] = new Proxy(fn, { - construct: handler, - get(target, prop, receiver) { - if ( prop === 'toString' ) { return toString; } - return Reflect.get(target, prop, receiver); - }, - }); - return (...args) => Reflect.construct(...args); - } - context[prop] = new Proxy(fn, { - apply: handler, + const proxyDetails = { + apply(target, thisArg, args) { + return handler(new proxyApplyFn.ApplyContext(target, thisArg, args)); + }, get(target, prop, receiver) { if ( prop === 'toString' ) { return toString; } return Reflect.get(target, prop, receiver); }, - }); - return (...args) => Reflect.apply(...args); + }; + if ( fn.prototype?.constructor === fn ) { + proxyDetails.construct = function(target, args) { + return handler(new proxyApplyFn.CtorContext(target, args)); + }; + } + context[prop] = new Proxy(fn, proxyDetails); } /******************************************************************************* @@ -1771,18 +1789,19 @@ function addEventListenerDefuser( return matchesBoth; }; runAt(( ) => { - proxyApplyFn('EventTarget.prototype.addEventListener', function(target, thisArg, args) { + proxyApplyFn('EventTarget.prototype.addEventListener', function(context) { + const { callArgs, thisArg } = context; let t, h; try { - t = String(args[0]); - if ( typeof args[1] === 'function' ) { - h = String(safe.Function_toString(args[1])); - } else if ( typeof args[1] === 'object' && args[1] !== null ) { - if ( typeof args[1].handleEvent === 'function' ) { - h = String(safe.Function_toString(args[1].handleEvent)); + t = String(callArgs[0]); + if ( typeof callArgs[1] === 'function' ) { + h = String(safe.Function_toString(callArgs[1])); + } else if ( typeof callArgs[1] === 'object' && callArgs[1] !== null ) { + if ( typeof callArgs[1].handleEvent === 'function' ) { + h = String(safe.Function_toString(callArgs[1].handleEvent)); } } else { - h = String(args[1]); + h = String(callArgs[1]); } } catch(ex) { } @@ -1791,7 +1810,7 @@ function addEventListenerDefuser( } else if ( shouldPrevent(thisArg, t, h) ) { return safe.uboLog(logPrefix, `Prevented: ${t}\n${h}\n${elementDetails(thisArg)}`); } - return Reflect.apply(target, thisArg, args); + return context.reflect(); }); }, extraArgs.runAt); } @@ -2176,10 +2195,11 @@ function noFetchIf( responseProps.type = { value: responseType }; } } - proxyApplyFn('fetch', function fetch(target, thisArg, args) { - const details = args[0] instanceof self.Request - ? args[0] - : Object.assign({ url: args[0] }, args[1]); + proxyApplyFn('fetch', function fetch(context) { + const { callArgs } = context; + const details = callArgs[0] instanceof self.Request + ? callArgs[0] + : Object.assign({ url: callArgs[0] }, callArgs[1]); let proceed = true; try { const props = new Map(); @@ -2197,7 +2217,7 @@ function noFetchIf( safe.uboLog(logPrefix, `Called: ${out.join('\n')}`); } if ( propsToMatch === '' && responseBody === '' ) { - return Reflect.apply(target, thisArg, args); + return context.reflect(); } proceed = needles.length === 0; for ( const { key, pattern } of needles ) { @@ -2212,7 +2232,7 @@ function noFetchIf( } catch(ex) { } if ( proceed ) { - return Reflect.apply(target, thisArg, args); + return context.reflect(); } return generateContentFn(responseBody).then(text => { safe.uboLog(logPrefix, `Prevented with response "${text}"`); @@ -2520,14 +2540,15 @@ function noSetIntervalIf( delay = parseInt(delay, 10); } const reNeedle = safe.patternToRegex(needle); - proxyApplyFn('setInterval', function setInterval(target, thisArg, args) { - const a = args[0] instanceof Function - ? String(safe.Function_toString(args[0])) - : String(args[0]); - const b = args[1]; + proxyApplyFn('setInterval', function setInterval(context) { + const { callArgs } = context; + const a = callArgs[0] instanceof Function + ? String(safe.Function_toString(callArgs[0])) + : String(callArgs[0]); + const b = callArgs[1]; if ( needle === '' && delay === undefined ) { safe.uboLog(logPrefix, `Called:\n${a}\n${b}`); - return Reflect.apply(target, thisArg, args); + return context.reflect(); } let defuse; if ( needle !== '' ) { @@ -2537,10 +2558,10 @@ function noSetIntervalIf( defuse = (b === delay || isNaN(b) && isNaN(delay) ) !== delayNot; } if ( defuse ) { - args[0] = function(){}; + callArgs[0] = function(){}; safe.uboLog(logPrefix, `Prevented:\n${a}\n${b}`); } - return Reflect.apply(target, thisArg, args); + return context.reflect(); }); } @@ -2576,14 +2597,15 @@ function noSetTimeoutIf( delay = parseInt(delay, 10); } const reNeedle = safe.patternToRegex(needle); - proxyApplyFn('setTimeout', function setTimeout(target, thisArg, args) { - const a = args[0] instanceof Function - ? String(safe.Function_toString(args[0])) - : String(args[0]); - const b = args[1]; + proxyApplyFn('setTimeout', function setTimeout(context) { + const { callArgs } = context; + const a = callArgs[0] instanceof Function + ? String(safe.Function_toString(callArgs[0])) + : String(callArgs[0]); + const b = callArgs[1]; if ( needle === '' && delay === undefined ) { safe.uboLog(logPrefix, `Called:\n${a}\n${b}`); - return Reflect.apply(target, thisArg, args); + return context.reflect(); } let defuse; if ( needle !== '' ) { @@ -2593,10 +2615,10 @@ function noSetTimeoutIf( defuse = (b === delay || isNaN(b) && isNaN(delay) ) !== delayNot; } if ( defuse ) { - args[0] = function(){}; + callArgs[0] = function(){}; safe.uboLog(logPrefix, `Prevented:\n${a}\n${b}`); } - return Reflect.apply(target, thisArg, args); + return context.reflect(); }); } @@ -2900,25 +2922,26 @@ function noWindowOpenIf( return decoyElem; }; const noopFunc = function(){}; - proxyApplyFn('open', function open(target, thisArg, args) { - const haystack = args.join(' '); + proxyApplyFn('open', function open(context) { + const { callArgs } = context; + const haystack = callArgs.join(' '); if ( rePattern.test(haystack) !== targetMatchResult ) { if ( safe.logLevel > 1 ) { - safe.uboLog(logPrefix, `Allowed (${args.join(', ')})`); + safe.uboLog(logPrefix, `Allowed (${callArgs.join(', ')})`); } - return Reflect.apply(target, thisArg, args); + return context.reflect(); } - safe.uboLog(logPrefix, `Prevented (${args.join(', ')})`); + safe.uboLog(logPrefix, `Prevented (${callArgs.join(', ')})`); if ( delay === '' ) { return null; } if ( decoy === 'blank' ) { - args[0] = 'about:blank'; - const r = Reflect.apply(target, thisArg, args); + callArgs[0] = 'about:blank'; + const r = context.reflect(); setTimeout(( ) => { r.close(); }, autoRemoveAfter); return r; } const decoyElem = decoy === 'obj' - ? createDecoy('object', 'data', ...args) - : createDecoy('iframe', 'src', ...args); + ? createDecoy('object', 'data', ...callArgs) + : createDecoy('iframe', 'src', ...callArgs); let popup = decoyElem.contentWindow; if ( typeof popup === 'object' && popup !== null ) { Object.defineProperty(popup, 'closed', { value: false }); @@ -4850,8 +4873,8 @@ function trustedPruneOutboundObject( if ( propChain === '' ) { return; } const safe = safeSelf(); const extraArgs = safe.getExtraArgs(Array.from(arguments), 3); - const reflector = proxyApplyFn(propChain, function(...args) { - const objBefore = reflector(...args); + proxyApplyFn(propChain, function(context) { + const objBefore = context.reflect(); if ( objBefore instanceof Object === false ) { return objBefore; } const objAfter = objectPruneFn( objBefore, @@ -4884,26 +4907,27 @@ function trustedReplaceArgument( if ( propChain === '' ) { return; } const safe = safeSelf(); const logPrefix = safe.makeLogPrefix('trusted-replace-argument', propChain, argposRaw, argraw); - const argpos = parseInt(argposRaw, 10) || 0; + const argoffset = parseInt(argposRaw, 10) || 0; const extraArgs = safe.getExtraArgs(Array.from(arguments), 3); const normalValue = validateConstantFn(true, argraw, extraArgs); const reCondition = extraArgs.condition ? safe.patternToRegex(extraArgs.condition) : /^/; - const reflector = proxyApplyFn(propChain, function(...args) { + proxyApplyFn(propChain, function(context) { + const { callArgs } = context; if ( argposRaw === '' ) { - safe.uboLog(logPrefix, `Arguments:\n${args.join('\n')}`); - return reflector(...args); - } - const arglist = args[args.length-1]; - if ( Array.isArray(arglist) === false ) { return reflector(...args); } - const argBefore = arglist[argpos]; - if ( safe.RegExp_test.call(reCondition, argBefore) === false ) { - return reflector(...args); - } - arglist[argpos] = normalValue; - safe.uboLog(logPrefix, `Replaced argument:\nBefore: ${JSON.stringify(argBefore)}\nAfter: ${normalValue}`); - return reflector(...args); + safe.uboLog(logPrefix, `Arguments:\n${callArgs.join('\n')}`); + return context.reflect(); + } + const argpos = argoffset >= 0 ? argoffset : callArgs.length - argoffset; + if ( argpos >= 0 && argpos < callArgs.length ) { + const argBefore = callArgs[argpos]; + if ( safe.RegExp_test.call(reCondition, argBefore) ) { + callArgs[argpos] = normalValue; + safe.uboLog(logPrefix, `Replaced argument:\nBefore: ${JSON.stringify(argBefore)}\nAfter: ${normalValue}`); + } + } + return context.reflect(); }); } @@ -4933,8 +4957,8 @@ function trustedReplaceOutboundText( : rawReplacement; const extraArgs = safe.getExtraArgs(args); const reCondition = safe.patternToRegex(extraArgs.condition || ''); - const reflector = proxyApplyFn(propChain, function(...args) { - const encodedTextBefore = reflector(...args); + proxyApplyFn(propChain, function(context) { + const encodedTextBefore = context.reflect(); let textBefore = encodedTextBefore; if ( extraArgs.encoding === 'base64' ) { try { textBefore = self.atob(encodedTextBefore); } @@ -5011,34 +5035,31 @@ function trustedSuppressNativeMethod( return { type: 'exact', value: undefined }; } }); - const reflector = proxyApplyFn(methodPath, function(...args) { + proxyApplyFn(methodPath, function(context) { + const { callArgs } = context; if ( signature === '' ) { - safe.uboLog(logPrefix, `Arguments:\n${args.join('\n')}`); - return reflector(...args); - } - const arglist = args[args.length-1]; - if ( Array.isArray(arglist) === false ) { - return reflector(...args); + safe.uboLog(logPrefix, `Arguments:\n${callArgs.join('\n')}`); + return context.reflect(); } - if ( arglist.length < signatureArgs.length ) { - return reflector(...args); + if ( callArgs.length < signatureArgs.length ) { + return context.reflect(); } for ( let i = 0; i < signatureArgs.length; i++ ) { const signatureArg = signatureArgs[i]; if ( signatureArg === undefined ) { continue; } - const targetArg = arglist[i]; + const targetArg = callArgs[i]; if ( signatureArg.type === 'exact' ) { if ( targetArg !== signatureArg.value ) { - return reflector(...args); + return context.reflect(); } } if ( signatureArg.type === 'pattern' ) { if ( safe.RegExp_test.call(signatureArg.re, targetArg) === false ) { - return reflector(...args); + return context.reflect(); } } } - safe.uboLog(logPrefix, `Suppressed:\n${args.join('\n')}`); + safe.uboLog(logPrefix, `Suppressed:\n${callArgs.join('\n')}`); if ( how === 'abort' ) { throw new ReferenceError(); } From 9155a89c08c4d2dffdd1db9c82a9b861f2360cbe Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Tue, 17 Sep 2024 09:28:02 -0400 Subject: [PATCH 0233/1099] Update changelog --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6a79906df5df0..f70b74100377e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,5 @@ +- [Improve scriptlet helper `proxy-apply`](https://github.com/gorhill/uBlock/commit/547fae4842) +- [Add an entry in _Report_ page for badware/phishing category](https://github.com/gorhill/uBlock/commit/e18a3707c7) - [New static network filter option `urlskip=`](https://github.com/gorhill/uBlock/commit/266ec4894b) - [Rewrite cname uncloaking code to account for new `ipaddress=` option](https://github.com/gorhill/uBlock/commit/6acf97bf51) - [Avoid using dns.resolve() for proxied DNS resolution](https://github.com/gorhill/uBlock/commit/d5f14ffa32) From 7fb626850e96ce8f300df13708a852bf63dea0f8 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Tue, 17 Sep 2024 09:28:31 -0400 Subject: [PATCH 0234/1099] New revision for release candidate --- dist/version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dist/version b/dist/version index a56296cd32544..1d97901e59c18 100644 --- a/dist/version +++ b/dist/version @@ -1 +1 @@ -1.59.1.22 \ No newline at end of file +1.59.1.100 \ No newline at end of file From d238baa374d82f17661cc07bd5302cfa761261f5 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Tue, 17 Sep 2024 09:35:36 -0400 Subject: [PATCH 0235/1099] Make Firefox dev build auto-update --- dist/firefox/updates.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dist/firefox/updates.json b/dist/firefox/updates.json index d52f231b67443..7d6cce4f17513 100644 --- a/dist/firefox/updates.json +++ b/dist/firefox/updates.json @@ -3,9 +3,9 @@ "uBlock0@raymondhill.net": { "updates": [ { - "version": "1.59.1.22", + "version": "1.59.1.100", "browser_specific_settings": { "gecko": { "strict_min_version": "78.0" } }, - "update_link": "https://github.com/gorhill/uBlock/releases/download/1.59.1b22/uBlock0_1.59.1b22.firefox.signed.xpi" + "update_link": "https://github.com/gorhill/uBlock/releases/download/1.59.1rc0/uBlock0_1.59.1rc0.firefox.signed.xpi" } ] } From 6a042f152b513bbf5b3c8623ea3fbe2574ac0e24 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Tue, 17 Sep 2024 10:26:40 -0400 Subject: [PATCH 0236/1099] Ignore browser-provided 0.0.0.0 ip address when DNS is proxied Related issue: https://github.com/uBlockOrigin/uBlock-issues/issues/3379 --- platform/firefox/vapi-background-ext.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/platform/firefox/vapi-background-ext.js b/platform/firefox/vapi-background-ext.js index 5d3ff8c5dfbdd..7fdc3dd5505f6 100644 --- a/platform/firefox/vapi-background-ext.js +++ b/platform/firefox/vapi-background-ext.js @@ -94,6 +94,10 @@ vAPI.Net = class extends vAPI.Net { } normalizeDetails(details) { + // https://github.com/uBlockOrigin/uBlock-issues/issues/3379 + if ( details.proxyInfo?.proxyDNS && details.ip === '0.0.0.0' ) { + details.ip = null; + } const type = details.type; if ( type === 'imageset' ) { details.type = 'image'; From 58c13bacb783366919b4e2079306b4bad1905db0 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Tue, 17 Sep 2024 10:28:54 -0400 Subject: [PATCH 0237/1099] new revision for release candidate --- dist/version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dist/version b/dist/version index 1d97901e59c18..91e99c278d4a1 100644 --- a/dist/version +++ b/dist/version @@ -1 +1 @@ -1.59.1.100 \ No newline at end of file +1.59.1.101 \ No newline at end of file From acffae6a21b35294c8d6f2c451441ada904190cd Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Tue, 17 Sep 2024 10:36:08 -0400 Subject: [PATCH 0238/1099] Make Firefox dev build auto-update --- dist/firefox/updates.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dist/firefox/updates.json b/dist/firefox/updates.json index 7d6cce4f17513..0a9608e02693a 100644 --- a/dist/firefox/updates.json +++ b/dist/firefox/updates.json @@ -3,9 +3,9 @@ "uBlock0@raymondhill.net": { "updates": [ { - "version": "1.59.1.100", + "version": "1.59.1.101", "browser_specific_settings": { "gecko": { "strict_min_version": "78.0" } }, - "update_link": "https://github.com/gorhill/uBlock/releases/download/1.59.1rc0/uBlock0_1.59.1rc0.firefox.signed.xpi" + "update_link": "https://github.com/gorhill/uBlock/releases/download/1.59.1rc1/uBlock0_1.59.1rc1.firefox.signed.xpi" } ] } From e8f6f3ddffd14887120bfe552b823816945aeda6 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Tue, 17 Sep 2024 17:33:06 -0400 Subject: [PATCH 0239/1099] Throttle down repeated scriptlet logging information Related feedback https://github.com/uBlockOrigin/uBlock-issues/issues/3378#issuecomment-2356422784 --- assets/resources/scriptlets.js | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/assets/resources/scriptlets.js b/assets/resources/scriptlets.js index b12580db40d78..dbdab81024586 100644 --- a/assets/resources/scriptlets.js +++ b/assets/resources/scriptlets.js @@ -176,9 +176,18 @@ function safeSelf() { const bc = new self.BroadcastChannel(scriptletGlobals.bcSecret); let bcBuffer = []; safe.logLevel = scriptletGlobals.logLevel || 1; + let lastLogType = ''; + let lastLogText = ''; + let lastLogTime = 0; safe.sendToLogger = (type, ...args) => { if ( args.length === 0 ) { return; } const text = `[${document.location.hostname || document.location.href}]${args.join(' ')}`; + if ( text === lastLogText && type === lastLogType ) { + if ( (Date.now() - lastLogTime) < 300000 ) { return; } + } + lastLogType = type; + lastLogText = text; + lastLogTime = Date.now(); if ( bcBuffer === undefined ) { return bc.postMessage({ what: 'messageToLogger', type, text }); } From 00d4262c0df668b996cda8c620d7e032e9f6a265 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Tue, 17 Sep 2024 17:35:22 -0400 Subject: [PATCH 0240/1099] New revision for release candidate --- dist/version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dist/version b/dist/version index 91e99c278d4a1..c19925015fc90 100644 --- a/dist/version +++ b/dist/version @@ -1 +1 @@ -1.59.1.101 \ No newline at end of file +1.59.1.102 \ No newline at end of file From 2375ca3ca0b0c1ca2eafba49cc007077f3279275 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Tue, 17 Sep 2024 17:36:12 -0400 Subject: [PATCH 0241/1099] Update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index f70b74100377e..8d6eb1b08b6a9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,4 @@ +- [Throttle down repeated scriptlet logging information](https://github.com/gorhill/uBlock/commit/e8f6f3ddff) - [Improve scriptlet helper `proxy-apply`](https://github.com/gorhill/uBlock/commit/547fae4842) - [Add an entry in _Report_ page for badware/phishing category](https://github.com/gorhill/uBlock/commit/e18a3707c7) - [New static network filter option `urlskip=`](https://github.com/gorhill/uBlock/commit/266ec4894b) From 3238fe48dbe89535814811628c751f2129e71de8 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Tue, 17 Sep 2024 17:48:45 -0400 Subject: [PATCH 0242/1099] Import translation work from https://crowdin.com/project/ublock --- platform/mv3/description/webstore.vi.txt | 2 +- platform/mv3/extension/_locales/hu/messages.json | 4 ++-- src/_locales/ar/messages.json | 2 +- src/_locales/bg/messages.json | 2 +- src/_locales/ca/messages.json | 2 +- src/_locales/cs/messages.json | 2 +- src/_locales/da/messages.json | 2 +- src/_locales/de/messages.json | 2 +- src/_locales/eo/messages.json | 10 +++++----- src/_locales/es/messages.json | 2 +- src/_locales/et/messages.json | 2 +- src/_locales/fr/messages.json | 2 +- src/_locales/fy/messages.json | 2 +- src/_locales/gl/messages.json | 2 +- src/_locales/he/messages.json | 2 +- src/_locales/hi/messages.json | 2 +- src/_locales/hr/messages.json | 2 +- src/_locales/hu/messages.json | 6 +++--- src/_locales/it/messages.json | 2 +- src/_locales/ja/messages.json | 2 +- src/_locales/lv/messages.json | 2 +- src/_locales/nl/messages.json | 2 +- src/_locales/pl/messages.json | 2 +- src/_locales/pt_BR/messages.json | 2 +- src/_locales/pt_PT/messages.json | 2 +- src/_locales/ru/messages.json | 2 +- src/_locales/sk/messages.json | 2 +- src/_locales/sv/messages.json | 2 +- src/_locales/tr/messages.json | 2 +- src/_locales/uk/messages.json | 2 +- src/_locales/vi/messages.json | 2 +- src/_locales/zh_CN/messages.json | 2 +- src/_locales/zh_TW/messages.json | 4 ++-- 33 files changed, 41 insertions(+), 41 deletions(-) diff --git a/platform/mv3/description/webstore.vi.txt b/platform/mv3/description/webstore.vi.txt index 249df6a5e8e37..1ea12d09442c1 100644 --- a/platform/mv3/description/webstore.vi.txt +++ b/platform/mv3/description/webstore.vi.txt @@ -7,7 +7,7 @@ Bộ quy tắc mặc định tương tự bộ lọc của uBlock Origin: - EasyPrivacy - Danh sách máy chủ chạy quảng cáo và trình theo dõi của Pete Lowe -You can enable more rulesets by visiting the options page -- click the _Cogs_ icon in the popup panel. +Bạn có thể tự thêm quy tắc mới ở trang cài đặt -- click vào biểu tượng _Bánh răng_ ở trong cửa sổ popup. uBOL mang tính khai báo hoàn toàn, vì vậy uBOL sẽ không cần phải liên tục chạy để chặn nội dung. Thay vào đó, chính trình duyệt sẽ thực hiện lọc nội dung bằng cách sử dụng công cụ chèn CSS/JS hiệu quả hơn có sẵn của nó. Điều này cũng đồng thời có nghĩa là uBOL sẽ không tiêu tốn tài nguyên CPU/bộ nhớ của bạn để chặn nội dung. uBOL sẽ chỉ chạy _khi và chỉ khi_ bạn đang xem cửa sổ popup của uBOL, hoặc bạn đang cấu hình uBOL ở trang cài đặt. diff --git a/platform/mv3/extension/_locales/hu/messages.json b/platform/mv3/extension/_locales/hu/messages.json index bb044590e905f..483b2eef56a22 100644 --- a/platform/mv3/extension/_locales/hu/messages.json +++ b/platform/mv3/extension/_locales/hu/messages.json @@ -128,7 +128,7 @@ "description": "Name of blocking mode 2" }, "filteringMode3Name": { - "message": "elkészült", + "message": "teljes", "description": "Name of blocking mode 3" }, "basicFilteringModeDescription": { @@ -148,7 +148,7 @@ "description": "A short description for the editable field which lists trusted sites" }, "noFilteringModePlaceholder": { - "message": "[hostnames only]\nexample.com\ngames.example\n...", + "message": "[csak kiszolgálónevek]\nexample.com\ngames.example\n...", "description": "Default text for in edit field" }, "behaviorSectionLabel": { diff --git a/src/_locales/ar/messages.json b/src/_locales/ar/messages.json index 17bd55c0f86c1..ca454de959e84 100644 --- a/src/_locales/ar/messages.json +++ b/src/_locales/ar/messages.json @@ -1012,7 +1012,7 @@ "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option7": { - "message": "Leads to badware, phishing", + "message": "يؤدي إلى البرامج الضارة والإحتيال", "description": "An entry in the widget used to select the type of issue" }, "supportS6Checkbox1": { diff --git a/src/_locales/bg/messages.json b/src/_locales/bg/messages.json index 98e3bd4a61606..086e813398ab2 100644 --- a/src/_locales/bg/messages.json +++ b/src/_locales/bg/messages.json @@ -1012,7 +1012,7 @@ "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option7": { - "message": "Leads to badware, phishing", + "message": "Води до зловреден софтуер, фишинг", "description": "An entry in the widget used to select the type of issue" }, "supportS6Checkbox1": { diff --git a/src/_locales/ca/messages.json b/src/_locales/ca/messages.json index 9ec5bda0cba6e..27963d45c2ccb 100644 --- a/src/_locales/ca/messages.json +++ b/src/_locales/ca/messages.json @@ -1012,7 +1012,7 @@ "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option7": { - "message": "Leads to badware, phishing", + "message": "Condueix a programari maliciós, pesca electrònica", "description": "An entry in the widget used to select the type of issue" }, "supportS6Checkbox1": { diff --git a/src/_locales/cs/messages.json b/src/_locales/cs/messages.json index 8fc18fd9623c4..9e28071c50cd1 100644 --- a/src/_locales/cs/messages.json +++ b/src/_locales/cs/messages.json @@ -1012,7 +1012,7 @@ "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option7": { - "message": "Leads to badware, phishing", + "message": "Vede k badwaru, phishingu", "description": "An entry in the widget used to select the type of issue" }, "supportS6Checkbox1": { diff --git a/src/_locales/da/messages.json b/src/_locales/da/messages.json index ca5ee9491d69b..20557ad5cae5e 100644 --- a/src/_locales/da/messages.json +++ b/src/_locales/da/messages.json @@ -1012,7 +1012,7 @@ "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option7": { - "message": "Leads to badware, phishing", + "message": "Fører til badware, phishing", "description": "An entry in the widget used to select the type of issue" }, "supportS6Checkbox1": { diff --git a/src/_locales/de/messages.json b/src/_locales/de/messages.json index 748653846b451..af7d997b6412e 100644 --- a/src/_locales/de/messages.json +++ b/src/_locales/de/messages.json @@ -1012,7 +1012,7 @@ "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option7": { - "message": "Leads to badware, phishing", + "message": "Führt zu Schadsoftware, Phishing", "description": "An entry in the widget used to select the type of issue" }, "supportS6Checkbox1": { diff --git a/src/_locales/eo/messages.json b/src/_locales/eo/messages.json index 084841e499d9f..448de1a33596f 100644 --- a/src/_locales/eo/messages.json +++ b/src/_locales/eo/messages.json @@ -484,11 +484,11 @@ "description": "Filter lists section name" }, "3pGroupSocial": { - "message": "Social widgets", + "message": "Socialaj fenestraĵoj", "description": "Filter lists section name" }, "3pGroupCookies": { - "message": "Cookie notices", + "message": "Avizoj de kuketoj", "description": "Filter lists section name" }, "3pGroupAnnoyances": { @@ -540,11 +540,11 @@ "description": "Warning against copy-pasting filters from random sources" }, "1pEnableMyFiltersLabel": { - "message": "Enable my custom filters", + "message": "Ŝalti miajn proprajn filtrilojn", "description": "Label for the checkbox use to enable/disable 'My filters' list" }, "1pTrustMyFiltersLabel": { - "message": "Allow custom filters requiring trust", + "message": "Permesi proprajn filtrilojn, kiuj postulas fidon", "description": "Label for the checkbox use to trust the content of 'My filters' list" }, "1pImport": { @@ -844,7 +844,7 @@ "description": "Logger setting: A sentence to describe the purpose of the settings below" }, "loggerSettingPerEntryMaxAge": { - "message": "Preserve entries from the last {{input}} minutes", + "message": "Konservi enigojn ekde la lastaj {{input}} minutoj", "description": "A logger setting" }, "loggerSettingPerTabMaxLoads": { diff --git a/src/_locales/es/messages.json b/src/_locales/es/messages.json index c88cc62193f60..76604d8fb9f90 100644 --- a/src/_locales/es/messages.json +++ b/src/_locales/es/messages.json @@ -1012,7 +1012,7 @@ "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option7": { - "message": "Leads to badware, phishing", + "message": "Conduce a malware y phishing", "description": "An entry in the widget used to select the type of issue" }, "supportS6Checkbox1": { diff --git a/src/_locales/et/messages.json b/src/_locales/et/messages.json index 63ac039bb19b2..178950aef80b9 100644 --- a/src/_locales/et/messages.json +++ b/src/_locales/et/messages.json @@ -1012,7 +1012,7 @@ "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option7": { - "message": "Leads to badware, phishing", + "message": "Põhjustab pahavara, õngitsuskirju", "description": "An entry in the widget used to select the type of issue" }, "supportS6Checkbox1": { diff --git a/src/_locales/fr/messages.json b/src/_locales/fr/messages.json index 3cf16c76e2e7a..3e6731330100d 100644 --- a/src/_locales/fr/messages.json +++ b/src/_locales/fr/messages.json @@ -1012,7 +1012,7 @@ "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option7": { - "message": "Leads to badware, phishing", + "message": "Conduit à/Redirige vers des logiciels malveillants, du hameçonnage...", "description": "An entry in the widget used to select the type of issue" }, "supportS6Checkbox1": { diff --git a/src/_locales/fy/messages.json b/src/_locales/fy/messages.json index 729a7d126d440..863d058ee3762 100644 --- a/src/_locales/fy/messages.json +++ b/src/_locales/fy/messages.json @@ -1012,7 +1012,7 @@ "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option7": { - "message": "Leads to badware, phishing", + "message": "Liedt ta badware, phishing", "description": "An entry in the widget used to select the type of issue" }, "supportS6Checkbox1": { diff --git a/src/_locales/gl/messages.json b/src/_locales/gl/messages.json index 358fe4043b989..f09c526ef8dee 100644 --- a/src/_locales/gl/messages.json +++ b/src/_locales/gl/messages.json @@ -1012,7 +1012,7 @@ "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option7": { - "message": "Leads to badware, phishing", + "message": "Leva a software malicioso, phishing", "description": "An entry in the widget used to select the type of issue" }, "supportS6Checkbox1": { diff --git a/src/_locales/he/messages.json b/src/_locales/he/messages.json index eb55156173ab1..a9bf5714bc254 100644 --- a/src/_locales/he/messages.json +++ b/src/_locales/he/messages.json @@ -1012,7 +1012,7 @@ "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option7": { - "message": "Leads to badware, phishing", + "message": "מוביל לנוזקה, פישינג", "description": "An entry in the widget used to select the type of issue" }, "supportS6Checkbox1": { diff --git a/src/_locales/hi/messages.json b/src/_locales/hi/messages.json index 0380515188a5c..1a26f87293c52 100644 --- a/src/_locales/hi/messages.json +++ b/src/_locales/hi/messages.json @@ -1012,7 +1012,7 @@ "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option7": { - "message": "Leads to badware, phishing", + "message": "बैडवेयर, फ़िशिंग की ओर ले जाता है", "description": "An entry in the widget used to select the type of issue" }, "supportS6Checkbox1": { diff --git a/src/_locales/hr/messages.json b/src/_locales/hr/messages.json index 59d07378a8bbe..e7a831de98333 100644 --- a/src/_locales/hr/messages.json +++ b/src/_locales/hr/messages.json @@ -1012,7 +1012,7 @@ "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option7": { - "message": "Leads to badware, phishing", + "message": "Vodi do zloćudnog softvera, krađe identiteta", "description": "An entry in the widget used to select the type of issue" }, "supportS6Checkbox1": { diff --git a/src/_locales/hu/messages.json b/src/_locales/hu/messages.json index 98c4d065e5cdc..e80959222fc59 100644 --- a/src/_locales/hu/messages.json +++ b/src/_locales/hu/messages.json @@ -76,7 +76,7 @@ "description": "Message to be read by screen readers" }, "popupPowerSwitchInfo2": { - "message": "Kattints az uBlock₀ engedélyezéséhez ezen a webhelyen.", + "message": "Kattintson a uBlock₀ engedélyezéséhez ezen a webhelyen.", "description": "Message to be read by screen readers" }, "popupBlockedRequestPrompt": { @@ -136,7 +136,7 @@ "description": "Tooltip for the no-popups per-site switch" }, "popupTipNoPopups1": { - "message": "Kattints az összes előugró ablak letiltásához ezen a webhelyen", + "message": "Kattintson az összes felugró ablak letiltásához ezen a webhelyen", "description": "Tooltip for the no-popups per-site switch" }, "popupTipNoPopups2": { @@ -1012,7 +1012,7 @@ "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option7": { - "message": "Leads to badware, phishing", + "message": "Kártékony programokhoz, adathalászathoz vezet", "description": "An entry in the widget used to select the type of issue" }, "supportS6Checkbox1": { diff --git a/src/_locales/it/messages.json b/src/_locales/it/messages.json index cafc1e0e94458..242d997e1fadd 100644 --- a/src/_locales/it/messages.json +++ b/src/_locales/it/messages.json @@ -1012,7 +1012,7 @@ "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option7": { - "message": "Leads to badware, phishing", + "message": "Porta a badware, phishing", "description": "An entry in the widget used to select the type of issue" }, "supportS6Checkbox1": { diff --git a/src/_locales/ja/messages.json b/src/_locales/ja/messages.json index 1a720ceb13d83..0b8c6d014a6ea 100644 --- a/src/_locales/ja/messages.json +++ b/src/_locales/ja/messages.json @@ -1012,7 +1012,7 @@ "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option7": { - "message": "Leads to badware, phishing", + "message": "悪質ソフトやフィッシングへの誘導", "description": "An entry in the widget used to select the type of issue" }, "supportS6Checkbox1": { diff --git a/src/_locales/lv/messages.json b/src/_locales/lv/messages.json index fc24c5e2bb0c0..f0359c580da59 100644 --- a/src/_locales/lv/messages.json +++ b/src/_locales/lv/messages.json @@ -1012,7 +1012,7 @@ "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option7": { - "message": "Leads to badware, phishing", + "message": "Noved pie slitkas programmatūras, pikšķerēšanas", "description": "An entry in the widget used to select the type of issue" }, "supportS6Checkbox1": { diff --git a/src/_locales/nl/messages.json b/src/_locales/nl/messages.json index 49b94078774c2..49a3af00b2fe9 100644 --- a/src/_locales/nl/messages.json +++ b/src/_locales/nl/messages.json @@ -1012,7 +1012,7 @@ "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option7": { - "message": "Leads to badware, phishing", + "message": "Leidt tot badware, phishing", "description": "An entry in the widget used to select the type of issue" }, "supportS6Checkbox1": { diff --git a/src/_locales/pl/messages.json b/src/_locales/pl/messages.json index b63a8a218b48f..b95ccce576959 100644 --- a/src/_locales/pl/messages.json +++ b/src/_locales/pl/messages.json @@ -1012,7 +1012,7 @@ "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option7": { - "message": "Leads to badware, phishing", + "message": "Prowadzi do szkodliwego oprogramowania, phishingu", "description": "An entry in the widget used to select the type of issue" }, "supportS6Checkbox1": { diff --git a/src/_locales/pt_BR/messages.json b/src/_locales/pt_BR/messages.json index 9b4d7c6f7ca38..f1a1e3666d399 100644 --- a/src/_locales/pt_BR/messages.json +++ b/src/_locales/pt_BR/messages.json @@ -1012,7 +1012,7 @@ "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option7": { - "message": "Leads to badware, phishing", + "message": "Leva a badware, phishing", "description": "An entry in the widget used to select the type of issue" }, "supportS6Checkbox1": { diff --git a/src/_locales/pt_PT/messages.json b/src/_locales/pt_PT/messages.json index 882b2c278ea13..54b86dc7d0072 100644 --- a/src/_locales/pt_PT/messages.json +++ b/src/_locales/pt_PT/messages.json @@ -1012,7 +1012,7 @@ "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option7": { - "message": "Leads to badware, phishing", + "message": "Leva a badware, phishing", "description": "An entry in the widget used to select the type of issue" }, "supportS6Checkbox1": { diff --git a/src/_locales/ru/messages.json b/src/_locales/ru/messages.json index fd4d542cb1e8d..c0bfd156f5427 100644 --- a/src/_locales/ru/messages.json +++ b/src/_locales/ru/messages.json @@ -1012,7 +1012,7 @@ "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option7": { - "message": "Leads to badware, phishing", + "message": "Вредоносное ПО, фишинг", "description": "An entry in the widget used to select the type of issue" }, "supportS6Checkbox1": { diff --git a/src/_locales/sk/messages.json b/src/_locales/sk/messages.json index e1adf45e0fe9c..c263032e10a41 100644 --- a/src/_locales/sk/messages.json +++ b/src/_locales/sk/messages.json @@ -1012,7 +1012,7 @@ "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option7": { - "message": "Leads to badware, phishing", + "message": "Smeruje k badvéru a phishingu", "description": "An entry in the widget used to select the type of issue" }, "supportS6Checkbox1": { diff --git a/src/_locales/sv/messages.json b/src/_locales/sv/messages.json index c6b46530ee7ac..da30a923a1ff7 100644 --- a/src/_locales/sv/messages.json +++ b/src/_locales/sv/messages.json @@ -1012,7 +1012,7 @@ "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option7": { - "message": "Leads to badware, phishing", + "message": "Leder till skadlig programvara, nätfiske", "description": "An entry in the widget used to select the type of issue" }, "supportS6Checkbox1": { diff --git a/src/_locales/tr/messages.json b/src/_locales/tr/messages.json index a3c4ad513c734..5d6544a7eb862 100644 --- a/src/_locales/tr/messages.json +++ b/src/_locales/tr/messages.json @@ -1012,7 +1012,7 @@ "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option7": { - "message": "Leads to badware, phishing", + "message": "Kötü niyetli yazılıma yönlendiriyor, oltalama", "description": "An entry in the widget used to select the type of issue" }, "supportS6Checkbox1": { diff --git a/src/_locales/uk/messages.json b/src/_locales/uk/messages.json index 0bb5e53cb8182..bf8dafa5f8476 100644 --- a/src/_locales/uk/messages.json +++ b/src/_locales/uk/messages.json @@ -1012,7 +1012,7 @@ "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option7": { - "message": "Leads to badware, phishing", + "message": "Веде до шкідливого ПЗ, фішингу", "description": "An entry in the widget used to select the type of issue" }, "supportS6Checkbox1": { diff --git a/src/_locales/vi/messages.json b/src/_locales/vi/messages.json index db6393db4db32..6c828ba4388c5 100644 --- a/src/_locales/vi/messages.json +++ b/src/_locales/vi/messages.json @@ -1012,7 +1012,7 @@ "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option7": { - "message": "Leads to badware, phishing", + "message": "Dẫn đến phần mềm độc hại, lừa đảo", "description": "An entry in the widget used to select the type of issue" }, "supportS6Checkbox1": { diff --git a/src/_locales/zh_CN/messages.json b/src/_locales/zh_CN/messages.json index 7004d01ff35ca..bd8c5513ce7a8 100644 --- a/src/_locales/zh_CN/messages.json +++ b/src/_locales/zh_CN/messages.json @@ -1012,7 +1012,7 @@ "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option7": { - "message": "Leads to badware, phishing", + "message": "指向恶意软件、钓鱼网站", "description": "An entry in the widget used to select the type of issue" }, "supportS6Checkbox1": { diff --git a/src/_locales/zh_TW/messages.json b/src/_locales/zh_TW/messages.json index 8fb32f4e43d9c..bdd82a0cb187d 100644 --- a/src/_locales/zh_TW/messages.json +++ b/src/_locales/zh_TW/messages.json @@ -4,7 +4,7 @@ "description": "extension name." }, "extShortDesc": { - "message": "終於,有款僅使用少量 CPU 及記憶體的高效率阻擋器。", + "message": "終於有一款高效能的封鎖工具。對 CPU 和記憶體的佔用極低。", "description": "this will be in the Chrome web store: must be 132 characters or less" }, "dashboardName": { @@ -1012,7 +1012,7 @@ "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option7": { - "message": "Leads to badware, phishing", + "message": "導致惡意軟體、網路釣魚", "description": "An entry in the widget used to select the type of issue" }, "supportS6Checkbox1": { From 11f43d4a3d4867e5446b5c8046eb562d9f367b53 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Tue, 17 Sep 2024 18:00:42 -0400 Subject: [PATCH 0243/1099] Make Firefox dev build auto-update --- dist/firefox/updates.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dist/firefox/updates.json b/dist/firefox/updates.json index 0a9608e02693a..d3598101729c9 100644 --- a/dist/firefox/updates.json +++ b/dist/firefox/updates.json @@ -3,9 +3,9 @@ "uBlock0@raymondhill.net": { "updates": [ { - "version": "1.59.1.101", + "version": "1.59.1.102", "browser_specific_settings": { "gecko": { "strict_min_version": "78.0" } }, - "update_link": "https://github.com/gorhill/uBlock/releases/download/1.59.1rc1/uBlock0_1.59.1rc1.firefox.signed.xpi" + "update_link": "https://github.com/gorhill/uBlock/releases/download/1.59.1rc2/uBlock0_1.59.1rc2.firefox.signed.xpi" } ] } From fe3846b72fea0ee5a1fa5f57000858cb4875db44 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Tue, 17 Sep 2024 18:10:09 -0400 Subject: [PATCH 0244/1099] Oops meant to be 5s, not 300s... --- assets/resources/scriptlets.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets/resources/scriptlets.js b/assets/resources/scriptlets.js index dbdab81024586..dc613c46f5cbf 100644 --- a/assets/resources/scriptlets.js +++ b/assets/resources/scriptlets.js @@ -183,7 +183,7 @@ function safeSelf() { if ( args.length === 0 ) { return; } const text = `[${document.location.hostname || document.location.href}]${args.join(' ')}`; if ( text === lastLogText && type === lastLogType ) { - if ( (Date.now() - lastLogTime) < 300000 ) { return; } + if ( (Date.now() - lastLogTime) < 5000 ) { return; } } lastLogType = type; lastLogText = text; From 4094f43daf4eda3fe0a08f1a4e38e8545228b36f Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Tue, 17 Sep 2024 18:10:32 -0400 Subject: [PATCH 0245/1099] New revision for release candidate --- dist/version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dist/version b/dist/version index c19925015fc90..ca5335ac542aa 100644 --- a/dist/version +++ b/dist/version @@ -1 +1 @@ -1.59.1.102 \ No newline at end of file +1.59.1.103 \ No newline at end of file From f0f859c685e93f80393835fff0ca3c0eaae80912 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Tue, 17 Sep 2024 18:16:11 -0400 Subject: [PATCH 0246/1099] Make Firefox dev build auto-update --- dist/firefox/updates.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dist/firefox/updates.json b/dist/firefox/updates.json index d3598101729c9..32ac2fd6ec0c5 100644 --- a/dist/firefox/updates.json +++ b/dist/firefox/updates.json @@ -3,9 +3,9 @@ "uBlock0@raymondhill.net": { "updates": [ { - "version": "1.59.1.102", + "version": "1.59.1.103", "browser_specific_settings": { "gecko": { "strict_min_version": "78.0" } }, - "update_link": "https://github.com/gorhill/uBlock/releases/download/1.59.1rc2/uBlock0_1.59.1rc2.firefox.signed.xpi" + "update_link": "https://github.com/gorhill/uBlock/releases/download/1.59.1rc3/uBlock0_1.59.1rc3.firefox.signed.xpi" } ] } From 62d74d4f1d44589cdf9cc1a52c2bcb5b28cd1598 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Wed, 18 Sep 2024 09:56:46 -0400 Subject: [PATCH 0247/1099] Add trailing wildcard syntax to `ipaddress=` option Related issue: https://github.com/uBlockOrigin/uBlock-issues/issues/3381 --- src/js/static-net-filtering.js | 49 +++++++++++++++++++++++----------- 1 file changed, 34 insertions(+), 15 deletions(-) diff --git a/src/js/static-net-filtering.js b/src/js/static-net-filtering.js index 87216156f7018..afbd9f01e072e 100644 --- a/src/js/static-net-filtering.js +++ b/src/js/static-net-filtering.js @@ -2979,26 +2979,48 @@ registerFilterClass(FilterOnHeaders); /******************************************************************************/ class FilterIPAddress { + static TYPE_UNKNOWN = 0; + static TYPE_EQUAL = 1; + static TYPE_STARTSWITH = 2; + static TYPE_LAN = 3; + static TYPE_LOOPBACK = 4; + static TYPE_RE = 5; static reIPv6IPv4lan = /^::ffff:(7f\w{2}|a\w{2}|a9fe|c0a8):\w+$/; static reIPv6local = /^f[cd]\w{2}:/; static match(idata) { const ipaddr = $requestAddress; - const details = filterRefs[filterData[idata+1]]; - if ( details.isRegex ) { - if ( details.$re === undefined ) { - details.$re = new RegExp(details.pattern.slice(1, -1)); - } - return details.$re.test(ipaddr); - } if ( ipaddr === '' ) { return false; } - if ( details.pattern === 'lan' ) { + const details = filterRefs[filterData[idata+1]]; + switch ( details.$type || this.TYPE_UNKNOWN ) { + case this.TYPE_EQUAL: + return ipaddr === details.pattern; + case this.TYPE_LAN: return this.isLAN(ipaddr); - } - if ( details.pattern === 'loopback' ) { + case this.TYPE_LOOPBACK: return this.isLoopback(ipaddr); + case this.TYPE_STARTSWITH: + return ipaddr.startsWith(details.$pattern); + case this.TYPE_RE: + return details.$pattern.test(ipaddr) + default: + break; + } + const { pattern } = details; + if ( pattern === 'lan' ) { + details.$type = this.TYPE_LAN; + } else if ( pattern === 'loopback' ) { + details.$type = this.TYPE_LOOPBACK; + } else if ( pattern.startsWith('/') && pattern.endsWith('/') ) { + details.$type = this.TYPE_RE; + details.$pattern = new RegExp(pattern.slice(1, -1)); + } else if ( pattern.endsWith('*') ) { + details.$type = this.TYPE_STARTSWITH; + details.$pattern = pattern.slice(0, -1); + } else { + details.$type = this.TYPE_EQUAL; } - return ipaddr.startsWith(details.pattern); + return this.match(idata); } // https://github.com/uBlockOrigin/uAssets/blob/master/filters/lan-block.txt @@ -3048,10 +3070,7 @@ class FilterIPAddress { static fromCompiled(args) { const pattern = args[1]; - const details = { - pattern, - isRegex: pattern.startsWith('/') && pattern.endsWith('/'), - }; + const details = { pattern }; return filterDataAlloc(args[0], filterRefAdd(details)); } From 11c3a160363034f2673be10f963a2c52cfe80904 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Wed, 18 Sep 2024 10:34:18 -0400 Subject: [PATCH 0248/1099] Fix exception thrown in `spoof-css` in Firefox Related feedback: https://github.com/uBlockOrigin/uAssets/issues/25358#issuecomment-2358278979 --- assets/resources/scriptlets.js | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/assets/resources/scriptlets.js b/assets/resources/scriptlets.js index dc613c46f5cbf..c5cf6cca87211 100644 --- a/assets/resources/scriptlets.js +++ b/assets/resources/scriptlets.js @@ -1524,9 +1524,9 @@ function proxyApplyFn( apply(target, thisArg, args) { return handler(new proxyApplyFn.ApplyContext(target, thisArg, args)); }, - get(target, prop, receiver) { + get(target, prop) { if ( prop === 'toString' ) { return toString; } - return Reflect.get(target, prop, receiver); + return Reflect.get(target, prop); }, }; if ( fn.prototype?.constructor === fn ) { @@ -3109,11 +3109,11 @@ function alertBuster() { apply: function(a) { console.info(a); }, - get(target, prop, receiver) { + get(target, prop) { if ( prop === 'toString' ) { return target.toString.bind(target); } - return Reflect.get(target, prop, receiver); + return Reflect.get(target, prop); }, }); } @@ -3836,7 +3836,7 @@ function spoofCSS( const targetElements = new WeakSet(document.querySelectorAll(selector)); if ( targetElements.has(args[0]) === false ) { return style; } const proxiedStyle = new Proxy(style, { - get(target, prop, receiver) { + get(target, prop) { if ( typeof target[prop] === 'function' ) { if ( prop === 'getPropertyValue' ) { return cloackFunc(function getPropertyValue(prop) { @@ -3848,7 +3848,7 @@ function spoofCSS( if ( instanceProperties.includes(prop) ) { return Reflect.get(target, prop); } - return spoofStyle(prop, Reflect.get(target, prop, receiver)); + return spoofStyle(prop, Reflect.get(target, prop)); }, getOwnPropertyDescriptor(target, prop) { if ( propToValueMap.has(prop) ) { @@ -3864,11 +3864,11 @@ function spoofCSS( }); return proxiedStyle; }, - get(target, prop, receiver) { + get(target, prop) { if ( prop === 'toString' ) { return target.toString.bind(target); } - return Reflect.get(target, prop, receiver); + return Reflect.get(target, prop); }, }); Element.prototype.getBoundingClientRect = new Proxy(Element.prototype.getBoundingClientRect, { @@ -3887,11 +3887,11 @@ function spoofCSS( } return new self.DOMRect(rect.x, rect.y, width, height); }, - get(target, prop, receiver) { + get(target, prop) { if ( prop === 'toString' ) { return target.toString.bind(target); } - return Reflect.get(target, prop, receiver); + return Reflect.get(target, prop); }, }); } From b3ffba723857334b9bb3a77f7b643dc9b6507a25 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Wed, 18 Sep 2024 11:32:16 -0400 Subject: [PATCH 0249/1099] Fine tune indent rules --- .eslintrc.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.eslintrc.yml b/.eslintrc.yml index 470a195bd90e7..d7db6e4e4f8bc 100644 --- a/.eslintrc.yml +++ b/.eslintrc.yml @@ -13,9 +13,10 @@ rules: - 4 - ignoredNodes: - Program > BlockStatement - - Program > IfStatement > BlockStatement - Program > ExpressionStatement > CallExpression > ArrowFunctionExpression > BlockStatement - Program > ExpressionStatement > CallExpression > FunctionExpression > BlockStatement + - Program > IfStatement > BlockStatement + - Program > VariableDeclaration > VariableDeclarator > CallExpression > ArrowFunctionExpression > BlockStatement - CallExpression > MemberExpression - ArrayExpression > * - ObjectExpression > * From 0a6dc47a729f7c2906350534b2f16d04e54d3750 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Wed, 18 Sep 2024 11:33:31 -0400 Subject: [PATCH 0250/1099] Fix contextual menu quirks Related issue: https://github.com/uBlockOrigin/uBlock-issues/issues/3382 --- src/js/contextmenu.js | 41 ++++++++++++++++++++--------------------- 1 file changed, 20 insertions(+), 21 deletions(-) diff --git a/src/js/contextmenu.js b/src/js/contextmenu.js index 788b62bb35687..a2b01e9a1f9c3 100644 --- a/src/js/contextmenu.js +++ b/src/js/contextmenu.js @@ -19,12 +19,8 @@ Home: https://github.com/gorhill/uBlock */ -'use strict'; - -/******************************************************************************/ - -import µb from './background.js'; import { i18n$ } from './i18n.js'; +import µb from './background.js'; /******************************************************************************/ @@ -55,18 +51,23 @@ const onBlockElement = function(details, tab) { let src = details.frameUrl || details.srcUrl || details.linkUrl || ''; if ( !tagName ) { - if ( typeof details.frameUrl === 'string' ) { + if ( typeof details.frameUrl === 'string' && details.frameId !== 0 ) { tagName = 'iframe'; + src = details.srcUrl; } else if ( typeof details.srcUrl === 'string' ) { if ( details.mediaType === 'image' ) { tagName = 'img'; + src = details.srcUrl; } else if ( details.mediaType === 'video' ) { tagName = 'video'; + src = details.srcUrl; } else if ( details.mediaType === 'audio' ) { tagName = 'audio'; + src = details.srcUrl; } } else if ( typeof details.linkUrl === 'string' ) { tagName = 'a'; + src = details.linkUrl; } } @@ -200,27 +201,25 @@ let currentBits = 0; const update = function(tabId = undefined) { let newBits = 0; - if ( - µb.userSettings.contextMenuEnabled && - µb.userFiltersAreEnabled() && - tabId !== undefined - ) { - const pageStore = µb.pageStoreFromTabId(tabId); - if ( pageStore && pageStore.getNetFilteringSwitch() ) { - if ( pageStore.shouldApplySpecificCosmeticFilters(0) ) { - newBits |= BLOCK_ELEMENT_BIT; - } else { - newBits |= BLOCK_RESOURCE_BIT; + if ( µb.userSettings.contextMenuEnabled ) { + const pageStore = tabId && µb.pageStoreFromTabId(tabId) || null; + if ( pageStore?.getNetFilteringSwitch() ) { + if ( µb.userFiltersAreEnabled() ) { + if ( pageStore.shouldApplySpecificCosmeticFilters(0) ) { + newBits |= BLOCK_ELEMENT_BIT; + } else { + newBits |= BLOCK_RESOURCE_BIT; + } } if ( pageStore.largeMediaCount !== 0 ) { newBits |= TEMP_ALLOW_LARGE_MEDIA_BIT; } } - newBits |= SUBSCRIBE_TO_LIST_BIT; - } - if ( µb.hiddenSettings.filterAuthorMode ) { - newBits |= VIEW_SOURCE_BIT; + if ( µb.hiddenSettings.filterAuthorMode ) { + newBits |= VIEW_SOURCE_BIT; + } } + newBits |= SUBSCRIBE_TO_LIST_BIT; if ( newBits === currentBits ) { return; } currentBits = newBits; const usedEntries = []; From b06c5337ec098bbfabbbedabdb0f1e765a70ebde Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Wed, 18 Sep 2024 11:36:39 -0400 Subject: [PATCH 0251/1099] Update changelog --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8d6eb1b08b6a9..fe56efbe49ebb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,5 @@ +- [Fix contextual menu quirks](https://github.com/gorhill/uBlock/commit/0a6dc47a72) +- [Fix exception thrown in `spoof-css` in Firefox](https://github.com/gorhill/uBlock/commit/11c3a16036) - [Throttle down repeated scriptlet logging information](https://github.com/gorhill/uBlock/commit/e8f6f3ddff) - [Improve scriptlet helper `proxy-apply`](https://github.com/gorhill/uBlock/commit/547fae4842) - [Add an entry in _Report_ page for badware/phishing category](https://github.com/gorhill/uBlock/commit/e18a3707c7) From 60039ab1336071f8be550ec65d034fe735d4ea67 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Wed, 18 Sep 2024 11:37:06 -0400 Subject: [PATCH 0252/1099] New revision for release candidate --- dist/version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dist/version b/dist/version index ca5335ac542aa..7e65fd9b6f136 100644 --- a/dist/version +++ b/dist/version @@ -1 +1 @@ -1.59.1.103 \ No newline at end of file +1.59.1.104 \ No newline at end of file From 7843a214287230bdb4431bd0e8f23859e9dfce41 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Wed, 18 Sep 2024 11:56:22 -0400 Subject: [PATCH 0253/1099] Make Firefox dev build auto-update --- dist/firefox/updates.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dist/firefox/updates.json b/dist/firefox/updates.json index 32ac2fd6ec0c5..28e558265f8f9 100644 --- a/dist/firefox/updates.json +++ b/dist/firefox/updates.json @@ -3,9 +3,9 @@ "uBlock0@raymondhill.net": { "updates": [ { - "version": "1.59.1.103", + "version": "1.59.1.104", "browser_specific_settings": { "gecko": { "strict_min_version": "78.0" } }, - "update_link": "https://github.com/gorhill/uBlock/releases/download/1.59.1rc3/uBlock0_1.59.1rc3.firefox.signed.xpi" + "update_link": "https://github.com/gorhill/uBlock/releases/download/1.59.1rc4/uBlock0_1.59.1rc4.firefox.signed.xpi" } ] } From e98fdeb0a5b4367e602256263e2127c216274bfe Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Wed, 18 Sep 2024 12:24:04 -0400 Subject: [PATCH 0254/1099] Mind `urlskip=` in built-in benchmark --- src/js/benchmarks.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/js/benchmarks.js b/src/js/benchmarks.js index 93d099bd448dc..e97490e60bd24 100644 --- a/src/js/benchmarks.js +++ b/src/js/benchmarks.js @@ -207,6 +207,9 @@ export async function benchmarkStaticNetFiltering(options = {}) { redirectCount += 1; } } + if ( fctxt.type === 'main_frame' ) { + sfne.matchAndFetchModifiers(fctxt, 'urlskip'); + } } const t1 = performance.now(); const dur = t1 - t0; From 4ccc0d0fda461b49cfa04cd246d9a39c38c97d51 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Thu, 19 Sep 2024 07:49:54 -0400 Subject: [PATCH 0255/1099] Minimize memory allocation in scriptlet helper `proxy-apply-fn` Probably beneficial in cases of proxied method called in a tight loop. Additionally, added `throwFunc` as valid constant in script helper `validate-constant.fn`. Does what the name implies. --- assets/resources/scriptlets.js | 40 +++++++++++++++++++++++++++++----- 1 file changed, 34 insertions(+), 6 deletions(-) diff --git a/assets/resources/scriptlets.js b/assets/resources/scriptlets.js index c5cf6cca87211..74e873974d739 100644 --- a/assets/resources/scriptlets.js +++ b/assets/resources/scriptlets.js @@ -535,6 +535,8 @@ function validateConstantFn(trusted, raw, extraArgs = {}) { value = function(){ return true; }; } else if ( raw === 'falseFunc' ) { value = function(){ return false; }; + } else if ( raw === 'throwFunc' ) { + value = function(){ throw ''; }; } else if ( /^-?\d+$/.test(raw) ) { value = parseInt(raw); if ( isNaN(raw) ) { return; } @@ -1498,23 +1500,49 @@ function proxyApplyFn( const fn = context[prop]; if ( typeof fn !== 'function' ) { return; } if ( proxyApplyFn.CtorContext === undefined ) { + proxyApplyFn.ctorContexts = []; proxyApplyFn.CtorContext = class { - constructor(callFn, callArgs) { + constructor(...args) { + this.init(...args); + } + init(callFn, callArgs) { this.callFn = callFn; this.callArgs = callArgs; + return this; } reflect() { - return Reflect.construct(this.callFn, this.callArgs); + const r = Reflect.construct(this.callFn, this.callArgs); + this.callFn = this.callArgs = undefined; + proxyApplyFn.ctorContexts.push(this); + return r; + } + static factory(...args) { + return proxyApplyFn.ctorContexts.length !== 0 + ? proxyApplyFn.ctorContexts.pop().init(...args) + : new proxyApplyFn.CtorContext(...args); } }; + proxyApplyFn.applyContexts = []; proxyApplyFn.ApplyContext = class { - constructor(callFn, thisArg, callArgs) { + constructor(...args) { + this.init(...args); + } + init(callFn, thisArg, callArgs) { this.callFn = callFn; this.thisArg = thisArg; this.callArgs = callArgs; + return this; } reflect() { - return Reflect.apply(this.callFn, this.thisArg, this.callArgs); + const r = Reflect.apply(this.callFn, this.thisArg, this.callArgs); + this.callFn = this.thisArg = this.callArgs = undefined; + proxyApplyFn.applyContexts.push(this); + return r; + } + static factory(...args) { + return proxyApplyFn.applyContexts.length !== 0 + ? proxyApplyFn.applyContexts.pop().init(...args) + : new proxyApplyFn.ApplyContext(...args); } }; } @@ -1522,7 +1550,7 @@ function proxyApplyFn( const toString = (function toString() { return fnStr; }).bind(null); const proxyDetails = { apply(target, thisArg, args) { - return handler(new proxyApplyFn.ApplyContext(target, thisArg, args)); + return handler(proxyApplyFn.ApplyContext.factory(target, thisArg, args)); }, get(target, prop) { if ( prop === 'toString' ) { return toString; } @@ -1531,7 +1559,7 @@ function proxyApplyFn( }; if ( fn.prototype?.constructor === fn ) { proxyDetails.construct = function(target, args) { - return handler(new proxyApplyFn.CtorContext(target, args)); + return handler(proxyApplyFn.CtorContext.factory(target, args)); }; } context[prop] = new Proxy(fn, proxyDetails); From b01a418073d3941cdd2b2129a05542eb8be26321 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Thu, 19 Sep 2024 08:08:49 -0400 Subject: [PATCH 0256/1099] Add filtering output expressions`replace`/`urlskip` to logger Additioanlly, removed `uritransform` as it is currently barely used, if at all. --- src/logger-ui.html | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/logger-ui.html b/src/logger-ui.html index 9517968fde049..8836bd373de45 100644 --- a/src/logger-ui.html +++ b/src/logger-ui.html @@ -61,7 +61,7 @@ angle-up

-
+
css/fontimagemediascript
@@ -72,8 +72,7 @@
getheadpost
-
csppermissionsredirect
-
removeparamuritransform
+ csppermissionsredirectremoveparamreplaceurlskip
From 760b2ffce6afe6f428d75482accf09bbd5b28a41 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Thu, 19 Sep 2024 08:43:54 -0400 Subject: [PATCH 0257/1099] Add advanced setting `dnsResolveEnabled` Default to `true`. Set to `false` to wholly disable calls to `dns.resolve()` (Firefox- only). Disabling calls to `dns.resolve()` will prevent cname-uncloaking and will limit ability to enforce `ipaddress` filter option. --- platform/firefox/vapi-background-ext.js | 5 +++++ src/js/background.js | 1 + src/js/storage.js | 1 + 3 files changed, 7 insertions(+) diff --git a/platform/firefox/vapi-background-ext.js b/platform/firefox/vapi-background-ext.js index 7fdc3dd5505f6..79787cc9967c4 100644 --- a/platform/firefox/vapi-background-ext.js +++ b/platform/firefox/vapi-background-ext.js @@ -62,6 +62,7 @@ vAPI.Net = class extends vAPI.Net { this.cnameIgnoreExceptions = true; this.cnameIgnoreRootDocument = true; this.cnameReplayFullURL = false; + this.dnsResolveEnabled = true; } setOptions(options) { @@ -89,6 +90,9 @@ vAPI.Net = class extends vAPI.Net { if ( 'cnameReplayFullURL' in options ) { this.cnameReplayFullURL = options.cnameReplayFullURL === true; } + if ( 'dnsResolveEnabled' in options ) { + this.dnsResolveEnabled = options.dnsResolveEnabled === true; + } this.dnsList.fill(null); this.dnsDict.clear(); } @@ -256,6 +260,7 @@ vAPI.Net = class extends vAPI.Net { } dnsShouldResolve(hn) { + if ( this.dnsResolveEnabled === false ) { return false; } if ( hn === '' ) { return false; } const c0 = hn.charCodeAt(0); if ( c0 === 0x5B /* [ */ ) { return false; } diff --git a/src/js/background.js b/src/js/background.js index 939c559c32167..8ea668ee96a0a 100644 --- a/src/js/background.js +++ b/src/js/background.js @@ -66,6 +66,7 @@ const hiddenSettingsDefault = { debugScriptletInjector: false, differentialUpdate: true, disableWebAssembly: false, + dnsResolveEnabled: true, extensionUpdateForceReload: false, filterAuthorMode: false, loggerPopupType: 'popup', diff --git a/src/js/storage.js b/src/js/storage.js index 77ec90b7ddfdc..c04e85c87e0c6 100644 --- a/src/js/storage.js +++ b/src/js/storage.js @@ -319,6 +319,7 @@ onBroadcast(msg => { cnameIgnoreRootDocument: µbhs.cnameIgnoreRootDocument, cnameMaxTTL: µbhs.cnameMaxTTL, cnameReplayFullURL: µbhs.cnameReplayFullURL, + dnsResolveEnabled: µbhs.dnsResolveEnabled, }); }); From 1139085ca5cd4825105593ff805b58bd7b52b28e Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Thu, 19 Sep 2024 08:56:08 -0400 Subject: [PATCH 0258/1099] Update changelog --- CHANGELOG.md | 1 + dist/version | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fe56efbe49ebb..019acfd46cb8f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,4 @@ +- [Add advanced setting `dnsResolveEnabled`](https://github.com/gorhill/uBlock/commit/760b2ffce6) - [Fix contextual menu quirks](https://github.com/gorhill/uBlock/commit/0a6dc47a72) - [Fix exception thrown in `spoof-css` in Firefox](https://github.com/gorhill/uBlock/commit/11c3a16036) - [Throttle down repeated scriptlet logging information](https://github.com/gorhill/uBlock/commit/e8f6f3ddff) diff --git a/dist/version b/dist/version index 7e65fd9b6f136..45585148a2abc 100644 --- a/dist/version +++ b/dist/version @@ -1 +1 @@ -1.59.1.104 \ No newline at end of file +1.59.1.105 \ No newline at end of file From 55ab6d6875035b2351aa62281631231432fa2a3f Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Thu, 19 Sep 2024 09:06:24 -0400 Subject: [PATCH 0259/1099] Make Firefox dev build auto-update --- dist/firefox/updates.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dist/firefox/updates.json b/dist/firefox/updates.json index 28e558265f8f9..854a5f439404f 100644 --- a/dist/firefox/updates.json +++ b/dist/firefox/updates.json @@ -3,9 +3,9 @@ "uBlock0@raymondhill.net": { "updates": [ { - "version": "1.59.1.104", + "version": "1.59.1.105", "browser_specific_settings": { "gecko": { "strict_min_version": "78.0" } }, - "update_link": "https://github.com/gorhill/uBlock/releases/download/1.59.1rc4/uBlock0_1.59.1rc4.firefox.signed.xpi" + "update_link": "https://github.com/gorhill/uBlock/releases/download/1.59.1rc5/uBlock0_1.59.1rc5.firefox.signed.xpi" } ] } From 4f181b0bc5a0f8377f6eeec8b4f27eaf289eafba Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Fri, 20 Sep 2024 07:20:55 -0400 Subject: [PATCH 0260/1099] Support matching against list of IP addresses Related commit: https://github.com/gorhill/uBlock/commit/6acf97bf5143543c036c38a82160e5f8efe8b3f1 --- platform/firefox/vapi-background-ext.js | 2 +- src/js/benchmarks.js | 1 + src/js/static-net-filtering.js | 29 ++++++++++++++----------- 3 files changed, 18 insertions(+), 14 deletions(-) diff --git a/platform/firefox/vapi-background-ext.js b/platform/firefox/vapi-background-ext.js index 79787cc9967c4..92c2d95c92e61 100644 --- a/platform/firefox/vapi-background-ext.js +++ b/platform/firefox/vapi-background-ext.js @@ -314,7 +314,7 @@ vAPI.Net = class extends vAPI.Net { const { addresses } = record; if ( Array.isArray(addresses) === false ) { return; } if ( addresses.length === 0 ) { return; } - return addresses[0]; + return addresses.join('\n'); } suspendOneRequest(details) { diff --git a/src/js/benchmarks.js b/src/js/benchmarks.js index e97490e60bd24..79cc899225b96 100644 --- a/src/js/benchmarks.js +++ b/src/js/benchmarks.js @@ -174,6 +174,7 @@ export async function benchmarkStaticNetFiltering(options = {}) { for ( let i = 0; i < requests.length; i++ ) { const request = requests[i]; fctxt.setURL(request.url); + fctxt.setIPAddress('93.184.215.14\n2606:2800:21f:cb07:6820:80da:af6b:8b2c'); fctxt.setDocOriginFromURL(request.frameUrl); fctxt.setType(request.cpt); sfne.redirectURL = undefined; diff --git a/src/js/static-net-filtering.js b/src/js/static-net-filtering.js index afbd9f01e072e..5ae59f062ca74 100644 --- a/src/js/static-net-filtering.js +++ b/src/js/static-net-filtering.js @@ -2993,16 +2993,14 @@ class FilterIPAddress { if ( ipaddr === '' ) { return false; } const details = filterRefs[filterData[idata+1]]; switch ( details.$type || this.TYPE_UNKNOWN ) { - case this.TYPE_EQUAL: - return ipaddr === details.pattern; case this.TYPE_LAN: return this.isLAN(ipaddr); case this.TYPE_LOOPBACK: return this.isLoopback(ipaddr); + case this.TYPE_EQUAL: case this.TYPE_STARTSWITH: - return ipaddr.startsWith(details.$pattern); case this.TYPE_RE: - return details.$pattern.test(ipaddr) + return details.$pattern.test(ipaddr); default: break; } @@ -3013,12 +3011,13 @@ class FilterIPAddress { details.$type = this.TYPE_LOOPBACK; } else if ( pattern.startsWith('/') && pattern.endsWith('/') ) { details.$type = this.TYPE_RE; - details.$pattern = new RegExp(pattern.slice(1, -1)); + details.$pattern = new RegExp(pattern.slice(1, -1), 'm'); } else if ( pattern.endsWith('*') ) { details.$type = this.TYPE_STARTSWITH; - details.$pattern = pattern.slice(0, -1); + details.$pattern = new RegExp(`^${restrFromPlainPattern(pattern.slice(0, -1))}`, 'm'); } else { details.$type = this.TYPE_EQUAL; + details.$pattern = new RegExp(`^${restrFromPlainPattern(pattern)}$`, 'm'); } return this.match(idata); } @@ -3049,13 +3048,11 @@ class FilterIPAddress { if ( ipaddr.startsWith('::ffff:') === false ) { return false; } return this.reIPv6IPv4lan.test(ipaddr); } - if ( ipaddr.includes(':') ) { - if ( c0 === 0x36 /* 6 */ ) { - return ipaddr.startsWith('64:ff9b:'); - } - if ( c0 === 0x66 /* f */ ) { - return this.reIPv6local.test(ipaddr); - } + if ( c0 === 0x36 /* 6 */ ) { + return ipaddr.startsWith('64:ff9b:'); + } + if ( c0 === 0x66 /* f */ ) { + return this.reIPv6local.test(ipaddr); } return false; } @@ -5641,6 +5638,12 @@ StaticNetFilteringEngine.prototype.test = function(details) { } } } + const urlskips = this.matchAndFetchModifiers(fctxt, 'urlskip'); + if ( urlskips ) { + for ( const urlskip of urlskips ) { + out.push(`modified: ${urlskip.logData().raw}`); + } + } return out.join('\n'); } From 59487b189cc65d9b0f030268eaeb3c3fc28c7c67 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Fri, 20 Sep 2024 08:15:31 -0400 Subject: [PATCH 0261/1099] Add `+https` directive to `urlskip=` option When present, the `+https` directive will force the protocol of the resulting URL to be `https:`. Related feedback: https://github.com/uBlockOrigin/uBlock-issues/issues/3206#issuecomment-2363392357 --- src/js/static-net-filtering.js | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/src/js/static-net-filtering.js b/src/js/static-net-filtering.js index 5ae59f062ca74..e5e9017d26b1a 100644 --- a/src/js/static-net-filtering.js +++ b/src/js/static-net-filtering.js @@ -5424,13 +5424,25 @@ function urlSkip(urlin, steps) { try { let urlout; for ( const step of steps ) { - if ( step.startsWith('?') === false ) { return; } - urlout = (new URL(urlin)).searchParams.get(step.slice(1)); - if ( urlout === null ) { return; } - if ( urlout.includes(' ') ) { - urlout = urlout.replace(/ /g, '%20'); + // Extract from URL parameter + if ( step.startsWith('?') ) { + urlout = (new URL(urlin)).searchParams.get(step.slice(1)); + if ( urlout === null ) { return; } + if ( urlout.includes(' ') ) { + urlout = urlout.replace(/ /g, '%20'); + } + urlin = urlout; + continue; } - urlin = urlout; + // Enforce https + if ( step === '+https' ) { + const s = urlin.replace(/^https?:\/\//, ''); + if ( /^[\w-]:\/\//.test(s) ) { return; } + urlin = urlout = `https://${s}`; + continue; + } + // Unknown directive + return; } void new URL(urlout); return urlout; From 055973cc3f77532e1be9c5e8430c75453f34e9d3 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Fri, 20 Sep 2024 09:16:32 -0400 Subject: [PATCH 0262/1099] Import translation work from https://crowdin.com/project/ublock --- platform/mv3/extension/_locales/pt_BR/messages.json | 2 +- src/_locales/sq/messages.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/platform/mv3/extension/_locales/pt_BR/messages.json b/platform/mv3/extension/_locales/pt_BR/messages.json index e6533c12712ff..f42f4b5a6ff3b 100644 --- a/platform/mv3/extension/_locales/pt_BR/messages.json +++ b/platform/mv3/extension/_locales/pt_BR/messages.json @@ -4,7 +4,7 @@ "description": "extension name." }, "extShortDesc": { - "message": "Um bloqueador de conteúdo com menos permissões - Bloqueie anúncios, rastreadores, mineradores e mais imediatamente após a instalação", + "message": "Um bloqueador de conteúdo com menos permissões — Bloqueie anúncios, rastreadores, mineradores e mais imediatamente após a instalação", "description": "this will be in the Chrome web store: must be 132 characters or less" }, "perRulesetStats": { diff --git a/src/_locales/sq/messages.json b/src/_locales/sq/messages.json index 6cdf1664316fd..cfcff08c0e3c4 100644 --- a/src/_locales/sq/messages.json +++ b/src/_locales/sq/messages.json @@ -1012,7 +1012,7 @@ "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option7": { - "message": "Leads to badware, phishing", + "message": "Çon në instalimin e programeve keqdashëse, mashtruese", "description": "An entry in the widget used to select the type of issue" }, "supportS6Checkbox1": { From 63f0a2b91229512b521d824019d43e35d1ab129c Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Fri, 20 Sep 2024 09:17:10 -0400 Subject: [PATCH 0263/1099] New revision for release candidate --- dist/version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dist/version b/dist/version index 45585148a2abc..69636e88d0374 100644 --- a/dist/version +++ b/dist/version @@ -1 +1 @@ -1.59.1.105 \ No newline at end of file +1.59.1.106 \ No newline at end of file From 37da838255546fe2a5139de3f3a6418ccbe72061 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Fri, 20 Sep 2024 09:26:10 -0400 Subject: [PATCH 0264/1099] Make Firefox dev build auto-update --- dist/firefox/updates.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dist/firefox/updates.json b/dist/firefox/updates.json index 854a5f439404f..2fa2f97f1daac 100644 --- a/dist/firefox/updates.json +++ b/dist/firefox/updates.json @@ -3,9 +3,9 @@ "uBlock0@raymondhill.net": { "updates": [ { - "version": "1.59.1.105", + "version": "1.59.1.106", "browser_specific_settings": { "gecko": { "strict_min_version": "78.0" } }, - "update_link": "https://github.com/gorhill/uBlock/releases/download/1.59.1rc5/uBlock0_1.59.1rc5.firefox.signed.xpi" + "update_link": "https://github.com/gorhill/uBlock/releases/download/1.59.1rc6/uBlock0_1.59.1rc6.firefox.signed.xpi" } ] } From 76e035989fff4b16306a2ff7efe6f97a66c03d49 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Fri, 20 Sep 2024 11:40:03 -0400 Subject: [PATCH 0265/1099] Minor code review --- platform/firefox/vapi-background-ext.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform/firefox/vapi-background-ext.js b/platform/firefox/vapi-background-ext.js index 92c2d95c92e61..895ddeb54df3b 100644 --- a/platform/firefox/vapi-background-ext.js +++ b/platform/firefox/vapi-background-ext.js @@ -99,7 +99,7 @@ vAPI.Net = class extends vAPI.Net { normalizeDetails(details) { // https://github.com/uBlockOrigin/uBlock-issues/issues/3379 - if ( details.proxyInfo?.proxyDNS && details.ip === '0.0.0.0' ) { + if ( details.ip === '0.0.0.0' && details.proxyInfo?.proxyDNS ) { details.ip = null; } const type = details.type; From f84b3e4ce9ab1ae50dae753c5c841d81777ba0c5 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Fri, 20 Sep 2024 17:52:31 -0400 Subject: [PATCH 0266/1099] Revert "Minor code review" This reverts commit 76e035989fff4b16306a2ff7efe6f97a66c03d49. --- platform/firefox/vapi-background-ext.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform/firefox/vapi-background-ext.js b/platform/firefox/vapi-background-ext.js index 895ddeb54df3b..92c2d95c92e61 100644 --- a/platform/firefox/vapi-background-ext.js +++ b/platform/firefox/vapi-background-ext.js @@ -99,7 +99,7 @@ vAPI.Net = class extends vAPI.Net { normalizeDetails(details) { // https://github.com/uBlockOrigin/uBlock-issues/issues/3379 - if ( details.ip === '0.0.0.0' && details.proxyInfo?.proxyDNS ) { + if ( details.proxyInfo?.proxyDNS && details.ip === '0.0.0.0' ) { details.ip = null; } const type = details.type; From 1c97ca10fc8ed113dfce276c46f3cfc8b17db3df Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sat, 21 Sep 2024 12:41:57 -0400 Subject: [PATCH 0267/1099] Minor code review Use class fields to declare/initialize instance and static properties. --- src/js/pagestore.js | 42 +++++++++++++++++++++++------------------- 1 file changed, 23 insertions(+), 19 deletions(-) diff --git a/src/js/pagestore.js b/src/js/pagestore.js index e201496366e3f..319367fe51b0d 100644 --- a/src/js/pagestore.js +++ b/src/js/pagestore.js @@ -53,6 +53,9 @@ To create a log of net requests /******************************************************************************/ const NetFilteringResultCache = class { + shelfLife = 15000; + extensionOriginURL = vAPI.getURL('/'); + constructor() { this.pruneTimer = vAPI.defer.create(( ) => { this.prune(); @@ -172,9 +175,6 @@ const NetFilteringResultCache = class { } }; -NetFilteringResultCache.prototype.shelfLife = 15000; -NetFilteringResultCache.prototype.extensionOriginURL = vAPI.getURL('/'); - /******************************************************************************/ // Frame stores are used solely to associate a URL with a frame id. @@ -274,18 +274,16 @@ const FrameStore = class { } static factory(frameURL, parentId = -1) { - const entry = FrameStore.junkyard.pop(); - if ( entry === undefined ) { - return new FrameStore(frameURL, parentId); + const FS = FrameStore; + if ( FS.junkyard.length !== 0 ) { + return FS.junkyard.pop().init(frameURL, parentId); } - return entry.init(frameURL, parentId); + return new FS(frameURL, parentId); } + static junkyard = []; + static junkyardMax = 50; }; -// To mitigate memory churning -FrameStore.junkyard = []; -FrameStore.junkyardMax = 50; - /******************************************************************************/ const CountDetails = class { @@ -314,18 +312,24 @@ const HostnameDetails = class { this.hostname = hostname; this.cname = vAPI.net.canonicalNameFromHostname(hostname); this.counts.reset(); + return this; } dispose() { - this.hostname = ''; - if ( HostnameDetails.junkyard.length < HostnameDetails.junkyardMax ) { - HostnameDetails.junkyard.push(this); + const HD = HostnameDetails; + if ( HD.junkyard.length >= HD.junkyardMax ) { return; } + HD.junkyard.push(this); + } + static factory(hostname) { + const HD = HostnameDetails; + if ( HD.junkyard.length !== 0 ) { + return HD.junkyard.pop().init(hostname); } + return new HD(hostname); } + static junkyard = []; + static junkyardMax = 100; }; -HostnameDetails.junkyard = []; -HostnameDetails.junkyardMax = 100; - const HostnameDetailsMap = class extends Map { reset() { this.clear(); @@ -623,7 +627,7 @@ const PageStore = class { ) { this.hostnameDetailsMap.set( this.tabHostname, - new HostnameDetails(this.tabHostname) + HostnameDetails.factory(this.tabHostname) ); } return this.hostnameDetailsMap; @@ -701,7 +705,7 @@ const PageStore = class { const hostname = journal[i+0]; let hnDetails = this.hostnameDetailsMap.get(hostname); if ( hnDetails === undefined ) { - hnDetails = new HostnameDetails(hostname); + hnDetails = HostnameDetails.factory(hostname); this.hostnameDetailsMap.set(hostname, hnDetails); this.contentLastModified = now; } From e7c783cefaea69e709281b02dd1efb970da9e412 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sun, 22 Sep 2024 10:02:45 -0400 Subject: [PATCH 0268/1099] Code review for new DNS cache code Prevent discarding DNS cache entries looked up during a passive read. Related feedback: https://github.com/uBlockOrigin/uBlock-issues/discussions/3376#discussioncomment-10711948 Add advanced setting `dnsCacheTTL` to control the TLL (in seconds) of DNS cache entries. Default to 600 (10 minutes). --- platform/firefox/vapi-background-ext.js | 15 +++++++++------ src/js/background.js | 1 + src/js/storage.js | 1 + 3 files changed, 11 insertions(+), 6 deletions(-) diff --git a/platform/firefox/vapi-background-ext.js b/platform/firefox/vapi-background-ext.js index 92c2d95c92e61..73a914d0ce650 100644 --- a/platform/firefox/vapi-background-ext.js +++ b/platform/firefox/vapi-background-ext.js @@ -52,9 +52,9 @@ vAPI.Net = class extends vAPI.Net { this.pendingRequests = []; this.dnsList = []; // ring buffer this.dnsWritePtr = 0; // next write pointer in ring buffer - this.dnsMaxCount = 256; // max size of ring buffer + this.dnsMaxCount = 512; // max size of ring buffer this.dnsDict = new Map(); // hn to index in ring buffer - this.dnsEntryTTL = 60000; // delay after which an entry is obsolete + this.dnsCacheTTL = 600; // TTL in seconds this.canUncloakCnames = true; this.cnameUncloakEnabled = true; this.cnameIgnoreList = null; @@ -90,6 +90,9 @@ vAPI.Net = class extends vAPI.Net { if ( 'cnameReplayFullURL' in options ) { this.cnameReplayFullURL = options.cnameReplayFullURL === true; } + if ( 'dnsCacheTTL' in options ) { + this.dnsCacheTTL = options.dnsCacheTTL; + } if ( 'dnsResolveEnabled' in options ) { this.dnsResolveEnabled = options.dnsResolveEnabled === true; } @@ -143,7 +146,7 @@ vAPI.Net = class extends vAPI.Net { canonicalNameFromHostname(hn) { if ( hn === '' ) { return; } - const dnsEntry = this.dnsFromCache(hn); + const dnsEntry = this.dnsFromCache(hn, true); if ( isResolvedObject(dnsEntry) === false ) { return; } return dnsEntry.cname; } @@ -212,7 +215,7 @@ vAPI.Net = class extends vAPI.Net { } dnsToCache(hn, record, details) { - const dnsEntry = { hn, until: Date.now() + this.dnsEntryTTL }; + const dnsEntry = { hn, until: Date.now() + this.dnsCacheTTL * 1000 }; if ( record ) { const cname = this.cnameFromRecord(hn, record, details); if ( cname ) { dnsEntry.cname = cname; } @@ -223,13 +226,13 @@ vAPI.Net = class extends vAPI.Net { return dnsEntry; } - dnsFromCache(hn) { + dnsFromCache(hn, passive = false) { const i = this.dnsDict.get(hn); if ( i === undefined ) { return; } if ( isPromise(i) ) { return i; } const dnsEntry = this.dnsList[i]; if ( dnsEntry !== null && dnsEntry.hn === hn ) { - if ( dnsEntry.until >= Date.now() ) { + if ( passive || dnsEntry.until >= Date.now() ) { return dnsEntry; } } diff --git a/src/js/background.js b/src/js/background.js index 8ea668ee96a0a..fa7e3f2e72bd4 100644 --- a/src/js/background.js +++ b/src/js/background.js @@ -66,6 +66,7 @@ const hiddenSettingsDefault = { debugScriptletInjector: false, differentialUpdate: true, disableWebAssembly: false, + dnsCacheTTL: 600, dnsResolveEnabled: true, extensionUpdateForceReload: false, filterAuthorMode: false, diff --git a/src/js/storage.js b/src/js/storage.js index c04e85c87e0c6..c3b541bd3c159 100644 --- a/src/js/storage.js +++ b/src/js/storage.js @@ -319,6 +319,7 @@ onBroadcast(msg => { cnameIgnoreRootDocument: µbhs.cnameIgnoreRootDocument, cnameMaxTTL: µbhs.cnameMaxTTL, cnameReplayFullURL: µbhs.cnameReplayFullURL, + dnsCacheTTL: µbhs.dnsCacheTTL, dnsResolveEnabled: µbhs.dnsResolveEnabled, }); }); From bd6d9c3296173b9bc7b03fd2efec475db3964b90 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sun, 22 Sep 2024 11:16:57 -0400 Subject: [PATCH 0269/1099] Fix benchmark quirk related to new `ipaddress` option --- src/js/benchmarks.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/js/benchmarks.js b/src/js/benchmarks.js index 79cc899225b96..83cd97c955a7f 100644 --- a/src/js/benchmarks.js +++ b/src/js/benchmarks.js @@ -130,8 +130,6 @@ const loadBenchmarkDataset = (( ) => { /******************************************************************************/ -// action: 1=test - export async function benchmarkStaticNetFiltering(options = {}) { const { target, redirectEngine } = options; @@ -174,7 +172,9 @@ export async function benchmarkStaticNetFiltering(options = {}) { for ( let i = 0; i < requests.length; i++ ) { const request = requests[i]; fctxt.setURL(request.url); - fctxt.setIPAddress('93.184.215.14\n2606:2800:21f:cb07:6820:80da:af6b:8b2c'); + if ( fctxt.getIPAddress() === '' ) { + fctxt.setIPAddress('93.184.215.14\n2606:2800:21f:cb07:6820:80da:af6b:8b2c'); + } fctxt.setDocOriginFromURL(request.frameUrl); fctxt.setType(request.cpt); sfne.redirectURL = undefined; From 4c5a9353b0bf868587b660e7cf48853264cc5b4e Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sun, 22 Sep 2024 11:50:20 -0400 Subject: [PATCH 0270/1099] Import translation work from https://crowdin.com/project/ublock --- platform/mv3/description/webstore.el.txt | 4 ++-- platform/mv3/extension/_locales/el/messages.json | 2 +- platform/mv3/extension/_locales/pt_PT/messages.json | 2 +- src/_locales/el/messages.json | 2 +- src/_locales/id/messages.json | 6 +++--- src/_locales/pt_PT/messages.json | 2 +- 6 files changed, 9 insertions(+), 9 deletions(-) diff --git a/platform/mv3/description/webstore.el.txt b/platform/mv3/description/webstore.el.txt index ae5aed364399c..73922f351e7e0 100644 --- a/platform/mv3/description/webstore.el.txt +++ b/platform/mv3/description/webstore.el.txt @@ -1,4 +1,4 @@ -Το uBO Lite (uBOL) είναι ένας blocker περιεχομένου *χωρίς άδειες* που βασίζεται σε MV3. +Το uBO Lite (uBOL) είναι ένα πρόσθετο φραγής περιεχομένου που *δεν απαιτεί δικαιώματα* και βασίζεται στο MV3. Το προεπιλεγμένο σύνολο κανόνων αντιστοιχεί στο προεπιλεγμένο σύνολο φίλτρων του uBlock Origin: @@ -7,7 +7,7 @@ - EasyPrivacy - Peter Lowe’s Ad and tracking server list -You can enable more rulesets by visiting the options page -- click the _Cogs_ icon in the popup panel. +Μπορείτε να προσθέσετε περισσότερα σύνολα κανόνων από τη σελίδα επιλογών -- κάντε κλικ στο εικονίδιο _γρανάζι_ στον αναδυόμενο πίνακα. Το uBOL είναι εξ'ολοκλήρου δηλωτικό, πράγμα που σημαίνει ότι δεν υπάρχει ανάγκη για μόνιμη διαδικασία uBOL για να πραγματοποιηθεί το φιλτράρισμα, και το φιλτράρισμα περιεχομένου που βασίζεται σε έγχυση CSS/JS εκτελείται αξιόπιστα από το ίδιο το πρόγραμμα περιήγησης και όχι από την επέκταση. Αυτό σημαίνει ότι το ίδιο το uBOL δεν καταναλώνει πόρους CPU/μνήμης ενώ ο αποκλεισμός περιεχομένου είναι σε εξέλιξη -- η διαδικασία του service worker του uBOL απαιτείται _μόνο_ όταν αλληλεπιδράτε με τον αναδυόμενο πίνακα ή τις σελίδες επιλογών. diff --git a/platform/mv3/extension/_locales/el/messages.json b/platform/mv3/extension/_locales/el/messages.json index 484b2f4ae268b..9fab54e52f482 100644 --- a/platform/mv3/extension/_locales/el/messages.json +++ b/platform/mv3/extension/_locales/el/messages.json @@ -148,7 +148,7 @@ "description": "A short description for the editable field which lists trusted sites" }, "noFilteringModePlaceholder": { - "message": "[hostnames only]\nexample.com\ngames.example\n...", + "message": "[μόνο ονόματα κεντρικών υπολογιστών]\nexample.com\ngames.example\n...", "description": "Default text for in edit field" }, "behaviorSectionLabel": { diff --git a/platform/mv3/extension/_locales/pt_PT/messages.json b/platform/mv3/extension/_locales/pt_PT/messages.json index e7f1ac7a71a37..a0cd625bda185 100644 --- a/platform/mv3/extension/_locales/pt_PT/messages.json +++ b/platform/mv3/extension/_locales/pt_PT/messages.json @@ -144,7 +144,7 @@ "description": "This describes the 'complete' filtering mode" }, "noFilteringModeDescription": { - "message": "Lista de sítios ‘web’ para os quais não será efetuada qualquer filtragem.", + "message": "Lista de nomes de websites para os quais não será efetuada qualquer filtragem.", "description": "A short description for the editable field which lists trusted sites" }, "noFilteringModePlaceholder": { diff --git a/src/_locales/el/messages.json b/src/_locales/el/messages.json index 1ef763e3b1e58..a382c2476a58d 100644 --- a/src/_locales/el/messages.json +++ b/src/_locales/el/messages.json @@ -1012,7 +1012,7 @@ "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option7": { - "message": "Leads to badware, phishing", + "message": "Οδηγεί σε κακόβουλο λογισμικό, ηλεκτρονικό «ψάρεμα»", "description": "An entry in the widget used to select the type of issue" }, "supportS6Checkbox1": { diff --git a/src/_locales/id/messages.json b/src/_locales/id/messages.json index 1e3b55b7accc0..955931f6adbc2 100644 --- a/src/_locales/id/messages.json +++ b/src/_locales/id/messages.json @@ -128,7 +128,7 @@ "description": "Tooltip used for the logger icon in the panel" }, "popupTipReport": { - "message": "Laporkan masalah situs web ini", + "message": "Laporkan masalah di situs web ini", "description": "Tooltip used for the 'chat' icon in the panel" }, "popupTipNoPopups": { @@ -536,7 +536,7 @@ "description": "used as a tooltip for error icon beside a list" }, "1pTrustWarning": { - "message": "Jangan tambah filter dari sumber yang tidak tepercaya.", + "message": "Jangan tambah filter dari sumber yang tidak terpercaya.", "description": "Warning against copy-pasting filters from random sources" }, "1pEnableMyFiltersLabel": { @@ -1012,7 +1012,7 @@ "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option7": { - "message": "Leads to badware, phishing", + "message": "Mengarah ke perangkat lunak jahat, phishing", "description": "An entry in the widget used to select the type of issue" }, "supportS6Checkbox1": { diff --git a/src/_locales/pt_PT/messages.json b/src/_locales/pt_PT/messages.json index 54b86dc7d0072..7c4df74bc522c 100644 --- a/src/_locales/pt_PT/messages.json +++ b/src/_locales/pt_PT/messages.json @@ -1012,7 +1012,7 @@ "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option7": { - "message": "Leva a badware, phishing", + "message": "Leva a badware e phishing", "description": "An entry in the widget used to select the type of issue" }, "supportS6Checkbox1": { From 854c1ee4ccf0ecf93c7552bb7962a78058e91f7d Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sun, 22 Sep 2024 11:53:54 -0400 Subject: [PATCH 0271/1099] New revision for release candidate --- dist/version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dist/version b/dist/version index 69636e88d0374..3ab456d740e06 100644 --- a/dist/version +++ b/dist/version @@ -1 +1 @@ -1.59.1.106 \ No newline at end of file +1.59.1.107 \ No newline at end of file From 687475ebf28f82ff217f1c04aa6e5421e2a1e958 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sun, 22 Sep 2024 12:01:33 -0400 Subject: [PATCH 0272/1099] Make Firefox dev build auto-update --- dist/firefox/updates.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dist/firefox/updates.json b/dist/firefox/updates.json index 2fa2f97f1daac..4cceaabffbdef 100644 --- a/dist/firefox/updates.json +++ b/dist/firefox/updates.json @@ -3,9 +3,9 @@ "uBlock0@raymondhill.net": { "updates": [ { - "version": "1.59.1.106", + "version": "1.59.1.107", "browser_specific_settings": { "gecko": { "strict_min_version": "78.0" } }, - "update_link": "https://github.com/gorhill/uBlock/releases/download/1.59.1rc6/uBlock0_1.59.1rc6.firefox.signed.xpi" + "update_link": "https://github.com/gorhill/uBlock/releases/download/1.59.1rc7/uBlock0_1.59.1rc7.firefox.signed.xpi" } ] } From ff57f01026f1b98ca9d9a8bb36386ae9dfd3eae3 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sun, 22 Sep 2024 12:14:52 -0400 Subject: [PATCH 0273/1099] Code review of fix re "internal error" Related issue: https://github.com/uBlockOrigin/uBOL-home/issues/199 --- platform/mv3/extension/js/background.js | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/platform/mv3/extension/js/background.js b/platform/mv3/extension/js/background.js index f0f6cb6cdfd69..17cfff4bc516b 100644 --- a/platform/mv3/extension/js/background.js +++ b/platform/mv3/extension/js/background.js @@ -388,15 +388,14 @@ async function start() { // https://github.com/uBlockOrigin/uBOL-home/issues/199 // Force a restart of the extension once when an "internal error" occurs -try { - start(); +start().then(( ) => { localWrite({ goodStart: true }); -} catch(reason) { +}).catch(reason => { console.trace(reason); - localRead.get('goodStart').then((bin = {}) => { - if ( bin.goodStart !== true ) { return; } + localRead('goodStart').then((bin = {}) => { + if ( bin.goodStart === false ) { return; } localWrite({ goodStart: false }).then(( ) => { runtime.reload(); }); }); -} +}); From 03df1a40d834bd7ff3d8af3492bcbd4b6352da97 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Thu, 26 Sep 2024 10:13:24 -0400 Subject: [PATCH 0274/1099] New version for stable release --- dist/version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dist/version b/dist/version index 3ab456d740e06..f8e1eccd91c86 100644 --- a/dist/version +++ b/dist/version @@ -1 +1 @@ -1.59.1.107 \ No newline at end of file +1.60.0 \ No newline at end of file From aec0bd39e3a7dc924885c79ccef80f177345c405 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Thu, 26 Sep 2024 13:27:06 -0400 Subject: [PATCH 0275/1099] Fix images not properly downloading on click Related feedback: https://github.com/uBlockOrigin/uBlock-issues/issues/1670#issuecomment-2372048056 The issue affected images supporting `srcset` attribute without the presence of `src` attribute. This commit takes add fallback onto `srcset` attribute when the `src` attribute is not present. --- .../load-large-media-interactive.js | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/js/scriptlets/load-large-media-interactive.js b/src/js/scriptlets/load-large-media-interactive.js index 57198e4b2dfec..4887616d9b0f9 100644 --- a/src/js/scriptlets/load-large-media-interactive.js +++ b/src/js/scriptlets/load-large-media-interactive.js @@ -19,10 +19,6 @@ Home: https://github.com/gorhill/uBlock */ -'use strict'; - -/******************************************************************************/ - (( ) => { /******************************************************************************/ @@ -132,15 +128,12 @@ if ( vAPI.largeMediaElementStyleSheet === undefined ) { const loadMedia = async function(elem) { const src = elem.getAttribute('src') || ''; + if ( src === '' ) { return; } elem.removeAttribute('src'); - await vAPI.messaging.send('scriptlets', { what: 'temporarilyAllowLargeMediaElement', }); - - if ( src !== '' ) { - elem.setAttribute('src', src); - } + elem.setAttribute('src', src); elem.load(); }; @@ -148,14 +141,21 @@ const loadMedia = async function(elem) { const loadImage = async function(elem) { const src = elem.getAttribute('src') || ''; - elem.removeAttribute('src'); - + const srcset = src === '' && elem.getAttribute('srcset') || ''; + if ( src === '' && srcset === '' ) { return; } + if ( src !== '' ) { + elem.removeAttribute('src'); + } + if ( srcset !== '' ) { + elem.removeAttribute('srcset'); + } await vAPI.messaging.send('scriptlets', { what: 'temporarilyAllowLargeMediaElement', }); - if ( src !== '' ) { elem.setAttribute('src', src); + } else if ( srcset !== '' ) { + elem.setAttribute('srcset', srcset); } }; From 82ab15f856a5531252a80ba95a8e27991eba2b4b Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Thu, 26 Sep 2024 13:32:24 -0400 Subject: [PATCH 0276/1099] Update changelog --- CHANGELOG.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 019acfd46cb8f..48a39d7b1859c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,11 @@ +- [Fix images not properly downloading on click](https://github.com/gorhill/uBlock/commit/aec0bd39e3) + +---------- + +# 1.60.0 + +## Fixes / changes + - [Add advanced setting `dnsResolveEnabled`](https://github.com/gorhill/uBlock/commit/760b2ffce6) - [Fix contextual menu quirks](https://github.com/gorhill/uBlock/commit/0a6dc47a72) - [Fix exception thrown in `spoof-css` in Firefox](https://github.com/gorhill/uBlock/commit/11c3a16036) From 7b585a733a91d97aa52afc2071756ed5e7c1cd62 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Thu, 26 Sep 2024 13:32:50 -0400 Subject: [PATCH 0277/1099] New revision for dev build --- dist/version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dist/version b/dist/version index f8e1eccd91c86..9fb35ca861864 100644 --- a/dist/version +++ b/dist/version @@ -1 +1 @@ -1.60.0 \ No newline at end of file +1.60.1.0 \ No newline at end of file From 5425ac23750519f9636235b2e2fa665ff51be628 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Thu, 26 Sep 2024 13:40:44 -0400 Subject: [PATCH 0278/1099] Make Firefox dev build auto-update --- dist/firefox/updates.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dist/firefox/updates.json b/dist/firefox/updates.json index 4cceaabffbdef..69eb03cba6fdd 100644 --- a/dist/firefox/updates.json +++ b/dist/firefox/updates.json @@ -3,9 +3,9 @@ "uBlock0@raymondhill.net": { "updates": [ { - "version": "1.59.1.107", + "version": "1.60.1.0", "browser_specific_settings": { "gecko": { "strict_min_version": "78.0" } }, - "update_link": "https://github.com/gorhill/uBlock/releases/download/1.59.1rc7/uBlock0_1.59.1rc7.firefox.signed.xpi" + "update_link": "https://github.com/gorhill/uBlock/releases/download/1.60.1b0/uBlock0_1.60.1b0.firefox.signed.xpi" } ] } From dcb86e36675d683c2a10d56894fbc26cb5dfc873 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Fri, 27 Sep 2024 11:04:42 -0400 Subject: [PATCH 0279/1099] Update README.md --- platform/mv3/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform/mv3/README.md b/platform/mv3/README.md index 2400bfe71cffa..28f0bdab2a395 100644 --- a/platform/mv3/README.md +++ b/platform/mv3/README.md @@ -15,7 +15,7 @@ Upon completion of the script, the resulting extension package will become prese - Chromium: `dist/build/uBOLite.chromium` - Firefox: `dist/build/uBOLite.firefox` -The folder `dist/build/mv3-data` will cache data fetched from remote server, so as to avoid fetching repeatedly from remote server with repeated build commands. Remove `dist/build/mv3-data` if you want to build with latest versions of filter lists. +The folder `dist/build/mv3-data` will cache data fetched from remote servers, so as to avoid fetching repeatedly from remote servers with repeated build commands. Use `make cleanassets` to remove all locally cached filter lists if you want to build with latest versions of filter lists. The file `dist/build/mv3-data/log.txt` will contain information about what happened during the build process. From 560def639fdfd9bf99096fcf81a664bc2e01f4d8 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sat, 28 Sep 2024 14:08:42 -0400 Subject: [PATCH 0280/1099] [mv3] Add a _chat_ icon in popup panel to report filter issues Just the same as with uBO, but for uBOL. --- .../mv3/extension/_locales/en/messages.json | 68 ++++++++ .../mv3/extension/css/dashboard-common.css | 13 ++ platform/mv3/extension/css/dashboard.css | 9 - platform/mv3/extension/css/settings.css | 4 - platform/mv3/extension/js/background.js | 41 +++++ platform/mv3/extension/js/popup.js | 37 ++++- platform/mv3/extension/js/report.js | 155 ++++++++++++++++++ platform/mv3/extension/matched-rules.html | 1 - platform/mv3/extension/popup.html | 2 + platform/mv3/extension/report.html | 61 +++++++ 10 files changed, 371 insertions(+), 20 deletions(-) create mode 100644 platform/mv3/extension/js/report.js create mode 100644 platform/mv3/extension/report.html diff --git a/platform/mv3/extension/_locales/en/messages.json b/platform/mv3/extension/_locales/en/messages.json index 9f281de19554b..f8c4b376c59a3 100644 --- a/platform/mv3/extension/_locales/en/messages.json +++ b/platform/mv3/extension/_locales/en/messages.json @@ -31,6 +31,10 @@ "message": "filtering mode", "description": "Label in the popup panel for the current filtering mode" }, + "popupTipReport": { + "message": "Report an issue on this website", + "description": "Tooltip used for the 'chat' icon in the panel" + }, "popupTipDashboard": { "message": "Open the dashboard", "description": "English: Click to open the dashboard" @@ -99,6 +103,70 @@ "message": "External dependencies (GPLv3-compatible):", "description": "Shown in the About pane" }, + "supportS6H": { + "message": "Report a filter issue", + "description": "Header of 'Report a filter issue' section in Support pane" + }, + "supportS3P1": { + "message": "Report filter issues with specific websites to the uBlockOrigin/uAssets issue tracker. Requires a GitHub account.", + "description": "First paragraph of 'Filter issues' section in Support pane" + }, + "supportS6P1S1": { + "message": "To avoid burdening volunteers with duplicate reports, please verify that the issue has not already been reported.", + "description": "A paragraph in the filter issue reporter section" + }, + "supportFindSpecificButton": { + "message": "Find similar reports", + "description": "A clickable link in the filter issue reporter section" + }, + "supportS6URL": { + "message": "Address of the webpage:", + "description": "Label for the URL of the page" + }, + "supportS6Select1": { + "message": "The webpage…", + "description": "Label for widget to select type of issue" + }, + "supportS6Select1Option0": { + "message": "-- Pick an entry --", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option1": { + "message": "Shows ads or ad leftovers", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option2": { + "message": "Has overlays or other nuisances", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option3": { + "message": "Detects uBO Lite", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option4": { + "message": "Has privacy-related issues", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option5": { + "message": "Malfunctions when uBO Lite is enabled", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option6": { + "message": "Opens unwanted tabs or windows", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option7": { + "message": "Leads to badware, phishing", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Checkbox1": { + "message": "Label the webpage as “NSFW” (“Not Safe For Work”)", + "description": "A checkbox to use for NSFW sites" + }, + "supportReportSpecificButton": { + "message": "Create new report", + "description": "Text for button which open an external webpage in Support pane" + }, "firstRunSectionLabel": { "message": "Welcome", "description": "The header text for the welcome message section" diff --git a/platform/mv3/extension/css/dashboard-common.css b/platform/mv3/extension/css/dashboard-common.css index 41d641621e125..8621d08704053 100644 --- a/platform/mv3/extension/css/dashboard-common.css +++ b/platform/mv3/extension/css/dashboard-common.css @@ -1,3 +1,14 @@ +body { + align-items: center; + box-sizing: border-box; + display: flex; + flex-direction: column; + padding: 0 var(--default-gap-xxsmall); + } +body > * { + width: min(640px, 100%); + } + h2, h3 { margin: 1em 0; } @@ -7,9 +18,11 @@ h2 { h3 { font-size: 16px; } + a { text-decoration: none; } + .fa-icon.info { color: var(--info0-ink); fill: var(--info0-ink); diff --git a/platform/mv3/extension/css/dashboard.css b/platform/mv3/extension/css/dashboard.css index e98bcc9a40497..6506c22722aca 100644 --- a/platform/mv3/extension/css/dashboard.css +++ b/platform/mv3/extension/css/dashboard.css @@ -1,12 +1,3 @@ -body { - align-items: center; - box-sizing: border-box; - display: flex; - flex-direction: column; - } -body > * { - width: min(640px, 100%); - } #dashboard-nav { background-color: var(--surface-1); border: 0; diff --git a/platform/mv3/extension/css/settings.css b/platform/mv3/extension/css/settings.css index 2e727fc0e33ab..b81e79a5256e2 100644 --- a/platform/mv3/extension/css/settings.css +++ b/platform/mv3/extension/css/settings.css @@ -22,10 +22,6 @@ p { white-space: pre-line; } -section > div { - padding: 0 var(--default-gap-xxsmall); - } - #showBlockedCount:has(input[type="checkbox"][disabled]) { opacity: 0.5; } diff --git a/platform/mv3/extension/js/background.js b/platform/mv3/extension/js/background.js index 17cfff4bc516b..c8183e9251895 100644 --- a/platform/mv3/extension/js/background.js +++ b/platform/mv3/extension/js/background.js @@ -145,6 +145,36 @@ async function onPermissionsRemoved() { /******************************************************************************/ +async function gotoURL(url, type) { + const pageURL = new URL(url, runtime.getURL('/')); + const tabs = await browser.tabs.query({ + url: pageURL.href, + windowType: type !== 'popup' ? 'normal' : 'popup' + }); + + if ( Array.isArray(tabs) && tabs.length !== 0 ) { + const { windowId, id } = tabs[0]; + return Promise.all([ + browser.windows.update(windowId, { focused: true }), + browser.tabs.update(id, { active: true }), + ]); + } + + if ( type === 'popup' ) { + return windows.create({ + type: 'popup', + url: pageURL.href, + }); + } + + return browser.tabs.create({ + active: true, + url: pageURL.href, + }); +} + +/******************************************************************************/ + function onMessage(request, sender, callback) { // Does not require trusted origin. @@ -265,6 +295,10 @@ function onMessage(request, sender, callback) { return true; } + case 'gotoURL': + gotoURL(request.url, request.type); + break; + case 'setFilteringMode': { getFilteringMode(request.hostname).then(actualLevel => { if ( request.level === actualLevel ) { return actualLevel; } @@ -276,6 +310,13 @@ function onMessage(request, sender, callback) { return true; } + case 'getDefaultFilteringMode': { + getDefaultFilteringMode().then(level => { + callback(level); + }); + return true; + } + case 'setDefaultFilteringMode': { getDefaultFilteringMode().then(beforeLevel => setDefaultFilteringMode(request.level).then(afterLevel => diff --git a/platform/mv3/extension/js/popup.js b/platform/mv3/extension/js/popup.js index 123c20bc73fa1..4242e5bacf332 100644 --- a/platform/mv3/extension/js/popup.js +++ b/platform/mv3/extension/js/popup.js @@ -261,12 +261,6 @@ dom.on('#lessButton', 'click', ( ) => { /******************************************************************************/ -dom.on('[data-i18n-title="popupTipDashboard"]', 'click', ev => { - if ( ev.isTrusted !== true ) { return; } - if ( ev.button !== 0 ) { return; } - runtime.openOptionsPage(); -}); - dom.on('#showMatchedRules', 'click', ev => { if ( ev.isTrusted !== true ) { return; } if ( ev.button !== 0 ) { return; } @@ -278,6 +272,33 @@ dom.on('#showMatchedRules', 'click', ev => { /******************************************************************************/ +dom.on('[data-i18n-title="popupTipReport"]', 'click', ev => { + if ( ev.isTrusted !== true ) { return; } + let url; + try { + url = new URL(currentTab.url); + } catch(_) { + } + if ( url === undefined ) { return; } + const reportURL = new URL(runtime.getURL('/report.html')); + reportURL.searchParams.set('url', url.href); + reportURL.searchParams.set('mode', popupPanelData.level); + sendMessage({ + what: 'gotoURL', + url: `${reportURL.pathname}${reportURL.search}`, + }); +}); + +/******************************************************************************/ + +dom.on('[data-i18n-title="popupTipDashboard"]', 'click', ev => { + if ( ev.isTrusted !== true ) { return; } + if ( ev.button !== 0 ) { return; } + runtime.openOptionsPage(); +}); + +/******************************************************************************/ + async function init() { const [ tab ] = await browser.tabs.query({ active: true, @@ -314,6 +335,10 @@ async function init() { isNaN(currentTab.id) === false ); + dom.cl.toggle('#reportFilterIssue', 'enabled', + /^https?:\/\//.test(url?.href) + ); + const parent = qs$('#rulesetStats'); for ( const details of popupPanelData.rulesetDetails || [] ) { const div = dom.clone('#templates .rulesetDetails'); diff --git a/platform/mv3/extension/js/report.js b/platform/mv3/extension/js/report.js new file mode 100644 index 0000000000000..676342b9e0ac3 --- /dev/null +++ b/platform/mv3/extension/js/report.js @@ -0,0 +1,155 @@ +/******************************************************************************* + + uBlock Origin - a comprehensive, efficient content blocker + Copyright (C) 2024-present Raymond Hill + + 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/}. + + Home: https://github.com/gorhill/uBlock +*/ + +import { dnr, runtime } from './ext.js'; +import { dom, qs$ } from './dom.js'; +import { sendMessage } from './ext.js'; + +/******************************************************************************/ + +const reportedPage = (( ) => { + const url = new URL(window.location.href); + try { + const pageURL = url.searchParams.get('url'); + if ( pageURL === null ) { return null; } + const parsedURL = new URL(pageURL); + parsedURL.username = ''; + parsedURL.password = ''; + parsedURL.hash = ''; + const select = qs$('select[name="url"]'); + dom.text(select.options[0], parsedURL.href); + if ( parsedURL.search !== '' ) { + const option = dom.create('option'); + parsedURL.search = ''; + dom.text(option, parsedURL.href); + select.append(option); + } + if ( parsedURL.pathname !== '/' ) { + const option = dom.create('option'); + parsedURL.pathname = ''; + dom.text(option, parsedURL.href); + select.append(option); + } + return { + hostname: parsedURL.hostname.replace(/^(m|mobile|www)\./, ''), + mode: url.searchParams.get('mode'), + }; + } catch(ex) { + } + return null; +})(); + +/******************************************************************************/ + +function reportSpecificFilterType() { + return qs$('select[name="type"]').value; +} + +/******************************************************************************/ + +function renderData(data, depth = 0) { + const indent = ' '.repeat(depth); + if ( Array.isArray(data) ) { + const out = []; + for ( const value of data ) { + out.push(renderData(value, depth)); + } + return out.join('\n'); + } + if ( typeof data !== 'object' || data === null ) { + return `${indent}${data}`; + } + const out = []; + for ( const [ name, value ] of Object.entries(data) ) { + if ( typeof value === 'object' && value !== null ) { + out.push(`${indent}${name}:`); + out.push(renderData(value, depth + 1)); + continue; + } + out.push(`${indent}${name}: ${value}`); + } + return out.join('\n'); +} + +/******************************************************************************/ + +async function reportSpecificFilterIssue() { + const githubURL = new URL( + 'https://github.com/uBlockOrigin/uAssets/issues/new?template=specific_report_from_ubol.yml' + ); + const issueType = reportSpecificFilterType(); + let title = `${reportedPage.hostname}: ${issueType}`; + if ( qs$('#isNSFW').checked ) { + title = `[nsfw] ${title}`; + } + githubURL.searchParams.set('title', title); + githubURL.searchParams.set( + 'url_address_of_the_web_page', + '`' + qs$('select[name="url"]').value + '`' + ); + githubURL.searchParams.set('category', issueType); + githubURL.searchParams.set('labels', 'uBOL'); + + const manifest = runtime.getManifest(); + const rulesets = await dnr.getEnabledRulesets(); + const defaultMode = await sendMessage({ what: 'getDefaultFilteringMode' }); + const modes = [ 'no filtering', 'basic', 'optimal', 'complete' ]; + const config = { + version: `uBOL ${manifest.version}`, + mode: `${modes[reportedPage.mode]} / ${modes[defaultMode]}`, + rulesets, + }; + const configBody = [ + '```yaml', + renderData(config), + '```', + '', + ].join('\n'); + githubURL.searchParams.set('configuration', configBody); + sendMessage({ what: 'gotoURL', url: githubURL.href }); +} + +/******************************************************************************/ + +(async ( ) => { + dom.on('[data-url]', 'click', ev => { + const elem = ev.target.closest('[data-url]'); + const url = dom.attr(elem, 'data-url'); + if ( typeof url !== 'string' || url === '' ) { return; } + sendMessage({ what: 'gotoURL', url }); + ev.preventDefault(); + }); + + if ( reportedPage !== null ) { + dom.on('[data-i18n="supportReportSpecificButton"]', 'click', ev => { + reportSpecificFilterIssue(); + ev.preventDefault(); + }); + + dom.on('[data-i18n="supportFindSpecificButton"]', 'click', ev => { + const url = new URL('https://github.com/uBlockOrigin/uAssets/issues'); + url.searchParams.set('q', `is:issue sort:updated-desc "${reportedPage.hostname}" in:title`); + sendMessage({ what: 'gotoURL', url: url.href }); + ev.preventDefault(); + }); + } + +})(); diff --git a/platform/mv3/extension/matched-rules.html b/platform/mv3/extension/matched-rules.html index ec392ca2338ab..488e150a3079e 100644 --- a/platform/mv3/extension/matched-rules.html +++ b/platform/mv3/extension/matched-rules.html @@ -7,7 +7,6 @@ - diff --git a/platform/mv3/extension/popup.html b/platform/mv3/extension/popup.html index 990f6590af438..e231acc9b9d2a 100644 --- a/platform/mv3/extension/popup.html +++ b/platform/mv3/extension/popup.html @@ -13,6 +13,7 @@ +
­
@@ -30,6 +31,7 @@ list-altShow matched rules + comment-alt cogs
diff --git a/platform/mv3/extension/report.html b/platform/mv3/extension/report.html new file mode 100644 index 0000000000000..21997e936eefc --- /dev/null +++ b/platform/mv3/extension/report.html @@ -0,0 +1,61 @@ + + + + + +uBO Lite — Report + + + + + + + + + + +
+ +

+

+
+
+

+ +
+
+
+

+
+ +

+

+
+ +

+

+ +

+ +
+ +
+ + + + + + + + From 99191d1363442d4cff440f28eb243f2315476f48 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sat, 28 Sep 2024 14:12:13 -0400 Subject: [PATCH 0281/1099] Import translation work from https://crowdin.com/project/ublock --- .../mv3/extension/_locales/cy/messages.json | 2 +- src/_locales/cy/messages.json | 22 +++++++++---------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/platform/mv3/extension/_locales/cy/messages.json b/platform/mv3/extension/_locales/cy/messages.json index 56a6d66e186bf..e3f1b68fef050 100644 --- a/platform/mv3/extension/_locales/cy/messages.json +++ b/platform/mv3/extension/_locales/cy/messages.json @@ -12,7 +12,7 @@ "description": "Appears aside each filter list in the _3rd-party filters_ pane" }, "dashboardName": { - "message": "uBO Lite — Dashfwrdd", + "message": "uBO Lite — Dangosfwrdd", "description": "English: uBO Lite — Dashboard" }, "settingsPageName": { diff --git a/src/_locales/cy/messages.json b/src/_locales/cy/messages.json index 813c625b3140b..d91b747e59b8d 100644 --- a/src/_locales/cy/messages.json +++ b/src/_locales/cy/messages.json @@ -4,15 +4,15 @@ "description": "extension name." }, "extShortDesc": { - "message": "O'r diwedd, rhwystrydd effeithlon. Ysgafn ar y CPU a'r cof.", + "message": "O'r diwedd, rhwystrydd effeithlon sy'n gwell ar y CPU a'r cof.", "description": "this will be in the Chrome web store: must be 132 characters or less" }, "dashboardName": { - "message": "uBlock₀ — Dashfwrdd", + "message": "uBlock₀ — Dangosfwrdd", "description": "English: uBlock₀ — Dashboard" }, "dashboardUnsavedWarning": { - "message": "Rhybudd! Mae yna newidiadau heb eu cadw", + "message": "Rhybudd! Mae gennych newidiadau heb eu cadw", "description": "A warning in the dashboard when navigating away from unsaved changes" }, "dashboardUnsavedWarningStay": { @@ -116,11 +116,11 @@ "description": "English: Click to open the dashboard" }, "popupTipZapper": { - "message": "Modd saethu elfen", + "message": "Galluogi modd saethu elfen", "description": "Tooltip for the element-zapper icon in the popup panel" }, "popupTipPicker": { - "message": "Modd dewis elfen", + "message": "Galluogi modd dewis elfen", "description": "English: Enter element picker mode" }, "popupTipLog": { @@ -208,7 +208,7 @@ "description": "Caption for the no-scripting per-site switch" }, "popupMoreButton_v2": { - "message": "Mwy", + "message": "Rhagor", "description": "Label to be used to show popup panel sections" }, "popupLessButton_v2": { @@ -336,7 +336,7 @@ "description": "English: Color-blind friendly" }, "settingsAppearance": { - "message": "Golwg", + "message": "Gwedd", "description": "Section for controlling user interface appearance" }, "settingsThemeLabel": { @@ -520,7 +520,7 @@ "description": "used as a tooltip for the out-of-date icon beside a list" }, "3pViewContent": { - "message": "gweld y cynnwys", + "message": "gweld cynnwys", "description": "used as a tooltip for eye icon beside a list" }, "3pLastUpdate": { @@ -528,7 +528,7 @@ "description": "used as a tooltip for the clock icon beside a list" }, "3pUpdating": { - "message": "Updating…", + "message": "Wrthi'n diweddaru…", "description": "used as a tooltip for the spinner icon beside a list" }, "3pNetworkError": { @@ -1140,7 +1140,7 @@ "description": "Firefox/Fennec-specific: Show Logger" }, "fennecMenuItemBlockingOff": { - "message": "off", + "message": "na", "description": "Firefox-specific: appears as 'uBlock₀ (off)'" }, "docblockedTitle": { @@ -1228,7 +1228,7 @@ "description": "for generic 'Revert' buttons" }, "genericBytes": { - "message": "bytes", + "message": "beit", "description": "" }, "contextMenuBlockElementInFrame": { From 7f117e8d21d583bb9cbc1a589e80924de194d3b2 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sat, 28 Sep 2024 14:14:26 -0400 Subject: [PATCH 0282/1099] Import translation work from https://crowdin.com/project/ublock --- .../mv3/extension/_locales/ar/messages.json | 68 +++++++++++++++++++ .../mv3/extension/_locales/az/messages.json | 68 +++++++++++++++++++ .../mv3/extension/_locales/be/messages.json | 68 +++++++++++++++++++ .../mv3/extension/_locales/bg/messages.json | 68 +++++++++++++++++++ .../mv3/extension/_locales/bn/messages.json | 68 +++++++++++++++++++ .../extension/_locales/br_FR/messages.json | 68 +++++++++++++++++++ .../mv3/extension/_locales/bs/messages.json | 68 +++++++++++++++++++ .../mv3/extension/_locales/ca/messages.json | 68 +++++++++++++++++++ .../mv3/extension/_locales/cs/messages.json | 68 +++++++++++++++++++ .../mv3/extension/_locales/cv/messages.json | 68 +++++++++++++++++++ .../mv3/extension/_locales/cy/messages.json | 68 +++++++++++++++++++ .../mv3/extension/_locales/da/messages.json | 68 +++++++++++++++++++ .../mv3/extension/_locales/de/messages.json | 68 +++++++++++++++++++ .../mv3/extension/_locales/el/messages.json | 68 +++++++++++++++++++ .../extension/_locales/en_GB/messages.json | 68 +++++++++++++++++++ .../mv3/extension/_locales/eo/messages.json | 68 +++++++++++++++++++ .../mv3/extension/_locales/es/messages.json | 68 +++++++++++++++++++ .../mv3/extension/_locales/et/messages.json | 68 +++++++++++++++++++ .../mv3/extension/_locales/eu/messages.json | 68 +++++++++++++++++++ .../mv3/extension/_locales/fa/messages.json | 68 +++++++++++++++++++ .../mv3/extension/_locales/fi/messages.json | 68 +++++++++++++++++++ .../mv3/extension/_locales/fil/messages.json | 68 +++++++++++++++++++ .../mv3/extension/_locales/fr/messages.json | 68 +++++++++++++++++++ .../mv3/extension/_locales/fy/messages.json | 68 +++++++++++++++++++ .../mv3/extension/_locales/gl/messages.json | 68 +++++++++++++++++++ .../mv3/extension/_locales/gu/messages.json | 68 +++++++++++++++++++ .../mv3/extension/_locales/he/messages.json | 68 +++++++++++++++++++ .../mv3/extension/_locales/hi/messages.json | 68 +++++++++++++++++++ .../mv3/extension/_locales/hr/messages.json | 68 +++++++++++++++++++ .../mv3/extension/_locales/hu/messages.json | 68 +++++++++++++++++++ .../mv3/extension/_locales/hy/messages.json | 68 +++++++++++++++++++ .../mv3/extension/_locales/id/messages.json | 68 +++++++++++++++++++ .../mv3/extension/_locales/it/messages.json | 68 +++++++++++++++++++ .../mv3/extension/_locales/ja/messages.json | 68 +++++++++++++++++++ .../mv3/extension/_locales/ka/messages.json | 68 +++++++++++++++++++ .../mv3/extension/_locales/kk/messages.json | 68 +++++++++++++++++++ .../mv3/extension/_locales/kn/messages.json | 68 +++++++++++++++++++ .../mv3/extension/_locales/ko/messages.json | 68 +++++++++++++++++++ .../mv3/extension/_locales/lt/messages.json | 68 +++++++++++++++++++ .../mv3/extension/_locales/lv/messages.json | 68 +++++++++++++++++++ .../mv3/extension/_locales/mk/messages.json | 68 +++++++++++++++++++ .../mv3/extension/_locales/ml/messages.json | 68 +++++++++++++++++++ .../mv3/extension/_locales/mr/messages.json | 68 +++++++++++++++++++ .../mv3/extension/_locales/ms/messages.json | 68 +++++++++++++++++++ .../mv3/extension/_locales/nb/messages.json | 68 +++++++++++++++++++ .../mv3/extension/_locales/nl/messages.json | 68 +++++++++++++++++++ .../mv3/extension/_locales/oc/messages.json | 68 +++++++++++++++++++ .../mv3/extension/_locales/pa/messages.json | 68 +++++++++++++++++++ .../mv3/extension/_locales/pl/messages.json | 68 +++++++++++++++++++ .../extension/_locales/pt_BR/messages.json | 68 +++++++++++++++++++ .../extension/_locales/pt_PT/messages.json | 68 +++++++++++++++++++ .../mv3/extension/_locales/ro/messages.json | 68 +++++++++++++++++++ .../mv3/extension/_locales/ru/messages.json | 68 +++++++++++++++++++ .../mv3/extension/_locales/si/messages.json | 68 +++++++++++++++++++ .../mv3/extension/_locales/sk/messages.json | 68 +++++++++++++++++++ .../mv3/extension/_locales/sl/messages.json | 68 +++++++++++++++++++ .../mv3/extension/_locales/so/messages.json | 68 +++++++++++++++++++ .../mv3/extension/_locales/sq/messages.json | 68 +++++++++++++++++++ .../mv3/extension/_locales/sr/messages.json | 68 +++++++++++++++++++ .../mv3/extension/_locales/sv/messages.json | 68 +++++++++++++++++++ .../mv3/extension/_locales/sw/messages.json | 68 +++++++++++++++++++ .../mv3/extension/_locales/ta/messages.json | 68 +++++++++++++++++++ .../mv3/extension/_locales/te/messages.json | 68 +++++++++++++++++++ .../mv3/extension/_locales/th/messages.json | 68 +++++++++++++++++++ .../mv3/extension/_locales/tr/messages.json | 68 +++++++++++++++++++ .../mv3/extension/_locales/uk/messages.json | 68 +++++++++++++++++++ .../mv3/extension/_locales/ur/messages.json | 68 +++++++++++++++++++ .../mv3/extension/_locales/vi/messages.json | 68 +++++++++++++++++++ .../extension/_locales/zh_CN/messages.json | 68 +++++++++++++++++++ .../extension/_locales/zh_TW/messages.json | 68 +++++++++++++++++++ 70 files changed, 4760 insertions(+) diff --git a/platform/mv3/extension/_locales/ar/messages.json b/platform/mv3/extension/_locales/ar/messages.json index 404066efaa0a9..f0cb94cebef39 100644 --- a/platform/mv3/extension/_locales/ar/messages.json +++ b/platform/mv3/extension/_locales/ar/messages.json @@ -31,6 +31,10 @@ "message": "وضع التصفية", "description": "Label in the popup panel for the current filtering mode" }, + "popupTipReport": { + "message": "Report an issue on this website", + "description": "Tooltip used for the 'chat' icon in the panel" + }, "popupTipDashboard": { "message": "افتح لوحة التحكم", "description": "English: Click to open the dashboard" @@ -99,6 +103,70 @@ "message": "التبعيات الخارجية (متوافقة مع GPLv3):", "description": "Shown in the About pane" }, + "supportS6H": { + "message": "Report a filter issue", + "description": "Header of 'Report a filter issue' section in Support pane" + }, + "supportS3P1": { + "message": "Report filter issues with specific websites to the uBlockOrigin/uAssets issue tracker. Requires a GitHub account.", + "description": "First paragraph of 'Filter issues' section in Support pane" + }, + "supportS6P1S1": { + "message": "To avoid burdening volunteers with duplicate reports, please verify that the issue has not already been reported.", + "description": "A paragraph in the filter issue reporter section" + }, + "supportFindSpecificButton": { + "message": "Find similar reports", + "description": "A clickable link in the filter issue reporter section" + }, + "supportS6URL": { + "message": "Address of the webpage:", + "description": "Label for the URL of the page" + }, + "supportS6Select1": { + "message": "The webpage…", + "description": "Label for widget to select type of issue" + }, + "supportS6Select1Option0": { + "message": "-- Pick an entry --", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option1": { + "message": "Shows ads or ad leftovers", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option2": { + "message": "Has overlays or other nuisances", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option3": { + "message": "Detects uBO Lite", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option4": { + "message": "Has privacy-related issues", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option5": { + "message": "Malfunctions when uBO Lite is enabled", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option6": { + "message": "Opens unwanted tabs or windows", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option7": { + "message": "Leads to badware, phishing", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Checkbox1": { + "message": "Label the webpage as “NSFW” (“Not Safe For Work”)", + "description": "A checkbox to use for NSFW sites" + }, + "supportReportSpecificButton": { + "message": "Create new report", + "description": "Text for button which open an external webpage in Support pane" + }, "firstRunSectionLabel": { "message": "مرحبا", "description": "The header text for the welcome message section" diff --git a/platform/mv3/extension/_locales/az/messages.json b/platform/mv3/extension/_locales/az/messages.json index 3f397e146f9ea..7c769e3f935dd 100644 --- a/platform/mv3/extension/_locales/az/messages.json +++ b/platform/mv3/extension/_locales/az/messages.json @@ -31,6 +31,10 @@ "message": "filtering mode", "description": "Label in the popup panel for the current filtering mode" }, + "popupTipReport": { + "message": "Report an issue on this website", + "description": "Tooltip used for the 'chat' icon in the panel" + }, "popupTipDashboard": { "message": "İdarəetmə panelini aç", "description": "English: Click to open the dashboard" @@ -99,6 +103,70 @@ "message": "External dependencies (GPLv3-compatible):", "description": "Shown in the About pane" }, + "supportS6H": { + "message": "Report a filter issue", + "description": "Header of 'Report a filter issue' section in Support pane" + }, + "supportS3P1": { + "message": "Report filter issues with specific websites to the uBlockOrigin/uAssets issue tracker. Requires a GitHub account.", + "description": "First paragraph of 'Filter issues' section in Support pane" + }, + "supportS6P1S1": { + "message": "To avoid burdening volunteers with duplicate reports, please verify that the issue has not already been reported.", + "description": "A paragraph in the filter issue reporter section" + }, + "supportFindSpecificButton": { + "message": "Find similar reports", + "description": "A clickable link in the filter issue reporter section" + }, + "supportS6URL": { + "message": "Address of the webpage:", + "description": "Label for the URL of the page" + }, + "supportS6Select1": { + "message": "The webpage…", + "description": "Label for widget to select type of issue" + }, + "supportS6Select1Option0": { + "message": "-- Pick an entry --", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option1": { + "message": "Shows ads or ad leftovers", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option2": { + "message": "Has overlays or other nuisances", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option3": { + "message": "Detects uBO Lite", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option4": { + "message": "Has privacy-related issues", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option5": { + "message": "Malfunctions when uBO Lite is enabled", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option6": { + "message": "Opens unwanted tabs or windows", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option7": { + "message": "Leads to badware, phishing", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Checkbox1": { + "message": "Label the webpage as “NSFW” (“Not Safe For Work”)", + "description": "A checkbox to use for NSFW sites" + }, + "supportReportSpecificButton": { + "message": "Create new report", + "description": "Text for button which open an external webpage in Support pane" + }, "firstRunSectionLabel": { "message": "Xoş gəldiniz", "description": "The header text for the welcome message section" diff --git a/platform/mv3/extension/_locales/be/messages.json b/platform/mv3/extension/_locales/be/messages.json index d15851fd1aad0..c25464d019166 100644 --- a/platform/mv3/extension/_locales/be/messages.json +++ b/platform/mv3/extension/_locales/be/messages.json @@ -31,6 +31,10 @@ "message": "рэжым фільтравання", "description": "Label in the popup panel for the current filtering mode" }, + "popupTipReport": { + "message": "Report an issue on this website", + "description": "Tooltip used for the 'chat' icon in the panel" + }, "popupTipDashboard": { "message": "Адкрыць панэль кіравання", "description": "English: Click to open the dashboard" @@ -99,6 +103,70 @@ "message": "Вонкавыя залежнасці (GPLv3-сумяшчальныя):", "description": "Shown in the About pane" }, + "supportS6H": { + "message": "Report a filter issue", + "description": "Header of 'Report a filter issue' section in Support pane" + }, + "supportS3P1": { + "message": "Report filter issues with specific websites to the uBlockOrigin/uAssets issue tracker. Requires a GitHub account.", + "description": "First paragraph of 'Filter issues' section in Support pane" + }, + "supportS6P1S1": { + "message": "To avoid burdening volunteers with duplicate reports, please verify that the issue has not already been reported.", + "description": "A paragraph in the filter issue reporter section" + }, + "supportFindSpecificButton": { + "message": "Find similar reports", + "description": "A clickable link in the filter issue reporter section" + }, + "supportS6URL": { + "message": "Address of the webpage:", + "description": "Label for the URL of the page" + }, + "supportS6Select1": { + "message": "The webpage…", + "description": "Label for widget to select type of issue" + }, + "supportS6Select1Option0": { + "message": "-- Pick an entry --", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option1": { + "message": "Shows ads or ad leftovers", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option2": { + "message": "Has overlays or other nuisances", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option3": { + "message": "Detects uBO Lite", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option4": { + "message": "Has privacy-related issues", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option5": { + "message": "Malfunctions when uBO Lite is enabled", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option6": { + "message": "Opens unwanted tabs or windows", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option7": { + "message": "Leads to badware, phishing", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Checkbox1": { + "message": "Label the webpage as “NSFW” (“Not Safe For Work”)", + "description": "A checkbox to use for NSFW sites" + }, + "supportReportSpecificButton": { + "message": "Create new report", + "description": "Text for button which open an external webpage in Support pane" + }, "firstRunSectionLabel": { "message": "Вітаем", "description": "The header text for the welcome message section" diff --git a/platform/mv3/extension/_locales/bg/messages.json b/platform/mv3/extension/_locales/bg/messages.json index 0d35e5ec11f44..b505ed5762c97 100644 --- a/platform/mv3/extension/_locales/bg/messages.json +++ b/platform/mv3/extension/_locales/bg/messages.json @@ -31,6 +31,10 @@ "message": "режим на филтриране", "description": "Label in the popup panel for the current filtering mode" }, + "popupTipReport": { + "message": "Report an issue on this website", + "description": "Tooltip used for the 'chat' icon in the panel" + }, "popupTipDashboard": { "message": "Табло с настройки", "description": "English: Click to open the dashboard" @@ -99,6 +103,70 @@ "message": "Външни зависимости (съвместими с GPLv3):", "description": "Shown in the About pane" }, + "supportS6H": { + "message": "Report a filter issue", + "description": "Header of 'Report a filter issue' section in Support pane" + }, + "supportS3P1": { + "message": "Report filter issues with specific websites to the uBlockOrigin/uAssets issue tracker. Requires a GitHub account.", + "description": "First paragraph of 'Filter issues' section in Support pane" + }, + "supportS6P1S1": { + "message": "To avoid burdening volunteers with duplicate reports, please verify that the issue has not already been reported.", + "description": "A paragraph in the filter issue reporter section" + }, + "supportFindSpecificButton": { + "message": "Find similar reports", + "description": "A clickable link in the filter issue reporter section" + }, + "supportS6URL": { + "message": "Address of the webpage:", + "description": "Label for the URL of the page" + }, + "supportS6Select1": { + "message": "The webpage…", + "description": "Label for widget to select type of issue" + }, + "supportS6Select1Option0": { + "message": "-- Pick an entry --", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option1": { + "message": "Shows ads or ad leftovers", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option2": { + "message": "Has overlays or other nuisances", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option3": { + "message": "Detects uBO Lite", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option4": { + "message": "Has privacy-related issues", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option5": { + "message": "Malfunctions when uBO Lite is enabled", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option6": { + "message": "Opens unwanted tabs or windows", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option7": { + "message": "Leads to badware, phishing", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Checkbox1": { + "message": "Label the webpage as “NSFW” (“Not Safe For Work”)", + "description": "A checkbox to use for NSFW sites" + }, + "supportReportSpecificButton": { + "message": "Create new report", + "description": "Text for button which open an external webpage in Support pane" + }, "firstRunSectionLabel": { "message": "Добре дошли", "description": "The header text for the welcome message section" diff --git a/platform/mv3/extension/_locales/bn/messages.json b/platform/mv3/extension/_locales/bn/messages.json index f987c1850a676..d080906856e76 100644 --- a/platform/mv3/extension/_locales/bn/messages.json +++ b/platform/mv3/extension/_locales/bn/messages.json @@ -31,6 +31,10 @@ "message": "ফিল্টারিং মোড", "description": "Label in the popup panel for the current filtering mode" }, + "popupTipReport": { + "message": "Report an issue on this website", + "description": "Tooltip used for the 'chat' icon in the panel" + }, "popupTipDashboard": { "message": "ড্যাশবোর্ড খুলুন", "description": "English: Click to open the dashboard" @@ -99,6 +103,70 @@ "message": "বাহ্যিক নির্ভরতা (GPLv3-সামঞ্জস্যপূর্ণ):", "description": "Shown in the About pane" }, + "supportS6H": { + "message": "Report a filter issue", + "description": "Header of 'Report a filter issue' section in Support pane" + }, + "supportS3P1": { + "message": "Report filter issues with specific websites to the uBlockOrigin/uAssets issue tracker. Requires a GitHub account.", + "description": "First paragraph of 'Filter issues' section in Support pane" + }, + "supportS6P1S1": { + "message": "To avoid burdening volunteers with duplicate reports, please verify that the issue has not already been reported.", + "description": "A paragraph in the filter issue reporter section" + }, + "supportFindSpecificButton": { + "message": "Find similar reports", + "description": "A clickable link in the filter issue reporter section" + }, + "supportS6URL": { + "message": "Address of the webpage:", + "description": "Label for the URL of the page" + }, + "supportS6Select1": { + "message": "The webpage…", + "description": "Label for widget to select type of issue" + }, + "supportS6Select1Option0": { + "message": "-- Pick an entry --", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option1": { + "message": "Shows ads or ad leftovers", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option2": { + "message": "Has overlays or other nuisances", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option3": { + "message": "Detects uBO Lite", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option4": { + "message": "Has privacy-related issues", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option5": { + "message": "Malfunctions when uBO Lite is enabled", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option6": { + "message": "Opens unwanted tabs or windows", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option7": { + "message": "Leads to badware, phishing", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Checkbox1": { + "message": "Label the webpage as “NSFW” (“Not Safe For Work”)", + "description": "A checkbox to use for NSFW sites" + }, + "supportReportSpecificButton": { + "message": "Create new report", + "description": "Text for button which open an external webpage in Support pane" + }, "firstRunSectionLabel": { "message": "স্বাগতম", "description": "The header text for the welcome message section" diff --git a/platform/mv3/extension/_locales/br_FR/messages.json b/platform/mv3/extension/_locales/br_FR/messages.json index dce348fb2e778..3b179eaf1dca6 100644 --- a/platform/mv3/extension/_locales/br_FR/messages.json +++ b/platform/mv3/extension/_locales/br_FR/messages.json @@ -31,6 +31,10 @@ "message": "mod silañ", "description": "Label in the popup panel for the current filtering mode" }, + "popupTipReport": { + "message": "Report an issue on this website", + "description": "Tooltip used for the 'chat' icon in the panel" + }, "popupTipDashboard": { "message": "Digeriñ an daolenn-vourzh", "description": "English: Click to open the dashboard" @@ -99,6 +103,70 @@ "message": "Dalc'hioù diavaez (a glot gant GPLv3)", "description": "Shown in the About pane" }, + "supportS6H": { + "message": "Report a filter issue", + "description": "Header of 'Report a filter issue' section in Support pane" + }, + "supportS3P1": { + "message": "Report filter issues with specific websites to the uBlockOrigin/uAssets issue tracker. Requires a GitHub account.", + "description": "First paragraph of 'Filter issues' section in Support pane" + }, + "supportS6P1S1": { + "message": "To avoid burdening volunteers with duplicate reports, please verify that the issue has not already been reported.", + "description": "A paragraph in the filter issue reporter section" + }, + "supportFindSpecificButton": { + "message": "Find similar reports", + "description": "A clickable link in the filter issue reporter section" + }, + "supportS6URL": { + "message": "Address of the webpage:", + "description": "Label for the URL of the page" + }, + "supportS6Select1": { + "message": "The webpage…", + "description": "Label for widget to select type of issue" + }, + "supportS6Select1Option0": { + "message": "-- Pick an entry --", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option1": { + "message": "Shows ads or ad leftovers", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option2": { + "message": "Has overlays or other nuisances", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option3": { + "message": "Detects uBO Lite", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option4": { + "message": "Has privacy-related issues", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option5": { + "message": "Malfunctions when uBO Lite is enabled", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option6": { + "message": "Opens unwanted tabs or windows", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option7": { + "message": "Leads to badware, phishing", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Checkbox1": { + "message": "Label the webpage as “NSFW” (“Not Safe For Work”)", + "description": "A checkbox to use for NSFW sites" + }, + "supportReportSpecificButton": { + "message": "Create new report", + "description": "Text for button which open an external webpage in Support pane" + }, "firstRunSectionLabel": { "message": "Donemat", "description": "The header text for the welcome message section" diff --git a/platform/mv3/extension/_locales/bs/messages.json b/platform/mv3/extension/_locales/bs/messages.json index 6f7f257782a20..178795ae3117a 100644 --- a/platform/mv3/extension/_locales/bs/messages.json +++ b/platform/mv3/extension/_locales/bs/messages.json @@ -31,6 +31,10 @@ "message": "način filtriranja", "description": "Label in the popup panel for the current filtering mode" }, + "popupTipReport": { + "message": "Report an issue on this website", + "description": "Tooltip used for the 'chat' icon in the panel" + }, "popupTipDashboard": { "message": "Otvorite nadzornu ploču", "description": "English: Click to open the dashboard" @@ -99,6 +103,70 @@ "message": "Vanjske ovisnosti (kompatibilno s GPLv3):", "description": "Shown in the About pane" }, + "supportS6H": { + "message": "Report a filter issue", + "description": "Header of 'Report a filter issue' section in Support pane" + }, + "supportS3P1": { + "message": "Report filter issues with specific websites to the uBlockOrigin/uAssets issue tracker. Requires a GitHub account.", + "description": "First paragraph of 'Filter issues' section in Support pane" + }, + "supportS6P1S1": { + "message": "To avoid burdening volunteers with duplicate reports, please verify that the issue has not already been reported.", + "description": "A paragraph in the filter issue reporter section" + }, + "supportFindSpecificButton": { + "message": "Find similar reports", + "description": "A clickable link in the filter issue reporter section" + }, + "supportS6URL": { + "message": "Address of the webpage:", + "description": "Label for the URL of the page" + }, + "supportS6Select1": { + "message": "The webpage…", + "description": "Label for widget to select type of issue" + }, + "supportS6Select1Option0": { + "message": "-- Pick an entry --", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option1": { + "message": "Shows ads or ad leftovers", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option2": { + "message": "Has overlays or other nuisances", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option3": { + "message": "Detects uBO Lite", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option4": { + "message": "Has privacy-related issues", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option5": { + "message": "Malfunctions when uBO Lite is enabled", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option6": { + "message": "Opens unwanted tabs or windows", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option7": { + "message": "Leads to badware, phishing", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Checkbox1": { + "message": "Label the webpage as “NSFW” (“Not Safe For Work”)", + "description": "A checkbox to use for NSFW sites" + }, + "supportReportSpecificButton": { + "message": "Create new report", + "description": "Text for button which open an external webpage in Support pane" + }, "firstRunSectionLabel": { "message": "Dobrodošli", "description": "The header text for the welcome message section" diff --git a/platform/mv3/extension/_locales/ca/messages.json b/platform/mv3/extension/_locales/ca/messages.json index ea727755353db..04f722f964bbb 100644 --- a/platform/mv3/extension/_locales/ca/messages.json +++ b/platform/mv3/extension/_locales/ca/messages.json @@ -31,6 +31,10 @@ "message": "mode de filtre", "description": "Label in the popup panel for the current filtering mode" }, + "popupTipReport": { + "message": "Report an issue on this website", + "description": "Tooltip used for the 'chat' icon in the panel" + }, "popupTipDashboard": { "message": "Obre el tauler", "description": "English: Click to open the dashboard" @@ -99,6 +103,70 @@ "message": "Dependències externes (compatibles amb GPLv3):", "description": "Shown in the About pane" }, + "supportS6H": { + "message": "Report a filter issue", + "description": "Header of 'Report a filter issue' section in Support pane" + }, + "supportS3P1": { + "message": "Report filter issues with specific websites to the uBlockOrigin/uAssets issue tracker. Requires a GitHub account.", + "description": "First paragraph of 'Filter issues' section in Support pane" + }, + "supportS6P1S1": { + "message": "To avoid burdening volunteers with duplicate reports, please verify that the issue has not already been reported.", + "description": "A paragraph in the filter issue reporter section" + }, + "supportFindSpecificButton": { + "message": "Find similar reports", + "description": "A clickable link in the filter issue reporter section" + }, + "supportS6URL": { + "message": "Address of the webpage:", + "description": "Label for the URL of the page" + }, + "supportS6Select1": { + "message": "The webpage…", + "description": "Label for widget to select type of issue" + }, + "supportS6Select1Option0": { + "message": "-- Pick an entry --", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option1": { + "message": "Shows ads or ad leftovers", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option2": { + "message": "Has overlays or other nuisances", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option3": { + "message": "Detects uBO Lite", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option4": { + "message": "Has privacy-related issues", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option5": { + "message": "Malfunctions when uBO Lite is enabled", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option6": { + "message": "Opens unwanted tabs or windows", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option7": { + "message": "Leads to badware, phishing", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Checkbox1": { + "message": "Label the webpage as “NSFW” (“Not Safe For Work”)", + "description": "A checkbox to use for NSFW sites" + }, + "supportReportSpecificButton": { + "message": "Create new report", + "description": "Text for button which open an external webpage in Support pane" + }, "firstRunSectionLabel": { "message": "Us donem la benvinguda", "description": "The header text for the welcome message section" diff --git a/platform/mv3/extension/_locales/cs/messages.json b/platform/mv3/extension/_locales/cs/messages.json index 6daf4a73a9f51..556e7da210650 100644 --- a/platform/mv3/extension/_locales/cs/messages.json +++ b/platform/mv3/extension/_locales/cs/messages.json @@ -31,6 +31,10 @@ "message": "Filtrovací režim", "description": "Label in the popup panel for the current filtering mode" }, + "popupTipReport": { + "message": "Report an issue on this website", + "description": "Tooltip used for the 'chat' icon in the panel" + }, "popupTipDashboard": { "message": "Otevřít ovládací panel", "description": "English: Click to open the dashboard" @@ -99,6 +103,70 @@ "message": "Externí závislosti (kompatibilní s GPLv3):", "description": "Shown in the About pane" }, + "supportS6H": { + "message": "Report a filter issue", + "description": "Header of 'Report a filter issue' section in Support pane" + }, + "supportS3P1": { + "message": "Report filter issues with specific websites to the uBlockOrigin/uAssets issue tracker. Requires a GitHub account.", + "description": "First paragraph of 'Filter issues' section in Support pane" + }, + "supportS6P1S1": { + "message": "To avoid burdening volunteers with duplicate reports, please verify that the issue has not already been reported.", + "description": "A paragraph in the filter issue reporter section" + }, + "supportFindSpecificButton": { + "message": "Find similar reports", + "description": "A clickable link in the filter issue reporter section" + }, + "supportS6URL": { + "message": "Address of the webpage:", + "description": "Label for the URL of the page" + }, + "supportS6Select1": { + "message": "The webpage…", + "description": "Label for widget to select type of issue" + }, + "supportS6Select1Option0": { + "message": "-- Pick an entry --", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option1": { + "message": "Shows ads or ad leftovers", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option2": { + "message": "Has overlays or other nuisances", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option3": { + "message": "Detects uBO Lite", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option4": { + "message": "Has privacy-related issues", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option5": { + "message": "Malfunctions when uBO Lite is enabled", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option6": { + "message": "Opens unwanted tabs or windows", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option7": { + "message": "Leads to badware, phishing", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Checkbox1": { + "message": "Label the webpage as “NSFW” (“Not Safe For Work”)", + "description": "A checkbox to use for NSFW sites" + }, + "supportReportSpecificButton": { + "message": "Create new report", + "description": "Text for button which open an external webpage in Support pane" + }, "firstRunSectionLabel": { "message": "Vítejte", "description": "The header text for the welcome message section" diff --git a/platform/mv3/extension/_locales/cv/messages.json b/platform/mv3/extension/_locales/cv/messages.json index 30c719d52722d..c5f77c667a506 100644 --- a/platform/mv3/extension/_locales/cv/messages.json +++ b/platform/mv3/extension/_locales/cv/messages.json @@ -31,6 +31,10 @@ "message": "filtering mode", "description": "Label in the popup panel for the current filtering mode" }, + "popupTipReport": { + "message": "Report an issue on this website", + "description": "Tooltip used for the 'chat' icon in the panel" + }, "popupTipDashboard": { "message": "Open the dashboard", "description": "English: Click to open the dashboard" @@ -99,6 +103,70 @@ "message": "External dependencies (GPLv3-compatible):", "description": "Shown in the About pane" }, + "supportS6H": { + "message": "Report a filter issue", + "description": "Header of 'Report a filter issue' section in Support pane" + }, + "supportS3P1": { + "message": "Report filter issues with specific websites to the uBlockOrigin/uAssets issue tracker. Requires a GitHub account.", + "description": "First paragraph of 'Filter issues' section in Support pane" + }, + "supportS6P1S1": { + "message": "To avoid burdening volunteers with duplicate reports, please verify that the issue has not already been reported.", + "description": "A paragraph in the filter issue reporter section" + }, + "supportFindSpecificButton": { + "message": "Find similar reports", + "description": "A clickable link in the filter issue reporter section" + }, + "supportS6URL": { + "message": "Address of the webpage:", + "description": "Label for the URL of the page" + }, + "supportS6Select1": { + "message": "The webpage…", + "description": "Label for widget to select type of issue" + }, + "supportS6Select1Option0": { + "message": "-- Pick an entry --", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option1": { + "message": "Shows ads or ad leftovers", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option2": { + "message": "Has overlays or other nuisances", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option3": { + "message": "Detects uBO Lite", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option4": { + "message": "Has privacy-related issues", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option5": { + "message": "Malfunctions when uBO Lite is enabled", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option6": { + "message": "Opens unwanted tabs or windows", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option7": { + "message": "Leads to badware, phishing", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Checkbox1": { + "message": "Label the webpage as “NSFW” (“Not Safe For Work”)", + "description": "A checkbox to use for NSFW sites" + }, + "supportReportSpecificButton": { + "message": "Create new report", + "description": "Text for button which open an external webpage in Support pane" + }, "firstRunSectionLabel": { "message": "Welcome", "description": "The header text for the welcome message section" diff --git a/platform/mv3/extension/_locales/cy/messages.json b/platform/mv3/extension/_locales/cy/messages.json index e3f1b68fef050..21ac20da0cddb 100644 --- a/platform/mv3/extension/_locales/cy/messages.json +++ b/platform/mv3/extension/_locales/cy/messages.json @@ -31,6 +31,10 @@ "message": "modd hidlo", "description": "Label in the popup panel for the current filtering mode" }, + "popupTipReport": { + "message": "Report an issue on this website", + "description": "Tooltip used for the 'chat' icon in the panel" + }, "popupTipDashboard": { "message": "Agor y dashfwrdd", "description": "English: Click to open the dashboard" @@ -99,6 +103,70 @@ "message": "Gofynion allanol (cydnaws â GPLv3):", "description": "Shown in the About pane" }, + "supportS6H": { + "message": "Report a filter issue", + "description": "Header of 'Report a filter issue' section in Support pane" + }, + "supportS3P1": { + "message": "Report filter issues with specific websites to the uBlockOrigin/uAssets issue tracker. Requires a GitHub account.", + "description": "First paragraph of 'Filter issues' section in Support pane" + }, + "supportS6P1S1": { + "message": "To avoid burdening volunteers with duplicate reports, please verify that the issue has not already been reported.", + "description": "A paragraph in the filter issue reporter section" + }, + "supportFindSpecificButton": { + "message": "Find similar reports", + "description": "A clickable link in the filter issue reporter section" + }, + "supportS6URL": { + "message": "Address of the webpage:", + "description": "Label for the URL of the page" + }, + "supportS6Select1": { + "message": "The webpage…", + "description": "Label for widget to select type of issue" + }, + "supportS6Select1Option0": { + "message": "-- Pick an entry --", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option1": { + "message": "Shows ads or ad leftovers", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option2": { + "message": "Has overlays or other nuisances", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option3": { + "message": "Detects uBO Lite", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option4": { + "message": "Has privacy-related issues", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option5": { + "message": "Malfunctions when uBO Lite is enabled", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option6": { + "message": "Opens unwanted tabs or windows", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option7": { + "message": "Leads to badware, phishing", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Checkbox1": { + "message": "Label the webpage as “NSFW” (“Not Safe For Work”)", + "description": "A checkbox to use for NSFW sites" + }, + "supportReportSpecificButton": { + "message": "Create new report", + "description": "Text for button which open an external webpage in Support pane" + }, "firstRunSectionLabel": { "message": "Croeso", "description": "The header text for the welcome message section" diff --git a/platform/mv3/extension/_locales/da/messages.json b/platform/mv3/extension/_locales/da/messages.json index 303d3bd3810ee..93de76bb35611 100644 --- a/platform/mv3/extension/_locales/da/messages.json +++ b/platform/mv3/extension/_locales/da/messages.json @@ -31,6 +31,10 @@ "message": "filtreringstilstand", "description": "Label in the popup panel for the current filtering mode" }, + "popupTipReport": { + "message": "Report an issue on this website", + "description": "Tooltip used for the 'chat' icon in the panel" + }, "popupTipDashboard": { "message": "Åbn kontrolpanelet", "description": "English: Click to open the dashboard" @@ -99,6 +103,70 @@ "message": "Eksterne afhængigheder (GPLv3-kompatible):", "description": "Shown in the About pane" }, + "supportS6H": { + "message": "Report a filter issue", + "description": "Header of 'Report a filter issue' section in Support pane" + }, + "supportS3P1": { + "message": "Report filter issues with specific websites to the uBlockOrigin/uAssets issue tracker. Requires a GitHub account.", + "description": "First paragraph of 'Filter issues' section in Support pane" + }, + "supportS6P1S1": { + "message": "To avoid burdening volunteers with duplicate reports, please verify that the issue has not already been reported.", + "description": "A paragraph in the filter issue reporter section" + }, + "supportFindSpecificButton": { + "message": "Find similar reports", + "description": "A clickable link in the filter issue reporter section" + }, + "supportS6URL": { + "message": "Address of the webpage:", + "description": "Label for the URL of the page" + }, + "supportS6Select1": { + "message": "The webpage…", + "description": "Label for widget to select type of issue" + }, + "supportS6Select1Option0": { + "message": "-- Pick an entry --", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option1": { + "message": "Shows ads or ad leftovers", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option2": { + "message": "Has overlays or other nuisances", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option3": { + "message": "Detects uBO Lite", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option4": { + "message": "Has privacy-related issues", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option5": { + "message": "Malfunctions when uBO Lite is enabled", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option6": { + "message": "Opens unwanted tabs or windows", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option7": { + "message": "Leads to badware, phishing", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Checkbox1": { + "message": "Label the webpage as “NSFW” (“Not Safe For Work”)", + "description": "A checkbox to use for NSFW sites" + }, + "supportReportSpecificButton": { + "message": "Create new report", + "description": "Text for button which open an external webpage in Support pane" + }, "firstRunSectionLabel": { "message": "Velkommen", "description": "The header text for the welcome message section" diff --git a/platform/mv3/extension/_locales/de/messages.json b/platform/mv3/extension/_locales/de/messages.json index 7e8e072ca80f5..65154b8d36dfc 100644 --- a/platform/mv3/extension/_locales/de/messages.json +++ b/platform/mv3/extension/_locales/de/messages.json @@ -31,6 +31,10 @@ "message": "Filtermodus", "description": "Label in the popup panel for the current filtering mode" }, + "popupTipReport": { + "message": "Report an issue on this website", + "description": "Tooltip used for the 'chat' icon in the panel" + }, "popupTipDashboard": { "message": "Dashboard öffnen", "description": "English: Click to open the dashboard" @@ -99,6 +103,70 @@ "message": "Externe Abhängigkeiten (GPLv3-kompatibel):", "description": "Shown in the About pane" }, + "supportS6H": { + "message": "Report a filter issue", + "description": "Header of 'Report a filter issue' section in Support pane" + }, + "supportS3P1": { + "message": "Report filter issues with specific websites to the uBlockOrigin/uAssets issue tracker. Requires a GitHub account.", + "description": "First paragraph of 'Filter issues' section in Support pane" + }, + "supportS6P1S1": { + "message": "To avoid burdening volunteers with duplicate reports, please verify that the issue has not already been reported.", + "description": "A paragraph in the filter issue reporter section" + }, + "supportFindSpecificButton": { + "message": "Find similar reports", + "description": "A clickable link in the filter issue reporter section" + }, + "supportS6URL": { + "message": "Address of the webpage:", + "description": "Label for the URL of the page" + }, + "supportS6Select1": { + "message": "The webpage…", + "description": "Label for widget to select type of issue" + }, + "supportS6Select1Option0": { + "message": "-- Pick an entry --", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option1": { + "message": "Shows ads or ad leftovers", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option2": { + "message": "Has overlays or other nuisances", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option3": { + "message": "Detects uBO Lite", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option4": { + "message": "Has privacy-related issues", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option5": { + "message": "Malfunctions when uBO Lite is enabled", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option6": { + "message": "Opens unwanted tabs or windows", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option7": { + "message": "Leads to badware, phishing", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Checkbox1": { + "message": "Label the webpage as “NSFW” (“Not Safe For Work”)", + "description": "A checkbox to use for NSFW sites" + }, + "supportReportSpecificButton": { + "message": "Create new report", + "description": "Text for button which open an external webpage in Support pane" + }, "firstRunSectionLabel": { "message": "Willkommen", "description": "The header text for the welcome message section" diff --git a/platform/mv3/extension/_locales/el/messages.json b/platform/mv3/extension/_locales/el/messages.json index 9fab54e52f482..e5e8f663b7889 100644 --- a/platform/mv3/extension/_locales/el/messages.json +++ b/platform/mv3/extension/_locales/el/messages.json @@ -31,6 +31,10 @@ "message": "λειτουργία φιλτραρίσματος", "description": "Label in the popup panel for the current filtering mode" }, + "popupTipReport": { + "message": "Report an issue on this website", + "description": "Tooltip used for the 'chat' icon in the panel" + }, "popupTipDashboard": { "message": "Ανοίξτε τον πίνακα ελέγχου", "description": "English: Click to open the dashboard" @@ -99,6 +103,70 @@ "message": "Εξωτερικές εξαρτήσεις (συμβατές με GPLv3):", "description": "Shown in the About pane" }, + "supportS6H": { + "message": "Report a filter issue", + "description": "Header of 'Report a filter issue' section in Support pane" + }, + "supportS3P1": { + "message": "Report filter issues with specific websites to the uBlockOrigin/uAssets issue tracker. Requires a GitHub account.", + "description": "First paragraph of 'Filter issues' section in Support pane" + }, + "supportS6P1S1": { + "message": "To avoid burdening volunteers with duplicate reports, please verify that the issue has not already been reported.", + "description": "A paragraph in the filter issue reporter section" + }, + "supportFindSpecificButton": { + "message": "Find similar reports", + "description": "A clickable link in the filter issue reporter section" + }, + "supportS6URL": { + "message": "Address of the webpage:", + "description": "Label for the URL of the page" + }, + "supportS6Select1": { + "message": "The webpage…", + "description": "Label for widget to select type of issue" + }, + "supportS6Select1Option0": { + "message": "-- Pick an entry --", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option1": { + "message": "Shows ads or ad leftovers", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option2": { + "message": "Has overlays or other nuisances", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option3": { + "message": "Detects uBO Lite", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option4": { + "message": "Has privacy-related issues", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option5": { + "message": "Malfunctions when uBO Lite is enabled", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option6": { + "message": "Opens unwanted tabs or windows", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option7": { + "message": "Leads to badware, phishing", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Checkbox1": { + "message": "Label the webpage as “NSFW” (“Not Safe For Work”)", + "description": "A checkbox to use for NSFW sites" + }, + "supportReportSpecificButton": { + "message": "Create new report", + "description": "Text for button which open an external webpage in Support pane" + }, "firstRunSectionLabel": { "message": "Καλώς ήρθατε", "description": "The header text for the welcome message section" diff --git a/platform/mv3/extension/_locales/en_GB/messages.json b/platform/mv3/extension/_locales/en_GB/messages.json index 42cde830929ad..f1d2e99626386 100644 --- a/platform/mv3/extension/_locales/en_GB/messages.json +++ b/platform/mv3/extension/_locales/en_GB/messages.json @@ -31,6 +31,10 @@ "message": "filtering mode", "description": "Label in the popup panel for the current filtering mode" }, + "popupTipReport": { + "message": "Report an issue on this website", + "description": "Tooltip used for the 'chat' icon in the panel" + }, "popupTipDashboard": { "message": "Open the dashboard", "description": "English: Click to open the dashboard" @@ -99,6 +103,70 @@ "message": "External dependencies (GPLv3-compatible):", "description": "Shown in the About pane" }, + "supportS6H": { + "message": "Report a filter issue", + "description": "Header of 'Report a filter issue' section in Support pane" + }, + "supportS3P1": { + "message": "Report filter issues with specific websites to the uBlockOrigin/uAssets issue tracker. Requires a GitHub account.", + "description": "First paragraph of 'Filter issues' section in Support pane" + }, + "supportS6P1S1": { + "message": "To avoid burdening volunteers with duplicate reports, please verify that the issue has not already been reported.", + "description": "A paragraph in the filter issue reporter section" + }, + "supportFindSpecificButton": { + "message": "Find similar reports", + "description": "A clickable link in the filter issue reporter section" + }, + "supportS6URL": { + "message": "Address of the webpage:", + "description": "Label for the URL of the page" + }, + "supportS6Select1": { + "message": "The webpage…", + "description": "Label for widget to select type of issue" + }, + "supportS6Select1Option0": { + "message": "-- Pick an entry --", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option1": { + "message": "Shows ads or ad leftovers", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option2": { + "message": "Has overlays or other nuisances", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option3": { + "message": "Detects uBO Lite", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option4": { + "message": "Has privacy-related issues", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option5": { + "message": "Malfunctions when uBO Lite is enabled", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option6": { + "message": "Opens unwanted tabs or windows", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option7": { + "message": "Leads to badware, phishing", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Checkbox1": { + "message": "Label the webpage as “NSFW” (“Not Safe For Work”)", + "description": "A checkbox to use for NSFW sites" + }, + "supportReportSpecificButton": { + "message": "Create new report", + "description": "Text for button which open an external webpage in Support pane" + }, "firstRunSectionLabel": { "message": "Welcome", "description": "The header text for the welcome message section" diff --git a/platform/mv3/extension/_locales/eo/messages.json b/platform/mv3/extension/_locales/eo/messages.json index 4481c48f28e8e..786ace6c3fa7c 100644 --- a/platform/mv3/extension/_locales/eo/messages.json +++ b/platform/mv3/extension/_locales/eo/messages.json @@ -31,6 +31,10 @@ "message": "filtering mode", "description": "Label in the popup panel for the current filtering mode" }, + "popupTipReport": { + "message": "Report an issue on this website", + "description": "Tooltip used for the 'chat' icon in the panel" + }, "popupTipDashboard": { "message": "Open the dashboard", "description": "English: Click to open the dashboard" @@ -99,6 +103,70 @@ "message": "External dependencies (GPLv3-compatible):", "description": "Shown in the About pane" }, + "supportS6H": { + "message": "Report a filter issue", + "description": "Header of 'Report a filter issue' section in Support pane" + }, + "supportS3P1": { + "message": "Report filter issues with specific websites to the uBlockOrigin/uAssets issue tracker. Requires a GitHub account.", + "description": "First paragraph of 'Filter issues' section in Support pane" + }, + "supportS6P1S1": { + "message": "To avoid burdening volunteers with duplicate reports, please verify that the issue has not already been reported.", + "description": "A paragraph in the filter issue reporter section" + }, + "supportFindSpecificButton": { + "message": "Find similar reports", + "description": "A clickable link in the filter issue reporter section" + }, + "supportS6URL": { + "message": "Address of the webpage:", + "description": "Label for the URL of the page" + }, + "supportS6Select1": { + "message": "The webpage…", + "description": "Label for widget to select type of issue" + }, + "supportS6Select1Option0": { + "message": "-- Pick an entry --", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option1": { + "message": "Shows ads or ad leftovers", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option2": { + "message": "Has overlays or other nuisances", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option3": { + "message": "Detects uBO Lite", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option4": { + "message": "Has privacy-related issues", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option5": { + "message": "Malfunctions when uBO Lite is enabled", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option6": { + "message": "Opens unwanted tabs or windows", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option7": { + "message": "Leads to badware, phishing", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Checkbox1": { + "message": "Label the webpage as “NSFW” (“Not Safe For Work”)", + "description": "A checkbox to use for NSFW sites" + }, + "supportReportSpecificButton": { + "message": "Create new report", + "description": "Text for button which open an external webpage in Support pane" + }, "firstRunSectionLabel": { "message": "Bonvenon", "description": "The header text for the welcome message section" diff --git a/platform/mv3/extension/_locales/es/messages.json b/platform/mv3/extension/_locales/es/messages.json index f4ff120d131d7..ff55964e57f24 100644 --- a/platform/mv3/extension/_locales/es/messages.json +++ b/platform/mv3/extension/_locales/es/messages.json @@ -31,6 +31,10 @@ "message": "modo de filtrado", "description": "Label in the popup panel for the current filtering mode" }, + "popupTipReport": { + "message": "Report an issue on this website", + "description": "Tooltip used for the 'chat' icon in the panel" + }, "popupTipDashboard": { "message": "Abrir panel de control", "description": "English: Click to open the dashboard" @@ -99,6 +103,70 @@ "message": "Dependencias externas (compatibles con GPLv3):", "description": "Shown in the About pane" }, + "supportS6H": { + "message": "Report a filter issue", + "description": "Header of 'Report a filter issue' section in Support pane" + }, + "supportS3P1": { + "message": "Report filter issues with specific websites to the uBlockOrigin/uAssets issue tracker. Requires a GitHub account.", + "description": "First paragraph of 'Filter issues' section in Support pane" + }, + "supportS6P1S1": { + "message": "To avoid burdening volunteers with duplicate reports, please verify that the issue has not already been reported.", + "description": "A paragraph in the filter issue reporter section" + }, + "supportFindSpecificButton": { + "message": "Find similar reports", + "description": "A clickable link in the filter issue reporter section" + }, + "supportS6URL": { + "message": "Address of the webpage:", + "description": "Label for the URL of the page" + }, + "supportS6Select1": { + "message": "The webpage…", + "description": "Label for widget to select type of issue" + }, + "supportS6Select1Option0": { + "message": "-- Pick an entry --", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option1": { + "message": "Shows ads or ad leftovers", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option2": { + "message": "Has overlays or other nuisances", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option3": { + "message": "Detects uBO Lite", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option4": { + "message": "Has privacy-related issues", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option5": { + "message": "Malfunctions when uBO Lite is enabled", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option6": { + "message": "Opens unwanted tabs or windows", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option7": { + "message": "Leads to badware, phishing", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Checkbox1": { + "message": "Label the webpage as “NSFW” (“Not Safe For Work”)", + "description": "A checkbox to use for NSFW sites" + }, + "supportReportSpecificButton": { + "message": "Create new report", + "description": "Text for button which open an external webpage in Support pane" + }, "firstRunSectionLabel": { "message": "Bienvenida", "description": "The header text for the welcome message section" diff --git a/platform/mv3/extension/_locales/et/messages.json b/platform/mv3/extension/_locales/et/messages.json index bdbf31a3afafd..62d13ed6cf447 100644 --- a/platform/mv3/extension/_locales/et/messages.json +++ b/platform/mv3/extension/_locales/et/messages.json @@ -31,6 +31,10 @@ "message": "filtreerimisrežiim", "description": "Label in the popup panel for the current filtering mode" }, + "popupTipReport": { + "message": "Report an issue on this website", + "description": "Tooltip used for the 'chat' icon in the panel" + }, "popupTipDashboard": { "message": "Ava töölaud", "description": "English: Click to open the dashboard" @@ -99,6 +103,70 @@ "message": "Välised sõltuvused (ühilduvad GPLv3-ga):", "description": "Shown in the About pane" }, + "supportS6H": { + "message": "Report a filter issue", + "description": "Header of 'Report a filter issue' section in Support pane" + }, + "supportS3P1": { + "message": "Report filter issues with specific websites to the uBlockOrigin/uAssets issue tracker. Requires a GitHub account.", + "description": "First paragraph of 'Filter issues' section in Support pane" + }, + "supportS6P1S1": { + "message": "To avoid burdening volunteers with duplicate reports, please verify that the issue has not already been reported.", + "description": "A paragraph in the filter issue reporter section" + }, + "supportFindSpecificButton": { + "message": "Find similar reports", + "description": "A clickable link in the filter issue reporter section" + }, + "supportS6URL": { + "message": "Address of the webpage:", + "description": "Label for the URL of the page" + }, + "supportS6Select1": { + "message": "The webpage…", + "description": "Label for widget to select type of issue" + }, + "supportS6Select1Option0": { + "message": "-- Pick an entry --", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option1": { + "message": "Shows ads or ad leftovers", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option2": { + "message": "Has overlays or other nuisances", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option3": { + "message": "Detects uBO Lite", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option4": { + "message": "Has privacy-related issues", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option5": { + "message": "Malfunctions when uBO Lite is enabled", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option6": { + "message": "Opens unwanted tabs or windows", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option7": { + "message": "Leads to badware, phishing", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Checkbox1": { + "message": "Label the webpage as “NSFW” (“Not Safe For Work”)", + "description": "A checkbox to use for NSFW sites" + }, + "supportReportSpecificButton": { + "message": "Create new report", + "description": "Text for button which open an external webpage in Support pane" + }, "firstRunSectionLabel": { "message": "Tere tulemast", "description": "The header text for the welcome message section" diff --git a/platform/mv3/extension/_locales/eu/messages.json b/platform/mv3/extension/_locales/eu/messages.json index 204ddba5bc649..a6dedc46b7d0d 100644 --- a/platform/mv3/extension/_locales/eu/messages.json +++ b/platform/mv3/extension/_locales/eu/messages.json @@ -31,6 +31,10 @@ "message": "Iragazteko modua", "description": "Label in the popup panel for the current filtering mode" }, + "popupTipReport": { + "message": "Report an issue on this website", + "description": "Tooltip used for the 'chat' icon in the panel" + }, "popupTipDashboard": { "message": "Ireki lan-lekua", "description": "English: Click to open the dashboard" @@ -99,6 +103,70 @@ "message": "Kanpo menpekotasunak (GPLv3 lizentziarekin bateragarriak):", "description": "Shown in the About pane" }, + "supportS6H": { + "message": "Report a filter issue", + "description": "Header of 'Report a filter issue' section in Support pane" + }, + "supportS3P1": { + "message": "Report filter issues with specific websites to the uBlockOrigin/uAssets issue tracker. Requires a GitHub account.", + "description": "First paragraph of 'Filter issues' section in Support pane" + }, + "supportS6P1S1": { + "message": "To avoid burdening volunteers with duplicate reports, please verify that the issue has not already been reported.", + "description": "A paragraph in the filter issue reporter section" + }, + "supportFindSpecificButton": { + "message": "Find similar reports", + "description": "A clickable link in the filter issue reporter section" + }, + "supportS6URL": { + "message": "Address of the webpage:", + "description": "Label for the URL of the page" + }, + "supportS6Select1": { + "message": "The webpage…", + "description": "Label for widget to select type of issue" + }, + "supportS6Select1Option0": { + "message": "-- Pick an entry --", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option1": { + "message": "Shows ads or ad leftovers", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option2": { + "message": "Has overlays or other nuisances", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option3": { + "message": "Detects uBO Lite", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option4": { + "message": "Has privacy-related issues", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option5": { + "message": "Malfunctions when uBO Lite is enabled", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option6": { + "message": "Opens unwanted tabs or windows", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option7": { + "message": "Leads to badware, phishing", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Checkbox1": { + "message": "Label the webpage as “NSFW” (“Not Safe For Work”)", + "description": "A checkbox to use for NSFW sites" + }, + "supportReportSpecificButton": { + "message": "Create new report", + "description": "Text for button which open an external webpage in Support pane" + }, "firstRunSectionLabel": { "message": "Ongi etorri", "description": "The header text for the welcome message section" diff --git a/platform/mv3/extension/_locales/fa/messages.json b/platform/mv3/extension/_locales/fa/messages.json index ba2d878aa6078..7f4e9782db8fb 100644 --- a/platform/mv3/extension/_locales/fa/messages.json +++ b/platform/mv3/extension/_locales/fa/messages.json @@ -31,6 +31,10 @@ "message": "filtering mode", "description": "Label in the popup panel for the current filtering mode" }, + "popupTipReport": { + "message": "Report an issue on this website", + "description": "Tooltip used for the 'chat' icon in the panel" + }, "popupTipDashboard": { "message": "باز کردن داشبورد", "description": "English: Click to open the dashboard" @@ -99,6 +103,70 @@ "message": "External dependencies (GPLv3-compatible):", "description": "Shown in the About pane" }, + "supportS6H": { + "message": "Report a filter issue", + "description": "Header of 'Report a filter issue' section in Support pane" + }, + "supportS3P1": { + "message": "Report filter issues with specific websites to the uBlockOrigin/uAssets issue tracker. Requires a GitHub account.", + "description": "First paragraph of 'Filter issues' section in Support pane" + }, + "supportS6P1S1": { + "message": "To avoid burdening volunteers with duplicate reports, please verify that the issue has not already been reported.", + "description": "A paragraph in the filter issue reporter section" + }, + "supportFindSpecificButton": { + "message": "Find similar reports", + "description": "A clickable link in the filter issue reporter section" + }, + "supportS6URL": { + "message": "Address of the webpage:", + "description": "Label for the URL of the page" + }, + "supportS6Select1": { + "message": "The webpage…", + "description": "Label for widget to select type of issue" + }, + "supportS6Select1Option0": { + "message": "-- Pick an entry --", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option1": { + "message": "Shows ads or ad leftovers", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option2": { + "message": "Has overlays or other nuisances", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option3": { + "message": "Detects uBO Lite", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option4": { + "message": "Has privacy-related issues", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option5": { + "message": "Malfunctions when uBO Lite is enabled", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option6": { + "message": "Opens unwanted tabs or windows", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option7": { + "message": "Leads to badware, phishing", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Checkbox1": { + "message": "Label the webpage as “NSFW” (“Not Safe For Work”)", + "description": "A checkbox to use for NSFW sites" + }, + "supportReportSpecificButton": { + "message": "Create new report", + "description": "Text for button which open an external webpage in Support pane" + }, "firstRunSectionLabel": { "message": "Welcome", "description": "The header text for the welcome message section" diff --git a/platform/mv3/extension/_locales/fi/messages.json b/platform/mv3/extension/_locales/fi/messages.json index 5e8ef6de8dc4b..6708ff96fa99c 100644 --- a/platform/mv3/extension/_locales/fi/messages.json +++ b/platform/mv3/extension/_locales/fi/messages.json @@ -31,6 +31,10 @@ "message": "suodatustila", "description": "Label in the popup panel for the current filtering mode" }, + "popupTipReport": { + "message": "Report an issue on this website", + "description": "Tooltip used for the 'chat' icon in the panel" + }, "popupTipDashboard": { "message": "Avaa hallintapaneeli", "description": "English: Click to open the dashboard" @@ -99,6 +103,70 @@ "message": "Ulkopuoliset riippuvuudet (GPLv3-yhteensopiva):", "description": "Shown in the About pane" }, + "supportS6H": { + "message": "Report a filter issue", + "description": "Header of 'Report a filter issue' section in Support pane" + }, + "supportS3P1": { + "message": "Report filter issues with specific websites to the uBlockOrigin/uAssets issue tracker. Requires a GitHub account.", + "description": "First paragraph of 'Filter issues' section in Support pane" + }, + "supportS6P1S1": { + "message": "To avoid burdening volunteers with duplicate reports, please verify that the issue has not already been reported.", + "description": "A paragraph in the filter issue reporter section" + }, + "supportFindSpecificButton": { + "message": "Find similar reports", + "description": "A clickable link in the filter issue reporter section" + }, + "supportS6URL": { + "message": "Address of the webpage:", + "description": "Label for the URL of the page" + }, + "supportS6Select1": { + "message": "The webpage…", + "description": "Label for widget to select type of issue" + }, + "supportS6Select1Option0": { + "message": "-- Pick an entry --", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option1": { + "message": "Shows ads or ad leftovers", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option2": { + "message": "Has overlays or other nuisances", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option3": { + "message": "Detects uBO Lite", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option4": { + "message": "Has privacy-related issues", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option5": { + "message": "Malfunctions when uBO Lite is enabled", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option6": { + "message": "Opens unwanted tabs or windows", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option7": { + "message": "Leads to badware, phishing", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Checkbox1": { + "message": "Label the webpage as “NSFW” (“Not Safe For Work”)", + "description": "A checkbox to use for NSFW sites" + }, + "supportReportSpecificButton": { + "message": "Create new report", + "description": "Text for button which open an external webpage in Support pane" + }, "firstRunSectionLabel": { "message": "Tervetuloa", "description": "The header text for the welcome message section" diff --git a/platform/mv3/extension/_locales/fil/messages.json b/platform/mv3/extension/_locales/fil/messages.json index 8b48cf12d2ac3..8f81e02a64ea5 100644 --- a/platform/mv3/extension/_locales/fil/messages.json +++ b/platform/mv3/extension/_locales/fil/messages.json @@ -31,6 +31,10 @@ "message": "moda nang pagsasala", "description": "Label in the popup panel for the current filtering mode" }, + "popupTipReport": { + "message": "Report an issue on this website", + "description": "Tooltip used for the 'chat' icon in the panel" + }, "popupTipDashboard": { "message": "Buksan ang dashboard", "description": "English: Click to open the dashboard" @@ -99,6 +103,70 @@ "message": "Mga panlabas na dependency (angkop sa GPLv3)", "description": "Shown in the About pane" }, + "supportS6H": { + "message": "Report a filter issue", + "description": "Header of 'Report a filter issue' section in Support pane" + }, + "supportS3P1": { + "message": "Report filter issues with specific websites to the uBlockOrigin/uAssets issue tracker. Requires a GitHub account.", + "description": "First paragraph of 'Filter issues' section in Support pane" + }, + "supportS6P1S1": { + "message": "To avoid burdening volunteers with duplicate reports, please verify that the issue has not already been reported.", + "description": "A paragraph in the filter issue reporter section" + }, + "supportFindSpecificButton": { + "message": "Find similar reports", + "description": "A clickable link in the filter issue reporter section" + }, + "supportS6URL": { + "message": "Address of the webpage:", + "description": "Label for the URL of the page" + }, + "supportS6Select1": { + "message": "The webpage…", + "description": "Label for widget to select type of issue" + }, + "supportS6Select1Option0": { + "message": "-- Pick an entry --", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option1": { + "message": "Shows ads or ad leftovers", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option2": { + "message": "Has overlays or other nuisances", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option3": { + "message": "Detects uBO Lite", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option4": { + "message": "Has privacy-related issues", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option5": { + "message": "Malfunctions when uBO Lite is enabled", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option6": { + "message": "Opens unwanted tabs or windows", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option7": { + "message": "Leads to badware, phishing", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Checkbox1": { + "message": "Label the webpage as “NSFW” (“Not Safe For Work”)", + "description": "A checkbox to use for NSFW sites" + }, + "supportReportSpecificButton": { + "message": "Create new report", + "description": "Text for button which open an external webpage in Support pane" + }, "firstRunSectionLabel": { "message": "Mabuhay", "description": "The header text for the welcome message section" diff --git a/platform/mv3/extension/_locales/fr/messages.json b/platform/mv3/extension/_locales/fr/messages.json index a0c557d0ea923..d41b35931148b 100644 --- a/platform/mv3/extension/_locales/fr/messages.json +++ b/platform/mv3/extension/_locales/fr/messages.json @@ -31,6 +31,10 @@ "message": "Mode de filtrage", "description": "Label in the popup panel for the current filtering mode" }, + "popupTipReport": { + "message": "Report an issue on this website", + "description": "Tooltip used for the 'chat' icon in the panel" + }, "popupTipDashboard": { "message": "Ouvrir le Tableau de bord", "description": "English: Click to open the dashboard" @@ -99,6 +103,70 @@ "message": "Dépendances externes (compatibles GPLv3) :", "description": "Shown in the About pane" }, + "supportS6H": { + "message": "Report a filter issue", + "description": "Header of 'Report a filter issue' section in Support pane" + }, + "supportS3P1": { + "message": "Report filter issues with specific websites to the uBlockOrigin/uAssets issue tracker. Requires a GitHub account.", + "description": "First paragraph of 'Filter issues' section in Support pane" + }, + "supportS6P1S1": { + "message": "To avoid burdening volunteers with duplicate reports, please verify that the issue has not already been reported.", + "description": "A paragraph in the filter issue reporter section" + }, + "supportFindSpecificButton": { + "message": "Find similar reports", + "description": "A clickable link in the filter issue reporter section" + }, + "supportS6URL": { + "message": "Address of the webpage:", + "description": "Label for the URL of the page" + }, + "supportS6Select1": { + "message": "The webpage…", + "description": "Label for widget to select type of issue" + }, + "supportS6Select1Option0": { + "message": "-- Pick an entry --", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option1": { + "message": "Shows ads or ad leftovers", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option2": { + "message": "Has overlays or other nuisances", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option3": { + "message": "Detects uBO Lite", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option4": { + "message": "Has privacy-related issues", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option5": { + "message": "Malfunctions when uBO Lite is enabled", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option6": { + "message": "Opens unwanted tabs or windows", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option7": { + "message": "Leads to badware, phishing", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Checkbox1": { + "message": "Label the webpage as “NSFW” (“Not Safe For Work”)", + "description": "A checkbox to use for NSFW sites" + }, + "supportReportSpecificButton": { + "message": "Create new report", + "description": "Text for button which open an external webpage in Support pane" + }, "firstRunSectionLabel": { "message": "Bienvenue", "description": "The header text for the welcome message section" diff --git a/platform/mv3/extension/_locales/fy/messages.json b/platform/mv3/extension/_locales/fy/messages.json index 634f28c88f3de..d74cc92db8a0e 100644 --- a/platform/mv3/extension/_locales/fy/messages.json +++ b/platform/mv3/extension/_locales/fy/messages.json @@ -31,6 +31,10 @@ "message": "filtermodus", "description": "Label in the popup panel for the current filtering mode" }, + "popupTipReport": { + "message": "Report an issue on this website", + "description": "Tooltip used for the 'chat' icon in the panel" + }, "popupTipDashboard": { "message": "Dashboerd iepenje", "description": "English: Click to open the dashboard" @@ -99,6 +103,70 @@ "message": "Eksterne ôfhinklikheden (GPLv3-kompatibel):", "description": "Shown in the About pane" }, + "supportS6H": { + "message": "Report a filter issue", + "description": "Header of 'Report a filter issue' section in Support pane" + }, + "supportS3P1": { + "message": "Report filter issues with specific websites to the uBlockOrigin/uAssets issue tracker. Requires a GitHub account.", + "description": "First paragraph of 'Filter issues' section in Support pane" + }, + "supportS6P1S1": { + "message": "To avoid burdening volunteers with duplicate reports, please verify that the issue has not already been reported.", + "description": "A paragraph in the filter issue reporter section" + }, + "supportFindSpecificButton": { + "message": "Find similar reports", + "description": "A clickable link in the filter issue reporter section" + }, + "supportS6URL": { + "message": "Address of the webpage:", + "description": "Label for the URL of the page" + }, + "supportS6Select1": { + "message": "The webpage…", + "description": "Label for widget to select type of issue" + }, + "supportS6Select1Option0": { + "message": "-- Pick an entry --", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option1": { + "message": "Shows ads or ad leftovers", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option2": { + "message": "Has overlays or other nuisances", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option3": { + "message": "Detects uBO Lite", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option4": { + "message": "Has privacy-related issues", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option5": { + "message": "Malfunctions when uBO Lite is enabled", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option6": { + "message": "Opens unwanted tabs or windows", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option7": { + "message": "Leads to badware, phishing", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Checkbox1": { + "message": "Label the webpage as “NSFW” (“Not Safe For Work”)", + "description": "A checkbox to use for NSFW sites" + }, + "supportReportSpecificButton": { + "message": "Create new report", + "description": "Text for button which open an external webpage in Support pane" + }, "firstRunSectionLabel": { "message": "Wolkom", "description": "The header text for the welcome message section" diff --git a/platform/mv3/extension/_locales/gl/messages.json b/platform/mv3/extension/_locales/gl/messages.json index 1dcc3844d46da..dc87e7591e00f 100644 --- a/platform/mv3/extension/_locales/gl/messages.json +++ b/platform/mv3/extension/_locales/gl/messages.json @@ -31,6 +31,10 @@ "message": "modo de filtrado", "description": "Label in the popup panel for the current filtering mode" }, + "popupTipReport": { + "message": "Report an issue on this website", + "description": "Tooltip used for the 'chat' icon in the panel" + }, "popupTipDashboard": { "message": "Abrir o panel", "description": "English: Click to open the dashboard" @@ -99,6 +103,70 @@ "message": "Dependencias externas (GPLv3-compatible):", "description": "Shown in the About pane" }, + "supportS6H": { + "message": "Report a filter issue", + "description": "Header of 'Report a filter issue' section in Support pane" + }, + "supportS3P1": { + "message": "Report filter issues with specific websites to the uBlockOrigin/uAssets issue tracker. Requires a GitHub account.", + "description": "First paragraph of 'Filter issues' section in Support pane" + }, + "supportS6P1S1": { + "message": "To avoid burdening volunteers with duplicate reports, please verify that the issue has not already been reported.", + "description": "A paragraph in the filter issue reporter section" + }, + "supportFindSpecificButton": { + "message": "Find similar reports", + "description": "A clickable link in the filter issue reporter section" + }, + "supportS6URL": { + "message": "Address of the webpage:", + "description": "Label for the URL of the page" + }, + "supportS6Select1": { + "message": "The webpage…", + "description": "Label for widget to select type of issue" + }, + "supportS6Select1Option0": { + "message": "-- Pick an entry --", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option1": { + "message": "Shows ads or ad leftovers", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option2": { + "message": "Has overlays or other nuisances", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option3": { + "message": "Detects uBO Lite", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option4": { + "message": "Has privacy-related issues", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option5": { + "message": "Malfunctions when uBO Lite is enabled", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option6": { + "message": "Opens unwanted tabs or windows", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option7": { + "message": "Leads to badware, phishing", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Checkbox1": { + "message": "Label the webpage as “NSFW” (“Not Safe For Work”)", + "description": "A checkbox to use for NSFW sites" + }, + "supportReportSpecificButton": { + "message": "Create new report", + "description": "Text for button which open an external webpage in Support pane" + }, "firstRunSectionLabel": { "message": "Benvida", "description": "The header text for the welcome message section" diff --git a/platform/mv3/extension/_locales/gu/messages.json b/platform/mv3/extension/_locales/gu/messages.json index 30c719d52722d..c5f77c667a506 100644 --- a/platform/mv3/extension/_locales/gu/messages.json +++ b/platform/mv3/extension/_locales/gu/messages.json @@ -31,6 +31,10 @@ "message": "filtering mode", "description": "Label in the popup panel for the current filtering mode" }, + "popupTipReport": { + "message": "Report an issue on this website", + "description": "Tooltip used for the 'chat' icon in the panel" + }, "popupTipDashboard": { "message": "Open the dashboard", "description": "English: Click to open the dashboard" @@ -99,6 +103,70 @@ "message": "External dependencies (GPLv3-compatible):", "description": "Shown in the About pane" }, + "supportS6H": { + "message": "Report a filter issue", + "description": "Header of 'Report a filter issue' section in Support pane" + }, + "supportS3P1": { + "message": "Report filter issues with specific websites to the uBlockOrigin/uAssets issue tracker. Requires a GitHub account.", + "description": "First paragraph of 'Filter issues' section in Support pane" + }, + "supportS6P1S1": { + "message": "To avoid burdening volunteers with duplicate reports, please verify that the issue has not already been reported.", + "description": "A paragraph in the filter issue reporter section" + }, + "supportFindSpecificButton": { + "message": "Find similar reports", + "description": "A clickable link in the filter issue reporter section" + }, + "supportS6URL": { + "message": "Address of the webpage:", + "description": "Label for the URL of the page" + }, + "supportS6Select1": { + "message": "The webpage…", + "description": "Label for widget to select type of issue" + }, + "supportS6Select1Option0": { + "message": "-- Pick an entry --", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option1": { + "message": "Shows ads or ad leftovers", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option2": { + "message": "Has overlays or other nuisances", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option3": { + "message": "Detects uBO Lite", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option4": { + "message": "Has privacy-related issues", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option5": { + "message": "Malfunctions when uBO Lite is enabled", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option6": { + "message": "Opens unwanted tabs or windows", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option7": { + "message": "Leads to badware, phishing", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Checkbox1": { + "message": "Label the webpage as “NSFW” (“Not Safe For Work”)", + "description": "A checkbox to use for NSFW sites" + }, + "supportReportSpecificButton": { + "message": "Create new report", + "description": "Text for button which open an external webpage in Support pane" + }, "firstRunSectionLabel": { "message": "Welcome", "description": "The header text for the welcome message section" diff --git a/platform/mv3/extension/_locales/he/messages.json b/platform/mv3/extension/_locales/he/messages.json index d76b50ebb83d1..16e21d55e13f6 100644 --- a/platform/mv3/extension/_locales/he/messages.json +++ b/platform/mv3/extension/_locales/he/messages.json @@ -31,6 +31,10 @@ "message": "מצב מסנן", "description": "Label in the popup panel for the current filtering mode" }, + "popupTipReport": { + "message": "Report an issue on this website", + "description": "Tooltip used for the 'chat' icon in the panel" + }, "popupTipDashboard": { "message": "פתיחת לוח־המחוונים", "description": "English: Click to open the dashboard" @@ -99,6 +103,70 @@ "message": "תלויות חיצוניות (תואם GPLv3):", "description": "Shown in the About pane" }, + "supportS6H": { + "message": "Report a filter issue", + "description": "Header of 'Report a filter issue' section in Support pane" + }, + "supportS3P1": { + "message": "Report filter issues with specific websites to the uBlockOrigin/uAssets issue tracker. Requires a GitHub account.", + "description": "First paragraph of 'Filter issues' section in Support pane" + }, + "supportS6P1S1": { + "message": "To avoid burdening volunteers with duplicate reports, please verify that the issue has not already been reported.", + "description": "A paragraph in the filter issue reporter section" + }, + "supportFindSpecificButton": { + "message": "Find similar reports", + "description": "A clickable link in the filter issue reporter section" + }, + "supportS6URL": { + "message": "Address of the webpage:", + "description": "Label for the URL of the page" + }, + "supportS6Select1": { + "message": "The webpage…", + "description": "Label for widget to select type of issue" + }, + "supportS6Select1Option0": { + "message": "-- Pick an entry --", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option1": { + "message": "Shows ads or ad leftovers", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option2": { + "message": "Has overlays or other nuisances", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option3": { + "message": "Detects uBO Lite", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option4": { + "message": "Has privacy-related issues", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option5": { + "message": "Malfunctions when uBO Lite is enabled", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option6": { + "message": "Opens unwanted tabs or windows", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option7": { + "message": "Leads to badware, phishing", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Checkbox1": { + "message": "Label the webpage as “NSFW” (“Not Safe For Work”)", + "description": "A checkbox to use for NSFW sites" + }, + "supportReportSpecificButton": { + "message": "Create new report", + "description": "Text for button which open an external webpage in Support pane" + }, "firstRunSectionLabel": { "message": "ברוך בואך", "description": "The header text for the welcome message section" diff --git a/platform/mv3/extension/_locales/hi/messages.json b/platform/mv3/extension/_locales/hi/messages.json index 0cfd586195cc7..d81b4d7bffb8a 100644 --- a/platform/mv3/extension/_locales/hi/messages.json +++ b/platform/mv3/extension/_locales/hi/messages.json @@ -31,6 +31,10 @@ "message": "फ़िल्टरिंग मोड", "description": "Label in the popup panel for the current filtering mode" }, + "popupTipReport": { + "message": "Report an issue on this website", + "description": "Tooltip used for the 'chat' icon in the panel" + }, "popupTipDashboard": { "message": "डैशबोर्ड खोलें", "description": "English: Click to open the dashboard" @@ -99,6 +103,70 @@ "message": "बाहरी निर्भरता (GPLv3-compatible):", "description": "Shown in the About pane" }, + "supportS6H": { + "message": "Report a filter issue", + "description": "Header of 'Report a filter issue' section in Support pane" + }, + "supportS3P1": { + "message": "Report filter issues with specific websites to the uBlockOrigin/uAssets issue tracker. Requires a GitHub account.", + "description": "First paragraph of 'Filter issues' section in Support pane" + }, + "supportS6P1S1": { + "message": "To avoid burdening volunteers with duplicate reports, please verify that the issue has not already been reported.", + "description": "A paragraph in the filter issue reporter section" + }, + "supportFindSpecificButton": { + "message": "Find similar reports", + "description": "A clickable link in the filter issue reporter section" + }, + "supportS6URL": { + "message": "Address of the webpage:", + "description": "Label for the URL of the page" + }, + "supportS6Select1": { + "message": "The webpage…", + "description": "Label for widget to select type of issue" + }, + "supportS6Select1Option0": { + "message": "-- Pick an entry --", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option1": { + "message": "Shows ads or ad leftovers", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option2": { + "message": "Has overlays or other nuisances", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option3": { + "message": "Detects uBO Lite", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option4": { + "message": "Has privacy-related issues", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option5": { + "message": "Malfunctions when uBO Lite is enabled", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option6": { + "message": "Opens unwanted tabs or windows", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option7": { + "message": "Leads to badware, phishing", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Checkbox1": { + "message": "Label the webpage as “NSFW” (“Not Safe For Work”)", + "description": "A checkbox to use for NSFW sites" + }, + "supportReportSpecificButton": { + "message": "Create new report", + "description": "Text for button which open an external webpage in Support pane" + }, "firstRunSectionLabel": { "message": "स्वागत", "description": "The header text for the welcome message section" diff --git a/platform/mv3/extension/_locales/hr/messages.json b/platform/mv3/extension/_locales/hr/messages.json index 5ca15ed148feb..1a5ad1da7eaba 100644 --- a/platform/mv3/extension/_locales/hr/messages.json +++ b/platform/mv3/extension/_locales/hr/messages.json @@ -31,6 +31,10 @@ "message": "Način filtriranja", "description": "Label in the popup panel for the current filtering mode" }, + "popupTipReport": { + "message": "Report an issue on this website", + "description": "Tooltip used for the 'chat' icon in the panel" + }, "popupTipDashboard": { "message": "Otvori nadzornu ploču", "description": "English: Click to open the dashboard" @@ -99,6 +103,70 @@ "message": "Vanjski korišteni programi (GPLv3-kompatiblini):", "description": "Shown in the About pane" }, + "supportS6H": { + "message": "Report a filter issue", + "description": "Header of 'Report a filter issue' section in Support pane" + }, + "supportS3P1": { + "message": "Report filter issues with specific websites to the uBlockOrigin/uAssets issue tracker. Requires a GitHub account.", + "description": "First paragraph of 'Filter issues' section in Support pane" + }, + "supportS6P1S1": { + "message": "To avoid burdening volunteers with duplicate reports, please verify that the issue has not already been reported.", + "description": "A paragraph in the filter issue reporter section" + }, + "supportFindSpecificButton": { + "message": "Find similar reports", + "description": "A clickable link in the filter issue reporter section" + }, + "supportS6URL": { + "message": "Address of the webpage:", + "description": "Label for the URL of the page" + }, + "supportS6Select1": { + "message": "The webpage…", + "description": "Label for widget to select type of issue" + }, + "supportS6Select1Option0": { + "message": "-- Pick an entry --", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option1": { + "message": "Shows ads or ad leftovers", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option2": { + "message": "Has overlays or other nuisances", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option3": { + "message": "Detects uBO Lite", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option4": { + "message": "Has privacy-related issues", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option5": { + "message": "Malfunctions when uBO Lite is enabled", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option6": { + "message": "Opens unwanted tabs or windows", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option7": { + "message": "Leads to badware, phishing", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Checkbox1": { + "message": "Label the webpage as “NSFW” (“Not Safe For Work”)", + "description": "A checkbox to use for NSFW sites" + }, + "supportReportSpecificButton": { + "message": "Create new report", + "description": "Text for button which open an external webpage in Support pane" + }, "firstRunSectionLabel": { "message": "Dobrodošli", "description": "The header text for the welcome message section" diff --git a/platform/mv3/extension/_locales/hu/messages.json b/platform/mv3/extension/_locales/hu/messages.json index 483b2eef56a22..53e7c2c672253 100644 --- a/platform/mv3/extension/_locales/hu/messages.json +++ b/platform/mv3/extension/_locales/hu/messages.json @@ -31,6 +31,10 @@ "message": "szűrési mód", "description": "Label in the popup panel for the current filtering mode" }, + "popupTipReport": { + "message": "Report an issue on this website", + "description": "Tooltip used for the 'chat' icon in the panel" + }, "popupTipDashboard": { "message": "Vezérlőpult megnyitása", "description": "English: Click to open the dashboard" @@ -99,6 +103,70 @@ "message": "Külső függőségek (GPLv3-kompatibilis):", "description": "Shown in the About pane" }, + "supportS6H": { + "message": "Report a filter issue", + "description": "Header of 'Report a filter issue' section in Support pane" + }, + "supportS3P1": { + "message": "Report filter issues with specific websites to the uBlockOrigin/uAssets issue tracker. Requires a GitHub account.", + "description": "First paragraph of 'Filter issues' section in Support pane" + }, + "supportS6P1S1": { + "message": "To avoid burdening volunteers with duplicate reports, please verify that the issue has not already been reported.", + "description": "A paragraph in the filter issue reporter section" + }, + "supportFindSpecificButton": { + "message": "Find similar reports", + "description": "A clickable link in the filter issue reporter section" + }, + "supportS6URL": { + "message": "Address of the webpage:", + "description": "Label for the URL of the page" + }, + "supportS6Select1": { + "message": "The webpage…", + "description": "Label for widget to select type of issue" + }, + "supportS6Select1Option0": { + "message": "-- Pick an entry --", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option1": { + "message": "Shows ads or ad leftovers", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option2": { + "message": "Has overlays or other nuisances", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option3": { + "message": "Detects uBO Lite", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option4": { + "message": "Has privacy-related issues", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option5": { + "message": "Malfunctions when uBO Lite is enabled", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option6": { + "message": "Opens unwanted tabs or windows", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option7": { + "message": "Leads to badware, phishing", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Checkbox1": { + "message": "Label the webpage as “NSFW” (“Not Safe For Work”)", + "description": "A checkbox to use for NSFW sites" + }, + "supportReportSpecificButton": { + "message": "Create new report", + "description": "Text for button which open an external webpage in Support pane" + }, "firstRunSectionLabel": { "message": "Üdvözöljük", "description": "The header text for the welcome message section" diff --git a/platform/mv3/extension/_locales/hy/messages.json b/platform/mv3/extension/_locales/hy/messages.json index 5a50db68ace07..a0cf19fa06bd7 100644 --- a/platform/mv3/extension/_locales/hy/messages.json +++ b/platform/mv3/extension/_locales/hy/messages.json @@ -31,6 +31,10 @@ "message": "զտման ռեժիմ", "description": "Label in the popup panel for the current filtering mode" }, + "popupTipReport": { + "message": "Report an issue on this website", + "description": "Tooltip used for the 'chat' icon in the panel" + }, "popupTipDashboard": { "message": "Բացել կառավահանը", "description": "English: Click to open the dashboard" @@ -99,6 +103,70 @@ "message": "Արտաքին կախվածություններ (GPLv3-համատեղելի)՝", "description": "Shown in the About pane" }, + "supportS6H": { + "message": "Report a filter issue", + "description": "Header of 'Report a filter issue' section in Support pane" + }, + "supportS3P1": { + "message": "Report filter issues with specific websites to the uBlockOrigin/uAssets issue tracker. Requires a GitHub account.", + "description": "First paragraph of 'Filter issues' section in Support pane" + }, + "supportS6P1S1": { + "message": "To avoid burdening volunteers with duplicate reports, please verify that the issue has not already been reported.", + "description": "A paragraph in the filter issue reporter section" + }, + "supportFindSpecificButton": { + "message": "Find similar reports", + "description": "A clickable link in the filter issue reporter section" + }, + "supportS6URL": { + "message": "Address of the webpage:", + "description": "Label for the URL of the page" + }, + "supportS6Select1": { + "message": "The webpage…", + "description": "Label for widget to select type of issue" + }, + "supportS6Select1Option0": { + "message": "-- Pick an entry --", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option1": { + "message": "Shows ads or ad leftovers", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option2": { + "message": "Has overlays or other nuisances", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option3": { + "message": "Detects uBO Lite", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option4": { + "message": "Has privacy-related issues", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option5": { + "message": "Malfunctions when uBO Lite is enabled", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option6": { + "message": "Opens unwanted tabs or windows", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option7": { + "message": "Leads to badware, phishing", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Checkbox1": { + "message": "Label the webpage as “NSFW” (“Not Safe For Work”)", + "description": "A checkbox to use for NSFW sites" + }, + "supportReportSpecificButton": { + "message": "Create new report", + "description": "Text for button which open an external webpage in Support pane" + }, "firstRunSectionLabel": { "message": "Բարի գալուստ", "description": "The header text for the welcome message section" diff --git a/platform/mv3/extension/_locales/id/messages.json b/platform/mv3/extension/_locales/id/messages.json index 7907760e09ecd..1fb1e31ffa003 100644 --- a/platform/mv3/extension/_locales/id/messages.json +++ b/platform/mv3/extension/_locales/id/messages.json @@ -31,6 +31,10 @@ "message": "mode filter", "description": "Label in the popup panel for the current filtering mode" }, + "popupTipReport": { + "message": "Report an issue on this website", + "description": "Tooltip used for the 'chat' icon in the panel" + }, "popupTipDashboard": { "message": "Buka dasbor", "description": "English: Click to open the dashboard" @@ -99,6 +103,70 @@ "message": "Dependensi eksternal (kompatibel GPLv3):", "description": "Shown in the About pane" }, + "supportS6H": { + "message": "Report a filter issue", + "description": "Header of 'Report a filter issue' section in Support pane" + }, + "supportS3P1": { + "message": "Report filter issues with specific websites to the uBlockOrigin/uAssets issue tracker. Requires a GitHub account.", + "description": "First paragraph of 'Filter issues' section in Support pane" + }, + "supportS6P1S1": { + "message": "To avoid burdening volunteers with duplicate reports, please verify that the issue has not already been reported.", + "description": "A paragraph in the filter issue reporter section" + }, + "supportFindSpecificButton": { + "message": "Find similar reports", + "description": "A clickable link in the filter issue reporter section" + }, + "supportS6URL": { + "message": "Address of the webpage:", + "description": "Label for the URL of the page" + }, + "supportS6Select1": { + "message": "The webpage…", + "description": "Label for widget to select type of issue" + }, + "supportS6Select1Option0": { + "message": "-- Pick an entry --", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option1": { + "message": "Shows ads or ad leftovers", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option2": { + "message": "Has overlays or other nuisances", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option3": { + "message": "Detects uBO Lite", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option4": { + "message": "Has privacy-related issues", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option5": { + "message": "Malfunctions when uBO Lite is enabled", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option6": { + "message": "Opens unwanted tabs or windows", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option7": { + "message": "Leads to badware, phishing", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Checkbox1": { + "message": "Label the webpage as “NSFW” (“Not Safe For Work”)", + "description": "A checkbox to use for NSFW sites" + }, + "supportReportSpecificButton": { + "message": "Create new report", + "description": "Text for button which open an external webpage in Support pane" + }, "firstRunSectionLabel": { "message": "Selamat datang", "description": "The header text for the welcome message section" diff --git a/platform/mv3/extension/_locales/it/messages.json b/platform/mv3/extension/_locales/it/messages.json index dc7fbd930783d..52df0e5306c14 100644 --- a/platform/mv3/extension/_locales/it/messages.json +++ b/platform/mv3/extension/_locales/it/messages.json @@ -31,6 +31,10 @@ "message": "Modalità di filtraggio", "description": "Label in the popup panel for the current filtering mode" }, + "popupTipReport": { + "message": "Report an issue on this website", + "description": "Tooltip used for the 'chat' icon in the panel" + }, "popupTipDashboard": { "message": "Apri il pannello di controllo", "description": "English: Click to open the dashboard" @@ -99,6 +103,70 @@ "message": "Dipendenze esterne (GPLv3-compatibili):", "description": "Shown in the About pane" }, + "supportS6H": { + "message": "Report a filter issue", + "description": "Header of 'Report a filter issue' section in Support pane" + }, + "supportS3P1": { + "message": "Report filter issues with specific websites to the uBlockOrigin/uAssets issue tracker. Requires a GitHub account.", + "description": "First paragraph of 'Filter issues' section in Support pane" + }, + "supportS6P1S1": { + "message": "To avoid burdening volunteers with duplicate reports, please verify that the issue has not already been reported.", + "description": "A paragraph in the filter issue reporter section" + }, + "supportFindSpecificButton": { + "message": "Find similar reports", + "description": "A clickable link in the filter issue reporter section" + }, + "supportS6URL": { + "message": "Address of the webpage:", + "description": "Label for the URL of the page" + }, + "supportS6Select1": { + "message": "The webpage…", + "description": "Label for widget to select type of issue" + }, + "supportS6Select1Option0": { + "message": "-- Pick an entry --", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option1": { + "message": "Shows ads or ad leftovers", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option2": { + "message": "Has overlays or other nuisances", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option3": { + "message": "Detects uBO Lite", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option4": { + "message": "Has privacy-related issues", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option5": { + "message": "Malfunctions when uBO Lite is enabled", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option6": { + "message": "Opens unwanted tabs or windows", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option7": { + "message": "Leads to badware, phishing", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Checkbox1": { + "message": "Label the webpage as “NSFW” (“Not Safe For Work”)", + "description": "A checkbox to use for NSFW sites" + }, + "supportReportSpecificButton": { + "message": "Create new report", + "description": "Text for button which open an external webpage in Support pane" + }, "firstRunSectionLabel": { "message": "Benvenuto", "description": "The header text for the welcome message section" diff --git a/platform/mv3/extension/_locales/ja/messages.json b/platform/mv3/extension/_locales/ja/messages.json index 53ad766bb031a..eb7a89af2b45c 100644 --- a/platform/mv3/extension/_locales/ja/messages.json +++ b/platform/mv3/extension/_locales/ja/messages.json @@ -31,6 +31,10 @@ "message": "フィルタリングモード", "description": "Label in the popup panel for the current filtering mode" }, + "popupTipReport": { + "message": "Report an issue on this website", + "description": "Tooltip used for the 'chat' icon in the panel" + }, "popupTipDashboard": { "message": "ダッシュボードを開く", "description": "English: Click to open the dashboard" @@ -99,6 +103,70 @@ "message": "外部依存関係 (GPLv3 互換):", "description": "Shown in the About pane" }, + "supportS6H": { + "message": "Report a filter issue", + "description": "Header of 'Report a filter issue' section in Support pane" + }, + "supportS3P1": { + "message": "Report filter issues with specific websites to the uBlockOrigin/uAssets issue tracker. Requires a GitHub account.", + "description": "First paragraph of 'Filter issues' section in Support pane" + }, + "supportS6P1S1": { + "message": "To avoid burdening volunteers with duplicate reports, please verify that the issue has not already been reported.", + "description": "A paragraph in the filter issue reporter section" + }, + "supportFindSpecificButton": { + "message": "Find similar reports", + "description": "A clickable link in the filter issue reporter section" + }, + "supportS6URL": { + "message": "Address of the webpage:", + "description": "Label for the URL of the page" + }, + "supportS6Select1": { + "message": "The webpage…", + "description": "Label for widget to select type of issue" + }, + "supportS6Select1Option0": { + "message": "-- Pick an entry --", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option1": { + "message": "Shows ads or ad leftovers", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option2": { + "message": "Has overlays or other nuisances", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option3": { + "message": "Detects uBO Lite", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option4": { + "message": "Has privacy-related issues", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option5": { + "message": "Malfunctions when uBO Lite is enabled", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option6": { + "message": "Opens unwanted tabs or windows", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option7": { + "message": "Leads to badware, phishing", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Checkbox1": { + "message": "Label the webpage as “NSFW” (“Not Safe For Work”)", + "description": "A checkbox to use for NSFW sites" + }, + "supportReportSpecificButton": { + "message": "Create new report", + "description": "Text for button which open an external webpage in Support pane" + }, "firstRunSectionLabel": { "message": "ようこそ", "description": "The header text for the welcome message section" diff --git a/platform/mv3/extension/_locales/ka/messages.json b/platform/mv3/extension/_locales/ka/messages.json index a0550c66e1a05..0f837f0152d45 100644 --- a/platform/mv3/extension/_locales/ka/messages.json +++ b/platform/mv3/extension/_locales/ka/messages.json @@ -31,6 +31,10 @@ "message": "გაფილტვრის რეჟიმი", "description": "Label in the popup panel for the current filtering mode" }, + "popupTipReport": { + "message": "Report an issue on this website", + "description": "Tooltip used for the 'chat' icon in the panel" + }, "popupTipDashboard": { "message": "სამართავის გახსნა", "description": "English: Click to open the dashboard" @@ -99,6 +103,70 @@ "message": "ცალკეული დაქვემდებარებული პროექტები (GPLv3-თან თავსებადი):", "description": "Shown in the About pane" }, + "supportS6H": { + "message": "Report a filter issue", + "description": "Header of 'Report a filter issue' section in Support pane" + }, + "supportS3P1": { + "message": "Report filter issues with specific websites to the uBlockOrigin/uAssets issue tracker. Requires a GitHub account.", + "description": "First paragraph of 'Filter issues' section in Support pane" + }, + "supportS6P1S1": { + "message": "To avoid burdening volunteers with duplicate reports, please verify that the issue has not already been reported.", + "description": "A paragraph in the filter issue reporter section" + }, + "supportFindSpecificButton": { + "message": "Find similar reports", + "description": "A clickable link in the filter issue reporter section" + }, + "supportS6URL": { + "message": "Address of the webpage:", + "description": "Label for the URL of the page" + }, + "supportS6Select1": { + "message": "The webpage…", + "description": "Label for widget to select type of issue" + }, + "supportS6Select1Option0": { + "message": "-- Pick an entry --", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option1": { + "message": "Shows ads or ad leftovers", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option2": { + "message": "Has overlays or other nuisances", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option3": { + "message": "Detects uBO Lite", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option4": { + "message": "Has privacy-related issues", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option5": { + "message": "Malfunctions when uBO Lite is enabled", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option6": { + "message": "Opens unwanted tabs or windows", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option7": { + "message": "Leads to badware, phishing", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Checkbox1": { + "message": "Label the webpage as “NSFW” (“Not Safe For Work”)", + "description": "A checkbox to use for NSFW sites" + }, + "supportReportSpecificButton": { + "message": "Create new report", + "description": "Text for button which open an external webpage in Support pane" + }, "firstRunSectionLabel": { "message": "მოგესალმებით", "description": "The header text for the welcome message section" diff --git a/platform/mv3/extension/_locales/kk/messages.json b/platform/mv3/extension/_locales/kk/messages.json index 30c719d52722d..c5f77c667a506 100644 --- a/platform/mv3/extension/_locales/kk/messages.json +++ b/platform/mv3/extension/_locales/kk/messages.json @@ -31,6 +31,10 @@ "message": "filtering mode", "description": "Label in the popup panel for the current filtering mode" }, + "popupTipReport": { + "message": "Report an issue on this website", + "description": "Tooltip used for the 'chat' icon in the panel" + }, "popupTipDashboard": { "message": "Open the dashboard", "description": "English: Click to open the dashboard" @@ -99,6 +103,70 @@ "message": "External dependencies (GPLv3-compatible):", "description": "Shown in the About pane" }, + "supportS6H": { + "message": "Report a filter issue", + "description": "Header of 'Report a filter issue' section in Support pane" + }, + "supportS3P1": { + "message": "Report filter issues with specific websites to the uBlockOrigin/uAssets issue tracker. Requires a GitHub account.", + "description": "First paragraph of 'Filter issues' section in Support pane" + }, + "supportS6P1S1": { + "message": "To avoid burdening volunteers with duplicate reports, please verify that the issue has not already been reported.", + "description": "A paragraph in the filter issue reporter section" + }, + "supportFindSpecificButton": { + "message": "Find similar reports", + "description": "A clickable link in the filter issue reporter section" + }, + "supportS6URL": { + "message": "Address of the webpage:", + "description": "Label for the URL of the page" + }, + "supportS6Select1": { + "message": "The webpage…", + "description": "Label for widget to select type of issue" + }, + "supportS6Select1Option0": { + "message": "-- Pick an entry --", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option1": { + "message": "Shows ads or ad leftovers", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option2": { + "message": "Has overlays or other nuisances", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option3": { + "message": "Detects uBO Lite", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option4": { + "message": "Has privacy-related issues", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option5": { + "message": "Malfunctions when uBO Lite is enabled", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option6": { + "message": "Opens unwanted tabs or windows", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option7": { + "message": "Leads to badware, phishing", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Checkbox1": { + "message": "Label the webpage as “NSFW” (“Not Safe For Work”)", + "description": "A checkbox to use for NSFW sites" + }, + "supportReportSpecificButton": { + "message": "Create new report", + "description": "Text for button which open an external webpage in Support pane" + }, "firstRunSectionLabel": { "message": "Welcome", "description": "The header text for the welcome message section" diff --git a/platform/mv3/extension/_locales/kn/messages.json b/platform/mv3/extension/_locales/kn/messages.json index c1261b1651f77..7ff919c228737 100644 --- a/platform/mv3/extension/_locales/kn/messages.json +++ b/platform/mv3/extension/_locales/kn/messages.json @@ -31,6 +31,10 @@ "message": "ಫಿಲ್ಟರಿಂಗ್ ಮೋಡ್", "description": "Label in the popup panel for the current filtering mode" }, + "popupTipReport": { + "message": "Report an issue on this website", + "description": "Tooltip used for the 'chat' icon in the panel" + }, "popupTipDashboard": { "message": "ಡ್ಯಾಶ್‌ಬೋರ್ಡ್ ತೆರೆಯಿರಿ", "description": "English: Click to open the dashboard" @@ -99,6 +103,70 @@ "message": "External dependencies (GPLv3-compatible):", "description": "Shown in the About pane" }, + "supportS6H": { + "message": "Report a filter issue", + "description": "Header of 'Report a filter issue' section in Support pane" + }, + "supportS3P1": { + "message": "Report filter issues with specific websites to the uBlockOrigin/uAssets issue tracker. Requires a GitHub account.", + "description": "First paragraph of 'Filter issues' section in Support pane" + }, + "supportS6P1S1": { + "message": "To avoid burdening volunteers with duplicate reports, please verify that the issue has not already been reported.", + "description": "A paragraph in the filter issue reporter section" + }, + "supportFindSpecificButton": { + "message": "Find similar reports", + "description": "A clickable link in the filter issue reporter section" + }, + "supportS6URL": { + "message": "Address of the webpage:", + "description": "Label for the URL of the page" + }, + "supportS6Select1": { + "message": "The webpage…", + "description": "Label for widget to select type of issue" + }, + "supportS6Select1Option0": { + "message": "-- Pick an entry --", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option1": { + "message": "Shows ads or ad leftovers", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option2": { + "message": "Has overlays or other nuisances", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option3": { + "message": "Detects uBO Lite", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option4": { + "message": "Has privacy-related issues", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option5": { + "message": "Malfunctions when uBO Lite is enabled", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option6": { + "message": "Opens unwanted tabs or windows", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option7": { + "message": "Leads to badware, phishing", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Checkbox1": { + "message": "Label the webpage as “NSFW” (“Not Safe For Work”)", + "description": "A checkbox to use for NSFW sites" + }, + "supportReportSpecificButton": { + "message": "Create new report", + "description": "Text for button which open an external webpage in Support pane" + }, "firstRunSectionLabel": { "message": "ಸ್ವಾಗತ", "description": "The header text for the welcome message section" diff --git a/platform/mv3/extension/_locales/ko/messages.json b/platform/mv3/extension/_locales/ko/messages.json index d7de8ce1654d5..edd5e747c28d7 100644 --- a/platform/mv3/extension/_locales/ko/messages.json +++ b/platform/mv3/extension/_locales/ko/messages.json @@ -31,6 +31,10 @@ "message": "필터링 모드", "description": "Label in the popup panel for the current filtering mode" }, + "popupTipReport": { + "message": "Report an issue on this website", + "description": "Tooltip used for the 'chat' icon in the panel" + }, "popupTipDashboard": { "message": "대시보드 열기", "description": "English: Click to open the dashboard" @@ -99,6 +103,70 @@ "message": "외부 의존성 (GPLv3 호환):", "description": "Shown in the About pane" }, + "supportS6H": { + "message": "Report a filter issue", + "description": "Header of 'Report a filter issue' section in Support pane" + }, + "supportS3P1": { + "message": "Report filter issues with specific websites to the uBlockOrigin/uAssets issue tracker. Requires a GitHub account.", + "description": "First paragraph of 'Filter issues' section in Support pane" + }, + "supportS6P1S1": { + "message": "To avoid burdening volunteers with duplicate reports, please verify that the issue has not already been reported.", + "description": "A paragraph in the filter issue reporter section" + }, + "supportFindSpecificButton": { + "message": "Find similar reports", + "description": "A clickable link in the filter issue reporter section" + }, + "supportS6URL": { + "message": "Address of the webpage:", + "description": "Label for the URL of the page" + }, + "supportS6Select1": { + "message": "The webpage…", + "description": "Label for widget to select type of issue" + }, + "supportS6Select1Option0": { + "message": "-- Pick an entry --", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option1": { + "message": "Shows ads or ad leftovers", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option2": { + "message": "Has overlays or other nuisances", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option3": { + "message": "Detects uBO Lite", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option4": { + "message": "Has privacy-related issues", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option5": { + "message": "Malfunctions when uBO Lite is enabled", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option6": { + "message": "Opens unwanted tabs or windows", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option7": { + "message": "Leads to badware, phishing", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Checkbox1": { + "message": "Label the webpage as “NSFW” (“Not Safe For Work”)", + "description": "A checkbox to use for NSFW sites" + }, + "supportReportSpecificButton": { + "message": "Create new report", + "description": "Text for button which open an external webpage in Support pane" + }, "firstRunSectionLabel": { "message": "환영합니다", "description": "The header text for the welcome message section" diff --git a/platform/mv3/extension/_locales/lt/messages.json b/platform/mv3/extension/_locales/lt/messages.json index 497cd30837082..d810f531ed358 100644 --- a/platform/mv3/extension/_locales/lt/messages.json +++ b/platform/mv3/extension/_locales/lt/messages.json @@ -31,6 +31,10 @@ "message": "filtering mode", "description": "Label in the popup panel for the current filtering mode" }, + "popupTipReport": { + "message": "Report an issue on this website", + "description": "Tooltip used for the 'chat' icon in the panel" + }, "popupTipDashboard": { "message": "Open the dashboard", "description": "English: Click to open the dashboard" @@ -99,6 +103,70 @@ "message": "Išorinės priklausomybės (suderinamos su „GPLv3“):", "description": "Shown in the About pane" }, + "supportS6H": { + "message": "Report a filter issue", + "description": "Header of 'Report a filter issue' section in Support pane" + }, + "supportS3P1": { + "message": "Report filter issues with specific websites to the uBlockOrigin/uAssets issue tracker. Requires a GitHub account.", + "description": "First paragraph of 'Filter issues' section in Support pane" + }, + "supportS6P1S1": { + "message": "To avoid burdening volunteers with duplicate reports, please verify that the issue has not already been reported.", + "description": "A paragraph in the filter issue reporter section" + }, + "supportFindSpecificButton": { + "message": "Find similar reports", + "description": "A clickable link in the filter issue reporter section" + }, + "supportS6URL": { + "message": "Address of the webpage:", + "description": "Label for the URL of the page" + }, + "supportS6Select1": { + "message": "The webpage…", + "description": "Label for widget to select type of issue" + }, + "supportS6Select1Option0": { + "message": "-- Pick an entry --", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option1": { + "message": "Shows ads or ad leftovers", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option2": { + "message": "Has overlays or other nuisances", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option3": { + "message": "Detects uBO Lite", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option4": { + "message": "Has privacy-related issues", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option5": { + "message": "Malfunctions when uBO Lite is enabled", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option6": { + "message": "Opens unwanted tabs or windows", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option7": { + "message": "Leads to badware, phishing", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Checkbox1": { + "message": "Label the webpage as “NSFW” (“Not Safe For Work”)", + "description": "A checkbox to use for NSFW sites" + }, + "supportReportSpecificButton": { + "message": "Create new report", + "description": "Text for button which open an external webpage in Support pane" + }, "firstRunSectionLabel": { "message": "Welcome", "description": "The header text for the welcome message section" diff --git a/platform/mv3/extension/_locales/lv/messages.json b/platform/mv3/extension/_locales/lv/messages.json index 4514c75b4f19a..810d5a0eab97b 100644 --- a/platform/mv3/extension/_locales/lv/messages.json +++ b/platform/mv3/extension/_locales/lv/messages.json @@ -31,6 +31,10 @@ "message": "aizturēšanas veids", "description": "Label in the popup panel for the current filtering mode" }, + "popupTipReport": { + "message": "Report an issue on this website", + "description": "Tooltip used for the 'chat' icon in the panel" + }, "popupTipDashboard": { "message": "Atvērt vadības paneli", "description": "English: Click to open the dashboard" @@ -99,6 +103,70 @@ "message": "Ārējās atkarības (GPLv3 saderīgas):", "description": "Shown in the About pane" }, + "supportS6H": { + "message": "Report a filter issue", + "description": "Header of 'Report a filter issue' section in Support pane" + }, + "supportS3P1": { + "message": "Report filter issues with specific websites to the uBlockOrigin/uAssets issue tracker. Requires a GitHub account.", + "description": "First paragraph of 'Filter issues' section in Support pane" + }, + "supportS6P1S1": { + "message": "To avoid burdening volunteers with duplicate reports, please verify that the issue has not already been reported.", + "description": "A paragraph in the filter issue reporter section" + }, + "supportFindSpecificButton": { + "message": "Find similar reports", + "description": "A clickable link in the filter issue reporter section" + }, + "supportS6URL": { + "message": "Address of the webpage:", + "description": "Label for the URL of the page" + }, + "supportS6Select1": { + "message": "The webpage…", + "description": "Label for widget to select type of issue" + }, + "supportS6Select1Option0": { + "message": "-- Pick an entry --", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option1": { + "message": "Shows ads or ad leftovers", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option2": { + "message": "Has overlays or other nuisances", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option3": { + "message": "Detects uBO Lite", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option4": { + "message": "Has privacy-related issues", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option5": { + "message": "Malfunctions when uBO Lite is enabled", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option6": { + "message": "Opens unwanted tabs or windows", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option7": { + "message": "Leads to badware, phishing", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Checkbox1": { + "message": "Label the webpage as “NSFW” (“Not Safe For Work”)", + "description": "A checkbox to use for NSFW sites" + }, + "supportReportSpecificButton": { + "message": "Create new report", + "description": "Text for button which open an external webpage in Support pane" + }, "firstRunSectionLabel": { "message": "Sveicināti!", "description": "The header text for the welcome message section" diff --git a/platform/mv3/extension/_locales/mk/messages.json b/platform/mv3/extension/_locales/mk/messages.json index 73d13cc15b35b..06f558231a77c 100644 --- a/platform/mv3/extension/_locales/mk/messages.json +++ b/platform/mv3/extension/_locales/mk/messages.json @@ -31,6 +31,10 @@ "message": "Начини на прочистување", "description": "Label in the popup panel for the current filtering mode" }, + "popupTipReport": { + "message": "Report an issue on this website", + "description": "Tooltip used for the 'chat' icon in the panel" + }, "popupTipDashboard": { "message": "Отварање на Контролна Табла", "description": "English: Click to open the dashboard" @@ -99,6 +103,70 @@ "message": "External dependencies (GPLv3-compatible):", "description": "Shown in the About pane" }, + "supportS6H": { + "message": "Report a filter issue", + "description": "Header of 'Report a filter issue' section in Support pane" + }, + "supportS3P1": { + "message": "Report filter issues with specific websites to the uBlockOrigin/uAssets issue tracker. Requires a GitHub account.", + "description": "First paragraph of 'Filter issues' section in Support pane" + }, + "supportS6P1S1": { + "message": "To avoid burdening volunteers with duplicate reports, please verify that the issue has not already been reported.", + "description": "A paragraph in the filter issue reporter section" + }, + "supportFindSpecificButton": { + "message": "Find similar reports", + "description": "A clickable link in the filter issue reporter section" + }, + "supportS6URL": { + "message": "Address of the webpage:", + "description": "Label for the URL of the page" + }, + "supportS6Select1": { + "message": "The webpage…", + "description": "Label for widget to select type of issue" + }, + "supportS6Select1Option0": { + "message": "-- Pick an entry --", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option1": { + "message": "Shows ads or ad leftovers", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option2": { + "message": "Has overlays or other nuisances", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option3": { + "message": "Detects uBO Lite", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option4": { + "message": "Has privacy-related issues", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option5": { + "message": "Malfunctions when uBO Lite is enabled", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option6": { + "message": "Opens unwanted tabs or windows", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option7": { + "message": "Leads to badware, phishing", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Checkbox1": { + "message": "Label the webpage as “NSFW” (“Not Safe For Work”)", + "description": "A checkbox to use for NSFW sites" + }, + "supportReportSpecificButton": { + "message": "Create new report", + "description": "Text for button which open an external webpage in Support pane" + }, "firstRunSectionLabel": { "message": "Welcome", "description": "The header text for the welcome message section" diff --git a/platform/mv3/extension/_locales/ml/messages.json b/platform/mv3/extension/_locales/ml/messages.json index 42db083577715..a80d375c23054 100644 --- a/platform/mv3/extension/_locales/ml/messages.json +++ b/platform/mv3/extension/_locales/ml/messages.json @@ -31,6 +31,10 @@ "message": "ഫിൽട്ടറിംഗ് മോഡ്", "description": "Label in the popup panel for the current filtering mode" }, + "popupTipReport": { + "message": "Report an issue on this website", + "description": "Tooltip used for the 'chat' icon in the panel" + }, "popupTipDashboard": { "message": "ഡാഷ്ബോർഡ് തുറക്കുക", "description": "English: Click to open the dashboard" @@ -99,6 +103,70 @@ "message": "ബാഹ്യ ഡിപൻഡൻസികൾ (ജിപിൽവി3-അനുയോജ്യമായത്):", "description": "Shown in the About pane" }, + "supportS6H": { + "message": "Report a filter issue", + "description": "Header of 'Report a filter issue' section in Support pane" + }, + "supportS3P1": { + "message": "Report filter issues with specific websites to the uBlockOrigin/uAssets issue tracker. Requires a GitHub account.", + "description": "First paragraph of 'Filter issues' section in Support pane" + }, + "supportS6P1S1": { + "message": "To avoid burdening volunteers with duplicate reports, please verify that the issue has not already been reported.", + "description": "A paragraph in the filter issue reporter section" + }, + "supportFindSpecificButton": { + "message": "Find similar reports", + "description": "A clickable link in the filter issue reporter section" + }, + "supportS6URL": { + "message": "Address of the webpage:", + "description": "Label for the URL of the page" + }, + "supportS6Select1": { + "message": "The webpage…", + "description": "Label for widget to select type of issue" + }, + "supportS6Select1Option0": { + "message": "-- Pick an entry --", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option1": { + "message": "Shows ads or ad leftovers", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option2": { + "message": "Has overlays or other nuisances", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option3": { + "message": "Detects uBO Lite", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option4": { + "message": "Has privacy-related issues", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option5": { + "message": "Malfunctions when uBO Lite is enabled", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option6": { + "message": "Opens unwanted tabs or windows", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option7": { + "message": "Leads to badware, phishing", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Checkbox1": { + "message": "Label the webpage as “NSFW” (“Not Safe For Work”)", + "description": "A checkbox to use for NSFW sites" + }, + "supportReportSpecificButton": { + "message": "Create new report", + "description": "Text for button which open an external webpage in Support pane" + }, "firstRunSectionLabel": { "message": "സ്വാഗതം", "description": "The header text for the welcome message section" diff --git a/platform/mv3/extension/_locales/mr/messages.json b/platform/mv3/extension/_locales/mr/messages.json index 30c719d52722d..c5f77c667a506 100644 --- a/platform/mv3/extension/_locales/mr/messages.json +++ b/platform/mv3/extension/_locales/mr/messages.json @@ -31,6 +31,10 @@ "message": "filtering mode", "description": "Label in the popup panel for the current filtering mode" }, + "popupTipReport": { + "message": "Report an issue on this website", + "description": "Tooltip used for the 'chat' icon in the panel" + }, "popupTipDashboard": { "message": "Open the dashboard", "description": "English: Click to open the dashboard" @@ -99,6 +103,70 @@ "message": "External dependencies (GPLv3-compatible):", "description": "Shown in the About pane" }, + "supportS6H": { + "message": "Report a filter issue", + "description": "Header of 'Report a filter issue' section in Support pane" + }, + "supportS3P1": { + "message": "Report filter issues with specific websites to the uBlockOrigin/uAssets issue tracker. Requires a GitHub account.", + "description": "First paragraph of 'Filter issues' section in Support pane" + }, + "supportS6P1S1": { + "message": "To avoid burdening volunteers with duplicate reports, please verify that the issue has not already been reported.", + "description": "A paragraph in the filter issue reporter section" + }, + "supportFindSpecificButton": { + "message": "Find similar reports", + "description": "A clickable link in the filter issue reporter section" + }, + "supportS6URL": { + "message": "Address of the webpage:", + "description": "Label for the URL of the page" + }, + "supportS6Select1": { + "message": "The webpage…", + "description": "Label for widget to select type of issue" + }, + "supportS6Select1Option0": { + "message": "-- Pick an entry --", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option1": { + "message": "Shows ads or ad leftovers", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option2": { + "message": "Has overlays or other nuisances", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option3": { + "message": "Detects uBO Lite", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option4": { + "message": "Has privacy-related issues", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option5": { + "message": "Malfunctions when uBO Lite is enabled", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option6": { + "message": "Opens unwanted tabs or windows", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option7": { + "message": "Leads to badware, phishing", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Checkbox1": { + "message": "Label the webpage as “NSFW” (“Not Safe For Work”)", + "description": "A checkbox to use for NSFW sites" + }, + "supportReportSpecificButton": { + "message": "Create new report", + "description": "Text for button which open an external webpage in Support pane" + }, "firstRunSectionLabel": { "message": "Welcome", "description": "The header text for the welcome message section" diff --git a/platform/mv3/extension/_locales/ms/messages.json b/platform/mv3/extension/_locales/ms/messages.json index 71f193001665a..6b015e7a9b1a6 100644 --- a/platform/mv3/extension/_locales/ms/messages.json +++ b/platform/mv3/extension/_locales/ms/messages.json @@ -31,6 +31,10 @@ "message": "mod penapisan", "description": "Label in the popup panel for the current filtering mode" }, + "popupTipReport": { + "message": "Report an issue on this website", + "description": "Tooltip used for the 'chat' icon in the panel" + }, "popupTipDashboard": { "message": "Buka papan pemuka", "description": "English: Click to open the dashboard" @@ -99,6 +103,70 @@ "message": "Pergantungan luaran (serasi dengan GPLv3):", "description": "Shown in the About pane" }, + "supportS6H": { + "message": "Report a filter issue", + "description": "Header of 'Report a filter issue' section in Support pane" + }, + "supportS3P1": { + "message": "Report filter issues with specific websites to the uBlockOrigin/uAssets issue tracker. Requires a GitHub account.", + "description": "First paragraph of 'Filter issues' section in Support pane" + }, + "supportS6P1S1": { + "message": "To avoid burdening volunteers with duplicate reports, please verify that the issue has not already been reported.", + "description": "A paragraph in the filter issue reporter section" + }, + "supportFindSpecificButton": { + "message": "Find similar reports", + "description": "A clickable link in the filter issue reporter section" + }, + "supportS6URL": { + "message": "Address of the webpage:", + "description": "Label for the URL of the page" + }, + "supportS6Select1": { + "message": "The webpage…", + "description": "Label for widget to select type of issue" + }, + "supportS6Select1Option0": { + "message": "-- Pick an entry --", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option1": { + "message": "Shows ads or ad leftovers", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option2": { + "message": "Has overlays or other nuisances", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option3": { + "message": "Detects uBO Lite", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option4": { + "message": "Has privacy-related issues", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option5": { + "message": "Malfunctions when uBO Lite is enabled", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option6": { + "message": "Opens unwanted tabs or windows", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option7": { + "message": "Leads to badware, phishing", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Checkbox1": { + "message": "Label the webpage as “NSFW” (“Not Safe For Work”)", + "description": "A checkbox to use for NSFW sites" + }, + "supportReportSpecificButton": { + "message": "Create new report", + "description": "Text for button which open an external webpage in Support pane" + }, "firstRunSectionLabel": { "message": "Selamat datang", "description": "The header text for the welcome message section" diff --git a/platform/mv3/extension/_locales/nb/messages.json b/platform/mv3/extension/_locales/nb/messages.json index 44234c059857e..4fd7bd3d1135c 100644 --- a/platform/mv3/extension/_locales/nb/messages.json +++ b/platform/mv3/extension/_locales/nb/messages.json @@ -31,6 +31,10 @@ "message": "filtreringsmodus", "description": "Label in the popup panel for the current filtering mode" }, + "popupTipReport": { + "message": "Report an issue on this website", + "description": "Tooltip used for the 'chat' icon in the panel" + }, "popupTipDashboard": { "message": "Åpne dashbordet", "description": "English: Click to open the dashboard" @@ -99,6 +103,70 @@ "message": "Eksterne avhengigheter (GPLv3-kompatible):", "description": "Shown in the About pane" }, + "supportS6H": { + "message": "Report a filter issue", + "description": "Header of 'Report a filter issue' section in Support pane" + }, + "supportS3P1": { + "message": "Report filter issues with specific websites to the uBlockOrigin/uAssets issue tracker. Requires a GitHub account.", + "description": "First paragraph of 'Filter issues' section in Support pane" + }, + "supportS6P1S1": { + "message": "To avoid burdening volunteers with duplicate reports, please verify that the issue has not already been reported.", + "description": "A paragraph in the filter issue reporter section" + }, + "supportFindSpecificButton": { + "message": "Find similar reports", + "description": "A clickable link in the filter issue reporter section" + }, + "supportS6URL": { + "message": "Address of the webpage:", + "description": "Label for the URL of the page" + }, + "supportS6Select1": { + "message": "The webpage…", + "description": "Label for widget to select type of issue" + }, + "supportS6Select1Option0": { + "message": "-- Pick an entry --", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option1": { + "message": "Shows ads or ad leftovers", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option2": { + "message": "Has overlays or other nuisances", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option3": { + "message": "Detects uBO Lite", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option4": { + "message": "Has privacy-related issues", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option5": { + "message": "Malfunctions when uBO Lite is enabled", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option6": { + "message": "Opens unwanted tabs or windows", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option7": { + "message": "Leads to badware, phishing", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Checkbox1": { + "message": "Label the webpage as “NSFW” (“Not Safe For Work”)", + "description": "A checkbox to use for NSFW sites" + }, + "supportReportSpecificButton": { + "message": "Create new report", + "description": "Text for button which open an external webpage in Support pane" + }, "firstRunSectionLabel": { "message": "Velkommen", "description": "The header text for the welcome message section" diff --git a/platform/mv3/extension/_locales/nl/messages.json b/platform/mv3/extension/_locales/nl/messages.json index a32f81e591895..1044db20c2840 100644 --- a/platform/mv3/extension/_locales/nl/messages.json +++ b/platform/mv3/extension/_locales/nl/messages.json @@ -31,6 +31,10 @@ "message": "filtermodus", "description": "Label in the popup panel for the current filtering mode" }, + "popupTipReport": { + "message": "Report an issue on this website", + "description": "Tooltip used for the 'chat' icon in the panel" + }, "popupTipDashboard": { "message": "Dashboard openen", "description": "English: Click to open the dashboard" @@ -99,6 +103,70 @@ "message": "Externe afhankelijkheden (GPLv3-compatibel):", "description": "Shown in the About pane" }, + "supportS6H": { + "message": "Report a filter issue", + "description": "Header of 'Report a filter issue' section in Support pane" + }, + "supportS3P1": { + "message": "Report filter issues with specific websites to the uBlockOrigin/uAssets issue tracker. Requires a GitHub account.", + "description": "First paragraph of 'Filter issues' section in Support pane" + }, + "supportS6P1S1": { + "message": "To avoid burdening volunteers with duplicate reports, please verify that the issue has not already been reported.", + "description": "A paragraph in the filter issue reporter section" + }, + "supportFindSpecificButton": { + "message": "Find similar reports", + "description": "A clickable link in the filter issue reporter section" + }, + "supportS6URL": { + "message": "Address of the webpage:", + "description": "Label for the URL of the page" + }, + "supportS6Select1": { + "message": "The webpage…", + "description": "Label for widget to select type of issue" + }, + "supportS6Select1Option0": { + "message": "-- Pick an entry --", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option1": { + "message": "Shows ads or ad leftovers", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option2": { + "message": "Has overlays or other nuisances", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option3": { + "message": "Detects uBO Lite", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option4": { + "message": "Has privacy-related issues", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option5": { + "message": "Malfunctions when uBO Lite is enabled", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option6": { + "message": "Opens unwanted tabs or windows", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option7": { + "message": "Leads to badware, phishing", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Checkbox1": { + "message": "Label the webpage as “NSFW” (“Not Safe For Work”)", + "description": "A checkbox to use for NSFW sites" + }, + "supportReportSpecificButton": { + "message": "Create new report", + "description": "Text for button which open an external webpage in Support pane" + }, "firstRunSectionLabel": { "message": "Welkom", "description": "The header text for the welcome message section" diff --git a/platform/mv3/extension/_locales/oc/messages.json b/platform/mv3/extension/_locales/oc/messages.json index 30c719d52722d..c5f77c667a506 100644 --- a/platform/mv3/extension/_locales/oc/messages.json +++ b/platform/mv3/extension/_locales/oc/messages.json @@ -31,6 +31,10 @@ "message": "filtering mode", "description": "Label in the popup panel for the current filtering mode" }, + "popupTipReport": { + "message": "Report an issue on this website", + "description": "Tooltip used for the 'chat' icon in the panel" + }, "popupTipDashboard": { "message": "Open the dashboard", "description": "English: Click to open the dashboard" @@ -99,6 +103,70 @@ "message": "External dependencies (GPLv3-compatible):", "description": "Shown in the About pane" }, + "supportS6H": { + "message": "Report a filter issue", + "description": "Header of 'Report a filter issue' section in Support pane" + }, + "supportS3P1": { + "message": "Report filter issues with specific websites to the uBlockOrigin/uAssets issue tracker. Requires a GitHub account.", + "description": "First paragraph of 'Filter issues' section in Support pane" + }, + "supportS6P1S1": { + "message": "To avoid burdening volunteers with duplicate reports, please verify that the issue has not already been reported.", + "description": "A paragraph in the filter issue reporter section" + }, + "supportFindSpecificButton": { + "message": "Find similar reports", + "description": "A clickable link in the filter issue reporter section" + }, + "supportS6URL": { + "message": "Address of the webpage:", + "description": "Label for the URL of the page" + }, + "supportS6Select1": { + "message": "The webpage…", + "description": "Label for widget to select type of issue" + }, + "supportS6Select1Option0": { + "message": "-- Pick an entry --", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option1": { + "message": "Shows ads or ad leftovers", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option2": { + "message": "Has overlays or other nuisances", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option3": { + "message": "Detects uBO Lite", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option4": { + "message": "Has privacy-related issues", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option5": { + "message": "Malfunctions when uBO Lite is enabled", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option6": { + "message": "Opens unwanted tabs or windows", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option7": { + "message": "Leads to badware, phishing", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Checkbox1": { + "message": "Label the webpage as “NSFW” (“Not Safe For Work”)", + "description": "A checkbox to use for NSFW sites" + }, + "supportReportSpecificButton": { + "message": "Create new report", + "description": "Text for button which open an external webpage in Support pane" + }, "firstRunSectionLabel": { "message": "Welcome", "description": "The header text for the welcome message section" diff --git a/platform/mv3/extension/_locales/pa/messages.json b/platform/mv3/extension/_locales/pa/messages.json index aff262e364ead..e3524501bce67 100644 --- a/platform/mv3/extension/_locales/pa/messages.json +++ b/platform/mv3/extension/_locales/pa/messages.json @@ -31,6 +31,10 @@ "message": "ਫਿਲਟਰ ਕਰਨ ਦਾ ਮੋਡ", "description": "Label in the popup panel for the current filtering mode" }, + "popupTipReport": { + "message": "Report an issue on this website", + "description": "Tooltip used for the 'chat' icon in the panel" + }, "popupTipDashboard": { "message": "ਡੈਸ਼ਬੋਰਡ ਨੂੰ ਖੋਲ੍ਹੋ", "description": "English: Click to open the dashboard" @@ -99,6 +103,70 @@ "message": "ਬਾਹਰੀ ਨਿਰਭਰਤਾਵਾਂ (GPLv3-ਅਨੁਕੂਲ):", "description": "Shown in the About pane" }, + "supportS6H": { + "message": "Report a filter issue", + "description": "Header of 'Report a filter issue' section in Support pane" + }, + "supportS3P1": { + "message": "Report filter issues with specific websites to the uBlockOrigin/uAssets issue tracker. Requires a GitHub account.", + "description": "First paragraph of 'Filter issues' section in Support pane" + }, + "supportS6P1S1": { + "message": "To avoid burdening volunteers with duplicate reports, please verify that the issue has not already been reported.", + "description": "A paragraph in the filter issue reporter section" + }, + "supportFindSpecificButton": { + "message": "Find similar reports", + "description": "A clickable link in the filter issue reporter section" + }, + "supportS6URL": { + "message": "Address of the webpage:", + "description": "Label for the URL of the page" + }, + "supportS6Select1": { + "message": "The webpage…", + "description": "Label for widget to select type of issue" + }, + "supportS6Select1Option0": { + "message": "-- Pick an entry --", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option1": { + "message": "Shows ads or ad leftovers", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option2": { + "message": "Has overlays or other nuisances", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option3": { + "message": "Detects uBO Lite", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option4": { + "message": "Has privacy-related issues", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option5": { + "message": "Malfunctions when uBO Lite is enabled", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option6": { + "message": "Opens unwanted tabs or windows", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option7": { + "message": "Leads to badware, phishing", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Checkbox1": { + "message": "Label the webpage as “NSFW” (“Not Safe For Work”)", + "description": "A checkbox to use for NSFW sites" + }, + "supportReportSpecificButton": { + "message": "Create new report", + "description": "Text for button which open an external webpage in Support pane" + }, "firstRunSectionLabel": { "message": "ਜੀ ਆਇਆਂ ਨੂੰ", "description": "The header text for the welcome message section" diff --git a/platform/mv3/extension/_locales/pl/messages.json b/platform/mv3/extension/_locales/pl/messages.json index 61a1b8eb16fe5..6aa14a77ebcaa 100644 --- a/platform/mv3/extension/_locales/pl/messages.json +++ b/platform/mv3/extension/_locales/pl/messages.json @@ -31,6 +31,10 @@ "message": "Tryb filtrowania", "description": "Label in the popup panel for the current filtering mode" }, + "popupTipReport": { + "message": "Report an issue on this website", + "description": "Tooltip used for the 'chat' icon in the panel" + }, "popupTipDashboard": { "message": "Otwórz panel sterowania", "description": "English: Click to open the dashboard" @@ -99,6 +103,70 @@ "message": "Zewnętrzne zależności (kompatybilne z GPLv3):", "description": "Shown in the About pane" }, + "supportS6H": { + "message": "Report a filter issue", + "description": "Header of 'Report a filter issue' section in Support pane" + }, + "supportS3P1": { + "message": "Report filter issues with specific websites to the uBlockOrigin/uAssets issue tracker. Requires a GitHub account.", + "description": "First paragraph of 'Filter issues' section in Support pane" + }, + "supportS6P1S1": { + "message": "To avoid burdening volunteers with duplicate reports, please verify that the issue has not already been reported.", + "description": "A paragraph in the filter issue reporter section" + }, + "supportFindSpecificButton": { + "message": "Find similar reports", + "description": "A clickable link in the filter issue reporter section" + }, + "supportS6URL": { + "message": "Address of the webpage:", + "description": "Label for the URL of the page" + }, + "supportS6Select1": { + "message": "The webpage…", + "description": "Label for widget to select type of issue" + }, + "supportS6Select1Option0": { + "message": "-- Pick an entry --", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option1": { + "message": "Shows ads or ad leftovers", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option2": { + "message": "Has overlays or other nuisances", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option3": { + "message": "Detects uBO Lite", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option4": { + "message": "Has privacy-related issues", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option5": { + "message": "Malfunctions when uBO Lite is enabled", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option6": { + "message": "Opens unwanted tabs or windows", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option7": { + "message": "Leads to badware, phishing", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Checkbox1": { + "message": "Label the webpage as “NSFW” (“Not Safe For Work”)", + "description": "A checkbox to use for NSFW sites" + }, + "supportReportSpecificButton": { + "message": "Create new report", + "description": "Text for button which open an external webpage in Support pane" + }, "firstRunSectionLabel": { "message": "Witaj", "description": "The header text for the welcome message section" diff --git a/platform/mv3/extension/_locales/pt_BR/messages.json b/platform/mv3/extension/_locales/pt_BR/messages.json index f42f4b5a6ff3b..52757d7a8f719 100644 --- a/platform/mv3/extension/_locales/pt_BR/messages.json +++ b/platform/mv3/extension/_locales/pt_BR/messages.json @@ -31,6 +31,10 @@ "message": "modo de filtragem", "description": "Label in the popup panel for the current filtering mode" }, + "popupTipReport": { + "message": "Report an issue on this website", + "description": "Tooltip used for the 'chat' icon in the panel" + }, "popupTipDashboard": { "message": "Abrir painel", "description": "English: Click to open the dashboard" @@ -99,6 +103,70 @@ "message": "Dependências externas (compatíveis com GPLv3):", "description": "Shown in the About pane" }, + "supportS6H": { + "message": "Report a filter issue", + "description": "Header of 'Report a filter issue' section in Support pane" + }, + "supportS3P1": { + "message": "Report filter issues with specific websites to the uBlockOrigin/uAssets issue tracker. Requires a GitHub account.", + "description": "First paragraph of 'Filter issues' section in Support pane" + }, + "supportS6P1S1": { + "message": "To avoid burdening volunteers with duplicate reports, please verify that the issue has not already been reported.", + "description": "A paragraph in the filter issue reporter section" + }, + "supportFindSpecificButton": { + "message": "Find similar reports", + "description": "A clickable link in the filter issue reporter section" + }, + "supportS6URL": { + "message": "Address of the webpage:", + "description": "Label for the URL of the page" + }, + "supportS6Select1": { + "message": "The webpage…", + "description": "Label for widget to select type of issue" + }, + "supportS6Select1Option0": { + "message": "-- Pick an entry --", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option1": { + "message": "Shows ads or ad leftovers", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option2": { + "message": "Has overlays or other nuisances", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option3": { + "message": "Detects uBO Lite", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option4": { + "message": "Has privacy-related issues", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option5": { + "message": "Malfunctions when uBO Lite is enabled", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option6": { + "message": "Opens unwanted tabs or windows", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option7": { + "message": "Leads to badware, phishing", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Checkbox1": { + "message": "Label the webpage as “NSFW” (“Not Safe For Work”)", + "description": "A checkbox to use for NSFW sites" + }, + "supportReportSpecificButton": { + "message": "Create new report", + "description": "Text for button which open an external webpage in Support pane" + }, "firstRunSectionLabel": { "message": "Bem-vindo", "description": "The header text for the welcome message section" diff --git a/platform/mv3/extension/_locales/pt_PT/messages.json b/platform/mv3/extension/_locales/pt_PT/messages.json index a0cd625bda185..a6bc083461719 100644 --- a/platform/mv3/extension/_locales/pt_PT/messages.json +++ b/platform/mv3/extension/_locales/pt_PT/messages.json @@ -31,6 +31,10 @@ "message": "modo de filtragem", "description": "Label in the popup panel for the current filtering mode" }, + "popupTipReport": { + "message": "Report an issue on this website", + "description": "Tooltip used for the 'chat' icon in the panel" + }, "popupTipDashboard": { "message": "Abrir o painel de controlo", "description": "English: Click to open the dashboard" @@ -99,6 +103,70 @@ "message": "Dependências externas (compatíveis com GPLv3):", "description": "Shown in the About pane" }, + "supportS6H": { + "message": "Report a filter issue", + "description": "Header of 'Report a filter issue' section in Support pane" + }, + "supportS3P1": { + "message": "Report filter issues with specific websites to the uBlockOrigin/uAssets issue tracker. Requires a GitHub account.", + "description": "First paragraph of 'Filter issues' section in Support pane" + }, + "supportS6P1S1": { + "message": "To avoid burdening volunteers with duplicate reports, please verify that the issue has not already been reported.", + "description": "A paragraph in the filter issue reporter section" + }, + "supportFindSpecificButton": { + "message": "Find similar reports", + "description": "A clickable link in the filter issue reporter section" + }, + "supportS6URL": { + "message": "Address of the webpage:", + "description": "Label for the URL of the page" + }, + "supportS6Select1": { + "message": "The webpage…", + "description": "Label for widget to select type of issue" + }, + "supportS6Select1Option0": { + "message": "-- Pick an entry --", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option1": { + "message": "Shows ads or ad leftovers", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option2": { + "message": "Has overlays or other nuisances", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option3": { + "message": "Detects uBO Lite", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option4": { + "message": "Has privacy-related issues", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option5": { + "message": "Malfunctions when uBO Lite is enabled", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option6": { + "message": "Opens unwanted tabs or windows", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option7": { + "message": "Leads to badware, phishing", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Checkbox1": { + "message": "Label the webpage as “NSFW” (“Not Safe For Work”)", + "description": "A checkbox to use for NSFW sites" + }, + "supportReportSpecificButton": { + "message": "Create new report", + "description": "Text for button which open an external webpage in Support pane" + }, "firstRunSectionLabel": { "message": "Bem-vindo(a)", "description": "The header text for the welcome message section" diff --git a/platform/mv3/extension/_locales/ro/messages.json b/platform/mv3/extension/_locales/ro/messages.json index 4502d6086b52a..38939b3439529 100644 --- a/platform/mv3/extension/_locales/ro/messages.json +++ b/platform/mv3/extension/_locales/ro/messages.json @@ -31,6 +31,10 @@ "message": "Mod de filtrare", "description": "Label in the popup panel for the current filtering mode" }, + "popupTipReport": { + "message": "Report an issue on this website", + "description": "Tooltip used for the 'chat' icon in the panel" + }, "popupTipDashboard": { "message": "Deschide panoul de control", "description": "English: Click to open the dashboard" @@ -99,6 +103,70 @@ "message": "Dependențe externe (compatibile GPLv3):", "description": "Shown in the About pane" }, + "supportS6H": { + "message": "Report a filter issue", + "description": "Header of 'Report a filter issue' section in Support pane" + }, + "supportS3P1": { + "message": "Report filter issues with specific websites to the uBlockOrigin/uAssets issue tracker. Requires a GitHub account.", + "description": "First paragraph of 'Filter issues' section in Support pane" + }, + "supportS6P1S1": { + "message": "To avoid burdening volunteers with duplicate reports, please verify that the issue has not already been reported.", + "description": "A paragraph in the filter issue reporter section" + }, + "supportFindSpecificButton": { + "message": "Find similar reports", + "description": "A clickable link in the filter issue reporter section" + }, + "supportS6URL": { + "message": "Address of the webpage:", + "description": "Label for the URL of the page" + }, + "supportS6Select1": { + "message": "The webpage…", + "description": "Label for widget to select type of issue" + }, + "supportS6Select1Option0": { + "message": "-- Pick an entry --", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option1": { + "message": "Shows ads or ad leftovers", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option2": { + "message": "Has overlays or other nuisances", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option3": { + "message": "Detects uBO Lite", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option4": { + "message": "Has privacy-related issues", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option5": { + "message": "Malfunctions when uBO Lite is enabled", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option6": { + "message": "Opens unwanted tabs or windows", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option7": { + "message": "Leads to badware, phishing", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Checkbox1": { + "message": "Label the webpage as “NSFW” (“Not Safe For Work”)", + "description": "A checkbox to use for NSFW sites" + }, + "supportReportSpecificButton": { + "message": "Create new report", + "description": "Text for button which open an external webpage in Support pane" + }, "firstRunSectionLabel": { "message": "Bun venit", "description": "The header text for the welcome message section" diff --git a/platform/mv3/extension/_locales/ru/messages.json b/platform/mv3/extension/_locales/ru/messages.json index 63fee1591a597..f6074ff68faa6 100644 --- a/platform/mv3/extension/_locales/ru/messages.json +++ b/platform/mv3/extension/_locales/ru/messages.json @@ -31,6 +31,10 @@ "message": "режим фильтрации", "description": "Label in the popup panel for the current filtering mode" }, + "popupTipReport": { + "message": "Report an issue on this website", + "description": "Tooltip used for the 'chat' icon in the panel" + }, "popupTipDashboard": { "message": "Открыть панель управления", "description": "English: Click to open the dashboard" @@ -99,6 +103,70 @@ "message": "Внешние зависимости (совместимые с GPLv3):", "description": "Shown in the About pane" }, + "supportS6H": { + "message": "Report a filter issue", + "description": "Header of 'Report a filter issue' section in Support pane" + }, + "supportS3P1": { + "message": "Report filter issues with specific websites to the uBlockOrigin/uAssets issue tracker. Requires a GitHub account.", + "description": "First paragraph of 'Filter issues' section in Support pane" + }, + "supportS6P1S1": { + "message": "To avoid burdening volunteers with duplicate reports, please verify that the issue has not already been reported.", + "description": "A paragraph in the filter issue reporter section" + }, + "supportFindSpecificButton": { + "message": "Find similar reports", + "description": "A clickable link in the filter issue reporter section" + }, + "supportS6URL": { + "message": "Address of the webpage:", + "description": "Label for the URL of the page" + }, + "supportS6Select1": { + "message": "The webpage…", + "description": "Label for widget to select type of issue" + }, + "supportS6Select1Option0": { + "message": "-- Pick an entry --", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option1": { + "message": "Shows ads or ad leftovers", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option2": { + "message": "Has overlays or other nuisances", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option3": { + "message": "Detects uBO Lite", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option4": { + "message": "Has privacy-related issues", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option5": { + "message": "Malfunctions when uBO Lite is enabled", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option6": { + "message": "Opens unwanted tabs or windows", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option7": { + "message": "Leads to badware, phishing", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Checkbox1": { + "message": "Label the webpage as “NSFW” (“Not Safe For Work”)", + "description": "A checkbox to use for NSFW sites" + }, + "supportReportSpecificButton": { + "message": "Create new report", + "description": "Text for button which open an external webpage in Support pane" + }, "firstRunSectionLabel": { "message": "Добро пожаловать", "description": "The header text for the welcome message section" diff --git a/platform/mv3/extension/_locales/si/messages.json b/platform/mv3/extension/_locales/si/messages.json index bc0e73fc0ddc2..2fa20d00c50e6 100644 --- a/platform/mv3/extension/_locales/si/messages.json +++ b/platform/mv3/extension/_locales/si/messages.json @@ -31,6 +31,10 @@ "message": "filtering mode", "description": "Label in the popup panel for the current filtering mode" }, + "popupTipReport": { + "message": "Report an issue on this website", + "description": "Tooltip used for the 'chat' icon in the panel" + }, "popupTipDashboard": { "message": "Open the dashboard", "description": "English: Click to open the dashboard" @@ -99,6 +103,70 @@ "message": "External dependencies (GPLv3-compatible):", "description": "Shown in the About pane" }, + "supportS6H": { + "message": "Report a filter issue", + "description": "Header of 'Report a filter issue' section in Support pane" + }, + "supportS3P1": { + "message": "Report filter issues with specific websites to the uBlockOrigin/uAssets issue tracker. Requires a GitHub account.", + "description": "First paragraph of 'Filter issues' section in Support pane" + }, + "supportS6P1S1": { + "message": "To avoid burdening volunteers with duplicate reports, please verify that the issue has not already been reported.", + "description": "A paragraph in the filter issue reporter section" + }, + "supportFindSpecificButton": { + "message": "Find similar reports", + "description": "A clickable link in the filter issue reporter section" + }, + "supportS6URL": { + "message": "Address of the webpage:", + "description": "Label for the URL of the page" + }, + "supportS6Select1": { + "message": "The webpage…", + "description": "Label for widget to select type of issue" + }, + "supportS6Select1Option0": { + "message": "-- Pick an entry --", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option1": { + "message": "Shows ads or ad leftovers", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option2": { + "message": "Has overlays or other nuisances", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option3": { + "message": "Detects uBO Lite", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option4": { + "message": "Has privacy-related issues", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option5": { + "message": "Malfunctions when uBO Lite is enabled", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option6": { + "message": "Opens unwanted tabs or windows", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option7": { + "message": "Leads to badware, phishing", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Checkbox1": { + "message": "Label the webpage as “NSFW” (“Not Safe For Work”)", + "description": "A checkbox to use for NSFW sites" + }, + "supportReportSpecificButton": { + "message": "Create new report", + "description": "Text for button which open an external webpage in Support pane" + }, "firstRunSectionLabel": { "message": "Welcome", "description": "The header text for the welcome message section" diff --git a/platform/mv3/extension/_locales/sk/messages.json b/platform/mv3/extension/_locales/sk/messages.json index b4ad74ba2fecc..cceb6c88b6132 100644 --- a/platform/mv3/extension/_locales/sk/messages.json +++ b/platform/mv3/extension/_locales/sk/messages.json @@ -31,6 +31,10 @@ "message": "Režim filtrovania", "description": "Label in the popup panel for the current filtering mode" }, + "popupTipReport": { + "message": "Report an issue on this website", + "description": "Tooltip used for the 'chat' icon in the panel" + }, "popupTipDashboard": { "message": "Otvoriť ovládací panel", "description": "English: Click to open the dashboard" @@ -99,6 +103,70 @@ "message": "Externé závislosti (kompatibilné s GPLv3):", "description": "Shown in the About pane" }, + "supportS6H": { + "message": "Report a filter issue", + "description": "Header of 'Report a filter issue' section in Support pane" + }, + "supportS3P1": { + "message": "Report filter issues with specific websites to the uBlockOrigin/uAssets issue tracker. Requires a GitHub account.", + "description": "First paragraph of 'Filter issues' section in Support pane" + }, + "supportS6P1S1": { + "message": "To avoid burdening volunteers with duplicate reports, please verify that the issue has not already been reported.", + "description": "A paragraph in the filter issue reporter section" + }, + "supportFindSpecificButton": { + "message": "Find similar reports", + "description": "A clickable link in the filter issue reporter section" + }, + "supportS6URL": { + "message": "Address of the webpage:", + "description": "Label for the URL of the page" + }, + "supportS6Select1": { + "message": "The webpage…", + "description": "Label for widget to select type of issue" + }, + "supportS6Select1Option0": { + "message": "-- Pick an entry --", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option1": { + "message": "Shows ads or ad leftovers", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option2": { + "message": "Has overlays or other nuisances", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option3": { + "message": "Detects uBO Lite", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option4": { + "message": "Has privacy-related issues", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option5": { + "message": "Malfunctions when uBO Lite is enabled", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option6": { + "message": "Opens unwanted tabs or windows", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option7": { + "message": "Leads to badware, phishing", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Checkbox1": { + "message": "Label the webpage as “NSFW” (“Not Safe For Work”)", + "description": "A checkbox to use for NSFW sites" + }, + "supportReportSpecificButton": { + "message": "Create new report", + "description": "Text for button which open an external webpage in Support pane" + }, "firstRunSectionLabel": { "message": "Vitajte", "description": "The header text for the welcome message section" diff --git a/platform/mv3/extension/_locales/sl/messages.json b/platform/mv3/extension/_locales/sl/messages.json index 30c719d52722d..c5f77c667a506 100644 --- a/platform/mv3/extension/_locales/sl/messages.json +++ b/platform/mv3/extension/_locales/sl/messages.json @@ -31,6 +31,10 @@ "message": "filtering mode", "description": "Label in the popup panel for the current filtering mode" }, + "popupTipReport": { + "message": "Report an issue on this website", + "description": "Tooltip used for the 'chat' icon in the panel" + }, "popupTipDashboard": { "message": "Open the dashboard", "description": "English: Click to open the dashboard" @@ -99,6 +103,70 @@ "message": "External dependencies (GPLv3-compatible):", "description": "Shown in the About pane" }, + "supportS6H": { + "message": "Report a filter issue", + "description": "Header of 'Report a filter issue' section in Support pane" + }, + "supportS3P1": { + "message": "Report filter issues with specific websites to the uBlockOrigin/uAssets issue tracker. Requires a GitHub account.", + "description": "First paragraph of 'Filter issues' section in Support pane" + }, + "supportS6P1S1": { + "message": "To avoid burdening volunteers with duplicate reports, please verify that the issue has not already been reported.", + "description": "A paragraph in the filter issue reporter section" + }, + "supportFindSpecificButton": { + "message": "Find similar reports", + "description": "A clickable link in the filter issue reporter section" + }, + "supportS6URL": { + "message": "Address of the webpage:", + "description": "Label for the URL of the page" + }, + "supportS6Select1": { + "message": "The webpage…", + "description": "Label for widget to select type of issue" + }, + "supportS6Select1Option0": { + "message": "-- Pick an entry --", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option1": { + "message": "Shows ads or ad leftovers", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option2": { + "message": "Has overlays or other nuisances", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option3": { + "message": "Detects uBO Lite", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option4": { + "message": "Has privacy-related issues", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option5": { + "message": "Malfunctions when uBO Lite is enabled", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option6": { + "message": "Opens unwanted tabs or windows", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option7": { + "message": "Leads to badware, phishing", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Checkbox1": { + "message": "Label the webpage as “NSFW” (“Not Safe For Work”)", + "description": "A checkbox to use for NSFW sites" + }, + "supportReportSpecificButton": { + "message": "Create new report", + "description": "Text for button which open an external webpage in Support pane" + }, "firstRunSectionLabel": { "message": "Welcome", "description": "The header text for the welcome message section" diff --git a/platform/mv3/extension/_locales/so/messages.json b/platform/mv3/extension/_locales/so/messages.json index 30c719d52722d..c5f77c667a506 100644 --- a/platform/mv3/extension/_locales/so/messages.json +++ b/platform/mv3/extension/_locales/so/messages.json @@ -31,6 +31,10 @@ "message": "filtering mode", "description": "Label in the popup panel for the current filtering mode" }, + "popupTipReport": { + "message": "Report an issue on this website", + "description": "Tooltip used for the 'chat' icon in the panel" + }, "popupTipDashboard": { "message": "Open the dashboard", "description": "English: Click to open the dashboard" @@ -99,6 +103,70 @@ "message": "External dependencies (GPLv3-compatible):", "description": "Shown in the About pane" }, + "supportS6H": { + "message": "Report a filter issue", + "description": "Header of 'Report a filter issue' section in Support pane" + }, + "supportS3P1": { + "message": "Report filter issues with specific websites to the uBlockOrigin/uAssets issue tracker. Requires a GitHub account.", + "description": "First paragraph of 'Filter issues' section in Support pane" + }, + "supportS6P1S1": { + "message": "To avoid burdening volunteers with duplicate reports, please verify that the issue has not already been reported.", + "description": "A paragraph in the filter issue reporter section" + }, + "supportFindSpecificButton": { + "message": "Find similar reports", + "description": "A clickable link in the filter issue reporter section" + }, + "supportS6URL": { + "message": "Address of the webpage:", + "description": "Label for the URL of the page" + }, + "supportS6Select1": { + "message": "The webpage…", + "description": "Label for widget to select type of issue" + }, + "supportS6Select1Option0": { + "message": "-- Pick an entry --", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option1": { + "message": "Shows ads or ad leftovers", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option2": { + "message": "Has overlays or other nuisances", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option3": { + "message": "Detects uBO Lite", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option4": { + "message": "Has privacy-related issues", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option5": { + "message": "Malfunctions when uBO Lite is enabled", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option6": { + "message": "Opens unwanted tabs or windows", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option7": { + "message": "Leads to badware, phishing", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Checkbox1": { + "message": "Label the webpage as “NSFW” (“Not Safe For Work”)", + "description": "A checkbox to use for NSFW sites" + }, + "supportReportSpecificButton": { + "message": "Create new report", + "description": "Text for button which open an external webpage in Support pane" + }, "firstRunSectionLabel": { "message": "Welcome", "description": "The header text for the welcome message section" diff --git a/platform/mv3/extension/_locales/sq/messages.json b/platform/mv3/extension/_locales/sq/messages.json index d7ec67c9dfe6d..20be851ec8e12 100644 --- a/platform/mv3/extension/_locales/sq/messages.json +++ b/platform/mv3/extension/_locales/sq/messages.json @@ -31,6 +31,10 @@ "message": "mënyra e filtrimit", "description": "Label in the popup panel for the current filtering mode" }, + "popupTipReport": { + "message": "Report an issue on this website", + "description": "Tooltip used for the 'chat' icon in the panel" + }, "popupTipDashboard": { "message": "Hapni panelin e kontrollit", "description": "English: Click to open the dashboard" @@ -99,6 +103,70 @@ "message": "Programet kushtëzuese (sipas GPLv3):", "description": "Shown in the About pane" }, + "supportS6H": { + "message": "Report a filter issue", + "description": "Header of 'Report a filter issue' section in Support pane" + }, + "supportS3P1": { + "message": "Report filter issues with specific websites to the uBlockOrigin/uAssets issue tracker. Requires a GitHub account.", + "description": "First paragraph of 'Filter issues' section in Support pane" + }, + "supportS6P1S1": { + "message": "To avoid burdening volunteers with duplicate reports, please verify that the issue has not already been reported.", + "description": "A paragraph in the filter issue reporter section" + }, + "supportFindSpecificButton": { + "message": "Find similar reports", + "description": "A clickable link in the filter issue reporter section" + }, + "supportS6URL": { + "message": "Address of the webpage:", + "description": "Label for the URL of the page" + }, + "supportS6Select1": { + "message": "The webpage…", + "description": "Label for widget to select type of issue" + }, + "supportS6Select1Option0": { + "message": "-- Pick an entry --", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option1": { + "message": "Shows ads or ad leftovers", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option2": { + "message": "Has overlays or other nuisances", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option3": { + "message": "Detects uBO Lite", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option4": { + "message": "Has privacy-related issues", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option5": { + "message": "Malfunctions when uBO Lite is enabled", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option6": { + "message": "Opens unwanted tabs or windows", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option7": { + "message": "Leads to badware, phishing", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Checkbox1": { + "message": "Label the webpage as “NSFW” (“Not Safe For Work”)", + "description": "A checkbox to use for NSFW sites" + }, + "supportReportSpecificButton": { + "message": "Create new report", + "description": "Text for button which open an external webpage in Support pane" + }, "firstRunSectionLabel": { "message": "Përshëndetje", "description": "The header text for the welcome message section" diff --git a/platform/mv3/extension/_locales/sr/messages.json b/platform/mv3/extension/_locales/sr/messages.json index c33af6db43976..b994f245dc511 100644 --- a/platform/mv3/extension/_locales/sr/messages.json +++ b/platform/mv3/extension/_locales/sr/messages.json @@ -31,6 +31,10 @@ "message": "режим филтрирања", "description": "Label in the popup panel for the current filtering mode" }, + "popupTipReport": { + "message": "Report an issue on this website", + "description": "Tooltip used for the 'chat' icon in the panel" + }, "popupTipDashboard": { "message": "Отвори контролну таблу", "description": "English: Click to open the dashboard" @@ -99,6 +103,70 @@ "message": "Спољне зависности (компатибилно са GPLv3):", "description": "Shown in the About pane" }, + "supportS6H": { + "message": "Report a filter issue", + "description": "Header of 'Report a filter issue' section in Support pane" + }, + "supportS3P1": { + "message": "Report filter issues with specific websites to the uBlockOrigin/uAssets issue tracker. Requires a GitHub account.", + "description": "First paragraph of 'Filter issues' section in Support pane" + }, + "supportS6P1S1": { + "message": "To avoid burdening volunteers with duplicate reports, please verify that the issue has not already been reported.", + "description": "A paragraph in the filter issue reporter section" + }, + "supportFindSpecificButton": { + "message": "Find similar reports", + "description": "A clickable link in the filter issue reporter section" + }, + "supportS6URL": { + "message": "Address of the webpage:", + "description": "Label for the URL of the page" + }, + "supportS6Select1": { + "message": "The webpage…", + "description": "Label for widget to select type of issue" + }, + "supportS6Select1Option0": { + "message": "-- Pick an entry --", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option1": { + "message": "Shows ads or ad leftovers", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option2": { + "message": "Has overlays or other nuisances", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option3": { + "message": "Detects uBO Lite", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option4": { + "message": "Has privacy-related issues", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option5": { + "message": "Malfunctions when uBO Lite is enabled", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option6": { + "message": "Opens unwanted tabs or windows", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option7": { + "message": "Leads to badware, phishing", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Checkbox1": { + "message": "Label the webpage as “NSFW” (“Not Safe For Work”)", + "description": "A checkbox to use for NSFW sites" + }, + "supportReportSpecificButton": { + "message": "Create new report", + "description": "Text for button which open an external webpage in Support pane" + }, "firstRunSectionLabel": { "message": "Добродошли", "description": "The header text for the welcome message section" diff --git a/platform/mv3/extension/_locales/sv/messages.json b/platform/mv3/extension/_locales/sv/messages.json index c95ce8d24db00..2431f0b9ab750 100644 --- a/platform/mv3/extension/_locales/sv/messages.json +++ b/platform/mv3/extension/_locales/sv/messages.json @@ -31,6 +31,10 @@ "message": "filtreringsläge", "description": "Label in the popup panel for the current filtering mode" }, + "popupTipReport": { + "message": "Report an issue on this website", + "description": "Tooltip used for the 'chat' icon in the panel" + }, "popupTipDashboard": { "message": "Öppna kontrollpanelen", "description": "English: Click to open the dashboard" @@ -99,6 +103,70 @@ "message": "Externa beroenden (GPLv3-kompatibla):", "description": "Shown in the About pane" }, + "supportS6H": { + "message": "Report a filter issue", + "description": "Header of 'Report a filter issue' section in Support pane" + }, + "supportS3P1": { + "message": "Report filter issues with specific websites to the uBlockOrigin/uAssets issue tracker. Requires a GitHub account.", + "description": "First paragraph of 'Filter issues' section in Support pane" + }, + "supportS6P1S1": { + "message": "To avoid burdening volunteers with duplicate reports, please verify that the issue has not already been reported.", + "description": "A paragraph in the filter issue reporter section" + }, + "supportFindSpecificButton": { + "message": "Find similar reports", + "description": "A clickable link in the filter issue reporter section" + }, + "supportS6URL": { + "message": "Address of the webpage:", + "description": "Label for the URL of the page" + }, + "supportS6Select1": { + "message": "The webpage…", + "description": "Label for widget to select type of issue" + }, + "supportS6Select1Option0": { + "message": "-- Pick an entry --", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option1": { + "message": "Shows ads or ad leftovers", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option2": { + "message": "Has overlays or other nuisances", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option3": { + "message": "Detects uBO Lite", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option4": { + "message": "Has privacy-related issues", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option5": { + "message": "Malfunctions when uBO Lite is enabled", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option6": { + "message": "Opens unwanted tabs or windows", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option7": { + "message": "Leads to badware, phishing", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Checkbox1": { + "message": "Label the webpage as “NSFW” (“Not Safe For Work”)", + "description": "A checkbox to use for NSFW sites" + }, + "supportReportSpecificButton": { + "message": "Create new report", + "description": "Text for button which open an external webpage in Support pane" + }, "firstRunSectionLabel": { "message": "Välkommen", "description": "The header text for the welcome message section" diff --git a/platform/mv3/extension/_locales/sw/messages.json b/platform/mv3/extension/_locales/sw/messages.json index 30c719d52722d..c5f77c667a506 100644 --- a/platform/mv3/extension/_locales/sw/messages.json +++ b/platform/mv3/extension/_locales/sw/messages.json @@ -31,6 +31,10 @@ "message": "filtering mode", "description": "Label in the popup panel for the current filtering mode" }, + "popupTipReport": { + "message": "Report an issue on this website", + "description": "Tooltip used for the 'chat' icon in the panel" + }, "popupTipDashboard": { "message": "Open the dashboard", "description": "English: Click to open the dashboard" @@ -99,6 +103,70 @@ "message": "External dependencies (GPLv3-compatible):", "description": "Shown in the About pane" }, + "supportS6H": { + "message": "Report a filter issue", + "description": "Header of 'Report a filter issue' section in Support pane" + }, + "supportS3P1": { + "message": "Report filter issues with specific websites to the uBlockOrigin/uAssets issue tracker. Requires a GitHub account.", + "description": "First paragraph of 'Filter issues' section in Support pane" + }, + "supportS6P1S1": { + "message": "To avoid burdening volunteers with duplicate reports, please verify that the issue has not already been reported.", + "description": "A paragraph in the filter issue reporter section" + }, + "supportFindSpecificButton": { + "message": "Find similar reports", + "description": "A clickable link in the filter issue reporter section" + }, + "supportS6URL": { + "message": "Address of the webpage:", + "description": "Label for the URL of the page" + }, + "supportS6Select1": { + "message": "The webpage…", + "description": "Label for widget to select type of issue" + }, + "supportS6Select1Option0": { + "message": "-- Pick an entry --", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option1": { + "message": "Shows ads or ad leftovers", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option2": { + "message": "Has overlays or other nuisances", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option3": { + "message": "Detects uBO Lite", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option4": { + "message": "Has privacy-related issues", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option5": { + "message": "Malfunctions when uBO Lite is enabled", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option6": { + "message": "Opens unwanted tabs or windows", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option7": { + "message": "Leads to badware, phishing", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Checkbox1": { + "message": "Label the webpage as “NSFW” (“Not Safe For Work”)", + "description": "A checkbox to use for NSFW sites" + }, + "supportReportSpecificButton": { + "message": "Create new report", + "description": "Text for button which open an external webpage in Support pane" + }, "firstRunSectionLabel": { "message": "Welcome", "description": "The header text for the welcome message section" diff --git a/platform/mv3/extension/_locales/ta/messages.json b/platform/mv3/extension/_locales/ta/messages.json index 30c719d52722d..c5f77c667a506 100644 --- a/platform/mv3/extension/_locales/ta/messages.json +++ b/platform/mv3/extension/_locales/ta/messages.json @@ -31,6 +31,10 @@ "message": "filtering mode", "description": "Label in the popup panel for the current filtering mode" }, + "popupTipReport": { + "message": "Report an issue on this website", + "description": "Tooltip used for the 'chat' icon in the panel" + }, "popupTipDashboard": { "message": "Open the dashboard", "description": "English: Click to open the dashboard" @@ -99,6 +103,70 @@ "message": "External dependencies (GPLv3-compatible):", "description": "Shown in the About pane" }, + "supportS6H": { + "message": "Report a filter issue", + "description": "Header of 'Report a filter issue' section in Support pane" + }, + "supportS3P1": { + "message": "Report filter issues with specific websites to the uBlockOrigin/uAssets issue tracker. Requires a GitHub account.", + "description": "First paragraph of 'Filter issues' section in Support pane" + }, + "supportS6P1S1": { + "message": "To avoid burdening volunteers with duplicate reports, please verify that the issue has not already been reported.", + "description": "A paragraph in the filter issue reporter section" + }, + "supportFindSpecificButton": { + "message": "Find similar reports", + "description": "A clickable link in the filter issue reporter section" + }, + "supportS6URL": { + "message": "Address of the webpage:", + "description": "Label for the URL of the page" + }, + "supportS6Select1": { + "message": "The webpage…", + "description": "Label for widget to select type of issue" + }, + "supportS6Select1Option0": { + "message": "-- Pick an entry --", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option1": { + "message": "Shows ads or ad leftovers", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option2": { + "message": "Has overlays or other nuisances", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option3": { + "message": "Detects uBO Lite", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option4": { + "message": "Has privacy-related issues", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option5": { + "message": "Malfunctions when uBO Lite is enabled", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option6": { + "message": "Opens unwanted tabs or windows", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option7": { + "message": "Leads to badware, phishing", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Checkbox1": { + "message": "Label the webpage as “NSFW” (“Not Safe For Work”)", + "description": "A checkbox to use for NSFW sites" + }, + "supportReportSpecificButton": { + "message": "Create new report", + "description": "Text for button which open an external webpage in Support pane" + }, "firstRunSectionLabel": { "message": "Welcome", "description": "The header text for the welcome message section" diff --git a/platform/mv3/extension/_locales/te/messages.json b/platform/mv3/extension/_locales/te/messages.json index 8437838c64a0c..dd2b1d19054ca 100644 --- a/platform/mv3/extension/_locales/te/messages.json +++ b/platform/mv3/extension/_locales/te/messages.json @@ -31,6 +31,10 @@ "message": "వడపోత మోడ్", "description": "Label in the popup panel for the current filtering mode" }, + "popupTipReport": { + "message": "Report an issue on this website", + "description": "Tooltip used for the 'chat' icon in the panel" + }, "popupTipDashboard": { "message": "డాష్‌బోర్డ్‌ను తెరవండి", "description": "English: Click to open the dashboard" @@ -99,6 +103,70 @@ "message": "External dependencies (GPLv3-compatible):", "description": "Shown in the About pane" }, + "supportS6H": { + "message": "Report a filter issue", + "description": "Header of 'Report a filter issue' section in Support pane" + }, + "supportS3P1": { + "message": "Report filter issues with specific websites to the uBlockOrigin/uAssets issue tracker. Requires a GitHub account.", + "description": "First paragraph of 'Filter issues' section in Support pane" + }, + "supportS6P1S1": { + "message": "To avoid burdening volunteers with duplicate reports, please verify that the issue has not already been reported.", + "description": "A paragraph in the filter issue reporter section" + }, + "supportFindSpecificButton": { + "message": "Find similar reports", + "description": "A clickable link in the filter issue reporter section" + }, + "supportS6URL": { + "message": "Address of the webpage:", + "description": "Label for the URL of the page" + }, + "supportS6Select1": { + "message": "The webpage…", + "description": "Label for widget to select type of issue" + }, + "supportS6Select1Option0": { + "message": "-- Pick an entry --", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option1": { + "message": "Shows ads or ad leftovers", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option2": { + "message": "Has overlays or other nuisances", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option3": { + "message": "Detects uBO Lite", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option4": { + "message": "Has privacy-related issues", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option5": { + "message": "Malfunctions when uBO Lite is enabled", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option6": { + "message": "Opens unwanted tabs or windows", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option7": { + "message": "Leads to badware, phishing", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Checkbox1": { + "message": "Label the webpage as “NSFW” (“Not Safe For Work”)", + "description": "A checkbox to use for NSFW sites" + }, + "supportReportSpecificButton": { + "message": "Create new report", + "description": "Text for button which open an external webpage in Support pane" + }, "firstRunSectionLabel": { "message": "Welcome", "description": "The header text for the welcome message section" diff --git a/platform/mv3/extension/_locales/th/messages.json b/platform/mv3/extension/_locales/th/messages.json index fee5db38e9fc4..3837f15a5e666 100644 --- a/platform/mv3/extension/_locales/th/messages.json +++ b/platform/mv3/extension/_locales/th/messages.json @@ -31,6 +31,10 @@ "message": "filtering mode", "description": "Label in the popup panel for the current filtering mode" }, + "popupTipReport": { + "message": "Report an issue on this website", + "description": "Tooltip used for the 'chat' icon in the panel" + }, "popupTipDashboard": { "message": "Open the dashboard", "description": "English: Click to open the dashboard" @@ -99,6 +103,70 @@ "message": "External dependencies (GPLv3-compatible):", "description": "Shown in the About pane" }, + "supportS6H": { + "message": "Report a filter issue", + "description": "Header of 'Report a filter issue' section in Support pane" + }, + "supportS3P1": { + "message": "Report filter issues with specific websites to the uBlockOrigin/uAssets issue tracker. Requires a GitHub account.", + "description": "First paragraph of 'Filter issues' section in Support pane" + }, + "supportS6P1S1": { + "message": "To avoid burdening volunteers with duplicate reports, please verify that the issue has not already been reported.", + "description": "A paragraph in the filter issue reporter section" + }, + "supportFindSpecificButton": { + "message": "Find similar reports", + "description": "A clickable link in the filter issue reporter section" + }, + "supportS6URL": { + "message": "Address of the webpage:", + "description": "Label for the URL of the page" + }, + "supportS6Select1": { + "message": "The webpage…", + "description": "Label for widget to select type of issue" + }, + "supportS6Select1Option0": { + "message": "-- Pick an entry --", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option1": { + "message": "Shows ads or ad leftovers", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option2": { + "message": "Has overlays or other nuisances", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option3": { + "message": "Detects uBO Lite", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option4": { + "message": "Has privacy-related issues", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option5": { + "message": "Malfunctions when uBO Lite is enabled", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option6": { + "message": "Opens unwanted tabs or windows", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option7": { + "message": "Leads to badware, phishing", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Checkbox1": { + "message": "Label the webpage as “NSFW” (“Not Safe For Work”)", + "description": "A checkbox to use for NSFW sites" + }, + "supportReportSpecificButton": { + "message": "Create new report", + "description": "Text for button which open an external webpage in Support pane" + }, "firstRunSectionLabel": { "message": "ยินดีต้อนรับ", "description": "The header text for the welcome message section" diff --git a/platform/mv3/extension/_locales/tr/messages.json b/platform/mv3/extension/_locales/tr/messages.json index 06e259997e21f..259d5412259af 100644 --- a/platform/mv3/extension/_locales/tr/messages.json +++ b/platform/mv3/extension/_locales/tr/messages.json @@ -31,6 +31,10 @@ "message": "Filtreleme modu", "description": "Label in the popup panel for the current filtering mode" }, + "popupTipReport": { + "message": "Report an issue on this website", + "description": "Tooltip used for the 'chat' icon in the panel" + }, "popupTipDashboard": { "message": "Kontrol panelini aç", "description": "English: Click to open the dashboard" @@ -99,6 +103,70 @@ "message": "Dış bağlılıklar (GPLv3-uyumlu):", "description": "Shown in the About pane" }, + "supportS6H": { + "message": "Report a filter issue", + "description": "Header of 'Report a filter issue' section in Support pane" + }, + "supportS3P1": { + "message": "Report filter issues with specific websites to the uBlockOrigin/uAssets issue tracker. Requires a GitHub account.", + "description": "First paragraph of 'Filter issues' section in Support pane" + }, + "supportS6P1S1": { + "message": "To avoid burdening volunteers with duplicate reports, please verify that the issue has not already been reported.", + "description": "A paragraph in the filter issue reporter section" + }, + "supportFindSpecificButton": { + "message": "Find similar reports", + "description": "A clickable link in the filter issue reporter section" + }, + "supportS6URL": { + "message": "Address of the webpage:", + "description": "Label for the URL of the page" + }, + "supportS6Select1": { + "message": "The webpage…", + "description": "Label for widget to select type of issue" + }, + "supportS6Select1Option0": { + "message": "-- Pick an entry --", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option1": { + "message": "Shows ads or ad leftovers", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option2": { + "message": "Has overlays or other nuisances", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option3": { + "message": "Detects uBO Lite", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option4": { + "message": "Has privacy-related issues", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option5": { + "message": "Malfunctions when uBO Lite is enabled", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option6": { + "message": "Opens unwanted tabs or windows", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option7": { + "message": "Leads to badware, phishing", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Checkbox1": { + "message": "Label the webpage as “NSFW” (“Not Safe For Work”)", + "description": "A checkbox to use for NSFW sites" + }, + "supportReportSpecificButton": { + "message": "Create new report", + "description": "Text for button which open an external webpage in Support pane" + }, "firstRunSectionLabel": { "message": "Hoş geldiniz", "description": "The header text for the welcome message section" diff --git a/platform/mv3/extension/_locales/uk/messages.json b/platform/mv3/extension/_locales/uk/messages.json index 9a1a054b0f274..f3b1f1a8c9338 100644 --- a/platform/mv3/extension/_locales/uk/messages.json +++ b/platform/mv3/extension/_locales/uk/messages.json @@ -31,6 +31,10 @@ "message": "режим фільтрації", "description": "Label in the popup panel for the current filtering mode" }, + "popupTipReport": { + "message": "Report an issue on this website", + "description": "Tooltip used for the 'chat' icon in the panel" + }, "popupTipDashboard": { "message": "Відкрити панель керування", "description": "English: Click to open the dashboard" @@ -99,6 +103,70 @@ "message": "Зовнішні залежності (Сумісні з GPLv3)", "description": "Shown in the About pane" }, + "supportS6H": { + "message": "Report a filter issue", + "description": "Header of 'Report a filter issue' section in Support pane" + }, + "supportS3P1": { + "message": "Report filter issues with specific websites to the uBlockOrigin/uAssets issue tracker. Requires a GitHub account.", + "description": "First paragraph of 'Filter issues' section in Support pane" + }, + "supportS6P1S1": { + "message": "To avoid burdening volunteers with duplicate reports, please verify that the issue has not already been reported.", + "description": "A paragraph in the filter issue reporter section" + }, + "supportFindSpecificButton": { + "message": "Find similar reports", + "description": "A clickable link in the filter issue reporter section" + }, + "supportS6URL": { + "message": "Address of the webpage:", + "description": "Label for the URL of the page" + }, + "supportS6Select1": { + "message": "The webpage…", + "description": "Label for widget to select type of issue" + }, + "supportS6Select1Option0": { + "message": "-- Pick an entry --", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option1": { + "message": "Shows ads or ad leftovers", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option2": { + "message": "Has overlays or other nuisances", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option3": { + "message": "Detects uBO Lite", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option4": { + "message": "Has privacy-related issues", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option5": { + "message": "Malfunctions when uBO Lite is enabled", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option6": { + "message": "Opens unwanted tabs or windows", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option7": { + "message": "Leads to badware, phishing", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Checkbox1": { + "message": "Label the webpage as “NSFW” (“Not Safe For Work”)", + "description": "A checkbox to use for NSFW sites" + }, + "supportReportSpecificButton": { + "message": "Create new report", + "description": "Text for button which open an external webpage in Support pane" + }, "firstRunSectionLabel": { "message": "Ласкаво просимо", "description": "The header text for the welcome message section" diff --git a/platform/mv3/extension/_locales/ur/messages.json b/platform/mv3/extension/_locales/ur/messages.json index 92ba9e9bc7b5f..4832e6feffc16 100644 --- a/platform/mv3/extension/_locales/ur/messages.json +++ b/platform/mv3/extension/_locales/ur/messages.json @@ -31,6 +31,10 @@ "message": "فلٹرنگ موڈ", "description": "Label in the popup panel for the current filtering mode" }, + "popupTipReport": { + "message": "Report an issue on this website", + "description": "Tooltip used for the 'chat' icon in the panel" + }, "popupTipDashboard": { "message": "ڈیش بورڈ کھولیں۔", "description": "English: Click to open the dashboard" @@ -99,6 +103,70 @@ "message": "بیرونی انحصار (GPLv3-مطابق):", "description": "Shown in the About pane" }, + "supportS6H": { + "message": "Report a filter issue", + "description": "Header of 'Report a filter issue' section in Support pane" + }, + "supportS3P1": { + "message": "Report filter issues with specific websites to the uBlockOrigin/uAssets issue tracker. Requires a GitHub account.", + "description": "First paragraph of 'Filter issues' section in Support pane" + }, + "supportS6P1S1": { + "message": "To avoid burdening volunteers with duplicate reports, please verify that the issue has not already been reported.", + "description": "A paragraph in the filter issue reporter section" + }, + "supportFindSpecificButton": { + "message": "Find similar reports", + "description": "A clickable link in the filter issue reporter section" + }, + "supportS6URL": { + "message": "Address of the webpage:", + "description": "Label for the URL of the page" + }, + "supportS6Select1": { + "message": "The webpage…", + "description": "Label for widget to select type of issue" + }, + "supportS6Select1Option0": { + "message": "-- Pick an entry --", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option1": { + "message": "Shows ads or ad leftovers", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option2": { + "message": "Has overlays or other nuisances", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option3": { + "message": "Detects uBO Lite", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option4": { + "message": "Has privacy-related issues", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option5": { + "message": "Malfunctions when uBO Lite is enabled", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option6": { + "message": "Opens unwanted tabs or windows", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option7": { + "message": "Leads to badware, phishing", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Checkbox1": { + "message": "Label the webpage as “NSFW” (“Not Safe For Work”)", + "description": "A checkbox to use for NSFW sites" + }, + "supportReportSpecificButton": { + "message": "Create new report", + "description": "Text for button which open an external webpage in Support pane" + }, "firstRunSectionLabel": { "message": "خوش آمدید", "description": "The header text for the welcome message section" diff --git a/platform/mv3/extension/_locales/vi/messages.json b/platform/mv3/extension/_locales/vi/messages.json index 9a612c32deee0..489c0522b0139 100644 --- a/platform/mv3/extension/_locales/vi/messages.json +++ b/platform/mv3/extension/_locales/vi/messages.json @@ -31,6 +31,10 @@ "message": "chế độ lọc", "description": "Label in the popup panel for the current filtering mode" }, + "popupTipReport": { + "message": "Report an issue on this website", + "description": "Tooltip used for the 'chat' icon in the panel" + }, "popupTipDashboard": { "message": "Mở bảng điều khiển", "description": "English: Click to open the dashboard" @@ -99,6 +103,70 @@ "message": "Các phụ thuộc bên ngoài (tương thích GPLv3):", "description": "Shown in the About pane" }, + "supportS6H": { + "message": "Report a filter issue", + "description": "Header of 'Report a filter issue' section in Support pane" + }, + "supportS3P1": { + "message": "Report filter issues with specific websites to the uBlockOrigin/uAssets issue tracker. Requires a GitHub account.", + "description": "First paragraph of 'Filter issues' section in Support pane" + }, + "supportS6P1S1": { + "message": "To avoid burdening volunteers with duplicate reports, please verify that the issue has not already been reported.", + "description": "A paragraph in the filter issue reporter section" + }, + "supportFindSpecificButton": { + "message": "Find similar reports", + "description": "A clickable link in the filter issue reporter section" + }, + "supportS6URL": { + "message": "Address of the webpage:", + "description": "Label for the URL of the page" + }, + "supportS6Select1": { + "message": "The webpage…", + "description": "Label for widget to select type of issue" + }, + "supportS6Select1Option0": { + "message": "-- Pick an entry --", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option1": { + "message": "Shows ads or ad leftovers", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option2": { + "message": "Has overlays or other nuisances", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option3": { + "message": "Detects uBO Lite", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option4": { + "message": "Has privacy-related issues", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option5": { + "message": "Malfunctions when uBO Lite is enabled", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option6": { + "message": "Opens unwanted tabs or windows", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option7": { + "message": "Leads to badware, phishing", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Checkbox1": { + "message": "Label the webpage as “NSFW” (“Not Safe For Work”)", + "description": "A checkbox to use for NSFW sites" + }, + "supportReportSpecificButton": { + "message": "Create new report", + "description": "Text for button which open an external webpage in Support pane" + }, "firstRunSectionLabel": { "message": "Chào mừng", "description": "The header text for the welcome message section" diff --git a/platform/mv3/extension/_locales/zh_CN/messages.json b/platform/mv3/extension/_locales/zh_CN/messages.json index 26f88f655f6d5..d50e605f35e36 100644 --- a/platform/mv3/extension/_locales/zh_CN/messages.json +++ b/platform/mv3/extension/_locales/zh_CN/messages.json @@ -31,6 +31,10 @@ "message": "过滤模式", "description": "Label in the popup panel for the current filtering mode" }, + "popupTipReport": { + "message": "Report an issue on this website", + "description": "Tooltip used for the 'chat' icon in the panel" + }, "popupTipDashboard": { "message": "在仪表板中", "description": "English: Click to open the dashboard" @@ -99,6 +103,70 @@ "message": "外部依赖(与 GPLv3 协议兼容):", "description": "Shown in the About pane" }, + "supportS6H": { + "message": "Report a filter issue", + "description": "Header of 'Report a filter issue' section in Support pane" + }, + "supportS3P1": { + "message": "Report filter issues with specific websites to the uBlockOrigin/uAssets issue tracker. Requires a GitHub account.", + "description": "First paragraph of 'Filter issues' section in Support pane" + }, + "supportS6P1S1": { + "message": "To avoid burdening volunteers with duplicate reports, please verify that the issue has not already been reported.", + "description": "A paragraph in the filter issue reporter section" + }, + "supportFindSpecificButton": { + "message": "Find similar reports", + "description": "A clickable link in the filter issue reporter section" + }, + "supportS6URL": { + "message": "Address of the webpage:", + "description": "Label for the URL of the page" + }, + "supportS6Select1": { + "message": "The webpage…", + "description": "Label for widget to select type of issue" + }, + "supportS6Select1Option0": { + "message": "-- Pick an entry --", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option1": { + "message": "Shows ads or ad leftovers", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option2": { + "message": "Has overlays or other nuisances", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option3": { + "message": "Detects uBO Lite", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option4": { + "message": "Has privacy-related issues", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option5": { + "message": "Malfunctions when uBO Lite is enabled", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option6": { + "message": "Opens unwanted tabs or windows", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option7": { + "message": "Leads to badware, phishing", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Checkbox1": { + "message": "Label the webpage as “NSFW” (“Not Safe For Work”)", + "description": "A checkbox to use for NSFW sites" + }, + "supportReportSpecificButton": { + "message": "Create new report", + "description": "Text for button which open an external webpage in Support pane" + }, "firstRunSectionLabel": { "message": "欢迎使用", "description": "The header text for the welcome message section" diff --git a/platform/mv3/extension/_locales/zh_TW/messages.json b/platform/mv3/extension/_locales/zh_TW/messages.json index d79f5d0ea4356..d1e273552b342 100644 --- a/platform/mv3/extension/_locales/zh_TW/messages.json +++ b/platform/mv3/extension/_locales/zh_TW/messages.json @@ -31,6 +31,10 @@ "message": "過濾模式", "description": "Label in the popup panel for the current filtering mode" }, + "popupTipReport": { + "message": "Report an issue on this website", + "description": "Tooltip used for the 'chat' icon in the panel" + }, "popupTipDashboard": { "message": "開啟控制台", "description": "English: Click to open the dashboard" @@ -99,6 +103,70 @@ "message": "外部相依套件(與 GPLv3 相容):", "description": "Shown in the About pane" }, + "supportS6H": { + "message": "Report a filter issue", + "description": "Header of 'Report a filter issue' section in Support pane" + }, + "supportS3P1": { + "message": "Report filter issues with specific websites to the uBlockOrigin/uAssets issue tracker. Requires a GitHub account.", + "description": "First paragraph of 'Filter issues' section in Support pane" + }, + "supportS6P1S1": { + "message": "To avoid burdening volunteers with duplicate reports, please verify that the issue has not already been reported.", + "description": "A paragraph in the filter issue reporter section" + }, + "supportFindSpecificButton": { + "message": "Find similar reports", + "description": "A clickable link in the filter issue reporter section" + }, + "supportS6URL": { + "message": "Address of the webpage:", + "description": "Label for the URL of the page" + }, + "supportS6Select1": { + "message": "The webpage…", + "description": "Label for widget to select type of issue" + }, + "supportS6Select1Option0": { + "message": "-- Pick an entry --", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option1": { + "message": "Shows ads or ad leftovers", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option2": { + "message": "Has overlays or other nuisances", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option3": { + "message": "Detects uBO Lite", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option4": { + "message": "Has privacy-related issues", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option5": { + "message": "Malfunctions when uBO Lite is enabled", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option6": { + "message": "Opens unwanted tabs or windows", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Select1Option7": { + "message": "Leads to badware, phishing", + "description": "An entry in the widget used to select the type of issue" + }, + "supportS6Checkbox1": { + "message": "Label the webpage as “NSFW” (“Not Safe For Work”)", + "description": "A checkbox to use for NSFW sites" + }, + "supportReportSpecificButton": { + "message": "Create new report", + "description": "Text for button which open an external webpage in Support pane" + }, "firstRunSectionLabel": { "message": "歡迎", "description": "The header text for the welcome message section" From e81e70937f5b6f8f98bdd8e6f71af030486efab8 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sun, 29 Sep 2024 10:51:56 -0400 Subject: [PATCH 0283/1099] Add ability to decode base64 in `urlskip=` Related case: https://github.com/uBlockOrigin/uAssets/issues/25467 New step: `-base64` Purpose: to decode base64-encoded output of previous step --- src/js/static-net-filtering.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/js/static-net-filtering.js b/src/js/static-net-filtering.js index e5e9017d26b1a..f5b1841752368 100644 --- a/src/js/static-net-filtering.js +++ b/src/js/static-net-filtering.js @@ -5441,6 +5441,11 @@ function urlSkip(urlin, steps) { urlin = urlout = `https://${s}`; continue; } + // Decode base64 + if ( step === '-base64' ) { + urlin = urlout = self.atob(urlin); + continue; + } // Unknown directive return; } From a2f81f19ff41d3fef462305ac4c0478c755f67af Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sun, 29 Sep 2024 10:56:54 -0400 Subject: [PATCH 0284/1099] Update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 48a39d7b1859c..913bfed9480f7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,4 @@ +- [Add ability to decode base64 in `urlskip=`](https://github.com/gorhill/uBlock/commit/e81e70937f) - [Fix images not properly downloading on click](https://github.com/gorhill/uBlock/commit/aec0bd39e3) ---------- From 7a9481b5a582a24a4de6bc0c3d067c08398f402b Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sun, 29 Sep 2024 10:57:12 -0400 Subject: [PATCH 0285/1099] New revision for dev build --- dist/version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dist/version b/dist/version index 9fb35ca861864..0c424f18d5483 100644 --- a/dist/version +++ b/dist/version @@ -1 +1 @@ -1.60.1.0 \ No newline at end of file +1.60.1.1 \ No newline at end of file From c6baa2fb51663756b1f76f380283fc1127ba571c Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sun, 29 Sep 2024 11:15:04 -0400 Subject: [PATCH 0286/1099] Import translation work from https://crowdin.com/project/ublock --- .../mv3/extension/_locales/ar/messages.json | 4 +-- .../mv3/extension/_locales/bg/messages.json | 34 +++++++++---------- .../mv3/extension/_locales/ca/messages.json | 34 +++++++++---------- .../mv3/extension/_locales/cs/messages.json | 34 +++++++++---------- .../mv3/extension/_locales/da/messages.json | 34 +++++++++---------- .../mv3/extension/_locales/de/messages.json | 34 +++++++++---------- .../mv3/extension/_locales/es/messages.json | 34 +++++++++---------- .../mv3/extension/_locales/fr/messages.json | 34 +++++++++---------- .../mv3/extension/_locales/he/messages.json | 34 +++++++++---------- .../mv3/extension/_locales/hr/messages.json | 34 +++++++++---------- .../mv3/extension/_locales/it/messages.json | 34 +++++++++---------- .../mv3/extension/_locales/ja/messages.json | 34 +++++++++---------- .../mv3/extension/_locales/nl/messages.json | 34 +++++++++---------- .../mv3/extension/_locales/pl/messages.json | 34 +++++++++---------- .../extension/_locales/pt_BR/messages.json | 34 +++++++++---------- .../mv3/extension/_locales/ru/messages.json | 34 +++++++++---------- .../mv3/extension/_locales/sk/messages.json | 34 +++++++++---------- .../mv3/extension/_locales/sv/messages.json | 34 +++++++++---------- .../mv3/extension/_locales/tr/messages.json | 34 +++++++++---------- .../mv3/extension/_locales/uk/messages.json | 6 ++-- .../extension/_locales/zh_TW/messages.json | 34 +++++++++---------- src/_locales/cs/messages.json | 4 +-- src/_locales/de/messages.json | 8 ++--- src/_locales/fr/messages.json | 2 +- 24 files changed, 335 insertions(+), 335 deletions(-) diff --git a/platform/mv3/extension/_locales/ar/messages.json b/platform/mv3/extension/_locales/ar/messages.json index f0cb94cebef39..588e258fd3100 100644 --- a/platform/mv3/extension/_locales/ar/messages.json +++ b/platform/mv3/extension/_locales/ar/messages.json @@ -32,7 +32,7 @@ "description": "Label in the popup panel for the current filtering mode" }, "popupTipReport": { - "message": "Report an issue on this website", + "message": "الإبلاغ عن مشكلة في هذا الموقع", "description": "Tooltip used for the 'chat' icon in the panel" }, "popupTipDashboard": { @@ -104,7 +104,7 @@ "description": "Shown in the About pane" }, "supportS6H": { - "message": "Report a filter issue", + "message": "الإبلاغ عن مشكلة في عوامل التصفية", "description": "Header of 'Report a filter issue' section in Support pane" }, "supportS3P1": { diff --git a/platform/mv3/extension/_locales/bg/messages.json b/platform/mv3/extension/_locales/bg/messages.json index b505ed5762c97..51df9ca15e460 100644 --- a/platform/mv3/extension/_locales/bg/messages.json +++ b/platform/mv3/extension/_locales/bg/messages.json @@ -32,7 +32,7 @@ "description": "Label in the popup panel for the current filtering mode" }, "popupTipReport": { - "message": "Report an issue on this website", + "message": "Докладване на проблем с този уебсайт", "description": "Tooltip used for the 'chat' icon in the panel" }, "popupTipDashboard": { @@ -104,67 +104,67 @@ "description": "Shown in the About pane" }, "supportS6H": { - "message": "Report a filter issue", + "message": "Докладване на проблем с филтъра", "description": "Header of 'Report a filter issue' section in Support pane" }, "supportS3P1": { - "message": "Report filter issues with specific websites to the uBlockOrigin/uAssets issue tracker. Requires a GitHub account.", + "message": "Докладвайте за проблеми с филтъра с конкретни уебсайтове към uBlockOrigin/uAssets инструмент за проследяване на проблеми. Изисква акаунт в GitHub.", "description": "First paragraph of 'Filter issues' section in Support pane" }, "supportS6P1S1": { - "message": "To avoid burdening volunteers with duplicate reports, please verify that the issue has not already been reported.", + "message": "За да се избегне натоварването на доброволците с дублиращи се доклади, моля, проверете дали проблемът вече не е докладван.", "description": "A paragraph in the filter issue reporter section" }, "supportFindSpecificButton": { - "message": "Find similar reports", + "message": "Намиране на подобни доклади", "description": "A clickable link in the filter issue reporter section" }, "supportS6URL": { - "message": "Address of the webpage:", + "message": "Адрес на уеб страницата:", "description": "Label for the URL of the page" }, "supportS6Select1": { - "message": "The webpage…", + "message": "Уеб страницата...", "description": "Label for widget to select type of issue" }, "supportS6Select1Option0": { - "message": "-- Pick an entry --", + "message": "-- Изберете --", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option1": { - "message": "Shows ads or ad leftovers", + "message": "Показва реклами или остатъци от реклами", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option2": { - "message": "Has overlays or other nuisances", + "message": "Има наслагвания или други неудобства", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option3": { - "message": "Detects uBO Lite", + "message": "Открива uBO Lite", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option4": { - "message": "Has privacy-related issues", + "message": "Има проблеми, свързани с поверителността", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option5": { - "message": "Malfunctions when uBO Lite is enabled", + "message": "Неправилно функциониране, когато е активиран uBO Lite", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option6": { - "message": "Opens unwanted tabs or windows", + "message": "Отваря нежелани раздели или прозорци", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option7": { - "message": "Leads to badware, phishing", + "message": "Води до зловреден софтуер, фишинг", "description": "An entry in the widget used to select the type of issue" }, "supportS6Checkbox1": { - "message": "Label the webpage as “NSFW” (“Not Safe For Work”)", + "message": "Маркиране на уебстраницата като \"NSFW\" (\"Не е безопасно за работа\")", "description": "A checkbox to use for NSFW sites" }, "supportReportSpecificButton": { - "message": "Create new report", + "message": "Създаване на нов доклад", "description": "Text for button which open an external webpage in Support pane" }, "firstRunSectionLabel": { diff --git a/platform/mv3/extension/_locales/ca/messages.json b/platform/mv3/extension/_locales/ca/messages.json index 04f722f964bbb..f307f31ca115a 100644 --- a/platform/mv3/extension/_locales/ca/messages.json +++ b/platform/mv3/extension/_locales/ca/messages.json @@ -32,7 +32,7 @@ "description": "Label in the popup panel for the current filtering mode" }, "popupTipReport": { - "message": "Report an issue on this website", + "message": "Informa d'un problema en aquest lloc web", "description": "Tooltip used for the 'chat' icon in the panel" }, "popupTipDashboard": { @@ -104,67 +104,67 @@ "description": "Shown in the About pane" }, "supportS6H": { - "message": "Report a filter issue", + "message": "Informeu d'un problema de filtre", "description": "Header of 'Report a filter issue' section in Support pane" }, "supportS3P1": { - "message": "Report filter issues with specific websites to the uBlockOrigin/uAssets issue tracker. Requires a GitHub account.", + "message": "Informeu d'un problema en llocs web específics mitjançant el uBlockOrigin/uAssets seguiment d'errors. Cal un compte al GitHub.", "description": "First paragraph of 'Filter issues' section in Support pane" }, "supportS6P1S1": { - "message": "To avoid burdening volunteers with duplicate reports, please verify that the issue has not already been reported.", + "message": "Per evitar carregar els voluntaris amb informes duplicats, verifiqueu que el problema encara no s'hagi notificat.", "description": "A paragraph in the filter issue reporter section" }, "supportFindSpecificButton": { - "message": "Find similar reports", + "message": "Cerca d'informes semblants", "description": "A clickable link in the filter issue reporter section" }, "supportS6URL": { - "message": "Address of the webpage:", + "message": "Adreça de la pàgina web:", "description": "Label for the URL of the page" }, "supportS6Select1": { - "message": "The webpage…", + "message": "La pàgina web...", "description": "Label for widget to select type of issue" }, "supportS6Select1Option0": { - "message": "-- Pick an entry --", + "message": "-- Trieu una entrada --", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option1": { - "message": "Shows ads or ad leftovers", + "message": "Mostra anuncis o restes d'anuncis", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option2": { - "message": "Has overlays or other nuisances", + "message": "Té superposicions o altres errors", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option3": { - "message": "Detects uBO Lite", + "message": "Detecta l'ús de l'uBO Lite", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option4": { - "message": "Has privacy-related issues", + "message": "Té problemes relacionats amb la privadesa", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option5": { - "message": "Malfunctions when uBO Lite is enabled", + "message": "No es mostra correctament amb l'uBO Lite habilitat", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option6": { - "message": "Opens unwanted tabs or windows", + "message": "Obre pestanyes o finestres no desitjades", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option7": { - "message": "Leads to badware, phishing", + "message": "Condueix a programari maliciós, pesca electrònica", "description": "An entry in the widget used to select the type of issue" }, "supportS6Checkbox1": { - "message": "Label the webpage as “NSFW” (“Not Safe For Work”)", + "message": "Etiqueta l'enllaç com a «NSFW» (“No apte per al treball”)", "description": "A checkbox to use for NSFW sites" }, "supportReportSpecificButton": { - "message": "Create new report", + "message": "Crea un informe nou", "description": "Text for button which open an external webpage in Support pane" }, "firstRunSectionLabel": { diff --git a/platform/mv3/extension/_locales/cs/messages.json b/platform/mv3/extension/_locales/cs/messages.json index 556e7da210650..fb24661d25a84 100644 --- a/platform/mv3/extension/_locales/cs/messages.json +++ b/platform/mv3/extension/_locales/cs/messages.json @@ -32,7 +32,7 @@ "description": "Label in the popup panel for the current filtering mode" }, "popupTipReport": { - "message": "Report an issue on this website", + "message": "Nahlásit problém na této webové stránce", "description": "Tooltip used for the 'chat' icon in the panel" }, "popupTipDashboard": { @@ -104,67 +104,67 @@ "description": "Shown in the About pane" }, "supportS6H": { - "message": "Report a filter issue", + "message": "Nahlásit problém s filtrem", "description": "Header of 'Report a filter issue' section in Support pane" }, "supportS3P1": { - "message": "Report filter issues with specific websites to the uBlockOrigin/uAssets issue tracker. Requires a GitHub account.", + "message": "Nahlaste problémy s filtrem u učitých webových stránek do sledovače problémů uBlockOrigin/uAssets. Vyžaduje účet GitHub.", "description": "First paragraph of 'Filter issues' section in Support pane" }, "supportS6P1S1": { - "message": "To avoid burdening volunteers with duplicate reports, please verify that the issue has not already been reported.", + "message": "Abyste dobrovolníky nezatěžovali duplicitními hlášeními, ověřte si, zda již problém nebyl nahlášen.", "description": "A paragraph in the filter issue reporter section" }, "supportFindSpecificButton": { - "message": "Find similar reports", + "message": "Vyhledat podobná hlášení", "description": "A clickable link in the filter issue reporter section" }, "supportS6URL": { - "message": "Address of the webpage:", + "message": "Adresa webové stránky:", "description": "Label for the URL of the page" }, "supportS6Select1": { - "message": "The webpage…", + "message": "Webová stránka…", "description": "Label for widget to select type of issue" }, "supportS6Select1Option0": { - "message": "-- Pick an entry --", + "message": "-- Vyberte položku --", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option1": { - "message": "Shows ads or ad leftovers", + "message": "Zobrazuje reklamy nebo zbytky reklam", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option2": { - "message": "Has overlays or other nuisances", + "message": "Je překrytá nebo má jiné nedostatky", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option3": { - "message": "Detects uBO Lite", + "message": "Detekuje uBO Lite", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option4": { - "message": "Has privacy-related issues", + "message": "Má problémy související se soukromím", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option5": { - "message": "Malfunctions when uBO Lite is enabled", + "message": "Je rozbitá, když je povolen uBO Lite", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option6": { - "message": "Opens unwanted tabs or windows", + "message": "Otevírá nechtěné karty nebo okna", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option7": { - "message": "Leads to badware, phishing", + "message": "Vede k badwaru, phishingu", "description": "An entry in the widget used to select the type of issue" }, "supportS6Checkbox1": { - "message": "Label the webpage as “NSFW” (“Not Safe For Work”)", + "message": "Označit webovou stránku jako “NSFW” (“Není bezpečné pro práci”)", "description": "A checkbox to use for NSFW sites" }, "supportReportSpecificButton": { - "message": "Create new report", + "message": "Vytvořit nové hlášení", "description": "Text for button which open an external webpage in Support pane" }, "firstRunSectionLabel": { diff --git a/platform/mv3/extension/_locales/da/messages.json b/platform/mv3/extension/_locales/da/messages.json index 93de76bb35611..8a8c760677c92 100644 --- a/platform/mv3/extension/_locales/da/messages.json +++ b/platform/mv3/extension/_locales/da/messages.json @@ -32,7 +32,7 @@ "description": "Label in the popup panel for the current filtering mode" }, "popupTipReport": { - "message": "Report an issue on this website", + "message": "Anmeld et problem på dette websted", "description": "Tooltip used for the 'chat' icon in the panel" }, "popupTipDashboard": { @@ -104,67 +104,67 @@ "description": "Shown in the About pane" }, "supportS6H": { - "message": "Report a filter issue", + "message": "Anmeld et filterproblem", "description": "Header of 'Report a filter issue' section in Support pane" }, "supportS3P1": { - "message": "Report filter issues with specific websites to the uBlockOrigin/uAssets issue tracker. Requires a GitHub account.", + "message": "Anmeld filterproblemer med bestemte websteder til uBlockOrigin/uAssets-problemsporingen. Kræver en GitHub-konto.", "description": "First paragraph of 'Filter issues' section in Support pane" }, "supportS6P1S1": { - "message": "To avoid burdening volunteers with duplicate reports, please verify that the issue has not already been reported.", + "message": "For at undgå at belaste frivillige med dubletanmeldelser, så tjek venligst, at problemet ikke allerede er anmeldt.", "description": "A paragraph in the filter issue reporter section" }, "supportFindSpecificButton": { - "message": "Find similar reports", + "message": "Find lign. anmeldelser", "description": "A clickable link in the filter issue reporter section" }, "supportS6URL": { - "message": "Address of the webpage:", + "message": "Websideadressen:", "description": "Label for the URL of the page" }, "supportS6Select1": { - "message": "The webpage…", + "message": "Websiden…", "description": "Label for widget to select type of issue" }, "supportS6Select1Option0": { - "message": "-- Pick an entry --", + "message": "-- Vælg problemtype --", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option1": { - "message": "Shows ads or ad leftovers", + "message": "Viser annoncer eller annoncerester", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option2": { - "message": "Has overlays or other nuisances", + "message": "Har overlejringer eller andre gener", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option3": { - "message": "Detects uBO Lite", + "message": "Detekterer uBO Lite", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option4": { - "message": "Has privacy-related issues", + "message": "Har fortrolighedsrelaterede problemer", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option5": { - "message": "Malfunctions when uBO Lite is enabled", + "message": "Fejlfungerer, når uBO Lite er aktiveret", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option6": { - "message": "Opens unwanted tabs or windows", + "message": "Åbner uønskede faner eller vinduer", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option7": { - "message": "Leads to badware, phishing", + "message": "Fører til badware, phishing", "description": "An entry in the widget used to select the type of issue" }, "supportS6Checkbox1": { - "message": "Label the webpage as “NSFW” (“Not Safe For Work”)", + "message": "Mærk websiden som “NSFW” (“Not Safe For Work”)", "description": "A checkbox to use for NSFW sites" }, "supportReportSpecificButton": { - "message": "Create new report", + "message": "Opret ny anmeldelse", "description": "Text for button which open an external webpage in Support pane" }, "firstRunSectionLabel": { diff --git a/platform/mv3/extension/_locales/de/messages.json b/platform/mv3/extension/_locales/de/messages.json index 65154b8d36dfc..a2504f952ed9e 100644 --- a/platform/mv3/extension/_locales/de/messages.json +++ b/platform/mv3/extension/_locales/de/messages.json @@ -32,7 +32,7 @@ "description": "Label in the popup panel for the current filtering mode" }, "popupTipReport": { - "message": "Report an issue on this website", + "message": "Ein Problem mit dieser Website melden", "description": "Tooltip used for the 'chat' icon in the panel" }, "popupTipDashboard": { @@ -104,67 +104,67 @@ "description": "Shown in the About pane" }, "supportS6H": { - "message": "Report a filter issue", + "message": "Ein Filterproblem melden", "description": "Header of 'Report a filter issue' section in Support pane" }, "supportS3P1": { - "message": "Report filter issues with specific websites to the uBlockOrigin/uAssets issue tracker. Requires a GitHub account.", + "message": "Bitte melden Sie Filterprobleme mit bestimmten Websites an den uBlockOrigin/uAssets Issue Tracker. Erfordert ein GitHub-Konto.", "description": "First paragraph of 'Filter issues' section in Support pane" }, "supportS6P1S1": { - "message": "To avoid burdening volunteers with duplicate reports, please verify that the issue has not already been reported.", + "message": "Um die Freiwilligen nicht mit doppelten Meldungen zu belasten, vergewissern Sie sich bitte, dass das Problem nicht schon einmal gemeldet wurde.", "description": "A paragraph in the filter issue reporter section" }, "supportFindSpecificButton": { - "message": "Find similar reports", + "message": "Ähnliche Meldungen finden", "description": "A clickable link in the filter issue reporter section" }, "supportS6URL": { - "message": "Address of the webpage:", + "message": "Adresse der Webseite:", "description": "Label for the URL of the page" }, "supportS6Select1": { - "message": "The webpage…", + "message": "Die Webseite …", "description": "Label for widget to select type of issue" }, "supportS6Select1Option0": { - "message": "-- Pick an entry --", + "message": "-- Einen Eintrag auswählen --", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option1": { - "message": "Shows ads or ad leftovers", + "message": "Zeigt Werbung oder Werbereste", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option2": { - "message": "Has overlays or other nuisances", + "message": "Hat Überdeckungen oder andere Belästigungen", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option3": { - "message": "Detects uBO Lite", + "message": "Erkennt uBO Lite", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option4": { - "message": "Has privacy-related issues", + "message": "Hat Datenschutzprobleme", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option5": { - "message": "Malfunctions when uBO Lite is enabled", + "message": "Funktioniert nicht richtig, wenn uBO Lite aktiviert ist", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option6": { - "message": "Opens unwanted tabs or windows", + "message": "Öffnet unerwünschte Tabs oder Fenster", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option7": { - "message": "Leads to badware, phishing", + "message": "Führt zu Schadsoftware, Phishing", "description": "An entry in the widget used to select the type of issue" }, "supportS6Checkbox1": { - "message": "Label the webpage as “NSFW” (“Not Safe For Work”)", + "message": "Webseite als »NSFW« kennzeichnen (»Unpassend für den Arbeitsplatz«)", "description": "A checkbox to use for NSFW sites" }, "supportReportSpecificButton": { - "message": "Create new report", + "message": "Neue Meldung erstellen", "description": "Text for button which open an external webpage in Support pane" }, "firstRunSectionLabel": { diff --git a/platform/mv3/extension/_locales/es/messages.json b/platform/mv3/extension/_locales/es/messages.json index ff55964e57f24..f7cfe51e61631 100644 --- a/platform/mv3/extension/_locales/es/messages.json +++ b/platform/mv3/extension/_locales/es/messages.json @@ -32,7 +32,7 @@ "description": "Label in the popup panel for the current filtering mode" }, "popupTipReport": { - "message": "Report an issue on this website", + "message": "Reportar un problema en este sitio web", "description": "Tooltip used for the 'chat' icon in the panel" }, "popupTipDashboard": { @@ -104,67 +104,67 @@ "description": "Shown in the About pane" }, "supportS6H": { - "message": "Report a filter issue", + "message": "Reportar un problema de filtro", "description": "Header of 'Report a filter issue' section in Support pane" }, "supportS3P1": { - "message": "Report filter issues with specific websites to the uBlockOrigin/uAssets issue tracker. Requires a GitHub account.", + "message": "Reportar problemas de filtros con sitios web específicos en uBlockOrigin/uAssets, un rastreador de problemas. Requiere una cuenta de GitHub.", "description": "First paragraph of 'Filter issues' section in Support pane" }, "supportS6P1S1": { - "message": "To avoid burdening volunteers with duplicate reports, please verify that the issue has not already been reported.", + "message": "Para evitar sobrecargar a voluntarios con reportes duplicados, verifique que el problema no haya sido reportado.", "description": "A paragraph in the filter issue reporter section" }, "supportFindSpecificButton": { - "message": "Find similar reports", + "message": "Encontrar reportes similares", "description": "A clickable link in the filter issue reporter section" }, "supportS6URL": { - "message": "Address of the webpage:", + "message": "Dirección de la página web:", "description": "Label for the URL of the page" }, "supportS6Select1": { - "message": "The webpage…", + "message": "La página web...", "description": "Label for widget to select type of issue" }, "supportS6Select1Option0": { - "message": "-- Pick an entry --", + "message": "-- Elige una entrada --", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option1": { - "message": "Shows ads or ad leftovers", + "message": "Muestra anuncios o restos de anuncios", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option2": { - "message": "Has overlays or other nuisances", + "message": "Tiene superposiciones u otras molestias", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option3": { - "message": "Detects uBO Lite", + "message": "Detecta uBO Lite", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option4": { - "message": "Has privacy-related issues", + "message": "Tiene problemas relacionados con la privacidad", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option5": { - "message": "Malfunctions when uBO Lite is enabled", + "message": "Funciona mal cuando uBO Lite está habilitado", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option6": { - "message": "Opens unwanted tabs or windows", + "message": "Abre pestañas o ventanas no deseadas", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option7": { - "message": "Leads to badware, phishing", + "message": "Conduce a malware, phishing", "description": "An entry in the widget used to select the type of issue" }, "supportS6Checkbox1": { - "message": "Label the webpage as “NSFW” (“Not Safe For Work”)", + "message": "Etiqueta la página web como “NSFW” (“No apto para el trabajo”)", "description": "A checkbox to use for NSFW sites" }, "supportReportSpecificButton": { - "message": "Create new report", + "message": "Crear nuevo reporte", "description": "Text for button which open an external webpage in Support pane" }, "firstRunSectionLabel": { diff --git a/platform/mv3/extension/_locales/fr/messages.json b/platform/mv3/extension/_locales/fr/messages.json index d41b35931148b..c18dccbeac544 100644 --- a/platform/mv3/extension/_locales/fr/messages.json +++ b/platform/mv3/extension/_locales/fr/messages.json @@ -32,7 +32,7 @@ "description": "Label in the popup panel for the current filtering mode" }, "popupTipReport": { - "message": "Report an issue on this website", + "message": "Rapporter un problème sur ce site Web", "description": "Tooltip used for the 'chat' icon in the panel" }, "popupTipDashboard": { @@ -104,67 +104,67 @@ "description": "Shown in the About pane" }, "supportS6H": { - "message": "Report a filter issue", + "message": "Rapporter un problème de filtre", "description": "Header of 'Report a filter issue' section in Support pane" }, "supportS3P1": { - "message": "Report filter issues with specific websites to the uBlockOrigin/uAssets issue tracker. Requires a GitHub account.", + "message": "Rapportez des problèmes de filtre sur des sites Web spécifiques dans le uBlockOrigin/uAssets suivi des problèmes. Nécessite un compte GitHub.", "description": "First paragraph of 'Filter issues' section in Support pane" }, "supportS6P1S1": { - "message": "To avoid burdening volunteers with duplicate reports, please verify that the issue has not already been reported.", + "message": "Pour éviter d'encombrer les contributeurs avec des rapports en double, veuillez vérifier que le problème n'a pas déjà été rapporté.", "description": "A paragraph in the filter issue reporter section" }, "supportFindSpecificButton": { - "message": "Find similar reports", + "message": "Trouver des rapports similaires", "description": "A clickable link in the filter issue reporter section" }, "supportS6URL": { - "message": "Address of the webpage:", + "message": "Adresse de la page Web :", "description": "Label for the URL of the page" }, "supportS6Select1": { - "message": "The webpage…", + "message": "La page Web…", "description": "Label for widget to select type of issue" }, "supportS6Select1Option0": { - "message": "-- Pick an entry --", + "message": "-- Choisir un type --", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option1": { - "message": "Shows ads or ad leftovers", + "message": "affiche des publicités ou des résidus de publicité", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option2": { - "message": "Has overlays or other nuisances", + "message": "a une surcouche ou d'autres nuisances", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option3": { - "message": "Detects uBO Lite", + "message": "détecte uBO Lite", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option4": { - "message": "Has privacy-related issues", + "message": "a des problèmes de confidentialité", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option5": { - "message": "Malfunctions when uBO Lite is enabled", + "message": "fonctionne mal quand uBO Lite est activé", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option6": { - "message": "Opens unwanted tabs or windows", + "message": "ouvre des onglets ou fenêtres indésirables", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option7": { - "message": "Leads to badware, phishing", + "message": "conduit à/redirige vers des logiciels malveillants, du hameçonnage", "description": "An entry in the widget used to select the type of issue" }, "supportS6Checkbox1": { - "message": "Label the webpage as “NSFW” (“Not Safe For Work”)", + "message": "Marquer la page Web comme \"IPLT\" (Inapproprié Pour Le Travail)", "description": "A checkbox to use for NSFW sites" }, "supportReportSpecificButton": { - "message": "Create new report", + "message": "Créer un nouveau rapport", "description": "Text for button which open an external webpage in Support pane" }, "firstRunSectionLabel": { diff --git a/platform/mv3/extension/_locales/he/messages.json b/platform/mv3/extension/_locales/he/messages.json index 16e21d55e13f6..6dfbf8126ed13 100644 --- a/platform/mv3/extension/_locales/he/messages.json +++ b/platform/mv3/extension/_locales/he/messages.json @@ -32,7 +32,7 @@ "description": "Label in the popup panel for the current filtering mode" }, "popupTipReport": { - "message": "Report an issue on this website", + "message": "דווח על בעיה באתר זה", "description": "Tooltip used for the 'chat' icon in the panel" }, "popupTipDashboard": { @@ -104,67 +104,67 @@ "description": "Shown in the About pane" }, "supportS6H": { - "message": "Report a filter issue", + "message": "דיווח על בעיית מסנן", "description": "Header of 'Report a filter issue' section in Support pane" }, "supportS3P1": { - "message": "Report filter issues with specific websites to the uBlockOrigin/uAssets issue tracker. Requires a GitHub account.", + "message": "לדיווח על בעיות באתרים ספציפים יש לפתוח דיווח חדש במעקב הדיווחים של uBlockOrigin/uAssets. נדרש חשבון ב GitHub.", "description": "First paragraph of 'Filter issues' section in Support pane" }, "supportS6P1S1": { - "message": "To avoid burdening volunteers with duplicate reports, please verify that the issue has not already been reported.", + "message": "כדי להימנע מהכבדה על מתנדבים בדווחים כפולים, נא לודא שבעיה דומה טרם דווחה.", "description": "A paragraph in the filter issue reporter section" }, "supportFindSpecificButton": { - "message": "Find similar reports", + "message": "מצאו דיווחים דומים", "description": "A clickable link in the filter issue reporter section" }, "supportS6URL": { - "message": "Address of the webpage:", + "message": "כתובת דף האינטרנט:", "description": "Label for the URL of the page" }, "supportS6Select1": { - "message": "The webpage…", + "message": "דף האינטרנט…", "description": "Label for widget to select type of issue" }, "supportS6Select1Option0": { - "message": "-- Pick an entry --", + "message": "-- בחר קטגוריה --", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option1": { - "message": "Shows ads or ad leftovers", + "message": "הצגת פרסומות או שאריות שלהן", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option2": { - "message": "Has overlays or other nuisances", + "message": "קיים ריבוד או מטרד אחר", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option3": { - "message": "Detects uBO Lite", + "message": "מזהה את uBO Lite", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option4": { - "message": "Has privacy-related issues", + "message": "בעיות הקשורות לפרטיות", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option5": { - "message": "Malfunctions when uBO Lite is enabled", + "message": "כשל תפעולי כאשר uBO Lite פעיל", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option6": { - "message": "Opens unwanted tabs or windows", + "message": "נפתחים לשוניות או חלונות לא רצויים", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option7": { - "message": "Leads to badware, phishing", + "message": "מוביל לנוזקה, פישינג", "description": "An entry in the widget used to select the type of issue" }, "supportS6Checkbox1": { - "message": "Label the webpage as “NSFW” (“Not Safe For Work”)", + "message": "תייג את הדף כ \"NSFW\" (“Not Safe For Work” - לא בטוח למקום העבודה)", "description": "A checkbox to use for NSFW sites" }, "supportReportSpecificButton": { - "message": "Create new report", + "message": "צור דיווח חדש", "description": "Text for button which open an external webpage in Support pane" }, "firstRunSectionLabel": { diff --git a/platform/mv3/extension/_locales/hr/messages.json b/platform/mv3/extension/_locales/hr/messages.json index 1a5ad1da7eaba..1834c868831d6 100644 --- a/platform/mv3/extension/_locales/hr/messages.json +++ b/platform/mv3/extension/_locales/hr/messages.json @@ -32,7 +32,7 @@ "description": "Label in the popup panel for the current filtering mode" }, "popupTipReport": { - "message": "Report an issue on this website", + "message": "Prijavite problem na ovoj web stranici", "description": "Tooltip used for the 'chat' icon in the panel" }, "popupTipDashboard": { @@ -104,67 +104,67 @@ "description": "Shown in the About pane" }, "supportS6H": { - "message": "Report a filter issue", + "message": "Prijavi problem sa filterom", "description": "Header of 'Report a filter issue' section in Support pane" }, "supportS3P1": { - "message": "Report filter issues with specific websites to the uBlockOrigin/uAssets issue tracker. Requires a GitHub account.", + "message": "Prijavite probleme s filtrima s određenim web-lokacijama uBlockOrigin/uAssets alatu za praćenje problema. Potreban je GitHub račun.", "description": "First paragraph of 'Filter issues' section in Support pane" }, "supportS6P1S1": { - "message": "To avoid burdening volunteers with duplicate reports, please verify that the issue has not already been reported.", + "message": "Kako biste izbjegli opterećivanje volontera duplim prijavama, provjerite nije li problem već prijavljen.", "description": "A paragraph in the filter issue reporter section" }, "supportFindSpecificButton": { - "message": "Find similar reports", + "message": "Nađi slične prijave", "description": "A clickable link in the filter issue reporter section" }, "supportS6URL": { - "message": "Address of the webpage:", + "message": "Adresa web stranice:", "description": "Label for the URL of the page" }, "supportS6Select1": { - "message": "The webpage…", + "message": "Web stranica...", "description": "Label for widget to select type of issue" }, "supportS6Select1Option0": { - "message": "-- Pick an entry --", + "message": "-- Odaberite unos --", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option1": { - "message": "Shows ads or ad leftovers", + "message": "Prikazuje oglase ili ostatke oglasa", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option2": { - "message": "Has overlays or other nuisances", + "message": "Ima overlaye ili druge smetnje", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option3": { - "message": "Detects uBO Lite", + "message": "Otkriva uBO Lite", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option4": { - "message": "Has privacy-related issues", + "message": "Ima problema u vezi s privatnošću", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option5": { - "message": "Malfunctions when uBO Lite is enabled", + "message": "Neispravno kada je uBO Lite omogućen", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option6": { - "message": "Opens unwanted tabs or windows", + "message": "Otvara neželjene kartice ili prozore", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option7": { - "message": "Leads to badware, phishing", + "message": "Vodi do zloćudnog softvera, krađe identiteta", "description": "An entry in the widget used to select the type of issue" }, "supportS6Checkbox1": { - "message": "Label the webpage as “NSFW” (“Not Safe For Work”)", + "message": "Označite web stranicu kao “NSFW” (“Nije sigurno za rad”)", "description": "A checkbox to use for NSFW sites" }, "supportReportSpecificButton": { - "message": "Create new report", + "message": "Napravi novu prijavu", "description": "Text for button which open an external webpage in Support pane" }, "firstRunSectionLabel": { diff --git a/platform/mv3/extension/_locales/it/messages.json b/platform/mv3/extension/_locales/it/messages.json index 52df0e5306c14..35bbc6c0ecb63 100644 --- a/platform/mv3/extension/_locales/it/messages.json +++ b/platform/mv3/extension/_locales/it/messages.json @@ -32,7 +32,7 @@ "description": "Label in the popup panel for the current filtering mode" }, "popupTipReport": { - "message": "Report an issue on this website", + "message": "Segnala un problema su questo sito web", "description": "Tooltip used for the 'chat' icon in the panel" }, "popupTipDashboard": { @@ -104,67 +104,67 @@ "description": "Shown in the About pane" }, "supportS6H": { - "message": "Report a filter issue", + "message": "Segnala un problema di filtro", "description": "Header of 'Report a filter issue' section in Support pane" }, "supportS3P1": { - "message": "Report filter issues with specific websites to the uBlockOrigin/uAssets issue tracker. Requires a GitHub account.", + "message": "Segnala i problemi di filtraggio con siti web specifici su uBlockOrigin/uAssets issue tracker. Richiede un account GitHub.", "description": "First paragraph of 'Filter issues' section in Support pane" }, "supportS6P1S1": { - "message": "To avoid burdening volunteers with duplicate reports, please verify that the issue has not already been reported.", + "message": "Per evitare di gravare sui volontari con segnalazioni doppie, verifica che il problema non sia già stato segnalato.", "description": "A paragraph in the filter issue reporter section" }, "supportFindSpecificButton": { - "message": "Find similar reports", + "message": "Trova segnalazioni simili", "description": "A clickable link in the filter issue reporter section" }, "supportS6URL": { - "message": "Address of the webpage:", + "message": "Indirizzo della pagina web:", "description": "Label for the URL of the page" }, "supportS6Select1": { - "message": "The webpage…", + "message": "La pagina web...", "description": "Label for widget to select type of issue" }, "supportS6Select1Option0": { - "message": "-- Pick an entry --", + "message": "Scegli una voce", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option1": { - "message": "Shows ads or ad leftovers", + "message": "Mostra pubblicità o avanzi di pubblicità", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option2": { - "message": "Has overlays or other nuisances", + "message": "Presenta sovrapposizioni o altri disturbi", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option3": { - "message": "Detects uBO Lite", + "message": "Rileva uBO Lite", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option4": { - "message": "Has privacy-related issues", + "message": "Ha problemi relativi alla privacy", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option5": { - "message": "Malfunctions when uBO Lite is enabled", + "message": "Malfunzionamenti quando uBO Lite è abilitato", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option6": { - "message": "Opens unwanted tabs or windows", + "message": "Apre schede o finestre indesiderate", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option7": { - "message": "Leads to badware, phishing", + "message": "Porta a badware, phishing", "description": "An entry in the widget used to select the type of issue" }, "supportS6Checkbox1": { - "message": "Label the webpage as “NSFW” (“Not Safe For Work”)", + "message": "Contrassegna la pagina web come “NSFW”. (“Not Safe For Work”)", "description": "A checkbox to use for NSFW sites" }, "supportReportSpecificButton": { - "message": "Create new report", + "message": "Crea una nuova segnalazione", "description": "Text for button which open an external webpage in Support pane" }, "firstRunSectionLabel": { diff --git a/platform/mv3/extension/_locales/ja/messages.json b/platform/mv3/extension/_locales/ja/messages.json index eb7a89af2b45c..0bbb9a0e36c06 100644 --- a/platform/mv3/extension/_locales/ja/messages.json +++ b/platform/mv3/extension/_locales/ja/messages.json @@ -32,7 +32,7 @@ "description": "Label in the popup panel for the current filtering mode" }, "popupTipReport": { - "message": "Report an issue on this website", + "message": "このサイト上での問題を報告", "description": "Tooltip used for the 'chat' icon in the panel" }, "popupTipDashboard": { @@ -104,67 +104,67 @@ "description": "Shown in the About pane" }, "supportS6H": { - "message": "Report a filter issue", + "message": "フィルターの問題を報告する", "description": "Header of 'Report a filter issue' section in Support pane" }, "supportS3P1": { - "message": "Report filter issues with specific websites to the uBlockOrigin/uAssets issue tracker. Requires a GitHub account.", + "message": "uBlockOrigin/uAssets issue tracker で特定のウェブサイトでのフィルターの問題を報告します。GitHub アカウントが必要です", "description": "First paragraph of 'Filter issues' section in Support pane" }, "supportS6P1S1": { - "message": "To avoid burdening volunteers with duplicate reports, please verify that the issue has not already been reported.", + "message": "重複した報告によってボランティアに負担をかけないように、問題がすでに報告されていないか確認してください。", "description": "A paragraph in the filter issue reporter section" }, "supportFindSpecificButton": { - "message": "Find similar reports", + "message": "似た報告を探す", "description": "A clickable link in the filter issue reporter section" }, "supportS6URL": { - "message": "Address of the webpage:", + "message": "ウェブページのアドレス:", "description": "Label for the URL of the page" }, "supportS6Select1": { - "message": "The webpage…", + "message": "ウェブページ…", "description": "Label for widget to select type of issue" }, "supportS6Select1Option0": { - "message": "-- Pick an entry --", + "message": "-- エントリーを選択 --", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option1": { - "message": "Shows ads or ad leftovers", + "message": "広告または消し残りが表示される", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option2": { - "message": "Has overlays or other nuisances", + "message": "オーバーレイなど邪魔なものがある", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option3": { - "message": "Detects uBO Lite", + "message": "uBO Lite が検出される", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option4": { - "message": "Has privacy-related issues", + "message": "プライバシーに関連する問題がある", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option5": { - "message": "Malfunctions when uBO Lite is enabled", + "message": "uBO Lite 有効時に機能しなくなる", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option6": { - "message": "Opens unwanted tabs or windows", + "message": "勝手にタブやウィンドウが開く", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option7": { - "message": "Leads to badware, phishing", + "message": "悪質ソフトやフィッシングへの誘導", "description": "An entry in the widget used to select the type of issue" }, "supportS6Checkbox1": { - "message": "Label the webpage as “NSFW” (“Not Safe For Work”)", + "message": "ウェブページを “NSFW” (“Not Safe For Work”) としてラベル付け", "description": "A checkbox to use for NSFW sites" }, "supportReportSpecificButton": { - "message": "Create new report", + "message": "新しい報告を作成", "description": "Text for button which open an external webpage in Support pane" }, "firstRunSectionLabel": { diff --git a/platform/mv3/extension/_locales/nl/messages.json b/platform/mv3/extension/_locales/nl/messages.json index 1044db20c2840..07f45e9c4b0bb 100644 --- a/platform/mv3/extension/_locales/nl/messages.json +++ b/platform/mv3/extension/_locales/nl/messages.json @@ -32,7 +32,7 @@ "description": "Label in the popup panel for the current filtering mode" }, "popupTipReport": { - "message": "Report an issue on this website", + "message": "Een probleem op deze website melden", "description": "Tooltip used for the 'chat' icon in the panel" }, "popupTipDashboard": { @@ -104,67 +104,67 @@ "description": "Shown in the About pane" }, "supportS6H": { - "message": "Report a filter issue", + "message": "Een filterprobleem melden", "description": "Header of 'Report a filter issue' section in Support pane" }, "supportS3P1": { - "message": "Report filter issues with specific websites to the uBlockOrigin/uAssets issue tracker. Requires a GitHub account.", + "message": "Meld filterproblemen met specifieke websites in de uBlockOrigin/uAssets-probleemtracker. Vereist een GitHub-account.", "description": "First paragraph of 'Filter issues' section in Support pane" }, "supportS6P1S1": { - "message": "To avoid burdening volunteers with duplicate reports, please verify that the issue has not already been reported.", + "message": "Controleer of het probleem niet eerder is gemeld om te voorkomen dat vrijwilligers met dubbele meldingen worden belast.", "description": "A paragraph in the filter issue reporter section" }, "supportFindSpecificButton": { - "message": "Find similar reports", + "message": "Soortgelijke meldingen zoeken", "description": "A clickable link in the filter issue reporter section" }, "supportS6URL": { - "message": "Address of the webpage:", + "message": "Adres van de webpagina:", "description": "Label for the URL of the page" }, "supportS6Select1": { - "message": "The webpage…", + "message": "De webpagina…", "description": "Label for widget to select type of issue" }, "supportS6Select1Option0": { - "message": "-- Pick an entry --", + "message": "-- Maak een keuze --", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option1": { - "message": "Shows ads or ad leftovers", + "message": "Toont advertenties of restanten", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option2": { - "message": "Has overlays or other nuisances", + "message": "Heeft overlappingen of andere ongemakken", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option3": { - "message": "Detects uBO Lite", + "message": "Detecteert uBO Lite", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option4": { - "message": "Has privacy-related issues", + "message": "Heeft privacy-gerelateerde problemen", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option5": { - "message": "Malfunctions when uBO Lite is enabled", + "message": "Werkt niet als uBO Lite is ingeschakeld", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option6": { - "message": "Opens unwanted tabs or windows", + "message": "Opent ongewenste tabbladen of vensters", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option7": { - "message": "Leads to badware, phishing", + "message": "Leidt tot badware, phishing", "description": "An entry in the widget used to select the type of issue" }, "supportS6Checkbox1": { - "message": "Label the webpage as “NSFW” (“Not Safe For Work”)", + "message": "De webpagina labelen als ‘NSFW’ (‘Not Safe For Work’)", "description": "A checkbox to use for NSFW sites" }, "supportReportSpecificButton": { - "message": "Create new report", + "message": "Nieuwe melding maken", "description": "Text for button which open an external webpage in Support pane" }, "firstRunSectionLabel": { diff --git a/platform/mv3/extension/_locales/pl/messages.json b/platform/mv3/extension/_locales/pl/messages.json index 6aa14a77ebcaa..5ecb3940621c0 100644 --- a/platform/mv3/extension/_locales/pl/messages.json +++ b/platform/mv3/extension/_locales/pl/messages.json @@ -32,7 +32,7 @@ "description": "Label in the popup panel for the current filtering mode" }, "popupTipReport": { - "message": "Report an issue on this website", + "message": "Zgłoś problem z tą stroną", "description": "Tooltip used for the 'chat' icon in the panel" }, "popupTipDashboard": { @@ -104,67 +104,67 @@ "description": "Shown in the About pane" }, "supportS6H": { - "message": "Report a filter issue", + "message": "Zgłoś problem z filtrem", "description": "Header of 'Report a filter issue' section in Support pane" }, "supportS3P1": { - "message": "Report filter issues with specific websites to the uBlockOrigin/uAssets issue tracker. Requires a GitHub account.", + "message": "Zgłoś problemy z filtrami dotyczące konkretnych witryn internetowych do systemu śledzenia problemów uBlockOrigin/uAssets. Wymagane jest konto GitHub.", "description": "First paragraph of 'Filter issues' section in Support pane" }, "supportS6P1S1": { - "message": "To avoid burdening volunteers with duplicate reports, please verify that the issue has not already been reported.", + "message": "Aby uniknąć obciążania wolontariuszy zduplikowanymi zgłoszeniami, sprawdź, czy problem nie został już zgłoszony.", "description": "A paragraph in the filter issue reporter section" }, "supportFindSpecificButton": { - "message": "Find similar reports", + "message": "Znajdź podobne raporty", "description": "A clickable link in the filter issue reporter section" }, "supportS6URL": { - "message": "Address of the webpage:", + "message": "Adres strony internetowej:", "description": "Label for the URL of the page" }, "supportS6Select1": { - "message": "The webpage…", + "message": "Strona internetowa…", "description": "Label for widget to select type of issue" }, "supportS6Select1Option0": { - "message": "-- Pick an entry --", + "message": "— Wybierz pozycję —", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option1": { - "message": "Shows ads or ad leftovers", + "message": "Wyświetla reklamy lub ich pozostałości", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option2": { - "message": "Has overlays or other nuisances", + "message": "Ma nakładki lub inne niedogodności", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option3": { - "message": "Detects uBO Lite", + "message": "Wykrywa uBO Lite", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option4": { - "message": "Has privacy-related issues", + "message": "Ma problemy związane z prywatnością", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option5": { - "message": "Malfunctions when uBO Lite is enabled", + "message": "Nieprawidłowe działanie po włączeniu uBO Lite", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option6": { - "message": "Opens unwanted tabs or windows", + "message": "Otwiera niepożądane karty lub okna", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option7": { - "message": "Leads to badware, phishing", + "message": "Prowadzi do szkodliwego oprogramowania, phishingu", "description": "An entry in the widget used to select the type of issue" }, "supportS6Checkbox1": { - "message": "Label the webpage as “NSFW” (“Not Safe For Work”)", + "message": "Oznacz stronę internetową jako „NSFW” („Not Safe For Work”)", "description": "A checkbox to use for NSFW sites" }, "supportReportSpecificButton": { - "message": "Create new report", + "message": "Utwórz nowy raport", "description": "Text for button which open an external webpage in Support pane" }, "firstRunSectionLabel": { diff --git a/platform/mv3/extension/_locales/pt_BR/messages.json b/platform/mv3/extension/_locales/pt_BR/messages.json index 52757d7a8f719..66f46188de874 100644 --- a/platform/mv3/extension/_locales/pt_BR/messages.json +++ b/platform/mv3/extension/_locales/pt_BR/messages.json @@ -32,7 +32,7 @@ "description": "Label in the popup panel for the current filtering mode" }, "popupTipReport": { - "message": "Report an issue on this website", + "message": "Reportar um problema neste site da web", "description": "Tooltip used for the 'chat' icon in the panel" }, "popupTipDashboard": { @@ -104,67 +104,67 @@ "description": "Shown in the About pane" }, "supportS6H": { - "message": "Report a filter issue", + "message": "Reportar um problema com o filtro", "description": "Header of 'Report a filter issue' section in Support pane" }, "supportS3P1": { - "message": "Report filter issues with specific websites to the uBlockOrigin/uAssets issue tracker. Requires a GitHub account.", + "message": "Reporte problemas dos filtros com sites da web específicos no rastreador de problemas uBlockOrigin/uAssets. Requer uma conta no GitHub.", "description": "First paragraph of 'Filter issues' section in Support pane" }, "supportS6P1S1": { - "message": "To avoid burdening volunteers with duplicate reports, please verify that the issue has not already been reported.", + "message": "Pra evitar sobrecarregar os voluntários com relatórios duplicados por favor verifique se o problema já não foi reportado.", "description": "A paragraph in the filter issue reporter section" }, "supportFindSpecificButton": { - "message": "Find similar reports", + "message": "Achar relatórios similares", "description": "A clickable link in the filter issue reporter section" }, "supportS6URL": { - "message": "Address of the webpage:", + "message": "Endereço da página da web:", "description": "Label for the URL of the page" }, "supportS6Select1": { - "message": "The webpage…", + "message": "A página da web…", "description": "Label for widget to select type of issue" }, "supportS6Select1Option0": { - "message": "-- Pick an entry --", + "message": "— Escolha uma entrada —", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option1": { - "message": "Shows ads or ad leftovers", + "message": "Mostra os anúncios ou restos de anúncios", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option2": { - "message": "Has overlays or other nuisances", + "message": "Tem sobreposições ou outros incômodos", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option3": { - "message": "Detects uBO Lite", + "message": "Detecta o uBO Lite", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option4": { - "message": "Has privacy-related issues", + "message": "Tem problemas relacionados a privacidade", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option5": { - "message": "Malfunctions when uBO Lite is enabled", + "message": "Funciona mal quando o uBO Lite está ativado", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option6": { - "message": "Opens unwanted tabs or windows", + "message": "Abre abas ou janelas indesejadas", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option7": { - "message": "Leads to badware, phishing", + "message": "Leva a badware, phishing", "description": "An entry in the widget used to select the type of issue" }, "supportS6Checkbox1": { - "message": "Label the webpage as “NSFW” (“Not Safe For Work”)", + "message": "Rotular a página da web como “NSFW” (“Not Safe For Work”)", "description": "A checkbox to use for NSFW sites" }, "supportReportSpecificButton": { - "message": "Create new report", + "message": "Criar novo relatório", "description": "Text for button which open an external webpage in Support pane" }, "firstRunSectionLabel": { diff --git a/platform/mv3/extension/_locales/ru/messages.json b/platform/mv3/extension/_locales/ru/messages.json index f6074ff68faa6..841b51491bd0b 100644 --- a/platform/mv3/extension/_locales/ru/messages.json +++ b/platform/mv3/extension/_locales/ru/messages.json @@ -32,7 +32,7 @@ "description": "Label in the popup panel for the current filtering mode" }, "popupTipReport": { - "message": "Report an issue on this website", + "message": "Сообщить о проблеме на этом сайте", "description": "Tooltip used for the 'chat' icon in the panel" }, "popupTipDashboard": { @@ -104,67 +104,67 @@ "description": "Shown in the About pane" }, "supportS6H": { - "message": "Report a filter issue", + "message": "Сообщить о проблеме в фильтре", "description": "Header of 'Report a filter issue' section in Support pane" }, "supportS3P1": { - "message": "Report filter issues with specific websites to the uBlockOrigin/uAssets issue tracker. Requires a GitHub account.", + "message": "Сообщайте о проблемах с фильтрами на определённых сайтах в трекер ошибок uBlockOrigin/uAssets. Требуется учётная запись в GitHub.", "description": "First paragraph of 'Filter issues' section in Support pane" }, "supportS6P1S1": { - "message": "To avoid burdening volunteers with duplicate reports, please verify that the issue has not already been reported.", + "message": "Чтобы не нагружать добровольцев повторяющимися сообщениями, убедитесь, что о вашей проблеме ещё не писали.", "description": "A paragraph in the filter issue reporter section" }, "supportFindSpecificButton": { - "message": "Find similar reports", + "message": "Найти похожие сообщения", "description": "A clickable link in the filter issue reporter section" }, "supportS6URL": { - "message": "Address of the webpage:", + "message": "Адрес веб-страницы:", "description": "Label for the URL of the page" }, "supportS6Select1": { - "message": "The webpage…", + "message": "Веб-страница…", "description": "Label for widget to select type of issue" }, "supportS6Select1Option0": { - "message": "-- Pick an entry --", + "message": "-- Выбрать тип --", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option1": { - "message": "Shows ads or ad leftovers", + "message": "Показывает рекламу или её остатки", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option2": { - "message": "Has overlays or other nuisances", + "message": "Содержит наложения или прочие помехи", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option3": { - "message": "Detects uBO Lite", + "message": "Определяет uBO Lite", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option4": { - "message": "Has privacy-related issues", + "message": "Содержит проблемы с конфиденциальностью", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option5": { - "message": "Malfunctions when uBO Lite is enabled", + "message": "Неполадки при работе uBO Lite", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option6": { - "message": "Opens unwanted tabs or windows", + "message": "Открываются нежелательные вкладки или окна", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option7": { - "message": "Leads to badware, phishing", + "message": "Вредоносное ПО, фишинг", "description": "An entry in the widget used to select the type of issue" }, "supportS6Checkbox1": { - "message": "Label the webpage as “NSFW” (“Not Safe For Work”)", + "message": "Пометить эту страницу «небезопасной для просмотра на работе» («NSFW»)", "description": "A checkbox to use for NSFW sites" }, "supportReportSpecificButton": { - "message": "Create new report", + "message": "Создать новое сообщение", "description": "Text for button which open an external webpage in Support pane" }, "firstRunSectionLabel": { diff --git a/platform/mv3/extension/_locales/sk/messages.json b/platform/mv3/extension/_locales/sk/messages.json index cceb6c88b6132..3354d50d4de24 100644 --- a/platform/mv3/extension/_locales/sk/messages.json +++ b/platform/mv3/extension/_locales/sk/messages.json @@ -32,7 +32,7 @@ "description": "Label in the popup panel for the current filtering mode" }, "popupTipReport": { - "message": "Report an issue on this website", + "message": "Nahlásiť problém na tejto webovej stránke", "description": "Tooltip used for the 'chat' icon in the panel" }, "popupTipDashboard": { @@ -104,67 +104,67 @@ "description": "Shown in the About pane" }, "supportS6H": { - "message": "Report a filter issue", + "message": "Nahlásiť problém s filtrom", "description": "Header of 'Report a filter issue' section in Support pane" }, "supportS3P1": { - "message": "Report filter issues with specific websites to the uBlockOrigin/uAssets issue tracker. Requires a GitHub account.", + "message": "Nahlásenie problémov s filtrom s konkrétnymi webovými stránkami na uBlockOrigin/uAssets issue tracker. Vyžaduje sa GitHub účet.", "description": "First paragraph of 'Filter issues' section in Support pane" }, "supportS6P1S1": { - "message": "To avoid burdening volunteers with duplicate reports, please verify that the issue has not already been reported.", + "message": "Aby ste dobrovoľníkov nezaťažovali duplicitnými hláseniami, overte si, či už problém nebol nahlásený.", "description": "A paragraph in the filter issue reporter section" }, "supportFindSpecificButton": { - "message": "Find similar reports", + "message": "Vyhľadať podobné hlásenia", "description": "A clickable link in the filter issue reporter section" }, "supportS6URL": { - "message": "Address of the webpage:", + "message": "Adresa webovej stránky:", "description": "Label for the URL of the page" }, "supportS6Select1": { - "message": "The webpage…", + "message": "Webová stránka…", "description": "Label for widget to select type of issue" }, "supportS6Select1Option0": { - "message": "-- Pick an entry --", + "message": "-- Vyberte položku --", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option1": { - "message": "Shows ads or ad leftovers", + "message": "Zobrazuje reklamy alebo zvyšky reklám", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option2": { - "message": "Has overlays or other nuisances", + "message": "Je prekrytá alebo má iné nedostatky", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option3": { - "message": "Detects uBO Lite", + "message": "Detegovaný uBO Lite", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option4": { - "message": "Has privacy-related issues", + "message": "Má problémy súvisiace so súkromím", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option5": { - "message": "Malfunctions when uBO Lite is enabled", + "message": "Poruchy pri povolenom uBO Lite", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option6": { - "message": "Opens unwanted tabs or windows", + "message": "Otvára nechcené karty alebo okná", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option7": { - "message": "Leads to badware, phishing", + "message": "Smeruje k badvéru a phishingu", "description": "An entry in the widget used to select the type of issue" }, "supportS6Checkbox1": { - "message": "Label the webpage as “NSFW” (“Not Safe For Work”)", + "message": "Označiť webovú stránku ako \"NSFW\" (“Not Safe For Work”)", "description": "A checkbox to use for NSFW sites" }, "supportReportSpecificButton": { - "message": "Create new report", + "message": "Vytvoriť nové hlásenie", "description": "Text for button which open an external webpage in Support pane" }, "firstRunSectionLabel": { diff --git a/platform/mv3/extension/_locales/sv/messages.json b/platform/mv3/extension/_locales/sv/messages.json index 2431f0b9ab750..7ef1749a8cd68 100644 --- a/platform/mv3/extension/_locales/sv/messages.json +++ b/platform/mv3/extension/_locales/sv/messages.json @@ -32,7 +32,7 @@ "description": "Label in the popup panel for the current filtering mode" }, "popupTipReport": { - "message": "Report an issue on this website", + "message": "Rapportera ett problem på denna webbplats", "description": "Tooltip used for the 'chat' icon in the panel" }, "popupTipDashboard": { @@ -104,67 +104,67 @@ "description": "Shown in the About pane" }, "supportS6H": { - "message": "Report a filter issue", + "message": "Rapportera ett filterproblem", "description": "Header of 'Report a filter issue' section in Support pane" }, "supportS3P1": { - "message": "Report filter issues with specific websites to the uBlockOrigin/uAssets issue tracker. Requires a GitHub account.", + "message": "Rapportera filterproblem med specifika webbplatser till uBlockOrigin/uAssets problemhanteringssystemet. Kräver ett GitHub-konto.", "description": "First paragraph of 'Filter issues' section in Support pane" }, "supportS6P1S1": { - "message": "To avoid burdening volunteers with duplicate reports, please verify that the issue has not already been reported.", + "message": "För att undvika att belasta volontärer med dubbletter av rapporter, kontrollera att problemet inte redan har rapporterats.", "description": "A paragraph in the filter issue reporter section" }, "supportFindSpecificButton": { - "message": "Find similar reports", + "message": "Hitta liknande rapporter", "description": "A clickable link in the filter issue reporter section" }, "supportS6URL": { - "message": "Address of the webpage:", + "message": "Hemsidans adress:", "description": "Label for the URL of the page" }, "supportS6Select1": { - "message": "The webpage…", + "message": "Hemsidan...", "description": "Label for widget to select type of issue" }, "supportS6Select1Option0": { - "message": "-- Pick an entry --", + "message": "-- Välj en post --", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option1": { - "message": "Shows ads or ad leftovers", + "message": "Visar annonser eller rester av annonser", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option2": { - "message": "Has overlays or other nuisances", + "message": "Har överlägg eller andra olägenheter", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option3": { - "message": "Detects uBO Lite", + "message": "Upptäcker uBO Lite", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option4": { - "message": "Has privacy-related issues", + "message": "Har integritetsrelaterade problem", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option5": { - "message": "Malfunctions when uBO Lite is enabled", + "message": "Fungerar inte när uBO Lite är aktiverad", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option6": { - "message": "Opens unwanted tabs or windows", + "message": "Öppnar oönskade flikar eller fönster", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option7": { - "message": "Leads to badware, phishing", + "message": "Leder till skadlig programvara, nätfiske", "description": "An entry in the widget used to select the type of issue" }, "supportS6Checkbox1": { - "message": "Label the webpage as “NSFW” (“Not Safe For Work”)", + "message": "Märk webbsidan som “NSFW” (“Inte lämplig på jobbet”)", "description": "A checkbox to use for NSFW sites" }, "supportReportSpecificButton": { - "message": "Create new report", + "message": "Skapa ny rapport", "description": "Text for button which open an external webpage in Support pane" }, "firstRunSectionLabel": { diff --git a/platform/mv3/extension/_locales/tr/messages.json b/platform/mv3/extension/_locales/tr/messages.json index 259d5412259af..175effdcc1ad9 100644 --- a/platform/mv3/extension/_locales/tr/messages.json +++ b/platform/mv3/extension/_locales/tr/messages.json @@ -32,7 +32,7 @@ "description": "Label in the popup panel for the current filtering mode" }, "popupTipReport": { - "message": "Report an issue on this website", + "message": "Bu sitedeki bir sorunu bildir", "description": "Tooltip used for the 'chat' icon in the panel" }, "popupTipDashboard": { @@ -104,67 +104,67 @@ "description": "Shown in the About pane" }, "supportS6H": { - "message": "Report a filter issue", + "message": "Bir filtre sorununu bildir", "description": "Header of 'Report a filter issue' section in Support pane" }, "supportS3P1": { - "message": "Report filter issues with specific websites to the uBlockOrigin/uAssets issue tracker. Requires a GitHub account.", + "message": "Bir sitedeki filtre sorunlarını bildirmek için uBlockOrigin/uAssets issue tracker kullanın. Bir GitHub hesabı gerekir.", "description": "First paragraph of 'Filter issues' section in Support pane" }, "supportS6P1S1": { - "message": "To avoid burdening volunteers with duplicate reports, please verify that the issue has not already been reported.", + "message": "Gönüllüleri benzer raporlar ile bezdirmemek için sorunun zaten bildirilip bildirilmediğine bakın.", "description": "A paragraph in the filter issue reporter section" }, "supportFindSpecificButton": { - "message": "Find similar reports", + "message": "Benzer raporları bul", "description": "A clickable link in the filter issue reporter section" }, "supportS6URL": { - "message": "Address of the webpage:", + "message": "Web sayfasının adresi:", "description": "Label for the URL of the page" }, "supportS6Select1": { - "message": "The webpage…", + "message": "Web sayfası…", "description": "Label for widget to select type of issue" }, "supportS6Select1Option0": { - "message": "-- Pick an entry --", + "message": "-- Bir girdi seçin --", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option1": { - "message": "Shows ads or ad leftovers", + "message": "Reklamlar veya reklam artıkları gösteriyor", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option2": { - "message": "Has overlays or other nuisances", + "message": "Arayüzde kaplamalar veya diğer sıkıntıları var", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option3": { - "message": "Detects uBO Lite", + "message": "uBO Lite tespit ediliyor", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option4": { - "message": "Has privacy-related issues", + "message": "Gizlilikle ilgili sorunları var", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option5": { - "message": "Malfunctions when uBO Lite is enabled", + "message": "uBO Lite kullanımdayken sayfa bozuluyor ", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option6": { - "message": "Opens unwanted tabs or windows", + "message": "İstenmeyen sekme veya pencereler açıyor", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option7": { - "message": "Leads to badware, phishing", + "message": "Kötü niyetli yazılıma yönlendiriyor, oltalama", "description": "An entry in the widget used to select the type of issue" }, "supportS6Checkbox1": { - "message": "Label the webpage as “NSFW” (“Not Safe For Work”)", + "message": "Sayfayı “NSFW” (“iş için güvenli değil”) olarak işaretle", "description": "A checkbox to use for NSFW sites" }, "supportReportSpecificButton": { - "message": "Create new report", + "message": "Yeni rapor oluştur", "description": "Text for button which open an external webpage in Support pane" }, "firstRunSectionLabel": { diff --git a/platform/mv3/extension/_locales/uk/messages.json b/platform/mv3/extension/_locales/uk/messages.json index f3b1f1a8c9338..b5826bc50029b 100644 --- a/platform/mv3/extension/_locales/uk/messages.json +++ b/platform/mv3/extension/_locales/uk/messages.json @@ -32,7 +32,7 @@ "description": "Label in the popup panel for the current filtering mode" }, "popupTipReport": { - "message": "Report an issue on this website", + "message": "Повідомити про помилку на цьому вебсайті", "description": "Tooltip used for the 'chat' icon in the panel" }, "popupTipDashboard": { @@ -152,7 +152,7 @@ "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option6": { - "message": "Opens unwanted tabs or windows", + "message": "Відкриває небажані вкладки або вікна", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option7": { @@ -164,7 +164,7 @@ "description": "A checkbox to use for NSFW sites" }, "supportReportSpecificButton": { - "message": "Create new report", + "message": "Створити новий звіт", "description": "Text for button which open an external webpage in Support pane" }, "firstRunSectionLabel": { diff --git a/platform/mv3/extension/_locales/zh_TW/messages.json b/platform/mv3/extension/_locales/zh_TW/messages.json index d1e273552b342..d3eec22438c34 100644 --- a/platform/mv3/extension/_locales/zh_TW/messages.json +++ b/platform/mv3/extension/_locales/zh_TW/messages.json @@ -32,7 +32,7 @@ "description": "Label in the popup panel for the current filtering mode" }, "popupTipReport": { - "message": "Report an issue on this website", + "message": "回報此網站的問題", "description": "Tooltip used for the 'chat' icon in the panel" }, "popupTipDashboard": { @@ -104,67 +104,67 @@ "description": "Shown in the About pane" }, "supportS6H": { - "message": "Report a filter issue", + "message": "回報過濾規則的問題", "description": "Header of 'Report a filter issue' section in Support pane" }, "supportS3P1": { - "message": "Report filter issues with specific websites to the uBlockOrigin/uAssets issue tracker. Requires a GitHub account.", + "message": "回報至特定網站的過濾器問題至 uBlockOrigin/uAssets 議題追蹤器需要 GitHub 帳號。", "description": "First paragraph of 'Filter issues' section in Support pane" }, "supportS6P1S1": { - "message": "To avoid burdening volunteers with duplicate reports, please verify that the issue has not already been reported.", + "message": "為免給志願者帶來額外負擔,請先檢查問題有沒有被回報過,避免重複回報。", "description": "A paragraph in the filter issue reporter section" }, "supportFindSpecificButton": { - "message": "Find similar reports", + "message": "尋找類似報告", "description": "A clickable link in the filter issue reporter section" }, "supportS6URL": { - "message": "Address of the webpage:", + "message": "網址:", "description": "Label for the URL of the page" }, "supportS6Select1": { - "message": "The webpage…", + "message": "網頁……", "description": "Label for widget to select type of issue" }, "supportS6Select1Option0": { - "message": "-- Pick an entry --", + "message": "-- 挑選一種情況 --", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option1": { - "message": "Shows ads or ad leftovers", + "message": "會顯示廣告或殘留空位", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option2": { - "message": "Has overlays or other nuisances", + "message": "含有覆蓋物或其他滋擾物", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option3": { - "message": "Detects uBO Lite", + "message": "偵測 uBO Lite", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option4": { - "message": "Has privacy-related issues", + "message": "有隱私權相關問題", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option5": { - "message": "Malfunctions when uBO Lite is enabled", + "message": "開啟 uBO Lite 的時候運作不正常", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option6": { - "message": "Opens unwanted tabs or windows", + "message": "會開啟不需要的分頁或視窗", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option7": { - "message": "Leads to badware, phishing", + "message": "導致惡意軟體、網路釣魚", "description": "An entry in the widget used to select the type of issue" }, "supportS6Checkbox1": { - "message": "Label the webpage as “NSFW” (“Not Safe For Work”)", + "message": "將網頁標記為「NSFW」(「工作場所不宜」)", "description": "A checkbox to use for NSFW sites" }, "supportReportSpecificButton": { - "message": "Create new report", + "message": "建立新報告", "description": "Text for button which open an external webpage in Support pane" }, "firstRunSectionLabel": { diff --git a/src/_locales/cs/messages.json b/src/_locales/cs/messages.json index 9e28071c50cd1..0ceaf90e5a8e7 100644 --- a/src/_locales/cs/messages.json +++ b/src/_locales/cs/messages.json @@ -928,7 +928,7 @@ "description": "Header of 'Filter issues' section in Support pane" }, "supportS3P1": { - "message": "Hlášení problémů s filtrem u učitých web. stránek do uBlockOrigin/uAssets issue tracker. Vyžaduje účet GitHub.", + "message": "Nahlaste problémy s filtrem u učitých webových stránek do sledovače problémů uBlockOrigin/uAssets. Vyžaduje účet GitHub.", "description": "First paragraph of 'Filter issues' section in Support pane" }, "supportS3P2": { @@ -1016,7 +1016,7 @@ "description": "An entry in the widget used to select the type of issue" }, "supportS6Checkbox1": { - "message": "Označit webovou stránku jako “NSFW” (Není bezpečné pro práci”)", + "message": "Označit webovou stránku jako “NSFW” (“Není bezpečné pro práci”)", "description": "A checkbox to use for NSFW sites" }, "supportRedact": { diff --git a/src/_locales/de/messages.json b/src/_locales/de/messages.json index af7d997b6412e..7290bf620b51a 100644 --- a/src/_locales/de/messages.json +++ b/src/_locales/de/messages.json @@ -900,11 +900,11 @@ "description": "Text for button which open an external webpage in Support pane" }, "supportReportSpecificButton": { - "message": "Neuen Bericht erstellen", + "message": "Neue Meldung erstellen", "description": "Text for button which open an external webpage in Support pane" }, "supportFindSpecificButton": { - "message": "Ähnliche Berichte suchen", + "message": "Ähnliche Meldungen finden", "description": "A clickable link in the filter issue reporter section" }, "supportS1H": { @@ -988,11 +988,11 @@ "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option1": { - "message": "Zeigt Werbung oder Anzeigenreste", + "message": "Zeigt Werbung oder Werbereste", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option2": { - "message": "Enthält Überdeckungen oder andere Belästigungen", + "message": "Hat Überdeckungen oder andere Belästigungen", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option3": { diff --git a/src/_locales/fr/messages.json b/src/_locales/fr/messages.json index 3e6731330100d..70c9d5920929f 100644 --- a/src/_locales/fr/messages.json +++ b/src/_locales/fr/messages.json @@ -1012,7 +1012,7 @@ "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option7": { - "message": "Conduit à/Redirige vers des logiciels malveillants, du hameçonnage...", + "message": "conduit à/redirige vers des logiciels malveillants, du hameçonnage", "description": "An entry in the widget used to select the type of issue" }, "supportS6Checkbox1": { From 63cf3dc959e0e8324aea7ac46e3d33128d45c2ea Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sun, 29 Sep 2024 12:50:55 -0400 Subject: [PATCH 0287/1099] Make Firefox dev build auto-update --- dist/firefox/updates.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dist/firefox/updates.json b/dist/firefox/updates.json index 69eb03cba6fdd..9b03f94f2941e 100644 --- a/dist/firefox/updates.json +++ b/dist/firefox/updates.json @@ -3,9 +3,9 @@ "uBlock0@raymondhill.net": { "updates": [ { - "version": "1.60.1.0", + "version": "1.60.1.1", "browser_specific_settings": { "gecko": { "strict_min_version": "78.0" } }, - "update_link": "https://github.com/gorhill/uBlock/releases/download/1.60.1b0/uBlock0_1.60.1b0.firefox.signed.xpi" + "update_link": "https://github.com/gorhill/uBlock/releases/download/1.60.1b1/uBlock0_1.60.1b1.firefox.signed.xpi" } ] } From 3d6984aeafa3f8115853d29d7fa918b869852ef9 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sun, 29 Sep 2024 17:06:51 -0400 Subject: [PATCH 0288/1099] Import translation work from https://crowdin.com/project/ublock --- .../mv3/extension/_locales/fi/messages.json | 34 +++++++++---------- .../mv3/extension/_locales/ru/messages.json | 16 ++++----- .../mv3/extension/_locales/uk/messages.json | 28 +++++++-------- src/_locales/fi/messages.json | 6 ++-- 4 files changed, 42 insertions(+), 42 deletions(-) diff --git a/platform/mv3/extension/_locales/fi/messages.json b/platform/mv3/extension/_locales/fi/messages.json index 6708ff96fa99c..4db333e85b415 100644 --- a/platform/mv3/extension/_locales/fi/messages.json +++ b/platform/mv3/extension/_locales/fi/messages.json @@ -32,7 +32,7 @@ "description": "Label in the popup panel for the current filtering mode" }, "popupTipReport": { - "message": "Report an issue on this website", + "message": "Ilmoita ongelmasta tällä verkkosivustolla", "description": "Tooltip used for the 'chat' icon in the panel" }, "popupTipDashboard": { @@ -104,67 +104,67 @@ "description": "Shown in the About pane" }, "supportS6H": { - "message": "Report a filter issue", + "message": "Ilmoita suodatinongelmasta", "description": "Header of 'Report a filter issue' section in Support pane" }, "supportS3P1": { - "message": "Report filter issues with specific websites to the uBlockOrigin/uAssets issue tracker. Requires a GitHub account.", + "message": "Ilmoita sivustokohtaisista suodatinongelmista uBlockOrigin/uAssets -ongelmaseurantaan. Vaatii GitHub-tilin.", "description": "First paragraph of 'Filter issues' section in Support pane" }, "supportS6P1S1": { - "message": "To avoid burdening volunteers with duplicate reports, please verify that the issue has not already been reported.", + "message": "Välttääksesi vapaaehtoisten kuormittamisen ylimääräisillä ilmoituksilla, tarkista ensin onko ongelmasta jo ilmoitettu.", "description": "A paragraph in the filter issue reporter section" }, "supportFindSpecificButton": { - "message": "Find similar reports", + "message": "Etsi samankaltaisia ilmoituksia", "description": "A clickable link in the filter issue reporter section" }, "supportS6URL": { - "message": "Address of the webpage:", + "message": "Verkkosivun osoite:", "description": "Label for the URL of the page" }, "supportS6Select1": { - "message": "The webpage…", + "message": "Verkkosivu…", "description": "Label for widget to select type of issue" }, "supportS6Select1Option0": { - "message": "-- Pick an entry --", + "message": "-- Valitse aihe --", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option1": { - "message": "Shows ads or ad leftovers", + "message": "Näyttää mainoksia tai niiden jäänteitä", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option2": { - "message": "Has overlays or other nuisances", + "message": "Sisältää peiteruutuja tai muita ärsykkeitä", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option3": { - "message": "Detects uBO Lite", + "message": "Tunnistaa uBO Liten", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option4": { - "message": "Has privacy-related issues", + "message": "Sisältää tietosuojaan liittyviä ongelmia", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option5": { - "message": "Malfunctions when uBO Lite is enabled", + "message": "Ei toimi oikein uBO Liten ollessa käytössä", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option6": { - "message": "Opens unwanted tabs or windows", + "message": "Avaa ei-toivottuja välilehtiä tai ikkunoita", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option7": { - "message": "Leads to badware, phishing", + "message": "Johtaa badwareen ja tietojenkalasteluun", "description": "An entry in the widget used to select the type of issue" }, "supportS6Checkbox1": { - "message": "Label the webpage as “NSFW” (“Not Safe For Work”)", + "message": "Luokittele verkkosivu \"NSFW\"-tyyppiseksi (\"Not Safe For Work\", eli ns. työpaikalle sopimattomaksi)", "description": "A checkbox to use for NSFW sites" }, "supportReportSpecificButton": { - "message": "Create new report", + "message": "Luo uusi ilmoitus", "description": "Text for button which open an external webpage in Support pane" }, "firstRunSectionLabel": { diff --git a/platform/mv3/extension/_locales/ru/messages.json b/platform/mv3/extension/_locales/ru/messages.json index 841b51491bd0b..e54dcead462a5 100644 --- a/platform/mv3/extension/_locales/ru/messages.json +++ b/platform/mv3/extension/_locales/ru/messages.json @@ -112,11 +112,11 @@ "description": "First paragraph of 'Filter issues' section in Support pane" }, "supportS6P1S1": { - "message": "Чтобы не нагружать добровольцев повторяющимися сообщениями, убедитесь, что о вашей проблеме ещё не писали.", + "message": "Чтобы не обременять волонтеров повторяющимися отчетами, пожалуйста, убедитесь, что о данной проблеме еще не сообщали.", "description": "A paragraph in the filter issue reporter section" }, "supportFindSpecificButton": { - "message": "Найти похожие сообщения", + "message": "Найти похожие отчеты", "description": "A clickable link in the filter issue reporter section" }, "supportS6URL": { @@ -128,23 +128,23 @@ "description": "Label for widget to select type of issue" }, "supportS6Select1Option0": { - "message": "-- Выбрать тип --", + "message": "-- Выберите категорию --", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option1": { - "message": "Показывает рекламу или её остатки", + "message": "Показывается реклама или ее заполнители", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option2": { - "message": "Содержит наложения или прочие помехи", + "message": "Всплывающие окна или другие помехи", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option3": { - "message": "Определяет uBO Lite", + "message": "Обнаруживается uBO Lite", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option4": { - "message": "Содержит проблемы с конфиденциальностью", + "message": "Проблемы, связанные с приватностью", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option5": { @@ -164,7 +164,7 @@ "description": "A checkbox to use for NSFW sites" }, "supportReportSpecificButton": { - "message": "Создать новое сообщение", + "message": "Создать новый отчет", "description": "Text for button which open an external webpage in Support pane" }, "firstRunSectionLabel": { diff --git a/platform/mv3/extension/_locales/uk/messages.json b/platform/mv3/extension/_locales/uk/messages.json index b5826bc50029b..eea2c7a415322 100644 --- a/platform/mv3/extension/_locales/uk/messages.json +++ b/platform/mv3/extension/_locales/uk/messages.json @@ -104,51 +104,51 @@ "description": "Shown in the About pane" }, "supportS6H": { - "message": "Report a filter issue", + "message": "Повідомити про ваду фільтра", "description": "Header of 'Report a filter issue' section in Support pane" }, "supportS3P1": { - "message": "Report filter issues with specific websites to the uBlockOrigin/uAssets issue tracker. Requires a GitHub account.", + "message": "Повідомляйте про вади с фільтрами на конкретних сайтах у трекер помилокuBlockOrigin/uAssets. Вимагає обліковий запис GitHub.", "description": "First paragraph of 'Filter issues' section in Support pane" }, "supportS6P1S1": { - "message": "To avoid burdening volunteers with duplicate reports, please verify that the issue has not already been reported.", + "message": "Щоб не обтяжувати волонтерів повторюваними звітами, переконайтеся, що про проблему ще не повідомлялося.", "description": "A paragraph in the filter issue reporter section" }, "supportFindSpecificButton": { - "message": "Find similar reports", + "message": "Знайти подібні звіти", "description": "A clickable link in the filter issue reporter section" }, "supportS6URL": { - "message": "Address of the webpage:", + "message": "Адреса вебсторінки:", "description": "Label for the URL of the page" }, "supportS6Select1": { - "message": "The webpage…", + "message": "Вебсторінка...", "description": "Label for widget to select type of issue" }, "supportS6Select1Option0": { - "message": "-- Pick an entry --", + "message": "-- Указати проблему --", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option1": { - "message": "Shows ads or ad leftovers", + "message": "З'являється реклама або залишки оголошень", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option2": { - "message": "Has overlays or other nuisances", + "message": "Накладання або інші прикрощі", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option3": { - "message": "Detects uBO Lite", + "message": "Виявляє uBO Lite", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option4": { - "message": "Has privacy-related issues", + "message": "Пов'язані з приватністю проблеми", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option5": { - "message": "Malfunctions when uBO Lite is enabled", + "message": "Вади коли uBO Lite включено", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option6": { @@ -156,11 +156,11 @@ "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option7": { - "message": "Leads to badware, phishing", + "message": "Веде до шкідливого ПЗ, фішингу", "description": "An entry in the widget used to select the type of issue" }, "supportS6Checkbox1": { - "message": "Label the webpage as “NSFW” (“Not Safe For Work”)", + "message": "Помітити цю сторінку як «Небезпечно для роботи» («NSFW»)", "description": "A checkbox to use for NSFW sites" }, "supportReportSpecificButton": { diff --git a/src/_locales/fi/messages.json b/src/_locales/fi/messages.json index 4d274bc102a57..8d8f045d1531b 100644 --- a/src/_locales/fi/messages.json +++ b/src/_locales/fi/messages.json @@ -928,7 +928,7 @@ "description": "Header of 'Filter issues' section in Support pane" }, "supportS3P1": { - "message": "Ilmoita ongelmista suodatuksessa tietyillä sivustoilla uBlockOrigin/uAssets-ongelmaseurannassa. Vaatii GitHub-tilin.", + "message": "Ilmoita sivustokohtaisista suodatinongelmista uBlockOrigin/uAssets -ongelmaseurantaan. Vaatii GitHub-tilin.", "description": "First paragraph of 'Filter issues' section in Support pane" }, "supportS3P2": { @@ -1004,7 +1004,7 @@ "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option5": { - "message": "Hajoaa, kun uBlock Origin on käytössä", + "message": "Ei toimi oikein uBlock Originin ollessa käytössä", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option6": { @@ -1012,7 +1012,7 @@ "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option7": { - "message": "Leads to badware, phishing", + "message": "Johtaa badwareen ja tietojenkalasteluun", "description": "An entry in the widget used to select the type of issue" }, "supportS6Checkbox1": { From 6cac64583059b6abdbc35d310847c5e225e78ef3 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Mon, 30 Sep 2024 10:16:48 -0400 Subject: [PATCH 0289/1099] Do not discard `!#else` block for unknown preprocessor tokens Related issue: https://github.com/uBlockOrigin/uBlock-issues/issues/3393 --- src/js/static-filtering-parser.js | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/src/js/static-filtering-parser.js b/src/js/static-filtering-parser.js index db5b2bd132e03..11858c20bda32 100644 --- a/src/js/static-filtering-parser.js +++ b/src/js/static-filtering-parser.js @@ -4430,14 +4430,14 @@ export const utils = (( ) => { const parts = [ 0 ]; let discard = false; - const shouldDiscard = ( ) => stack.some(v => v); + const shouldDiscard = ( ) => stack.some(v => v.known && v.discard); - const begif = (startDiscard, match) => { - if ( discard === false && startDiscard ) { - parts.push(match.index); + const begif = details => { + if ( discard === false && details.known && details.discard ) { + parts.push(details.pos); discard = true; } - stack.push(startDiscard); + stack.push(details); }; const endif = match => { @@ -4455,15 +4455,21 @@ export const utils = (( ) => { switch ( match[1] ) { case 'if': { - const startDiscard = this.evaluateExpr(match[2].trim(), env) === false; - begif(startDiscard, match); + const result = this.evaluateExpr(match[2].trim(), env); + begif({ + known: result !== undefined, + discard: result === false, + pos: match.index, + }); break; } case 'else': { if ( stack.length === 0 ) { break; } - const startDiscard = stack[stack.length-1] === false; + const details = stack[stack.length-1]; endif(match); - begif(startDiscard, match); + details.discard = details.discard === false; + details.pos = match.index; + begif(details); break; } case 'endif': { From 0b02c7ccb6f8f2c1b91bf820746902c3c721e1e3 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Tue, 1 Oct 2024 12:27:56 -0400 Subject: [PATCH 0290/1099] Import translation work from https://crowdin.com/project/ublock --- .../mv3/extension/_locales/ar/messages.json | 28 +++++++-------- .../extension/_locales/br_FR/messages.json | 26 +++++++------- .../mv3/extension/_locales/el/messages.json | 34 +++++++++---------- .../mv3/extension/_locales/es/messages.json | 6 ++-- .../mv3/extension/_locales/et/messages.json | 34 +++++++++---------- .../mv3/extension/_locales/fy/messages.json | 34 +++++++++---------- .../mv3/extension/_locales/gl/messages.json | 34 +++++++++---------- .../mv3/extension/_locales/hu/messages.json | 34 +++++++++---------- 8 files changed, 115 insertions(+), 115 deletions(-) diff --git a/platform/mv3/extension/_locales/ar/messages.json b/platform/mv3/extension/_locales/ar/messages.json index 588e258fd3100..3f051e050e2b0 100644 --- a/platform/mv3/extension/_locales/ar/messages.json +++ b/platform/mv3/extension/_locales/ar/messages.json @@ -112,59 +112,59 @@ "description": "First paragraph of 'Filter issues' section in Support pane" }, "supportS6P1S1": { - "message": "To avoid burdening volunteers with duplicate reports, please verify that the issue has not already been reported.", + "message": "لتجنب تحميل المتطوعين بتقارير مكررة، يرجى التأكد من أن المشكلة لم يتم الإبلاغ عنها بالفعل.", "description": "A paragraph in the filter issue reporter section" }, "supportFindSpecificButton": { - "message": "Find similar reports", + "message": "العثور على تقارير مماثلة", "description": "A clickable link in the filter issue reporter section" }, "supportS6URL": { - "message": "Address of the webpage:", + "message": "عنوان صفحة الويب", "description": "Label for the URL of the page" }, "supportS6Select1": { - "message": "The webpage…", + "message": "صفحة الويب...", "description": "Label for widget to select type of issue" }, "supportS6Select1Option0": { - "message": "-- Pick an entry --", + "message": "— اختر إدخالًا —", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option1": { - "message": "Shows ads or ad leftovers", + "message": "يظهر الإعلانات أو بقايا الإعلانات", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option2": { - "message": "Has overlays or other nuisances", + "message": "يحتوي على تراكبات أو إزعاجات أخرى.", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option3": { - "message": "Detects uBO Lite", + "message": "يكتشف uBO Lite", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option4": { - "message": "Has privacy-related issues", + "message": "لديه مشاكل متعلقة بالخصوصية", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option5": { - "message": "Malfunctions when uBO Lite is enabled", + "message": "تعطل عند تفعيل uBO Lite.", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option6": { - "message": "Opens unwanted tabs or windows", + "message": "يفتح علامات تبويب أو نوافذ غير مرغوب فيها.", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option7": { - "message": "Leads to badware, phishing", + "message": "يؤدي إلى البرامج الضارة والإحتيال", "description": "An entry in the widget used to select the type of issue" }, "supportS6Checkbox1": { - "message": "Label the webpage as “NSFW” (“Not Safe For Work”)", + "message": "تصنيف صفحة الويب على أنها \"NSFW\" (\"غير آمنة للعمل\")", "description": "A checkbox to use for NSFW sites" }, "supportReportSpecificButton": { - "message": "Create new report", + "message": "إنشاء تقرير جديد", "description": "Text for button which open an external webpage in Support pane" }, "firstRunSectionLabel": { diff --git a/platform/mv3/extension/_locales/br_FR/messages.json b/platform/mv3/extension/_locales/br_FR/messages.json index 3b179eaf1dca6..8bea1bb7cd344 100644 --- a/platform/mv3/extension/_locales/br_FR/messages.json +++ b/platform/mv3/extension/_locales/br_FR/messages.json @@ -32,7 +32,7 @@ "description": "Label in the popup panel for the current filtering mode" }, "popupTipReport": { - "message": "Report an issue on this website", + "message": "Danevelliñ ur gudenn war al lec'hienn-mañ", "description": "Tooltip used for the 'chat' icon in the panel" }, "popupTipDashboard": { @@ -104,7 +104,7 @@ "description": "Shown in the About pane" }, "supportS6H": { - "message": "Report a filter issue", + "message": "Danevelliñ ur gudenn gant ur sil", "description": "Header of 'Report a filter issue' section in Support pane" }, "supportS3P1": { @@ -116,43 +116,43 @@ "description": "A paragraph in the filter issue reporter section" }, "supportFindSpecificButton": { - "message": "Find similar reports", + "message": "Klask danevelloù koulz ha homañ", "description": "A clickable link in the filter issue reporter section" }, "supportS6URL": { - "message": "Address of the webpage:", + "message": "Chomlec'h ar bajenn web:", "description": "Label for the URL of the page" }, "supportS6Select1": { - "message": "The webpage…", + "message": "Ar bajenn web-mañ…", "description": "Label for widget to select type of issue" }, "supportS6Select1Option0": { - "message": "-- Pick an entry --", + "message": "-- Dibab ur seurt --", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option1": { - "message": "Shows ads or ad leftovers", + "message": "Diskouez a ra bruderezh pe restachoù bruderezh", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option2": { - "message": "Has overlays or other nuisances", + "message": "Gwiskadoù pe saotradurioù all en deus", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option3": { - "message": "Detects uBO Lite", + "message": "Diguzhat a ra uBO Lite", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option4": { - "message": "Has privacy-related issues", + "message": "Kudennoù a-fed prevezded he deus", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option5": { - "message": "Malfunctions when uBO Lite is enabled", + "message": "Ne ya ket mat en-dro p'eo enaouet uBO Lite", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option6": { - "message": "Opens unwanted tabs or windows", + "message": "Digeriñ a ra ivinelloù pe prenestroù noazus", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option7": { @@ -164,7 +164,7 @@ "description": "A checkbox to use for NSFW sites" }, "supportReportSpecificButton": { - "message": "Create new report", + "message": "Sevel ur rentañ-kont nevez", "description": "Text for button which open an external webpage in Support pane" }, "firstRunSectionLabel": { diff --git a/platform/mv3/extension/_locales/el/messages.json b/platform/mv3/extension/_locales/el/messages.json index e5e8f663b7889..7020c8b4c1e5c 100644 --- a/platform/mv3/extension/_locales/el/messages.json +++ b/platform/mv3/extension/_locales/el/messages.json @@ -32,7 +32,7 @@ "description": "Label in the popup panel for the current filtering mode" }, "popupTipReport": { - "message": "Report an issue on this website", + "message": "Αναφορά ενός ζητήματος σε αυτόν τον ιστότοπο", "description": "Tooltip used for the 'chat' icon in the panel" }, "popupTipDashboard": { @@ -104,67 +104,67 @@ "description": "Shown in the About pane" }, "supportS6H": { - "message": "Report a filter issue", + "message": "Αναφορά ζητήματος φίλτρου", "description": "Header of 'Report a filter issue' section in Support pane" }, "supportS3P1": { - "message": "Report filter issues with specific websites to the uBlockOrigin/uAssets issue tracker. Requires a GitHub account.", + "message": "Αναφέρετε ζητήματα φίλτρου για συγκεκριμένους ιστοτόπους στο εργαλείο παρακολούθησης ζητημάτων του uBlockOrigin/uAssets. Απαιτείται λογαριασμός GitHub.", "description": "First paragraph of 'Filter issues' section in Support pane" }, "supportS6P1S1": { - "message": "To avoid burdening volunteers with duplicate reports, please verify that the issue has not already been reported.", + "message": "Για να αποφύγετε την επιβάρυνση των εθελοντών με διπλές αναφορές, βεβαιωθείτε ότι το ζήτημα δεν έχει ήδη αναφερθεί.", "description": "A paragraph in the filter issue reporter section" }, "supportFindSpecificButton": { - "message": "Find similar reports", + "message": "Βρείτε παρόμοιες αναφορές", "description": "A clickable link in the filter issue reporter section" }, "supportS6URL": { - "message": "Address of the webpage:", + "message": "Διεύθυνση ιστοσελίδας:", "description": "Label for the URL of the page" }, "supportS6Select1": { - "message": "The webpage…", + "message": "Η ιστοσελίδα…", "description": "Label for widget to select type of issue" }, "supportS6Select1Option0": { - "message": "-- Pick an entry --", + "message": "-- Επιλέξτε μια καταχώρηση --", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option1": { - "message": "Shows ads or ad leftovers", + "message": "Εμφανίζει διαφημίσεις ή υπολείμματα διαφημίσεων", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option2": { - "message": "Has overlays or other nuisances", + "message": "Έχει επικαλύψεις ή άλλες ενοχλήσεις", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option3": { - "message": "Detects uBO Lite", + "message": "Ανιχνεύει το uBO Lite", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option4": { - "message": "Has privacy-related issues", + "message": "Έχει ζητήματα σχετικά με το απόρρητο", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option5": { - "message": "Malfunctions when uBO Lite is enabled", + "message": "Δυσλειτουργεί όταν είναι ενεργό το uBO Lite", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option6": { - "message": "Opens unwanted tabs or windows", + "message": "Ανοίγει ανεπιθύμητες καρτέλες ή παράθυρα", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option7": { - "message": "Leads to badware, phishing", + "message": "Οδηγεί σε κακόβουλο λογισμικό, ηλεκτρονικό «ψάρεμα»", "description": "An entry in the widget used to select the type of issue" }, "supportS6Checkbox1": { - "message": "Label the webpage as “NSFW” (“Not Safe For Work”)", + "message": "Επισήμανση ιστοσελίδας ως «NSFW» («Not Safe For Work»)", "description": "A checkbox to use for NSFW sites" }, "supportReportSpecificButton": { - "message": "Create new report", + "message": "Δημιουργία νέας αναφοράς", "description": "Text for button which open an external webpage in Support pane" }, "firstRunSectionLabel": { diff --git a/platform/mv3/extension/_locales/es/messages.json b/platform/mv3/extension/_locales/es/messages.json index f7cfe51e61631..323bb1520a827 100644 --- a/platform/mv3/extension/_locales/es/messages.json +++ b/platform/mv3/extension/_locales/es/messages.json @@ -32,7 +32,7 @@ "description": "Label in the popup panel for the current filtering mode" }, "popupTipReport": { - "message": "Reportar un problema en este sitio web", + "message": "Informar de un problema en este sitio web", "description": "Tooltip used for the 'chat' icon in the panel" }, "popupTipDashboard": { @@ -104,7 +104,7 @@ "description": "Shown in the About pane" }, "supportS6H": { - "message": "Reportar un problema de filtro", + "message": "Informar de un problema de filtro", "description": "Header of 'Report a filter issue' section in Support pane" }, "supportS3P1": { @@ -164,7 +164,7 @@ "description": "A checkbox to use for NSFW sites" }, "supportReportSpecificButton": { - "message": "Crear nuevo reporte", + "message": "Crear informe nuevo", "description": "Text for button which open an external webpage in Support pane" }, "firstRunSectionLabel": { diff --git a/platform/mv3/extension/_locales/et/messages.json b/platform/mv3/extension/_locales/et/messages.json index 62d13ed6cf447..ae248a7b0fc5f 100644 --- a/platform/mv3/extension/_locales/et/messages.json +++ b/platform/mv3/extension/_locales/et/messages.json @@ -32,7 +32,7 @@ "description": "Label in the popup panel for the current filtering mode" }, "popupTipReport": { - "message": "Report an issue on this website", + "message": "Teavita veast selle veebilehega", "description": "Tooltip used for the 'chat' icon in the panel" }, "popupTipDashboard": { @@ -104,67 +104,67 @@ "description": "Shown in the About pane" }, "supportS6H": { - "message": "Report a filter issue", + "message": "Teavita filtri veast", "description": "Header of 'Report a filter issue' section in Support pane" }, "supportS3P1": { - "message": "Report filter issues with specific websites to the uBlockOrigin/uAssets issue tracker. Requires a GitHub account.", + "message": "Teavita vigasest filtrist kindlate veebilehtedega uBlockOrigin/uAssets vigade andmebaasi kaudu. Nõuab GitHubi kontot.", "description": "First paragraph of 'Filter issues' section in Support pane" }, "supportS6P1S1": { - "message": "To avoid burdening volunteers with duplicate reports, please verify that the issue has not already been reported.", + "message": "Vabatahtlike koormamise vältimiseks samade teadetega veenduge, et keegi pole selle murega juba varem pöördunud.", "description": "A paragraph in the filter issue reporter section" }, "supportFindSpecificButton": { - "message": "Find similar reports", + "message": "Leidke sarnased aruanded", "description": "A clickable link in the filter issue reporter section" }, "supportS6URL": { - "message": "Address of the webpage:", + "message": "Veebilehe aadress:", "description": "Label for the URL of the page" }, "supportS6Select1": { - "message": "The webpage…", + "message": "Veebileht...", "description": "Label for widget to select type of issue" }, "supportS6Select1Option0": { - "message": "-- Pick an entry --", + "message": "-- Valige --", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option1": { - "message": "Shows ads or ad leftovers", + "message": "Näitab reklaame või reklaami kohatäitjaid", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option2": { - "message": "Has overlays or other nuisances", + "message": "Omab ülekatteid või teisi nuhtlusi", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option3": { - "message": "Detects uBO Lite", + "message": "Tuvastab uBO Lite'i", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option4": { - "message": "Has privacy-related issues", + "message": "Omab privaatsusega seonduvaid probleeme", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option5": { - "message": "Malfunctions when uBO Lite is enabled", + "message": "Streigib, kui uBO Lite on kasutusel", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option6": { - "message": "Opens unwanted tabs or windows", + "message": "Avab soovimatuid kaarte või aknaid", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option7": { - "message": "Leads to badware, phishing", + "message": "Põhjustab pahavara, õngitsuskirju", "description": "An entry in the widget used to select the type of issue" }, "supportS6Checkbox1": { - "message": "Label the webpage as “NSFW” (“Not Safe For Work”)", + "message": "Märgi veebileht kui „NSFW” („Ohtlik”)", "description": "A checkbox to use for NSFW sites" }, "supportReportSpecificButton": { - "message": "Create new report", + "message": "Loo uus aruanne", "description": "Text for button which open an external webpage in Support pane" }, "firstRunSectionLabel": { diff --git a/platform/mv3/extension/_locales/fy/messages.json b/platform/mv3/extension/_locales/fy/messages.json index d74cc92db8a0e..ac1895fd5da09 100644 --- a/platform/mv3/extension/_locales/fy/messages.json +++ b/platform/mv3/extension/_locales/fy/messages.json @@ -32,7 +32,7 @@ "description": "Label in the popup panel for the current filtering mode" }, "popupTipReport": { - "message": "Report an issue on this website", + "message": "In probleem op dizze website melde", "description": "Tooltip used for the 'chat' icon in the panel" }, "popupTipDashboard": { @@ -104,67 +104,67 @@ "description": "Shown in the About pane" }, "supportS6H": { - "message": "Report a filter issue", + "message": "In filterprobleem melde", "description": "Header of 'Report a filter issue' section in Support pane" }, "supportS3P1": { - "message": "Report filter issues with specific websites to the uBlockOrigin/uAssets issue tracker. Requires a GitHub account.", + "message": "Meld filterproblemen mei spesifike websites yn de uBlockOrigin/uAssets-probleemtracker. Fereasket in GitHub-account.", "description": "First paragraph of 'Filter issues' section in Support pane" }, "supportS6P1S1": { - "message": "To avoid burdening volunteers with duplicate reports, please verify that the issue has not already been reported.", + "message": "Kontrolearje oft it probleem net earder meld is om foar te kommen dat frijwilligers mei dûbele meldingen belêst wurde.", "description": "A paragraph in the filter issue reporter section" }, "supportFindSpecificButton": { - "message": "Find similar reports", + "message": "Soartgelikense meldingen sykje", "description": "A clickable link in the filter issue reporter section" }, "supportS6URL": { - "message": "Address of the webpage:", + "message": "Adres fan de webside:", "description": "Label for the URL of the page" }, "supportS6Select1": { - "message": "The webpage…", + "message": "De webside…", "description": "Label for widget to select type of issue" }, "supportS6Select1Option0": { - "message": "-- Pick an entry --", + "message": "-- Meitsje in kar --", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option1": { - "message": "Shows ads or ad leftovers", + "message": "Toant advertinsjes of restanten", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option2": { - "message": "Has overlays or other nuisances", + "message": "Hat oerlapingen of oare ûngemakken", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option3": { - "message": "Detects uBO Lite", + "message": "Detektearret uBO Lite", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option4": { - "message": "Has privacy-related issues", + "message": "Hat privacy-relatearre problemen", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option5": { - "message": "Malfunctions when uBO Lite is enabled", + "message": "Wurket net as uBO Lite ynskeakele is", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option6": { - "message": "Opens unwanted tabs or windows", + "message": "Iepenet net-winske ljepblêden of finsters", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option7": { - "message": "Leads to badware, phishing", + "message": "Liedt ta badware, phishing", "description": "An entry in the widget used to select the type of issue" }, "supportS6Checkbox1": { - "message": "Label the webpage as “NSFW” (“Not Safe For Work”)", + "message": "De webside labelje as ‘NSFW’ (‘Not Safe For Work’)", "description": "A checkbox to use for NSFW sites" }, "supportReportSpecificButton": { - "message": "Create new report", + "message": "Nije melding meitsje", "description": "Text for button which open an external webpage in Support pane" }, "firstRunSectionLabel": { diff --git a/platform/mv3/extension/_locales/gl/messages.json b/platform/mv3/extension/_locales/gl/messages.json index dc87e7591e00f..59833de63f371 100644 --- a/platform/mv3/extension/_locales/gl/messages.json +++ b/platform/mv3/extension/_locales/gl/messages.json @@ -32,7 +32,7 @@ "description": "Label in the popup panel for the current filtering mode" }, "popupTipReport": { - "message": "Report an issue on this website", + "message": "Informar dun problema nesta web", "description": "Tooltip used for the 'chat' icon in the panel" }, "popupTipDashboard": { @@ -104,67 +104,67 @@ "description": "Shown in the About pane" }, "supportS6H": { - "message": "Report a filter issue", + "message": "Informar dun problema co filtro", "description": "Header of 'Report a filter issue' section in Support pane" }, "supportS3P1": { - "message": "Report filter issues with specific websites to the uBlockOrigin/uAssets issue tracker. Requires a GitHub account.", + "message": "Informa de problemas cos filtros en webs concretas no seguimento de problemas en uBlockOrigin/uAssets. Require unha conta GitHub.", "description": "First paragraph of 'Filter issues' section in Support pane" }, "supportS6P1S1": { - "message": "To avoid burdening volunteers with duplicate reports, please verify that the issue has not already been reported.", + "message": "Para evitar a sobrecarga de traballo para as persoas voluntarias con duplicados dos problemas, comproba que aínda non se informou acerca do problema.", "description": "A paragraph in the filter issue reporter section" }, "supportFindSpecificButton": { - "message": "Find similar reports", + "message": "Atopar denuncias parecidas", "description": "A clickable link in the filter issue reporter section" }, "supportS6URL": { - "message": "Address of the webpage:", + "message": "Enderezo da páxina web:", "description": "Label for the URL of the page" }, "supportS6Select1": { - "message": "The webpage…", + "message": "A páxina web...", "description": "Label for widget to select type of issue" }, "supportS6Select1Option0": { - "message": "-- Pick an entry --", + "message": "-- Escolle unha opción --", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option1": { - "message": "Shows ads or ad leftovers", + "message": "Mostra publicidade ou restos dela", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option2": { - "message": "Has overlays or other nuisances", + "message": "Ten capas sobreimpostas ou elementos molestos", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option3": { - "message": "Detects uBO Lite", + "message": "Detecta uBO Lite", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option4": { - "message": "Has privacy-related issues", + "message": "Ten problemas relacionados coa privacidade", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option5": { - "message": "Malfunctions when uBO Lite is enabled", + "message": "Funciona mal se uBO Lite está activado", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option6": { - "message": "Opens unwanted tabs or windows", + "message": "Abre xanelas ou pestanas non solicitadas", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option7": { - "message": "Leads to badware, phishing", + "message": "Leva a software malicioso, phishing", "description": "An entry in the widget used to select the type of issue" }, "supportS6Checkbox1": { - "message": "Label the webpage as “NSFW” (“Not Safe For Work”)", + "message": "Etiqueta a páxina como «NSFW» (Non axeitada no traballo)", "description": "A checkbox to use for NSFW sites" }, "supportReportSpecificButton": { - "message": "Create new report", + "message": "Crear nova denuncia", "description": "Text for button which open an external webpage in Support pane" }, "firstRunSectionLabel": { diff --git a/platform/mv3/extension/_locales/hu/messages.json b/platform/mv3/extension/_locales/hu/messages.json index 53e7c2c672253..987469483ed27 100644 --- a/platform/mv3/extension/_locales/hu/messages.json +++ b/platform/mv3/extension/_locales/hu/messages.json @@ -32,7 +32,7 @@ "description": "Label in the popup panel for the current filtering mode" }, "popupTipReport": { - "message": "Report an issue on this website", + "message": "Oldalon lévő probléma jelentése", "description": "Tooltip used for the 'chat' icon in the panel" }, "popupTipDashboard": { @@ -104,67 +104,67 @@ "description": "Shown in the About pane" }, "supportS6H": { - "message": "Report a filter issue", + "message": "Szűrőhiba jelentése", "description": "Header of 'Report a filter issue' section in Support pane" }, "supportS3P1": { - "message": "Report filter issues with specific websites to the uBlockOrigin/uAssets issue tracker. Requires a GitHub account.", + "message": "Az adott webhelyeket érintő szűrőhibákat a uBlockOrigin/uAssets hibakövetőjében jelentse. Ehhez GitHub-fiók szükséges.", "description": "First paragraph of 'Filter issues' section in Support pane" }, "supportS6P1S1": { - "message": "To avoid burdening volunteers with duplicate reports, please verify that the issue has not already been reported.", + "message": "Az önkéntesek terhelésének csökkentése érdekében győződjön meg róla, hogy a hiba még nem lett jelentve.", "description": "A paragraph in the filter issue reporter section" }, "supportFindSpecificButton": { - "message": "Find similar reports", + "message": "Hasonló jelentések keresése", "description": "A clickable link in the filter issue reporter section" }, "supportS6URL": { - "message": "Address of the webpage:", + "message": "A weboldal címe:", "description": "Label for the URL of the page" }, "supportS6Select1": { - "message": "The webpage…", + "message": "A weboldal…", "description": "Label for widget to select type of issue" }, "supportS6Select1Option0": { - "message": "-- Pick an entry --", + "message": "-- Válasszon egy bejegyzést --", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option1": { - "message": "Shows ads or ad leftovers", + "message": "Hirdetéseket vagy azok maradványait jeleníti meg", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option2": { - "message": "Has overlays or other nuisances", + "message": "Átfedő vagy egyéb zavaró elemeket tartalmaz", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option3": { - "message": "Detects uBO Lite", + "message": "Észleli az uBO Lite-ot", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option4": { - "message": "Has privacy-related issues", + "message": "Adatvédelmi problémákat vet fel", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option5": { - "message": "Malfunctions when uBO Lite is enabled", + "message": "Hibásan működik, ha a uBO Lite be van kapcsolva", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option6": { - "message": "Opens unwanted tabs or windows", + "message": "Kéretlen lapokat vagy ablakokat nyit meg", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option7": { - "message": "Leads to badware, phishing", + "message": "Kártékony programokhoz, adathalászathoz vezet", "description": "An entry in the widget used to select the type of issue" }, "supportS6Checkbox1": { - "message": "Label the webpage as “NSFW” (“Not Safe For Work”)", + "message": "A weboldal megjelölése „NSFW”-ként („Not Safe For Work”)", "description": "A checkbox to use for NSFW sites" }, "supportReportSpecificButton": { - "message": "Create new report", + "message": "Új jelentés létrehozása", "description": "Text for button which open an external webpage in Support pane" }, "firstRunSectionLabel": { From 73ce4e6bcf557254d7dd230f7635cbd9098a7a9c Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Wed, 2 Oct 2024 13:39:36 -0400 Subject: [PATCH 0291/1099] Blocking large media elements also prevents autoplay, regardless of size Related issue: https://github.com/uBlockOrigin/uBlock-issues/issues/3394 When the "No large media elements" per-site switch is toggled on, it will also act to prevent autoplay of video/audio media, regardless of their size. This also works for xhr-based media streaming. If blocking by size is not desirable while blocking autoplay is desired, one can toggle on "No large media elements" switch while setting "Block media elements larger than ..." to a very high value. --- platform/chromium/vapi-background-ext.js | 70 +++++---------- platform/common/vapi-background.js | 8 ++ platform/firefox/vapi-background-ext.js | 15 ++-- src/js/pagestore.js | 51 +++++------ .../load-large-media-interactive.js | 85 +++++++++++-------- src/js/traffic.js | 42 ++++++--- 6 files changed, 139 insertions(+), 132 deletions(-) diff --git a/platform/chromium/vapi-background-ext.js b/platform/chromium/vapi-background-ext.js index 29de305846647..9ee69d8199ae5 100644 --- a/platform/chromium/vapi-background-ext.js +++ b/platform/chromium/vapi-background-ext.js @@ -19,10 +19,6 @@ Home: https://github.com/gorhill/uBlock */ -/* globals browser */ - -'use strict'; - /******************************************************************************/ // https://github.com/uBlockOrigin/uBlock-issues/issues/1659 @@ -90,71 +86,47 @@ vAPI.Tabs = class extends vAPI.Tabs { ['gif','image'],['ico','image'],['jpeg','image'],['jpg','image'],['png','image'],['webp','image'] ]); - const headerValue = (headers, name) => { - let i = headers.length; - while ( i-- ) { - if ( headers[i].name.toLowerCase() === name ) { - return headers[i].value.trim(); - } - } - return ''; - }; - const parsedURL = new URL('https://www.example.org/'); - // Extend base class to normalize as per platform. + // Extend base class to normalize as per platform vAPI.Net = class extends vAPI.Net { normalizeDetails(details) { // Chromium 63+ supports the `initiator` property, which contains - // the URL of the origin from which the network request was made. - if ( - typeof details.initiator === 'string' && - details.initiator !== 'null' - ) { + // the URL of the origin from which the network request was made + if ( details.initiator && details.initiator !== 'null' ) { details.documentUrl = details.initiator; } - - let type = details.type; - + const type = details.type; if ( type === 'imageset' ) { details.type = 'image'; return; } - - // The rest of the function code is to normalize type if ( type !== 'other' ) { return; } - - // Try to map known "extension" part of URL to request type. - parsedURL.href = details.url; - const path = parsedURL.pathname, - pos = path.indexOf('.', path.length - 6); - if ( pos !== -1 && (type = extToTypeMap.get(path.slice(pos + 1))) ) { - details.type = type; + // Try to map known "extension" part of URL to request type + if ( details.responseHeaders === undefined ) { + parsedURL.href = details.url; + const path = parsedURL.pathname; + const pos = path.indexOf('.', path.length - 6); + if ( pos !== -1 ) { + details.type = extToTypeMap.get(path.slice(pos + 1)) || type; + } return; } - - // Try to extract type from response headers if present. - if ( details.responseHeaders ) { - type = headerValue(details.responseHeaders, 'content-type'); - if ( type.startsWith('font/') ) { - details.type = 'font'; - return; - } - if ( type.startsWith('image/') ) { - details.type = 'image'; - return; - } - if ( type.startsWith('audio/') || type.startsWith('video/') ) { - details.type = 'media'; - return; - } + // Try to extract type from response headers + const ctype = this.headerValue(details.responseHeaders, 'content-type'); + if ( ctype.startsWith('font/') ) { + details.type = 'font'; + } else if ( ctype.startsWith('image/') ) { + details.type = 'image'; + } else if ( ctype.startsWith('audio/') || ctype.startsWith('video/') ) { + details.type = 'media'; } } // https://www.reddit.com/r/uBlockOrigin/comments/9vcrk3/ // Some types can be mapped from 'other', thus include 'other' if and - // only if the caller is interested in at least one of those types. + // only if the caller is interested in at least one of those types denormalizeTypes(types) { if ( types.length === 0 ) { return Array.from(this.validTypes); diff --git a/platform/common/vapi-background.js b/platform/common/vapi-background.js index 26507982a51e9..fac5121c00082 100644 --- a/platform/common/vapi-background.js +++ b/platform/common/vapi-background.js @@ -1382,6 +1382,14 @@ vAPI.Net = class { if ( this.suspendDepth !== 0 ) { return; } this.unsuspendAllRequests(discard); } + headerValue(headers, name) { + for ( const header of headers ) { + if ( header.name.toLowerCase() === name ) { + return header.value.trim(); + } + } + return ''; + } static canSuspend() { return false; } diff --git a/platform/firefox/vapi-background-ext.js b/platform/firefox/vapi-background-ext.js index 73a914d0ce650..a6032066d9258 100644 --- a/platform/firefox/vapi-background-ext.js +++ b/platform/firefox/vapi-background-ext.js @@ -110,18 +110,15 @@ vAPI.Net = class extends vAPI.Net { details.type = 'image'; return; } + if ( type !== 'object' ) { return; } + // Try to extract type from response headers if present. + if ( details.responseHeaders === undefined ) { return; } + const ctype = this.headerValue(details.responseHeaders, 'content-type'); // https://github.com/uBlockOrigin/uBlock-issues/issues/345 // Re-categorize an embedded object as a `sub_frame` if its // content type is that of a HTML document. - if ( type === 'object' && Array.isArray(details.responseHeaders) ) { - for ( const header of details.responseHeaders ) { - if ( header.name.toLowerCase() === 'content-type' ) { - if ( header.value.startsWith('text/html') ) { - details.type = 'sub_frame'; - } - break; - } - } + if ( ctype === 'text/html' ) { + details.type = 'sub_frame'; } } diff --git a/src/js/pagestore.js b/src/js/pagestore.js index 319367fe51b0d..1b94b1fef6d95 100644 --- a/src/js/pagestore.js +++ b/src/js/pagestore.js @@ -1019,48 +1019,51 @@ const PageStore = class { } // The caller is responsible to check whether filtering is enabled or not. - filterLargeMediaElement(fctxt, size) { + filterLargeMediaElement(fctxt, headers) { fctxt.filter = undefined; - - if ( this.allowLargeMediaElementsUntil === 0 ) { + if ( this.allowLargeMediaElementsUntil === 0 ) { return 0; } + if ( sessionSwitches.evaluateZ('no-large-media', fctxt.getTabHostname() ) !== true ) { + this.allowLargeMediaElementsUntil = 0; return 0; } - // Disregard large media elements previously allowed: for example, to - // seek inside a previously allowed audio/video. - if ( - this.allowLargeMediaElementsRegex instanceof RegExp && - this.allowLargeMediaElementsRegex.test(fctxt.url) - ) { + // XHR-based streaming is never blocked but we want to prevent autoplay + if ( fctxt.itype === fctxt.XMLHTTPREQUEST ) { + const ctype = headers.contentType; + if ( ctype.startsWith('audio/') || ctype.startsWith('video/') ) { + this.largeMediaTimer.on(500); + } return 0; } if ( Date.now() < this.allowLargeMediaElementsUntil ) { - const sources = this.allowLargeMediaElementsRegex instanceof RegExp - ? [ this.allowLargeMediaElementsRegex.source ] - : []; - sources.push('^' + µb.escapeRegex(fctxt.url)); - this.allowLargeMediaElementsRegex = new RegExp(sources.join('|')); + if ( fctxt.itype === fctxt.MEDIA ) { + const sources = this.allowLargeMediaElementsRegex instanceof RegExp + ? [ this.allowLargeMediaElementsRegex.source ] + : []; + sources.push('^' + µb.escapeRegex(fctxt.url)); + this.allowLargeMediaElementsRegex = new RegExp(sources.join('|')); + } return 0; } + // Disregard large media elements previously allowed: for example, to + // seek inside a previously allowed audio/video. if ( - sessionSwitches.evaluateZ( - 'no-large-media', - fctxt.getTabHostname() - ) !== true + this.allowLargeMediaElementsRegex instanceof RegExp && + this.allowLargeMediaElementsRegex.test(fctxt.url) ) { - this.allowLargeMediaElementsUntil = 0; return 0; } - if ( (size >>> 10) < µb.userSettings.largeMediaSize ) { - return 0; + // Regardless of whether a media is blocked, we want to prevent autoplay + if ( fctxt.itype === fctxt.MEDIA ) { + this.largeMediaTimer.on(500); } - + const size = headers.contentLength; + if ( isNaN(size) ) { return 0; } + if ( (size >>> 10) < µb.userSettings.largeMediaSize ) { return 0; } this.largeMediaCount += 1; this.largeMediaTimer.on(500); - if ( logger.enabled ) { fctxt.filter = sessionSwitches.toLogData(); } - return 1; } diff --git a/src/js/scriptlets/load-large-media-interactive.js b/src/js/scriptlets/load-large-media-interactive.js index 4887616d9b0f9..6158124726c70 100644 --- a/src/js/scriptlets/load-large-media-interactive.js +++ b/src/js/scriptlets/load-large-media-interactive.js @@ -28,8 +28,6 @@ if ( typeof vAPI !== 'object' || vAPI.loadAllLargeMedia instanceof Function ) { return; } -/******************************************************************************/ - const largeMediaElementAttribute = 'data-' + vAPI.sessionId; const largeMediaElementSelector = ':root audio[' + largeMediaElementAttribute + '],\n' + @@ -37,25 +35,19 @@ const largeMediaElementSelector = ':root picture[' + largeMediaElementAttribute + '],\n' + ':root video[' + largeMediaElementAttribute + ']'; -/******************************************************************************/ +const isMediaElement = elem => + (/^(?:audio|img|picture|video)$/.test(elem.localName)); -const isMediaElement = function(elem) { - return /^(?:audio|img|picture|video)$/.test(elem.localName); -}; +const isPlayableMediaElement = elem => + (/^(?:audio|video)$/.test(elem.localName)); /******************************************************************************/ const mediaNotLoaded = function(elem) { switch ( elem.localName ) { case 'audio': - case 'video': { - const src = elem.src || ''; - if ( src.startsWith('blob:') ) { - elem.autoplay = false; - elem.pause(); - } + case 'video': return elem.readyState === 0 || elem.error !== null; - } case 'img': { if ( elem.naturalWidth !== 0 || elem.naturalHeight !== 0 ) { break; @@ -99,29 +91,29 @@ const surveyMissingMediaElements = function() { return largeMediaElementCount; }; -if ( surveyMissingMediaElements() === 0 ) { return; } - -// Insert CSS to highlight blocked media elements. -if ( vAPI.largeMediaElementStyleSheet === undefined ) { - vAPI.largeMediaElementStyleSheet = [ - largeMediaElementSelector + ' {', - 'border: 2px dotted red !important;', - 'box-sizing: border-box !important;', - 'cursor: zoom-in !important;', - 'display: inline-block;', - 'filter: none !important;', - 'font-size: 1rem !important;', - 'min-height: 1em !important;', - 'min-width: 1em !important;', - 'opacity: 1 !important;', - 'outline: none !important;', - 'transform: none !important;', - 'visibility: visible !important;', - 'z-index: 2147483647', - '}', - ].join('\n'); - vAPI.userStylesheet.add(vAPI.largeMediaElementStyleSheet); - vAPI.userStylesheet.apply(); +if ( surveyMissingMediaElements() ) { + // Insert CSS to highlight blocked media elements. + if ( vAPI.largeMediaElementStyleSheet === undefined ) { + vAPI.largeMediaElementStyleSheet = [ + largeMediaElementSelector + ' {', + 'border: 2px dotted red !important;', + 'box-sizing: border-box !important;', + 'cursor: zoom-in !important;', + 'display: inline-block;', + 'filter: none !important;', + 'font-size: 1rem !important;', + 'min-height: 1em !important;', + 'min-width: 1em !important;', + 'opacity: 1 !important;', + 'outline: none !important;', + 'transform: none !important;', + 'visibility: visible !important;', + 'z-index: 2147483647', + '}', + ].join('\n'); + vAPI.userStylesheet.add(vAPI.largeMediaElementStyleSheet); + vAPI.userStylesheet.apply(); + } } /******************************************************************************/ @@ -258,6 +250,27 @@ document.addEventListener('error', onLoadError, true); /******************************************************************************/ +const autoPausedMedia = new WeakMap(); + +for ( const elem of document.querySelectorAll('audio,video') ) { + elem.setAttribute('autoplay', 'false'); +} + +const preventAutoplay = function(ev) { + const elem = ev.target; + if ( isPlayableMediaElement(elem) === false ) { return; } + const currentSrc = elem.getAttribute('src') || ''; + const pausedSrc = autoPausedMedia.get(elem); + if ( pausedSrc === currentSrc ) { return; } + autoPausedMedia.set(elem, currentSrc); + elem.setAttribute('autoplay', 'false'); + elem.pause(); +}; + +document.addEventListener('timeupdate', preventAutoplay, true); + +/******************************************************************************/ + vAPI.loadAllLargeMedia = function() { document.removeEventListener('click', onMouseClick, true); document.removeEventListener('loadeddata', onLoadedData, true); diff --git a/src/js/traffic.js b/src/js/traffic.js index 55538fa7e425e..5de002797671e 100644 --- a/src/js/traffic.js +++ b/src/js/traffic.js @@ -488,7 +488,7 @@ const onHeadersReceived = function(details) { } if ( pageStore.getNetFilteringSwitch(fctxt) === false ) { return; } - if ( fctxt.itype === fctxt.IMAGE || fctxt.itype === fctxt.MEDIA ) { + if ( (fctxt.itype & foilLargeMediaElement.TYPE_BITS) !== 0 ) { const result = foilLargeMediaElement(details, fctxt, pageStore); if ( result !== undefined ) { return result; } } @@ -1124,15 +1124,12 @@ const injectPP = function(fctxt, pageStore, responseHeaders) { const foilLargeMediaElement = function(details, fctxt, pageStore) { if ( details.fromCache === true ) { return; } - let size = 0; - if ( µb.userSettings.largeMediaSize !== 0 ) { - const headers = details.responseHeaders; - const i = headerIndexFromName('content-length', headers); - if ( i === -1 ) { return; } - size = parseInt(headers[i].value, 10) || 0; - } + onDemandHeaders.setHeaders(details.responseHeaders); + + const result = pageStore.filterLargeMediaElement(fctxt, onDemandHeaders); + + onDemandHeaders.reset(); - const result = pageStore.filterLargeMediaElement(fctxt, size); if ( result === 0 ) { return; } if ( logger.enabled ) { @@ -1142,16 +1139,15 @@ const foilLargeMediaElement = function(details, fctxt, pageStore) { return { cancel: true }; }; +foilLargeMediaElement.TYPE_BITS = fc.IMAGE | fc.MEDIA | fc.XMLHTTPREQUEST; + /******************************************************************************/ // Caller must ensure headerName is normalized to lower case. const headerIndexFromName = function(headerName, headers) { - let i = headers.length; - while ( i-- ) { - if ( headers[i].name.toLowerCase() === headerName ) { - return i; - } + for ( let i = 0, n = headers.length; i < n; i++ ) { + if ( headers[i].name.toLowerCase() === headerName ) { return i; } } return -1; }; @@ -1161,6 +1157,24 @@ const headerValueFromName = function(headerName, headers) { return i !== -1 ? headers[i].value : ''; }; +const onDemandHeaders = { + headers: [], + get contentLength() { + const contentLength = headerValueFromName('content-length', this.headers); + if ( contentLength === '' ) { return Number.NaN; } + return parseInt(contentLength, 10) || 0; + }, + get contentType() { + return headerValueFromName('content-type', this.headers); + }, + setHeaders(headers) { + this.headers = headers; + }, + reset() { + this.headers = []; + } +}; + /******************************************************************************/ const strictBlockBypasser = { From 4305bfbdb10f7cccd3fabbff11f0510dce794afb Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Wed, 2 Oct 2024 14:51:26 -0400 Subject: [PATCH 0292/1099] Skip dns resolution when requests are proxied through http Related issue: https://github.com/uBlockOrigin/uBlock-issues/discussions/3396 Reference: https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/proxy/ProxyInfo#type_2 --- platform/firefox/vapi-background-ext.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/platform/firefox/vapi-background-ext.js b/platform/firefox/vapi-background-ext.js index a6032066d9258..4cf7ec97824aa 100644 --- a/platform/firefox/vapi-background-ext.js +++ b/platform/firefox/vapi-background-ext.js @@ -36,6 +36,8 @@ const isPromise = o => o instanceof Promise; const isResolvedObject = o => o instanceof Object && o instanceof Promise === false; const reIPv4 = /^\d+\.\d+\.\d+\.\d+$/ +const skipDNS = proxyInfo => + proxyInfo?.proxyDNS || proxyInfo?.type?.startsWith('http'); /******************************************************************************/ @@ -102,7 +104,7 @@ vAPI.Net = class extends vAPI.Net { normalizeDetails(details) { // https://github.com/uBlockOrigin/uBlock-issues/issues/3379 - if ( details.proxyInfo?.proxyDNS && details.ip === '0.0.0.0' ) { + if ( skipDNS(details.proxyInfo) && details.ip === '0.0.0.0' ) { details.ip = null; } const type = details.type; @@ -182,8 +184,8 @@ vAPI.Net = class extends vAPI.Net { if ( isResolvedObject(dnsEntry) ) { return this.onAfterDNSResolution(hn, details, dnsEntry); } + if ( skipDNS(details.proxyInfo) ) { return; } if ( this.dnsShouldResolve(hn) === false ) { return; } - if ( details.proxyInfo?.proxyDNS ) { return; } const promise = dnsEntry || this.dnsResolve(hn, details); return promise.then(( ) => this.onAfterDNSResolution(hn, details)); } From 99593f82f4127cb357a251dd1c2d2905f195f60d Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Wed, 2 Oct 2024 14:58:31 -0400 Subject: [PATCH 0293/1099] New revision for dev build --- dist/version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dist/version b/dist/version index 0c424f18d5483..77b51621e1800 100644 --- a/dist/version +++ b/dist/version @@ -1 +1 @@ -1.60.1.1 \ No newline at end of file +1.60.1.2 \ No newline at end of file From ce8cc4793c12dccdb953c135d2181829f1e0d30c Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Wed, 2 Oct 2024 14:58:39 -0400 Subject: [PATCH 0294/1099] Update changelog --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 913bfed9480f7..54c5126219204 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,5 @@ +- [Skip dns resolution when requests are proxied through http](https://github.com/gorhill/uBlock/commit/4305bfbdb1) +- [Blocking large media elements also prevents autoplay, regardless of size](https://github.com/gorhill/uBlock/commit/73ce4e6bcf) - [Add ability to decode base64 in `urlskip=`](https://github.com/gorhill/uBlock/commit/e81e70937f) - [Fix images not properly downloading on click](https://github.com/gorhill/uBlock/commit/aec0bd39e3) From b25d2153afe1854e7252fceaa04ef942f0ac706d Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Wed, 2 Oct 2024 15:05:42 -0400 Subject: [PATCH 0295/1099] Make Firefox dev build auto-update --- dist/firefox/updates.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dist/firefox/updates.json b/dist/firefox/updates.json index 9b03f94f2941e..c4cc4eca09d3f 100644 --- a/dist/firefox/updates.json +++ b/dist/firefox/updates.json @@ -3,9 +3,9 @@ "uBlock0@raymondhill.net": { "updates": [ { - "version": "1.60.1.1", + "version": "1.60.1.2", "browser_specific_settings": { "gecko": { "strict_min_version": "78.0" } }, - "update_link": "https://github.com/gorhill/uBlock/releases/download/1.60.1b1/uBlock0_1.60.1b1.firefox.signed.xpi" + "update_link": "https://github.com/gorhill/uBlock/releases/download/1.60.1b2/uBlock0_1.60.1b2.firefox.signed.xpi" } ] } From bcb31db176883aa377d7619551e39ac7c3235bcd Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Wed, 2 Oct 2024 15:11:26 -0400 Subject: [PATCH 0296/1099] Update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 54c5126219204..cd68e2beebf33 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,6 @@ - [Skip dns resolution when requests are proxied through http](https://github.com/gorhill/uBlock/commit/4305bfbdb1) - [Blocking large media elements also prevents autoplay, regardless of size](https://github.com/gorhill/uBlock/commit/73ce4e6bcf) +- [Do not discard `!#else` block for unknown preprocessor tokens](https://github.com/gorhill/uBlock/commit/6cac645830) - [Add ability to decode base64 in `urlskip=`](https://github.com/gorhill/uBlock/commit/e81e70937f) - [Fix images not properly downloading on click](https://github.com/gorhill/uBlock/commit/aec0bd39e3) From fe49ced2ac937a8556a19cb61f4c2cb05ab3c54c Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Thu, 3 Oct 2024 13:31:52 -0400 Subject: [PATCH 0297/1099] Imrpove `prevent-xhr` scriptlet; add `trusted-prevent-xhr` scriptlet Add support for synchronous `send()` calls. `trusted-prevent-xhr` is essentially the same as `prevent-xhr` except that if the `directive` argument is not a known token, it will be used as is as the response text of the xhr request, whereas `prevent-xhr` returns an empty string when the directive is unknown. --- assets/resources/scriptlets.js | 432 ++++++++++++++++++--------------- 1 file changed, 236 insertions(+), 196 deletions(-) diff --git a/assets/resources/scriptlets.js b/assets/resources/scriptlets.js index 74e873974d739..895159baacd01 100644 --- a/assets/resources/scriptlets.js +++ b/assets/resources/scriptlets.js @@ -321,6 +321,9 @@ function runAtHtmlElementFn(fn) { // Reference: // https://github.com/AdguardTeam/Scriptlets/blob/master/wiki/about-scriptlets.md#prevent-xhr +// +// Added `trusted` argument to allow for returning arbitrary text. Can only +// be used through scriptlets requiring trusted source. builtinScriptlets.push({ name: 'generate-content.fn', @@ -329,7 +332,7 @@ builtinScriptlets.push({ 'safe-self.fn', ], }); -function generateContentFn(directive) { +function generateContentFn(trusted, directive) { const safe = safeSelf(); const randomize = len => { const chunks = []; @@ -343,27 +346,27 @@ function generateContentFn(directive) { return chunks.join(' ').slice(0, len); }; if ( directive === 'true' ) { - return Promise.resolve(randomize(10)); + return randomize(10); } if ( directive === 'emptyObj' ) { - return Promise.resolve('{}'); + return '{}'; } if ( directive === 'emptyArr' ) { - return Promise.resolve('[]'); + return '[]'; } if ( directive === 'emptyStr' ) { - return Promise.resolve(''); + return ''; } if ( directive.startsWith('length:') ) { const match = /^length:(\d+)(?:-(\d+))?$/.exec(directive); - if ( match ) { - const min = parseInt(match[1], 10); - const extent = safe.Math_max(parseInt(match[2], 10) || 0, min) - min; - const len = safe.Math_min(min + extent * safe.Math_random(), 500000); - return Promise.resolve(randomize(len | 0)); - } + if ( match === null ) { return ''; } + const min = parseInt(match[1], 10); + const extent = safe.Math_max(parseInt(match[2], 10) || 0, min) - min; + const len = safe.Math_min(min + extent * safe.Math_random(), 500000); + return randomize(len | 0); } - if ( directive.startsWith('war:') && scriptletGlobals.warOrigin ) { + if ( directive.startsWith('war:') ) { + if ( scriptletGlobals.warOrigin === undefined ) { return ''; } return new Promise(resolve => { const warOrigin = scriptletGlobals.warOrigin; const warName = directive.slice(4); @@ -379,9 +382,12 @@ function generateContentFn(directive) { }; warXHR.open('GET', fullpath.join('')); warXHR.send(); - }); + }).catch(( ) => ''); } - return Promise.resolve(''); + if ( trusted ) { + return directive; + } + return ''; } /******************************************************************************/ @@ -1565,6 +1571,196 @@ function proxyApplyFn( context[prop] = new Proxy(fn, proxyDetails); } +/******************************************************************************/ + +builtinScriptlets.push({ + name: 'prevent-xhr.fn', + fn: preventXhrFn, + dependencies: [ + 'generate-content.fn', + 'match-object-properties.fn', + 'parse-properties-to-match.fn', + 'safe-self.fn', + ], +}); +function preventXhrFn( + trusted = false, + propsToMatch = '', + directive = '' +) { + if ( typeof propsToMatch !== 'string' ) { return; } + const safe = safeSelf(); + const scriptletName = trusted ? 'trusted-prevent-xhr' : 'prevent-xhr'; + const logPrefix = safe.makeLogPrefix(scriptletName, propsToMatch, directive); + const xhrInstances = new WeakMap(); + const propNeedles = parsePropertiesToMatch(propsToMatch, 'url'); + const warOrigin = scriptletGlobals.warOrigin; + const safeDispatchEvent = (xhr, type) => { + try { + xhr.dispatchEvent(new Event(type)); + } catch(_) { + } + }; + const XHRBefore = XMLHttpRequest.prototype; + self.XMLHttpRequest = class extends self.XMLHttpRequest { + open(method, url, defer, ...args) { + xhrInstances.delete(this); + if ( warOrigin !== undefined && url.startsWith(warOrigin) ) { + return super.open(method, url, defer, ...args); + } + const haystack = { method, url }; + if ( propsToMatch === '' && directive === '' ) { + safe.uboLog(logPrefix, `Called: ${safe.JSON_stringify(haystack, null, 2)}`); + return super.open(method, url, defer, ...args); + } + if ( matchObjectProperties(propNeedles, haystack) ) { + const xhrDetails = Object.assign(haystack, { + xhr: this, + defer, + directive, + headers: { + 'date': '', + 'content-type': '', + 'content-length': '', + }, + props: { + response: { value: '' }, + responseText: { value: '' }, + responseXML: { value: null }, + responseURL: { value: haystack.url }, + }, + }); + xhrInstances.set(this, xhrDetails); + } + return super.open(method, url, defer, ...args); + } + send(...args) { + const xhrDetails = xhrInstances.get(this); + if ( xhrDetails === undefined ) { + return super.send(...args); + } + xhrDetails.headers['date'] = (new Date()).toUTCString(); + let xhrText = ''; + switch ( this.responseType ) { + case 'arraybuffer': + xhrDetails.props.response.value = new ArrayBuffer(0); + xhrDetails.headers['content-type'] = 'application/octet-stream'; + break; + case 'blob': + xhrDetails.props.response.value = new Blob([]); + xhrDetails.headers['content-type'] = 'application/octet-stream'; + break; + case 'document': { + const parser = new DOMParser(); + const doc = parser.parseFromString('', 'text/html'); + xhrDetails.props.response.value = doc; + xhrDetails.props.responseXML.value = doc; + xhrDetails.headers['content-type'] = 'text/html'; + break; + } + case 'json': + xhrDetails.props.response.value = {}; + xhrDetails.props.responseText.value = '{}'; + xhrDetails.headers['content-type'] = 'application/json'; + break; + default: { + if ( directive === '' ) { break; } + xhrText = generateContentFn(trusted, xhrDetails.directive); + if ( xhrText instanceof Promise ) { + xhrText = xhrText.then(text => { + xhrDetails.props.response.value = text; + xhrDetails.props.responseText.value = text; + }); + } else { + xhrDetails.props.response.value = xhrText; + xhrDetails.props.responseText.value = xhrText; + } + xhrDetails.headers['content-type'] = 'text/plain'; + break; + } + } + if ( xhrDetails.defer === false ) { + xhrDetails.headers['content-length'] = `${xhrDetails.props.response.value}`.length; + Object.defineProperties(xhrDetails.xhr, { + readyState: { value: 4 }, + status: { value: 200 }, + statusText: { value: 'OK' }, + }); + Object.defineProperties(xhrDetails.xhr, xhrDetails.props); + return; + } + Promise.resolve(xhrText).then(( ) => xhrDetails).then(details => { + Object.defineProperties(details.xhr, { + readyState: { value: 1, configurable: true }, + }); + safeDispatchEvent(details.xhr, 'readystatechange'); + return details; + }).then(details => { + xhrDetails.headers['content-length'] = `${details.props.response.value}`.length; + Object.defineProperties(details.xhr, { + readyState: { value: 2, configurable: true }, + status: { value: 200 }, + statusText: { value: 'OK' }, + }); + safeDispatchEvent(details.xhr, 'readystatechange'); + return details; + }).then(details => { + Object.defineProperties(details.xhr, { + readyState: { value: 3, configurable: true }, + }); + Object.defineProperties(details.xhr, details.props); + safeDispatchEvent(details.xhr, 'readystatechange'); + return details; + }).then(details => { + Object.defineProperties(details.xhr, { + readyState: { value: 4 }, + }); + safeDispatchEvent(details.xhr, 'readystatechange'); + safeDispatchEvent(details.xhr, 'load'); + safeDispatchEvent(details.xhr, 'loadend'); + safe.uboLog(logPrefix, `Prevented with response:\n${details.xhr.response}`); + }); + } + getResponseHeader(headerName) { + const xhrDetails = xhrInstances.get(this); + if ( xhrDetails === undefined || this.readyState < this.HEADERS_RECEIVED ) { + return super.getResponseHeader(headerName); + } + const value = xhrDetails.headers[headerName.toLowerCase()]; + if ( value !== undefined && value !== '' ) { return value; } + return null; + } + getAllResponseHeaders() { + const xhrDetails = xhrInstances.get(this); + if ( xhrDetails === undefined || this.readyState < this.HEADERS_RECEIVED ) { + return super.getAllResponseHeaders(); + } + const out = []; + for ( const [ name, value ] of Object.entries(xhrDetails.headers) ) { + if ( !value ) { continue; } + out.push(`${name}: ${value}`); + } + if ( out.length !== 0 ) { out.push(''); } + return out.join('\r\n'); + } + }; + self.XMLHttpRequest.prototype.open.toString = function() { + return XHRBefore.open.toString(); + }; + self.XMLHttpRequest.prototype.send.toString = function() { + return XHRBefore.send.toString(); + }; + self.XMLHttpRequest.prototype.getResponseHeader.toString = function() { + return XHRBefore.getResponseHeader.toString(); + }; + self.XMLHttpRequest.prototype.getAllResponseHeaders.toString = function() { + return XHRBefore.getAllResponseHeaders.toString(); + }; +} + + + + /******************************************************************************* Injectable scriptlets @@ -2271,7 +2467,7 @@ function noFetchIf( if ( proceed ) { return context.reflect(); } - return generateContentFn(responseBody).then(text => { + return Promise.resolve(generateContentFn(false, responseBody)).then(text => { safe.uboLog(logPrefix, `Prevented with response "${text}"`); const response = new Response(text, { headers: { @@ -2730,192 +2926,17 @@ function webrtcIf( /******************************************************************************/ builtinScriptlets.push({ - name: 'no-xhr-if.js', + name: 'prevent-xhr.js', aliases: [ - 'prevent-xhr.js', + 'no-xhr-if.js', ], - fn: noXhrIf, + fn: preventXhr, dependencies: [ - 'generate-content.fn', - 'match-object-properties.fn', - 'parse-properties-to-match.fn', - 'safe-self.fn', + 'prevent-xhr.fn', ], }); -function noXhrIf( - propsToMatch = '', - directive = '' -) { - if ( typeof propsToMatch !== 'string' ) { return; } - const safe = safeSelf(); - const logPrefix = safe.makeLogPrefix('prevent-xhr', propsToMatch, directive); - const xhrInstances = new WeakMap(); - const propNeedles = parsePropertiesToMatch(propsToMatch, 'url'); - const warOrigin = scriptletGlobals.warOrigin; - const headers = { - 'date': '', - 'content-type': '', - 'content-length': '', - }; - const safeDispatchEvent = (xhr, type) => { - try { - xhr.dispatchEvent(new Event(type)); - } catch(_) { - } - }; - const XHRBefore = XMLHttpRequest.prototype; - self.XMLHttpRequest = class extends self.XMLHttpRequest { - open(method, url, ...args) { - xhrInstances.delete(this); - if ( warOrigin !== undefined && url.startsWith(warOrigin) ) { - return super.open(method, url, ...args); - } - const haystack = { method, url }; - if ( propsToMatch === '' && directive === '' ) { - safe.uboLog(logPrefix, `Called: ${safe.JSON_stringify(haystack, null, 2)}`); - return super.open(method, url, ...args); - } - if ( matchObjectProperties(propNeedles, haystack) ) { - xhrInstances.set(this, haystack); - } - haystack.headers = Object.assign({}, headers); - return super.open(method, url, ...args); - } - send(...args) { - const haystack = xhrInstances.get(this); - if ( haystack === undefined ) { - return super.send(...args); - } - haystack.headers['date'] = (new Date()).toUTCString(); - let promise = Promise.resolve({ - xhr: this, - directive, - response: { - response: { value: '' }, - responseText: { value: '' }, - responseXML: { value: null }, - responseURL: { value: haystack.url }, - } - }); - switch ( this.responseType ) { - case 'arraybuffer': - promise = promise.then(details => { - const response = details.response; - response.response.value = new ArrayBuffer(0); - return details; - }); - haystack.headers['content-type'] = 'application/octet-stream'; - break; - case 'blob': - promise = promise.then(details => { - const response = details.response; - response.response.value = new Blob([]); - return details; - }); - haystack.headers['content-type'] = 'application/octet-stream'; - break; - case 'document': { - promise = promise.then(details => { - const parser = new DOMParser(); - const doc = parser.parseFromString('', 'text/html'); - const response = details.response; - response.response.value = doc; - response.responseXML.value = doc; - return details; - }); - haystack.headers['content-type'] = 'text/html'; - break; - } - case 'json': - promise = promise.then(details => { - const response = details.response; - response.response.value = {}; - response.responseText.value = '{}'; - return details; - }); - haystack.headers['content-type'] = 'application/json'; - break; - default: - if ( directive === '' ) { break; } - promise = promise.then(details => { - return generateContentFn(details.directive).then(text => { - const response = details.response; - response.response.value = text; - response.responseText.value = text; - return details; - }); - }); - haystack.headers['content-type'] = 'text/plain'; - break; - } - promise.then(details => { - Object.defineProperties(details.xhr, { - readyState: { value: 1, configurable: true }, - }); - safeDispatchEvent(details.xhr, 'readystatechange'); - return details; - }).then(details => { - const response = details.response; - haystack.headers['content-length'] = `${response.response.value}`.length; - Object.defineProperties(details.xhr, { - readyState: { value: 2, configurable: true }, - status: { value: 200 }, - statusText: { value: 'OK' }, - }); - safeDispatchEvent(details.xhr, 'readystatechange'); - return details; - }).then(details => { - Object.defineProperties(details.xhr, { - readyState: { value: 3, configurable: true }, - }); - Object.defineProperties(details.xhr, details.response); - safeDispatchEvent(details.xhr, 'readystatechange'); - return details; - }).then(details => { - Object.defineProperties(details.xhr, { - readyState: { value: 4 }, - }); - safeDispatchEvent(details.xhr, 'readystatechange'); - safeDispatchEvent(details.xhr, 'load'); - safeDispatchEvent(details.xhr, 'loadend'); - safe.uboLog(logPrefix, `Prevented with response:\n${details.xhr.response}`); - }); - } - getResponseHeader(headerName) { - const haystack = xhrInstances.get(this); - if ( haystack === undefined || this.readyState < this.HEADERS_RECEIVED ) { - return super.getResponseHeader(headerName); - } - const value = haystack.headers[headerName.toLowerCase()]; - if ( value !== undefined && value !== '' ) { return value; } - return null; - } - getAllResponseHeaders() { - const haystack = xhrInstances.get(this); - if ( haystack === undefined || this.readyState < this.HEADERS_RECEIVED ) { - return super.getAllResponseHeaders(); - } - const out = []; - for ( const [ name, value ] of Object.entries(haystack.headers) ) { - if ( !value ) { continue; } - out.push(`${name}: ${value}`); - } - if ( out.length !== 0 ) { out.push(''); } - return out.join('\r\n'); - } - }; - self.XMLHttpRequest.prototype.open.toString = function() { - return XHRBefore.open.toString(); - }; - self.XMLHttpRequest.prototype.send.toString = function() { - return XHRBefore.send.toString(); - }; - self.XMLHttpRequest.prototype.getResponseHeader.toString = function() { - return XHRBefore.getResponseHeader.toString(); - }; - self.XMLHttpRequest.prototype.getAllResponseHeaders.toString = function() { - return XHRBefore.getAllResponseHeaders.toString(); - }; +function preventXhr(...args) { + return preventXhrFn(false, ...args); } /******************************************************************************/ @@ -5103,4 +5124,23 @@ function trustedSuppressNativeMethod( }); } +/******************************************************************************* + * + * Trusted version of prevent-xhr(), which allows the use of an arbitrary + * string as response text. + * + * */ + +builtinScriptlets.push({ + name: 'trusted-prevent-xhr.js', + requiresTrust: true, + fn: trustedPreventXhr, + dependencies: [ + 'prevent-xhr.fn', + ], +}); +function trustedPreventXhr(...args) { + return preventXhrFn(true, ...args); +} + /******************************************************************************/ From f3bc426a57e5aaf9e783c5f4b3897ecb6e7a4085 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Thu, 3 Oct 2024 13:42:03 -0400 Subject: [PATCH 0298/1099] Update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index cd68e2beebf33..493f6ab04347f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,4 @@ +- [Improve `prevent-xhr` scriptlet; add `trusted-prevent-xhr` scriptlet](https://github.com/gorhill/uBlock/commit/fe49ced2ac) - [Skip dns resolution when requests are proxied through http](https://github.com/gorhill/uBlock/commit/4305bfbdb1) - [Blocking large media elements also prevents autoplay, regardless of size](https://github.com/gorhill/uBlock/commit/73ce4e6bcf) - [Do not discard `!#else` block for unknown preprocessor tokens](https://github.com/gorhill/uBlock/commit/6cac645830) From 89a1fd5b0e7dfa778c954627bc83b66fee0f128f Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Thu, 3 Oct 2024 13:42:24 -0400 Subject: [PATCH 0299/1099] New revision for dev build --- dist/version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dist/version b/dist/version index 77b51621e1800..471c6bcf69459 100644 --- a/dist/version +++ b/dist/version @@ -1 +1 @@ -1.60.1.2 \ No newline at end of file +1.60.1.3 \ No newline at end of file From 8196b99e9da59a50534eaab75c03d262c9200b4b Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Thu, 3 Oct 2024 13:51:14 -0400 Subject: [PATCH 0300/1099] Make Firefox dev build auto-update --- dist/firefox/updates.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dist/firefox/updates.json b/dist/firefox/updates.json index c4cc4eca09d3f..f86d4ec19fff6 100644 --- a/dist/firefox/updates.json +++ b/dist/firefox/updates.json @@ -3,9 +3,9 @@ "uBlock0@raymondhill.net": { "updates": [ { - "version": "1.60.1.2", + "version": "1.60.1.3", "browser_specific_settings": { "gecko": { "strict_min_version": "78.0" } }, - "update_link": "https://github.com/gorhill/uBlock/releases/download/1.60.1b2/uBlock0_1.60.1b2.firefox.signed.xpi" + "update_link": "https://github.com/gorhill/uBlock/releases/download/1.60.1b3/uBlock0_1.60.1b3.firefox.signed.xpi" } ] } From 9f4123a4e2f89d817e69129cd01055fd77d9d502 Mon Sep 17 00:00:00 2001 From: "Ilya (Marshal)" Date: Fri, 4 Oct 2024 11:27:24 +0200 Subject: [PATCH 0301/1099] Fix AdGuard Knowledge Base URLs --- assets/assets.dev.json | 28 ++++++++++++++-------------- assets/assets.json | 28 ++++++++++++++-------------- src/js/static-filtering-parser.js | 2 +- 3 files changed, 29 insertions(+), 29 deletions(-) diff --git a/assets/assets.dev.json b/assets/assets.dev.json index aa54da8290962..9c7accc0c3fea 100644 --- a/assets/assets.dev.json +++ b/assets/assets.dev.json @@ -137,7 +137,7 @@ "tags": "ads", "contentURL": "https://filters.adtidy.org/extension/ublock/filters/2_without_easylist.txt", "supportURL": "https://github.com/AdguardTeam/AdguardFilters#adguard-filters", - "instructionURL": "https://kb.adguard.com/en/general/adguard-ad-filters" + "instructionURL": "https://adguard.com/kb/general/ad-filtering/adguard-filters/" }, "adguard-mobile": { "content": "filters", @@ -148,7 +148,7 @@ "ua": "mobile", "contentURL": "https://filters.adtidy.org/extension/ublock/filters/11.txt", "supportURL": "https://github.com/AdguardTeam/AdguardFilters#adguard-filters", - "instructionURL": "https://kb.adguard.com/en/general/adguard-ad-filters" + "instructionURL": "https://adguard.com/kb/general/ad-filtering/adguard-filters/" }, "easylist": { "content": "filters", @@ -175,7 +175,7 @@ "tags": "privacy", "contentURL": "https://filters.adtidy.org/extension/ublock/filters/17.txt", "supportURL": "https://github.com/AdguardTeam/AdguardFilters#adguard-filters", - "instructionURL": "https://kb.adguard.com/en/general/adguard-ad-filters" + "instructionURL": "https://adguard.com/kb/general/ad-filtering/adguard-filters/" }, "adguard-spyware": { "content": "filters", @@ -184,7 +184,7 @@ "title": "AdGuard Tracking Protection", "contentURL": "https://filters.adtidy.org/extension/ublock/filters/3.txt", "supportURL": "https://github.com/AdguardTeam/AdguardFilters#adguard-filters", - "instructionURL": "https://kb.adguard.com/en/general/adguard-ad-filters" + "instructionURL": "https://adguard.com/kb/general/ad-filtering/adguard-filters/" }, "block-lan": { "content": "filters", @@ -256,7 +256,7 @@ "tags": "annoyances cookies", "contentURL": "https://filters.adtidy.org/extension/ublock/filters/18.txt", "supportURL": "https://github.com/AdguardTeam/AdguardFilters#adguard-filters", - "instructionURL": "https://kb.adguard.com/en/general/adguard-ad-filters" + "instructionURL": "https://adguard.com/kb/general/ad-filtering/adguard-filters/" }, "ublock-cookies-adguard": { "content": "filters", @@ -325,7 +325,7 @@ "tags": "annoyances social", "contentURL": "https://filters.adtidy.org/extension/ublock/filters/4.txt", "supportURL": "https://github.com/AdguardTeam/AdguardFilters#adguard-filters", - "instructionURL": "https://kb.adguard.com/en/general/adguard-ad-filters" + "instructionURL": "https://adguard.com/kb/general/ad-filtering/adguard-filters/" }, "fanboy-social": { "content": "filters", @@ -368,7 +368,7 @@ "tags": "annoyances", "contentURL": "https://filters.adtidy.org/extension/ublock/filters/19.txt", "supportURL": "https://github.com/AdguardTeam/AdguardFilters#adguard-filters", - "instructionURL": "https://kb.adguard.com/en/general/adguard-ad-filters" + "instructionURL": "https://adguard.com/kb/general/ad-filtering/adguard-filters/" }, "adguard-mobile-app-banners": { "content": "filters", @@ -379,7 +379,7 @@ "tags": "annoyances mobile", "contentURL": "https://filters.adtidy.org/extension/ublock/filters/20.txt", "supportURL": "https://github.com/AdguardTeam/AdguardFilters#adguard-filters", - "instructionURL": "https://kb.adguard.com/en/general/adguard-ad-filters" + "instructionURL": "https://adguard.com/kb/general/ad-filtering/adguard-filters/" }, "adguard-other-annoyances": { "content": "filters", @@ -390,7 +390,7 @@ "tags": "annoyances", "contentURL": "https://filters.adtidy.org/extension/ublock/filters/21.txt", "supportURL": "https://github.com/AdguardTeam/AdguardFilters#adguard-filters", - "instructionURL": "https://kb.adguard.com/en/general/adguard-ad-filters" + "instructionURL": "https://adguard.com/kb/general/ad-filtering/adguard-filters/" }, "adguard-widgets": { "content": "filters", @@ -401,7 +401,7 @@ "tags": "annoyances", "contentURL": "https://filters.adtidy.org/extension/ublock/filters/22.txt", "supportURL": "https://github.com/AdguardTeam/AdguardFilters#adguard-filters", - "instructionURL": "https://kb.adguard.com/en/general/adguard-ad-filters" + "instructionURL": "https://adguard.com/kb/general/ad-filtering/adguard-filters/" }, "easylist-annoyances": { "content": "filters", @@ -717,7 +717,7 @@ "lang": "ja", "contentURL": "https://filters.adtidy.org/extension/ublock/filters/7.txt", "supportURL": "https://github.com/AdguardTeam/AdguardFilters#adguard-filters", - "instructionURL": "https://kb.adguard.com/en/general/adguard-ad-filters" + "instructionURL": "https://adguard.com/kb/general/ad-filtering/adguard-filters/" }, "KOR-1": { "content": "filters", @@ -772,7 +772,7 @@ "lang": "af fy nl", "contentURL": "https://filters.adtidy.org/extension/ublock/filters/8.txt", "cdnURLs": null, - "supportURL": "https://kb.adguard.com/en/general/adguard-ad-filters" + "supportURL": "https://adguard.com/kb/general/ad-filtering/adguard-filters/" }, "NOR-0": { "content": "filters", @@ -858,7 +858,7 @@ "lang": "an ast ca cak es eu gl gn trs pt quz", "contentURL": "https://filters.adtidy.org/extension/ublock/filters/9.txt", "supportURL": "https://github.com/AdguardTeam/AdguardFilters#adguard-filters", - "instructionURL": "https://kb.adguard.com/en/general/adguard-ad-filters" + "instructionURL": "https://adguard.com/kb/general/ad-filtering/adguard-filters/" }, "SVN-0": { "content": "filters", @@ -903,7 +903,7 @@ "lang": "tr", "contentURL": "https://filters.adtidy.org/extension/ublock/filters/13.txt", "supportURL": "https://github.com/AdguardTeam/AdguardFilters#adguard-filters", - "instructionURL": "https://kb.adguard.com/en/general/adguard-ad-filters" + "instructionURL": "https://adguard.com/kb/general/ad-filtering/adguard-filters/" }, "VIE-1": { "content": "filters", diff --git a/assets/assets.json b/assets/assets.json index 8665c36e8a2bc..8b0e79f39cdd5 100644 --- a/assets/assets.json +++ b/assets/assets.json @@ -137,7 +137,7 @@ "tags": "ads", "contentURL": "https://filters.adtidy.org/extension/ublock/filters/2_without_easylist.txt", "supportURL": "https://github.com/AdguardTeam/AdguardFilters#adguard-filters", - "instructionURL": "https://kb.adguard.com/en/general/adguard-ad-filters" + "instructionURL": "https://adguard.com/kb/general/ad-filtering/adguard-filters/" }, "adguard-mobile": { "content": "filters", @@ -148,7 +148,7 @@ "ua": "mobile", "contentURL": "https://filters.adtidy.org/extension/ublock/filters/11.txt", "supportURL": "https://github.com/AdguardTeam/AdguardFilters#adguard-filters", - "instructionURL": "https://kb.adguard.com/en/general/adguard-ad-filters" + "instructionURL": "https://adguard.com/kb/general/ad-filtering/adguard-filters/" }, "easylist": { "content": "filters", @@ -175,7 +175,7 @@ "tags": "privacy", "contentURL": "https://filters.adtidy.org/extension/ublock/filters/17.txt", "supportURL": "https://github.com/AdguardTeam/AdguardFilters#adguard-filters", - "instructionURL": "https://kb.adguard.com/en/general/adguard-ad-filters" + "instructionURL": "https://adguard.com/kb/general/ad-filtering/adguard-filters/" }, "adguard-spyware": { "content": "filters", @@ -184,7 +184,7 @@ "title": "AdGuard Tracking Protection", "contentURL": "https://filters.adtidy.org/extension/ublock/filters/3.txt", "supportURL": "https://github.com/AdguardTeam/AdguardFilters#adguard-filters", - "instructionURL": "https://kb.adguard.com/en/general/adguard-ad-filters" + "instructionURL": "https://adguard.com/kb/general/ad-filtering/adguard-filters/" }, "block-lan": { "content": "filters", @@ -256,7 +256,7 @@ "tags": "annoyances cookies", "contentURL": "https://filters.adtidy.org/extension/ublock/filters/18.txt", "supportURL": "https://github.com/AdguardTeam/AdguardFilters#adguard-filters", - "instructionURL": "https://kb.adguard.com/en/general/adguard-ad-filters" + "instructionURL": "https://adguard.com/kb/general/ad-filtering/adguard-filters/" }, "ublock-cookies-adguard": { "content": "filters", @@ -325,7 +325,7 @@ "tags": "annoyances social", "contentURL": "https://filters.adtidy.org/extension/ublock/filters/4.txt", "supportURL": "https://github.com/AdguardTeam/AdguardFilters#adguard-filters", - "instructionURL": "https://kb.adguard.com/en/general/adguard-ad-filters" + "instructionURL": "https://adguard.com/kb/general/ad-filtering/adguard-filters/" }, "fanboy-social": { "content": "filters", @@ -368,7 +368,7 @@ "tags": "annoyances", "contentURL": "https://filters.adtidy.org/extension/ublock/filters/19.txt", "supportURL": "https://github.com/AdguardTeam/AdguardFilters#adguard-filters", - "instructionURL": "https://kb.adguard.com/en/general/adguard-ad-filters" + "instructionURL": "https://adguard.com/kb/general/ad-filtering/adguard-filters/" }, "adguard-mobile-app-banners": { "content": "filters", @@ -379,7 +379,7 @@ "tags": "annoyances mobile", "contentURL": "https://filters.adtidy.org/extension/ublock/filters/20.txt", "supportURL": "https://github.com/AdguardTeam/AdguardFilters#adguard-filters", - "instructionURL": "https://kb.adguard.com/en/general/adguard-ad-filters" + "instructionURL": "https://adguard.com/kb/general/ad-filtering/adguard-filters/" }, "adguard-other-annoyances": { "content": "filters", @@ -390,7 +390,7 @@ "tags": "annoyances", "contentURL": "https://filters.adtidy.org/extension/ublock/filters/21.txt", "supportURL": "https://github.com/AdguardTeam/AdguardFilters#adguard-filters", - "instructionURL": "https://kb.adguard.com/en/general/adguard-ad-filters" + "instructionURL": "https://adguard.com/kb/general/ad-filtering/adguard-filters/" }, "adguard-widgets": { "content": "filters", @@ -401,7 +401,7 @@ "tags": "annoyances", "contentURL": "https://filters.adtidy.org/extension/ublock/filters/22.txt", "supportURL": "https://github.com/AdguardTeam/AdguardFilters#adguard-filters", - "instructionURL": "https://kb.adguard.com/en/general/adguard-ad-filters" + "instructionURL": "https://adguard.com/kb/general/ad-filtering/adguard-filters/" }, "easylist-annoyances": { "content": "filters", @@ -717,7 +717,7 @@ "lang": "ja", "contentURL": "https://filters.adtidy.org/extension/ublock/filters/7.txt", "supportURL": "https://github.com/AdguardTeam/AdguardFilters#adguard-filters", - "instructionURL": "https://kb.adguard.com/en/general/adguard-ad-filters" + "instructionURL": "https://adguard.com/kb/general/ad-filtering/adguard-filters/" }, "KOR-1": { "content": "filters", @@ -772,7 +772,7 @@ "lang": "af fy nl", "contentURL": "https://filters.adtidy.org/extension/ublock/filters/8.txt", "cdnURLs": null, - "supportURL": "https://kb.adguard.com/en/general/adguard-ad-filters" + "supportURL": "https://adguard.com/kb/general/ad-filtering/adguard-filters/" }, "NOR-0": { "content": "filters", @@ -858,7 +858,7 @@ "lang": "an ast ca cak es eu gl gn trs pt quz", "contentURL": "https://filters.adtidy.org/extension/ublock/filters/9.txt", "supportURL": "https://github.com/AdguardTeam/AdguardFilters#adguard-filters", - "instructionURL": "https://kb.adguard.com/en/general/adguard-ad-filters" + "instructionURL": "https://adguard.com/kb/general/ad-filtering/adguard-filters/" }, "SVN-0": { "content": "filters", @@ -903,7 +903,7 @@ "lang": "tr", "contentURL": "https://filters.adtidy.org/extension/ublock/filters/13.txt", "supportURL": "https://github.com/AdguardTeam/AdguardFilters#adguard-filters", - "instructionURL": "https://kb.adguard.com/en/general/adguard-ad-filters" + "instructionURL": "https://adguard.com/kb/general/ad-filtering/adguard-filters/" }, "VIE-1": { "content": "filters", diff --git a/src/js/static-filtering-parser.js b/src/js/static-filtering-parser.js index 11858c20bda32..efbe8d97cfe53 100644 --- a/src/js/static-filtering-parser.js +++ b/src/js/static-filtering-parser.js @@ -4362,7 +4362,7 @@ export const utils = (( ) => { // only ABP. [ 'ext_abp', 'false' ], // Compatibility with other blockers - // https://kb.adguard.com/en/general/how-to-create-your-own-ad-filters#adguard-specific + // https://adguard.com/kb/general/ad-filtering/create-own-filters/#conditions-directive [ 'adguard', 'adguard' ], [ 'adguard_app_android', 'false' ], [ 'adguard_app_ios', 'false' ], From 5133991f7e04ee3ad20dba2c42491c85f10438ea Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Fri, 4 Oct 2024 08:57:10 -0400 Subject: [PATCH 0302/1099] Fix spurious error in content script No guarantee vAPI.bootstrap will still be present when callback executes. --- src/js/scriptlets/should-inject-contentscript.js | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/js/scriptlets/should-inject-contentscript.js b/src/js/scriptlets/should-inject-contentscript.js index 94d0cd3f1e571..df3378a88fb2c 100644 --- a/src/js/scriptlets/should-inject-contentscript.js +++ b/src/js/scriptlets/should-inject-contentscript.js @@ -19,8 +19,6 @@ Home: https://github.com/gorhill/uBlock */ -'use strict'; - // If content scripts are already injected, we need to respond with `false`, // to "should inject content scripts?" // @@ -31,7 +29,7 @@ try { const status = vAPI.uBO !== true; if ( status === false && vAPI.bootstrap ) { - self.requestIdleCallback(( ) => vAPI && vAPI.bootstrap()); + self.requestIdleCallback(( ) => vAPI?.bootstrap()); } return status; } catch(ex) { From 1abc8647425752960f6778fc451bd2a07cb3d3a6 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Fri, 4 Oct 2024 12:24:35 -0400 Subject: [PATCH 0303/1099] Add `trusted-prevent-dom-bypass` scriptlet @description Prevent the bypassing of uBO scriptlets through anonymous embedded context. To ensure that a target method in the embedded context is using the corresponding parent context's method (which is assumed to be properly patched), or to replace the embedded context with that of the parent context. Root issue: https://issues.chromium.org/issues/40202434 @param methodPath The method which calls must be intercepted. The arguments of the intercepted calls are assumed to be HTMLElement, anything else will be ignored. @param selector (optional) A plain CSS selector which will be used in a `document.querySelector()` call, to validate that the returned element must be processed by the scriptlet. If no selector is provided, all elements will be processed. @param targetMethod (optional) The method in the embedded context which should be delegated to the parent context. If no method is specified, the embedded context becomes the parent one, i.e. all properties of the embedded context will be that of the parent context. --- assets/resources/scriptlets.js | 75 ++++++++++++++++++++++++++++++++++ 1 file changed, 75 insertions(+) diff --git a/assets/resources/scriptlets.js b/assets/resources/scriptlets.js index 895159baacd01..49861db339466 100644 --- a/assets/resources/scriptlets.js +++ b/assets/resources/scriptlets.js @@ -5143,4 +5143,79 @@ function trustedPreventXhr(...args) { return preventXhrFn(true, ...args); } +/** + * + * @trustedScriptlet trusted-prevent-dom-bypass + * + * @description + * Prevent the bypassing of uBO scriptlets through anonymous embedded context. + * + * Ensure that a target method in the embedded context is using the + * corresponding parent context's method (which is assumed to be + * properly patched), or to replace the embedded context with that of the + * parent context. + * + * Root issue: + * https://issues.chromium.org/issues/40202434 + * + * @param methodPath + * The method which calls must be intercepted. The arguments + * of the intercepted calls are assumed to be HTMLElement, anything else will + * be ignored. + * + * @param selector (optional) + * A plain CSS selector which will be used in a `document.querySelector()` + * call, to validate that the returned element must be processed by the + * scriptlet. If no selector is provided, all elements will be processed. + * + * @param targetMethod (optional) + * The method in the embedded context which should be delegated to the + * parent context. If no method is specified, the embedded context becomes + * the parent one, i.e. all properties of the embedded context will be that + * of the parent context. + * + * */ + +builtinScriptlets.push({ + name: 'trusted-prevent-dom-bypass.js', + requiresTrust: true, + fn: trustedPreventDomBypass, + dependencies: [ + 'proxy-apply.fn', + 'safe-self.fn', + ], +}); +function trustedPreventDomBypass( + methodPath = '', + selector = '', + targetMethod = '' +) { + if ( methodPath === '' ) { return; } + const safe = safeSelf(); + const logPrefix = safe.makeLogPrefix('trusted-prevent-dom-bypass', methodPath, selector, targetMethod); + proxyApplyFn(methodPath, function(context) { + const elems = context.callArgs.filter(e => e instanceof HTMLElement); + const r = context.reflect(); + if ( elems.length === 0 ) { return r; } + const targetContexts = selector !== '' + ? new Set(document.querySelectorAll(selector)) + : undefined; + for ( const elem of elems ) { + try { + if ( `${elem.contentWindow}` !== '[object Window]' ) { continue; } + if ( elem.contentWindow.location.href !== 'about:blank' ) { continue; } + if ( targetContexts && targetContexts.has(elem) === false ) { continue; } + if ( targetMethod !== '' ) { + elem.contentWindow[targetMethod] = self[targetMethod]; + } else { + Object.defineProperty(elem, 'contentWindow', { value: self }); + } + safe.uboLog(logPrefix, 'Bypass prevented'); + } catch(_) { + } + } + return r; + }); +} + /******************************************************************************/ From 5e2f94bebcf590b0e4751097921ae7af2c6bf204 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Fri, 4 Oct 2024 12:27:57 -0400 Subject: [PATCH 0304/1099] New revision for dev build --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 493f6ab04347f..f07376d8a2c91 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,4 @@ +- [Add `trusted-prevent-dom-bypass` scriptlet](https://github.com/gorhill/uBlock/commit/1abc864742) - [Improve `prevent-xhr` scriptlet; add `trusted-prevent-xhr` scriptlet](https://github.com/gorhill/uBlock/commit/fe49ced2ac) - [Skip dns resolution when requests are proxied through http](https://github.com/gorhill/uBlock/commit/4305bfbdb1) - [Blocking large media elements also prevents autoplay, regardless of size](https://github.com/gorhill/uBlock/commit/73ce4e6bcf) From fccda96bd7d4902da46d19f6877ebb97c501fe59 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Fri, 4 Oct 2024 12:28:25 -0400 Subject: [PATCH 0305/1099] New revision for dev build --- dist/version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dist/version b/dist/version index 471c6bcf69459..b83080e7d3331 100644 --- a/dist/version +++ b/dist/version @@ -1 +1 @@ -1.60.1.3 \ No newline at end of file +1.60.1.4 \ No newline at end of file From d24ffe6bb8e8c1920a8a92d9731f114f0b7d45fa Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Fri, 4 Oct 2024 12:30:37 -0400 Subject: [PATCH 0306/1099] Import translation work from https://crowdin.com/project/ublock --- platform/mv3/description/webstore.uk.txt | 6 ++-- .../mv3/extension/_locales/be/messages.json | 36 +++++++++---------- .../mv3/extension/_locales/sq/messages.json | 34 +++++++++--------- .../mv3/extension/_locales/uk/messages.json | 14 ++++---- .../extension/_locales/zh_CN/messages.json | 20 +++++------ src/_locales/be/messages.json | 2 +- 6 files changed, 56 insertions(+), 56 deletions(-) diff --git a/platform/mv3/description/webstore.uk.txt b/platform/mv3/description/webstore.uk.txt index 9fbd578480a61..e6f8a978d37e8 100644 --- a/platform/mv3/description/webstore.uk.txt +++ b/platform/mv3/description/webstore.uk.txt @@ -1,13 +1,13 @@ -uBO Lite (uBOL) - це блокувальник вмісту на основі MV3, що не потребує дозволу. +uBO Lite (uBOL) - це блокувальник вмісту на основі MV3, що не потребує дозволів. -Усталений набір правил відповідає типовому набору фільтрів uBlock Origin: +Набір правил за замовчанням відповідає типовому набору фільтрів uBlock Origin: - Вбудовані списки фільтрів uBlock Origin - EasyList - EasyPrivacy - Список серверів реклами та стеження від Peter Lowe -Ви можете ввімкнути більше наборів правил, перейшовши на сторінку налаштувань — натисніть на піктограму _Шестерень_ на спливній панелі. +Ви можете ввімкнути більше наборів правил, перейшовши на сторінку налаштувань — натисніть на піктограму "Перейти до панелі керування" на спливній панелі. uBOL повністю декларативний, тобто немає необхідності в постійному процесі uBOL для здійснення фільтрації, а фільтрація вмісту на основі CSS/JS-ін'єкцій надійно виконується самим браузером, а не розширенням. Це означає, що сам uBOL не споживає ресурси процесора/пам'яті під час блокування вмісту — службовий робочий процес uBOL потрібен _лише_ під час взаємодії зі спливною панеллю або сторінками опцій. diff --git a/platform/mv3/extension/_locales/be/messages.json b/platform/mv3/extension/_locales/be/messages.json index c25464d019166..ee5a133da0542 100644 --- a/platform/mv3/extension/_locales/be/messages.json +++ b/platform/mv3/extension/_locales/be/messages.json @@ -8,7 +8,7 @@ "description": "this will be in the Chrome web store: must be 132 characters or less" }, "perRulesetStats": { - "message": "{{ruleCount}} правілаў, сканвертаваных з {{filterCount}} сеткавых фільтраў", + "message": "{{ruleCount}} правілаў, пераўтвораных з {{filterCount}} сеткавых фільтраў", "description": "Appears aside each filter list in the _3rd-party filters_ pane" }, "dashboardName": { @@ -32,7 +32,7 @@ "description": "Label in the popup panel for the current filtering mode" }, "popupTipReport": { - "message": "Report an issue on this website", + "message": "Паведаміць пра праблему на гэтым вэб-сайце", "description": "Tooltip used for the 'chat' icon in the panel" }, "popupTipDashboard": { @@ -104,67 +104,67 @@ "description": "Shown in the About pane" }, "supportS6H": { - "message": "Report a filter issue", + "message": "Паведаміць пра праблему з фільтрам", "description": "Header of 'Report a filter issue' section in Support pane" }, "supportS3P1": { - "message": "Report filter issues with specific websites to the uBlockOrigin/uAssets issue tracker. Requires a GitHub account.", + "message": "Паведамляйце пра праблемы з фільтрамі, датычныя канкрэтных вэб-сайтаў, праз трэкер праблемuBlockOrigin/uAssets . Патрэбны ўліковы запіс GitHub.", "description": "First paragraph of 'Filter issues' section in Support pane" }, "supportS6P1S1": { - "message": "To avoid burdening volunteers with duplicate reports, please verify that the issue has not already been reported.", + "message": "Каб не абцяжарваць добраахвотнікаў дубляванымі справаздачамі, калі ласка, пераканайцеся, што пра гэтую праблему не паведамлялі раней.", "description": "A paragraph in the filter issue reporter section" }, "supportFindSpecificButton": { - "message": "Find similar reports", + "message": "Знайсці падобныя справаздачы", "description": "A clickable link in the filter issue reporter section" }, "supportS6URL": { - "message": "Address of the webpage:", + "message": "Адрас вэб-старонкі:", "description": "Label for the URL of the page" }, "supportS6Select1": { - "message": "The webpage…", + "message": "Вэб-старонка…", "description": "Label for widget to select type of issue" }, "supportS6Select1Option0": { - "message": "-- Pick an entry --", + "message": "-- Выберыце тэму --", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option1": { - "message": "Shows ads or ad leftovers", + "message": "Паказвае рэкламу або яе астачу", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option2": { - "message": "Has overlays or other nuisances", + "message": "Мае накладкі або іншыя недарэчнасці", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option3": { - "message": "Detects uBO Lite", + "message": "Выяўляе uBO Lite", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option4": { - "message": "Has privacy-related issues", + "message": "Мае праблемы, датычныя прыватнасці", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option5": { - "message": "Malfunctions when uBO Lite is enabled", + "message": "Няспраўнасці пры ўключаным uBO Lite", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option6": { - "message": "Opens unwanted tabs or windows", + "message": "Адкрывае непажаданыя карткі або вокны", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option7": { - "message": "Leads to badware, phishing", + "message": "Вядзе да шкодных праграм, фішынгу", "description": "An entry in the widget used to select the type of issue" }, "supportS6Checkbox1": { - "message": "Label the webpage as “NSFW” (“Not Safe For Work”)", + "message": "Пазначыць вэб-старонку як “NSFW” (“небяспечна для працы”)", "description": "A checkbox to use for NSFW sites" }, "supportReportSpecificButton": { - "message": "Create new report", + "message": "Стварыць новую справаздачу", "description": "Text for button which open an external webpage in Support pane" }, "firstRunSectionLabel": { diff --git a/platform/mv3/extension/_locales/sq/messages.json b/platform/mv3/extension/_locales/sq/messages.json index 20be851ec8e12..4bcd36d07283a 100644 --- a/platform/mv3/extension/_locales/sq/messages.json +++ b/platform/mv3/extension/_locales/sq/messages.json @@ -32,7 +32,7 @@ "description": "Label in the popup panel for the current filtering mode" }, "popupTipReport": { - "message": "Report an issue on this website", + "message": "Raportoj problemin me uebsajtin", "description": "Tooltip used for the 'chat' icon in the panel" }, "popupTipDashboard": { @@ -104,67 +104,67 @@ "description": "Shown in the About pane" }, "supportS6H": { - "message": "Report a filter issue", + "message": "Raportoni problemet me filtrat", "description": "Header of 'Report a filter issue' section in Support pane" }, "supportS3P1": { - "message": "Report filter issues with specific websites to the uBlockOrigin/uAssets issue tracker. Requires a GitHub account.", + "message": "Raportoni problemet me filtrat në uebsajt specifikë në uBlockOrigin/uAssets issue tracker. Kërkon një llogari GitHub.", "description": "First paragraph of 'Filter issues' section in Support pane" }, "supportS6P1S1": { - "message": "To avoid burdening volunteers with duplicate reports, please verify that the issue has not already been reported.", + "message": "Verifikoni a është raportuar më parë problemi që të mos i lodhni vullnetarët e tjerë me të njëjtat gjëra.", "description": "A paragraph in the filter issue reporter section" }, "supportFindSpecificButton": { - "message": "Find similar reports", + "message": "Gjej raporte të ngjashme", "description": "A clickable link in the filter issue reporter section" }, "supportS6URL": { - "message": "Address of the webpage:", + "message": "Adresa e uebsajtit:", "description": "Label for the URL of the page" }, "supportS6Select1": { - "message": "The webpage…", + "message": "Uebsajti...", "description": "Label for widget to select type of issue" }, "supportS6Select1Option0": { - "message": "-- Pick an entry --", + "message": "-- Zgjidhni --", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option1": { - "message": "Shows ads or ad leftovers", + "message": "Shfaq reklama ose pjesë reklamash", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option2": { - "message": "Has overlays or other nuisances", + "message": "Ka mbivendosje ose parregullsi të tjera", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option3": { - "message": "Detects uBO Lite", + "message": "Zbulon uBO Lite", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option4": { - "message": "Has privacy-related issues", + "message": "Ka probleme me privatësinë", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option5": { - "message": "Malfunctions when uBO Lite is enabled", + "message": "Krijon mosfunksionime kur uBO Lite është i aktivizuar", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option6": { - "message": "Opens unwanted tabs or windows", + "message": "Hap skeda ose dritare të panevojshme", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option7": { - "message": "Leads to badware, phishing", + "message": "Çon në instalimin e programeve keqdashëse, mashtruese", "description": "An entry in the widget used to select the type of issue" }, "supportS6Checkbox1": { - "message": "Label the webpage as “NSFW” (“Not Safe For Work”)", + "message": "Etiketoni faqen e internetit si “NSFW” (“Not Safe For Work”)", "description": "A checkbox to use for NSFW sites" }, "supportReportSpecificButton": { - "message": "Create new report", + "message": "Krijoj raport të ri", "description": "Text for button which open an external webpage in Support pane" }, "firstRunSectionLabel": { diff --git a/platform/mv3/extension/_locales/uk/messages.json b/platform/mv3/extension/_locales/uk/messages.json index eea2c7a415322..5e5e47e002990 100644 --- a/platform/mv3/extension/_locales/uk/messages.json +++ b/platform/mv3/extension/_locales/uk/messages.json @@ -108,7 +108,7 @@ "description": "Header of 'Report a filter issue' section in Support pane" }, "supportS3P1": { - "message": "Повідомляйте про вади с фільтрами на конкретних сайтах у трекер помилокuBlockOrigin/uAssets. Вимагає обліковий запис GitHub.", + "message": "Повідомляйте про вади з фільтрами на конкретних вебсайтах у відстежувач помилок uBlockOrigin/uAssets. Потрібен обліковий запис GitHub.", "description": "First paragraph of 'Filter issues' section in Support pane" }, "supportS6P1S1": { @@ -148,7 +148,7 @@ "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option5": { - "message": "Вади коли uBO Lite включено", + "message": "Вади коли uBO Lite ввімкнено", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option6": { @@ -160,7 +160,7 @@ "description": "An entry in the widget used to select the type of issue" }, "supportS6Checkbox1": { - "message": "Помітити цю сторінку як «Небезпечно для роботи» («NSFW»)", + "message": "Позначити цю сторінку «Небезпечною для роботи» («NSFW»)", "description": "A checkbox to use for NSFW sites" }, "supportReportSpecificButton": { @@ -208,15 +208,15 @@ "description": "This describes the 'optimal' filtering mode" }, "completeFilteringModeDescription": { - "message": "Розширена мережева фільтрація плюс специфічна та загальна розширена фільтрація з вибраних списків фільтрів.\n\nПотребує широкого дозволу на читання та зміну даних на всіх сайтах.\n\nЗагальна розширена фільтрація може призвести до збільшення використання ресурсів веб-сторінки.", + "message": "Розширена мережева фільтрація плюс специфічна та загальна розширена фільтрація з вибраних списків фільтрів.\n\nПотребує розширених дозволів на читання та зміну даних на всіх сайтах.\n\nЗагальна розширена фільтрація може призвести до збільшення використання ресурсів вебсторінкою.", "description": "This describes the 'complete' filtering mode" }, "noFilteringModeDescription": { - "message": "Список імен хостів, для яких буде відбуватись фільтрування", + "message": "Список імен хостів, для яких не буде застосовуватись фільтрування", "description": "A short description for the editable field which lists trusted sites" }, "noFilteringModePlaceholder": { - "message": "[тільки імена доменів]\nexample.com\ngames.example\n...", + "message": "[тільки імена хостів]\nexample.com\ngames.example\n...", "description": "Default text for in edit field" }, "behaviorSectionLabel": { @@ -228,7 +228,7 @@ "description": "Label for a checkbox in the options page" }, "showBlockedCountLabel": { - "message": "Показувати кількість заблокованих запитів на піктограмі на панелі інструментів", + "message": "Показувати кількість заблокованих запитів на піктограмі панелі інструментів", "description": "Label for a checkbox in the options page" } } diff --git a/platform/mv3/extension/_locales/zh_CN/messages.json b/platform/mv3/extension/_locales/zh_CN/messages.json index d50e605f35e36..e8b4b31de453e 100644 --- a/platform/mv3/extension/_locales/zh_CN/messages.json +++ b/platform/mv3/extension/_locales/zh_CN/messages.json @@ -32,7 +32,7 @@ "description": "Label in the popup panel for the current filtering mode" }, "popupTipReport": { - "message": "Report an issue on this website", + "message": "反馈该网站上的问题", "description": "Tooltip used for the 'chat' icon in the panel" }, "popupTipDashboard": { @@ -112,19 +112,19 @@ "description": "First paragraph of 'Filter issues' section in Support pane" }, "supportS6P1S1": { - "message": "To avoid burdening volunteers with duplicate reports, please verify that the issue has not already been reported.", + "message": "请确认该问题未曾上报,以避免加重志愿者负担。", "description": "A paragraph in the filter issue reporter section" }, "supportFindSpecificButton": { - "message": "Find similar reports", + "message": "找到相似报告", "description": "A clickable link in the filter issue reporter section" }, "supportS6URL": { - "message": "Address of the webpage:", + "message": "网页地址:", "description": "Label for the URL of the page" }, "supportS6Select1": { - "message": "The webpage…", + "message": "网页...", "description": "Label for widget to select type of issue" }, "supportS6Select1Option0": { @@ -136,7 +136,7 @@ "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option2": { - "message": "Has overlays or other nuisances", + "message": "存在遮盖或类似问题", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option3": { @@ -144,7 +144,7 @@ "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option4": { - "message": "Has privacy-related issues", + "message": "存在隐私相关问题", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option5": { @@ -152,7 +152,7 @@ "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option6": { - "message": "Opens unwanted tabs or windows", + "message": "打开了不想要的标签页或窗口", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option7": { @@ -164,7 +164,7 @@ "description": "A checkbox to use for NSFW sites" }, "supportReportSpecificButton": { - "message": "Create new report", + "message": "创建新报告", "description": "Text for button which open an external webpage in Support pane" }, "firstRunSectionLabel": { @@ -216,7 +216,7 @@ "description": "A short description for the editable field which lists trusted sites" }, "noFilteringModePlaceholder": { - "message": "[hostnames only]\nexample.com\ngames.example\n...", + "message": "[域名]\nexample.com\ngames.example\n...", "description": "Default text for in edit field" }, "behaviorSectionLabel": { diff --git a/src/_locales/be/messages.json b/src/_locales/be/messages.json index 6ae12101583fe..7d66c4d1e1fe3 100644 --- a/src/_locales/be/messages.json +++ b/src/_locales/be/messages.json @@ -1012,7 +1012,7 @@ "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option7": { - "message": "Leads to badware, phishing", + "message": "Вядзе да шкодных праграм, фішынгу", "description": "An entry in the widget used to select the type of issue" }, "supportS6Checkbox1": { From 05ba71aef0b714e1a19d292c456853510da967fa Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Fri, 4 Oct 2024 13:30:54 -0400 Subject: [PATCH 0307/1099] Make Firefox dev build auto-update --- dist/firefox/updates.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dist/firefox/updates.json b/dist/firefox/updates.json index f86d4ec19fff6..ba091f474950a 100644 --- a/dist/firefox/updates.json +++ b/dist/firefox/updates.json @@ -3,9 +3,9 @@ "uBlock0@raymondhill.net": { "updates": [ { - "version": "1.60.1.3", + "version": "1.60.1.4", "browser_specific_settings": { "gecko": { "strict_min_version": "78.0" } }, - "update_link": "https://github.com/gorhill/uBlock/releases/download/1.60.1b3/uBlock0_1.60.1b3.firefox.signed.xpi" + "update_link": "https://github.com/gorhill/uBlock/releases/download/1.60.1b4/uBlock0_1.60.1b4.firefox.signed.xpi" } ] } From a0a33eb9b98cdab1e3e3412ed33ce2ad96181f86 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sat, 5 Oct 2024 07:41:44 -0400 Subject: [PATCH 0308/1099] Code review for new `trusted-prevent-dom-bypass` scriptlet Related commit: https://github.com/gorhill/uBlock/commit/1abc864742 --- assets/resources/scriptlets.js | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/assets/resources/scriptlets.js b/assets/resources/scriptlets.js index 49861db339466..f674c714d4041 100644 --- a/assets/resources/scriptlets.js +++ b/assets/resources/scriptlets.js @@ -5163,17 +5163,18 @@ function trustedPreventXhr(...args) { * of the intercepted calls are assumed to be HTMLElement, anything else will * be ignored. * - * @param selector (optional) - * A plain CSS selector which will be used in a `document.querySelector()` - * call, to validate that the returned element must be processed by the - * scriptlet. If no selector is provided, all elements will be processed. - * - * @param targetMethod (optional) + * @param [targetProp] * The method in the embedded context which should be delegated to the * parent context. If no method is specified, the embedded context becomes * the parent one, i.e. all properties of the embedded context will be that * of the parent context. * + * @example + * ##+js(trusted-prevent-dom-bypass, Element.prototype.append, open) + * + * @example + * ##+js(trusted-prevent-dom-bypass, Element.prototype.appendChild, XMLHttpRequest) + * * */ builtinScriptlets.push({ @@ -5187,26 +5188,25 @@ builtinScriptlets.push({ }); function trustedPreventDomBypass( methodPath = '', - selector = '', - targetMethod = '' + targetProp = '' ) { if ( methodPath === '' ) { return; } const safe = safeSelf(); - const logPrefix = safe.makeLogPrefix('trusted-prevent-dom-bypass', methodPath, selector, targetMethod); + const logPrefix = safe.makeLogPrefix('trusted-prevent-dom-bypass', methodPath, targetProp); proxyApplyFn(methodPath, function(context) { - const elems = context.callArgs.filter(e => e instanceof HTMLElement); + const elems = new Set(context.callArgs.filter(e => e instanceof HTMLElement)); const r = context.reflect(); if ( elems.length === 0 ) { return r; } - const targetContexts = selector !== '' - ? new Set(document.querySelectorAll(selector)) - : undefined; for ( const elem of elems ) { try { if ( `${elem.contentWindow}` !== '[object Window]' ) { continue; } - if ( elem.contentWindow.location.href !== 'about:blank' ) { continue; } - if ( targetContexts && targetContexts.has(elem) === false ) { continue; } - if ( targetMethod !== '' ) { - elem.contentWindow[targetMethod] = self[targetMethod]; + if ( elem.contentWindow.location.href !== 'about:blank' ) { + if ( elem.contentWindow.location.href !== self.location.href ) { + continue; + } + } + if ( targetProp !== '' ) { + elem.contentWindow[targetProp] = self[targetProp]; } else { Object.defineProperty(elem, 'contentWindow', { value: self }); } From 95b0ce5e3a64d966924dd701d1e336402d586b25 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sat, 5 Oct 2024 08:35:43 -0400 Subject: [PATCH 0309/1099] Add `trusted-override-element-method` scriptlet @description Override the behavior of a method on matching elements. @param methodPath The method which calls must be intercepted. @param [selector] A CSS selector which the target element must match. If not specified, the override will occur for all elements. @param [disposition] How the override should be handled. If not specified, the overridden call will be equivalent to an empty function. If set to `throw`, an exception will be thrown. Any other value will be validated and returned as a supported safe constant. @example ..##+js(trusted-override-element-method, HTMLAnchorElement.prototype.click, a[target="_blank"][style]) --- assets/resources/scriptlets.js | 64 ++++++++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) diff --git a/assets/resources/scriptlets.js b/assets/resources/scriptlets.js index f674c714d4041..2b188ebef8216 100644 --- a/assets/resources/scriptlets.js +++ b/assets/resources/scriptlets.js @@ -5218,4 +5218,68 @@ function trustedPreventDomBypass( }); } +/** + * + * @trustedScriptlet trusted-override-element-method + * + * @description + * Override the behavior of a method on matching elements. + * + * @param methodPath + * The method which calls must be intercepted. + * + * @param [selector] + * A CSS selector which the target element must match. If not specified, + * the override will occur for all elements. + * + * @param [disposition] + * How the override should be handled. If not specified, the overridden call + * will be equivalent to an empty function. If set to `throw`, an exception + * will be thrown. Any other value will be validated and returned as a + * supported safe constant. + * + * @example + * ##+js(trusted-override-element-method, HTMLAnchorElement.prototype.click, a[target="_blank"][style]) + * + * */ + +builtinScriptlets.push({ + name: 'trusted-override-element-method.js', + requiresTrust: true, + fn: trustedOverrideElementMethod, + dependencies: [ + 'proxy-apply.fn', + 'safe-self.fn', + 'validate-constant.fn', + ], +}); +function trustedOverrideElementMethod( + methodPath = '', + selector = '', + disposition = '' +) { + if ( methodPath === '' ) { return; } + const safe = safeSelf(); + const logPrefix = safe.makeLogPrefix('trusted-override-element-method', methodPath, selector, disposition); + proxyApplyFn(methodPath, function(context) { + let override = selector === ''; + if ( override === false ) { + const { thisArg } = context; + try { + override = thisArg.closest(selector) === thisArg; + } catch(_) { + } + } + if ( override === false ) { + return context.reflect(); + } + safe.uboLog(logPrefix, 'Overridden'); + if ( disposition === '' ) { return; } + if ( disposition === 'throw' ) { + throw new ReferenceError(); + } + return validateConstantFn(false, disposition); + }); +} + /******************************************************************************/ From 3b53d8e5b73e77f85cebf3786c854c6e1bf7501e Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sat, 5 Oct 2024 08:43:14 -0400 Subject: [PATCH 0310/1099] Update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index f07376d8a2c91..7bb559f6bb64e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,4 @@ +- [Add `trusted-override-element-method` scriptlet](https://github.com/gorhill/uBlock/commit/95b0ce5e3a) - [Add `trusted-prevent-dom-bypass` scriptlet](https://github.com/gorhill/uBlock/commit/1abc864742) - [Improve `prevent-xhr` scriptlet; add `trusted-prevent-xhr` scriptlet](https://github.com/gorhill/uBlock/commit/fe49ced2ac) - [Skip dns resolution when requests are proxied through http](https://github.com/gorhill/uBlock/commit/4305bfbdb1) From 41693407b27d517486741ff19c2774cb55fd1319 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sat, 5 Oct 2024 11:32:59 -0400 Subject: [PATCH 0311/1099] Fix npm test suite Ensure serialization returns copy of data rather than live references to data. This allows to immediately deserialize() the result of serialize(). Also, adjust code to modified behavior of filterQuery(). --- platform/nodejs/index.js | 1 + platform/npm/package-lock.json | 91 +++++++++++++++++++--------------- src/js/biditrie.js | 4 +- src/js/hntrie.js | 4 +- src/js/static-net-filtering.js | 29 +++-------- 5 files changed, 65 insertions(+), 64 deletions(-) diff --git a/platform/nodejs/index.js b/platform/nodejs/index.js index 1d39a7d11292e..7bc808e89941f 100644 --- a/platform/nodejs/index.js +++ b/platform/nodejs/index.js @@ -218,6 +218,7 @@ class StaticNetFilteringEngine { } filterQuery(details) { + fctx.redirectURL = undefined; const directives = snfe.filterQuery(fctx.fromDetails(details)); if ( directives === undefined ) { return; } return { redirectURL: fctx.redirectURL, directives }; diff --git a/platform/npm/package-lock.json b/platform/npm/package-lock.json index b819bb0fddb68..3a1790e27a906 100644 --- a/platform/npm/package-lock.json +++ b/platform/npm/package-lock.json @@ -242,12 +242,15 @@ } }, "node_modules/braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", "dev": true, "dependencies": { - "fill-range": "^7.0.1" + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" } }, "node_modules/browser-stdout": { @@ -683,12 +686,15 @@ } }, "node_modules/fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", "dev": true, "dependencies": { "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" } }, "node_modules/find-up": { @@ -892,7 +898,10 @@ "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true + "dev": true, + "engines": { + "node": ">=0.12.0" + } }, "node_modules/is-path-inside": { "version": "3.0.3", @@ -1062,15 +1071,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/make-dir/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true, - "bin": { - "semver": "bin/semver.js" - } - }, "node_modules/minimatch": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", @@ -1398,6 +1398,15 @@ "resolved": "git+ssh://git@github.com/mjethani/scaling-palm-tree.git#15cf1ab37e038771e1ff8005edc46d95f176739f", "dev": true }, + "node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, "node_modules/serialize-javascript": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", @@ -1481,6 +1490,9 @@ "dev": true, "dependencies": { "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" } }, "node_modules/type-check": { @@ -1537,10 +1549,13 @@ } }, "node_modules/word-wrap": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", - "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", - "dev": true + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } }, "node_modules/workerpool": { "version": "6.2.1", @@ -1818,12 +1833,12 @@ } }, "braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", "dev": true, "requires": { - "fill-range": "^7.0.1" + "fill-range": "^7.1.1" } }, "browser-stdout": { @@ -2202,9 +2217,9 @@ } }, "fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", "dev": true, "requires": { "to-regex-range": "^5.0.1" @@ -2532,14 +2547,6 @@ "dev": true, "requires": { "semver": "^6.0.0" - }, - "dependencies": { - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true - } } }, "minimatch": { @@ -2808,6 +2815,12 @@ "dev": true, "from": "scaling-palm-tree@github:mjethani/scaling-palm-tree#15cf1ab37e038771e1ff8005edc46d95f176739f" }, + "semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true + }, "serialize-javascript": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", @@ -2935,9 +2948,9 @@ } }, "word-wrap": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", - "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", "dev": true }, "workerpool": { diff --git a/src/js/biditrie.js b/src/js/biditrie.js index 06566d2e1ac98..2fa0495062fc6 100644 --- a/src/js/biditrie.js +++ b/src/js/biditrie.js @@ -582,12 +582,12 @@ class BidiTrieContainer { } toSelfie() { - const buf32 = this.buf32.subarray(0, this.buf32[CHAR1_SLOT] + 3 >>> 2); + const buf32 = this.buf32.slice(0, this.buf32[CHAR1_SLOT] + 3 >>> 2); return { buf32, checksum: i32Checksum(buf32) }; } fromSelfie(selfie) { - if ( selfie instanceof Object === false ) { return false; } + if ( typeof selfie !== 'object' || selfie === null ) { return false; } if ( selfie.buf32 instanceof Uint32Array === false ) { return false; } if ( selfie.checksum !== i32Checksum(selfie.buf32) ) { return false; } const byteLength = selfie.buf32.length << 2; diff --git a/src/js/hntrie.js b/src/js/hntrie.js index 4f89a99642633..5885e6e21175d 100644 --- a/src/js/hntrie.js +++ b/src/js/hntrie.js @@ -452,12 +452,12 @@ class HNTrieContainer { } toSelfie() { - const buf32 = this.buf32.subarray(0, this.buf32[CHAR1_SLOT] + 3 >>> 2); + const buf32 = this.buf32.slice(0, this.buf32[CHAR1_SLOT] + 3 >>> 2); return { buf32, checksum: i32Checksum(buf32) }; } fromSelfie(selfie) { - if ( selfie instanceof Object === false ) { return false; } + if ( typeof selfie !== 'object' || selfie === null ) { return false; } if ( selfie.buf32 instanceof Uint32Array === false ) { return false; } if ( selfie.checksum !== i32Checksum(selfie.buf32) ) { return false; } this.needle = ''; diff --git a/src/js/static-net-filtering.js b/src/js/static-net-filtering.js index f5b1841752368..ac4b373df616d 100644 --- a/src/js/static-net-filtering.js +++ b/src/js/static-net-filtering.js @@ -495,7 +495,7 @@ const filterDataReset = ( ) => { filterDataWritePtr = 2; }; const filterDataToSelfie = ( ) => - filterData.subarray(0, filterDataWritePtr); + filterData.slice(0, filterDataWritePtr); const filterDataFromSelfie = selfie => { if ( selfie instanceof Int32Array === false ) { return false; } @@ -3193,7 +3193,7 @@ const urlTokenizer = new (class { } toSelfie() { - return this.knownTokens; + return this.knownTokens.slice(); } fromSelfie(selfie) { @@ -4779,7 +4779,7 @@ StaticNetFilteringEngine.prototype.toSelfie = function() { processedFilterCount: this.processedFilterCount, acceptedCount: this.acceptedCount, discardedCount: this.discardedCount, - bitsToBucket: this.bitsToBucket, + bitsToBucket: new Map(this.bitsToBucket), urlTokenizer: urlTokenizer.toSelfie(), destHNTrieContainer: destHNTrieContainer.toSelfie(), origHNTrieContainer: origHNTrieContainer.toSelfie(), @@ -4789,20 +4789,13 @@ StaticNetFilteringEngine.prototype.toSelfie = function() { }; }; -StaticNetFilteringEngine.prototype.serialize = async function() { - const selfie = []; - const storage = { - put(name, data) { - selfie.push([ name, data ]); - } - }; - await this.toSelfie(storage, ''); - return JSON.stringify(selfie); +StaticNetFilteringEngine.prototype.serialize = function() { + return this.toSelfie(); }; /******************************************************************************/ -StaticNetFilteringEngine.prototype.fromSelfie = async function(selfie) { +StaticNetFilteringEngine.prototype.fromSelfie = function(selfie) { if ( typeof selfie !== 'object' || selfie === null ) { return; } this.reset(); @@ -4835,14 +4828,8 @@ StaticNetFilteringEngine.prototype.fromSelfie = async function(selfie) { return true; }; -StaticNetFilteringEngine.prototype.unserialize = async function(s) { - const selfie = new Map(JSON.parse(s)); - const storage = { - async get(name) { - return { content: selfie.get(name) }; - } - }; - return this.fromSelfie(storage, ''); +StaticNetFilteringEngine.prototype.unserialize = function(selfie) { + return this.fromSelfie(selfie); }; /******************************************************************************/ From 6d2b3375f863d21751794f2d3745c6705600f7f2 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sat, 5 Oct 2024 12:42:30 -0400 Subject: [PATCH 0312/1099] Mind that BroadcastChannel contructor can throw in Firefox BroadcastChannel constructor throws in Firefox when Enhanced Tracking Protection is set to "strict". This behavior could cause scriptlet injection to wholly break when uBO's logger was opened, as BroadcastChannel() is used by scriptlets to report information to the logger. This commit ensures that exceptions from BroadcastChannel constructor are properly handled. The scriptlets will fall back to report at the console should they be unable to report to the logger through BroadcastChannel. --- assets/resources/scriptlets.js | 67 ++++++++++++++++++++-------------- src/js/scriptlet-filtering.js | 39 +++++++++++--------- 2 files changed, 60 insertions(+), 46 deletions(-) diff --git a/assets/resources/scriptlets.js b/assets/resources/scriptlets.js index 2b188ebef8216..788354f920e1c 100644 --- a/assets/resources/scriptlets.js +++ b/assets/resources/scriptlets.js @@ -173,13 +173,11 @@ function safeSelf() { scriptletGlobals.safeSelf = safe; if ( scriptletGlobals.bcSecret === undefined ) { return safe; } // This is executed only when the logger is opened - const bc = new self.BroadcastChannel(scriptletGlobals.bcSecret); - let bcBuffer = []; safe.logLevel = scriptletGlobals.logLevel || 1; let lastLogType = ''; let lastLogText = ''; let lastLogTime = 0; - safe.sendToLogger = (type, ...args) => { + safe.toLogText = (type, ...args) => { if ( args.length === 0 ) { return; } const text = `[${document.location.hostname || document.location.href}]${args.join(' ')}`; if ( text === lastLogText && type === lastLogType ) { @@ -188,30 +186,45 @@ function safeSelf() { lastLogType = type; lastLogText = text; lastLogTime = Date.now(); - if ( bcBuffer === undefined ) { - return bc.postMessage({ what: 'messageToLogger', type, text }); - } - bcBuffer.push({ type, text }); - }; - bc.onmessage = ev => { - const msg = ev.data; - switch ( msg ) { - case 'iamready!': - if ( bcBuffer === undefined ) { break; } - bcBuffer.forEach(({ type, text }) => - bc.postMessage({ what: 'messageToLogger', type, text }) - ); - bcBuffer = undefined; - break; - case 'setScriptletLogLevelToOne': - safe.logLevel = 1; - break; - case 'setScriptletLogLevelToTwo': - safe.logLevel = 2; - break; - } + return text; }; - bc.postMessage('areyouready?'); + try { + const bc = new self.BroadcastChannel(scriptletGlobals.bcSecret); + let bcBuffer = []; + safe.sendToLogger = (type, ...args) => { + const text = safe.toLogText(type, ...args); + if ( text === undefined ) { return; } + if ( bcBuffer === undefined ) { + return bc.postMessage({ what: 'messageToLogger', type, text }); + } + bcBuffer.push({ type, text }); + }; + bc.onmessage = ev => { + const msg = ev.data; + switch ( msg ) { + case 'iamready!': + if ( bcBuffer === undefined ) { break; } + bcBuffer.forEach(({ type, text }) => + bc.postMessage({ what: 'messageToLogger', type, text }) + ); + bcBuffer = undefined; + break; + case 'setScriptletLogLevelToOne': + safe.logLevel = 1; + break; + case 'setScriptletLogLevelToTwo': + safe.logLevel = 2; + break; + } + }; + bc.postMessage('areyouready?'); + } catch(_) { + safe.sendToLogger = (type, ...args) => { + const text = safe.toLogText(type, ...args); + if ( text === undefined ) { return; } + console.log(`uBO${text}`); + }; + } return safe; } @@ -5144,7 +5157,6 @@ function trustedPreventXhr(...args) { } /** - * * @trustedScriptlet trusted-prevent-dom-bypass * * @description @@ -5219,7 +5231,6 @@ function trustedPreventDomBypass( } /** - * * @trustedScriptlet trusted-override-element-method * * @description diff --git a/src/js/scriptlet-filtering.js b/src/js/scriptlet-filtering.js index e221a4197c0d0..1cc6a959b3536 100644 --- a/src/js/scriptlet-filtering.js +++ b/src/js/scriptlet-filtering.js @@ -176,25 +176,28 @@ const onScriptletMessageInjector = (( ) => { '(', function(name) { if ( self.uBO_bcSecret ) { return; } - const bcSecret = new self.BroadcastChannel(name); - bcSecret.onmessage = ev => { - const msg = ev.data; - switch ( typeof msg ) { - case 'string': - if ( msg !== 'areyouready?' ) { break; } - bcSecret.postMessage('iamready!'); - break; - case 'object': - if ( self.vAPI && self.vAPI.messaging ) { - self.vAPI.messaging.send('contentscript', msg); - } else { - console.log(`[uBO][${msg.type}]${msg.text}`); + try { + const bcSecret = new self.BroadcastChannel(name); + bcSecret.onmessage = ev => { + const msg = ev.data; + switch ( typeof msg ) { + case 'string': + if ( msg !== 'areyouready?' ) { break; } + bcSecret.postMessage('iamready!'); + break; + case 'object': + if ( self.vAPI && self.vAPI.messaging ) { + self.vAPI.messaging.send('contentscript', msg); + } else { + console.log(`[uBO][${msg.type}]${msg.text}`); + } + break; } - break; - } - }; - bcSecret.postMessage('iamready!'); - self.uBO_bcSecret = bcSecret; + }; + bcSecret.postMessage('iamready!'); + self.uBO_bcSecret = bcSecret; + } catch(_) { + } }.toString(), ')(', 'bcSecret-slot', From 5145747ac78d6e8fda002821be2debaebb028f87 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sat, 5 Oct 2024 13:30:18 -0400 Subject: [PATCH 0313/1099] Update chengelog --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7bb559f6bb64e..807a49ec6dfac 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,5 @@ +- [Mind that BroadcastChannel contructor can throw in Firefox](https://github.com/gorhill/uBlock/commit/6d2b3375f8) +- [Fix npm test suite](https://github.com/gorhill/uBlock/commit/41693407b2) - [Add `trusted-override-element-method` scriptlet](https://github.com/gorhill/uBlock/commit/95b0ce5e3a) - [Add `trusted-prevent-dom-bypass` scriptlet](https://github.com/gorhill/uBlock/commit/1abc864742) - [Improve `prevent-xhr` scriptlet; add `trusted-prevent-xhr` scriptlet](https://github.com/gorhill/uBlock/commit/fe49ced2ac) From 73404e59884b49fb4bf63af5c51b034cd6996304 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sat, 5 Oct 2024 13:30:49 -0400 Subject: [PATCH 0314/1099] New revision for dev build --- dist/version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dist/version b/dist/version index b83080e7d3331..eacccbee2b04d 100644 --- a/dist/version +++ b/dist/version @@ -1 +1 @@ -1.60.1.4 \ No newline at end of file +1.60.1.5 \ No newline at end of file From 5f2ee6caf85a71a3f819edebac49ed315e08f9e2 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sat, 5 Oct 2024 14:00:44 -0400 Subject: [PATCH 0315/1099] Make Firefox dev build auto-update --- dist/firefox/updates.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dist/firefox/updates.json b/dist/firefox/updates.json index ba091f474950a..63e7097041649 100644 --- a/dist/firefox/updates.json +++ b/dist/firefox/updates.json @@ -3,9 +3,9 @@ "uBlock0@raymondhill.net": { "updates": [ { - "version": "1.60.1.4", + "version": "1.60.1.5", "browser_specific_settings": { "gecko": { "strict_min_version": "78.0" } }, - "update_link": "https://github.com/gorhill/uBlock/releases/download/1.60.1b4/uBlock0_1.60.1b4.firefox.signed.xpi" + "update_link": "https://github.com/gorhill/uBlock/releases/download/1.60.1b5/uBlock0_1.60.1b5.firefox.signed.xpi" } ] } From 02cba63331b4ba8f62d72ef7222584d003367e94 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sat, 5 Oct 2024 14:51:38 -0400 Subject: [PATCH 0316/1099] Partially revert 4169340 --- src/js/biditrie.js | 2 +- src/js/hntrie.js | 2 +- src/js/static-net-filtering.js | 6 +++--- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/js/biditrie.js b/src/js/biditrie.js index 2fa0495062fc6..560d5918a2276 100644 --- a/src/js/biditrie.js +++ b/src/js/biditrie.js @@ -582,7 +582,7 @@ class BidiTrieContainer { } toSelfie() { - const buf32 = this.buf32.slice(0, this.buf32[CHAR1_SLOT] + 3 >>> 2); + const buf32 = this.buf32.subarray(0, this.buf32[CHAR1_SLOT] + 3 >>> 2); return { buf32, checksum: i32Checksum(buf32) }; } diff --git a/src/js/hntrie.js b/src/js/hntrie.js index 5885e6e21175d..518d0b8ddb19c 100644 --- a/src/js/hntrie.js +++ b/src/js/hntrie.js @@ -452,7 +452,7 @@ class HNTrieContainer { } toSelfie() { - const buf32 = this.buf32.slice(0, this.buf32[CHAR1_SLOT] + 3 >>> 2); + const buf32 = this.buf32.subarray(0, this.buf32[CHAR1_SLOT] + 3 >>> 2); return { buf32, checksum: i32Checksum(buf32) }; } diff --git a/src/js/static-net-filtering.js b/src/js/static-net-filtering.js index ac4b373df616d..ec62bb945aea7 100644 --- a/src/js/static-net-filtering.js +++ b/src/js/static-net-filtering.js @@ -495,7 +495,7 @@ const filterDataReset = ( ) => { filterDataWritePtr = 2; }; const filterDataToSelfie = ( ) => - filterData.slice(0, filterDataWritePtr); + filterData.subarray(0, filterDataWritePtr); const filterDataFromSelfie = selfie => { if ( selfie instanceof Int32Array === false ) { return false; } @@ -3193,7 +3193,7 @@ const urlTokenizer = new (class { } toSelfie() { - return this.knownTokens.slice(); + return this.knownTokens; } fromSelfie(selfie) { @@ -4779,7 +4779,7 @@ StaticNetFilteringEngine.prototype.toSelfie = function() { processedFilterCount: this.processedFilterCount, acceptedCount: this.acceptedCount, discardedCount: this.discardedCount, - bitsToBucket: new Map(this.bitsToBucket), + bitsToBucket: this.bitsToBucket, urlTokenizer: urlTokenizer.toSelfie(), destHNTrieContainer: destHNTrieContainer.toSelfie(), origHNTrieContainer: origHNTrieContainer.toSelfie(), From 64b2086ba44e986af2f1b052cdbe2b07b3034e1a Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sat, 5 Oct 2024 14:59:43 -0400 Subject: [PATCH 0317/1099] Add ability to lookup parameter name in `urlskip=` Relate case: https://github.com/uBlockOrigin/uBlock-issues/issues/3206#issuecomment-2395121619 Newly supported step: `&i`, meant to lookup a parameter's name at position `i` (1-based). The parameter name will be used as the URL (whereas `?` is meant to lookup a parameter's value). --- src/js/static-filtering-parser.js | 2 +- src/js/static-net-filtering.js | 13 ++++++++++++- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/src/js/static-filtering-parser.js b/src/js/static-filtering-parser.js index efbe8d97cfe53..1249b07b8e35c 100644 --- a/src/js/static-filtering-parser.js +++ b/src/js/static-filtering-parser.js @@ -1531,7 +1531,7 @@ export class AstFilterParser { break; } const value = this.getNetOptionValue(NODE_TYPE_NET_OPTION_NAME_URLSKIP); - if ( value.startsWith('?') === false || value.length < 2 ) { + if ( value.length < 2 ) { this.astError = AST_ERROR_OPTION_BADVALUE; realBad = true; } diff --git a/src/js/static-net-filtering.js b/src/js/static-net-filtering.js index ec62bb945aea7..1942295732215 100644 --- a/src/js/static-net-filtering.js +++ b/src/js/static-net-filtering.js @@ -5411,8 +5411,9 @@ function urlSkip(urlin, steps) { try { let urlout; for ( const step of steps ) { + const c0 = step.charCodeAt(0); // Extract from URL parameter - if ( step.startsWith('?') ) { + if ( c0 === 0x3F ) { /* ? */ urlout = (new URL(urlin)).searchParams.get(step.slice(1)); if ( urlout === null ) { return; } if ( urlout.includes(' ') ) { @@ -5421,6 +5422,16 @@ function urlSkip(urlin, steps) { urlin = urlout; continue; } + // Extract from URL parameter name at position i + if ( c0 === 0x26 ) { /* & */ + const i = (parseInt(step.slice(1)) || 0) - 1; + if ( i < 0 ) { return; } + const url = new URL(urlin); + if ( i >= url.searchParams.size ) { return; } + const params = Array.from(url.searchParams.keys()); + urlin = urlout = decodeURIComponent(params[i]); + continue; + } // Enforce https if ( step === '+https' ) { const s = urlin.replace(/^https?:\/\//, ''); From 4c1b1d4cdb3602c92c0f09fd6893cb832506e593 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sat, 5 Oct 2024 16:09:41 -0400 Subject: [PATCH 0318/1099] Update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 807a49ec6dfac..916250a61119e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,4 @@ +- [Add ability to lookup parameter name in `urlskip=`](https://github.com/gorhill/uBlock/commit/64b2086ba4) - [Mind that BroadcastChannel contructor can throw in Firefox](https://github.com/gorhill/uBlock/commit/6d2b3375f8) - [Fix npm test suite](https://github.com/gorhill/uBlock/commit/41693407b2) - [Add `trusted-override-element-method` scriptlet](https://github.com/gorhill/uBlock/commit/95b0ce5e3a) From 4775c9cb458b46d90ba0a9ac8c4d9108be7b7127 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sat, 5 Oct 2024 16:10:03 -0400 Subject: [PATCH 0319/1099] New revision for dev build --- dist/version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dist/version b/dist/version index eacccbee2b04d..7b36c924aba8c 100644 --- a/dist/version +++ b/dist/version @@ -1 +1 @@ -1.60.1.5 \ No newline at end of file +1.60.1.6 \ No newline at end of file From 1f45902a61175c4206f7fcda499f15380c513339 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sat, 5 Oct 2024 16:21:29 -0400 Subject: [PATCH 0320/1099] Make Firefox dev build auto-update --- dist/firefox/updates.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dist/firefox/updates.json b/dist/firefox/updates.json index 63e7097041649..c303b212de4ee 100644 --- a/dist/firefox/updates.json +++ b/dist/firefox/updates.json @@ -3,9 +3,9 @@ "uBlock0@raymondhill.net": { "updates": [ { - "version": "1.60.1.5", + "version": "1.60.1.6", "browser_specific_settings": { "gecko": { "strict_min_version": "78.0" } }, - "update_link": "https://github.com/gorhill/uBlock/releases/download/1.60.1b5/uBlock0_1.60.1b5.firefox.signed.xpi" + "update_link": "https://github.com/gorhill/uBlock/releases/download/1.60.1b6/uBlock0_1.60.1b6.firefox.signed.xpi" } ] } From 160d7f3c33b9880735246b023df12a5f87df08c4 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sun, 6 Oct 2024 11:07:42 -0400 Subject: [PATCH 0321/1099] Improve `trusted-override-element-method` scriptlet Support `debug` as valid `disposition` value: to trigger a `debugger` statement, but only if uBO's logger is opened. Related feedback: https://github.com/uBlockOrigin/uAssets/issues/25510#issuecomment-2395446342 --- assets/resources/scriptlets.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/assets/resources/scriptlets.js b/assets/resources/scriptlets.js index 788354f920e1c..e35757cfa4d0c 100644 --- a/assets/resources/scriptlets.js +++ b/assets/resources/scriptlets.js @@ -222,7 +222,7 @@ function safeSelf() { safe.sendToLogger = (type, ...args) => { const text = safe.toLogText(type, ...args); if ( text === undefined ) { return; } - console.log(`uBO${text}`); + safe.log(`uBO ${text}`); }; } return safe; @@ -5286,6 +5286,9 @@ function trustedOverrideElementMethod( } safe.uboLog(logPrefix, 'Overridden'); if ( disposition === '' ) { return; } + if ( disposition === 'debug' && safe.logLevel !== 0 ) { + debugger; // eslint-disable-line no-debugger + } if ( disposition === 'throw' ) { throw new ReferenceError(); } From 818cb2d801d9a486ee010133a67a824856951ee6 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sun, 6 Oct 2024 11:15:08 -0400 Subject: [PATCH 0322/1099] Fix npm test suite Related commits: - https://github.com/gorhill/uBlock/commit/02cba63331 - https://github.com/gorhill/uBlock/commit/41693407b2 --- .gitignore | 1 + platform/nodejs/.eslintrc.json | 38 +++++++++++++++++++++++++ platform/nodejs/index.js | 51 +++++++++++++++++++++------------- platform/npm/package.json | 2 +- src/js/s14e-serializer.js | 35 ++++++++++++++++++----- tools/make-nodejs.sh | 1 + 6 files changed, 100 insertions(+), 28 deletions(-) create mode 100644 platform/nodejs/.eslintrc.json diff --git a/.gitignore b/.gitignore index 1f064b847d71f..2c0e571513d19 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ *.bak *.pem __pycache__/ +node_modules/ /dist/build/ /tmp/ diff --git a/platform/nodejs/.eslintrc.json b/platform/nodejs/.eslintrc.json new file mode 100644 index 0000000000000..5f7c6b58c5ccf --- /dev/null +++ b/platform/nodejs/.eslintrc.json @@ -0,0 +1,38 @@ +{ + "root": true, + "env": { + "es2021": true, + "node": true + }, + "extends": "eslint:recommended", + "parserOptions": { + "ecmaVersion": 12, + "sourceType": "module" + }, + "rules": { + "eqeqeq": [ "warn", "always" ], + "indent": [ + "warn", + 4, + { + "ArrayExpression": "first", + "CallExpression": { "arguments": "first" }, + "MemberExpression": "off", + "ObjectExpression": "off", + "ignoreComments": true, + "ignoredNodes": [ + "AssignmentExpression:has(Literal)" + ] + } + ], + "getter-return": "off", + "no-control-regex": "off", + "no-empty": [ "error", { "allowEmptyCatch": true } ], + "no-promise-executor-return": [ "error" ], + "no-template-curly-in-string": [ "error" ], + "no-unreachable-loop": [ "error" ], + "no-useless-backreference": [ "error" ], + "no-useless-escape": "off", + "require-atomic-updates": [ "warn" ] + } +} diff --git a/platform/nodejs/index.js b/platform/nodejs/index.js index 7bc808e89941f..fbc17da859000 100644 --- a/platform/nodejs/index.js +++ b/platform/nodejs/index.js @@ -19,31 +19,40 @@ Home: https://github.com/gorhill/uBlock */ -/* globals WebAssembly */ - -'use strict'; +import * as s14e from './js/s14e-serializer.js'; +import * as sfp from './js/static-filtering-parser.js'; -/******************************************************************************/ +import { + CompiledListReader, + CompiledListWriter, +} from './js/static-filtering-io.js'; +import { + TextDecoder, + TextEncoder, +} from 'util'; +import { + dirname, + resolve +} from 'path'; +import { + domainToASCII, + fileURLToPath +} from 'url'; +import { FilteringContext } from './js/filtering-context.js'; +import { LineIterator } from './js/text-utils.js'; import { createRequire } from 'module'; - +import publicSuffixList from './lib/publicsuffixlist/publicsuffixlist.js'; import { readFileSync } from 'fs'; -import { dirname, resolve } from 'path'; -import { domainToASCII, fileURLToPath } from 'url'; - -const __dirname = dirname(fileURLToPath(import.meta.url)); +import snfe from './js/static-net-filtering.js'; -import publicSuffixList from './lib/publicsuffixlist/publicsuffixlist.js'; +/******************************************************************************/ -import snfe from './js/static-net-filtering.js'; -import { FilteringContext } from './js/filtering-context.js'; -import { LineIterator } from './js/text-utils.js'; -import * as sfp from './js/static-filtering-parser.js'; +const __dirname = dirname(fileURLToPath(import.meta.url)); -import { - CompiledListReader, - CompiledListWriter, -} from './js/static-filtering-io.js'; +// https://stackoverflow.com/questions/69187442/const-utf8encoder-new-textencoder-in-node-js +globalThis.TextDecoder = TextDecoder; +globalThis.TextEncoder = TextEncoder; /******************************************************************************/ @@ -241,11 +250,13 @@ class StaticNetFilteringEngine { } serialize() { - return snfe.serialize(); + const data = snfe.serialize(); + return s14e.serialize(data, { compress: true }); } deserialize(serialized) { - return snfe.unserialize(serialized); + const data = s14e.deserialize(serialized); + return snfe.unserialize(data); } static async create({ noPSL = false } = {}) { diff --git a/platform/npm/package.json b/platform/npm/package.json index a505449e624f6..a65faffd37569 100644 --- a/platform/npm/package.json +++ b/platform/npm/package.json @@ -31,7 +31,7 @@ }, "homepage": "https://github.com/gorhill/uBlock#readme", "engines": { - "node": ">=14.0.0", + "node": ">=18.0.0", "npm": ">=6.14.4" }, "devDependencies": { diff --git a/src/js/s14e-serializer.js b/src/js/s14e-serializer.js index 8ef715a23ac10..98f0d9cc20982 100644 --- a/src/js/s14e-serializer.js +++ b/src/js/s14e-serializer.js @@ -249,8 +249,29 @@ const toArrayBufferViewConstructor = { /******************************************************************************/ -const textDecoder = new TextDecoder(); -const textEncoder = new TextEncoder(); +const textCodec = { + decoder: null, + encoder: null, + decode(...args) { + if ( this.decoder === null ) { + this.decoder = new globalThis.TextDecoder(); + } + return this.decoder.decode(...args); + }, + encode(...args) { + if ( this.encoder === null ) { + this.encoder = new globalThis.TextEncoder(); + } + return this.encoder.encode(...args); + }, + encodeInto(...args) { + if ( this.encoder === null ) { + this.encoder = new globalThis.TextEncoder(); + } + return this.encoder.encodeInto(...args); + }, +}; + const isInteger = Number.isInteger; const writeRefs = new Map(); @@ -269,7 +290,7 @@ const uint8InputFromAsciiStr = s => { if ( uint8Input === null || uint8Input.length < s.length ) { uint8Input = new Uint8Array(s.length + 0x03FF & ~0x03FF); } - textEncoder.encodeInto(s, uint8Input); + textCodec.encodeInto(s, uint8Input); return uint8Input; }; @@ -407,7 +428,7 @@ const denseArrayBufferToStr = (arrbuf, details) => { } } } - return textDecoder.decode(output); + return textCodec.decode(output); }; const BASE88_POW1 = NUMSAFECHARS; @@ -489,7 +510,7 @@ const sparseArrayBufferToStr = (arrbuf, details) => { uint8out[j++] = SEPARATORCHARCODE; } } - return textDecoder.decode(uint8out); + return textCodec.decode(uint8out); }; const sparseArrayBufferFromStr = (sparseStr, arrbuf) => { @@ -1060,7 +1081,7 @@ export const serialize = (data, options = {}) => { writeBuffer.length = 0; if ( shouldCompress(s, options) === false ) { return s; } const lz4Util = new LZ4BlockJS(); - const uint8ArrayBefore = textEncoder.encode(s); + const uint8ArrayBefore = textCodec.encode(s); const uint8ArrayAfter = lz4Util.encode(uint8ArrayBefore, 0); const lz4 = { size: uint8ArrayBefore.length, @@ -1087,7 +1108,7 @@ export const deserialize = s => { readStr = ''; const lz4Util = new LZ4BlockJS(); const uint8ArrayAfter = lz4Util.decode(lz4.data, 0, lz4.size); - s = textDecoder.decode(new Uint8Array(uint8ArrayAfter)); + s = textCodec.decode(new Uint8Array(uint8ArrayAfter)); } if ( s.startsWith(MAGICPREFIX) === false ) { return; } refCounter = 1; diff --git a/tools/make-nodejs.sh b/tools/make-nodejs.sh index 1e38ba1434043..b17a0a4695db0 100755 --- a/tools/make-nodejs.sh +++ b/tools/make-nodejs.sh @@ -14,6 +14,7 @@ cp src/js/filtering-context.js $DES/js cp src/js/hnswitches.js $DES/js cp src/js/hntrie.js $DES/js cp src/js/redirect-resources.js $DES/js +cp src/js/s14e-serializer.js $DES/js cp src/js/static-dnr-filtering.js $DES/js cp src/js/static-filtering-parser.js $DES/js cp src/js/static-net-filtering.js $DES/js From 57004247164df301939b49c928e7a80517c658cc Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sun, 6 Oct 2024 12:34:26 -0400 Subject: [PATCH 0323/1099] Update changelog --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 916250a61119e..1bf594905b03a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ +- [Fix npm test suite](https://github.com/gorhill/uBlock/commit/818cb2d801) - [Add ability to lookup parameter name in `urlskip=`](https://github.com/gorhill/uBlock/commit/64b2086ba4) - [Mind that BroadcastChannel contructor can throw in Firefox](https://github.com/gorhill/uBlock/commit/6d2b3375f8) -- [Fix npm test suite](https://github.com/gorhill/uBlock/commit/41693407b2) - [Add `trusted-override-element-method` scriptlet](https://github.com/gorhill/uBlock/commit/95b0ce5e3a) - [Add `trusted-prevent-dom-bypass` scriptlet](https://github.com/gorhill/uBlock/commit/1abc864742) - [Improve `prevent-xhr` scriptlet; add `trusted-prevent-xhr` scriptlet](https://github.com/gorhill/uBlock/commit/fe49ced2ac) From e0eb59c5d45ba294751f460c99083cfab0348526 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sun, 6 Oct 2024 12:34:58 -0400 Subject: [PATCH 0324/1099] New revision for dev build --- dist/version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dist/version b/dist/version index 7b36c924aba8c..894536ebf496a 100644 --- a/dist/version +++ b/dist/version @@ -1 +1 @@ -1.60.1.6 \ No newline at end of file +1.60.1.7 \ No newline at end of file From a12bf9405e303507403e68ac72f5b89945a28b83 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sun, 6 Oct 2024 12:40:51 -0400 Subject: [PATCH 0325/1099] Make Firefox dev build auto-update --- dist/firefox/updates.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dist/firefox/updates.json b/dist/firefox/updates.json index c303b212de4ee..785e9000e8a80 100644 --- a/dist/firefox/updates.json +++ b/dist/firefox/updates.json @@ -3,9 +3,9 @@ "uBlock0@raymondhill.net": { "updates": [ { - "version": "1.60.1.6", + "version": "1.60.1.7", "browser_specific_settings": { "gecko": { "strict_min_version": "78.0" } }, - "update_link": "https://github.com/gorhill/uBlock/releases/download/1.60.1b6/uBlock0_1.60.1b6.firefox.signed.xpi" + "update_link": "https://github.com/gorhill/uBlock/releases/download/1.60.1b7/uBlock0_1.60.1b7.firefox.signed.xpi" } ] } From 447476ab9bcf112743277d89e355809967393624 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sun, 6 Oct 2024 14:27:46 -0400 Subject: [PATCH 0326/1099] New npm package version --- platform/npm/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform/npm/package.json b/platform/npm/package.json index a65faffd37569..74625c48e8f26 100644 --- a/platform/npm/package.json +++ b/platform/npm/package.json @@ -1,6 +1,6 @@ { "name": "@gorhill/ubo-core", - "version": "0.1.26", + "version": "0.1.27", "description": "To create a working instance of uBlock Origin's static network filtering engine", "type": "module", "main": "index.js", From c7466336934d7d22378a10c7c9bb5d77a8f7d11b Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sun, 6 Oct 2024 14:27:55 -0400 Subject: [PATCH 0327/1099] Validate result type of XPath expressions Related issue: https://github.com/uBlockOrigin/uBlock-issues/issues/3403 To ensure XPath expressions not meant to return a nodeset are discarded at compile time. --- src/js/static-filtering-parser.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/js/static-filtering-parser.js b/src/js/static-filtering-parser.js index 1249b07b8e35c..8f95b88d662a5 100644 --- a/src/js/static-filtering-parser.js +++ b/src/js/static-filtering-parser.js @@ -4116,9 +4116,11 @@ class ExtSelectorCompiler { compileXpathExpression(s) { const r = this.unquoteString(s); if ( r.i !== s.length ) { return; } - if ( globalThis.document instanceof Object === false ) { return r.s; } + const doc = globalThis.document; + if ( doc instanceof Object === false ) { return r.s; } try { - globalThis.document.createExpression(r.s, null); + const expr = doc.createExpression(r.s, null); + expr.evaluate(doc, XPathResult.ANY_UNORDERED_NODE_TYPE); } catch (e) { return; } From cc60dfa4c5d7c191a4439be8ddfa7611456b39e7 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sun, 6 Oct 2024 14:56:47 -0400 Subject: [PATCH 0328/1099] Update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1bf594905b03a..e9acf0bb0c22a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,4 @@ +- [Validate result type of XPath expressions](https://github.com/gorhill/uBlock/commit/c746633693) - [Fix npm test suite](https://github.com/gorhill/uBlock/commit/818cb2d801) - [Add ability to lookup parameter name in `urlskip=`](https://github.com/gorhill/uBlock/commit/64b2086ba4) - [Mind that BroadcastChannel contructor can throw in Firefox](https://github.com/gorhill/uBlock/commit/6d2b3375f8) From 1f32bbc322f9103ba76d10e3bbebc69642bf9dff Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sun, 6 Oct 2024 14:57:07 -0400 Subject: [PATCH 0329/1099] New revision for dev build --- dist/version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dist/version b/dist/version index 894536ebf496a..a4692c736eff0 100644 --- a/dist/version +++ b/dist/version @@ -1 +1 @@ -1.60.1.7 \ No newline at end of file +1.60.1.8 \ No newline at end of file From ccbf957f5599578117722352ae0ae454f44341e5 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sun, 6 Oct 2024 15:06:21 -0400 Subject: [PATCH 0330/1099] Make Firefox dev build auto-update --- dist/firefox/updates.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dist/firefox/updates.json b/dist/firefox/updates.json index 785e9000e8a80..650baa7b55ca7 100644 --- a/dist/firefox/updates.json +++ b/dist/firefox/updates.json @@ -3,9 +3,9 @@ "uBlock0@raymondhill.net": { "updates": [ { - "version": "1.60.1.7", + "version": "1.60.1.8", "browser_specific_settings": { "gecko": { "strict_min_version": "78.0" } }, - "update_link": "https://github.com/gorhill/uBlock/releases/download/1.60.1b7/uBlock0_1.60.1b7.firefox.signed.xpi" + "update_link": "https://github.com/gorhill/uBlock/releases/download/1.60.1b8/uBlock0_1.60.1b8.firefox.signed.xpi" } ] } From 9233e6b7c66fa0d518c3a0320665493beb02ad6e Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sun, 6 Oct 2024 16:29:42 -0400 Subject: [PATCH 0331/1099] Fix npm documentation Related issue: https://github.com/uBlockOrigin/uBlock-issues/issues/3402 --- platform/nodejs/README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/platform/nodejs/README.md b/platform/nodejs/README.md index 0b3e3d8c074dc..bd5775abb6de4 100644 --- a/platform/nodejs/README.md +++ b/platform/nodejs/README.md @@ -44,7 +44,7 @@ const { StaticNetFilteringEngine } = await import('@gorhill/ubo-core'); Create an instance of SNFE: ```js -const snfe = StaticNetFilteringEngine.create(); +const snfe = await StaticNetFilteringEngine.create(); ``` Feed the SNFE with filter lists -- `useLists()` accepts an array of @@ -54,8 +54,8 @@ through the `raw` property, and optionally the name of the list through the ```js await snfe.useLists([ - fetch('easylist').then(raw => ({ name: 'easylist', raw })), - fetch('easyprivacy').then(raw => ({ name: 'easyprivacy', raw })), + fetch('easylist').then(r => r.text()).then(raw => ({ name: 'easylist', raw })), + fetch('easyprivacy').then(r => r.text()).then(raw => ({ name: 'easyprivacy', raw })), ]); ``` From 86aeae75dfd125f6e4cb7a22521b4badaf54837d Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sun, 6 Oct 2024 17:03:18 -0400 Subject: [PATCH 0332/1099] Further fix npm documentation, minor code review --- platform/nodejs/README.md | 18 ++++++++++++++---- platform/nodejs/index.js | 7 +++---- 2 files changed, 17 insertions(+), 8 deletions(-) diff --git a/platform/nodejs/README.md b/platform/nodejs/README.md index bd5775abb6de4..db6c5d775475d 100644 --- a/platform/nodejs/README.md +++ b/platform/nodejs/README.md @@ -90,10 +90,20 @@ if ( snfe.matchRequest({ } ``` -It is possible to pre-parse filter lists and save the intermediate results for -later use -- useful to speed up the loading of filter lists. This will be -documented eventually, but if you feel adventurous, you can look at the code -and use this capability now if you figure out the details. +Once all the filter lists are loaded into the static network filtering engine, +you can serialize the content of the engine into a JS string: + +```js +const serializedData = await snfe.serialize(); +``` + +You can save and later use that JS string to fast-load the content of the +static network filtering engine without having to parse and compile the lists: + +```js +const snfe = await StaticNetFilteringEngine.create(); +await snfe.deserialize(serializedData); +``` --- diff --git a/platform/nodejs/index.js b/platform/nodejs/index.js index fbc17da859000..d50fb4e1969d3 100644 --- a/platform/nodejs/index.js +++ b/platform/nodejs/index.js @@ -182,8 +182,7 @@ async function useLists(lists, options = {}) { // Populate filtering engine with resolved filter lists const promises = []; for ( const list of lists ) { - const promise = list instanceof Promise ? list : Promise.resolve(list); - promises.push(promise.then(list => consumeList(list))); + promises.push(Promise.resolve(list).then(list => consumeList(list)); } useLists.promise = Promise.all(promises); @@ -249,12 +248,12 @@ class StaticNetFilteringEngine { return compileList(...args); } - serialize() { + async serialize() { const data = snfe.serialize(); return s14e.serialize(data, { compress: true }); } - deserialize(serialized) { + async deserialize(serialized) { const data = s14e.deserialize(serialized); return snfe.unserialize(data); } From acf7f39a60e7881b55b82440ecb3208573fcb3cc Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sun, 6 Oct 2024 17:04:08 -0400 Subject: [PATCH 0333/1099] New revision for npm package --- platform/npm/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform/npm/package.json b/platform/npm/package.json index 74625c48e8f26..308404d2346a2 100644 --- a/platform/npm/package.json +++ b/platform/npm/package.json @@ -1,6 +1,6 @@ { "name": "@gorhill/ubo-core", - "version": "0.1.27", + "version": "0.1.28", "description": "To create a working instance of uBlock Origin's static network filtering engine", "type": "module", "main": "index.js", From 013e6db6ef6ea5f412282c7af697897aeb98b206 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sun, 6 Oct 2024 17:05:44 -0400 Subject: [PATCH 0334/1099] Fix typo --- platform/nodejs/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform/nodejs/index.js b/platform/nodejs/index.js index d50fb4e1969d3..5fad3bec2b818 100644 --- a/platform/nodejs/index.js +++ b/platform/nodejs/index.js @@ -182,7 +182,7 @@ async function useLists(lists, options = {}) { // Populate filtering engine with resolved filter lists const promises = []; for ( const list of lists ) { - promises.push(Promise.resolve(list).then(list => consumeList(list)); + promises.push(Promise.resolve(list).then(list => consumeList(list))); } useLists.promise = Promise.all(promises); From 26c0aa357e887ed524e6092e3d3c65f1849d5d24 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Mon, 7 Oct 2024 07:38:41 -0400 Subject: [PATCH 0335/1099] New npm version --- platform/npm/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform/npm/package.json b/platform/npm/package.json index 308404d2346a2..180b297ed1c6d 100644 --- a/platform/npm/package.json +++ b/platform/npm/package.json @@ -1,6 +1,6 @@ { "name": "@gorhill/ubo-core", - "version": "0.1.28", + "version": "0.1.29", "description": "To create a working instance of uBlock Origin's static network filtering engine", "type": "module", "main": "index.js", From 34508a1c2d690f3552b4f8feef739483a7f3ccee Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Mon, 7 Oct 2024 08:47:45 -0400 Subject: [PATCH 0336/1099] Add demo.js to npm build To help people to get quickly started with using the package. --- platform/nodejs/index.js | 1 + platform/npm/demo.js | 120 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 121 insertions(+) create mode 100644 platform/npm/demo.js diff --git a/platform/nodejs/index.js b/platform/nodejs/index.js index 5fad3bec2b818..56aa74f42c67c 100644 --- a/platform/nodejs/index.js +++ b/platform/nodejs/index.js @@ -119,6 +119,7 @@ function pslInit(raw) { /******************************************************************************/ function compileList({ name, raw }, compiler, writer, options = {}) { + if ( typeof raw !== 'string' || raw === '' ) { return; } const lineIter = new LineIterator(raw); const events = Array.isArray(options.events) ? options.events : undefined; diff --git a/platform/npm/demo.js b/platform/npm/demo.js new file mode 100644 index 0000000000000..ebe314b2fdf6d --- /dev/null +++ b/platform/npm/demo.js @@ -0,0 +1,120 @@ +/******************************************************************************* + * + * A simple demo to quickly get started. + * + * Command line: + * + * mkdir myproject + * cd myproject + * npm install @gorhill/ubo-core + * cp node_modules/@gorhill/ubo-core/demo.js . + * + * There will be a `demo.js` file in your `myproject` folder, which you can + * modify and execute: + * + * node demo.js + * + * Since the demo here uses ES module syntax, you may want to add the following + * to the generated package.json file to avoid the warning: + * + * "type": "module", + * + * The demo will fetch filter lists from EasyList server, then serialize the + * content of the static network filtering engine into a local `./cache/` + * folder. + * + * The serialized data will be reused if available in order to avoid fetching + * from remote server each time it is executed. + * + * This demo is kept as simple as possible, so there is not a lot of error + * handling. + * + * */ + +import fs from 'fs/promises'; +import { StaticNetFilteringEngine } from '@gorhill/ubo-core'; + +/******************************************************************************/ + +async function main() { + const pathToSelfie = 'cache/selfie.txt'; + + const snfe = await StaticNetFilteringEngine.create(); + + // Up to date serialization data (aka selfie) available? + let selfie; + const ageInDays = await fs.stat(pathToSelfie).then(stat => { + const fileDate = new Date(stat.mtime); + return (Date.now() - fileDate.getTime()) / (7 * 24 * 60 * 60); + }).catch(( ) => Number.MAX_SAFE_INTEGER); + + // Use a selfie if available and not older than 7 days + if ( ageInDays <= 7 ) { + selfie = await fs.readFile(pathToSelfie, { encoding: 'utf8' }) + .then(data => typeof data === 'string' && data !== '' && data) + .catch(( ) => { }); + if ( typeof selfie === 'string' ) { + await snfe.deserialize(selfie); + } + } + + // Fetch filter lists if no up to date selfie available + if ( !selfie ) { + console.log(`Fetching lists...`); + await snfe.useLists([ + fetch('https://easylist.to/easylist/easylist.txt') + .then(r => { + return r.text(); + }).then(raw => { + console.log(`easylist fetched`); + return { name: 'easylist', raw }; + }).catch(reason => { + console.error(reason); + }), + fetch('https://easylist.to/easylist/easyprivacy.txt') + .then(r => { + return r.text(); + }).then(raw => { + console.log(`easyprivacy fetched`); + return { name: 'easyprivacy', raw }; + }).catch(reason => { + console.error(reason); + }), + ]); + const selfie = await snfe.serialize(); + await fs.mkdir('cache', { recursive: true }); + await fs.writeFile(pathToSelfie, selfie); + } + + // List of tests to perform + const tests = [ + { + originURL: 'https://www.bloomberg.com/', + url: 'https://www.google-analytics.com/gs.js', + type: 'script', + }, { + originURL: 'https://www.bloomberg.com/', + url: 'https://securepubads.g.doubleclick.net/tag/js/gpt.js', + type: 'script', + }, { + originURL: 'https://www.bloomberg.com/', + url: 'https://bloomberg.com/main.css', + type: 'stylesheet', + } + ]; + + // Test each entry for a match against the content of the engine + for ( const test of tests ) { + console.log('\nRequest details:', test); + const r = snfe.matchRequest(test); + if ( r === 1 ) { // Blocked + console.log('Blocked:', snfe.toLogData()); + } else if ( r === 2 ) { // Unblocked + console.log('Unblocked:', snfe.toLogData()); + } else { // Not blocked + console.log('Not blocked'); + } + } +} + +main(); From 9a286495a5ebf9116ff5bbb8ef6d42a5a6384dbf Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Mon, 7 Oct 2024 08:49:09 -0400 Subject: [PATCH 0337/1099] New npm version --- platform/npm/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform/npm/package.json b/platform/npm/package.json index 180b297ed1c6d..6430bec8e52c9 100644 --- a/platform/npm/package.json +++ b/platform/npm/package.json @@ -1,6 +1,6 @@ { "name": "@gorhill/ubo-core", - "version": "0.1.29", + "version": "0.1.30", "description": "To create a working instance of uBlock Origin's static network filtering engine", "type": "module", "main": "index.js", From daa62c30812a258c18524ba5d3821958973a338a Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Mon, 7 Oct 2024 08:56:34 -0400 Subject: [PATCH 0338/1099] Update README for npm package --- platform/{nodejs => npm}/README.md | 2 ++ tools/make-nodejs.sh | 1 - tools/make-npm.sh | 1 + 3 files changed, 3 insertions(+), 1 deletion(-) rename platform/{nodejs => npm}/README.md (98%) diff --git a/platform/nodejs/README.md b/platform/npm/README.md similarity index 98% rename from platform/nodejs/README.md rename to platform/npm/README.md index db6c5d775475d..a1ce7f42e7004 100644 --- a/platform/nodejs/README.md +++ b/platform/npm/README.md @@ -27,6 +27,8 @@ and also lists of domain names or hosts file format (i.e. block lists from [The ## Usage +See `./demo.js` in package for instructions to quickly get started. + At the moment, there can be only one instance of the static network filtering engine ("SNFE"), which proxy API must be imported as follow: diff --git a/tools/make-nodejs.sh b/tools/make-nodejs.sh index b17a0a4695db0..270456814b9b3 100755 --- a/tools/make-nodejs.sh +++ b/tools/make-nodejs.sh @@ -41,5 +41,4 @@ node -pe "JSON.stringify(Array.from(fs.readFileSync('src/lib/publicsuffixlist/wa > $DES/lib/publicsuffixlist/wasm/publicsuffixlist.wasm.json cp platform/nodejs/*.js $DES/ -cp platform/nodejs/README.md $DES/ cp LICENSE.txt $DES/ diff --git a/tools/make-npm.sh b/tools/make-npm.sh index 6bffadc735646..8a17b0e3c1e51 100755 --- a/tools/make-npm.sh +++ b/tools/make-npm.sh @@ -20,6 +20,7 @@ cp platform/npm/*.json $DES/ cp platform/npm/.*.json $DES/ cp platform/npm/*.js $DES/ cp -R platform/npm/tests $DES/ +cp platform/npm/README.md $DES/ cd $DES cd tests/data From 4aae1bdf474ec1d2df88bd79235a9b91a68c77f1 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Mon, 7 Oct 2024 11:56:22 -0400 Subject: [PATCH 0339/1099] Minor code review --- platform/firefox/vapi-background-ext.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform/firefox/vapi-background-ext.js b/platform/firefox/vapi-background-ext.js index 4cf7ec97824aa..65420bc3f39cf 100644 --- a/platform/firefox/vapi-background-ext.js +++ b/platform/firefox/vapi-background-ext.js @@ -37,7 +37,7 @@ const isResolvedObject = o => o instanceof Object && o instanceof Promise === false; const reIPv4 = /^\d+\.\d+\.\d+\.\d+$/ const skipDNS = proxyInfo => - proxyInfo?.proxyDNS || proxyInfo?.type?.startsWith('http'); + proxyInfo && (proxyInfo.proxyDNS || proxyInfo.type?.charCodeAt(0) === 0x68 /* h */); /******************************************************************************/ From f825a65f8e8c5e1c54c5b71bd70f8883a0d4cd75 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Mon, 7 Oct 2024 12:00:21 -0400 Subject: [PATCH 0340/1099] New revision for dev build --- dist/version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dist/version b/dist/version index a4692c736eff0..2cf4877c51e61 100644 --- a/dist/version +++ b/dist/version @@ -1 +1 @@ -1.60.1.8 \ No newline at end of file +1.60.1.9 \ No newline at end of file From a80c749096c7e9c9eb4fa0acdf6091c93078642a Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Mon, 7 Oct 2024 12:05:41 -0400 Subject: [PATCH 0341/1099] Make Firefox dev build auto-update --- dist/firefox/updates.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dist/firefox/updates.json b/dist/firefox/updates.json index 650baa7b55ca7..4cbf182466d39 100644 --- a/dist/firefox/updates.json +++ b/dist/firefox/updates.json @@ -3,9 +3,9 @@ "uBlock0@raymondhill.net": { "updates": [ { - "version": "1.60.1.8", + "version": "1.60.1.9", "browser_specific_settings": { "gecko": { "strict_min_version": "78.0" } }, - "update_link": "https://github.com/gorhill/uBlock/releases/download/1.60.1b8/uBlock0_1.60.1b8.firefox.signed.xpi" + "update_link": "https://github.com/gorhill/uBlock/releases/download/1.60.1b9/uBlock0_1.60.1b9.firefox.signed.xpi" } ] } From ec3852b74598fe9679dc6ea2d366ac355019237b Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Mon, 7 Oct 2024 15:06:12 -0400 Subject: [PATCH 0342/1099] Re-word: it's timing per request, not per call to SNFE.match() A single request can require multiple calls to the static network filtering engine (SNFE). The reported timing is the result of going through *all* the required calls to SNFE. In effect, a single call to SNFE.match() is a fraction of the reported timing. --- src/js/benchmarks.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/js/benchmarks.js b/src/js/benchmarks.js index 83cd97c955a7f..c4102469863fb 100644 --- a/src/js/benchmarks.js +++ b/src/js/benchmarks.js @@ -217,7 +217,7 @@ export async function benchmarkStaticNetFiltering(options = {}) { const output = [ 'Benchmarked static network filtering engine:', - `\tEvaluated ${matchCount} match calls in ${dur.toFixed(0)} ms`, + `\tEvaluated ${matchCount} requests in ${dur.toFixed(0)} ms`, `\tAverage: ${(dur / matchCount).toFixed(3)} ms per request`, `\tNot blocked: ${matchCount - blockCount - allowCount}`, `\tBlocked: ${blockCount}`, From 58bfe4c846e1065a56f16b15f7a99f5c3b116167 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Tue, 8 Oct 2024 19:19:08 -0400 Subject: [PATCH 0343/1099] Add support to parse Adguard's `[$domain=/.../]` regex-based modifier The modifier will be converted to one that is compatible with uBO, i.e.: [$domain=/^trgoals\d+\.xyz$/]##+js(set-constant, isShow, true) Will parsed as equivalent of: /^trgoals\d+\.xyz$/##+js(set-constant, isShow, true) Related issue: https://github.com/AdguardTeam/FiltersCompiler/issues/204 Reference: https://adguard.com/kb/general/ad-filtering/create-own-filters/#non-basic-domain-modifier --- src/js/static-filtering-parser.js | 107 ++++++++++++++++++------------ 1 file changed, 66 insertions(+), 41 deletions(-) diff --git a/src/js/static-filtering-parser.js b/src/js/static-filtering-parser.js index 8f95b88d662a5..1cc4b5a8db9dd 100644 --- a/src/js/static-filtering-parser.js +++ b/src/js/static-filtering-parser.js @@ -1988,6 +1988,7 @@ export class AstFilterParser { if ( parentEnd === parentBeg ) { return 0; } const s = this.getNodeString(parent); const optionsEnd = s.length; + const parseDetails = { node: 0, len: 0 }; const head = this.allocHeadNode(); let prev = head, next = 0; let optionBeg = 0, optionEnd = 0; @@ -1997,11 +1998,11 @@ export class AstFilterParser { parentBeg + optionBeg, parentBeg + optionsEnd // open ended ); - const { node: down, len: optionLen } = this.parseNetOption(next); + this.parseNetOption(next, parseDetails); // set next's end to down's end - optionEnd += optionLen; + optionEnd += parseDetails.len; this.nodes[next+NODE_END_INDEX] = parentBeg + optionEnd; - this.linkDown(next, down); + this.linkDown(next, parseDetails.node); prev = this.linkRight(prev, next); if ( optionEnd === optionsEnd ) { break; } optionBeg = optionEnd + 1; @@ -2010,7 +2011,7 @@ export class AstFilterParser { parentBeg + optionEnd, parentBeg + optionBeg ); - if ( optionLen === 0 || optionBeg === optionsEnd ) { + if ( parseDetails.len === 0 || optionBeg === optionsEnd ) { this.addNodeFlags(next, NODE_FLAG_ERROR); this.addFlags(AST_FLAG_HAS_ERROR); } @@ -2023,7 +2024,7 @@ export class AstFilterParser { return this.throwHeadNode(head); } - parseNetOption(parent) { + parseNetOption(parent, parseDetails) { const parentBeg = this.nodes[parent+NODE_BEG_INDEX]; const s = this.getNodeString(parent); const match = this.reNetOption.exec(s) || []; @@ -2031,7 +2032,9 @@ export class AstFilterParser { this.addNodeFlags(parent, NODE_FLAG_ERROR); this.addFlags(AST_FLAG_HAS_ERROR); this.astError = AST_ERROR_OPTION_UNKNOWN; - return { node: 0, len: s.length }; + parseDetails.node = 0; + parseDetails.len = s.length; + return; } const head = this.allocHeadNode(); let prev = head, next = 0; @@ -2073,7 +2076,9 @@ export class AstFilterParser { } prev = this.linkRight(prev, next); if ( assigned === false ) { - return { node: this.throwHeadNode(head), len: matchEnd }; + parseDetails.node = this.throwHeadNode(head); + parseDetails.len = matchEnd; + return; } next = this.allocTypedNode( NODE_TYPE_NET_OPTION_ASSIGN, @@ -2129,7 +2134,8 @@ export class AstFilterParser { ); this.linkRight(prev, next); } - return { node: this.throwHeadNode(head), len: details.quoteEnd }; + parseDetails.node = this.throwHeadNode(head); + parseDetails.len = details.quoteEnd; } endOfNetOption(s, beg) { @@ -2156,32 +2162,26 @@ export class AstFilterParser { ); if ( parentEnd === parentBeg ) { return containerNode; } const separatorCode = separator.charCodeAt(0); + const parseDetails = { separator, mode, node: 0, len: 0 }; const listNode = this.allocHeadNode(); let prev = listNode; let domainNode = 0; let separatorNode = 0; const s = this.getNodeString(parent); const listEnd = s.length; - let beg = 0, end = 0, c = 0; + let beg = 0, end = 0; while ( beg < listEnd ) { - c = s.charCodeAt(beg); - if ( c === 0x7E /* ~ */ ) { - c = s.charCodeAt(beg+1) || 0; - } - if ( c !== 0x2F /* / */ ) { - end = s.indexOf(separator, beg); - } else { - end = s.indexOf('/', beg+1); - end = s.indexOf(separator, end !== -1 ? end+1 : beg); - } - if ( end === -1 ) { end = listEnd; } + const next = this.allocTypedNode( + NODE_TYPE_OPTION_VALUE_DOMAIN_RAW, + parentBeg + beg, + parentBeg + listEnd // open ended + ); + this.parseDomain(next, parseDetails); + end = beg + parseDetails.len; + this.nodes[next+NODE_END_INDEX] = parentBeg + end; if ( end !== beg ) { - domainNode = this.allocTypedNode( - NODE_TYPE_OPTION_VALUE_DOMAIN_RAW, - parentBeg + beg, - parentBeg + end - ); - this.linkDown(domainNode, this.parseDomain(domainNode, mode)); + domainNode = next; + this.linkDown(domainNode, parseDetails.node); prev = this.linkRight(prev, domainNode); } else { domainNode = 0; @@ -2217,23 +2217,38 @@ export class AstFilterParser { return containerNode; } - parseDomain(parent, mode = 0b0000) { + parseDomain(parent, parseDetails) { const parentBeg = this.nodes[parent+NODE_BEG_INDEX]; const parentEnd = this.nodes[parent+NODE_END_INDEX]; + const not = this.charCodeAt(parentBeg) === 0x7E /* ~ */; let head = 0, next = 0; let beg = parentBeg; - const c = this.charCodeAt(beg); - if ( c === 0x7E /* ~ */ ) { + if ( not ) { this.addNodeFlags(parent, NODE_FLAG_IS_NEGATED); head = this.allocTypedNode(NODE_TYPE_OPTION_VALUE_NOT, beg, beg + 1); - if ( (mode & 0b1000) === 0 ) { + if ( (parseDetails.mode & 0b1000) === 0 ) { this.addNodeFlags(parent, NODE_FLAG_ERROR); } beg += 1; } - if ( beg !== parentEnd ) { - next = this.allocTypedNode(NODE_TYPE_OPTION_VALUE_DOMAIN, beg, parentEnd); - const hn = this.normalizeDomainValue(this.getNodeString(next), mode); + const c0 = this.charCodeAt(beg); + let end = beg; + let type = 0; + if ( c0 === 0x2F /* / */ ) { + end = this.indexOf('/', beg + 1, parentEnd); + if ( end !== -1 ) { end += 1; } + type = 1; + } else if ( c0 === 0x5B /* [ */ && this.startsWith('[$domain=/', beg) ) { + end = this.indexOf('/]', beg + 10, parentEnd); + if ( end !== -1 ) { end += 2; } + type = 2; + } else { + end = this.indexOf(parseDetails.separator, end, parentEnd); + } + if ( end === -1 ) { end = parentEnd; } + if ( beg !== end ) { + next = this.allocTypedNode(NODE_TYPE_OPTION_VALUE_DOMAIN, beg, end); + const hn = this.normalizeDomainValue(next, type, parseDetails.mode); if ( hn !== undefined ) { if ( hn !== '' ) { this.setNodeTransform(next, hn); @@ -2252,7 +2267,8 @@ export class AstFilterParser { this.addNodeFlags(parent, NODE_FLAG_ERROR); this.addFlags(AST_FLAG_HAS_ERROR); } - return head; + parseDetails.node = head; + parseDetails.len = end - parentBeg; } // mode bits: @@ -2261,16 +2277,16 @@ export class AstFilterParser { // 0b00100: can use single wildcard // 0b01000: can be negated // 0b10000: can be a regex - normalizeDomainValue(s, modeBits) { - if ( (modeBits & 0b10000) === 0 || - s.length <= 2 || - s.charCodeAt(0) !== 0x2F /* / */ || - exCharCodeAt(s, -1) !== 0x2F /* / */ - ) { + normalizeDomainValue(node, type, modeBits) { + const s = this.getNodeString(node); + if ( type === 0 ) { return this.normalizeHostnameValue(s, modeBits); } - const source = this.normalizeRegexPattern(s); + if ( (modeBits & 0b10000) === 0 ) { return ''; } + const regex = type === 1 ? s : `/${s.slice(10, -2)}/`; + const source = this.normalizeRegexPattern(regex); if ( source === '' ) { return ''; } + if ( type === 1 && source === regex ) { return; } return `/${source}/`; } @@ -2857,6 +2873,15 @@ export class AstFilterParser { return pos < this.rawEnd ? this.raw.charCodeAt(pos) : -1; } + indexOf(needle, beg, end = 0) { + const haystack = end === 0 ? this.raw : this.raw.slice(0, end); + return haystack.indexOf(needle, beg); + } + + startsWith(s, pos) { + return pos < this.rawEnd && this.raw.startsWith(s, pos); + } + isTokenCharCode(c) { return c === 0x25 || c >= 0x30 && c <= 0x39 || From 85877b12ed75c30cd7b07cefee787fdf02f454f3 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Tue, 8 Oct 2024 19:47:35 -0400 Subject: [PATCH 0344/1099] Improve `prevent-window-open` scriptlet Support triggering a `debugger` statement when `window.open()` is called. Related feedback: https://github.com/uBlockOrigin/uAssets/issues/25510#issuecomment-2400067735 --- assets/resources/scriptlets.js | 42 +++++++++++++++++++++++++++++++--- 1 file changed, 39 insertions(+), 3 deletions(-) diff --git a/assets/resources/scriptlets.js b/assets/resources/scriptlets.js index e35757cfa4d0c..cd4c7ea0cb227 100644 --- a/assets/resources/scriptlets.js +++ b/assets/resources/scriptlets.js @@ -2952,13 +2952,45 @@ function preventXhr(...args) { return preventXhrFn(false, ...args); } -/******************************************************************************/ +/** + * @scriptlet prevent-window-open + * + * @description + * Prevent a webpage from opening new tabs through `window.open()`. + * + * @param pattern + * A plain string or regex to match against the `url` argument for the + * prevention to be triggered. If not provided, all calls to `window.open()` + * are prevented. + * If set to the special value `debug` *and* the logger is opened, the scriptlet + * will trigger a `debugger` statement and the prevention will not occur. + * + * @param [delay] + * If provided, a decoy will be created or opened, and this parameter states + * the number of seconds to wait for before the decoy is terminated, i.e. + * either removed from the DOM or closed. + * + * @param [decoy] + * A string representing the type of decoy to use: + * - `blank`: replace the `url` parameter with `about:blank` + * - `object`: create and append an `object` element to the DOM, and return + * its `contentWindow` property. + * - `frame`: create and append an `iframe` element to the DOM, and return + * its `contentWindow` property. + * + * @example + * ##+js(prevent-window-open, ads.example.com/) + * + * @example + * ##+js(prevent-window-open, ads.example.com/, 1, iframe) + * + * */ builtinScriptlets.push({ - name: 'no-window-open-if.js', + name: 'prevent-window-open.js', aliases: [ 'nowoif.js', - 'prevent-window-open.js', + 'no-window-open-if.js', 'window.open-defuser.js', ], fn: noWindowOpenIf, @@ -2994,6 +3026,10 @@ function noWindowOpenIf( }; const noopFunc = function(){}; proxyApplyFn('open', function open(context) { + if ( pattern === 'debug' && safe.logLevel !== 0 ) { + debugger; // eslint-disable-line no-debugger + return context.reflect(); + } const { callArgs } = context; const haystack = callArgs.join(' '); if ( rePattern.test(haystack) !== targetMatchResult ) { From 987e198016a1bc61e7ec8fe0255554f51f3aa2c3 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Tue, 8 Oct 2024 19:50:14 -0400 Subject: [PATCH 0345/1099] Update changelog --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index e9acf0bb0c22a..2a6341a977cf7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,5 @@ +- [Improve `prevent-window-open` scriptlet](https://github.com/gorhill/uBlock/commit/85877b12ed) +- [Add support to parse Adguard's `[$domain=/.../]` regex-based modifier](https://github.com/gorhill/uBlock/commit/58bfe4c846) - [Validate result type of XPath expressions](https://github.com/gorhill/uBlock/commit/c746633693) - [Fix npm test suite](https://github.com/gorhill/uBlock/commit/818cb2d801) - [Add ability to lookup parameter name in `urlskip=`](https://github.com/gorhill/uBlock/commit/64b2086ba4) From a72def1cbf4b7ad4ac063d01e91943cd00b10f70 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Tue, 8 Oct 2024 19:50:34 -0400 Subject: [PATCH 0346/1099] New revision for dev build --- dist/version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dist/version b/dist/version index 2cf4877c51e61..8e10b8e25a3ea 100644 --- a/dist/version +++ b/dist/version @@ -1 +1 @@ -1.60.1.9 \ No newline at end of file +1.60.1.10 \ No newline at end of file From 913f20f0a8a8ad7f706e347f4fd2f0fe81d63edb Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Tue, 8 Oct 2024 19:56:23 -0400 Subject: [PATCH 0347/1099] Make Firefox dev build auto-update --- dist/firefox/updates.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dist/firefox/updates.json b/dist/firefox/updates.json index 4cbf182466d39..2958ae715b05b 100644 --- a/dist/firefox/updates.json +++ b/dist/firefox/updates.json @@ -3,9 +3,9 @@ "uBlock0@raymondhill.net": { "updates": [ { - "version": "1.60.1.9", + "version": "1.60.1.10", "browser_specific_settings": { "gecko": { "strict_min_version": "78.0" } }, - "update_link": "https://github.com/gorhill/uBlock/releases/download/1.60.1b9/uBlock0_1.60.1b9.firefox.signed.xpi" + "update_link": "https://github.com/gorhill/uBlock/releases/download/1.60.1b10/uBlock0_1.60.1b10.firefox.signed.xpi" } ] } From c86ed5287b6277ee0768125d145b868519039537 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Thu, 10 Oct 2024 11:11:07 -0400 Subject: [PATCH 0348/1099] Add regex extraction transformation step to `urlskip=` option Related feedback: https://github.com/uBlockOrigin/uBlock-issues/issues/3206#issuecomment-2403795984 The first capture group of the regex will be used as the result of the transformation. Example: ||podtrac.com/pts/redirect.mp3/$urlskip=/podtrac\.com\/pts\/redirect\.mp3\/(.*?\.mp3\b)/ +https If the regex is invalid, or if it fails to extract a first capture group, no redirection will occur. --- src/js/static-net-filtering.js | 101 +++++++++++++++++++++++++-------- 1 file changed, 76 insertions(+), 25 deletions(-) diff --git a/src/js/static-net-filtering.js b/src/js/static-net-filtering.js index 1942295732215..9f50e0b16616a 100644 --- a/src/js/static-net-filtering.js +++ b/src/js/static-net-filtering.js @@ -5356,12 +5356,10 @@ StaticNetFilteringEngine.prototype.transformRequest = function(fctxt, out = []) out.push(directive); continue; } - const { refs } = directive; - if ( refs instanceof Object === false ) { continue; } - if ( refs.$cache === null ) { - refs.$cache = sfp.parseReplaceValue(refs.value); + if ( directive.cache === null ) { + directive.cache = sfp.parseReplaceValue(directive.value); } - const cache = refs.$cache; + const cache = directive.cache; if ( cache === undefined ) { continue; } const before = `${redirectURL.pathname}${redirectURL.search}${redirectURL.hash}`; if ( cache.re.test(before) !== true ) { continue; } @@ -5382,7 +5380,49 @@ StaticNetFilteringEngine.prototype.transformRequest = function(fctxt, out = []) return out; }; -/******************************************************************************/ +/** + * @trustedOption urlkip + * + * @description + * Extract a URL from another URL according to one or more transformation steps, + * thereby skipping over intermediate network request(s) to remote servers. + * Requires a trusted source. + * + * @param steps + * A serie of space-separated directives representing the transformation steps + * to perform to extract the final URL to which a network request should be + * redirected. + * + * Supported directives: + * + * `?name`: extract the value of parameter `name` as the current string. + * + * `&i`: extract the name of the parameter at position `i` as the current + * string. The position is 1-based. + * + * `/.../`: extract the first capture group of a regex as the current string. + * + * `+https`: prepend the current string with `https://`. + * + * `-base64`: decode the current string as a base64-encoded string. + * + * At any given step, the currently extracted string may not necessarily be + * a valid URL, and more transformation steps may be needed to obtain a valid + * URL once all the steps are applied. + * + * An unsupported step or a failed step will abort the transformation and no + * redirection will be performed. + * + * The final step is expected to yield a valid URL. If the result is not a + * valid URL, no redirection will be performed. + * + * @example + * ||example.com/path/to/tracker$urlskip=?url + * ||example.com/path/to/tracker$urlskip=?url ?to + * ||pixiv.net/jump.php?$urlskip=&1 + * ||podtrac.com/pts/redirect.mp3/$urlskip=/podtrac\.com\/pts\/redirect\.mp3\/(.*?\.mp3\b)/ +https + * + * */ StaticNetFilteringEngine.prototype.urlSkip = function(fctxt, out = []) { if ( fctxt.redirectURL !== undefined ) { return; } @@ -5396,7 +5436,7 @@ StaticNetFilteringEngine.prototype.urlSkip = function(fctxt, out = []) { const urlin = fctxt.url; const value = directive.value; const steps = value.includes(' ') && value.split(/ +/) || [ value ]; - const urlout = urlSkip(urlin, steps); + const urlout = urlSkip(directive, urlin, steps); if ( urlout === undefined ) { continue; } if ( urlout === urlin ) { continue; } fctxt.redirectURL = urlout; @@ -5407,41 +5447,52 @@ StaticNetFilteringEngine.prototype.urlSkip = function(fctxt, out = []) { return out; }; -function urlSkip(urlin, steps) { +function urlSkip(directive, urlin, steps) { try { - let urlout; + let urlout = urlin; for ( const step of steps ) { + const urlin = urlout; const c0 = step.charCodeAt(0); - // Extract from URL parameter - if ( c0 === 0x3F ) { /* ? */ - urlout = (new URL(urlin)).searchParams.get(step.slice(1)); - if ( urlout === null ) { return; } - if ( urlout.includes(' ') ) { - urlout = urlout.replace(/ /g, '%20'); - } - urlin = urlout; - continue; - } // Extract from URL parameter name at position i - if ( c0 === 0x26 ) { /* & */ + if ( c0 === 0x26 ) { // & const i = (parseInt(step.slice(1)) || 0) - 1; if ( i < 0 ) { return; } const url = new URL(urlin); if ( i >= url.searchParams.size ) { return; } const params = Array.from(url.searchParams.keys()); - urlin = urlout = decodeURIComponent(params[i]); + urlout = decodeURIComponent(params[i]); continue; } // Enforce https - if ( step === '+https' ) { + if ( c0 === 0x2B && step === '+https' ) { const s = urlin.replace(/^https?:\/\//, ''); if ( /^[\w-]:\/\//.test(s) ) { return; } - urlin = urlout = `https://${s}`; + urlout = `https://${s}`; continue; } // Decode base64 - if ( step === '-base64' ) { - urlin = urlout = self.atob(urlin); + if ( c0 === 0x2D && step === '-base64' ) { + urlout = self.atob(urlin); + continue; + } + // Regex extraction from first capture group + if ( c0 === 0x2F ) { // / + if ( directive.cache === null ) { + directive.cache = new RegExp(step.slice(1, -1)); + } + const match = directive.cache.exec(urlin); + if ( match === null ) { return; } + if ( match.length <= 1 ) { return; } + urlout = match[1]; + continue; + } + // Extract from URL parameter + if ( c0 === 0x3F ) { // ? + urlout = (new URL(urlin)).searchParams.get(step.slice(1)); + if ( urlout === null ) { return; } + if ( urlout.includes(' ') ) { + urlout = urlout.replace(/ /g, '%20'); + } continue; } // Unknown directive From 0c462e3ca1fba8f3bf01c762073c7dcbed59df00 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Thu, 10 Oct 2024 11:22:23 -0400 Subject: [PATCH 0349/1099] Update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2a6341a977cf7..d8241635b1edf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,4 @@ +- [Add regex extraction transformation step to `urlskip=` option](https://github.com/gorhill/uBlock/commit/c86ed5287b) - [Improve `prevent-window-open` scriptlet](https://github.com/gorhill/uBlock/commit/85877b12ed) - [Add support to parse Adguard's `[$domain=/.../]` regex-based modifier](https://github.com/gorhill/uBlock/commit/58bfe4c846) - [Validate result type of XPath expressions](https://github.com/gorhill/uBlock/commit/c746633693) From 2e1dac5950ce806f01959193ad6bbb4a91f6e950 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Thu, 10 Oct 2024 11:22:44 -0400 Subject: [PATCH 0350/1099] New revision for dev build --- dist/version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dist/version b/dist/version index 8e10b8e25a3ea..738bba80466b5 100644 --- a/dist/version +++ b/dist/version @@ -1 +1 @@ -1.60.1.10 \ No newline at end of file +1.60.1.11 \ No newline at end of file From 9e3c978d55ea52fce34fb3ccdb97808fccdfd237 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Thu, 10 Oct 2024 11:31:57 -0400 Subject: [PATCH 0351/1099] Minor edit to comment --- src/js/static-net-filtering.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/js/static-net-filtering.js b/src/js/static-net-filtering.js index 9f50e0b16616a..6a9f8cd129078 100644 --- a/src/js/static-net-filtering.js +++ b/src/js/static-net-filtering.js @@ -5420,7 +5420,7 @@ StaticNetFilteringEngine.prototype.transformRequest = function(fctxt, out = []) * ||example.com/path/to/tracker$urlskip=?url * ||example.com/path/to/tracker$urlskip=?url ?to * ||pixiv.net/jump.php?$urlskip=&1 - * ||podtrac.com/pts/redirect.mp3/$urlskip=/podtrac\.com\/pts\/redirect\.mp3\/(.*?\.mp3\b)/ +https + * ||podtrac.com/pts/redirect.mp3/$urlskip=/\/redirect\.mp3\/(.*?\.mp3\b)/ +https * * */ From 3cee922e688547a427b79dec6540da936828784c Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Thu, 10 Oct 2024 11:35:40 -0400 Subject: [PATCH 0352/1099] Make Firefox dev build auto-update --- dist/firefox/updates.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dist/firefox/updates.json b/dist/firefox/updates.json index 2958ae715b05b..9637cf7c5f058 100644 --- a/dist/firefox/updates.json +++ b/dist/firefox/updates.json @@ -3,9 +3,9 @@ "uBlock0@raymondhill.net": { "updates": [ { - "version": "1.60.1.10", + "version": "1.60.1.11", "browser_specific_settings": { "gecko": { "strict_min_version": "78.0" } }, - "update_link": "https://github.com/gorhill/uBlock/releases/download/1.60.1b10/uBlock0_1.60.1b10.firefox.signed.xpi" + "update_link": "https://github.com/gorhill/uBlock/releases/download/1.60.1b11/uBlock0_1.60.1b11.firefox.signed.xpi" } ] } From e5d16a1883b6f30ce621e59890059881d33fa5a8 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Thu, 10 Oct 2024 11:35:58 -0400 Subject: [PATCH 0353/1099] Typo --- src/js/static-net-filtering.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/js/static-net-filtering.js b/src/js/static-net-filtering.js index 6a9f8cd129078..6bd9fe00cd83f 100644 --- a/src/js/static-net-filtering.js +++ b/src/js/static-net-filtering.js @@ -5381,7 +5381,7 @@ StaticNetFilteringEngine.prototype.transformRequest = function(fctxt, out = []) }; /** - * @trustedOption urlkip + * @trustedOption urlskip * * @description * Extract a URL from another URL according to one or more transformation steps, From 4d982d9972be4eb0a23e289c6f3ec3ae8cca36f2 Mon Sep 17 00:00:00 2001 From: Fanboynz Date: Sat, 12 Oct 2024 01:26:37 +1300 Subject: [PATCH 0354/1099] Add forbidden/forever values (#3925) --- assets/resources/scriptlets.js | 1 + 1 file changed, 1 insertion(+) diff --git a/assets/resources/scriptlets.js b/assets/resources/scriptlets.js index cd4c7ea0cb227..8db2877fcb87a 100644 --- a/assets/resources/scriptlets.js +++ b/assets/resources/scriptlets.js @@ -1002,6 +1002,7 @@ function getSafeCookieValuesFn() { 'enable', 'disable', 'enabled', 'disabled', 'essential', 'nonessential', + 'forbidden', 'forever', 'hide', 'hidden', 'necessary', 'required', 'ok', From 01eebffc1fd133f4f72f886551e79ba4bfc3bb24 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Fri, 11 Oct 2024 08:54:50 -0400 Subject: [PATCH 0355/1099] Add `-uricomponent` to `urlskip=` option To unescape URI-encoded characters. Related discussion: https://github.com/uBlockOrigin/uBlock-issues/issues/3206#issuecomment-2406479971 --- src/js/static-net-filtering.js | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/src/js/static-net-filtering.js b/src/js/static-net-filtering.js index 6bd9fe00cd83f..92fc51b33cb2a 100644 --- a/src/js/static-net-filtering.js +++ b/src/js/static-net-filtering.js @@ -5406,6 +5406,8 @@ StaticNetFilteringEngine.prototype.transformRequest = function(fctxt, out = []) * * `-base64`: decode the current string as a base64-encoded string. * + * `-uricomponent`: decode the current string as a URI component string. + * * At any given step, the currently extracted string may not necessarily be * a valid URL, and more transformation steps may be needed to obtain a valid * URL once all the steps are applied. @@ -5470,10 +5472,18 @@ function urlSkip(directive, urlin, steps) { urlout = `https://${s}`; continue; } - // Decode base64 - if ( c0 === 0x2D && step === '-base64' ) { - urlout = self.atob(urlin); - continue; + // Decode + if ( c0 === 0x2D ) { + // Base64 + if ( step === '-base64' ) { + urlout = self.atob(urlin); + continue; + } + // URI component + if ( step === '-uricomponent' ) { + urlout = self.decodeURIComponent(urlin); + continue; + } } // Regex extraction from first capture group if ( c0 === 0x2F ) { // / From caba9cdefa082fa36c82a9888bc84f3dbec5c7a2 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Fri, 11 Oct 2024 09:03:30 -0400 Subject: [PATCH 0356/1099] Use uBO's default listset --- platform/npm/demo.js | 37 +++++++++++++++++++------------------ 1 file changed, 19 insertions(+), 18 deletions(-) diff --git a/platform/npm/demo.js b/platform/npm/demo.js index ebe314b2fdf6d..bc0ca87a1f7e3 100644 --- a/platform/npm/demo.js +++ b/platform/npm/demo.js @@ -36,6 +36,17 @@ import { StaticNetFilteringEngine } from '@gorhill/ubo-core'; /******************************************************************************/ +async function fetchList(name, url) { + return fetch(url).then(r => { + return r.text(); + }).then(raw => { + console.log(`${name} fetched`); + return { name, raw }; + }).catch(reason => { + console.error(reason); + }); +} + async function main() { const pathToSelfie = 'cache/selfie.txt'; @@ -62,24 +73,14 @@ async function main() { if ( !selfie ) { console.log(`Fetching lists...`); await snfe.useLists([ - fetch('https://easylist.to/easylist/easylist.txt') - .then(r => { - return r.text(); - }).then(raw => { - console.log(`easylist fetched`); - return { name: 'easylist', raw }; - }).catch(reason => { - console.error(reason); - }), - fetch('https://easylist.to/easylist/easyprivacy.txt') - .then(r => { - return r.text(); - }).then(raw => { - console.log(`easyprivacy fetched`); - return { name: 'easyprivacy', raw }; - }).catch(reason => { - console.error(reason); - }), + fetchList('ubo-ads', 'https://ublockorigin.github.io/uAssetsCDN/filters/filters.min.txt'), + fetchList('ubo-badware', 'https://ublockorigin.github.io/uAssetsCDN/filters/badware.min.txt'), + fetchList('ubo-privacy', 'https://ublockorigin.github.io/uAssetsCDN/filters/privacy.min.txt'), + fetchList('ubo-unbreak', 'https://ublockorigin.github.io/uAssetsCDN/filters/unbreak.min.txt'), + fetchList('ubo-quick', 'https://ublockorigin.github.io/uAssetsCDN/filters/quick-fixes.min.txt'), + fetchList('easylist', 'https://easylist.to/easylist/easylist.txt'), + fetchList('easyprivacy', 'https://easylist.to/easylist/easyprivacy.txt'), + fetchList('plowe', 'https://pgl.yoyo.org/adservers/serverlist.php?hostformat=hosts&showintro=1&mimetype=plaintext'), ]); const selfie = await snfe.serialize(); await fs.mkdir('cache', { recursive: true }); From b8959dcca9ad2bfcd50bc2c322021f5d1ac473f2 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Fri, 11 Oct 2024 09:04:23 -0400 Subject: [PATCH 0357/1099] Comment --- src/js/static-net-filtering.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/js/static-net-filtering.js b/src/js/static-net-filtering.js index 92fc51b33cb2a..73218aba5b194 100644 --- a/src/js/static-net-filtering.js +++ b/src/js/static-net-filtering.js @@ -5406,7 +5406,7 @@ StaticNetFilteringEngine.prototype.transformRequest = function(fctxt, out = []) * * `-base64`: decode the current string as a base64-encoded string. * - * `-uricomponent`: decode the current string as a URI component string. + * `-uricomponent`: decode the current string as a URI encoded string. * * At any given step, the currently extracted string may not necessarily be * a valid URL, and more transformation steps may be needed to obtain a valid From d0ae3c3e77c7e5408ddd440c7205c9732bf26a88 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Fri, 11 Oct 2024 09:06:49 -0400 Subject: [PATCH 0358/1099] Update changelog --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index d8241635b1edf..7111358dd76d0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,5 @@ +- [Add `-uricomponent` to `urlskip=` option](https://github.com/gorhill/uBlock/commit/01eebffc1f) +- [Add `forbidden`/`forever` as safe cookie values](https://github.com/gorhill/uBlock/commit/4d982d9972) (by @ryanbr) - [Add regex extraction transformation step to `urlskip=` option](https://github.com/gorhill/uBlock/commit/c86ed5287b) - [Improve `prevent-window-open` scriptlet](https://github.com/gorhill/uBlock/commit/85877b12ed) - [Add support to parse Adguard's `[$domain=/.../]` regex-based modifier](https://github.com/gorhill/uBlock/commit/58bfe4c846) From f5a7053acb8bf1d67579ecaa1561bb9abe8ee4fa Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Fri, 11 Oct 2024 09:07:12 -0400 Subject: [PATCH 0359/1099] New revision for dev build --- dist/version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dist/version b/dist/version index 738bba80466b5..080adefd7cd65 100644 --- a/dist/version +++ b/dist/version @@ -1 +1 @@ -1.60.1.11 \ No newline at end of file +1.60.1.12 \ No newline at end of file From 44bcb5fd996e6f54eea355ed667bac8a5d664b68 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Fri, 11 Oct 2024 09:15:40 -0400 Subject: [PATCH 0360/1099] Make Firefox dev build auto-update --- dist/firefox/updates.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dist/firefox/updates.json b/dist/firefox/updates.json index 9637cf7c5f058..ace180c1a03d2 100644 --- a/dist/firefox/updates.json +++ b/dist/firefox/updates.json @@ -3,9 +3,9 @@ "uBlock0@raymondhill.net": { "updates": [ { - "version": "1.60.1.11", + "version": "1.60.1.12", "browser_specific_settings": { "gecko": { "strict_min_version": "78.0" } }, - "update_link": "https://github.com/gorhill/uBlock/releases/download/1.60.1b11/uBlock0_1.60.1b11.firefox.signed.xpi" + "update_link": "https://github.com/gorhill/uBlock/releases/download/1.60.1b12/uBlock0_1.60.1b12.firefox.signed.xpi" } ] } From 2c60bb3b07fc0b234e1c843889e047f8f3a04a84 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Fri, 11 Oct 2024 16:41:03 -0400 Subject: [PATCH 0361/1099] [mv3] Issue labels cannot be set client-side --- platform/mv3/extension/js/report.js | 1 - 1 file changed, 1 deletion(-) diff --git a/platform/mv3/extension/js/report.js b/platform/mv3/extension/js/report.js index 676342b9e0ac3..35c3a85094100 100644 --- a/platform/mv3/extension/js/report.js +++ b/platform/mv3/extension/js/report.js @@ -106,7 +106,6 @@ async function reportSpecificFilterIssue() { '`' + qs$('select[name="url"]').value + '`' ); githubURL.searchParams.set('category', issueType); - githubURL.searchParams.set('labels', 'uBOL'); const manifest = runtime.getManifest(); const rulesets = await dnr.getEnabledRulesets(); From ce9fc5dc14e198c83f6f7e553477f5c82ff26e4b Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sat, 12 Oct 2024 08:52:05 -0400 Subject: [PATCH 0362/1099] Add support to `urlskip=` media resources Related feedback: https://github.com/uBlockOrigin/uBlock-issues/issues/3206#issuecomment-2406777654 --- src/js/benchmarks.js | 20 ++++++++++------- src/js/pagestore.js | 41 +++++++++++++++++++++------------- src/js/static-net-filtering.js | 4 ++-- src/js/traffic.js | 3 ++- 4 files changed, 42 insertions(+), 26 deletions(-) diff --git a/src/js/benchmarks.js b/src/js/benchmarks.js index c4102469863fb..8603b8737a89c 100644 --- a/src/js/benchmarks.js +++ b/src/js/benchmarks.js @@ -166,11 +166,11 @@ export async function benchmarkStaticNetFiltering(options = {}) { let allowCount = 0; let redirectCount = 0; let removeparamCount = 0; + let urlskipCount = 0; let cspCount = 0; let permissionsCount = 0; let replaceCount = 0; - for ( let i = 0; i < requests.length; i++ ) { - const request = requests[i]; + for ( const request of requests ) { fctxt.setURL(request.url); if ( fctxt.getIPAddress() === '' ) { fctxt.setIPAddress('93.184.215.14\n2606:2800:21f:cb07:6820:80da:af6b:8b2c'); @@ -186,12 +186,15 @@ export async function benchmarkStaticNetFiltering(options = {}) { if ( sfne.transformRequest(fctxt) ) { redirectCount += 1; } - if ( fctxt.redirectURL !== undefined && sfne.hasQuery(fctxt) ) { - if ( sfne.filterQuery(fctxt, 'removeparam') ) { + if ( sfne.hasQuery(fctxt) ) { + if ( sfne.filterQuery(fctxt) ) { removeparamCount += 1; } } - if ( fctxt.type === 'main_frame' || fctxt.type === 'sub_frame' ) { + if ( sfne.urlSkip(fctxt) ) { + urlskipCount += 1; + } + if ( fctxt.isDocument() ) { if ( sfne.matchAndFetchModifiers(fctxt, 'csp') ) { cspCount += 1; } @@ -207,9 +210,9 @@ export async function benchmarkStaticNetFiltering(options = {}) { if ( sfne.redirectRequest(redirectEngine, fctxt) ) { redirectCount += 1; } - } - if ( fctxt.type === 'main_frame' ) { - sfne.matchAndFetchModifiers(fctxt, 'urlskip'); + if ( fctxt.isRootDocument() && sfne.urlSkip(fctxt) ) { + urlskipCount += 1; + } } } const t1 = performance.now(); @@ -224,6 +227,7 @@ export async function benchmarkStaticNetFiltering(options = {}) { `\tUnblocked: ${allowCount}`, `\tredirect=: ${redirectCount}`, `\tremoveparam=: ${removeparamCount}`, + `\turlskip=: ${urlskipCount}`, `\tcsp=: ${cspCount}`, `\tpermissions=: ${permissionsCount}`, `\treplace=: ${replaceCount}`, diff --git a/src/js/pagestore.js b/src/js/pagestore.js index 1b94b1fef6d95..e23f0950ec8f3 100644 --- a/src/js/pagestore.js +++ b/src/js/pagestore.js @@ -942,6 +942,9 @@ const PageStore = class { if ( staticNetFilteringEngine.hasQuery(fctxt) ) { staticNetFilteringEngine.filterQuery(fctxt, directives); } + if ( this.urlSkippableResources.has(fctxt.itype) ) { + staticNetFilteringEngine.urlSkip(fctxt, directives); + } if ( directives.length === 0 ) { return; } if ( logger.enabled !== true ) { return; } fctxt.pushFilters(directives.map(a => a.logData())); @@ -1132,22 +1135,30 @@ const PageStore = class { response.blockedResources = this.netFilteringCache.lookupAllBlocked(fctxt.getDocHostname()); } -}; - -PageStore.prototype.cacheableResults = new Set([ - µb.FilteringContext.SUB_FRAME, -]); -PageStore.prototype.collapsibleResources = new Set([ - µb.FilteringContext.IMAGE, - µb.FilteringContext.MEDIA, - µb.FilteringContext.OBJECT, - µb.FilteringContext.SUB_FRAME, -]); - -// To mitigate memory churning -PageStore.junkyard = []; -PageStore.junkyardMax = 10; + cacheableResults = new Set([ + µb.FilteringContext.SUB_FRAME + ]); + + collapsibleResources = new Set([ + µb.FilteringContext.IMAGE, + µb.FilteringContext.MEDIA, + µb.FilteringContext.OBJECT, + µb.FilteringContext.SUB_FRAME, + ]); + + urlSkippableResources = new Set([ + µb.FilteringContext.IMAGE, + µb.FilteringContext.MAIN_FRAME, + µb.FilteringContext.MEDIA, + µb.FilteringContext.OBJECT, + µb.FilteringContext.SUB_FRAME, + ]); + + // To mitigate memory churning + static junkyard = []; + static junkyardMax = 10; +}; /******************************************************************************/ diff --git a/src/js/static-net-filtering.js b/src/js/static-net-filtering.js index 73218aba5b194..0fb6dc5b86b30 100644 --- a/src/js/static-net-filtering.js +++ b/src/js/static-net-filtering.js @@ -5449,9 +5449,9 @@ StaticNetFilteringEngine.prototype.urlSkip = function(fctxt, out = []) { return out; }; -function urlSkip(directive, urlin, steps) { +function urlSkip(directive, url, steps) { try { - let urlout = urlin; + let urlout = url; for ( const step of steps ) { const urlin = urlout; const c0 = step.charCodeAt(0); diff --git a/src/js/traffic.js b/src/js/traffic.js index 5de002797671e..eab93be742432 100644 --- a/src/js/traffic.js +++ b/src/js/traffic.js @@ -196,8 +196,9 @@ const onBeforeRootFrameRequest = function(fctxt) { if ( trusted === false && pageStore !== null ) { if ( result !== 1 ) { pageStore.redirectNonBlockedRequest(fctxt); + } else { + pageStore.skipMainDocument(fctxt); } - pageStore.skipMainDocument(fctxt); } if ( logger.enabled ) { From 3e7a79ebefc46929dd43a56bdff6f118d75a12f2 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sat, 12 Oct 2024 08:55:43 -0400 Subject: [PATCH 0363/1099] Update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7111358dd76d0..188fcda42137a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,4 @@ +- [Add support to `urlskip=` media resources](https://github.com/gorhill/uBlock/commit/ce9fc5dc14) - [Add `-uricomponent` to `urlskip=` option](https://github.com/gorhill/uBlock/commit/01eebffc1f) - [Add `forbidden`/`forever` as safe cookie values](https://github.com/gorhill/uBlock/commit/4d982d9972) (by @ryanbr) - [Add regex extraction transformation step to `urlskip=` option](https://github.com/gorhill/uBlock/commit/c86ed5287b) From 6df1ea8cd6bd064d7f99a7f609870689c5b33e15 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sat, 12 Oct 2024 08:56:20 -0400 Subject: [PATCH 0364/1099] New revision for dev build --- dist/version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dist/version b/dist/version index 080adefd7cd65..83a9e98f25b3a 100644 --- a/dist/version +++ b/dist/version @@ -1 +1 @@ -1.60.1.12 \ No newline at end of file +1.60.1.13 \ No newline at end of file From 94ca27acd1ee38322a5106cfb3ea7f6b9d8e557e Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sat, 12 Oct 2024 09:25:59 -0400 Subject: [PATCH 0365/1099] Make Firefox dev build auto-update --- dist/firefox/updates.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dist/firefox/updates.json b/dist/firefox/updates.json index ace180c1a03d2..20abab4c95a73 100644 --- a/dist/firefox/updates.json +++ b/dist/firefox/updates.json @@ -3,9 +3,9 @@ "uBlock0@raymondhill.net": { "updates": [ { - "version": "1.60.1.12", + "version": "1.60.1.13", "browser_specific_settings": { "gecko": { "strict_min_version": "78.0" } }, - "update_link": "https://github.com/gorhill/uBlock/releases/download/1.60.1b12/uBlock0_1.60.1b12.firefox.signed.xpi" + "update_link": "https://github.com/gorhill/uBlock/releases/download/1.60.1b13/uBlock0_1.60.1b13.firefox.signed.xpi" } ] } From 32f27c5131ecffa4dba412c9f39ff3d3e2c21340 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Wed, 16 Oct 2024 08:25:56 -0400 Subject: [PATCH 0366/1099] Ensure `urlskip=` redirects only to `https:` --- src/js/static-net-filtering.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/js/static-net-filtering.js b/src/js/static-net-filtering.js index 0fb6dc5b86b30..6a2f6c9c37418 100644 --- a/src/js/static-net-filtering.js +++ b/src/js/static-net-filtering.js @@ -5508,7 +5508,8 @@ function urlSkip(directive, url, steps) { // Unknown directive return; } - void new URL(urlout); + const urlfinal = new URL(urlout); + if ( urlfinal.protocol !== 'https:' ) { return; } return urlout; } catch(x) { } From e9c0ad59dd47dcd7f33840aea35f517b9401f928 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Wed, 16 Oct 2024 08:29:25 -0400 Subject: [PATCH 0367/1099] Update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 188fcda42137a..41a61ca55926c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,4 @@ +- [Ensure `urlskip=` redirects only to `https:`](https://github.com/gorhill/uBlock/commit/32f27c5131) - [Add support to `urlskip=` media resources](https://github.com/gorhill/uBlock/commit/ce9fc5dc14) - [Add `-uricomponent` to `urlskip=` option](https://github.com/gorhill/uBlock/commit/01eebffc1f) - [Add `forbidden`/`forever` as safe cookie values](https://github.com/gorhill/uBlock/commit/4d982d9972) (by @ryanbr) From e6b7f06ab42ba86b260fb75413584520f8210a08 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Wed, 16 Oct 2024 08:29:52 -0400 Subject: [PATCH 0368/1099] New revision for dev build --- dist/version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dist/version b/dist/version index 83a9e98f25b3a..9769226cad84b 100644 --- a/dist/version +++ b/dist/version @@ -1 +1 @@ -1.60.1.13 \ No newline at end of file +1.60.1.14 \ No newline at end of file From 98db549bb762d6ad07d4f626af9bf94313faca50 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Wed, 16 Oct 2024 08:56:20 -0400 Subject: [PATCH 0369/1099] Make Firefox dev build auto-update --- dist/firefox/updates.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dist/firefox/updates.json b/dist/firefox/updates.json index 20abab4c95a73..79c5afb38d0dd 100644 --- a/dist/firefox/updates.json +++ b/dist/firefox/updates.json @@ -3,9 +3,9 @@ "uBlock0@raymondhill.net": { "updates": [ { - "version": "1.60.1.13", + "version": "1.60.1.14", "browser_specific_settings": { "gecko": { "strict_min_version": "78.0" } }, - "update_link": "https://github.com/gorhill/uBlock/releases/download/1.60.1b13/uBlock0_1.60.1b13.firefox.signed.xpi" + "update_link": "https://github.com/gorhill/uBlock/releases/download/1.60.1b14/uBlock0_1.60.1b14.firefox.signed.xpi" } ] } From 4291c874d9bdfe3d6997afdf5863c532f463e877 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Thu, 17 Oct 2024 12:11:50 -0400 Subject: [PATCH 0370/1099] Fix regression in `prevent-xhr` scriptlet Related issue: https://github.com/uBlockOrigin/uBlock-issues/issues/3418 --- assets/resources/scriptlets.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/assets/resources/scriptlets.js b/assets/resources/scriptlets.js index 8db2877fcb87a..3a7511ba9d187 100644 --- a/assets/resources/scriptlets.js +++ b/assets/resources/scriptlets.js @@ -1617,20 +1617,20 @@ function preventXhrFn( }; const XHRBefore = XMLHttpRequest.prototype; self.XMLHttpRequest = class extends self.XMLHttpRequest { - open(method, url, defer, ...args) { + open(method, url, ...args) { xhrInstances.delete(this); if ( warOrigin !== undefined && url.startsWith(warOrigin) ) { - return super.open(method, url, defer, ...args); + return super.open(method, url, ...args); } const haystack = { method, url }; if ( propsToMatch === '' && directive === '' ) { safe.uboLog(logPrefix, `Called: ${safe.JSON_stringify(haystack, null, 2)}`); - return super.open(method, url, defer, ...args); + return super.open(method, url, ...args); } if ( matchObjectProperties(propNeedles, haystack) ) { const xhrDetails = Object.assign(haystack, { xhr: this, - defer, + defer: args.length === 0 || !!args[0], directive, headers: { 'date': '', @@ -1646,7 +1646,7 @@ function preventXhrFn( }); xhrInstances.set(this, xhrDetails); } - return super.open(method, url, defer, ...args); + return super.open(method, url, ...args); } send(...args) { const xhrDetails = xhrInstances.get(this); From 89c353640e39f0aaece005071be832e5448fb334 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Thu, 17 Oct 2024 12:27:49 -0400 Subject: [PATCH 0371/1099] [mv3] Add EST-0 list --- platform/mv3/make-rulesets.js | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/platform/mv3/make-rulesets.js b/platform/mv3/make-rulesets.js index c85a11a5869f0..09719101055b4 100644 --- a/platform/mv3/make-rulesets.js +++ b/platform/mv3/make-rulesets.js @@ -1194,6 +1194,16 @@ async function main() { }); } + await rulesetFromURLs({ + id: 'est-0', + group: 'regions', + lang: 'et', + name: '🇪🇪ee: Eesti saitidele kohandatud filter', + enabled: false, + urls: [ 'https://ubol-et.adblock.ee/list.txt' ], + homeURL: 'https://github.com/sander85/uBOL-et', + }); + // Handpicked rulesets from assets.json const handpicked = [ 'block-lan', From 27f3612fdfeb6a1b4b7c1a95d0c49c1f6a23d0ec Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Thu, 17 Oct 2024 12:32:28 -0400 Subject: [PATCH 0372/1099] [mv3] Set Optimal as default if extension broad permission at install time This is to prepare uBOL to be ready to set Optimal mode as default if ever Chromium-based browsers support a way to pre-grant broad host permissions in a policy. Related issue: https://github.com/uBlockOrigin/uBOL-home/discussions/232 https://github.com/uBlockOrigin/uBOL-home/discussions/135 --- platform/mv3/extension/js/background.js | 14 +++++++++++++- platform/mv3/extension/js/settings.js | 4 ++-- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/platform/mv3/extension/js/background.js b/platform/mv3/extension/js/background.js index c8183e9251895..791625d2a1c92 100644 --- a/platform/mv3/extension/js/background.js +++ b/platform/mv3/extension/js/background.js @@ -38,6 +38,8 @@ import { } from './ruleset-manager.js'; import { + MODE_BASIC, + MODE_OPTIMAL, getDefaultFilteringMode, getFilteringMode, getTrustedSites, @@ -136,7 +138,7 @@ async function onPermissionsRemoved() { const modified = await syncWithBrowserPermissions(); if ( modified === false ) { return false; } const afterMode = await getDefaultFilteringMode(); - if ( beforeMode > 1 && afterMode <= 1 ) { + if ( beforeMode > MODE_BASIC && afterMode <= MODE_BASIC ) { updateDynamicRules(); } registerInjectables(); @@ -420,9 +422,19 @@ async function start() { ); if ( firstRun ) { + const enableOptimal = await hasOmnipotence(); + if ( enableOptimal ) { + const afterLevel = await setDefaultFilteringMode(MODE_OPTIMAL); + if ( afterLevel === MODE_OPTIMAL ) { + updateDynamicRules(); + registerInjectables(); + } + } const disableFirstRunPage = await adminRead('disableFirstRunPage'); if ( disableFirstRunPage !== true ) { runtime.openOptionsPage(); + } else { + firstRun = false; } } } diff --git a/platform/mv3/extension/js/settings.js b/platform/mv3/extension/js/settings.js index 6f50055df6554..c916c46577f96 100644 --- a/platform/mv3/extension/js/settings.js +++ b/platform/mv3/extension/js/settings.js @@ -316,7 +316,7 @@ dom.on('#showBlockedCount input[type="checkbox"]', 'change', ev => { function renderTrustedSites() { const textarea = qs$('#trustedSites'); - const hostnames = cachedRulesetData.trustedSites; + const hostnames = cachedRulesetData.trustedSites || []; textarea.value = hostnames.map(hn => punycode.toUnicode(hn)).join('\n'); if ( textarea.value !== '' ) { textarea.value += '\n'; @@ -325,7 +325,7 @@ function renderTrustedSites() { function changeTrustedSites() { const hostnames = getStagedTrustedSites(); - const hash = hashFromIterable(cachedRulesetData.trustedSites); + const hash = hashFromIterable(cachedRulesetData.trustedSites || []); if ( hashFromIterable(hostnames) === hash ) { return; } sendMessage({ what: 'setTrustedSites', From 0425bdfd35fc6e9d423cab839610bc7eaa7c8917 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Thu, 17 Oct 2024 12:44:02 -0400 Subject: [PATCH 0373/1099] Import translation work from https://crowdin.com/project/ublock --- platform/mv3/description/webstore.zh_CN.txt | 2 +- .../mv3/extension/_locales/ar/messages.json | 2 +- .../mv3/extension/_locales/hi/messages.json | 2 +- .../extension/_locales/pt_PT/messages.json | 34 +++++++++---------- .../mv3/extension/_locales/sq/messages.json | 10 +++--- .../mv3/extension/_locales/th/messages.json | 6 ++-- .../mv3/extension/_locales/vi/messages.json | 34 +++++++++---------- .../extension/_locales/zh_CN/messages.json | 16 ++++----- src/_locales/ar/messages.json | 2 +- src/_locales/fa/messages.json | 20 +++++------ src/_locales/ja/messages.json | 4 +-- 11 files changed, 66 insertions(+), 66 deletions(-) diff --git a/platform/mv3/description/webstore.zh_CN.txt b/platform/mv3/description/webstore.zh_CN.txt index 413f499ea0d50..e0ae72dd5eb33 100644 --- a/platform/mv3/description/webstore.zh_CN.txt +++ b/platform/mv3/description/webstore.zh_CN.txt @@ -7,7 +7,7 @@ uBO Lite (uBOL) 是一个基于最新浏览器扩展接口(Manifest Version 3 - EasyPrivacy - Peter Lowe’s Ad and tracking server list -You can enable more rulesets by visiting the options page -- click the _Cogs_ icon in the popup panel. +访问选项页面,点击弹出面板中的齿轮图标,即可启用更多规则集。 uBOL 的过滤规则是完全声明式的,并不需要固定保留一个 uBOL 扩展进程,基于 CSS/JS 注入的内容过滤更是交由浏览器进行调度,比起扩展本身更为可靠。 这也即是说当内容被过滤时 uBOL 自身并不占用额外 CPU 和内存资源,只有在您打开弹出面板或是设置页面时才会生成 uBOL 扩展进程。 diff --git a/platform/mv3/extension/_locales/ar/messages.json b/platform/mv3/extension/_locales/ar/messages.json index 3f051e050e2b0..d3c4d9a76f88e 100644 --- a/platform/mv3/extension/_locales/ar/messages.json +++ b/platform/mv3/extension/_locales/ar/messages.json @@ -52,7 +52,7 @@ "description": "Header for a ruleset section in 'Filter lists pane'" }, "3pGroupAds": { - "message": "اعلانات", + "message": "إعلانات", "description": "Header for a ruleset section in 'Filter lists pane'" }, "3pGroupPrivacy": { diff --git a/platform/mv3/extension/_locales/hi/messages.json b/platform/mv3/extension/_locales/hi/messages.json index d81b4d7bffb8a..2a9dae26b6814 100644 --- a/platform/mv3/extension/_locales/hi/messages.json +++ b/platform/mv3/extension/_locales/hi/messages.json @@ -32,7 +32,7 @@ "description": "Label in the popup panel for the current filtering mode" }, "popupTipReport": { - "message": "Report an issue on this website", + "message": "इस वेबसाइट पर किसी समस्या को रिपोर्ट करें", "description": "Tooltip used for the 'chat' icon in the panel" }, "popupTipDashboard": { diff --git a/platform/mv3/extension/_locales/pt_PT/messages.json b/platform/mv3/extension/_locales/pt_PT/messages.json index a6bc083461719..f73c906c6560b 100644 --- a/platform/mv3/extension/_locales/pt_PT/messages.json +++ b/platform/mv3/extension/_locales/pt_PT/messages.json @@ -32,7 +32,7 @@ "description": "Label in the popup panel for the current filtering mode" }, "popupTipReport": { - "message": "Report an issue on this website", + "message": "Relatar um problema neste website", "description": "Tooltip used for the 'chat' icon in the panel" }, "popupTipDashboard": { @@ -104,67 +104,67 @@ "description": "Shown in the About pane" }, "supportS6H": { - "message": "Report a filter issue", + "message": "Relatar um problema de filtro", "description": "Header of 'Report a filter issue' section in Support pane" }, "supportS3P1": { - "message": "Report filter issues with specific websites to the uBlockOrigin/uAssets issue tracker. Requires a GitHub account.", + "message": "Relate problemas de filtros em websites específicos no rastreador de problemas uBlockOrigin/uAssets. Requer uma conta no GitHub.", "description": "First paragraph of 'Filter issues' section in Support pane" }, "supportS6P1S1": { - "message": "To avoid burdening volunteers with duplicate reports, please verify that the issue has not already been reported.", + "message": "Para evitar sobrecarregar os voluntários com relatórios duplicados, por favor verifique se o problema ainda não foi relatado.", "description": "A paragraph in the filter issue reporter section" }, "supportFindSpecificButton": { - "message": "Find similar reports", + "message": "Encontrar relatórios semelhantes", "description": "A clickable link in the filter issue reporter section" }, "supportS6URL": { - "message": "Address of the webpage:", + "message": "Endereço da página web:", "description": "Label for the URL of the page" }, "supportS6Select1": { - "message": "The webpage…", + "message": "A página web...", "description": "Label for widget to select type of issue" }, "supportS6Select1Option0": { - "message": "-- Pick an entry --", + "message": "-- Escolha uma entrada --", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option1": { - "message": "Shows ads or ad leftovers", + "message": "Mostra anúncios ou restos de anúncios", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option2": { - "message": "Has overlays or other nuisances", + "message": "Tem sobreposições ou outros incómodos", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option3": { - "message": "Detects uBO Lite", + "message": "Deteta uBO Lite", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option4": { - "message": "Has privacy-related issues", + "message": "Tem problemas relacionados com a privacidade", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option5": { - "message": "Malfunctions when uBO Lite is enabled", + "message": "Falha quando o uBO Lite é ativado", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option6": { - "message": "Opens unwanted tabs or windows", + "message": "Abre separadores ou janelas indesejáveis", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option7": { - "message": "Leads to badware, phishing", + "message": "Leva a badware e phishing", "description": "An entry in the widget used to select the type of issue" }, "supportS6Checkbox1": { - "message": "Label the webpage as “NSFW” (“Not Safe For Work”)", + "message": "Classifica a página web como “NSFW” (“Não segura para o trabalho”)", "description": "A checkbox to use for NSFW sites" }, "supportReportSpecificButton": { - "message": "Create new report", + "message": "Criar novo relatório", "description": "Text for button which open an external webpage in Support pane" }, "firstRunSectionLabel": { diff --git a/platform/mv3/extension/_locales/sq/messages.json b/platform/mv3/extension/_locales/sq/messages.json index 4bcd36d07283a..464f49455b042 100644 --- a/platform/mv3/extension/_locales/sq/messages.json +++ b/platform/mv3/extension/_locales/sq/messages.json @@ -108,7 +108,7 @@ "description": "Header of 'Report a filter issue' section in Support pane" }, "supportS3P1": { - "message": "Raportoni problemet me filtrat në uebsajt specifikë në uBlockOrigin/uAssets issue tracker. Kërkon një llogari GitHub.", + "message": "Problemet e disa faqeve me filtrat duhen raportuar në ditarin e problemeve uBlockOrigin/uAssets. Duhet një konto GitHub.", "description": "First paragraph of 'Filter issues' section in Support pane" }, "supportS6P1S1": { @@ -124,7 +124,7 @@ "description": "Label for the URL of the page" }, "supportS6Select1": { - "message": "Uebsajti...", + "message": "Uebsajti…", "description": "Label for widget to select type of issue" }, "supportS6Select1Option0": { @@ -144,11 +144,11 @@ "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option4": { - "message": "Ka probleme me privatësinë", + "message": "Ka probleme me ruajtjen e privatësisë", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option5": { - "message": "Krijon mosfunksionime kur uBO Lite është i aktivizuar", + "message": "Punon keq kur uBO Lite është i aktivizuar", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option6": { @@ -156,7 +156,7 @@ "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option7": { - "message": "Çon në instalimin e programeve keqdashëse, mashtruese", + "message": "Çon te programet keqdashëse, mashtruese", "description": "An entry in the widget used to select the type of issue" }, "supportS6Checkbox1": { diff --git a/platform/mv3/extension/_locales/th/messages.json b/platform/mv3/extension/_locales/th/messages.json index 3837f15a5e666..9905daf714b4e 100644 --- a/platform/mv3/extension/_locales/th/messages.json +++ b/platform/mv3/extension/_locales/th/messages.json @@ -28,7 +28,7 @@ "description": "Link to privacy policy on GitHub (English)" }, "popupFilteringModeLabel": { - "message": "filtering mode", + "message": "โหมดตัวกรอง", "description": "Label in the popup panel for the current filtering mode" }, "popupTipReport": { @@ -52,7 +52,7 @@ "description": "Header for a ruleset section in 'Filter lists pane'" }, "3pGroupAds": { - "message": "Ads", + "message": "โฆษณา", "description": "Header for a ruleset section in 'Filter lists pane'" }, "3pGroupPrivacy": { @@ -164,7 +164,7 @@ "description": "A checkbox to use for NSFW sites" }, "supportReportSpecificButton": { - "message": "Create new report", + "message": "สร้างรายงานใหม่", "description": "Text for button which open an external webpage in Support pane" }, "firstRunSectionLabel": { diff --git a/platform/mv3/extension/_locales/vi/messages.json b/platform/mv3/extension/_locales/vi/messages.json index 489c0522b0139..061c7416a4398 100644 --- a/platform/mv3/extension/_locales/vi/messages.json +++ b/platform/mv3/extension/_locales/vi/messages.json @@ -32,7 +32,7 @@ "description": "Label in the popup panel for the current filtering mode" }, "popupTipReport": { - "message": "Report an issue on this website", + "message": "Báo cáo lỗi trên trang này", "description": "Tooltip used for the 'chat' icon in the panel" }, "popupTipDashboard": { @@ -104,67 +104,67 @@ "description": "Shown in the About pane" }, "supportS6H": { - "message": "Report a filter issue", + "message": "Báo cáo lỗi bộ lọc cụ thể", "description": "Header of 'Report a filter issue' section in Support pane" }, "supportS3P1": { - "message": "Report filter issues with specific websites to the uBlockOrigin/uAssets issue tracker. Requires a GitHub account.", + "message": "Báo cáo các vấn đề về bộ lọc với các trang web cụ thể cho trình theo dõi vấn đề uBlockOrigin/uAssets xử lý. Cần có tài khoản GitHub.", "description": "First paragraph of 'Filter issues' section in Support pane" }, "supportS6P1S1": { - "message": "To avoid burdening volunteers with duplicate reports, please verify that the issue has not already been reported.", + "message": "Để tránh tạo thêm gánh nặng cho các tình nguyện viên, hãy chắc chắn rằng chưa từng có vấn đề tương tự được báo cáo.", "description": "A paragraph in the filter issue reporter section" }, "supportFindSpecificButton": { - "message": "Find similar reports", + "message": "Tìm các báo cáo tương tự", "description": "A clickable link in the filter issue reporter section" }, "supportS6URL": { - "message": "Address of the webpage:", + "message": "Địa chỉ của trang web:", "description": "Label for the URL of the page" }, "supportS6Select1": { - "message": "The webpage…", + "message": "Trang web…", "description": "Label for widget to select type of issue" }, "supportS6Select1Option0": { - "message": "-- Pick an entry --", + "message": "-- Chọn một mục --", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option1": { - "message": "Shows ads or ad leftovers", + "message": "Hiện quảng cáo hoặc vùng chứa quảng cáo", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option2": { - "message": "Has overlays or other nuisances", + "message": "Có lớp phủ hoặc những phiền toái khác", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option3": { - "message": "Detects uBO Lite", + "message": "Phát hiện uBO Lite", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option4": { - "message": "Has privacy-related issues", + "message": "Có các cấn đề về quyền riêng tư", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option5": { - "message": "Malfunctions when uBO Lite is enabled", + "message": "Trục trặc khi bật uBO Lite", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option6": { - "message": "Opens unwanted tabs or windows", + "message": "Xuất hiện các tab và cửa sổ ngoài mong muốn", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option7": { - "message": "Leads to badware, phishing", + "message": "Dẫn đến phần mềm độc hại, lừa đảo", "description": "An entry in the widget used to select the type of issue" }, "supportS6Checkbox1": { - "message": "Label the webpage as “NSFW” (“Not Safe For Work”)", + "message": "Đánh dấu trang web là “NSFW” (“Không an toàn cho công việc”)", "description": "A checkbox to use for NSFW sites" }, "supportReportSpecificButton": { - "message": "Create new report", + "message": "Tạo báo cáo mới", "description": "Text for button which open an external webpage in Support pane" }, "firstRunSectionLabel": { diff --git a/platform/mv3/extension/_locales/zh_CN/messages.json b/platform/mv3/extension/_locales/zh_CN/messages.json index e8b4b31de453e..bcc72a5fc3f9b 100644 --- a/platform/mv3/extension/_locales/zh_CN/messages.json +++ b/platform/mv3/extension/_locales/zh_CN/messages.json @@ -104,11 +104,11 @@ "description": "Shown in the About pane" }, "supportS6H": { - "message": "Report a filter issue", + "message": "报告过滤问题", "description": "Header of 'Report a filter issue' section in Support pane" }, "supportS3P1": { - "message": "Report filter issues with specific websites to the uBlockOrigin/uAssets issue tracker. Requires a GitHub account.", + "message": "将特定网站的过滤问题报告给uBlockOrigin/uAssets issue tracker需要有 GitHub 账户。", "description": "First paragraph of 'Filter issues' section in Support pane" }, "supportS6P1S1": { @@ -128,11 +128,11 @@ "description": "Label for widget to select type of issue" }, "supportS6Select1Option0": { - "message": "-- Pick an entry --", + "message": "-- 选择一个条目 --", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option1": { - "message": "Shows ads or ad leftovers", + "message": "显示广告或残留广告", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option2": { @@ -140,7 +140,7 @@ "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option3": { - "message": "Detects uBO Lite", + "message": "检测到 uBO Lite", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option4": { @@ -148,7 +148,7 @@ "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option5": { - "message": "Malfunctions when uBO Lite is enabled", + "message": "启用 uBO Lite 时发生故障", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option6": { @@ -156,11 +156,11 @@ "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option7": { - "message": "Leads to badware, phishing", + "message": "导致恶意软件、网络钓鱼", "description": "An entry in the widget used to select the type of issue" }, "supportS6Checkbox1": { - "message": "Label the webpage as “NSFW” (“Not Safe For Work”)", + "message": "将网页标记为 “NSFW”(“工作场所不宜”)", "description": "A checkbox to use for NSFW sites" }, "supportReportSpecificButton": { diff --git a/src/_locales/ar/messages.json b/src/_locales/ar/messages.json index ca454de959e84..e4f8e6505622f 100644 --- a/src/_locales/ar/messages.json +++ b/src/_locales/ar/messages.json @@ -1248,7 +1248,7 @@ "description": "A context menu entry, to view the source code of the target resource" }, "shortcutCapturePlaceholder": { - "message": "ادخل اختصار", + "message": "أدخِل اختصار", "description": "Placeholder string for input field used to capture a keyboard shortcut" }, "genericMergeViewScrollLock": { diff --git a/src/_locales/fa/messages.json b/src/_locales/fa/messages.json index 23838c8ff4412..366b8689317f7 100644 --- a/src/_locales/fa/messages.json +++ b/src/_locales/fa/messages.json @@ -484,7 +484,7 @@ "description": "Filter lists section name" }, "3pGroupSocial": { - "message": "Social widgets", + "message": "ابزارک‌های اجتماعی", "description": "Filter lists section name" }, "3pGroupCookies": { @@ -856,7 +856,7 @@ "description": "A logger setting" }, "loggerSettingPerEntryLineCount": { - "message": "Use {{input}} lines per entry in vertically expanded mode", + "message": "استفاده از {{input}} خط برای هر ورودی در حالت عمودی گسترش‌یافته", "description": "A logger setting" }, "loggerSettingHideColumnsPrompt": { @@ -928,7 +928,7 @@ "description": "Header of 'Filter issues' section in Support pane" }, "supportS3P1": { - "message": "Report filter issues with specific websites to the uBlockOrigin/uAssets issue tracker. Requires a GitHub account.", + "message": "مشکلات فیلتر با وب‌سایت‌های خاص را به uBlockOrigin/uAssets گزارش دهید. نیاز به یک حساب کاربری GitHub دارد.", "description": "First paragraph of 'Filter issues' section in Support pane" }, "supportS3P2": { @@ -936,7 +936,7 @@ "description": "Second paragraph of 'Filter issues' section in Support pane" }, "supportS3P3": { - "message": "Tips: Be sure your filter lists are up to date. The logger is the primary tool to diagnose filter-related issues.", + "message": "نکته‌ها: مطمئن شوید که فهرست‌های فیلتر شما به‌روز هستند. گزارشگر ابزار اصلی برای تشخیص مشکلات مربوط به فیلترهاست.", "description": "Third paragraph of 'Filter issues' section in Support pane" }, "supportS4H": { @@ -944,7 +944,7 @@ "description": "Header of 'Bug report' section in Support pane" }, "supportS4P1": { - "message": "Report issues with uBlock Origin itself to the uBlockOrigin/uBlock-issue issue tracker. Requires a GitHub account.", + "message": "مشکلات مربوط به خود uBlock Origin را به uBlockOrigin/uBlock-issue گزارش دهید. نیاز به یک حساب کاربری GitHub دارد.", "description": "First paragraph of 'Bug report' section in Support pane" }, "supportS5H": { @@ -972,7 +972,7 @@ "description": "A paragraph in the filter issue reporter section" }, "supportS6P2S2": { - "message": "Verify that the issue still exists after reloading the problematic webpage.", + "message": "تأیید کنید که مشکل پس از بارگذاری مجدد صفحهٔ مشکل‌دار همچنان وجود دارد.", "description": "A paragraph in the filter issue reporter section" }, "supportS6URL": { @@ -992,7 +992,7 @@ "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option2": { - "message": "Has overlays or other nuisances", + "message": "دارای لایه‌های اضافی یا مشکلات مزاحم دیگر", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option3": { @@ -1012,7 +1012,7 @@ "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option7": { - "message": "Leads to badware, phishing", + "message": "منجر به بدافزار یا فیشینگ می‌شود", "description": "An entry in the widget used to select the type of issue" }, "supportS6Checkbox1": { @@ -1264,7 +1264,7 @@ "description": "Label for buttons used to select all text in editor" }, "toggleCosmeticFiltering": { - "message": "Toggle cosmetic filtering", + "message": "فیلترگذاری ظاهری را فعال/غیرفعال کنید", "description": "Label for keyboard shortcut used to toggle cosmetic filtering" }, "toggleJavascript": { @@ -1300,7 +1300,7 @@ "description": "Summary of number of errors as reported by the linter " }, "unprocessedRequestTooltip": { - "message": "Could not filter properly at browser launch. Reload the page to ensure proper filtering.", + "message": "فیلترگذاری به‌درستی در هنگام راه‌اندازی مرورگر انجام نشد. صفحه را دوباره بارگذاری کنید تا از فیلترگذاری صحیح اطمینان حاصل کنید.", "description": "A warning which will appear in the popup panel if needed" }, "dummy": { diff --git a/src/_locales/ja/messages.json b/src/_locales/ja/messages.json index 0b8c6d014a6ea..6401003460508 100644 --- a/src/_locales/ja/messages.json +++ b/src/_locales/ja/messages.json @@ -480,7 +480,7 @@ "description": "Filter lists section name" }, "3pGroupMalware": { - "message": "マルウェアドメイン", + "message": "マルウェアからの保護、セキュリティ", "description": "Filter lists section name" }, "3pGroupSocial": { @@ -540,7 +540,7 @@ "description": "Warning against copy-pasting filters from random sources" }, "1pEnableMyFiltersLabel": { - "message": "マイカスタムフィルターを有効化", + "message": "自分のカスタムフィルターを有効化", "description": "Label for the checkbox use to enable/disable 'My filters' list" }, "1pTrustMyFiltersLabel": { From d5e662a50fc68fe795ba9534d0ecf1fa712c2354 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Thu, 17 Oct 2024 12:45:04 -0400 Subject: [PATCH 0374/1099] New revision for dev build --- dist/version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dist/version b/dist/version index 9769226cad84b..e65c396f2941b 100644 --- a/dist/version +++ b/dist/version @@ -1 +1 @@ -1.60.1.14 \ No newline at end of file +1.60.1.15 \ No newline at end of file From 12435d96ccc932b608626c0931b1e524f25f530f Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Thu, 17 Oct 2024 13:05:37 -0400 Subject: [PATCH 0375/1099] Make Firefox dev build auto-update --- dist/firefox/updates.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dist/firefox/updates.json b/dist/firefox/updates.json index 79c5afb38d0dd..fc072739944bc 100644 --- a/dist/firefox/updates.json +++ b/dist/firefox/updates.json @@ -3,9 +3,9 @@ "uBlock0@raymondhill.net": { "updates": [ { - "version": "1.60.1.14", + "version": "1.60.1.15", "browser_specific_settings": { "gecko": { "strict_min_version": "78.0" } }, - "update_link": "https://github.com/gorhill/uBlock/releases/download/1.60.1b14/uBlock0_1.60.1b14.firefox.signed.xpi" + "update_link": "https://github.com/gorhill/uBlock/releases/download/1.60.1b15/uBlock0_1.60.1b15.firefox.signed.xpi" } ] } From d4f15ca6357bdd744e6078e411153157d0aa430e Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sat, 19 Oct 2024 10:26:31 -0400 Subject: [PATCH 0376/1099] [mv3] Fix regression in extended filtering with some lists Related issue: https://github.com/uBlockOrigin/uBOL-home/issues/236 Regression from: https://github.com/gorhill/uBlock/commit/58bfe4c846 Cosmetic- and scriptlet injection-based filters broke with filter lists using AdGuard's `[$domain=/.../]` syntax. Potentially affected filter lists: - AdGuard Chinese - AdGuard Turkish --- src/js/static-dnr-filtering.js | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/src/js/static-dnr-filtering.js b/src/js/static-dnr-filtering.js index ca66b861df619..2e87695bf593f 100644 --- a/src/js/static-dnr-filtering.js +++ b/src/js/static-dnr-filtering.js @@ -19,12 +19,6 @@ Home: https://github.com/gorhill/uBlock */ -'use strict'; - -/******************************************************************************/ - -import staticNetFilteringEngine from './static-net-filtering.js'; -import { LineIterator } from './text-utils.js'; import * as sfp from './static-filtering-parser.js'; import { @@ -32,6 +26,9 @@ import { CompiledListWriter, } from './static-filtering-io.js'; +import { LineIterator } from './text-utils.js'; +import staticNetFilteringEngine from './static-net-filtering.js'; + /******************************************************************************/ // http://www.cse.yorku.ca/~oz/hash.html#djb2 @@ -47,13 +44,15 @@ const hashFromStr = (type, s) => { return hash & 0xFFFFFF; }; +const isRegex = hn => hn.startsWith('/') && hn.endsWith('/'); + /******************************************************************************/ // Copied from cosmetic-filter.js for the time being to avoid unwanted // dependencies const rePlainSelector = /^[#.][\w\\-]+/; -const rePlainSelectorEx = /^[^#.\[(]+([#.][\w-]+)|([#.][\w-]+)$/; +const rePlainSelectorEx = /^[^#.[(]+([#.][\w-]+)|([#.][\w-]+)$/; const rePlainSelectorEscaped = /^[#.](?:\\[0-9A-Fa-f]+ |\\.|\w|-)+/; const reEscapeSequence = /\\([0-9A-Fa-f]+ |.)/g; @@ -106,6 +105,7 @@ function addExtendedToDNR(context, parser) { for ( const { hn, not, bad } of parser.getExtFilterDomainIterator() ) { if ( bad ) { continue; } if ( exception ) { continue; } + if ( isRegex(hn) ) { continue; } let details = context.scriptletFilters.get(argsToken); if ( details === undefined ) { context.scriptletFilters.set(argsToken, details = { args }); @@ -163,6 +163,7 @@ function addExtendedToDNR(context, parser) { }; for ( const { hn, not, bad } of parser.getExtFilterDomainIterator() ) { if ( bad ) { continue; } + if ( isRegex(hn) ) { continue; } if ( not ) { if ( rule.condition.excludedInitiatorDomains === undefined ) { rule.condition.excludedInitiatorDomains = []; @@ -235,6 +236,7 @@ function addExtendedToDNR(context, parser) { } for ( const { hn, not, bad } of parser.getExtFilterDomainIterator() ) { if ( bad ) { continue; } + if ( isRegex(hn) ) { continue; } let { compiled, exception, raw } = parser.result; if ( exception ) { continue; } let rejected; From b3408a46d16ebe5a4e001c007d63705c545a66fc Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sat, 19 Oct 2024 12:09:48 -0400 Subject: [PATCH 0377/1099] Fix spurious error message re. bootstrap() not being present Need `?.` before parenthesis for optional chaining to apply on method call. Related commit: https://github.com/gorhill/uBlock/commit/5133991f7e --- src/js/scriptlets/should-inject-contentscript.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/js/scriptlets/should-inject-contentscript.js b/src/js/scriptlets/should-inject-contentscript.js index df3378a88fb2c..2317e20688777 100644 --- a/src/js/scriptlets/should-inject-contentscript.js +++ b/src/js/scriptlets/should-inject-contentscript.js @@ -29,7 +29,7 @@ try { const status = vAPI.uBO !== true; if ( status === false && vAPI.bootstrap ) { - self.requestIdleCallback(( ) => vAPI?.bootstrap()); + self.requestIdleCallback(( ) => vAPI?.bootstrap?.()); } return status; } catch(ex) { From 2782b16ecf4e25414ca1b27a144535676a764740 Mon Sep 17 00:00:00 2001 From: Nadav Mermer Date: Thu, 4 Jul 2024 11:54:17 +0300 Subject: [PATCH 0378/1099] Allow rulesets enabled in manifest.json to stay enabled when finding language defaults --- platform/mv3/extension/js/ruleset-manager.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform/mv3/extension/js/ruleset-manager.js b/platform/mv3/extension/js/ruleset-manager.js index 749a83c708c0a..7f7c6e1eefd2b 100644 --- a/platform/mv3/extension/js/ruleset-manager.js +++ b/platform/mv3/extension/js/ruleset-manager.js @@ -428,7 +428,7 @@ async function updateDynamicRules() { /******************************************************************************/ async function defaultRulesetsFromLanguage() { - const out = [ 'default' ]; + const out = await dnr.getEnabledRulesets(); const dropCountry = lang => { const pos = lang.indexOf('-'); From 2621c908c31b70a983dacf5482600c116ee35102 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Tue, 22 Oct 2024 19:49:29 -0400 Subject: [PATCH 0379/1099] Remove `64:ff9b:` as private network block Related issue: https://github.com/uBlockOrigin/uBlock-issues/issues/3426 --- src/js/static-net-filtering.js | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/js/static-net-filtering.js b/src/js/static-net-filtering.js index 6a2f6c9c37418..23343acec60f2 100644 --- a/src/js/static-net-filtering.js +++ b/src/js/static-net-filtering.js @@ -3048,9 +3048,6 @@ class FilterIPAddress { if ( ipaddr.startsWith('::ffff:') === false ) { return false; } return this.reIPv6IPv4lan.test(ipaddr); } - if ( c0 === 0x36 /* 6 */ ) { - return ipaddr.startsWith('64:ff9b:'); - } if ( c0 === 0x66 /* f */ ) { return this.reIPv6local.test(ipaddr); } From 76041bdc6467d71bd54157656a210003214be516 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Tue, 22 Oct 2024 19:57:11 -0400 Subject: [PATCH 0380/1099] Update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 41a61ca55926c..89ec03c7681b9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,4 @@ +- [Remove `64:ff9b:` as private network block](https://github.com/gorhill/uBlock/commit/2621c908c3) - [Ensure `urlskip=` redirects only to `https:`](https://github.com/gorhill/uBlock/commit/32f27c5131) - [Add support to `urlskip=` media resources](https://github.com/gorhill/uBlock/commit/ce9fc5dc14) - [Add `-uricomponent` to `urlskip=` option](https://github.com/gorhill/uBlock/commit/01eebffc1f) From c9ebe07d394685ac176da7f3d776a4850dea383e Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Tue, 22 Oct 2024 19:57:45 -0400 Subject: [PATCH 0381/1099] New revision for dev build --- dist/version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dist/version b/dist/version index e65c396f2941b..2cd675e52b1ae 100644 --- a/dist/version +++ b/dist/version @@ -1 +1 @@ -1.60.1.15 \ No newline at end of file +1.60.1.16 \ No newline at end of file From 0851015d7d69100b05e903a472fb1bb8a785a179 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Tue, 22 Oct 2024 20:11:11 -0400 Subject: [PATCH 0382/1099] Make Firefox dev build auto-update --- dist/firefox/updates.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dist/firefox/updates.json b/dist/firefox/updates.json index fc072739944bc..c431e6bcb9016 100644 --- a/dist/firefox/updates.json +++ b/dist/firefox/updates.json @@ -3,9 +3,9 @@ "uBlock0@raymondhill.net": { "updates": [ { - "version": "1.60.1.15", + "version": "1.60.1.16", "browser_specific_settings": { "gecko": { "strict_min_version": "78.0" } }, - "update_link": "https://github.com/gorhill/uBlock/releases/download/1.60.1b15/uBlock0_1.60.1b15.firefox.signed.xpi" + "update_link": "https://github.com/gorhill/uBlock/releases/download/1.60.1b16/uBlock0_1.60.1b16.firefox.signed.xpi" } ] } From 11ca4a39239478e35605ec072fca140ac4c70d3b Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Fri, 25 Oct 2024 19:12:08 -0400 Subject: [PATCH 0383/1099] Add `trusted-set-attr` scriptlet @trustedScriptlet trusted-set-attr @description Sets the specified attribute on the specified elements. This scriptlet runs once when the page loads then afterward on DOM mutations. Reference: https://github.com/AdguardTeam/Scriptlets/blob/master/wiki/about-trusted-scriptlets.md#-%EF%B8%8F-trusted-set-attr @param selector A CSS selector for the elements to target. @param attr The name of the attribute to modify. @param value The new value of the attribute. Since the scriptlet requires a trusted source, the value can be anything. ===== Additionally, start to move scriptlets into their own source files for easier maintenance and code review. --- assets/resources/run-at.js | 64 ++++++ assets/resources/safe-self.js | 218 ++++++++++++++++++++ assets/resources/scriptlets.js | 367 +++------------------------------ assets/resources/set-attr.js | 201 ++++++++++++++++++ tools/make-mv3.sh | 2 +- 5 files changed, 512 insertions(+), 340 deletions(-) create mode 100644 assets/resources/run-at.js create mode 100644 assets/resources/safe-self.js create mode 100644 assets/resources/set-attr.js diff --git a/assets/resources/run-at.js b/assets/resources/run-at.js new file mode 100644 index 0000000000000..f9971155ae91b --- /dev/null +++ b/assets/resources/run-at.js @@ -0,0 +1,64 @@ +/******************************************************************************* + + uBlock Origin - a comprehensive, efficient content blocker + Copyright (C) 2019-present Raymond Hill + + 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/}. + + Home: https://github.com/gorhill/uBlock + + The scriptlets below are meant to be injected only into a + web page context. +*/ + +import { safeSelf } from './safe-self.js'; + +/* eslint no-prototype-builtins: 0 */ + +/******************************************************************************/ + +export function runAt(fn, when) { + const intFromReadyState = state => { + const targets = { + 'loading': 1, 'asap': 1, + 'interactive': 2, 'end': 2, '2': 2, + 'complete': 3, 'idle': 3, '3': 3, + }; + const tokens = Array.isArray(state) ? state : [ state ]; + for ( const token of tokens ) { + const prop = `${token}`; + if ( targets.hasOwnProperty(prop) === false ) { continue; } + return targets[prop]; + } + return 0; + }; + const runAt = intFromReadyState(when); + if ( intFromReadyState(document.readyState) >= runAt ) { + fn(); return; + } + const onStateChange = ( ) => { + if ( intFromReadyState(document.readyState) < runAt ) { return; } + fn(); + safe.removeEventListener.apply(document, args); + }; + const safe = safeSelf(); + const args = [ 'readystatechange', onStateChange, { capture: true } ]; + safe.addEventListener.apply(document, args); +} +runAt.details = { + name: 'run-at.fn', + dependencies: [ + safeSelf, + ], +}; diff --git a/assets/resources/safe-self.js b/assets/resources/safe-self.js new file mode 100644 index 0000000000000..33f3201e7550e --- /dev/null +++ b/assets/resources/safe-self.js @@ -0,0 +1,218 @@ +/******************************************************************************* + + uBlock Origin - a comprehensive, efficient content blocker + Copyright (C) 2019-present Raymond Hill + + 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/}. + + Home: https://github.com/gorhill/uBlock + + The scriptlets below are meant to be injected only into a + web page context. +*/ + +/******************************************************************************/ + +// Externally added to the private namespace in which scriptlets execute. +/* global scriptletGlobals */ + +export function safeSelf() { + if ( scriptletGlobals.safeSelf ) { + return scriptletGlobals.safeSelf; + } + const self = globalThis; + const safe = { + 'Array_from': Array.from, + 'Error': self.Error, + 'Function_toStringFn': self.Function.prototype.toString, + 'Function_toString': thisArg => safe.Function_toStringFn.call(thisArg), + 'Math_floor': Math.floor, + 'Math_max': Math.max, + 'Math_min': Math.min, + 'Math_random': Math.random, + 'Object': Object, + 'Object_defineProperty': Object.defineProperty.bind(Object), + 'Object_defineProperties': Object.defineProperties.bind(Object), + 'Object_fromEntries': Object.fromEntries.bind(Object), + 'Object_getOwnPropertyDescriptor': Object.getOwnPropertyDescriptor.bind(Object), + 'RegExp': self.RegExp, + 'RegExp_test': self.RegExp.prototype.test, + 'RegExp_exec': self.RegExp.prototype.exec, + 'Request_clone': self.Request.prototype.clone, + 'String_fromCharCode': String.fromCharCode, + 'XMLHttpRequest': self.XMLHttpRequest, + 'addEventListener': self.EventTarget.prototype.addEventListener, + 'removeEventListener': self.EventTarget.prototype.removeEventListener, + 'fetch': self.fetch, + 'JSON': self.JSON, + 'JSON_parseFn': self.JSON.parse, + 'JSON_stringifyFn': self.JSON.stringify, + 'JSON_parse': (...args) => safe.JSON_parseFn.call(safe.JSON, ...args), + 'JSON_stringify': (...args) => safe.JSON_stringifyFn.call(safe.JSON, ...args), + 'log': console.log.bind(console), + // Properties + logLevel: 0, + // Methods + makeLogPrefix(...args) { + return this.sendToLogger && `[${args.join(' \u205D ')}]` || ''; + }, + uboLog(...args) { + if ( this.sendToLogger === undefined ) { return; } + if ( args === undefined || args[0] === '' ) { return; } + return this.sendToLogger('info', ...args); + + }, + uboErr(...args) { + if ( this.sendToLogger === undefined ) { return; } + if ( args === undefined || args[0] === '' ) { return; } + return this.sendToLogger('error', ...args); + }, + escapeRegexChars(s) { + return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); + }, + initPattern(pattern, options = {}) { + if ( pattern === '' ) { + return { matchAll: true, expect: true }; + } + const expect = (options.canNegate !== true || pattern.startsWith('!') === false); + if ( expect === false ) { + pattern = pattern.slice(1); + } + const match = /^\/(.+)\/([gimsu]*)$/.exec(pattern); + if ( match !== null ) { + return { + re: new this.RegExp( + match[1], + match[2] || options.flags + ), + expect, + }; + } + if ( options.flags !== undefined ) { + return { + re: new this.RegExp(this.escapeRegexChars(pattern), + options.flags + ), + expect, + }; + } + return { pattern, expect }; + }, + testPattern(details, haystack) { + if ( details.matchAll ) { return true; } + if ( details.re ) { + return this.RegExp_test.call(details.re, haystack) === details.expect; + } + return haystack.includes(details.pattern) === details.expect; + }, + patternToRegex(pattern, flags = undefined, verbatim = false) { + if ( pattern === '' ) { return /^/; } + const match = /^\/(.+)\/([gimsu]*)$/.exec(pattern); + if ( match === null ) { + const reStr = this.escapeRegexChars(pattern); + return new RegExp(verbatim ? `^${reStr}$` : reStr, flags); + } + try { + return new RegExp(match[1], match[2] || undefined); + } + catch(ex) { + } + return /^/; + }, + getExtraArgs(args, offset = 0) { + const entries = args.slice(offset).reduce((out, v, i, a) => { + if ( (i & 1) === 0 ) { + const rawValue = a[i+1]; + const value = /^\d+$/.test(rawValue) + ? parseInt(rawValue, 10) + : rawValue; + out.push([ a[i], value ]); + } + return out; + }, []); + return this.Object_fromEntries(entries); + }, + onIdle(fn, options) { + if ( self.requestIdleCallback ) { + return self.requestIdleCallback(fn, options); + } + return self.requestAnimationFrame(fn); + }, + offIdle(id) { + if ( self.requestIdleCallback ) { + return self.cancelIdleCallback(id); + } + return self.cancelAnimationFrame(id); + } + }; + scriptletGlobals.safeSelf = safe; + if ( scriptletGlobals.bcSecret === undefined ) { return safe; } + // This is executed only when the logger is opened + safe.logLevel = scriptletGlobals.logLevel || 1; + let lastLogType = ''; + let lastLogText = ''; + let lastLogTime = 0; + safe.toLogText = (type, ...args) => { + if ( args.length === 0 ) { return; } + const text = `[${document.location.hostname || document.location.href}]${args.join(' ')}`; + if ( text === lastLogText && type === lastLogType ) { + if ( (Date.now() - lastLogTime) < 5000 ) { return; } + } + lastLogType = type; + lastLogText = text; + lastLogTime = Date.now(); + return text; + }; + try { + const bc = new self.BroadcastChannel(scriptletGlobals.bcSecret); + let bcBuffer = []; + safe.sendToLogger = (type, ...args) => { + const text = safe.toLogText(type, ...args); + if ( text === undefined ) { return; } + if ( bcBuffer === undefined ) { + return bc.postMessage({ what: 'messageToLogger', type, text }); + } + bcBuffer.push({ type, text }); + }; + bc.onmessage = ev => { + const msg = ev.data; + switch ( msg ) { + case 'iamready!': + if ( bcBuffer === undefined ) { break; } + bcBuffer.forEach(({ type, text }) => + bc.postMessage({ what: 'messageToLogger', type, text }) + ); + bcBuffer = undefined; + break; + case 'setScriptletLogLevelToOne': + safe.logLevel = 1; + break; + case 'setScriptletLogLevelToTwo': + safe.logLevel = 2; + break; + } + }; + bc.postMessage('areyouready?'); + } catch(_) { + safe.sendToLogger = (type, ...args) => { + const text = safe.toLogText(type, ...args); + if ( text === undefined ) { return; } + safe.log(`uBO ${text}`); + }; + } + return safe; +} +safeSelf.details = { + name: 'safe-self.fn', +}; diff --git a/assets/resources/scriptlets.js b/assets/resources/scriptlets.js index 3a7511ba9d187..1a835f7279f7f 100644 --- a/assets/resources/scriptlets.js +++ b/assets/resources/scriptlets.js @@ -22,6 +22,10 @@ web page context. */ +import { setAttr, setAttrFn, trustedSetAttr } from './set-attr.js'; +import { runAt } from './run-at.js'; +import { safeSelf } from './safe-self.js'; + /* eslint no-prototype-builtins: 0 */ // Externally added to the private namespace in which scriptlets execute. @@ -29,6 +33,30 @@ export const builtinScriptlets = []; +/******************************************************************************/ + +// Register scriptlets declared in other files. + +const registerScriptlet = fn => { + const details = fn.details; + if ( typeof details !== 'object' ) { + throw new ReferenceError('Unknown scriptlet function'); + } + details.fn = fn; + if ( Array.isArray(details.dependencies) ) { + details.dependencies.forEach((fn, i, array) => { + if ( typeof fn !== 'function' ) { return; } + array[i] = fn.details.name; + }); + } + builtinScriptlets.push(details); +}; + +registerScriptlet(safeSelf); +registerScriptlet(setAttrFn); +registerScriptlet(setAttr); +registerScriptlet(trustedSetAttr); + /******************************************************************************* Helper functions @@ -37,199 +65,6 @@ export const builtinScriptlets = []; *******************************************************************************/ -builtinScriptlets.push({ - name: 'safe-self.fn', - fn: safeSelf, -}); -function safeSelf() { - if ( scriptletGlobals.safeSelf ) { - return scriptletGlobals.safeSelf; - } - const self = globalThis; - const safe = { - 'Array_from': Array.from, - 'Error': self.Error, - 'Function_toStringFn': self.Function.prototype.toString, - 'Function_toString': thisArg => safe.Function_toStringFn.call(thisArg), - 'Math_floor': Math.floor, - 'Math_max': Math.max, - 'Math_min': Math.min, - 'Math_random': Math.random, - 'Object': Object, - 'Object_defineProperty': Object.defineProperty.bind(Object), - 'Object_defineProperties': Object.defineProperties.bind(Object), - 'Object_fromEntries': Object.fromEntries.bind(Object), - 'Object_getOwnPropertyDescriptor': Object.getOwnPropertyDescriptor.bind(Object), - 'RegExp': self.RegExp, - 'RegExp_test': self.RegExp.prototype.test, - 'RegExp_exec': self.RegExp.prototype.exec, - 'Request_clone': self.Request.prototype.clone, - 'String_fromCharCode': String.fromCharCode, - 'XMLHttpRequest': self.XMLHttpRequest, - 'addEventListener': self.EventTarget.prototype.addEventListener, - 'removeEventListener': self.EventTarget.prototype.removeEventListener, - 'fetch': self.fetch, - 'JSON': self.JSON, - 'JSON_parseFn': self.JSON.parse, - 'JSON_stringifyFn': self.JSON.stringify, - 'JSON_parse': (...args) => safe.JSON_parseFn.call(safe.JSON, ...args), - 'JSON_stringify': (...args) => safe.JSON_stringifyFn.call(safe.JSON, ...args), - 'log': console.log.bind(console), - // Properties - logLevel: 0, - // Methods - makeLogPrefix(...args) { - return this.sendToLogger && `[${args.join(' \u205D ')}]` || ''; - }, - uboLog(...args) { - if ( this.sendToLogger === undefined ) { return; } - if ( args === undefined || args[0] === '' ) { return; } - return this.sendToLogger('info', ...args); - - }, - uboErr(...args) { - if ( this.sendToLogger === undefined ) { return; } - if ( args === undefined || args[0] === '' ) { return; } - return this.sendToLogger('error', ...args); - }, - escapeRegexChars(s) { - return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); - }, - initPattern(pattern, options = {}) { - if ( pattern === '' ) { - return { matchAll: true, expect: true }; - } - const expect = (options.canNegate !== true || pattern.startsWith('!') === false); - if ( expect === false ) { - pattern = pattern.slice(1); - } - const match = /^\/(.+)\/([gimsu]*)$/.exec(pattern); - if ( match !== null ) { - return { - re: new this.RegExp( - match[1], - match[2] || options.flags - ), - expect, - }; - } - if ( options.flags !== undefined ) { - return { - re: new this.RegExp(this.escapeRegexChars(pattern), - options.flags - ), - expect, - }; - } - return { pattern, expect }; - }, - testPattern(details, haystack) { - if ( details.matchAll ) { return true; } - if ( details.re ) { - return this.RegExp_test.call(details.re, haystack) === details.expect; - } - return haystack.includes(details.pattern) === details.expect; - }, - patternToRegex(pattern, flags = undefined, verbatim = false) { - if ( pattern === '' ) { return /^/; } - const match = /^\/(.+)\/([gimsu]*)$/.exec(pattern); - if ( match === null ) { - const reStr = this.escapeRegexChars(pattern); - return new RegExp(verbatim ? `^${reStr}$` : reStr, flags); - } - try { - return new RegExp(match[1], match[2] || undefined); - } - catch(ex) { - } - return /^/; - }, - getExtraArgs(args, offset = 0) { - const entries = args.slice(offset).reduce((out, v, i, a) => { - if ( (i & 1) === 0 ) { - const rawValue = a[i+1]; - const value = /^\d+$/.test(rawValue) - ? parseInt(rawValue, 10) - : rawValue; - out.push([ a[i], value ]); - } - return out; - }, []); - return this.Object_fromEntries(entries); - }, - onIdle(fn, options) { - if ( self.requestIdleCallback ) { - return self.requestIdleCallback(fn, options); - } - return self.requestAnimationFrame(fn); - }, - offIdle(id) { - if ( self.requestIdleCallback ) { - return self.cancelIdleCallback(id); - } - return self.cancelAnimationFrame(id); - } - }; - scriptletGlobals.safeSelf = safe; - if ( scriptletGlobals.bcSecret === undefined ) { return safe; } - // This is executed only when the logger is opened - safe.logLevel = scriptletGlobals.logLevel || 1; - let lastLogType = ''; - let lastLogText = ''; - let lastLogTime = 0; - safe.toLogText = (type, ...args) => { - if ( args.length === 0 ) { return; } - const text = `[${document.location.hostname || document.location.href}]${args.join(' ')}`; - if ( text === lastLogText && type === lastLogType ) { - if ( (Date.now() - lastLogTime) < 5000 ) { return; } - } - lastLogType = type; - lastLogText = text; - lastLogTime = Date.now(); - return text; - }; - try { - const bc = new self.BroadcastChannel(scriptletGlobals.bcSecret); - let bcBuffer = []; - safe.sendToLogger = (type, ...args) => { - const text = safe.toLogText(type, ...args); - if ( text === undefined ) { return; } - if ( bcBuffer === undefined ) { - return bc.postMessage({ what: 'messageToLogger', type, text }); - } - bcBuffer.push({ type, text }); - }; - bc.onmessage = ev => { - const msg = ev.data; - switch ( msg ) { - case 'iamready!': - if ( bcBuffer === undefined ) { break; } - bcBuffer.forEach(({ type, text }) => - bc.postMessage({ what: 'messageToLogger', type, text }) - ); - bcBuffer = undefined; - break; - case 'setScriptletLogLevelToOne': - safe.logLevel = 1; - break; - case 'setScriptletLogLevelToTwo': - safe.logLevel = 2; - break; - } - }; - bc.postMessage('areyouready?'); - } catch(_) { - safe.sendToLogger = (type, ...args) => { - const text = safe.toLogText(type, ...args); - if ( text === undefined ) { return; } - safe.log(`uBO ${text}`); - }; - } - return safe; -} - -/******************************************************************************/ - builtinScriptlets.push({ name: 'get-random-token.fn', fn: getRandomToken, @@ -283,34 +118,6 @@ builtinScriptlets.push({ 'safe-self.fn', ], }); -function runAt(fn, when) { - const intFromReadyState = state => { - const targets = { - 'loading': 1, 'asap': 1, - 'interactive': 2, 'end': 2, '2': 2, - 'complete': 3, 'idle': 3, '3': 3, - }; - const tokens = Array.isArray(state) ? state : [ state ]; - for ( const token of tokens ) { - const prop = `${token}`; - if ( targets.hasOwnProperty(prop) === false ) { continue; } - return targets[prop]; - } - return 0; - }; - const runAt = intFromReadyState(when); - if ( intFromReadyState(document.readyState) >= runAt ) { - fn(); return; - } - const onStateChange = ( ) => { - if ( intFromReadyState(document.readyState) < runAt ) { return; } - fn(); - safe.removeEventListener.apply(document, args); - }; - const safe = safeSelf(); - const args = [ 'readystatechange', onStateChange, { capture: true } ]; - safe.addEventListener.apply(document, args); -} /******************************************************************************/ @@ -4119,124 +3926,6 @@ function setSessionStorageItem(key = '', value = '') { setLocalStorageItemFn('session', false, key, value); } -/******************************************************************************* - * - * @scriptlet set-attr - * - * @description - * Sets the specified attribute on the specified elements. This scriptlet runs - * once when the page loads then afterward on DOM mutations. - - * Reference: https://github.com/AdguardTeam/Scriptlets/blob/master/src/scriptlets/set-attr.js - * - * ### Syntax - * - * ```text - * example.org##+js(set-attr, selector, attr [, value]) - * ``` - * - * - `selector`: CSS selector of DOM elements for which the attribute `attr` - * must be modified. - * - `attr`: the name of the attribute to modify - * - `value`: the value to assign to the target attribute. Possible values: - * - `''`: empty string (default) - * - `true` - * - `false` - * - positive decimal integer 0 <= value < 32768 - * - `[other]`: copy the value from attribute `other` on the same element - * */ - -builtinScriptlets.push({ - name: 'set-attr.js', - fn: setAttr, - world: 'ISOLATED', - dependencies: [ - 'run-at.fn', - 'safe-self.fn', - ], -}); -function setAttr( - selector = '', - attr = '', - value = '' -) { - if ( selector === '' ) { return; } - if ( attr === '' ) { return; } - - const safe = safeSelf(); - const logPrefix = safe.makeLogPrefix('set-attr', attr, value); - const validValues = [ '', 'false', 'true' ]; - let copyFrom = ''; - - if ( validValues.includes(value.toLowerCase()) === false ) { - if ( /^\d+$/.test(value) ) { - const n = parseInt(value, 10); - if ( n >= 32768 ) { return; } - value = `${n}`; - } else if ( /^\[.+\]$/.test(value) ) { - copyFrom = value.slice(1, -1); - } else { - return; - } - } - - const extractValue = elem => { - if ( copyFrom !== '' ) { - return elem.getAttribute(copyFrom) || ''; - } - return value; - }; - - const applySetAttr = ( ) => { - const elems = []; - try { - elems.push(...document.querySelectorAll(selector)); - } - catch(ex) { - return false; - } - for ( const elem of elems ) { - const before = elem.getAttribute(attr); - const after = extractValue(elem); - if ( after === before ) { continue; } - if ( after !== '' && /^on/i.test(attr) ) { - if ( attr.toLowerCase() in elem ) { continue; } - } - elem.setAttribute(attr, after); - safe.uboLog(logPrefix, `${attr}="${after}"`); - } - return true; - }; - let observer, timer; - const onDomChanged = mutations => { - if ( timer !== undefined ) { return; } - let shouldWork = false; - for ( const mutation of mutations ) { - if ( mutation.addedNodes.length === 0 ) { continue; } - for ( const node of mutation.addedNodes ) { - if ( node.nodeType !== 1 ) { continue; } - shouldWork = true; - break; - } - if ( shouldWork ) { break; } - } - if ( shouldWork === false ) { return; } - timer = self.requestAnimationFrame(( ) => { - timer = undefined; - applySetAttr(); - }); - }; - const start = ( ) => { - if ( applySetAttr() === false ) { return; } - observer = new MutationObserver(onDomChanged); - observer.observe(document.body, { - subtree: true, - childList: true, - }); - }; - runAt(( ) => { start(); }, 'idle'); -} - /******************************************************************************* * * @scriptlet prevent-canvas diff --git a/assets/resources/set-attr.js b/assets/resources/set-attr.js new file mode 100644 index 0000000000000..cb90b51fae153 --- /dev/null +++ b/assets/resources/set-attr.js @@ -0,0 +1,201 @@ +/******************************************************************************* + + uBlock Origin - a comprehensive, efficient content blocker + Copyright (C) 2019-present Raymond Hill + + 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/}. + + Home: https://github.com/gorhill/uBlock + + The scriptlets below are meant to be injected only into a + web page context. +*/ + +import { runAt } from './run-at.js'; +import { safeSelf } from './safe-self.js'; + +/******************************************************************************/ + +export function setAttrFn( + logPrefix, + selector = '', + attr = '', + value = '' +) { + if ( selector === '' ) { return; } + if ( attr === '' ) { return; } + + const safe = safeSelf(); + const copyFrom = /^\[.+\]$/.test(value) + ? value.slice(1, -1) + : ''; + + const extractValue = elem => copyFrom !== '' + ? elem.getAttribute(copyFrom) || '' + : value; + + const applySetAttr = ( ) => { + let elems; + try { + elems = document.querySelectorAll(selector); + } catch(_) { + return false; + } + for ( const elem of elems ) { + const before = elem.getAttribute(attr); + const after = extractValue(elem); + if ( after === before ) { continue; } + if ( after !== '' && /^on/i.test(attr) ) { + if ( attr.toLowerCase() in elem ) { continue; } + } + elem.setAttribute(attr, after); + safe.uboLog(logPrefix, `${attr}="${after}"`); + } + return true; + }; + + let observer, timer; + const onDomChanged = mutations => { + if ( timer !== undefined ) { return; } + let shouldWork = false; + for ( const mutation of mutations ) { + if ( mutation.addedNodes.length === 0 ) { continue; } + for ( const node of mutation.addedNodes ) { + if ( node.nodeType !== 1 ) { continue; } + shouldWork = true; + break; + } + if ( shouldWork ) { break; } + } + if ( shouldWork === false ) { return; } + timer = self.requestAnimationFrame(( ) => { + timer = undefined; + applySetAttr(); + }); + }; + + const start = ( ) => { + if ( applySetAttr() === false ) { return; } + observer = new MutationObserver(onDomChanged); + observer.observe(document.body, { + subtree: true, + childList: true, + }); + }; + runAt(( ) => { start(); }, 'idle'); +} +setAttrFn.details = { + name: 'set-attr.fn', + dependencies: [ + runAt, + safeSelf, + ], +}; + +/** + * @scriptlet set-attr + * + * @description + * Sets the specified attribute on the specified elements. This scriptlet runs + * once when the page loads then afterward on DOM mutations. + * + * Reference: https://github.com/AdguardTeam/Scriptlets/blob/master/src/scriptlets/set-attr.js + * + * @param selector + * A CSS selector for the elements to target. + * + * @param attr + * The name of the attribute to modify. + * + * @param value + * The new value of the attribute. Supported values: + * - `''`: empty string (default) + * - `true` + * - `false` + * - positive decimal integer 0 <= value < 32768 + * - `[other]`: copy the value from attribute `other` on the same element + * + * */ + +export function setAttr( + selector = '', + attr = '', + value = '' +) { + const safe = safeSelf(); + const logPrefix = safe.makeLogPrefix('set-attr', selector, attr, value); + const validValues = [ '', 'false', 'true' ]; + + if ( validValues.includes(value.toLowerCase()) === false ) { + if ( /^\d+$/.test(value) ) { + const n = parseInt(value, 10); + if ( n >= 32768 ) { return; } + value = `${n}`; + } else if ( /^\[.+\]$/.test(value) === false ) { + return; + } + } + + setAttrFn(logPrefix, selector, attr, value); +} +setAttr.details = { + name: 'set-attr.js', + dependencies: [ + safeSelf, + setAttrFn, + ], + world: 'ISOLATED', +}; + +/** + * @trustedScriptlet trusted-set-attr + * + * @description + * Sets the specified attribute on the specified elements. This scriptlet runs + * once when the page loads then afterward on DOM mutations. + * + * Reference: https://github.com/AdguardTeam/Scriptlets/blob/master/wiki/about-trusted-scriptlets.md#-%EF%B8%8F-trusted-set-attr + * + * @param selector + * A CSS selector for the elements to target. + * + * @param attr + * The name of the attribute to modify. + * + * @param value + * The new value of the attribute. Since the scriptlet requires a trusted + * source, the value can be anything. + * + * */ + +export function trustedSetAttr( + selector = '', + attr = '', + value = '' +) { + const safe = safeSelf(); + const logPrefix = safe.makeLogPrefix('trusted-set-attr', selector, attr, value); + setAttrFn(logPrefix, selector, attr, value); +} +trustedSetAttr.details = { + name: 'trusted-set-attr.js', + requiresTrust: true, + dependencies: [ + safeSelf, + setAttrFn, + ], + world: 'ISOLATED', +}; + +/******************************************************************************/ diff --git a/tools/make-mv3.sh b/tools/make-mv3.sh index a4c20c48e5737..3bb3c9ce4ae67 100755 --- a/tools/make-mv3.sh +++ b/tools/make-mv3.sh @@ -109,7 +109,7 @@ if [ "$QUICK" != "yes" ]; then cp platform/mv3/*.mjs "$TMPDIR"/ cp platform/mv3/extension/js/utils.js "$TMPDIR"/js/ cp "$UBO_DIR"/assets/assets.json "$TMPDIR"/ - cp "$UBO_DIR"/assets/resources/scriptlets.js "$TMPDIR"/ + cp "$UBO_DIR"/assets/resources/*.js "$TMPDIR"/ cp -R platform/mv3/scriptlets "$TMPDIR"/ mkdir -p "$TMPDIR"/web_accessible_resources cp "$UBO_DIR"/src/web_accessible_resources/* "$TMPDIR"/web_accessible_resources/ From 033314ed342a67d5b49a0731efa0a6c62ce7e49d Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Fri, 25 Oct 2024 19:25:44 -0400 Subject: [PATCH 0384/1099] Update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 89ec03c7681b9..ab2d6a45d0c45 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,4 @@ +- [Add `trusted-set-attr` scriptlet](https://github.com/gorhill/uBlock/commit/11ca4a3923) - [Remove `64:ff9b:` as private network block](https://github.com/gorhill/uBlock/commit/2621c908c3) - [Ensure `urlskip=` redirects only to `https:`](https://github.com/gorhill/uBlock/commit/32f27c5131) - [Add support to `urlskip=` media resources](https://github.com/gorhill/uBlock/commit/ce9fc5dc14) From d49062db089cd75019db776570b963337e5af5a1 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Fri, 25 Oct 2024 19:25:57 -0400 Subject: [PATCH 0385/1099] New revision for dev build --- dist/version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dist/version b/dist/version index 2cd675e52b1ae..507682f248c74 100644 --- a/dist/version +++ b/dist/version @@ -1 +1 @@ -1.60.1.16 \ No newline at end of file +1.60.1.17 \ No newline at end of file From 50785ea38b08cefa5736df0a450ab2b528efa8fa Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Fri, 25 Oct 2024 19:31:28 -0400 Subject: [PATCH 0386/1099] Make Firefox dev build auto-update --- dist/firefox/updates.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dist/firefox/updates.json b/dist/firefox/updates.json index c431e6bcb9016..52a035bb6f809 100644 --- a/dist/firefox/updates.json +++ b/dist/firefox/updates.json @@ -3,9 +3,9 @@ "uBlock0@raymondhill.net": { "updates": [ { - "version": "1.60.1.16", + "version": "1.60.1.17", "browser_specific_settings": { "gecko": { "strict_min_version": "78.0" } }, - "update_link": "https://github.com/gorhill/uBlock/releases/download/1.60.1b16/uBlock0_1.60.1b16.firefox.signed.xpi" + "update_link": "https://github.com/gorhill/uBlock/releases/download/1.60.1b17/uBlock0_1.60.1b17.firefox.signed.xpi" } ] } From d04dc4c767721fb13d91a67cb62ecad9b9219103 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sat, 26 Oct 2024 12:50:40 -0400 Subject: [PATCH 0387/1099] Add `-blocked` directive to `urlskip=` option Potentially breaking change: `urlskip=` option will no longer apply by default to blocked network requests, only network requests which are not blocked can be skipped through a `urlskip=` filter. The new `urlskip=` directive `-blocked` can be used to explicitly allow a `urlskip=` filter to also apply to blocked network requests. Example: given the filter `||example.com^`, the filter: ||example.com/path/to/tracker$urlskip=?url Will not prevent strict-blocking when navigating to: https://example.com/path/to/tracker?url=https://example.org/ However, the filter: ||example.com/path/to/tracker$urlskip=-blocked ?url Will cause the strict-blocking to be ignored and allow navigation to proceed to the URL extracted as a result of applying the `urlskip=` filter: https://example.org/ Related discussion: https://github.com/uBlockOrigin/uBlock-issues/issues/3206#issuecomment-2439627386 --- src/js/benchmarks.js | 4 ++-- src/js/pagestore.js | 2 +- src/js/static-net-filtering.js | 20 +++++++++++++++++--- 3 files changed, 20 insertions(+), 6 deletions(-) diff --git a/src/js/benchmarks.js b/src/js/benchmarks.js index 8603b8737a89c..59996b0f63746 100644 --- a/src/js/benchmarks.js +++ b/src/js/benchmarks.js @@ -191,7 +191,7 @@ export async function benchmarkStaticNetFiltering(options = {}) { removeparamCount += 1; } } - if ( sfne.urlSkip(fctxt) ) { + if ( sfne.urlSkip(fctxt, false) ) { urlskipCount += 1; } if ( fctxt.isDocument() ) { @@ -210,7 +210,7 @@ export async function benchmarkStaticNetFiltering(options = {}) { if ( sfne.redirectRequest(redirectEngine, fctxt) ) { redirectCount += 1; } - if ( fctxt.isRootDocument() && sfne.urlSkip(fctxt) ) { + if ( fctxt.isRootDocument() && sfne.urlSkip(fctxt, true) ) { urlskipCount += 1; } } diff --git a/src/js/pagestore.js b/src/js/pagestore.js index e23f0950ec8f3..e6ca91f9a3cea 100644 --- a/src/js/pagestore.js +++ b/src/js/pagestore.js @@ -943,7 +943,7 @@ const PageStore = class { staticNetFilteringEngine.filterQuery(fctxt, directives); } if ( this.urlSkippableResources.has(fctxt.itype) ) { - staticNetFilteringEngine.urlSkip(fctxt, directives); + staticNetFilteringEngine.urlSkip(fctxt, false, directives); } if ( directives.length === 0 ) { return; } if ( logger.enabled !== true ) { return; } diff --git a/src/js/static-net-filtering.js b/src/js/static-net-filtering.js index 23343acec60f2..71b67e30c6a21 100644 --- a/src/js/static-net-filtering.js +++ b/src/js/static-net-filtering.js @@ -5405,6 +5405,9 @@ StaticNetFilteringEngine.prototype.transformRequest = function(fctxt, out = []) * * `-uricomponent`: decode the current string as a URI encoded string. * + * `-blocked`: allow the redirection of blocked requests. By default, blocked + * requests can't by urlskip'ed. + * * At any given step, the currently extracted string may not necessarily be * a valid URL, and more transformation steps may be needed to obtain a valid * URL once all the steps are applied. @@ -5423,7 +5426,11 @@ StaticNetFilteringEngine.prototype.transformRequest = function(fctxt, out = []) * * */ -StaticNetFilteringEngine.prototype.urlSkip = function(fctxt, out = []) { +StaticNetFilteringEngine.prototype.urlSkip = function( + fctxt, + blocked, + out = [] +) { if ( fctxt.redirectURL !== undefined ) { return; } const directives = this.matchAndFetchModifiers(fctxt, 'urlskip'); if ( directives === undefined ) { return; } @@ -5435,7 +5442,7 @@ StaticNetFilteringEngine.prototype.urlSkip = function(fctxt, out = []) { const urlin = fctxt.url; const value = directive.value; const steps = value.includes(' ') && value.split(/ +/) || [ value ]; - const urlout = urlSkip(directive, urlin, steps); + const urlout = urlSkip(directive, urlin, blocked, steps); if ( urlout === undefined ) { continue; } if ( urlout === urlin ) { continue; } fctxt.redirectURL = urlout; @@ -5446,8 +5453,9 @@ StaticNetFilteringEngine.prototype.urlSkip = function(fctxt, out = []) { return out; }; -function urlSkip(directive, url, steps) { +function urlSkip(directive, url, blocked, steps) { try { + let redirectBlocked = false; let urlout = url; for ( const step of steps ) { const urlin = urlout; @@ -5481,6 +5489,11 @@ function urlSkip(directive, url, steps) { urlout = self.decodeURIComponent(urlin); continue; } + // Enable skip of blocked requests + if ( step === '-blocked' ) { + redirectBlocked = true; + continue; + } } // Regex extraction from first capture group if ( c0 === 0x2F ) { // / @@ -5507,6 +5520,7 @@ function urlSkip(directive, url, steps) { } const urlfinal = new URL(urlout); if ( urlfinal.protocol !== 'https:' ) { return; } + if ( blocked && redirectBlocked !== true ) { return; } return urlout; } catch(x) { } From 4cb92102c77947c9000b05b81c01d9e7b65e4801 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sat, 26 Oct 2024 14:03:58 -0400 Subject: [PATCH 0388/1099] Update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index ab2d6a45d0c45..acc25b034ab46 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,4 @@ +- [Add `-blocked` directive to `urlskip=` option](https://github.com/gorhill/uBlock/commit/d04dc4c767) - [Add `trusted-set-attr` scriptlet](https://github.com/gorhill/uBlock/commit/11ca4a3923) - [Remove `64:ff9b:` as private network block](https://github.com/gorhill/uBlock/commit/2621c908c3) - [Ensure `urlskip=` redirects only to `https:`](https://github.com/gorhill/uBlock/commit/32f27c5131) From 87716cf7814e92398e7544c2da7930302ab52119 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sat, 26 Oct 2024 14:04:55 -0400 Subject: [PATCH 0389/1099] New revision for stable release candidate --- dist/version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dist/version b/dist/version index 507682f248c74..3421f2601185b 100644 --- a/dist/version +++ b/dist/version @@ -1 +1 @@ -1.60.1.17 \ No newline at end of file +1.60.1.100 \ No newline at end of file From 6aa9391c8d853c84a61579c692706027821a2ccf Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sat, 26 Oct 2024 14:31:33 -0400 Subject: [PATCH 0390/1099] Make Firefox dev build auto-update --- dist/firefox/updates.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dist/firefox/updates.json b/dist/firefox/updates.json index 52a035bb6f809..f404906cd32d3 100644 --- a/dist/firefox/updates.json +++ b/dist/firefox/updates.json @@ -3,9 +3,9 @@ "uBlock0@raymondhill.net": { "updates": [ { - "version": "1.60.1.17", + "version": "1.60.1.100", "browser_specific_settings": { "gecko": { "strict_min_version": "78.0" } }, - "update_link": "https://github.com/gorhill/uBlock/releases/download/1.60.1b17/uBlock0_1.60.1b17.firefox.signed.xpi" + "update_link": "https://github.com/gorhill/uBlock/releases/download/1.60.1rc0/uBlock0_1.60.1rc0.firefox.signed.xpi" } ] } From 20b54185fac6f56ea871cc81554ee7ce8521d606 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sun, 27 Oct 2024 14:24:08 -0400 Subject: [PATCH 0391/1099] Offer ability to skip redirects in strict-blocked page Related discussion: https://github.com/uBlockOrigin/uBlock-issues/issues/3206#issuecomment-2439639215 If a strict-blocked page matches a `urlskip=` filter, the page will show the user the destination URL as a result of applying the `urlskip` filter should they choose to proceed with the navigation. --- src/_locales/en/messages.json | 4 ++++ src/css/document-blocked.css | 19 ++++++++++++++++++- src/document-blocked.html | 16 +++++++++------- src/js/document-blocked.js | 24 ++++++++++++++++++++---- src/js/i18n.js | 6 ++---- src/js/pagestore.js | 4 ++-- src/js/traffic.js | 12 ++++++++---- 7 files changed, 63 insertions(+), 22 deletions(-) diff --git a/src/_locales/en/messages.json b/src/_locales/en/messages.json index 433ff60772a69..de0a8af1500eb 100644 --- a/src/_locales/en/messages.json +++ b/src/_locales/en/messages.json @@ -1193,6 +1193,10 @@ "message": "Proceed", "description": "Button text to navigate to the blocked page" }, + "docblockedRedirectPrompt": { + "message": "The blocked page wants to redirect to another site. If you choose to proceed, you will navigate directly to: {{url}}", + "description": "Text warning about an incoming redirect" + }, "cloudPush": { "message": "Export to cloud storage", "description": "tooltip" diff --git a/src/css/document-blocked.css b/src/css/document-blocked.css index 62d49216a5b03..e0504230a7862 100644 --- a/src/css/document-blocked.css +++ b/src/css/document-blocked.css @@ -28,12 +28,18 @@ body { } #rootContainer { - width: min(100vw, 640px); + width: min(100%, 640px); } #rootContainer > * { margin: 0 0 var(--default-gap-xxlarge) 0; } +:root.mobile #rootContainer > * { + margin-bottom: var(--default-gap-xlarge); + } +p { + margin: 0.5em 0; + } a { text-decoration: none; } @@ -45,8 +51,12 @@ a { color: var(--accent-surface-1); fill: var(--accent-surface-1); font-size: 96px; + line-height: 1; width: 100%; } +:root.mobile #warningSign { + font-size: 64px; + } #theURL { color: var(--ink-2); padding: 0; @@ -120,6 +130,13 @@ body[dir="rtl"] #toggleParse { padding-inline-start: var(--default-gap-xsmall); } +#urlskip a { + display: block; + overflow-x: hidden; + text-overflow: ellipsis; + white-space: nowrap; + } + #actionContainer { display: flex; justify-content: space-between; diff --git a/src/document-blocked.html b/src/document-blocked.html index 80a45461afd7f..56d6a95a42b9d 100644 --- a/src/document-blocked.html +++ b/src/document-blocked.html @@ -33,6 +33,9 @@
+ +
@@ -42,13 +45,12 @@
- - +
+ diff --git a/src/js/document-blocked.js b/src/js/document-blocked.js index 59a6bc85a3533..82511436c1ceb 100644 --- a/src/js/document-blocked.js +++ b/src/js/document-blocked.js @@ -19,10 +19,8 @@ Home: https://github.com/gorhill/uBlock */ -'use strict'; - -import { i18n, i18n$ } from './i18n.js'; import { dom, qs$ } from './dom.js'; +import { i18n, i18n$ } from './i18n.js'; /******************************************************************************/ @@ -47,7 +45,7 @@ let details = {}; let lists; for ( const rawFilter in response ) { - if ( response.hasOwnProperty(rawFilter) ) { + if ( Object.prototype.hasOwnProperty.call(response, rawFilter) ) { lists = response[rawFilter]; break; } @@ -80,6 +78,24 @@ let details = {}; dom.text('#theURL > p > span:first-of-type', details.url); dom.text('#why', details.fs); +if ( typeof details.to === 'string' && details.to.length !== 0 ) { + const fragment = new DocumentFragment(); + const text = i18n$('docblockedRedirectPrompt'); + const linkPlaceholder = '{{url}}'; + let pos = text.indexOf(linkPlaceholder); + if ( pos !== -1 ) { + const link = document.createElement('a'); + link.href = link.textContent = details.to; + fragment.append( + text.slice(0, pos), + link, + text.slice(pos + linkPlaceholder.length) + ); + qs$('#urlskip').append(fragment); + dom.attr('#urlskip', 'hidden', null); + } +} + /******************************************************************************/ // https://github.com/gorhill/uBlock/issues/691 diff --git a/src/js/i18n.js b/src/js/i18n.js index 18c7e1456cd43..6ce3b5f96284c 100644 --- a/src/js/i18n.js +++ b/src/js/i18n.js @@ -19,8 +19,6 @@ Home: https://github.com/gorhill/uBlock */ -'use strict'; - /******************************************************************************/ const i18n = @@ -168,14 +166,14 @@ if ( isBackgroundProcess !== true ) { const re = /\{\{\w+\}\}/g; let textout = ''; for (;;) { - let match = re.exec(textin); + const match = re.exec(textin); if ( match === null ) { textout += textin; break; } textout += textin.slice(0, match.index); let prop = match[0].slice(2, -2); - if ( dict.hasOwnProperty(prop) ) { + if ( Object.prototype.hasOwnProperty.call(dict, prop) ) { textout += dict[prop].replace(//g, '>'); } else { diff --git a/src/js/pagestore.js b/src/js/pagestore.js index e6ca91f9a3cea..4b19f3975493c 100644 --- a/src/js/pagestore.js +++ b/src/js/pagestore.js @@ -955,8 +955,8 @@ const PageStore = class { }); } - skipMainDocument(fctxt) { - const directives = staticNetFilteringEngine.urlSkip(fctxt); + skipMainDocument(fctxt, blocked) { + const directives = staticNetFilteringEngine.urlSkip(fctxt, blocked); if ( directives === undefined ) { return; } if ( logger.enabled !== true ) { return; } fctxt.pushFilters(directives.map(a => a.logData())); diff --git a/src/js/traffic.js b/src/js/traffic.js index eab93be742432..894554a804f70 100644 --- a/src/js/traffic.js +++ b/src/js/traffic.js @@ -197,7 +197,7 @@ const onBeforeRootFrameRequest = function(fctxt) { if ( result !== 1 ) { pageStore.redirectNonBlockedRequest(fctxt); } else { - pageStore.skipMainDocument(fctxt); + pageStore.skipMainDocument(fctxt, true); } } @@ -216,16 +216,20 @@ const onBeforeRootFrameRequest = function(fctxt) { if ( result !== 1 ) { return; } // No log data means no strict blocking (because we need to report why - // the blocking occurs. + // the blocking occurs if ( logData === undefined ) { return; } // Blocked + // Find out the URL navigated to should the document not be strict-blocked + pageStore.skipMainDocument(fctxt, false); + const query = encodeURIComponent(JSON.stringify({ url: requestURL, - hn: requestHostname, dn: fctxt.getDomain() || requestHostname, - fs: logData.raw + fs: logData.raw, + hn: requestHostname, + to: fctxt.redirectURL || '', })); vAPI.tabs.replace( From 9b3e94b23ff1023c5b61344732ffd845b35df81e Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sun, 27 Oct 2024 14:46:25 -0400 Subject: [PATCH 0392/1099] Import translation work from https://crowdin.com/project/ --- platform/mv3/description/webstore.ka.txt | 2 +- .../mv3/extension/_locales/ar/messages.json | 2 +- .../mv3/extension/_locales/id/messages.json | 34 +++++++++--------- .../mv3/extension/_locales/ka/messages.json | 36 +++++++++---------- .../mv3/extension/_locales/pa/messages.json | 18 +++++----- .../mv3/extension/_locales/vi/messages.json | 6 ++-- src/_locales/ka/messages.json | 2 +- src/_locales/pa/messages.json | 2 +- src/_locales/sv/messages.json | 2 +- src/_locales/uk/messages.json | 6 ++-- 10 files changed, 55 insertions(+), 55 deletions(-) diff --git a/platform/mv3/description/webstore.ka.txt b/platform/mv3/description/webstore.ka.txt index 7fb68dd2508b8..e659ad87953ee 100644 --- a/platform/mv3/description/webstore.ka.txt +++ b/platform/mv3/description/webstore.ka.txt @@ -7,7 +7,7 @@ uBO Lite (uBOL) *ნებართვებისგან თავისუ - EasyPrivacy - Peter Lowe – სარეკლამო სერვერების სია -You can enable more rulesets by visiting the options page -- click the _Cogs_ icon in the popup panel. +შეგიძლიათ სხვა კრებულებიც აამოქმედოთ პარამეტრების გვერდიდან -- დაწკაპეთ _Cogs_ ხატულაზე ამომხტომ არეში. uBOL სრულად დეკლარაციულია, ანუ არაა საჭირო მუდმივად იყოს გაშვებული uBOL-პროცესი გასაფილტრად, CSS/JS ჩანაცვლებით შიგთავსის გაფილტვრას თავად ბრაუზერი უზრუნველყოფს ნაცვლად გაფართოებისა, რაც მეტად საიმედოა. შესაბამისად, uBOL თავად არ დატვირთავს პროცესორს/ოპერატიულს შიგთავსის შეზღუდვის დროს -- uBOL-ის შუამავალი მომსახურე პროცესი საჭიროა _მხოლოდ_ მაშინ, როცა ამომხტომ არესთან ურთიერთქმედებთ ან ცვლით პარამეტრებს. diff --git a/platform/mv3/extension/_locales/ar/messages.json b/platform/mv3/extension/_locales/ar/messages.json index d3c4d9a76f88e..76d422b078854 100644 --- a/platform/mv3/extension/_locales/ar/messages.json +++ b/platform/mv3/extension/_locales/ar/messages.json @@ -20,7 +20,7 @@ "description": "appears as tab name in dashboard" }, "aboutPageName": { - "message": "عن التطبيق", + "message": "حول البرنامج", "description": "appears as tab name in dashboard" }, "aboutPrivacyPolicy": { diff --git a/platform/mv3/extension/_locales/id/messages.json b/platform/mv3/extension/_locales/id/messages.json index 1fb1e31ffa003..806263ff961c1 100644 --- a/platform/mv3/extension/_locales/id/messages.json +++ b/platform/mv3/extension/_locales/id/messages.json @@ -32,7 +32,7 @@ "description": "Label in the popup panel for the current filtering mode" }, "popupTipReport": { - "message": "Report an issue on this website", + "message": "Laporkan masalah pada situs ini", "description": "Tooltip used for the 'chat' icon in the panel" }, "popupTipDashboard": { @@ -104,67 +104,67 @@ "description": "Shown in the About pane" }, "supportS6H": { - "message": "Report a filter issue", + "message": "Laporkan masalah filter", "description": "Header of 'Report a filter issue' section in Support pane" }, "supportS3P1": { - "message": "Report filter issues with specific websites to the uBlockOrigin/uAssets issue tracker. Requires a GitHub account.", + "message": "Laporkan masalah filter situs web tertentu ke pelacak masalah uBlockOrigin/uAssets. Membutuhkan akun GitHub.", "description": "First paragraph of 'Filter issues' section in Support pane" }, "supportS6P1S1": { - "message": "To avoid burdening volunteers with duplicate reports, please verify that the issue has not already been reported.", + "message": "Untuk menghindari membebani sukarelawan dengan laporan duplikat, harap verifikasi bahwa masalah tersebut belum dilaporkan.", "description": "A paragraph in the filter issue reporter section" }, "supportFindSpecificButton": { - "message": "Find similar reports", + "message": "Temukan laporan serupa", "description": "A clickable link in the filter issue reporter section" }, "supportS6URL": { - "message": "Address of the webpage:", + "message": "Alamat laman web:", "description": "Label for the URL of the page" }, "supportS6Select1": { - "message": "The webpage…", + "message": "Laman web…", "description": "Label for widget to select type of issue" }, "supportS6Select1Option0": { - "message": "-- Pick an entry --", + "message": "-- Pilih entri --", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option1": { - "message": "Shows ads or ad leftovers", + "message": "Menampilkan iklan atau sejenisnya", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option2": { - "message": "Has overlays or other nuisances", + "message": "Memiliki overlay atau gangguan lainnya", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option3": { - "message": "Detects uBO Lite", + "message": "Mendeteksi uBO Lite", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option4": { - "message": "Has privacy-related issues", + "message": "Memiliki masalah terkait privasi", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option5": { - "message": "Malfunctions when uBO Lite is enabled", + "message": "Malfungsi saat uBO Lite diaktifkan", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option6": { - "message": "Opens unwanted tabs or windows", + "message": "Membuka tab atau jendela yang tidak diinginkan", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option7": { - "message": "Leads to badware, phishing", + "message": "Mengarah ke perangkat lunak jahat, phishing", "description": "An entry in the widget used to select the type of issue" }, "supportS6Checkbox1": { - "message": "Label the webpage as “NSFW” (“Not Safe For Work”)", + "message": "Label laman web sebagai “NSFW” (“Not Safe For Work”)", "description": "A checkbox to use for NSFW sites" }, "supportReportSpecificButton": { - "message": "Create new report", + "message": "Buat laporan baru", "description": "Text for button which open an external webpage in Support pane" }, "firstRunSectionLabel": { diff --git a/platform/mv3/extension/_locales/ka/messages.json b/platform/mv3/extension/_locales/ka/messages.json index 0f837f0152d45..3f64b336305e0 100644 --- a/platform/mv3/extension/_locales/ka/messages.json +++ b/platform/mv3/extension/_locales/ka/messages.json @@ -32,7 +32,7 @@ "description": "Label in the popup panel for the current filtering mode" }, "popupTipReport": { - "message": "Report an issue on this website", + "message": "ამ საიტზე ხარვეზის მოხსენება", "description": "Tooltip used for the 'chat' icon in the panel" }, "popupTipDashboard": { @@ -104,67 +104,67 @@ "description": "Shown in the About pane" }, "supportS6H": { - "message": "Report a filter issue", + "message": "ფილტრის ხარვეზის მოხსენება", "description": "Header of 'Report a filter issue' section in Support pane" }, "supportS3P1": { - "message": "Report filter issues with specific websites to the uBlockOrigin/uAssets issue tracker. Requires a GitHub account.", + "message": "ცალკეულ საიტზე ფილტრების ხარვეზების მოსახსენებლად გამოიყენეთ uBlockOrigin/uAssets ხარვეზების აღსარიცხავი. დაგჭირდებათ GitHub-ანგარიში.", "description": "First paragraph of 'Filter issues' section in Support pane" }, "supportS6P1S1": { - "message": "To avoid burdening volunteers with duplicate reports, please verify that the issue has not already been reported.", + "message": "მოხალისეები რომ არ მოცდნენ ერთნაირი მოსხენებების ნახვით, გთოხვთ გადაამოწმოთ, უკვე ხომ არაა გაგზავნილი საჩივარი ამ ხარვეზზე.", "description": "A paragraph in the filter issue reporter section" }, "supportFindSpecificButton": { - "message": "Find similar reports", + "message": "იპოვეთ მსგავსი მოხსენებები", "description": "A clickable link in the filter issue reporter section" }, "supportS6URL": { - "message": "Address of the webpage:", + "message": "ვებგვერდის მისამართი:", "description": "Label for the URL of the page" }, "supportS6Select1": { - "message": "The webpage…", + "message": "ვებგვერდი...", "description": "Label for widget to select type of issue" }, "supportS6Select1Option0": { - "message": "-- Pick an entry --", + "message": "-- შეარჩიეთ --", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option1": { - "message": "Shows ads or ad leftovers", + "message": "აჩვენებს რეკლამებს ან მის ნარჩენებს", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option2": { - "message": "Has overlays or other nuisances", + "message": "ადებს შემაწუხებელ შრეებსა და მისთანებს", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option3": { - "message": "Detects uBO Lite", + "message": "ამჩნევს, რომ uBO Lite ჩართულია", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option4": { - "message": "Has privacy-related issues", + "message": " პირადულობის დაცვის ხარვეზებითაა", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option5": { - "message": "Malfunctions when uBO Lite is enabled", + "message": "შეფერხებით მუშაობს, როცა uBO Lite ჩართულია", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option6": { - "message": "Opens unwanted tabs or windows", + "message": "ხსნის არასასურველ ჩანართებს ან ფანჯრებს", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option7": { - "message": "Leads to badware, phishing", + "message": "გადადის მავნე, თაღლითურ შიგთავსზე", "description": "An entry in the widget used to select the type of issue" }, "supportS6Checkbox1": { - "message": "Label the webpage as “NSFW” (“Not Safe For Work”)", + "message": "გვერდს დაედოს მონიშვნა „NSFW“ („შეუფერებელი შიგთავსი“ (Not Safe For Work))", "description": "A checkbox to use for NSFW sites" }, "supportReportSpecificButton": { - "message": "Create new report", + "message": "შექმენით ახალი მოსხენება", "description": "Text for button which open an external webpage in Support pane" }, "firstRunSectionLabel": { @@ -216,7 +216,7 @@ "description": "A short description for the editable field which lists trusted sites" }, "noFilteringModePlaceholder": { - "message": "[hostnames only]\nexample.com\ngames.example\n...", + "message": "[მხოლოდ მისამართის საწყისი]\nexample.com\ngames.example\n…", "description": "Default text for in edit field" }, "behaviorSectionLabel": { diff --git a/platform/mv3/extension/_locales/pa/messages.json b/platform/mv3/extension/_locales/pa/messages.json index e3524501bce67..6858a0393cc7a 100644 --- a/platform/mv3/extension/_locales/pa/messages.json +++ b/platform/mv3/extension/_locales/pa/messages.json @@ -32,7 +32,7 @@ "description": "Label in the popup panel for the current filtering mode" }, "popupTipReport": { - "message": "Report an issue on this website", + "message": "ਇਸ ਵੈੱਬਸਾਈਟ ਉੱਤੇ ਮਸਲੇ ਬਾਰੇ ਰਿਪੋਰਟ ਕਰੋ", "description": "Tooltip used for the 'chat' icon in the panel" }, "popupTipDashboard": { @@ -104,7 +104,7 @@ "description": "Shown in the About pane" }, "supportS6H": { - "message": "Report a filter issue", + "message": "ਫਿਲਟਰ ਮਸਲੇ ਬਾਰੇ ਰਿਪੋਰਟ ਕਰੋ", "description": "Header of 'Report a filter issue' section in Support pane" }, "supportS3P1": { @@ -116,15 +116,15 @@ "description": "A paragraph in the filter issue reporter section" }, "supportFindSpecificButton": { - "message": "Find similar reports", + "message": "ਰਲਦੀਆਂ ਰਿਪੋਰਟਾਂ ਲੱਭੋ", "description": "A clickable link in the filter issue reporter section" }, "supportS6URL": { - "message": "Address of the webpage:", + "message": "ਵੈੱਬ-ਸਫ਼ੇ ਦਾ ਸਿਰਨਾਵਾਂ:", "description": "Label for the URL of the page" }, "supportS6Select1": { - "message": "The webpage…", + "message": "ਵੈੱਬ ਸਫ਼ਾ…", "description": "Label for widget to select type of issue" }, "supportS6Select1Option0": { @@ -132,7 +132,7 @@ "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option1": { - "message": "Shows ads or ad leftovers", + "message": "ਇਸ਼ਤਿਹਾਰ ਜਾਂ ਇਸ਼ਤਿਹਾਰ ਦੀ ਰਹਿੰਦ-ਖੂੰਦ ਦਿਖਾਉਂਦਾ ਹੈ", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option2": { @@ -144,7 +144,7 @@ "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option4": { - "message": "Has privacy-related issues", + "message": "ਪਰਦੇਦਾਰੀ ਸੰਬੰਧੀ ਮਸਲੇ ਹਨ", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option5": { @@ -152,7 +152,7 @@ "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option6": { - "message": "Opens unwanted tabs or windows", + "message": "ਬੇਲੋੜੀਆਂ ਟੈਬਾਂ ਜਾਂ ਵਿੰਡੋ ਖੋਲ੍ਹਦਾ ਹੈ", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option7": { @@ -164,7 +164,7 @@ "description": "A checkbox to use for NSFW sites" }, "supportReportSpecificButton": { - "message": "Create new report", + "message": "ਨਵੀਂ ਰਿਪੋਰਟ ਬਣਾਓ", "description": "Text for button which open an external webpage in Support pane" }, "firstRunSectionLabel": { diff --git a/platform/mv3/extension/_locales/vi/messages.json b/platform/mv3/extension/_locales/vi/messages.json index 061c7416a4398..306b1b1f46992 100644 --- a/platform/mv3/extension/_locales/vi/messages.json +++ b/platform/mv3/extension/_locales/vi/messages.json @@ -44,7 +44,7 @@ "description": "Label to be used to show popup panel sections" }, "popupLessButton": { - "message": "Ít hơn", + "message": "Thu gọn", "description": "Label to be used to hide popup panel sections" }, "3pGroupDefault": { @@ -56,7 +56,7 @@ "description": "Header for a ruleset section in 'Filter lists pane'" }, "3pGroupPrivacy": { - "message": "Riêng tư", + "message": "Bảo mật", "description": "Header for a ruleset section in 'Filter lists pane'" }, "3pGroupMalware": { @@ -84,7 +84,7 @@ "description": "English: Source code (GPLv3)" }, "aboutContributors": { - "message": "Những người đóng góp", + "message": "Người đóng góp", "description": "English: Contributors" }, "aboutSourceCode": { diff --git a/src/_locales/ka/messages.json b/src/_locales/ka/messages.json index 08b504bf58fce..82f8ca3896be2 100644 --- a/src/_locales/ka/messages.json +++ b/src/_locales/ka/messages.json @@ -1012,7 +1012,7 @@ "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option7": { - "message": "Leads to badware, phishing", + "message": "გადადის მავნე, თაღლითურ შიგთავსზე", "description": "An entry in the widget used to select the type of issue" }, "supportS6Checkbox1": { diff --git a/src/_locales/pa/messages.json b/src/_locales/pa/messages.json index e295df49b7e2e..3fb287b198e82 100644 --- a/src/_locales/pa/messages.json +++ b/src/_locales/pa/messages.json @@ -484,7 +484,7 @@ "description": "Filter lists section name" }, "3pGroupSocial": { - "message": "Social widgets", + "message": "ਸ਼ੋਸ਼ਲ ਵਿਜੈਟ", "description": "Filter lists section name" }, "3pGroupCookies": { diff --git a/src/_locales/sv/messages.json b/src/_locales/sv/messages.json index da30a923a1ff7..8b57d82fa512e 100644 --- a/src/_locales/sv/messages.json +++ b/src/_locales/sv/messages.json @@ -4,7 +4,7 @@ "description": "extension name." }, "extShortDesc": { - "message": "Äntligen en effektiv blockerare. Snäll mot både processor och minne.", + "message": "Äntligen en effektiv blockerare. Skonsam mot både processor och minne.", "description": "this will be in the Chrome web store: must be 132 characters or less" }, "dashboardName": { diff --git a/src/_locales/uk/messages.json b/src/_locales/uk/messages.json index bf8dafa5f8476..4b93c67ad9a98 100644 --- a/src/_locales/uk/messages.json +++ b/src/_locales/uk/messages.json @@ -108,7 +108,7 @@ "description": "For the new mobile-friendly popup design" }, "popupDomainsConnected_v2": { - "message": "Доменів під'єднано", + "message": "Підключені домени", "description": "For the new mobile-friendly popup design" }, "popupTipDashboard": { @@ -336,7 +336,7 @@ "description": "English: Color-blind friendly" }, "settingsAppearance": { - "message": "Вигляд", + "message": "Зовнішній вигляд", "description": "Section for controlling user interface appearance" }, "settingsThemeLabel": { @@ -344,7 +344,7 @@ "description": "Label for checkbox to enable a custom dark theme" }, "settingsThemeAccent0Label": { - "message": "Акцент кольору користувача", + "message": "Індивідуальний колір акценту", "description": "Label for checkbox to pick an accent color" }, "settingsCloudStorageEnabledPrompt": { From 60a1dea0f6d0f70667481104893e8e8fda2bf4ad Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sun, 27 Oct 2024 14:49:14 -0400 Subject: [PATCH 0393/1099] Import translation work from https://crowdin.com/project/ublock --- src/_locales/ar/messages.json | 4 ++++ src/_locales/az/messages.json | 4 ++++ src/_locales/be/messages.json | 4 ++++ src/_locales/bg/messages.json | 4 ++++ src/_locales/bn/messages.json | 4 ++++ src/_locales/br_FR/messages.json | 4 ++++ src/_locales/bs/messages.json | 4 ++++ src/_locales/ca/messages.json | 4 ++++ src/_locales/cs/messages.json | 4 ++++ src/_locales/cv/messages.json | 4 ++++ src/_locales/cy/messages.json | 4 ++++ src/_locales/da/messages.json | 4 ++++ src/_locales/de/messages.json | 4 ++++ src/_locales/el/messages.json | 4 ++++ src/_locales/en_GB/messages.json | 4 ++++ src/_locales/eo/messages.json | 4 ++++ src/_locales/es/messages.json | 4 ++++ src/_locales/et/messages.json | 4 ++++ src/_locales/eu/messages.json | 4 ++++ src/_locales/fa/messages.json | 4 ++++ src/_locales/fi/messages.json | 4 ++++ src/_locales/fil/messages.json | 4 ++++ src/_locales/fr/messages.json | 4 ++++ src/_locales/fy/messages.json | 4 ++++ src/_locales/gl/messages.json | 4 ++++ src/_locales/gu/messages.json | 4 ++++ src/_locales/he/messages.json | 4 ++++ src/_locales/hi/messages.json | 4 ++++ src/_locales/hr/messages.json | 4 ++++ src/_locales/hu/messages.json | 4 ++++ src/_locales/hy/messages.json | 4 ++++ src/_locales/id/messages.json | 4 ++++ src/_locales/it/messages.json | 4 ++++ src/_locales/ja/messages.json | 4 ++++ src/_locales/ka/messages.json | 4 ++++ src/_locales/kk/messages.json | 4 ++++ src/_locales/kn/messages.json | 4 ++++ src/_locales/ko/messages.json | 4 ++++ src/_locales/lt/messages.json | 4 ++++ src/_locales/lv/messages.json | 4 ++++ src/_locales/mk/messages.json | 4 ++++ src/_locales/ml/messages.json | 4 ++++ src/_locales/mr/messages.json | 4 ++++ src/_locales/ms/messages.json | 4 ++++ src/_locales/nb/messages.json | 4 ++++ src/_locales/nl/messages.json | 4 ++++ src/_locales/oc/messages.json | 4 ++++ src/_locales/pa/messages.json | 4 ++++ src/_locales/pl/messages.json | 4 ++++ src/_locales/pt_BR/messages.json | 4 ++++ src/_locales/pt_PT/messages.json | 4 ++++ src/_locales/ro/messages.json | 4 ++++ src/_locales/ru/messages.json | 4 ++++ src/_locales/si/messages.json | 4 ++++ src/_locales/sk/messages.json | 4 ++++ src/_locales/sl/messages.json | 4 ++++ src/_locales/so/messages.json | 4 ++++ src/_locales/sq/messages.json | 4 ++++ src/_locales/sr/messages.json | 4 ++++ src/_locales/sv/messages.json | 4 ++++ src/_locales/sw/messages.json | 4 ++++ src/_locales/ta/messages.json | 4 ++++ src/_locales/te/messages.json | 4 ++++ src/_locales/th/messages.json | 4 ++++ src/_locales/tr/messages.json | 4 ++++ src/_locales/uk/messages.json | 4 ++++ src/_locales/ur/messages.json | 4 ++++ src/_locales/vi/messages.json | 4 ++++ src/_locales/zh_CN/messages.json | 4 ++++ src/_locales/zh_TW/messages.json | 4 ++++ 70 files changed, 280 insertions(+) diff --git a/src/_locales/ar/messages.json b/src/_locales/ar/messages.json index e4f8e6505622f..ac025f2060466 100644 --- a/src/_locales/ar/messages.json +++ b/src/_locales/ar/messages.json @@ -1191,6 +1191,10 @@ "message": "تقدّم", "description": "Button text to navigate to the blocked page" }, + "docblockedRedirectPrompt": { + "message": "The blocked page wants to redirect to another site. If you choose to proceed, you will navigate directly to: {{url}}", + "description": "Text warning about an incoming redirect" + }, "cloudPush": { "message": "التصدير إلى سحابة التخزين", "description": "tooltip" diff --git a/src/_locales/az/messages.json b/src/_locales/az/messages.json index 38540ecc79e74..13549bd06b28d 100644 --- a/src/_locales/az/messages.json +++ b/src/_locales/az/messages.json @@ -1191,6 +1191,10 @@ "message": "Davam et", "description": "Button text to navigate to the blocked page" }, + "docblockedRedirectPrompt": { + "message": "The blocked page wants to redirect to another site. If you choose to proceed, you will navigate directly to: {{url}}", + "description": "Text warning about an incoming redirect" + }, "cloudPush": { "message": "Bulud yaddaşa göndər", "description": "tooltip" diff --git a/src/_locales/be/messages.json b/src/_locales/be/messages.json index 7d66c4d1e1fe3..990acacdc99d8 100644 --- a/src/_locales/be/messages.json +++ b/src/_locales/be/messages.json @@ -1191,6 +1191,10 @@ "message": "Працягнуць", "description": "Button text to navigate to the blocked page" }, + "docblockedRedirectPrompt": { + "message": "The blocked page wants to redirect to another site. If you choose to proceed, you will navigate directly to: {{url}}", + "description": "Text warning about an incoming redirect" + }, "cloudPush": { "message": "Экспартаваць у воблачнае сховішча", "description": "tooltip" diff --git a/src/_locales/bg/messages.json b/src/_locales/bg/messages.json index 086e813398ab2..01d4f48c2bcb0 100644 --- a/src/_locales/bg/messages.json +++ b/src/_locales/bg/messages.json @@ -1191,6 +1191,10 @@ "message": "Продължаване", "description": "Button text to navigate to the blocked page" }, + "docblockedRedirectPrompt": { + "message": "The blocked page wants to redirect to another site. If you choose to proceed, you will navigate directly to: {{url}}", + "description": "Text warning about an incoming redirect" + }, "cloudPush": { "message": "Изнасяне в облачно хранилище", "description": "tooltip" diff --git a/src/_locales/bn/messages.json b/src/_locales/bn/messages.json index 0c3b776afa454..13aa3ac653e1f 100644 --- a/src/_locales/bn/messages.json +++ b/src/_locales/bn/messages.json @@ -1191,6 +1191,10 @@ "message": "এগিয়ে যান", "description": "Button text to navigate to the blocked page" }, + "docblockedRedirectPrompt": { + "message": "The blocked page wants to redirect to another site. If you choose to proceed, you will navigate directly to: {{url}}", + "description": "Text warning about an incoming redirect" + }, "cloudPush": { "message": "ক্লাউড সঞ্চয়ে রপ্তানি করুন", "description": "tooltip" diff --git a/src/_locales/br_FR/messages.json b/src/_locales/br_FR/messages.json index 92c0b73ca0d75..47e133aa46fed 100644 --- a/src/_locales/br_FR/messages.json +++ b/src/_locales/br_FR/messages.json @@ -1191,6 +1191,10 @@ "message": "Kenderc'hel", "description": "Button text to navigate to the blocked page" }, + "docblockedRedirectPrompt": { + "message": "The blocked page wants to redirect to another site. If you choose to proceed, you will navigate directly to: {{url}}", + "description": "Text warning about an incoming redirect" + }, "cloudPush": { "message": "Ezporzhiañ etrezek stokañ ar goumoulenn (cloud)", "description": "tooltip" diff --git a/src/_locales/bs/messages.json b/src/_locales/bs/messages.json index d5e299aab9bf6..d8464590ffc60 100644 --- a/src/_locales/bs/messages.json +++ b/src/_locales/bs/messages.json @@ -1191,6 +1191,10 @@ "message": "Nastavi", "description": "Button text to navigate to the blocked page" }, + "docblockedRedirectPrompt": { + "message": "The blocked page wants to redirect to another site. If you choose to proceed, you will navigate directly to: {{url}}", + "description": "Text warning about an incoming redirect" + }, "cloudPush": { "message": "Izvezi u cloud pohranu", "description": "tooltip" diff --git a/src/_locales/ca/messages.json b/src/_locales/ca/messages.json index 27963d45c2ccb..0d1121f775aca 100644 --- a/src/_locales/ca/messages.json +++ b/src/_locales/ca/messages.json @@ -1191,6 +1191,10 @@ "message": "Procedeix", "description": "Button text to navigate to the blocked page" }, + "docblockedRedirectPrompt": { + "message": "The blocked page wants to redirect to another site. If you choose to proceed, you will navigate directly to: {{url}}", + "description": "Text warning about an incoming redirect" + }, "cloudPush": { "message": "Exporta a un servei al núvol", "description": "tooltip" diff --git a/src/_locales/cs/messages.json b/src/_locales/cs/messages.json index 0ceaf90e5a8e7..e583814b5d1f1 100644 --- a/src/_locales/cs/messages.json +++ b/src/_locales/cs/messages.json @@ -1191,6 +1191,10 @@ "message": "Pokračovat", "description": "Button text to navigate to the blocked page" }, + "docblockedRedirectPrompt": { + "message": "The blocked page wants to redirect to another site. If you choose to proceed, you will navigate directly to: {{url}}", + "description": "Text warning about an incoming redirect" + }, "cloudPush": { "message": "Exportovat do cloudového úložiště", "description": "tooltip" diff --git a/src/_locales/cv/messages.json b/src/_locales/cv/messages.json index 70ee6f7e21e16..b648059a7aed4 100644 --- a/src/_locales/cv/messages.json +++ b/src/_locales/cv/messages.json @@ -1191,6 +1191,10 @@ "message": "Proceed", "description": "Button text to navigate to the blocked page" }, + "docblockedRedirectPrompt": { + "message": "The blocked page wants to redirect to another site. If you choose to proceed, you will navigate directly to: {{url}}", + "description": "Text warning about an incoming redirect" + }, "cloudPush": { "message": "Export to cloud storage", "description": "tooltip" diff --git a/src/_locales/cy/messages.json b/src/_locales/cy/messages.json index d91b747e59b8d..6a327d423fa44 100644 --- a/src/_locales/cy/messages.json +++ b/src/_locales/cy/messages.json @@ -1191,6 +1191,10 @@ "message": "Parhau", "description": "Button text to navigate to the blocked page" }, + "docblockedRedirectPrompt": { + "message": "The blocked page wants to redirect to another site. If you choose to proceed, you will navigate directly to: {{url}}", + "description": "Text warning about an incoming redirect" + }, "cloudPush": { "message": "Export to cloud storage", "description": "tooltip" diff --git a/src/_locales/da/messages.json b/src/_locales/da/messages.json index 20557ad5cae5e..834c9f27a395e 100644 --- a/src/_locales/da/messages.json +++ b/src/_locales/da/messages.json @@ -1191,6 +1191,10 @@ "message": "Fortsæt", "description": "Button text to navigate to the blocked page" }, + "docblockedRedirectPrompt": { + "message": "The blocked page wants to redirect to another site. If you choose to proceed, you will navigate directly to: {{url}}", + "description": "Text warning about an incoming redirect" + }, "cloudPush": { "message": "Eksportér til Skylager", "description": "tooltip" diff --git a/src/_locales/de/messages.json b/src/_locales/de/messages.json index 7290bf620b51a..7f1eecba1d065 100644 --- a/src/_locales/de/messages.json +++ b/src/_locales/de/messages.json @@ -1191,6 +1191,10 @@ "message": "Fortfahren", "description": "Button text to navigate to the blocked page" }, + "docblockedRedirectPrompt": { + "message": "The blocked page wants to redirect to another site. If you choose to proceed, you will navigate directly to: {{url}}", + "description": "Text warning about an incoming redirect" + }, "cloudPush": { "message": "In den Cloud-Speicher exportieren", "description": "tooltip" diff --git a/src/_locales/el/messages.json b/src/_locales/el/messages.json index a382c2476a58d..2785503d83910 100644 --- a/src/_locales/el/messages.json +++ b/src/_locales/el/messages.json @@ -1191,6 +1191,10 @@ "message": "Συνέχεια", "description": "Button text to navigate to the blocked page" }, + "docblockedRedirectPrompt": { + "message": "The blocked page wants to redirect to another site. If you choose to proceed, you will navigate directly to: {{url}}", + "description": "Text warning about an incoming redirect" + }, "cloudPush": { "message": "Εξαγωγή στο cloud storage", "description": "tooltip" diff --git a/src/_locales/en_GB/messages.json b/src/_locales/en_GB/messages.json index 69334adb0d506..4b56a88ab99c9 100644 --- a/src/_locales/en_GB/messages.json +++ b/src/_locales/en_GB/messages.json @@ -1191,6 +1191,10 @@ "message": "Proceed", "description": "Button text to navigate to the blocked page" }, + "docblockedRedirectPrompt": { + "message": "The blocked page wants to redirect to another site. If you choose to proceed, you will navigate directly to: {{url}}", + "description": "Text warning about an incoming redirect" + }, "cloudPush": { "message": "Export to cloud storage", "description": "tooltip" diff --git a/src/_locales/eo/messages.json b/src/_locales/eo/messages.json index 448de1a33596f..1f00db737c90e 100644 --- a/src/_locales/eo/messages.json +++ b/src/_locales/eo/messages.json @@ -1191,6 +1191,10 @@ "message": "Procedi", "description": "Button text to navigate to the blocked page" }, + "docblockedRedirectPrompt": { + "message": "The blocked page wants to redirect to another site. If you choose to proceed, you will navigate directly to: {{url}}", + "description": "Text warning about an incoming redirect" + }, "cloudPush": { "message": "Eksporti al nuba konservado", "description": "tooltip" diff --git a/src/_locales/es/messages.json b/src/_locales/es/messages.json index 76604d8fb9f90..e1a37e59784d8 100644 --- a/src/_locales/es/messages.json +++ b/src/_locales/es/messages.json @@ -1191,6 +1191,10 @@ "message": "Continuar", "description": "Button text to navigate to the blocked page" }, + "docblockedRedirectPrompt": { + "message": "The blocked page wants to redirect to another site. If you choose to proceed, you will navigate directly to: {{url}}", + "description": "Text warning about an incoming redirect" + }, "cloudPush": { "message": "Exportar datos a la nube", "description": "tooltip" diff --git a/src/_locales/et/messages.json b/src/_locales/et/messages.json index 178950aef80b9..0884c8a186921 100644 --- a/src/_locales/et/messages.json +++ b/src/_locales/et/messages.json @@ -1191,6 +1191,10 @@ "message": "Jätka", "description": "Button text to navigate to the blocked page" }, + "docblockedRedirectPrompt": { + "message": "The blocked page wants to redirect to another site. If you choose to proceed, you will navigate directly to: {{url}}", + "description": "Text warning about an incoming redirect" + }, "cloudPush": { "message": "Ekspordi pilvehoidlasse", "description": "tooltip" diff --git a/src/_locales/eu/messages.json b/src/_locales/eu/messages.json index 302800d723aef..c99740bb9436d 100644 --- a/src/_locales/eu/messages.json +++ b/src/_locales/eu/messages.json @@ -1191,6 +1191,10 @@ "message": "Aurrera", "description": "Button text to navigate to the blocked page" }, + "docblockedRedirectPrompt": { + "message": "The blocked page wants to redirect to another site. If you choose to proceed, you will navigate directly to: {{url}}", + "description": "Text warning about an incoming redirect" + }, "cloudPush": { "message": "Esportatu hodei biltegiratzera", "description": "tooltip" diff --git a/src/_locales/fa/messages.json b/src/_locales/fa/messages.json index 366b8689317f7..654dead392add 100644 --- a/src/_locales/fa/messages.json +++ b/src/_locales/fa/messages.json @@ -1191,6 +1191,10 @@ "message": "ادامه", "description": "Button text to navigate to the blocked page" }, + "docblockedRedirectPrompt": { + "message": "The blocked page wants to redirect to another site. If you choose to proceed, you will navigate directly to: {{url}}", + "description": "Text warning about an incoming redirect" + }, "cloudPush": { "message": "صدور به فضای ذخیره سازی ابری", "description": "tooltip" diff --git a/src/_locales/fi/messages.json b/src/_locales/fi/messages.json index 8d8f045d1531b..cb3290d859c46 100644 --- a/src/_locales/fi/messages.json +++ b/src/_locales/fi/messages.json @@ -1191,6 +1191,10 @@ "message": "Jatka", "description": "Button text to navigate to the blocked page" }, + "docblockedRedirectPrompt": { + "message": "The blocked page wants to redirect to another site. If you choose to proceed, you will navigate directly to: {{url}}", + "description": "Text warning about an incoming redirect" + }, "cloudPush": { "message": "Vie pilvitallennustilaan", "description": "tooltip" diff --git a/src/_locales/fil/messages.json b/src/_locales/fil/messages.json index 58305b46858be..cc8937f5ead86 100644 --- a/src/_locales/fil/messages.json +++ b/src/_locales/fil/messages.json @@ -1191,6 +1191,10 @@ "message": "Tumuloy", "description": "Button text to navigate to the blocked page" }, + "docblockedRedirectPrompt": { + "message": "The blocked page wants to redirect to another site. If you choose to proceed, you will navigate directly to: {{url}}", + "description": "Text warning about an incoming redirect" + }, "cloudPush": { "message": "I-export sa imbakan sa cloud", "description": "tooltip" diff --git a/src/_locales/fr/messages.json b/src/_locales/fr/messages.json index 70c9d5920929f..d29b44d9685dc 100644 --- a/src/_locales/fr/messages.json +++ b/src/_locales/fr/messages.json @@ -1191,6 +1191,10 @@ "message": "Poursuivre", "description": "Button text to navigate to the blocked page" }, + "docblockedRedirectPrompt": { + "message": "The blocked page wants to redirect to another site. If you choose to proceed, you will navigate directly to: {{url}}", + "description": "Text warning about an incoming redirect" + }, "cloudPush": { "message": "Exporter vers le stockage dans le nuage", "description": "tooltip" diff --git a/src/_locales/fy/messages.json b/src/_locales/fy/messages.json index 863d058ee3762..afd16cd77172d 100644 --- a/src/_locales/fy/messages.json +++ b/src/_locales/fy/messages.json @@ -1191,6 +1191,10 @@ "message": "Trochgean", "description": "Button text to navigate to the blocked page" }, + "docblockedRedirectPrompt": { + "message": "The blocked page wants to redirect to another site. If you choose to proceed, you will navigate directly to: {{url}}", + "description": "Text warning about an incoming redirect" + }, "cloudPush": { "message": "Eksportearje nei cloudûnthâld", "description": "tooltip" diff --git a/src/_locales/gl/messages.json b/src/_locales/gl/messages.json index f09c526ef8dee..3d6596c00573a 100644 --- a/src/_locales/gl/messages.json +++ b/src/_locales/gl/messages.json @@ -1191,6 +1191,10 @@ "message": "Proceder", "description": "Button text to navigate to the blocked page" }, + "docblockedRedirectPrompt": { + "message": "The blocked page wants to redirect to another site. If you choose to proceed, you will navigate directly to: {{url}}", + "description": "Text warning about an incoming redirect" + }, "cloudPush": { "message": "Exportar ó almacenamento na nube", "description": "tooltip" diff --git a/src/_locales/gu/messages.json b/src/_locales/gu/messages.json index b2c8b432116e8..ef8377fd70c8f 100644 --- a/src/_locales/gu/messages.json +++ b/src/_locales/gu/messages.json @@ -1191,6 +1191,10 @@ "message": "Proceed", "description": "Button text to navigate to the blocked page" }, + "docblockedRedirectPrompt": { + "message": "The blocked page wants to redirect to another site. If you choose to proceed, you will navigate directly to: {{url}}", + "description": "Text warning about an incoming redirect" + }, "cloudPush": { "message": "Export to cloud storage", "description": "tooltip" diff --git a/src/_locales/he/messages.json b/src/_locales/he/messages.json index a9bf5714bc254..4d5f023f07dce 100644 --- a/src/_locales/he/messages.json +++ b/src/_locales/he/messages.json @@ -1191,6 +1191,10 @@ "message": "המשך", "description": "Button text to navigate to the blocked page" }, + "docblockedRedirectPrompt": { + "message": "The blocked page wants to redirect to another site. If you choose to proceed, you will navigate directly to: {{url}}", + "description": "Text warning about an incoming redirect" + }, "cloudPush": { "message": "ייצא לאחסון ענן", "description": "tooltip" diff --git a/src/_locales/hi/messages.json b/src/_locales/hi/messages.json index 1a26f87293c52..34b7c17617134 100644 --- a/src/_locales/hi/messages.json +++ b/src/_locales/hi/messages.json @@ -1191,6 +1191,10 @@ "message": "आगे बढ़ें", "description": "Button text to navigate to the blocked page" }, + "docblockedRedirectPrompt": { + "message": "The blocked page wants to redirect to another site. If you choose to proceed, you will navigate directly to: {{url}}", + "description": "Text warning about an incoming redirect" + }, "cloudPush": { "message": "क्लाउड स्टोरेज में भेजें", "description": "tooltip" diff --git a/src/_locales/hr/messages.json b/src/_locales/hr/messages.json index e7a831de98333..87e66e36aeee2 100644 --- a/src/_locales/hr/messages.json +++ b/src/_locales/hr/messages.json @@ -1191,6 +1191,10 @@ "message": "Nastavi", "description": "Button text to navigate to the blocked page" }, + "docblockedRedirectPrompt": { + "message": "The blocked page wants to redirect to another site. If you choose to proceed, you will navigate directly to: {{url}}", + "description": "Text warning about an incoming redirect" + }, "cloudPush": { "message": "Izvezi u pohranu u oblaku", "description": "tooltip" diff --git a/src/_locales/hu/messages.json b/src/_locales/hu/messages.json index e80959222fc59..6408662920d50 100644 --- a/src/_locales/hu/messages.json +++ b/src/_locales/hu/messages.json @@ -1191,6 +1191,10 @@ "message": "Továbblépés", "description": "Button text to navigate to the blocked page" }, + "docblockedRedirectPrompt": { + "message": "The blocked page wants to redirect to another site. If you choose to proceed, you will navigate directly to: {{url}}", + "description": "Text warning about an incoming redirect" + }, "cloudPush": { "message": "Exportálás a felhőszolgáltatásba", "description": "tooltip" diff --git a/src/_locales/hy/messages.json b/src/_locales/hy/messages.json index 7956bce8736f7..e76041419c9c7 100644 --- a/src/_locales/hy/messages.json +++ b/src/_locales/hy/messages.json @@ -1191,6 +1191,10 @@ "message": "Շարունակել", "description": "Button text to navigate to the blocked page" }, + "docblockedRedirectPrompt": { + "message": "The blocked page wants to redirect to another site. If you choose to proceed, you will navigate directly to: {{url}}", + "description": "Text warning about an incoming redirect" + }, "cloudPush": { "message": "Արտահանել առ ամպային պահեստ", "description": "tooltip" diff --git a/src/_locales/id/messages.json b/src/_locales/id/messages.json index 955931f6adbc2..493b6210bbe57 100644 --- a/src/_locales/id/messages.json +++ b/src/_locales/id/messages.json @@ -1191,6 +1191,10 @@ "message": "Lanjutkan", "description": "Button text to navigate to the blocked page" }, + "docblockedRedirectPrompt": { + "message": "The blocked page wants to redirect to another site. If you choose to proceed, you will navigate directly to: {{url}}", + "description": "Text warning about an incoming redirect" + }, "cloudPush": { "message": "Ekspor ke penyimpanan awan", "description": "tooltip" diff --git a/src/_locales/it/messages.json b/src/_locales/it/messages.json index 242d997e1fadd..2a325986ebf21 100644 --- a/src/_locales/it/messages.json +++ b/src/_locales/it/messages.json @@ -1191,6 +1191,10 @@ "message": "Procedi", "description": "Button text to navigate to the blocked page" }, + "docblockedRedirectPrompt": { + "message": "The blocked page wants to redirect to another site. If you choose to proceed, you will navigate directly to: {{url}}", + "description": "Text warning about an incoming redirect" + }, "cloudPush": { "message": "Esporta nel cloud", "description": "tooltip" diff --git a/src/_locales/ja/messages.json b/src/_locales/ja/messages.json index 6401003460508..067f6d7448577 100644 --- a/src/_locales/ja/messages.json +++ b/src/_locales/ja/messages.json @@ -1191,6 +1191,10 @@ "message": "続行する", "description": "Button text to navigate to the blocked page" }, + "docblockedRedirectPrompt": { + "message": "The blocked page wants to redirect to another site. If you choose to proceed, you will navigate directly to: {{url}}", + "description": "Text warning about an incoming redirect" + }, "cloudPush": { "message": "クラウドストレージにエクスポートします", "description": "tooltip" diff --git a/src/_locales/ka/messages.json b/src/_locales/ka/messages.json index 82f8ca3896be2..7655d2857e9dc 100644 --- a/src/_locales/ka/messages.json +++ b/src/_locales/ka/messages.json @@ -1191,6 +1191,10 @@ "message": "მაინც გადასვლა", "description": "Button text to navigate to the blocked page" }, + "docblockedRedirectPrompt": { + "message": "The blocked page wants to redirect to another site. If you choose to proceed, you will navigate directly to: {{url}}", + "description": "Text warning about an incoming redirect" + }, "cloudPush": { "message": "ღრუბლოვან საცავში შენახვა", "description": "tooltip" diff --git a/src/_locales/kk/messages.json b/src/_locales/kk/messages.json index 55f5653a015ae..a352345886b29 100644 --- a/src/_locales/kk/messages.json +++ b/src/_locales/kk/messages.json @@ -1191,6 +1191,10 @@ "message": "Proceed", "description": "Button text to navigate to the blocked page" }, + "docblockedRedirectPrompt": { + "message": "The blocked page wants to redirect to another site. If you choose to proceed, you will navigate directly to: {{url}}", + "description": "Text warning about an incoming redirect" + }, "cloudPush": { "message": "Бұлтты жадқа экспорттау", "description": "tooltip" diff --git a/src/_locales/kn/messages.json b/src/_locales/kn/messages.json index 27374f2fca4e5..84394e4656a4f 100644 --- a/src/_locales/kn/messages.json +++ b/src/_locales/kn/messages.json @@ -1191,6 +1191,10 @@ "message": "Proceed", "description": "Button text to navigate to the blocked page" }, + "docblockedRedirectPrompt": { + "message": "The blocked page wants to redirect to another site. If you choose to proceed, you will navigate directly to: {{url}}", + "description": "Text warning about an incoming redirect" + }, "cloudPush": { "message": "Export to cloud storage", "description": "tooltip" diff --git a/src/_locales/ko/messages.json b/src/_locales/ko/messages.json index 1907260429fd3..c0e354ef7f31e 100644 --- a/src/_locales/ko/messages.json +++ b/src/_locales/ko/messages.json @@ -1191,6 +1191,10 @@ "message": "계속", "description": "Button text to navigate to the blocked page" }, + "docblockedRedirectPrompt": { + "message": "The blocked page wants to redirect to another site. If you choose to proceed, you will navigate directly to: {{url}}", + "description": "Text warning about an incoming redirect" + }, "cloudPush": { "message": "클라우드 저장소로 내보내기", "description": "tooltip" diff --git a/src/_locales/lt/messages.json b/src/_locales/lt/messages.json index 5769d17566d35..263a9d8fe6e9a 100644 --- a/src/_locales/lt/messages.json +++ b/src/_locales/lt/messages.json @@ -1191,6 +1191,10 @@ "message": "Tęsti", "description": "Button text to navigate to the blocked page" }, + "docblockedRedirectPrompt": { + "message": "The blocked page wants to redirect to another site. If you choose to proceed, you will navigate directly to: {{url}}", + "description": "Text warning about an incoming redirect" + }, "cloudPush": { "message": "Eksportuoti į nuotolinę saugyklą", "description": "tooltip" diff --git a/src/_locales/lv/messages.json b/src/_locales/lv/messages.json index f0359c580da59..b9f696cd0e5da 100644 --- a/src/_locales/lv/messages.json +++ b/src/_locales/lv/messages.json @@ -1191,6 +1191,10 @@ "message": "Turpināt", "description": "Button text to navigate to the blocked page" }, + "docblockedRedirectPrompt": { + "message": "The blocked page wants to redirect to another site. If you choose to proceed, you will navigate directly to: {{url}}", + "description": "Text warning about an incoming redirect" + }, "cloudPush": { "message": "Eksports uz mākoņdatu glabātuvi", "description": "tooltip" diff --git a/src/_locales/mk/messages.json b/src/_locales/mk/messages.json index 2eedde6695197..35c5285872a98 100644 --- a/src/_locales/mk/messages.json +++ b/src/_locales/mk/messages.json @@ -1191,6 +1191,10 @@ "message": "Proceed", "description": "Button text to navigate to the blocked page" }, + "docblockedRedirectPrompt": { + "message": "The blocked page wants to redirect to another site. If you choose to proceed, you will navigate directly to: {{url}}", + "description": "Text warning about an incoming redirect" + }, "cloudPush": { "message": "Export to cloud storage", "description": "tooltip" diff --git a/src/_locales/ml/messages.json b/src/_locales/ml/messages.json index 584f165d68b16..1bb8d1e866f65 100644 --- a/src/_locales/ml/messages.json +++ b/src/_locales/ml/messages.json @@ -1191,6 +1191,10 @@ "message": "Proceed", "description": "Button text to navigate to the blocked page" }, + "docblockedRedirectPrompt": { + "message": "The blocked page wants to redirect to another site. If you choose to proceed, you will navigate directly to: {{url}}", + "description": "Text warning about an incoming redirect" + }, "cloudPush": { "message": "ക്ലൌഡ് സ്റ്റോറേജിലേക്ക് എക്സ്പോര്‍ട്ട്‌ ചെയ്യുക", "description": "tooltip" diff --git a/src/_locales/mr/messages.json b/src/_locales/mr/messages.json index e0bc26d1ec638..56407af1acfa3 100644 --- a/src/_locales/mr/messages.json +++ b/src/_locales/mr/messages.json @@ -1191,6 +1191,10 @@ "message": "Proceed", "description": "Button text to navigate to the blocked page" }, + "docblockedRedirectPrompt": { + "message": "The blocked page wants to redirect to another site. If you choose to proceed, you will navigate directly to: {{url}}", + "description": "Text warning about an incoming redirect" + }, "cloudPush": { "message": "Export to cloud storage", "description": "tooltip" diff --git a/src/_locales/ms/messages.json b/src/_locales/ms/messages.json index 88e52805548bd..de9b9cdee9c4d 100644 --- a/src/_locales/ms/messages.json +++ b/src/_locales/ms/messages.json @@ -1191,6 +1191,10 @@ "message": "Teruskan", "description": "Button text to navigate to the blocked page" }, + "docblockedRedirectPrompt": { + "message": "The blocked page wants to redirect to another site. If you choose to proceed, you will navigate directly to: {{url}}", + "description": "Text warning about an incoming redirect" + }, "cloudPush": { "message": "Eksport ke storan awan", "description": "tooltip" diff --git a/src/_locales/nb/messages.json b/src/_locales/nb/messages.json index 634fcca615fe2..912dbf4d127b6 100644 --- a/src/_locales/nb/messages.json +++ b/src/_locales/nb/messages.json @@ -1191,6 +1191,10 @@ "message": "Fortsett", "description": "Button text to navigate to the blocked page" }, + "docblockedRedirectPrompt": { + "message": "The blocked page wants to redirect to another site. If you choose to proceed, you will navigate directly to: {{url}}", + "description": "Text warning about an incoming redirect" + }, "cloudPush": { "message": "Eksporter til nettlagring", "description": "tooltip" diff --git a/src/_locales/nl/messages.json b/src/_locales/nl/messages.json index 49a3af00b2fe9..acf9375cae478 100644 --- a/src/_locales/nl/messages.json +++ b/src/_locales/nl/messages.json @@ -1191,6 +1191,10 @@ "message": "Doorgaan", "description": "Button text to navigate to the blocked page" }, + "docblockedRedirectPrompt": { + "message": "The blocked page wants to redirect to another site. If you choose to proceed, you will navigate directly to: {{url}}", + "description": "Text warning about an incoming redirect" + }, "cloudPush": { "message": "Exporteren naar cloudopslag", "description": "tooltip" diff --git a/src/_locales/oc/messages.json b/src/_locales/oc/messages.json index 8dd23f9833000..1b4b4c0d251de 100644 --- a/src/_locales/oc/messages.json +++ b/src/_locales/oc/messages.json @@ -1191,6 +1191,10 @@ "message": "Contunhar", "description": "Button text to navigate to the blocked page" }, + "docblockedRedirectPrompt": { + "message": "The blocked page wants to redirect to another site. If you choose to proceed, you will navigate directly to: {{url}}", + "description": "Text warning about an incoming redirect" + }, "cloudPush": { "message": "Export to cloud storage", "description": "tooltip" diff --git a/src/_locales/pa/messages.json b/src/_locales/pa/messages.json index 3fb287b198e82..570ef52205114 100644 --- a/src/_locales/pa/messages.json +++ b/src/_locales/pa/messages.json @@ -1191,6 +1191,10 @@ "message": "ਜਾਰੀ ਰੱਖੋ", "description": "Button text to navigate to the blocked page" }, + "docblockedRedirectPrompt": { + "message": "The blocked page wants to redirect to another site. If you choose to proceed, you will navigate directly to: {{url}}", + "description": "Text warning about an incoming redirect" + }, "cloudPush": { "message": "ਕਲਾਉਡ ਸਟੋਰੇਜ਼ ਉੱਤੇ ਐਕਸਪੋਰਟ ਕਰੋ", "description": "tooltip" diff --git a/src/_locales/pl/messages.json b/src/_locales/pl/messages.json index b95ccce576959..598f185b3c49d 100644 --- a/src/_locales/pl/messages.json +++ b/src/_locales/pl/messages.json @@ -1191,6 +1191,10 @@ "message": "Kontynuuj", "description": "Button text to navigate to the blocked page" }, + "docblockedRedirectPrompt": { + "message": "The blocked page wants to redirect to another site. If you choose to proceed, you will navigate directly to: {{url}}", + "description": "Text warning about an incoming redirect" + }, "cloudPush": { "message": "Eksportuj do chmury", "description": "tooltip" diff --git a/src/_locales/pt_BR/messages.json b/src/_locales/pt_BR/messages.json index f1a1e3666d399..f1bf87f53b42e 100644 --- a/src/_locales/pt_BR/messages.json +++ b/src/_locales/pt_BR/messages.json @@ -1191,6 +1191,10 @@ "message": "Prosseguir", "description": "Button text to navigate to the blocked page" }, + "docblockedRedirectPrompt": { + "message": "The blocked page wants to redirect to another site. If you choose to proceed, you will navigate directly to: {{url}}", + "description": "Text warning about an incoming redirect" + }, "cloudPush": { "message": "Exportar pro armazenamento na nuvem", "description": "tooltip" diff --git a/src/_locales/pt_PT/messages.json b/src/_locales/pt_PT/messages.json index 7c4df74bc522c..79b0efb6b1868 100644 --- a/src/_locales/pt_PT/messages.json +++ b/src/_locales/pt_PT/messages.json @@ -1191,6 +1191,10 @@ "message": "Proceder", "description": "Button text to navigate to the blocked page" }, + "docblockedRedirectPrompt": { + "message": "The blocked page wants to redirect to another site. If you choose to proceed, you will navigate directly to: {{url}}", + "description": "Text warning about an incoming redirect" + }, "cloudPush": { "message": "Exportar para a nuvem", "description": "tooltip" diff --git a/src/_locales/ro/messages.json b/src/_locales/ro/messages.json index ce4595d71c440..5aacc0ff2048a 100644 --- a/src/_locales/ro/messages.json +++ b/src/_locales/ro/messages.json @@ -1191,6 +1191,10 @@ "message": "Continuă", "description": "Button text to navigate to the blocked page" }, + "docblockedRedirectPrompt": { + "message": "The blocked page wants to redirect to another site. If you choose to proceed, you will navigate directly to: {{url}}", + "description": "Text warning about an incoming redirect" + }, "cloudPush": { "message": "Exportă către stocarea în cloud", "description": "tooltip" diff --git a/src/_locales/ru/messages.json b/src/_locales/ru/messages.json index c0bfd156f5427..a4b4f92f5b271 100644 --- a/src/_locales/ru/messages.json +++ b/src/_locales/ru/messages.json @@ -1191,6 +1191,10 @@ "message": "Продолжить", "description": "Button text to navigate to the blocked page" }, + "docblockedRedirectPrompt": { + "message": "The blocked page wants to redirect to another site. If you choose to proceed, you will navigate directly to: {{url}}", + "description": "Text warning about an incoming redirect" + }, "cloudPush": { "message": "Экспорт в облачное хранилище", "description": "tooltip" diff --git a/src/_locales/si/messages.json b/src/_locales/si/messages.json index 5b1819838b86a..cd39d0ddcaa1a 100644 --- a/src/_locales/si/messages.json +++ b/src/_locales/si/messages.json @@ -1191,6 +1191,10 @@ "message": "Proceed", "description": "Button text to navigate to the blocked page" }, + "docblockedRedirectPrompt": { + "message": "The blocked page wants to redirect to another site. If you choose to proceed, you will navigate directly to: {{url}}", + "description": "Text warning about an incoming redirect" + }, "cloudPush": { "message": "මේඝ ආචයනයට නිර්යාත කරන්න", "description": "tooltip" diff --git a/src/_locales/sk/messages.json b/src/_locales/sk/messages.json index c263032e10a41..2ce73295e3636 100644 --- a/src/_locales/sk/messages.json +++ b/src/_locales/sk/messages.json @@ -1191,6 +1191,10 @@ "message": "Pokračovať", "description": "Button text to navigate to the blocked page" }, + "docblockedRedirectPrompt": { + "message": "The blocked page wants to redirect to another site. If you choose to proceed, you will navigate directly to: {{url}}", + "description": "Text warning about an incoming redirect" + }, "cloudPush": { "message": "Exportovať do cloudového úložiska", "description": "tooltip" diff --git a/src/_locales/sl/messages.json b/src/_locales/sl/messages.json index 703ce9f8732db..201ae9988a820 100644 --- a/src/_locales/sl/messages.json +++ b/src/_locales/sl/messages.json @@ -1191,6 +1191,10 @@ "message": "Proceed", "description": "Button text to navigate to the blocked page" }, + "docblockedRedirectPrompt": { + "message": "The blocked page wants to redirect to another site. If you choose to proceed, you will navigate directly to: {{url}}", + "description": "Text warning about an incoming redirect" + }, "cloudPush": { "message": "Izvozi v shrambe oblaka", "description": "tooltip" diff --git a/src/_locales/so/messages.json b/src/_locales/so/messages.json index 4276bf8c7ac68..c0ba2c7c70356 100644 --- a/src/_locales/so/messages.json +++ b/src/_locales/so/messages.json @@ -1191,6 +1191,10 @@ "message": "Proceed", "description": "Button text to navigate to the blocked page" }, + "docblockedRedirectPrompt": { + "message": "The blocked page wants to redirect to another site. If you choose to proceed, you will navigate directly to: {{url}}", + "description": "Text warning about an incoming redirect" + }, "cloudPush": { "message": "{{value}} saac kahor", "description": "tooltip" diff --git a/src/_locales/sq/messages.json b/src/_locales/sq/messages.json index cfcff08c0e3c4..629c0ce102de7 100644 --- a/src/_locales/sq/messages.json +++ b/src/_locales/sq/messages.json @@ -1191,6 +1191,10 @@ "message": "Vazhdoj", "description": "Button text to navigate to the blocked page" }, + "docblockedRedirectPrompt": { + "message": "The blocked page wants to redirect to another site. If you choose to proceed, you will navigate directly to: {{url}}", + "description": "Text warning about an incoming redirect" + }, "cloudPush": { "message": "Eksportoni në renë informatike", "description": "tooltip" diff --git a/src/_locales/sr/messages.json b/src/_locales/sr/messages.json index 734925be9e56d..bf92e441d98e2 100644 --- a/src/_locales/sr/messages.json +++ b/src/_locales/sr/messages.json @@ -1191,6 +1191,10 @@ "message": "Настави", "description": "Button text to navigate to the blocked page" }, + "docblockedRedirectPrompt": { + "message": "The blocked page wants to redirect to another site. If you choose to proceed, you will navigate directly to: {{url}}", + "description": "Text warning about an incoming redirect" + }, "cloudPush": { "message": "Извези у складиште у облаку", "description": "tooltip" diff --git a/src/_locales/sv/messages.json b/src/_locales/sv/messages.json index 8b57d82fa512e..e9f9457b56590 100644 --- a/src/_locales/sv/messages.json +++ b/src/_locales/sv/messages.json @@ -1191,6 +1191,10 @@ "message": "Fortsätt", "description": "Button text to navigate to the blocked page" }, + "docblockedRedirectPrompt": { + "message": "The blocked page wants to redirect to another site. If you choose to proceed, you will navigate directly to: {{url}}", + "description": "Text warning about an incoming redirect" + }, "cloudPush": { "message": "Exportera till molnlagring", "description": "tooltip" diff --git a/src/_locales/sw/messages.json b/src/_locales/sw/messages.json index ecb8214e3e0d6..5ecd7de52bfab 100644 --- a/src/_locales/sw/messages.json +++ b/src/_locales/sw/messages.json @@ -1191,6 +1191,10 @@ "message": "Proceed", "description": "Button text to navigate to the blocked page" }, + "docblockedRedirectPrompt": { + "message": "The blocked page wants to redirect to another site. If you choose to proceed, you will navigate directly to: {{url}}", + "description": "Text warning about an incoming redirect" + }, "cloudPush": { "message": "Hamisha hadi hifadhi ya wingu", "description": "tooltip" diff --git a/src/_locales/ta/messages.json b/src/_locales/ta/messages.json index fe1be27a9ef3c..262a988ed4256 100644 --- a/src/_locales/ta/messages.json +++ b/src/_locales/ta/messages.json @@ -1191,6 +1191,10 @@ "message": "Proceed", "description": "Button text to navigate to the blocked page" }, + "docblockedRedirectPrompt": { + "message": "The blocked page wants to redirect to another site. If you choose to proceed, you will navigate directly to: {{url}}", + "description": "Text warning about an incoming redirect" + }, "cloudPush": { "message": "மேகக்கணினி சேமிப்பகத்திற்கு ஏற்று", "description": "tooltip" diff --git a/src/_locales/te/messages.json b/src/_locales/te/messages.json index e4d15b99375c1..68ebfc80a0801 100644 --- a/src/_locales/te/messages.json +++ b/src/_locales/te/messages.json @@ -1191,6 +1191,10 @@ "message": "Proceed", "description": "Button text to navigate to the blocked page" }, + "docblockedRedirectPrompt": { + "message": "The blocked page wants to redirect to another site. If you choose to proceed, you will navigate directly to: {{url}}", + "description": "Text warning about an incoming redirect" + }, "cloudPush": { "message": "క్లౌడ్ లో పొందుపరచు", "description": "tooltip" diff --git a/src/_locales/th/messages.json b/src/_locales/th/messages.json index 8bcdce36b5c7f..55ba15a03f0a6 100644 --- a/src/_locales/th/messages.json +++ b/src/_locales/th/messages.json @@ -1191,6 +1191,10 @@ "message": "ดำเนินการ", "description": "Button text to navigate to the blocked page" }, + "docblockedRedirectPrompt": { + "message": "The blocked page wants to redirect to another site. If you choose to proceed, you will navigate directly to: {{url}}", + "description": "Text warning about an incoming redirect" + }, "cloudPush": { "message": "Export to cloud storage", "description": "tooltip" diff --git a/src/_locales/tr/messages.json b/src/_locales/tr/messages.json index 5d6544a7eb862..6191786471489 100644 --- a/src/_locales/tr/messages.json +++ b/src/_locales/tr/messages.json @@ -1191,6 +1191,10 @@ "message": "Devam et", "description": "Button text to navigate to the blocked page" }, + "docblockedRedirectPrompt": { + "message": "The blocked page wants to redirect to another site. If you choose to proceed, you will navigate directly to: {{url}}", + "description": "Text warning about an incoming redirect" + }, "cloudPush": { "message": "Bulut depolamaya aktar", "description": "tooltip" diff --git a/src/_locales/uk/messages.json b/src/_locales/uk/messages.json index 4b93c67ad9a98..aef75f9c163cc 100644 --- a/src/_locales/uk/messages.json +++ b/src/_locales/uk/messages.json @@ -1191,6 +1191,10 @@ "message": "Продовжити", "description": "Button text to navigate to the blocked page" }, + "docblockedRedirectPrompt": { + "message": "The blocked page wants to redirect to another site. If you choose to proceed, you will navigate directly to: {{url}}", + "description": "Text warning about an incoming redirect" + }, "cloudPush": { "message": "Експортувати до хмарного сховища", "description": "tooltip" diff --git a/src/_locales/ur/messages.json b/src/_locales/ur/messages.json index 4a608998bc2d0..ec794cbfb966e 100644 --- a/src/_locales/ur/messages.json +++ b/src/_locales/ur/messages.json @@ -1191,6 +1191,10 @@ "message": "Proceed", "description": "Button text to navigate to the blocked page" }, + "docblockedRedirectPrompt": { + "message": "The blocked page wants to redirect to another site. If you choose to proceed, you will navigate directly to: {{url}}", + "description": "Text warning about an incoming redirect" + }, "cloudPush": { "message": "کلاؤڈ سٹوریج میں برآمد کریں", "description": "tooltip" diff --git a/src/_locales/vi/messages.json b/src/_locales/vi/messages.json index 6c828ba4388c5..4b6559af68124 100644 --- a/src/_locales/vi/messages.json +++ b/src/_locales/vi/messages.json @@ -1191,6 +1191,10 @@ "message": "Tiếp tục", "description": "Button text to navigate to the blocked page" }, + "docblockedRedirectPrompt": { + "message": "The blocked page wants to redirect to another site. If you choose to proceed, you will navigate directly to: {{url}}", + "description": "Text warning about an incoming redirect" + }, "cloudPush": { "message": "Xuất ra lưu trữ trực tuyến", "description": "tooltip" diff --git a/src/_locales/zh_CN/messages.json b/src/_locales/zh_CN/messages.json index bd8c5513ce7a8..cfec9ea169c09 100644 --- a/src/_locales/zh_CN/messages.json +++ b/src/_locales/zh_CN/messages.json @@ -1191,6 +1191,10 @@ "message": "继续加载", "description": "Button text to navigate to the blocked page" }, + "docblockedRedirectPrompt": { + "message": "The blocked page wants to redirect to another site. If you choose to proceed, you will navigate directly to: {{url}}", + "description": "Text warning about an incoming redirect" + }, "cloudPush": { "message": "导出到云端储存", "description": "tooltip" diff --git a/src/_locales/zh_TW/messages.json b/src/_locales/zh_TW/messages.json index bdd82a0cb187d..4d8d1bcc34748 100644 --- a/src/_locales/zh_TW/messages.json +++ b/src/_locales/zh_TW/messages.json @@ -1191,6 +1191,10 @@ "message": "繼續載入", "description": "Button text to navigate to the blocked page" }, + "docblockedRedirectPrompt": { + "message": "The blocked page wants to redirect to another site. If you choose to proceed, you will navigate directly to: {{url}}", + "description": "Text warning about an incoming redirect" + }, "cloudPush": { "message": "匯出至雲端儲存空間", "description": "tooltip" From c90eab5e10d3d35e41c1e1150e47046d9e19a0b6 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sun, 27 Oct 2024 14:53:33 -0400 Subject: [PATCH 0394/1099] Update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index acc25b034ab46..9e9fa9a69ed5f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,4 @@ +- [Offer ability to skip redirects in strict-blocked page](https://github.com/gorhill/uBlock/commit/20b54185fa) - [Add `-blocked` directive to `urlskip=` option](https://github.com/gorhill/uBlock/commit/d04dc4c767) - [Add `trusted-set-attr` scriptlet](https://github.com/gorhill/uBlock/commit/11ca4a3923) - [Remove `64:ff9b:` as private network block](https://github.com/gorhill/uBlock/commit/2621c908c3) From 0face5e362323dc5a02e06febb3a78954f1d7b8f Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sun, 27 Oct 2024 14:54:03 -0400 Subject: [PATCH 0395/1099] New revision for stable release candidate --- dist/version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dist/version b/dist/version index 3421f2601185b..7ccd40de168e9 100644 --- a/dist/version +++ b/dist/version @@ -1 +1 @@ -1.60.1.100 \ No newline at end of file +1.60.1.101 \ No newline at end of file From 672f2f376901c1ab9305ccbba2574e5a3547ddf6 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sun, 27 Oct 2024 15:06:13 -0400 Subject: [PATCH 0396/1099] Make Firefox dev build auto-update --- dist/firefox/updates.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dist/firefox/updates.json b/dist/firefox/updates.json index f404906cd32d3..c0bcbc5cf9e74 100644 --- a/dist/firefox/updates.json +++ b/dist/firefox/updates.json @@ -3,9 +3,9 @@ "uBlock0@raymondhill.net": { "updates": [ { - "version": "1.60.1.100", + "version": "1.60.1.101", "browser_specific_settings": { "gecko": { "strict_min_version": "78.0" } }, - "update_link": "https://github.com/gorhill/uBlock/releases/download/1.60.1rc0/uBlock0_1.60.1rc0.firefox.signed.xpi" + "update_link": "https://github.com/gorhill/uBlock/releases/download/1.60.1rc1/uBlock0_1.60.1rc1.firefox.signed.xpi" } ] } From ac4506091e49cb1767d2eca040055c375c2f9d0f Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Mon, 28 Oct 2024 12:04:34 -0400 Subject: [PATCH 0397/1099] Import translation work from https://crowdin.com/project/ublock --- platform/mv3/extension/_locales/br_FR/messages.json | 2 +- src/_locales/be/messages.json | 2 +- src/_locales/bg/messages.json | 2 +- src/_locales/br_FR/messages.json | 2 +- src/_locales/ca/messages.json | 2 +- src/_locales/da/messages.json | 2 +- src/_locales/de/messages.json | 2 +- src/_locales/es/messages.json | 2 +- src/_locales/et/messages.json | 2 +- src/_locales/fr/messages.json | 2 +- src/_locales/fy/messages.json | 2 +- src/_locales/gl/messages.json | 2 +- src/_locales/hr/messages.json | 2 +- src/_locales/it/messages.json | 2 +- src/_locales/ja/messages.json | 2 +- src/_locales/lv/messages.json | 2 +- src/_locales/nl/messages.json | 2 +- src/_locales/pl/messages.json | 2 +- src/_locales/pt_BR/messages.json | 2 +- src/_locales/ru/messages.json | 4 ++-- src/_locales/sk/messages.json | 2 +- src/_locales/sq/messages.json | 2 +- src/_locales/tr/messages.json | 2 +- src/_locales/uk/messages.json | 2 +- src/_locales/zh_TW/messages.json | 2 +- 25 files changed, 26 insertions(+), 26 deletions(-) diff --git a/platform/mv3/extension/_locales/br_FR/messages.json b/platform/mv3/extension/_locales/br_FR/messages.json index 8bea1bb7cd344..38d5e7715ba5a 100644 --- a/platform/mv3/extension/_locales/br_FR/messages.json +++ b/platform/mv3/extension/_locales/br_FR/messages.json @@ -112,7 +112,7 @@ "description": "First paragraph of 'Filter issues' section in Support pane" }, "supportS6P1S1": { - "message": "To avoid burdening volunteers with duplicate reports, please verify that the issue has not already been reported.", + "message": "Evit nompas sammañ ar genlabourerien a-youl vat gant meur a zanevell heñvel, gwiriit ma n'eo ket bet danevellet ho kudenn en ar-raok mar plij.", "description": "A paragraph in the filter issue reporter section" }, "supportFindSpecificButton": { diff --git a/src/_locales/be/messages.json b/src/_locales/be/messages.json index 990acacdc99d8..43aef84b7a2ff 100644 --- a/src/_locales/be/messages.json +++ b/src/_locales/be/messages.json @@ -1192,7 +1192,7 @@ "description": "Button text to navigate to the blocked page" }, "docblockedRedirectPrompt": { - "message": "The blocked page wants to redirect to another site. If you choose to proceed, you will navigate directly to: {{url}}", + "message": "Заблакаваная старонка мае намер перанакіраваць на іншы сайт. Калі вырашыце працягнуць, вы пяройдзеце непасрэдна да: {{url}}", "description": "Text warning about an incoming redirect" }, "cloudPush": { diff --git a/src/_locales/bg/messages.json b/src/_locales/bg/messages.json index 01d4f48c2bcb0..c17e58fc00399 100644 --- a/src/_locales/bg/messages.json +++ b/src/_locales/bg/messages.json @@ -1192,7 +1192,7 @@ "description": "Button text to navigate to the blocked page" }, "docblockedRedirectPrompt": { - "message": "The blocked page wants to redirect to another site. If you choose to proceed, you will navigate directly to: {{url}}", + "message": "Блокираната страница иска да ви пренасочи към друг сайт. Ако изберете да продължите, ще отидете директно на: {{url}}", "description": "Text warning about an incoming redirect" }, "cloudPush": { diff --git a/src/_locales/br_FR/messages.json b/src/_locales/br_FR/messages.json index 47e133aa46fed..d0a4f82abff8a 100644 --- a/src/_locales/br_FR/messages.json +++ b/src/_locales/br_FR/messages.json @@ -1192,7 +1192,7 @@ "description": "Button text to navigate to the blocked page" }, "docblockedRedirectPrompt": { - "message": "The blocked page wants to redirect to another site. If you choose to proceed, you will navigate directly to: {{url}}", + "message": "Ar bajenn stanket a fell dezhi adkas d'ul lec'hienn all. M'ho peus c'hoant da genderc'hel e vioc'h kaset d'ar chomlec'h-mañ: {{url}}", "description": "Text warning about an incoming redirect" }, "cloudPush": { diff --git a/src/_locales/ca/messages.json b/src/_locales/ca/messages.json index 0d1121f775aca..e2f4623328e4f 100644 --- a/src/_locales/ca/messages.json +++ b/src/_locales/ca/messages.json @@ -1192,7 +1192,7 @@ "description": "Button text to navigate to the blocked page" }, "docblockedRedirectPrompt": { - "message": "The blocked page wants to redirect to another site. If you choose to proceed, you will navigate directly to: {{url}}", + "message": "La pàgina blocada vol redirigir-vos a un altre web diferent. Si continueu, si us reenviarà a: {{url}}", "description": "Text warning about an incoming redirect" }, "cloudPush": { diff --git a/src/_locales/da/messages.json b/src/_locales/da/messages.json index 834c9f27a395e..b737ac367952e 100644 --- a/src/_locales/da/messages.json +++ b/src/_locales/da/messages.json @@ -1192,7 +1192,7 @@ "description": "Button text to navigate to the blocked page" }, "docblockedRedirectPrompt": { - "message": "The blocked page wants to redirect to another site. If you choose to proceed, you will navigate directly to: {{url}}", + "message": "Den blokerede side ønsker at omdirigere til et andet websted. Vælger man at fortsætte, navigeres direkte til: {{url}}", "description": "Text warning about an incoming redirect" }, "cloudPush": { diff --git a/src/_locales/de/messages.json b/src/_locales/de/messages.json index 7f1eecba1d065..68523b1b28235 100644 --- a/src/_locales/de/messages.json +++ b/src/_locales/de/messages.json @@ -1192,7 +1192,7 @@ "description": "Button text to navigate to the blocked page" }, "docblockedRedirectPrompt": { - "message": "The blocked page wants to redirect to another site. If you choose to proceed, you will navigate directly to: {{url}}", + "message": "Die blockierte Seite möchte zu einer anderen Website weiterleiten. Wenn Sie fortfahren, wird folgende Seite aufgerufen: {{url}}", "description": "Text warning about an incoming redirect" }, "cloudPush": { diff --git a/src/_locales/es/messages.json b/src/_locales/es/messages.json index e1a37e59784d8..2f19a0836cee1 100644 --- a/src/_locales/es/messages.json +++ b/src/_locales/es/messages.json @@ -1192,7 +1192,7 @@ "description": "Button text to navigate to the blocked page" }, "docblockedRedirectPrompt": { - "message": "The blocked page wants to redirect to another site. If you choose to proceed, you will navigate directly to: {{url}}", + "message": "La página bloqueada quiere redireccionar a otro sitio web. Si eliges proceder, navegaras directamente a: {{url}}", "description": "Text warning about an incoming redirect" }, "cloudPush": { diff --git a/src/_locales/et/messages.json b/src/_locales/et/messages.json index 0884c8a186921..0829551848ef9 100644 --- a/src/_locales/et/messages.json +++ b/src/_locales/et/messages.json @@ -1192,7 +1192,7 @@ "description": "Button text to navigate to the blocked page" }, "docblockedRedirectPrompt": { - "message": "The blocked page wants to redirect to another site. If you choose to proceed, you will navigate directly to: {{url}}", + "message": "Tõkestatud veebileht üritab suunata muule veebilehele. Jätkates suunatakse teid veebilehele {{url}}", "description": "Text warning about an incoming redirect" }, "cloudPush": { diff --git a/src/_locales/fr/messages.json b/src/_locales/fr/messages.json index d29b44d9685dc..309bba8600080 100644 --- a/src/_locales/fr/messages.json +++ b/src/_locales/fr/messages.json @@ -1192,7 +1192,7 @@ "description": "Button text to navigate to the blocked page" }, "docblockedRedirectPrompt": { - "message": "The blocked page wants to redirect to another site. If you choose to proceed, you will navigate directly to: {{url}}", + "message": "La page bloquée souhaite rediriger vers un autre site. Si vous choisissez de continuer, vous vous rendrez à l'adresse suivante : {{url}}", "description": "Text warning about an incoming redirect" }, "cloudPush": { diff --git a/src/_locales/fy/messages.json b/src/_locales/fy/messages.json index afd16cd77172d..3c21e63033e23 100644 --- a/src/_locales/fy/messages.json +++ b/src/_locales/fy/messages.json @@ -1192,7 +1192,7 @@ "description": "Button text to navigate to the blocked page" }, "docblockedRedirectPrompt": { - "message": "The blocked page wants to redirect to another site. If you choose to proceed, you will navigate directly to: {{url}}", + "message": "De blokkearre side wol nei in oare website trochferwize. As jo fierder gean, wurdt de folgjende side oproppen: {{url}}", "description": "Text warning about an incoming redirect" }, "cloudPush": { diff --git a/src/_locales/gl/messages.json b/src/_locales/gl/messages.json index 3d6596c00573a..e1104c0ebe33f 100644 --- a/src/_locales/gl/messages.json +++ b/src/_locales/gl/messages.json @@ -1192,7 +1192,7 @@ "description": "Button text to navigate to the blocked page" }, "docblockedRedirectPrompt": { - "message": "The blocked page wants to redirect to another site. If you choose to proceed, you will navigate directly to: {{url}}", + "message": "A páxina bloqueada quere redirixir a outra web. Se elixes continuar vas ir directamente a: {{url}}", "description": "Text warning about an incoming redirect" }, "cloudPush": { diff --git a/src/_locales/hr/messages.json b/src/_locales/hr/messages.json index 87e66e36aeee2..68e37c5dd8eb2 100644 --- a/src/_locales/hr/messages.json +++ b/src/_locales/hr/messages.json @@ -1192,7 +1192,7 @@ "description": "Button text to navigate to the blocked page" }, "docblockedRedirectPrompt": { - "message": "The blocked page wants to redirect to another site. If you choose to proceed, you will navigate directly to: {{url}}", + "message": "Blokirana stranica želi preusmjeriti na drugu stranicu. Ako odlučite nastaviti, otići ćete izravno na: {{url}}", "description": "Text warning about an incoming redirect" }, "cloudPush": { diff --git a/src/_locales/it/messages.json b/src/_locales/it/messages.json index 2a325986ebf21..fc7b8499500bc 100644 --- a/src/_locales/it/messages.json +++ b/src/_locales/it/messages.json @@ -1192,7 +1192,7 @@ "description": "Button text to navigate to the blocked page" }, "docblockedRedirectPrompt": { - "message": "The blocked page wants to redirect to another site. If you choose to proceed, you will navigate directly to: {{url}}", + "message": "La pagina bloccata vuole reindirizzare a un altro sito. Se scegli di procedere, navigherai direttamente a: {{url}}", "description": "Text warning about an incoming redirect" }, "cloudPush": { diff --git a/src/_locales/ja/messages.json b/src/_locales/ja/messages.json index 067f6d7448577..673909dfe8f7c 100644 --- a/src/_locales/ja/messages.json +++ b/src/_locales/ja/messages.json @@ -1192,7 +1192,7 @@ "description": "Button text to navigate to the blocked page" }, "docblockedRedirectPrompt": { - "message": "The blocked page wants to redirect to another site. If you choose to proceed, you will navigate directly to: {{url}}", + "message": "ブロックしたページは別のサイトへリダイレクトしようとしています。続行すると次の URL へ移動します: {{url}}", "description": "Text warning about an incoming redirect" }, "cloudPush": { diff --git a/src/_locales/lv/messages.json b/src/_locales/lv/messages.json index b9f696cd0e5da..0775a23ec7dcc 100644 --- a/src/_locales/lv/messages.json +++ b/src/_locales/lv/messages.json @@ -1192,7 +1192,7 @@ "description": "Button text to navigate to the blocked page" }, "docblockedRedirectPrompt": { - "message": "The blocked page wants to redirect to another site. If you choose to proceed, you will navigate directly to: {{url}}", + "message": "Aizturētā lapa veic pārvirzīšanu uz citu vietni. Ja izvēlēsies turpināt, nonāksi uzreiz šeit: {{url}}", "description": "Text warning about an incoming redirect" }, "cloudPush": { diff --git a/src/_locales/nl/messages.json b/src/_locales/nl/messages.json index acf9375cae478..e0ab0747789a4 100644 --- a/src/_locales/nl/messages.json +++ b/src/_locales/nl/messages.json @@ -1192,7 +1192,7 @@ "description": "Button text to navigate to the blocked page" }, "docblockedRedirectPrompt": { - "message": "The blocked page wants to redirect to another site. If you choose to proceed, you will navigate directly to: {{url}}", + "message": "De geblokkeerde pagina wil u omleiden naar een andere website. Als u doorgaat, navigeert u rechtstreeks naar: {{url}}", "description": "Text warning about an incoming redirect" }, "cloudPush": { diff --git a/src/_locales/pl/messages.json b/src/_locales/pl/messages.json index 598f185b3c49d..4599037658dce 100644 --- a/src/_locales/pl/messages.json +++ b/src/_locales/pl/messages.json @@ -1192,7 +1192,7 @@ "description": "Button text to navigate to the blocked page" }, "docblockedRedirectPrompt": { - "message": "The blocked page wants to redirect to another site. If you choose to proceed, you will navigate directly to: {{url}}", + "message": "Zablokowana strona chce przekierować na inną witrynę. Jeśli zdecydujesz się kontynuować, przejdziesz bezpośrednio do: {{url}}", "description": "Text warning about an incoming redirect" }, "cloudPush": { diff --git a/src/_locales/pt_BR/messages.json b/src/_locales/pt_BR/messages.json index f1bf87f53b42e..7c0790dca68bb 100644 --- a/src/_locales/pt_BR/messages.json +++ b/src/_locales/pt_BR/messages.json @@ -1192,7 +1192,7 @@ "description": "Button text to navigate to the blocked page" }, "docblockedRedirectPrompt": { - "message": "The blocked page wants to redirect to another site. If you choose to proceed, you will navigate directly to: {{url}}", + "message": "A página bloqueada quer redirecionar pra outro site. Se você escolher prosseguir você navegará diretamente para: {{url}}", "description": "Text warning about an incoming redirect" }, "cloudPush": { diff --git a/src/_locales/ru/messages.json b/src/_locales/ru/messages.json index a4b4f92f5b271..8cd58ef040c49 100644 --- a/src/_locales/ru/messages.json +++ b/src/_locales/ru/messages.json @@ -1,6 +1,6 @@ { "extName": { - "message": "uBlock₀", + "message": "uBlock Origin", "description": "extension name." }, "extShortDesc": { @@ -1192,7 +1192,7 @@ "description": "Button text to navigate to the blocked page" }, "docblockedRedirectPrompt": { - "message": "The blocked page wants to redirect to another site. If you choose to proceed, you will navigate directly to: {{url}}", + "message": "Заблокированная страница собирается перенаправить вас на другой сайт. Если вы решите продолжить, вы перейдете непосредственно на: {{url}}", "description": "Text warning about an incoming redirect" }, "cloudPush": { diff --git a/src/_locales/sk/messages.json b/src/_locales/sk/messages.json index 2ce73295e3636..04d46777ce198 100644 --- a/src/_locales/sk/messages.json +++ b/src/_locales/sk/messages.json @@ -1192,7 +1192,7 @@ "description": "Button text to navigate to the blocked page" }, "docblockedRedirectPrompt": { - "message": "The blocked page wants to redirect to another site. If you choose to proceed, you will navigate directly to: {{url}}", + "message": "Zablokovaná stránka chce presmerovať na inú stránku. Ak sa rozhodnete pokračovať, prejdete priamo na: {{url}}", "description": "Text warning about an incoming redirect" }, "cloudPush": { diff --git a/src/_locales/sq/messages.json b/src/_locales/sq/messages.json index 629c0ce102de7..8fa200e0bb16b 100644 --- a/src/_locales/sq/messages.json +++ b/src/_locales/sq/messages.json @@ -1192,7 +1192,7 @@ "description": "Button text to navigate to the blocked page" }, "docblockedRedirectPrompt": { - "message": "The blocked page wants to redirect to another site. If you choose to proceed, you will navigate directly to: {{url}}", + "message": "Faqja e bllokuar do t'ju drejtojë në një uebsajt tjetër. Në rast se vijoni do të shkoni te: {{url}}", "description": "Text warning about an incoming redirect" }, "cloudPush": { diff --git a/src/_locales/tr/messages.json b/src/_locales/tr/messages.json index 6191786471489..f3d2276db5950 100644 --- a/src/_locales/tr/messages.json +++ b/src/_locales/tr/messages.json @@ -1192,7 +1192,7 @@ "description": "Button text to navigate to the blocked page" }, "docblockedRedirectPrompt": { - "message": "The blocked page wants to redirect to another site. If you choose to proceed, you will navigate directly to: {{url}}", + "message": "Engellenen sayfa sizi başka bir siteye yönlendirmek istiyor. Devam etmek isterseniz doğrudan şuraya yönlendirileceksiniz: {{url}}", "description": "Text warning about an incoming redirect" }, "cloudPush": { diff --git a/src/_locales/uk/messages.json b/src/_locales/uk/messages.json index aef75f9c163cc..aeb7440ad3adf 100644 --- a/src/_locales/uk/messages.json +++ b/src/_locales/uk/messages.json @@ -1192,7 +1192,7 @@ "description": "Button text to navigate to the blocked page" }, "docblockedRedirectPrompt": { - "message": "The blocked page wants to redirect to another site. If you choose to proceed, you will navigate directly to: {{url}}", + "message": "Заблокована сторінка хоче переадресувати на інший сайт. Якщо ви вирішите продовжити, ви перейдете безпосередньо на: {{url}}", "description": "Text warning about an incoming redirect" }, "cloudPush": { diff --git a/src/_locales/zh_TW/messages.json b/src/_locales/zh_TW/messages.json index 4d8d1bcc34748..14e980968ced6 100644 --- a/src/_locales/zh_TW/messages.json +++ b/src/_locales/zh_TW/messages.json @@ -1192,7 +1192,7 @@ "description": "Button text to navigate to the blocked page" }, "docblockedRedirectPrompt": { - "message": "The blocked page wants to redirect to another site. If you choose to proceed, you will navigate directly to: {{url}}", + "message": "被封鎖的頁面想要重新導向至另一個網站。如果您選擇繼續,則會直接前往:{{url}}", "description": "Text warning about an incoming redirect" }, "cloudPush": { From c07db7553d4990bf3e9a8b00aef4ba557a23af18 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Mon, 28 Oct 2024 14:13:50 -0400 Subject: [PATCH 0398/1099] Fix `urlskip=` with `-blocked` directive for blocked requests Related feedback: https://github.com/uBlockOrigin/uBlock-issues/issues/3206#issuecomment-2441470631 --- src/js/pagestore.js | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/js/pagestore.js b/src/js/pagestore.js index 4b19f3975493c..44a5674cbe718 100644 --- a/src/js/pagestore.js +++ b/src/js/pagestore.js @@ -925,8 +925,11 @@ const PageStore = class { } redirectBlockedRequest(fctxt) { - const directives = staticNetFilteringEngine.redirectRequest(redirectEngine, fctxt); - if ( directives === undefined ) { return; } + const directives = staticNetFilteringEngine.redirectRequest(redirectEngine, fctxt) || []; + if ( this.urlSkippableResources.has(fctxt.itype) ) { + staticNetFilteringEngine.urlSkip(fctxt, true, directives); + } + if ( directives.length === 0 ) { return; } if ( logger.enabled !== true ) { return; } fctxt.pushFilters(directives.map(a => a.logData())); if ( fctxt.redirectURL === undefined ) { return; } @@ -1152,6 +1155,7 @@ const PageStore = class { µb.FilteringContext.MAIN_FRAME, µb.FilteringContext.MEDIA, µb.FilteringContext.OBJECT, + µb.FilteringContext.OTHER, µb.FilteringContext.SUB_FRAME, ]); From 1264284a3c6bdf871ea3c4189a504c57c0a388cb Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Mon, 28 Oct 2024 14:17:07 -0400 Subject: [PATCH 0399/1099] New revision for stable release candidate --- dist/version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dist/version b/dist/version index 7ccd40de168e9..4c884c028bd00 100644 --- a/dist/version +++ b/dist/version @@ -1 +1 @@ -1.60.1.101 \ No newline at end of file +1.60.1.102 \ No newline at end of file From a793d693b561cb4fb88cebb0177ef94e69d8a696 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Mon, 28 Oct 2024 14:26:05 -0400 Subject: [PATCH 0400/1099] Make Firefox dev build auto-update --- dist/firefox/updates.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dist/firefox/updates.json b/dist/firefox/updates.json index c0bcbc5cf9e74..7c7502613c907 100644 --- a/dist/firefox/updates.json +++ b/dist/firefox/updates.json @@ -3,9 +3,9 @@ "uBlock0@raymondhill.net": { "updates": [ { - "version": "1.60.1.101", + "version": "1.60.1.102", "browser_specific_settings": { "gecko": { "strict_min_version": "78.0" } }, - "update_link": "https://github.com/gorhill/uBlock/releases/download/1.60.1rc1/uBlock0_1.60.1rc1.firefox.signed.xpi" + "update_link": "https://github.com/gorhill/uBlock/releases/download/1.60.1rc2/uBlock0_1.60.1rc2.firefox.signed.xpi" } ] } From f645e8f0d28e4afbfd2825ee5c3aa688eb1b8e27 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Wed, 30 Oct 2024 09:12:58 -0400 Subject: [PATCH 0401/1099] Improve `googlesyndication_adsbygoogle.js` scriptlet Related discussion: https://github.com/uBlockOrigin/uBlock-discussions/discussions/321#discussioncomment-11000356 --- src/web_accessible_resources/googlesyndication_adsbygoogle.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/web_accessible_resources/googlesyndication_adsbygoogle.js b/src/web_accessible_resources/googlesyndication_adsbygoogle.js index dec634bea57b3..294d054e51a23 100644 --- a/src/web_accessible_resources/googlesyndication_adsbygoogle.js +++ b/src/web_accessible_resources/googlesyndication_adsbygoogle.js @@ -38,7 +38,9 @@ const cfr = document.createElement('iframe'); cfr.id = `google_ads_frame${i}`; fr.appendChild(cfr); - phs[i].appendChild(fr); + const ph = phs[i]; + ph.appendChild(fr); + ph.setAttribute('data-adsbygoogle-status', 'done'); } }; if ( From a5d7e68ebea82547be8d857cad031d07c68e434a Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Wed, 30 Oct 2024 19:06:57 -0400 Subject: [PATCH 0402/1099] Update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9e9fa9a69ed5f..d2807561a9816 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,4 @@ +- [Improve `googlesyndication_adsbygoogle.js` scriptlet](https://github.com/gorhill/uBlock/commit/f645e8f0d2) - [Offer ability to skip redirects in strict-blocked page](https://github.com/gorhill/uBlock/commit/20b54185fa) - [Add `-blocked` directive to `urlskip=` option](https://github.com/gorhill/uBlock/commit/d04dc4c767) - [Add `trusted-set-attr` scriptlet](https://github.com/gorhill/uBlock/commit/11ca4a3923) From eb37cd870d2c99f1a5d6b749dd81d163c1329648 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Wed, 30 Oct 2024 19:07:24 -0400 Subject: [PATCH 0403/1099] New revision for stable release candidate --- dist/version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dist/version b/dist/version index 4c884c028bd00..3d03d9b9baa78 100644 --- a/dist/version +++ b/dist/version @@ -1 +1 @@ -1.60.1.102 \ No newline at end of file +1.60.1.103 \ No newline at end of file From fd3624f5413a848f0c944f35d679b55e98aa72d5 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Wed, 30 Oct 2024 19:16:41 -0400 Subject: [PATCH 0404/1099] Make Firefox dev build auto-update --- dist/firefox/updates.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dist/firefox/updates.json b/dist/firefox/updates.json index 7c7502613c907..2892c96fbacb9 100644 --- a/dist/firefox/updates.json +++ b/dist/firefox/updates.json @@ -3,9 +3,9 @@ "uBlock0@raymondhill.net": { "updates": [ { - "version": "1.60.1.102", + "version": "1.60.1.103", "browser_specific_settings": { "gecko": { "strict_min_version": "78.0" } }, - "update_link": "https://github.com/gorhill/uBlock/releases/download/1.60.1rc2/uBlock0_1.60.1rc2.firefox.signed.xpi" + "update_link": "https://github.com/gorhill/uBlock/releases/download/1.60.1rc3/uBlock0_1.60.1rc3.firefox.signed.xpi" } ] } From 34771d02d18a3a5e1ed15f18e88fb8ee50dd4418 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Thu, 31 Oct 2024 11:18:42 -0400 Subject: [PATCH 0405/1099] Fine tune visuals in document-blocked page Related feedback: https://github.com/uBlockOrigin/uBlock-issues/issues/3206#issuecomment-2449415643 --- src/css/document-blocked.css | 16 +++++++++++++--- src/js/document-blocked.js | 24 ++++++++++++++++++++++-- 2 files changed, 35 insertions(+), 5 deletions(-) diff --git a/src/css/document-blocked.css b/src/css/document-blocked.css index e0504230a7862..75fa392f0a06a 100644 --- a/src/css/document-blocked.css +++ b/src/css/document-blocked.css @@ -68,6 +68,14 @@ a { position: relative; z-index: 10; } +#theURL > p > span:first-of-type { + display: block; + max-height: 6lh; + overflow-y: auto; + } +:root.mobile #theURL > p > span:first-of-type { + max-height: 3lh; + } #theURL #toggleParse { background-color: transparent; top: 100%; @@ -132,9 +140,11 @@ body[dir="rtl"] #toggleParse { #urlskip a { display: block; - overflow-x: hidden; - text-overflow: ellipsis; - white-space: nowrap; + overflow-y: auto; + word-break: break-all; + } +:root.mobile #urlskip a { + max-height: 3lh; } #actionContainer { diff --git a/src/js/document-blocked.js b/src/js/document-blocked.js index 82511436c1ceb..0bccb06a208e8 100644 --- a/src/js/document-blocked.js +++ b/src/js/document-blocked.js @@ -75,7 +75,25 @@ let details = {}; /******************************************************************************/ -dom.text('#theURL > p > span:first-of-type', details.url); +const urlToFragment = raw => { + try { + const fragment = new DocumentFragment(); + const url = new URL(raw); + const hn = url.hostname; + const i = raw.indexOf(hn); + const b = document.createElement('b'); + b.append(hn); + fragment.append(raw.slice(0,i), b, raw.slice(i+hn.length)); + return fragment; + } catch(_) { + } + return raw; +}; + +/******************************************************************************/ + +dom.clear('#theURL > p > span:first-of-type'); +qs$('#theURL > p > span:first-of-type').append(urlToFragment(details.url)); dom.text('#why', details.fs); if ( typeof details.to === 'string' && details.to.length !== 0 ) { @@ -85,7 +103,9 @@ if ( typeof details.to === 'string' && details.to.length !== 0 ) { let pos = text.indexOf(linkPlaceholder); if ( pos !== -1 ) { const link = document.createElement('a'); - link.href = link.textContent = details.to; + link.href = details.to; + dom.cl.add(link, 'code'); + link.append(urlToFragment(details.to)); fragment.append( text.slice(0, pos), link, From d70c60257551a57d3b3f74414dfd33900615c0e5 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Thu, 31 Oct 2024 11:25:18 -0400 Subject: [PATCH 0406/1099] New revision for stable release candidate --- dist/version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dist/version b/dist/version index 3d03d9b9baa78..55108a9ce0f69 100644 --- a/dist/version +++ b/dist/version @@ -1 +1 @@ -1.60.1.103 \ No newline at end of file +1.60.1.104 \ No newline at end of file From d401527e8303c809be27e466e0a5b34fba3e82fe Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Thu, 31 Oct 2024 11:28:06 -0400 Subject: [PATCH 0407/1099] Import translation work from https://crowdin.com/project/ublock --- platform/mv3/extension/_locales/es/messages.json | 14 +++++++------- src/_locales/de/messages.json | 2 +- src/_locales/el/messages.json | 2 +- src/_locales/es/messages.json | 4 ++-- src/_locales/he/messages.json | 2 +- src/_locales/pt_PT/messages.json | 2 +- src/_locales/sv/messages.json | 2 +- 7 files changed, 14 insertions(+), 14 deletions(-) diff --git a/platform/mv3/extension/_locales/es/messages.json b/platform/mv3/extension/_locales/es/messages.json index 323bb1520a827..0232f371a0065 100644 --- a/platform/mv3/extension/_locales/es/messages.json +++ b/platform/mv3/extension/_locales/es/messages.json @@ -32,7 +32,7 @@ "description": "Label in the popup panel for the current filtering mode" }, "popupTipReport": { - "message": "Informar de un problema en este sitio web", + "message": "Reportar un problema en este sitio web", "description": "Tooltip used for the 'chat' icon in the panel" }, "popupTipDashboard": { @@ -104,15 +104,15 @@ "description": "Shown in the About pane" }, "supportS6H": { - "message": "Informar de un problema de filtro", + "message": "Reportar un problema de filtro", "description": "Header of 'Report a filter issue' section in Support pane" }, "supportS3P1": { - "message": "Reportar problemas de filtros con sitios web específicos en uBlockOrigin/uAssets, un rastreador de problemas. Requiere una cuenta de GitHub.", + "message": "Reportar problemas de filtros con sitios web específicos en el registro de problemas uBlockOrigin/uAssets. Requiere una cuenta en GitHub.", "description": "First paragraph of 'Filter issues' section in Support pane" }, "supportS6P1S1": { - "message": "Para evitar sobrecargar a voluntarios con reportes duplicados, verifique que el problema no haya sido reportado.", + "message": "Para evitar sobrecargar a voluntarios con reportes duplicados, verifica que el problema no haya sido reportado.", "description": "A paragraph in the filter issue reporter section" }, "supportFindSpecificButton": { @@ -156,15 +156,15 @@ "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option7": { - "message": "Conduce a malware, phishing", + "message": "Conduce a malware y phishing", "description": "An entry in the widget used to select the type of issue" }, "supportS6Checkbox1": { - "message": "Etiqueta la página web como “NSFW” (“No apto para el trabajo”)", + "message": "Etiquetar la página web como “NSFW” (“no es seguro/apropiado para el trabajo”)", "description": "A checkbox to use for NSFW sites" }, "supportReportSpecificButton": { - "message": "Crear informe nuevo", + "message": "Crear nuevo reporte", "description": "Text for button which open an external webpage in Support pane" }, "firstRunSectionLabel": { diff --git a/src/_locales/de/messages.json b/src/_locales/de/messages.json index 68523b1b28235..60289ec911790 100644 --- a/src/_locales/de/messages.json +++ b/src/_locales/de/messages.json @@ -1192,7 +1192,7 @@ "description": "Button text to navigate to the blocked page" }, "docblockedRedirectPrompt": { - "message": "Die blockierte Seite möchte zu einer anderen Website weiterleiten. Wenn Sie fortfahren, wird folgende Seite aufgerufen: {{url}}", + "message": "Die blockierte Seite möchte zu einer anderen Website weiterleiten. Beim Fortfahren wird folgende Seite aufgerufen: {{url}}", "description": "Text warning about an incoming redirect" }, "cloudPush": { diff --git a/src/_locales/el/messages.json b/src/_locales/el/messages.json index 2785503d83910..aefa2c6a580ad 100644 --- a/src/_locales/el/messages.json +++ b/src/_locales/el/messages.json @@ -1192,7 +1192,7 @@ "description": "Button text to navigate to the blocked page" }, "docblockedRedirectPrompt": { - "message": "The blocked page wants to redirect to another site. If you choose to proceed, you will navigate directly to: {{url}}", + "message": "Η αποκλεισμένη σελίδα θέλει να κάνει ανακατεύθυνση σε άλλο ιστότοπο. Αν επιλέξετε να συνεχίσετε, θα μεταβείτε απευθείας στο: {{url}}", "description": "Text warning about an incoming redirect" }, "cloudPush": { diff --git a/src/_locales/es/messages.json b/src/_locales/es/messages.json index 2f19a0836cee1..a4a418d0b45fc 100644 --- a/src/_locales/es/messages.json +++ b/src/_locales/es/messages.json @@ -1004,7 +1004,7 @@ "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option5": { - "message": "Se rompe cuando uBlock Origin está habilitado", + "message": "Funciona mal cuando uBlock Origin está habilitado", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option6": { @@ -1192,7 +1192,7 @@ "description": "Button text to navigate to the blocked page" }, "docblockedRedirectPrompt": { - "message": "La página bloqueada quiere redireccionar a otro sitio web. Si eliges proceder, navegaras directamente a: {{url}}", + "message": "La página bloqueada quiere redirigir a otro sitio. Si eliges continuar, navegarás directamente a: {{url}}", "description": "Text warning about an incoming redirect" }, "cloudPush": { diff --git a/src/_locales/he/messages.json b/src/_locales/he/messages.json index 4d5f023f07dce..ca9073ecb951b 100644 --- a/src/_locales/he/messages.json +++ b/src/_locales/he/messages.json @@ -1192,7 +1192,7 @@ "description": "Button text to navigate to the blocked page" }, "docblockedRedirectPrompt": { - "message": "The blocked page wants to redirect to another site. If you choose to proceed, you will navigate directly to: {{url}}", + "message": "הדף החסום מבקש לעבור לאתר אחר. אם תחליטו להמשיך, תעברו ישירות ל: {{url}}", "description": "Text warning about an incoming redirect" }, "cloudPush": { diff --git a/src/_locales/pt_PT/messages.json b/src/_locales/pt_PT/messages.json index 79b0efb6b1868..5298956affd17 100644 --- a/src/_locales/pt_PT/messages.json +++ b/src/_locales/pt_PT/messages.json @@ -1192,7 +1192,7 @@ "description": "Button text to navigate to the blocked page" }, "docblockedRedirectPrompt": { - "message": "The blocked page wants to redirect to another site. If you choose to proceed, you will navigate directly to: {{url}}", + "message": "A página bloqueada pretende redirecionar para outro site. Se optar por continuar, navegará diretamente para: {{url}}", "description": "Text warning about an incoming redirect" }, "cloudPush": { diff --git a/src/_locales/sv/messages.json b/src/_locales/sv/messages.json index e9f9457b56590..e1dbccb445bb8 100644 --- a/src/_locales/sv/messages.json +++ b/src/_locales/sv/messages.json @@ -1192,7 +1192,7 @@ "description": "Button text to navigate to the blocked page" }, "docblockedRedirectPrompt": { - "message": "The blocked page wants to redirect to another site. If you choose to proceed, you will navigate directly to: {{url}}", + "message": "Den blockerade sidan vill omdirigera dig till en annan webbplats. Om du väljer att fortsätta skickas du direkt till: {{url}}", "description": "Text warning about an incoming redirect" }, "cloudPush": { From eab0fd4e5796a500c6745c22ca93944b34f72607 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Thu, 31 Oct 2024 12:11:01 -0400 Subject: [PATCH 0408/1099] Make Firefox dev build auto-update --- dist/firefox/updates.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dist/firefox/updates.json b/dist/firefox/updates.json index 2892c96fbacb9..8adf2f81a40a4 100644 --- a/dist/firefox/updates.json +++ b/dist/firefox/updates.json @@ -3,9 +3,9 @@ "uBlock0@raymondhill.net": { "updates": [ { - "version": "1.60.1.103", + "version": "1.60.1.104", "browser_specific_settings": { "gecko": { "strict_min_version": "78.0" } }, - "update_link": "https://github.com/gorhill/uBlock/releases/download/1.60.1rc3/uBlock0_1.60.1rc3.firefox.signed.xpi" + "update_link": "https://github.com/gorhill/uBlock/releases/download/1.60.1rc4/uBlock0_1.60.1rc4.firefox.signed.xpi" } ] } From 8884f259c154ef88f17c67d70dbf177c854efdff Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Fri, 1 Nov 2024 11:33:34 -0400 Subject: [PATCH 0409/1099] Improve `prevent-refresh` scriptlet Related discussion: https://github.com/uBlockOrigin/uAssets/issues/25859#issuecomment-2449623891 --- assets/resources/scriptlets.js | 30 +++++++++++++++++------------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/assets/resources/scriptlets.js b/assets/resources/scriptlets.js index 1a835f7279f7f..ba08824916705 100644 --- a/assets/resources/scriptlets.js +++ b/assets/resources/scriptlets.js @@ -2315,30 +2315,34 @@ builtinScriptlets.push({ fn: preventRefresh, world: 'ISOLATED', dependencies: [ - 'run-at.fn', 'safe-self.fn', ], }); // https://www.reddit.com/r/uBlockOrigin/comments/q0frv0/while_reading_a_sports_article_i_was_redirected/hf7wo9v/ function preventRefresh( - arg1 = '' + delay = '' ) { - if ( typeof arg1 !== 'string' ) { return; } + if ( typeof delay !== 'string' ) { return; } const safe = safeSelf(); - const logPrefix = safe.makeLogPrefix('prevent-refresh', arg1); + const logPrefix = safe.makeLogPrefix('prevent-refresh', delay); + const stop = content => { + window.stop(); + safe.uboLog(logPrefix, `Prevented "${content}"`); + }; const defuse = ( ) => { const meta = document.querySelector('meta[http-equiv="refresh" i][content]'); if ( meta === null ) { return; } - safe.uboLog(logPrefix, `Prevented "${meta.textContent}"`); - const s = arg1 === '' - ? meta.getAttribute('content') - : arg1; - const ms = Math.max(parseFloat(s) || 0, 0) * 1000; - setTimeout(( ) => { window.stop(); }, ms); + const content = meta.getAttribute('content') || ''; + const ms = delay === '' + ? Math.max(parseFloat(content) || 0, 0) * 500 + : 0; + if ( ms === 0 ) { + stop(content); + } else { + setTimeout(( ) => { stop(content); }, ms); + } }; - runAt(( ) => { - defuse(); - }, 'interactive'); + self.addEventListener('load', defuse, { capture: true, once: true }); } /******************************************************************************/ From 331a82775f30d9a7e66f16551851b0d7433d44ee Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Fri, 1 Nov 2024 11:36:08 -0400 Subject: [PATCH 0410/1099] Update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index d2807561a9816..2d62d815d12a4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,4 @@ +- [Improve `prevent-refresh` scriptlet](https://github.com/gorhill/uBlock/commit/8884f259c1) - [Improve `googlesyndication_adsbygoogle.js` scriptlet](https://github.com/gorhill/uBlock/commit/f645e8f0d2) - [Offer ability to skip redirects in strict-blocked page](https://github.com/gorhill/uBlock/commit/20b54185fa) - [Add `-blocked` directive to `urlskip=` option](https://github.com/gorhill/uBlock/commit/d04dc4c767) From 67310b0fb6120931bdf4d0edfd59439615b5f970 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Fri, 1 Nov 2024 11:36:43 -0400 Subject: [PATCH 0411/1099] New revision for stable release candidate --- dist/version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dist/version b/dist/version index 55108a9ce0f69..77c1ed4ee7b33 100644 --- a/dist/version +++ b/dist/version @@ -1 +1 @@ -1.60.1.104 \ No newline at end of file +1.60.1.105 \ No newline at end of file From 121ac712f79d74cdce982c4137b7f1409614df15 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Fri, 1 Nov 2024 11:41:35 -0400 Subject: [PATCH 0412/1099] Make Firefox dev build auto-update --- dist/firefox/updates.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dist/firefox/updates.json b/dist/firefox/updates.json index 8adf2f81a40a4..a34746cba91dc 100644 --- a/dist/firefox/updates.json +++ b/dist/firefox/updates.json @@ -3,9 +3,9 @@ "uBlock0@raymondhill.net": { "updates": [ { - "version": "1.60.1.104", + "version": "1.60.1.105", "browser_specific_settings": { "gecko": { "strict_min_version": "78.0" } }, - "update_link": "https://github.com/gorhill/uBlock/releases/download/1.60.1rc4/uBlock0_1.60.1rc4.firefox.signed.xpi" + "update_link": "https://github.com/gorhill/uBlock/releases/download/1.60.1rc5/uBlock0_1.60.1rc5.firefox.signed.xpi" } ] } From 2831a0d0fddf3c9b397addda91c4a6d4ae3b2b50 Mon Sep 17 00:00:00 2001 From: daylight Date: Fri, 1 Nov 2024 17:45:31 +0300 Subject: [PATCH 0413/1099] Update config --- .github/ISSUE_TEMPLATE/config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml index f1ca168f9fba9..097d0eccd5979 100644 --- a/.github/ISSUE_TEMPLATE/config.yml +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -10,5 +10,5 @@ contact_links: url: https://github.com/uBlockOrigin/uAssets/issues about: Report issues with filter lists or broken website functionality in the uAssets issue tracker. - name: uBO Lite (uBOL) Issues - url: https://github.com/uBlockOrigin/uBOL-issues/issues + url: https://github.com/uBlockOrigin/uBOL-home/issues about: Report issues specific to the Manifest Version 3 (MV3) variant in the uBOL issue tracker. From 3b9333dbd8e69e8a2eecdb77113082caa307e0ad Mon Sep 17 00:00:00 2001 From: daylight Date: Fri, 1 Nov 2024 17:45:53 +0300 Subject: [PATCH 0414/1099] Update CI --- .github/workflows/main.yml | 76 ++++++++++++++++++++------------------ 1 file changed, 41 insertions(+), 35 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 5ce7af1212a4f..503c7ac54d9e8 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -1,15 +1,13 @@ name: uBO release on: + workflow_dispatch: create: branches: master permissions: contents: read -# I used the following project as template to get started: -# https://github.com/dessant/search-by-image/blob/master/.github/workflows/ci.yml - jobs: build: permissions: @@ -18,73 +16,81 @@ jobs: runs-on: ubuntu-latest if: startsWith(github.ref, 'refs/tags/') steps: - - name: Clone repository + - name: Checkout repository uses: actions/checkout@v4 with: persist-credentials: false - - name: Clone uAssets + + - name: Checkout uAssets repository run: | tools/pull-assets.sh - # https://www.gnu.org/software/bash/manual/html_node/Shell-Parameter-Expansion.html + - name: Get release information id: release_info run: | - echo ::set-output name=VERSION::${GITHUB_REF/refs\/tags\//} + echo "VERSION=${GITHUB_REF/refs\/tags\//}" >> $GITHUB_ENV + - name: Assemble release notes run: | > release.body.txt grep -m1 -B10000 -- "----------" CHANGELOG.md >> release.body.txt - sed -e 's/%version%/${{ steps.release_info.outputs.VERSION }}/g' RELEASE.HEAD.md >> release.body.txt + sed -e 's/%version%/'"$VERSION"'/g' RELEASE.HEAD.md >> release.body.txt + + - name: Build MV2 packages + run: | + tools/make-chromium.sh $VERSION + tools/make-firefox.sh $VERSION + tools/make-thunderbird.sh $VERSION + tools/make-npm.sh $VERSION + - name: Create GitHub release id: create_release - uses: actions/create-release@v1 - env: - GITHUB_TOKEN: ${{ github.token }} + uses: softprops/action-gh-release@v2 with: - tag_name: ${{ steps.release_info.outputs.VERSION }} - release_name: ${{ steps.release_info.outputs.VERSION }} + tag_name: ${{ env.VERSION }} + release_name: ${{ env.VERSION }} draft: true prerelease: true body_path: release.body.txt - - name: Build MV2 packages - run: | - tools/make-chromium.sh ${{ steps.release_info.outputs.VERSION }} - tools/make-firefox.sh ${{ steps.release_info.outputs.VERSION }} - tools/make-thunderbird.sh ${{ steps.release_info.outputs.VERSION }} - tools/make-npm.sh ${{ steps.release_info.outputs.VERSION }} - - name: Upload Chromium package - uses: actions/upload-release-asset@v1 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + - name: Upload Chromium package + uses: softprops/action-gh-release@v2 with: upload_url: ${{ steps.create_release.outputs.upload_url }} - asset_path: dist/build/uBlock0_${{ steps.release_info.outputs.VERSION }}.chromium.zip - asset_name: uBlock0_${{ steps.release_info.outputs.VERSION }}.chromium.zip + asset_path: dist/build/uBlock0_${{ env.VERSION }}.chromium.zip + asset_name: uBlock0_${{ env.VERSION }}.chromium.zip asset_content_type: application/octet-stream - - name: Upload Firefox package - uses: actions/upload-release-asset@v1 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + - name: Upload Firefox package + uses: softprops/action-gh-release@v2 with: upload_url: ${{ steps.create_release.outputs.upload_url }} - asset_path: dist/build/uBlock0_${{ steps.release_info.outputs.VERSION }}.firefox.xpi - asset_name: uBlock0_${{ steps.release_info.outputs.VERSION }}.firefox.xpi + asset_path: dist/build/uBlock0_${{ env.VERSION }}.firefox.xpi + asset_name: uBlock0_${{ env.VERSION }}.firefox.xpi asset_content_type: application/octet-stream - - name: Upload Thunderbird package - uses: actions/upload-release-asset@v1 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + - name: Upload Thunderbird package + uses: softprops/action-gh-release@v2 with: upload_url: ${{ steps.create_release.outputs.upload_url }} - asset_path: dist/build/uBlock0_${{ steps.release_info.outputs.VERSION }}.thunderbird.xpi - asset_name: uBlock0_${{ steps.release_info.outputs.VERSION }}.thunderbird.xpi + asset_path: dist/build/uBlock0_${{ env.VERSION }}.thunderbird.xpi + asset_name: uBlock0_${{ env.VERSION }}.thunderbird.xpi asset_content_type: application/octet-stream - - name: Upload NodeJS package - uses: actions/upload-release-asset@v1 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + - name: Upload NodeJS package + uses: softprops/action-gh-release@v2 with: upload_url: ${{ steps.create_release.outputs.upload_url }} - asset_path: dist/build/uBlock0_${{ steps.release_info.outputs.VERSION }}.npm.tgz - asset_name: uBlock0_${{ steps.release_info.outputs.VERSION }}.npm.tgz + asset_path: dist/build/uBlock0_${{ env.VERSION }}.npm.tgz + asset_name: uBlock0_${{ env.VERSION }}.npm.tgz asset_content_type: application/octet-stream + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} From 3019dfc37a5eb8acc52d55fb1b5f218be6a30694 Mon Sep 17 00:00:00 2001 From: daylight Date: Fri, 1 Nov 2024 17:46:16 +0300 Subject: [PATCH 0415/1099] Update contributing guide --- CONTRIBUTING.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 2717e823dd9f4..ce2d70ee0b5dc 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,31 +1,31 @@ # Contributions -Refer to the following sections to direct you to the appropriate destination. Thank you in advance for your help. +Please refer to the sections below to find the appropriate destination for your contributions. Thank you for your support! --- ### Translations -Help translate uBO via [Crowdin](https://crowdin.com/project/ublock). +You can help translate uBO via [Crowdin](https://crowdin.com/project/ublock). --- ### Reporting Issues -The issue tracker in this repository is deprecated. Use the links below to guide you to where you need to report your issue. +The issue tracker in this repository is deprecated. Use the links below to report your issues. #### Support Forum -For support, questions, or help, visit [/r/uBlockOrigin](https://www.reddit.com/r/uBlockOrigin/). +For support, questions, or assistance, please visit [/r/uBlockOrigin](https://www.reddit.com/r/uBlockOrigin/). #### Filter List Issues -Report issues with filter lists or broken website functionality in the [uAssets issue tracker](https://github.com/uBlockOrigin/uAssets/issues). +Report issues related to filter lists or broken website functionality in the [uAssets issue tracker](https://github.com/uBlockOrigin/uAssets/issues). #### uBlock Origin (uBO) Issues -Report issues with uBO in the [uBO issue tracker](https://github.com/uBlockOrigin/uBlock-issues/issues). +For issues specifically about uBO, please use the [uBO issue tracker](https://github.com/uBlockOrigin/uBlock-issues/issues). #### uBO Lite (uBOL) Issues -Report issues specific to the Manifest Version 3 (MV3) variant in the [uBOL issue tracker](https://github.com/uBlockOrigin/uBOL-issues/issues). +For issues related to the Manifest Version 3 (MV3) variant, report them in the [uBOL issue tracker](https://github.com/uBlockOrigin/uBOL-home/issues). From be2da15508c0c34bb5e6319a32e7c06522a923a7 Mon Sep 17 00:00:00 2001 From: daylight Date: Fri, 1 Nov 2024 17:46:37 +0300 Subject: [PATCH 0416/1099] Update links in readme --- README.md | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index d91e592142693..18e6aacad9e6b 100644 --- a/README.md +++ b/README.md @@ -31,7 +31,7 @@ uBlock Origin (uBO) ***

-Get uBlock Origin for Chromium
+Get uBlock Origin for Chromium
IMPORTANT: About Google Chrome's "This extension may soon no longer be supported"

@@ -151,13 +151,13 @@ If you ever want to contribute something, think about the people working hard to [Performance]: https://www.debugbear.com/blog/chrome-extension-performance-2021#how-do-ad-blockers-and-privacy-tools-affect-browser-performance [EasyPrivacy]: https://easylist.to/#easyprivacy [Thunderbird]: https://addons.thunderbird.net/thunderbird/addon/ublock-origin/ -[Chrome Dev]: https://chrome.google.com/webstore/detail/ublock-origin-development/cgbcahbpdhpcegmbfconppldiemgcoii +[Chrome Dev]: https://chromewebstore.google.com/detail/ublock-origin-development/cgbcahbpdhpcegmbfconppldiemgcoii [EasyList]: https://easylist.to/#easylist [Mozilla]: https://addons.mozilla.org/addon/ublock-origin/ [Crowdin]: https://crowdin.com/project/ublock -[Chrome]: https://chrome.google.com/webstore/detail/ublock-origin/cjpalhdlnbpafiamejdnhcphjbkeiagm +[Chrome]: https://chromewebstore.google.com/detail/ublock-origin/cjpalhdlnbpafiamejdnhcphjbkeiagm [Reddit]: https://www.reddit.com/r/uBlockOrigin/ -[Theft]: https://twitter.com/LeaVerou/status/518154828166725632 +[Theft]: https://x.com/LeaVerou/status/518154828166725632 [Opera]: https://addons.opera.com/extensions/details/ublock/ [Edge]: https://microsoftedge.microsoft.com/addons/detail/ublock-origin/odfafepnkmbhccpbejgmiehpchacaeak [NPM]: https://www.npmjs.com/package/@gorhill/ubo-core @@ -167,7 +167,6 @@ If you ever want to contribute something, think about the people working hard to [Nicole Rolls]: https://github.com/nicole-ashley - [Manual Installation]: https://github.com/gorhill/uBlock/tree/master/dist#install @@ -184,7 +183,6 @@ If you ever want to contribute something, think about the people working hard to [Beta]: https://github.com/gorhill/uBlock/blob/master/dist/README.md#for-beta-version [Wiki]: https://github.com/gorhill/uBlock/wiki - [Badge Localization]: https://d322cqt584bo4o.cloudfront.net/ublock/localized.svg @@ -195,4 +193,3 @@ If you ever want to contribute something, think about the people working hard to [Badge Edge]: https://img.shields.io/badge/dynamic/json?label=Edge&color=brightgreen&query=%24.averageRating&suffix=%2F%35&url=https%3A%2F%2Fmicrosoftedge.microsoft.com%2Faddons%2Fgetproductdetailsbycrxid%2Fodfafepnkmbhccpbejgmiehpchacaeak [Badge Issues]: https://img.shields.io/github/issues/uBlockOrigin/uBlock-issues [Badge NPM]: https://img.shields.io/npm/v/@gorhill/ubo-core - From b5aaec47b38b584deaa0e3b6351b0f5a8f1f286f Mon Sep 17 00:00:00 2001 From: daylight Date: Fri, 1 Nov 2024 17:46:53 +0300 Subject: [PATCH 0417/1099] Update release head --- RELEASE.HEAD.md | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/RELEASE.HEAD.md b/RELEASE.HEAD.md index ce149ad9c489e..986319b72bd8c 100644 --- a/RELEASE.HEAD.md +++ b/RELEASE.HEAD.md @@ -1,10 +1,12 @@ +[Commits to Master Since This Release](https://github.com/gorhill/uBlock/compare/%version%...master) -[Commits to master since this release](https://github.com/gorhill/uBlock/compare/%version%...master) +#### How to Install the Developer Build: -To install the developer build: +- **Firefox**: Download the build from [uBlock0_%version%.firefox.signed.xpi](https://github.com/gorhill/uBlock/releases/download/%version%/uBlock0_%version%.firefox.signed.xpi). + - uBO works best on Firefox, check out [why](https://github.com/gorhill/uBlock/wiki/uBlock-Origin-works-best-on-Firefox). + +- **Chromium**: Install directly from the [Chrome Web Store](https://chromewebstore.google.com/detail/ublock-origin-development/cgbcahbpdhpcegmbfconppldiemgcoii). -- **Firefox**: Click [uBlock0_%version%.firefox.signed.xpi](https://github.com/gorhill/uBlock/releases/download/%version%/uBlock0_%version%.firefox.signed.xpi) - - [uBO works best on Firefox](https://github.com/gorhill/uBlock/wiki/uBlock-Origin-works-best-on-Firefox). -- **Chromium**: Install from the Chrome Web Store (CWS): . -- **Thunderbird**: Download [uBlock0_%version%.thunderbird.xpi](https://github.com/gorhill/uBlock/releases/download/%version%/uBlock0_%version%.thunderbird.xpi), then drag-n-drop it into Thunderbird's _Add-ons Manager_ pane (Thunderbird 91+ required) -- **Node.js**: Import from [npm](https://www.npmjs.com/package/@gorhill/ubo-core), or download and unzip [uBlock0_%version%.npm.tgz](https://github.com/gorhill/uBlock/releases/download/%version%/uBlock0_%version%.npm.tgz). +- **Thunderbird**: Download [uBlock0_%version%.thunderbird.xpi](https://github.com/gorhill/uBlock/releases/download/%version%/uBlock0_%version%.thunderbird.xpi) and drag it into Thunderbird's _Add-ons Manager_ (requires Thunderbird 91+). + +- **Node.js**: You can import from [npm](https://www.npmjs.com/package/@gorhill/ubo-core) or download and unzip [uBlock0_%version%.npm.tgz](https://github.com/gorhill/uBlock/releases/download/%version%/uBlock0_%version%.npm.tgz). From c936a72bb3030c821243c34720fba4327af58631 Mon Sep 17 00:00:00 2001 From: daylight Date: Fri, 1 Nov 2024 17:47:16 +0300 Subject: [PATCH 0418/1099] Update dist/README.md --- dist/README.md | 158 ++++++++++++++++++++++++++++--------------------- 1 file changed, 90 insertions(+), 68 deletions(-) diff --git a/dist/README.md b/dist/README.md index 37622c5e96da9..98cb46dc06607 100644 --- a/dist/README.md +++ b/dist/README.md @@ -1,81 +1,103 @@ -## INSTALL +# INSTALL -### Chromium +## Chromium -- Download and unzip `ublock0.chromium.zip` ([latest release desirable](https://github.com/gorhill/uBlock/releases)). -- Rename the unzipped directory to `ublock` - - When you later update manually, replace the **content** of the `ublock` folder with the **content** of the latest zipped version. - - This will ensure that all the extension settings will be preserved - - As long as the extension loads **from same folder path from which it was originally installed**, all your settings will be preserved. -- Go to chromium/chrome *Extensions*. -- Click to check *Developer mode*. -- Click *Load unpacked extension...*. -- In the file selector dialog: - - Select the directory `ublock` which was created above. - - Click *Open*. +1. Download and unzip `ublock0.chromium.zip` ([latest release desirable](https://github.com/gorhill/uBlock/releases)). +2. Rename the unzipped directory to `ublock`. + - When you update manually, replace the **content** of the `ublock` folder with the **content** of the latest zipped version. This ensures all extension settings are preserved. + - As long as the extension loads from the same folder path as it was originally installed, your settings will be kept. +3. Open Chromium/Chrome and go to *Extensions*. +4. Click to enable *Developer mode*. +5. Click *Load unpacked extension...*. +6. In the file selector dialog: + - Select the `ublock` directory you created. + - Click *Open*. -The extension will now be available in your chromium/chromium-based browser. +The extension will now be available in your Chromium/Chromium-based browser. -Remember that you have to update manually also. For some users, updating manually is actually an advantage because: -- You can update when **you** want -- If ever a new version sucks, you can easily just re-install the previous one +**Note:** You must update manually. For some users, manual updates are beneficial because: +- You can update when **you** want. +- If a new version is unsatisfactory, you can easily reinstall the previous one. -### Firefox +## Firefox -Compatible with Firefox 52 and beyond. +Compatible with Firefox 52 and beyond. -#### For stable release version +### For Stable Release Version -This works only if you set `xpinstall.signatures.required` to `false` in `about:config`.[see "Add-on signing in Firefox"](https://support.mozilla.org/en-US/kb/add-on-signing-in-firefox) +This method only works if you set `xpinstall.signatures.required` to `false` in `about:config`.[see "Add-on signing in Firefox"](https://support.mozilla.org/en-US/kb/add-on-signing-in-firefox) -- Download `ublock0.firefox.xpi` ([latest release desirable](https://github.com/gorhill/uBlock/releases)). - - Right-click and choose _"Save As..."_. -- Drag and drop the previously downloaded `ublock0.firefox.xpi` into Firefox +1. Download `ublock0.firefox.xpi` ([latest release desirable](https://github.com/gorhill/uBlock/releases)). + - Right-click and choose _"Save As..."_. +2. Drag and drop the downloaded `ublock0.firefox.xpi` into Firefox. -#### For beta version +### For Beta Version - Click on `ublock0.firefox.signed.xpi` ([latest release desirable](https://github.com/gorhill/uBlock/releases)). -#### Location of uBO settings - -On Linux, the settings are saved in a JSON file located at `~/.mozilla/firefox/[profile name]/browser-extension-data/uBlock0@raymondhill.net/storage.js`. - -When you uninstall the extension, Firefox deletes that file, so all your settings are lost when you uninstall. - -### Firefox legacy - -Compatible with Firefox 24-56, [Pale Moon](https://www.palemoon.org/) and [SeaMonkey](http://www.seamonkey-project.org/). - -- Download `ublock0.firefox-legacy.xpi` ([latest release desirable](https://github.com/gorhill/uBlock-for-firefox-legacy/releases)). - - Right-click and select "Save Link As..." -- Drag and drop the previously downloaded `ublock0.firefox-legacy.xpi` into Firefox - -With Firefox 43 and beyond, you may need to toggle the setting `xpinstall.signatures.required` to `false` in `about:config`.[see "Add-on signing in Firefox"](https://support.mozilla.org/en-US/kb/add-on-signing-in-firefox) - -Your uBlock Origin settings are kept intact even after you uninstall the addon. - -On Linux, the settings are saved in a SQlite file located at `~/.mozilla/firefox/[profile name]/extension-data/ublock0.sqlite`. - -On Windows, the settings are saved in a SQlite file located at `%APPDATA%\Mozilla\Firefox\Profiles\[profile name]\extension-data\ublock0.sqlite`. - -### Build instructions (for developers) - -- Clone [uBlock repo](https://github.com/gorhill/uBlock): `git clone https://github.com/gorhill/uBlock.git` -- Set path to uBlock: `cd uBlock` -- The official version of uBO is in the `master` branch - - `git checkout master` -- Build the plugin: - - Chromium: `make chromium` - - Firefox: `make firefox` - - NPM package: `make npm` -- Load the result of the build into your browser: - - Chromium: - - Navigate to `chrome://extensions/` - - Check _"Developer mode"_ - - Click _"Load unpacked"_ - - Select `/uBlock/dist/build/uBlock0.chromium/` - - Firefox: - - Navigate to `about:debugging#/runtime/this-firefox` - - Click _"Load Temporary Add-on..."_ - - Select `/uBlock/dist/build/uBlock0.firefox/` - +### Location of uBO Settings + +On Linux, the settings are saved in a JSON file located at: +``` +~/.mozilla/firefox/[profile name]/browser-extension-data/uBlock0@raymondhill.net/storage.js +``` +When you uninstall the extension, Firefox deletes this file, and all your settings will be lost. + +### Firefox Legacy + +Compatible with Firefox 24-56, [Pale Moon](https://www.palemoon.org/), and [SeaMonkey](https://www.seamonkey-project.org/). + +1. Download `ublock0.firefox-legacy.xpi` ([latest release desirable](https://github.com/gorhill/uBlock-for-firefox-legacy/releases)). + - Right-click and select "Save Link As..." +2. Drag and drop the downloaded `ublock0.firefox-legacy.xpi` into Firefox. + +For Firefox 43 and beyond, you may need to toggle the setting `xpinstall.signatures.required` to `false` in `about:config`.[see "Add-on signing in Firefox"](https://support.mozilla.org/en-US/kb/add-on-signing-in-firefox) + +Your uBlock Origin settings are preserved even after uninstalling the addon. + +- On Linux, settings are saved in a SQLite file located at: +``` +~/.mozilla/firefox/[profile name]/extension-data/ublock0.sqlite +``` +- On Windows, settings are saved in a SQLite file located at: +``` +%APPDATA%\Mozilla\Firefox\Profiles\[profile name]\extension-data\ublock0.sqlite +``` + +## Build Instructions (for Developers) + +1. Clone the [uBlock repository](https://github.com/gorhill/uBlock): + ```bash + git clone https://github.com/gorhill/uBlock.git + ``` +2. Set the path to uBlock: + ```bash + cd uBlock + ``` +3. The official version of uBO is in the `master` branch: + ```bash + git checkout master + ``` +4. Build the plugin: + - Chromium: + ```bash + make chromium + ``` + - Firefox: + ```bash + make firefox + ``` + - NPM package: + ```bash + make npm + ``` +5. Load the result of the build into your browser: + - **Chromium:** + - Navigate to `chrome://extensions/` + - Check _"Developer mode"_ + - Click _"Load unpacked"_ + - Select `/uBlock/dist/build/uBlock0.chromium/` + - **Firefox:** + - Navigate to `about:debugging#/runtime/this-firefox` + - Click _"Load Temporary Add-on..."_ + - Select `/uBlock/dist/build/uBlock0.firefox/` From 57b7d9814851e504f9c9f179a1bd594ded8184a7 Mon Sep 17 00:00:00 2001 From: daylight Date: Fri, 1 Nov 2024 17:47:50 +0300 Subject: [PATCH 0419/1099] Delete dist/mv3/log.txt --- dist/mv3/log.txt | 1584 ---------------------------------------------- 1 file changed, 1584 deletions(-) delete mode 100644 dist/mv3/log.txt diff --git a/dist/mv3/log.txt b/dist/mv3/log.txt deleted file mode 100644 index 122c47e95d023..0000000000000 --- a/dist/mv3/log.txt +++ /dev/null @@ -1,1584 +0,0 @@ -Version: 2023.8.19.910 -Secret: 72d7360bdd9117ff -============================ -Listset for 'default': - Fetching remote https://ublockorigin.github.io/uAssets/filters/filters.min.txt - Fetching remote https://ublockorigin.github.io/uAssets/filters/badware.txt - Fetching remote https://ublockorigin.github.io/uAssets/filters/privacy.min.txt - Fetching remote https://ublockorigin.github.io/uAssets/filters/unbreak.min.txt - Fetching remote https://ublockorigin.github.io/uAssets/filters/quick-fixes.txt - Fetching remote https://ublockorigin.github.io/uAssets/filters/ubol-filters.txt - Fetching remote https://ublockorigin.github.io/uAssets/thirdparties/easylist.txt - Fetching remote https://ublockorigin.github.io/uAssets/thirdparties/easyprivacy.txt - Fetching remote https://pgl.yoyo.org/adservers/serverlist.php?hostformat=hosts&showintro=1&mimetype=plaintext -Input filter count: 92855 - Accepted filter count: 91275 - Rejected filter count: 146 -Output rule count: 17561 - Pruning requestDomains: from 54014 to 53859 - Pruning requestDomains: from 2681 to 2675 - Pruning requestDomains: from 5929 to 5925 - Plain good: 16732 - Salvaged rule by ignoring 1 entity-based domain= option: erotic-beauties.com|hardsex.cc|rule34.top|sex-movies.biz|tube18.sexy|xvideos.name|booru.* - Salvaged rule by ignoring 19 entity-based domain= option: fullxh.com|hamsterix.*|megaxh.com|unlockxh4.com|xhadult2.com|xhadult3.com|xhadult4.com|xhadult5.com|xhamster.*|xhamster10.*|xhamster11.*|xhamster12.*|xhamster13.*|xhamster14.*|xhamster15.*|xhamster16.*|xhamster17.*|xhamster18.*|xhamster19.*|xhamster2.*|xhamster20.*|xhamster3.*|xhamster4.*|xhamster46.com|xhamster5.*|xhamster7.*|xhamster8.*|xhday.com|xhday1.com|xhmoon5.com|xhplanet1.com|xhplanet2.com|xhreal2.com|xhreal3.com|xhtab2.com|xhvictory.com|xhwebsite.com|xhwebsite2.com|xhwide1.com|xhwide8.com - Salvaged rule by ignoring 19 entity-based domain= option: fullxh.com|hamsterix.*|megaxh.com|unlockxh4.com|xhadult2.com|xhadult3.com|xhadult4.com|xhadult5.com|xhamster.*|xhamster10.*|xhamster11.*|xhamster12.*|xhamster13.*|xhamster14.*|xhamster15.*|xhamster16.*|xhamster17.*|xhamster18.*|xhamster19.*|xhamster2.*|xhamster20.*|xhamster3.*|xhamster4.*|xhamster46.com|xhamster5.*|xhamster7.*|xhamster8.*|xhday.com|xhday1.com|xhmoon5.com|xhplanet1.com|xhplanet2.com|xhreal2.com|xhreal3.com|xhtab2.com|xhvictory.com|xhwebsite.com|xhwebsite2.com|xhwide1.com|xhwide8.com - Salvaged rule by ignoring 1 entity-based domain= option: 8boobs.com|angelgals.com|babesexy.com|babesinporn.com|fooxybabes.com|hotbabeswanted.com|hotstunners.com|mainbabes.com|nakedbabes.club|nakedgirlsroom.com|nudebabes.sexy|pleasuregirl.net|rabbitsfun.com|sexybabes.club|sexybabesart.com|wantedbabes.com|silkengirl.* - Salvaged rule by ignoring 1 entity-based domain= option: web.de|gmx.* - Salvaged rule by ignoring 2 entity-based domain= option: vizcloud.*|vizcloud2.*|mcloud.to - Salvaged rule by ignoring 1 entity-based domain= option: 8boobs.com|babesinporn.com|fooxybabes.com|hotstunners.com|mainbabes.com|pleasuregirl.net|rabbitsfun.com|wantedbabes.com|silkengirl.* - Salvaged rule by ignoring 2 entity-based domain= option: dood.*|dooood.*|doods.pro - Salvaged rule by ignoring 1 entity-based domain= option: vtplay.net|vtplayer.net|vtube.to|vtbe.* - Salvaged rule by ignoring 1 entity-based domain= option: piraproxy.app|unblocksite.pw|theproxy.* - Salvaged rule by ignoring 1 entity-based domain= option: androidapks.biz|androidsite.net|animeonlinefree.org|animesite.net|computercrack.com|crackedsoftware.biz|crackfree.org|cracksite.info|downloadapk.info|downloadapps.info|downloadgames.info|downloadmusic.info|downloadsite.org|ebooksite.org|emulatorsite.com|fmovies24.com|freeflix.info|freemoviesu4.com|freesoccer.net|fseries.org|gamefast.org|gamesite.info|gostreamon.net|hindisite.net|isosite.org|macsite.info|mangasite.org|megamovies.org|moviefree2.com|moviesite.app|moviesx.org|musicsite.biz|patchsite.net|pdfsite.net|play1002.com|productkeysite.com|romsite.org|seriesite.net|siteapk.net|siteflix.org|sitegames.net|sitekeys.net|sitepdf.com|sitetorrent.com|softwaresite.net|superapk.org|supermovies.org|tvonlinesports.com|ultramovies.org|warezsite.net|watchmovies2.com|watchmoviesforfree.org|watchsite.net|youapk.net|sitesunblocked.* - Salvaged rule by ignoring 19 entity-based domain= option: fullxh.com|hamsterix.*|megaxh.com|unlockxh4.com|xhadult2.com|xhadult3.com|xhadult4.com|xhadult5.com|xhamster.*|xhamster10.*|xhamster11.*|xhamster12.*|xhamster13.*|xhamster14.*|xhamster15.*|xhamster16.*|xhamster17.*|xhamster18.*|xhamster19.*|xhamster2.*|xhamster20.*|xhamster3.*|xhamster4.*|xhamster46.com|xhamster5.*|xhamster7.*|xhamster8.*|xhday.com|xhday1.com|xhmoon5.com|xhplanet1.com|xhplanet2.com|xhreal2.com|xhreal3.com|xhtab2.com|xhvictory.com|xhwebsite.com|xhwebsite2.com|xhwide1.com|xhwide8.com - Salvaged rule by ignoring 19 entity-based domain= option: fullxh.com|hamsterix.*|megaxh.com|unlockxh4.com|xhadult2.com|xhadult3.com|xhadult4.com|xhadult5.com|xhamster.*|xhamster10.*|xhamster11.*|xhamster12.*|xhamster13.*|xhamster14.*|xhamster15.*|xhamster16.*|xhamster17.*|xhamster18.*|xhamster19.*|xhamster2.*|xhamster20.*|xhamster3.*|xhamster4.*|xhamster46.com|xhamster5.*|xhamster7.*|xhamster8.*|xhday.com|xhday1.com|xhmoon5.com|xhplanet1.com|xhplanet2.com|xhreal2.com|xhreal3.com|xhtab2.com|xhvictory.com|xhwebsite.com|xhwebsite2.com|xhwide1.com|xhwide8.com - Salvaged rule by ignoring 11 entity-based domain= option: clik.pw|1ink.cc|pornfay.*|picbaron.com|bit-url.com|upbam.org|sexvid.*|sexrura.pl|isohuntz.*|isohunt.*|isohunts.*|isohuntx.*|isohunthydra.*|isohunters.*|isohunting.*|myisohunt.*|torrentproject2.* - Salvaged rule by ignoring 19 entity-based domain= option: fullxh.com|hamsterix.*|megaxh.com|unlockxh4.com|xhadult2.com|xhadult3.com|xhadult4.com|xhadult5.com|xhamster.*|xhamster10.*|xhamster11.*|xhamster12.*|xhamster13.*|xhamster14.*|xhamster15.*|xhamster16.*|xhamster17.*|xhamster18.*|xhamster19.*|xhamster20.*|xhamster2.*|xhamster3.*|xhamster4.*|xhamster46.com|xhamster5.*|xhamster7.*|xhamster8.*|xhday.com|xhday1.com|xhmoon5.com|xhplanet1.com|xhplanet2.com|xhreal2.com|xhreal3.com|xhtab2.com|xhvictory.com|xhwebsite.com|xhwebsite2.com|xhwide1.com|xhwide8.com|webnovel.com - Salvaged rule by ignoring 1 entity-based domain= option: torrentproject2.*|click.allkeyshop.com - Salvaged rule by ignoring 3 entity-based domain= option: 0gomovies.*|cdn1.fastvid.co|cdnqq.net|gorockmovies.top|kokostream.net|movi.pk|ncdn22.xyz|netu.ac|player.msmini.*|vapley.* - Salvaged rule by ignoring 1 entity-based domain= option: isaidub1.com|isaidubhd.* - Salvaged rule by ignoring 1 entity-based domain= option: d3ward.github.io|direct-cloud.* - Salvaged rule by ignoring 1 entity-based domain= option: povvldeo.lol|povvldeo.* - Salvaged rule by ignoring 1 entity-based domain= option: pngit.live|pingit.* - Salvaged rule by ignoring 1 entity-based domain= option: enrt.eu|seulink.* - Salvaged rule by ignoring 1 entity-based domain= option: imgair.net|imgblaze.net|imgfrost.net|imgwia.buzz|pixsera.net|vestimage.site|pixlev.* - Salvaged rule by ignoring 1 entity-based domain= option: javthe.com|javfree.* - Salvaged rule by ignoring 1 entity-based domain= option: dropcoins.xyz|fastcoin.ga|faucetbr.tk|is2btc.com|swift4claim.com|quickclaims.* - Salvaged rule by ignoring 1 entity-based domain= option: olympicstreams.me|vipboxtv.* - Salvaged rule by ignoring 8 entity-based domain= option: adbull.org|zdnet.fr|imgsen.com|titsbox.com|senmanga.com|hitomi.la|mangovideo.*|bolly4umovies.*|gaybeeg.info|lovelynudez.com|classicpornbest.com|skymovieshd.*|topwwnews.com|elsfile.org|javdoe.to|javtc.*|webmusic.*|pics4you.net|kiwiexploits.com|pornxp.com|silverpic.com|suicidepics.com|tanix.net|freeuseporn.com|ukrainesmodels.com|freeadultcomix.com|xxxwebdlxxx.top|uproxy2.biz|crownimg.com|masaporn.xyz|dvdplay.*|mangaraw.org|imgstar.eu|imgsto.*|picdollar.com|pics4upload.com|amateurblog.tv|fashionblog.tv|latinblog.tv|silverblog.tv|tokyoblog.tv|xblog.tv|maxsport.one|sportz.football|streamgo.to|streamgoto.*|amazingstream.net|imwatchingmovies.com|zinchanmanga.com|weaksports.xyz|vidoza.co|vidoza.net|govid.co|up-4ever.net|abcvideo.cc|ouo.io|ouo.press|imgbox.com|pirateproxy.live|thehiddenbay.com|thepiratebay.org|thepiratebay10.org - Salvaged rule by ignoring 1 entity-based domain= option: ceesty.com|corneey.com|destyy.com|festyy.com|gestyy.com|hd-easyporn.com|bolly4umovies.*|pcgamez-download.com|torrentvhd.biz|lovelynudez.com|gayforit.eu|movieston.com|kiwiexploits.com|dropload.io|nsfwyoutube.com|pomvideo.cc|steampiay.cc|vidoza.co|vidoza.net|mixdrop.co|govid.co|up-4ever.net|abcvideo.cc|ouo.io|ouo.press|pirateproxy.live|thehiddenbay.com|thepiratebay10.org|opensubtitles.org - Salvaged rule by ignoring 19 entity-based domain= option: fullxh.com|hamsterix.*|megaxh.com|unlockxh4.com|xhadult2.com|xhadult3.com|xhadult4.com|xhadult5.com|xhamster.*|xhamster10.*|xhamster11.*|xhamster12.*|xhamster13.*|xhamster14.*|xhamster15.*|xhamster16.*|xhamster17.*|xhamster18.*|xhamster19.*|xhamster2.*|xhamster20.*|xhamster3.*|xhamster4.*|xhamster46.com|xhamster5.*|xhamster7.*|xhamster8.*|xhday.com|xhday1.com|xhmoon5.com|xhplanet1.com|xhplanet2.com|xhreal2.com|xhreal3.com|xhtab2.com|xhvictory.com|xhwebsite.com|xhwebsite2.com|xhwide1.com|xhwide8.com - Salvaged rule by ignoring 1 entity-based domain= option: cpmlink.net|mwpaste.com|lusthero.com|22pixx.xyz|goto.com.np|imgtorrnt.in|shrinkearn.com|9ig.de|pingit.im|pngit.live|elil.cc|vev.red|vidop.icu|vidup.io|tubepornclassic.com|ironysub.net|bolly4umovies.*|pcgamez-download.com|curto.win|freeadultcomix.com|xxxwebdlxxx.top|crownimg.com|pomvideo.cc|steampiay.cc|bc.vc|vidoza.co|vidoza.net|pirateproxy.live|thehiddenbay.com|thepiratebay.org|thepiratebay10.org - Salvaged rule by ignoring 1 entity-based domain= option: financemonk.net|dropgalaxy.* - Salvaged rule by ignoring 1 entity-based domain= option: financemonk.net|dropgalaxy.* - Salvaged rule by ignoring 2 entity-based domain= option: gentside.*|ohmymag.*|maxisciences.com - Salvaged rule by ignoring 2 entity-based domain= option: gentside.*|ohmymag.*|maxisciences.com - Salvaged rule by ignoring 2 entity-based domain= option: gentside.*|ohmymag.*|maxisciences.com - Salvaged rule by ignoring 2 entity-based domain= option: gentside.*|ohmymag.*|maxisciences.com - Salvaged rule by ignoring 2 entity-based domain= option: gentside.*|ohmymag.*|maxisciences.com - Salvaged rule by ignoring 1 entity-based domain= option: gentside.co.uk|gentside.com|gentside.de|maxisciences.com|ohmymag.co.uk|ohmymag.com|ohmymag.de|gentside.* - Salvaged rule by ignoring 1 entity-based domain= option: financemonk.net|dropgalaxy.* - Salvaged rule by ignoring 1 entity-based domain= option: financemonk.net|techthematter.xyz|dropgalaxy.* - Salvaged rule by ignoring 1 entity-based domain= option: ladbible.com|tyla.com|unilad.com|gamingbible.* - Salvaged rule by ignoring 1 entity-based domain= option: ladbible.com|tyla.com|unilad.com|gamingbible.* - Salvaged rule by ignoring 1 entity-based domain= option: educatiocenter.online|a2zapk.* - Salvaged rule by ignoring 2 entity-based domain= option: gentside.*|ohmymag.*|maxisciences.com - Salvaged rule by ignoring 1 entity-based domain= option: financemonk.net|dropgalaxy.* - Salvaged rule by ignoring 1 entity-based domain= option: ladbible.com|tyla.com|unilad.com|gamingbible.* - Salvaged rule by ignoring 1 entity-based domain= option: ladbible.com|tyla.com|unilad.com|gamingbible.* - Salvaged rule by ignoring 1 entity-based domain= option: educatiocenter.online|a2zapk.* - Salvaged rule by ignoring 1 entity-based domain= option: cbhours.com|pussyspace.* - Salvaged rule by ignoring 2 entity-based domain= option: acortalo.*|acortar.*|megadescarga.net - Salvaged rule by ignoring 2 entity-based domain= option: gentside.*|ohmymag.*|maxisciences.com - Salvaged rule by ignoring 5 entity-based domain= option: mylink.*|my1ink.*|myl1nk.*|myli3k.*|audiotools.pro|magesy.blog|magesypro.pro|audioztools.com|solvetube.*|promo-visits.site|satoshi-win.xyz|healdad.com|mobitaak.com|gamalk-sehetk.com|allcryptoz.net|crewbase.net|crewus.net|shinbhu.net|shinchu.net|thumb8.net|thumb9.net|topcryptoz.net|uniqueten.net|ultraten.net - Maybe good (regexes): 146 - redirect=: 342 - removeparams= (accepted/discarded): 34/12 - modifyHeaders=: 64 - Unsupported: 243 - Can't salvage rule with only entity-based domain= option: vidmoly.* - Can't salvage rule with only entity-based domain= option: megalink.* - Can't salvage rule with only entity-based domain= option: bg-gledai.* - FilterStrictParty: Strict partyness strict3p not supported - Can't salvage rule with only entity-based domain= option: nishankhatri.* - Can't salvage rule with only entity-based domain= option: oploverz.* - Can't salvage rule with only entity-based domain= option: mangaku.* - Can't salvage rule with only entity-based domain= option: nekopoi.* - Can't salvage rule with only entity-based domain= option: vinaurl.* - Can't salvage rule with only entity-based domain= option: komikcast.* - Can't salvage rule with only entity-based domain= option: movs4u.* - Can't salvage rule with only entity-based domain= option: movieon21.* - Can't salvage rule with only entity-based domain= option: aagmaal.* - Can't salvage rule with only entity-based domain= option: otakudesu.* - Can't salvage rule with only entity-based domain= option: myflixer.* - FilterStrictParty: Strict partyness strict3p not supported - Can't salvage rule with only entity-based domain= option: yts.* - regexFilter is not RE2-compatible: \/[a-z]{4,}\/(?!holly7)(?!siksik7)[0-9a-z]{3,}\d\.\d{1,2}\.\d{1,2}\.[0-9a-f]{32}\.js$ - Can't salvage rule with only entity-based domain= option: ouo.* - Can't salvage rule with only entity-based domain= option: dewimg.*|imgtown.*|imgviu.*|mazpic.*|outletpic.*|picrok.* - Can't salvage rule with only entity-based domain= option: vinaurl.* - Can't salvage rule with only entity-based domain= option: mirrorace.* - Can't salvage rule with only entity-based domain= option: the-voice-of-germany.* - Can't salvage rule with only entity-based domain= option: linkvertise.* - regexFilter is not RE2-compatible: ^https?:\/\/a\.[-0-9a-z]{4,21}\.[a-z]{2,5}\/(?=[a-z]*[0-9A-Z])[0-9a-zA-Z]{5,7}\.js$ - regexFilter is not RE2-compatible: ^https?:\/\/asg\.[-0-9a-z]{4,21}\.[a-z]{2,5}\/(?=[a-z]{0,6}[0-9A-Z])[0-9a-zA-Z]{7}\.js$ - regexFilter is not RE2-compatible: ^https?:\/\/pre\.[0-9a-z]{6,12}\.[a-z]{3,4}\/(?=[a-z]{0,6}[0-9A-Z])[0-9a-zA-Z]{7}\.js$ - regexFilter is not RE2-compatible: ^https?:\/\/oi\.[0-9a-z]{6,12}\.[a-z]{3}\/(?=[a-z]{0,6}[0-9A-Z])[0-9a-zA-Z]{7}\.js$ - Can't salvage rule with only entity-based domain= option: vegamovies.* - Can't salvage rule with only entity-based domain= option: waaaw.*|waaw.* - Can't salvage rule with only entity-based domain= option: vizcloud.*|vizcloud2.* - FilterStrictParty: Strict partyness strict3p not supported - Can't salvage rule with only entity-based domain= option: sexvid.* - Can't salvage rule with only entity-based domain= option: slreamplay.* - Can't salvage rule with only entity-based domain= option: sexwebvideo.* - Can't salvage rule with only entity-based domain= option: dutchycorp.* - regexFilter is not RE2-compatible: ^https:\/\/(?:www\d\.)?[-a-z]{6,}\.(?:com|info|net|org)\/(?=[-_a-zA-Z]{0,42}\d)(?=[-_0-9a-z]{0,42}[A-Z])[-_0-9a-zA-Z]{43}\/\?cid=[-_0-9a-zA-Z]{10,36}(?:&qs\d=\S+)?&(?:s|pub)id=[-_0-9a-z{}]{1,32}(?:&s=0\.\d+)?(?:#\S+)?$ - regexFilter is not RE2-compatible: ^https:\/\/(?:www\d\.)?[-a-z]{6,}\.(?:com|info|net|org)\/(?=[-_a-zA-Z]{0,42}\d)(?=[-_0-9a-z]{0,42}[A-Z])[-_0-9a-zA-Z]{43}\/\?(?:pub|s)id=[-_0-9a-z{}]{1,32}(?:&qs\d=\S+)?&cid=[-_0-9a-zA-Z]{10,36}(?:&s=0\.\d+)?(?:#\S+)?$ - Can't salvage rule with only entity-based domain= option: 1337x.*|1337x.g3g.*|unblockit.*|x1337x.* - regexFilter is not RE2-compatible: \/img\/(?!new).+\.gif - FilterStrictParty: Strict partyness strict1p not supported - Can't salvage rule with only entity-based domain= option: slreamplay.* - Can't salvage rule with only entity-based domain= option: pouvideo.*|povvideo.*|povw1deo.*|povwideo.*|powv1deo.*|powvibeo.*|powvideo.*|powvldeo.* - Can't salvage rule with only entity-based domain= option: pouvideo.*|povvideo.*|povw1deo.*|povwideo.*|powv1deo.*|powvibeo.*|powvideo.*|powvldeo.* - Can't salvage rule with only entity-based domain= option: strcloud.*|streamta.*|streamtape.*|strtape.*|strtapeadblock.*|strtpe.* - Can't salvage rule with only entity-based domain= option: bigkickass.*|kat.*|kat2.*|katbay.*|katfreak.*|kathydra.*|katkickass.*|katkickass.*|kattracker.*|kick4ss.*|kickass-usa.*|kickass.*|kickass2.*|kickassaustralia.*|kickassbay.*|kickassdb.*|kickassfull.*|kickassgo.*|kickasshydra.*|kickassindia.*|kickasskat.*|kickassminds.*|kickassmovies.*|kickasspk.*|kickasst.*|kickasstorrents.*|kickasstorrents2.*|kickasstracker.*|kickasstrusty.*|kickassuk.*|kickassunlocked.*|kickassz.*|kkat.*|kkickass.*|thekat.*|thekickass.*|topkickass.*|torrentkat.*|torrentskickass.* - Can't salvage rule with only entity-based domain= option: isohunt.*|isohunters.*|isohunthydra.*|isohunting.*|isohunts.*|isohuntx.*|isohuntz.*|myisohunt.* - FilterStrictParty: Strict partyness strict1p not supported - Can't salvage rule with only entity-based domain= option: uptomega.* - Can't salvage rule with only entity-based domain= option: uplinkto.* - Can't salvage rule with only entity-based domain= option: link1s.* - Can't salvage rule with only entity-based domain= option: moviesda1.* - Can't salvage rule with only entity-based domain= option: dloader.* - Can't salvage rule with only entity-based domain= option: isaidub.* - Can't salvage rule with only entity-based domain= option: zone-telechargement.* - Can't salvage rule with only entity-based domain= option: earnload.* - regexFilter is not RE2-compatible: ^https?:\/\/[a-z]{7,16}\.com?\/(?=[+\/0-9a-zA-Z]*\+)(?=[+\/a-zA-Z]*\d)(?=[+\/0-9a-z]*[A-Z])[+\/0-9a-zA-Z]{140,}$ - regexFilter is not RE2-compatible: ^https?:\/\/[a-z]{7,16}\.org\/(?=[+\/0-9a-zA-Z]*\+)(?=[+\/a-zA-Z]*\d)(?=[+\/0-9a-z]*[A-Z])[+\/0-9a-zA-Z]{140,}$ - Can't salvage rule with only entity-based domain= option: torlock.*|torlock2.* - Can't salvage rule with only entity-based domain= option: vipleague.* - Can't salvage rule with only entity-based domain= option: my1ink.*|myl1nk.*|myli3k.*|mylink.* - Can't salvage rule with only entity-based domain= option: sxyprn.* - Can't salvage rule with only entity-based domain= option: palimas.* - Can't salvage rule with only entity-based domain= option: vjav.* - Can't salvage rule with only entity-based domain= option: linkshorts.* - Can't salvage rule with only entity-based domain= option: mazpic.* - Can't salvage rule with only entity-based domain= option: picrok.* - Can't salvage rule with only entity-based domain= option: imgviu.* - Can't salvage rule with only entity-based domain= option: outletpic.* - Can't salvage rule with only entity-based domain= option: dewimg.* - Can't salvage rule with only entity-based domain= option: imgtown.* - Can't salvage rule with only entity-based domain= option: oploverz.* - Can't salvage rule with only entity-based domain= option: readcomiconline.* - Can't salvage rule with only entity-based domain= option: adsrt.* - Can't salvage rule with only entity-based domain= option: animeflv.* - Can't salvage rule with only entity-based domain= option: kiss-anime.* - Can't salvage rule with only entity-based domain= option: japscan.* - Can't salvage rule with only entity-based domain= option: downloadhub.* - Can't salvage rule with only entity-based domain= option: 9xbuddy.* - Can't salvage rule with only entity-based domain= option: viprow.* - Can't salvage rule with only entity-based domain= option: anitube.* - Can't salvage rule with only entity-based domain= option: mixdroop.*|mixdrop.*|mixdrp.* - Can't salvage rule with only entity-based domain= option: dramacool9.* - Can't salvage rule with only entity-based domain= option: hdfriday.* - Can't salvage rule with only entity-based domain= option: extramovies.* - Can't salvage rule with only entity-based domain= option: atomixhq.*|pctfenix.* - Can't salvage rule with only entity-based domain= option: shortearn.* - Can't salvage rule with only entity-based domain= option: okstream.* - Can't salvage rule with only entity-based domain= option: megavideo.* - Can't salvage rule with only entity-based domain= option: tmearn.* - Can't salvage rule with only entity-based domain= option: leechall.* - Can't salvage rule with only entity-based domain= option: allcalidad.* - Can't salvage rule with only entity-based domain= option: movieshub.* - Can't salvage rule with only entity-based domain= option: dailysport.* - Can't salvage rule with only entity-based domain= option: mkvcinemas.* - Can't salvage rule with only entity-based domain= option: pelispedia.* - Can't salvage rule with only entity-based domain= option: linkviet.* - Can't salvage rule with only entity-based domain= option: btdb.* - Can't salvage rule with only entity-based domain= option: animesvision.* - Can't salvage rule with only entity-based domain= option: miniurl.* - Can't salvage rule with only entity-based domain= option: uploadhub.* - Can't salvage rule with only entity-based domain= option: bollyflix.* - Can't salvage rule with only entity-based domain= option: veranime.*|verhentai.* - Can't salvage rule with only entity-based domain= option: shortzzy.* - Can't salvage rule with only entity-based domain= option: xtits.* - Can't salvage rule with only entity-based domain= option: shorttey.* - Can't salvage rule with only entity-based domain= option: hdmovieplus.* - Can't salvage rule with only entity-based domain= option: img4fap.* - Can't salvage rule with only entity-based domain= option: elitetorrent.* - Can't salvage rule with only entity-based domain= option: lite-link.* - Can't salvage rule with only entity-based domain= option: adcorto.* - Can't salvage rule with only entity-based domain= option: streamhub.* - Can't salvage rule with only entity-based domain= option: 720pstream.* - Can't salvage rule with only entity-based domain= option: toonanime.* - Can't salvage rule with only entity-based domain= option: buffstreams.* - Can't salvage rule with only entity-based domain= option: cinemakottaga.* - Can't salvage rule with only entity-based domain= option: hog.* - Can't salvage rule with only entity-based domain= option: samehadaku.* - Can't salvage rule with only entity-based domain= option: atishmkv.* - Can't salvage rule with only entity-based domain= option: watchomovies.* - Can't salvage rule with only entity-based domain= option: hdhub4u.* - Can't salvage rule with only entity-based domain= option: livetvon.* - Can't salvage rule with only entity-based domain= option: sports-stream.* - regexFilter is not RE2-compatible: ^https?:\/\/[0-9a-z]{4,8}\.autos\/(?=[a-z]{0,6}[0-9A-Z])[0-9a-zA-Z]{7}\.js$ - regexFilter is not RE2-compatible: ^https?:\/\/[0-9a-z]{4,8}\.beauty\/(?=[a-z]{0,6}[0-9A-Z])[0-9a-zA-Z]{7}\.js$ - regexFilter is not RE2-compatible: ^https?:\/\/[0-9a-z]{4,8}\.lol\/(?=[a-z]{0,6}[0-9A-Z])[0-9a-zA-Z]{7}\.js$ - regexFilter is not RE2-compatible: ^https?:\/\/[0-9a-z]{4,8}\.mom\/(?=[a-z]{0,6}[0-9A-Z])[0-9a-zA-Z]{7}\.js$ - regexFilter is not RE2-compatible: ^https?:\/\/[0-9a-z]{4,8}\.pro\/(?=[a-z]{0,6}[0-9A-Z])[0-9a-zA-Z]{7}\.js$ - regexFilter is not RE2-compatible: ^https?:\/\/[0-9a-z]{4,8}\.xyz\/(?=[a-z]{0,6}[0-9A-Z])[0-9a-zA-Z]{7}\.js$ - regexFilter is not RE2-compatible: ^https?:\/\/[a-z]{8,15}\.com?\/(?=[0-9a-zA-Z]*%)(?=[%a-zA-Z]*\d)(?=[%0-9a-z]*[A-Z])[%0-9a-zA-Z]{170,}$ - regexFilter is not RE2-compatible: ^https:\/\/(?:[a-z]{2}\.)?[a-z]{7,14}\.com\/r(?=[a-z]*[0-9A-Z])[0-9A-Za-z]{10,16}\/[A-Za-z]{5}$ - Can't salvage rule with only entity-based domain= option: sdmoviespoint.* - Can't salvage rule with only entity-based domain= option: torrentgalaxy.* - regexFilter is not RE2-compatible: ^https:\/\/[0-9a-z]{7,25}\.com\/v2(?:\/0\/)?(?=[-_0-9a-z]{0,84}[A-Z])(?=[-_a-zA-Z]{0,84}[0-9])[-_0-9a-zA-Z]{54,85}(#\?v=[0-9a-f]{32})?$ - Can't salvage rule with only entity-based domain= option: mazpic.* - Can't salvage rule with only entity-based domain= option: picrok.* - Can't salvage rule with only entity-based domain= option: imgviu.* - Can't salvage rule with only entity-based domain= option: outletpic.* - Can't salvage rule with only entity-based domain= option: dewimg.* - Can't salvage rule with only entity-based domain= option: imgtown.* - Can't salvage rule with only entity-based domain= option: btdb.* - regexFilter is not RE2-compatible: ^https?:\/\/(?:[a-z]{2}\.)?[0-9a-z]{7,16}\.com\/[a-z](?=[a-z]{0,25}[0-9A-Z])[0-9a-zA-Z]{3,26}\/(?:[1-6]\d{4}|[3-9]\d{3})\??(?:_=\d+|v=\d)?$ - regexFilter is not RE2-compatible: ^https?:\/\/(?:[a-z]{2}\.)?[0-9a-z]{7,16}\.website\/[a-z](?=[a-z]{0,25}[0-9A-Z])[0-9a-zA-Z]{3,26}\/(?:[1-6]\d{4}|[3-9]\d{3})\??(?:_=\d+|v=\d)?$ - regexFilter is not RE2-compatible: ^https:\/\/[0-9a-z]{7,25}\.com\/v2(?:\/0\/)?(?=[-_0-9a-z]{0,84}[A-Z])(?=[-_a-zA-Z]{0,84}[0-9])[-_0-9a-zA-Z]{54,85}(#\?v=[0-9a-f]{32})?$ - Can't salvage rule with only entity-based domain= option: sxyprn.* - Can't salvage rule with only entity-based domain= option: txxx.* - Can't salvage rule with only entity-based domain= option: mazpic.* - Can't salvage rule with only entity-based domain= option: picrok.* - Can't salvage rule with only entity-based domain= option: imgviu.* - Can't salvage rule with only entity-based domain= option: outletpic.* - Can't salvage rule with only entity-based domain= option: dewimg.* - Can't salvage rule with only entity-based domain= option: imgtown.* - Can't salvage rule with only entity-based domain= option: pouvideo.*|povvideo.*|povvldeo.*|povw1deo.*|povwideo.*|powv1deo.*|powvibeo.*|powvideo.*|powvldeo.* - Can't salvage rule with only entity-based domain= option: btdb.* - regexFilter is not RE2-compatible: ^https?:\/\/(?:[a-z]{2}\.)?[0-9a-z]{7,16}\.com\/[a-z](?=[a-z]{0,25}[0-9A-Z])[0-9a-zA-Z]{3,26}\/(?:[1-6]\d{4}|[3-9]\d{3})\??(?:_=\d+|v=\d)?$ - regexFilter is not RE2-compatible: ^https?:\/\/(?:[a-z]{2}\.)?[0-9a-z]{7,16}\.website\/[a-z](?=[a-z]{0,25}[0-9A-Z])[0-9a-zA-Z]{3,26}\/(?:[1-6]\d{4}|[3-9]\d{3})\??(?:_=\d+|v=\d)?$ - Can't salvage rule with only entity-based domain= option: xtits.* - Can't salvage rule with only entity-based domain= option: animesa.* - regexFilter is not RE2-compatible: ^https?:\/\/[a-z]{7,16}\.com?\/(?=[+\/0-9a-zA-Z]*\+)(?=[+\/a-zA-Z]*\d)(?=[+\/0-9a-z]*[A-Z])[+\/0-9a-zA-Z]{400,}$ - regexFilter is not RE2-compatible: ^https?:\/\/[a-z]{7,16}\.org\/(?=[+\/0-9a-zA-Z]*\+)(?=[+\/a-zA-Z]*\d)(?=[+\/0-9a-z]*[A-Z])[+\/0-9a-zA-Z]{400,}$ - regexFilter is not RE2-compatible: ^https:\/\/[a-z]{7}\.com\/sub\/(?=[a-z]{0,9}[0-9A-Z])[0-9A-Za-z]{10}$ - Can't salvage rule with only entity-based domain= option: hqq.* - Can't salvage rule with only entity-based domain= option: bloomberg.* - Can't salvage rule with only entity-based domain= option: my1ink.*|myl1nk.*|myli3k.*|mylink.* - Can't salvage rule with only entity-based domain= option: my1ink.*|myl1nk.*|myli3k.*|mylink.* - Can't salvage rule with only entity-based domain= option: hqq.* - Can't salvage rule with only entity-based domain= option: einthusan.* - Can't salvage rule with only entity-based domain= option: gentside.*|ohmymag.* - Can't salvage rule with only entity-based domain= option: gentside.*|ohmymag.* - Can't salvage rule with only entity-based domain= option: pasty.* - Can't salvage rule with only entity-based domain= option: pasty.* - Can't salvage rule with only entity-based domain= option: wstream.* - Can't salvage rule with only entity-based domain= option: viafree.* - Can't salvage rule with only entity-based domain= option: hotfrog.* - Can't salvage rule with only entity-based domain= option: goodstream.* - Can't salvage rule with only entity-based domain= option: now.* - Can't salvage rule with only entity-based domain= option: bloomberg.* - Can't salvage rule with only entity-based domain= option: bloomberg.* - Can't salvage rule with only entity-based domain= option: bloomberg.* - Can't salvage rule with only entity-based domain= option: allestoringen.*|downdetector.*|xn--allestrungen-9ib.* - Can't salvage rule with only entity-based domain= option: allestoringen.*|downdetector.*|xn--allestrungen-9ib.* - Can't salvage rule with only entity-based domain= option: allestoringen.*|downdetector.*|xn--allestrungen-9ib.* - Can't salvage rule with only entity-based domain= option: tube8.* - Can't salvage rule with only entity-based domain= option: audible.* - Can't salvage rule with only entity-based domain= option: savethechildren.* - Can't salvage rule with only entity-based domain= option: discoveryplus.* - Can't salvage rule with only entity-based domain= option: viafree.* - Can't salvage rule with only entity-based domain= option: streamingcommunity.* - Can't salvage rule with only entity-based domain= option: streamingcommunity.* - Can't salvage rule with only entity-based domain= option: filepress.* - Can't salvage rule with only entity-based domain= option: bloomberg.* - Can't salvage rule with only entity-based domain= option: soap2day.* - Can't salvage rule with only entity-based domain= option: soap2day.* - Can't salvage rule with only entity-based domain= option: pussyspace.* - Can't salvage rule with only entity-based domain= option: pussyspace.* - Can't salvage rule with only entity-based domain= option: pussyspace.* - Can't salvage rule with only entity-based domain= option: pussyspace.* - Can't salvage rule with only entity-based domain= option: pussyspace.* - Can't salvage rule with only entity-based domain= option: pussyspace.* - Can't salvage rule with only entity-based domain= option: slreamplay.* - Can't salvage rule with only entity-based domain= option: pouvideo.*|povvideo.*|povw1deo.*|povwideo.*|powv1deo.*|powvibeo.*|powvideo.*|powvldeo.* - Unsupported regex-based removeParam: /utm_source|utm_campaign|utm_content/ - Unsupported regex-based removeParam: /utm_source|utm_campaign|utm_content/ - Unsupported regex-based removeParam: /utm_source|utm_campaign|utm_content|utm_term|wr/ - Unpatchable redirect filter: abp-resource:blank-mp4 - Unpatchable redirect filter: abp-resource:blank-mp4 - Unsupported regex-based removeParam: /^\/_ui\/desktop\/common\/js\/uiAnalytics\// - Can't salvage rule with only entity-based domain= option: 1movies.* - Can't salvage rule with only entity-based domain= option: 1movies.* - Unsupported regex-based removeParam: /^ad/ - Unsupported regex-based removeParam: /^ad/ - Can't salvage rule with only entity-based domain= option: discoveryplus.* - Unsupported regex-based removeParam: /^(cookie|ga_|u_)/ - Unsupported regex-based removeParam: /^((?!SMIL|formats).)*$/ - Unsupported regex-based removeParam: /^((?!formats|profile).)*$/ - Unsupported modifier exception - Unsupported modifier exception - Can't salvage rule with only entity-based domain= option: isohunt.*|isohunters.*|isohunthydra.*|isohunting.*|isohunts.*|isohuntx.*|isohuntz.*|myisohunt.* - Can't salvage rule with only entity-based domain= option: torrentproject2.* - Unsupported regex-based removeParam: /^(?:correlator|f[cr-w]|p[e-sv]|[abdeg-or-x])/ - Unsupported regex-based removeParam: /^(?:correlator|f[cr-w]|p[e-sv]|u_|ga_|url|dt|adk)/ - Unsupported modifier exception - Unsupported modifier exception - Unsupported modifier exception - Unsupported modifier exception - Can't salvage rule with only entity-based domain= option: the-voice-of-germany.* - Can't salvage rule with only entity-based domain= option: discoveryplus.* - Can't salvage rule with only entity-based domain= option: pobre.* - Unsupported modifier exception - Can't salvage rule with only entity-based domain= option: bigkickass.*|kat.*|kat2.*|katbay.*|katfreak.*|kathydra.*|katkickass.*|katkickass.*|kattracker.*|kick4ss.*|kickass-usa.*|kickass.*|kickass2.*|kickassaustralia.*|kickassbay.*|kickassdb.*|kickassfull.*|kickassgo.*|kickasshydra.*|kickassindia.*|kickasskat.*|kickassminds.*|kickassmovies.*|kickasspk.*|kickasst.*|kickasstorrents2.*|kickasstracker.*|kickasstrusty.*|kickassuk.*|kickassunlocked.*|kickassz.*|kkat.*|kkickass.*|thekat.*|thekickass.*|topkickass.*|torrentkat.*|torrentskickass.* - Can't salvage rule with only entity-based domain= option: isohunt.*|isohunters.*|isohunthydra.*|isohunting.*|isohunts.*|isohuntx.*|isohuntz.*|myisohunt.* - Can't salvage rule with only entity-based domain= option: torrentproject2.* - Unsupported modifier exception - Unsupported modifier exception - Unsupported modifier exception - Unsupported regex-based removeParam: /^(?!offer_id=).*/ - Unsupported modifier exception - Can't salvage rule with only entity-based domain= option: empire-stream.*|empire-streaming.* - Unsupported modifier exception - Unsupported modifier exception - Can't salvage rule with only entity-based domain= option: empire-stream.*|empire-streaming.* - Unsupported modifier exception - regexFilter is not RE2-compatible: ^https?:\/\/(?:[a-z]{2}\.)?[0-9a-z]{5,16}\.[a-z]{3,7}\/[a-z](?=[a-z]{0,25}[0-9A-Z])[0-9a-zA-Z]{3,26}\/\d{4,5}(?:\?[_v]=\d+)?$ -CSS-generic: 15682 plain CSS selectors -CSS-generic-high: 553 plain CSS selectors -CSS-specific: 9630 distinct filters - Combined into 9055 distinct hostnames - Combined into 491 distinct entities -CSS-declarative: 421 distinct filters - Combined into 622 distinct hostnames - Combined into 51 distinct entities -Procedural-related distinct filters: 866 distinct combined selectors - Combined into 1381 distinct hostnames - Combined into 201 distinct entities -============================ -Listset for 'alb-0': - Fetching remote https://raw.githubusercontent.com/AnXh3L0/blocklist/master/albanian-easylist-addition/Albania.txt -Input filter count: 34 - Accepted filter count: 31 - Rejected filter count: 0 -Output rule count: 28 - Plain good: 25 - - Maybe good (regexes): 0 - redirect=: 0 - removeparams= (accepted/discarded): 0/0 - modifyHeaders=: 0 - Unsupported: 3 - Can't salvage rule with only entity-based domain= option: filma24.* - Can't salvage rule with only entity-based domain= option: www.filma24.* - Can't salvage rule with only entity-based domain= option: filma24.* -CSS-generic: 6 plain CSS selectors -CSS-specific: 239 distinct filters - Combined into 104 distinct hostnames - Combined into 1 distinct entities -CSS-declarative: 3 distinct filters - Combined into 2 distinct hostnames - Combined into 1 distinct entities -Procedural-related distinct filters: 4 distinct combined selectors - Combined into 4 distinct hostnames - Combined into 0 distinct entities -============================ -Listset for 'bgr-0': - Fetching remote https://stanev.org/abp/adblock_bg.txt -Input filter count: 661 - Accepted filter count: 661 - Rejected filter count: 0 -Output rule count: 650 - Plain good: 650 - - Maybe good (regexes): 0 - redirect=: 0 - removeparams= (accepted/discarded): 0/0 - modifyHeaders=: 0 - Unsupported: 0 - -CSS-generic: 4 plain CSS selectors -CSS-specific: 350 distinct filters - Combined into 175 distinct hostnames - Combined into 0 distinct entities -============================ -Listset for 'chn-0': - Fetching remote https://filters.adtidy.org/extension/ublock/filters/224.txt -Input filter count: 15280 - Accepted filter count: 15215 - Rejected filter count: 1 -Output rule count: 6478 - Pruning requestDomains: from 6276 to 6268 - Pruning requestDomains: from 520 to 518 - Plain good: 6413 - - Maybe good (regexes): 11 - redirect=: 41 - removeparams= (accepted/discarded): 0/0 - modifyHeaders=: 8 - Unsupported: 5 - regexFilter is not RE2-compatible: ^(?!.*(sharecast.ws|bunnycdn.ru|bootstrapcdn.com|cdn.ampproject.org|cloudflare.com|cdn.staticfile.org|disqus.com|disquscdn.com|dmca.com|ebacdn.com|facebook.net|fastlylb.net|fbcdn.net|fluidplayer.com|fontawesome.com|github.io|google.com|googleapis.com|googletagmanager.com|gstatic.com|jquery.com|jsdelivr.net|jwpcdn.com|jwplatform.com|polyfill.io|recaptcha.net|shrink.pe|twitter.com|ulogin.ru|unpkg.com|userapi.com|vidazoo.com|vk.com|yandex.|yastatic.net|ytimg.com|zencdn.net|player|youtube.com|cackle.me|googleoptimize.com|vuukle.com|chatango.com|twimg.com|google-analytics.com|hcaptcha.com|raincaptcha.com|media-imdb.com|blogger.com|hwcdn.net|instagram.com|wp.com|imgsmail.ru)).*$ - Unsupported modifier exception - Unsupported modifier exception - Invalid network filter in chn-0: @@||ad.alimama.com^$genericblock - Invalid network filter in chn-0: @@||cmechina.net^$genericblock -CSS-generic: 754 plain CSS selectors -CSS-generic-high: 403 plain CSS selectors -CSS-specific: 6507 distinct filters - Combined into 2795 distinct hostnames - Combined into 1 distinct entities -CSS-declarative: 77 distinct filters - Combined into 70 distinct hostnames - Combined into 0 distinct entities -Procedural-related distinct filters: 92 distinct combined selectors - Combined into 65 distinct hostnames - Combined into 0 distinct entities -============================ -Listset for 'cze-0': - Fetching remote https://raw.githubusercontent.com/tomasko126/easylistczechandslovak/master/filters.txt - Fetching remote https://raw.githubusercontent.com/tomasko126/easylistczechandslovak/master/filters_ublock.txt -Input filter count: 228 - Accepted filter count: 228 - Rejected filter count: 0 -Output rule count: 120 - Plain good: 115 - - Maybe good (regexes): 0 - redirect=: 4 - removeparams= (accepted/discarded): 0/0 - modifyHeaders=: 1 - Unsupported: 0 - -CSS-generic: 36 plain CSS selectors -CSS-generic-high: 4 plain CSS selectors -CSS-specific: 231 distinct filters - Combined into 167 distinct hostnames - Combined into 0 distinct entities -CSS-declarative: 8 distinct filters - Combined into 11 distinct hostnames - Combined into 0 distinct entities -Procedural-related distinct filters: 7 distinct combined selectors - Combined into 7 distinct hostnames - Combined into 0 distinct entities -============================ -Listset for 'deu-0': - Fetching remote https://easylist.to/easylistgermany/easylistgermany.txt -Input filter count: 2340 - Accepted filter count: 2340 - Rejected filter count: 0 -Output rule count: 1783 - Plain good: 1777 - - Maybe good (regexes): 4 - redirect=: 0 - removeparams= (accepted/discarded): 0/0 - modifyHeaders=: 0 - Unsupported: 2 - Unpatchable redirect filter: abp-resource:blank-mp4 - Invalid network filter in deu-0: @@||gofeminin.de^$genericblock -CSS-generic: 356 plain CSS selectors -CSS-generic-high: 34 plain CSS selectors -CSS-specific: 2398 distinct filters - Combined into 1772 distinct hostnames - Combined into 0 distinct entities -Procedural-related distinct filters: 84 distinct combined selectors - Combined into 60 distinct hostnames - Combined into 0 distinct entities -============================ -Listset for 'fin-0': - Fetching remote https://raw.githubusercontent.com/finnish-easylist-addition/finnish-easylist-addition/gh-pages/Finland_adb.txt - Fetching remote https://raw.githubusercontent.com/finnish-easylist-addition/finnish-easylist-addition/gh-pages/Finland_adb_uBO_extras.txt -Input filter count: 177 - Accepted filter count: 177 - Rejected filter count: 0 -Output rule count: 157 - Plain good: 149 - - Maybe good (regexes): 3 - redirect=: 4 - removeparams= (accepted/discarded): 1/0 - modifyHeaders=: 0 - Unsupported: 0 - -CSS-generic: 53 plain CSS selectors -CSS-generic-high: 14 plain CSS selectors -CSS-specific: 1019 distinct filters - Combined into 556 distinct hostnames - Combined into 0 distinct entities -CSS-declarative: 24 distinct filters - Combined into 16 distinct hostnames - Combined into 0 distinct entities -Procedural-related distinct filters: 149 distinct combined selectors - Combined into 114 distinct hostnames - Combined into 0 distinct entities -============================ -Listset for 'fra-0': - Fetching remote https://filters.adtidy.org/extension/ublock/filters/16.txt -Input filter count: 18886 - Accepted filter count: 18813 - Rejected filter count: 58 -Output rule count: 6567 - Pruning requestDomains: from 7820 to 7809 - Plain good: 6514 - Salvaged rule by ignoring 2 entity-based domain= option: gentside.*|ohmymag.*|maxisciences.com - Salvaged rule by ignoring 2 entity-based domain= option: gentside.*|ohmymag.*|maxisciences.com - Maybe good (regexes): 7 - redirect=: 32 - removeparams= (accepted/discarded): 0/0 - modifyHeaders=: 5 - Unsupported: 9 - regexFilter is not RE2-compatible: ^https?:\/\/vitamiiin\.com\/(?!wp-content|uploads|plugins|themes)(.*) - regexFilter is not RE2-compatible: ^(?!.*(sharecast.ws|bunnycdn.ru|bootstrapcdn.com|cdn.ampproject.org|cloudflare.com|cdn.staticfile.org|disqus.com|disquscdn.com|dmca.com|ebacdn.com|facebook.net|fastlylb.net|fbcdn.net|fluidplayer.com|fontawesome.com|github.io|google.com|googleapis.com|googletagmanager.com|gstatic.com|jquery.com|jsdelivr.net|jwpcdn.com|jwplatform.com|polyfill.io|recaptcha.net|shrink.pe|twitter.com|ulogin.ru|unpkg.com|userapi.com|vidazoo.com|vk.com|yandex.|yastatic.net|ytimg.com|zencdn.net|player|youtube.com|cackle.me|googleoptimize.com|vuukle.com|chatango.com|twimg.com|google-analytics.com|hcaptcha.com|raincaptcha.com|media-imdb.com|blogger.com|hwcdn.net|instagram.com|wp.com|imgsmail.ru)).*$ - Can't salvage rule with only entity-based domain= option: downdetector.* - Can't salvage rule with only entity-based domain= option: vidembed.* - Can't salvage rule with only entity-based domain= option: vidembed.* - Can't salvage rule with only entity-based domain= option: fmovies.* - Can't salvage rule with only entity-based domain= option: fmovies.* - Unsupported modifier exception - Unsupported modifier exception -CSS-generic: 2419 plain CSS selectors -CSS-generic-high: 522 plain CSS selectors -CSS-specific: 2218 distinct filters - Combined into 1693 distinct hostnames - Combined into 3 distinct entities -CSS-declarative: 42 distinct filters - Combined into 43 distinct hostnames - Combined into 1 distinct entities -Procedural-related distinct filters: 97 distinct combined selectors - Combined into 117 distinct hostnames - Combined into 0 distinct entities -============================ -Listset for 'grc-0': - Fetching remote https://www.void.gr/kargig/void-gr-filters.txt -Input filter count: 451 - Accepted filter count: 451 - Rejected filter count: 0 -Output rule count: 416 - Plain good: 416 - - Maybe good (regexes): 0 - redirect=: 0 - removeparams= (accepted/discarded): 0/0 - modifyHeaders=: 0 - Unsupported: 0 - -CSS-generic: 3 plain CSS selectors -CSS-generic-high: 5 plain CSS selectors -CSS-specific: 533 distinct filters - Combined into 162 distinct hostnames - Combined into 0 distinct entities -CSS-declarative: 5 distinct filters - Combined into 5 distinct hostnames - Combined into 0 distinct entities -Procedural-related distinct filters: 2 distinct combined selectors - Combined into 2 distinct hostnames - Combined into 0 distinct entities -============================ -Listset for 'hrv-0': - Fetching remote https://raw.githubusercontent.com/DandelionSprout/adfilt/master/SerboCroatianList.txt -Input filter count: 53 - Accepted filter count: 53 - Rejected filter count: 0 -Output rule count: 44 - Plain good: 44 - - Maybe good (regexes): 0 - redirect=: 0 - removeparams= (accepted/discarded): 0/0 - modifyHeaders=: 0 - Unsupported: 0 - -CSS-generic: 11 plain CSS selectors -CSS-specific: 249 distinct filters - Combined into 149 distinct hostnames - Combined into 0 distinct entities -CSS-declarative: 1 distinct filters - Combined into 1 distinct hostnames - Combined into 0 distinct entities -Procedural-related distinct filters: 2 distinct combined selectors - Combined into 2 distinct hostnames - Combined into 0 distinct entities -============================ -Listset for 'hun-0': - Fetching remote https://raw.githubusercontent.com/hufilter/hufilter/master/hufilter-ublock.txt -Input filter count: 325 - Accepted filter count: 325 - Rejected filter count: 0 -Output rule count: 200 - Plain good: 197 - - Maybe good (regexes): 1 - redirect=: 2 - removeparams= (accepted/discarded): 0/0 - modifyHeaders=: 0 - Unsupported: 0 - -CSS-generic: 60 plain CSS selectors -CSS-generic-high: 15 plain CSS selectors -CSS-specific: 1020 distinct filters - Combined into 469 distinct hostnames - Combined into 0 distinct entities -CSS-declarative: 23 distinct filters - Combined into 21 distinct hostnames - Combined into 0 distinct entities -Procedural-related distinct filters: 21 distinct combined selectors - Combined into 22 distinct hostnames - Combined into 0 distinct entities -============================ -Listset for 'idn-0': - Fetching remote https://raw.githubusercontent.com/ABPindo/indonesianadblockrules/master/subscriptions/abpindo.txt -Input filter count: 4286 - Accepted filter count: 4283 - Rejected filter count: 0 -Output rule count: 3017 - Plain good: 3017 - - Maybe good (regexes): 0 - redirect=: 0 - removeparams= (accepted/discarded): 0/0 - modifyHeaders=: 0 - Unsupported: 0 - -CSS-generic: 239 plain CSS selectors -CSS-generic-high: 3609 plain CSS selectors -CSS-specific: 847 distinct filters - Combined into 690 distinct hostnames - Combined into 12 distinct entities -CSS-declarative: 2 distinct filters - Combined into 2 distinct hostnames - Combined into 0 distinct entities -Procedural-related distinct filters: 1 distinct combined selectors - Combined into 1 distinct hostnames - Combined into 0 distinct entities -============================ -Listset for 'ind-0': - Fetching remote https://easylist-downloads.adblockplus.org/indianlist.txt -Input filter count: 4882 - Accepted filter count: 4882 - Rejected filter count: 0 -Output rule count: 4836 - Plain good: 4836 - - Maybe good (regexes): 0 - redirect=: 0 - removeparams= (accepted/discarded): 0/0 - modifyHeaders=: 0 - Unsupported: 0 - -CSS-specific: 3728 distinct filters - Combined into 4088 distinct hostnames - Combined into 0 distinct entities -Procedural-related distinct filters: 93 distinct combined selectors - Combined into 89 distinct hostnames - Combined into 0 distinct entities -============================ -Listset for 'irn-0': - Fetching remote https://raw.githubusercontent.com/MasterKia/PersianBlocker/main/PersianBlocker.txt -Input filter count: 1103 - Accepted filter count: 1103 - Rejected filter count: 0 -Output rule count: 605 - Pruning requestDomains: from 215 to 210 - Plain good: 564 - - Maybe good (regexes): 0 - redirect=: 0 - removeparams= (accepted/discarded): 12/26 - modifyHeaders=: 1 - Unsupported: 28 - FilterStrictParty: Strict partyness strict3p not supported - FilterStrictParty: Strict partyness strict3p not supported - Unsupported regex-based removeParam: /^utm_/ - Unsupported regex-based removeParam: /^promo/ - Unsupported regex-based removeParam: /^utm_/ - Unsupported regex-based removeParam: /promo/ - Unsupported regex-based removeParam: /^utm_/ - Unsupported regex-based removeParam: /promo/ - Unsupported regex-based removeParam: /^utm_/ - Unsupported regex-based removeParam: /^utm_/ - Unsupported regex-based removeParam: /^utm_/ - Unsupported regex-based removeParam: /^utm_/ - Unsupported regex-based removeParam: /^utm_/ - Unsupported regex-based removeParam: /^utm_/ - Unsupported regex-based removeParam: /^itm_/ - Unsupported regex-based removeParam: /^utm_/ - Unsupported regex-based removeParam: /^utm_/ - Unsupported regex-based removeParam: /^utm_/ - Unsupported regex-based removeParam: /^utm_/ - Unsupported regex-based removeParam: /^utm_/ - Unsupported regex-based removeParam: /^utm_/ - Unsupported regex-based removeParam: /^utm_/ - Unsupported regex-based removeParam: /^utm_/ - Unsupported regex-based removeParam: /^utm_/ - Unsupported regex-based removeParam: /^utm_/ - Unsupported regex-based removeParam: /^utm_/ - Unsupported regex-based removeParam: /^utm_/ - Unsupported regex-based removeParam: /^utm_|tatoken/ -CSS-generic: 17 plain CSS selectors -CSS-specific: 742 distinct filters - Combined into 391 distinct hostnames - Combined into 0 distinct entities -CSS-declarative: 226 distinct filters - Combined into 53 distinct hostnames - Combined into 1 distinct entities -Procedural-related distinct filters: 163 distinct combined selectors - Combined into 119 distinct hostnames - Combined into 1 distinct entities -============================ -Listset for 'isl-0': - Fetching remote https://adblock.gardar.net/is.abp.txt -Input filter count: 68 - Accepted filter count: 68 - Rejected filter count: 0 -Output rule count: 68 - Plain good: 68 - - Maybe good (regexes): 0 - redirect=: 0 - removeparams= (accepted/discarded): 0/0 - modifyHeaders=: 0 - Unsupported: 0 - -CSS-generic-high: 1 plain CSS selectors -CSS-specific: 121 distinct filters - Combined into 40 distinct hostnames - Combined into 0 distinct entities -============================ -Listset for 'isr-0': - Fetching remote https://raw.githubusercontent.com/easylist/EasyListHebrew/master/EasyListHebrew.txt - Fetching remote https://raw.githubusercontent.com/easylist/EasyListHebrew/master/EasyListHebrew-uBO.txt -Input filter count: 703 - Accepted filter count: 702 - Rejected filter count: 1 -Output rule count: 274 - Plain good: 249 - - Maybe good (regexes): 4 - redirect=: 10 - removeparams= (accepted/discarded): 0/0 - modifyHeaders=: 1 - Unsupported: 10 - regexFilter is not RE2-compatible: haaretz\.co\.il\/(?!.*\.(js)($|\?)).* - regexFilter is not RE2-compatible: ^(?![a-zA-Z0-9\-]+:\/+(api-mail|dal|dcx|isc|iscwne|6days|animals|astrology|b|buzzit|calendar|cars|celebs|e|elections|euro|fashion|finance|food|forums|fun|healthy|home|judaism|kids|mag|maps|milon|movies|mundial|nadlan|news|nick|olympics|search|sports|tags|tech|translate|travel|tv-guide|tv|usaelections|viva|vod|weather|www)\.walla\.co\.il\.?(\/|:|$))^[a-zA-Z0-9\-]+:\/+([a-zA-Z0-9\-]+\.)+walla\.co\.il\.?(\/|:|$) - regexFilter is not RE2-compatible: ^(?![a-zA-Z0-9\-]+:\/+www\.sheee\.co\.il\.?(\/|:|$))^[a-zA-Z0-9\-]+:\/+([a-zA-Z0-9\-]+\.)+sheee\.co\.il\.?(\/|:|$) - regexFilter is not RE2-compatible: ^(?![a-zA-Z0-9\-]+\:\/+([^\/\:\.]+\.)*((gov|idf|muni|ac|k12|net)\.il|(google|blogspot|phpbb|minifier|enable|nagich|nagishplus|nagishly|livedns|user-a|emap|23tv|glz|icast|ecast|mediacast|live1|siz|gif|meduzot|telesport|teleline|livegames|2net|weather2day|mekorotapp|e-vrit|fav|slash|rabbi|kaplanopensource|systematics|israelcoronamap|icdn|wcdn|wallanews|wallashops|wallatours|wallaart|wallaprint|hamal|sheee|globes|madlan|yad2|mipo|b144|bezeq|yes|fxp|nick|d|maariv|iol|dominos|magazineitsuv|doctors|mishpati|lawguide|arcdb|zebarur|wlcdn|linicom|erate)\.co\.il|(kan|kankids|makan|iba|oref|iaf|parks|imj|nli|bh|isoc|hebrew-academy|kineret|teva|zavit|ip6|profile)\.org\.il)\.?([\/\:]|$))^[a-zA-Z0-9\-]+\:\/+[^\/\:]+\.il\.?([\/\:]|$) - regexFilter is not RE2-compatible: ^(?![a-zA-Z0-9\-]+\:\/+([^\/\:\.]+\.)*(facebook|fbcdn|threads|dmcdn|slideshare|cloudfront|cloudflare|fastly|fastlylb|gammacdn|edgecastcdn|footprint|incapdns|cloudapp|brightcove|jsdelivr|akamai|akamaihd|akamaized|akamaiedge|akahost|ctedgecdn|2mdn|edgesuite|azurewebsites|azureedge|windows|hwcdn|zencdn|llnwd|llnwi|boltdns|msecnd|bitsngo|nocookie|datatables|docdroid|iframely|algolia|anvato|maphub|dwcdn|typekit|edgefonts|recaptcha|ampproject|viafoura|yastatic|yahoodns|behance|darksky|google|twitchcdn|ttvnw|jtvnw|dailyuploads|deviantart|8ch|b-cdn|vodgc|hlsplayer|streamlock|web-view|streamgates|cdnwz|playgorithm|vidiom|radwarecloud|f-static|chartbeat|doubleclick|advsnx|sc-static|artipbox)\.net\.?([\/\:]|$))^[a-zA-Z0-9\-]+\:\/+[^\/\:]+\.net\.?([\/\:]|$) - regexFilter is not RE2-compatible: ^(?![a-zA-Z0-9\-]+\:\/+([^\/\:\.]+\.)*(flowplayer|amara|h5p|d3js|ampproject|promisejs|backbonejs|angularjs|dojotoolkit|telegram|telegram-cdn|openstreetmap|wmflabs|wikimapia|wikimedia|wikipedia|w3|schema|archive|mozilla|documentcloud|w|mathjax|userway|pannellum|tmdb|muses|openweathermap|uploadimage|postimages|postimage|imgsafe|4chan|4channel|4cdn|olympic|pbs|pbskids|npr|ntp|gnu|creativecommons|eff|icann|iana|ietf|wikileaks|ourworldindata|cookielaw|google|cdn77|browser-update|consensu|wp-accessibility|covid19maps|coronaisrael)\.org\.?([\/\:]|$))^[a-zA-Z0-9\-]+\:\/+[^\/\:]+\.org\.?([\/\:]|$) - regexFilter is not RE2-compatible: ^(?![a-zA-Z0-9\-]+\:\/+([^\/\:\.]+\.)*(google|gstatic|googleapis|jquery|youtube|youtubekids|youtube-nocookie|ytimg|facebook|fbsbx|twitter|twimg|x|instagram|cdninstagram|pinterest|pinimg|tumblr|giphy|vimeo|vimeocdn|dailymotion|flickr|staticflickr|soundcloud|sndcdn|scribd|scribdassets|tiktok|tiktokcdn|ttwstatic|muscdn|ibytedtos|sharethis|addthis|addthisedge|addthiscdn|reddit|redditmedia|redditstatic|redditgifts|linkedin|licdn|fontawesome|image-maps|cloudflare|bootstrapcdn|unpkg|cdnjs|stackpathdns|stackpathcdn|maxcdn|maxcdn-edge|netdna-ssl|netdna-cdn|kxcdn|ssl-cdn|muicss|tinymce|createjs|github|githubusercontent|aspnetcdn|azure|amazonaws|awswaf|elasticbeanstalk|rackcdn|netlify|jwplayer|jwpcdn|jwpltx|jwpsrv|jwplatform|brightcove|brightcovecdn|flowplayer|foliovision|streamable|kaltura|streamtheworld|mixcloud|bandcamp|bcbits|spotify|omnystudio|omnycontent|iheart|spreaker|podbean|buzzsprout|simplecast|podtail|apple|nobexpartners|vocaroo|embedly|iframely|snapwidget|thinglink|infogram|highcharts|airtable|printfriendly|algolianet|gravatar|svgur|svgshare|imgur|imgflip|gifer|gfycat|tenor|disqus|disquscdn|disqusservice|oneall|oneallcdn|tapatalk|tapatalk-cdn|mapbox|maptiler|mapquest|arcgis|arcgisonline|esri|here|ted|tedcdn|kickstarter|riddle|strawpoll|9gag|9cache|unsplash|freepik|imageshack|tinypic|photobox|photobucket|imgbox|imagebam|gifyu|makeagif|reactiongifs|gifbin|gif-finder|pastebin|rawgit|rawgithub|knockoutjs|gridstackjs|ravenjs|liveleak|metacafe|mcstatic|ign|ignimgs|365scores|buzzfeed|digg|stumbleupon|mix|getpocket|blogspot|wordpress|wp|videopress|wptavern|livejournal|withgoogle|googlegroups|googleusercontent|googlevideo|ggpht|noembed|appspot|firebaseio|firebaseapp|libring|hcaptcha|paypal|paypalobjects|amazon|media-amazon|media-imdb|ebay|microsoft|live|bing|msn|yahoo|yimg|yahooapis|duckduckgo|yandex|webflow|rtlcss|dropbox|dropboxusercontent|dropboxstatic|dropbox-dns|timeanddate|momentjs|weather|accuweather|theweathernetwork|windy|sat24|rainviewer|uvlens|statcounter|adobe|onesignal|livefyre|pushwoosh|tinypass|addtoany|addthisevent|addevent|addtocalendar|sumo|sumome|chatango|bitly|tinyurl|ipcamlive|steamstatic|playstation|discord|discordapp|mixer|odysee|rumble|bitchute|parler|gab|slideplayer|kym-cdn|gyazo|icons8|iconfinder|iconarchive|iconscout|flaticon|kindpng|pngitem|prntscr|deviantart|firefoxusercontent|box|feedly|feedburner|phpbb|vk|userapi|whatsapp|vroptimal-3dx-assets|bbc|cnn|go|nytimes|nyt|today|gofundme|fifa|uefa|nba|turner|xkcd|mtvnservices|cc|tmz|bugsnag|zoro|hebcal|fontsproject|kayma|kayma-dashboards|kayma-insights|kampyle|vicomi|openweb|cincopa|avplayer|vidnt|peer5|h-cdn|bynetcdn|cdnwiz|best-tv|viewbix|streamrail|smv-cdn|cloudvideoplatform|dxmcdn|dxmdp|waze|hunchbots|jeeng|cloudinary|sphereup|poloriz|applicaster|cloudwm|cloudwm-waf|negishim|accessibe|accessibeapp|acsbap|vollotech|mk-sense|allyable|shortaudition|spaceil|clear-map|segmanta|opinionstage|playbuzz|apester|qmerce|outbrain|taboola|taboolasyndication|googleoptimize|google-analytics|googletagservices|googletagmanager|googleadservices|googlesyndication|cloudflareinsights|chartbeat|scorecardresearch|serving-sys|exposebox|dynamicyield|coralogix|browsiprod|ip-api|petametrics|cooladata|hotjar|pusher|carto|fortvision|fortcdn|getsentry|trackjs|gamezhero|nick|nickjr|teennick|travelriskmap|sinclairstoryline|fresnobee|nbcchicago|magazina-il|raxcdn|pagewiz|pas-rahav|aniview|adnxs|sekindo)\.com\.?([\/\:]|$))^[a-zA-Z0-9\-]+\:\/+[^\/\:]+\.com\.?([\/\:]|$) - regexFilter is not RE2-compatible: ^(?![a-zA-Z0-9\-]+\:\/+([^\/\:]+\.(il|com|net|org|gov|mil|edu|int|(ac|gov|nhs)\.uk|(google)|(google)\.(com?\.)?[a-zA-Z]{2,3})|[0-9\.]+|([^\/\:\.]+\.)*(omny\.fm|anchor\.fm|simplecast\.fm|castbox\.fm|github\.io|socket\.io|codepen\.io|polyfill\.io|embed\.ly|iframe\.ly|infogr\.am|t\.me|flourish\.studio|flourish\.rocks|uri\.sh|po\.st|plyr\.io|piano\.io|tg\.dev|periscope\.tv|pscp\.tv|vine\.co|popkey\.co|tenor\.co|redd\.it|ibb\.co|vgy\.me|postimg\.cc|imageshack\.us|prnt\.sc|imagesup\.co|weserv\.nl|telesco\.pe|powr\.io|pippa\.io|last\.fm|scdn\.co|adobe\.io|viafoura\.co|lmao\.ninja|disease\.sh|web\.app|twitch\.tv|rmbl\.ws|stories\.sc|vid\.me|spot\.im|spots\.im|inthegame\.io|cybercdn\.live|h-cdn\.co|minute\.ly|vttp\.co|tldw\.me|feeder\.co|del\.icio\.us|telegram\.me|yandex\.ru|dailymail\.co\.uk|dailystar\.co\.uk|bbc\.net\.uk|bbc\.co\.uk|cnn\.io|bit\.ly|goo\.gl|g\.co|youtu\.be|t\.co|fb\.me|m\.me|instagr\.am|wa\.me|amzn\.to|wp\.me|git\.io|docdro\.id|arcg\.is|ow\.ly|disq\.us|discord\.gg|tiny\.cc|ex\.co|jogo\.studio|nagishly\.co|user1st\.info|knesset\.tv|knesset\.live|walla\.cloud|103\.fm|nickjr\.tv|amagi\.tv|logidea\.info|zoomanalytics\.co|firstimpression\.io|rtk\.io|trb\.tv|ren\.tv|atom-data\.io|sentry\.io|outbid\.io))\.?([\/\:]|$))^[^\/\:\.]+\:\/+[^\/\:\.] - regexFilter is not RE2-compatible: ^(?![a-zA-Z0-9\-]+:\/+www\.(walla(news|shops|tours|art|print)|hamal|sheee)\.co\.il\.?(\/|:|$))^[a-zA-Z0-9\-]+:\/+([a-zA-Z0-9\-]+\.)+(walla(news|shops|tours|art|print)|hamal|sheee)\.co\.il\.?(\/|:|$) - regexFilter is not RE2-compatible: ^(?![a-zA-Z0-9\-]+:\/+(www\.(walla(news|shops|tours|art|print)|hamal)|(api-mail|dal|dcx|isc|iscwne|www)\.walla)\.co\.il\.?(\/|:|$))^[a-zA-Z0-9\-]+:\/+([a-zA-Z0-9\-]+\.)+(walla(news|shops|tours|art|print)?|hamal)\.co\.il\.?(\/|:|$) -CSS-generic: 5 plain CSS selectors -CSS-specific: 441 distinct filters - Combined into 320 distinct hostnames - Combined into 1 distinct entities -CSS-declarative: 5 distinct filters - Combined into 3 distinct hostnames - Combined into 1 distinct entities -Procedural-related distinct filters: 5 distinct combined selectors - Combined into 4 distinct hostnames - Combined into 0 distinct entities -============================ -Listset for 'ita-0': - Fetching remote https://easylist-downloads.adblockplus.org/easylistitaly.txt -Input filter count: 3547 - Accepted filter count: 3545 - Rejected filter count: 0 -Output rule count: 3279 - Plain good: 3275 - - Maybe good (regexes): 4 - redirect=: 0 - removeparams= (accepted/discarded): 0/0 - modifyHeaders=: 0 - Unsupported: 0 - -CSS-generic: 363 plain CSS selectors -CSS-generic-high: 53 plain CSS selectors -CSS-specific: 2980 distinct filters - Combined into 3211 distinct hostnames - Combined into 0 distinct entities -Procedural-related distinct filters: 26 distinct combined selectors - Combined into 26 distinct hostnames - Combined into 0 distinct entities -============================ -Listset for 'jpn-1': - Fetching remote https://filters.adtidy.org/extension/ublock/filters/7.txt -Input filter count: 1891 - Accepted filter count: 1891 - Rejected filter count: 0 -Output rule count: 1311 - Plain good: 1267 - - Maybe good (regexes): 16 - redirect=: 23 - removeparams= (accepted/discarded): 0/1 - modifyHeaders=: 0 - Unsupported: 5 - regexFilter is not RE2-compatible: \/kyodopress_cms\/wp-content\/(themes\/kyodopress\/img_banner\/(?!bn_newspaper\.gif)|banners).* - regexFilter is not RE2-compatible: ^https:\/\/(?!www)[a-z]{3,}\.[a-z]{8,}\.com\/index\.php\?main_page=product_info(&stl=\d)?&(?:cPath|products_id)= - regexFilter is not RE2-compatible: ^https?:\/\/(?!www)[a-z]{3,5}\.[0-9a-z]{4,10}\.[a-z]{2,6}\/[a-z]{3,15}\/(?=[a-z]{0,9}[0-9A-Z])[0-9A-z]{10}\.html$ - regexFilter is not RE2-compatible: ^(?!.*(sharecast.ws|bunnycdn.ru|bootstrapcdn.com|cdn.ampproject.org|cloudflare.com|cdn.staticfile.org|disqus.com|disquscdn.com|dmca.com|ebacdn.com|facebook.net|fastlylb.net|fbcdn.net|fluidplayer.com|fontawesome.com|github.io|google.com|googleapis.com|googletagmanager.com|gstatic.com|jquery.com|jsdelivr.net|jwpcdn.com|jwplatform.com|polyfill.io|recaptcha.net|shrink.pe|twitter.com|ulogin.ru|unpkg.com|userapi.com|vidazoo.com|vk.com|yandex.|yastatic.net|ytimg.com|zencdn.net|player|youtube.com|cackle.me|googleoptimize.com|vuukle.com|chatango.com|twimg.com|google-analytics.com|hcaptcha.com|raincaptcha.com|media-imdb.com|blogger.com|hwcdn.net|instagram.com|wp.com|imgsmail.ru)).*$ - Unsupported regex-based removeParam: /^(cookie|ga_|u_)/ -CSS-generic: 110 plain CSS selectors -CSS-generic-high: 10 plain CSS selectors -CSS-specific: 5991 distinct filters - Combined into 4055 distinct hostnames - Combined into 7 distinct entities -CSS-declarative: 174 distinct filters - Combined into 151 distinct hostnames - Combined into 0 distinct entities -Procedural-related distinct filters: 875 distinct combined selectors - Combined into 840 distinct hostnames - Combined into 0 distinct entities -============================ -Listset for 'kor-1': - Fetching remote https://cdn.jsdelivr.net/gh/List-KR/List-KR@master/filter-uBlockOrigin.txt - Fetching remote https://cdn.jsdelivr.net/gh/List-KR/List-KR@master/filters-share/3rd_domains.txt - Fetching remote https://cdn.jsdelivr.net/gh/List-KR/List-KR@master/filters-share/1st_domains.txt - Fetching remote https://cdn.jsdelivr.net/gh/List-KR/List-KR@master/filters-share/general_elemhide.txt - Fetching remote https://cdn.jsdelivr.net/gh/List-KR/List-KR@master/filters-uBO/specific_ELEMHIDE.txt - Fetching remote https://cdn.jsdelivr.net/gh/List-KR/List-KR@master/filters-share/general_url.txt - Fetching remote https://cdn.jsdelivr.net/gh/List-KR/List-KR@master/filters-uBO/general_url.txt - Fetching remote https://cdn.jsdelivr.net/gh/List-KR/List-KR@master/filters-share/specific_URL.txt - Fetching remote https://cdn.jsdelivr.net/gh/List-KR/List-KR@master/filters-share/specific_ELEMHIDE.txt - Fetching remote https://cdn.jsdelivr.net/gh/List-KR/List-KR@master/filters-share/allowlist.txt - Fetching remote https://cdn.jsdelivr.net/gh/List-KR/List-KR@master/filters-share/extended_css_ELEMHIDE.txt - Fetching remote https://cdn.jsdelivr.net/gh/List-KR/List-KR@master/filters-share/extended_css_INJECTION.txt - Fetching remote https://cdn.jsdelivr.net/gh/List-KR/List-KR@master/filters-uBO/specific_REDIRECT.txt - Fetching remote https://cdn.jsdelivr.net/gh/List-KR/List-KR@master/filters-uBO/extended_css_ELEMHIDE.txt - Fetching remote https://cdn.jsdelivr.net/gh/List-KR/List-KR@master/filters-uBO/extended_css_INJECTION.txt - Fetching remote https://cdn.jsdelivr.net/gh/List-KR/List-KR@master/filters-uBO/scriptlets.txt - Fetching remote https://cdn.jsdelivr.net/gh/List-KR/List-KR@master/filters-share/javascript.txt - Fetching remote https://cdn.jsdelivr.net/gh/List-KR/List-KR@master/filters-uBO/javascript.txt - Fetching remote https://cdn.jsdelivr.net/gh/List-KR/List-KR@master/filters-uBO/antiadblock.txt - Fetching remote https://cdn.jsdelivr.net/gh/List-KR/List-KR@master/filters-share/removeparam.txt - Fetching remote https://cdn.jsdelivr.net/gh/List-KR/List-KR@master/filters-uBO/allowlist.txt - Fetching remote https://cdn.jsdelivr.net/gh/List-KR/List-KR@master/filters-share/specific_CSS.txt - Fetching remote https://cdn.jsdelivr.net/gh/List-KR/List-KR@master/filters-uBO/specific_CSS.txt -Input filter count: 1253 - Accepted filter count: 1247 - Rejected filter count: 0 -Output rule count: 896 - Pruning requestDomains: from 297 to 292 - Plain good: 728 - - Maybe good (regexes): 134 - redirect=: 21 - removeparams= (accepted/discarded): 1/0 - modifyHeaders=: 0 - Unsupported: 12 - regexFilter is not RE2-compatible: ^https:\/\/nstatic\.dcinside\.com\/dc\/event\/nft_gaejugi\/(?!nftcon) - regexFilter is not RE2-compatible: ^https?:\/\/img\.kidkids\.net\/banner\/upimage\/[A-Z]+(_|-)[A-Z0-9]+(_|-)(?!LOGO) - regexFilter is not RE2-compatible: ^https:\/\/image\.aladin\.co\.kr\/img\/banner\/flash\/welcome\/nav\/(?!181010)[0-9]+_tab - regexFilter is not RE2-compatible: ^https:\/\/static\.wixstatic\.com\/media\/[0-9a-z]{6}_[a-z0-9]{32}~(?!.+doc).+ - regexFilter is not RE2-compatible: ^https:\/\/thumb\.toomics\.com\/upload\/banner\/(?!main|cut) - Can't salvage rule with only entity-based domain= option: xn--h10b90b998c.* - Can't salvage rule with only entity-based domain= option: newtoki.* - regexFilter is not RE2-compatible: ^https:\/\/(www\.)?filetender\.com\/images\/(?!logo).+\.(jpg|png)$ - regexFilter is not RE2-compatible: ^https:\/\/(www\.)?ruru\.tv\/uploads\/[0-9]+\/((?!16682220461360)[0-9]+) - regexFilter is not RE2-compatible: ^https:\/\/today-sports\.io\/img\/.*(?=(evolution|banner|\.gif)) - regexFilter is not RE2-compatible: ^https:\/\/s[0-9]+\.sonagitv\.[a-z]+\/sonagi[0-9]*_media\/sites\/[0-9]+\/[0-9]+\/[0-9]+\/(?!(SSNGINDSALC|cropped|sonagitvlogo))[a-z0-9-]+\., Can't salvage rule with only entity-based domain= option: sonagitv.* - Unpatchable redirect filter: google-ima3.js -CSS-generic: 12 plain CSS selectors -CSS-generic-high: 67 plain CSS selectors -CSS-specific: 1194 distinct filters - Combined into 1308 distinct hostnames - Combined into 2 distinct entities -CSS-declarative: 170 distinct filters - Combined into 153 distinct hostnames - Combined into 0 distinct entities -Procedural-related distinct filters: 102 distinct combined selectors - Combined into 150 distinct hostnames - Combined into 0 distinct entities -============================ -Listset for 'ltu-0': - Fetching remote https://raw.githubusercontent.com/EasyList-Lithuania/easylist_lithuania/master/easylistlithuania.txt -Input filter count: 568 - Accepted filter count: 568 - Rejected filter count: 0 -Output rule count: 523 - Plain good: 520 - - Maybe good (regexes): 1 - redirect=: 2 - removeparams= (accepted/discarded): 0/0 - modifyHeaders=: 0 - Unsupported: 0 - -CSS-generic: 5 plain CSS selectors -CSS-generic-high: 5 plain CSS selectors -CSS-specific: 564 distinct filters - Combined into 320 distinct hostnames - Combined into 0 distinct entities -CSS-declarative: 7 distinct filters - Combined into 7 distinct hostnames - Combined into 0 distinct entities -Procedural-related distinct filters: 7 distinct combined selectors - Combined into 8 distinct hostnames - Combined into 0 distinct entities -============================ -Listset for 'lva-0': - Fetching remote https://raw.githubusercontent.com/Latvian-List/adblock-latvian/master/lists/latvian-list.txt -Input filter count: 185 - Accepted filter count: 185 - Rejected filter count: 0 -Output rule count: 144 - Plain good: 144 - - Maybe good (regexes): 0 - redirect=: 0 - removeparams= (accepted/discarded): 0/0 - modifyHeaders=: 0 - Unsupported: 0 - -CSS-generic-high: 2 plain CSS selectors -CSS-specific: 184 distinct filters - Combined into 62 distinct hostnames - Combined into 0 distinct entities -============================ -Listset for 'mkd-0': - Fetching remote https://raw.githubusercontent.com/DeepSpaceHarbor/Macedonian-adBlock-Filters/master/Filters -Input filter count: 289 - Accepted filter count: 289 - Rejected filter count: 0 -Output rule count: 158 - Plain good: 157 - - Maybe good (regexes): 0 - redirect=: 0 - removeparams= (accepted/discarded): 0/0 - modifyHeaders=: 0 - Unsupported: 1 - Invalid network filter in mkd-0: data:image/jpg;base64,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$domain=reporter.mk -CSS-generic: 2 plain CSS selectors -CSS-specific: 426 distinct filters - Combined into 114 distinct hostnames - Combined into 0 distinct entities -CSS-declarative: 2 distinct filters - Combined into 2 distinct hostnames - Combined into 0 distinct entities -============================ -Listset for 'nld-0': - Fetching remote https://easydutch-ubo.github.io/EasyDutch/EasyDutch.txt - Fetching remote https://easydutch-ubo.github.io/EasyDutch/EasyDutch/Block_General.txt - Fetching remote https://easydutch-ubo.github.io/EasyDutch/EasyDutch/Block_first_party_Server.txt - Fetching remote https://easydutch-ubo.github.io/EasyDutch/EasyDutch/Block_third_party_Server.txt - Fetching remote https://easydutch-ubo.github.io/EasyDutch/EasyDutch/Block_Resources.txt - Fetching remote https://easydutch-ubo.github.io/EasyDutch/EasyDutch/Block_Whitelist.txt - Fetching remote https://easydutch-ubo.github.io/EasyDutch/EasyDutch/Hide_General.txt - Fetching remote https://easydutch-ubo.github.io/EasyDutch/EasyDutch/Hide_Specific.txt - Fetching remote https://easydutch-ubo.github.io/EasyDutch/EasyDutch/Hide_Whitelist.txt - Fetching remote https://easydutch-ubo.github.io/EasyDutch/EasyDutch/Anti-Adblock.txt - Fetching remote https://easydutch-ubo.github.io/EasyDutch/EasyDutch/No_uBlock_Filters.txt -Input filter count: 734 - Accepted filter count: 733 - Rejected filter count: 0 -Output rule count: 626 - Plain good: 615 - - Maybe good (regexes): 6 - redirect=: 2 - removeparams= (accepted/discarded): 0/0 - modifyHeaders=: 0 - Unsupported: 3 - FilterStrictParty: Strict partyness strict3p not supported - regexFilter is not RE2-compatible: https\:\/\/nieuwsfiets\.nu\/wp-content\/uploads\/.*\/.*(?:banner-(?!mis|tip).*|\.gif) - Can't salvage rule with only entity-based domain= option: allestoringen.* -CSS-generic: 14 plain CSS selectors -CSS-generic-high: 1 plain CSS selectors -CSS-specific: 956 distinct filters - Combined into 994 distinct hostnames - Combined into 1 distinct entities -CSS-declarative: 11 distinct filters - Combined into 10 distinct hostnames - Combined into 0 distinct entities -Procedural-related distinct filters: 244 distinct combined selectors - Combined into 221 distinct hostnames - Combined into 0 distinct entities -============================ -Listset for 'nor-0': - Fetching remote https://raw.githubusercontent.com/DandelionSprout/adfilt/master/NorwegianList.txt - Fetching remote https://raw.githubusercontent.com/DandelionSprout/adfilt/master/NorwegianExperimentalList%20alternate%20versions/AntiAdblockEntries.txt - Fetching remote https://raw.githubusercontent.com/DandelionSprout/adfilt/master/NorwegianExperimentalList%20alternate%20versions/NordicFilters-NotFirefox.txt - Fetching remote https://raw.githubusercontent.com/DandelionSprout/adfilt/master/NorwegianExperimentalList%20alternate%20versions/NordicFilters-NotBrave.txt -Input filter count: 1400 - Accepted filter count: 1400 - Rejected filter count: 0 -Output rule count: 505 - Plain good: 459 - - Maybe good (regexes): 4 - redirect=: 5 - removeparams= (accepted/discarded): 25/4 - modifyHeaders=: 1 - Unsupported: 11 - Can't salvage rule with only entity-based domain= option: eniro.* - Can't salvage rule with only entity-based domain= option: eniro.*|proff.* - Can't salvage rule with only entity-based domain= option: eurosport.* - Can't salvage rule with only entity-based domain= option: discoveryplus.* - Can't salvage rule with only entity-based domain= option: discoveryplus.* - Can't salvage rule with only entity-based domain= option: discoveryplus.* - Can't salvage rule with only entity-based domain= option: discoveryplus.* - Unsupported regex-based removeParam: /^rs\d/ - Unsupported regex-based removeParam: /^source=partnerads$/ - Unsupported regex-based removeParam: /^source=tradedoubler$/ - Unsupported regex-based removeParam: /^amp;/ -CSS-generic: 132 plain CSS selectors -CSS-generic-high: 28 plain CSS selectors -CSS-specific: 1127 distinct filters - Combined into 580 distinct hostnames - Combined into 13 distinct entities -CSS-declarative: 48 distinct filters - Combined into 117 distinct hostnames - Combined into 1 distinct entities -Procedural-related distinct filters: 92 distinct combined selectors - Combined into 80 distinct hostnames - Combined into 2 distinct entities -============================ -Listset for 'pol-0': - Fetching remote https://raw.githubusercontent.com/MajkiIT/polish-ads-filter/master/polish-adblock-filters/adblock.txt - Fetching remote https://raw.githubusercontent.com/olegwukr/polish-privacy-filters/master/anti-adblock.txt - Fetching remote https://raw.githubusercontent.com/MajkiIT/polish-ads-filter/master/polish-adblock-filters/adblock_ublock.txt - Fetching remote https://raw.githubusercontent.com/olegwukr/polish-privacy-filters/master/anti-adblock-suplement.txt -Input filter count: 1409 - Accepted filter count: 1408 - Rejected filter count: 1 -Output rule count: 1046 - Plain good: 967 - - Maybe good (regexes): 43 - redirect=: 27 - removeparams= (accepted/discarded): 0/0 - modifyHeaders=: 0 - Unsupported: 9 - regexFilter is not RE2-compatible: \/[0-9a-zA-Z]{5,7}\_(?!adaptiveresize)[a-z]{12,17}\_[0-9]{3,3}\x[0-9]{3,3}\.jpg$ - regexFilter is not RE2-compatible: ^https:\/\/eku24.net\/images\/slajdy\/(?!zyczenia)[a-z]{3,10}\/[a-zA-Z0-9_-]{10,50}\.jpg - regexFilter is not RE2-compatible: ^https:\/\/(?!horrortube)(?!filman.cc)(?!horlol.pl)[a-z.0-9]{3,15}\.[a-z]{2,3}\/ - regexFilter is not RE2-compatible: https?:\/\/naekranie\.pl\/wp-content\/uploads\/[0-9]{4,4}\/[0-9]{2,2}\/(?!jpg)[0-9a-z]{7,10}$ - regexFilter is not RE2-compatible: https?:\/\/(?!(poczta|bc))[a-z.]{3,15}\.wp\.pl\/.{20,} - regexFilter is not RE2-compatible: ^http:\/\/((?!192\.168)(?!10\.)(?!172\.16)(?!172\.17)(?!172\.18)(?!172\.19)(?!172\.2)(?!172\.30)(?!172\.31)([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\/[a-zA-Z0-9]{30,} - regexFilter is not RE2-compatible: ^(http|https):\/\/www\.portel\.pl\/(?!pasaz)[-a-z0-9A-Z_%$&+=[\].]{1,200}\/[-a-z0-9A-Z_%$&+=[\]/.]{2,200}.(html|htm) - regexFilter is not RE2-compatible: ^(http|https):\/\/(?!www.speedvid)(?!streamcherry.com)(?!vshare)(?!vidoza)(?!www.youtube)[a-zA-Z0-9\W]{5,10}.[a-z]{2,20}\/(?!anime)[\w\W\d]{5,20}\/[a-z]{5,20}\/ - Can't salvage rule with only entity-based domain= option: trojmiasto.* -CSS-generic: 63 plain CSS selectors -CSS-generic-high: 48 plain CSS selectors -CSS-specific: 4214 distinct filters - Combined into 3231 distinct hostnames - Combined into 0 distinct entities -CSS-declarative: 131 distinct filters - Combined into 182 distinct hostnames - Combined into 0 distinct entities -Procedural-related distinct filters: 96 distinct combined selectors - Combined into 95 distinct hostnames - Combined into 0 distinct entities -============================ -Listset for 'rou-1': - Fetching remote https://road.adblock.ro/lista.txt -No valid content for undefined -Input filter count: 0 - Accepted filter count: 0 - Rejected filter count: 0 -Output rule count: 0 - Plain good: 0 - - Maybe good (regexes): 0 - redirect=: 0 - removeparams= (accepted/discarded): 0/0 - modifyHeaders=: 0 - Unsupported: 0 - -============================ -Listset for 'rus-0': - Fetching remote https://raw.githubusercontent.com/easylist/ruadlist/master/RuAdList-uBO.txt - Fetching remote https://raw.githubusercontent.com/easylist/ruadlist/master/advblock/adservers.txt - Fetching remote https://raw.githubusercontent.com/easylist/ruadlist/master/advblock/first_level.txt - Fetching remote https://raw.githubusercontent.com/easylist/ruadlist/master/advblock/general_block.txt - Fetching remote https://raw.githubusercontent.com/easylist/ruadlist/master/advblock/general_hide.txt - Fetching remote https://raw.githubusercontent.com/easylist/ruadlist/master/advblock/popup.txt - Fetching remote https://raw.githubusercontent.com/easylist/ruadlist/master/advblock/specific_antisocial.txt - Fetching remote https://raw.githubusercontent.com/easylist/ruadlist/master/advblock/specific_block.txt - Fetching remote https://raw.githubusercontent.com/easylist/ruadlist/master/advblock/specific_hide.txt - Fetching remote https://raw.githubusercontent.com/easylist/ruadlist/master/advblock/specific_special.txt - Fetching remote https://raw.githubusercontent.com/easylist/ruadlist/master/advblock/thirdparty.txt - Fetching remote https://raw.githubusercontent.com/easylist/ruadlist/master/advblock/whitelist.txt - Fetching remote https://raw.githubusercontent.com/easylist/ruadlist/master/css-fixes-experimental.txt - Fetching remote https://raw.githubusercontent.com/easylist/ruadlist/master/js-fixes-experimental.txt - Fetching remote https://raw.githubusercontent.com/easylist/ruadlist/master/AWRL-non-sync.txt -Input filter count: 17309 - Accepted filter count: 17299 - Rejected filter count: 8 -Output rule count: 5832 - Pruning requestDomains: from 6634 to 6633 - Plain good: 5745 - - Maybe good (regexes): 21 - redirect=: 16 - removeparams= (accepted/discarded): 15/2 - modifyHeaders=: 29 - Unsupported: 6 - regexFilter is not RE2-compatible: ^(?!.*(24liveblog.com|24liveplus.com|acint.net|addthis.com|addthisedge.com|akamai.net|akamaiedge.net|alloha.tv|ampproject.org|anycomment.io|apester.com|api-ssl.bitly.com|api.corr.life|api.here.com|api.sypexgeo.net|app.hoversignal.com|appsmail.ru|bam.nr-data.net|blogger.com|bootstrapcdn.com|cackle.me|cdn.ampproject.org|cdn.iframe.ly|cdn.rawgit.com|cdnstats.ru|cdnvideo.ru|chartbeat.com|chatango.com|chatbro.com|chimpstatic.com|cloudflare.com|cloudflare.net|cloudfront.net|cloudfunctions.net|code.createjs.com|columbus.te.ua|cultserv.ru|disqus.com|disquscdn.com|dmca.com|documentcloud.org|each.im|ebacdn.com|edgecastcdn.net|edgecdn.ru|ellinagraypel.com|embed.ex.co|embed.widgetpack.com|embedstorage.net|eurosolidarity.org|facebook.com|facebook.net|fastly.net|fastlylb.net|fbcdn.net|fbvkcdn.com|feonet.net|fluidplayer.com|fontawesome.com|fonts.w.tools|freecurrencyrates.com|fwcdn1.com|fwdcdn.com|gcdn.co|getsitecontrol.com|gismeteo.ru|github.io|gitlab.io|google-analytics.com|google.com|googleadservices.com|googleapis.com|googleoptimize.com|googletagmanager.com|googletagservices.com|gravatar.com|gravitec.media|gravitec.net|gstatic.com|hcaptcha.com|hupso.com|hwcdn.net|hypercomments.com|ibytedtos.com|imgsmail.ru|imgur.com|informers.ukr.net|instagram.com|intensedebate.com|intercom.io|intercomcdn.com|intravideo.net|issuu.com|ivideon.com|jivosite.com|jquery.com|js-agent.newrelic.com|jsdelivr.net|jsonip.com|jwpcdn.com|jwplatform.com|keycaptcha.com|kin-x.com|kinogram.best|kinohod.ru|kinoplayer.co|kinotreiler.com|kitbit.net|kodik-add.com|kodikapi.com|libria.fun|licdn.com|likebtn.com|linkedin.com|lp4.io|mail.ru|mailchimp.com|mapbox.com|media-imdb.com|media.reformal.ru|mediator.media|meteobar.com|meteonova.ru|mirtesen.ru|netdna-cdn.com|ngenix.net|nuipogoda.ru|odnaknopka.ru|odnoklassniki.ru|ok.ru|oneall.com|onesignal.com|onthe.io|parastorage.com|phnx.click|piktochart.com|pinterest.com|pixars.org|platformcraft.ru|playbuzz.com|player|player.panda.video|pljs.ru|plrjs.com|pluso.ru|plyr.io|polldaddy.com|polyfill.io|pv.pjtsu.com|quiz.ink|raincaptcha.com|readymag.com|recaptcha.net|relap.io|ren.tv|renteres.ru|rumer.club|s5o.ru|securedtouch.com|selcdn.net|sendpulse.com|sentry-cdn.com|shareaholic.com|shareaholic.net|sharethis.com|shrink.pe|sinoptik.ua|source.mmi.bemobile.ua|sporcle.com|sportradar.com|sportrecs.com|sports.ru|stackpathcdn.com|static.addtoany.com|statically.io|streamvid.club|telegram.im|telegram.org|tenews.org.ua|tenews.te.ua|tiktok.com|tilda.ws|tildacdn.com|tns-counter.ru|tolstoycomments.com|traq.li|trbcdn.net|trbna.com|ttrace.ru|ttwstatic.com|tumblr.com|tvget.ru|tvsok.ru|twimg.com|twitter.com|typekit.net|uanews.org.ua|unpkg.com|uptolike.com|userapi.com|usocial.pro|uweb.ru|vicomi.com|vidazoo.com|videocdn.tv|videoplayers.club|viglink.com|viqeo.tv|vk.com|vkontakte.ru|vuukle.com|webflow.com|weblium.com|weblium.site|widget.speechki.org|widget.vp.ru|widgets.getpocket.com|world-weather.ru|wp.com|yabber.cloud|yandex.ru|yandex.st|yastatic.net|yohoho.cc|yohoho.online|yoomoney.ru|yourwebsite.life|youtube-nocookie.com|youtube.com|ytimg.com|zencdn.net)).*$ - regexFilter is not RE2-compatible: ^(?!.*(24liveblog.com|24liveplus.com|acint.net|addthis.com|addthisedge.com|akamai.net|akamaiedge.net|alloha.tv|ampproject.org|anycomment.io|apester.com|api-ssl.bitly.com|api.corr.life|api.here.com|api.sypexgeo.net|app.hoversignal.com|appsmail.ru|bam.nr-data.net|blogger.com|bootstrapcdn.com|cackle.me|cdn.ampproject.org|cdn.iframe.ly|cdn.rawgit.com|cdnstats.ru|cdnvideo.ru|chartbeat.com|chatango.com|chatbro.com|chimpstatic.com|cloudflare.com|cloudflare.net|cloudfront.net|cloudfunctions.net|code.createjs.com|columbus.te.ua|cultserv.ru|disqus.com|disquscdn.com|dmca.com|documentcloud.org|each.im|ebacdn.com|edgecastcdn.net|edgecdn.ru|ellinagraypel.com|embed.ex.co|embed.widgetpack.com|embedstorage.net|eurosolidarity.org|facebook.com|facebook.net|fastly.net|fastlylb.net|fbcdn.net|fbvkcdn.com|feonet.net|fluidplayer.com|fontawesome.com|fonts.w.tools|freecurrencyrates.com|fwcdn1.com|fwdcdn.com|gcdn.co|getsitecontrol.com|gismeteo.ru|github.io|gitlab.io|google-analytics.com|google.com|googleadservices.com|googleapis.com|googleoptimize.com|googletagmanager.com|googletagservices.com|gravatar.com|gravitec.media|gravitec.net|gstatic.com|hcaptcha.com|hupso.com|hwcdn.net|hypercomments.com|ibytedtos.com|imgsmail.ru|imgur.com|informers.ukr.net|instagram.com|intensedebate.com|intercom.io|intercomcdn.com|intravideo.net|issuu.com|ivideon.com|jivosite.com|jquery.com|js-agent.newrelic.com|jsdelivr.net|jsonip.com|jwpcdn.com|jwplatform.com|keycaptcha.com|kin-x.com|kinogram.best|kinohod.ru|kinoplayer.co|kinotreiler.com|kitbit.net|kodik-add.com|kodikapi.com|libria.fun|licdn.com|likebtn.com|linkedin.com|lp4.io|mail.ru|mailchimp.com|mapbox.com|media-imdb.com|media.reformal.ru|mediator.media|meteobar.com|meteonova.ru|mirtesen.ru|netdna-cdn.com|ngenix.net|nuipogoda.ru|odnaknopka.ru|odnoklassniki.ru|ok.ru|oneall.com|onesignal.com|onthe.io|parastorage.com|phnx.click|piktochart.com|pinterest.com|pixars.org|platformcraft.ru|playbuzz.com|player|player.panda.video|pljs.ru|plrjs.com|pluso.ru|plyr.io|polldaddy.com|polyfill.io|pv.pjtsu.com|quiz.ink|raincaptcha.com|readymag.com|recaptcha.net|relap.io|ren.tv|renteres.ru|rumer.club|s5o.ru|securedtouch.com|selcdn.net|sendpulse.com|sentry-cdn.com|shareaholic.com|shareaholic.net|sharethis.com|shrink.pe|sinoptik.ua|source.mmi.bemobile.ua|sporcle.com|sportradar.com|sportrecs.com|sports.ru|stackpathcdn.com|static.addtoany.com|statically.io|streamvid.club|telegram.im|telegram.org|tenews.org.ua|tenews.te.ua|tiktok.com|tilda.ws|tildacdn.com|tns-counter.ru|tolstoycomments.com|traq.li|trbcdn.net|trbna.com|ttrace.ru|ttwstatic.com|tumblr.com|tvget.ru|tvsok.ru|twimg.com|twitter.com|typekit.net|uanews.org.ua|unpkg.com|uptolike.com|userapi.com|usocial.pro|uweb.ru|vicomi.com|vidazoo.com|videocdn.tv|videoplayers.club|viglink.com|viqeo.tv|vk.com|vkontakte.ru|vuukle.com|webflow.com|weblium.com|weblium.site|widget.speechki.org|widget.vp.ru|widgets.getpocket.com|world-weather.ru|wp.com|yabber.cloud|yandex.ru|yandex.st|yastatic.net|yohoho.cc|yohoho.online|yoomoney.ru|yourwebsite.life|youtube-nocookie.com|youtube.com|ytimg.com|zencdn.net)).*$ - regexFilter is not RE2-compatible: ^(?!.*(spac.me)).*$ - regexFilter is not RE2-compatible: ^(?!.*(24liveblog.com|24liveplus.com|acint.net|addthis.com|addthisedge.com|akamai.net|akamaiedge.net|alloha.tv|ampproject.org|anycomment.io|apester.com|api-ssl.bitly.com|api.corr.life|api.here.com|api.sypexgeo.net|app.hoversignal.com|appsmail.ru|bam.nr-data.net|blogger.com|bootstrapcdn.com|cackle.me|cdn.ampproject.org|cdn.iframe.ly|cdn.rawgit.com|cdnstats.ru|cdnvideo.ru|chartbeat.com|chatango.com|chatbro.com|chimpstatic.com|cloudflare.com|cloudflare.net|cloudfront.net|cloudfunctions.net|code.createjs.com|columbus.te.ua|cultserv.ru|disqus.com|disquscdn.com|dmca.com|documentcloud.org|each.im|ebacdn.com|edgecastcdn.net|edgecdn.ru|ellinagraypel.com|embed.ex.co|embed.widgetpack.com|embedstorage.net|eurosolidarity.org|facebook.com|facebook.net|fastly.net|fastlylb.net|fbcdn.net|fbvkcdn.com|feonet.net|fluidplayer.com|fontawesome.com|fonts.w.tools|freecurrencyrates.com|fwcdn1.com|fwdcdn.com|gcdn.co|getsitecontrol.com|gismeteo.ru|github.io|gitlab.io|google-analytics.com|google.com|googleadservices.com|googleapis.com|googleoptimize.com|googletagmanager.com|googletagservices.com|gravatar.com|gravitec.media|gravitec.net|gstatic.com|hcaptcha.com|hupso.com|hwcdn.net|hypercomments.com|ibytedtos.com|imgsmail.ru|imgur.com|informers.ukr.net|instagram.com|intensedebate.com|intercom.io|intercomcdn.com|intravideo.net|issuu.com|ivideon.com|jivosite.com|jquery.com|js-agent.newrelic.com|jsdelivr.net|jsonip.com|jwpcdn.com|jwplatform.com|keycaptcha.com|kin-x.com|kinogram.best|kinohod.ru|kinoplayer.co|kinotreiler.com|kitbit.net|kodik-add.com|kodikapi.com|libria.fun|licdn.com|likebtn.com|linkedin.com|lp4.io|mail.ru|mailchimp.com|mapbox.com|media-imdb.com|media.reformal.ru|mediator.media|meteobar.com|meteonova.ru|mirtesen.ru|netdna-cdn.com|ngenix.net|nuipogoda.ru|odnaknopka.ru|odnoklassniki.ru|ok.ru|oneall.com|onesignal.com|onthe.io|parastorage.com|phnx.click|piktochart.com|pinterest.com|pixars.org|platformcraft.ru|playbuzz.com|player|player.panda.video|pljs.ru|plrjs.com|pluso.ru|plyr.io|polldaddy.com|polyfill.io|pv.pjtsu.com|quiz.ink|raincaptcha.com|readymag.com|recaptcha.net|relap.io|ren.tv|renteres.ru|rumer.club|s5o.ru|securedtouch.com|selcdn.net|sendpulse.com|sentry-cdn.com|shareaholic.com|shareaholic.net|sharethis.com|shrink.pe|sinoptik.ua|source.mmi.bemobile.ua|sporcle.com|sportradar.com|sportrecs.com|sports.ru|stackpathcdn.com|static.addtoany.com|statically.io|streamvid.club|telegram.im|telegram.org|tenews.org.ua|tenews.te.ua|tiktok.com|tilda.ws|tildacdn.com|tns-counter.ru|tolstoycomments.com|traq.li|trbcdn.net|trbna.com|ttrace.ru|ttwstatic.com|tumblr.com|tvget.ru|tvsok.ru|twimg.com|twitter.com|typekit.net|uanews.org.ua|unpkg.com|uptolike.com|userapi.com|usocial.pro|uweb.ru|vicomi.com|vidazoo.com|videocdn.tv|videoplayers.club|viglink.com|viqeo.tv|vk.com|vkontakte.ru|vuukle.com|webflow.com|weblium.com|weblium.site|widget.speechki.org|widget.vp.ru|widgets.getpocket.com|world-weather.ru|wp.com|yabber.cloud|yandex.ru|yandex.st|yastatic.net|yohoho.cc|yohoho.online|yoomoney.ru|yourwebsite.life|youtube-nocookie.com|youtube.com|ytimg.com|zencdn.net)).*$ - Unsupported modifier exception - Unsupported modifier exception -CSS-generic: 318 plain CSS selectors -CSS-generic-high: 461 plain CSS selectors -CSS-specific: 9772 distinct filters - Combined into 7075 distinct hostnames - Combined into 0 distinct entities -CSS-declarative: 904 distinct filters - Combined into 876 distinct hostnames - Combined into 0 distinct entities -Procedural-related distinct filters: 271 distinct combined selectors - Combined into 336 distinct hostnames - Combined into 0 distinct entities -============================ -Listset for 'spa-0': - Fetching remote https://easylist-downloads.adblockplus.org/easylistspanish.txt -Input filter count: 1115 - Accepted filter count: 1115 - Rejected filter count: 0 -Output rule count: 642 - Plain good: 634 - - Maybe good (regexes): 8 - redirect=: 0 - removeparams= (accepted/discarded): 0/0 - modifyHeaders=: 0 - Unsupported: 0 - -CSS-generic: 243 plain CSS selectors -CSS-generic-high: 15 plain CSS selectors -CSS-specific: 1469 distinct filters - Combined into 1198 distinct hostnames - Combined into 0 distinct entities -Procedural-related distinct filters: 26 distinct combined selectors - Combined into 25 distinct hostnames - Combined into 0 distinct entities -============================ -Listset for 'spa-1': - Fetching remote https://filters.adtidy.org/extension/ublock/filters/9.txt -Input filter count: 1393 - Accepted filter count: 1387 - Rejected filter count: 0 -Output rule count: 958 - Plain good: 921 - Salvaged rule by ignoring 1 entity-based domain= option: pelisplushd.net|cuevana3.* - Maybe good (regexes): 6 - redirect=: 27 - removeparams= (accepted/discarded): 0/0 - modifyHeaders=: 0 - Unsupported: 4 - Can't salvage rule with only entity-based domain= option: netcine.* - Can't salvage rule with only entity-based domain= option: netcine.* - regexFilter is not RE2-compatible: ^(?!.*(sharecast.ws|bunnycdn.ru|bootstrapcdn.com|cdn.ampproject.org|cloudflare.com|cdn.staticfile.org|disqus.com|disquscdn.com|dmca.com|ebacdn.com|facebook.net|fastlylb.net|fbcdn.net|fluidplayer.com|fontawesome.com|github.io|google.com|googleapis.com|googletagmanager.com|gstatic.com|jquery.com|jsdelivr.net|jwpcdn.com|jwplatform.com|polyfill.io|recaptcha.net|shrink.pe|twitter.com|ulogin.ru|unpkg.com|userapi.com|vidazoo.com|vk.com|yandex.|yastatic.net|ytimg.com|zencdn.net|player|youtube.com|cackle.me|googleoptimize.com|vuukle.com|chatango.com|twimg.com|google-analytics.com|hcaptcha.com|raincaptcha.com|media-imdb.com|blogger.com|hwcdn.net|instagram.com|wp.com|imgsmail.ru)).*$ - Can't salvage rule with only entity-based domain= option: anitube.* -CSS-generic: 84 plain CSS selectors -CSS-specific: 2526 distinct filters - Combined into 1464 distinct hostnames - Combined into 6 distinct entities -CSS-declarative: 99 distinct filters - Combined into 149 distinct hostnames - Combined into 0 distinct entities -Procedural-related distinct filters: 70 distinct combined selectors - Combined into 65 distinct hostnames - Combined into 0 distinct entities -============================ -Listset for 'svn-0': - Fetching remote https://raw.githubusercontent.com/betterwebleon/slovenian-list/master/filters.txt -Input filter count: 148 - Accepted filter count: 148 - Rejected filter count: 0 -Output rule count: 100 - Plain good: 100 - - Maybe good (regexes): 0 - redirect=: 0 - removeparams= (accepted/discarded): 0/0 - modifyHeaders=: 0 - Unsupported: 0 - -CSS-generic: 4 plain CSS selectors -CSS-specific: 332 distinct filters - Combined into 148 distinct hostnames - Combined into 0 distinct entities -Procedural-related distinct filters: 2 distinct combined selectors - Combined into 2 distinct hostnames - Combined into 0 distinct entities -============================ -Listset for 'swe-1': - Fetching remote https://raw.githubusercontent.com/lassekongo83/Frellwits-filter-lists/master/Frellwits-Swedish-Filter.txt - Fetching remote https://raw.githubusercontent.com/lassekongo83/Frellwits-filter-lists/master/Swedish/swe-ubo-filters.txt - Fetching remote https://raw.githubusercontent.com/lassekongo83/Frellwits-filter-lists/master/Swedish/chromium.txt - Fetching remote https://raw.githubusercontent.com/lassekongo83/Frellwits-filter-lists/master/Swedish/not_mobile.txt -Input filter count: 1663 - Accepted filter count: 1663 - Rejected filter count: 0 -Output rule count: 1241 - Plain good: 1219 - - Maybe good (regexes): 2 - redirect=: 10 - removeparams= (accepted/discarded): 5/3 - modifyHeaders=: 2 - Unsupported: 3 - Unsupported regex-based removeParam: /^ap/ - Unsupported regex-based removeParam: /^browser/ - Unsupported regex-based removeParam: /^utm_/ -CSS-generic: 271 plain CSS selectors -CSS-generic-high: 44 plain CSS selectors -CSS-specific: 661 distinct filters - Combined into 1130 distinct hostnames - Combined into 1 distinct entities -CSS-declarative: 79 distinct filters - Combined into 211 distinct hostnames - Combined into 0 distinct entities -Procedural-related distinct filters: 374 distinct combined selectors - Combined into 324 distinct hostnames - Combined into 1 distinct entities -============================ -Listset for 'tha-0': - Fetching remote https://raw.githubusercontent.com/easylist-thailand/easylist-thailand/master/subscription/easylist-thailand.txt - Fetching remote https://raw.githubusercontent.com/easylist-thailand/easylist-thailand/master/subscription/ublock.txt -Input filter count: 760 - Accepted filter count: 760 - Rejected filter count: 0 -Output rule count: 750 - Plain good: 746 - - Maybe good (regexes): 2 - redirect=: 2 - removeparams= (accepted/discarded): 0/0 - modifyHeaders=: 0 - Unsupported: 0 - -CSS-generic-high: 5 plain CSS selectors -CSS-specific: 614 distinct filters - Combined into 166 distinct hostnames - Combined into 0 distinct entities -Procedural-related distinct filters: 14 distinct combined selectors - Combined into 11 distinct hostnames - Combined into 0 distinct entities -============================ -Listset for 'tur-0': - Fetching remote https://filters.adtidy.org/extension/ublock/filters/13.txt -Input filter count: 1779 - Accepted filter count: 1778 - Rejected filter count: 0 -Output rule count: 1338 - Plain good: 1297 - Salvaged rule by ignoring 1 entity-based domain= option: ajans32.com|asyadiziizle.com|balfilmizle1.com|birasyadizi.com|buyuktorbali.com|dizilost.com|duzcetv.com|erotikfilmtube.com|erotikizlefilm.com|ertehaber.com|filmjr1.com|filmsezonu.com|haber32.com.tr|haberant.com|jokerfilmizle.com|kozfilm.com|malatyamegahaber.com|medya32.com|sexfilmleriizle.com|sinemangoo.org|technopat.net|unyenethaber.com|zerotikk.com|dizicaps.* - Salvaged rule by ignoring 1 entity-based domain= option: fullhdfilm.pro|fullhdfilmizle5.* - Salvaged rule by ignoring 1 entity-based domain= option: turkcealtyazi.org|filmmakinesi.* - Salvaged rule by ignoring 1 entity-based domain= option: cdn.diziyou.co|geyvemedya.com|hdfilmcehennemi2.* - Salvaged rule by ignoring 1 entity-based domain= option: forum.donanimhaber.com|mp3indirdur.mobi|setfilmizle.* - Maybe good (regexes): 14 - redirect=: 17 - removeparams= (accepted/discarded): 0/0 - modifyHeaders=: 0 - Unsupported: 10 - Can't salvage rule with only entity-based domain= option: filmizletv.* - Can't salvage rule with only entity-based domain= option: fullhdfilmizle5.* - Can't salvage rule with only entity-based domain= option: jetfilmizle.* - Can't salvage rule with only entity-based domain= option: siyahfilmizle.* - Can't salvage rule with only entity-based domain= option: fullhdfilmizlesene.* - Can't salvage rule with only entity-based domain= option: filmmakinesi.* - regexFilter is not RE2-compatible: yenihaberden.com\/d\/other\/(?!yeni-haber-youtube) - Can't salvage rule with only entity-based domain= option: yabancidizi.* - regexFilter is not RE2-compatible: ^(?!.*(sharecast.ws|bunnycdn.ru|bootstrapcdn.com|cdn.ampproject.org|cloudflare.com|cdn.staticfile.org|disqus.com|disquscdn.com|dmca.com|ebacdn.com|facebook.net|fastlylb.net|fbcdn.net|fluidplayer.com|fontawesome.com|github.io|google.com|googleapis.com|googletagmanager.com|gstatic.com|jquery.com|jsdelivr.net|jwpcdn.com|jwplatform.com|polyfill.io|recaptcha.net|shrink.pe|twitter.com|ulogin.ru|unpkg.com|userapi.com|vidazoo.com|vk.com|yandex.|yastatic.net|ytimg.com|zencdn.net|player|youtube.com|cackle.me|googleoptimize.com|vuukle.com|chatango.com|twimg.com|google-analytics.com|hcaptcha.com|raincaptcha.com|media-imdb.com|blogger.com|hwcdn.net|instagram.com|wp.com|imgsmail.ru)).*$ - Can't salvage rule with only entity-based domain= option: filmizletv.* -CSS-generic: 146 plain CSS selectors -CSS-generic-high: 60 plain CSS selectors -CSS-specific: 3395 distinct filters - Combined into 2763 distinct hostnames - Combined into 40 distinct entities -CSS-declarative: 169 distinct filters - Combined into 558 distinct hostnames - Combined into 22 distinct entities -Procedural-related distinct filters: 159 distinct combined selectors - Combined into 128 distinct hostnames - Combined into 3 distinct entities -============================ -Listset for 'vie-1': - Fetching remote https://raw.githubusercontent.com/abpvn/abpvn/master/filter/abpvn_ublock.txt -Input filter count: 567 - Accepted filter count: 567 - Rejected filter count: 0 -Output rule count: 467 - Plain good: 455 - - Maybe good (regexes): 4 - redirect=: 5 - removeparams= (accepted/discarded): 0/0 - modifyHeaders=: 3 - Unsupported: 0 - -CSS-generic: 10 plain CSS selectors -CSS-generic-high: 4 plain CSS selectors -CSS-specific: 769 distinct filters - Combined into 439 distinct hostnames - Combined into 0 distinct entities -CSS-declarative: 4 distinct filters - Combined into 14 distinct hostnames - Combined into 0 distinct entities -Procedural-related distinct filters: 4 distinct combined selectors - Combined into 3 distinct hostnames - Combined into 0 distinct entities -============================ -Listset for 'block-lan': - Fetching remote https://ublockorigin.github.io/uAssets/filters/lan-block.txt -Input filter count: 48 - Accepted filter count: 48 - Rejected filter count: 0 -Output rule count: 12 - Plain good: 5 - - Maybe good (regexes): 7 - redirect=: 0 - removeparams= (accepted/discarded): 0/0 - modifyHeaders=: 0 - Unsupported: 0 - -============================ -Listset for 'dpollock-0': - Fetching remote https://someonewhocares.org/hosts/hosts -Input filter count: 11543 - Accepted filter count: 11542 - Rejected filter count: 0 -Output rule count: 1 - Pruning requestDomains: from 11542 to 9296 - Plain good: 1 - - Maybe good (regexes): 0 - redirect=: 0 - removeparams= (accepted/discarded): 0/0 - modifyHeaders=: 0 - Unsupported: 0 - -============================ -Listset for 'adguard-spyware-url': - Fetching remote https://filters.adtidy.org/extension/ublock/filters/17.txt -Input filter count: 1186 - Accepted filter count: 1183 - Rejected filter count: 0 -Output rule count: 448 - Plain good: 0 - - Maybe good (regexes): 0 - redirect=: 0 - removeparams= (accepted/discarded): 367/81 - modifyHeaders=: 0 - Unsupported: 81 - Unsupported regex-based removeParam: /^cm_mmc/ - Unsupported regex-based removeParam: /^__s=[A-Za-z0-9]{6\,}/ - Unsupported regex-based removeParam: /^via%3D/ - Unsupported regex-based removeParam: /ga[ct]id/ - Unsupported regex-based removeParam: /param[0-9]{1}|utm_si|matchtype|device|creative|keyword|placement|adposition|campaignid|adgroupid|feeditemid|targetid|loc_|searchtype|network|search_pos|cat_pos|block|position/ - Unsupported regex-based removeParam: /pfx|adj/ - Unsupported regex-based removeParam: /^event_callback_/ - Unsupported regex-based removeParam: /elq/ - Unsupported regex-based removeParam: /utm_/ - Unsupported regex-based removeParam: /web_only|_branch_referrer/ - Unsupported regex-based removeParam: /premiumVisit|utm_compaign/ - Unsupported regex-based removeParam: /utm_partner_id|frommail/ - Unsupported regex-based removeParam: /^(udid|DeviceID|ver|appbuild|vendor|model|device_name|device_type|instanceid|device_year|connection_class|appsflyerid)/ - Unsupported regex-based removeParam: /^cd\d+/ - Unsupported regex-based removeParam: /^subid/ - Unsupported regex-based removeParam: /^mkt_tok/ - Unsupported regex-based removeParam: /fx_(source|medium|campaign)/ - Unsupported regex-based removeParam: /^ref_/ - Unsupported regex-based removeParam: /^cx_/ - Unsupported regex-based removeParam: /^pickup_list_click/ - Unsupported regex-based removeParam: /distributorid|wfr|ifr|share_relation/ - Unsupported regex-based removeParam: /cUrl|ref/ - Unsupported regex-based removeParam: /topicPageSponsorship|^itm_/ - Unsupported regex-based removeParam: /^utm_/ - Unsupported regex-based removeParam: /^trk/ - Unsupported regex-based removeParam: /^utm_cid/ - Unsupported regex-based removeParam: /entries/ - Unsupported regex-based removeParam: /Version/ - Unsupported regex-based removeParam: /^at_custom/ - Unsupported regex-based removeParam: /mcorgid|mid|ts/ - Unsupported regex-based removeParam: /^dc_trk_/ - Unsupported regex-based removeParam: /^(ppref|ref|pid)=/ - Unsupported regex-based removeParam: /^subid/ - Unsupported regex-based removeParam: /^(_requestid|reff)=/ - Unsupported regex-based removeParam: /^affExtParam/ - Unsupported regex-based removeParam: /^otracker/ - Unsupported regex-based removeParam: /spm=|scm=|from=|keyori=|sugg=|search=|mp=|c=|^abtest|^abbucket|pos=|themeID=|algArgs=|clickTrackInfo=|acm=|item_id=|version=|up_id=|pvid=/ - Unsupported modifier exception - Unsupported modifier exception - Unsupported modifier exception - Unsupported modifier exception - Unsupported modifier exception - Unsupported modifier exception - Unsupported modifier exception - Unsupported modifier exception - Unsupported modifier exception - Unsupported modifier exception - Unsupported modifier exception - Unsupported modifier exception - Unsupported modifier exception - Unsupported modifier exception - Unsupported modifier exception - Unsupported modifier exception - Unsupported modifier exception - Unsupported modifier exception - Unsupported modifier exception - Unsupported modifier exception - Unsupported modifier exception - Unsupported modifier exception - Unsupported modifier exception - Unsupported modifier exception - Unsupported modifier exception - Unsupported modifier exception - Unsupported modifier exception - Unsupported modifier exception - Unsupported modifier exception - Unsupported modifier exception - Unsupported modifier exception - Unsupported modifier exception - Unsupported modifier exception - Unsupported modifier exception - Unsupported modifier exception - Unsupported regex-based removeParam: /^\/_ui\/desktop\/common\/js\/uiAnalytics\// - Unsupported regex-based removeParam: /_ui\/shared\/common\/js\/analytics\/with-intersection-track.js/ - Unsupported regex-based removeParam: /_ui\/shared\/common\/js\/InappCommunicationManager.js/ - Unsupported regex-based removeParam: /_ui\/shared\/common\/js\/util\/jquery.analytics-utils.js/ - Unsupported regex-based removeParam: /^(device|country|path)=/ - Unsupported regex-based removeParam: /cdt|ref/ - Unsupported regex-based removeParam: ~/^(primer|subset_id)=/ - Unsupported regex-based removeParam: /tour|campaign/ - Unsupported modifier exception -CSS-specific: 1 distinct filters - Combined into 3 distinct hostnames - Combined into 0 distinct entities -============================ -Listset for 'annoyances-cookies': - Fetching remote https://ublockorigin.github.io/uAssets/thirdparties/easylist-cookies.txt - Fetching remote https://ublockorigin.github.io/uAssets/filters/annoyances-cookies.txt -Input filter count: 2000 - Accepted filter count: 1997 - Rejected filter count: 0 -Output rule count: 1691 - Plain good: 1690 - - Maybe good (regexes): 1 - redirect=: 0 - removeparams= (accepted/discarded): 0/0 - modifyHeaders=: 0 - Unsupported: 0 - -CSS-generic: 16939 plain CSS selectors -CSS-generic-high: 351 plain CSS selectors -CSS-specific: 5385 distinct filters - Combined into 16378 distinct hostnames - Combined into 1 distinct entities -CSS-declarative: 66 distinct filters - Combined into 5469 distinct hostnames - Combined into 0 distinct entities -Procedural-related distinct filters: 7 distinct combined selectors - Combined into 17 distinct hostnames - Combined into 0 distinct entities -============================ -Listset for 'annoyances-overlays': - Fetching remote https://filters.adtidy.org/extension/ublock/filters/19.txt - Fetching remote https://ublockorigin.github.io/uAssets/filters/annoyances-others.txt -Input filter count: 2483 - Accepted filter count: 2481 - Rejected filter count: 0 -Output rule count: 1520 - Pruning requestDomains: from 530 to 529 - Plain good: 1454 - - Maybe good (regexes): 3 - redirect=: 52 - removeparams= (accepted/discarded): 1/0 - modifyHeaders=: 6 - Unsupported: 4 - Can't salvage rule with only entity-based domain= option: gmx.* - regexFilter is not RE2-compatible: ^https:\/\/[0-9a-z]{7,25}\.com\/v2(?:\/0\/)?(?=[0-9a-z_-]{0,84}[A-Z])(?=[a-zA-Z_-]{0,84}[0-9])[0-9a-zA-Z_-]{54,85}(#\?v=[0-9a-f]{32})?$ - regexFilter is not RE2-compatible: ^https:\/\/[0-9a-z]{7,25}\.com\/v2(?:\/0\/)?(?=[0-9a-z_-]{0,84}[A-Z])(?=[a-zA-Z_-]{0,84}[0-9])[0-9a-zA-Z_-]{54,85}(#\?v=[0-9a-f]{32})?$ - regexFilter is not RE2-compatible: ^https:\/\/[0-9a-z]{7,25}\.com\/v2(?:\/0\/)?(?=[0-9a-z_-]{0,84}[A-Z])(?=[a-zA-Z_-]{0,84}[0-9])[0-9a-zA-Z_-]{54,85}(#\?v=[0-9a-f]{32})?$ -CSS-generic: 35 plain CSS selectors -CSS-generic-high: 3 plain CSS selectors -CSS-specific: 10701 distinct filters - Combined into 10527 distinct hostnames - Combined into 68 distinct entities -CSS-declarative: 610 distinct filters - Combined into 2227 distinct hostnames - Combined into 25 distinct entities -Procedural-related distinct filters: 377 distinct combined selectors - Combined into 758 distinct hostnames - Combined into 7 distinct entities -============================ -Listset for 'annoyances-social': - Fetching remote https://filters.adtidy.org/extension/ublock/filters/4.txt -Input filter count: 643 - Accepted filter count: 643 - Rejected filter count: 0 -Output rule count: 544 - Plain good: 541 - - Maybe good (regexes): 0 - redirect=: 1 - removeparams= (accepted/discarded): 0/0 - modifyHeaders=: 0 - Unsupported: 2 - regexFilter is not RE2-compatible: \/icons_addtoany\/(?!a2a|bookmark|print)[a-z]+ - Can't salvage rule with only entity-based domain= option: freelancer.* -CSS-generic: 753 plain CSS selectors -CSS-generic-high: 86 plain CSS selectors -CSS-specific: 10807 distinct filters - Combined into 11984 distinct hostnames - Combined into 63 distinct entities -CSS-declarative: 115 distinct filters - Combined into 204 distinct hostnames - Combined into 7 distinct entities -Procedural-related distinct filters: 514 distinct combined selectors - Combined into 563 distinct hostnames - Combined into 4 distinct entities -============================ -Listset for 'annoyances-widgets': - Fetching remote https://filters.adtidy.org/extension/ublock/filters/22.txt -Input filter count: 665 - Accepted filter count: 665 - Rejected filter count: 0 -Output rule count: 432 - Plain good: 432 - - Maybe good (regexes): 0 - redirect=: 0 - removeparams= (accepted/discarded): 0/0 - modifyHeaders=: 0 - Unsupported: 0 - -CSS-generic: 25 plain CSS selectors -CSS-generic-high: 7 plain CSS selectors -CSS-specific: 1014 distinct filters - Combined into 981 distinct hostnames - Combined into 2 distinct entities -CSS-declarative: 15 distinct filters - Combined into 14 distinct hostnames - Combined into 0 distinct entities -Procedural-related distinct filters: 73 distinct combined selectors - Combined into 54 distinct hostnames - Combined into 1 distinct entities -============================ -Listset for 'annoyances-others': - Fetching remote https://filters.adtidy.org/extension/ublock/filters/21.txt -Input filter count: 427 - Accepted filter count: 427 - Rejected filter count: 0 -Output rule count: 398 - Plain good: 393 - - Maybe good (regexes): 2 - redirect=: 1 - removeparams= (accepted/discarded): 2/0 - modifyHeaders=: 0 - Unsupported: 0 - -CSS-generic: 6 plain CSS selectors -CSS-specific: 3598 distinct filters - Combined into 3352 distinct hostnames - Combined into 33 distinct entities -CSS-declarative: 391 distinct filters - Combined into 937 distinct hostnames - Combined into 5 distinct entities -Procedural-related distinct filters: 169 distinct combined selectors - Combined into 144 distinct hostnames - Combined into 3 distinct entities -============================ -Listset for 'stevenblack-hosts': - Fetching remote https://raw.githubusercontent.com/StevenBlack/hosts/master/hosts -Input filter count: 210426 - Accepted filter count: 210426 - Rejected filter count: 0 -Output rule count: 1 - Pruning requestDomains: from 210426 to 108459 - Plain good: 1 - - Maybe good (regexes): 0 - redirect=: 0 - removeparams= (accepted/discarded): 0/0 - modifyHeaders=: 0 - Unsupported: 0 - From a4d319437a25de6b98cc4dcec58f1a14ad310d63 Mon Sep 17 00:00:00 2001 From: daylight Date: Fri, 1 Nov 2024 21:01:06 +0300 Subject: [PATCH 0420/1099] Delete .gitmodules --- .gitmodules | 0 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 .gitmodules diff --git a/.gitmodules b/.gitmodules deleted file mode 100644 index e69de29bb2d1d..0000000000000 From 12817eee57751cefb59646d7cb5cc28d3d3bc37b Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Mon, 4 Nov 2024 10:12:25 -0500 Subject: [PATCH 0421/1099] Squashed commit of the following: commit a4d319437a25de6b98cc4dcec58f1a14ad310d63 Author: daylight Date: Fri Nov 1 21:01:06 2024 +0300 Delete .gitmodules commit 57b7d9814851e504f9c9f179a1bd594ded8184a7 Author: daylight Date: Fri Nov 1 17:47:50 2024 +0300 Delete dist/mv3/log.txt commit c936a72bb3030c821243c34720fba4327af58631 Author: daylight Date: Fri Nov 1 17:47:16 2024 +0300 Update dist/README.md commit b5aaec47b38b584deaa0e3b6351b0f5a8f1f286f Author: daylight Date: Fri Nov 1 17:46:53 2024 +0300 Update release head commit be2da15508c0c34bb5e6319a32e7c06522a923a7 Author: daylight Date: Fri Nov 1 17:46:37 2024 +0300 Update links in readme commit 3019dfc37a5eb8acc52d55fb1b5f218be6a30694 Author: daylight Date: Fri Nov 1 17:46:16 2024 +0300 Update contributing guide commit 3b9333dbd8e69e8a2eecdb77113082caa307e0ad Author: daylight Date: Fri Nov 1 17:45:53 2024 +0300 Update CI commit 2831a0d0fddf3c9b397addda91c4a6d4ae3b2b50 Author: daylight Date: Fri Nov 1 17:45:31 2024 +0300 Update config Revert "Update CI" This reverts commit 3b9333dbd8e69e8a2eecdb77113082caa307e0ad. --- .github/workflows/main.yml | 76 ++++++++++++++++++-------------------- 1 file changed, 35 insertions(+), 41 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 503c7ac54d9e8..5ce7af1212a4f 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -1,13 +1,15 @@ name: uBO release on: - workflow_dispatch: create: branches: master permissions: contents: read +# I used the following project as template to get started: +# https://github.com/dessant/search-by-image/blob/master/.github/workflows/ci.yml + jobs: build: permissions: @@ -16,81 +18,73 @@ jobs: runs-on: ubuntu-latest if: startsWith(github.ref, 'refs/tags/') steps: - - name: Checkout repository + - name: Clone repository uses: actions/checkout@v4 with: persist-credentials: false - - - name: Checkout uAssets repository + - name: Clone uAssets run: | tools/pull-assets.sh - + # https://www.gnu.org/software/bash/manual/html_node/Shell-Parameter-Expansion.html - name: Get release information id: release_info run: | - echo "VERSION=${GITHUB_REF/refs\/tags\//}" >> $GITHUB_ENV - + echo ::set-output name=VERSION::${GITHUB_REF/refs\/tags\//} - name: Assemble release notes run: | > release.body.txt grep -m1 -B10000 -- "----------" CHANGELOG.md >> release.body.txt - sed -e 's/%version%/'"$VERSION"'/g' RELEASE.HEAD.md >> release.body.txt - - - name: Build MV2 packages - run: | - tools/make-chromium.sh $VERSION - tools/make-firefox.sh $VERSION - tools/make-thunderbird.sh $VERSION - tools/make-npm.sh $VERSION - + sed -e 's/%version%/${{ steps.release_info.outputs.VERSION }}/g' RELEASE.HEAD.md >> release.body.txt - name: Create GitHub release id: create_release - uses: softprops/action-gh-release@v2 + uses: actions/create-release@v1 + env: + GITHUB_TOKEN: ${{ github.token }} with: - tag_name: ${{ env.VERSION }} - release_name: ${{ env.VERSION }} + tag_name: ${{ steps.release_info.outputs.VERSION }} + release_name: ${{ steps.release_info.outputs.VERSION }} draft: true prerelease: true body_path: release.body.txt + - name: Build MV2 packages + run: | + tools/make-chromium.sh ${{ steps.release_info.outputs.VERSION }} + tools/make-firefox.sh ${{ steps.release_info.outputs.VERSION }} + tools/make-thunderbird.sh ${{ steps.release_info.outputs.VERSION }} + tools/make-npm.sh ${{ steps.release_info.outputs.VERSION }} + - name: Upload Chromium package + uses: actions/upload-release-asset@v1 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - - name: Upload Chromium package - uses: softprops/action-gh-release@v2 with: upload_url: ${{ steps.create_release.outputs.upload_url }} - asset_path: dist/build/uBlock0_${{ env.VERSION }}.chromium.zip - asset_name: uBlock0_${{ env.VERSION }}.chromium.zip + asset_path: dist/build/uBlock0_${{ steps.release_info.outputs.VERSION }}.chromium.zip + asset_name: uBlock0_${{ steps.release_info.outputs.VERSION }}.chromium.zip asset_content_type: application/octet-stream + - name: Upload Firefox package + uses: actions/upload-release-asset@v1 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - - name: Upload Firefox package - uses: softprops/action-gh-release@v2 with: upload_url: ${{ steps.create_release.outputs.upload_url }} - asset_path: dist/build/uBlock0_${{ env.VERSION }}.firefox.xpi - asset_name: uBlock0_${{ env.VERSION }}.firefox.xpi + asset_path: dist/build/uBlock0_${{ steps.release_info.outputs.VERSION }}.firefox.xpi + asset_name: uBlock0_${{ steps.release_info.outputs.VERSION }}.firefox.xpi asset_content_type: application/octet-stream + - name: Upload Thunderbird package + uses: actions/upload-release-asset@v1 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - - name: Upload Thunderbird package - uses: softprops/action-gh-release@v2 with: upload_url: ${{ steps.create_release.outputs.upload_url }} - asset_path: dist/build/uBlock0_${{ env.VERSION }}.thunderbird.xpi - asset_name: uBlock0_${{ env.VERSION }}.thunderbird.xpi + asset_path: dist/build/uBlock0_${{ steps.release_info.outputs.VERSION }}.thunderbird.xpi + asset_name: uBlock0_${{ steps.release_info.outputs.VERSION }}.thunderbird.xpi asset_content_type: application/octet-stream + - name: Upload NodeJS package + uses: actions/upload-release-asset@v1 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - - name: Upload NodeJS package - uses: softprops/action-gh-release@v2 with: upload_url: ${{ steps.create_release.outputs.upload_url }} - asset_path: dist/build/uBlock0_${{ env.VERSION }}.npm.tgz - asset_name: uBlock0_${{ env.VERSION }}.npm.tgz + asset_path: dist/build/uBlock0_${{ steps.release_info.outputs.VERSION }}.npm.tgz + asset_name: uBlock0_${{ steps.release_info.outputs.VERSION }}.npm.tgz asset_content_type: application/octet-stream - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} From 54ed02e3020cb8a8faa02b5b882dfa140f1add7f Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Tue, 5 Nov 2024 09:25:01 -0500 Subject: [PATCH 0422/1099] Import translation work from https://crowdin.com/project/ublock --- platform/mv3/description/webstore.cy.txt | 4 +-- platform/mv3/description/webstore.ko.txt | 2 +- .../mv3/extension/_locales/cy/messages.json | 6 ++-- .../mv3/extension/_locales/ko/messages.json | 36 +++++++++---------- src/_locales/cs/messages.json | 2 +- src/_locales/fi/messages.json | 2 +- src/_locales/ka/messages.json | 2 +- src/_locales/ko/messages.json | 4 +-- src/_locales/zh_CN/messages.json | 2 +- 9 files changed, 30 insertions(+), 30 deletions(-) diff --git a/platform/mv3/description/webstore.cy.txt b/platform/mv3/description/webstore.cy.txt index 7669a5939f98c..dfc59430e5ecf 100644 --- a/platform/mv3/description/webstore.cy.txt +++ b/platform/mv3/description/webstore.cy.txt @@ -1,6 +1,6 @@ -uBO Lite (uBOL) is a *permission-less* MV3-based content blocker. +uBO Lite (uBOL) yw blocwr cynnwys MV3 sy'n gweithredu heb ganiatâd safonol. -The default ruleset corresponds to uBlock Origin's default filterset: +Mae'r set reolau ddiofyn yn cyfateb i set hidlo diofyn uBlock Origin: - uBlock Origin's built-in filter lists - EasyList diff --git a/platform/mv3/description/webstore.ko.txt b/platform/mv3/description/webstore.ko.txt index 37bf49d9252ff..2d064281a7cc0 100644 --- a/platform/mv3/description/webstore.ko.txt +++ b/platform/mv3/description/webstore.ko.txt @@ -7,7 +7,7 @@ uBO Lite(uBOL)는 *적은 권한을 요구하는* MV3 기반 콘텐츠 차단기 - EasyPrivacy - Peter Lowe’s Ad and tracking server list -You can enable more rulesets by visiting the options page -- click the _Cogs_ icon in the popup panel. +설정 페이지에서 규칙 목록을 더 활성화할 수 있습니다. 팝업 창의 _Cogs_ 아이콘을 누르세요. uBOL은 완전히 선언적이라 필터링 중 영구적으로 실행되는 uBOL 프로세스를 필요로 하지 않으며, CSS/JS 주입 기반 콘텐츠 필터링이 확장 프로그램이 아닌 브라우저 자체에서 더욱 안정적으로 동작합니다. 즉 uBOL 자체는 콘텐츠 차단을 하는 동안 CPU/메모리 리소스를 소비하지 않습니다. uBOL 서비스워커 프로세스는 사용자가 팝업 창이나 설정을 열었을 _때에만_ 동작합니다. diff --git a/platform/mv3/extension/_locales/cy/messages.json b/platform/mv3/extension/_locales/cy/messages.json index 21ac20da0cddb..5a445929a8b0d 100644 --- a/platform/mv3/extension/_locales/cy/messages.json +++ b/platform/mv3/extension/_locales/cy/messages.json @@ -32,7 +32,7 @@ "description": "Label in the popup panel for the current filtering mode" }, "popupTipReport": { - "message": "Report an issue on this website", + "message": "Rhoi gwybod am broblem ar y wefan hon", "description": "Tooltip used for the 'chat' icon in the panel" }, "popupTipDashboard": { @@ -48,7 +48,7 @@ "description": "Label to be used to hide popup panel sections" }, "3pGroupDefault": { - "message": "Default", + "message": "Diofyn", "description": "Header for a ruleset section in 'Filter lists pane'" }, "3pGroupAds": { @@ -104,7 +104,7 @@ "description": "Shown in the About pane" }, "supportS6H": { - "message": "Report a filter issue", + "message": "Adrodd nam ar hidlydd", "description": "Header of 'Report a filter issue' section in Support pane" }, "supportS3P1": { diff --git a/platform/mv3/extension/_locales/ko/messages.json b/platform/mv3/extension/_locales/ko/messages.json index edd5e747c28d7..af43131206324 100644 --- a/platform/mv3/extension/_locales/ko/messages.json +++ b/platform/mv3/extension/_locales/ko/messages.json @@ -32,7 +32,7 @@ "description": "Label in the popup panel for the current filtering mode" }, "popupTipReport": { - "message": "Report an issue on this website", + "message": "이 사이트의 이슈를 신고하기", "description": "Tooltip used for the 'chat' icon in the panel" }, "popupTipDashboard": { @@ -104,67 +104,67 @@ "description": "Shown in the About pane" }, "supportS6H": { - "message": "Report a filter issue", + "message": "필터 이슈 신고", "description": "Header of 'Report a filter issue' section in Support pane" }, "supportS3P1": { - "message": "Report filter issues with specific websites to the uBlockOrigin/uAssets issue tracker. Requires a GitHub account.", + "message": "특정 웹사이트에서 발생하는 필터 이슈를 uBlockOrigin/uAssets 이슈 트래커에 신고할 수 있습니다. GitHub 계정이 필요합니다.", "description": "First paragraph of 'Filter issues' section in Support pane" }, "supportS6P1S1": { - "message": "To avoid burdening volunteers with duplicate reports, please verify that the issue has not already been reported.", + "message": "봉사자들이 중복 신고로 인해 부담을 겪지 않도록, 해당 이슈가 이미 신고되지는 않았는지 확인해주시기 바랍니다.", "description": "A paragraph in the filter issue reporter section" }, "supportFindSpecificButton": { - "message": "Find similar reports", + "message": "유사한 신고 탐색", "description": "A clickable link in the filter issue reporter section" }, "supportS6URL": { - "message": "Address of the webpage:", + "message": "웹페이지 주소:", "description": "Label for the URL of the page" }, "supportS6Select1": { - "message": "The webpage…", + "message": "웹페이지가…", "description": "Label for widget to select type of issue" }, "supportS6Select1Option0": { - "message": "-- Pick an entry --", + "message": "-- 주제 선택 --", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option1": { - "message": "Shows ads or ad leftovers", + "message": "광고나 광고 흔적을 보여줍니다", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option2": { - "message": "Has overlays or other nuisances", + "message": "오버레이나 기타 성가신 요소를 보여줍니다", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option3": { - "message": "Detects uBO Lite", + "message": "uBO Lite 사용을 감지합니다", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option4": { - "message": "Has privacy-related issues", + "message": "개인정보 보호 관련 이슈가 있습니다", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option5": { - "message": "Malfunctions when uBO Lite is enabled", + "message": "uBO Lite를 켜면 깨집니다", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option6": { - "message": "Opens unwanted tabs or windows", + "message": "원치 않는 탭이나 창을 엽니다", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option7": { - "message": "Leads to badware, phishing", + "message": "악성코드, 피싱으로 유도합니다", "description": "An entry in the widget used to select the type of issue" }, "supportS6Checkbox1": { - "message": "Label the webpage as “NSFW” (“Not Safe For Work”)", + "message": "웹페이지를 \"NSFW\" (“Not Safe For Work”)로 분류", "description": "A checkbox to use for NSFW sites" }, "supportReportSpecificButton": { - "message": "Create new report", + "message": "새 신고 생성", "description": "Text for button which open an external webpage in Support pane" }, "firstRunSectionLabel": { @@ -216,7 +216,7 @@ "description": "A short description for the editable field which lists trusted sites" }, "noFilteringModePlaceholder": { - "message": "[hostnames only]\nexample.com\ngames.example\n...", + "message": "[호스트 이름만 작성]\nexample.com\ngames.example\n...", "description": "Default text for in edit field" }, "behaviorSectionLabel": { diff --git a/src/_locales/cs/messages.json b/src/_locales/cs/messages.json index e583814b5d1f1..f2c26a6c8ae31 100644 --- a/src/_locales/cs/messages.json +++ b/src/_locales/cs/messages.json @@ -1192,7 +1192,7 @@ "description": "Button text to navigate to the blocked page" }, "docblockedRedirectPrompt": { - "message": "The blocked page wants to redirect to another site. If you choose to proceed, you will navigate directly to: {{url}}", + "message": "Zablokovaná stránka vás chce přesměrovat na jiný web. Pokud se rozhodnete pokračovat, přejdete přímo na: {{url}}", "description": "Text warning about an incoming redirect" }, "cloudPush": { diff --git a/src/_locales/fi/messages.json b/src/_locales/fi/messages.json index cb3290d859c46..dbdc0638b3e7f 100644 --- a/src/_locales/fi/messages.json +++ b/src/_locales/fi/messages.json @@ -1192,7 +1192,7 @@ "description": "Button text to navigate to the blocked page" }, "docblockedRedirectPrompt": { - "message": "The blocked page wants to redirect to another site. If you choose to proceed, you will navigate directly to: {{url}}", + "message": "Estetty sivu ohjautuu eri sivustolle. Jos jatkat, siirryt suoraan osoitteeseen {{url}}", "description": "Text warning about an incoming redirect" }, "cloudPush": { diff --git a/src/_locales/ka/messages.json b/src/_locales/ka/messages.json index 7655d2857e9dc..b95c3d428376d 100644 --- a/src/_locales/ka/messages.json +++ b/src/_locales/ka/messages.json @@ -1192,7 +1192,7 @@ "description": "Button text to navigate to the blocked page" }, "docblockedRedirectPrompt": { - "message": "The blocked page wants to redirect to another site. If you choose to proceed, you will navigate directly to: {{url}}", + "message": "ეს შეზღუდული გვერდი ცდილობს სხვა საიტზე გადაყვანას. თუ განაგრძობთ, პირდაპირ გაიხსნება: {{url}}", "description": "Text warning about an incoming redirect" }, "cloudPush": { diff --git a/src/_locales/ko/messages.json b/src/_locales/ko/messages.json index c0e354ef7f31e..c3cb5cea26b5f 100644 --- a/src/_locales/ko/messages.json +++ b/src/_locales/ko/messages.json @@ -1012,7 +1012,7 @@ "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option7": { - "message": "Leads to badware, phishing", + "message": "악성코드, 피싱으로 유도합니다", "description": "An entry in the widget used to select the type of issue" }, "supportS6Checkbox1": { @@ -1192,7 +1192,7 @@ "description": "Button text to navigate to the blocked page" }, "docblockedRedirectPrompt": { - "message": "The blocked page wants to redirect to another site. If you choose to proceed, you will navigate directly to: {{url}}", + "message": "차단된 페이지에서 다른 사이트로 이동하려 합니다. 계속하시면, {{url}} 주소로 바로 이동합니다.", "description": "Text warning about an incoming redirect" }, "cloudPush": { diff --git a/src/_locales/zh_CN/messages.json b/src/_locales/zh_CN/messages.json index cfec9ea169c09..651c4224d6862 100644 --- a/src/_locales/zh_CN/messages.json +++ b/src/_locales/zh_CN/messages.json @@ -1192,7 +1192,7 @@ "description": "Button text to navigate to the blocked page" }, "docblockedRedirectPrompt": { - "message": "The blocked page wants to redirect to another site. If you choose to proceed, you will navigate directly to: {{url}}", + "message": "被屏蔽的网页想重定向到另一个网站。如果您选择继续,将直接导航到:{{url}}", "description": "Text warning about an incoming redirect" }, "cloudPush": { From e164250feab9b1da5e9d32d57fcfa00fb9dd8789 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Tue, 5 Nov 2024 09:26:23 -0500 Subject: [PATCH 0423/1099] Comment --- assets/resources/run-at.js | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/assets/resources/run-at.js b/assets/resources/run-at.js index f9971155ae91b..1119699e50c02 100644 --- a/assets/resources/run-at.js +++ b/assets/resources/run-at.js @@ -26,7 +26,23 @@ import { safeSelf } from './safe-self.js'; /* eslint no-prototype-builtins: 0 */ -/******************************************************************************/ +/** + * @helperScriptlet run-at.fn + * + * @description + * Execute a function at a specific page-load milestone. + * + * @param fn + * The function to call. + * + * @param when + * An identifier which tells when the function should be executed. + * See . + * + * @example + * `runAt(( ) => { start(); }, 'interactive')` + * + * */ export function runAt(fn, when) { const intFromReadyState = state => { From 652f1787878ba434c3a65287afcd84082c409397 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Tue, 5 Nov 2024 09:27:11 -0500 Subject: [PATCH 0424/1099] New revision for stable release --- dist/version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dist/version b/dist/version index 77c1ed4ee7b33..84c7d0bad2dae 100644 --- a/dist/version +++ b/dist/version @@ -1 +1 @@ -1.60.1.105 \ No newline at end of file +1.61.0 \ No newline at end of file From e613282698167feda708e62a44b65d44dd528f06 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Thu, 7 Nov 2024 10:23:34 -0500 Subject: [PATCH 0425/1099] Improve `set-cookie` scriptlet Allow negative integer as valid value. Related issue: https://github.com/gorhill/uBlock/pull/3927 Additionally, move cookie-related scriptlets/helpers into its own source code file. --- .../resources/{set-attr.js => attribute.js} | 104 ++++ assets/resources/cookie.js | 410 +++++++++++++++ assets/resources/scriptlets.js | 472 ++---------------- 3 files changed, 545 insertions(+), 441 deletions(-) rename assets/resources/{set-attr.js => attribute.js} (62%) create mode 100644 assets/resources/cookie.js diff --git a/assets/resources/set-attr.js b/assets/resources/attribute.js similarity index 62% rename from assets/resources/set-attr.js rename to assets/resources/attribute.js index cb90b51fae153..511c8eafa8f3c 100644 --- a/assets/resources/set-attr.js +++ b/assets/resources/attribute.js @@ -198,4 +198,108 @@ trustedSetAttr.details = { world: 'ISOLATED', }; +/** + * @scriptlet remove-attr + * + * @description + * Remove one or more attributes from a set of elements. + * + * @param attribute + * The name of the attribute(s) to remove. This can be a list of space- + * separated attribute names. + * + * @param [selector] + * Optional. A CSS selector for the elements to target. Default to + * `[attribute]`, or `[attribute1],[attribute2],...` if more than one + * attribute name is specified. + * + * @param [behavior] + * Optional. Space-separated tokens which modify the default behavior. + * - `asap`: Try to remove the attribute as soon as possible. Default behavior + * is to remove the attribute(s) asynchronously. + * - `stay`: Keep trying to remove the specified attribute(s) on DOM mutations. + * */ + +export function removeAttr( + rawToken = '', + rawSelector = '', + behavior = '' +) { + if ( typeof rawToken !== 'string' ) { return; } + if ( rawToken === '' ) { return; } + const safe = safeSelf(); + const logPrefix = safe.makeLogPrefix('remove-attr', rawToken, rawSelector, behavior); + const tokens = rawToken.split(/\s*\|\s*/); + const selector = tokens + .map(a => `${rawSelector}[${CSS.escape(a)}]`) + .join(','); + if ( safe.logLevel > 1 ) { + safe.uboLog(logPrefix, `Target selector:\n\t${selector}`); + } + const asap = /\basap\b/.test(behavior); + let timerId; + const rmattrAsync = ( ) => { + if ( timerId !== undefined ) { return; } + timerId = safe.onIdle(( ) => { + timerId = undefined; + rmattr(); + }, { timeout: 17 }); + }; + const rmattr = ( ) => { + if ( timerId !== undefined ) { + safe.offIdle(timerId); + timerId = undefined; + } + try { + const nodes = document.querySelectorAll(selector); + for ( const node of nodes ) { + for ( const attr of tokens ) { + if ( node.hasAttribute(attr) === false ) { continue; } + node.removeAttribute(attr); + safe.uboLog(logPrefix, `Removed attribute '${attr}'`); + } + } + } catch(ex) { + } + }; + const mutationHandler = mutations => { + if ( timerId !== undefined ) { return; } + let skip = true; + for ( let i = 0; i < mutations.length && skip; i++ ) { + const { type, addedNodes, removedNodes } = mutations[i]; + if ( type === 'attributes' ) { skip = false; } + for ( let j = 0; j < addedNodes.length && skip; j++ ) { + if ( addedNodes[j].nodeType === 1 ) { skip = false; break; } + } + for ( let j = 0; j < removedNodes.length && skip; j++ ) { + if ( removedNodes[j].nodeType === 1 ) { skip = false; break; } + } + } + if ( skip ) { return; } + asap ? rmattr() : rmattrAsync(); + }; + const start = ( ) => { + rmattr(); + if ( /\bstay\b/.test(behavior) === false ) { return; } + const observer = new MutationObserver(mutationHandler); + observer.observe(document, { + attributes: true, + attributeFilter: tokens, + childList: true, + subtree: true, + }); + }; + runAt(( ) => { start(); }, behavior.split(/\s+/)); +} +removeAttr.details = { + name: 'remove-attr.js', + aliases: [ + 'ra.js', + ], + dependencies: [ + runAt, + safeSelf, + ], +}; + /******************************************************************************/ diff --git a/assets/resources/cookie.js b/assets/resources/cookie.js new file mode 100644 index 0000000000000..6d344ad04eff0 --- /dev/null +++ b/assets/resources/cookie.js @@ -0,0 +1,410 @@ +/******************************************************************************* + + uBlock Origin - a comprehensive, efficient content blocker + Copyright (C) 2019-present Raymond Hill + + 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/}. + + Home: https://github.com/gorhill/uBlock + + The scriptlets below are meant to be injected only into a + web page context. +*/ + +import { safeSelf } from './safe-self.js'; + +/******************************************************************************/ + +export function getSafeCookieValuesFn() { + return [ + 'accept', 'reject', + 'accepted', 'rejected', 'notaccepted', + 'allow', 'disallow', 'deny', + 'allowed', 'denied', + 'approved', 'disapproved', + 'checked', 'unchecked', + 'dismiss', 'dismissed', + 'enable', 'disable', + 'enabled', 'disabled', + 'essential', 'nonessential', + 'forbidden', 'forever', + 'hide', 'hidden', + 'necessary', 'required', + 'ok', + 'on', 'off', + 'true', 't', 'false', 'f', + 'yes', 'y', 'no', 'n', + ]; +} +getSafeCookieValuesFn.details = { + name: 'get-safe-cookie-values.fn', +}; + +/******************************************************************************/ + +export function getAllCookiesFn() { + return document.cookie.split(/\s*;\s*/).map(s => { + const pos = s.indexOf('='); + if ( pos === 0 ) { return; } + if ( pos === -1 ) { return `${s.trim()}=`; } + const key = s.slice(0, pos).trim(); + const value = s.slice(pos+1).trim(); + return { key, value }; + }).filter(s => s !== undefined); +} +getAllCookiesFn.details = { + name: 'get-all-cookies.fn', +}; + +/******************************************************************************/ + +export function getCookieFn( + name = '' +) { + for ( const s of document.cookie.split(/\s*;\s*/) ) { + const pos = s.indexOf('='); + if ( pos === -1 ) { continue; } + if ( s.slice(0, pos) !== name ) { continue; } + return s.slice(pos+1).trim(); + } +} +getCookieFn.details = { + name: 'get-cookie.fn', +}; + +/******************************************************************************/ + +export function setCookieFn( + trusted = false, + name = '', + value = '', + expires = '', + path = '', + options = {}, +) { + // https://datatracker.ietf.org/doc/html/rfc2616#section-2.2 + // https://github.com/uBlockOrigin/uBlock-issues/issues/2777 + if ( trusted === false && /[^!#$%&'*+\-.0-9A-Z[\]^_`a-z|~]/.test(name) ) { + name = encodeURIComponent(name); + } + // https://datatracker.ietf.org/doc/html/rfc6265#section-4.1.1 + // The characters [",] are given a pass from the RFC requirements because + // apparently browsers do not follow the RFC to the letter. + if ( /[^ -:<-[\]-~]/.test(value) ) { + value = encodeURIComponent(value); + } + + const cookieBefore = getCookieFn(name); + if ( cookieBefore !== undefined && options.dontOverwrite ) { return; } + if ( cookieBefore === value && options.reload ) { return; } + + const cookieParts = [ name, '=', value ]; + if ( expires !== '' ) { + cookieParts.push('; expires=', expires); + } + + if ( path === '' ) { path = '/'; } + else if ( path === 'none' ) { path = ''; } + if ( path !== '' && path !== '/' ) { return; } + if ( path === '/' ) { + cookieParts.push('; path=/'); + } + + if ( trusted ) { + if ( options.domain ) { + cookieParts.push(`; domain=${options.domain}`); + } + cookieParts.push('; Secure'); + } else if ( /^__(Host|Secure)-/.test(name) ) { + cookieParts.push('; Secure'); + } + + try { + document.cookie = cookieParts.join(''); + } catch(_) { + } + + const done = getCookieFn(name) === value; + if ( done && options.reload ) { + window.location.reload(); + } + + return done; +} +setCookieFn.details = { + name: 'set-cookie.fn', + dependencies: [ + 'get-cookie.fn', + ], +}; + +/** + * @scriptlet set-cookie + * + * @description + * Set a cookie to a safe value. + * + * @param name + * The name of the cookie to set. + * + * @param value + * The value of the cookie to set. Must be a safe value. Unsafe values will be + * ignored and no cookie will be set. See getSafeCookieValuesFn() helper above. + * + * @param [path] + * Optional. The path of the cookie to set. Default to `/`. + * + * Reference: + * https://github.com/AdguardTeam/Scriptlets/blob/master/src/scriptlets/set-cookie.js + * */ + +export function setCookie( + name = '', + value = '', + path = '' +) { + if ( name === '' ) { return; } + const safe = safeSelf(); + const logPrefix = safe.makeLogPrefix('set-cookie', name, value, path); + const normalized = value.toLowerCase(); + const match = /^("?)(.+)\1$/.exec(normalized); + const unquoted = match && match[2] || normalized; + const validValues = getSafeCookieValuesFn(); + if ( validValues.includes(unquoted) === false ) { + if ( /^-?\d+$/.test(unquoted) === false ) { return; } + const n = parseInt(value, 10) || 0; + if ( n < -32767 || n > 32767 ) { return; } + } + + const done = setCookieFn( + false, + name, + value, + '', + path, + safe.getExtraArgs(Array.from(arguments), 3) + ); + + if ( done ) { + safe.uboLog(logPrefix, 'Done'); + } +} +setCookie.details = { + name: 'set-cookie.js', + world: 'ISOLATED', + dependencies: [ + 'get-safe-cookie-values.fn', + 'safe-self.fn', + 'set-cookie.fn', + ], +}; + +// For compatibility with AdGuard +export function setCookieReload(name, value, path, ...args) { + setCookie(name, value, path, 'reload', '1', ...args); +} +setCookieReload.details = { + name: 'set-cookie-reload.js', + world: 'ISOLATED', + dependencies: [ + 'set-cookie.js', + ], +}; + +/** + * @trustedScriptlet trusted-set-cookie + * + * @description + * Set a cookie to any value. This scriptlet can be used only from a trusted + * source. + * + * @param name + * The name of the cookie to set. + * + * @param value + * The value of the cookie to set. Must be a safe value. Unsafe values will be + * ignored and no cookie will be set. See getSafeCookieValuesFn() helper above. + * + * @param [offsetExpiresSec] + * Optional. The path of the cookie to set. Default to `/`. + * + * @param [path] + * Optional. The path of the cookie to set. Default to `/`. + * + * Reference: + * https://github.com/AdguardTeam/Scriptlets/blob/master/src/scriptlets/set-cookie.js + * */ + +export function trustedSetCookie( + name = '', + value = '', + offsetExpiresSec = '', + path = '' +) { + if ( name === '' ) { return; } + + const safe = safeSelf(); + const logPrefix = safe.makeLogPrefix('set-cookie', name, value, path); + const time = new Date(); + + if ( value.includes('$now$') ) { + value = value.replaceAll('$now$', time.getTime()); + } + if ( value.includes('$currentDate$') ) { + value = value.replaceAll('$currentDate$', time.toUTCString()); + } + if ( value.includes('$currentISODate$') ) { + value = value.replaceAll('$currentISODate$', time.toISOString()); + } + + let expires = ''; + if ( offsetExpiresSec !== '' ) { + if ( offsetExpiresSec === '1day' ) { + time.setDate(time.getDate() + 1); + } else if ( offsetExpiresSec === '1year' ) { + time.setFullYear(time.getFullYear() + 1); + } else { + if ( /^\d+$/.test(offsetExpiresSec) === false ) { return; } + time.setSeconds(time.getSeconds() + parseInt(offsetExpiresSec, 10)); + } + expires = time.toUTCString(); + } + + const done = setCookieFn( + true, + name, + value, + expires, + path, + safeSelf().getExtraArgs(Array.from(arguments), 4) + ); + + if ( done ) { + safe.uboLog(logPrefix, 'Done'); + } +} +trustedSetCookie.details = { + name: 'trusted-set-cookie.js', + requiresTrust: true, + world: 'ISOLATED', + dependencies: [ + 'safe-self.fn', + 'set-cookie.fn', + ], +}; + +// For compatibility with AdGuard +export function trustedSetCookieReload(name, value, offsetExpiresSec, path, ...args) { + trustedSetCookie(name, value, offsetExpiresSec, path, 'reload', '1', ...args); +} +trustedSetCookieReload.details = { + name: 'trusted-set-cookie-reload.js', + requiresTrust: true, + world: 'ISOLATED', + dependencies: [ + 'trusted-set-cookie.js', + ], +}; + +/** + * @scriptlet remove-cookie + * + * @description + * Removes current site cookies specified by name. The removal operation occurs + * immediately when the scriptlet is injected, then when the page is unloaded. + * + * @param needle + * A string or a regex matching the name of the cookie(s) to remove. + * + * @param ['when', token] + * Vararg, optional. The parameter following 'when' tells when extra removal + * operations should take place. + * - `scroll`: when the page is scrolled + * - `keydown`: when a keyboard touch is pressed + * + * */ + +export function removeCookie( + needle = '' +) { + if ( typeof needle !== 'string' ) { return; } + const safe = safeSelf(); + const reName = safe.patternToRegex(needle); + const extraArgs = safe.getExtraArgs(Array.from(arguments), 1); + const throttle = (fn, ms = 500) => { + if ( throttle.timer !== undefined ) { return; } + throttle.timer = setTimeout(( ) => { + throttle.timer = undefined; + fn(); + }, ms); + }; + const remove = ( ) => { + document.cookie.split(';').forEach(cookieStr => { + const pos = cookieStr.indexOf('='); + if ( pos === -1 ) { return; } + const cookieName = cookieStr.slice(0, pos).trim(); + if ( reName.test(cookieName) === false ) { return; } + const part1 = cookieName + '='; + const part2a = '; domain=' + document.location.hostname; + const part2b = '; domain=.' + document.location.hostname; + let part2c, part2d; + const domain = document.domain; + if ( domain ) { + if ( domain !== document.location.hostname ) { + part2c = '; domain=.' + domain; + } + if ( domain.startsWith('www.') ) { + part2d = '; domain=' + domain.replace('www', ''); + } + } + const part3 = '; path=/'; + const part4 = '; Max-Age=-1000; expires=Thu, 01 Jan 1970 00:00:00 GMT'; + document.cookie = part1 + part4; + document.cookie = part1 + part2a + part4; + document.cookie = part1 + part2b + part4; + document.cookie = part1 + part3 + part4; + document.cookie = part1 + part2a + part3 + part4; + document.cookie = part1 + part2b + part3 + part4; + if ( part2c !== undefined ) { + document.cookie = part1 + part2c + part3 + part4; + } + if ( part2d !== undefined ) { + document.cookie = part1 + part2d + part3 + part4; + } + }); + }; + remove(); + window.addEventListener('beforeunload', remove); + if ( typeof extraArgs.when !== 'string' ) { return; } + const supportedEventTypes = [ 'scroll', 'keydown' ]; + const eventTypes = extraArgs.when.split(/\s/); + for ( const type of eventTypes ) { + if ( supportedEventTypes.includes(type) === false ) { continue; } + document.addEventListener(type, ( ) => { + throttle(remove); + }, { passive: true }); + } +} +removeCookie.details = { + name: 'remove-cookie.js', + aliases: [ + 'cookie-remover.js', + ], + world: 'ISOLATED', + dependencies: [ + 'safe-self.fn', + ], +}; + +/******************************************************************************/ diff --git a/assets/resources/scriptlets.js b/assets/resources/scriptlets.js index ba08824916705..8b70e48a5aac6 100644 --- a/assets/resources/scriptlets.js +++ b/assets/resources/scriptlets.js @@ -22,7 +22,25 @@ web page context. */ -import { setAttr, setAttrFn, trustedSetAttr } from './set-attr.js'; +import { + getAllCookiesFn, + getCookieFn, + getSafeCookieValuesFn, + removeCookie, + setCookie, + setCookieFn, + setCookieReload, + trustedSetCookie, + trustedSetCookieReload, +} from './cookie.js'; + +import { + removeAttr, + setAttr, + setAttrFn, + trustedSetAttr, +} from './attribute.js'; + import { runAt } from './run-at.js'; import { safeSelf } from './safe-self.js'; @@ -53,10 +71,22 @@ const registerScriptlet = fn => { }; registerScriptlet(safeSelf); + +registerScriptlet(removeAttr); registerScriptlet(setAttrFn); registerScriptlet(setAttr); registerScriptlet(trustedSetAttr); +registerScriptlet(getAllCookiesFn); +registerScriptlet(getCookieFn); +registerScriptlet(getSafeCookieValuesFn); +registerScriptlet(removeCookie); +registerScriptlet(setCookie); +registerScriptlet(setCookieFn); +registerScriptlet(setCookieReload); +registerScriptlet(trustedSetCookie); +registerScriptlet(trustedSetCookieReload); + /******************************************************************************* Helper functions @@ -793,51 +823,6 @@ function objectFindOwnerFn( /******************************************************************************/ -builtinScriptlets.push({ - name: 'get-safe-cookie-values.fn', - fn: getSafeCookieValuesFn, -}); -function getSafeCookieValuesFn() { - return [ - 'accept', 'reject', - 'accepted', 'rejected', 'notaccepted', - 'allow', 'disallow', 'deny', - 'allowed', 'denied', - 'approved', 'disapproved', - 'checked', 'unchecked', - 'dismiss', 'dismissed', - 'enable', 'disable', - 'enabled', 'disabled', - 'essential', 'nonessential', - 'forbidden', 'forever', - 'hide', 'hidden', - 'necessary', 'required', - 'ok', - 'on', 'off', - 'true', 't', 'false', 'f', - 'yes', 'y', 'no', 'n', - ]; -} - -/******************************************************************************/ - -builtinScriptlets.push({ - name: 'get-all-cookies.fn', - fn: getAllCookiesFn, -}); -function getAllCookiesFn() { - return document.cookie.split(/\s*;\s*/).map(s => { - const pos = s.indexOf('='); - if ( pos === 0 ) { return; } - if ( pos === -1 ) { return `${s.trim()}=`; } - const key = s.slice(0, pos).trim(); - const value = s.slice(pos+1).trim(); - return { key, value }; - }).filter(s => s !== undefined); -} - -/******************************************************************************/ - builtinScriptlets.push({ name: 'get-all-local-storage.fn', fn: getAllLocalStorageFn, @@ -855,90 +840,6 @@ function getAllLocalStorageFn(which = 'localStorage') { /******************************************************************************/ -builtinScriptlets.push({ - name: 'get-cookie.fn', - fn: getCookieFn, -}); -function getCookieFn( - name = '' -) { - for ( const s of document.cookie.split(/\s*;\s*/) ) { - const pos = s.indexOf('='); - if ( pos === -1 ) { continue; } - if ( s.slice(0, pos) !== name ) { continue; } - return s.slice(pos+1).trim(); - } -} - -/******************************************************************************/ - -builtinScriptlets.push({ - name: 'set-cookie.fn', - fn: setCookieFn, - dependencies: [ - 'get-cookie.fn', - ], -}); -function setCookieFn( - trusted = false, - name = '', - value = '', - expires = '', - path = '', - options = {}, -) { - // https://datatracker.ietf.org/doc/html/rfc2616#section-2.2 - // https://github.com/uBlockOrigin/uBlock-issues/issues/2777 - if ( trusted === false && /[^!#$%&'*+\-.0-9A-Z[\]^_`a-z|~]/.test(name) ) { - name = encodeURIComponent(name); - } - // https://datatracker.ietf.org/doc/html/rfc6265#section-4.1.1 - // The characters [",] are given a pass from the RFC requirements because - // apparently browsers do not follow the RFC to the letter. - if ( /[^ -:<-[\]-~]/.test(value) ) { - value = encodeURIComponent(value); - } - - const cookieBefore = getCookieFn(name); - if ( cookieBefore !== undefined && options.dontOverwrite ) { return; } - if ( cookieBefore === value && options.reload ) { return; } - - const cookieParts = [ name, '=', value ]; - if ( expires !== '' ) { - cookieParts.push('; expires=', expires); - } - - if ( path === '' ) { path = '/'; } - else if ( path === 'none' ) { path = ''; } - if ( path !== '' && path !== '/' ) { return; } - if ( path === '/' ) { - cookieParts.push('; path=/'); - } - - if ( trusted ) { - if ( options.domain ) { - cookieParts.push(`; domain=${options.domain}`); - } - cookieParts.push('; Secure'); - } else if ( /^__(Host|Secure)-/.test(name) ) { - cookieParts.push('; Secure'); - } - - try { - document.cookie = cookieParts.join(''); - } catch(_) { - } - - const done = getCookieFn(name) === value; - if ( done && options.reload ) { - window.location.reload(); - } - - return done; -} - -/******************************************************************************/ - builtinScriptlets.push({ name: 'set-local-storage-item.fn', fn: setLocalStorageItemFn, @@ -2347,91 +2248,6 @@ function preventRefresh( /******************************************************************************/ -builtinScriptlets.push({ - name: 'remove-attr.js', - aliases: [ - 'ra.js', - ], - fn: removeAttr, - dependencies: [ - 'run-at.fn', - 'safe-self.fn', - ], -}); -function removeAttr( - rawToken = '', - rawSelector = '', - behavior = '' -) { - if ( typeof rawToken !== 'string' ) { return; } - if ( rawToken === '' ) { return; } - const safe = safeSelf(); - const logPrefix = safe.makeLogPrefix('remove-attr', rawToken, rawSelector, behavior); - const tokens = rawToken.split(/\s*\|\s*/); - const selector = tokens - .map(a => `${rawSelector}[${CSS.escape(a)}]`) - .join(','); - if ( safe.logLevel > 1 ) { - safe.uboLog(logPrefix, `Target selector:\n\t${selector}`); - } - const asap = /\basap\b/.test(behavior); - let timerId; - const rmattrAsync = ( ) => { - if ( timerId !== undefined ) { return; } - timerId = safe.onIdle(( ) => { - timerId = undefined; - rmattr(); - }, { timeout: 17 }); - }; - const rmattr = ( ) => { - if ( timerId !== undefined ) { - safe.offIdle(timerId); - timerId = undefined; - } - try { - const nodes = document.querySelectorAll(selector); - for ( const node of nodes ) { - for ( const attr of tokens ) { - if ( node.hasAttribute(attr) === false ) { continue; } - node.removeAttribute(attr); - safe.uboLog(logPrefix, `Removed attribute '${attr}'`); - } - } - } catch(ex) { - } - }; - const mutationHandler = mutations => { - if ( timerId !== undefined ) { return; } - let skip = true; - for ( let i = 0; i < mutations.length && skip; i++ ) { - const { type, addedNodes, removedNodes } = mutations[i]; - if ( type === 'attributes' ) { skip = false; } - for ( let j = 0; j < addedNodes.length && skip; j++ ) { - if ( addedNodes[j].nodeType === 1 ) { skip = false; break; } - } - for ( let j = 0; j < removedNodes.length && skip; j++ ) { - if ( removedNodes[j].nodeType === 1 ) { skip = false; break; } - } - } - if ( skip ) { return; } - asap ? rmattr() : rmattrAsync(); - }; - const start = ( ) => { - rmattr(); - if ( /\bstay\b/.test(behavior) === false ) { return; } - const observer = new MutationObserver(mutationHandler); - observer.observe(document, { - attributes: true, - attributeFilter: tokens, - childList: true, - subtree: true, - }); - }; - runAt(( ) => { start(); }, behavior.split(/\s+/)); -} - -/******************************************************************************/ - builtinScriptlets.push({ name: 'remove-class.js', aliases: [ @@ -3090,82 +2906,6 @@ function disableNewtabLinks() { /******************************************************************************/ -builtinScriptlets.push({ - name: 'remove-cookie.js', - aliases: [ - 'cookie-remover.js', - ], - fn: cookieRemover, - world: 'ISOLATED', - dependencies: [ - 'safe-self.fn', - ], -}); -// https://github.com/NanoAdblocker/NanoFilters/issues/149 -function cookieRemover( - needle = '' -) { - if ( typeof needle !== 'string' ) { return; } - const safe = safeSelf(); - const reName = safe.patternToRegex(needle); - const extraArgs = safe.getExtraArgs(Array.from(arguments), 1); - const throttle = (fn, ms = 500) => { - if ( throttle.timer !== undefined ) { return; } - throttle.timer = setTimeout(( ) => { - throttle.timer = undefined; - fn(); - }, ms); - }; - const removeCookie = ( ) => { - document.cookie.split(';').forEach(cookieStr => { - const pos = cookieStr.indexOf('='); - if ( pos === -1 ) { return; } - const cookieName = cookieStr.slice(0, pos).trim(); - if ( reName.test(cookieName) === false ) { return; } - const part1 = cookieName + '='; - const part2a = '; domain=' + document.location.hostname; - const part2b = '; domain=.' + document.location.hostname; - let part2c, part2d; - const domain = document.domain; - if ( domain ) { - if ( domain !== document.location.hostname ) { - part2c = '; domain=.' + domain; - } - if ( domain.startsWith('www.') ) { - part2d = '; domain=' + domain.replace('www', ''); - } - } - const part3 = '; path=/'; - const part4 = '; Max-Age=-1000; expires=Thu, 01 Jan 1970 00:00:00 GMT'; - document.cookie = part1 + part4; - document.cookie = part1 + part2a + part4; - document.cookie = part1 + part2b + part4; - document.cookie = part1 + part3 + part4; - document.cookie = part1 + part2a + part3 + part4; - document.cookie = part1 + part2b + part3 + part4; - if ( part2c !== undefined ) { - document.cookie = part1 + part2c + part3 + part4; - } - if ( part2d !== undefined ) { - document.cookie = part1 + part2d + part3 + part4; - } - }); - }; - removeCookie(); - window.addEventListener('beforeunload', removeCookie); - if ( typeof extraArgs.when !== 'string' ) { return; } - const supportedEventTypes = [ 'scroll', 'keydown' ]; - const eventTypes = extraArgs.when.split(/\s/); - for ( const type of eventTypes ) { - if ( supportedEventTypes.includes(type) === false ) { continue; } - document.addEventListener(type, ( ) => { - throttle(removeCookie); - }, { passive: true }); - } -} - -/******************************************************************************/ - builtinScriptlets.push({ name: 'xml-prune.js', fn: xmlPrune, @@ -3827,72 +3567,6 @@ function removeNodeText( replaceNodeTextFn(nodeName, '', '', 'includes', includes || '', ...extraArgs); } -/******************************************************************************* - * - * set-cookie.js - * - * Set specified cookie to a specific value. - * - * Reference: - * https://github.com/AdguardTeam/Scriptlets/blob/master/src/scriptlets/set-cookie.js - * - **/ - -builtinScriptlets.push({ - name: 'set-cookie.js', - fn: setCookie, - world: 'ISOLATED', - dependencies: [ - 'get-safe-cookie-values.fn', - 'safe-self.fn', - 'set-cookie.fn', - ], -}); -function setCookie( - name = '', - value = '', - path = '' -) { - if ( name === '' ) { return; } - const safe = safeSelf(); - const logPrefix = safe.makeLogPrefix('set-cookie', name, value, path); - const normalized = value.toLowerCase(); - const match = /^("?)(.+)\1$/.exec(normalized); - const unquoted = match && match[2] || normalized; - const validValues = getSafeCookieValuesFn(); - if ( validValues.includes(unquoted) === false ) { - if ( /^\d+$/.test(unquoted) === false ) { return; } - const n = parseInt(value, 10); - if ( n > 32767 ) { return; } - } - - const done = setCookieFn( - false, - name, - value, - '', - path, - safe.getExtraArgs(Array.from(arguments), 3) - ); - - if ( done ) { - safe.uboLog(logPrefix, 'Done'); - } -} - -// For compatibility with AdGuard -builtinScriptlets.push({ - name: 'set-cookie-reload.js', - fn: setCookieReload, - world: 'ISOLATED', - dependencies: [ - 'set-cookie.js', - ], -}); -function setCookieReload(name, value, path, ...args) { - setCookie(name, value, path, 'reload', '1', ...args); -} - /******************************************************************************* * * set-local-storage-item.js @@ -4155,90 +3829,6 @@ function trustedSetConstant( setConstantFn(true, ...args); } -/******************************************************************************* - * - * trusted-set-cookie.js - * - * Set specified cookie to an arbitrary value. - * - * Reference: - * https://github.com/AdguardTeam/Scriptlets/blob/master/src/scriptlets/trusted-set-cookie.js#L23 - * - **/ - -builtinScriptlets.push({ - name: 'trusted-set-cookie.js', - requiresTrust: true, - fn: trustedSetCookie, - world: 'ISOLATED', - dependencies: [ - 'safe-self.fn', - 'set-cookie.fn', - ], -}); -function trustedSetCookie( - name = '', - value = '', - offsetExpiresSec = '', - path = '' -) { - if ( name === '' ) { return; } - - const safe = safeSelf(); - const logPrefix = safe.makeLogPrefix('set-cookie', name, value, path); - const time = new Date(); - - if ( value.includes('$now$') ) { - value = value.replaceAll('$now$', time.getTime()); - } - if ( value.includes('$currentDate$') ) { - value = value.replaceAll('$currentDate$', time.toUTCString()); - } - if ( value.includes('$currentISODate$') ) { - value = value.replaceAll('$currentISODate$', time.toISOString()); - } - - let expires = ''; - if ( offsetExpiresSec !== '' ) { - if ( offsetExpiresSec === '1day' ) { - time.setDate(time.getDate() + 1); - } else if ( offsetExpiresSec === '1year' ) { - time.setFullYear(time.getFullYear() + 1); - } else { - if ( /^\d+$/.test(offsetExpiresSec) === false ) { return; } - time.setSeconds(time.getSeconds() + parseInt(offsetExpiresSec, 10)); - } - expires = time.toUTCString(); - } - - const done = setCookieFn( - true, - name, - value, - expires, - path, - safeSelf().getExtraArgs(Array.from(arguments), 4) - ); - - if ( done ) { - safe.uboLog(logPrefix, 'Done'); - } -} - -// For compatibility with AdGuard -builtinScriptlets.push({ - name: 'trusted-set-cookie-reload.js', - requiresTrust: true, - fn: trustedSetCookieReload, - world: 'ISOLATED', - dependencies: [ - 'trusted-set-cookie.js', - ], -}); -function trustedSetCookieReload(name, value, offsetExpiresSec, path, ...args) { - trustedSetCookie(name, value, offsetExpiresSec, path, 'reload', '1', ...args); -} - /******************************************************************************* * * trusted-set-local-storage-item.js From f90ccb7c6229830216b55a8bae2330dee8e04fc0 Mon Sep 17 00:00:00 2001 From: Sander Lepik Date: Thu, 7 Nov 2024 17:27:26 +0200 Subject: [PATCH 0426/1099] Move Estonian list away from .php extension (#3926) --- assets/assets.dev.json | 4 ++-- assets/assets.json | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/assets/assets.dev.json b/assets/assets.dev.json index 9c7accc0c3fea..f776738c10eb9 100644 --- a/assets/assets.dev.json +++ b/assets/assets.dev.json @@ -584,8 +584,8 @@ "title": "🇪🇪ee: Eesti saitidele kohandatud filter", "tags": "ads estonian", "lang": "et", - "contentURL": "https://adblock.ee/list.php", - "supportURL": "https://adblock.ee/" + "contentURL": "https://adblock.ee/list.txt", + "supportURL": "https://adblock.ee" }, "FIN-0": { "content": "filters", diff --git a/assets/assets.json b/assets/assets.json index 8b0e79f39cdd5..76967068a6e99 100644 --- a/assets/assets.json +++ b/assets/assets.json @@ -584,8 +584,8 @@ "title": "🇪🇪ee: Eesti saitidele kohandatud filter", "tags": "ads estonian", "lang": "et", - "contentURL": "https://adblock.ee/list.php", - "supportURL": "https://adblock.ee/" + "contentURL": "https://adblock.ee/list.txt", + "supportURL": "https://adblock.ee" }, "FIN-0": { "content": "filters", From e854c4752bd8db3631c26e5dcb7a0c95b41a3c07 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Thu, 7 Nov 2024 11:59:57 -0500 Subject: [PATCH 0427/1099] New revision for dev build --- dist/version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dist/version b/dist/version index 84c7d0bad2dae..4e9a7a74d7d84 100644 --- a/dist/version +++ b/dist/version @@ -1 +1 @@ -1.61.0 \ No newline at end of file +1.61.1.0 \ No newline at end of file From 34eed9abef96baffa70c4b2a62db0513d7dd73d1 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Thu, 7 Nov 2024 12:09:41 -0500 Subject: [PATCH 0428/1099] Update changelog --- CHANGELOG.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2d62d815d12a4..033673f3cdaca 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,11 @@ +- [Improve `set-cookie` scriptlet](https://github.com/gorhill/uBlock/commit/e613282698) + +---------- + +# 1.61.0 + +## Fixes / changes + - [Improve `prevent-refresh` scriptlet](https://github.com/gorhill/uBlock/commit/8884f259c1) - [Improve `googlesyndication_adsbygoogle.js` scriptlet](https://github.com/gorhill/uBlock/commit/f645e8f0d2) - [Offer ability to skip redirects in strict-blocked page](https://github.com/gorhill/uBlock/commit/20b54185fa) From fd60f54a5f23179639b409f059704a6b587a1413 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Thu, 7 Nov 2024 12:15:43 -0500 Subject: [PATCH 0429/1099] Make Firefox dev build auto-update --- dist/firefox/updates.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dist/firefox/updates.json b/dist/firefox/updates.json index a34746cba91dc..62d26e1c496c5 100644 --- a/dist/firefox/updates.json +++ b/dist/firefox/updates.json @@ -3,9 +3,9 @@ "uBlock0@raymondhill.net": { "updates": [ { - "version": "1.60.1.105", + "version": "1.61.1.0", "browser_specific_settings": { "gecko": { "strict_min_version": "78.0" } }, - "update_link": "https://github.com/gorhill/uBlock/releases/download/1.60.1rc5/uBlock0_1.60.1rc5.firefox.signed.xpi" + "update_link": "https://github.com/gorhill/uBlock/releases/download/1.61.1b0/uBlock0_1.61.1b0.firefox.signed.xpi" } ] } From 41616df866f5f87dfc0787479376f268e9722b29 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Fri, 8 Nov 2024 08:32:19 -0500 Subject: [PATCH 0430/1099] Improve `trusted-suppress-native-method` scriptlet Add `debug` as disposition option: if the `how` parameter is `debug`, the scriptlet will trigger a `debugger` statement and the target method won't be suppressed. Useful to find out how the method is being called by page code. To be used for investigation purpose only. --- assets/resources/scriptlets.js | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/assets/resources/scriptlets.js b/assets/resources/scriptlets.js index 8b70e48a5aac6..900f9423952e0 100644 --- a/assets/resources/scriptlets.js +++ b/assets/resources/scriptlets.js @@ -4432,13 +4432,10 @@ function trustedSuppressNativeMethod( safe.uboLog(logPrefix, `Arguments:\n${callArgs.join('\n')}`); return context.reflect(); } - if ( callArgs.length < signatureArgs.length ) { - return context.reflect(); - } for ( let i = 0; i < signatureArgs.length; i++ ) { const signatureArg = signatureArgs[i]; if ( signatureArg === undefined ) { continue; } - const targetArg = callArgs[i]; + const targetArg = i < callArgs.length ? callArgs[i] : undefined; if ( signatureArg.type === 'exact' ) { if ( targetArg !== signatureArg.value ) { return context.reflect(); @@ -4450,6 +4447,10 @@ function trustedSuppressNativeMethod( } } } + if ( how === 'debug' ) { + debugger; // eslint-disable-line no-debugger + return context.reflect(); + } safe.uboLog(logPrefix, `Suppressed:\n${callArgs.join('\n')}`); if ( how === 'abort' ) { throw new ReferenceError(); From ce4908b3415317cff1ab6dfc7d8c90478cdbbcdf Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Fri, 8 Nov 2024 08:48:07 -0500 Subject: [PATCH 0431/1099] Improve `prevent-xhr` scriptlet --- assets/resources/scriptlets.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/assets/resources/scriptlets.js b/assets/resources/scriptlets.js index 900f9423952e0..1218d0dd73e80 100644 --- a/assets/resources/scriptlets.js +++ b/assets/resources/scriptlets.js @@ -1345,11 +1345,11 @@ function preventXhrFn( 'content-type': '', 'content-length': '', }, + url: haystack.url, props: { response: { value: '' }, responseText: { value: '' }, responseXML: { value: null }, - responseURL: { value: haystack.url }, }, }); xhrInstances.set(this, xhrDetails); @@ -1405,6 +1405,7 @@ function preventXhrFn( xhrDetails.headers['content-length'] = `${xhrDetails.props.response.value}`.length; Object.defineProperties(xhrDetails.xhr, { readyState: { value: 4 }, + responseURL: { value: xhrDetails.url }, status: { value: 200 }, statusText: { value: 'OK' }, }); @@ -1414,6 +1415,7 @@ function preventXhrFn( Promise.resolve(xhrText).then(( ) => xhrDetails).then(details => { Object.defineProperties(details.xhr, { readyState: { value: 1, configurable: true }, + responseURL: { value: xhrDetails.url }, }); safeDispatchEvent(details.xhr, 'readystatechange'); return details; From e5a088738dd7aeb7b84df6766ae036de76f82000 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Fri, 8 Nov 2024 11:22:31 -0500 Subject: [PATCH 0432/1099] Keep moving related scriptlets into separate files --- assets/resources/attribute.js | 17 +- assets/resources/base.js | 40 ++++ assets/resources/cookie.js | 55 +++--- assets/resources/localstorage.js | 237 +++++++++++++++++++++++ assets/resources/run-at.js | 22 ++- assets/resources/safe-self.js | 6 +- assets/resources/scriptlets.js | 312 ++----------------------------- src/js/redirect-engine.js | 2 + 8 files changed, 351 insertions(+), 340 deletions(-) create mode 100644 assets/resources/base.js create mode 100644 assets/resources/localstorage.js diff --git a/assets/resources/attribute.js b/assets/resources/attribute.js index 511c8eafa8f3c..0c50daf879003 100644 --- a/assets/resources/attribute.js +++ b/assets/resources/attribute.js @@ -22,6 +22,7 @@ web page context. */ +import { registerScriptlet } from './base.js'; import { runAt } from './run-at.js'; import { safeSelf } from './safe-self.js'; @@ -95,13 +96,13 @@ export function setAttrFn( }; runAt(( ) => { start(); }, 'idle'); } -setAttrFn.details = { +registerScriptlet(setAttrFn, { name: 'set-attr.fn', dependencies: [ runAt, safeSelf, ], -}; +}); /** * @scriptlet set-attr @@ -149,14 +150,14 @@ export function setAttr( setAttrFn(logPrefix, selector, attr, value); } -setAttr.details = { +registerScriptlet(setAttr, { name: 'set-attr.js', dependencies: [ safeSelf, setAttrFn, ], world: 'ISOLATED', -}; +}); /** * @trustedScriptlet trusted-set-attr @@ -188,7 +189,7 @@ export function trustedSetAttr( const logPrefix = safe.makeLogPrefix('trusted-set-attr', selector, attr, value); setAttrFn(logPrefix, selector, attr, value); } -trustedSetAttr.details = { +registerScriptlet(trustedSetAttr, { name: 'trusted-set-attr.js', requiresTrust: true, dependencies: [ @@ -196,7 +197,7 @@ trustedSetAttr.details = { setAttrFn, ], world: 'ISOLATED', -}; +}); /** * @scriptlet remove-attr @@ -291,7 +292,7 @@ export function removeAttr( }; runAt(( ) => { start(); }, behavior.split(/\s+/)); } -removeAttr.details = { +registerScriptlet(removeAttr, { name: 'remove-attr.js', aliases: [ 'ra.js', @@ -300,6 +301,6 @@ removeAttr.details = { runAt, safeSelf, ], -}; +}); /******************************************************************************/ diff --git a/assets/resources/base.js b/assets/resources/base.js new file mode 100644 index 0000000000000..dc677b7cb794c --- /dev/null +++ b/assets/resources/base.js @@ -0,0 +1,40 @@ +/******************************************************************************* + + uBlock Origin - a comprehensive, efficient content blocker + Copyright (C) 2019-present Raymond Hill + + 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/}. + + Home: https://github.com/gorhill/uBlock + + The scriptlets below are meant to be injected only into a + web page context. +*/ + +export const registeredScriptlets = []; + +export const registerScriptlet = (fn, details) => { + if ( typeof details !== 'object' ) { + throw new ReferenceError('Missing scriptlet details'); + } + details.fn = fn; + fn.details = details; + if ( Array.isArray(details.dependencies) ) { + details.dependencies.forEach((fn, i, array) => { + if ( typeof fn !== 'function' ) { return; } + array[i] = fn.details.name; + }); + } + registeredScriptlets.push(details); +}; diff --git a/assets/resources/cookie.js b/assets/resources/cookie.js index 6d344ad04eff0..1be419121f5b5 100644 --- a/assets/resources/cookie.js +++ b/assets/resources/cookie.js @@ -22,6 +22,7 @@ web page context. */ +import { registerScriptlet } from './base.js'; import { safeSelf } from './safe-self.js'; /******************************************************************************/ @@ -47,9 +48,9 @@ export function getSafeCookieValuesFn() { 'yes', 'y', 'no', 'n', ]; } -getSafeCookieValuesFn.details = { +registerScriptlet(getSafeCookieValuesFn, { name: 'get-safe-cookie-values.fn', -}; +}); /******************************************************************************/ @@ -63,9 +64,9 @@ export function getAllCookiesFn() { return { key, value }; }).filter(s => s !== undefined); } -getAllCookiesFn.details = { +registerScriptlet(getAllCookiesFn, { name: 'get-all-cookies.fn', -}; +}); /******************************************************************************/ @@ -79,9 +80,9 @@ export function getCookieFn( return s.slice(pos+1).trim(); } } -getCookieFn.details = { +registerScriptlet(getCookieFn, { name: 'get-cookie.fn', -}; +}); /******************************************************************************/ @@ -142,12 +143,12 @@ export function setCookieFn( return done; } -setCookieFn.details = { +registerScriptlet(setCookieFn, { name: 'set-cookie.fn', dependencies: [ - 'get-cookie.fn', + getCookieFn, ], -}; +}); /** * @scriptlet set-cookie @@ -200,27 +201,27 @@ export function setCookie( safe.uboLog(logPrefix, 'Done'); } } -setCookie.details = { +registerScriptlet(setCookie, { name: 'set-cookie.js', world: 'ISOLATED', dependencies: [ - 'get-safe-cookie-values.fn', - 'safe-self.fn', - 'set-cookie.fn', + getSafeCookieValuesFn, + safeSelf, + setCookieFn, ], -}; +}); // For compatibility with AdGuard export function setCookieReload(name, value, path, ...args) { setCookie(name, value, path, 'reload', '1', ...args); } -setCookieReload.details = { +registerScriptlet(setCookieReload, { name: 'set-cookie-reload.js', world: 'ISOLATED', dependencies: [ - 'set-cookie.js', + setCookie, ], -}; +}); /** * @trustedScriptlet trusted-set-cookie @@ -294,28 +295,28 @@ export function trustedSetCookie( safe.uboLog(logPrefix, 'Done'); } } -trustedSetCookie.details = { +registerScriptlet(trustedSetCookie, { name: 'trusted-set-cookie.js', requiresTrust: true, world: 'ISOLATED', dependencies: [ - 'safe-self.fn', - 'set-cookie.fn', + safeSelf, + setCookieFn, ], -}; +}); // For compatibility with AdGuard export function trustedSetCookieReload(name, value, offsetExpiresSec, path, ...args) { trustedSetCookie(name, value, offsetExpiresSec, path, 'reload', '1', ...args); } -trustedSetCookieReload.details = { +registerScriptlet(trustedSetCookieReload, { name: 'trusted-set-cookie-reload.js', requiresTrust: true, world: 'ISOLATED', dependencies: [ - 'trusted-set-cookie.js', + trustedSetCookie, ], -}; +}); /** * @scriptlet remove-cookie @@ -396,15 +397,15 @@ export function removeCookie( }, { passive: true }); } } -removeCookie.details = { +registerScriptlet(removeCookie, { name: 'remove-cookie.js', aliases: [ 'cookie-remover.js', ], world: 'ISOLATED', dependencies: [ - 'safe-self.fn', + safeSelf, ], -}; +}); /******************************************************************************/ diff --git a/assets/resources/localstorage.js b/assets/resources/localstorage.js new file mode 100644 index 0000000000000..6ea13df73a49e --- /dev/null +++ b/assets/resources/localstorage.js @@ -0,0 +1,237 @@ +/******************************************************************************* + + uBlock Origin - a comprehensive, efficient content blocker + Copyright (C) 2019-present Raymond Hill + + 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/}. + + Home: https://github.com/gorhill/uBlock + + The scriptlets below are meant to be injected only into a + web page context. +*/ + +import { getSafeCookieValuesFn } from './cookie.js'; +import { registerScriptlet } from './base.js'; +import { safeSelf } from './safe-self.js'; + +/******************************************************************************/ + +export function getAllLocalStorageFn(which = 'localStorage') { + const storage = self[which]; + const out = []; + for ( let i = 0; i < storage.length; i++ ) { + const key = storage.key(i); + const value = storage.getItem(key); + return { key, value }; + } + return out; +} +registerScriptlet(getAllLocalStorageFn, { + name: 'get-all-local-storage.fn', +}); + +/******************************************************************************/ + +export function setLocalStorageItemFn( + which = 'local', + trusted = false, + key = '', + value = '', +) { + if ( key === '' ) { return; } + + // For increased compatibility with AdGuard + if ( value === 'emptyArr' ) { + value = '[]'; + } else if ( value === 'emptyObj' ) { + value = '{}'; + } + + const trustedValues = [ + '', + 'undefined', 'null', + '{}', '[]', '""', + '$remove$', + ...getSafeCookieValuesFn(), + ]; + + if ( trusted ) { + if ( value.includes('$now$') ) { + value = value.replaceAll('$now$', Date.now()); + } + if ( value.includes('$currentDate$') ) { + value = value.replaceAll('$currentDate$', `${Date()}`); + } + if ( value.includes('$currentISODate$') ) { + value = value.replaceAll('$currentISODate$', (new Date()).toISOString()); + } + } else { + const normalized = value.toLowerCase(); + const match = /^("?)(.+)\1$/.exec(normalized); + const unquoted = match && match[2] || normalized; + if ( trustedValues.includes(unquoted) === false ) { + if ( /^-?\d+$/.test(unquoted) === false ) { return; } + const n = parseInt(unquoted, 10) || 0; + if ( n < -32767 || n > 32767 ) { return; } + } + } + + try { + const storage = self[`${which}Storage`]; + if ( value === '$remove$' ) { + const safe = safeSelf(); + const pattern = safe.patternToRegex(key, undefined, true ); + const toRemove = []; + for ( let i = 0, n = storage.length; i < n; i++ ) { + const key = storage.key(i); + if ( pattern.test(key) ) { toRemove.push(key); } + } + for ( const key of toRemove ) { + storage.removeItem(key); + } + } else { + storage.setItem(key, `${value}`); + } + } catch(ex) { + } +} +registerScriptlet(setLocalStorageItemFn, { + name: 'set-local-storage-item.fn', + dependencies: [ + getSafeCookieValuesFn, + safeSelf, + ], +}); + +/******************************************************************************/ + +export function removeCacheStorageItem( + cacheNamePattern = '', + requestPattern = '' +) { + if ( cacheNamePattern === '' ) { return; } + const safe = safeSelf(); + const logPrefix = safe.makeLogPrefix('remove-cache-storage-item', cacheNamePattern, requestPattern); + const cacheStorage = self.caches; + if ( cacheStorage instanceof Object === false ) { return; } + const reCache = safe.patternToRegex(cacheNamePattern, undefined, true); + const reRequest = safe.patternToRegex(requestPattern, undefined, true); + cacheStorage.keys().then(cacheNames => { + for ( const cacheName of cacheNames ) { + if ( reCache.test(cacheName) === false ) { continue; } + if ( requestPattern === '' ) { + cacheStorage.delete(cacheName).then(result => { + if ( safe.logLevel > 1 ) { + safe.uboLog(logPrefix, `Deleting ${cacheName}`); + } + if ( result !== true ) { return; } + safe.uboLog(logPrefix, `Deleted ${cacheName}: ${result}`); + }); + continue; + } + cacheStorage.open(cacheName).then(cache => { + cache.keys().then(requests => { + for ( const request of requests ) { + if ( reRequest.test(request.url) === false ) { continue; } + if ( safe.logLevel > 1 ) { + safe.uboLog(logPrefix, `Deleting ${cacheName}/${request.url}`); + } + cache.delete(request).then(result => { + if ( result !== true ) { return; } + safe.uboLog(logPrefix, `Deleted ${cacheName}/${request.url}: ${result}`); + }); + } + }); + }); + } + }); +} +registerScriptlet(removeCacheStorageItem, { + name: 'remove-cache-storage-item.fn', + world: 'ISOLATED', + dependencies: [ + safeSelf, + ], +}); + +/******************************************************************************* + * + * set-local-storage-item.js + * set-session-storage-item.js + * + * Set a local/session storage entry to a specific, allowed value. + * + * Reference: + * https://github.com/AdguardTeam/Scriptlets/blob/master/src/scriptlets/set-local-storage-item.js + * https://github.com/AdguardTeam/Scriptlets/blob/master/src/scriptlets/set-session-storage-item.js + * + **/ + +export function setLocalStorageItem(key = '', value = '') { + setLocalStorageItemFn('local', false, key, value); +} +registerScriptlet(setLocalStorageItem, { + name: 'set-local-storage-item.js', + world: 'ISOLATED', + dependencies: [ + setLocalStorageItemFn, + ], +}); + +export function setSessionStorageItem(key = '', value = '') { + setLocalStorageItemFn('session', false, key, value); +} +registerScriptlet(setSessionStorageItem, { + name: 'set-session-storage-item.js', + world: 'ISOLATED', + dependencies: [ + setLocalStorageItemFn, + ], +}); + +/******************************************************************************* + * + * trusted-set-local-storage-item.js + * + * Set a local storage entry to an arbitrary value. + * + * Reference: + * https://github.com/AdguardTeam/Scriptlets/blob/master/src/scriptlets/trusted-set-local-storage-item.js + * + **/ + +export function trustedSetLocalStorageItem(key = '', value = '') { + setLocalStorageItemFn('local', true, key, value); +} +registerScriptlet(trustedSetLocalStorageItem, { + name: 'trusted-set-local-storage-item.js', + requiresTrust: true, + world: 'ISOLATED', + dependencies: [ + setLocalStorageItemFn, + ], +}); + +export function trustedSetSessionStorageItem(key = '', value = '') { + setLocalStorageItemFn('session', true, key, value); +} +registerScriptlet(trustedSetSessionStorageItem, { + name: 'trusted-set-session-storage-item.js', + requiresTrust: true, + world: 'ISOLATED', + dependencies: [ + setLocalStorageItemFn, + ], +}); diff --git a/assets/resources/run-at.js b/assets/resources/run-at.js index 1119699e50c02..0a7dfb5e4dbe5 100644 --- a/assets/resources/run-at.js +++ b/assets/resources/run-at.js @@ -22,6 +22,7 @@ web page context. */ +import { registerScriptlet } from './base.js'; import { safeSelf } from './safe-self.js'; /* eslint no-prototype-builtins: 0 */ @@ -72,9 +73,26 @@ export function runAt(fn, when) { const args = [ 'readystatechange', onStateChange, { capture: true } ]; safe.addEventListener.apply(document, args); } -runAt.details = { +registerScriptlet(runAt, { name: 'run-at.fn', dependencies: [ safeSelf, ], -}; +}); + +/******************************************************************************/ + +function runAtHtmlElementFn(fn) { + if ( document.documentElement ) { + fn(); + return; + } + const observer = new MutationObserver(( ) => { + observer.disconnect(); + fn(); + }); + observer.observe(document, { childList: true }); +} +registerScriptlet(runAtHtmlElementFn, { + name: 'run-at-html-element.fn', +}); diff --git a/assets/resources/safe-self.js b/assets/resources/safe-self.js index 33f3201e7550e..bad9eece359ae 100644 --- a/assets/resources/safe-self.js +++ b/assets/resources/safe-self.js @@ -22,6 +22,8 @@ web page context. */ +import { registerScriptlet } from './base.js'; + /******************************************************************************/ // Externally added to the private namespace in which scriptlets execute. @@ -213,6 +215,6 @@ export function safeSelf() { } return safe; } -safeSelf.details = { +registerScriptlet(safeSelf, { name: 'safe-self.fn', -}; +}); diff --git a/assets/resources/scriptlets.js b/assets/resources/scriptlets.js index 1218d0dd73e80..531959e23de66 100644 --- a/assets/resources/scriptlets.js +++ b/assets/resources/scriptlets.js @@ -22,70 +22,24 @@ web page context. */ -import { - getAllCookiesFn, - getCookieFn, - getSafeCookieValuesFn, - removeCookie, - setCookie, - setCookieFn, - setCookieReload, - trustedSetCookie, - trustedSetCookieReload, -} from './cookie.js'; - -import { - removeAttr, - setAttr, - setAttrFn, - trustedSetAttr, -} from './attribute.js'; - +import './attribute.js'; +import './cookie.js'; +import './localstorage.js'; +import './run-at.js'; +import './safe-self.js'; + +import { getAllCookiesFn } from './cookie.js'; +import { getAllLocalStorageFn } from './localstorage.js'; +import { registeredScriptlets } from './base.js'; import { runAt } from './run-at.js'; import { safeSelf } from './safe-self.js'; -/* eslint no-prototype-builtins: 0 */ - // Externally added to the private namespace in which scriptlets execute. /* global scriptletGlobals */ -export const builtinScriptlets = []; - -/******************************************************************************/ +/* eslint no-prototype-builtins: 0 */ -// Register scriptlets declared in other files. - -const registerScriptlet = fn => { - const details = fn.details; - if ( typeof details !== 'object' ) { - throw new ReferenceError('Unknown scriptlet function'); - } - details.fn = fn; - if ( Array.isArray(details.dependencies) ) { - details.dependencies.forEach((fn, i, array) => { - if ( typeof fn !== 'function' ) { return; } - array[i] = fn.details.name; - }); - } - builtinScriptlets.push(details); -}; - -registerScriptlet(safeSelf); - -registerScriptlet(removeAttr); -registerScriptlet(setAttrFn); -registerScriptlet(setAttr); -registerScriptlet(trustedSetAttr); - -registerScriptlet(getAllCookiesFn); -registerScriptlet(getCookieFn); -registerScriptlet(getSafeCookieValuesFn); -registerScriptlet(removeCookie); -registerScriptlet(setCookie); -registerScriptlet(setCookieFn); -registerScriptlet(setCookieReload); -registerScriptlet(trustedSetCookie); -registerScriptlet(trustedSetCookieReload); +export const builtinScriptlets = registeredScriptlets; /******************************************************************************* @@ -141,34 +95,6 @@ function shouldDebug(details) { /******************************************************************************/ -builtinScriptlets.push({ - name: 'run-at.fn', - fn: runAt, - dependencies: [ - 'safe-self.fn', - ], -}); - -/******************************************************************************/ - -builtinScriptlets.push({ - name: 'run-at-html-element.fn', - fn: runAtHtmlElementFn, -}); -function runAtHtmlElementFn(fn) { - if ( document.documentElement ) { - fn(); - return; - } - const observer = new MutationObserver(( ) => { - observer.disconnect(); - fn(); - }); - observer.observe(document, { childList: true }); -} - -/******************************************************************************/ - // Reference: // https://github.com/AdguardTeam/Scriptlets/blob/master/wiki/about-scriptlets.md#prevent-xhr // @@ -823,97 +749,6 @@ function objectFindOwnerFn( /******************************************************************************/ -builtinScriptlets.push({ - name: 'get-all-local-storage.fn', - fn: getAllLocalStorageFn, -}); -function getAllLocalStorageFn(which = 'localStorage') { - const storage = self[which]; - const out = []; - for ( let i = 0; i < storage.length; i++ ) { - const key = storage.key(i); - const value = storage.getItem(key); - return { key, value }; - } - return out; -} - -/******************************************************************************/ - -builtinScriptlets.push({ - name: 'set-local-storage-item.fn', - fn: setLocalStorageItemFn, - dependencies: [ - 'get-safe-cookie-values.fn', - 'safe-self.fn', - ], -}); -function setLocalStorageItemFn( - which = 'local', - trusted = false, - key = '', - value = '', -) { - if ( key === '' ) { return; } - - // For increased compatibility with AdGuard - if ( value === 'emptyArr' ) { - value = '[]'; - } else if ( value === 'emptyObj' ) { - value = '{}'; - } - - const trustedValues = [ - '', - 'undefined', 'null', - '{}', '[]', '""', - '$remove$', - ...getSafeCookieValuesFn(), - ]; - - if ( trusted ) { - if ( value.includes('$now$') ) { - value = value.replaceAll('$now$', Date.now()); - } - if ( value.includes('$currentDate$') ) { - value = value.replaceAll('$currentDate$', `${Date()}`); - } - if ( value.includes('$currentISODate$') ) { - value = value.replaceAll('$currentISODate$', (new Date()).toISOString()); - } - } else { - const normalized = value.toLowerCase(); - const match = /^("?)(.+)\1$/.exec(normalized); - const unquoted = match && match[2] || normalized; - if ( trustedValues.includes(unquoted) === false ) { - if ( /^\d+$/.test(unquoted) === false ) { return; } - const n = parseInt(unquoted, 10); - if ( n > 32767 ) { return; } - } - } - - try { - const storage = self[`${which}Storage`]; - if ( value === '$remove$' ) { - const safe = safeSelf(); - const pattern = safe.patternToRegex(key, undefined, true ); - const toRemove = []; - for ( let i = 0, n = storage.length; i < n; i++ ) { - const key = storage.key(i); - if ( pattern.test(key) ) { toRemove.push(key); } - } - for ( const key of toRemove ) { - storage.removeItem(key); - } - } else { - storage.setItem(key, `${value}`); - } - } catch(ex) { - } -} - -/******************************************************************************/ - builtinScriptlets.push({ name: 'matches-stack-trace.fn', fn: matchesStackTrace, @@ -3569,43 +3404,6 @@ function removeNodeText( replaceNodeTextFn(nodeName, '', '', 'includes', includes || '', ...extraArgs); } -/******************************************************************************* - * - * set-local-storage-item.js - * set-session-storage-item.js - * - * Set a local/session storage entry to a specific, allowed value. - * - * Reference: - * https://github.com/AdguardTeam/Scriptlets/blob/master/src/scriptlets/set-local-storage-item.js - * https://github.com/AdguardTeam/Scriptlets/blob/master/src/scriptlets/set-session-storage-item.js - * - **/ - -builtinScriptlets.push({ - name: 'set-local-storage-item.js', - fn: setLocalStorageItem, - world: 'ISOLATED', - dependencies: [ - 'set-local-storage-item.fn', - ], -}); -function setLocalStorageItem(key = '', value = '') { - setLocalStorageItemFn('local', false, key, value); -} - -builtinScriptlets.push({ - name: 'set-session-storage-item.js', - fn: setSessionStorageItem, - world: 'ISOLATED', - dependencies: [ - 'set-local-storage-item.fn', - ], -}); -function setSessionStorageItem(key = '', value = '') { - setLocalStorageItemFn('session', false, key, value); -} - /******************************************************************************* * * @scriptlet prevent-canvas @@ -3693,57 +3491,6 @@ function multiup() { document.addEventListener('click', handler, { capture: true }); } -/******************************************************************************/ - -builtinScriptlets.push({ - name: 'remove-cache-storage-item.js', - fn: removeCacheStorageItem, - world: 'ISOLATED', - dependencies: [ - 'safe-self.fn', - ], -}); -function removeCacheStorageItem( - cacheNamePattern = '', - requestPattern = '' -) { - if ( cacheNamePattern === '' ) { return; } - const safe = safeSelf(); - const logPrefix = safe.makeLogPrefix('remove-cache-storage-item', cacheNamePattern, requestPattern); - const cacheStorage = self.caches; - if ( cacheStorage instanceof Object === false ) { return; } - const reCache = safe.patternToRegex(cacheNamePattern, undefined, true); - const reRequest = safe.patternToRegex(requestPattern, undefined, true); - cacheStorage.keys().then(cacheNames => { - for ( const cacheName of cacheNames ) { - if ( reCache.test(cacheName) === false ) { continue; } - if ( requestPattern === '' ) { - cacheStorage.delete(cacheName).then(result => { - if ( safe.logLevel > 1 ) { - safe.uboLog(logPrefix, `Deleting ${cacheName}`); - } - if ( result !== true ) { return; } - safe.uboLog(logPrefix, `Deleted ${cacheName}: ${result}`); - }); - continue; - } - cacheStorage.open(cacheName).then(cache => { - cache.keys().then(requests => { - for ( const request of requests ) { - if ( reRequest.test(request.url) === false ) { continue; } - if ( safe.logLevel > 1 ) { - safe.uboLog(logPrefix, `Deleting ${cacheName}/${request.url}`); - } - cache.delete(request).then(result => { - if ( result !== true ) { return; } - safe.uboLog(logPrefix, `Deleted ${cacheName}/${request.url}: ${result}`); - }); - } - }); - }); - } - }); -} /******************************************************************************* @@ -3831,43 +3578,6 @@ function trustedSetConstant( setConstantFn(true, ...args); } -/******************************************************************************* - * - * trusted-set-local-storage-item.js - * - * Set a local storage entry to an arbitrary value. - * - * Reference: - * https://github.com/AdguardTeam/Scriptlets/blob/master/src/scriptlets/trusted-set-local-storage-item.js - * - **/ - -builtinScriptlets.push({ - name: 'trusted-set-local-storage-item.js', - requiresTrust: true, - fn: trustedSetLocalStorageItem, - world: 'ISOLATED', - dependencies: [ - 'set-local-storage-item.fn', - ], -}); -function trustedSetLocalStorageItem(key = '', value = '') { - setLocalStorageItemFn('local', true, key, value); -} - -builtinScriptlets.push({ - name: 'trusted-set-session-storage-item.js', - requiresTrust: true, - fn: trustedSetSessionStorageItem, - world: 'ISOLATED', - dependencies: [ - 'set-local-storage-item.fn', - ], -}); -function trustedSetSessionStorageItem(key = '', value = '') { - setLocalStorageItemFn('session', true, key, value); -} - /******************************************************************************* * * trusted-replace-fetch-response.js diff --git a/src/js/redirect-engine.js b/src/js/redirect-engine.js index 1edb37624c582..f16b73ca0765f 100644 --- a/src/js/redirect-engine.js +++ b/src/js/redirect-engine.js @@ -333,6 +333,8 @@ class RedirectEngine { } } this.modifyTime = Date.now(); + }).catch(reason => { + console.error(reason); }), ]; From 93e2d7f1436ed537d160433d3a4ad5cea80933f7 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Fri, 8 Nov 2024 11:25:56 -0500 Subject: [PATCH 0433/1099] Update changelog --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 033673f3cdaca..065ac0936ea71 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,6 @@ +- [Keep moving related scriptlets into separate files](https://github.com/gorhill/uBlock/commit/e5a088738d) +- [Improve `prevent-xhr` scriptlet](https://github.com/gorhill/uBlock/commit/ce4908b341) +- [Improve `trusted-suppress-native-method` scriptlet](https://github.com/gorhill/uBlock/commit/41616df866) - [Improve `set-cookie` scriptlet](https://github.com/gorhill/uBlock/commit/e613282698) ---------- From dc81a39a996e9f9b1b78f3a3e444d136a8068832 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Fri, 8 Nov 2024 11:26:13 -0500 Subject: [PATCH 0434/1099] New revision for dev build --- dist/version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dist/version b/dist/version index 4e9a7a74d7d84..1c77daac69716 100644 --- a/dist/version +++ b/dist/version @@ -1 +1 @@ -1.61.1.0 \ No newline at end of file +1.61.1.1 \ No newline at end of file From 689ffbe7d3b791e0e8100379f3b3d028b3d1491d Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Fri, 8 Nov 2024 12:07:30 -0500 Subject: [PATCH 0435/1099] Address workflow warnings --- .github/workflows/main.yml | 57 +++++++++----------------------------- 1 file changed, 13 insertions(+), 44 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 5ce7af1212a4f..f70f3fbe57096 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -25,6 +25,12 @@ jobs: - name: Clone uAssets run: | tools/pull-assets.sh + - name: Build MV2 packages + run: | + tools/make-chromium.sh ${{ steps.release_info.outputs.VERSION }} + tools/make-firefox.sh ${{ steps.release_info.outputs.VERSION }} + tools/make-thunderbird.sh ${{ steps.release_info.outputs.VERSION }} + tools/make-npm.sh ${{ steps.release_info.outputs.VERSION }} # https://www.gnu.org/software/bash/manual/html_node/Shell-Parameter-Expansion.html - name: Get release information id: release_info @@ -37,54 +43,17 @@ jobs: sed -e 's/%version%/${{ steps.release_info.outputs.VERSION }}/g' RELEASE.HEAD.md >> release.body.txt - name: Create GitHub release id: create_release - uses: actions/create-release@v1 + uses: softprops/action-gh-release@v2 env: GITHUB_TOKEN: ${{ github.token }} with: tag_name: ${{ steps.release_info.outputs.VERSION }} - release_name: ${{ steps.release_info.outputs.VERSION }} + name: ${{ steps.release_info.outputs.VERSION }} draft: true prerelease: true body_path: release.body.txt - - name: Build MV2 packages - run: | - tools/make-chromium.sh ${{ steps.release_info.outputs.VERSION }} - tools/make-firefox.sh ${{ steps.release_info.outputs.VERSION }} - tools/make-thunderbird.sh ${{ steps.release_info.outputs.VERSION }} - tools/make-npm.sh ${{ steps.release_info.outputs.VERSION }} - - name: Upload Chromium package - uses: actions/upload-release-asset@v1 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - with: - upload_url: ${{ steps.create_release.outputs.upload_url }} - asset_path: dist/build/uBlock0_${{ steps.release_info.outputs.VERSION }}.chromium.zip - asset_name: uBlock0_${{ steps.release_info.outputs.VERSION }}.chromium.zip - asset_content_type: application/octet-stream - - name: Upload Firefox package - uses: actions/upload-release-asset@v1 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - with: - upload_url: ${{ steps.create_release.outputs.upload_url }} - asset_path: dist/build/uBlock0_${{ steps.release_info.outputs.VERSION }}.firefox.xpi - asset_name: uBlock0_${{ steps.release_info.outputs.VERSION }}.firefox.xpi - asset_content_type: application/octet-stream - - name: Upload Thunderbird package - uses: actions/upload-release-asset@v1 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - with: - upload_url: ${{ steps.create_release.outputs.upload_url }} - asset_path: dist/build/uBlock0_${{ steps.release_info.outputs.VERSION }}.thunderbird.xpi - asset_name: uBlock0_${{ steps.release_info.outputs.VERSION }}.thunderbird.xpi - asset_content_type: application/octet-stream - - name: Upload NodeJS package - uses: actions/upload-release-asset@v1 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - with: - upload_url: ${{ steps.create_release.outputs.upload_url }} - asset_path: dist/build/uBlock0_${{ steps.release_info.outputs.VERSION }}.npm.tgz - asset_name: uBlock0_${{ steps.release_info.outputs.VERSION }}.npm.tgz - asset_content_type: application/octet-stream + files: | + dist/build/uBlock0_${{ steps.release_info.outputs.VERSION }}.chromium.zip + dist/build/uBlock0_${{ steps.release_info.outputs.VERSION }}.firefox.xpi + dist/build/uBlock0_${{ steps.release_info.outputs.VERSION }}.thunderbird.xpi + dist/build/uBlock0_${{ steps.release_info.outputs.VERSION }}.npm.tgz From 74d8be96d25143959a6d8bbcd55b4ac04de63bfd Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Fri, 8 Nov 2024 12:17:27 -0500 Subject: [PATCH 0436/1099] Fix more workflow warnings; fix steps order --- .github/workflows/main.yml | 33 ++++++++++++++------------------- 1 file changed, 14 insertions(+), 19 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index f70f3fbe57096..1d34c0242d012 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -7,9 +7,6 @@ on: permissions: contents: read -# I used the following project as template to get started: -# https://github.com/dessant/search-by-image/blob/master/.github/workflows/ci.yml - jobs: build: permissions: @@ -25,35 +22,33 @@ jobs: - name: Clone uAssets run: | tools/pull-assets.sh - - name: Build MV2 packages - run: | - tools/make-chromium.sh ${{ steps.release_info.outputs.VERSION }} - tools/make-firefox.sh ${{ steps.release_info.outputs.VERSION }} - tools/make-thunderbird.sh ${{ steps.release_info.outputs.VERSION }} - tools/make-npm.sh ${{ steps.release_info.outputs.VERSION }} - # https://www.gnu.org/software/bash/manual/html_node/Shell-Parameter-Expansion.html - name: Get release information - id: release_info run: | - echo ::set-output name=VERSION::${GITHUB_REF/refs\/tags\//} + echo "VERSION=${GITHUB_REF/refs\/tags\//}" >> $GITHUB_ENV + - name: Build MV2 packages + run: | + tools/make-chromium.sh ${{ env.VERSION }} + tools/make-firefox.sh ${{ env.VERSION }} + tools/make-thunderbird.sh ${{ env.VERSION }} + tools/make-npm.sh ${{ env.VERSION }} - name: Assemble release notes run: | > release.body.txt grep -m1 -B10000 -- "----------" CHANGELOG.md >> release.body.txt - sed -e 's/%version%/${{ steps.release_info.outputs.VERSION }}/g' RELEASE.HEAD.md >> release.body.txt + sed -e 's/%version%/${{ env.VERSION }}/g' RELEASE.HEAD.md >> release.body.txt - name: Create GitHub release id: create_release uses: softprops/action-gh-release@v2 env: GITHUB_TOKEN: ${{ github.token }} with: - tag_name: ${{ steps.release_info.outputs.VERSION }} - name: ${{ steps.release_info.outputs.VERSION }} + tag_name: ${{ env.VERSION }} + name: ${{ env.VERSION }} draft: true prerelease: true body_path: release.body.txt files: | - dist/build/uBlock0_${{ steps.release_info.outputs.VERSION }}.chromium.zip - dist/build/uBlock0_${{ steps.release_info.outputs.VERSION }}.firefox.xpi - dist/build/uBlock0_${{ steps.release_info.outputs.VERSION }}.thunderbird.xpi - dist/build/uBlock0_${{ steps.release_info.outputs.VERSION }}.npm.tgz + dist/build/uBlock0_${{ env.VERSION }}.chromium.zip + dist/build/uBlock0_${{ env.VERSION }}.firefox.xpi + dist/build/uBlock0_${{ env.VERSION }}.thunderbird.xpi + dist/build/uBlock0_${{ env.VERSION }}.npm.tgz From 74921a0f270537e1bb86c529d85f34cf240b104d Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Fri, 8 Nov 2024 12:26:24 -0500 Subject: [PATCH 0437/1099] Make Firefox dev build auto-update --- dist/firefox/updates.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dist/firefox/updates.json b/dist/firefox/updates.json index 62d26e1c496c5..1d32fd31a1b9e 100644 --- a/dist/firefox/updates.json +++ b/dist/firefox/updates.json @@ -3,9 +3,9 @@ "uBlock0@raymondhill.net": { "updates": [ { - "version": "1.61.1.0", + "version": "1.61.1.1", "browser_specific_settings": { "gecko": { "strict_min_version": "78.0" } }, - "update_link": "https://github.com/gorhill/uBlock/releases/download/1.61.1b0/uBlock0_1.61.1b0.firefox.signed.xpi" + "update_link": "https://github.com/gorhill/uBlock/releases/download/1.61.1b1/uBlock0_1.61.1b1.firefox.signed.xpi" } ] } From 15dae359f7e9e75c49a86bad8a05ec3f7add3c35 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Mon, 11 Nov 2024 13:20:54 -0500 Subject: [PATCH 0438/1099] [mv3] Add support to add/remove rulesets through policies Related discussion: https://github.com/uBlockOrigin/uBOL-home/discussions/35#discussioncomment-11157444 New policy setting: `rulesets` Type: array Type of array items: string Each item in the list is a list id (as seen in `rulesets/ruleset-details.json`), prefixed with either `+` to enable the ruleset, or `-` to disable the ruleset. Users will not be able to enable or disable rulesets present in the `rulesets` policy. Disabled rulesets will not appear in the dashboard. Use `-*` to remove all non-default rulesets, except for those added using `+[ruleset_id]`. Additionally, some work has been done to properly handle policy changes in a non-blocking and deferred manner, as I observed that it often takes long for calls to `storage.manage.get` to resolve. This potentailly takes care of the following issue: https://github.com/uBlockOrigin/uBOL-home/issues/174 --- Makefile | 11 +- platform/mv3/extension/css/settings.css | 8 ++ platform/mv3/extension/js/admin.js | 121 +++++++++++++++++++ platform/mv3/extension/js/background.js | 107 ++++++---------- platform/mv3/extension/js/config.js | 81 +++++++++++++ platform/mv3/extension/js/mode-manager.js | 53 ++++---- platform/mv3/extension/js/ruleset-manager.js | 37 ++++-- platform/mv3/extension/js/settings.js | 38 +++++- platform/mv3/extension/js/utils.js | 3 +- platform/mv3/extension/managed_storage.json | 6 + 10 files changed, 352 insertions(+), 113 deletions(-) create mode 100644 platform/mv3/extension/js/admin.js create mode 100644 platform/mv3/extension/js/config.js diff --git a/Makefile b/Makefile index 194a3af43af08..b308dc4be1e0b 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,8 @@ # https://stackoverflow.com/a/6273809 run_options := $(filter-out $@,$(MAKECMDGOALS)) -.PHONY: all clean cleanassets test lint chromium opera firefox npm dig mv3 mv3-quick \ +.PHONY: all clean cleanassets test lint chromium opera firefox npm dig \ + mv3 mv3-quick mv3-chromium mv3-firefox \ compare maxcost medcost mincost modifiers record wasm sources := $(wildcard assets/* assets/*/* dist/version src/* src/*/* src/*/*/* src/*/*/*/*) @@ -55,12 +56,16 @@ dig: dist/build/uBlock0.dig dig-snfe: dig cd dist/build/uBlock0.dig && npm run snfe $(run_options) -mv3-chromium: tools/make-mv3.sh $(sources) $(platform) +dist/build/uBOLite.chromium: tools/make-mv3.sh $(sources) $(platform) tools/make-mv3.sh chromium -mv3-firefox: tools/make-mv3.sh $(sources) $(platform) +mv3-chromium: dist/build/uBOLite.chromium + +dist/build/uBOLite.firefox: tools/make-mv3.sh $(sources) $(platform) tools/make-mv3.sh firefox +mv3-firefox: dist/build/uBOLite.firefox + mv3-quick: tools/make-mv3.sh $(sources) $(platform) tools/make-mv3.sh quick diff --git a/platform/mv3/extension/css/settings.css b/platform/mv3/extension/css/settings.css index b81e79a5256e2..3de13560cc4f1 100644 --- a/platform/mv3/extension/css/settings.css +++ b/platform/mv3/extension/css/settings.css @@ -114,11 +114,19 @@ h3[data-i18n="filteringMode0Name"]::first-letter { .groupEntry:not([data-groupkey="user"]) .listEntry:not(.isDefault).unused { display: none; } + +.listEntry.fromAdmin:has(input[disabled]:not(:checked)) { + display: none; + } .listEntry > * { margin-left: 0; margin-right: 0; unicode-bidi: embed; } +.listEntry .checkbox:has(input[disabled]), +.listEntry .checkbox:has(input[disabled]) ~ span { + filter: var(--checkbox-disabled-filter); + } .listEntry .listname { white-space: nowrap; } diff --git a/platform/mv3/extension/js/admin.js b/platform/mv3/extension/js/admin.js new file mode 100644 index 0000000000000..8a6b91fb77765 --- /dev/null +++ b/platform/mv3/extension/js/admin.js @@ -0,0 +1,121 @@ +/******************************************************************************* + + uBlock Origin Lite - a comprehensive, MV3-compliant content blocker + Copyright (C) 2022-present Raymond Hill + + 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/}. + + Home: https://github.com/gorhill/uBlock +*/ + +import { + adminRead, + localRead, localWrite, + sessionRead, sessionWrite, +} from './ext.js'; + +import { + enableRulesets, + getRulesetDetails, +} from './ruleset-manager.js'; + +import { + getTrustedSites, + readFilteringModeDetails, +} from './mode-manager.js'; + +import { broadcastMessage } from './utils.js'; +import { dnr } from './ext.js'; +import { registerInjectables } from './scripting-manager.js'; +import { rulesetConfig } from './config.js'; +import { ubolLog } from './debug.js'; + +/******************************************************************************/ + +const adminSettings = { + keys: new Set(), + timer: undefined, + change(key) { + this.keys.add(key); + if ( this.timer !== undefined ) { return; } + this.timer = self.setTimeout(( ) => { + this.timer = undefined; + this.process(); + }, 127); + }, + async process() { + if ( this.keys.has('rulesets') ) { + ubolLog('admin setting "rulesets" changed'); + await enableRulesets(rulesetConfig.enabledRulesets); + await registerInjectables(); + const results = await Promise.all([ + getAdminRulesets(), + dnr.getEnabledRulesets(), + ]); + const [ adminRulesets, enabledRulesets ] = results; + broadcastMessage({ adminRulesets, enabledRulesets }); + } + if ( this.keys.has('noFiltering') ) { + ubolLog('admin setting "noFiltering" changed'); + await readFilteringModeDetails(true); + const trustedSites = await getTrustedSites(); + broadcastMessage({ trustedSites: Array.from(trustedSites) }); + } + this.keys.clear(); + } +}; + +/******************************************************************************/ + +export async function getAdminRulesets() { + const adminList = await adminReadEx('rulesets'); + const adminRulesets = new Set(Array.isArray(adminList) && adminList || []); + if ( adminRulesets.has('-*') ) { + adminRulesets.delete('-*'); + const rulesetDetails = await getRulesetDetails(); + for ( const ruleset of rulesetDetails.values() ) { + if ( ruleset.enabled ) { continue; } + if ( adminRulesets.has(`+${ruleset.id}`) ) { continue; } + adminRulesets.add(`-${ruleset.id}`); + } + } + return Array.from(adminRulesets); +} + +/******************************************************************************/ + +export async function adminReadEx(key) { + let cacheValue; + const session = await sessionRead(`admin_${key}`); + if ( session ) { + cacheValue = session.data; + } else { + const local = await localRead(`admin_${key}`); + if ( local ) { + cacheValue = local.data; + } + } + adminRead(key).then(async value => { + const adminKey = `admin_${key}`; + await Promise.all([ + sessionWrite(adminKey, { data: value }), + localWrite(adminKey, { data: value }), + ]); + if ( JSON.stringify(value) === JSON.stringify(cacheValue) ) { return; } + adminSettings.change(key); + }); + return cacheValue; +} + +/******************************************************************************/ diff --git a/platform/mv3/extension/js/background.js b/platform/mv3/extension/js/background.js index 791625d2a1c92..ea8c9f608c1ce 100644 --- a/platform/mv3/extension/js/background.js +++ b/platform/mv3/extension/js/background.js @@ -19,105 +19,63 @@ Home: https://github.com/gorhill/uBlock */ +import { + MODE_BASIC, + MODE_OPTIMAL, + getDefaultFilteringMode, + getFilteringMode, + getTrustedSites, + setDefaultFilteringMode, + setFilteringMode, + setTrustedSites, + syncWithBrowserPermissions, +} from './mode-manager.js'; + import { adminRead, browser, dnr, localRead, localWrite, runtime, - sessionRead, sessionWrite, windows, } from './ext.js'; import { - defaultRulesetsFromLanguage, enableRulesets, getEnabledRulesetsDetails, getRulesetDetails, updateDynamicRules, } from './ruleset-manager.js'; -import { - MODE_BASIC, - MODE_OPTIMAL, - getDefaultFilteringMode, - getFilteringMode, - getTrustedSites, - setDefaultFilteringMode, - setFilteringMode, - setTrustedSites, - syncWithBrowserPermissions, -} from './mode-manager.js'; - import { getMatchedRules, isSideloaded, ubolLog, } from './debug.js'; +import { + loadRulesetConfig, + process, + rulesetConfig, + saveRulesetConfig, +} from './config.js'; + import { broadcastMessage } from './utils.js'; +import { getAdminRulesets } from './admin.js'; import { registerInjectables } from './scripting-manager.js'; /******************************************************************************/ -const rulesetConfig = { - version: '', - enabledRulesets: [ 'default' ], - autoReload: true, - showBlockedCount: true, -}; - const UBOL_ORIGIN = runtime.getURL('').replace(/\/$/, ''); const canShowBlockedCount = typeof dnr.setExtensionActionOptions === 'function'; -let firstRun = false; -let wakeupRun = false; - /******************************************************************************/ function getCurrentVersion() { return runtime.getManifest().version; } -async function loadRulesetConfig() { - let data = await sessionRead('rulesetConfig'); - if ( data ) { - rulesetConfig.version = data.version; - rulesetConfig.enabledRulesets = data.enabledRulesets; - rulesetConfig.autoReload = typeof data.autoReload === 'boolean' - ? data.autoReload - : true; - rulesetConfig.showBlockedCount = typeof data.showBlockedCount === 'boolean' - ? data.showBlockedCount - : true; - wakeupRun = true; - return; - } - data = await localRead('rulesetConfig'); - if ( data ) { - rulesetConfig.version = data.version; - rulesetConfig.enabledRulesets = data.enabledRulesets; - rulesetConfig.autoReload = typeof data.autoReload === 'boolean' - ? data.autoReload - : true; - rulesetConfig.showBlockedCount = typeof data.showBlockedCount === 'boolean' - ? data.showBlockedCount - : true; - sessionWrite('rulesetConfig', rulesetConfig); - return; - } - rulesetConfig.enabledRulesets = await defaultRulesetsFromLanguage(); - sessionWrite('rulesetConfig', rulesetConfig); - localWrite('rulesetConfig', rulesetConfig); - firstRun = true; -} - -async function saveRulesetConfig() { - sessionWrite('rulesetConfig', rulesetConfig); - return localWrite('rulesetConfig', rulesetConfig); -} - /******************************************************************************/ async function hasGreatPowers(origin) { @@ -216,7 +174,9 @@ function onMessage(request, sender, callback) { }).then(( ) => { registerInjectables(); callback(); - broadcastMessage({ enabledRulesets: rulesetConfig.enabledRulesets }); + return dnr.getEnabledRulesets(); + }).then(enabledRulesets => { + broadcastMessage({ enabledRulesets }); }); return true; } @@ -227,25 +187,28 @@ function onMessage(request, sender, callback) { getTrustedSites(), getRulesetDetails(), dnr.getEnabledRulesets(), + getAdminRulesets(), ]).then(results => { const [ defaultFilteringMode, trustedSites, rulesetDetails, enabledRulesets, + adminRulesets, ] = results; callback({ defaultFilteringMode, trustedSites: Array.from(trustedSites), enabledRulesets, + adminRulesets, maxNumberOfEnabledRulesets: dnr.MAX_NUMBER_OF_ENABLED_STATIC_RULESETS, rulesetDetails: Array.from(rulesetDetails.values()), autoReload: rulesetConfig.autoReload, showBlockedCount: rulesetConfig.showBlockedCount, canShowBlockedCount, - firstRun, + firstRun: process.firstRun, }); - firstRun = false; + process.firstRun = false; }); return true; } @@ -367,6 +330,8 @@ function onMessage(request, sender, callback) { default: break; } + + return false; } /******************************************************************************/ @@ -374,12 +339,12 @@ function onMessage(request, sender, callback) { async function start() { await loadRulesetConfig(); - if ( wakeupRun === false ) { + if ( process.wakeupRun === false ) { await enableRulesets(rulesetConfig.enabledRulesets); } // We need to update the regex rules only when ruleset version changes. - if ( wakeupRun === false ) { + if ( process.wakeupRun === false ) { const currentVersion = getCurrentVersion(); if ( currentVersion !== rulesetConfig.version ) { ubolLog(`Version change: ${rulesetConfig.version} => ${currentVersion}`); @@ -396,7 +361,7 @@ async function start() { // Unsure whether the browser remembers correctly registered css/scripts // after we quit the browser. For now uBOL will check unconditionally at // launch time whether content css/scripts are properly registered. - if ( wakeupRun === false || permissionsChanged ) { + if ( process.wakeupRun === false || permissionsChanged ) { registerInjectables(); const enabledRulesets = await dnr.getEnabledRulesets(); @@ -409,7 +374,7 @@ async function start() { // https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/declarativeNetRequest // Firefox API does not support `dnr.setExtensionActionOptions` - if ( wakeupRun === false && canShowBlockedCount ) { + if ( process.wakeupRun === false && canShowBlockedCount ) { dnr.setExtensionActionOptions({ displayActionCountAsBadgeText: rulesetConfig.showBlockedCount, }); @@ -421,7 +386,7 @@ async function start() { ( ) => { onPermissionsRemoved(); } ); - if ( firstRun ) { + if ( process.firstRun ) { const enableOptimal = await hasOmnipotence(); if ( enableOptimal ) { const afterLevel = await setDefaultFilteringMode(MODE_OPTIMAL); @@ -434,7 +399,7 @@ async function start() { if ( disableFirstRunPage !== true ) { runtime.openOptionsPage(); } else { - firstRun = false; + process.firstRun = false; } } } diff --git a/platform/mv3/extension/js/config.js b/platform/mv3/extension/js/config.js new file mode 100644 index 0000000000000..bb74a4416c551 --- /dev/null +++ b/platform/mv3/extension/js/config.js @@ -0,0 +1,81 @@ +/******************************************************************************* + + uBlock Origin Lite - a comprehensive, MV3-compliant content blocker + Copyright (C) 2022-present Raymond Hill + + 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/}. + + Home: https://github.com/gorhill/uBlock +*/ + +import { + localRead, localWrite, + sessionRead, sessionWrite, +} from './ext.js'; + +import { defaultRulesetsFromLanguage } from './ruleset-manager.js'; + +/******************************************************************************/ + +export const rulesetConfig = { + version: '', + enabledRulesets: [ 'default' ], + autoReload: true, + showBlockedCount: true, +}; + +export const process = { + firstRun: false, + wakeupRun: false, +}; + +/******************************************************************************/ + +export async function loadRulesetConfig() { + const sessionData = await sessionRead('rulesetConfig'); + if ( sessionData ) { + rulesetConfig.version = sessionData.version; + rulesetConfig.enabledRulesets = sessionData.enabledRulesets; + rulesetConfig.autoReload = typeof sessionData.autoReload === 'boolean' + ? sessionData.autoReload + : true; + rulesetConfig.showBlockedCount = typeof sessionData.showBlockedCount === 'boolean' + ? sessionData.showBlockedCount + : true; + process.wakeupRun = true; + return; + } + const localData = await localRead('rulesetConfig'); + if ( localData ) { + rulesetConfig.version = localData.version; + rulesetConfig.enabledRulesets = localData.enabledRulesets; + rulesetConfig.autoReload = typeof localData.autoReload === 'boolean' + ? localData.autoReload + : true; + rulesetConfig.showBlockedCount = typeof localData.showBlockedCount === 'boolean' + ? localData.showBlockedCount + : true; + sessionWrite('rulesetConfig', rulesetConfig); + return; + } + rulesetConfig.enabledRulesets = await defaultRulesetsFromLanguage(); + sessionWrite('rulesetConfig', rulesetConfig); + localWrite('rulesetConfig', rulesetConfig); + process.firstRun = true; +} + +export async function saveRulesetConfig() { + sessionWrite('rulesetConfig', rulesetConfig); + return localWrite('rulesetConfig', rulesetConfig); +} diff --git a/platform/mv3/extension/js/mode-manager.js b/platform/mv3/extension/js/mode-manager.js index c3b9a01bb877b..335c8da166d73 100644 --- a/platform/mv3/extension/js/mode-manager.js +++ b/platform/mv3/extension/js/mode-manager.js @@ -24,14 +24,6 @@ import { getDynamicRules, } from './ruleset-manager.js'; -import { - adminRead, - browser, - dnr, - localRead, localRemove, localWrite, - sessionRead, sessionWrite, -} from './ext.js'; - import { broadcastMessage, hostnamesFromMatches, @@ -39,6 +31,15 @@ import { toBroaderHostname, } from './utils.js'; +import { + browser, + dnr, + localRead, localWrite, + sessionRead, sessionWrite, +} from './ext.js'; + +import { adminReadEx } from './admin.js'; + /******************************************************************************/ // 0: no filtering @@ -224,38 +225,40 @@ function applyFilteringMode(filteringModes, hostname, afterLevel) { /******************************************************************************/ -async function readFilteringModeDetails() { - if ( readFilteringModeDetails.cache ) { - return readFilteringModeDetails.cache; - } - const sessionModes = await sessionRead('filteringModeDetails'); - if ( sessionModes instanceof Object ) { - readFilteringModeDetails.cache = unserializeModeDetails(sessionModes); - return readFilteringModeDetails.cache; +export async function readFilteringModeDetails(bypassCache = false) { + if ( bypassCache === false ) { + if ( readFilteringModeDetails.cache ) { + return readFilteringModeDetails.cache; + } + const sessionModes = await sessionRead('filteringModeDetails'); + if ( sessionModes instanceof Object ) { + readFilteringModeDetails.cache = unserializeModeDetails(sessionModes); + return readFilteringModeDetails.cache; + } } let [ userModes, adminNoFiltering ] = await Promise.all([ localRead('filteringModeDetails'), - localRead('adminNoFiltering'), + adminReadEx('noFiltering'), ]); if ( userModes === undefined ) { userModes = { basic: [ 'all-urls' ] }; } userModes = unserializeModeDetails(userModes); if ( Array.isArray(adminNoFiltering) ) { + if ( adminNoFiltering.includes('-*') ) { + userModes.none.clear(); + } for ( const hn of adminNoFiltering ) { - applyFilteringMode(userModes, hn, 0); + if ( hn.charAt(0) === '-' ) { + userModes.none.delete(hn.slice(1)); + } else { + applyFilteringMode(userModes, hn, 0); + } } } filteringModesToDNR(userModes); sessionWrite('filteringModeDetails', serializeModeDetails(userModes)); readFilteringModeDetails.cache = userModes; - adminRead('noFiltering').then(results => { - if ( results ) { - localWrite('adminNoFiltering', results); - } else { - localRemove('adminNoFiltering'); - } - }); return userModes; } diff --git a/platform/mv3/extension/js/ruleset-manager.js b/platform/mv3/extension/js/ruleset-manager.js index 7f7c6e1eefd2b..d19a9f28e9d65 100644 --- a/platform/mv3/extension/js/ruleset-manager.js +++ b/platform/mv3/extension/js/ruleset-manager.js @@ -19,8 +19,14 @@ Home: https://github.com/gorhill/uBlock */ -import { browser, dnr, i18n } from './ext.js'; +import { + browser, + dnr, + i18n, +} from './ext.js'; + import { fetchJSON } from './fetch.js'; +import { getAdminRulesets } from './admin.js'; import { ubolLog } from './debug.js'; /******************************************************************************/ @@ -460,7 +466,22 @@ async function defaultRulesetsFromLanguage() { async function enableRulesets(ids) { const afterIds = new Set(ids); - const beforeIds = new Set(await dnr.getEnabledRulesets()); + const [ beforeIds, adminIds, rulesetDetails ] = await Promise.all([ + dnr.getEnabledRulesets().then(ids => new Set(ids)), + getAdminRulesets(), + getRulesetDetails(), + ]); + + for ( const token of adminIds ) { + const c0 = token.charAt(0); + const id = token.slice(1); + if ( c0 === '+' ) { + afterIds.add(id); + } else if ( c0 === '-' ) { + afterIds.delete(id); + } + } + const enableRulesetSet = new Set(); const disableRulesetSet = new Set(); for ( const id of afterIds ) { @@ -472,13 +493,8 @@ async function enableRulesets(ids) { disableRulesetSet.add(id); } - if ( enableRulesetSet.size === 0 && disableRulesetSet.size === 0 ) { - return; - } - // Be sure the rulesets to enable/disable do exist in the current version, // otherwise the API throws. - const rulesetDetails = await getRulesetDetails(); for ( const id of enableRulesetSet ) { if ( rulesetDetails.has(id) ) { continue; } enableRulesetSet.delete(id); @@ -487,6 +503,11 @@ async function enableRulesets(ids) { if ( rulesetDetails.has(id) ) { continue; } disableRulesetSet.delete(id); } + + if ( enableRulesetSet.size === 0 && disableRulesetSet.size === 0 ) { + return; + } + const enableRulesetIds = Array.from(enableRulesetSet); const disableRulesetIds = Array.from(disableRulesetSet); @@ -497,7 +518,7 @@ async function enableRulesets(ids) { ubolLog(`Disable ruleset: ${disableRulesetIds}`); } await dnr.updateEnabledRulesets({ enableRulesetIds, disableRulesetIds }); - + return updateDynamicRules(); } diff --git a/platform/mv3/extension/js/settings.js b/platform/mv3/extension/js/settings.js index c916c46577f96..a970430f33118 100644 --- a/platform/mv3/extension/js/settings.js +++ b/platform/mv3/extension/js/settings.js @@ -40,6 +40,18 @@ function hashFromIterable(iter) { return Array.from(iter).sort().join('\n'); } +function isAdminRuleset(listkey) { + const { adminRulesets = [] } = cachedRulesetData; + for ( const id of adminRulesets ) { + const pos = id.indexOf(listkey); + if ( pos === 0 ) { return true; } + if ( pos !== 1 ) { continue; } + const c = id.charAt(0); + if ( c === '+' || c === '-' ) { return true; } + } + return false; +} + /******************************************************************************/ function rulesetStats(rulesetId) { @@ -94,10 +106,13 @@ function renderFilterLists() { li.title = listStatsTemplate .replace('{{ruleCount}}', renderNumber(stats.ruleCount)) .replace('{{filterCount}}', renderNumber(stats.filterCount)); + const fromAdmin = isAdminRuleset(ruleset.id); + dom.cl.toggle(li, 'fromAdmin', fromAdmin); + const disabled = stats.ruleCount === 0 || fromAdmin; dom.attr( - qs$(li, '.input.checkbox'), + qs$(li, '.input.checkbox input'), 'disabled', - stats.ruleCount === 0 ? '' : null + disabled ? '' : null ); dom.cl.remove(li, 'discard'); return li; @@ -358,7 +373,9 @@ async function applyEnabledRulesets() { const checked = qs$(liEntry, 'input[type="checkbox"]:checked') !== null; dom.cl.toggle(liEntry, 'checked', checked); if ( checked === false ) { continue; } - enabledRulesets.push(liEntry.dataset.listkey); + const { listkey } = liEntry.dataset; + if ( isAdminRuleset(listkey) ) { continue; } + enabledRulesets.push(listkey); } await sendMessage({ @@ -433,9 +450,12 @@ localRead('hideUnusedFilterLists').then(value => { /******************************************************************************/ -const bc = new self.BroadcastChannel('uBOL'); +function listen() { + const bc = new self.BroadcastChannel('uBOL'); + bc.onmessage = listen.onmessage; +} -bc.onmessage = ev => { +listen.onmessage = ev => { const message = ev.data; if ( message instanceof Object === false ) { return; } const local = cachedRulesetData; @@ -477,6 +497,13 @@ bc.onmessage = ev => { } } + if ( message.adminRulesets !== undefined ) { + if ( hashFromIterable(message.adminRulesets) !== hashFromIterable(local.adminRulesets) ) { + local.adminRulesets = message.adminRulesets; + render = true; + } + } + if ( message.enabledRulesets !== undefined ) { if ( hashFromIterable(message.enabledRulesets) !== hashFromIterable(local.enabledRulesets) ) { local.enabledRulesets = message.enabledRulesets; @@ -503,6 +530,7 @@ sendMessage({ renderWidgets(); } catch(ex) { } + listen(); }).catch(reason => { console.trace(reason); }); diff --git a/platform/mv3/extension/js/utils.js b/platform/mv3/extension/js/utils.js index cdfc98b25d12d..b32104f946062 100644 --- a/platform/mv3/extension/js/utils.js +++ b/platform/mv3/extension/js/utils.js @@ -115,7 +115,7 @@ const hostnamesFromMatches = origins => { /******************************************************************************/ -export const broadcastMessage = message => { +const broadcastMessage = message => { const bc = new self.BroadcastChannel('uBOL'); bc.postMessage(message); }; @@ -123,6 +123,7 @@ export const broadcastMessage = message => { /******************************************************************************/ export { + broadcastMessage, parsedURLromOrigin, toBroaderHostname, isDescendantHostname, diff --git a/platform/mv3/extension/managed_storage.json b/platform/mv3/extension/managed_storage.json index 8571f59dbaef4..5d645fb03ea92 100644 --- a/platform/mv3/extension/managed_storage.json +++ b/platform/mv3/extension/managed_storage.json @@ -10,6 +10,12 @@ "disableFirstRunPage": { "title": "Disable first run page", "type": "boolean" + }, + "rulesets": { + "title": "Rulesets to add/remove", + "description": "Prefix a ruleset id with '+' to add, or '-' to remove. Use '-*' to disable all non-default lists.", + "type": "array", + "items": { "type": "string" } } } } From 335d947c100e2543b84ab2f1385e8b2ee5458b9a Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Mon, 11 Nov 2024 15:08:10 -0500 Subject: [PATCH 0439/1099] Fix potential infinite async loop Related issue: https://bugzilla.mozilla.org/show_bug.cgi?id=1929326 As identified by @Rob--W: https://bugzilla.mozilla.org/show_bug.cgi?id=1929326#c9 Truncated or otherwise corrupted asset content in extension storage could lead to infinite async loop causing high CPU usage in uBO and its workers. Likely related to the issue of the asset content returned as `undefined`: https://github.com/gorhill/uBlock/blob/652f1787878ba434c3a65287afcd84082c409397/src/js/cachestorage.js#L98 --- src/js/assets.js | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/src/js/assets.js b/src/js/assets.js index e1bc4e616a3ea..4f02700f8c46e 100644 --- a/src/js/assets.js +++ b/src/js/assets.js @@ -19,18 +19,15 @@ Home: https://github.com/gorhill/uBlock */ -'use strict'; - -/******************************************************************************/ +import * as sfp from './static-filtering-parser.js'; -import µb from './background.js'; import { broadcast } from './broadcast.js'; import cacheStorage from './cachestorage.js'; -import { ubolog } from './console.js'; import { i18n$ } from './i18n.js'; import logger from './logger.js'; -import * as sfp from './static-filtering-parser.js'; -import { orphanizeString, } from './text-utils.js'; +import { orphanizeString } from './text-utils.js'; +import { ubolog } from './console.js'; +import µb from './background.js'; /******************************************************************************/ @@ -50,6 +47,9 @@ let remoteServerFriendly = false; /******************************************************************************/ +const hasOwnProperty = (o, p) => + Object.prototype.hasOwnProperty.call(o, p); + const stringIsNotEmpty = s => typeof s === 'string' && s !== ''; const parseExpires = s => { @@ -107,8 +107,8 @@ const resourceTimeFromXhr = xhr => { const resourceTimeFromParts = (parts, time) => { const goodParts = parts.filter(part => typeof part === 'object'); - return goodParts.reduce((acc, part) => - ((part.resourceTime || 0) > acc ? part.resourceTime : acc), + return goodParts.reduce( + (acc, part) => ((part.resourceTime || 0) > acc ? part.resourceTime : acc), time ); }; @@ -246,6 +246,7 @@ const fireNotification = function(topic, details) { assets.fetch = function(url, options = {}) { return new Promise((resolve, reject) => { // Start of executor + /* eslint-disable indent */ const timeoutAfter = µb.hiddenSettings.assetFetchTimeout || 30; const xhr = new XMLHttpRequest(); @@ -322,6 +323,7 @@ assets.fetch = function(url, options = {}) { onErrorEvent.call(xhr); } + /* eslint-enable indent */ // End of executor }); }; @@ -733,7 +735,7 @@ async function assetCacheRead(assetKey, updateReadTime = false) { } if ( bin instanceof Object === false ) { return reportBack(''); } - if ( bin.hasOwnProperty(internalKey) === false ) { return reportBack(''); } + if ( hasOwnProperty(bin, internalKey) === false ) { return reportBack(''); } const entry = assetCacheRegistry[assetKey]; if ( entry === undefined ) { return reportBack(''); } @@ -1277,7 +1279,9 @@ async function diffUpdater() { if ( data.status === 'needtext' ) { ubolog('Diff updater: need text for', data.assetKey); assetCacheRead(data.assetKey).then(result => { - data.text = result.content; + // https://bugzilla.mozilla.org/show_bug.cgi?id=1929326#c9 + // Must never be set to undefined! + data.text = result.content || ''; data.status = undefined; checkAndCorrectDiffPath(data); bc.postMessage(data); From 52d21b8df9e22d05938095701e74b7ff1b66c67f Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Mon, 11 Nov 2024 15:21:16 -0500 Subject: [PATCH 0440/1099] Update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 065ac0936ea71..cb2966fe2e030 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,4 @@ +- [Fix potential infinite async loop](https://github.com/gorhill/uBlock/commit/335d947c10) (issue found by @Rob--W) - [Keep moving related scriptlets into separate files](https://github.com/gorhill/uBlock/commit/e5a088738d) - [Improve `prevent-xhr` scriptlet](https://github.com/gorhill/uBlock/commit/ce4908b341) - [Improve `trusted-suppress-native-method` scriptlet](https://github.com/gorhill/uBlock/commit/41616df866) From 7ccb4c6314f8161a05f3ff997ad75a8cd2220bfb Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Mon, 11 Nov 2024 15:21:41 -0500 Subject: [PATCH 0441/1099] New revision for dev build --- dist/version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dist/version b/dist/version index 1c77daac69716..c7aa7422abb56 100644 --- a/dist/version +++ b/dist/version @@ -1 +1 @@ -1.61.1.1 \ No newline at end of file +1.61.1.2 \ No newline at end of file From d325dcd19268ff430516258bb133bbdfe155a7c7 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Mon, 11 Nov 2024 15:45:32 -0500 Subject: [PATCH 0442/1099] Make Firefox dev build auto-update --- dist/firefox/updates.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dist/firefox/updates.json b/dist/firefox/updates.json index 1d32fd31a1b9e..4206cc148d91d 100644 --- a/dist/firefox/updates.json +++ b/dist/firefox/updates.json @@ -3,9 +3,9 @@ "uBlock0@raymondhill.net": { "updates": [ { - "version": "1.61.1.1", + "version": "1.61.1.2", "browser_specific_settings": { "gecko": { "strict_min_version": "78.0" } }, - "update_link": "https://github.com/gorhill/uBlock/releases/download/1.61.1b1/uBlock0_1.61.1b1.firefox.signed.xpi" + "update_link": "https://github.com/gorhill/uBlock/releases/download/1.61.1b2/uBlock0_1.61.1b2.firefox.signed.xpi" } ] } From 2e745f9bfb2cf7e5e241160b2a20edf5edfcad92 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Wed, 13 Nov 2024 09:10:51 -0500 Subject: [PATCH 0443/1099] [mv3] Remove obsolete Firefox-only workaround in scriptlet template Related bugzilla issue: https://bugzilla.mozilla.org/show_bug.cgi?id=1736575 The issue was fixed months ago. The removed code block is causing uBOL to be flagged as "including remotely hosted code". To be clear, the removed obsolete code block was not related to executing remote code. The referenced code was in the file itself, not remote, and this was a workaround for when Firefox was not supporting injecting script in the `MAIN` world. The issue was fixed months ago in Firefox, so there is no point for the workaround. --- platform/mv3/scriptlets/scriptlet.template.js | 40 +------------------ 1 file changed, 1 insertion(+), 39 deletions(-) diff --git a/platform/mv3/scriptlets/scriptlet.template.js b/platform/mv3/scriptlets/scriptlet.template.js index 9ce165d094d8e..19560c8aedbef 100644 --- a/platform/mv3/scriptlets/scriptlet.template.js +++ b/platform/mv3/scriptlets/scriptlet.template.js @@ -21,7 +21,6 @@ */ /* eslint-disable indent */ -/* global cloneInto */ // ruleset: $rulesetId$ @@ -136,44 +135,7 @@ argsList.length = 0; /******************************************************************************/ -// Inject code - -// https://bugzilla.mozilla.org/show_bug.cgi?id=1736575 -// 'MAIN' world not yet supported in Firefox, so we inject the code into -// 'MAIN' ourself when environment in Firefox. - -const targetWorld = '$world$'; - -// Not Firefox -if ( typeof wrappedJSObject !== 'object' || targetWorld === 'ISOLATED' ) { - return uBOL_$scriptletName$(); -} - -// Firefox -{ - const page = self.wrappedJSObject; - let script, url; - try { - page.uBOL_$scriptletName$ = cloneInto([ - [ '(', uBOL_$scriptletName$.toString(), ')();' ], - { type: 'text/javascript; charset=utf-8' }, - ], self); - const blob = new page.Blob(...page.uBOL_$scriptletName$); - url = page.URL.createObjectURL(blob); - const doc = page.document; - script = doc.createElement('script'); - script.async = false; - script.src = url; - (doc.head || doc.documentElement || doc).append(script); - } catch (ex) { - console.error(ex); - } - if ( url ) { - if ( script ) { script.remove(); } - page.URL.revokeObjectURL(url); - } - delete page.uBOL_$scriptletName$; -} +uBOL_$scriptletName$(); /******************************************************************************/ From ff5fc61753e979323556b35efb0d5d90546faf55 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Thu, 14 Nov 2024 10:24:50 -0500 Subject: [PATCH 0444/1099] Add support for EasyList `{ remove: true }` cosmetic filter syntax Related issue: https://github.com/uBlockOrigin/uBlock-issues/issues/3451 --- src/js/static-filtering-parser.js | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/src/js/static-filtering-parser.js b/src/js/static-filtering-parser.js index 1cc4b5a8db9dd..f49b0bfe7208d 100644 --- a/src/js/static-filtering-parser.js +++ b/src/js/static-filtering-parser.js @@ -3345,10 +3345,13 @@ class ExtSelectorCompiler { // We have an Adguard/ABP cosmetic filter if and only if the // character is `$`, `%` or `?`, otherwise it's not a cosmetic // filter. - // Adguard's style injection: translate to uBO's format. - if ( compileOptions.adgStyleSyntax === true ) { - raw = this.translateAdguardCSSInjectionFilter(raw); - if ( raw === '' ) { return false; } + // Adguard/EasyList style injection: translate to uBO's format. + if ( this.isStyleInjectionFilter(raw) ) { + const translated = this.translateStyleInjectionFilter(raw); + if ( translated === undefined ) { return false; } + raw = translated; + } else if ( compileOptions.adgStyleSyntax === true ) { + return false; } // Normalize AdGuard's attribute-based procedural operators. @@ -3884,9 +3887,14 @@ class ExtSelectorCompiler { return true; } - translateAdguardCSSInjectionFilter(suffix) { - const matches = /^(.*)\s*\{([^}]+)\}\s*$/.exec(suffix); - if ( matches === null ) { return ''; } + isStyleInjectionFilter(selector) { + const len = selector.length; + return len !== 0 && selector.charCodeAt(len-1) === 0x7D /* } */; + } + + translateStyleInjectionFilter(raw) { + const matches = /^(.+)\s*\{([^}]+)\}$/.exec(raw); + if ( matches === null ) { return; } const selector = matches[1].trim(); const style = matches[2].trim(); // Special style directive `remove: true` is converted into a From 4c299bfca91c85bce73b7a17672050594d0b3d29 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Thu, 14 Nov 2024 10:32:15 -0500 Subject: [PATCH 0445/1099] Better handle unexpected conditions when deserializing For example, when deserialzing from corrupted storage. --- src/js/s14e-serializer.js | 36 ++++++++++++++++++++---------------- 1 file changed, 20 insertions(+), 16 deletions(-) diff --git a/src/js/s14e-serializer.js b/src/js/s14e-serializer.js index 98f0d9cc20982..8b1850f136fe9 100644 --- a/src/js/s14e-serializer.js +++ b/src/js/s14e-serializer.js @@ -1097,32 +1097,36 @@ export const serialize = (data, options = {}) => { return ratio <= 0.85 ? t : s; }; -export const deserialize = s => { - if ( s.startsWith(MAGICLZ4PREFIX) ) { - refCounter = 1; - readStr = s; - readEnd = s.length; - readPtr = MAGICLZ4PREFIX.length; - const lz4 = _deserialize(); - readRefs.clear(); - readStr = ''; - const lz4Util = new LZ4BlockJS(); - const uint8ArrayAfter = lz4Util.decode(lz4.data, 0, lz4.size); - s = textCodec.decode(new Uint8Array(uint8ArrayAfter)); - } - if ( s.startsWith(MAGICPREFIX) === false ) { return; } +const deserializeById = (blockid, s) => { refCounter = 1; readStr = s; readEnd = s.length; - readPtr = MAGICPREFIX.length; + readPtr = blockid.length; const data = _deserialize(); readRefs.clear(); readStr = ''; - uint8Input = null; if ( readPtr === FAILMARK ) { return; } return data; }; +export const deserialize = s => { + if ( s.startsWith(MAGICLZ4PREFIX) ) { + const lz4 = deserializeById(MAGICLZ4PREFIX, s); + if ( lz4 ) { + const lz4Util = new LZ4BlockJS(); + const uint8ArrayAfter = lz4Util.decode(lz4.data, 0, lz4.size); + if ( uint8ArrayAfter ) { + s = textCodec.decode(new Uint8Array(uint8ArrayAfter)); + } + } + } + const data = s.startsWith(MAGICPREFIX) + ? deserializeById(MAGICPREFIX, s) + : undefined; + uint8Input = null; + return data; +}; + export const isSerialized = s => typeof s === 'string' && (s.startsWith(MAGICLZ4PREFIX) || s.startsWith(MAGICPREFIX)); From b709a56ce66cfd502f9120ed3843fe5190058280 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Thu, 14 Nov 2024 10:35:01 -0500 Subject: [PATCH 0446/1099] Update changelog --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index cb2966fe2e030..9bd7090450386 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,5 @@ +- [Better handle unexpected conditions when deserializing](https://github.com/gorhill/uBlock/commit/4c299bfca9) +- [Add support for EasyList `{ remove: true }` cosmetic filter syntax](https://github.com/gorhill/uBlock/commit/ff5fc61753) - [Fix potential infinite async loop](https://github.com/gorhill/uBlock/commit/335d947c10) (issue found by @Rob--W) - [Keep moving related scriptlets into separate files](https://github.com/gorhill/uBlock/commit/e5a088738d) - [Improve `prevent-xhr` scriptlet](https://github.com/gorhill/uBlock/commit/ce4908b341) From 3066386d0e3bc15299fa114db8364ec83405de5f Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Thu, 14 Nov 2024 10:35:21 -0500 Subject: [PATCH 0447/1099] New revision for dev build --- dist/version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dist/version b/dist/version index c7aa7422abb56..c530652b600c5 100644 --- a/dist/version +++ b/dist/version @@ -1 +1 @@ -1.61.1.2 \ No newline at end of file +1.61.1.3 \ No newline at end of file From 2e66d7bd557e7638c9e5ade165ba8086d6cf2ffb Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Thu, 14 Nov 2024 10:40:55 -0500 Subject: [PATCH 0448/1099] Import translation work from https://crowdin.com/project/ublock --- .../mv3/extension/_locales/si/messages.json | 86 ++++---- .../extension/_locales/zh_TW/messages.json | 20 +- src/_locales/ar/messages.json | 2 +- src/_locales/br_FR/messages.json | 2 +- src/_locales/si/messages.json | 200 +++++++++--------- src/_locales/vi/messages.json | 28 +-- 6 files changed, 169 insertions(+), 169 deletions(-) diff --git a/platform/mv3/extension/_locales/si/messages.json b/platform/mv3/extension/_locales/si/messages.json index 2fa20d00c50e6..559224a0ca335 100644 --- a/platform/mv3/extension/_locales/si/messages.json +++ b/platform/mv3/extension/_locales/si/messages.json @@ -4,15 +4,15 @@ "description": "extension name." }, "extShortDesc": { - "message": "A permission-less content blocker. Blocks ads, trackers, miners, and more immediately upon installation.", + "message": "අවසර අනවශ්‍ය අන්තර්ගත අවහිරකය. ස්ථාපනය කළ වහාම දැන්වීම්, ලුහුබැඳීම්, කැණීම් සහ තවත් දෑ අවහිර කරයි.", "description": "this will be in the Chrome web store: must be 132 characters or less" }, "perRulesetStats": { - "message": "{{ruleCount}} rules, converted from {{filterCount}} network filters", + "message": "නීති {{ruleCount}} ක් ජාල පෙරහන් {{filterCount}} කින් හරවා ඇත", "description": "Appears aside each filter list in the _3rd-party filters_ pane" }, "dashboardName": { - "message": "uBO Lite — Dashboard", + "message": "uBO Lite - උපකරණ පුවරුව", "description": "English: uBO Lite — Dashboard" }, "settingsPageName": { @@ -20,91 +20,91 @@ "description": "appears as tab name in dashboard" }, "aboutPageName": { - "message": "පිළිබඳව", + "message": "පිළිබඳ", "description": "appears as tab name in dashboard" }, "aboutPrivacyPolicy": { - "message": "Privacy policy", + "message": "රහස්‍යතා ප්‍රතිපත්තිය", "description": "Link to privacy policy on GitHub (English)" }, "popupFilteringModeLabel": { - "message": "filtering mode", + "message": "පෙරීමේ ප්‍රකාරය", "description": "Label in the popup panel for the current filtering mode" }, "popupTipReport": { - "message": "Report an issue on this website", + "message": "මෙම අඩවියේ ගැටලුවක් වාර්තා කරන්න", "description": "Tooltip used for the 'chat' icon in the panel" }, "popupTipDashboard": { - "message": "Open the dashboard", + "message": "උපකරණ පුවරුව අරින්න", "description": "English: Click to open the dashboard" }, "popupMoreButton": { - "message": "More", + "message": "තව", "description": "Label to be used to show popup panel sections" }, "popupLessButton": { - "message": "Less", + "message": "අඩුවෙන්", "description": "Label to be used to hide popup panel sections" }, "3pGroupDefault": { - "message": "Default", + "message": "පෙරනිමි", "description": "Header for a ruleset section in 'Filter lists pane'" }, "3pGroupAds": { - "message": "Ads", + "message": "දැන්වීම්", "description": "Header for a ruleset section in 'Filter lists pane'" }, "3pGroupPrivacy": { - "message": "Privacy", + "message": "පෞද්ගලිකත්‍වය", "description": "Header for a ruleset section in 'Filter lists pane'" }, "3pGroupMalware": { - "message": "Malware domains", + "message": "ද්වේශාංග වසම්", "description": "Header for a ruleset section in 'Filter lists pane'" }, "3pGroupAnnoyances": { - "message": "Annoyances", + "message": "පීඩාකාරී", "description": "Header for a ruleset section in 'Filter lists pane'" }, "3pGroupMisc": { - "message": "Miscellaneous", + "message": "වෙනත්", "description": "Header for a ruleset section in 'Filter lists pane'" }, "3pGroupRegions": { - "message": "Regions, languages", + "message": "කලාපීය, භාෂා", "description": "Header for a ruleset section in 'Filter lists pane'" }, "aboutChangelog": { - "message": "Changelog", + "message": "වෙනස්කම් සටහන", "description": "" }, "aboutCode": { - "message": "Source code (GPLv3)", + "message": "මූලාශ්‍ර කේත (GPLv3)", "description": "English: Source code (GPLv3)" }, "aboutContributors": { - "message": "Contributors", + "message": "දායකයින්", "description": "English: Contributors" }, "aboutSourceCode": { - "message": "Source code", + "message": "මූලාශ්‍ර කේත", "description": "Link text to source code repo" }, "aboutTranslations": { - "message": "Translations", + "message": "පරිවර්තන", "description": "Link text to translations repo" }, "aboutFilterLists": { - "message": "Filter lists", + "message": "පෙරහන් ලැයිස්තු", "description": "Link text to uBO's own filter lists repo" }, "aboutDependencies": { - "message": "External dependencies (GPLv3-compatible):", + "message": "බාහිර පරායත්ත (GPLv3-අනුකූල):", "description": "Shown in the About pane" }, "supportS6H": { - "message": "Report a filter issue", + "message": "පෙරහන් ගැටලු වාර්තා කරන්න", "description": "Header of 'Report a filter issue' section in Support pane" }, "supportS3P1": { @@ -116,19 +116,19 @@ "description": "A paragraph in the filter issue reporter section" }, "supportFindSpecificButton": { - "message": "Find similar reports", + "message": "සමාන වාර්තා සොයන්න", "description": "A clickable link in the filter issue reporter section" }, "supportS6URL": { - "message": "Address of the webpage:", + "message": "අඩවියේ ලිපිනය:", "description": "Label for the URL of the page" }, "supportS6Select1": { - "message": "The webpage…", + "message": "වියමන පිටුව…", "description": "Label for widget to select type of issue" }, "supportS6Select1Option0": { - "message": "-- Pick an entry --", + "message": "-- නිවේශිතයක් තෝරන්න --", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option1": { @@ -140,7 +140,7 @@ "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option3": { - "message": "Detects uBO Lite", + "message": "uBO Lite අනාවරණය කරයි", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option4": { @@ -152,7 +152,7 @@ "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option6": { - "message": "Opens unwanted tabs or windows", + "message": "අනවශ්‍ය පටිති හෝ කවුළු අරියි", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option7": { @@ -164,11 +164,11 @@ "description": "A checkbox to use for NSFW sites" }, "supportReportSpecificButton": { - "message": "Create new report", + "message": "නව වාර්තාවක් සාදන්න", "description": "Text for button which open an external webpage in Support pane" }, "firstRunSectionLabel": { - "message": "Welcome", + "message": "සාදරයෙන් පිළිගනිමු", "description": "The header text for the welcome message section" }, "firstRunDescription": { @@ -176,7 +176,7 @@ "description": "Descriptive text shown at first install time only " }, "defaultFilteringModeSectionLabel": { - "message": "Default filtering mode", + "message": "පෙරනිමි පෙරීමේ ප්‍රකාරය", "description": "The header text for the default filtering mode section" }, "defaultFilteringModeDescription": { @@ -184,19 +184,19 @@ "description": "This describes the default filtering mode setting" }, "filteringMode0Name": { - "message": "no filtering", + "message": "පෙරීමක් නැත", "description": "Name of blocking mode 0" }, "filteringMode1Name": { - "message": "basic", + "message": "මූලික", "description": "Name of blocking mode 1" }, "filteringMode2Name": { - "message": "optimal", + "message": "ප්‍රශස්ත", "description": "Name of blocking mode 2" }, "filteringMode3Name": { - "message": "complete", + "message": "සම්පූර්ණ", "description": "Name of blocking mode 3" }, "basicFilteringModeDescription": { @@ -212,23 +212,23 @@ "description": "This describes the 'complete' filtering mode" }, "noFilteringModeDescription": { - "message": "List of websites for which no filtering will take place.", + "message": "පෙරීමක් නොවන අඩවි ලැයිස්තුව.", "description": "A short description for the editable field which lists trusted sites" }, "noFilteringModePlaceholder": { - "message": "[hostnames only]\nexample.com\ngames.example\n...", + "message": "[සත්කාරක පමණි]\nexample.com\ngames.example\n...", "description": "Default text for in edit field" }, "behaviorSectionLabel": { - "message": "Behavior", + "message": "හැසිරීම", "description": "The header text for the 'Behavior' section" }, "autoReloadLabel": { - "message": "Automatically reload page when changing filtering mode", + "message": "පෙරීමේ ප්‍රකාරය වෙනස් වූ විට පිටුව ස්වයංක්‍රීයව යළි පූරණය කරන්න", "description": "Label for a checkbox in the options page" }, "showBlockedCountLabel": { - "message": "Show the number of blocked requests on the toolbar icon", + "message": "මෙවලම් තීරු නිරූපකයේ අවහිර කළ ඉල්ලීම් ගණන පෙන්වන්න", "description": "Label for a checkbox in the options page" } } diff --git a/platform/mv3/extension/_locales/zh_TW/messages.json b/platform/mv3/extension/_locales/zh_TW/messages.json index d3eec22438c34..d497e91c6be01 100644 --- a/platform/mv3/extension/_locales/zh_TW/messages.json +++ b/platform/mv3/extension/_locales/zh_TW/messages.json @@ -4,7 +4,7 @@ "description": "extension name." }, "extShortDesc": { - "message": "一個無須任何權限的內容阻擋器。安裝即可阻擋廣告、追蹤器、挖礦程式等網頁內容。", + "message": "無須權限的內容阻擋器。安裝即可阻擋廣告、追蹤器、挖礦程式等網頁內容。", "description": "this will be in the Chrome web store: must be 132 characters or less" }, "perRulesetStats": { @@ -32,7 +32,7 @@ "description": "Label in the popup panel for the current filtering mode" }, "popupTipReport": { - "message": "回報此網站的問題", + "message": "回報網站問題", "description": "Tooltip used for the 'chat' icon in the panel" }, "popupTipDashboard": { @@ -100,19 +100,19 @@ "description": "Link text to uBO's own filter lists repo" }, "aboutDependencies": { - "message": "外部相依套件(與 GPLv3 相容):", + "message": "外部依存套件(相容 GPLv3):", "description": "Shown in the About pane" }, "supportS6H": { - "message": "回報過濾規則的問題", + "message": "回報過濾規則問題", "description": "Header of 'Report a filter issue' section in Support pane" }, "supportS3P1": { - "message": "回報至特定網站的過濾器問題至 uBlockOrigin/uAssets 議題追蹤器需要 GitHub 帳號。", + "message": "回報特定網站的過濾器問題至 uBlockOrigin/uAssets 議題追蹤器需要 GitHub 帳號。", "description": "First paragraph of 'Filter issues' section in Support pane" }, "supportS6P1S1": { - "message": "為免給志願者帶來額外負擔,請先檢查問題有沒有被回報過,避免重複回報。", + "message": "為免給志願者帶來額外負擔,請先檢查問題是否被回報過,以免重複。", "description": "A paragraph in the filter issue reporter section" }, "supportFindSpecificButton": { @@ -136,15 +136,15 @@ "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option2": { - "message": "含有覆蓋物或其他滋擾物", + "message": "有覆蓋物或其他滋擾物", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option3": { - "message": "偵測 uBO Lite", + "message": "偵測到 uBO Lite", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option4": { - "message": "有隱私權相關問題", + "message": "有隱私權問題", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option5": { @@ -172,7 +172,7 @@ "description": "The header text for the welcome message section" }, "firstRunDescription": { - "message": "您剛安裝了 uBO Lite。您可以在此處選擇要在所有網站上使用的預設過濾模式。\n\n預設情況下將會選取基礎模式,因為其不需要讀取與變更資料的權限。若您信任 uBO Lite,您可以給予其讀取並變更在所有網站上資料的廣泛權限,以便為所有網站啟用更進階的過濾功能。", + "message": "您剛安裝了 uBO Lite。您可以在此處選擇會套用在所有網站上的預設過濾模式。\n\n預設情況下,將會使用基礎模式,因為其不需要讀取與變更資料的權限。若您信任 uBO Lite,您可以給予其讀取並變更在所有網站上資料的廣泛權限,以便為所有網站啟用更進階的過濾功能。", "description": "Descriptive text shown at first install time only " }, "defaultFilteringModeSectionLabel": { diff --git a/src/_locales/ar/messages.json b/src/_locales/ar/messages.json index ac025f2060466..3d1023f561df5 100644 --- a/src/_locales/ar/messages.json +++ b/src/_locales/ar/messages.json @@ -1192,7 +1192,7 @@ "description": "Button text to navigate to the blocked page" }, "docblockedRedirectPrompt": { - "message": "The blocked page wants to redirect to another site. If you choose to proceed, you will navigate directly to: {{url}}", + "message": "الصفحة المحظورة تريد إعادة توجيهك إلى موقع آخر. إذا اخترت المتابعة، فسوف تنتقل مباشرة إلى: {{url}}", "description": "Text warning about an incoming redirect" }, "cloudPush": { diff --git a/src/_locales/br_FR/messages.json b/src/_locales/br_FR/messages.json index d0a4f82abff8a..abddf0af58f94 100644 --- a/src/_locales/br_FR/messages.json +++ b/src/_locales/br_FR/messages.json @@ -12,7 +12,7 @@ "description": "English: uBlock₀ — Dashboard" }, "dashboardUnsavedWarning": { - "message": "Diwallit! Kemmoù zo ha n'ho peus enrollet anezho", + "message": "Diwallit! Kemmoù zo ha n'ho peus ket enrollet", "description": "A warning in the dashboard when navigating away from unsaved changes" }, "dashboardUnsavedWarningStay": { diff --git a/src/_locales/si/messages.json b/src/_locales/si/messages.json index cd39d0ddcaa1a..eeafed6f67220 100644 --- a/src/_locales/si/messages.json +++ b/src/_locales/si/messages.json @@ -12,11 +12,11 @@ "description": "English: uBlock₀ — Dashboard" }, "dashboardUnsavedWarning": { - "message": "Warning! You have unsaved changes", + "message": "අවවාදයයි! ඔබ නොසුරැකි වෙනස්කම් ඇත", "description": "A warning in the dashboard when navigating away from unsaved changes" }, "dashboardUnsavedWarningStay": { - "message": "නවතින්න", + "message": "මෙතැන ඉන්න", "description": "Label for button to prevent navigating away from unsaved changes" }, "dashboardUnsavedWarningIgnore": { @@ -28,7 +28,7 @@ "description": "appears as tab name in dashboard" }, "3pPageName": { - "message": "Filter lists", + "message": "පෙරහන් ලැයිස්තු", "description": "appears as tab name in dashboard" }, "1pPageName": { @@ -52,11 +52,11 @@ "description": "Title for the logger window" }, "aboutPageName": { - "message": "පිළිබඳව", + "message": "පිළිබඳ", "description": "appears as tab name in dashboard" }, "supportPageName": { - "message": "Support", + "message": "සහාය", "description": "appears as tab name in dashboard" }, "assetViewerPageName": { @@ -64,7 +64,7 @@ "description": "Title for the asset viewer page" }, "advancedSettingsPageName": { - "message": "වැඩිදුර සැකසුම්", + "message": "සංකීර්ණ සැකසුම්", "description": "Title for the advanced settings page" }, "popupPowerSwitchInfo": { @@ -92,7 +92,7 @@ "description": "Example: 15 (13%)" }, "popupBlockedSinceInstallPrompt": { - "message": "since install", + "message": "ස්ථාපනයේ සිට", "description": "English: since install" }, "popupOr": { @@ -100,11 +100,11 @@ "description": "English: or" }, "popupBlockedOnThisPage_v2": { - "message": "මෙම පිටුවේ අවහිර කළ", + "message": "මෙම පිටුවේ අවහිර", "description": "For the new mobile-friendly popup design" }, "popupBlockedSinceInstall_v2": { - "message": "ස්ථාපනයෙන් පසු අවහිර කළ", + "message": "ස්ථාපනයෙන් පසු අවහිර", "description": "For the new mobile-friendly popup design" }, "popupDomainsConnected_v2": { @@ -112,7 +112,7 @@ "description": "For the new mobile-friendly popup design" }, "popupTipDashboard": { - "message": "උපකරණපුවරුව විවෘත කරන්න", + "message": "උපකරණ පුවරුව අරින්න", "description": "English: Click to open the dashboard" }, "popupTipZapper": { @@ -128,7 +128,7 @@ "description": "Tooltip used for the logger icon in the panel" }, "popupTipReport": { - "message": "Report an issue on this website", + "message": "මෙම අඩවියේ ගැටලුවක් වාර්තා කරන්න", "description": "Tooltip used for the 'chat' icon in the panel" }, "popupTipNoPopups": { @@ -180,19 +180,19 @@ "description": "Tooltip for the no-remote-fonts per-site switch" }, "popupTipNoScripting1": { - "message": "Click to disable JavaScript on this site", + "message": "මෙම අඩවියේ ජාවාස්ක්‍රිප්ට් අබල කිරීමට ඔබන්න", "description": "Tooltip for the no-scripting per-site switch" }, "popupTipNoScripting2": { - "message": "Click to no longer disable JavaScript on this site", + "message": "මෙම අඩවියේ තවදුරටත් ජාවාස්ක්‍රිප්ට් අබල නොකිරීමට ඔබන්න", "description": "Tooltip for the no-scripting per-site switch" }, "popupNoPopups_v2": { - "message": "Pop-up windows", + "message": "උත්පතන කවුළු", "description": "Caption for the no-popups per-site switch" }, "popupNoLargeMedia_v2": { - "message": "Large media elements", + "message": "විශාල මාධ්‍ය අංග", "description": "Caption for the no-large-media per-site switch" }, "popupNoCosmeticFiltering_v2": { @@ -200,11 +200,11 @@ "description": "Caption for the no-cosmetic-filtering per-site switch" }, "popupNoRemoteFonts_v2": { - "message": "දුරස්ථ මුද්‍රණඅකුරු", + "message": "දුරස්ථ රුවකුරු", "description": "Caption for the no-remote-fonts per-site switch" }, "popupNoScripting_v2": { - "message": "JavaScript", + "message": "ජාවාස්ක්‍රිප්ට්", "description": "Caption for the no-scripting per-site switch" }, "popupMoreButton_v2": { @@ -212,7 +212,7 @@ "description": "Label to be used to show popup panel sections" }, "popupLessButton_v2": { - "message": "Less", + "message": "අඩුවෙන්", "description": "Label to be used to hide popup panel sections" }, "popupTipGlobalRules": { @@ -236,31 +236,31 @@ "description": "" }, "popupImageRulePrompt": { - "message": "images", + "message": "රූප", "description": "" }, "popup3pAnyRulePrompt": { - "message": "තෙවන-පාර්ශවීය", + "message": "තෙවන පාර්ශ්ව", "description": "" }, "popup3pPassiveRulePrompt": { - "message": "3rd-party CSS/images", + "message": "තෙවන පාර්ශ්ව CSS/රූප", "description": "" }, "popupInlineScriptRulePrompt": { - "message": "inline scripts", + "message": "එක්තල අත්පත්", "description": "" }, "popup1pScriptRulePrompt": { - "message": "1st-party scripts", + "message": "පළමු පාර්ශ්ව අත්පත්", "description": "" }, "popup3pScriptRulePrompt": { - "message": "3rd-party scripts", + "message": "තෙවන පාර්ශ්ව අත්පත්", "description": "" }, "popup3pFrameRulePrompt": { - "message": "3rd-party frames", + "message": "තෙවන පාර්ශ්ව රාමු", "description": "" }, "popupHitDomainCountPrompt": { @@ -276,11 +276,11 @@ "description": "Example of use: Version 1.26.4" }, "popup3pScriptFilter": { - "message": "script", + "message": "අත්පත", "description": "Appears as an option to filter out firewall rows" }, "popup3pFrameFilter": { - "message": "frame", + "message": "රාමුව", "description": "Appears as an option to filter out firewall rows" }, "pickerCreate": { @@ -308,11 +308,11 @@ "description": "English: Cosmetic filters" }, "pickerCosmeticFiltersHint": { - "message": "Click, Ctrl-click", + "message": "ඔබන්න, Ctrl-ඔබන්න", "description": "English: Click, Ctrl-click" }, "pickerContextMenuEntry": { - "message": "Block element…", + "message": "අංග අවහිර කරන්න", "description": "An entry in the browser's contextual menu" }, "settingsCollapseBlockedPrompt": { @@ -320,7 +320,7 @@ "description": "English: Hide placeholders of blocked elements" }, "settingsIconBadgePrompt": { - "message": "Show the number of blocked requests on the icon", + "message": "නිරූපකයේ අවහිර කළ ඉල්ලීම් ගණන පෙන්වන්න", "description": "English: Show the number of blocked requests on the icon" }, "settingsTooltipsPrompt": { @@ -328,7 +328,7 @@ "description": "A checkbox in the Settings pane" }, "settingsContextMenuPrompt": { - "message": "Make use of context menu where appropriate", + "message": "සුදුසු අවස්ථා වල දී සන්දර්භ වට්ටෝරුව භාවිතා කරන්න", "description": "English: Make use of context menu where appropriate" }, "settingsColorBlindPrompt": { @@ -336,11 +336,11 @@ "description": "English: Color-blind friendly" }, "settingsAppearance": { - "message": "Appearance", + "message": "පෙනුම", "description": "Section for controlling user interface appearance" }, "settingsThemeLabel": { - "message": "Theme", + "message": "තේමාව", "description": "Label for checkbox to enable a custom dark theme" }, "settingsThemeAccent0Label": { @@ -352,7 +352,7 @@ "description": "" }, "settingsAdvancedUserPrompt": { - "message": "I am an advanced user", + "message": "මම ප්‍රගත පරිශ්‍රීලකයෙකි", "description": "Checkbox to let user access advanced, technical features" }, "settingsPrefetchingDisabledPrompt": { @@ -360,7 +360,7 @@ "description": "English: " }, "settingsHyperlinkAuditingDisabledPrompt": { - "message": "Disable hyperlink auditing", + "message": "අතිසබැඳි විගණනය අබල කරන්න", "description": "English: " }, "settingsWebRTCIPAddressHiddenPrompt": { @@ -380,19 +380,19 @@ "description": "" }, "settingsNoLargeMediaPrompt": { - "message": "Block media elements larger than {{input}} KB", + "message": "කි.බ. {{input}} කට වඩා විශාල මාධ්‍ය අංග අවහිර කරන්න", "description": "" }, "settingsNoRemoteFontsPrompt": { - "message": "දුරස්ථ මුද්‍රණඅකුරු බ්ලොක් කරන්න", + "message": "දුරස්ථ රුවකුරු අවහිර කරන්න", "description": "" }, "settingsNoScriptingPrompt": { - "message": "Disable JavaScript", + "message": "ජාවාස්ක්‍රිප්ට් අබල කරන්න", "description": "The default state for the per-site no-scripting switch" }, "settingsNoCSPReportsPrompt": { - "message": "අ.ආ.ප්‍ර.(සීඑස්පී) වාර්තා අවහිරකරන්න", + "message": "අ.ආ.ප්‍ර. (CSP) වාර්තා අවහිර කරන්න", "description": "background information: https://github.com/gorhill/uBlock/issues/3150" }, "settingsUncloakCnamePrompt": { @@ -400,23 +400,23 @@ "description": "background information: https://github.com/uBlockOrigin/uBlock-issues/issues/1513" }, "settingsAdvanced": { - "message": "Advanced", + "message": "සංකීර්ණ", "description": "Section for controlling advanced-user settings" }, "settingsAdvancedSynopsis": { - "message": "Features suitable only for technical users", + "message": "තාක්‍ෂණික පරිශ්‍රීලකයින්ට පමණක් සුදුසු විශේෂාංග", "description": "Description of section controlling advanced-user settings" }, "settingsAdvancedUserSettings": { - "message": "වැඩිදුර සැකසුම්", + "message": "සංකීර්ණ සැකසුම්", "description": "For the tooltip of a link which gives access to advanced settings" }, "settingsLastRestorePrompt": { - "message": "Last restore:", + "message": "අවසාන ප්‍රත්‍යර්පණය:", "description": "English: Last restore:" }, "settingsLastBackupPrompt": { - "message": "Last backup:", + "message": "අවසාන උපස්ථය:", "description": "English: Last backup:" }, "3pListsOfBlockedHostsPrompt": { @@ -432,7 +432,7 @@ "description": "A checkbox in the _3rd-party filters_ pane" }, "3pUpdateNow": { - "message": "දැන් යාවත්කාල කරන්න", + "message": "යාවත්කාල කරන්න", "description": "A button in the in the _3rd-party filters_ pane" }, "3pPurgeAll": { @@ -460,7 +460,7 @@ "description": "A checkbox in the 'Filter lists' pane" }, "3pListsOfBlockedHostsHeader": { - "message": "Lists of blocked hosts", + "message": "අවහිර කළ සත්කාරක", "description": "English: Lists of blocked hosts" }, "3pApplyChanges": { @@ -468,7 +468,7 @@ "description": "English: Apply changes" }, "3pGroupDefault": { - "message": "Built-in", + "message": "තිළෑලි", "description": "Filter lists section name" }, "3pGroupAds": { @@ -476,7 +476,7 @@ "description": "Filter lists section name" }, "3pGroupPrivacy": { - "message": "පෞද්ගලිකත්වය", + "message": "පෞද්ගලිකත්‍වය", "description": "Filter lists section name" }, "3pGroupMalware": { @@ -488,7 +488,7 @@ "description": "Filter lists section name" }, "3pGroupCookies": { - "message": "Cookie notices", + "message": "දත්තකඩ දැන්වීම්", "description": "Filter lists section name" }, "3pGroupAnnoyances": { @@ -508,7 +508,7 @@ "description": "Filter lists section name" }, "3pImport": { - "message": "ඇතුල් කරන්න...", + "message": "ආයාත කරන්න...", "description": "The label for the checkbox used to import external filter lists" }, "3pExternalListsHint": { @@ -520,7 +520,7 @@ "description": "used as a tooltip for the out-of-date icon beside a list" }, "3pViewContent": { - "message": "view content", + "message": "අන්තර්ගතය බලන්න", "description": "used as a tooltip for eye icon beside a list" }, "3pLastUpdate": { @@ -552,7 +552,7 @@ "description": "Button in the 'My filters' pane" }, "1pExport": { - "message": "නිර්යාත", + "message": "නිර්යාත කරන්න...", "description": "Button in the 'My filters' pane" }, "1pExportFilename": { @@ -572,7 +572,7 @@ "description": "header" }, "rulesRevert": { - "message": "Revert", + "message": "ප්‍රතිවර්තනය", "description": "This will remove all temporary rules" }, "rulesCommit": { @@ -588,11 +588,11 @@ "description": "Will save manually-edited content and exit manual-edit mode" }, "rulesEditDiscard": { - "message": "Discard", + "message": "ඉවතලන්න", "description": "Will discard manually-edited content and exit manual-edit mode" }, "rulesImport": { - "message": "ගොනුවකින් ඇතුල් කරන්න...", + "message": "ගොනුවකින් ආයාත කරන්න...", "description": "" }, "rulesExport": { @@ -600,7 +600,7 @@ "description": "Button in the 'My rules' pane" }, "rulesDefaultFileName": { - "message": "මගේ-ස්ථිර-නීති_{{datetime}}.txt", + "message": "මාගේ-ublock-ගතික-නීති_{{datetime}}.txt", "description": "default file name to use" }, "rulesHint": { @@ -612,7 +612,7 @@ "description": "English: dynamic rule syntax and full documentation." }, "rulesSort": { - "message": "Sort:", + "message": "වර්ගනය:", "description": "English: label for sort option." }, "rulesSortByType": { @@ -620,7 +620,7 @@ "description": "English: a sort option for list of rules." }, "rulesSortBySource": { - "message": "Source", + "message": "මූලාශ්‍රය", "description": "English: a sort option for list of rules." }, "rulesSortByDestination": { @@ -636,11 +636,11 @@ "description": "Button in the 'Trusted sites' pane" }, "whitelistExport": { - "message": "නිර්යාත", + "message": "නිර්යාත…", "description": "Button in the 'Trusted sites' pane" }, "whitelistExportFilename": { - "message": "මට-විශ්වාස-වෙබ්-අඩවි_{{datetime}}.txt", + "message": "මාගේ-ublock-විශ්වාසදායී-අඩවි_{{datetime}}.txt", "description": "The default filename to use for import/export purpose" }, "whitelistApply": { @@ -724,23 +724,23 @@ "description": "A keyword in the built-in row filtering expression: all items corresponding to uBO doing something (blocked, allowed, redirected, etc.)" }, "loggerRowFiltererBuiltinBlocked": { - "message": "blocked", + "message": "අවහිරයි", "description": "A keyword in the built-in row filtering expression" }, "loggerRowFiltererBuiltinAllowed": { - "message": "allowed", + "message": "ඉඩදුන්", "description": "A keyword in the built-in row filtering expression" }, "loggerRowFiltererBuiltinModified": { - "message": "modified", + "message": "සංශෝධිත", "description": "A keyword in the built-in row filtering expression" }, "loggerRowFiltererBuiltin1p": { - "message": "ප්‍රථම-පාර්ශවීය", + "message": "පළමු පාර්ශ්ව", "description": "A keyword in the built-in row filtering expression" }, "loggerRowFiltererBuiltin3p": { - "message": "තෙවන-පාර්ශවීය", + "message": "තෙවන පාර්ශ්ව", "description": "A keyword in the built-in row filtering expression" }, "loggerEntryDetailsHeader": { @@ -752,7 +752,7 @@ "description": "Label to identify a filter field" }, "loggerEntryDetailsFilterList": { - "message": "Filter list", + "message": "පෙරහන් ලැයිස්තුව", "description": "Label to identify a filter list field" }, "loggerEntryDetailsRule": { @@ -760,11 +760,11 @@ "description": "Label to identify a rule field" }, "loggerEntryDetailsContext": { - "message": "Context", + "message": "සන්දර්භය", "description": "Label to identify a context field (typically a hostname)" }, "loggerEntryDetailsRootContext": { - "message": "Root context", + "message": "මූල සන්දර්භය", "description": "Label to identify a root context field (typically a hostname)" }, "loggerEntryDetailsPartyness": { @@ -784,7 +784,7 @@ "description": "Small header to identify the dynamic URL filtering section" }, "loggerURLFilteringContextLabel": { - "message": "Context:", + "message": "සන්දර්භය:", "description": "Label for the context selector" }, "loggerURLFilteringTypeLabel": { @@ -792,7 +792,7 @@ "description": "Label for the type selector" }, "loggerStaticFilteringHeader": { - "message": "Static filter", + "message": "ස්ථිතික පෙරහන", "description": "Small header to identify the static filtering section" }, "loggerStaticFilteringSentence": { @@ -804,7 +804,7 @@ "description": "Used in the static filtering wizard" }, "loggerStaticFilteringSentencePartAllow": { - "message": "ඉඩදෙන්න", + "message": "ඉඩ දෙන්න", "description": "Used in the static filtering wizard" }, "loggerStaticFilteringSentencePartType": { @@ -816,7 +816,7 @@ "description": "Used in the static filtering wizard" }, "loggerStaticFilteringSentencePartOrigin": { - "message": "from “{{origin}}”", + "message": "“{{origin}}” වෙතින්", "description": "Used in the static filtering wizard" }, "loggerStaticFilteringSentencePartAnyOrigin": { @@ -896,19 +896,19 @@ "description": "Label for radio-button to pick export text format" }, "supportOpenButton": { - "message": "Open", + "message": "අරින්න", "description": "Text for button which open an external webpage in Support pane" }, "supportReportSpecificButton": { - "message": "Create new report", + "message": "නව වාර්තාවක් සාදන්න", "description": "Text for button which open an external webpage in Support pane" }, "supportFindSpecificButton": { - "message": "Find similar reports", + "message": "සමාන වාර්තා සොයන්න", "description": "A clickable link in the filter issue reporter section" }, "supportS1H": { - "message": "Documentation", + "message": "ප්‍රලේඛනය", "description": "Header of 'Documentation' section in Support pane" }, "supportS1P1": { @@ -916,7 +916,7 @@ "description": "First paragraph of 'Documentation' section in Support pane" }, "supportS2H": { - "message": "Questions and support", + "message": "ප්‍රශ්න සහ සහාය", "description": "Header of 'Questions and support' section in Support pane" }, "supportS2P1": { @@ -940,7 +940,7 @@ "description": "Third paragraph of 'Filter issues' section in Support pane" }, "supportS4H": { - "message": "Bug report", + "message": "දෝෂ වාර්තාව", "description": "Header of 'Bug report' section in Support pane" }, "supportS4P1": { @@ -980,7 +980,7 @@ "description": "Label for the URL of the page" }, "supportS6Select1": { - "message": "The web page…", + "message": "වියමන පිටුව…", "description": "Label for widget to select type of issue" }, "supportS6Select1Option0": { @@ -1032,11 +1032,11 @@ "description": "Link to privacy policy on GitHub (English)" }, "aboutChangelog": { - "message": "Changelog", + "message": "වෙනස්කම් සටහන", "description": "" }, "aboutCode": { - "message": "Source code (GPLv3)", + "message": "මූලාශ්‍ර කේත (GPLv3)", "description": "English: Source code (GPLv3)" }, "aboutContributors": { @@ -1044,7 +1044,7 @@ "description": "English: Contributors" }, "aboutSourceCode": { - "message": "Source code", + "message": "මූලාශ්‍ර කේත", "description": "Link text to source code repo" }, "aboutTranslations": { @@ -1052,7 +1052,7 @@ "description": "Link text to translations repo" }, "aboutFilterLists": { - "message": "Filter lists", + "message": "පෙරහන් ලැයිස්තු", "description": "Link text to uBO's own filter lists repo" }, "aboutDependencies": { @@ -1076,11 +1076,11 @@ "description": "English: my-ublock-backup_{{datetime}}.txt" }, "aboutRestoreDataButton": { - "message": "ගොනුවකින් ප්‍රත්‍යර්පණය කරන්න...", + "message": "ගොනුවකින් ප්‍රත්‍යර්පණය කරන්න", "description": "English: Restore from file..." }, "aboutResetDataButton": { - "message": "Reset to default settings…", + "message": "සැකසුම් පෙරනිමියට සකසන්න", "description": "English: Reset to default settings..." }, "aboutRestoreDataConfirm": { @@ -1124,7 +1124,7 @@ "description": "English: {{value}} hours ago" }, "elapsedOneDayAgo": { - "message": "දිනකට පෙර", + "message": "දවසකට පෙර", "description": "English: a day ago" }, "elapsedManyDaysAgo": { @@ -1140,11 +1140,11 @@ "description": "Firefox/Fennec-specific: Show Logger" }, "fennecMenuItemBlockingOff": { - "message": "off", + "message": "අක්‍රියයි", "description": "Firefox-specific: appears as 'uBlock₀ (off)'" }, "docblockedTitle": { - "message": "Page blocked", + "message": "පිටුව අවහිරයි", "description": "Used as a title for the document-blocked page" }, "docblockedPrompt1": { @@ -1160,7 +1160,7 @@ "description": "label to be used for the parameter-less URL: https://cloud.githubusercontent.com/assets/585534/9832014/bfb1b8f0-593b-11e5-8a27-fba472a5529a.png" }, "docblockedFoundIn": { - "message": "Found in:", + "message": "මෙහි හමු විය:", "description": "English: List of filter list names follows" }, "docblockedBack": { @@ -1184,7 +1184,7 @@ "description": "English: Temporarily" }, "docblockedDisablePermanent": { - "message": "සදාකාලිකව", + "message": "සදහටම", "description": "English: Permanently" }, "docblockedDisable": { @@ -1204,7 +1204,7 @@ "description": "tooltip" }, "cloudPullAndMerge": { - "message": "මේඝ ආචයනයෙන් ආයාත කර වත්මන් සැකසුම් සමඟ සංයුක්ත කරන්න", + "message": "මේඝ ආචයනයෙන් ආයාත කර වත්මන් සැකසුම් වලට ඒකාබද්ධ කරන්න", "description": "tooltip" }, "cloudNoData": { @@ -1216,11 +1216,11 @@ "description": "used as a prompt for the user to provide a custom device name" }, "advancedSettingsWarning": { - "message": "Warning! Change these advanced settings at your own risk.", + "message": "අවවාදයයි! මෙම සංකීර්ණ සැකසුම් සංශෝධනයේ අවදානම ඔබ සතුයි.", "description": "A warning to users at the top of 'Advanced settings' page" }, "genericSubmit": { - "message": "Submit", + "message": "යොමන්න", "description": "for generic 'Submit' buttons" }, "genericApplyChanges": { @@ -1228,7 +1228,7 @@ "description": "for generic 'Apply changes' buttons" }, "genericRevert": { - "message": "Revert", + "message": "ප්‍රතිවර්තනය", "description": "for generic 'Revert' buttons" }, "genericBytes": { @@ -1248,11 +1248,11 @@ "description": "A context menu entry, present when large media elements have been blocked on the current site" }, "contextMenuViewSource": { - "message": "View source code…", + "message": "මූලාශ්‍ර කේත බලන්න…", "description": "A context menu entry, to view the source code of the target resource" }, "shortcutCapturePlaceholder": { - "message": "Type a shortcut", + "message": "කෙටිමඟක් ලියන්න", "description": "Placeholder string for input field used to capture a keyboard shortcut" }, "genericMergeViewScrollLock": { @@ -1260,11 +1260,11 @@ "description": "Tooltip for the button used to lock scrolling between the views in the 'My rules' pane" }, "genericCopyToClipboard": { - "message": "පසුරුපුවරුවට පිටපත් කරන්න", + "message": "පසුරු පුවරුවට පිටපතක්", "description": "Label for buttons used to copy something to the clipboard" }, "genericSelectAll": { - "message": "Select all", + "message": "සියල්ල තෝරන්න", "description": "Label for buttons used to select all text in editor" }, "toggleCosmeticFiltering": { @@ -1280,7 +1280,7 @@ "description": "Label for keyboard shortcut used to relax blocking mode" }, "storageUsed": { - "message": "Storage used: {{value}} {{unit}}", + "message": "ආචයනය භාවිතය: {{value}} {{unit}}", "description": " In Setting pane, renders as (example): Storage used: 13.2 MB" }, "KB": { @@ -1300,7 +1300,7 @@ "description": "Message used in frame placeholders" }, "linterMainReport": { - "message": "Errors: {{count}}", + "message": "දෝෂ: {{count}}", "description": "Summary of number of errors as reported by the linter " }, "unprocessedRequestTooltip": { diff --git a/src/_locales/vi/messages.json b/src/_locales/vi/messages.json index 4b6559af68124..bfea4310a0f38 100644 --- a/src/_locales/vi/messages.json +++ b/src/_locales/vi/messages.json @@ -140,7 +140,7 @@ "description": "Tooltip for the no-popups per-site switch" }, "popupTipNoPopups2": { - "message": "Nhấn để bỏ chặn tất cả cửa sổ bật lên trên trang này", + "message": "Nhấp để bỏ chặn tất cả cửa sổ bật lên trên trang này", "description": "Tooltip for the no-popups per-site switch" }, "popupTipNoLargeMedia": { @@ -480,7 +480,7 @@ "description": "Filter lists section name" }, "3pGroupMalware": { - "message": "Bảo mật, bảo vệ khỏi phần mềm nguy hiểm", + "message": "Bảo mật, bảo vệ khỏi phần mềm độc hại", "description": "Filter lists section name" }, "3pGroupSocial": { @@ -628,7 +628,7 @@ "description": "English: a sort option for list of rules." }, "whitelistPrompt": { - "message": "Các chỉ thị trang web đáng tin cậy ra lệnh trên đó các trang web uBlock Origin sẽ bị vô hiệu hóa. Một mục nhập cho mỗi dòng. Chỉ thị không hợp lệ sẽ được âm thầm bỏ qua và nhận xét ra.", + "message": "Các đường dẫn trang web đáng tin cậy cố định mà uBlock Origin sẽ bị vô hiệu hóa trên trang đó. Một mục nhập cho mỗi dòng.", "description": "A concise description of the 'Trusted sites' pane." }, "whitelistImport": { @@ -640,7 +640,7 @@ "description": "Button in the 'Trusted sites' pane" }, "whitelistExportFilename": { - "message": "my-ublock-whitelist_{{datetime}}.txt", + "message": "my-ublock-trusted-sites_{{datetime}}.txt", "description": "The default filename to use for import/export purpose" }, "whitelistApply": { @@ -844,11 +844,11 @@ "description": "Logger setting: A sentence to describe the purpose of the settings below" }, "loggerSettingPerEntryMaxAge": { - "message": "Bảo tồn các mục từ {{input}} phút trước", + "message": "Giữ lại các mục từ {{input}} phút trước", "description": "A logger setting" }, "loggerSettingPerTabMaxLoads": { - "message": "Bảo tồn nhiều nhất {{input}} trang được tải trên mỗi thẻ", + "message": "Giữ lại nhiều nhất {{input}} trang được tải trên mỗi thẻ", "description": "A logger setting" }, "loggerSettingPerTabMaxEntries": { @@ -928,7 +928,7 @@ "description": "Header of 'Filter issues' section in Support pane" }, "supportS3P1": { - "message": "Cần tài khoản GitHub: Báo cáo lỗi bộ lọc với tên miền cụ thể cho uBlock Origin tại đây. ", + "message": "Báo cáo lỗi bộ lọc với tên miền cụ thể cho uBlockOrigin/uAssets issue tracker. Cần tài khoản GitHub ", "description": "First paragraph of 'Filter issues' section in Support pane" }, "supportS3P2": { @@ -944,7 +944,7 @@ "description": "Header of 'Bug report' section in Support pane" }, "supportS4P1": { - "message": "Cần tài khoản GitHub: Báo cáo lỗi bộ lọc với tên miền cụ thể cho uBlock Origin tại đây. ", + "message": "Báo cáo lỗi cho uBlock Origin cho uBlockOrigin/uBlock-issue issue tracker. Cần tài khoản GitHub ", "description": "First paragraph of 'Bug report' section in Support pane" }, "supportS5H": { @@ -968,7 +968,7 @@ "description": "A paragraph in the filter issue reporter section" }, "supportS6P2S1": { - "message": "Danh sách bộ lọc được cập nhật hàng ngày. Đảm bảo vấn đề của bạn chưa được giải quyết trong danh sách bộ lọc gần đây nhất.", + "message": "Danh sách bộ lọc được cập nhật hàng ngày. Hãy đảm bảo vấn đề của bạn chưa được giải quyết trong danh sách bộ lọc gần đây nhất.", "description": "A paragraph in the filter issue reporter section" }, "supportS6P2S2": { @@ -984,7 +984,7 @@ "description": "Label for widget to select type of issue" }, "supportS6Select1Option0": { - "message": "--Chọn một lỗi--", + "message": "-- Chọn một lỗi --", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option1": { @@ -1100,7 +1100,7 @@ "description": "English: Network error: {{msg}}" }, "subscriberConfirm": { - "message": "uBlock₀: Thêm URL dưới đây vào bộ lọc tuỳ biến của bạn?\n\nTên: \"{{title}}\"\nURL: {{url}}", + "message": "Thêm URL dưới đây vào bộ lọc tuỳ biến của bạn?\n\nTên: \"{{title}}\"\nURL: {{url}}", "description": "No longer used" }, "subscribeButton": { @@ -1192,7 +1192,7 @@ "description": "Button text to navigate to the blocked page" }, "docblockedRedirectPrompt": { - "message": "The blocked page wants to redirect to another site. If you choose to proceed, you will navigate directly to: {{url}}", + "message": "Trang đã chặn muốn chuyển hướng sang trang khác. Nếu đồng ý, bạn sẽ được chuyển hướng sang {{url}}", "description": "Text warning about an incoming redirect" }, "cloudPush": { @@ -1248,7 +1248,7 @@ "description": "A context menu entry, present when large media elements have been blocked on the current site" }, "contextMenuViewSource": { - "message": "Xem mã nguồn", + "message": "Xem mã nguồn...", "description": "A context menu entry, to view the source code of the target resource" }, "shortcutCapturePlaceholder": { @@ -1280,7 +1280,7 @@ "description": "Label for keyboard shortcut used to relax blocking mode" }, "storageUsed": { - "message": "Bộ nhớ đã dùng: {{value}} byte", + "message": "Bộ nhớ đã dùng: {{value}} {{unit}}", "description": " In Setting pane, renders as (example): Storage used: 13.2 MB" }, "KB": { From 7fcd2a51be94a5b5de432a43a4cf54b73e084ae4 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Thu, 14 Nov 2024 10:51:21 -0500 Subject: [PATCH 0449/1099] Make Firefox dev build auto-update --- dist/firefox/updates.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dist/firefox/updates.json b/dist/firefox/updates.json index 4206cc148d91d..757609c27b914 100644 --- a/dist/firefox/updates.json +++ b/dist/firefox/updates.json @@ -3,9 +3,9 @@ "uBlock0@raymondhill.net": { "updates": [ { - "version": "1.61.1.2", + "version": "1.61.1.3", "browser_specific_settings": { "gecko": { "strict_min_version": "78.0" } }, - "update_link": "https://github.com/gorhill/uBlock/releases/download/1.61.1b2/uBlock0_1.61.1b2.firefox.signed.xpi" + "update_link": "https://github.com/gorhill/uBlock/releases/download/1.61.1b3/uBlock0_1.61.1b3.firefox.signed.xpi" } ] } From 947602d4fe46ec832d3f95ee3a65633fc4113f44 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Fri, 15 Nov 2024 10:01:29 -0500 Subject: [PATCH 0450/1099] [mv3] Slightly mitigate DNR flaw re. `removeparam` filters Related issue: https://github.com/uBlockOrigin/uBOL-home/issues/140 This will allow more specific `removeparam` filters to override generic ones. This doesn't fix the related issue but should help more specific `removeparam` filters to be applied. Related webextensions issue: https://github.com/w3c/webextensions/issues/468 --- src/js/static-net-filtering.js | 36 ++++++++++++++++++++++------------ 1 file changed, 24 insertions(+), 12 deletions(-) diff --git a/src/js/static-net-filtering.js b/src/js/static-net-filtering.js index 71b67e30c6a21..bc14d71dbbdb0 100644 --- a/src/js/static-net-filtering.js +++ b/src/js/static-net-filtering.js @@ -4452,22 +4452,23 @@ StaticNetFilteringEngine.prototype.dnrFromCompiled = function(op, context, ...ar } // Priority: - // Block: 1 (default priority) - // Redirect: 2-9 - // Excepted redirect: 12-19 - // Allow: 20 - // Block important: 30 - // Redirect important: 32-39 + // Removeparam: 1-4 + // Block: 10 (default priority) + // Redirect: 11-19 + // Excepted redirect: 21-29 + // Allow: 30 + // Block important: 40 + // Redirect important: 41-49 const realms = new Map([ - [ BLOCK_REALM, { type: 'block', priority: 0 } ], - [ ALLOW_REALM, { type: 'allow', priority: 20 } ], - [ REDIRECT_REALM, { type: 'redirect', priority: 2 } ], + [ BLOCK_REALM, { type: 'block', priority: 10 } ], + [ ALLOW_REALM, { type: 'allow', priority: 30 } ], + [ REDIRECT_REALM, { type: 'redirect', priority: 11 } ], [ REMOVEPARAM_REALM, { type: 'removeparam', priority: 0 } ], [ CSP_REALM, { type: 'csp', priority: 0 } ], [ PERMISSIONS_REALM, { type: 'permissions', priority: 0 } ], [ URLTRANSFORM_REALM, { type: 'uritransform', priority: 0 } ], - [ HEADERS_REALM, { type: 'block', priority: 0 } ], + [ HEADERS_REALM, { type: 'block', priority: 10 } ], [ URLSKIP_REALM, { type: 'urlskip', priority: 0 } ], ]); const partyness = new Map([ @@ -4605,7 +4606,7 @@ StaticNetFilteringEngine.prototype.dnrFromCompiled = function(op, context, ...ar if ( token !== '' ) { const match = /:(\d+)$/.exec(token); if ( match !== null ) { - rule.priority = Math.min(rule.priority + parseInt(match[1], 10), 9); + rule.priority += Math.min(rule.priority + parseInt(match[1], 10), 9); token = token.slice(0, match.index); } } @@ -4623,7 +4624,7 @@ StaticNetFilteringEngine.prototype.dnrFromCompiled = function(op, context, ...ar } break; } - case 'removeparam': + case 'removeparam': { rule.action.type = 'redirect'; if ( rule.__modifierValue === '|' ) { rule.__modifierValue = ''; @@ -4657,10 +4658,21 @@ StaticNetFilteringEngine.prototype.dnrFromCompiled = function(op, context, ...ar 'xmlhttprequest', ]; } + // https://github.com/uBlockOrigin/uBOL-home/issues/140 + // Mitigate until DNR API flaw is addressed by browser vendors + let priority = rule.priority || 1; + if ( rule.condition.urlFilter !== undefined ) { priority += 1; } + if ( rule.condition.regexFilter !== undefined ) { priority += 1; } + if ( rule.condition.initiatorDomains !== undefined ) { priority += 1; } + if ( rule.condition.requestDomains !== undefined ) { priority += 1; } + if ( priority !== 1 ) { + rule.priority = priority; + } if ( rule.__modifierAction === ALLOW_REALM ) { dnrAddRuleError(rule, `Unsupported removeparam exception: ${rule.__modifierValue}`); } break; + } case 'uritransform': { dnrAddRuleError(rule, `Incompatible with DNR: uritransform=${rule.__modifierValue}`); break; From c8174d60320cedb8a22afedae2a3acddac9c8bf1 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sat, 16 Nov 2024 08:10:25 -0500 Subject: [PATCH 0451/1099] Improve `trusted-set-attr` scriptlet Related feedback: https://github.com/gorhill/uBlock/commit/11ca4a39239478e35605ec072fca140ac4c70d3b#commitcomment-149148167 --- assets/resources/attribute.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/assets/resources/attribute.js b/assets/resources/attribute.js index 0c50daf879003..7873183de16b4 100644 --- a/assets/resources/attribute.js +++ b/assets/resources/attribute.js @@ -29,6 +29,7 @@ import { safeSelf } from './safe-self.js'; /******************************************************************************/ export function setAttrFn( + trusted = false, logPrefix, selector = '', attr = '', @@ -38,7 +39,7 @@ export function setAttrFn( if ( attr === '' ) { return; } const safe = safeSelf(); - const copyFrom = /^\[.+\]$/.test(value) + const copyFrom = trusted === false && /^\[.+\]$/.test(value) ? value.slice(1, -1) : ''; @@ -148,7 +149,7 @@ export function setAttr( } } - setAttrFn(logPrefix, selector, attr, value); + setAttrFn(false, logPrefix, selector, attr, value); } registerScriptlet(setAttr, { name: 'set-attr.js', @@ -187,7 +188,7 @@ export function trustedSetAttr( ) { const safe = safeSelf(); const logPrefix = safe.makeLogPrefix('trusted-set-attr', selector, attr, value); - setAttrFn(logPrefix, selector, attr, value); + setAttrFn(true, logPrefix, selector, attr, value); } registerScriptlet(trustedSetAttr, { name: 'trusted-set-attr.js', From 5f5e3d730fabe0e98e2f9931ad3c36113b7b9893 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sat, 16 Nov 2024 09:26:09 -0500 Subject: [PATCH 0452/1099] Improve `spoof-css` scriptlet Added special properties to spoof output of getBoundingClientRect(). --- assets/resources/run-at.js | 2 +- assets/resources/scriptlets.js | 118 +---------------------- assets/resources/spoof-css.js | 166 +++++++++++++++++++++++++++++++++ 3 files changed, 170 insertions(+), 116 deletions(-) create mode 100644 assets/resources/spoof-css.js diff --git a/assets/resources/run-at.js b/assets/resources/run-at.js index 0a7dfb5e4dbe5..65e1be534dd5a 100644 --- a/assets/resources/run-at.js +++ b/assets/resources/run-at.js @@ -82,7 +82,7 @@ registerScriptlet(runAt, { /******************************************************************************/ -function runAtHtmlElementFn(fn) { +export function runAtHtmlElementFn(fn) { if ( document.documentElement ) { fn(); return; diff --git a/assets/resources/scriptlets.js b/assets/resources/scriptlets.js index 531959e23de66..46c31b6dc4e13 100644 --- a/assets/resources/scriptlets.js +++ b/assets/resources/scriptlets.js @@ -27,11 +27,13 @@ import './cookie.js'; import './localstorage.js'; import './run-at.js'; import './safe-self.js'; +import './spoof-css.js'; + +import { runAt, runAtHtmlElementFn } from './run-at.js'; import { getAllCookiesFn } from './cookie.js'; import { getAllLocalStorageFn } from './localstorage.js'; import { registeredScriptlets } from './base.js'; -import { runAt } from './run-at.js'; import { safeSelf } from './safe-self.js'; // Externally added to the private namespace in which scriptlets execute. @@ -3269,120 +3271,6 @@ function callNothrow( }); } - -/******************************************************************************/ - -builtinScriptlets.push({ - name: 'spoof-css.js', - fn: spoofCSS, - dependencies: [ - 'safe-self.fn', - ], -}); -function spoofCSS( - selector, - ...args -) { - if ( typeof selector !== 'string' ) { return; } - if ( selector === '' ) { return; } - const toCamelCase = s => s.replace(/-[a-z]/g, s => s.charAt(1).toUpperCase()); - const propToValueMap = new Map(); - for ( let i = 0; i < args.length; i += 2 ) { - if ( typeof args[i+0] !== 'string' ) { break; } - if ( args[i+0] === '' ) { break; } - if ( typeof args[i+1] !== 'string' ) { break; } - propToValueMap.set(toCamelCase(args[i+0]), args[i+1]); - } - const safe = safeSelf(); - const logPrefix = safe.makeLogPrefix('spoof-css', selector, ...args); - const canDebug = scriptletGlobals.canDebug; - const shouldDebug = canDebug && propToValueMap.get('debug') || 0; - const instanceProperties = [ 'cssText', 'length', 'parentRule' ]; - const spoofStyle = (prop, real) => { - const normalProp = toCamelCase(prop); - const shouldSpoof = propToValueMap.has(normalProp); - const value = shouldSpoof ? propToValueMap.get(normalProp) : real; - if ( shouldSpoof ) { - safe.uboLog(logPrefix, `Spoofing ${prop} to ${value}`); - } - return value; - }; - const cloackFunc = (fn, thisArg, name) => { - const trap = fn.bind(thisArg); - Object.defineProperty(trap, 'name', { value: name }); - Object.defineProperty(trap, 'toString', { - value: ( ) => `function ${name}() { [native code] }` - }); - return trap; - }; - self.getComputedStyle = new Proxy(self.getComputedStyle, { - apply: function(target, thisArg, args) { - // eslint-disable-next-line no-debugger - if ( shouldDebug !== 0 ) { debugger; } - const style = Reflect.apply(target, thisArg, args); - const targetElements = new WeakSet(document.querySelectorAll(selector)); - if ( targetElements.has(args[0]) === false ) { return style; } - const proxiedStyle = new Proxy(style, { - get(target, prop) { - if ( typeof target[prop] === 'function' ) { - if ( prop === 'getPropertyValue' ) { - return cloackFunc(function getPropertyValue(prop) { - return spoofStyle(prop, target[prop]); - }, target, 'getPropertyValue'); - } - return cloackFunc(target[prop], target, prop); - } - if ( instanceProperties.includes(prop) ) { - return Reflect.get(target, prop); - } - return spoofStyle(prop, Reflect.get(target, prop)); - }, - getOwnPropertyDescriptor(target, prop) { - if ( propToValueMap.has(prop) ) { - return { - configurable: true, - enumerable: true, - value: propToValueMap.get(prop), - writable: true, - }; - } - return Reflect.getOwnPropertyDescriptor(target, prop); - }, - }); - return proxiedStyle; - }, - get(target, prop) { - if ( prop === 'toString' ) { - return target.toString.bind(target); - } - return Reflect.get(target, prop); - }, - }); - Element.prototype.getBoundingClientRect = new Proxy(Element.prototype.getBoundingClientRect, { - apply: function(target, thisArg, args) { - // eslint-disable-next-line no-debugger - if ( shouldDebug !== 0 ) { debugger; } - const rect = Reflect.apply(target, thisArg, args); - const targetElements = new WeakSet(document.querySelectorAll(selector)); - if ( targetElements.has(thisArg) === false ) { return rect; } - let { height, width } = rect; - if ( propToValueMap.has('width') ) { - width = parseFloat(propToValueMap.get('width')); - } - if ( propToValueMap.has('height') ) { - height = parseFloat(propToValueMap.get('height')); - } - return new self.DOMRect(rect.x, rect.y, width, height); - }, - get(target, prop) { - if ( prop === 'toString' ) { - return target.toString.bind(target); - } - return Reflect.get(target, prop); - }, - }); -} - /******************************************************************************/ builtinScriptlets.push({ diff --git a/assets/resources/spoof-css.js b/assets/resources/spoof-css.js new file mode 100644 index 0000000000000..431c6f96b4d60 --- /dev/null +++ b/assets/resources/spoof-css.js @@ -0,0 +1,166 @@ +/******************************************************************************* + + uBlock Origin - a comprehensive, efficient content blocker + Copyright (C) 2019-present Raymond Hill + + 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/}. + + Home: https://github.com/gorhill/uBlock + + The scriptlets below are meant to be injected only into a + web page context. +*/ + +import { registerScriptlet } from './base.js'; +import { safeSelf } from './safe-self.js'; + +/** + * @scriptlet spoof-css.js + * + * @description + * Spoof the value of CSS properties. + * + * @param selector + * A CSS selector for the element(s) to target. + * + * @param [property, value, ...] + * A list of property-value pairs of the style properties to spoof to the + * specified values. +* + * */ + +export function spoofCSS( + selector, + ...args +) { + if ( typeof selector !== 'string' ) { return; } + if ( selector === '' ) { return; } + const toCamelCase = s => s.replace(/-[a-z]/g, s => s.charAt(1).toUpperCase()); + const propToValueMap = new Map(); + const privatePropToValueMap = new Map(); + for ( let i = 0; i < args.length; i += 2 ) { + const prop = toCamelCase(args[i+0]); + if ( typeof prop !== 'string' ) { break; } + if ( prop === '' ) { break; } + const value = args[i+1]; + if ( typeof value !== 'string' ) { break; } + if ( prop.charCodeAt(0) === 0x5F /* _ */ ) { + privatePropToValueMap.set(prop, value); + } else { + propToValueMap.set(toCamelCase(prop), value); + } + } + const safe = safeSelf(); + const logPrefix = safe.makeLogPrefix('spoof-css', selector, ...args); + const instanceProperties = [ 'cssText', 'length', 'parentRule' ]; + const spoofStyle = (prop, real) => { + const normalProp = toCamelCase(prop); + const shouldSpoof = propToValueMap.has(normalProp); + const value = shouldSpoof ? propToValueMap.get(normalProp) : real; + if ( shouldSpoof ) { + safe.uboLog(logPrefix, `Spoofing ${prop} to ${value}`); + } + return value; + }; + const cloackFunc = (fn, thisArg, name) => { + const trap = fn.bind(thisArg); + Object.defineProperty(trap, 'name', { value: name }); + Object.defineProperty(trap, 'toString', { + value: ( ) => `function ${name}() { [native code] }` + }); + return trap; + }; + self.getComputedStyle = new Proxy(self.getComputedStyle, { + apply: function(target, thisArg, args) { + // eslint-disable-next-line no-debugger + if ( privatePropToValueMap.has('_debug') ) { debugger; } + const style = Reflect.apply(target, thisArg, args); + const targetElements = new WeakSet(document.querySelectorAll(selector)); + if ( targetElements.has(args[0]) === false ) { return style; } + const proxiedStyle = new Proxy(style, { + get(target, prop) { + if ( typeof target[prop] === 'function' ) { + if ( prop === 'getPropertyValue' ) { + return cloackFunc(function getPropertyValue(prop) { + return spoofStyle(prop, target[prop]); + }, target, 'getPropertyValue'); + } + return cloackFunc(target[prop], target, prop); + } + if ( instanceProperties.includes(prop) ) { + return Reflect.get(target, prop); + } + return spoofStyle(prop, Reflect.get(target, prop)); + }, + getOwnPropertyDescriptor(target, prop) { + if ( propToValueMap.has(prop) ) { + return { + configurable: true, + enumerable: true, + value: propToValueMap.get(prop), + writable: true, + }; + } + return Reflect.getOwnPropertyDescriptor(target, prop); + }, + }); + return proxiedStyle; + }, + get(target, prop) { + if ( prop === 'toString' ) { + return target.toString.bind(target); + } + return Reflect.get(target, prop); + }, + }); + Element.prototype.getBoundingClientRect = new Proxy(Element.prototype.getBoundingClientRect, { + apply: function(target, thisArg, args) { + // eslint-disable-next-line no-debugger + if ( privatePropToValueMap.has('_debug') ) { debugger; } + const rect = Reflect.apply(target, thisArg, args); + const targetElements = new WeakSet(document.querySelectorAll(selector)); + if ( targetElements.has(thisArg) === false ) { return rect; } + let { x, y, height, width } = rect; + if ( privatePropToValueMap.has('_rectx') ) { + x = parseFloat(privatePropToValueMap.get('_rectx')); + } + if ( privatePropToValueMap.has('_recty') ) { + y = parseFloat(privatePropToValueMap.get('_recty')); + } + if ( privatePropToValueMap.has('_rectw') ) { + width = parseFloat(privatePropToValueMap.get('_rectw')); + } else if ( propToValueMap.has('width') ) { + width = parseFloat(propToValueMap.get('width')); + } + if ( privatePropToValueMap.has('_recth') ) { + height = parseFloat(privatePropToValueMap.get('_recth')); + } else if ( propToValueMap.has('height') ) { + height = parseFloat(propToValueMap.get('height')); + } + return new self.DOMRect(x, y, width, height); + }, + get(target, prop) { + if ( prop === 'toString' ) { + return target.toString.bind(target); + } + return Reflect.get(target, prop); + }, + }); +} +registerScriptlet(spoofCSS, { + name: 'spoof-css.js', + dependencies: [ + safeSelf, + ], +}); From edf4f52fe5cfd017a93313e0b0899489158d6992 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sat, 16 Nov 2024 09:31:02 -0500 Subject: [PATCH 0453/1099] Update changelog --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9bd7090450386..2482e13d7ffa4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,5 @@ +- [Improve `spoof-css` scriptlet](https://github.com/gorhill/uBlock/commit/5f5e3d730f) +- [Improve `trusted-set-attr` scriptlet](https://github.com/gorhill/uBlock/commit/c8174d6032) - [Better handle unexpected conditions when deserializing](https://github.com/gorhill/uBlock/commit/4c299bfca9) - [Add support for EasyList `{ remove: true }` cosmetic filter syntax](https://github.com/gorhill/uBlock/commit/ff5fc61753) - [Fix potential infinite async loop](https://github.com/gorhill/uBlock/commit/335d947c10) (issue found by @Rob--W) From f8bd9a10073154bde006eaba61c1ae034a63e1ed Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sat, 16 Nov 2024 09:31:29 -0500 Subject: [PATCH 0454/1099] New revision for dev build --- dist/version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dist/version b/dist/version index c530652b600c5..060bb16eff91d 100644 --- a/dist/version +++ b/dist/version @@ -1 +1 @@ -1.61.1.3 \ No newline at end of file +1.61.1.4 \ No newline at end of file From 442331136cbdbeb06dece0dafa881491d1335edc Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sat, 16 Nov 2024 09:50:51 -0500 Subject: [PATCH 0455/1099] Make Firefox dev build auto-update --- dist/firefox/updates.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dist/firefox/updates.json b/dist/firefox/updates.json index 757609c27b914..89febecae6472 100644 --- a/dist/firefox/updates.json +++ b/dist/firefox/updates.json @@ -3,9 +3,9 @@ "uBlock0@raymondhill.net": { "updates": [ { - "version": "1.61.1.3", + "version": "1.61.1.4", "browser_specific_settings": { "gecko": { "strict_min_version": "78.0" } }, - "update_link": "https://github.com/gorhill/uBlock/releases/download/1.61.1b3/uBlock0_1.61.1b3.firefox.signed.xpi" + "update_link": "https://github.com/gorhill/uBlock/releases/download/1.61.1b4/uBlock0_1.61.1b4.firefox.signed.xpi" } ] } From 51edb13a0699efe3e9c05004594b1fcbd54b15ed Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sat, 16 Nov 2024 09:54:19 -0500 Subject: [PATCH 0456/1099] Minor code review --- assets/resources/spoof-css.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/assets/resources/spoof-css.js b/assets/resources/spoof-css.js index 431c6f96b4d60..32b51acf407c0 100644 --- a/assets/resources/spoof-css.js +++ b/assets/resources/spoof-css.js @@ -51,14 +51,13 @@ export function spoofCSS( const privatePropToValueMap = new Map(); for ( let i = 0; i < args.length; i += 2 ) { const prop = toCamelCase(args[i+0]); - if ( typeof prop !== 'string' ) { break; } if ( prop === '' ) { break; } const value = args[i+1]; if ( typeof value !== 'string' ) { break; } if ( prop.charCodeAt(0) === 0x5F /* _ */ ) { privatePropToValueMap.set(prop, value); } else { - propToValueMap.set(toCamelCase(prop), value); + propToValueMap.set(prop, value); } } const safe = safeSelf(); From b4a5b411b57906780ecd14c717cf438c2b179a32 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sat, 16 Nov 2024 12:01:30 -0500 Subject: [PATCH 0457/1099] Add "RU AdList: Counters" to stock list Shouldn't be enabled by default, to conform it's working as intended. Related discussion: https://github.com/uBlockOrigin/uBOL-home/discussions/37#discussioncomment-9629942 --- assets/assets.dev.json | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/assets/assets.dev.json b/assets/assets.dev.json index f776738c10eb9..a51bc714c3933 100644 --- a/assets/assets.dev.json +++ b/assets/assets.dev.json @@ -826,6 +826,7 @@ "RUS-0": { "content": "filters", "group": "regions", + "parent": "🇷🇺ru 🇺🇦ua 🇺🇿uz 🇰🇿kz: RU AdList", "off": true, "title": "🇷🇺ru 🇺🇦ua 🇺🇿uz 🇰🇿kz: RU AdList", "tags": "ads belarusian беларуская kazakh tatar russian русский ukrainian українська uzbek", @@ -839,6 +840,22 @@ "supportURL": "https://forums.lanik.us/viewforum.php?f=102", "instructionURL": "https://forums.lanik.us/viewtopic.php?f=102&t=22512" }, + "RUS-1": { + "content": "filters", + "group": "regions", + "parent": "🇷🇺ru 🇺🇦ua 🇺🇿uz 🇰🇿kz: RU AdList", + "off": true, + "title": "🇷🇺ru 🇺🇦ua 🇺🇿uz 🇰🇿kz: RU AdList: Counters", + "tags": "ads belarusian беларуская kazakh tatar russian русский ukrainian українська uzbek be kk tt ru uk uz", + "contentURL": "https://raw.githubusercontent.com/easylist/ruadlist/master/cntblock.txt", + "cdnURLs": [ + "https://cdn.jsdelivr.net/gh/easylist/ruadlist@master/cntblock.txt", + "https://cdn.statically.io/gh/easylist/ruadlist/master/cntblock.txt", + "https://raw.githubusercontent.com/easylist/ruadlist/master/cntblock.txt" + ], + "supportURL": "https://forums.lanik.us/viewforum.php?f=102", + "instructionURL": "https://forums.lanik.us/viewtopic.php?f=102&t=22512" + }, "spa-0": { "content": "filters", "group": "regions", From ae4754415c9a01f18d3219972ef35b959536891f Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sun, 17 Nov 2024 17:27:27 -0500 Subject: [PATCH 0458/1099] [mv3] Re-work dashboard: move list of rulesets in its own pane Related issue: https://github.com/uBlockOrigin/uBOL-home/issues/229 Add "Filter lists" pane in dashboard The DNR API now supports enabling 50 static rulesets put of a maximum of 100 (instead of 10 out of 50 originally). Thus given the potentially growing number of static rulesets, the available stock rulesets has been moved to its own pane, with the following improvements: - Support sublists - Support search Aditionally, "RU AdList: Counter" has been added as a stock ruleset. Other changes: - Do not re-evaluate regexes which failed validation - Better reduce `removeparam` rules --- platform/mv3/extension/css/dashboard.css | 2 + platform/mv3/extension/css/settings.css | 103 ++--- platform/mv3/extension/dashboard.html | 54 ++- platform/mv3/extension/js/filter-lists.js | 402 +++++++++++++++++++ platform/mv3/extension/js/ruleset-manager.js | 6 + platform/mv3/extension/js/settings.js | 298 +------------- platform/mv3/make-rulesets.js | 109 ++--- src/js/static-dnr-filtering.js | 2 +- tools/make-mv3.sh | 2 +- 9 files changed, 566 insertions(+), 412 deletions(-) create mode 100644 platform/mv3/extension/js/filter-lists.js diff --git a/platform/mv3/extension/css/dashboard.css b/platform/mv3/extension/css/dashboard.css index 6506c22722aca..454a799874fee 100644 --- a/platform/mv3/extension/css/dashboard.css +++ b/platform/mv3/extension/css/dashboard.css @@ -33,6 +33,7 @@ } body[data-pane="settings"] #dashboard-nav .tabButton[data-pane="settings"], +body[data-pane="rulesets"] #dashboard-nav .tabButton[data-pane="rulesets"], body[data-pane="about"] #dashboard-nav .tabButton[data-pane="about"] { background-color: var(--dashboard-tab-active-surface); border-bottom: 3px solid var(--dashboard-tab-active-ink); @@ -44,6 +45,7 @@ body > section { display: none; } body[data-pane="settings"] > section[data-pane="settings"], +body[data-pane="rulesets"] > section[data-pane="rulesets"], body[data-pane="about"] > section[data-pane="about"] { display: block; } diff --git a/platform/mv3/extension/css/settings.css b/platform/mv3/extension/css/settings.css index 3de13560cc4f1..da6e13a1aaf2f 100644 --- a/platform/mv3/extension/css/settings.css +++ b/platform/mv3/extension/css/settings.css @@ -79,92 +79,101 @@ h3[data-i18n="filteringMode0Name"]::first-letter { } #lists { - margin: 0.5em 0 0 0; - padding: 0; + padding-block-end: 8rem; } -.groupEntry:not([data-groupkey="user"]) .geDetails::before { - color: var(--ink-3); - content: '\2212'; - font-family: monospace; - font-size: large; - margin-inline-end: 0.25em; - -webkit-margin-end: 0.25em; - } -.groupEntry.hideUnused:not([data-groupkey="user"]) .geDetails::before { - content: '+'; +.listEntry { + display: flex; + flex-direction: column; } -.groupEntry { - margin: 0.5em 0; +.listEntry[data-nodeid] > .detailbar .listExpander { + cursor: pointer; + top: 2px; } -.groupEntry .geDetails { +.listEntry[data-role="rootnode"] > .detailbar, +.listEntry[data-nodeid] > .detailbar .count { cursor: pointer; } -.groupEntry .geName { +.listEntry[data-role="rootnode"] > .detailbar > *:not(.listExpander) { pointer-events: none; } -.groupEntry .geCount { +.listEntry .detailbar .count { + align-self: flex-end; color: var(--ink-3); - font-size: 90%; + font-size: small; pointer-events: none; } .listEntries { - margin-inline-start: 0.6em; - -webkit-margin-start: 0.6em; + display: flex; + flex-direction: column; + } +.listEntry:not([data-role="rootnode"]) > .listEntries { + margin-inline-start: var(--checkbox-size); } -.groupEntry:not([data-groupkey="user"]) .listEntry:not(.isDefault).unused { +.listEntry.hideUnused > .listEntries > .listEntry:not(.isDefault):has(> .detailbar input:not(:checked)) { display: none; } - .listEntry.fromAdmin:has(input[disabled]:not(:checked)) { display: none; } .listEntry > * { - margin-left: 0; - margin-right: 0; unicode-bidi: embed; } +.listEntry h3 { + display: inline-block; + margin: 0; + } +.listEntry > .detailbar { + align-items: center; + display: inline-flex; + margin: calc(var(--default-gap-xsmall) / 2 + var(--default-gap-xxsmall) / 2) 0; + white-space: nowrap; + } +.listEntry > .detailbar > *:not(:first-child) { + margin-inline-start: var(--default-gap-xxsmall); + } +.listEntry[data-nodeid="default"] > .detailbar > .listExpander { + display: none; + } +.listEntry > .detailbar > .listExpander svg { + transform: rotate(180deg); + transform-origin: 50%; + } +.listEntry.hideUnused > .detailbar > .listExpander svg { + transform: rotate(90deg); + } .listEntry .checkbox:has(input[disabled]), .listEntry .checkbox:has(input[disabled]) ~ span { filter: var(--checkbox-disabled-filter); } -.listEntry .listname { - white-space: nowrap; - } .listEntry a, .listEntry .fa-icon { color: var(--info0-ink); fill: var(--info0-ink); - display: none; font-size: 120%; - margin: 0 0.2em 0 0; } .listEntry .fa-icon:hover { transform: scale(1.25); } -.listEntry .content { - display: inline-flex; - } -.listEntry a.towiki { - display: inline-flex; - } -.listEntry.support a.support { - display: inline-flex; - } -.listEntry.mustread a.mustread { - color: var(--info1-ink); - fill: var(--info1-ink); - display: inline-flex; - } -.listEntry .status { - cursor: default; +.listEntry .iconbar a.support[href="#"] { display: none; -} + } -body.noMoreRuleset .listEntry:not(.checked) { +body.noMoreRuleset .listEntry:has(> .detailbar input:not(:checked)) { opacity: 0.5; pointer-events: none; } +#lists.searchMode > .listEntries .listEntries, +#lists.searchMode > .listEntries .listEntry.searchMatch { + display: flex !important; + } +#lists.searchMode > .listEntries .listEntry { + display: none; + } +#lists.searchMode > .listEntries .listExpander { + visibility: hidden; + } + /* touch-screen devices */ :root.mobile .listEntry .fa-icon { font-size: 120%; diff --git a/platform/mv3/extension/dashboard.html b/platform/mv3/extension/dashboard.html index 1cf7582050951..73dd72ac0fece 100644 --- a/platform/mv3/extension/dashboard.html +++ b/platform/mv3/extension/dashboard.html @@ -21,6 +21,7 @@
@@ -95,29 +96,14 @@

- + + +
-

-
-

-
-
-
-
-
- -
-
-
-
-
- +

+
search
+
@@ -146,6 +132,32 @@

+
+
+
+ + + home + + +
+ + +
diff --git a/platform/mv3/extension/js/filter-lists.js b/platform/mv3/extension/js/filter-lists.js new file mode 100644 index 0000000000000..a52dcb433c846 --- /dev/null +++ b/platform/mv3/extension/js/filter-lists.js @@ -0,0 +1,402 @@ +/******************************************************************************* + + uBlock Origin Lite - a comprehensive, MV3-compliant content blocker + Copyright (C) 2014-present Raymond Hill + + 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/}. + + Home: https://github.com/gorhill/uBlock +*/ + +import { dom, qs$, qsa$ } from './dom.js'; +import { i18n, i18n$ } from './i18n.js'; +import { localRead, localWrite, sendMessage } from './ext.js'; + +/******************************************************************************/ + +export const rulesetMap = new Map(); + +let cachedRulesetData = {}; +let hideUnusedSet = new Set([ 'regions' ]); + +/******************************************************************************/ + +function renderNumber(value) { + return value.toLocaleString(); +} + +function renderRuleCounts() { + let rulesetCount = 0; + let filterCount = 0; + let ruleCount = 0; + for ( const liEntry of qsa$('#lists .listEntry[data-role="leaf"][data-rulesetid]') ) { + if ( qs$(liEntry, 'input[type="checkbox"]:checked') === null ) { continue; } + rulesetCount += 1; + const stats = rulesetStats(liEntry.dataset.rulesetid); + if ( stats === undefined ) { continue; } + ruleCount += stats.ruleCount; + filterCount += stats.filterCount; + } + dom.text('#listsOfBlockedHostsPrompt', i18n$('perRulesetStats') + .replace('{{ruleCount}}', ruleCount.toLocaleString()) + .replace('{{filterCount}}', filterCount.toLocaleString()) + ); + + dom.cl.toggle(dom.body, 'noMoreRuleset', + rulesetCount === cachedRulesetData.maxNumberOfEnabledRulesets + ); +} + +/******************************************************************************/ + +function updateNodes(listEntries) { + listEntries = listEntries || qs$('#lists'); + for ( const listEntry of qsa$(listEntries, '.listEntry[data-nodeid]') ) { + const totalCount = qsa$(listEntry, '.listEntry[data-rulesetid] input').length; + const checkedCount = qsa$(listEntry, '.listEntry[data-rulesetid] input:checked').length; + dom.text(qs$(listEntry, '.detailbar .count'), `${checkedCount}/${totalCount}`); + const checkbox = qs$(listEntry, ':scope > .detailbar .checkbox'); + if ( checkbox === null ) { continue; } + dom.prop(qs$(checkbox, 'input'), 'checked', checkedCount !== 0); + dom.cl.toggle(checkbox, 'partial', + checkedCount !== 0 && checkedCount !== totalCount + ); + } +} + +/******************************************************************************/ + +function rulesetStats(rulesetId) { + const hasOmnipotence = cachedRulesetData.defaultFilteringMode > 1; + const rulesetDetails = rulesetMap.get(rulesetId); + if ( rulesetDetails === undefined ) { return; } + const { rules, filters } = rulesetDetails; + let ruleCount = rules.plain + rules.regex; + if ( hasOmnipotence ) { + ruleCount += rules.removeparam + rules.redirect + rules.modifyHeaders; + } + const filterCount = filters.accepted; + return { ruleCount, filterCount }; +} + +/******************************************************************************/ + +function isAdminRuleset(listkey) { + const { adminRulesets = [] } = cachedRulesetData; + for ( const id of adminRulesets ) { + const pos = id.indexOf(listkey); + if ( pos === 0 ) { return true; } + if ( pos !== 1 ) { continue; } + const c = id.charAt(0); + if ( c === '+' || c === '-' ) { return true; } + } + return false; +} + +/******************************************************************************/ + +export function renderFilterLists(rulesetData) { + cachedRulesetData = rulesetData; + const { enabledRulesets, rulesetDetails } = cachedRulesetData; + + const shouldUpdate = rulesetMap.size !== 0; + + rulesetDetails.forEach(rule => rulesetMap.set(rule.id, rule)); + + const listStatsTemplate = i18n$('perRulesetStats'); + + const initializeListEntry = (ruleset, listEntry) => { + const on = enabledRulesets.includes(ruleset.id); + dom.prop(qs$(listEntry, ':scope > .detailbar input'), 'checked', on); + if ( ruleset.homeURL ) { + dom.attr(qs$(listEntry, 'a.support'), 'href', ruleset.homeURL); + } + dom.cl.toggle(listEntry, 'isDefault', ruleset.id === 'default'); + const stats = rulesetStats(ruleset.id); + listEntry.title = listStatsTemplate + .replace('{{ruleCount}}', renderNumber(stats.ruleCount)) + .replace('{{filterCount}}', renderNumber(stats.filterCount)); + const fromAdmin = isAdminRuleset(ruleset.id); + dom.cl.toggle(listEntry, 'fromAdmin', fromAdmin); + const disabled = stats.ruleCount === 0 || fromAdmin; + dom.attr( + qs$(listEntry, '.input.checkbox input'), + 'disabled', + disabled ? '' : null + ); + return listEntry; + }; + + // Update already rendered DOM lists + if ( shouldUpdate ) { + for ( const listEntry of qsa$('#lists .listEntry[data-rulesetid]') ) { + const rulesetid = listEntry.dataset.rulesetid; + const ruleset = rulesetMap.get(rulesetid); + initializeListEntry(ruleset, listEntry); + } + updateNodes(); + renderRuleCounts(); + return; + } + + const createListEntry = (listDetails, depth) => { + if ( listDetails.lists === undefined ) { + return dom.clone('#templates .listEntry[data-role="leaf"]'); + } + if ( depth !== 0 ) { + return dom.clone('#templates .listEntry[data-role="node"]'); + } + return dom.clone('#templates .listEntry[data-role="rootnode"]'); + }; + + const createListEntries = (parentkey, listTree, depth = 0) => { + const listEntries = dom.clone('#templates .listEntries'); + const treeEntries = Object.entries(listTree); + if ( depth !== 0 ) { + const reEmojis = /\p{Emoji}+/gu; + treeEntries.sort((a ,b) => { + const ap = a[1].preferred === true; + const bp = b[1].preferred === true; + if ( ap !== bp ) { return ap ? -1 : 1; } + const as = (a[1].title || a[0]).replace(reEmojis, ''); + const bs = (b[1].title || b[0]).replace(reEmojis, ''); + return as.localeCompare(bs); + }); + } + for ( const [ listkey, listDetails ] of treeEntries ) { + const listEntry = createListEntry(listDetails, depth); + if ( listDetails.lists === undefined ) { + listEntry.dataset.rulesetid = listkey; + } else { + listEntry.dataset.nodeid = listkey; + dom.cl.toggle(listEntry, 'hideUnused', hideUnusedSet.has(listkey)); + } + qs$(listEntry, ':scope > .detailbar .listname').append( + i18n.patchUnicodeFlags(listDetails.name) + ); + if ( listDetails.lists !== undefined ) { + listEntry.append(createListEntries(listkey, listDetails.lists, depth+1)); + dom.cl.toggle(listEntry, 'expanded', true/*listIsExpanded(listkey)*/); + //updateListNode(listEntry); + } else { + initializeListEntry(listDetails, listEntry); + } + listEntries.append(listEntry); + } + return listEntries; + }; + + // Visually split the filter lists in groups + const groups = new Map([ + [ + 'default', + rulesetDetails.filter(ruleset => + ruleset.id === 'default' + ), + ], [ + 'annoyances', + rulesetDetails.filter(ruleset => + ruleset.group === 'annoyances' + ), + ], [ + 'misc', + rulesetDetails.filter(ruleset => + ruleset.id !== 'default' && + ruleset.group === undefined && + typeof ruleset.lang !== 'string' + ), + ], [ + 'regions', + rulesetDetails.filter(ruleset => + ruleset.group === 'regions' + ), + ], + ]); + + dom.cl.toggle(dom.body, 'hideUnused', mustHideUnusedLists('*')); + + // Build list tree + const listTree = {}; + const groupNames = new Map(); + for ( const [ groupKey, groupRulesets ] of groups ) { + let groupName = groupNames.get(groupKey); + if ( groupName === undefined ) { + groupName = i18n$('3pGroup' + groupKey.charAt(0).toUpperCase() + groupKey.slice(1)); + groupNames.set(groupKey, groupName); + } + const groupDetails = { + name: groupName, + lists: {}, + }; + listTree[groupKey] = groupDetails; + for ( const ruleset of groupRulesets ) { + if ( ruleset.parent !== undefined ) { + let lists = groupDetails.lists; + for ( const parent of ruleset.parent.split('|') ) { + if ( lists[parent] === undefined ) { + lists[parent] = { name: parent, lists: {} }; + } + lists = lists[parent].lists; + } + lists[ruleset.id] = ruleset; + } else { + groupDetails.lists[ruleset.id] = ruleset; + } + } + } + // Move lonely sublist to list level + const promoteLonelySublist = (parent, depth = 0) => { + if ( Boolean(parent.lists) === false ) { return parent; } + const childKeys = Object.keys(parent.lists); + for ( const childKey of childKeys ) { + const child = promoteLonelySublist(parent.lists[childKey], depth + 1); + if ( child === parent.lists[childKey] ) { continue; } + parent.lists[child.id] = child; + delete parent.lists[childKey]; + } + if ( depth === 0 ) { return parent; } + if ( childKeys.length > 1 ) { return parent; } + return parent.lists[childKeys[0]] + }; + for ( const key of Object.keys(listTree) ) { + promoteLonelySublist(listTree[key]); + } + const listEntries = createListEntries('root', listTree); + + updateNodes(listEntries); + + dom.clear('#lists'); + qs$('#lists').append(listEntries); + + renderRuleCounts(); +} + +/******************************************************************************/ + +// Collapsing of unused lists. + +function mustHideUnusedLists(which) { + const hideAll = hideUnusedSet.has('*'); + if ( which === '*' ) { return hideAll; } + return hideUnusedSet.has(which) !== hideAll; +} + +function toggleHideUnusedLists(which) { + const doesHideAll = hideUnusedSet.has('*'); + if ( which === '*' ) { + const mustHide = doesHideAll === false; + hideUnusedSet.clear(); + if ( mustHide ) { + hideUnusedSet.add(which); + } + dom.cl.toggle('#lists', 'hideUnused', mustHide); + dom.cl.toggle('.listEntry[data-nodeid]', 'hideUnused', mustHide); + } else { + const doesHide = hideUnusedSet.has(which); + if ( doesHide ) { + hideUnusedSet.delete(which); + } else { + hideUnusedSet.add(which); + } + const mustHide = doesHide === doesHideAll; + const groupSelector = `.listEntry[data-nodeid="${which}"]`; + dom.cl.toggle(groupSelector, 'hideUnused', mustHide); + } + + localWrite('hideUnusedFilterLists', Array.from(hideUnusedSet)); +} + +dom.on('#lists', 'click', '.listEntry[data-nodeid] > .detailbar, .listExpander', ev => { + toggleHideUnusedLists( + dom.attr(ev.target.closest('[data-nodeid]'), 'data-nodeid') + ); +}); + +// Initialize from saved state. +localRead('hideUnusedFilterLists').then(value => { + if ( Array.isArray(value) === false ) { return; } + hideUnusedSet = new Set(value); + for ( const listEntry of qsa$('[data-nodeid]') ) { + dom.cl.toggle(listEntry, 'hideUnused', + hideUnusedSet.has(listEntry.dataset.nodeid) + ); + } +}); + +/******************************************************************************/ + +const searchFilterLists = ( ) => { + const pattern = dom.prop('.searchfield input', 'value') || ''; + dom.cl.toggle('#lists', 'searchMode', pattern !== ''); + if ( pattern === '' ) { return; } + const re = new RegExp(pattern.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'), 'i'); + for ( const listEntry of qsa$('#lists [data-role="leaf"]') ) { + const rulesetid = listEntry.dataset.rulesetid; + const rulesetDetails = rulesetMap.get(rulesetid); + if ( rulesetDetails === undefined ) { continue; } + let haystack = perListHaystack.get(rulesetDetails); + if ( haystack === undefined ) { + haystack = [ + rulesetDetails.name, + listEntry.dataset.nodeid, + rulesetDetails.tags || '', + ].join(' ').trim(); + perListHaystack.set(rulesetDetails, haystack); + } + dom.cl.toggle(listEntry, 'searchMatch', re.test(haystack)); + } + for ( const listEntry of qsa$('#lists .listEntry:not([data-role="leaf"])') ) { + dom.cl.toggle(listEntry, 'searchMatch', + qs$(listEntry, '.listEntries .listEntry.searchMatch') !== null + ); + } +}; + +const perListHaystack = new WeakMap(); + +dom.on('.searchfield input', 'input', searchFilterLists); + +/******************************************************************************/ + +async function applyEnabledRulesets() { + const enabledRulesets = []; + for ( const liEntry of qsa$('#lists .listEntry[data-role="leaf"][data-rulesetid]') ) { + const checked = qs$(liEntry, 'input[type="checkbox"]:checked') !== null; + if ( checked === false ) { continue; } + const { rulesetid } = liEntry.dataset; + if ( dom.cl.has(liEntry, 'fromAdmin') ) { continue; } + enabledRulesets.push(rulesetid); + } + + await sendMessage({ + what: 'applyRulesets', + enabledRulesets, + }); +} + +dom.on('#lists', 'change', '.listEntry input[type="checkbox"]', ev => { + const input = ev.target; + const listEntry = input.closest('.listEntry'); + if ( listEntry === null ) { return; } + if ( listEntry.dataset.nodeid !== undefined ) { + let checkAll = input.checked || + dom.cl.has(qs$(listEntry, ':scope > .detailbar .checkbox'), 'partial'); + for ( const input of qsa$(listEntry, '.listEntries input') ) { + input.checked = checkAll; + } + } + renderRuleCounts(); + updateNodes(); + applyEnabledRulesets(); +}); diff --git a/platform/mv3/extension/js/ruleset-manager.js b/platform/mv3/extension/js/ruleset-manager.js index d19a9f28e9d65..f7d811e7d87bd 100644 --- a/platform/mv3/extension/js/ruleset-manager.js +++ b/platform/mv3/extension/js/ruleset-manager.js @@ -101,9 +101,14 @@ async function pruneInvalidRegexRules(realm, rulesIn) { toCheck.push(true); continue; } + if ( pruneInvalidRegexRules.invalidRegexes.has(regex) ) { + toCheck.push(false); + continue; + } toCheck.push( dnr.isRegexSupported({ regex, isCaseSensitive }).then(result => { if ( result.isSupported ) { return true; } + pruneInvalidRegexRules.invalidRegexes.add(regex); rejectedRegexRules.push(`\t${regex} ${result.reason}`); return false; }) @@ -122,6 +127,7 @@ async function pruneInvalidRegexRules(realm, rulesIn) { return rulesIn.filter((v, i) => isValid[i]); } +pruneInvalidRegexRules.invalidRegexes = new Set(); /******************************************************************************/ diff --git a/platform/mv3/extension/js/settings.js b/platform/mv3/extension/js/settings.js index a970430f33118..81f55d03cc416 100644 --- a/platform/mv3/extension/js/settings.js +++ b/platform/mv3/extension/js/settings.js @@ -19,201 +19,21 @@ Home: https://github.com/gorhill/uBlock */ -import { browser, localRead, localWrite, sendMessage } from './ext.js'; -import { dom, qs$, qsa$ } from './dom.js'; -import { i18n, i18n$ } from './i18n.js'; +import { browser, sendMessage } from './ext.js'; +import { dom, qs$ } from './dom.js'; import punycode from './punycode.js'; +import { renderFilterLists } from './filter-lists.js'; /******************************************************************************/ -const rulesetMap = new Map(); let cachedRulesetData = {}; -let hideUnusedSet = new Set([ 'regions' ]); /******************************************************************************/ -function renderNumber(value) { - return value.toLocaleString(); -} - function hashFromIterable(iter) { return Array.from(iter).sort().join('\n'); } -function isAdminRuleset(listkey) { - const { adminRulesets = [] } = cachedRulesetData; - for ( const id of adminRulesets ) { - const pos = id.indexOf(listkey); - if ( pos === 0 ) { return true; } - if ( pos !== 1 ) { continue; } - const c = id.charAt(0); - if ( c === '+' || c === '-' ) { return true; } - } - return false; -} - -/******************************************************************************/ - -function rulesetStats(rulesetId) { - const hasOmnipotence = cachedRulesetData.defaultFilteringMode > 1; - const rulesetDetails = rulesetMap.get(rulesetId); - if ( rulesetDetails === undefined ) { return; } - const { rules, filters } = rulesetDetails; - let ruleCount = rules.plain + rules.regex; - if ( hasOmnipotence ) { - ruleCount += rules.removeparam + rules.redirect + rules.modifyHeaders; - } - const filterCount = filters.accepted; - return { ruleCount, filterCount }; -} - -/******************************************************************************/ - -function renderFilterLists() { - const { enabledRulesets, rulesetDetails } = cachedRulesetData; - const listGroupTemplate = qs$('#templates .groupEntry'); - const listEntryTemplate = qs$('#templates .listEntry'); - const listStatsTemplate = i18n$('perRulesetStats'); - const groupNames = new Map([ [ 'user', '' ] ]); - - const liFromListEntry = function(ruleset, li, hideUnused) { - if ( !li ) { - li = dom.clone(listEntryTemplate); - } - const on = enabledRulesets.includes(ruleset.id); - dom.cl.toggle(li, 'checked', on); - dom.cl.toggle(li, 'unused', hideUnused && !on); - qs$(li, 'input[type="checkbox"]').checked = on; - if ( dom.attr(li, 'data-listkey') !== ruleset.id ) { - dom.attr(li, 'data-listkey', ruleset.id); - qs$(li, '.listname').append(i18n.patchUnicodeFlags(ruleset.name)); - dom.cl.remove(li, 'toRemove'); - if ( ruleset.homeURL ) { - dom.cl.add(li, 'support'); - dom.attr(qs$(li, 'a.support'), 'href', ruleset.homeURL); - } else { - dom.cl.remove(li, 'support'); - } - if ( ruleset.instructionURL ) { - dom.cl.add(li, 'mustread'); - dom.attr(qs$(li, 'a.mustread'), 'href', ruleset.instructionURL); - } else { - dom.cl.remove(li, 'mustread'); - } - dom.cl.toggle(li, 'isDefault', ruleset.id === 'default'); - } - const stats = rulesetStats(ruleset.id); - li.title = listStatsTemplate - .replace('{{ruleCount}}', renderNumber(stats.ruleCount)) - .replace('{{filterCount}}', renderNumber(stats.filterCount)); - const fromAdmin = isAdminRuleset(ruleset.id); - dom.cl.toggle(li, 'fromAdmin', fromAdmin); - const disabled = stats.ruleCount === 0 || fromAdmin; - dom.attr( - qs$(li, '.input.checkbox input'), - 'disabled', - disabled ? '' : null - ); - dom.cl.remove(li, 'discard'); - return li; - }; - - const listEntryCountFromGroup = function(groupRulesets) { - if ( Array.isArray(groupRulesets) === false ) { return ''; } - let count = 0, - total = 0; - for ( const ruleset of groupRulesets ) { - if ( enabledRulesets.includes(ruleset.id) ) { - count += 1; - } - total += 1; - } - return total !== 0 ? - `(${count.toLocaleString()}/${total.toLocaleString()})` : - ''; - }; - - const liFromListGroup = function(groupKey, groupRulesets) { - let liGroup = qs$(`#lists > .groupEntry[data-groupkey="${groupKey}"]`); - if ( liGroup === null ) { - liGroup = dom.clone(listGroupTemplate); - let groupName = groupNames.get(groupKey); - if ( groupName === undefined ) { - groupName = i18n$('3pGroup' + groupKey.charAt(0).toUpperCase() + groupKey.slice(1)); - groupNames.set(groupKey, groupName); - } - if ( groupName !== '' ) { - dom.text(qs$(liGroup, '.geName'), groupName); - } - } - if ( qs$(liGroup, '.geName:empty') === null ) { - dom.text( - qs$(liGroup, '.geCount'), - listEntryCountFromGroup(groupRulesets) - ); - } - const hideUnused = mustHideUnusedLists(groupKey); - dom.cl.toggle(liGroup, 'hideUnused', hideUnused); - const ulGroup = qs$(liGroup, '.listEntries'); - if ( !groupRulesets ) { return liGroup; } - groupRulesets.sort(function(a, b) { - return (a.name || '').localeCompare(b.name || ''); - }); - for ( let i = 0; i < groupRulesets.length; i++ ) { - const liEntry = liFromListEntry( - groupRulesets[i], - ulGroup.children[i], - hideUnused - ); - if ( liEntry.parentElement === null ) { - ulGroup.appendChild(liEntry); - } - } - return liGroup; - }; - - // Visually split the filter lists in groups - const ulLists = qs$('#lists'); - const groups = new Map([ - [ - 'default', - rulesetDetails.filter(ruleset => - ruleset.id === 'default' - ), - ], - [ - 'annoyances', - rulesetDetails.filter(ruleset => - ruleset.group === 'annoyances' - ), - ], - [ - 'misc', - rulesetDetails.filter(ruleset => - ruleset.id !== 'default' && - ruleset.group === undefined && - typeof ruleset.lang !== 'string' - ), - ], - [ - 'regions', - rulesetDetails.filter(ruleset => - typeof ruleset.lang === 'string' - ), - ], - ]); - - dom.cl.toggle(dom.body, 'hideUnused', mustHideUnusedLists('*')); - - for ( const [ groupKey, groupRulesets ] of groups ) { - const liGroup = liFromListGroup(groupKey, groupRulesets); - dom.attr(liGroup, 'data-groupkey', groupKey); - if ( liGroup.parentElement === null ) { - ulLists.appendChild(liGroup); - } - } -} - /******************************************************************************/ function renderWidgets() { @@ -235,27 +55,6 @@ function renderWidgets() { dom.attr(input, 'disabled', ''); } } - - // Compute total counts - let rulesetCount = 0; - let filterCount = 0; - let ruleCount = 0; - for ( const liEntry of qsa$('#lists .listEntry[data-listkey]') ) { - if ( qs$(liEntry, 'input[type="checkbox"]:checked') === null ) { continue; } - rulesetCount += 1; - const stats = rulesetStats(liEntry.dataset.listkey); - if ( stats === undefined ) { continue; } - ruleCount += stats.ruleCount; - filterCount += stats.filterCount; - } - dom.text('#listsOfBlockedHostsPrompt', i18n$('perRulesetStats') - .replace('{{ruleCount}}', ruleCount.toLocaleString()) - .replace('{{filterCount}}', filterCount.toLocaleString()) - ); - - dom.cl.toggle(dom.body, 'noMoreRuleset', - rulesetCount === cachedRulesetData.maxNumberOfEnabledRulesets - ); } /******************************************************************************/ @@ -300,7 +99,7 @@ async function onFilteringModeChange(ev) { default: break; } - renderFilterLists(); + renderFilterLists(cachedRulesetData); renderWidgets(); } @@ -367,89 +166,6 @@ self.addEventListener('beforeunload', changeTrustedSites); /******************************************************************************/ -async function applyEnabledRulesets() { - const enabledRulesets = []; - for ( const liEntry of qsa$('#lists .listEntry[data-listkey]') ) { - const checked = qs$(liEntry, 'input[type="checkbox"]:checked') !== null; - dom.cl.toggle(liEntry, 'checked', checked); - if ( checked === false ) { continue; } - const { listkey } = liEntry.dataset; - if ( isAdminRuleset(listkey) ) { continue; } - enabledRulesets.push(listkey); - } - - await sendMessage({ - what: 'applyRulesets', - enabledRulesets, - }); - - renderWidgets(); -} - -dom.on('#lists', 'change', '.listEntry input[type="checkbox"]', ( ) => { - applyEnabledRulesets(); -}); - -/******************************************************************************/ - -// Collapsing of unused lists. - -function mustHideUnusedLists(which) { - const hideAll = hideUnusedSet.has('*'); - if ( which === '*' ) { return hideAll; } - return hideUnusedSet.has(which) !== hideAll; -} - -function toggleHideUnusedLists(which) { - const doesHideAll = hideUnusedSet.has('*'); - let groupSelector; - let mustHide; - if ( which === '*' ) { - mustHide = doesHideAll === false; - groupSelector = ''; - hideUnusedSet.clear(); - if ( mustHide ) { - hideUnusedSet.add(which); - } - dom.cl.toggle(dom.body, 'hideUnused', mustHide); - dom.cl.toggle('.groupEntry[data-groupkey]', 'hideUnused', mustHide); - } else { - const doesHide = hideUnusedSet.has(which); - if ( doesHide ) { - hideUnusedSet.delete(which); - } else { - hideUnusedSet.add(which); - } - mustHide = doesHide === doesHideAll; - groupSelector = `.groupEntry[data-groupkey="${which}"]`; - dom.cl.toggle(groupSelector, 'hideUnused', mustHide); - } - - for ( const elem of qsa$(`#lists ${groupSelector} .listEntry[data-listkey] input[type="checkbox"]:not(:checked)`) ) { - dom.cl.toggle( - elem.closest('.listEntry[data-listkey]'), - 'unused', - mustHide - ); - } - - localWrite('hideUnusedFilterLists', Array.from(hideUnusedSet)); -} - -dom.on('#lists', 'click', '.groupEntry[data-groupkey] > .geDetails', ev => { - toggleHideUnusedLists( - dom.attr(ev.target.closest('[data-groupkey]'), 'data-groupkey') - ); -}); - -// Initialize from saved state. -localRead('hideUnusedFilterLists').then(value => { - if ( Array.isArray(value) === false ) { return; } - hideUnusedSet = new Set(value); -}); - -/******************************************************************************/ - function listen() { const bc = new self.BroadcastChannel('uBOL'); bc.onmessage = listen.onmessage; @@ -512,7 +228,7 @@ listen.onmessage = ev => { } if ( render === false ) { return; } - renderFilterLists(); + renderFilterLists(cachedRulesetData); renderWidgets(); }; @@ -523,10 +239,8 @@ sendMessage({ }).then(data => { if ( !data ) { return; } cachedRulesetData = data; - rulesetMap.clear(); - cachedRulesetData.rulesetDetails.forEach(rule => rulesetMap.set(rule.id, rule)); try { - renderFilterLists(); + renderFilterLists(cachedRulesetData); renderWidgets(); } catch(ex) { } diff --git a/platform/mv3/make-rulesets.js b/platform/mv3/make-rulesets.js index 09719101055b4..501af6f706aaf 100644 --- a/platform/mv3/make-rulesets.js +++ b/platform/mv3/make-rulesets.js @@ -1068,8 +1068,10 @@ async function rulesetFromURLs(assetDetails) { id: assetDetails.id, name: assetDetails.name, group: assetDetails.group, + parent: assetDetails.parent, enabled: assetDetails.enabled, lang: assetDetails.lang, + tags: assetDetails.tags, homeURL: assetDetails.homeURL, filters: { total: results.network.filterCount, @@ -1121,7 +1123,7 @@ async function main() { // Get assets.json content const assets = await fs.readFile( - `./assets.json`, + `./assets.dev.json`, { encoding: 'utf8' } ).then(text => JSON.parse(text) @@ -1155,55 +1157,6 @@ async function main() { ], }); - // Regional rulesets - const excludedLists = [ - 'ara-0', - 'EST-0', - ]; - // Merge lists which have same target languages - const langToListsMap = new Map(); - for ( const [ id, asset ] of Object.entries(assets) ) { - if ( asset.content !== 'filters' ) { continue; } - if ( asset.off !== true ) { continue; } - if ( typeof asset.lang !== 'string' ) { continue; } - if ( excludedLists.includes(id) ) { continue; } - let ids = langToListsMap.get(asset.lang); - if ( ids === undefined ) { - langToListsMap.set(asset.lang, ids = []); - } - ids.push(id); - } - for ( const ids of langToListsMap.values() ) { - const urls = []; - for ( const id of ids ) { - const asset = assets[id]; - const contentURL = Array.isArray(asset.contentURL) - ? asset.contentURL[0] - : asset.contentURL; - urls.push(contentURL); - } - const id = ids[0]; - const asset = assets[id]; - await rulesetFromURLs({ - id: id.toLowerCase(), - lang: asset.lang, - name: asset.title, - enabled: false, - urls, - homeURL: asset.supportURL, - }); - } - - await rulesetFromURLs({ - id: 'est-0', - group: 'regions', - lang: 'et', - name: '🇪🇪ee: Eesti saitidele kohandatud filter', - enabled: false, - urls: [ 'https://ubol-et.adblock.ee/list.txt' ], - homeURL: 'https://github.com/sander85/uBOL-et', - }); - // Handpicked rulesets from assets.json const handpicked = [ 'block-lan', @@ -1290,6 +1243,62 @@ async function main() { homeURL: 'https://github.com/StevenBlack/hosts#readme', }); + // Regional rulesets + const excludedLists = [ + 'ara-0', + 'EST-0', + ]; + // Merge lists which have same target languages + const langToListsMap = new Map(); + for ( const [ id, asset ] of Object.entries(assets) ) { + if ( asset.content !== 'filters' ) { continue; } + if ( asset.off !== true ) { continue; } + if ( asset.group !== 'regions' ) { continue; } + if ( excludedLists.includes(id) ) { continue; } + // Not all "regions" lists have a set language + const bundleId = asset.lang || + createHash('sha256').update(randomBytes(16)).digest('hex').slice(0,16); + let ids = langToListsMap.get(bundleId); + if ( ids === undefined ) { + langToListsMap.set(bundleId, ids = []); + } + ids.push(id); + } + for ( const ids of langToListsMap.values() ) { + const urls = []; + for ( const id of ids ) { + const asset = assets[id]; + const contentURL = Array.isArray(asset.contentURL) + ? asset.contentURL[0] + : asset.contentURL; + urls.push(contentURL); + } + const id = ids[0]; + const asset = assets[id]; + const rulesetDetails = { + id: id.toLowerCase(), + group: 'regions', + parent: asset.parent, + lang: asset.lang, + name: asset.title, + tags: asset.tags, + enabled: false, + urls, + homeURL: asset.supportURL, + }; + await rulesetFromURLs(rulesetDetails); + } + + await rulesetFromURLs({ + id: 'est-0', + group: 'regions', + lang: 'et', + name: '🇪🇪ee: Eesti saitidele kohandatud filter', + enabled: false, + urls: [ 'https://ubol-et.adblock.ee/list.txt' ], + homeURL: 'https://github.com/sander85/uBOL-et', + }); + writeFile( `${rulesetDir}/ruleset-details.json`, `${JSON.stringify(rulesetDetails, null, 1)}\n` diff --git a/src/js/static-dnr-filtering.js b/src/js/static-dnr-filtering.js index 2e87695bf593f..d3e7732a6771f 100644 --- a/src/js/static-dnr-filtering.js +++ b/src/js/static-dnr-filtering.js @@ -440,9 +440,9 @@ function finalizeRuleset(context, network) { } }; mergeRules(rulesetMap, 'resourceTypes'); + mergeRules(rulesetMap, 'removeParams'); mergeRules(rulesetMap, 'initiatorDomains'); mergeRules(rulesetMap, 'requestDomains'); - mergeRules(rulesetMap, 'removeParams'); mergeRules(rulesetMap, 'responseHeaders'); // Patch id diff --git a/tools/make-mv3.sh b/tools/make-mv3.sh index 3bb3c9ce4ae67..58a0505314968 100755 --- a/tools/make-mv3.sh +++ b/tools/make-mv3.sh @@ -108,7 +108,7 @@ if [ "$QUICK" != "yes" ]; then cp platform/mv3/*.js "$TMPDIR"/ cp platform/mv3/*.mjs "$TMPDIR"/ cp platform/mv3/extension/js/utils.js "$TMPDIR"/js/ - cp "$UBO_DIR"/assets/assets.json "$TMPDIR"/ + cp "$UBO_DIR"/assets/assets.dev.json "$TMPDIR"/ cp "$UBO_DIR"/assets/resources/*.js "$TMPDIR"/ cp -R platform/mv3/scriptlets "$TMPDIR"/ mkdir -p "$TMPDIR"/web_accessible_resources From d7c6b4199223e8e9015739d1a5400880271e1798 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Mon, 18 Nov 2024 10:16:01 -0500 Subject: [PATCH 0459/1099] [mv3] Code review re. re-worked dashboard Related commit: https://github.com/gorhill/uBlock/commit/ae4754415c9a01f18d3219972ef35b959536891f Fine-tuned visuals; fixed sublist quirks related to admin-selected lists. --- .../mv3/extension/_locales/en/messages.json | 4 ++ .../mv3/extension/css/dashboard-common.css | 1 + platform/mv3/extension/css/dashboard.css | 5 +- platform/mv3/extension/css/settings.css | 26 +++---- platform/mv3/extension/dashboard.html | 2 +- platform/mv3/extension/js/filter-lists.js | 72 +++++++++++-------- 6 files changed, 64 insertions(+), 46 deletions(-) diff --git a/platform/mv3/extension/_locales/en/messages.json b/platform/mv3/extension/_locales/en/messages.json index f8c4b376c59a3..60cc95401447c 100644 --- a/platform/mv3/extension/_locales/en/messages.json +++ b/platform/mv3/extension/_locales/en/messages.json @@ -230,5 +230,9 @@ "showBlockedCountLabel": { "message": "Show the number of blocked requests on the toolbar icon", "description": "Label for a checkbox in the options page" + }, + "findListsPlaceholder": { + "message": "Find lists", + "description": "Placeholder for the input field used to find lists" } } diff --git a/platform/mv3/extension/css/dashboard-common.css b/platform/mv3/extension/css/dashboard-common.css index 8621d08704053..7abaeb5664dd0 100644 --- a/platform/mv3/extension/css/dashboard-common.css +++ b/platform/mv3/extension/css/dashboard-common.css @@ -3,6 +3,7 @@ body { box-sizing: border-box; display: flex; flex-direction: column; + max-height: 100vh; padding: 0 var(--default-gap-xxsmall); } body > * { diff --git a/platform/mv3/extension/css/dashboard.css b/platform/mv3/extension/css/dashboard.css index 454a799874fee..b7aa5adeee110 100644 --- a/platform/mv3/extension/css/dashboard.css +++ b/platform/mv3/extension/css/dashboard.css @@ -7,9 +7,6 @@ flex-wrap: wrap; overflow-x: hidden; padding: 0; - position: sticky; - top: 0; - z-index: 100; } .tabButton { background-color: transparent; @@ -43,6 +40,8 @@ body[data-pane="about"] #dashboard-nav .tabButton[data-pane="about"] { body > section { display: none; + overflow: auto; + padding-bottom: 8rem; } body[data-pane="settings"] > section[data-pane="settings"], body[data-pane="rulesets"] > section[data-pane="rulesets"], diff --git a/platform/mv3/extension/css/settings.css b/platform/mv3/extension/css/settings.css index da6e13a1aaf2f..fb5ba91517ab1 100644 --- a/platform/mv3/extension/css/settings.css +++ b/platform/mv3/extension/css/settings.css @@ -1,12 +1,3 @@ -@keyframes spin { - 0% { transform: rotate(0deg); } - 100% { transform: rotate(360deg); } - } -legend { - color: var(--ink-3); - font-size: var(--font-size-smaller); - padding: var(--default-gap-xxsmall); - } body .firstRun { display: none; } @@ -78,8 +69,19 @@ h3[data-i18n="filteringMode0Name"]::first-letter { width: 100%; } -#lists { - padding-block-end: 8rem; +section[data-pane="rulesets"] > div:first-of-type { + background-color: var(--surface-1); + flex-shrink: 0; + padding: 1em 0; + position: sticky; + top: 0; + z-index: 10; + } +section[data-pane="rulesets"] > div:first-of-type > p:first-of-type { + margin-top: 0; + } +section[data-pane="rulesets"] > div:first-of-type > p:last-of-type { + margin-bottom: 0; } .listEntry { display: flex; @@ -112,7 +114,7 @@ h3[data-i18n="filteringMode0Name"]::first-letter { .listEntry.hideUnused > .listEntries > .listEntry:not(.isDefault):has(> .detailbar input:not(:checked)) { display: none; } -.listEntry.fromAdmin:has(input[disabled]:not(:checked)) { +.listEntry.fromAdmin:has(> .detailbar input[disabled]:not(:checked)) { display: none; } .listEntry > * { diff --git a/platform/mv3/extension/dashboard.html b/platform/mv3/extension/dashboard.html index 73dd72ac0fece..2169031eb4b1c 100644 --- a/platform/mv3/extension/dashboard.html +++ b/platform/mv3/extension/dashboard.html @@ -101,8 +101,8 @@

+

-
search
diff --git a/platform/mv3/extension/js/filter-lists.js b/platform/mv3/extension/js/filter-lists.js index a52dcb433c846..c48f3e357e045 100644 --- a/platform/mv3/extension/js/filter-lists.js +++ b/platform/mv3/extension/js/filter-lists.js @@ -36,7 +36,9 @@ function renderNumber(value) { return value.toLocaleString(); } -function renderRuleCounts() { +/******************************************************************************/ + +function renderTotalRuleCounts() { let rulesetCount = 0; let filterCount = 0; let ruleCount = 0; @@ -49,8 +51,8 @@ function renderRuleCounts() { filterCount += stats.filterCount; } dom.text('#listsOfBlockedHostsPrompt', i18n$('perRulesetStats') - .replace('{{ruleCount}}', ruleCount.toLocaleString()) - .replace('{{filterCount}}', filterCount.toLocaleString()) + .replace('{{ruleCount}}', renderNumber(ruleCount)) + .replace('{{filterCount}}', renderNumber(filterCount)) ); dom.cl.toggle(dom.body, 'noMoreRuleset', @@ -62,16 +64,26 @@ function renderRuleCounts() { function updateNodes(listEntries) { listEntries = listEntries || qs$('#lists'); + const sublistSelector = '.listEntry[data-rulesetid] > .detailbar input'; + const checkedSublistSelector = `${sublistSelector}:checked`; + const adminSublistSelector = '.listEntry.fromAdmin[data-rulesetid] > .detailbar input'; for ( const listEntry of qsa$(listEntries, '.listEntry[data-nodeid]') ) { - const totalCount = qsa$(listEntry, '.listEntry[data-rulesetid] input').length; - const checkedCount = qsa$(listEntry, '.listEntry[data-rulesetid] input:checked').length; - dom.text(qs$(listEntry, '.detailbar .count'), `${checkedCount}/${totalCount}`); - const checkbox = qs$(listEntry, ':scope > .detailbar .checkbox'); - if ( checkbox === null ) { continue; } - dom.prop(qs$(checkbox, 'input'), 'checked', checkedCount !== 0); - dom.cl.toggle(checkbox, 'partial', + const countElem = qs$(listEntry, ':scope > .detailbar .count'); + if ( countElem === null ) { continue; } + const totalCount = qsa$(listEntry, sublistSelector).length; + const checkedCount = qsa$(listEntry, checkedSublistSelector).length; + dom.text(countElem, `${checkedCount}/${totalCount}`); + const checkboxElem = qs$(listEntry, ':scope > .detailbar .checkbox'); + if ( checkboxElem === null ) { continue; } + const checkboxInput = qs$(checkboxElem, 'input'); + dom.prop(checkboxInput, 'checked', checkedCount !== 0); + dom.cl.toggle(checkboxElem, 'partial', checkedCount !== 0 && checkedCount !== totalCount ); + const adminCount = qsa$(listEntry, adminSublistSelector).length; + const fromAdmin = adminCount === totalCount; + dom.cl.toggle(listEntry, 'fromAdmin', fromAdmin); + dom.attr(checkboxInput, 'disabled', fromAdmin ? '' : null); } } @@ -124,6 +136,7 @@ export function renderFilterLists(rulesetData) { } dom.cl.toggle(listEntry, 'isDefault', ruleset.id === 'default'); const stats = rulesetStats(ruleset.id); + if ( stats === undefined ) { return; } listEntry.title = listStatsTemplate .replace('{{ruleCount}}', renderNumber(stats.ruleCount)) .replace('{{filterCount}}', renderNumber(stats.filterCount)); @@ -135,7 +148,6 @@ export function renderFilterLists(rulesetData) { 'disabled', disabled ? '' : null ); - return listEntry; }; // Update already rendered DOM lists @@ -146,7 +158,7 @@ export function renderFilterLists(rulesetData) { initializeListEntry(ruleset, listEntry); } updateNodes(); - renderRuleCounts(); + renderTotalRuleCounts(); return; } @@ -229,20 +241,17 @@ export function renderFilterLists(rulesetData) { // Build list tree const listTree = {}; const groupNames = new Map(); - for ( const [ groupKey, groupRulesets ] of groups ) { - let groupName = groupNames.get(groupKey); - if ( groupName === undefined ) { - groupName = i18n$('3pGroup' + groupKey.charAt(0).toUpperCase() + groupKey.slice(1)); - groupNames.set(groupKey, groupName); + for ( const [ nodeid, rulesets ] of groups ) { + let name = groupNames.get(nodeid); + if ( name === undefined ) { + name = i18n$(`3pGroup${nodeid.charAt(0).toUpperCase()}${nodeid.slice(1)}`); + groupNames.set(nodeid, name); } - const groupDetails = { - name: groupName, - lists: {}, - }; - listTree[groupKey] = groupDetails; - for ( const ruleset of groupRulesets ) { + const details = { name, lists: {} }; + listTree[nodeid] = details; + for ( const ruleset of rulesets ) { if ( ruleset.parent !== undefined ) { - let lists = groupDetails.lists; + let lists = details.lists; for ( const parent of ruleset.parent.split('|') ) { if ( lists[parent] === undefined ) { lists[parent] = { name: parent, lists: {} }; @@ -251,11 +260,11 @@ export function renderFilterLists(rulesetData) { } lists[ruleset.id] = ruleset; } else { - groupDetails.lists[ruleset.id] = ruleset; + details.lists[ruleset.id] = ruleset; } } } - // Move lonely sublist to list level + // Replace composite list with only one sublist with sublist itself const promoteLonelySublist = (parent, depth = 0) => { if ( Boolean(parent.lists) === false ) { return parent; } const childKeys = Object.keys(parent.lists); @@ -279,7 +288,7 @@ export function renderFilterLists(rulesetData) { dom.clear('#lists'); qs$('#lists').append(listEntries); - renderRuleCounts(); + renderTotalRuleCounts(); } /******************************************************************************/ @@ -337,11 +346,12 @@ localRead('hideUnusedFilterLists').then(value => { /******************************************************************************/ const searchFilterLists = ( ) => { - const pattern = dom.prop('.searchfield input', 'value') || ''; + const pattern = dom.prop('#findInLists', 'value') || ''; dom.cl.toggle('#lists', 'searchMode', pattern !== ''); if ( pattern === '' ) { return; } const re = new RegExp(pattern.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'), 'i'); for ( const listEntry of qsa$('#lists [data-role="leaf"]') ) { + if ( dom.cl.has(listEntry, 'fromAdmin') ) { continue; } const rulesetid = listEntry.dataset.rulesetid; const rulesetDetails = rulesetMap.get(rulesetid); if ( rulesetDetails === undefined ) { continue; } @@ -365,7 +375,7 @@ const searchFilterLists = ( ) => { const perListHaystack = new WeakMap(); -dom.on('.searchfield input', 'input', searchFilterLists); +dom.on('#findInLists', 'input', searchFilterLists); /******************************************************************************/ @@ -379,6 +389,8 @@ async function applyEnabledRulesets() { enabledRulesets.push(rulesetid); } + if ( enabledRulesets.length === 0 ) { return; } + await sendMessage({ what: 'applyRulesets', enabledRulesets, @@ -396,7 +408,7 @@ dom.on('#lists', 'change', '.listEntry input[type="checkbox"]', ev => { input.checked = checkAll; } } - renderRuleCounts(); updateNodes(); + renderTotalRuleCounts(); applyEnabledRulesets(); }); From c95b08d76015380ec419105598bcec767ac43b74 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Mon, 18 Nov 2024 10:24:08 -0500 Subject: [PATCH 0460/1099] Import translation work from https://crowdin.com/project/ublock --- platform/mv3/description/webstore.ja.txt | 8 ++++---- platform/mv3/extension/_locales/ja/messages.json | 2 +- src/_locales/zh_TW/messages.json | 8 ++++---- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/platform/mv3/description/webstore.ja.txt b/platform/mv3/description/webstore.ja.txt index 4271d4a7d3562..7a3068b6be7f7 100644 --- a/platform/mv3/description/webstore.ja.txt +++ b/platform/mv3/description/webstore.ja.txt @@ -1,8 +1,8 @@ uBO Lite (uBOL) は権限を必要としない MV3 ベースのコンテンツブロッカーです。 -デフォルトのルールセットは以下の通り。uBlock Origin のデフォルトフィルターセットと同じです。 +デフォルトのルールセットは以下の通りです。uBlock Origin のデフォルトフィルターセットと同じです。 -- uBlock Origin の内製フィルターリスト +- uBlock Origin の内蔵フィルターリスト - EasyList - EasyPrivacy - Peter Lowe’s Ad and tracking server list @@ -11,13 +11,13 @@ uBO Lite (uBOL) は権限を必要としない MV3 ベースのコンテンツ uBOL は完全に宣言的です。つまり、フィルタリングを行うための恒久的な uBOL プロセスは必要なく、CSS/JS インジェクション ベースのコンテンツフィルタリングは拡張機能ではなくブラウザによって、確実に実行されます。 これは uBOL がコンテンツブロッキングの際に CPU、メモリを消費しないことを意味します。uBOL のサービス ワーカーは ポップアップ パネルや設定ページでのみ必要とされます。 -uBOL はインストール時に広範な「データの読み取りと変更」の権限を要求しません。したがって uBlock Origin やその他の、インストール時に広範な「データの読み取りと変更」の権限を要求するコンテンツ ブロッカーに比べて、行えることが制限されています。 +uBOL はインストール時に広範な「データの読み取りと変更」の権限を要求しません。したがって uBlock Origin やその他の、インストール時に広範な「データの読み取りと変更」の権限を要求するコンテンツブロッカーに比べて、行えることが制限されています。 しかし、ユーザーの選んだ特定のサイトに対する拡張権限を「明示的に」付与すれば、そのサイト上で整形フィルターやスクリプトレットの挿入を用いた優れたフィルタリングを行うことができます。 特定のサイトで拡張された権限を付与するには、ポップアップ パネルを開いて、「最適」や「完全」のようなより高いフィルタリングモードを選択します。 -ブラウザは、現在のサイトで拡張機能によってリクエストされた追加の権限を付与することによってもたらされる影響について警告します。承認または拒否することができます。 +ブラウザーは、現在のサイトで拡張機能によってリクエストされた追加の権限を付与することによってもたらされる影響について警告します。承認または拒否することができます。 現在のサイトでの uBOL のリクエストを承認すると、現在のサイトにより良いフィルターを適用できるようになります。 diff --git a/platform/mv3/extension/_locales/ja/messages.json b/platform/mv3/extension/_locales/ja/messages.json index 0bbb9a0e36c06..a45ed763e734e 100644 --- a/platform/mv3/extension/_locales/ja/messages.json +++ b/platform/mv3/extension/_locales/ja/messages.json @@ -172,7 +172,7 @@ "description": "The header text for the welcome message section" }, "firstRunDescription": { - "message": "uBO Lite のインストールが完了しました。すべてのサイトに適用するデフォルトのフィルタリングモードを選んでください。\n\nデフォルトでは、データの読み書きの権限が必要ない基本モードが選択されています。 uBO Lite を信用してもらえるなら、データの読み書きや変更の権限を許可してもらえればすべてのサイトに対してより詳細なフィルタリングを有効化できます。", + "message": "uBO Lite のインストールが完了しました。すべてのサイトに適用するデフォルトのフィルタリングモードを選んでください。\n\nデフォルトでは、データの読み書きの権限が必要ない基本モードが選択されています。 uBO Lite を信用する場合は、データの読み書きや変更の権限を許可して、すべてのサイトに対してより高度なフィルタリングを有効化できます。", "description": "Descriptive text shown at first install time only " }, "defaultFilteringModeSectionLabel": { diff --git a/src/_locales/zh_TW/messages.json b/src/_locales/zh_TW/messages.json index 14e980968ced6..05ec46212f2d8 100644 --- a/src/_locales/zh_TW/messages.json +++ b/src/_locales/zh_TW/messages.json @@ -4,7 +4,7 @@ "description": "extension name." }, "extShortDesc": { - "message": "終於有一款高效能的封鎖工具。對 CPU 和記憶體的佔用極低。", + "message": "終於,有款僅佔用少量 CPU 及記憶體的高效率阻擋器。", "description": "this will be in the Chrome web store: must be 132 characters or less" }, "dashboardName": { @@ -136,11 +136,11 @@ "description": "Tooltip for the no-popups per-site switch" }, "popupTipNoPopups1": { - "message": "點擊以阻擋此網站的所有彈出式視窗", + "message": "點擊以封鎖此網站的所有彈出式視窗", "description": "Tooltip for the no-popups per-site switch" }, "popupTipNoPopups2": { - "message": "點擊以停止阻擋此網站的所有彈出式視窗", + "message": "點擊以解除封鎖此網站的所有彈出式視窗", "description": "Tooltip for the no-popups per-site switch" }, "popupTipNoLargeMedia": { @@ -152,7 +152,7 @@ "description": "Tooltip for the no-large-media per-site switch" }, "popupTipNoLargeMedia2": { - "message": "點擊以停止封鎖此網站的大型媒體元素", + "message": "點擊以解除封鎖此網站的大型媒體元素", "description": "Tooltip for the no-large-media per-site switch" }, "popupTipNoCosmeticFiltering": { From 17e0a35650175afa6a000528eb0d86e93fa972b5 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Mon, 18 Nov 2024 10:25:46 -0500 Subject: [PATCH 0461/1099] Import translation work from https://crowdin.com/project/ublock --- platform/mv3/extension/_locales/ar/messages.json | 4 ++++ platform/mv3/extension/_locales/az/messages.json | 4 ++++ platform/mv3/extension/_locales/be/messages.json | 4 ++++ platform/mv3/extension/_locales/bg/messages.json | 4 ++++ platform/mv3/extension/_locales/bn/messages.json | 4 ++++ platform/mv3/extension/_locales/br_FR/messages.json | 4 ++++ platform/mv3/extension/_locales/bs/messages.json | 4 ++++ platform/mv3/extension/_locales/ca/messages.json | 4 ++++ platform/mv3/extension/_locales/cs/messages.json | 4 ++++ platform/mv3/extension/_locales/cv/messages.json | 4 ++++ platform/mv3/extension/_locales/cy/messages.json | 4 ++++ platform/mv3/extension/_locales/da/messages.json | 4 ++++ platform/mv3/extension/_locales/de/messages.json | 4 ++++ platform/mv3/extension/_locales/el/messages.json | 4 ++++ platform/mv3/extension/_locales/en_GB/messages.json | 4 ++++ platform/mv3/extension/_locales/eo/messages.json | 4 ++++ platform/mv3/extension/_locales/es/messages.json | 4 ++++ platform/mv3/extension/_locales/et/messages.json | 4 ++++ platform/mv3/extension/_locales/eu/messages.json | 4 ++++ platform/mv3/extension/_locales/fa/messages.json | 4 ++++ platform/mv3/extension/_locales/fi/messages.json | 4 ++++ platform/mv3/extension/_locales/fil/messages.json | 4 ++++ platform/mv3/extension/_locales/fr/messages.json | 4 ++++ platform/mv3/extension/_locales/fy/messages.json | 4 ++++ platform/mv3/extension/_locales/gl/messages.json | 4 ++++ platform/mv3/extension/_locales/gu/messages.json | 4 ++++ platform/mv3/extension/_locales/he/messages.json | 4 ++++ platform/mv3/extension/_locales/hi/messages.json | 4 ++++ platform/mv3/extension/_locales/hr/messages.json | 4 ++++ platform/mv3/extension/_locales/hu/messages.json | 4 ++++ platform/mv3/extension/_locales/hy/messages.json | 4 ++++ platform/mv3/extension/_locales/id/messages.json | 4 ++++ platform/mv3/extension/_locales/it/messages.json | 4 ++++ platform/mv3/extension/_locales/ja/messages.json | 4 ++++ platform/mv3/extension/_locales/ka/messages.json | 4 ++++ platform/mv3/extension/_locales/kk/messages.json | 4 ++++ platform/mv3/extension/_locales/kn/messages.json | 4 ++++ platform/mv3/extension/_locales/ko/messages.json | 4 ++++ platform/mv3/extension/_locales/lt/messages.json | 4 ++++ platform/mv3/extension/_locales/lv/messages.json | 4 ++++ platform/mv3/extension/_locales/mk/messages.json | 4 ++++ platform/mv3/extension/_locales/ml/messages.json | 4 ++++ platform/mv3/extension/_locales/mr/messages.json | 4 ++++ platform/mv3/extension/_locales/ms/messages.json | 4 ++++ platform/mv3/extension/_locales/nb/messages.json | 4 ++++ platform/mv3/extension/_locales/nl/messages.json | 4 ++++ platform/mv3/extension/_locales/oc/messages.json | 4 ++++ platform/mv3/extension/_locales/pa/messages.json | 4 ++++ platform/mv3/extension/_locales/pl/messages.json | 4 ++++ platform/mv3/extension/_locales/pt_BR/messages.json | 4 ++++ platform/mv3/extension/_locales/pt_PT/messages.json | 4 ++++ platform/mv3/extension/_locales/ro/messages.json | 4 ++++ platform/mv3/extension/_locales/ru/messages.json | 4 ++++ platform/mv3/extension/_locales/si/messages.json | 4 ++++ platform/mv3/extension/_locales/sk/messages.json | 4 ++++ platform/mv3/extension/_locales/sl/messages.json | 4 ++++ platform/mv3/extension/_locales/so/messages.json | 4 ++++ platform/mv3/extension/_locales/sq/messages.json | 4 ++++ platform/mv3/extension/_locales/sr/messages.json | 4 ++++ platform/mv3/extension/_locales/sv/messages.json | 4 ++++ platform/mv3/extension/_locales/sw/messages.json | 4 ++++ platform/mv3/extension/_locales/ta/messages.json | 4 ++++ platform/mv3/extension/_locales/te/messages.json | 4 ++++ platform/mv3/extension/_locales/th/messages.json | 4 ++++ platform/mv3/extension/_locales/tr/messages.json | 4 ++++ platform/mv3/extension/_locales/uk/messages.json | 4 ++++ platform/mv3/extension/_locales/ur/messages.json | 4 ++++ platform/mv3/extension/_locales/vi/messages.json | 4 ++++ platform/mv3/extension/_locales/zh_CN/messages.json | 4 ++++ platform/mv3/extension/_locales/zh_TW/messages.json | 4 ++++ 70 files changed, 280 insertions(+) diff --git a/platform/mv3/extension/_locales/ar/messages.json b/platform/mv3/extension/_locales/ar/messages.json index 76d422b078854..34bb05e35cad7 100644 --- a/platform/mv3/extension/_locales/ar/messages.json +++ b/platform/mv3/extension/_locales/ar/messages.json @@ -230,5 +230,9 @@ "showBlockedCountLabel": { "message": "إظهار عدد الطلبات المحظورة على أيقونة شريط الأدوات", "description": "Label for a checkbox in the options page" + }, + "findListsPlaceholder": { + "message": "Find lists", + "description": "Placeholder for the input field used to find lists" } } diff --git a/platform/mv3/extension/_locales/az/messages.json b/platform/mv3/extension/_locales/az/messages.json index 7c769e3f935dd..b573b25e394cf 100644 --- a/platform/mv3/extension/_locales/az/messages.json +++ b/platform/mv3/extension/_locales/az/messages.json @@ -230,5 +230,9 @@ "showBlockedCountLabel": { "message": "Show the number of blocked requests on the toolbar icon", "description": "Label for a checkbox in the options page" + }, + "findListsPlaceholder": { + "message": "Find lists", + "description": "Placeholder for the input field used to find lists" } } diff --git a/platform/mv3/extension/_locales/be/messages.json b/platform/mv3/extension/_locales/be/messages.json index ee5a133da0542..5453c7b967e9e 100644 --- a/platform/mv3/extension/_locales/be/messages.json +++ b/platform/mv3/extension/_locales/be/messages.json @@ -230,5 +230,9 @@ "showBlockedCountLabel": { "message": "Паказваць колькасць заблакаваных запытаў на значку панэлі інструментаў", "description": "Label for a checkbox in the options page" + }, + "findListsPlaceholder": { + "message": "Find lists", + "description": "Placeholder for the input field used to find lists" } } diff --git a/platform/mv3/extension/_locales/bg/messages.json b/platform/mv3/extension/_locales/bg/messages.json index 51df9ca15e460..0a1fcef9b5dcf 100644 --- a/platform/mv3/extension/_locales/bg/messages.json +++ b/platform/mv3/extension/_locales/bg/messages.json @@ -230,5 +230,9 @@ "showBlockedCountLabel": { "message": "Показване на брояч в иконката за блокираните заявки", "description": "Label for a checkbox in the options page" + }, + "findListsPlaceholder": { + "message": "Find lists", + "description": "Placeholder for the input field used to find lists" } } diff --git a/platform/mv3/extension/_locales/bn/messages.json b/platform/mv3/extension/_locales/bn/messages.json index d080906856e76..f2593fc516db7 100644 --- a/platform/mv3/extension/_locales/bn/messages.json +++ b/platform/mv3/extension/_locales/bn/messages.json @@ -230,5 +230,9 @@ "showBlockedCountLabel": { "message": "কতগুলো অনুরোধ ব্লক করা হয়েছে তা টুলবার আইকনে দেখাও", "description": "Label for a checkbox in the options page" + }, + "findListsPlaceholder": { + "message": "Find lists", + "description": "Placeholder for the input field used to find lists" } } diff --git a/platform/mv3/extension/_locales/br_FR/messages.json b/platform/mv3/extension/_locales/br_FR/messages.json index 38d5e7715ba5a..5c3cb4830a02c 100644 --- a/platform/mv3/extension/_locales/br_FR/messages.json +++ b/platform/mv3/extension/_locales/br_FR/messages.json @@ -230,5 +230,9 @@ "showBlockedCountLabel": { "message": "Diskouez an niver a rekedoù bet stanket war arouez ar varrenn-ostilhoù", "description": "Label for a checkbox in the options page" + }, + "findListsPlaceholder": { + "message": "Find lists", + "description": "Placeholder for the input field used to find lists" } } diff --git a/platform/mv3/extension/_locales/bs/messages.json b/platform/mv3/extension/_locales/bs/messages.json index 178795ae3117a..43466580b2805 100644 --- a/platform/mv3/extension/_locales/bs/messages.json +++ b/platform/mv3/extension/_locales/bs/messages.json @@ -230,5 +230,9 @@ "showBlockedCountLabel": { "message": "Prikažite broj blokiranih zahtjeva na ikoni trake sa alatkama", "description": "Label for a checkbox in the options page" + }, + "findListsPlaceholder": { + "message": "Find lists", + "description": "Placeholder for the input field used to find lists" } } diff --git a/platform/mv3/extension/_locales/ca/messages.json b/platform/mv3/extension/_locales/ca/messages.json index f307f31ca115a..8c9860785ad04 100644 --- a/platform/mv3/extension/_locales/ca/messages.json +++ b/platform/mv3/extension/_locales/ca/messages.json @@ -230,5 +230,9 @@ "showBlockedCountLabel": { "message": "Mostra el nombre de sol·licituds blocades a la icona de la barra d'eines", "description": "Label for a checkbox in the options page" + }, + "findListsPlaceholder": { + "message": "Find lists", + "description": "Placeholder for the input field used to find lists" } } diff --git a/platform/mv3/extension/_locales/cs/messages.json b/platform/mv3/extension/_locales/cs/messages.json index fb24661d25a84..059ba02b198e6 100644 --- a/platform/mv3/extension/_locales/cs/messages.json +++ b/platform/mv3/extension/_locales/cs/messages.json @@ -230,5 +230,9 @@ "showBlockedCountLabel": { "message": "Zobrazit počet blokovaných požadavků u ikony v panelu nástrojů", "description": "Label for a checkbox in the options page" + }, + "findListsPlaceholder": { + "message": "Find lists", + "description": "Placeholder for the input field used to find lists" } } diff --git a/platform/mv3/extension/_locales/cv/messages.json b/platform/mv3/extension/_locales/cv/messages.json index c5f77c667a506..001ac9feaf606 100644 --- a/platform/mv3/extension/_locales/cv/messages.json +++ b/platform/mv3/extension/_locales/cv/messages.json @@ -230,5 +230,9 @@ "showBlockedCountLabel": { "message": "Show the number of blocked requests on the toolbar icon", "description": "Label for a checkbox in the options page" + }, + "findListsPlaceholder": { + "message": "Find lists", + "description": "Placeholder for the input field used to find lists" } } diff --git a/platform/mv3/extension/_locales/cy/messages.json b/platform/mv3/extension/_locales/cy/messages.json index 5a445929a8b0d..bae4fc9205771 100644 --- a/platform/mv3/extension/_locales/cy/messages.json +++ b/platform/mv3/extension/_locales/cy/messages.json @@ -230,5 +230,9 @@ "showBlockedCountLabel": { "message": "Show the number of blocked requests on the toolbar icon", "description": "Label for a checkbox in the options page" + }, + "findListsPlaceholder": { + "message": "Find lists", + "description": "Placeholder for the input field used to find lists" } } diff --git a/platform/mv3/extension/_locales/da/messages.json b/platform/mv3/extension/_locales/da/messages.json index 8a8c760677c92..5ca16cd167912 100644 --- a/platform/mv3/extension/_locales/da/messages.json +++ b/platform/mv3/extension/_locales/da/messages.json @@ -230,5 +230,9 @@ "showBlockedCountLabel": { "message": "Vis antallet af blokerede forespørgsler på værktøjsbjælkeikonet", "description": "Label for a checkbox in the options page" + }, + "findListsPlaceholder": { + "message": "Find lists", + "description": "Placeholder for the input field used to find lists" } } diff --git a/platform/mv3/extension/_locales/de/messages.json b/platform/mv3/extension/_locales/de/messages.json index a2504f952ed9e..b1a7d4fd1544f 100644 --- a/platform/mv3/extension/_locales/de/messages.json +++ b/platform/mv3/extension/_locales/de/messages.json @@ -230,5 +230,9 @@ "showBlockedCountLabel": { "message": "Anzahl der blockierten Anfragen auf dem Symbol in der Symbolleiste anzeigen", "description": "Label for a checkbox in the options page" + }, + "findListsPlaceholder": { + "message": "Find lists", + "description": "Placeholder for the input field used to find lists" } } diff --git a/platform/mv3/extension/_locales/el/messages.json b/platform/mv3/extension/_locales/el/messages.json index 7020c8b4c1e5c..b1941281a3299 100644 --- a/platform/mv3/extension/_locales/el/messages.json +++ b/platform/mv3/extension/_locales/el/messages.json @@ -230,5 +230,9 @@ "showBlockedCountLabel": { "message": "Εμφάνιση του αριθμού των αποκλεισμένων αιτημάτων στο εικονίδιο της γραμμής εργαλείων", "description": "Label for a checkbox in the options page" + }, + "findListsPlaceholder": { + "message": "Find lists", + "description": "Placeholder for the input field used to find lists" } } diff --git a/platform/mv3/extension/_locales/en_GB/messages.json b/platform/mv3/extension/_locales/en_GB/messages.json index f1d2e99626386..ee80a063a5e79 100644 --- a/platform/mv3/extension/_locales/en_GB/messages.json +++ b/platform/mv3/extension/_locales/en_GB/messages.json @@ -230,5 +230,9 @@ "showBlockedCountLabel": { "message": "Show the number of blocked requests on the toolbar icon", "description": "Label for a checkbox in the options page" + }, + "findListsPlaceholder": { + "message": "Find lists", + "description": "Placeholder for the input field used to find lists" } } diff --git a/platform/mv3/extension/_locales/eo/messages.json b/platform/mv3/extension/_locales/eo/messages.json index 786ace6c3fa7c..ec68394744eac 100644 --- a/platform/mv3/extension/_locales/eo/messages.json +++ b/platform/mv3/extension/_locales/eo/messages.json @@ -230,5 +230,9 @@ "showBlockedCountLabel": { "message": "Show the number of blocked requests on the toolbar icon", "description": "Label for a checkbox in the options page" + }, + "findListsPlaceholder": { + "message": "Find lists", + "description": "Placeholder for the input field used to find lists" } } diff --git a/platform/mv3/extension/_locales/es/messages.json b/platform/mv3/extension/_locales/es/messages.json index 0232f371a0065..5c649d305bba1 100644 --- a/platform/mv3/extension/_locales/es/messages.json +++ b/platform/mv3/extension/_locales/es/messages.json @@ -230,5 +230,9 @@ "showBlockedCountLabel": { "message": "Mostrar el número de peticiones bloqueadas en el icono de la barra de herramientas", "description": "Label for a checkbox in the options page" + }, + "findListsPlaceholder": { + "message": "Find lists", + "description": "Placeholder for the input field used to find lists" } } diff --git a/platform/mv3/extension/_locales/et/messages.json b/platform/mv3/extension/_locales/et/messages.json index ae248a7b0fc5f..ad7b35ced04c1 100644 --- a/platform/mv3/extension/_locales/et/messages.json +++ b/platform/mv3/extension/_locales/et/messages.json @@ -230,5 +230,9 @@ "showBlockedCountLabel": { "message": "Kuva tööriistariba ikoonil blokeeritud elementide arv", "description": "Label for a checkbox in the options page" + }, + "findListsPlaceholder": { + "message": "Find lists", + "description": "Placeholder for the input field used to find lists" } } diff --git a/platform/mv3/extension/_locales/eu/messages.json b/platform/mv3/extension/_locales/eu/messages.json index a6dedc46b7d0d..a477239fc0fbe 100644 --- a/platform/mv3/extension/_locales/eu/messages.json +++ b/platform/mv3/extension/_locales/eu/messages.json @@ -230,5 +230,9 @@ "showBlockedCountLabel": { "message": "Tresna-barraren ikonoan blokeatutako eskaeren kopurua erakutsi", "description": "Label for a checkbox in the options page" + }, + "findListsPlaceholder": { + "message": "Find lists", + "description": "Placeholder for the input field used to find lists" } } diff --git a/platform/mv3/extension/_locales/fa/messages.json b/platform/mv3/extension/_locales/fa/messages.json index 7f4e9782db8fb..cfdb831610f5d 100644 --- a/platform/mv3/extension/_locales/fa/messages.json +++ b/platform/mv3/extension/_locales/fa/messages.json @@ -230,5 +230,9 @@ "showBlockedCountLabel": { "message": "Show the number of blocked requests on the toolbar icon", "description": "Label for a checkbox in the options page" + }, + "findListsPlaceholder": { + "message": "Find lists", + "description": "Placeholder for the input field used to find lists" } } diff --git a/platform/mv3/extension/_locales/fi/messages.json b/platform/mv3/extension/_locales/fi/messages.json index 4db333e85b415..0daddc50f5a53 100644 --- a/platform/mv3/extension/_locales/fi/messages.json +++ b/platform/mv3/extension/_locales/fi/messages.json @@ -230,5 +230,9 @@ "showBlockedCountLabel": { "message": "Näytä estettyjen pyyntöjen määrä työkalupalkin kuvakkeessa", "description": "Label for a checkbox in the options page" + }, + "findListsPlaceholder": { + "message": "Find lists", + "description": "Placeholder for the input field used to find lists" } } diff --git a/platform/mv3/extension/_locales/fil/messages.json b/platform/mv3/extension/_locales/fil/messages.json index 8f81e02a64ea5..6450bb8f60597 100644 --- a/platform/mv3/extension/_locales/fil/messages.json +++ b/platform/mv3/extension/_locales/fil/messages.json @@ -230,5 +230,9 @@ "showBlockedCountLabel": { "message": "Show the number of blocked requests on the toolbar icon", "description": "Label for a checkbox in the options page" + }, + "findListsPlaceholder": { + "message": "Find lists", + "description": "Placeholder for the input field used to find lists" } } diff --git a/platform/mv3/extension/_locales/fr/messages.json b/platform/mv3/extension/_locales/fr/messages.json index c18dccbeac544..cd8af002f5622 100644 --- a/platform/mv3/extension/_locales/fr/messages.json +++ b/platform/mv3/extension/_locales/fr/messages.json @@ -230,5 +230,9 @@ "showBlockedCountLabel": { "message": "Afficher le nombre de requêtes bloquées sur l'icône", "description": "Label for a checkbox in the options page" + }, + "findListsPlaceholder": { + "message": "Find lists", + "description": "Placeholder for the input field used to find lists" } } diff --git a/platform/mv3/extension/_locales/fy/messages.json b/platform/mv3/extension/_locales/fy/messages.json index ac1895fd5da09..a6c66a520a936 100644 --- a/platform/mv3/extension/_locales/fy/messages.json +++ b/platform/mv3/extension/_locales/fy/messages.json @@ -230,5 +230,9 @@ "showBlockedCountLabel": { "message": "It tal blokkearre oanfragen op it arkbalkepiktogram toane", "description": "Label for a checkbox in the options page" + }, + "findListsPlaceholder": { + "message": "Find lists", + "description": "Placeholder for the input field used to find lists" } } diff --git a/platform/mv3/extension/_locales/gl/messages.json b/platform/mv3/extension/_locales/gl/messages.json index 59833de63f371..f45f9f240ed25 100644 --- a/platform/mv3/extension/_locales/gl/messages.json +++ b/platform/mv3/extension/_locales/gl/messages.json @@ -230,5 +230,9 @@ "showBlockedCountLabel": { "message": "Mostrar na icona da barra o número de peticións bloqueadas", "description": "Label for a checkbox in the options page" + }, + "findListsPlaceholder": { + "message": "Find lists", + "description": "Placeholder for the input field used to find lists" } } diff --git a/platform/mv3/extension/_locales/gu/messages.json b/platform/mv3/extension/_locales/gu/messages.json index c5f77c667a506..001ac9feaf606 100644 --- a/platform/mv3/extension/_locales/gu/messages.json +++ b/platform/mv3/extension/_locales/gu/messages.json @@ -230,5 +230,9 @@ "showBlockedCountLabel": { "message": "Show the number of blocked requests on the toolbar icon", "description": "Label for a checkbox in the options page" + }, + "findListsPlaceholder": { + "message": "Find lists", + "description": "Placeholder for the input field used to find lists" } } diff --git a/platform/mv3/extension/_locales/he/messages.json b/platform/mv3/extension/_locales/he/messages.json index 6dfbf8126ed13..2a49c7d181309 100644 --- a/platform/mv3/extension/_locales/he/messages.json +++ b/platform/mv3/extension/_locales/he/messages.json @@ -230,5 +230,9 @@ "showBlockedCountLabel": { "message": "הצגת מספר הבקשות החסומות על הסמל", "description": "Label for a checkbox in the options page" + }, + "findListsPlaceholder": { + "message": "Find lists", + "description": "Placeholder for the input field used to find lists" } } diff --git a/platform/mv3/extension/_locales/hi/messages.json b/platform/mv3/extension/_locales/hi/messages.json index 2a9dae26b6814..710ac407d6515 100644 --- a/platform/mv3/extension/_locales/hi/messages.json +++ b/platform/mv3/extension/_locales/hi/messages.json @@ -230,5 +230,9 @@ "showBlockedCountLabel": { "message": "टूलबार आइकन पर अवरुद्ध अनुरोधों की संख्या दिखाएं", "description": "Label for a checkbox in the options page" + }, + "findListsPlaceholder": { + "message": "Find lists", + "description": "Placeholder for the input field used to find lists" } } diff --git a/platform/mv3/extension/_locales/hr/messages.json b/platform/mv3/extension/_locales/hr/messages.json index 1834c868831d6..ce1be00206e5a 100644 --- a/platform/mv3/extension/_locales/hr/messages.json +++ b/platform/mv3/extension/_locales/hr/messages.json @@ -230,5 +230,9 @@ "showBlockedCountLabel": { "message": "Prikaži broj blokiranih zahtjeva na ikoni alatne trake", "description": "Label for a checkbox in the options page" + }, + "findListsPlaceholder": { + "message": "Find lists", + "description": "Placeholder for the input field used to find lists" } } diff --git a/platform/mv3/extension/_locales/hu/messages.json b/platform/mv3/extension/_locales/hu/messages.json index 987469483ed27..3ba4ccdcace10 100644 --- a/platform/mv3/extension/_locales/hu/messages.json +++ b/platform/mv3/extension/_locales/hu/messages.json @@ -230,5 +230,9 @@ "showBlockedCountLabel": { "message": "Blokkolt kérések számának megjelenítése az eszköztárikonon", "description": "Label for a checkbox in the options page" + }, + "findListsPlaceholder": { + "message": "Find lists", + "description": "Placeholder for the input field used to find lists" } } diff --git a/platform/mv3/extension/_locales/hy/messages.json b/platform/mv3/extension/_locales/hy/messages.json index a0cf19fa06bd7..9f6b7e347715e 100644 --- a/platform/mv3/extension/_locales/hy/messages.json +++ b/platform/mv3/extension/_locales/hy/messages.json @@ -230,5 +230,9 @@ "showBlockedCountLabel": { "message": "Ցուցադրել արգելափակված հայտերի քանակը գործիքների վահանակին", "description": "Label for a checkbox in the options page" + }, + "findListsPlaceholder": { + "message": "Find lists", + "description": "Placeholder for the input field used to find lists" } } diff --git a/platform/mv3/extension/_locales/id/messages.json b/platform/mv3/extension/_locales/id/messages.json index 806263ff961c1..90b49987c904e 100644 --- a/platform/mv3/extension/_locales/id/messages.json +++ b/platform/mv3/extension/_locales/id/messages.json @@ -230,5 +230,9 @@ "showBlockedCountLabel": { "message": "Tampilkan jumlah permintaan yang diblokir pada ikon toolbar", "description": "Label for a checkbox in the options page" + }, + "findListsPlaceholder": { + "message": "Find lists", + "description": "Placeholder for the input field used to find lists" } } diff --git a/platform/mv3/extension/_locales/it/messages.json b/platform/mv3/extension/_locales/it/messages.json index 35bbc6c0ecb63..d2221f3c53620 100644 --- a/platform/mv3/extension/_locales/it/messages.json +++ b/platform/mv3/extension/_locales/it/messages.json @@ -230,5 +230,9 @@ "showBlockedCountLabel": { "message": "Mostra il numero delle richieste bloccate sull'icona nella barra degli strumenti", "description": "Label for a checkbox in the options page" + }, + "findListsPlaceholder": { + "message": "Find lists", + "description": "Placeholder for the input field used to find lists" } } diff --git a/platform/mv3/extension/_locales/ja/messages.json b/platform/mv3/extension/_locales/ja/messages.json index a45ed763e734e..0efa9bd95eb7b 100644 --- a/platform/mv3/extension/_locales/ja/messages.json +++ b/platform/mv3/extension/_locales/ja/messages.json @@ -230,5 +230,9 @@ "showBlockedCountLabel": { "message": "ブロックしたリクエストの数をツールバーのアイコンに表示", "description": "Label for a checkbox in the options page" + }, + "findListsPlaceholder": { + "message": "Find lists", + "description": "Placeholder for the input field used to find lists" } } diff --git a/platform/mv3/extension/_locales/ka/messages.json b/platform/mv3/extension/_locales/ka/messages.json index 3f64b336305e0..0f95d40652fc8 100644 --- a/platform/mv3/extension/_locales/ka/messages.json +++ b/platform/mv3/extension/_locales/ka/messages.json @@ -230,5 +230,9 @@ "showBlockedCountLabel": { "message": "აჩვენეთ დაბლოკილი მოთხოვნების რაოდენობა ხელსაწყოთა ზოლის ხატულაზე", "description": "Label for a checkbox in the options page" + }, + "findListsPlaceholder": { + "message": "Find lists", + "description": "Placeholder for the input field used to find lists" } } diff --git a/platform/mv3/extension/_locales/kk/messages.json b/platform/mv3/extension/_locales/kk/messages.json index c5f77c667a506..001ac9feaf606 100644 --- a/platform/mv3/extension/_locales/kk/messages.json +++ b/platform/mv3/extension/_locales/kk/messages.json @@ -230,5 +230,9 @@ "showBlockedCountLabel": { "message": "Show the number of blocked requests on the toolbar icon", "description": "Label for a checkbox in the options page" + }, + "findListsPlaceholder": { + "message": "Find lists", + "description": "Placeholder for the input field used to find lists" } } diff --git a/platform/mv3/extension/_locales/kn/messages.json b/platform/mv3/extension/_locales/kn/messages.json index 7ff919c228737..7b349ff0a0c16 100644 --- a/platform/mv3/extension/_locales/kn/messages.json +++ b/platform/mv3/extension/_locales/kn/messages.json @@ -230,5 +230,9 @@ "showBlockedCountLabel": { "message": "Show the number of blocked requests on the toolbar icon", "description": "Label for a checkbox in the options page" + }, + "findListsPlaceholder": { + "message": "Find lists", + "description": "Placeholder for the input field used to find lists" } } diff --git a/platform/mv3/extension/_locales/ko/messages.json b/platform/mv3/extension/_locales/ko/messages.json index af43131206324..a5125f5ad676d 100644 --- a/platform/mv3/extension/_locales/ko/messages.json +++ b/platform/mv3/extension/_locales/ko/messages.json @@ -230,5 +230,9 @@ "showBlockedCountLabel": { "message": "차단된 요청 개수를 도구 모음 아이콘에 표시", "description": "Label for a checkbox in the options page" + }, + "findListsPlaceholder": { + "message": "Find lists", + "description": "Placeholder for the input field used to find lists" } } diff --git a/platform/mv3/extension/_locales/lt/messages.json b/platform/mv3/extension/_locales/lt/messages.json index d810f531ed358..7e7ae212faeef 100644 --- a/platform/mv3/extension/_locales/lt/messages.json +++ b/platform/mv3/extension/_locales/lt/messages.json @@ -230,5 +230,9 @@ "showBlockedCountLabel": { "message": "Show the number of blocked requests on the toolbar icon", "description": "Label for a checkbox in the options page" + }, + "findListsPlaceholder": { + "message": "Find lists", + "description": "Placeholder for the input field used to find lists" } } diff --git a/platform/mv3/extension/_locales/lv/messages.json b/platform/mv3/extension/_locales/lv/messages.json index 810d5a0eab97b..03dd849089d62 100644 --- a/platform/mv3/extension/_locales/lv/messages.json +++ b/platform/mv3/extension/_locales/lv/messages.json @@ -230,5 +230,9 @@ "showBlockedCountLabel": { "message": "Rādīt aizturēto pieprasījumu skaitu rīkjoslas ikonā", "description": "Label for a checkbox in the options page" + }, + "findListsPlaceholder": { + "message": "Find lists", + "description": "Placeholder for the input field used to find lists" } } diff --git a/platform/mv3/extension/_locales/mk/messages.json b/platform/mv3/extension/_locales/mk/messages.json index 06f558231a77c..60186c0844080 100644 --- a/platform/mv3/extension/_locales/mk/messages.json +++ b/platform/mv3/extension/_locales/mk/messages.json @@ -230,5 +230,9 @@ "showBlockedCountLabel": { "message": "Show the number of blocked requests on the toolbar icon", "description": "Label for a checkbox in the options page" + }, + "findListsPlaceholder": { + "message": "Find lists", + "description": "Placeholder for the input field used to find lists" } } diff --git a/platform/mv3/extension/_locales/ml/messages.json b/platform/mv3/extension/_locales/ml/messages.json index a80d375c23054..0862924b4fa66 100644 --- a/platform/mv3/extension/_locales/ml/messages.json +++ b/platform/mv3/extension/_locales/ml/messages.json @@ -230,5 +230,9 @@ "showBlockedCountLabel": { "message": "Show the number of blocked requests on the toolbar icon", "description": "Label for a checkbox in the options page" + }, + "findListsPlaceholder": { + "message": "Find lists", + "description": "Placeholder for the input field used to find lists" } } diff --git a/platform/mv3/extension/_locales/mr/messages.json b/platform/mv3/extension/_locales/mr/messages.json index c5f77c667a506..001ac9feaf606 100644 --- a/platform/mv3/extension/_locales/mr/messages.json +++ b/platform/mv3/extension/_locales/mr/messages.json @@ -230,5 +230,9 @@ "showBlockedCountLabel": { "message": "Show the number of blocked requests on the toolbar icon", "description": "Label for a checkbox in the options page" + }, + "findListsPlaceholder": { + "message": "Find lists", + "description": "Placeholder for the input field used to find lists" } } diff --git a/platform/mv3/extension/_locales/ms/messages.json b/platform/mv3/extension/_locales/ms/messages.json index 6b015e7a9b1a6..853c8ce344b05 100644 --- a/platform/mv3/extension/_locales/ms/messages.json +++ b/platform/mv3/extension/_locales/ms/messages.json @@ -230,5 +230,9 @@ "showBlockedCountLabel": { "message": "Tunjukkan bilangan permintaan yang disekat pada ikon bar alat", "description": "Label for a checkbox in the options page" + }, + "findListsPlaceholder": { + "message": "Find lists", + "description": "Placeholder for the input field used to find lists" } } diff --git a/platform/mv3/extension/_locales/nb/messages.json b/platform/mv3/extension/_locales/nb/messages.json index 4fd7bd3d1135c..a977e85326f83 100644 --- a/platform/mv3/extension/_locales/nb/messages.json +++ b/platform/mv3/extension/_locales/nb/messages.json @@ -230,5 +230,9 @@ "showBlockedCountLabel": { "message": "Vis antall blokkerte forespørsler på verktøylinjeikonet", "description": "Label for a checkbox in the options page" + }, + "findListsPlaceholder": { + "message": "Find lists", + "description": "Placeholder for the input field used to find lists" } } diff --git a/platform/mv3/extension/_locales/nl/messages.json b/platform/mv3/extension/_locales/nl/messages.json index 07f45e9c4b0bb..50908b9723445 100644 --- a/platform/mv3/extension/_locales/nl/messages.json +++ b/platform/mv3/extension/_locales/nl/messages.json @@ -230,5 +230,9 @@ "showBlockedCountLabel": { "message": "Het aantal geblokkeerde aanvragen op het werkbalkpictogram tonen", "description": "Label for a checkbox in the options page" + }, + "findListsPlaceholder": { + "message": "Find lists", + "description": "Placeholder for the input field used to find lists" } } diff --git a/platform/mv3/extension/_locales/oc/messages.json b/platform/mv3/extension/_locales/oc/messages.json index c5f77c667a506..001ac9feaf606 100644 --- a/platform/mv3/extension/_locales/oc/messages.json +++ b/platform/mv3/extension/_locales/oc/messages.json @@ -230,5 +230,9 @@ "showBlockedCountLabel": { "message": "Show the number of blocked requests on the toolbar icon", "description": "Label for a checkbox in the options page" + }, + "findListsPlaceholder": { + "message": "Find lists", + "description": "Placeholder for the input field used to find lists" } } diff --git a/platform/mv3/extension/_locales/pa/messages.json b/platform/mv3/extension/_locales/pa/messages.json index 6858a0393cc7a..f8ce344bc6e10 100644 --- a/platform/mv3/extension/_locales/pa/messages.json +++ b/platform/mv3/extension/_locales/pa/messages.json @@ -230,5 +230,9 @@ "showBlockedCountLabel": { "message": "Show the number of blocked requests on the toolbar icon", "description": "Label for a checkbox in the options page" + }, + "findListsPlaceholder": { + "message": "Find lists", + "description": "Placeholder for the input field used to find lists" } } diff --git a/platform/mv3/extension/_locales/pl/messages.json b/platform/mv3/extension/_locales/pl/messages.json index 5ecb3940621c0..57f1a9b193782 100644 --- a/platform/mv3/extension/_locales/pl/messages.json +++ b/platform/mv3/extension/_locales/pl/messages.json @@ -230,5 +230,9 @@ "showBlockedCountLabel": { "message": "Wyświetlaj liczbę zablokowanych żądań na ikonie paska narzędzi", "description": "Label for a checkbox in the options page" + }, + "findListsPlaceholder": { + "message": "Find lists", + "description": "Placeholder for the input field used to find lists" } } diff --git a/platform/mv3/extension/_locales/pt_BR/messages.json b/platform/mv3/extension/_locales/pt_BR/messages.json index 66f46188de874..c13d3d63d6551 100644 --- a/platform/mv3/extension/_locales/pt_BR/messages.json +++ b/platform/mv3/extension/_locales/pt_BR/messages.json @@ -230,5 +230,9 @@ "showBlockedCountLabel": { "message": "Mostrar o número de requisições bloqueadas no ícone da barra de ferramentas", "description": "Label for a checkbox in the options page" + }, + "findListsPlaceholder": { + "message": "Find lists", + "description": "Placeholder for the input field used to find lists" } } diff --git a/platform/mv3/extension/_locales/pt_PT/messages.json b/platform/mv3/extension/_locales/pt_PT/messages.json index f73c906c6560b..b15553d53cfff 100644 --- a/platform/mv3/extension/_locales/pt_PT/messages.json +++ b/platform/mv3/extension/_locales/pt_PT/messages.json @@ -230,5 +230,9 @@ "showBlockedCountLabel": { "message": "Mostra o número de pedidos bloqueados no ícone da barra de ferramentas", "description": "Label for a checkbox in the options page" + }, + "findListsPlaceholder": { + "message": "Find lists", + "description": "Placeholder for the input field used to find lists" } } diff --git a/platform/mv3/extension/_locales/ro/messages.json b/platform/mv3/extension/_locales/ro/messages.json index 38939b3439529..0b6384fb5a384 100644 --- a/platform/mv3/extension/_locales/ro/messages.json +++ b/platform/mv3/extension/_locales/ro/messages.json @@ -230,5 +230,9 @@ "showBlockedCountLabel": { "message": "Arată numărul cererilor blocate pe simbolul extensiei", "description": "Label for a checkbox in the options page" + }, + "findListsPlaceholder": { + "message": "Find lists", + "description": "Placeholder for the input field used to find lists" } } diff --git a/platform/mv3/extension/_locales/ru/messages.json b/platform/mv3/extension/_locales/ru/messages.json index e54dcead462a5..ed4dea09eb30f 100644 --- a/platform/mv3/extension/_locales/ru/messages.json +++ b/platform/mv3/extension/_locales/ru/messages.json @@ -230,5 +230,9 @@ "showBlockedCountLabel": { "message": "Показывать количество заблокированных запросов на иконке в панели инструментов", "description": "Label for a checkbox in the options page" + }, + "findListsPlaceholder": { + "message": "Find lists", + "description": "Placeholder for the input field used to find lists" } } diff --git a/platform/mv3/extension/_locales/si/messages.json b/platform/mv3/extension/_locales/si/messages.json index 559224a0ca335..cf5b4f4413db1 100644 --- a/platform/mv3/extension/_locales/si/messages.json +++ b/platform/mv3/extension/_locales/si/messages.json @@ -230,5 +230,9 @@ "showBlockedCountLabel": { "message": "මෙවලම් තීරු නිරූපකයේ අවහිර කළ ඉල්ලීම් ගණන පෙන්වන්න", "description": "Label for a checkbox in the options page" + }, + "findListsPlaceholder": { + "message": "Find lists", + "description": "Placeholder for the input field used to find lists" } } diff --git a/platform/mv3/extension/_locales/sk/messages.json b/platform/mv3/extension/_locales/sk/messages.json index 3354d50d4de24..f319ffdd19382 100644 --- a/platform/mv3/extension/_locales/sk/messages.json +++ b/platform/mv3/extension/_locales/sk/messages.json @@ -230,5 +230,9 @@ "showBlockedCountLabel": { "message": "Zobraziť počet zablokovaných požiadaviek na ikone rozšírenia", "description": "Label for a checkbox in the options page" + }, + "findListsPlaceholder": { + "message": "Find lists", + "description": "Placeholder for the input field used to find lists" } } diff --git a/platform/mv3/extension/_locales/sl/messages.json b/platform/mv3/extension/_locales/sl/messages.json index c5f77c667a506..001ac9feaf606 100644 --- a/platform/mv3/extension/_locales/sl/messages.json +++ b/platform/mv3/extension/_locales/sl/messages.json @@ -230,5 +230,9 @@ "showBlockedCountLabel": { "message": "Show the number of blocked requests on the toolbar icon", "description": "Label for a checkbox in the options page" + }, + "findListsPlaceholder": { + "message": "Find lists", + "description": "Placeholder for the input field used to find lists" } } diff --git a/platform/mv3/extension/_locales/so/messages.json b/platform/mv3/extension/_locales/so/messages.json index c5f77c667a506..001ac9feaf606 100644 --- a/platform/mv3/extension/_locales/so/messages.json +++ b/platform/mv3/extension/_locales/so/messages.json @@ -230,5 +230,9 @@ "showBlockedCountLabel": { "message": "Show the number of blocked requests on the toolbar icon", "description": "Label for a checkbox in the options page" + }, + "findListsPlaceholder": { + "message": "Find lists", + "description": "Placeholder for the input field used to find lists" } } diff --git a/platform/mv3/extension/_locales/sq/messages.json b/platform/mv3/extension/_locales/sq/messages.json index 464f49455b042..da316b303a8ef 100644 --- a/platform/mv3/extension/_locales/sq/messages.json +++ b/platform/mv3/extension/_locales/sq/messages.json @@ -230,5 +230,9 @@ "showBlockedCountLabel": { "message": "Shfaq numrin e kërkesave të bllokuara në ikonën e instrumenteve", "description": "Label for a checkbox in the options page" + }, + "findListsPlaceholder": { + "message": "Find lists", + "description": "Placeholder for the input field used to find lists" } } diff --git a/platform/mv3/extension/_locales/sr/messages.json b/platform/mv3/extension/_locales/sr/messages.json index b994f245dc511..19357465d89a6 100644 --- a/platform/mv3/extension/_locales/sr/messages.json +++ b/platform/mv3/extension/_locales/sr/messages.json @@ -230,5 +230,9 @@ "showBlockedCountLabel": { "message": "Прикажи број блокираних захтева на иконици на траци алата", "description": "Label for a checkbox in the options page" + }, + "findListsPlaceholder": { + "message": "Find lists", + "description": "Placeholder for the input field used to find lists" } } diff --git a/platform/mv3/extension/_locales/sv/messages.json b/platform/mv3/extension/_locales/sv/messages.json index 7ef1749a8cd68..064f191794f75 100644 --- a/platform/mv3/extension/_locales/sv/messages.json +++ b/platform/mv3/extension/_locales/sv/messages.json @@ -230,5 +230,9 @@ "showBlockedCountLabel": { "message": "Visa antalet blockerade förfrågningar på verktygsfältsikonen", "description": "Label for a checkbox in the options page" + }, + "findListsPlaceholder": { + "message": "Find lists", + "description": "Placeholder for the input field used to find lists" } } diff --git a/platform/mv3/extension/_locales/sw/messages.json b/platform/mv3/extension/_locales/sw/messages.json index c5f77c667a506..001ac9feaf606 100644 --- a/platform/mv3/extension/_locales/sw/messages.json +++ b/platform/mv3/extension/_locales/sw/messages.json @@ -230,5 +230,9 @@ "showBlockedCountLabel": { "message": "Show the number of blocked requests on the toolbar icon", "description": "Label for a checkbox in the options page" + }, + "findListsPlaceholder": { + "message": "Find lists", + "description": "Placeholder for the input field used to find lists" } } diff --git a/platform/mv3/extension/_locales/ta/messages.json b/platform/mv3/extension/_locales/ta/messages.json index c5f77c667a506..001ac9feaf606 100644 --- a/platform/mv3/extension/_locales/ta/messages.json +++ b/platform/mv3/extension/_locales/ta/messages.json @@ -230,5 +230,9 @@ "showBlockedCountLabel": { "message": "Show the number of blocked requests on the toolbar icon", "description": "Label for a checkbox in the options page" + }, + "findListsPlaceholder": { + "message": "Find lists", + "description": "Placeholder for the input field used to find lists" } } diff --git a/platform/mv3/extension/_locales/te/messages.json b/platform/mv3/extension/_locales/te/messages.json index dd2b1d19054ca..7a1cb67fa6a3b 100644 --- a/platform/mv3/extension/_locales/te/messages.json +++ b/platform/mv3/extension/_locales/te/messages.json @@ -230,5 +230,9 @@ "showBlockedCountLabel": { "message": "Show the number of blocked requests on the toolbar icon", "description": "Label for a checkbox in the options page" + }, + "findListsPlaceholder": { + "message": "Find lists", + "description": "Placeholder for the input field used to find lists" } } diff --git a/platform/mv3/extension/_locales/th/messages.json b/platform/mv3/extension/_locales/th/messages.json index 9905daf714b4e..dd77461de48e0 100644 --- a/platform/mv3/extension/_locales/th/messages.json +++ b/platform/mv3/extension/_locales/th/messages.json @@ -230,5 +230,9 @@ "showBlockedCountLabel": { "message": "แสดงจำนวนคำขอที่ถูกปิดกั้นบนไอคอนแถบเครื่องมือ", "description": "Label for a checkbox in the options page" + }, + "findListsPlaceholder": { + "message": "Find lists", + "description": "Placeholder for the input field used to find lists" } } diff --git a/platform/mv3/extension/_locales/tr/messages.json b/platform/mv3/extension/_locales/tr/messages.json index 175effdcc1ad9..8699c2d9fc150 100644 --- a/platform/mv3/extension/_locales/tr/messages.json +++ b/platform/mv3/extension/_locales/tr/messages.json @@ -230,5 +230,9 @@ "showBlockedCountLabel": { "message": "Engellenen isteklerin sayısını araç çubuğu simgesinde göster", "description": "Label for a checkbox in the options page" + }, + "findListsPlaceholder": { + "message": "Find lists", + "description": "Placeholder for the input field used to find lists" } } diff --git a/platform/mv3/extension/_locales/uk/messages.json b/platform/mv3/extension/_locales/uk/messages.json index 5e5e47e002990..876a289449f94 100644 --- a/platform/mv3/extension/_locales/uk/messages.json +++ b/platform/mv3/extension/_locales/uk/messages.json @@ -230,5 +230,9 @@ "showBlockedCountLabel": { "message": "Показувати кількість заблокованих запитів на піктограмі панелі інструментів", "description": "Label for a checkbox in the options page" + }, + "findListsPlaceholder": { + "message": "Find lists", + "description": "Placeholder for the input field used to find lists" } } diff --git a/platform/mv3/extension/_locales/ur/messages.json b/platform/mv3/extension/_locales/ur/messages.json index 4832e6feffc16..0af9983ebe907 100644 --- a/platform/mv3/extension/_locales/ur/messages.json +++ b/platform/mv3/extension/_locales/ur/messages.json @@ -230,5 +230,9 @@ "showBlockedCountLabel": { "message": "ٹول بار کے آئیکن پر بلاک شدہ درخواستوں کی تعداد دکھائیں۔", "description": "Label for a checkbox in the options page" + }, + "findListsPlaceholder": { + "message": "Find lists", + "description": "Placeholder for the input field used to find lists" } } diff --git a/platform/mv3/extension/_locales/vi/messages.json b/platform/mv3/extension/_locales/vi/messages.json index 306b1b1f46992..5826504b1cea4 100644 --- a/platform/mv3/extension/_locales/vi/messages.json +++ b/platform/mv3/extension/_locales/vi/messages.json @@ -230,5 +230,9 @@ "showBlockedCountLabel": { "message": "Hiện số lượng yêu cầu bị chặn trên biểu tượng thanh công cụ", "description": "Label for a checkbox in the options page" + }, + "findListsPlaceholder": { + "message": "Find lists", + "description": "Placeholder for the input field used to find lists" } } diff --git a/platform/mv3/extension/_locales/zh_CN/messages.json b/platform/mv3/extension/_locales/zh_CN/messages.json index bcc72a5fc3f9b..def4c6ac4a4c9 100644 --- a/platform/mv3/extension/_locales/zh_CN/messages.json +++ b/platform/mv3/extension/_locales/zh_CN/messages.json @@ -230,5 +230,9 @@ "showBlockedCountLabel": { "message": "在工具栏图标上显示拦截请求数", "description": "Label for a checkbox in the options page" + }, + "findListsPlaceholder": { + "message": "Find lists", + "description": "Placeholder for the input field used to find lists" } } diff --git a/platform/mv3/extension/_locales/zh_TW/messages.json b/platform/mv3/extension/_locales/zh_TW/messages.json index d497e91c6be01..7657061ef5404 100644 --- a/platform/mv3/extension/_locales/zh_TW/messages.json +++ b/platform/mv3/extension/_locales/zh_TW/messages.json @@ -230,5 +230,9 @@ "showBlockedCountLabel": { "message": "在工具列圖示上顯示被阻擋的連線請求的數量", "description": "Label for a checkbox in the options page" + }, + "findListsPlaceholder": { + "message": "Find lists", + "description": "Placeholder for the input field used to find lists" } } From f9ce06977d57bd579db0549149a4b8bf4d0141c3 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Mon, 18 Nov 2024 14:05:19 -0500 Subject: [PATCH 0462/1099] [mv3] Fix `removeparam` potentially causing invalid DNR rules --- src/js/static-net-filtering.js | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/js/static-net-filtering.js b/src/js/static-net-filtering.js index bc14d71dbbdb0..7e7e8bd5f2cae 100644 --- a/src/js/static-net-filtering.js +++ b/src/js/static-net-filtering.js @@ -4652,11 +4652,13 @@ StaticNetFilteringEngine.prototype.dnrFromCompiled = function(op, context, ...ar }; } if ( rule.condition.resourceTypes === undefined ) { - rule.condition.resourceTypes = [ - 'main_frame', - 'sub_frame', - 'xmlhttprequest', - ]; + if ( rule.condition.excludedResourceTypes === undefined ) { + rule.condition.resourceTypes = [ + 'main_frame', + 'sub_frame', + 'xmlhttprequest', + ]; + } } // https://github.com/uBlockOrigin/uBOL-home/issues/140 // Mitigate until DNR API flaw is addressed by browser vendors From 114acacd2e4104d2ab51a31d20fdd4a24eb526bb Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Mon, 18 Nov 2024 14:08:30 -0500 Subject: [PATCH 0463/1099] [mv3] Batch changes thru dashboard UI to reduce worker's workload --- platform/mv3/extension/js/filter-lists.js | 65 ++++++++++++++++------- 1 file changed, 47 insertions(+), 18 deletions(-) diff --git a/platform/mv3/extension/js/filter-lists.js b/platform/mv3/extension/js/filter-lists.js index c48f3e357e045..daa647c5e28c9 100644 --- a/platform/mv3/extension/js/filter-lists.js +++ b/platform/mv3/extension/js/filter-lists.js @@ -130,7 +130,9 @@ export function renderFilterLists(rulesetData) { const initializeListEntry = (ruleset, listEntry) => { const on = enabledRulesets.includes(ruleset.id); - dom.prop(qs$(listEntry, ':scope > .detailbar input'), 'checked', on); + if ( dom.cl.has(listEntry, 'toggled') === false ) { + dom.prop(qs$(listEntry, ':scope > .detailbar input'), 'checked', on); + } if ( ruleset.homeURL ) { dom.attr(qs$(listEntry, 'a.support'), 'href', ruleset.homeURL); } @@ -360,6 +362,7 @@ const searchFilterLists = ( ) => { haystack = [ rulesetDetails.name, listEntry.dataset.nodeid, + rulesetDetails.group || '', rulesetDetails.tags || '', ].join(' ').trim(); perListHaystack.set(rulesetDetails, haystack); @@ -379,34 +382,60 @@ dom.on('#findInLists', 'input', searchFilterLists); /******************************************************************************/ -async function applyEnabledRulesets() { - const enabledRulesets = []; - for ( const liEntry of qsa$('#lists .listEntry[data-role="leaf"][data-rulesetid]') ) { - const checked = qs$(liEntry, 'input[type="checkbox"]:checked') !== null; - if ( checked === false ) { continue; } - const { rulesetid } = liEntry.dataset; - if ( dom.cl.has(liEntry, 'fromAdmin') ) { continue; } - enabledRulesets.push(rulesetid); - } +const applyEnabledRulesets = (( ) => { + const apply = async ( ) => { + const enabledRulesets = []; + for ( const liEntry of qsa$('#lists .listEntry[data-role="leaf"][data-rulesetid]') ) { + const checked = qs$(liEntry, 'input[type="checkbox"]:checked') !== null; + if ( checked === false ) { continue; } + const { rulesetid } = liEntry.dataset; + if ( dom.cl.has(liEntry, 'fromAdmin') ) { continue; } + enabledRulesets.push(rulesetid); + } - if ( enabledRulesets.length === 0 ) { return; } + dom.cl.remove('#lists .listEntry.toggled', 'toggled'); + + if ( enabledRulesets.length === 0 ) { return; } + + await sendMessage({ + what: 'applyRulesets', + enabledRulesets, + }); + }; - await sendMessage({ - what: 'applyRulesets', - enabledRulesets, + let timer; + + self.addEventListener('beforeunload', ( ) => { + if ( timer !== undefined ) { return; } + self.clearTimeout(timer); + timer = undefined; + apply(); }); -} + + return function() { + if ( timer !== undefined ) { + self.clearTimeout(timer); + } + timer = self.setTimeout(( ) => { + timer = undefined; + apply(); + }, 997); + } +})(); dom.on('#lists', 'change', '.listEntry input[type="checkbox"]', ev => { const input = ev.target; const listEntry = input.closest('.listEntry'); if ( listEntry === null ) { return; } if ( listEntry.dataset.nodeid !== undefined ) { - let checkAll = input.checked || + const checkAll = input.checked || dom.cl.has(qs$(listEntry, ':scope > .detailbar .checkbox'), 'partial'); - for ( const input of qsa$(listEntry, '.listEntries input') ) { - input.checked = checkAll; + for ( const subListEntry of qsa$(listEntry, ':scope > .listEntries .listEntry[data-rulesetid]') ) { + dom.cl.add(subListEntry, 'toggled'); + dom.prop(qsa$(subListEntry, ':scope > .detailbar input'), 'checked', checkAll); } + } else { + dom.cl.add(listEntry, 'toggled'); } updateNodes(); renderTotalRuleCounts(); From 77ed83ff2f6b35b530301f6ec59ee902d5fb9a31 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Mon, 18 Nov 2024 14:19:21 -0500 Subject: [PATCH 0464/1099] Improve `urlskip=` filter option Automatically upgrade `http:` to `https:` in the resulting URL. Related feedback: https://github.com/uBlockOrigin/uBlock-issues/issues/3206#issuecomment-2480930555 --- src/js/static-net-filtering.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/js/static-net-filtering.js b/src/js/static-net-filtering.js index 7e7e8bd5f2cae..422d5af5d957e 100644 --- a/src/js/static-net-filtering.js +++ b/src/js/static-net-filtering.js @@ -5533,7 +5533,10 @@ function urlSkip(directive, url, blocked, steps) { return; } const urlfinal = new URL(urlout); - if ( urlfinal.protocol !== 'https:' ) { return; } + if ( urlfinal.protocol !== 'https:' ) { + if ( urlfinal.protocol !== 'http:' ) { return; } + urlout = urlout.replace('http', 'https'); + } if ( blocked && redirectBlocked !== true ) { return; } return urlout; } catch(x) { From dfc3c252d29540f16e0b26f9cb8d72efff880841 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Mon, 18 Nov 2024 14:22:02 -0500 Subject: [PATCH 0465/1099] Update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2482e13d7ffa4..4ee6ad46eb1a2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,4 @@ +- [Improve `urlskip=` filter option](https://github.com/gorhill/uBlock/commit/77ed83ff2f) - [Improve `spoof-css` scriptlet](https://github.com/gorhill/uBlock/commit/5f5e3d730f) - [Improve `trusted-set-attr` scriptlet](https://github.com/gorhill/uBlock/commit/c8174d6032) - [Better handle unexpected conditions when deserializing](https://github.com/gorhill/uBlock/commit/4c299bfca9) From 9fb90ad14cbc026536653c2828e3472ec9f7a52d Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Mon, 18 Nov 2024 14:22:27 -0500 Subject: [PATCH 0466/1099] New revision for dev build --- dist/version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dist/version b/dist/version index 060bb16eff91d..5ed06fb6b14ba 100644 --- a/dist/version +++ b/dist/version @@ -1 +1 @@ -1.61.1.4 \ No newline at end of file +1.61.1.5 \ No newline at end of file From 2f2f383c1b3e30960289ee9d3d05c96e7be97da5 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Mon, 18 Nov 2024 14:25:44 -0500 Subject: [PATCH 0467/1099] Make Firefox dev build auto-update --- dist/firefox/updates.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dist/firefox/updates.json b/dist/firefox/updates.json index 89febecae6472..cf2fa66bb6b8a 100644 --- a/dist/firefox/updates.json +++ b/dist/firefox/updates.json @@ -3,9 +3,9 @@ "uBlock0@raymondhill.net": { "updates": [ { - "version": "1.61.1.4", + "version": "1.61.1.5", "browser_specific_settings": { "gecko": { "strict_min_version": "78.0" } }, - "update_link": "https://github.com/gorhill/uBlock/releases/download/1.61.1b4/uBlock0_1.61.1b4.firefox.signed.xpi" + "update_link": "https://github.com/gorhill/uBlock/releases/download/1.61.1b5/uBlock0_1.61.1b5.firefox.signed.xpi" } ] } From f3486275e9bee0b21a767135f0adab8a0b557765 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Tue, 19 Nov 2024 13:16:56 -0500 Subject: [PATCH 0468/1099] [mv3] Fix force-reloading repeatedly when erroring at load time Related issue: https://github.com/uBlockOrigin/uBOL-home/issues/234 --- platform/mv3/extension/js/background.js | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/platform/mv3/extension/js/background.js b/platform/mv3/extension/js/background.js index ea8c9f608c1ce..0455b5a3c4755 100644 --- a/platform/mv3/extension/js/background.js +++ b/platform/mv3/extension/js/background.js @@ -35,7 +35,7 @@ import { adminRead, browser, dnr, - localRead, localWrite, + localRead, localRemove, localWrite, runtime, windows, } from './ext.js'; @@ -407,13 +407,19 @@ async function start() { // https://github.com/uBlockOrigin/uBOL-home/issues/199 // Force a restart of the extension once when an "internal error" occurs start().then(( ) => { - localWrite({ goodStart: true }); + localRemove('goodStart'); + return false; }).catch(reason => { console.trace(reason); - localRead('goodStart').then((bin = {}) => { - if ( bin.goodStart === false ) { return; } - localWrite({ goodStart: false }).then(( ) => { - runtime.reload(); - }); + if ( process.wakeupRun ) { return; } + return localRead('goodStart').then(goodStart => { + if ( goodStart === false ) { + localRemove('goodStart'); + return false; + } + return localWrite('goodStart', false).then(( ) => true); }); +}).then(restart => { + if ( restart !== true ) { return; } + runtime.reload(); }); From 3aac2a7c972d108eefdced5b4a9e540ec732d3e1 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Tue, 19 Nov 2024 13:44:25 -0500 Subject: [PATCH 0469/1099] Import translation work from https://crowdin.com/project/ublock --- .../mv3/extension/_locales/ar/messages.json | 2 +- .../mv3/extension/_locales/bg/messages.json | 2 +- .../extension/_locales/br_FR/messages.json | 2 +- .../mv3/extension/_locales/ca/messages.json | 2 +- .../mv3/extension/_locales/cs/messages.json | 2 +- .../mv3/extension/_locales/da/messages.json | 4 +- .../mv3/extension/_locales/de/messages.json | 2 +- .../mv3/extension/_locales/es/messages.json | 2 +- .../mv3/extension/_locales/et/messages.json | 2 +- .../mv3/extension/_locales/fa/messages.json | 4 +- .../mv3/extension/_locales/fi/messages.json | 2 +- .../mv3/extension/_locales/fr/messages.json | 2 +- .../mv3/extension/_locales/fy/messages.json | 2 +- .../mv3/extension/_locales/he/messages.json | 2 +- .../mv3/extension/_locales/hr/messages.json | 2 +- .../mv3/extension/_locales/it/messages.json | 2 +- .../mv3/extension/_locales/ka/messages.json | 2 +- .../mv3/extension/_locales/ko/messages.json | 4 +- .../mv3/extension/_locales/lv/messages.json | 54 +++++++++---------- .../mv3/extension/_locales/nl/messages.json | 2 +- .../mv3/extension/_locales/pl/messages.json | 2 +- .../extension/_locales/pt_BR/messages.json | 2 +- .../mv3/extension/_locales/ru/messages.json | 2 +- .../mv3/extension/_locales/sk/messages.json | 2 +- .../mv3/extension/_locales/sr/messages.json | 2 +- .../mv3/extension/_locales/sv/messages.json | 2 +- .../mv3/extension/_locales/tr/messages.json | 2 +- .../mv3/extension/_locales/uk/messages.json | 2 +- src/_locales/cy/messages.json | 22 ++++---- src/_locales/lv/messages.json | 8 +-- src/_locales/pt_BR/messages.json | 2 +- 31 files changed, 73 insertions(+), 73 deletions(-) diff --git a/platform/mv3/extension/_locales/ar/messages.json b/platform/mv3/extension/_locales/ar/messages.json index 34bb05e35cad7..b925a42e0bfb9 100644 --- a/platform/mv3/extension/_locales/ar/messages.json +++ b/platform/mv3/extension/_locales/ar/messages.json @@ -232,7 +232,7 @@ "description": "Label for a checkbox in the options page" }, "findListsPlaceholder": { - "message": "Find lists", + "message": "البحث عن القوائم", "description": "Placeholder for the input field used to find lists" } } diff --git a/platform/mv3/extension/_locales/bg/messages.json b/platform/mv3/extension/_locales/bg/messages.json index 0a1fcef9b5dcf..e3d6b2545756d 100644 --- a/platform/mv3/extension/_locales/bg/messages.json +++ b/platform/mv3/extension/_locales/bg/messages.json @@ -232,7 +232,7 @@ "description": "Label for a checkbox in the options page" }, "findListsPlaceholder": { - "message": "Find lists", + "message": "Намиране на списъци", "description": "Placeholder for the input field used to find lists" } } diff --git a/platform/mv3/extension/_locales/br_FR/messages.json b/platform/mv3/extension/_locales/br_FR/messages.json index 5c3cb4830a02c..4db287b541a92 100644 --- a/platform/mv3/extension/_locales/br_FR/messages.json +++ b/platform/mv3/extension/_locales/br_FR/messages.json @@ -232,7 +232,7 @@ "description": "Label for a checkbox in the options page" }, "findListsPlaceholder": { - "message": "Find lists", + "message": "Kavout rolloù", "description": "Placeholder for the input field used to find lists" } } diff --git a/platform/mv3/extension/_locales/ca/messages.json b/platform/mv3/extension/_locales/ca/messages.json index 8c9860785ad04..ac4da9e1d2035 100644 --- a/platform/mv3/extension/_locales/ca/messages.json +++ b/platform/mv3/extension/_locales/ca/messages.json @@ -232,7 +232,7 @@ "description": "Label for a checkbox in the options page" }, "findListsPlaceholder": { - "message": "Find lists", + "message": "Cerca llistes", "description": "Placeholder for the input field used to find lists" } } diff --git a/platform/mv3/extension/_locales/cs/messages.json b/platform/mv3/extension/_locales/cs/messages.json index 059ba02b198e6..5bf5f9d8720f9 100644 --- a/platform/mv3/extension/_locales/cs/messages.json +++ b/platform/mv3/extension/_locales/cs/messages.json @@ -232,7 +232,7 @@ "description": "Label for a checkbox in the options page" }, "findListsPlaceholder": { - "message": "Find lists", + "message": "Najít seznamy", "description": "Placeholder for the input field used to find lists" } } diff --git a/platform/mv3/extension/_locales/da/messages.json b/platform/mv3/extension/_locales/da/messages.json index 5ca16cd167912..7d987b92b3102 100644 --- a/platform/mv3/extension/_locales/da/messages.json +++ b/platform/mv3/extension/_locales/da/messages.json @@ -212,7 +212,7 @@ "description": "This describes the 'complete' filtering mode" }, "noFilteringModeDescription": { - "message": "Liste over værtsnavne, for hvilke ingen filtrering vil ske.", + "message": "Liste over websteder, for hvilke ingen filtrering vil ske.", "description": "A short description for the editable field which lists trusted sites" }, "noFilteringModePlaceholder": { @@ -232,7 +232,7 @@ "description": "Label for a checkbox in the options page" }, "findListsPlaceholder": { - "message": "Find lists", + "message": "Find lister", "description": "Placeholder for the input field used to find lists" } } diff --git a/platform/mv3/extension/_locales/de/messages.json b/platform/mv3/extension/_locales/de/messages.json index b1a7d4fd1544f..bdf6c74bb09c1 100644 --- a/platform/mv3/extension/_locales/de/messages.json +++ b/platform/mv3/extension/_locales/de/messages.json @@ -232,7 +232,7 @@ "description": "Label for a checkbox in the options page" }, "findListsPlaceholder": { - "message": "Find lists", + "message": "Listen suchen", "description": "Placeholder for the input field used to find lists" } } diff --git a/platform/mv3/extension/_locales/es/messages.json b/platform/mv3/extension/_locales/es/messages.json index 5c649d305bba1..89fa0aaddee9d 100644 --- a/platform/mv3/extension/_locales/es/messages.json +++ b/platform/mv3/extension/_locales/es/messages.json @@ -232,7 +232,7 @@ "description": "Label for a checkbox in the options page" }, "findListsPlaceholder": { - "message": "Find lists", + "message": "Encontrar listas", "description": "Placeholder for the input field used to find lists" } } diff --git a/platform/mv3/extension/_locales/et/messages.json b/platform/mv3/extension/_locales/et/messages.json index ad7b35ced04c1..008059cc3c63b 100644 --- a/platform/mv3/extension/_locales/et/messages.json +++ b/platform/mv3/extension/_locales/et/messages.json @@ -232,7 +232,7 @@ "description": "Label for a checkbox in the options page" }, "findListsPlaceholder": { - "message": "Find lists", + "message": "Otsi nimekirju", "description": "Placeholder for the input field used to find lists" } } diff --git a/platform/mv3/extension/_locales/fa/messages.json b/platform/mv3/extension/_locales/fa/messages.json index cfdb831610f5d..ebc8ca22f8f43 100644 --- a/platform/mv3/extension/_locales/fa/messages.json +++ b/platform/mv3/extension/_locales/fa/messages.json @@ -84,7 +84,7 @@ "description": "English: Source code (GPLv3)" }, "aboutContributors": { - "message": "Contributors", + "message": "شرکت کنندگان", "description": "English: Contributors" }, "aboutSourceCode": { @@ -168,7 +168,7 @@ "description": "Text for button which open an external webpage in Support pane" }, "firstRunSectionLabel": { - "message": "Welcome", + "message": "درود", "description": "The header text for the welcome message section" }, "firstRunDescription": { diff --git a/platform/mv3/extension/_locales/fi/messages.json b/platform/mv3/extension/_locales/fi/messages.json index 0daddc50f5a53..ecce31bfecf64 100644 --- a/platform/mv3/extension/_locales/fi/messages.json +++ b/platform/mv3/extension/_locales/fi/messages.json @@ -232,7 +232,7 @@ "description": "Label for a checkbox in the options page" }, "findListsPlaceholder": { - "message": "Find lists", + "message": "Etsi listoja", "description": "Placeholder for the input field used to find lists" } } diff --git a/platform/mv3/extension/_locales/fr/messages.json b/platform/mv3/extension/_locales/fr/messages.json index cd8af002f5622..96c47b8e32218 100644 --- a/platform/mv3/extension/_locales/fr/messages.json +++ b/platform/mv3/extension/_locales/fr/messages.json @@ -232,7 +232,7 @@ "description": "Label for a checkbox in the options page" }, "findListsPlaceholder": { - "message": "Find lists", + "message": "Trouver des listes", "description": "Placeholder for the input field used to find lists" } } diff --git a/platform/mv3/extension/_locales/fy/messages.json b/platform/mv3/extension/_locales/fy/messages.json index a6c66a520a936..4befbce49f5d5 100644 --- a/platform/mv3/extension/_locales/fy/messages.json +++ b/platform/mv3/extension/_locales/fy/messages.json @@ -232,7 +232,7 @@ "description": "Label for a checkbox in the options page" }, "findListsPlaceholder": { - "message": "Find lists", + "message": "Sykje listen", "description": "Placeholder for the input field used to find lists" } } diff --git a/platform/mv3/extension/_locales/he/messages.json b/platform/mv3/extension/_locales/he/messages.json index 2a49c7d181309..e9b9d50d094c6 100644 --- a/platform/mv3/extension/_locales/he/messages.json +++ b/platform/mv3/extension/_locales/he/messages.json @@ -232,7 +232,7 @@ "description": "Label for a checkbox in the options page" }, "findListsPlaceholder": { - "message": "Find lists", + "message": "חיפוש רשימות", "description": "Placeholder for the input field used to find lists" } } diff --git a/platform/mv3/extension/_locales/hr/messages.json b/platform/mv3/extension/_locales/hr/messages.json index ce1be00206e5a..d8918586540c5 100644 --- a/platform/mv3/extension/_locales/hr/messages.json +++ b/platform/mv3/extension/_locales/hr/messages.json @@ -232,7 +232,7 @@ "description": "Label for a checkbox in the options page" }, "findListsPlaceholder": { - "message": "Find lists", + "message": "Pronađi liste", "description": "Placeholder for the input field used to find lists" } } diff --git a/platform/mv3/extension/_locales/it/messages.json b/platform/mv3/extension/_locales/it/messages.json index d2221f3c53620..058376269d5d7 100644 --- a/platform/mv3/extension/_locales/it/messages.json +++ b/platform/mv3/extension/_locales/it/messages.json @@ -232,7 +232,7 @@ "description": "Label for a checkbox in the options page" }, "findListsPlaceholder": { - "message": "Find lists", + "message": "Trova elenchi", "description": "Placeholder for the input field used to find lists" } } diff --git a/platform/mv3/extension/_locales/ka/messages.json b/platform/mv3/extension/_locales/ka/messages.json index 0f95d40652fc8..18931165b03ac 100644 --- a/platform/mv3/extension/_locales/ka/messages.json +++ b/platform/mv3/extension/_locales/ka/messages.json @@ -232,7 +232,7 @@ "description": "Label for a checkbox in the options page" }, "findListsPlaceholder": { - "message": "Find lists", + "message": "სიების მოძიება", "description": "Placeholder for the input field used to find lists" } } diff --git a/platform/mv3/extension/_locales/ko/messages.json b/platform/mv3/extension/_locales/ko/messages.json index a5125f5ad676d..fbdc2048ef4da 100644 --- a/platform/mv3/extension/_locales/ko/messages.json +++ b/platform/mv3/extension/_locales/ko/messages.json @@ -96,7 +96,7 @@ "description": "Link text to translations repo" }, "aboutFilterLists": { - "message": "필터 리스트", + "message": "필터 목록", "description": "Link text to uBO's own filter lists repo" }, "aboutDependencies": { @@ -232,7 +232,7 @@ "description": "Label for a checkbox in the options page" }, "findListsPlaceholder": { - "message": "Find lists", + "message": "목록 찾기", "description": "Placeholder for the input field used to find lists" } } diff --git a/platform/mv3/extension/_locales/lv/messages.json b/platform/mv3/extension/_locales/lv/messages.json index 03dd849089d62..bc81840985140 100644 --- a/platform/mv3/extension/_locales/lv/messages.json +++ b/platform/mv3/extension/_locales/lv/messages.json @@ -4,7 +4,7 @@ "description": "extension name." }, "extShortDesc": { - "message": "Izmēģinājuma, bezatļauju satura aizturētājs. Aiztur reklāmas, izsekotājus, kriptoracējus un vēl uzreiz pēc uzstādīšanas.", + "message": "Bezatļauju satura aizturētājs. Aiztur reklāmas, izsekotājus, kriptoracējus un daudz ko citu uzreiz pēc uzstādīšanas.", "description": "this will be in the Chrome web store: must be 132 characters or less" }, "perRulesetStats": { @@ -24,7 +24,7 @@ "description": "appears as tab name in dashboard" }, "aboutPrivacyPolicy": { - "message": "Privātuma nosacījumi", + "message": "Konfidencialitātes nosacījumi", "description": "Link to privacy policy on GitHub (English)" }, "popupFilteringModeLabel": { @@ -32,7 +32,7 @@ "description": "Label in the popup panel for the current filtering mode" }, "popupTipReport": { - "message": "Report an issue on this website", + "message": "Ziņot par problēmu šajā tīmekļa vietnē", "description": "Tooltip used for the 'chat' icon in the panel" }, "popupTipDashboard": { @@ -64,7 +64,7 @@ "description": "Header for a ruleset section in 'Filter lists pane'" }, "3pGroupAnnoyances": { - "message": "Traucējumi", + "message": "Kaitinoši elementi", "description": "Header for a ruleset section in 'Filter lists pane'" }, "3pGroupMisc": { @@ -104,67 +104,67 @@ "description": "Shown in the About pane" }, "supportS6H": { - "message": "Report a filter issue", + "message": "Ziņot aizturēšanas filtra problēmu", "description": "Header of 'Report a filter issue' section in Support pane" }, "supportS3P1": { - "message": "Report filter issues with specific websites to the uBlockOrigin/uAssets issue tracker. Requires a GitHub account.", + "message": "Ziņot par aizturēšanas filtru problēmām konkrētās vietnēs uBlockOrigin/uAssets problēmu izsekotājam. Nepieciešams GitHub konts.", "description": "First paragraph of 'Filter issues' section in Support pane" }, "supportS6P1S1": { - "message": "To avoid burdening volunteers with duplicate reports, please verify that the issue has not already been reported.", + "message": "Lai izvairītos no brīvprātīgo noslogošanas ar ziņojumiem, kas atkārtojas, lūgums pārbaudīt, ka par šādu problēmu jau nav ziņots.", "description": "A paragraph in the filter issue reporter section" }, "supportFindSpecificButton": { - "message": "Find similar reports", + "message": "Meklēt līdzīgus ziņojumus", "description": "A clickable link in the filter issue reporter section" }, "supportS6URL": { - "message": "Address of the webpage:", + "message": "Tīmekļa lapas adrese:", "description": "Label for the URL of the page" }, "supportS6Select1": { - "message": "The webpage…", + "message": "Tīmekļa lapa…", "description": "Label for widget to select type of issue" }, "supportS6Select1Option0": { - "message": "-- Pick an entry --", + "message": "-- Atlasiet ierakstu --", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option1": { - "message": "Shows ads or ad leftovers", + "message": "Rāda reklāmas vai to paliekas", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option2": { - "message": "Has overlays or other nuisances", + "message": "Ir pārklājumi vai citas neērtības (kaitinoši elementi)", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option3": { - "message": "Detects uBO Lite", + "message": "Nosaka uBO Lite", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option4": { - "message": "Has privacy-related issues", + "message": "Ir ar konfidencialitāti saistītas nebūšanas", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option5": { - "message": "Malfunctions when uBO Lite is enabled", + "message": "Darbības traucējumi, kad uBO Lite ir iespējots", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option6": { - "message": "Opens unwanted tabs or windows", + "message": "Atver nevēlamas cilnes vai logus", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option7": { - "message": "Leads to badware, phishing", + "message": "Noved pie ļaunprātīgas programmatūras, pikšķerēšanas", "description": "An entry in the widget used to select the type of issue" }, "supportS6Checkbox1": { - "message": "Label the webpage as “NSFW” (“Not Safe For Work”)", + "message": "Atzīmēt šo lapu kā “nav droša skatīšanai darbā (NSFW)” (“Not Safe For Work”)", "description": "A checkbox to use for NSFW sites" }, "supportReportSpecificButton": { - "message": "Create new report", + "message": "Izveidot jaunu ziņojumu", "description": "Text for button which open an external webpage in Support pane" }, "firstRunSectionLabel": { @@ -172,7 +172,7 @@ "description": "The header text for the welcome message section" }, "firstRunDescription": { - "message": "Tikko ir uzstādīts uBO Lite. Šeit var izvēlēties noklusējuma aizturēšanas veidu, ko izmantot visām tīmekļa vietnēm.\n\nPēc noklusējuma ir atlasīts Pamata, jo tam nav nepieciešama atļauja lasīt un mainīt datus. Ja uBO Lite šķiet uzticams, ir iespējams piešķirt plašas atļaujas lasīt un mainīt datus visās tīmekļa vietnēs, lai pēc noklusējuma iespējotu pilnīgākas aizturēšanas spējas visās tīmekļa vietnēs.", + "message": "Tikko tika uzstādīts uBO Lite. Šeit var izvēlēties noklusējuma aizturēšanas veidu, ko izmantot visām tīmekļa vietnēm.\n\nPēc noklusējuma ir atlasīts Pamata, jo tam nav nepieciešama atļauja lasīt un mainīt datus. Ja uBO Lite šķiet uzticams, ir iespējams piešķirt plašas atļaujas lasīt un mainīt datus visās tīmekļa vietnēs, lai pēc noklusējuma iespējotu pilnīgākas aizturēšanas spējas visās tīmekļa vietnēs.", "description": "Descriptive text shown at first install time only " }, "defaultFilteringModeSectionLabel": { @@ -196,7 +196,7 @@ "description": "Name of blocking mode 2" }, "filteringMode3Name": { - "message": "pilnīgais", + "message": "pilnais", "description": "Name of blocking mode 3" }, "basicFilteringModeDescription": { @@ -204,19 +204,19 @@ "description": "This describes the 'basic' filtering mode" }, "optimalFilteringModeDescription": { - "message": "Attīstītāka tīkla aizturēšana ar atsevišķu paplašinātu aizturēšanu, izmantojot atlasītos aizturēšanas sarakstus.\n\nNepieciešamas plašas atļaujas lasīt un mainīt visu tīmekļa vietņu datus.", + "message": "Labāka tīkla aizturēšana ar atsevišķu paplašinātu aizturēšanu, izmantojot atlasītos aizturēšanas sarakstus.\n\nNepieciešamas plašas atļaujas lasīt un mainīt visu tīmekļa vietņu datus.", "description": "This describes the 'optimal' filtering mode" }, "completeFilteringModeDescription": { - "message": "Attīstītāka tīkla aizturēšana ar pamata un papildu paplašinātu aizturēšanu, izmantojot atlasītos aizturēšanas sarakstus.\n\nNepieciešamas plašas atļaujas lasīt un mainīt visu tīmekļa vietņu datus.\n\nPamata paplašinātā aizturēšana var izraisīt paaugstinātu tīmekļa vietnes resursu izmantošanu.", + "message": "Pilna tīkla aizturēšana ar pamata un papildu paplašinātu aizturēšanu, izmantojot atlasītos aizturēšanas sarakstus.\n\nNepieciešamas plašas atļaujas lasīt un mainīt visu tīmekļa vietņu datus.\n\nPamata paplašinātā aizturēšana var izraisīt paaugstinātu tīmekļa vietnes resursu izmantošanu.", "description": "This describes the 'complete' filtering mode" }, "noFilteringModeDescription": { - "message": "Saraksts ar saimniekdatoru nosaukumiem, kuriem netiks pielietota aizturēšana", + "message": "Saraksts ar resursdatoru nosaukumiem, kuriem netiks pielietota aizturēšana", "description": "A short description for the editable field which lists trusted sites" }, "noFilteringModePlaceholder": { - "message": "[tikai saimniekdatoru nosaukumi]\nexample.com\ngames.example\n...", + "message": "[tikai resursdatoru nosaukumi]\nexample.com\ngames.example\n...", "description": "Default text for in edit field" }, "behaviorSectionLabel": { @@ -232,7 +232,7 @@ "description": "Label for a checkbox in the options page" }, "findListsPlaceholder": { - "message": "Find lists", + "message": "Atrast sarakstus", "description": "Placeholder for the input field used to find lists" } } diff --git a/platform/mv3/extension/_locales/nl/messages.json b/platform/mv3/extension/_locales/nl/messages.json index 50908b9723445..04734466a7481 100644 --- a/platform/mv3/extension/_locales/nl/messages.json +++ b/platform/mv3/extension/_locales/nl/messages.json @@ -232,7 +232,7 @@ "description": "Label for a checkbox in the options page" }, "findListsPlaceholder": { - "message": "Find lists", + "message": "Lijsten zoeken", "description": "Placeholder for the input field used to find lists" } } diff --git a/platform/mv3/extension/_locales/pl/messages.json b/platform/mv3/extension/_locales/pl/messages.json index 57f1a9b193782..257d2f0f3b680 100644 --- a/platform/mv3/extension/_locales/pl/messages.json +++ b/platform/mv3/extension/_locales/pl/messages.json @@ -232,7 +232,7 @@ "description": "Label for a checkbox in the options page" }, "findListsPlaceholder": { - "message": "Find lists", + "message": "Znajdź listy", "description": "Placeholder for the input field used to find lists" } } diff --git a/platform/mv3/extension/_locales/pt_BR/messages.json b/platform/mv3/extension/_locales/pt_BR/messages.json index c13d3d63d6551..e539229d5c972 100644 --- a/platform/mv3/extension/_locales/pt_BR/messages.json +++ b/platform/mv3/extension/_locales/pt_BR/messages.json @@ -232,7 +232,7 @@ "description": "Label for a checkbox in the options page" }, "findListsPlaceholder": { - "message": "Find lists", + "message": "Achar listas", "description": "Placeholder for the input field used to find lists" } } diff --git a/platform/mv3/extension/_locales/ru/messages.json b/platform/mv3/extension/_locales/ru/messages.json index ed4dea09eb30f..b2e7be8d3185e 100644 --- a/platform/mv3/extension/_locales/ru/messages.json +++ b/platform/mv3/extension/_locales/ru/messages.json @@ -232,7 +232,7 @@ "description": "Label for a checkbox in the options page" }, "findListsPlaceholder": { - "message": "Find lists", + "message": "Найти списки", "description": "Placeholder for the input field used to find lists" } } diff --git a/platform/mv3/extension/_locales/sk/messages.json b/platform/mv3/extension/_locales/sk/messages.json index f319ffdd19382..b31acd2fdc324 100644 --- a/platform/mv3/extension/_locales/sk/messages.json +++ b/platform/mv3/extension/_locales/sk/messages.json @@ -232,7 +232,7 @@ "description": "Label for a checkbox in the options page" }, "findListsPlaceholder": { - "message": "Find lists", + "message": "Nájsť zoznamy", "description": "Placeholder for the input field used to find lists" } } diff --git a/platform/mv3/extension/_locales/sr/messages.json b/platform/mv3/extension/_locales/sr/messages.json index 19357465d89a6..20c22b359194d 100644 --- a/platform/mv3/extension/_locales/sr/messages.json +++ b/platform/mv3/extension/_locales/sr/messages.json @@ -232,7 +232,7 @@ "description": "Label for a checkbox in the options page" }, "findListsPlaceholder": { - "message": "Find lists", + "message": "Пронађи листе", "description": "Placeholder for the input field used to find lists" } } diff --git a/platform/mv3/extension/_locales/sv/messages.json b/platform/mv3/extension/_locales/sv/messages.json index 064f191794f75..857cfc707f316 100644 --- a/platform/mv3/extension/_locales/sv/messages.json +++ b/platform/mv3/extension/_locales/sv/messages.json @@ -232,7 +232,7 @@ "description": "Label for a checkbox in the options page" }, "findListsPlaceholder": { - "message": "Find lists", + "message": "Hitta listor", "description": "Placeholder for the input field used to find lists" } } diff --git a/platform/mv3/extension/_locales/tr/messages.json b/platform/mv3/extension/_locales/tr/messages.json index 8699c2d9fc150..511312a66afdf 100644 --- a/platform/mv3/extension/_locales/tr/messages.json +++ b/platform/mv3/extension/_locales/tr/messages.json @@ -232,7 +232,7 @@ "description": "Label for a checkbox in the options page" }, "findListsPlaceholder": { - "message": "Find lists", + "message": "Liste bul", "description": "Placeholder for the input field used to find lists" } } diff --git a/platform/mv3/extension/_locales/uk/messages.json b/platform/mv3/extension/_locales/uk/messages.json index 876a289449f94..ea7338db5f6f7 100644 --- a/platform/mv3/extension/_locales/uk/messages.json +++ b/platform/mv3/extension/_locales/uk/messages.json @@ -232,7 +232,7 @@ "description": "Label for a checkbox in the options page" }, "findListsPlaceholder": { - "message": "Find lists", + "message": "Знайти списки", "description": "Placeholder for the input field used to find lists" } } diff --git a/src/_locales/cy/messages.json b/src/_locales/cy/messages.json index 6a327d423fa44..02cfc802feb66 100644 --- a/src/_locales/cy/messages.json +++ b/src/_locales/cy/messages.json @@ -236,15 +236,15 @@ "description": "" }, "popupImageRulePrompt": { - "message": "images", + "message": "delweddau", "description": "" }, "popup3pAnyRulePrompt": { - "message": "3rd-party", + "message": "3ydd-parti", "description": "" }, "popup3pPassiveRulePrompt": { - "message": "3rd-party CSS/images", + "message": "CSS/delweddau 3ydd-parti", "description": "" }, "popupInlineScriptRulePrompt": { @@ -312,7 +312,7 @@ "description": "English: Click, Ctrl-click" }, "pickerContextMenuEntry": { - "message": "Block element…", + "message": "Blocio elfen…", "description": "An entry in the browser's contextual menu" }, "settingsCollapseBlockedPrompt": { @@ -464,11 +464,11 @@ "description": "English: Lists of blocked hosts" }, "3pApplyChanges": { - "message": "Apply changes", + "message": "Rhoi newidiadau ar waith", "description": "English: Apply changes" }, "3pGroupDefault": { - "message": "Built-in", + "message": "Mewnol", "description": "Filter lists section name" }, "3pGroupAds": { @@ -488,7 +488,7 @@ "description": "Filter lists section name" }, "3pGroupCookies": { - "message": "Cookie notices", + "message": "Hysbysiadau cwci", "description": "Filter lists section name" }, "3pGroupAnnoyances": { @@ -940,7 +940,7 @@ "description": "Third paragraph of 'Filter issues' section in Support pane" }, "supportS4H": { - "message": "Bug report", + "message": "Adroddiad nam", "description": "Header of 'Bug report' section in Support pane" }, "supportS4P1": { @@ -1032,7 +1032,7 @@ "description": "Link to privacy policy on GitHub (English)" }, "aboutChangelog": { - "message": "Changelog", + "message": "Cofnod newidiadau", "description": "" }, "aboutCode": { @@ -1224,11 +1224,11 @@ "description": "for generic 'Submit' buttons" }, "genericApplyChanges": { - "message": "Apply changes", + "message": "Rhoi newidiadau ar waith", "description": "for generic 'Apply changes' buttons" }, "genericRevert": { - "message": "Revert", + "message": "Gwrthdroi", "description": "for generic 'Revert' buttons" }, "genericBytes": { diff --git a/src/_locales/lv/messages.json b/src/_locales/lv/messages.json index 0775a23ec7dcc..5135003bf11a5 100644 --- a/src/_locales/lv/messages.json +++ b/src/_locales/lv/messages.json @@ -340,7 +340,7 @@ "description": "Section for controlling user interface appearance" }, "settingsThemeLabel": { - "message": "Izskats", + "message": "Motīvs", "description": "Label for checkbox to enable a custom dark theme" }, "settingsThemeAccent0Label": { @@ -480,7 +480,7 @@ "description": "Filter lists section name" }, "3pGroupMalware": { - "message": "Ļaundabīgo programmu domēni", + "message": "Aizsardzība pret ļaunprātīgām (inficētas vai satur vīrusus) vietnēm, drošība", "description": "Filter lists section name" }, "3pGroupSocial": { @@ -552,7 +552,7 @@ "description": "Button in the 'My filters' pane" }, "1pExport": { - "message": "Eksportēt", + "message": "Eksportēt…", "description": "Button in the 'My filters' pane" }, "1pExportFilename": { @@ -636,7 +636,7 @@ "description": "Button in the 'Trusted sites' pane" }, "whitelistExport": { - "message": "Eksportēt", + "message": "Eksportēt…", "description": "Button in the 'Trusted sites' pane" }, "whitelistExportFilename": { diff --git a/src/_locales/pt_BR/messages.json b/src/_locales/pt_BR/messages.json index 7c0790dca68bb..5929b4e56bcc4 100644 --- a/src/_locales/pt_BR/messages.json +++ b/src/_locales/pt_BR/messages.json @@ -4,7 +4,7 @@ "description": "extension name." }, "extShortDesc": { - "message": "Finalmente, um bloqueador eficiente. Com baixo uso de memória e CPU.", + "message": "Finalmente, um bloqueador eficiente. Com baixo uso de CPU e memória.", "description": "this will be in the Chrome web store: must be 132 characters or less" }, "dashboardName": { From bcc058eba75d4cbb2cd0447885f75e0705812883 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Wed, 20 Nov 2024 07:53:52 -0500 Subject: [PATCH 0470/1099] Add `-safebase64` directive in `urlskip=` Related issue: https://github.com/uBlockOrigin/uBlock-issues/issues/3206#issuecomment-2487392846 --- src/js/static-net-filtering.js | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/js/static-net-filtering.js b/src/js/static-net-filtering.js index 422d5af5d957e..f11b2b1472881 100644 --- a/src/js/static-net-filtering.js +++ b/src/js/static-net-filtering.js @@ -5417,6 +5417,8 @@ StaticNetFilteringEngine.prototype.transformRequest = function(fctxt, out = []) * * `-base64`: decode the current string as a base64-encoded string. * + * `-safebase64`: decode the current string as a safe base64-encoded string. + * * `-uricomponent`: decode the current string as a URI encoded string. * * `-blocked`: allow the redirection of blocked requests. By default, blocked @@ -5498,6 +5500,12 @@ function urlSkip(directive, url, blocked, steps) { urlout = self.atob(urlin); continue; } + // Safe Base64 + if ( step === '-safebase64' ) { + urlout = urlin.replace(/[-_]/, safeBase64Replacer); + urlout = self.atob(urlout); + continue; + } // URI component if ( step === '-uricomponent' ) { urlout = self.decodeURIComponent(urlin); @@ -5543,6 +5551,9 @@ function urlSkip(directive, url, blocked, steps) { } } +const safeBase64Map = { '-': '+', '_': '/' }; +const safeBase64Replacer = s => safeBase64Map[s]; + /******************************************************************************/ // https://github.com/uBlockOrigin/uBlock-issues/issues/1626 From fbbd5765c85227d94970f2e4afc925c290c600b5 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Wed, 20 Nov 2024 08:01:27 -0500 Subject: [PATCH 0471/1099] Update changelog --- CHANGELOG.md | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4ee6ad46eb1a2..3016630f05dd4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,9 +1,8 @@ +- [Add `-safebase64` directive to `urlskip=` option](https://github.com/gorhill/uBlock/commit/bcc058eba7) - [Improve `urlskip=` filter option](https://github.com/gorhill/uBlock/commit/77ed83ff2f) - [Improve `spoof-css` scriptlet](https://github.com/gorhill/uBlock/commit/5f5e3d730f) - [Improve `trusted-set-attr` scriptlet](https://github.com/gorhill/uBlock/commit/c8174d6032) -- [Better handle unexpected conditions when deserializing](https://github.com/gorhill/uBlock/commit/4c299bfca9) - [Add support for EasyList `{ remove: true }` cosmetic filter syntax](https://github.com/gorhill/uBlock/commit/ff5fc61753) -- [Fix potential infinite async loop](https://github.com/gorhill/uBlock/commit/335d947c10) (issue found by @Rob--W) - [Keep moving related scriptlets into separate files](https://github.com/gorhill/uBlock/commit/e5a088738d) - [Improve `prevent-xhr` scriptlet](https://github.com/gorhill/uBlock/commit/ce4908b341) - [Improve `trusted-suppress-native-method` scriptlet](https://github.com/gorhill/uBlock/commit/41616df866) @@ -11,6 +10,15 @@ ---------- +# 1.61.2 + +## Fixes / changes + +- [Better handle unexpected conditions when deserializing](https://github.com/gorhill/uBlock/commit/4c299bfca9) +- [Fix potential infinite async loop](https://github.com/gorhill/uBlock/commit/335d947c10) (issue found by @Rob--W) + +---------- + # 1.61.0 ## Fixes / changes From 11bbee93fe1d87eb371307b2977313cbf1ab2acd Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Wed, 20 Nov 2024 08:01:50 -0500 Subject: [PATCH 0472/1099] Ne wrevision for dev build --- dist/version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dist/version b/dist/version index 5ed06fb6b14ba..9dbe8ea6eefd8 100644 --- a/dist/version +++ b/dist/version @@ -1 +1 @@ -1.61.1.5 \ No newline at end of file +1.61.3.0 \ No newline at end of file From 4a8efe1ed840702ced1f7fb849f1cb07b7100917 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Wed, 20 Nov 2024 08:07:52 -0500 Subject: [PATCH 0473/1099] Replace all instances, not just the first one --- src/js/static-net-filtering.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/js/static-net-filtering.js b/src/js/static-net-filtering.js index f11b2b1472881..f4769c2c21702 100644 --- a/src/js/static-net-filtering.js +++ b/src/js/static-net-filtering.js @@ -5502,7 +5502,7 @@ function urlSkip(directive, url, blocked, steps) { } // Safe Base64 if ( step === '-safebase64' ) { - urlout = urlin.replace(/[-_]/, safeBase64Replacer); + urlout = urlin.replace(/[-_]/g, safeBase64Replacer); urlout = self.atob(urlout); continue; } From 8ae33afb760446948fe1f4a1b7e87c343f8a9f33 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Wed, 20 Nov 2024 08:15:59 -0500 Subject: [PATCH 0474/1099] Make Firefox dev build auto-update --- dist/firefox/updates.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dist/firefox/updates.json b/dist/firefox/updates.json index cf2fa66bb6b8a..1a51d97ba7d92 100644 --- a/dist/firefox/updates.json +++ b/dist/firefox/updates.json @@ -3,9 +3,9 @@ "uBlock0@raymondhill.net": { "updates": [ { - "version": "1.61.1.5", + "version": "1.61.3.0", "browser_specific_settings": { "gecko": { "strict_min_version": "78.0" } }, - "update_link": "https://github.com/gorhill/uBlock/releases/download/1.61.1b5/uBlock0_1.61.1b5.firefox.signed.xpi" + "update_link": "https://github.com/gorhill/uBlock/releases/download/1.61.3b0/uBlock0_1.61.3b0.firefox.signed.xpi" } ] } From 6355a17187f70c288ff8354656835bdc0a1e13eb Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Wed, 20 Nov 2024 09:04:52 -0500 Subject: [PATCH 0475/1099] [mv3] Fix flaw breaking scriptlets injection in optimal/basic mode Not all matching scriptlets were injected on a given site in Optimal or Complete mode when default mode was set to Basic or less. A high profile manifestation of this bug was that Youtube ads were not being blocked when using Optimal on Youtube while default mode was Basic. --- platform/mv3/extension/js/scripting-manager.js | 4 ++-- platform/mv3/extension/js/utils.js | 14 ++++++++++++++ 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/platform/mv3/extension/js/scripting-manager.js b/platform/mv3/extension/js/scripting-manager.js index 814343ebe7500..23a6444ea0acd 100644 --- a/platform/mv3/extension/js/scripting-manager.js +++ b/platform/mv3/extension/js/scripting-manager.js @@ -450,8 +450,8 @@ function registerScriptlet(context, scriptletDetails) { targetHostnames = permissionGrantedHostnames; } else { targetHostnames = ut.intersectHostnameIters( - permissionGrantedHostnames, - scriptletHostnames + scriptletHostnames, + permissionGrantedHostnames ); } } diff --git a/platform/mv3/extension/js/utils.js b/platform/mv3/extension/js/utils.js index b32104f946062..7bf6e13503dfd 100644 --- a/platform/mv3/extension/js/utils.js +++ b/platform/mv3/extension/js/utils.js @@ -47,6 +47,13 @@ const isDescendantHostname = (hna, hnb) => { return hna.charCodeAt(hna.length - hnb.length - 1) === 0x2E /* '.' */; }; +/** + * Returns whether a hostname is part of a collection, or is descendant of an + * item in the collection. + * @param hna - the hostname representing the needle. + * @param iterb - an iterable representing the haystack of hostnames. + */ + const isDescendantHostnameOfIter = (hna, iterb) => { const setb = iterb instanceof Set ? iterb : new Set(iterb); if ( setb.has('all-urls') || setb.has('*') ) { return true; } @@ -60,6 +67,13 @@ const isDescendantHostnameOfIter = (hna, iterb) => { return false; }; +/** + * Returns all hostnames in the first collection which are equal or descendant + * of hostnames in the second collection. + * @param itera - an iterable which hostnames must be filtered out. + * @param iterb - an iterable which hostnames must be matched. + */ + const intersectHostnameIters = (itera, iterb) => { const setb = iterb instanceof Set ? iterb : new Set(iterb); if ( setb.has('all-urls') || setb.has('*') ) { return Array.from(itera); } From b2d7bb72c79968894ff3c69abd231c8bef96f3cb Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Wed, 20 Nov 2024 12:55:13 -0500 Subject: [PATCH 0476/1099] [mv3] Write log.txt file to extension folder --- platform/mv3/make-rulesets.js | 2 +- tools/make-mv3.sh | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/platform/mv3/make-rulesets.js b/platform/mv3/make-rulesets.js index 501af6f706aaf..04688f15a0ae3 100644 --- a/platform/mv3/make-rulesets.js +++ b/platform/mv3/make-rulesets.js @@ -1351,7 +1351,7 @@ async function main() { // Log results const logContent = stdOutput.join('\n') + '\n'; - await fs.writeFile(`${cacheDir}/log.txt`, logContent); + await fs.writeFile(`${outputDir}/log.txt`, logContent); } main(); diff --git a/tools/make-mv3.sh b/tools/make-mv3.sh index 58a0505314968..92ce9d445177a 100755 --- a/tools/make-mv3.sh +++ b/tools/make-mv3.sh @@ -161,6 +161,7 @@ if [ "$FULL" = "yes" ]; then mkdir -p "$TMPDIR" cp -R "$DES"/* "$TMPDIR"/ cd "$TMPDIR" > /dev/null + rm -f ./log.txt zip "$PACKAGENAME" -qr ./* cd - > /dev/null cp "$TMPDIR"/"$PACKAGENAME" dist/build/ From 4979aa51f5eaad190acad512ff45ec28fefe1fde Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Thu, 21 Nov 2024 12:54:28 -0500 Subject: [PATCH 0477/1099] [mv3] Do not collect matched rules by default when side-loaded Collecting matched rules when the extension is side-loaded is now opt-in, by enabling "Developer mode" in the dashboard. The reason is to allow the extension to behave same as the official released version when side-loaded. Specifically, as side-loaded extension, uBOL's service worker would wake up due to matched-rule listener even though it would not wake up the worker with same configuration in stable release. --- platform/mv3/extension/dashboard.html | 1 + platform/mv3/extension/js/background.js | 14 +++ platform/mv3/extension/js/config.js | 19 ++-- platform/mv3/extension/js/debug.js | 136 ++++++++++++------------ platform/mv3/extension/js/popup.js | 1 + platform/mv3/extension/js/settings.js | 16 +++ 6 files changed, 108 insertions(+), 79 deletions(-) diff --git a/platform/mv3/extension/dashboard.html b/platform/mv3/extension/dashboard.html index 2169031eb4b1c..c8442cabc76a7 100644 --- a/platform/mv3/extension/dashboard.html +++ b/platform/mv3/extension/dashboard.html @@ -31,6 +31,7 @@

+

diff --git a/platform/mv3/extension/js/background.js b/platform/mv3/extension/js/background.js index 0455b5a3c4755..fcbc9af6036c4 100644 --- a/platform/mv3/extension/js/background.js +++ b/platform/mv3/extension/js/background.js @@ -50,6 +50,7 @@ import { import { getMatchedRules, isSideloaded, + toggleDeveloperMode, ubolLog, } from './debug.js'; @@ -207,6 +208,8 @@ function onMessage(request, sender, callback) { showBlockedCount: rulesetConfig.showBlockedCount, canShowBlockedCount, firstRun: process.firstRun, + isSideloaded, + developerMode: rulesetConfig.developerMode, }); process.firstRun = false; }); @@ -234,6 +237,14 @@ function onMessage(request, sender, callback) { }); return true; + case 'setDeveloperMode': + rulesetConfig.developerMode = request.state; + toggleDeveloperMode(rulesetConfig.developerMode); + saveRulesetConfig().then(( ) => { + callback(); + }); + return true; + case 'popupPanelData': { Promise.all([ getFilteringMode(request.hostname), @@ -248,6 +259,7 @@ function onMessage(request, sender, callback) { hasGreatPowers: results[2], rulesetDetails: results[3], isSideloaded, + developerMode: rulesetConfig.developerMode, }); }); return true; @@ -402,6 +414,8 @@ async function start() { process.firstRun = false; } } + + toggleDeveloperMode(rulesetConfig.developerMode); } // https://github.com/uBlockOrigin/uBOL-home/issues/199 diff --git a/platform/mv3/extension/js/config.js b/platform/mv3/extension/js/config.js index bb74a4416c551..71524e56048bb 100644 --- a/platform/mv3/extension/js/config.js +++ b/platform/mv3/extension/js/config.js @@ -33,6 +33,7 @@ export const rulesetConfig = { enabledRulesets: [ 'default' ], autoReload: true, showBlockedCount: true, + developerMode: false, }; export const process = { @@ -47,12 +48,9 @@ export async function loadRulesetConfig() { if ( sessionData ) { rulesetConfig.version = sessionData.version; rulesetConfig.enabledRulesets = sessionData.enabledRulesets; - rulesetConfig.autoReload = typeof sessionData.autoReload === 'boolean' - ? sessionData.autoReload - : true; - rulesetConfig.showBlockedCount = typeof sessionData.showBlockedCount === 'boolean' - ? sessionData.showBlockedCount - : true; + rulesetConfig.autoReload = sessionData.autoReload ?? true; + rulesetConfig.showBlockedCount = sessionData.showBlockedCount ?? true; + rulesetConfig.developerMode = sessionData.developerMode ?? false; process.wakeupRun = true; return; } @@ -60,12 +58,9 @@ export async function loadRulesetConfig() { if ( localData ) { rulesetConfig.version = localData.version; rulesetConfig.enabledRulesets = localData.enabledRulesets; - rulesetConfig.autoReload = typeof localData.autoReload === 'boolean' - ? localData.autoReload - : true; - rulesetConfig.showBlockedCount = typeof localData.showBlockedCount === 'boolean' - ? localData.showBlockedCount - : true; + rulesetConfig.autoReload = localData.autoReload ?? true; + rulesetConfig.showBlockedCount = localData.showBlockedCount ?? true; + rulesetConfig.developerMode = localData.developerMode ?? false; sessionWrite('rulesetConfig', rulesetConfig); return; } diff --git a/platform/mv3/extension/js/debug.js b/platform/mv3/extension/js/debug.js index 9488260d856c2..0a6ecaffe70ee 100644 --- a/platform/mv3/extension/js/debug.js +++ b/platform/mv3/extension/js/debug.js @@ -19,21 +19,11 @@ Home: https://github.com/gorhill/uBlock */ -import { browser, dnr } from './ext.js'; +import { dnr } from './ext.js'; /******************************************************************************/ -export const isSideloaded = (( ) => { - if ( dnr.onRuleMatchedDebug instanceof Object === false ) { return false; } - const { id } = browser.runtime; - // https://addons.mozilla.org/en-US/firefox/addon/ublock-origin-lite/ - if ( id === 'uBOLite@raymondhill.net' ) { return false; } - // https://chromewebstore.google.com/detail/ddkjiahejlhfcafbddmgiahcphecmpfh - if ( id === 'ddkjiahejlhfcafbddmgiahcphecmpfh' ) { return false; } - // https://microsoftedge.microsoft.com/addons/detail/cimighlppcgcoapaliogpjjdehbnofhn - if ( id === 'cimighlppcgcoapaliogpjjdehbnofhn' ) { return false; } - return true; -})(); +export const isSideloaded = dnr.onRuleMatchedDebug instanceof Object; /******************************************************************************/ @@ -45,66 +35,62 @@ export const ubolLog = (...args) => { /******************************************************************************/ -export const getMatchedRules = (( ) => { - const noopFn = ( ) => Promise.resolve([]); - if ( isSideloaded !== true ) { return noopFn; } - if ( dnr.onRuleMatchedDebug instanceof Object === false ) { return noopFn; } - - const rulesets = new Map(); - const bufferSize = 256; - const matchedRules = new Array(bufferSize); - matchedRules.fill(null); - let writePtr = 0; - - const pruneLongLists = list => { - if ( list.length <= 21 ) { return list; } - return [ ...list.slice(0, 10), '...', ...list.slice(-10) ]; - - }; +const rulesets = new Map(); +const bufferSize = isSideloaded ? 256 : 1; +const matchedRules = new Array(bufferSize); +matchedRules.fill(null); +let writePtr = 0; - const getRuleset = async rulesetId => { - if ( rulesets.has(rulesetId) ) { - return rulesets.get(rulesetId); - } - let rules; - if ( rulesetId === dnr.DYNAMIC_RULESET_ID ) { - rules = await dnr.getDynamicRules().catch(( ) => undefined); - } else { - const response = await fetch(`/rulesets/main/${rulesetId}.json`).catch(( ) => undefined); - if ( response === undefined ) { return; } - rules = await response.json().catch(( ) => undefined); - } - if ( Array.isArray(rules) === false ) { return; } - const ruleset = new Map(); - for ( const rule of rules ) { - const condition = rule.condition; - if ( condition ) { - if ( condition.requestDomains ) { - condition.requestDomains = pruneLongLists(condition.requestDomains); - } - if ( condition.initiatorDomains ) { - condition.initiatorDomains = pruneLongLists(condition.initiatorDomains); - } +const pruneLongLists = list => { + if ( list.length <= 21 ) { return list; } + return [ ...list.slice(0, 10), '...', ...list.slice(-10) ]; + +}; + +const getRuleset = async rulesetId => { + if ( rulesets.has(rulesetId) ) { + return rulesets.get(rulesetId); + } + let rules; + if ( rulesetId === dnr.DYNAMIC_RULESET_ID ) { + rules = await dnr.getDynamicRules().catch(( ) => undefined); + } else { + const response = await fetch(`/rulesets/main/${rulesetId}.json`).catch(( ) => undefined); + if ( response === undefined ) { return; } + rules = await response.json().catch(( ) => undefined); + } + if ( Array.isArray(rules) === false ) { return; } + const ruleset = new Map(); + for ( const rule of rules ) { + const condition = rule.condition; + if ( condition ) { + if ( condition.requestDomains ) { + condition.requestDomains = pruneLongLists(condition.requestDomains); + } + if ( condition.initiatorDomains ) { + condition.initiatorDomains = pruneLongLists(condition.initiatorDomains); } - const ruleId = rule.id; - rule.id = `${rulesetId}/${ruleId}`; - ruleset.set(ruleId, rule); } - rulesets.set(rulesetId, ruleset); - return ruleset; - }; + const ruleId = rule.id; + rule.id = `${rulesetId}/${ruleId}`; + ruleset.set(ruleId, rule); + } + rulesets.set(rulesetId, ruleset); + return ruleset; +}; - const getRuleDetails = async ruleInfo => { - const { rulesetId, ruleId } = ruleInfo.rule; - const ruleset = await getRuleset(rulesetId); - if ( ruleset === undefined ) { return; } - return { request: ruleInfo.request, rule: ruleset.get(ruleId) }; - }; +const getRuleDetails = async ruleInfo => { + const { rulesetId, ruleId } = ruleInfo.rule; + const ruleset = await getRuleset(rulesetId); + if ( ruleset === undefined ) { return; } + return { request: ruleInfo.request, rule: ruleset.get(ruleId) }; +}; - dnr.onRuleMatchedDebug.addListener(ruleInfo => { - matchedRules[writePtr] = ruleInfo; - writePtr = (writePtr + 1) % bufferSize; - }); +/******************************************************************************/ + +export const getMatchedRules = (( ) => { + const noopFn = ( ) => Promise.resolve([]); + if ( isSideloaded !== true ) { return noopFn; } return async tabId => { const promises = []; @@ -124,3 +110,19 @@ export const getMatchedRules = (( ) => { })(); /******************************************************************************/ + +const matchedRuleListener = ruleInfo => { + matchedRules[writePtr] = ruleInfo; + writePtr = (writePtr + 1) % bufferSize; +}; + +export const toggleDeveloperMode = state => { + if ( isSideloaded !== true ) { return; } + if ( state ) { + dnr.onRuleMatchedDebug.addListener(matchedRuleListener); + } else { + dnr.onRuleMatchedDebug.removeListener(matchedRuleListener); + } +}; + +/******************************************************************************/ diff --git a/platform/mv3/extension/js/popup.js b/platform/mv3/extension/js/popup.js index 4242e5bacf332..9a62eca3f18e0 100644 --- a/platform/mv3/extension/js/popup.js +++ b/platform/mv3/extension/js/popup.js @@ -331,6 +331,7 @@ async function init() { dom.cl.toggle('#showMatchedRules', 'enabled', popupPanelData.isSideloaded === true && + popupPanelData.developerMode && typeof currentTab.id === 'number' && isNaN(currentTab.id) === false ); diff --git a/platform/mv3/extension/js/settings.js b/platform/mv3/extension/js/settings.js index 81f55d03cc416..0233674d4bd10 100644 --- a/platform/mv3/extension/js/settings.js +++ b/platform/mv3/extension/js/settings.js @@ -55,6 +55,15 @@ function renderWidgets() { dom.attr(input, 'disabled', ''); } } + + { + dom.prop('#developerMode input[type="checkbox"]', 'checked', + Boolean(cachedRulesetData.developerMode) + ); + if ( cachedRulesetData.isSideloaded ) { + dom.attr('#developerMode', 'hidden', null); + } + } } /******************************************************************************/ @@ -126,6 +135,13 @@ dom.on('#showBlockedCount input[type="checkbox"]', 'change', ev => { }); }); +dom.on('#developerMode input[type="checkbox"]', 'change', ev => { + sendMessage({ + what: 'setDeveloperMode', + state: ev.target.checked, + }); +}); + /******************************************************************************/ function renderTrustedSites() { From 346b5ded7c5c740cffa5ed7df7ad30e5d92d9c8b Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sat, 23 Nov 2024 13:17:13 -0500 Subject: [PATCH 0478/1099] [mv3] Add ability for admins to disable features New managed setting: "disabledFeatures": { "title": "User interface features to disable", "description": "A list of tokens, each of which correspond to a user interface feature to disable.", "type": "array", "items": { "type": "string" } } Supported tokens: - `dashboard`: Prevent access to all dashboard settings - `filteringMode`: Prevent changes to the default filtering mode, or the current filtering mode of any site Related feedback: https://github.com/uBlockOrigin/uBOL-home/discussions/35#discussioncomment-11326086 --- platform/mv3/extension/css/filtering-mode.css | 17 +++++++++++++---- platform/mv3/extension/css/popup.css | 7 +++++++ platform/mv3/extension/css/settings.css | 14 ++++++++++++++ platform/mv3/extension/dashboard.html | 2 +- platform/mv3/extension/js/background.js | 16 +++++++++++++++- platform/mv3/extension/js/popup.js | 10 ++++++++++ platform/mv3/extension/js/settings.js | 13 +++++++++++++ platform/mv3/extension/managed_storage.json | 6 ++++++ platform/mv3/extension/popup.html | 2 +- 9 files changed, 80 insertions(+), 7 deletions(-) diff --git a/platform/mv3/extension/css/filtering-mode.css b/platform/mv3/extension/css/filtering-mode.css index fecb1ac3cf406..f6ed7783197bc 100644 --- a/platform/mv3/extension/css/filtering-mode.css +++ b/platform/mv3/extension/css/filtering-mode.css @@ -13,7 +13,7 @@ border-radius: 30% 15% / 15% 30%; height: 100%; position: absolute; - width: 25%; + width: calc(25% + 2px); z-index: 10; } @@ -39,12 +39,18 @@ .filteringModeSlider span[data-level] { background-color: var(--accent-surface-1); + border: 1px solid var(--accent-surface-1); + box-sizing: border-box; display: inline-flex; height: 30%; margin-left: 1px; width: 25%; } +.filteringModeSlider span[data-level]:first-of-type { + margin-left: 0; + } + .filteringModeSlider.moving span[data-level] { pointer-events: none; } @@ -53,13 +59,13 @@ left: 0; } .filteringModeSlider[data-level="1"] .filteringModeButton { - left: 25%; + left: calc(25% - 1px); } .filteringModeSlider[data-level="2"] .filteringModeButton { - left: 50%; + left: calc(50% - 1px); } .filteringModeSlider[data-level="3"] .filteringModeButton { - left: 75%; + left: calc(75% - 1px); } [dir="rtl"] .filteringModeSlider[data-level="0"] .filteringModeButton { left: 75%; @@ -77,14 +83,17 @@ .filteringModeSlider[data-level="0"] span[data-level] { background-color: var(--surface-2); + border-color: var(--surface-2); } .filteringModeSlider[data-level="1"] span[data-level]:nth-of-type(1) ~ span[data-level] { background-color: var(--surface-2); + border-color: var(--surface-2); } .filteringModeSlider[data-level="2"] span[data-level]:nth-of-type(2) ~ span[data-level] { background-color: var(--surface-2); + border-color: var(--surface-2); } .filteringModeSlider[data-level]:not(.moving) span[data-level]:hover { diff --git a/platform/mv3/extension/css/popup.css b/platform/mv3/extension/css/popup.css index 385a051652f82..cc6824674a18a 100644 --- a/platform/mv3/extension/css/popup.css +++ b/platform/mv3/extension/css/popup.css @@ -63,6 +63,13 @@ hr { font-weight: 600; } +body[data-forbid~="filteringMode"] .filteringModeSlider { + pointer-events: none; + } +body[data-forbid~="dashboard"] #gotoDashboard { + display: none; + } + #filteringModeText { color: var(--ink-3); margin: var(--default-gap-small); diff --git a/platform/mv3/extension/css/settings.css b/platform/mv3/extension/css/settings.css index fb5ba91517ab1..feefcadd8c3ee 100644 --- a/platform/mv3/extension/css/settings.css +++ b/platform/mv3/extension/css/settings.css @@ -1,3 +1,6 @@ +body.loading { + visibility: hidden; + } body .firstRun { display: none; } @@ -13,6 +16,17 @@ p { white-space: pre-line; } +body[data-forbid~="dashboard"] #dashboard-nav [data-pane="settings"], +body[data-forbid~="dashboard"] section[data-pane="settings"], +body[data-forbid~="dashboard"] #dashboard-nav [data-pane="rulesets"], +body[data-forbid~="dashboard"] section[data-pane="rulesets"] { + display: none; + } +body[data-forbid~="filteringMode"] section[data-pane="settings"] > div:has(> h3[data-i18n="defaultFilteringModeSectionLabel"]), +body[data-forbid~="filteringMode"] section[data-pane="settings"] > div:has(> h3[data-i18n="filteringMode0Name"]) { + display: none; + } + #showBlockedCount:has(input[type="checkbox"][disabled]) { opacity: 0.5; } diff --git a/platform/mv3/extension/dashboard.html b/platform/mv3/extension/dashboard.html index c8442cabc76a7..bcd1726bd05a3 100644 --- a/platform/mv3/extension/dashboard.html +++ b/platform/mv3/extension/dashboard.html @@ -16,7 +16,7 @@ - +
From 50ddedb9921b7b3e6e74eb43d200275ed186d697 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sun, 24 Nov 2024 10:24:08 -0500 Subject: [PATCH 0479/1099] Import translation work from https://crowdin.com/project/ublock --- platform/mv3/description/webstore.mk.txt | 30 +- .../mv3/extension/_locales/be/messages.json | 2 +- .../mv3/extension/_locales/mk/messages.json | 90 +++--- .../extension/_locales/pt_PT/messages.json | 2 +- .../mv3/extension/_locales/sq/messages.json | 2 +- .../extension/_locales/zh_CN/messages.json | 2 +- .../extension/_locales/zh_TW/messages.json | 2 +- src/_locales/mk/messages.json | 260 +++++++++--------- src/_locales/zh_TW/messages.json | 26 +- 9 files changed, 208 insertions(+), 208 deletions(-) diff --git a/platform/mv3/description/webstore.mk.txt b/platform/mv3/description/webstore.mk.txt index 7669a5939f98c..10a6944e5f205 100644 --- a/platform/mv3/description/webstore.mk.txt +++ b/platform/mv3/description/webstore.mk.txt @@ -1,30 +1,30 @@ -uBO Lite (uBOL) is a *permission-less* MV3-based content blocker. +uBO Lite (uBOL) е *без дозвола* MV3-базиран блокатор на содржини. -The default ruleset corresponds to uBlock Origin's default filterset: +Стандардниот сет на правила одговара на стандардниот филтер сет на uBlock Origin: -- uBlock Origin's built-in filter lists +- Вградени филтер листи на uBlock Origin - EasyList - EasyPrivacy -- Peter Lowe’s Ad and tracking server list +- Листа на реклами и следачи на Peter Lowe -You can enable more rulesets by visiting the options page -- click the _Cogs_ icon in the popup panel. +Можете да овозможите повеќе сетови на правила посетувајќи ја страницата со опции - кликнете на иконата _запчаник_ во попап панел. -uBOL is entirely declarative, meaning there is no need for a permanent uBOL process for the filtering to occur, and CSS/JS injection-based content filtering is performed reliably by the browser itself rather than by the extension. This means that uBOL itself does not consume CPU/memory resources while content blocking is ongoing -- uBOL's service worker process is required _only_ when you interact with the popup panel or the option pages. +uBOL е целосно декларативен, што значи дека не е потребен траен процес на uBOL за филтрирање да се одвива, а филтрирањето на содржини врз основа на инјекција на CSS/JS се извршува со сигурност од самото браузер, а не од самата екстензија. Ова значи дека самиот uBOL не консумира ресурси на CPU/меморија додека блокирањето на содржини е во тек - процесот на службениот работник на uBOL е потребен _само_ кога ќе е потребен со попап панел или страниците со опции. -uBOL does not require broad "read and modify data" permission at install time, hence its limited capabilities out of the box compared to uBlock Origin or other content blockers requiring broad "read and modify data" permissions at install time. +uBOL не бара широка дозвола за „читање и модификување на податоци“ во време на инсталација, па затоа неговите ограничени можности излезат од кутијата во споредба со uBlock Origin или други блокатори на содржини кои бараат широка „читање и модификување на податоци“ дозволи во време на инсталација. -However, uBOL allows you to *explicitly* grant extended permissions on specific sites of your choice so that it can better filter on those sites using cosmetic filtering and scriptlet injections. +Сепак, uBOL ви овозможува *експлицитно* да доделите проширени дозволи на специфични страници по ваш избор, така што може подобро да филтрира на тие страници користејќи козметичко филтрирање и инјекции на скрипти. -To grant extended permissions on a given site, open the popup panel and pick a higher filtering mode such as Optimal or Complete. +За да доделите проширени дозволи на одредена страница, отворете го исфрлениот панел и изберете повисок режим на филтрирање, како што се Оптимален или Комплетен. -The browser will then warn you about the effects of granting the additional permissions requested by the extension on the current site, and you will have to tell the browser whether you accept or decline the request. +Браузерот ќе ве предупреди за ефектите на задолжителното доделување на дополнителните дозволи кои ги побарала екстензијата на тековната страница, а вие треба да му кажете на браузерот дали ја прифаќате или одбивате побараното. -If you accept uBOL's request for additional permissions on the current site, it will be able to better filter content for the current site. +Ако ја прифатите побараното од uBOL за дополнителни дозволи на тековната страница, тоа ќе може подобро да филтрира содржина за тековната страница. -You can set the default filtering mode from uBOL's options page. If you pick the Optimal or Complete mode as the default one, you will need to grant uBOL the permission to read and modify data on all websites. +Можете да го поставите подразбираниот режим на филтрирање од страницата со опции на uBOL. Ако ја изберете Оптималната или Комплетната верзија како подразбирана, ќе треба да му овозможите на uBOL дозвола да чита и модифицира податоци на сите веб-страници. -Keep in mind this is still a work in progress, with these end goals: +Имајте на ум дека ова сè уште е работа во тек, со следниве завршни цели: -- No broad host permissions at install time -- extended permissions are granted explicitly by the user on a per-site basis. +- Нема широки хост дозволи при инсталација - проширените дозволи се доделуваат експлицитно од корисникот на основа на секоја страница. -- Entirely declarative for reliability and CPU/memory efficiency. +- Целосно декларативен за сигурност и ефикасност на CPU/меморија. diff --git a/platform/mv3/extension/_locales/be/messages.json b/platform/mv3/extension/_locales/be/messages.json index 5453c7b967e9e..3042d674c8b22 100644 --- a/platform/mv3/extension/_locales/be/messages.json +++ b/platform/mv3/extension/_locales/be/messages.json @@ -232,7 +232,7 @@ "description": "Label for a checkbox in the options page" }, "findListsPlaceholder": { - "message": "Find lists", + "message": "Знайсці спісы", "description": "Placeholder for the input field used to find lists" } } diff --git a/platform/mv3/extension/_locales/mk/messages.json b/platform/mv3/extension/_locales/mk/messages.json index 60186c0844080..13a59807181ac 100644 --- a/platform/mv3/extension/_locales/mk/messages.json +++ b/platform/mv3/extension/_locales/mk/messages.json @@ -4,7 +4,7 @@ "description": "extension name." }, "extShortDesc": { - "message": "A permission-less content blocker. Blocks ads, trackers, miners, and more immediately upon installation.", + "message": "Блокатор на содржини без дозволи. Блокира реклами, трекери, мајнери и уште многу повеќе веднаш по инсталацијата.", "description": "this will be in the Chrome web store: must be 132 characters or less" }, "perRulesetStats": { @@ -32,11 +32,11 @@ "description": "Label in the popup panel for the current filtering mode" }, "popupTipReport": { - "message": "Report an issue on this website", + "message": "Пријави проблем на оваа веб-страница", "description": "Tooltip used for the 'chat' icon in the panel" }, "popupTipDashboard": { - "message": "Отварање на Контролна Табла", + "message": "Отварање на Контролна плоча", "description": "English: Click to open the dashboard" }, "popupMoreButton": { @@ -44,7 +44,7 @@ "description": "Label to be used to show popup panel sections" }, "popupLessButton": { - "message": "Less", + "message": "Помалку", "description": "Label to be used to hide popup panel sections" }, "3pGroupDefault": { @@ -64,7 +64,7 @@ "description": "Header for a ruleset section in 'Filter lists pane'" }, "3pGroupAnnoyances": { - "message": "Annoyances", + "message": "Досадувања", "description": "Header for a ruleset section in 'Filter lists pane'" }, "3pGroupMisc": { @@ -76,163 +76,163 @@ "description": "Header for a ruleset section in 'Filter lists pane'" }, "aboutChangelog": { - "message": "Changelog", + "message": "Промени", "description": "" }, "aboutCode": { - "message": "Source code (GPLv3)", + "message": "Изворен код (GPLv3)", "description": "English: Source code (GPLv3)" }, "aboutContributors": { - "message": "Contributors", + "message": "Сов contributors", "description": "English: Contributors" }, "aboutSourceCode": { - "message": "Source code", + "message": "Изворен код", "description": "Link text to source code repo" }, "aboutTranslations": { - "message": "Translations", + "message": "Преводи", "description": "Link text to translations repo" }, "aboutFilterLists": { - "message": "Filter lists", + "message": "Листи за филтрирање", "description": "Link text to uBO's own filter lists repo" }, "aboutDependencies": { - "message": "External dependencies (GPLv3-compatible):", + "message": "Надворешни зависности (компатибилни со GPLv3):", "description": "Shown in the About pane" }, "supportS6H": { - "message": "Report a filter issue", + "message": "Пријави проблем со филтерот", "description": "Header of 'Report a filter issue' section in Support pane" }, "supportS3P1": { - "message": "Report filter issues with specific websites to the uBlockOrigin/uAssets issue tracker. Requires a GitHub account.", + "message": "Пријавете проблеми со филтерите за специфични веб-страници до uBlockOrigin/uAssets issue tracker. Потребен е GitHub профил.", "description": "First paragraph of 'Filter issues' section in Support pane" }, "supportS6P1S1": { - "message": "To avoid burdening volunteers with duplicate reports, please verify that the issue has not already been reported.", + "message": "За да се избегне оптоварување на волонтерите со дупликат пријави, ве молиме проверете дали проблемот веќе не е пријавен.", "description": "A paragraph in the filter issue reporter section" }, "supportFindSpecificButton": { - "message": "Find similar reports", + "message": "Најди слични пријави", "description": "A clickable link in the filter issue reporter section" }, "supportS6URL": { - "message": "Address of the webpage:", + "message": "Адреса на веб-страницата:", "description": "Label for the URL of the page" }, "supportS6Select1": { - "message": "The webpage…", + "message": "Веб-страницата…", "description": "Label for widget to select type of issue" }, "supportS6Select1Option0": { - "message": "-- Pick an entry --", + "message": "-- Изберете внос --", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option1": { - "message": "Shows ads or ad leftovers", + "message": "Покажува реклами или остатоци од реклами", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option2": { - "message": "Has overlays or other nuisances", + "message": "Има преOverlayи или други непријатности", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option3": { - "message": "Detects uBO Lite", + "message": "Детектира uBO Lite", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option4": { - "message": "Has privacy-related issues", + "message": "Има проблеми поврзани со приватноста", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option5": { - "message": "Malfunctions when uBO Lite is enabled", + "message": "Има многу проблеми кога е вклучен uBO Lite", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option6": { - "message": "Opens unwanted tabs or windows", + "message": "Отвора непожелни табови или прозорци", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option7": { - "message": "Leads to badware, phishing", + "message": "Води до злонамерен софтвер, фишинг", "description": "An entry in the widget used to select the type of issue" }, "supportS6Checkbox1": { - "message": "Label the webpage as “NSFW” (“Not Safe For Work”)", + "message": "Означи ја веб-страницата како “NSFW” (“Не е безбедно за работа”)", "description": "A checkbox to use for NSFW sites" }, "supportReportSpecificButton": { - "message": "Create new report", + "message": "Создај нова пријава", "description": "Text for button which open an external webpage in Support pane" }, "firstRunSectionLabel": { - "message": "Welcome", + "message": "Добредојде", "description": "The header text for the welcome message section" }, "firstRunDescription": { - "message": "You have just installed uBO Lite. Here you can choose the default filtering mode to use on all websites.\n\nBy default, Basic mode is selected because it does not require the permission to read and modify data. If you trust uBO Lite, you can give it broad permission to read and modify data on all websites in order to enable more advanced filtering capabilities for all websites by default.", + "message": "Токму ја инсталиравте uBO Lite. Овде можете да изберете режим на филтрирање по подразбирање што ќе го користите на сите веб-страници.\n\nПо подразбирање, е избран режимот Основен затоа што не бара дозвола за читање и модификација на податоци. Ако му верувате на uBO Lite, можете да му дадете ширување на дозволата за читање и модификација на податоци на сите веб-страници за да овозможите попрецизни способности за филтрирање на сите веб-страници по подразбирање.", "description": "Descriptive text shown at first install time only " }, "defaultFilteringModeSectionLabel": { - "message": "Default filtering mode", + "message": "Режим на филтрирање по подразбирање", "description": "The header text for the default filtering mode section" }, "defaultFilteringModeDescription": { - "message": "The default filtering mode will be overridden by per-website filtering modes. You can adjust the filtering mode on any given website according to whichever mode works best on that website. Each mode has its advantages and disadvantages.", + "message": "Режимот на филтрирање по подразбирање ќе биде заменет со режимите на филтрирање за секоја веб-страница. Можете да го прилагодите режимот на филтрирање на која било дадена веб-страница во согласност со тој режим што најдобро функционира на таа веб-страница. Секој режим има свои предности и недостатоци.", "description": "This describes the default filtering mode setting" }, "filteringMode0Name": { - "message": "no filtering", + "message": "без филтрирање", "description": "Name of blocking mode 0" }, "filteringMode1Name": { - "message": "basic", + "message": "основен", "description": "Name of blocking mode 1" }, "filteringMode2Name": { - "message": "optimal", + "message": "оптимален", "description": "Name of blocking mode 2" }, "filteringMode3Name": { - "message": "complete", + "message": "целосен", "description": "Name of blocking mode 3" }, "basicFilteringModeDescription": { - "message": "Basic network filtering from selected filter lists.\n\nDoes not require permission to read and modify data on websites.", + "message": "Основно мрежно филтрирање од селектираните листи со филтри.\n\nНе бара дозвола за читање и модификација на податоци на веб-страниците.", "description": "This describes the 'basic' filtering mode" }, "optimalFilteringModeDescription": { - "message": "Advanced network filtering plus specific extended filtering from selected filter lists.\n\nRequires broad permission to read and modify data on all websites.", + "message": "Напредно мрежно филтрирање плус специфично проширено филтрирање од селектираните листи со филтри.\n\nБара широка дозвола за читање и модификација на податоци на сите веб-страници.", "description": "This describes the 'optimal' filtering mode" }, "completeFilteringModeDescription": { - "message": "Advanced network filtering plus specific and generic extended filtering from selected filter lists.\n\nRequires broad permission to read and modify data on all websites.\n\nGeneric extended filtering may cause higher webpage resources usage.", + "message": "Напредно мрежно филтрирање плус специфично и генералистичко проширено филтрирање од селектираните листи со филтри.\n\nБара широка дозвола за читање и модификација на податоци на сите веб-страници.\n\nГенералистичкото проширено филтрирање може да предизвика поголема потрошувачка на ресурси на веб-страниците.", "description": "This describes the 'complete' filtering mode" }, "noFilteringModeDescription": { - "message": "List of websites for which no filtering will take place.", + "message": "Листата на веб-страници за кои нема да се врши филтрирање.", "description": "A short description for the editable field which lists trusted sites" }, "noFilteringModePlaceholder": { - "message": "[hostnames only]\nexample.com\ngames.example\n...", + "message": "[само домени]\nexample.com\ngames.example\n...", "description": "Default text for in edit field" }, "behaviorSectionLabel": { - "message": "Behavior", + "message": "Понашање", "description": "The header text for the 'Behavior' section" }, "autoReloadLabel": { - "message": "Automatically reload page when changing filtering mode", + "message": "Автоматски освежи ја страницата кога се менува режимот на филтрирање", "description": "Label for a checkbox in the options page" }, "showBlockedCountLabel": { - "message": "Show the number of blocked requests on the toolbar icon", + "message": "Прикажи го бројот на блокирани барања на иконата во алатникот", "description": "Label for a checkbox in the options page" }, "findListsPlaceholder": { - "message": "Find lists", + "message": "Најди листи", "description": "Placeholder for the input field used to find lists" } } diff --git a/platform/mv3/extension/_locales/pt_PT/messages.json b/platform/mv3/extension/_locales/pt_PT/messages.json index b15553d53cfff..be2ee3752f79c 100644 --- a/platform/mv3/extension/_locales/pt_PT/messages.json +++ b/platform/mv3/extension/_locales/pt_PT/messages.json @@ -232,7 +232,7 @@ "description": "Label for a checkbox in the options page" }, "findListsPlaceholder": { - "message": "Find lists", + "message": "Encontrar listas", "description": "Placeholder for the input field used to find lists" } } diff --git a/platform/mv3/extension/_locales/sq/messages.json b/platform/mv3/extension/_locales/sq/messages.json index da316b303a8ef..9aa0b63e6b238 100644 --- a/platform/mv3/extension/_locales/sq/messages.json +++ b/platform/mv3/extension/_locales/sq/messages.json @@ -232,7 +232,7 @@ "description": "Label for a checkbox in the options page" }, "findListsPlaceholder": { - "message": "Find lists", + "message": "Gjej listat", "description": "Placeholder for the input field used to find lists" } } diff --git a/platform/mv3/extension/_locales/zh_CN/messages.json b/platform/mv3/extension/_locales/zh_CN/messages.json index def4c6ac4a4c9..05b31c9d07b20 100644 --- a/platform/mv3/extension/_locales/zh_CN/messages.json +++ b/platform/mv3/extension/_locales/zh_CN/messages.json @@ -232,7 +232,7 @@ "description": "Label for a checkbox in the options page" }, "findListsPlaceholder": { - "message": "Find lists", + "message": "查找列表", "description": "Placeholder for the input field used to find lists" } } diff --git a/platform/mv3/extension/_locales/zh_TW/messages.json b/platform/mv3/extension/_locales/zh_TW/messages.json index 7657061ef5404..096df7c4e23f3 100644 --- a/platform/mv3/extension/_locales/zh_TW/messages.json +++ b/platform/mv3/extension/_locales/zh_TW/messages.json @@ -232,7 +232,7 @@ "description": "Label for a checkbox in the options page" }, "findListsPlaceholder": { - "message": "Find lists", + "message": "尋找清單", "description": "Placeholder for the input field used to find lists" } } diff --git a/src/_locales/mk/messages.json b/src/_locales/mk/messages.json index 35c5285872a98..f0aca830ee686 100644 --- a/src/_locales/mk/messages.json +++ b/src/_locales/mk/messages.json @@ -56,11 +56,11 @@ "description": "appears as tab name in dashboard" }, "supportPageName": { - "message": "Support", + "message": "Поддршка", "description": "appears as tab name in dashboard" }, "assetViewerPageName": { - "message": "uBlock₀ — Asset viewer", + "message": "uBlock₀ — Прегледувач на средства", "description": "Title for the asset viewer page" }, "advancedSettingsPageName": { @@ -68,15 +68,15 @@ "description": "Title for the advanced settings page" }, "popupPowerSwitchInfo": { - "message": "Click: disable/enable uBlock₀ for this site.\n\nCtrl+click: disable uBlock₀ only on this page.", + "message": "Кликнете: деактивирајте/активирајте uBlock₀ за оваа страница.\n\nCtrl+клик: деактивирајте uBlock₀ само на оваа страница.", "description": "English: Click: disable/enable uBlock₀ for this site.\n\nCtrl+click: disable uBlock₀ only on this page." }, "popupPowerSwitchInfo1": { - "message": "Click to disable uBlock₀ for this site.\n\nCtrl+click to disable uBlock₀ only on this page.", + "message": "Кликнете за да го деактивирате uBlock₀ за оваа страница.\n\nCtrl+кликнете за да го деактивирате uBlock₀ само на оваа страница.", "description": "Message to be read by screen readers" }, "popupPowerSwitchInfo2": { - "message": "Click to enable uBlock₀ for this site.", + "message": "Кликнете за да го активирате uBlock₀ за оваа страница.", "description": "Message to be read by screen readers" }, "popupBlockedRequestPrompt": { @@ -128,11 +128,11 @@ "description": "Tooltip used for the logger icon in the panel" }, "popupTipReport": { - "message": "Report an issue on this website", + "message": "Пријави проблем на оваа веб-страница", "description": "Tooltip used for the 'chat' icon in the panel" }, "popupTipNoPopups": { - "message": "Toggle the blocking of all popups for this site", + "message": "Промени ја блокадата на сите поп-упи за оваа страница", "description": "Tooltip for the no-popups per-site switch" }, "popupTipNoPopups1": { @@ -140,23 +140,23 @@ "description": "Tooltip for the no-popups per-site switch" }, "popupTipNoPopups2": { - "message": "Click to no longer block all popups on this site", + "message": "Кликнете за да не блокирате повеќе сите поп-упи на оваа страница", "description": "Tooltip for the no-popups per-site switch" }, "popupTipNoLargeMedia": { - "message": "Toggle the blocking of large media elements for this site", + "message": "Промени ја блокадата на големите медиумски елементи за оваа страница", "description": "Tooltip for the no-large-media per-site switch" }, "popupTipNoLargeMedia1": { - "message": "Click to block large media elements on this site", + "message": "Кликнете за да блокирате големи медиумски елементи на оваа страница", "description": "Tooltip for the no-large-media per-site switch" }, "popupTipNoLargeMedia2": { - "message": "Click to no longer block large media elements on this site", + "message": "Кликнете за да не блокирате повеќе големи медиумски елементи на оваа страница", "description": "Tooltip for the no-large-media per-site switch" }, "popupTipNoCosmeticFiltering": { - "message": "Toggle cosmetic filtering for this site", + "message": "Промени го козметичкото филтрирање за оваа страница", "description": "Tooltip for the no-cosmetic-filtering per-site switch" }, "popupTipNoCosmeticFiltering1": { @@ -168,15 +168,15 @@ "description": "Tooltip for the no-cosmetic-filtering per-site switch" }, "popupTipNoRemoteFonts": { - "message": "Toggle the blocking of remote fonts for this site", + "message": "Промени ја блокадата на далечински фонтови за оваа страница", "description": "Tooltip for the no-remote-fonts per-site switch" }, "popupTipNoRemoteFonts1": { - "message": "Click to block remote fonts on this site", + "message": "Кликнете за да блокирате далечински фонтови на оваа страница", "description": "Tooltip for the no-remote-fonts per-site switch" }, "popupTipNoRemoteFonts2": { - "message": "Click to no longer block remote fonts on this site", + "message": "Кликнете за да не блокирате повеќе далечински фонтови на оваа страница", "description": "Tooltip for the no-remote-fonts per-site switch" }, "popupTipNoScripting1": { @@ -276,11 +276,11 @@ "description": "Example of use: Version 1.26.4" }, "popup3pScriptFilter": { - "message": "script", + "message": "скрипта", "description": "Appears as an option to filter out firewall rows" }, "popup3pFrameFilter": { - "message": "frame", + "message": "рамка", "description": "Appears as an option to filter out firewall rows" }, "pickerCreate": { @@ -316,7 +316,7 @@ "description": "An entry in the browser's contextual menu" }, "settingsCollapseBlockedPrompt": { - "message": "Hide placeholders of blocked elements", + "message": "Скријте ги местата за блокирани елементи", "description": "English: Hide placeholders of blocked elements" }, "settingsIconBadgePrompt": { @@ -328,7 +328,7 @@ "description": "A checkbox in the Settings pane" }, "settingsContextMenuPrompt": { - "message": "Make use of context menu where appropriate", + "message": "Искористете го контекстуалното мени каде што е соодветно", "description": "English: Make use of context menu where appropriate" }, "settingsColorBlindPrompt": { @@ -336,15 +336,15 @@ "description": "English: Color-blind friendly" }, "settingsAppearance": { - "message": "Appearance", + "message": "Изглед", "description": "Section for controlling user interface appearance" }, "settingsThemeLabel": { - "message": "Theme", + "message": "Тема", "description": "Label for checkbox to enable a custom dark theme" }, "settingsThemeAccent0Label": { - "message": "Custom accent color", + "message": "Прилагодена боја на акцентот", "description": "Label for checkbox to pick an accent color" }, "settingsCloudStorageEnabledPrompt": { @@ -356,15 +356,15 @@ "description": "Checkbox to let user access advanced, technical features" }, "settingsPrefetchingDisabledPrompt": { - "message": "Disable pre-fetching (to prevent any connection for blocked network requests)", + "message": "Деактивирајте пред-фаќање (за да се спречат било какви врски за блокирани мрежни барања)", "description": "English: " }, "settingsHyperlinkAuditingDisabledPrompt": { - "message": "Disable hyperlink auditing", + "message": "Деактивирајте проверка на хиперврски", "description": "English: " }, "settingsWebRTCIPAddressHiddenPrompt": { - "message": "Prevent WebRTC from leaking local IP addresses", + "message": "Спречете WebRTC да ги открие локалните IP адреси", "description": "English: " }, "settingPerSiteSwitchGroup": { @@ -392,19 +392,19 @@ "description": "The default state for the per-site no-scripting switch" }, "settingsNoCSPReportsPrompt": { - "message": "Block CSP reports", + "message": "Блокирајте CSP извештаи", "description": "background information: https://github.com/gorhill/uBlock/issues/3150" }, "settingsUncloakCnamePrompt": { - "message": "Uncloak canonical names", + "message": "Откријте канонски имиња", "description": "background information: https://github.com/uBlockOrigin/uBlock-issues/issues/1513" }, "settingsAdvanced": { - "message": "Advanced", + "message": "Напредно", "description": "Section for controlling advanced-user settings" }, "settingsAdvancedSynopsis": { - "message": "Features suitable only for technical users", + "message": "Функции погодни само за технички корисници", "description": "Description of section controlling advanced-user settings" }, "settingsAdvancedUserSettings": { @@ -440,23 +440,23 @@ "description": "A button in the in the _3rd-party filters_ pane" }, "3pParseAllABPHideFiltersPrompt1": { - "message": "Parse and enforce cosmetic filters", + "message": "Парсирајте и спроведете козметички филтри", "description": "English: Parse and enforce Adblock+ element hiding filters." }, "3pParseAllABPHideFiltersInfo": { - "message": "Cosmetic filters serve to hide elements in a web page which are deemed to be a visual nuisance, and which can't be blocked by the network request-based filtering engines.", + "message": "Козметичките филтри служат за сокривање на елементи во веб-страница кои се сметаат за визуелна непријатност, и кои не можат да се блокираат со мрежните мотори за филтрирање базирани на барања.", "description": "Describes the purpose of the 'Parse and enforce cosmetic filters' feature." }, "3pIgnoreGenericCosmeticFilters": { - "message": "Ignore generic cosmetic filters", + "message": "Игнорирајте генералистички козметички филтри", "description": "This will cause uBO to ignore all generic cosmetic filters." }, "3pIgnoreGenericCosmeticFiltersInfo": { - "message": "Generic cosmetic filters are those cosmetic filters which are meant to apply on all web sites. Enabling this option will eliminate the memory and CPU overhead added to web pages as a result of handling generic cosmetic filters.\n\nIt is recommended to enable this option on less powerful devices.", + "message": "Генералистичките козметички филтри се оние козметички филтри што се наменети да се применуваат на сите веб-страници. Вклучувањето на оваа опција ќе ја елиминира оптовареноста на меморијата и ЦПУ-то додадена на веб-страниците како резултат на обработката на генералистичките козметички филтри.\n\nСе препорачува да се вклучи оваа опција на помалку моќни уреди.", "description": "Describes the purpose of the 'Ignore generic cosmetic filters' feature." }, "3pSuspendUntilListsAreLoaded": { - "message": "Suspend network activity until all filter lists are loaded", + "message": "Суспендирајте ја мрежната активност додека не се вчитат сите листи со филтри", "description": "A checkbox in the 'Filter lists' pane" }, "3pListsOfBlockedHostsHeader": { @@ -484,11 +484,11 @@ "description": "Filter lists section name" }, "3pGroupSocial": { - "message": "Social widgets", + "message": "Социјални widgets", "description": "Filter lists section name" }, "3pGroupCookies": { - "message": "Cookie notices", + "message": "Известија за колачиња", "description": "Filter lists section name" }, "3pGroupAnnoyances": { @@ -496,7 +496,7 @@ "description": "Filter lists section name" }, "3pGroupMultipurpose": { - "message": "Multipurpose", + "message": "Многуцелно", "description": "Filter lists section name" }, "3pGroupRegions": { @@ -504,7 +504,7 @@ "description": "Filter lists section name" }, "3pGroupCustom": { - "message": "Custom", + "message": "Прилагодено", "description": "Filter lists section name" }, "3pImport": { @@ -520,11 +520,11 @@ "description": "used as a tooltip for the out-of-date icon beside a list" }, "3pViewContent": { - "message": "view content", + "message": "прегледајте содржина", "description": "used as a tooltip for eye icon beside a list" }, "3pLastUpdate": { - "message": "Last update: {{ago}}.\nClick to force an update.", + "message": "Последно обновување: {{ago}}.\nКликнете за да принудите обновување.", "description": "used as a tooltip for the clock icon beside a list" }, "3pUpdating": { @@ -532,19 +532,19 @@ "description": "used as a tooltip for the spinner icon beside a list" }, "3pNetworkError": { - "message": "A network error prevented the resource from being updated.", + "message": "Мрежна грешка ја спречи обновата на ресурсот.", "description": "used as a tooltip for error icon beside a list" }, "1pTrustWarning": { - "message": "Do not add filters from untrusted sources.", + "message": "Не додавајте филтри од ненадежни извори.", "description": "Warning against copy-pasting filters from random sources" }, "1pEnableMyFiltersLabel": { - "message": "Enable my custom filters", + "message": "Вклучете ги моите прилагодени филтри", "description": "Label for the checkbox use to enable/disable 'My filters' list" }, "1pTrustMyFiltersLabel": { - "message": "Allow custom filters requiring trust", + "message": "Дозволете прилагодени филтри што бараат доверба", "description": "Label for the checkbox use to trust the content of 'My filters' list" }, "1pImport": { @@ -560,7 +560,7 @@ "description": "English: my-ublock-static-filters_{{datetime}}.txt" }, "1pApplyChanges": { - "message": "Apply changes", + "message": "Применете ги промените", "description": "English: Apply changes" }, "rulesPermanentHeader": { @@ -608,7 +608,7 @@ "description": "English: List of your dynamic filtering rules." }, "rulesFormatHint": { - "message": "Rule syntax: source destination type action (full documentation).", + "message": "Синтакса на правило: извор дестинација тип акција (целосна документација).", "description": "English: dynamic rule syntax and full documentation." }, "rulesSort": { @@ -628,7 +628,7 @@ "description": "English: a sort option for list of rules." }, "whitelistPrompt": { - "message": "The trusted site directives dictate on which web pages uBlock Origin should be disabled. One entry per line.", + "message": "Директивите за доверливи веб-страници одредуваат на кои веб-страници uBlock Origin треба да биде исклучен. Еден внос по ред.", "description": "A concise description of the 'Trusted sites' pane." }, "whitelistImport": { @@ -676,19 +676,19 @@ "description": "Appears in the logger's tab selector" }, "loggerReloadTip": { - "message": "Reload the tab content", + "message": "Понови ја содржината на табот", "description": "Tooltip for the reload button in the logger page" }, "loggerDomInspectorTip": { - "message": "Toggle the DOM inspector", + "message": "Вклучи/исклучи DOM инспектор", "description": "Tooltip for the DOM inspector button in the logger page" }, "loggerPopupPanelTip": { - "message": "Toggle the popup panel", + "message": "Вклучи/исклучи панелот за поп-уп", "description": "Tooltip for the popup panel button in the logger page" }, "loggerInfoTip": { - "message": "uBlock Origin wiki: The logger", + "message": "uBlock Origin вики: Логер", "description": "Tooltip for the top-right info label in the logger page" }, "loggerClearTip": { @@ -704,15 +704,15 @@ "description": "Tooltip for the play button in the logger page" }, "loggerRowFiltererButtonTip": { - "message": "Toggle logger filtering", + "message": "Вклучи/исклучи филтрирање на логерот", "description": "Tooltip for the row filterer button in the logger page" }, "logFilterPrompt": { - "message": "filter logger content", + "message": "филтрирајте ја содржината на логерот", "description": "Placeholder string for logger output filtering input field" }, "loggerRowFiltererBuiltinTip": { - "message": "Logger filtering options", + "message": "Опции за филтрирање на логерот", "description": "Tooltip for the button to bring up logger output filtering options" }, "loggerRowFiltererBuiltinNot": { @@ -732,7 +732,7 @@ "description": "A keyword in the built-in row filtering expression" }, "loggerRowFiltererBuiltinModified": { - "message": "modified", + "message": "модифицирано", "description": "A keyword in the built-in row filtering expression" }, "loggerRowFiltererBuiltin1p": { @@ -764,7 +764,7 @@ "description": "Label to identify a context field (typically a hostname)" }, "loggerEntryDetailsRootContext": { - "message": "Root context", + "message": "Корен контекст", "description": "Label to identify a root context field (typically a hostname)" }, "loggerEntryDetailsPartyness": { @@ -836,31 +836,31 @@ "description": "Below this sentence, the filter list(s) in which the filter was found" }, "loggerStaticFilteringFinderSentence2": { - "message": "Static filter could not be found in any of the currently enabled filter lists", + "message": "Статичкиот филтер не може да биде најден во ниедна од моментално овозможените листи со филтри.", "description": "Message to show when a filter cannot be found in any filter lists" }, "loggerSettingDiscardPrompt": { - "message": "Logger entries which do not fulfill all three conditions below will be automatically discarded:", + "message": "Записите во логерот кои не ги исполнуваат сите три услови подолу автоматски ќе бидат игнорирани:", "description": "Logger setting: A sentence to describe the purpose of the settings below" }, "loggerSettingPerEntryMaxAge": { - "message": "Preserve entries from the last {{input}} minutes", + "message": "Зачувајте записи од последните {{input}} минути", "description": "A logger setting" }, "loggerSettingPerTabMaxLoads": { - "message": "Preserve at most {{input}} page loads per tab", + "message": "Зачувајте најмногу {{input}} вчитувања на страници по таб", "description": "A logger setting" }, "loggerSettingPerTabMaxEntries": { - "message": "Preserve at most {{input}} entries per tab", + "message": "Зачувајте најмногу {{input}} записи по таб", "description": "A logger setting" }, "loggerSettingPerEntryLineCount": { - "message": "Use {{input}} lines per entry in vertically expanded mode", + "message": "Користете {{input}} редови по запис во вертикално проширен режим", "description": "A logger setting" }, "loggerSettingHideColumnsPrompt": { - "message": "Hide columns:", + "message": "Скријте колони:", "description": "Logger settings: a sentence to describe the purpose of the checkboxes below" }, "loggerSettingHideColumnTime": { @@ -896,127 +896,127 @@ "description": "Label for radio-button to pick export text format" }, "supportOpenButton": { - "message": "Open", + "message": "Отвори", "description": "Text for button which open an external webpage in Support pane" }, "supportReportSpecificButton": { - "message": "Create new report", + "message": "Создај нова пријава", "description": "Text for button which open an external webpage in Support pane" }, "supportFindSpecificButton": { - "message": "Find similar reports", + "message": "Најди слични пријави", "description": "A clickable link in the filter issue reporter section" }, "supportS1H": { - "message": "Documentation", + "message": "Документација", "description": "Header of 'Documentation' section in Support pane" }, "supportS1P1": { - "message": "Read the documentation at uBlock/wiki to learn about all of uBlock Origin's features.", + "message": "Прочитајте ја документацијата на uBlock/wiki за да научите за сите функции на uBlock Origin.", "description": "First paragraph of 'Documentation' section in Support pane" }, "supportS2H": { - "message": "Questions and support", + "message": "Прашања и поддршка", "description": "Header of 'Questions and support' section in Support pane" }, "supportS2P1": { - "message": "Answers to questions and other kinds of help support is provided on the subreddit /r/uBlockOrigin.", + "message": "Одговори на прашања и други видови поддршка се обезбедени на subreddit /r/uBlockOrigin.", "description": "First paragraph of 'Questions and support' section in Support pane" }, "supportS3H": { - "message": "Filter issues/website is broken", + "message": "Проблеми со филтрирањето/веб-страницата е расипана", "description": "Header of 'Filter issues' section in Support pane" }, "supportS3P1": { - "message": "Report filter issues with specific websites to the uBlockOrigin/uAssets issue tracker. Requires a GitHub account.", + "message": "Пријавете проблеми со филтрите за специфични веб-страници на uBlockOrigin/uAssets issue tracker. Потребен е GitHub профил.", "description": "First paragraph of 'Filter issues' section in Support pane" }, "supportS3P2": { - "message": "Important: Avoid using other similarly-purposed blockers along with uBlock Origin, as this may cause filter issues on specific websites.", + "message": "Важно: Избегнувајте да користите други блокатори со слична намена заедно со uBlock Origin, бидејќи тоа може да предизвика проблеми со филтрањето на специфични веб-страници.", "description": "Second paragraph of 'Filter issues' section in Support pane" }, "supportS3P3": { - "message": "Tips: Be sure your filter lists are up to date. The logger is the primary tool to diagnose filter-related issues.", + "message": "Совети: Проверете дали вашите листи со филтри се ажурирани. Логерот е главниот алат за дијагностицирање на проблемите поврзани со филтрите.", "description": "Third paragraph of 'Filter issues' section in Support pane" }, "supportS4H": { - "message": "Bug report", + "message": "Пријава за грешка", "description": "Header of 'Bug report' section in Support pane" }, "supportS4P1": { - "message": "Report issues with uBlock Origin itself to the uBlockOrigin/uBlock-issue issue tracker. Requires a GitHub account.", + "message": "Пријавете проблеми со самиот uBlock Origin на uBlockOrigin/uBlock-issue issue tracker. Потребен е GitHub профил.", "description": "First paragraph of 'Bug report' section in Support pane" }, "supportS5H": { - "message": "Troubleshooting Information", + "message": "Информации за решавање проблеми", "description": "Header of 'Troubleshooting Information' section in Support pane" }, "supportS5P1": { - "message": "Below is technical information that might be useful when volunteers are trying to help you solve a problem.", + "message": "Подолу е техничка информација која може да биде корисна кога волонтерите се обидуваат да ви помогнат да решите проблем.", "description": "First paragraph of 'Troubleshooting Information' section in Support pane" }, "supportS5P2": { - "message": "Important: Potentially private or sensitive information is redacted by default. Redacted information may make it more difficult to solve a problem.", + "message": "Важно: Потенцијално приватни или осетливи информации се цензурирани по подразбирање. Цензурираните информации можат да го отежнат решавањето на проблемот.", "description": "Second paragraph of 'Troubleshooting Information' section in Support pane" }, "supportS6H": { - "message": "Report a filter issue", + "message": "Пријави проблем со филтерот", "description": "Header of 'Report a filter issue' section in Support pane" }, "supportS6P1S1": { - "message": "To avoid burdening volunteers with duplicate reports, please verify that the issue has not already been reported.", + "message": "За да се избегне оптоварување на волонтерите со дупликат пријави, ве молиме проверете дека проблемот веќе не е пријавен.", "description": "A paragraph in the filter issue reporter section" }, "supportS6P2S1": { - "message": "Filter lists are updated daily. Be sure your issue has not already been addressed in the most recent filter lists.", + "message": "Листите со филтри се обновуваат дневно. Осигурајте се дека вашиот проблем веќе не е решен во најновите листи со филтри.", "description": "A paragraph in the filter issue reporter section" }, "supportS6P2S2": { - "message": "Verify that the issue still exists after reloading the problematic webpage.", + "message": "Проверете дали проблемот сè уште постои по повторно вчитување на проблематичната веб-страница.", "description": "A paragraph in the filter issue reporter section" }, "supportS6URL": { - "message": "Address of the web page:", + "message": "Адреса на веб-страницата:", "description": "Label for the URL of the page" }, "supportS6Select1": { - "message": "The web page…", + "message": "Веб-страницата…", "description": "Label for widget to select type of issue" }, "supportS6Select1Option0": { - "message": "-- Pick an entry --", + "message": "-- Изберете внос --", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option1": { - "message": "Shows ads or ad leftovers", + "message": "Покажува реклами или остатоци од реклами", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option2": { - "message": "Has overlays or other nuisances", + "message": "Има преOverlayи или други непријатности", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option3": { - "message": "Detects uBlock Origin", + "message": "Детектира uBlock Origin", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option4": { - "message": "Has privacy-related issues", + "message": "Има проблеми поврзани со приватноста", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option5": { - "message": "Malfunctions when uBlock Origin is enabled", + "message": "Има многу проблеми кога е вклучен uBlock Origin", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option6": { - "message": "Opens unwanted tabs or windows", + "message": "Отвора непожелни табови или прозорци", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option7": { - "message": "Leads to badware, phishing", + "message": "Води до злонамерен софтвер, фишинг", "description": "An entry in the widget used to select the type of issue" }, "supportS6Checkbox1": { - "message": "Label the web page as “NSFW” (“Not Safe For Work”)", + "message": "Означи ја веб-страницата како “NSFW” (“Не е безбедно за работа”)", "description": "A checkbox to use for NSFW sites" }, "supportRedact": { @@ -1028,7 +1028,7 @@ "description": "Text for 'Unredact' button" }, "aboutPrivacyPolicy": { - "message": "Privacy policy", + "message": "Политика на приватност", "description": "Link to privacy policy on GitHub (English)" }, "aboutChangelog": { @@ -1056,19 +1056,19 @@ "description": "Link text to uBO's own filter lists repo" }, "aboutDependencies": { - "message": "External dependencies (GPLv3-compatible):", + "message": "Надворешни зависности (компатибилни со GPLv3):", "description": "Shown in the About pane" }, "aboutCDNs": { - "message": "uBO's own filter lists are freely hosted on the following CDNs:", + "message": "Листите со филтри на uBO се одвиваат слободно на следните CDN-ови:", "description": "Shown in the About pane" }, "aboutCDNsInfo": { - "message": "A randomly picked CDN is used when a filter list needs to be updated.", + "message": "Се користи случајно одбран CDN кога е потребно да се обнови листата со филтри.", "description": "Shown in the About pane" }, "aboutBackupDataButton": { - "message": "Back up to file…", + "message": "Направи резервна копија во датотека…", "description": "Text for button to create a backup of all settings" }, "aboutBackupFilename": { @@ -1076,23 +1076,23 @@ "description": "English: my-ublock-backup_{{datetime}}.txt" }, "aboutRestoreDataButton": { - "message": "Restore from file…", + "message": "Врати од датотека…", "description": "English: Restore from file..." }, "aboutResetDataButton": { - "message": "Reset to default settings…", + "message": "Врати на подразбирање…", "description": "English: Reset to default settings..." }, "aboutRestoreDataConfirm": { - "message": "All your settings will be overwritten using data backed up on {{time}}, and uBlock₀ will restart.\n\nOverwrite all existing settings using backed up data?", + "message": "Сите ваши поставки ќе бидат надминати со податоците направени на {{time}}, и uBlock₀ ќе се рестартира.\n\nДали сакате да го надминете сите постоечки поставки со резервираните податоци?", "description": "Message asking user to confirm restore" }, "aboutRestoreDataError": { - "message": "The data could not be read or is invalid", + "message": "Податоците не можат да бидат прочитани или се невалидни", "description": "Message to display when an error occurred during restore" }, "aboutResetDataConfirm": { - "message": "All your settings will be removed, and uBlock₀ will restart.\n\nReset uBlock₀ to factory settings?", + "message": "Сите ваши поставки ќе бидат отстранети, и uBlock₀ ќе се рестартира.\n\nДали сакате да го ресетирате uBlock₀ на фабрички поставки?", "description": "Message asking user to confirm reset" }, "errorCantConnectTo": { @@ -1100,11 +1100,11 @@ "description": "English: Network error: {{msg}}" }, "subscriberConfirm": { - "message": "Add the following URL to your custom filter lists?\n\nTitle: \"{{title}}\"\nURL: {{url}}", + "message": "Дали да ја додадете следната URL адреса на вашите прилагодени листи со филтри?\n\nНаслов: \"{{title}}\"\nURL: {{url}}", "description": "No longer used" }, "subscribeButton": { - "message": "Subscribe", + "message": "Пријави се", "description": "For the button used to subscribe to a filter list" }, "elapsedOneMinuteAgo": { @@ -1144,15 +1144,15 @@ "description": "Firefox-specific: appears as 'uBlock₀ (off)'" }, "docblockedTitle": { - "message": "Page blocked", + "message": "Страницата е блокирана", "description": "Used as a title for the document-blocked page" }, "docblockedPrompt1": { - "message": "uBlock Origin has prevented the following page from loading:", + "message": "uBlock Origin го спречи вчитувањето на следната страница:", "description": "Used in the strict-blocking page" }, "docblockedPrompt2": { - "message": "Because of the following filter:", + "message": "Поради следниот филтер:", "description": "Used in the strict-blocking page" }, "docblockedNoParamsPrompt": { @@ -1172,11 +1172,11 @@ "description": "English: Close this window" }, "docblockedDontWarn": { - "message": "Don't warn me again about this site", + "message": "Не ми предупредувај повторно за оваа веб-страница", "description": "Label for checkbox in document-blocked page" }, "docblockedProceed": { - "message": "Disable strict blocking for {{hostname}}", + "message": "Исклучи строго блокирање за {{hostname}}", "description": "English: Disable strict blocking for {{hostname}} ..." }, "docblockedDisableTemporary": { @@ -1188,23 +1188,23 @@ "description": "English: Permanently" }, "docblockedDisable": { - "message": "Proceed", + "message": "Продолжи", "description": "Button text to navigate to the blocked page" }, "docblockedRedirectPrompt": { - "message": "The blocked page wants to redirect to another site. If you choose to proceed, you will navigate directly to: {{url}}", + "message": "Блокираната страница сака да ве пренасочи на друга веб-страница. Ако изберете да продолжите, директно ќе навигирате до: {{url}}", "description": "Text warning about an incoming redirect" }, "cloudPush": { - "message": "Export to cloud storage", + "message": "Извези во облачно складиште", "description": "tooltip" }, "cloudPull": { - "message": "Import from cloud storage", + "message": "Импортирај од облачно складиште", "description": "tooltip" }, "cloudPullAndMerge": { - "message": "Import from cloud storage and merge with current settings", + "message": "Импортирај од облачно складиште и спои со тековните поставки", "description": "tooltip" }, "cloudNoData": { @@ -1216,11 +1216,11 @@ "description": "used as a prompt for the user to provide a custom device name" }, "advancedSettingsWarning": { - "message": "Warning! Change these advanced settings at your own risk.", + "message": "Предупредување! Менувањето на овие напредни поставки е на ваша сопствена одговорност.", "description": "A warning to users at the top of 'Advanced settings' page" }, "genericSubmit": { - "message": "Submit", + "message": "Испрати", "description": "for generic 'Submit' buttons" }, "genericApplyChanges": { @@ -1236,19 +1236,19 @@ "description": "" }, "contextMenuBlockElementInFrame": { - "message": "Block element in frame…", + "message": "Блокирај елемент во рамката…", "description": "An entry in the browser's contextual menu" }, "contextMenuSubscribeToList": { - "message": "Subscribe to filter list…", + "message": "Пријави се на списокот со филтри…", "description": "An entry in the browser's contextual menu" }, "contextMenuTemporarilyAllowLargeMediaElements": { - "message": "Temporarily allow large media elements", + "message": "Привремено дозволи големи медиумски елементи", "description": "A context menu entry, present when large media elements have been blocked on the current site" }, "contextMenuViewSource": { - "message": "View source code…", + "message": "Прикажи изворен код…", "description": "A context menu entry, to view the source code of the target resource" }, "shortcutCapturePlaceholder": { @@ -1256,7 +1256,7 @@ "description": "Placeholder string for input field used to capture a keyboard shortcut" }, "genericMergeViewScrollLock": { - "message": "Toggle locked scrolling", + "message": "Промени заклучување на скролувањето", "description": "Tooltip for the button used to lock scrolling between the views in the 'My rules' pane" }, "genericCopyToClipboard": { @@ -1264,15 +1264,15 @@ "description": "Label for buttons used to copy something to the clipboard" }, "genericSelectAll": { - "message": "Select all", + "message": "Избери сè", "description": "Label for buttons used to select all text in editor" }, "toggleCosmeticFiltering": { - "message": "Toggle cosmetic filtering", + "message": "Промени козметичко филтрирање", "description": "Label for keyboard shortcut used to toggle cosmetic filtering" }, "toggleJavascript": { - "message": "Toggle JavaScript", + "message": "Промени JavaScript", "description": "Label for keyboard shortcut used to toggle no-scripting switch" }, "relaxBlockingMode": { @@ -1280,7 +1280,7 @@ "description": "Label for keyboard shortcut used to relax blocking mode" }, "storageUsed": { - "message": "Место вземеноЧ {{value}} {{unit}}", + "message": "Место вземено: {{value}} {{unit}}", "description": " In Setting pane, renders as (example): Storage used: 13.2 MB" }, "KB": { @@ -1296,15 +1296,15 @@ "description": "short for 'gigabytes'" }, "clickToLoad": { - "message": "Click to load", + "message": "Кликнете за да вчитате", "description": "Message used in frame placeholders" }, "linterMainReport": { - "message": "Errors: {{count}}", + "message": "Грешки: {{count}}", "description": "Summary of number of errors as reported by the linter " }, "unprocessedRequestTooltip": { - "message": "Could not filter properly at browser launch. Reload the page to ensure proper filtering.", + "message": "Не можеше да се филтрира правилно при стартување на прелистувачот. Повторно вчитате ја страницата за да осигурите правилно филтрирање.", "description": "A warning which will appear in the popup panel if needed" }, "dummy": { diff --git a/src/_locales/zh_TW/messages.json b/src/_locales/zh_TW/messages.json index 05ec46212f2d8..871a414a90737 100644 --- a/src/_locales/zh_TW/messages.json +++ b/src/_locales/zh_TW/messages.json @@ -4,7 +4,7 @@ "description": "extension name." }, "extShortDesc": { - "message": "終於,有款僅佔用少量 CPU 及記憶體的高效率阻擋器。", + "message": "終於,有一款僅使用少量 CPU 及記憶體的高效能攔截器。", "description": "this will be in the Chrome web store: must be 132 characters or less" }, "dashboardName": { @@ -176,7 +176,7 @@ "description": "Tooltip for the no-remote-fonts per-site switch" }, "popupTipNoRemoteFonts2": { - "message": "點擊以解除封鎖此網站的遠端字體", + "message": "點擊以解除封鎖此網站的遠端字型", "description": "Tooltip for the no-remote-fonts per-site switch" }, "popupTipNoScripting1": { @@ -200,7 +200,7 @@ "description": "Caption for the no-cosmetic-filtering per-site switch" }, "popupNoRemoteFonts_v2": { - "message": "遠端字體", + "message": "遠端字型", "description": "Caption for the no-remote-fonts per-site switch" }, "popupNoScripting_v2": { @@ -276,11 +276,11 @@ "description": "Example of use: Version 1.26.4" }, "popup3pScriptFilter": { - "message": "指令碼", + "message": "程式碼", "description": "Appears as an option to filter out firewall rows" }, "popup3pFrameFilter": { - "message": "框架", + "message": "框架元素", "description": "Appears as an option to filter out firewall rows" }, "pickerCreate": { @@ -356,7 +356,7 @@ "description": "Checkbox to let user access advanced, technical features" }, "settingsPrefetchingDisabledPrompt": { - "message": "停用「預先取回連結」功能(避免連接至已阻擋的網路請求)", + "message": "停用「預先取回連結」(避免連接至已阻擋的網路請求)", "description": "English: " }, "settingsHyperlinkAuditingDisabledPrompt": { @@ -364,7 +364,7 @@ "description": "English: " }, "settingsWebRTCIPAddressHiddenPrompt": { - "message": "防止 WebRTC 洩漏本地 IP 位址", + "message": "防止「網頁即時通訊」洩漏本地 IP 位址", "description": "English: " }, "settingPerSiteSwitchGroup": { @@ -384,7 +384,7 @@ "description": "" }, "settingsNoRemoteFontsPrompt": { - "message": "封鎖遠端字體", + "message": "封鎖遠端字型", "description": "" }, "settingsNoScriptingPrompt": { @@ -396,7 +396,7 @@ "description": "background information: https://github.com/gorhill/uBlock/issues/3150" }, "settingsUncloakCnamePrompt": { - "message": "揭露網域真實名稱", + "message": "揭露網域「正規名稱」", "description": "background information: https://github.com/uBlockOrigin/uBlock-issues/issues/1513" }, "settingsAdvanced": { @@ -404,7 +404,7 @@ "description": "Section for controlling advanced-user settings" }, "settingsAdvancedSynopsis": { - "message": "僅適合技術性使用者的功能", + "message": "僅合適技術使用者的功能", "description": "Description of section controlling advanced-user settings" }, "settingsAdvancedUserSettings": { @@ -452,7 +452,7 @@ "description": "This will cause uBO to ignore all generic cosmetic filters." }, "3pIgnoreGenericCosmeticFiltersInfo": { - "message": "「通用元素隱藏過濾規則」是會套用在所有網站上的網頁元素過濾規則。啟用此選項會消除網頁處理此類規則時增加的額外記憶體與 CPU 使用量。\n\n建議在擁有較低效能的裝置上啟用此選項。", + "message": "「通用元素過濾規則」是會套用在所有網站上的網頁元素過濾規則。啟用此選項會消除網頁處理此類規則時增加的額外記憶體與 CPU 使用量。\n\n建議在效能較弱的裝置上啟用此選項。", "description": "Describes the purpose of the 'Ignore generic cosmetic filters' feature." }, "3pSuspendUntilListsAreLoaded": { @@ -480,11 +480,11 @@ "description": "Filter lists section name" }, "3pGroupMalware": { - "message": "惡意軟體保護及保安", + "message": "惡意軟體保護、保安", "description": "Filter lists section name" }, "3pGroupSocial": { - "message": "社交媒體小工具", + "message": "社交小工具", "description": "Filter lists section name" }, "3pGroupCookies": { From e43cb6771ae6b0574579b8aae1464a5e624b30a0 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Mon, 25 Nov 2024 12:31:50 -0500 Subject: [PATCH 0480/1099] [mv3] Open options page in tab in Firefox --- platform/mv3/firefox/manifest.json | 1 + 1 file changed, 1 insertion(+) diff --git a/platform/mv3/firefox/manifest.json b/platform/mv3/firefox/manifest.json index 723ef0fc8ba5b..e191e3c6be43c 100644 --- a/platform/mv3/firefox/manifest.json +++ b/platform/mv3/firefox/manifest.json @@ -37,6 +37,7 @@ "manifest_version": 3, "name": "__MSG_extName__", "options_ui": { + "open_in_tab": true, "page": "dashboard.html" }, "optional_permissions": [ From adced29b5bd26165ca89c4562a048c7e73a667f6 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Wed, 27 Nov 2024 15:47:17 -0500 Subject: [PATCH 0481/1099] Improve `trusted-replace-argument` scriptlet As discussed with filter list maintainers, added ability to partially replace an argument using the `repl:` prefix. Updated documentation: --- @scriptlet trusted-replace-argument.js @description Replace an argument passed to a method. Requires a trusted source. @param propChain The property chain to the function which argument must be replaced when called. @param argposRaw The zero-based position of the argument in the argument list. Use a negative number for a position relative to the last argument. @param argraw The replacement value, validated using the same heuristic as with the `set-constant.js` scriptlet. If the replacement value matches `json:...`, the value will be the json-parsed string after `json:`. If the replacement value matches `repl:/.../.../`, the target argument will be replaced according the regex-replacement directive following `repl:` @param [, condition, pattern] Optional. The replacement will occur only when pattern matches the target argument. --- Aditionally, more scriptlets moved into their own files. --- assets/resources/attribute.js | 2 - assets/resources/base.js | 2 - assets/resources/cookie.js | 2 - assets/resources/localstorage.js | 2 - assets/resources/parse-replace.js | 54 ++++ assets/resources/proxy-apply.js | 109 +++++++ assets/resources/replace-argument.js | 105 +++++++ assets/resources/run-at.js | 2 - assets/resources/safe-self.js | 2 - assets/resources/scriptlets.js | 406 +-------------------------- assets/resources/set-constant.js | 287 +++++++++++++++++++ assets/resources/shared.js | 44 +++ assets/resources/spoof-css.js | 2 - src/js/arglist-parser.js | 116 ++++++++ src/js/static-filtering-parser.js | 103 +------ tools/make-nodejs.sh | 1 + 16 files changed, 723 insertions(+), 516 deletions(-) create mode 100644 assets/resources/parse-replace.js create mode 100644 assets/resources/proxy-apply.js create mode 100644 assets/resources/replace-argument.js create mode 100644 assets/resources/set-constant.js create mode 100644 assets/resources/shared.js create mode 100644 src/js/arglist-parser.js diff --git a/assets/resources/attribute.js b/assets/resources/attribute.js index 7873183de16b4..b6ecdf7a3e597 100644 --- a/assets/resources/attribute.js +++ b/assets/resources/attribute.js @@ -18,8 +18,6 @@ Home: https://github.com/gorhill/uBlock - The scriptlets below are meant to be injected only into a - web page context. */ import { registerScriptlet } from './base.js'; diff --git a/assets/resources/base.js b/assets/resources/base.js index dc677b7cb794c..2c54418a777d8 100644 --- a/assets/resources/base.js +++ b/assets/resources/base.js @@ -18,8 +18,6 @@ Home: https://github.com/gorhill/uBlock - The scriptlets below are meant to be injected only into a - web page context. */ export const registeredScriptlets = []; diff --git a/assets/resources/cookie.js b/assets/resources/cookie.js index 1be419121f5b5..3fbeb1858d122 100644 --- a/assets/resources/cookie.js +++ b/assets/resources/cookie.js @@ -18,8 +18,6 @@ Home: https://github.com/gorhill/uBlock - The scriptlets below are meant to be injected only into a - web page context. */ import { registerScriptlet } from './base.js'; diff --git a/assets/resources/localstorage.js b/assets/resources/localstorage.js index 6ea13df73a49e..73e41546e135d 100644 --- a/assets/resources/localstorage.js +++ b/assets/resources/localstorage.js @@ -18,8 +18,6 @@ Home: https://github.com/gorhill/uBlock - The scriptlets below are meant to be injected only into a - web page context. */ import { getSafeCookieValuesFn } from './cookie.js'; diff --git a/assets/resources/parse-replace.js b/assets/resources/parse-replace.js new file mode 100644 index 0000000000000..da638735f7140 --- /dev/null +++ b/assets/resources/parse-replace.js @@ -0,0 +1,54 @@ +/******************************************************************************* + + uBlock Origin - a comprehensive, efficient content blocker + Copyright (C) 2019-present Raymond Hill + + 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/}. + + Home: https://github.com/gorhill/uBlock + +*/ + +import { createArglistParser } from './shared.js'; +import { registerScriptlet } from './base.js'; + +/******************************************************************************/ + +export function parseReplaceFn(s) { + if ( s.charCodeAt(0) !== 0x2F /* / */ ) { return; } + const parser = createArglistParser('/'); + parser.nextArg(s, 1); + let pattern = s.slice(parser.argBeg, parser.argEnd); + if ( parser.transform ) { + pattern = parser.normalizeArg(pattern); + } + if ( pattern === '' ) { return; } + parser.nextArg(s, parser.separatorEnd); + let replacement = s.slice(parser.argBeg, parser.argEnd); + if ( parser.separatorEnd === parser.separatorBeg ) { return; } + if ( parser.transform ) { + replacement = parser.normalizeArg(replacement); + } + const flags = s.slice(parser.separatorEnd); + try { + return { re: new RegExp(pattern, flags), replacement }; + } catch(_) { + } +} +registerScriptlet(parseReplaceFn, { + name: 'parse-replace.fn', + dependencies: [ + createArglistParser, + ], +}); diff --git a/assets/resources/proxy-apply.js b/assets/resources/proxy-apply.js new file mode 100644 index 0000000000000..09d357f9052b6 --- /dev/null +++ b/assets/resources/proxy-apply.js @@ -0,0 +1,109 @@ +/******************************************************************************* + + uBlock Origin - a comprehensive, efficient content blocker + Copyright (C) 2019-present Raymond Hill + + 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/}. + + Home: https://github.com/gorhill/uBlock + +*/ + +import { registerScriptlet } from './base.js'; + +/******************************************************************************/ + +export function proxyApplyFn( + target = '', + handler = '' +) { + let context = globalThis; + let prop = target; + for (;;) { + const pos = prop.indexOf('.'); + if ( pos === -1 ) { break; } + context = context[prop.slice(0, pos)]; + if ( context instanceof Object === false ) { return; } + prop = prop.slice(pos+1); + } + const fn = context[prop]; + if ( typeof fn !== 'function' ) { return; } + if ( proxyApplyFn.CtorContext === undefined ) { + proxyApplyFn.ctorContexts = []; + proxyApplyFn.CtorContext = class { + constructor(...args) { + this.init(...args); + } + init(callFn, callArgs) { + this.callFn = callFn; + this.callArgs = callArgs; + return this; + } + reflect() { + const r = Reflect.construct(this.callFn, this.callArgs); + this.callFn = this.callArgs = undefined; + proxyApplyFn.ctorContexts.push(this); + return r; + } + static factory(...args) { + return proxyApplyFn.ctorContexts.length !== 0 + ? proxyApplyFn.ctorContexts.pop().init(...args) + : new proxyApplyFn.CtorContext(...args); + } + }; + proxyApplyFn.applyContexts = []; + proxyApplyFn.ApplyContext = class { + constructor(...args) { + this.init(...args); + } + init(callFn, thisArg, callArgs) { + this.callFn = callFn; + this.thisArg = thisArg; + this.callArgs = callArgs; + return this; + } + reflect() { + const r = Reflect.apply(this.callFn, this.thisArg, this.callArgs); + this.callFn = this.thisArg = this.callArgs = undefined; + proxyApplyFn.applyContexts.push(this); + return r; + } + static factory(...args) { + return proxyApplyFn.applyContexts.length !== 0 + ? proxyApplyFn.applyContexts.pop().init(...args) + : new proxyApplyFn.ApplyContext(...args); + } + }; + } + const fnStr = fn.toString(); + const toString = (function toString() { return fnStr; }).bind(null); + const proxyDetails = { + apply(target, thisArg, args) { + return handler(proxyApplyFn.ApplyContext.factory(target, thisArg, args)); + }, + get(target, prop) { + if ( prop === 'toString' ) { return toString; } + return Reflect.get(target, prop); + }, + }; + if ( fn.prototype?.constructor === fn ) { + proxyDetails.construct = function(target, args) { + return handler(proxyApplyFn.CtorContext.factory(target, args)); + }; + } + context[prop] = new Proxy(fn, proxyDetails); +} +registerScriptlet(proxyApplyFn, { + name: 'proxy-apply.fn', +}); diff --git a/assets/resources/replace-argument.js b/assets/resources/replace-argument.js new file mode 100644 index 0000000000000..150bf047b8a39 --- /dev/null +++ b/assets/resources/replace-argument.js @@ -0,0 +1,105 @@ +/******************************************************************************* + + uBlock Origin - a comprehensive, efficient content blocker + Copyright (C) 2019-present Raymond Hill + + 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/}. + + Home: https://github.com/gorhill/uBlock + +*/ + +import { parseReplaceFn } from './parse-replace.js'; +import { proxyApplyFn } from './proxy-apply.js'; +import { registerScriptlet } from './base.js'; +import { safeSelf } from './safe-self.js'; +import { validateConstantFn } from './set-constant.js'; + +/** + * @scriptlet trusted-replace-argument.js + * + * @description + * Replace an argument passed to a method. Requires a trusted source. + * + * @param propChain + * The property chain to the function which argument must be replaced when + * called. + * + * @param argposRaw + * The zero-based position of the argument in the argument list. Use a negative + * number for a position relative to the last argument. + * + * @param argraw + * The replacement value, validated using the same heuristic as with the + * `set-constant.js` scriptlet. + * If the replacement value matches `json:...`, the value will be the + * json-parsed string after `json:`. + * If the replacement value matches `repl:/.../.../`, the target argument will + * be replaced according the regex-replacement directive following `repl:` + * + * @param [, condition, pattern] + * Optional. The replacement will occur only when pattern matches the target + * argument. + * + * */ + +export function trustedReplaceArgument( + propChain = '', + argposRaw = '', + argraw = '' +) { + if ( propChain === '' ) { return; } + const safe = safeSelf(); + const logPrefix = safe.makeLogPrefix('trusted-replace-argument', propChain, argposRaw, argraw); + const argoffset = parseInt(argposRaw, 10) || 0; + const extraArgs = safe.getExtraArgs(Array.from(arguments), 3); + const replacer = argraw.startsWith('repl:/') && + parseReplaceFn(argraw.slice(5)) || undefined; + const value = replacer === undefined && + validateConstantFn(true, argraw, extraArgs) || undefined; + const reCondition = extraArgs.condition + ? safe.patternToRegex(extraArgs.condition) + : /^/; + proxyApplyFn(propChain, function(context) { + const { callArgs } = context; + if ( argposRaw === '' ) { + safe.uboLog(logPrefix, `Arguments:\n${callArgs.join('\n')}`); + return context.reflect(); + } + const argpos = argoffset >= 0 ? argoffset : callArgs.length - argoffset; + if ( argpos < 0 || argpos >= callArgs.length ) { + return context.reflect(); + } + const argBefore = callArgs[argpos]; + if ( safe.RegExp_test.call(reCondition, argBefore) === false ) { + return context.reflect(); + } + const argAfter = replacer && typeof argBefore === 'string' + ? argBefore.replace(replacer.re, replacer.replacement) + : value; + callArgs[argpos] = argAfter; + safe.uboLog(logPrefix, `Replaced argument:\nBefore: ${JSON.stringify(argBefore)}\nAfter: ${argAfter}`); + return context.reflect(); + }); +} +registerScriptlet(trustedReplaceArgument, { + name: 'trusted-replace-argument.js', + requiresTrust: true, + dependencies: [ + parseReplaceFn, + proxyApplyFn, + safeSelf, + validateConstantFn, + ], +}); diff --git a/assets/resources/run-at.js b/assets/resources/run-at.js index 65e1be534dd5a..545324dcf1160 100644 --- a/assets/resources/run-at.js +++ b/assets/resources/run-at.js @@ -18,8 +18,6 @@ Home: https://github.com/gorhill/uBlock - The scriptlets below are meant to be injected only into a - web page context. */ import { registerScriptlet } from './base.js'; diff --git a/assets/resources/safe-self.js b/assets/resources/safe-self.js index bad9eece359ae..a1840e13b6464 100644 --- a/assets/resources/safe-self.js +++ b/assets/resources/safe-self.js @@ -18,8 +18,6 @@ Home: https://github.com/gorhill/uBlock - The scriptlets below are meant to be injected only into a - web page context. */ import { registerScriptlet } from './base.js'; diff --git a/assets/resources/scriptlets.js b/assets/resources/scriptlets.js index 46c31b6dc4e13..d045352acc4d3 100644 --- a/assets/resources/scriptlets.js +++ b/assets/resources/scriptlets.js @@ -18,23 +18,20 @@ Home: https://github.com/gorhill/uBlock - The scriptlets below are meant to be injected only into a - web page context. */ import './attribute.js'; -import './cookie.js'; -import './localstorage.js'; -import './run-at.js'; -import './safe-self.js'; +import './replace-argument.js'; import './spoof-css.js'; import { runAt, runAtHtmlElementFn } from './run-at.js'; import { getAllCookiesFn } from './cookie.js'; import { getAllLocalStorageFn } from './localstorage.js'; +import { proxyApplyFn } from './proxy-apply.js'; import { registeredScriptlets } from './base.js'; import { safeSelf } from './safe-self.js'; +import { validateConstantFn } from './set-constant.js'; // Externally added to the private namespace in which scriptlets execute. /* global scriptletGlobals */ @@ -289,228 +286,6 @@ function abortCurrentScriptCore( /******************************************************************************/ -builtinScriptlets.push({ - name: 'validate-constant.fn', - fn: validateConstantFn, - dependencies: [ - 'safe-self.fn', - ], -}); -function validateConstantFn(trusted, raw, extraArgs = {}) { - const safe = safeSelf(); - let value; - if ( raw === 'undefined' ) { - value = undefined; - } else if ( raw === 'false' ) { - value = false; - } else if ( raw === 'true' ) { - value = true; - } else if ( raw === 'null' ) { - value = null; - } else if ( raw === "''" || raw === '' ) { - value = ''; - } else if ( raw === '[]' || raw === 'emptyArr' ) { - value = []; - } else if ( raw === '{}' || raw === 'emptyObj' ) { - value = {}; - } else if ( raw === 'noopFunc' ) { - value = function(){}; - } else if ( raw === 'trueFunc' ) { - value = function(){ return true; }; - } else if ( raw === 'falseFunc' ) { - value = function(){ return false; }; - } else if ( raw === 'throwFunc' ) { - value = function(){ throw ''; }; - } else if ( /^-?\d+$/.test(raw) ) { - value = parseInt(raw); - if ( isNaN(raw) ) { return; } - if ( Math.abs(raw) > 0x7FFF ) { return; } - } else if ( trusted ) { - if ( raw.startsWith('json:') ) { - try { value = safe.JSON_parse(raw.slice(5)); } catch(ex) { return; } - } else if ( raw.startsWith('{') && raw.endsWith('}') ) { - try { value = safe.JSON_parse(raw).value; } catch(ex) { return; } - } - } else { - return; - } - if ( extraArgs.as !== undefined ) { - if ( extraArgs.as === 'function' ) { - return ( ) => value; - } else if ( extraArgs.as === 'callback' ) { - return ( ) => (( ) => value); - } else if ( extraArgs.as === 'resolved' ) { - return Promise.resolve(value); - } else if ( extraArgs.as === 'rejected' ) { - return Promise.reject(value); - } - } - return value; -} - -/******************************************************************************/ - -builtinScriptlets.push({ - name: 'set-constant.fn', - fn: setConstantFn, - dependencies: [ - 'run-at.fn', - 'safe-self.fn', - 'validate-constant.fn', - ], -}); -function setConstantFn( - trusted = false, - chain = '', - rawValue = '' -) { - if ( chain === '' ) { return; } - const safe = safeSelf(); - const logPrefix = safe.makeLogPrefix('set-constant', chain, rawValue); - const extraArgs = safe.getExtraArgs(Array.from(arguments), 3); - function setConstant(chain, rawValue) { - const trappedProp = (( ) => { - const pos = chain.lastIndexOf('.'); - if ( pos === -1 ) { return chain; } - return chain.slice(pos+1); - })(); - const cloakFunc = fn => { - safe.Object_defineProperty(fn, 'name', { value: trappedProp }); - return new Proxy(fn, { - defineProperty(target, prop) { - if ( prop !== 'toString' ) { - return Reflect.defineProperty(...arguments); - } - return true; - }, - deleteProperty(target, prop) { - if ( prop !== 'toString' ) { - return Reflect.deleteProperty(...arguments); - } - return true; - }, - get(target, prop) { - if ( prop === 'toString' ) { - return function() { - return `function ${trappedProp}() { [native code] }`; - }.bind(null); - } - return Reflect.get(...arguments); - }, - }); - }; - if ( trappedProp === '' ) { return; } - const thisScript = document.currentScript; - let normalValue = validateConstantFn(trusted, rawValue, extraArgs); - if ( rawValue === 'noopFunc' || rawValue === 'trueFunc' || rawValue === 'falseFunc' ) { - normalValue = cloakFunc(normalValue); - } - let aborted = false; - const mustAbort = function(v) { - if ( trusted ) { return false; } - if ( aborted ) { return true; } - aborted = - (v !== undefined && v !== null) && - (normalValue !== undefined && normalValue !== null) && - (typeof v !== typeof normalValue); - if ( aborted ) { - safe.uboLog(logPrefix, `Aborted because value set to ${v}`); - } - return aborted; - }; - // https://github.com/uBlockOrigin/uBlock-issues/issues/156 - // Support multiple trappers for the same property. - const trapProp = function(owner, prop, configurable, handler) { - if ( handler.init(configurable ? owner[prop] : normalValue) === false ) { return; } - const odesc = safe.Object_getOwnPropertyDescriptor(owner, prop); - let prevGetter, prevSetter; - if ( odesc instanceof safe.Object ) { - owner[prop] = normalValue; - if ( odesc.get instanceof Function ) { - prevGetter = odesc.get; - } - if ( odesc.set instanceof Function ) { - prevSetter = odesc.set; - } - } - try { - safe.Object_defineProperty(owner, prop, { - configurable, - get() { - if ( prevGetter !== undefined ) { - prevGetter(); - } - return handler.getter(); - }, - set(a) { - if ( prevSetter !== undefined ) { - prevSetter(a); - } - handler.setter(a); - } - }); - safe.uboLog(logPrefix, 'Trap installed'); - } catch(ex) { - safe.uboErr(logPrefix, ex); - } - }; - const trapChain = function(owner, chain) { - const pos = chain.indexOf('.'); - if ( pos === -1 ) { - trapProp(owner, chain, false, { - v: undefined, - init: function(v) { - if ( mustAbort(v) ) { return false; } - this.v = v; - return true; - }, - getter: function() { - if ( document.currentScript === thisScript ) { - return this.v; - } - safe.uboLog(logPrefix, 'Property read'); - return normalValue; - }, - setter: function(a) { - if ( mustAbort(a) === false ) { return; } - normalValue = a; - } - }); - return; - } - const prop = chain.slice(0, pos); - const v = owner[prop]; - chain = chain.slice(pos + 1); - if ( v instanceof safe.Object || typeof v === 'object' && v !== null ) { - trapChain(v, chain); - return; - } - trapProp(owner, prop, true, { - v: undefined, - init: function(v) { - this.v = v; - return true; - }, - getter: function() { - return this.v; - }, - setter: function(a) { - this.v = a; - if ( a instanceof safe.Object ) { - trapChain(a, chain); - } - } - }); - }; - trapChain(window, chain); - } - runAt(( ) => { - setConstant(chain, rawValue); - }, extraArgs.runAt); -} - -/******************************************************************************/ - builtinScriptlets.push({ name: 'replace-node-text.fn', fn: replaceNodeTextFn, @@ -1045,93 +820,6 @@ function replaceFetchResponseFn( /******************************************************************************/ -builtinScriptlets.push({ - name: 'proxy-apply.fn', - fn: proxyApplyFn, -}); -function proxyApplyFn( - target = '', - handler = '' -) { - let context = globalThis; - let prop = target; - for (;;) { - const pos = prop.indexOf('.'); - if ( pos === -1 ) { break; } - context = context[prop.slice(0, pos)]; - if ( context instanceof Object === false ) { return; } - prop = prop.slice(pos+1); - } - const fn = context[prop]; - if ( typeof fn !== 'function' ) { return; } - if ( proxyApplyFn.CtorContext === undefined ) { - proxyApplyFn.ctorContexts = []; - proxyApplyFn.CtorContext = class { - constructor(...args) { - this.init(...args); - } - init(callFn, callArgs) { - this.callFn = callFn; - this.callArgs = callArgs; - return this; - } - reflect() { - const r = Reflect.construct(this.callFn, this.callArgs); - this.callFn = this.callArgs = undefined; - proxyApplyFn.ctorContexts.push(this); - return r; - } - static factory(...args) { - return proxyApplyFn.ctorContexts.length !== 0 - ? proxyApplyFn.ctorContexts.pop().init(...args) - : new proxyApplyFn.CtorContext(...args); - } - }; - proxyApplyFn.applyContexts = []; - proxyApplyFn.ApplyContext = class { - constructor(...args) { - this.init(...args); - } - init(callFn, thisArg, callArgs) { - this.callFn = callFn; - this.thisArg = thisArg; - this.callArgs = callArgs; - return this; - } - reflect() { - const r = Reflect.apply(this.callFn, this.thisArg, this.callArgs); - this.callFn = this.thisArg = this.callArgs = undefined; - proxyApplyFn.applyContexts.push(this); - return r; - } - static factory(...args) { - return proxyApplyFn.applyContexts.length !== 0 - ? proxyApplyFn.applyContexts.pop().init(...args) - : new proxyApplyFn.ApplyContext(...args); - } - }; - } - const fnStr = fn.toString(); - const toString = (function toString() { return fnStr; }).bind(null); - const proxyDetails = { - apply(target, thisArg, args) { - return handler(proxyApplyFn.ApplyContext.factory(target, thisArg, args)); - }, - get(target, prop) { - if ( prop === 'toString' ) { return toString; } - return Reflect.get(target, prop); - }, - }; - if ( fn.prototype?.constructor === fn ) { - proxyDetails.construct = function(target, args) { - return handler(proxyApplyFn.CtorContext.factory(target, args)); - }; - } - context[prop] = new Proxy(fn, proxyDetails); -} - -/******************************************************************************/ - builtinScriptlets.push({ name: 'prevent-xhr.fn', fn: preventXhrFn, @@ -2205,24 +1893,6 @@ function noRequestAnimationFrameIf( /******************************************************************************/ -builtinScriptlets.push({ - name: 'set-constant.js', - aliases: [ - 'set.js', - ], - fn: setConstant, - dependencies: [ - 'set-constant.fn' - ], -}); -function setConstant( - ...args -) { - setConstantFn(false, ...args); -} - -/******************************************************************************/ - builtinScriptlets.push({ name: 'no-setInterval-if.js', aliases: [ @@ -3440,32 +3110,6 @@ function replaceNodeText( replaceNodeTextFn(nodeName, pattern, replacement, ...extraArgs); } -/******************************************************************************* - * - * trusted-set-constant.js - * - * Set specified property to any value. This is essentially the same as - * set-constant.js, but with no restriction as to which values can be used. - * - **/ - -builtinScriptlets.push({ - name: 'trusted-set-constant.js', - requiresTrust: true, - aliases: [ - 'trusted-set.js', - ], - fn: trustedSetConstant, - dependencies: [ - 'set-constant.fn' - ], -}); -function trustedSetConstant( - ...args -) { - setConstantFn(true, ...args); -} - /******************************************************************************* * * trusted-replace-fetch-response.js @@ -3880,50 +3524,6 @@ function trustedPruneOutboundObject( /******************************************************************************/ -builtinScriptlets.push({ - name: 'trusted-replace-argument.js', - requiresTrust: true, - fn: trustedReplaceArgument, - dependencies: [ - 'proxy-apply.fn', - 'safe-self.fn', - 'validate-constant.fn', - ], -}); -function trustedReplaceArgument( - propChain = '', - argposRaw = '', - argraw = '' -) { - if ( propChain === '' ) { return; } - const safe = safeSelf(); - const logPrefix = safe.makeLogPrefix('trusted-replace-argument', propChain, argposRaw, argraw); - const argoffset = parseInt(argposRaw, 10) || 0; - const extraArgs = safe.getExtraArgs(Array.from(arguments), 3); - const normalValue = validateConstantFn(true, argraw, extraArgs); - const reCondition = extraArgs.condition - ? safe.patternToRegex(extraArgs.condition) - : /^/; - proxyApplyFn(propChain, function(context) { - const { callArgs } = context; - if ( argposRaw === '' ) { - safe.uboLog(logPrefix, `Arguments:\n${callArgs.join('\n')}`); - return context.reflect(); - } - const argpos = argoffset >= 0 ? argoffset : callArgs.length - argoffset; - if ( argpos >= 0 && argpos < callArgs.length ) { - const argBefore = callArgs[argpos]; - if ( safe.RegExp_test.call(reCondition, argBefore) ) { - callArgs[argpos] = normalValue; - safe.uboLog(logPrefix, `Replaced argument:\nBefore: ${JSON.stringify(argBefore)}\nAfter: ${normalValue}`); - } - } - return context.reflect(); - }); -} - -/******************************************************************************/ - builtinScriptlets.push({ name: 'trusted-replace-outbound-text.js', requiresTrust: true, diff --git a/assets/resources/set-constant.js b/assets/resources/set-constant.js new file mode 100644 index 0000000000000..127f27bbbe910 --- /dev/null +++ b/assets/resources/set-constant.js @@ -0,0 +1,287 @@ +/******************************************************************************* + + uBlock Origin - a comprehensive, efficient content blocker + Copyright (C) 2019-present Raymond Hill + + 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/}. + + Home: https://github.com/gorhill/uBlock + +*/ + +import { registerScriptlet } from './base.js'; +import { runAt } from './run-at.js'; +import { safeSelf } from './safe-self.js'; + +/******************************************************************************/ + +export function validateConstantFn(trusted, raw, extraArgs = {}) { + const safe = safeSelf(); + let value; + if ( raw === 'undefined' ) { + value = undefined; + } else if ( raw === 'false' ) { + value = false; + } else if ( raw === 'true' ) { + value = true; + } else if ( raw === 'null' ) { + value = null; + } else if ( raw === "''" || raw === '' ) { + value = ''; + } else if ( raw === '[]' || raw === 'emptyArr' ) { + value = []; + } else if ( raw === '{}' || raw === 'emptyObj' ) { + value = {}; + } else if ( raw === 'noopFunc' ) { + value = function(){}; + } else if ( raw === 'trueFunc' ) { + value = function(){ return true; }; + } else if ( raw === 'falseFunc' ) { + value = function(){ return false; }; + } else if ( raw === 'throwFunc' ) { + value = function(){ throw ''; }; + } else if ( /^-?\d+$/.test(raw) ) { + value = parseInt(raw); + if ( isNaN(raw) ) { return; } + if ( Math.abs(raw) > 0x7FFF ) { return; } + } else if ( trusted ) { + if ( raw.startsWith('json:') ) { + try { value = safe.JSON_parse(raw.slice(5)); } catch(ex) { return; } + } else if ( raw.startsWith('{') && raw.endsWith('}') ) { + try { value = safe.JSON_parse(raw).value; } catch(ex) { return; } + } + } else { + return; + } + if ( extraArgs.as !== undefined ) { + if ( extraArgs.as === 'function' ) { + return ( ) => value; + } else if ( extraArgs.as === 'callback' ) { + return ( ) => (( ) => value); + } else if ( extraArgs.as === 'resolved' ) { + return Promise.resolve(value); + } else if ( extraArgs.as === 'rejected' ) { + return Promise.reject(value); + } + } + return value; +} +registerScriptlet(validateConstantFn, { + name: 'validate-constant.fn', + dependencies: [ + safeSelf, + ], +}); + +/******************************************************************************/ + +export function setConstantFn( + trusted = false, + chain = '', + rawValue = '' +) { + if ( chain === '' ) { return; } + const safe = safeSelf(); + const logPrefix = safe.makeLogPrefix('set-constant', chain, rawValue); + const extraArgs = safe.getExtraArgs(Array.from(arguments), 3); + function setConstant(chain, rawValue) { + const trappedProp = (( ) => { + const pos = chain.lastIndexOf('.'); + if ( pos === -1 ) { return chain; } + return chain.slice(pos+1); + })(); + const cloakFunc = fn => { + safe.Object_defineProperty(fn, 'name', { value: trappedProp }); + return new Proxy(fn, { + defineProperty(target, prop) { + if ( prop !== 'toString' ) { + return Reflect.defineProperty(...arguments); + } + return true; + }, + deleteProperty(target, prop) { + if ( prop !== 'toString' ) { + return Reflect.deleteProperty(...arguments); + } + return true; + }, + get(target, prop) { + if ( prop === 'toString' ) { + return function() { + return `function ${trappedProp}() { [native code] }`; + }.bind(null); + } + return Reflect.get(...arguments); + }, + }); + }; + if ( trappedProp === '' ) { return; } + const thisScript = document.currentScript; + let normalValue = validateConstantFn(trusted, rawValue, extraArgs); + if ( rawValue === 'noopFunc' || rawValue === 'trueFunc' || rawValue === 'falseFunc' ) { + normalValue = cloakFunc(normalValue); + } + let aborted = false; + const mustAbort = function(v) { + if ( trusted ) { return false; } + if ( aborted ) { return true; } + aborted = + (v !== undefined && v !== null) && + (normalValue !== undefined && normalValue !== null) && + (typeof v !== typeof normalValue); + if ( aborted ) { + safe.uboLog(logPrefix, `Aborted because value set to ${v}`); + } + return aborted; + }; + // https://github.com/uBlockOrigin/uBlock-issues/issues/156 + // Support multiple trappers for the same property. + const trapProp = function(owner, prop, configurable, handler) { + if ( handler.init(configurable ? owner[prop] : normalValue) === false ) { return; } + const odesc = safe.Object_getOwnPropertyDescriptor(owner, prop); + let prevGetter, prevSetter; + if ( odesc instanceof safe.Object ) { + owner[prop] = normalValue; + if ( odesc.get instanceof Function ) { + prevGetter = odesc.get; + } + if ( odesc.set instanceof Function ) { + prevSetter = odesc.set; + } + } + try { + safe.Object_defineProperty(owner, prop, { + configurable, + get() { + if ( prevGetter !== undefined ) { + prevGetter(); + } + return handler.getter(); + }, + set(a) { + if ( prevSetter !== undefined ) { + prevSetter(a); + } + handler.setter(a); + } + }); + safe.uboLog(logPrefix, 'Trap installed'); + } catch(ex) { + safe.uboErr(logPrefix, ex); + } + }; + const trapChain = function(owner, chain) { + const pos = chain.indexOf('.'); + if ( pos === -1 ) { + trapProp(owner, chain, false, { + v: undefined, + init: function(v) { + if ( mustAbort(v) ) { return false; } + this.v = v; + return true; + }, + getter: function() { + if ( document.currentScript === thisScript ) { + return this.v; + } + safe.uboLog(logPrefix, 'Property read'); + return normalValue; + }, + setter: function(a) { + if ( mustAbort(a) === false ) { return; } + normalValue = a; + } + }); + return; + } + const prop = chain.slice(0, pos); + const v = owner[prop]; + chain = chain.slice(pos + 1); + if ( v instanceof safe.Object || typeof v === 'object' && v !== null ) { + trapChain(v, chain); + return; + } + trapProp(owner, prop, true, { + v: undefined, + init: function(v) { + this.v = v; + return true; + }, + getter: function() { + return this.v; + }, + setter: function(a) { + this.v = a; + if ( a instanceof safe.Object ) { + trapChain(a, chain); + } + } + }); + }; + trapChain(window, chain); + } + runAt(( ) => { + setConstant(chain, rawValue); + }, extraArgs.runAt); +} +registerScriptlet(setConstantFn, { + name: 'set-constant.fn', + dependencies: [ + runAt, + safeSelf, + validateConstantFn, + ], +}); + +/******************************************************************************/ + +export function setConstant( + ...args +) { + setConstantFn(false, ...args); +} +registerScriptlet(setConstant, { + name: 'set-constant.js', + aliases: [ + 'set.js', + ], + dependencies: [ + setConstantFn, + ], +}); + +/******************************************************************************* + * + * trusted-set-constant.js + * + * Set specified property to any value. This is essentially the same as + * set-constant.js, but with no restriction as to which values can be used. + * + **/ + +export function trustedSetConstant( + ...args +) { + setConstantFn(true, ...args); +} +registerScriptlet(trustedSetConstant, { + name: 'trusted-set-constant.js', + requiresTrust: true, + aliases: [ + 'trusted-set.js', + ], + dependencies: [ + setConstantFn, + ], +}); diff --git a/assets/resources/shared.js b/assets/resources/shared.js new file mode 100644 index 0000000000000..89d8503e7e4d0 --- /dev/null +++ b/assets/resources/shared.js @@ -0,0 +1,44 @@ +/******************************************************************************* + + uBlock Origin - a comprehensive, efficient content blocker + Copyright (C) 2019-present Raymond Hill + + 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/}. + + Home: https://github.com/gorhill/uBlock + +*/ + +// Code imported from main code base and exposed as injectable scriptlets +import { ArglistParser } from '../../js/arglist-parser.js'; + +import { registerScriptlet } from './base.js'; + +/******************************************************************************/ + +registerScriptlet(ArglistParser, { + name: 'arglist-parser.fn', +}); + +/******************************************************************************/ + +export function createArglistParser(...args) { + return new ArglistParser(...args); +} +registerScriptlet(createArglistParser, { + name: 'create-arglist-parser.fn', + dependencies: [ + ArglistParser, + ], +}); diff --git a/assets/resources/spoof-css.js b/assets/resources/spoof-css.js index 32b51acf407c0..7cc7b9f95574c 100644 --- a/assets/resources/spoof-css.js +++ b/assets/resources/spoof-css.js @@ -18,8 +18,6 @@ Home: https://github.com/gorhill/uBlock - The scriptlets below are meant to be injected only into a - web page context. */ import { registerScriptlet } from './base.js'; diff --git a/src/js/arglist-parser.js b/src/js/arglist-parser.js new file mode 100644 index 0000000000000..d8200df5822a1 --- /dev/null +++ b/src/js/arglist-parser.js @@ -0,0 +1,116 @@ +/******************************************************************************* + + uBlock Origin - a comprehensive, efficient content blocker + Copyright (C) 2020-present Raymond Hill + + 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/}. + + Home: https://github.com/gorhill/uBlock +*/ + +/******************************************************************************/ + +export class ArglistParser { + constructor(separatorChar = ',', mustQuote = false) { + this.separatorChar = this.actualSeparatorChar = separatorChar; + this.separatorCode = this.actualSeparatorCode = separatorChar.charCodeAt(0); + this.mustQuote = mustQuote; + this.quoteBeg = 0; this.quoteEnd = 0; + this.argBeg = 0; this.argEnd = 0; + this.separatorBeg = 0; this.separatorEnd = 0; + this.transform = false; + this.failed = false; + this.reWhitespaceStart = /^\s+/; + this.reWhitespaceEnd = /\s+$/; + this.reOddTrailingEscape = /(?:^|[^\\])(?:\\\\)*\\$/; + this.reTrailingEscapeChars = /\\+$/; + } + nextArg(pattern, beg = 0) { + const len = pattern.length; + this.quoteBeg = beg + this.leftWhitespaceCount(pattern.slice(beg)); + this.failed = false; + const qc = pattern.charCodeAt(this.quoteBeg); + if ( qc === 0x22 /* " */ || qc === 0x27 /* ' */ || qc === 0x60 /* ` */ ) { + this.indexOfNextArgSeparator(pattern, qc); + if ( this.argEnd !== len ) { + this.quoteEnd = this.argEnd + 1; + this.separatorBeg = this.separatorEnd = this.quoteEnd; + this.separatorEnd += this.leftWhitespaceCount(pattern.slice(this.quoteEnd)); + if ( this.separatorEnd === len ) { return this; } + if ( pattern.charCodeAt(this.separatorEnd) === this.separatorCode ) { + this.separatorEnd += 1; + return this; + } + } + } + this.indexOfNextArgSeparator(pattern, this.separatorCode); + this.separatorBeg = this.separatorEnd = this.argEnd; + if ( this.separatorBeg < len ) { + this.separatorEnd += 1; + } + this.argEnd -= this.rightWhitespaceCount(pattern.slice(0, this.separatorBeg)); + this.quoteEnd = this.argEnd; + if ( this.mustQuote ) { + this.failed = true; + } + return this; + } + normalizeArg(s, char = '') { + if ( char === '' ) { char = this.actualSeparatorChar; } + let out = ''; + let pos = 0; + while ( (pos = s.lastIndexOf(char)) !== -1 ) { + out = s.slice(pos) + out; + s = s.slice(0, pos); + const match = this.reTrailingEscapeChars.exec(s); + if ( match === null ) { continue; } + const tail = (match[0].length & 1) !== 0 + ? match[0].slice(0, -1) + : match[0]; + out = tail + out; + s = s.slice(0, -match[0].length); + } + if ( out === '' ) { return s; } + return s + out; + } + leftWhitespaceCount(s) { + const match = this.reWhitespaceStart.exec(s); + return match === null ? 0 : match[0].length; + } + rightWhitespaceCount(s) { + const match = this.reWhitespaceEnd.exec(s); + return match === null ? 0 : match[0].length; + } + indexOfNextArgSeparator(pattern, separatorCode) { + this.argBeg = this.argEnd = separatorCode !== this.separatorCode + ? this.quoteBeg + 1 + : this.quoteBeg; + this.transform = false; + if ( separatorCode !== this.actualSeparatorCode ) { + this.actualSeparatorCode = separatorCode; + this.actualSeparatorChar = String.fromCharCode(separatorCode); + } + while ( this.argEnd < pattern.length ) { + const pos = pattern.indexOf(this.actualSeparatorChar, this.argEnd); + if ( pos === -1 ) { + return (this.argEnd = pattern.length); + } + if ( this.reOddTrailingEscape.test(pattern.slice(0, pos)) === false ) { + return (this.argEnd = pos); + } + this.transform = true; + this.argEnd = pos + 1; + } + } +} diff --git a/src/js/static-filtering-parser.js b/src/js/static-filtering-parser.js index f49b0bfe7208d..2be165eb107ef 100644 --- a/src/js/static-filtering-parser.js +++ b/src/js/static-filtering-parser.js @@ -22,6 +22,7 @@ /******************************************************************************/ import * as cssTree from '../lib/csstree/css-tree.js'; +import { ArglistParser } from './arglist-parser.js'; import Regex from '../lib/regexanalyzer/regex.js'; /******************************************************************************* @@ -606,102 +607,6 @@ const exCharCodeAt = (s, i) => { /******************************************************************************/ -class ArgListParser { - constructor(separatorChar = ',', mustQuote = false) { - this.separatorChar = this.actualSeparatorChar = separatorChar; - this.separatorCode = this.actualSeparatorCode = separatorChar.charCodeAt(0); - this.mustQuote = mustQuote; - this.quoteBeg = 0; this.quoteEnd = 0; - this.argBeg = 0; this.argEnd = 0; - this.separatorBeg = 0; this.separatorEnd = 0; - this.transform = false; - this.failed = false; - this.reWhitespaceStart = /^\s+/; - this.reWhitespaceEnd = /\s+$/; - this.reOddTrailingEscape = /(?:^|[^\\])(?:\\\\)*\\$/; - this.reTrailingEscapeChars = /\\+$/; - } - nextArg(pattern, beg = 0) { - const len = pattern.length; - this.quoteBeg = beg + this.leftWhitespaceCount(pattern.slice(beg)); - this.failed = false; - const qc = pattern.charCodeAt(this.quoteBeg); - if ( qc === 0x22 /* " */ || qc === 0x27 /* ' */ || qc === 0x60 /* ` */ ) { - this.indexOfNextArgSeparator(pattern, qc); - if ( this.argEnd !== len ) { - this.quoteEnd = this.argEnd + 1; - this.separatorBeg = this.separatorEnd = this.quoteEnd; - this.separatorEnd += this.leftWhitespaceCount(pattern.slice(this.quoteEnd)); - if ( this.separatorEnd === len ) { return this; } - if ( pattern.charCodeAt(this.separatorEnd) === this.separatorCode ) { - this.separatorEnd += 1; - return this; - } - } - } - this.indexOfNextArgSeparator(pattern, this.separatorCode); - this.separatorBeg = this.separatorEnd = this.argEnd; - if ( this.separatorBeg < len ) { - this.separatorEnd += 1; - } - this.argEnd -= this.rightWhitespaceCount(pattern.slice(0, this.separatorBeg)); - this.quoteEnd = this.argEnd; - if ( this.mustQuote ) { - this.failed = true; - } - return this; - } - normalizeArg(s, char = '') { - if ( char === '' ) { char = this.actualSeparatorChar; } - let out = ''; - let pos = 0; - while ( (pos = s.lastIndexOf(char)) !== -1 ) { - out = s.slice(pos) + out; - s = s.slice(0, pos); - const match = this.reTrailingEscapeChars.exec(s); - if ( match === null ) { continue; } - const tail = (match[0].length & 1) !== 0 - ? match[0].slice(0, -1) - : match[0]; - out = tail + out; - s = s.slice(0, -match[0].length); - } - if ( out === '' ) { return s; } - return s + out; - } - leftWhitespaceCount(s) { - const match = this.reWhitespaceStart.exec(s); - return match === null ? 0 : match[0].length; - } - rightWhitespaceCount(s) { - const match = this.reWhitespaceEnd.exec(s); - return match === null ? 0 : match[0].length; - } - indexOfNextArgSeparator(pattern, separatorCode) { - this.argBeg = this.argEnd = separatorCode !== this.separatorCode - ? this.quoteBeg + 1 - : this.quoteBeg; - this.transform = false; - if ( separatorCode !== this.actualSeparatorCode ) { - this.actualSeparatorCode = separatorCode; - this.actualSeparatorChar = String.fromCharCode(separatorCode); - } - while ( this.argEnd < pattern.length ) { - const pos = pattern.indexOf(this.actualSeparatorChar, this.argEnd); - if ( pos === -1 ) { - return (this.argEnd = pattern.length); - } - if ( this.reOddTrailingEscape.test(pattern.slice(0, pos)) === false ) { - return (this.argEnd = pos); - } - this.transform = true; - this.argEnd = pos + 1; - } - } -} - -/******************************************************************************/ - class AstWalker { constructor(parser, from = 0) { this.parser = parser; @@ -904,8 +809,8 @@ export class AstFilterParser { this.reBadPP = /(?:^|[;,])\s*report-to\b/i; this.reNetOption = /^(~?)([134a-z_-]+)(=?)/; this.reNoopOption = /^_+$/; - this.netOptionValueParser = new ArgListParser(','); - this.scriptletArgListParser = new ArgListParser(','); + this.netOptionValueParser = new ArglistParser(','); + this.scriptletArgListParser = new ArglistParser(','); } finish() { @@ -3100,7 +3005,7 @@ export function parseHeaderValue(arg) { export function parseReplaceValue(s) { if ( s.charCodeAt(0) !== 0x2F /* / */ ) { return; } - const parser = new ArgListParser('/'); + const parser = new ArglistParser('/'); parser.nextArg(s, 1); let pattern = s.slice(parser.argBeg, parser.argEnd); if ( parser.transform ) { diff --git a/tools/make-nodejs.sh b/tools/make-nodejs.sh index 270456814b9b3..87e96ddade909 100755 --- a/tools/make-nodejs.sh +++ b/tools/make-nodejs.sh @@ -7,6 +7,7 @@ set -e DES=$1 mkdir -p $DES/js +cp src/js/arglist-parser.js $DES/js cp src/js/base64-custom.js $DES/js cp src/js/biditrie.js $DES/js cp src/js/dynamic-net-filtering.js $DES/js From 4d525f1a55c7d55b525b98414b4c184611745296 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Wed, 27 Nov 2024 16:47:11 -0500 Subject: [PATCH 0482/1099] Move `assets/resources` into `src/js/` --- platform/mv3/make-scriptlets.js | 6 +----- src/js/redirect-engine.js | 2 +- {assets => src/js}/resources/attribute.js | 0 {assets => src/js}/resources/base.js | 0 {assets => src/js}/resources/cookie.js | 0 {assets => src/js}/resources/localstorage.js | 0 {assets => src/js}/resources/parse-replace.js | 0 {assets => src/js}/resources/proxy-apply.js | 0 {assets => src/js}/resources/replace-argument.js | 0 {assets => src/js}/resources/run-at.js | 0 {assets => src/js}/resources/safe-self.js | 0 {assets => src/js}/resources/scriptlets.js | 0 {assets => src/js}/resources/set-constant.js | 0 {assets => src/js}/resources/shared.js | 2 +- {assets => src/js}/resources/spoof-css.js | 0 tools/copy-common-files.sh | 1 + tools/make-mv3.sh | 2 +- 17 files changed, 5 insertions(+), 8 deletions(-) rename {assets => src/js}/resources/attribute.js (100%) rename {assets => src/js}/resources/base.js (100%) rename {assets => src/js}/resources/cookie.js (100%) rename {assets => src/js}/resources/localstorage.js (100%) rename {assets => src/js}/resources/parse-replace.js (100%) rename {assets => src/js}/resources/proxy-apply.js (100%) rename {assets => src/js}/resources/replace-argument.js (100%) rename {assets => src/js}/resources/run-at.js (100%) rename {assets => src/js}/resources/safe-self.js (100%) rename {assets => src/js}/resources/scriptlets.js (100%) rename {assets => src/js}/resources/set-constant.js (100%) rename {assets => src/js}/resources/shared.js (96%) rename {assets => src/js}/resources/spoof-css.js (100%) diff --git a/platform/mv3/make-scriptlets.js b/platform/mv3/make-scriptlets.js index d276a56a6ace4..4ca52cd1ba7b8 100644 --- a/platform/mv3/make-scriptlets.js +++ b/platform/mv3/make-scriptlets.js @@ -19,12 +19,8 @@ Home: https://github.com/gorhill/uBlock */ -'use strict'; - -/******************************************************************************/ - +import { builtinScriptlets } from './resources/scriptlets.js'; import fs from 'fs/promises'; -import { builtinScriptlets } from './scriptlets.js'; import { safeReplace } from './safe-replace.js'; /******************************************************************************/ diff --git a/src/js/redirect-engine.js b/src/js/redirect-engine.js index f16b73ca0765f..d768db546f537 100644 --- a/src/js/redirect-engine.js +++ b/src/js/redirect-engine.js @@ -316,7 +316,7 @@ class RedirectEngine { this.aliases = new Map(); const fetches = [ - import('/assets/resources/scriptlets.js').then(module => { + import('/js/resources/scriptlets.js').then(module => { for ( const scriptlet of module.builtinScriptlets ) { const details = {}; details.mime = mimeFromName(scriptlet.name); diff --git a/assets/resources/attribute.js b/src/js/resources/attribute.js similarity index 100% rename from assets/resources/attribute.js rename to src/js/resources/attribute.js diff --git a/assets/resources/base.js b/src/js/resources/base.js similarity index 100% rename from assets/resources/base.js rename to src/js/resources/base.js diff --git a/assets/resources/cookie.js b/src/js/resources/cookie.js similarity index 100% rename from assets/resources/cookie.js rename to src/js/resources/cookie.js diff --git a/assets/resources/localstorage.js b/src/js/resources/localstorage.js similarity index 100% rename from assets/resources/localstorage.js rename to src/js/resources/localstorage.js diff --git a/assets/resources/parse-replace.js b/src/js/resources/parse-replace.js similarity index 100% rename from assets/resources/parse-replace.js rename to src/js/resources/parse-replace.js diff --git a/assets/resources/proxy-apply.js b/src/js/resources/proxy-apply.js similarity index 100% rename from assets/resources/proxy-apply.js rename to src/js/resources/proxy-apply.js diff --git a/assets/resources/replace-argument.js b/src/js/resources/replace-argument.js similarity index 100% rename from assets/resources/replace-argument.js rename to src/js/resources/replace-argument.js diff --git a/assets/resources/run-at.js b/src/js/resources/run-at.js similarity index 100% rename from assets/resources/run-at.js rename to src/js/resources/run-at.js diff --git a/assets/resources/safe-self.js b/src/js/resources/safe-self.js similarity index 100% rename from assets/resources/safe-self.js rename to src/js/resources/safe-self.js diff --git a/assets/resources/scriptlets.js b/src/js/resources/scriptlets.js similarity index 100% rename from assets/resources/scriptlets.js rename to src/js/resources/scriptlets.js diff --git a/assets/resources/set-constant.js b/src/js/resources/set-constant.js similarity index 100% rename from assets/resources/set-constant.js rename to src/js/resources/set-constant.js diff --git a/assets/resources/shared.js b/src/js/resources/shared.js similarity index 96% rename from assets/resources/shared.js rename to src/js/resources/shared.js index 89d8503e7e4d0..9a38fca48105b 100644 --- a/assets/resources/shared.js +++ b/src/js/resources/shared.js @@ -21,7 +21,7 @@ */ // Code imported from main code base and exposed as injectable scriptlets -import { ArglistParser } from '../../js/arglist-parser.js'; +import { ArglistParser } from '../arglist-parser.js'; import { registerScriptlet } from './base.js'; diff --git a/assets/resources/spoof-css.js b/src/js/resources/spoof-css.js similarity index 100% rename from assets/resources/spoof-css.js rename to src/js/resources/spoof-css.js diff --git a/tools/copy-common-files.sh b/tools/copy-common-files.sh index 56fb20ab3b8cf..29f7ee45c1e4d 100644 --- a/tools/copy-common-files.sh +++ b/tools/copy-common-files.sh @@ -12,6 +12,7 @@ cp -R src/css $DES/ cp -R src/img $DES/ mkdir $DES/js cp -R src/js/*.js $DES/js/ +cp -R src/js/resources $DES/js/ cp -R src/js/codemirror $DES/js/ cp -R src/js/scriptlets $DES/js/ cp -R src/js/wasm $DES/js/ diff --git a/tools/make-mv3.sh b/tools/make-mv3.sh index 92ce9d445177a..8b18039873c08 100755 --- a/tools/make-mv3.sh +++ b/tools/make-mv3.sh @@ -109,7 +109,7 @@ if [ "$QUICK" != "yes" ]; then cp platform/mv3/*.mjs "$TMPDIR"/ cp platform/mv3/extension/js/utils.js "$TMPDIR"/js/ cp "$UBO_DIR"/assets/assets.dev.json "$TMPDIR"/ - cp "$UBO_DIR"/assets/resources/*.js "$TMPDIR"/ + cp -R "$UBO_DIR"/src/js/resources "$TMPDIR"/ cp -R platform/mv3/scriptlets "$TMPDIR"/ mkdir -p "$TMPDIR"/web_accessible_resources cp "$UBO_DIR"/src/web_accessible_resources/* "$TMPDIR"/web_accessible_resources/ From 703fdf673c62841af0650cccce852bcbdab767c6 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Wed, 27 Nov 2024 17:55:18 -0500 Subject: [PATCH 0483/1099] [mv3] Fix mv3 build script --- platform/mv3/make-scriptlets.js | 2 +- tools/make-mv3.sh | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/platform/mv3/make-scriptlets.js b/platform/mv3/make-scriptlets.js index 4ca52cd1ba7b8..27dc5d247921b 100644 --- a/platform/mv3/make-scriptlets.js +++ b/platform/mv3/make-scriptlets.js @@ -19,7 +19,7 @@ Home: https://github.com/gorhill/uBlock */ -import { builtinScriptlets } from './resources/scriptlets.js'; +import { builtinScriptlets } from './js/resources/scriptlets.js'; import fs from 'fs/promises'; import { safeReplace } from './safe-replace.js'; diff --git a/tools/make-mv3.sh b/tools/make-mv3.sh index 8b18039873c08..c6b02abddfec9 100755 --- a/tools/make-mv3.sh +++ b/tools/make-mv3.sh @@ -108,8 +108,8 @@ if [ "$QUICK" != "yes" ]; then cp platform/mv3/*.js "$TMPDIR"/ cp platform/mv3/*.mjs "$TMPDIR"/ cp platform/mv3/extension/js/utils.js "$TMPDIR"/js/ + cp -R "$UBO_DIR"/src/js/resources "$TMPDIR"/js/ cp "$UBO_DIR"/assets/assets.dev.json "$TMPDIR"/ - cp -R "$UBO_DIR"/src/js/resources "$TMPDIR"/ cp -R platform/mv3/scriptlets "$TMPDIR"/ mkdir -p "$TMPDIR"/web_accessible_resources cp "$UBO_DIR"/src/web_accessible_resources/* "$TMPDIR"/web_accessible_resources/ From 3b7fa79a6813dd34e18dcd448a1417a67e0f1f58 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Thu, 28 Nov 2024 11:47:28 -0500 Subject: [PATCH 0484/1099] Improve `prevent-setTimeout`/`prevent-setInterval` scriptlet Add support for range for the `delay` paramater: --- @param [delay] A value to match against the delay. Can be a single value for exact match, or a range: - `min-max`: matches if delay >= min and delay <= max - `min-`: matches if delay >= min - `-max`: matches if delay <= max No delay means to match any delay value. Prepend with `!` to reverse the match condition. --- As discussed with filter list maintainers. --- src/js/resources/prevent-settimeout.js | 236 +++++++++++++++++++++++++ src/js/resources/scriptlets.js | 156 +--------------- 2 files changed, 237 insertions(+), 155 deletions(-) create mode 100644 src/js/resources/prevent-settimeout.js diff --git a/src/js/resources/prevent-settimeout.js b/src/js/resources/prevent-settimeout.js new file mode 100644 index 0000000000000..d8cfefad739d6 --- /dev/null +++ b/src/js/resources/prevent-settimeout.js @@ -0,0 +1,236 @@ +/******************************************************************************* + + uBlock Origin - a comprehensive, efficient content blocker + Copyright (C) 2019-present Raymond Hill + + 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/}. + + Home: https://github.com/gorhill/uBlock + +*/ + +import { proxyApplyFn } from './proxy-apply.js'; +import { registerScriptlet } from './base.js'; +import { safeSelf } from './safe-self.js'; + +/******************************************************************************/ + +class RangeParser { + constructor(s) { + this.not = s.charAt(0) === '!'; + if ( this.not ) { s = s.slice(1); } + if ( s === '' ) { return; } + const pos = s.indexOf('-'); + if ( pos !== 0 ) { + this.min = this.max = parseInt(s, 10) || 0; + } + if ( pos !== -1 ) { + this.max = parseInt(s.slice(1), 10) || Number.MAX_SAFE_INTEGER; + } + } + unbound() { + return this.min === undefined && this.max === undefined; + } + test(v) { + const n = Math.min(Math.max(Number(v) || 0, 0), Number.MAX_SAFE_INTEGER); + if ( this.min === this.max ) { + return (this.min === undefined || n === this.min) !== this.not; + } + if ( this.min === undefined ) { + return (n <= this.max) !== this.not; + } + if ( this.max === undefined ) { + return (n >= this.min) !== this.not; + } + return (n >= this.min && n <= this.max) !== this.not; + } +} +registerScriptlet(RangeParser, { + name: 'range-parser.fn', +}); + +/** + * @scriptlet prevent-setTimeout + * + * @description + * Conditionally prevent execution of the callback function passed to native + * setTimeout method. With no parameters, all calls to setTimeout will be + * shown in the logger. + * + * @param [needle] + * A pattern to match against the stringified callback. The pattern can be a + * plain string, or a regex. Prepend with `!` to reverse the match condition. + * + * @param [delay] + * A value to match against the delay. Can be a single value for exact match, + * or a range: + * - `min-max`: matches if delay >= min and delay <= max + * - `min-`: matches if delay >= min + * - `-max`: matches if delay <= max + * No delay means to match any delay value. + * Prepend with `!` to reverse the match condition. + * + * */ + +export function preventSetTimeout( + needleRaw = '', + delayRaw = '' +) { + const safe = safeSelf(); + const logPrefix = safe.makeLogPrefix('prevent-setTimeout', needleRaw, delayRaw); + const needleNot = needleRaw.charAt(0) === '!'; + const reNeedle = safe.patternToRegex(needleNot ? needleRaw.slice(1) : needleRaw); + const range = new RangeParser(delayRaw); + proxyApplyFn('setTimeout', function(context) { + const { callArgs } = context; + const a = callArgs[0] instanceof Function + ? String(safe.Function_toString(callArgs[0])) + : String(callArgs[0]); + const b = callArgs[1]; + if ( needleRaw === '' && range.unbound() ) { + safe.uboLog(logPrefix, `Called:\n${a}\n${b}`); + return context.reflect(); + } + if ( reNeedle.test(a) !== needleNot && range.test(b) ) { + callArgs[0] = function(){}; + safe.uboLog(logPrefix, `Prevented:\n${a}\n${b}`); + } + return context.reflect(); + }); +} +registerScriptlet(preventSetTimeout, { + name: 'prevent-setTimeout.js', + aliases: [ + 'no-setTimeout-if.js', + 'nostif.js', + 'setTimeout-defuser.js', + ], + dependencies: [ + proxyApplyFn, + RangeParser, + safeSelf, + ], +}); + +/** + * @scriptlet prevent-setInterval + * + * @description + * Conditionally prevent execution of the callback function passed to native + * setInterval method. With no parameters, all calls to setInterval will be + * shown in the logger. + * + * @param [needle] + * A pattern to match against the stringified callback. The pattern can be a + * plain string, or a regex. Prepend with `!` to reverse the match condition. + * No pattern means to match anything. + * + * @param [delay] + * A value to match against the delay. Can be a single value for exact match, + * or a range: + * - `min-max`: matches if delay >= min and delay <= max + * - `min-`: matches if delay >= min + * - `-max`: matches if delay <= max + * No delay means to match any delay value. + * Prepend with `!` to reverse the match condition. + * + * */ + +export function preventSetInterval( + needleRaw = '', + delayRaw = '' +) { + const safe = safeSelf(); + const logPrefix = safe.makeLogPrefix('prevent-setInterval', needleRaw, delayRaw); + const needleNot = needleRaw.charAt(0) === '!'; + const reNeedle = safe.patternToRegex(needleNot ? needleRaw.slice(1) : needleRaw); + const range = new RangeParser(delayRaw); + proxyApplyFn('setInterval', function(context) { + const { callArgs } = context; + const a = callArgs[0] instanceof Function + ? String(safe.Function_toString(callArgs[0])) + : String(callArgs[0]); + const b = callArgs[1]; + if ( needleRaw === '' && range.unbound() ) { + safe.uboLog(logPrefix, `Called:\n${a}\n${b}`); + return context.reflect(); + } + if ( reNeedle.test(a) !== needleNot && range.test(b) ) { + callArgs[0] = function(){}; + safe.uboLog(logPrefix, `Prevented:\n${a}\n${b}`); + } + return context.reflect(); + }); +} +registerScriptlet(preventSetInterval, { + name: 'prevent-setInterval.js', + aliases: [ + 'no-setInterval-if.js', + 'nosiif.js', + 'setInterval-defuser.js', + ], + dependencies: [ + proxyApplyFn, + RangeParser, + safeSelf, + ], +}); + +/** + * @scriptlet prevent-requestAnimationFrame + * + * @description + * Conditionally prevent execution of the callback function passed to native + * requestAnimationFrame method. With no parameters, all calls to + * requestAnimationFrame will be shown in the logger. + * + * @param [needle] + * A pattern to match against the stringified callback. The pattern can be a + * plain string, or a regex. + * Prepend with `!` to reverse the match condition. + * + * */ + +export function preventRequestAnimationFrame( + needleRaw = '' +) { + const safe = safeSelf(); + const logPrefix = safe.makeLogPrefix('prevent-requestAnimationFrame', needleRaw); + const needleNot = needleRaw.charAt(0) === '!'; + const reNeedle = safe.patternToRegex(needleNot ? needleRaw.slice(1) : needleRaw); + proxyApplyFn('requestAnimationFrame', function(context) { + const { callArgs } = context; + const a = callArgs[0] instanceof Function + ? String(safe.Function_toString(callArgs[0])) + : String(callArgs[0]); + if ( needleRaw === '' ) { + safe.uboLog(logPrefix, `Called:\n${a}`); + } else if ( reNeedle.test(a) !== needleNot ) { + callArgs[0] = function(){}; + safe.uboLog(logPrefix, `Prevented:\n${a}`); + } + return context.reflect(); + }); +} +registerScriptlet(preventRequestAnimationFrame, { + name: 'prevent-requestAnimationFrame.js', + aliases: [ + 'no-requestAnimationFrame-if.js', + 'norafif.js', + ], + dependencies: [ + proxyApplyFn, + safeSelf, + ], +}); diff --git a/src/js/resources/scriptlets.js b/src/js/resources/scriptlets.js index d045352acc4d3..d1e4c4b58d8e1 100644 --- a/src/js/resources/scriptlets.js +++ b/src/js/resources/scriptlets.js @@ -23,6 +23,7 @@ import './attribute.js'; import './replace-argument.js'; import './spoof-css.js'; +import './prevent-settimeout.js'; import { runAt, runAtHtmlElementFn } from './run-at.js'; @@ -1852,161 +1853,6 @@ function removeClass( /******************************************************************************/ -builtinScriptlets.push({ - name: 'no-requestAnimationFrame-if.js', - aliases: [ - 'norafif.js', - 'prevent-requestAnimationFrame.js', - ], - fn: noRequestAnimationFrameIf, - dependencies: [ - 'safe-self.fn', - ], -}); -function noRequestAnimationFrameIf( - needle = '' -) { - if ( typeof needle !== 'string' ) { return; } - const safe = safeSelf(); - const needleNot = needle.charAt(0) === '!'; - if ( needleNot ) { needle = needle.slice(1); } - const log = needleNot === false && needle === '' ? console.log : undefined; - const reNeedle = safe.patternToRegex(needle); - window.requestAnimationFrame = new Proxy(window.requestAnimationFrame, { - apply: function(target, thisArg, args) { - const a = args[0] instanceof Function - ? String(safe.Function_toString(args[0])) - : String(args[0]); - let defuse = false; - if ( log !== undefined ) { - log('uBO: requestAnimationFrame("%s")', a); - } else { - defuse = reNeedle.test(a) !== needleNot; - } - if ( defuse ) { - args[0] = function(){}; - } - return target.apply(thisArg, args); - } - }); -} - -/******************************************************************************/ - -builtinScriptlets.push({ - name: 'no-setInterval-if.js', - aliases: [ - 'nosiif.js', - 'prevent-setInterval.js', - 'setInterval-defuser.js', - ], - fn: noSetIntervalIf, - dependencies: [ - 'proxy-apply.fn', - 'safe-self.fn', - ], -}); -function noSetIntervalIf( - needle = '', - delay = '' -) { - if ( typeof needle !== 'string' ) { return; } - const safe = safeSelf(); - const logPrefix = safe.makeLogPrefix('prevent-setInterval', needle, delay); - const needleNot = needle.charAt(0) === '!'; - if ( needleNot ) { needle = needle.slice(1); } - if ( delay === '' ) { delay = undefined; } - let delayNot = false; - if ( delay !== undefined ) { - delayNot = delay.charAt(0) === '!'; - if ( delayNot ) { delay = delay.slice(1); } - delay = parseInt(delay, 10); - } - const reNeedle = safe.patternToRegex(needle); - proxyApplyFn('setInterval', function setInterval(context) { - const { callArgs } = context; - const a = callArgs[0] instanceof Function - ? String(safe.Function_toString(callArgs[0])) - : String(callArgs[0]); - const b = callArgs[1]; - if ( needle === '' && delay === undefined ) { - safe.uboLog(logPrefix, `Called:\n${a}\n${b}`); - return context.reflect(); - } - let defuse; - if ( needle !== '' ) { - defuse = reNeedle.test(a) !== needleNot; - } - if ( defuse !== false && delay !== undefined ) { - defuse = (b === delay || isNaN(b) && isNaN(delay) ) !== delayNot; - } - if ( defuse ) { - callArgs[0] = function(){}; - safe.uboLog(logPrefix, `Prevented:\n${a}\n${b}`); - } - return context.reflect(); - }); -} - -/******************************************************************************/ - -builtinScriptlets.push({ - name: 'no-setTimeout-if.js', - aliases: [ - 'nostif.js', - 'prevent-setTimeout.js', - 'setTimeout-defuser.js', - ], - fn: noSetTimeoutIf, - dependencies: [ - 'proxy-apply.fn', - 'safe-self.fn', - ], -}); -function noSetTimeoutIf( - needle = '', - delay = '' -) { - if ( typeof needle !== 'string' ) { return; } - const safe = safeSelf(); - const logPrefix = safe.makeLogPrefix('prevent-setTimeout', needle, delay); - const needleNot = needle.charAt(0) === '!'; - if ( needleNot ) { needle = needle.slice(1); } - if ( delay === '' ) { delay = undefined; } - let delayNot = false; - if ( delay !== undefined ) { - delayNot = delay.charAt(0) === '!'; - if ( delayNot ) { delay = delay.slice(1); } - delay = parseInt(delay, 10); - } - const reNeedle = safe.patternToRegex(needle); - proxyApplyFn('setTimeout', function setTimeout(context) { - const { callArgs } = context; - const a = callArgs[0] instanceof Function - ? String(safe.Function_toString(callArgs[0])) - : String(callArgs[0]); - const b = callArgs[1]; - if ( needle === '' && delay === undefined ) { - safe.uboLog(logPrefix, `Called:\n${a}\n${b}`); - return context.reflect(); - } - let defuse; - if ( needle !== '' ) { - defuse = reNeedle.test(a) !== needleNot; - } - if ( defuse !== false && delay !== undefined ) { - defuse = (b === delay || isNaN(b) && isNaN(delay) ) !== delayNot; - } - if ( defuse ) { - callArgs[0] = function(){}; - safe.uboLog(logPrefix, `Prevented:\n${a}\n${b}`); - } - return context.reflect(); - }); -} - -/******************************************************************************/ - builtinScriptlets.push({ name: 'webrtc-if.js', fn: webrtcIf, From 58a5a23763e5f8449928ef84173f0e67985117a8 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Thu, 28 Nov 2024 11:51:05 -0500 Subject: [PATCH 0485/1099] Update changelog --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3016630f05dd4..898505fa9e206 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,5 @@ +- [Improve `prevent-setTimeout`/`prevent-setInterval` scriptlets](https://github.com/gorhill/uBlock/commit/3b7fa79a68) +- [Improve `trusted-replace-argument` scriptlet](https://github.com/gorhill/uBlock/commit/adced29b5b) - [Add `-safebase64` directive to `urlskip=` option](https://github.com/gorhill/uBlock/commit/bcc058eba7) - [Improve `urlskip=` filter option](https://github.com/gorhill/uBlock/commit/77ed83ff2f) - [Improve `spoof-css` scriptlet](https://github.com/gorhill/uBlock/commit/5f5e3d730f) From f04645ab9fd37ca748403f35cd6ec5434aeaad19 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Thu, 28 Nov 2024 11:51:27 -0500 Subject: [PATCH 0486/1099] New revision for dev build --- dist/version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dist/version b/dist/version index 9dbe8ea6eefd8..21de96197f693 100644 --- a/dist/version +++ b/dist/version @@ -1 +1 @@ -1.61.3.0 \ No newline at end of file +1.61.3.1 \ No newline at end of file From d6867699c98c9d71056a901a975da6b366438d5d Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Thu, 28 Nov 2024 12:01:12 -0500 Subject: [PATCH 0487/1099] Make Firefox dev build auto-update --- dist/firefox/updates.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dist/firefox/updates.json b/dist/firefox/updates.json index 1a51d97ba7d92..c10ca37b0d9dc 100644 --- a/dist/firefox/updates.json +++ b/dist/firefox/updates.json @@ -3,9 +3,9 @@ "uBlock0@raymondhill.net": { "updates": [ { - "version": "1.61.3.0", + "version": "1.61.3.1", "browser_specific_settings": { "gecko": { "strict_min_version": "78.0" } }, - "update_link": "https://github.com/gorhill/uBlock/releases/download/1.61.3b0/uBlock0_1.61.3b0.firefox.signed.xpi" + "update_link": "https://github.com/gorhill/uBlock/releases/download/1.61.3b1/uBlock0_1.61.3b1.firefox.signed.xpi" } ] } From b1a00145bd704b87369c680621f68123e70cef52 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Fri, 29 Nov 2024 10:13:39 -0500 Subject: [PATCH 0488/1099] Mitigate potentially delayed execution of scriptlets in Firefox Related issue: https://github.com/uBlockOrigin/uBlock-issues/issues/3452 Use blob-based injection only when direct injection fails because of a page's CSP. This is a mitigation until a better approach is devised. Such future better approach to investigate: - Use `MAIN` world injection supported by contentScript.register() since Firefox 128 - Investigate registering script to inject ahead of time thru some heuristic --- platform/chromium/vapi-background-ext.js | 52 ++++++++++---- platform/firefox/vapi-background-ext.js | 92 ++++++++++++++++++------ src/js/scriptlet-filtering.js | 32 +-------- 3 files changed, 111 insertions(+), 65 deletions(-) diff --git a/platform/chromium/vapi-background-ext.js b/platform/chromium/vapi-background-ext.js index 9ee69d8199ae5..acbdc3e9d7b74 100644 --- a/platform/chromium/vapi-background-ext.js +++ b/platform/chromium/vapi-background-ext.js @@ -208,19 +208,43 @@ vAPI.prefetching = (( ) => { /******************************************************************************/ -vAPI.scriptletsInjector = ((doc, details) => { - let script; - try { - script = doc.createElement('script'); - script.appendChild(doc.createTextNode(details.scriptlets)); - (doc.head || doc.documentElement).appendChild(script); - self.uBO_scriptletsInjected = details.filters; - } catch (ex) { - } - if ( script ) { - script.remove(); - script.textContent = ''; - } -}).toString(); +vAPI.scriptletsInjector = (( ) => { + const parts = [ + '(', + function(details) { + if ( typeof self.uBO_scriptletsInjected === 'string' ) { return; } + const doc = document; + const { location } = doc; + if ( location === null ) { return; } + const { hostname } = location; + if ( hostname !== '' && details.hostname !== hostname ) { return; } + let script; + try { + script = doc.createElement('script'); + script.appendChild(doc.createTextNode(details.scriptlets)); + (doc.head || doc.documentElement).appendChild(script); + self.uBO_scriptletsInjected = details.filters; + } catch (ex) { + } + if ( script ) { + script.remove(); + script.textContent = ''; + } + return 0; + }.toString(), + ')(', + 'json-slot', + ');', + ]; + const jsonSlot = parts.indexOf('json-slot'); + return (hostname, details) => { + parts[jsonSlot] = JSON.stringify({ + hostname, + scriptlets: details.mainWorld, + filters: details.filters, + }); + return parts.join(''); + }; +})(); /******************************************************************************/ diff --git a/platform/firefox/vapi-background-ext.js b/platform/firefox/vapi-background-ext.js index 65420bc3f39cf..5710224a34adb 100644 --- a/platform/firefox/vapi-background-ext.js +++ b/platform/firefox/vapi-background-ext.js @@ -351,25 +351,77 @@ vAPI.Net = class extends vAPI.Net { /******************************************************************************/ -vAPI.scriptletsInjector = ((doc, details) => { - let script, url; - try { - const blob = new self.Blob( - [ details.scriptlets ], - { type: 'text/javascript; charset=utf-8' } - ); - url = self.URL.createObjectURL(blob); - script = doc.createElement('script'); - script.async = false; - script.src = url; - (doc.head || doc.documentElement || doc).append(script); - self.uBO_scriptletsInjected = details.filters; - } catch (ex) { - } - if ( url ) { - if ( script ) { script.remove(); } - self.URL.revokeObjectURL(url); - } -}).toString(); +vAPI.scriptletsInjector = (( ) => { + const parts = [ + '(', + function(details) { + if ( typeof self.uBO_scriptletsInjected === 'string' ) { return; } + const doc = document; + const { location } = doc; + if ( location === null ) { return; } + const { hostname } = location; + if ( hostname !== '' && details.hostname !== hostname ) { return; } + // Use a page world sentinel to verify that execution was + // successful + const { sentinel } = details; + let script; + try { + const code = [ + `self['${sentinel}'] = true;`, + details.scriptlets, + ].join('\n'); + script = doc.createElement('script'); + script.appendChild(doc.createTextNode(code)); + (doc.head || doc.documentElement).appendChild(script); + } catch (ex) { + } + if ( script ) { + script.remove(); + script.textContent = ''; + script = undefined; + } + if ( self.wrappedJSObject[sentinel] ) { + delete self.wrappedJSObject[sentinel]; + self.uBO_scriptletsInjected = details.filters; + return 0; + } + // https://github.com/uBlockOrigin/uBlock-issues/issues/235 + // Fall back to blob injection if execution through direct + // injection failed + let url; + try { + const blob = new self.Blob( + [ details.scriptlets ], + { type: 'text/javascript; charset=utf-8' } + ); + url = self.URL.createObjectURL(blob); + script = doc.createElement('script'); + script.async = false; + script.src = url; + (doc.head || doc.documentElement || doc).append(script); + self.uBO_scriptletsInjected = details.filters; + } catch (ex) { + } + if ( url ) { + if ( script ) { script.remove(); } + self.URL.revokeObjectURL(url); + } + return 0; + }.toString(), + ')(', + 'json-slot', + ');', + ]; + const jsonSlot = parts.indexOf('json-slot'); + return (hostname, details) => { + parts[jsonSlot] = JSON.stringify({ + hostname, + scriptlets: details.mainWorld, + filters: details.filters, + sentinel: vAPI.generateSecret(3), + }); + return parts.join(''); + }; +})(); /******************************************************************************/ diff --git a/src/js/scriptlet-filtering.js b/src/js/scriptlet-filtering.js index 1cc6a959b3536..b7a0617722838 100644 --- a/src/js/scriptlet-filtering.js +++ b/src/js/scriptlet-filtering.js @@ -106,36 +106,6 @@ const contentScriptRegisterer = new (class { /******************************************************************************/ -const mainWorldInjector = (( ) => { - const parts = [ - '(', - function(injector, details) { - if ( typeof self.uBO_scriptletsInjected === 'string' ) { return; } - const doc = document; - if ( doc.location === null ) { return; } - const hostname = doc.location.hostname; - if ( hostname !== '' && details.hostname !== hostname ) { return; } - injector(doc, details); - return 0; - }.toString(), - ')(', - vAPI.scriptletsInjector, ', ', - 'json-slot', - ');', - ]; - const jsonSlot = parts.indexOf('json-slot'); - return { - assemble: function(hostname, details) { - parts[jsonSlot] = JSON.stringify({ - hostname, - scriptlets: details.mainWorld, - filters: details.filters, - }); - return parts.join(''); - }, - }; -})(); - const isolatedWorldInjector = (( ) => { const parts = [ '(', @@ -334,7 +304,7 @@ export class ScriptletFilteringEngineEx extends ScriptletFilteringEngine { const contentScript = []; if ( scriptletDetails.mainWorld ) { - contentScript.push(mainWorldInjector.assemble(hostname, scriptletDetails)); + contentScript.push(vAPI.scriptletsInjector(hostname, scriptletDetails)); } if ( scriptletDetails.isolatedWorld ) { contentScript.push(isolatedWorldInjector.assemble(hostname, scriptletDetails)); From 580f2dee06c0f8defee19bb5b0be6ee0be6aba08 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Fri, 29 Nov 2024 10:54:43 -0500 Subject: [PATCH 0489/1099] Update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 898505fa9e206..ec88ef78c4ea0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,4 @@ +- [Mitigate potentially delayed execution of scriptlets in Firefox](https://github.com/gorhill/uBlock/commit/b1a00145bd) - [Improve `prevent-setTimeout`/`prevent-setInterval` scriptlets](https://github.com/gorhill/uBlock/commit/3b7fa79a68) - [Improve `trusted-replace-argument` scriptlet](https://github.com/gorhill/uBlock/commit/adced29b5b) - [Add `-safebase64` directive to `urlskip=` option](https://github.com/gorhill/uBlock/commit/bcc058eba7) From 47bdec422a683815c8594d71e8b7c97ea4ecfa99 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Fri, 29 Nov 2024 10:55:07 -0500 Subject: [PATCH 0490/1099] New revision for dev build --- dist/version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dist/version b/dist/version index 21de96197f693..cf4063a4b0380 100644 --- a/dist/version +++ b/dist/version @@ -1 +1 @@ -1.61.3.1 \ No newline at end of file +1.61.3.2 \ No newline at end of file From 556bea809e9990d3670a1079b8fca19f9dc1b4d2 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Fri, 29 Nov 2024 11:01:23 -0500 Subject: [PATCH 0491/1099] Make Firefox dev build auto-update --- dist/firefox/updates.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dist/firefox/updates.json b/dist/firefox/updates.json index c10ca37b0d9dc..247f6252182f4 100644 --- a/dist/firefox/updates.json +++ b/dist/firefox/updates.json @@ -3,9 +3,9 @@ "uBlock0@raymondhill.net": { "updates": [ { - "version": "1.61.3.1", + "version": "1.61.3.2", "browser_specific_settings": { "gecko": { "strict_min_version": "78.0" } }, - "update_link": "https://github.com/gorhill/uBlock/releases/download/1.61.3b1/uBlock0_1.61.3b1.firefox.signed.xpi" + "update_link": "https://github.com/gorhill/uBlock/releases/download/1.61.3b2/uBlock0_1.61.3b2.firefox.signed.xpi" } ] } From d7df6cda4ae5467d679fbdcc5a7fab3a80f5cdc0 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sun, 1 Dec 2024 19:46:29 -0500 Subject: [PATCH 0492/1099] Import translation work from https://crowdin.com/project/ublock --- .../mv3/extension/_locales/bg/messages.json | 8 ++++---- .../mv3/extension/_locales/el/messages.json | 2 +- .../mv3/extension/_locales/fy/messages.json | 2 +- .../mv3/extension/_locales/gl/messages.json | 2 +- .../mv3/extension/_locales/hu/messages.json | 2 +- .../mv3/extension/_locales/lv/messages.json | 18 +++++++++--------- src/_locales/bg/messages.json | 2 +- src/_locales/hu/messages.json | 2 +- src/_locales/zh_TW/messages.json | 2 +- 9 files changed, 20 insertions(+), 20 deletions(-) diff --git a/platform/mv3/extension/_locales/bg/messages.json b/platform/mv3/extension/_locales/bg/messages.json index e3d6b2545756d..5c2ecb76f4ac9 100644 --- a/platform/mv3/extension/_locales/bg/messages.json +++ b/platform/mv3/extension/_locales/bg/messages.json @@ -108,7 +108,7 @@ "description": "Header of 'Report a filter issue' section in Support pane" }, "supportS3P1": { - "message": "Докладвайте за проблеми с филтъра с конкретни уебсайтове към uBlockOrigin/uAssets инструмент за проследяване на проблеми. Изисква акаунт в GitHub.", + "message": "Докладвайте за проблеми с филтрирането на конкретни уебсайтове в uBlockOrigin/uAssets за проследяване на проблеми. Изисква се акаунт в GitHub.", "description": "First paragraph of 'Filter issues' section in Support pane" }, "supportS6P1S1": { @@ -140,7 +140,7 @@ "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option3": { - "message": "Открива uBO Lite", + "message": "Засича uBO Lite", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option4": { @@ -148,7 +148,7 @@ "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option5": { - "message": "Неправилно функциониране, когато е активиран uBO Lite", + "message": "Неправилно функциониране, когато uBO Lite е активиран", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option6": { @@ -212,7 +212,7 @@ "description": "This describes the 'complete' filtering mode" }, "noFilteringModeDescription": { - "message": "Списък с имена на хостове, за които няма да се извършва филтриране", + "message": "Списък с имена на хостове, за които няма да се извършва филтриране.", "description": "A short description for the editable field which lists trusted sites" }, "noFilteringModePlaceholder": { diff --git a/platform/mv3/extension/_locales/el/messages.json b/platform/mv3/extension/_locales/el/messages.json index b1941281a3299..45ef7ec9d9cc2 100644 --- a/platform/mv3/extension/_locales/el/messages.json +++ b/platform/mv3/extension/_locales/el/messages.json @@ -232,7 +232,7 @@ "description": "Label for a checkbox in the options page" }, "findListsPlaceholder": { - "message": "Find lists", + "message": "Εύρεση λιστών", "description": "Placeholder for the input field used to find lists" } } diff --git a/platform/mv3/extension/_locales/fy/messages.json b/platform/mv3/extension/_locales/fy/messages.json index 4befbce49f5d5..44d68458821ed 100644 --- a/platform/mv3/extension/_locales/fy/messages.json +++ b/platform/mv3/extension/_locales/fy/messages.json @@ -232,7 +232,7 @@ "description": "Label for a checkbox in the options page" }, "findListsPlaceholder": { - "message": "Sykje listen", + "message": "Listen sykje", "description": "Placeholder for the input field used to find lists" } } diff --git a/platform/mv3/extension/_locales/gl/messages.json b/platform/mv3/extension/_locales/gl/messages.json index f45f9f240ed25..53589b6f6cf72 100644 --- a/platform/mv3/extension/_locales/gl/messages.json +++ b/platform/mv3/extension/_locales/gl/messages.json @@ -232,7 +232,7 @@ "description": "Label for a checkbox in the options page" }, "findListsPlaceholder": { - "message": "Find lists", + "message": "Atopa listas", "description": "Placeholder for the input field used to find lists" } } diff --git a/platform/mv3/extension/_locales/hu/messages.json b/platform/mv3/extension/_locales/hu/messages.json index 3ba4ccdcace10..cacd7fee325ad 100644 --- a/platform/mv3/extension/_locales/hu/messages.json +++ b/platform/mv3/extension/_locales/hu/messages.json @@ -232,7 +232,7 @@ "description": "Label for a checkbox in the options page" }, "findListsPlaceholder": { - "message": "Find lists", + "message": "Listák keresése", "description": "Placeholder for the input field used to find lists" } } diff --git a/platform/mv3/extension/_locales/lv/messages.json b/platform/mv3/extension/_locales/lv/messages.json index bc81840985140..1d470f70f1ee0 100644 --- a/platform/mv3/extension/_locales/lv/messages.json +++ b/platform/mv3/extension/_locales/lv/messages.json @@ -32,7 +32,7 @@ "description": "Label in the popup panel for the current filtering mode" }, "popupTipReport": { - "message": "Ziņot par problēmu šajā tīmekļa vietnē", + "message": "Ziņot par nepilnību šajā tīmekļa vietnē", "description": "Tooltip used for the 'chat' icon in the panel" }, "popupTipDashboard": { @@ -104,15 +104,15 @@ "description": "Shown in the About pane" }, "supportS6H": { - "message": "Ziņot aizturēšanas filtra problēmu", + "message": "Ziņot par aizturēšanas filtra nepilnību", "description": "Header of 'Report a filter issue' section in Support pane" }, "supportS3P1": { - "message": "Ziņot par aizturēšanas filtru problēmām konkrētās vietnēs uBlockOrigin/uAssets problēmu izsekotājam. Nepieciešams GitHub konts.", + "message": "Ziņot par aizturēšanas filtru nepilnībām noteiktās vietnēs uBlockOrigin/uAssets pieteikumu izsekotājā. Nepieciešams GitHub konts.", "description": "First paragraph of 'Filter issues' section in Support pane" }, "supportS6P1S1": { - "message": "Lai izvairītos no brīvprātīgo noslogošanas ar ziņojumiem, kas atkārtojas, lūgums pārbaudīt, ka par šādu problēmu jau nav ziņots.", + "message": "Lai izvairītos no brīvprātīgo noslogošanas ar ziņojumiem, kas atkārtojas, lūgums pārbaudīt, ka par šādu nepilnību jau nav ziņots.", "description": "A paragraph in the filter issue reporter section" }, "supportFindSpecificButton": { @@ -128,7 +128,7 @@ "description": "Label for widget to select type of issue" }, "supportS6Select1Option0": { - "message": "-- Atlasiet ierakstu --", + "message": "-- Atlasīt ierakstu --", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option1": { @@ -136,7 +136,7 @@ "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option2": { - "message": "Ir pārklājumi vai citas neērtības (kaitinoši elementi)", + "message": "Ir pārklājumi vai citi traucējumi", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option3": { @@ -144,11 +144,11 @@ "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option4": { - "message": "Ir ar konfidencialitāti saistītas nebūšanas", + "message": "Ir ar privātumu saistītas nepilnības", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option5": { - "message": "Darbības traucējumi, kad uBO Lite ir iespējots", + "message": "Darbības traucējumi, kad ir iespējots uBO Lite", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option6": { @@ -156,7 +156,7 @@ "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option7": { - "message": "Noved pie ļaunprātīgas programmatūras, pikšķerēšanas", + "message": "Noved pie ļaunatūras, pikšķerēšanas", "description": "An entry in the widget used to select the type of issue" }, "supportS6Checkbox1": { diff --git a/src/_locales/bg/messages.json b/src/_locales/bg/messages.json index c17e58fc00399..b5cbeadfab1ab 100644 --- a/src/_locales/bg/messages.json +++ b/src/_locales/bg/messages.json @@ -1192,7 +1192,7 @@ "description": "Button text to navigate to the blocked page" }, "docblockedRedirectPrompt": { - "message": "Блокираната страница иска да ви пренасочи към друг сайт. Ако изберете да продължите, ще отидете директно на: {{url}}", + "message": "Блокираната страница иска да Ви пренасочи към друг сайт. Ако изберете да продължите, ще отидете директно на: {{url}}", "description": "Text warning about an incoming redirect" }, "cloudPush": { diff --git a/src/_locales/hu/messages.json b/src/_locales/hu/messages.json index 6408662920d50..062d1dae1c9c2 100644 --- a/src/_locales/hu/messages.json +++ b/src/_locales/hu/messages.json @@ -1192,7 +1192,7 @@ "description": "Button text to navigate to the blocked page" }, "docblockedRedirectPrompt": { - "message": "The blocked page wants to redirect to another site. If you choose to proceed, you will navigate directly to: {{url}}", + "message": "A blokkolt oldal egy másik webhelyre akarja átirányítani. Ha a folytatást választja, akkor követlenül ide fog navigálni: {{url}}", "description": "Text warning about an incoming redirect" }, "cloudPush": { diff --git a/src/_locales/zh_TW/messages.json b/src/_locales/zh_TW/messages.json index 871a414a90737..92923a9e122c8 100644 --- a/src/_locales/zh_TW/messages.json +++ b/src/_locales/zh_TW/messages.json @@ -1192,7 +1192,7 @@ "description": "Button text to navigate to the blocked page" }, "docblockedRedirectPrompt": { - "message": "被封鎖的頁面想要重新導向至另一個網站。如果您選擇繼續,則會直接前往:{{url}}", + "message": "被封鎖的網頁想要重新導向至其他網站。如果您選擇繼續,則會直接前往:{{url}}", "description": "Text warning about an incoming redirect" }, "cloudPush": { From aa05cb32c6e6b25a29cdb3ade8003ad3b0173883 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Tue, 3 Dec 2024 16:41:34 -0500 Subject: [PATCH 0493/1099] [mv3] Implement strict blocking Related issue: https://github.com/uBlockOrigin/uBOL-home/issues/214 This implements basic functionality for strict blocking, i.e. the ability to block navigation to undesirable websites. This is a first implementation, which converts only filters that are plain hostnames. Unlike with uBO, it is not possible to know from which ruleset a blocking rule originates. Nonetheless, users will have to make a choice as to whether navigation should proceed or not. A setting has been added to the dashboard to wholly enable/disable strict blocking. It is enabled by default. Potential future improvements, pending investigation on feasability in an MV3 framework: - Extend coverage to explicit `document` filters - Leverage and use `urlskip=` filters in the blocking page in order to proceed while bypassing unwanted redirects. --- platform/mv3/chromium/manifest.json | 5 +- .../mv3/extension/_locales/en/messages.json | 36 ++ platform/mv3/extension/css/settings.css | 7 +- platform/mv3/extension/css/strictblock.css | 147 +++++ platform/mv3/extension/dashboard.html | 1 + platform/mv3/extension/js/background.js | 29 +- platform/mv3/extension/js/config.js | 3 + platform/mv3/extension/js/ext.js | 6 + platform/mv3/extension/js/mode-manager.js | 107 +--- platform/mv3/extension/js/popup.js | 20 +- platform/mv3/extension/js/ruleset-manager.js | 594 +++++++++++------- platform/mv3/extension/js/settings.js | 16 + platform/mv3/extension/js/strictblock.js | 178 ++++++ platform/mv3/firefox/manifest.json | 7 +- platform/mv3/make-rulesets.js | 40 +- platform/mv3/strictblock.html | 43 ++ src/document-blocked.html | 4 +- tools/make-mv3.sh | 1 + 18 files changed, 879 insertions(+), 365 deletions(-) create mode 100644 platform/mv3/extension/css/strictblock.css create mode 100644 platform/mv3/extension/js/strictblock.js create mode 100644 platform/mv3/strictblock.html diff --git a/platform/mv3/chromium/manifest.json b/platform/mv3/chromium/manifest.json index 49f1381b2c679..72760301c5a32 100644 --- a/platform/mv3/chromium/manifest.json +++ b/platform/mv3/chromium/manifest.json @@ -25,7 +25,7 @@ "128": "img/icon_128.png" }, "manifest_version": 3, - "minimum_chrome_version": "119.0", + "minimum_chrome_version": "122.0", "name": "__MSG_extName__", "options_page": "dashboard.html", "optional_host_permissions": [ @@ -41,6 +41,5 @@ "storage": { "managed_schema": "managed_storage.json" }, - "version": "1.0", - "web_accessible_resources": [] + "version": "1.0" } diff --git a/platform/mv3/extension/_locales/en/messages.json b/platform/mv3/extension/_locales/en/messages.json index 60cc95401447c..17a5b24cd7935 100644 --- a/platform/mv3/extension/_locales/en/messages.json +++ b/platform/mv3/extension/_locales/en/messages.json @@ -231,8 +231,44 @@ "message": "Show the number of blocked requests on the toolbar icon", "description": "Label for a checkbox in the options page" }, + "enableStrictBlockLabel": { + "message": "Enable strict blocking", + "description": "Label for a checkbox in the options page" + }, + "enableStrictBlockLegend": { + "message": "Navigation to potentially undesirable sites will be blocked, and you will be offered the option to proceed.", + "description": "Label for a checkbox in the options page" + }, "findListsPlaceholder": { "message": "Find lists", "description": "Placeholder for the input field used to find lists" + }, + "strictblockTitle": { + "message": "Page blocked", + "description": "Webpage title for the strict-blocked page" + }, + "strictblockSentence1": { + "message": "uBO Lite has prevented the following page from loading:", + "description": "Sentence used in the strict-blocked page" + }, + "strictblockNoParamsPrompt": { + "message": "without parameters", + "description": "Label to be used for the parameter-less URL" + }, + "strictblockBack": { + "message": "Go back", + "description": "A button to go back to the previous webpage" + }, + "strictblockClose": { + "message": "Close this window", + "description": "A button to close the current tab" + }, + "strictblockDontWarn": { + "message": "Don't warn me again about this site", + "description": "Label for checkbox in document-blocked page" + }, + "strictblockProceed": { + "message": "Proceed", + "description": "A button to navigate to the blocked page" } } diff --git a/platform/mv3/extension/css/settings.css b/platform/mv3/extension/css/settings.css index feefcadd8c3ee..a8b07d2a1894c 100644 --- a/platform/mv3/extension/css/settings.css +++ b/platform/mv3/extension/css/settings.css @@ -12,8 +12,11 @@ body.firstRun .firstRun { h3 { margin: 1em 0; } -p { - white-space: pre-line; + +label + legend { + color: color-mix(in srgb, currentColor 69%, transparent); + font-size: small; + margin-inline-start: var(--default-gap-large); } body[data-forbid~="dashboard"] #dashboard-nav [data-pane="settings"], diff --git a/platform/mv3/extension/css/strictblock.css b/platform/mv3/extension/css/strictblock.css new file mode 100644 index 0000000000000..f7534078cca7b --- /dev/null +++ b/platform/mv3/extension/css/strictblock.css @@ -0,0 +1,147 @@ +/** + uBlock Origin - a browser extension to block requests. + Copyright (C) 2018-present Raymond Hill + + 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/}. + + Home: https://github.com/gorhill/uBlock +*/ + +body { + display: flex; + padding: var(--default-gap-xxlarge) var(--default-gap-small); + justify-content: center; + } +:root.mobile body { + padding: var(--default-gap-small); + } + +#rootContainer { + width: min(100%, 640px); + } +#rootContainer > * { + margin: 0 0 var(--default-gap-xxlarge) 0; + } +:root.mobile #rootContainer > * { + margin-bottom: var(--default-gap-xlarge); + } + +p { + margin: 0.5em 0; + } +a { + text-decoration: none; + } +.code { + font-size: 13px; + word-break: break-all; + } +#warningSign { + color: var(--accent-surface-1); + fill: var(--accent-surface-1); + font-size: 96px; + line-height: 1; + width: 100%; + } +:root.mobile #warningSign { + font-size: 64px; + } +#theURL { + color: var(--ink-2); + padding: 0; + } +#theURL > * { + margin: 0; + } +#theURL > p { + position: relative; + z-index: 10; + } +#theURL > p > span:first-of-type { + display: block; + max-height: 6lh; + overflow-y: auto; + } +:root.mobile #theURL > p > span:first-of-type { + max-height: 3lh; + } +#theURL #toggleParse { + background-color: transparent; + top: 100%; + box-sizing: border-box; + color: var(--ink-3); + fill: var(--ink-3); + cursor: pointer; + font-size: 1.2rem; + padding: var(--default-gap-xxsmall); + position: absolute; + transform: translate(0, -50%); + } +#theURL:not(.collapsed) #toggleParse > span:first-of-type { + display: none; + } +#theURL.collapsed #toggleParse > span:last-of-type { + display: none; + } +body[dir="ltr"] #toggleParse { + right: 0; + } +body[dir="rtl"] #toggleParse { + left: 0; + } +#theURL > p:hover #toggleParse { + transform: translate(0, -50%) scale(1.15); + } +#parsed { + background-color: var(--surface-1); + border: 4px solid var(--surface-2); + font-size: small; + overflow-x: auto; + padding: var(--default-gap-xxsmall); + text-align: initial; + text-overflow: ellipsis; + } +#theURL.collapsed > #parsed { + display: none; + } +#parsed ul, #parsed li { + list-style-type: none; + } +#parsed li { + white-space: nowrap; + } +#parsed span { + display: inline-block; + } +#parsed span:first-of-type { + font-weight: bold; + } + +#actionContainer { + display: flex; + justify-content: space-between; + } +:root.mobile #actionContainer { + justify-content: center; + display: flex; + flex-direction: column; + } +#actionContainer > button { + margin-bottom: 2rem + } + +/* Small-screen devices */ +:root.mobile button { + width: 100%; + } diff --git a/platform/mv3/extension/dashboard.html b/platform/mv3/extension/dashboard.html index bcd1726bd05a3..5ac63f3d06d0f 100644 --- a/platform/mv3/extension/dashboard.html +++ b/platform/mv3/extension/dashboard.html @@ -31,6 +31,7 @@

+

diff --git a/platform/mv3/extension/js/background.js b/platform/mv3/extension/js/background.js index 2e3a41124f5c0..a58dd70639a48 100644 --- a/platform/mv3/extension/js/background.js +++ b/platform/mv3/extension/js/background.js @@ -47,8 +47,10 @@ import { import { enableRulesets, + excludeFromStrictBlock, getEnabledRulesetsDetails, getRulesetDetails, + setStrictBlockMode, updateDynamicRules, } from './ruleset-manager.js'; @@ -213,6 +215,7 @@ function onMessage(request, sender, callback) { autoReload: rulesetConfig.autoReload, showBlockedCount: rulesetConfig.showBlockedCount, canShowBlockedCount, + strictBlockMode: rulesetConfig.strictBlockMode, firstRun: process.firstRun, isSideloaded, developerMode: rulesetConfig.developerMode, @@ -244,6 +247,13 @@ function onMessage(request, sender, callback) { }); return true; + case 'setStrictBlockMode': + setStrictBlockMode(request.state).then(( ) => { + callback(); + broadcastMessage({ strictBlockMode: rulesetConfig.strictBlockMode }); + }); + return true; + case 'setDeveloperMode': rulesetConfig.developerMode = request.state; toggleDeveloperMode(rulesetConfig.developerMode); @@ -335,6 +345,13 @@ function onMessage(request, sender, callback) { }); return true; + case 'excludeFromStrictBlock': { + excludeFromStrictBlock(request.hostname, request.permanent).then(( ) => { + callback(); + }); + return true; + } + case 'getMatchedRules': getMatchedRules(request.tabId).then(entries => { callback(entries); @@ -360,19 +377,19 @@ function onMessage(request, sender, callback) { async function start() { await loadRulesetConfig(); - if ( process.wakeupRun === false ) { + const rulesetsUpdated = process.wakeupRun === false && await enableRulesets(rulesetConfig.enabledRulesets); - } // We need to update the regex rules only when ruleset version changes. if ( process.wakeupRun === false ) { const currentVersion = getCurrentVersion(); if ( currentVersion !== rulesetConfig.version ) { ubolLog(`Version change: ${rulesetConfig.version} => ${currentVersion}`); - updateDynamicRules().then(( ) => { - rulesetConfig.version = currentVersion; - saveRulesetConfig(); - }); + rulesetConfig.version = currentVersion; + saveRulesetConfig(); + if ( rulesetsUpdated === false ) { + updateDynamicRules(); + } } } diff --git a/platform/mv3/extension/js/config.js b/platform/mv3/extension/js/config.js index 71524e56048bb..e3859e63fa42f 100644 --- a/platform/mv3/extension/js/config.js +++ b/platform/mv3/extension/js/config.js @@ -33,6 +33,7 @@ export const rulesetConfig = { enabledRulesets: [ 'default' ], autoReload: true, showBlockedCount: true, + strictBlockMode: true, developerMode: false, }; @@ -50,6 +51,7 @@ export async function loadRulesetConfig() { rulesetConfig.enabledRulesets = sessionData.enabledRulesets; rulesetConfig.autoReload = sessionData.autoReload ?? true; rulesetConfig.showBlockedCount = sessionData.showBlockedCount ?? true; + rulesetConfig.strictBlockMode = sessionData.strictBlockMode ?? true; rulesetConfig.developerMode = sessionData.developerMode ?? false; process.wakeupRun = true; return; @@ -60,6 +62,7 @@ export async function loadRulesetConfig() { rulesetConfig.enabledRulesets = localData.enabledRulesets; rulesetConfig.autoReload = localData.autoReload ?? true; rulesetConfig.showBlockedCount = localData.showBlockedCount ?? true; + rulesetConfig.strictBlockMode = localData.strictBlockMode ?? true; rulesetConfig.developerMode = localData.developerMode ?? false; sessionWrite('rulesetConfig', rulesetConfig); return; diff --git a/platform/mv3/extension/js/ext.js b/platform/mv3/extension/js/ext.js index 94d5118a211b5..0a5a98f4f59a2 100644 --- a/platform/mv3/extension/js/ext.js +++ b/platform/mv3/extension/js/ext.js @@ -98,6 +98,12 @@ export async function sessionWrite(key, value) { return browser.storage.session.set({ [key]: value }); } +export async function sessionRemove(key) { + if ( browser.storage instanceof Object === false ) { return; } + if ( browser.storage.session instanceof Object === false ) { return; } + return browser.storage.session.remove(key); +} + /******************************************************************************/ export async function adminRead(key) { diff --git a/platform/mv3/extension/js/mode-manager.js b/platform/mv3/extension/js/mode-manager.js index 335c8da166d73..3630925caad9a 100644 --- a/platform/mv3/extension/js/mode-manager.js +++ b/platform/mv3/extension/js/mode-manager.js @@ -19,11 +19,6 @@ Home: https://github.com/gorhill/uBlock */ -import { - TRUSTED_DIRECTIVE_BASE_RULE_ID, - getDynamicRules, -} from './ruleset-manager.js'; - import { broadcastMessage, hostnamesFromMatches, @@ -33,12 +28,12 @@ import { import { browser, - dnr, localRead, localWrite, sessionRead, sessionWrite, } from './ext.js'; import { adminReadEx } from './admin.js'; +import { filteringModesToDNR } from './ruleset-manager.js'; /******************************************************************************/ @@ -74,19 +69,6 @@ const pruneHostnameFromSet = (hostname, hnSet) => { /******************************************************************************/ -const eqSets = (setBefore, setAfter) => { - if ( setBefore.size !== setAfter.size ) { return false; } - for ( const hn of setAfter ) { - if ( setBefore.has(hn) === false ) { return false; } - } - for ( const hn of setBefore ) { - if ( setAfter.has(hn) === false ) { return false; } - } - return true; -}; - -/******************************************************************************/ - const serializeModeDetails = details => { return { none: Array.from(details.none), @@ -284,93 +266,6 @@ async function writeFilteringModeDetails(afterDetails) { /******************************************************************************/ -async function filteringModesToDNR(modes) { - const dynamicRuleMap = await getDynamicRules(); - const trustedRule = dynamicRuleMap.get(TRUSTED_DIRECTIVE_BASE_RULE_ID+0); - const beforeRequestDomainSet = new Set(trustedRule?.condition.requestDomains); - const beforeExcludedRrequestDomainSet = new Set(trustedRule?.condition.excludedRequestDomains); - if ( trustedRule !== undefined && beforeRequestDomainSet.size === 0 ) { - beforeRequestDomainSet.add('all-urls'); - } else { - beforeExcludedRrequestDomainSet.add('all-urls'); - } - - const noneHostnames = new Set([ ...modes.none ]); - const notNoneHostnames = new Set([ ...modes.basic, ...modes.optimal, ...modes.complete ]); - let afterRequestDomainSet = new Set(); - let afterExcludedRequestDomainSet = new Set(); - if ( noneHostnames.has('all-urls') ) { - afterRequestDomainSet = new Set([ 'all-urls' ]); - afterExcludedRequestDomainSet = notNoneHostnames; - } else { - afterRequestDomainSet = noneHostnames; - afterExcludedRequestDomainSet = new Set([ 'all-urls' ]); - } - - if ( eqSets(beforeRequestDomainSet, afterRequestDomainSet) ) { - if ( eqSets(beforeExcludedRrequestDomainSet, afterExcludedRequestDomainSet) ) { - return; - } - } - - const removeRuleIds = [ - TRUSTED_DIRECTIVE_BASE_RULE_ID+0, - TRUSTED_DIRECTIVE_BASE_RULE_ID+1, - ]; - dynamicRuleMap.delete(TRUSTED_DIRECTIVE_BASE_RULE_ID+0); - dynamicRuleMap.delete(TRUSTED_DIRECTIVE_BASE_RULE_ID+1); - - const allowEverywhere = afterRequestDomainSet.delete('all-urls'); - afterExcludedRequestDomainSet.delete('all-urls'); - - const addRules = []; - if ( - allowEverywhere || - afterRequestDomainSet.size !== 0 || - afterExcludedRequestDomainSet.size !== 0 - ) { - const rule0 = { - id: TRUSTED_DIRECTIVE_BASE_RULE_ID+0, - action: { type: 'allowAllRequests' }, - condition: { - resourceTypes: [ 'main_frame' ], - }, - priority: 100, - }; - if ( afterRequestDomainSet.size !== 0 ) { - rule0.condition.requestDomains = Array.from(afterRequestDomainSet); - } else if ( afterExcludedRequestDomainSet.size !== 0 ) { - rule0.condition.excludedRequestDomains = Array.from(afterExcludedRequestDomainSet); - } - addRules.push(rule0); - dynamicRuleMap.set(TRUSTED_DIRECTIVE_BASE_RULE_ID+0, rule0); - // https://github.com/uBlockOrigin/uBOL-home/issues/114 - const rule1 = { - id: TRUSTED_DIRECTIVE_BASE_RULE_ID+1, - action: { type: 'allow' }, - condition: { - resourceTypes: [ 'script' ], - }, - priority: 100, - }; - if ( rule0.condition.requestDomains ) { - rule1.condition.initiatorDomains = rule0.condition.requestDomains.slice(); - } else if ( rule0.condition.excludedRequestDomains ) { - rule1.condition.excludedInitiatorDomains = rule0.condition.excludedRequestDomains.slice(); - } - addRules.push(rule1); - dynamicRuleMap.set(TRUSTED_DIRECTIVE_BASE_RULE_ID+1, rule1); - } - - const updateOptions = { removeRuleIds }; - if ( addRules.length ) { - updateOptions.addRules = addRules; - } - await dnr.updateDynamicRules(updateOptions); -} - -/******************************************************************************/ - export async function getFilteringModeDetails() { const actualDetails = await readFilteringModeDetails(); return { diff --git a/platform/mv3/extension/js/popup.js b/platform/mv3/extension/js/popup.js index b12a3fbcc9cef..98b4d54ce1434 100644 --- a/platform/mv3/extension/js/popup.js +++ b/platform/mv3/extension/js/popup.js @@ -34,7 +34,7 @@ import punycode from './punycode.js'; const popupPanelData = {}; const currentTab = {}; -let tabHostname = ''; +const tabURL = new URL(runtime.getURL('/')); /******************************************************************************/ @@ -68,8 +68,8 @@ function setFilteringMode(level, commit = false) { } async function commitFilteringMode() { - if ( tabHostname === '' ) { return; } - const targetHostname = normalizedHostname(tabHostname); + if ( tabURL.hostname === '' ) { return; } + const targetHostname = normalizedHostname(tabURL.hostname); const modeSlider = qs$('.filteringModeSlider'); const afterLevel = parseInt(modeSlider.dataset.level, 10); const beforeLevel = parseInt(modeSlider.dataset.levelBefore, 10); @@ -100,7 +100,9 @@ async function commitFilteringMode() { } if ( actualLevel !== beforeLevel && popupPanelData.autoReload ) { self.setTimeout(( ) => { - browser.tabs.reload(currentTab.id); + browser.tabs.update(currentTab.id, { + url: tabURL.href, + }); }, 437); } } @@ -317,8 +319,12 @@ async function init() { let url; try { + const strictBlockURL = runtime.getURL('/strictblock.'); url = new URL(currentTab.url); - tabHostname = url.hostname || ''; + if ( url.href.startsWith(strictBlockURL) ) { + url = new URL(url.hash.slice(1)); + } + tabURL.href = url.href || ''; } catch(ex) { } @@ -326,7 +332,7 @@ async function init() { const response = await sendMessage({ what: 'popupPanelData', origin: url.origin, - hostname: normalizedHostname(tabHostname), + hostname: normalizedHostname(tabURL.hostname), }); if ( response instanceof Object ) { Object.assign(popupPanelData, response); @@ -337,7 +343,7 @@ async function init() { setFilteringMode(popupPanelData.level); - dom.text('#hostname', punycode.toUnicode(tabHostname)); + dom.text('#hostname', punycode.toUnicode(tabURL.hostname)); dom.cl.toggle('#showMatchedRules', 'enabled', popupPanelData.isSideloaded === true && diff --git a/platform/mv3/extension/js/ruleset-manager.js b/platform/mv3/extension/js/ruleset-manager.js index f7d811e7d87bd..c9490e6836592 100644 --- a/platform/mv3/extension/js/ruleset-manager.js +++ b/platform/mv3/extension/js/ruleset-manager.js @@ -23,25 +23,44 @@ import { browser, dnr, i18n, + runtime, } from './ext.js'; +import { + localRead, localRemove, localWrite, + sessionRead, sessionRemove, sessionWrite, +} from './ext.js'; + +import { + rulesetConfig, + saveRulesetConfig, +} from './config.js'; + + import { fetchJSON } from './fetch.js'; import { getAdminRulesets } from './admin.js'; import { ubolLog } from './debug.js'; /******************************************************************************/ -const RULE_REALM_SIZE = 1000000; -const REGEXES_REALM_START = 1000000; -const REGEXES_REALM_END = REGEXES_REALM_START + RULE_REALM_SIZE; -const REMOVEPARAMS_REALM_START = REGEXES_REALM_END; -const REMOVEPARAMS_REALM_END = REMOVEPARAMS_REALM_START + RULE_REALM_SIZE; -const REDIRECT_REALM_START = REMOVEPARAMS_REALM_END; -const REDIRECT_REALM_END = REDIRECT_REALM_START + RULE_REALM_SIZE; -const MODIFYHEADERS_REALM_START = REDIRECT_REALM_END; -const MODIFYHEADERS_REALM_END = MODIFYHEADERS_REALM_START + RULE_REALM_SIZE; +const STRICTBLOCK_BASE_RULE_ID = 7000000; const TRUSTED_DIRECTIVE_BASE_RULE_ID = 8000000; +let dynamicRuleId = 1; + +/******************************************************************************/ + +const eqSets = (setBefore, setAfter) => { + if ( setBefore.size !== setAfter.size ) { return false; } + for ( const hn of setAfter ) { + if ( setBefore.has(hn) === false ) { return false; } + } + for ( const hn of setBefore ) { + if ( setAfter.has(hn) === false ) { return false; } + } + return true; +}; + /******************************************************************************/ function getRulesetDetails() { @@ -59,79 +78,50 @@ function getRulesetDetails() { /******************************************************************************/ -function getDynamicRules() { - if ( getDynamicRules.dynamicRuleMapPromise !== undefined ) { - return getDynamicRules.dynamicRuleMapPromise; - } - getDynamicRules.dynamicRuleMapPromise = dnr.getDynamicRules().then(rules => { - const rulesMap = new Map(rules.map(rule => [ rule.id, rule ])); - ubolLog(`Dynamic rule count: ${rulesMap.size}`); - ubolLog(`Available dynamic rule count: ${dnr.MAX_NUMBER_OF_DYNAMIC_AND_SESSION_RULES - rulesMap.size}`); - return rulesMap; - }); - return getDynamicRules.dynamicRuleMapPromise; -} - -/******************************************************************************/ - async function pruneInvalidRegexRules(realm, rulesIn) { - // Avoid testing already tested regexes - const dynamicRules = await dnr.getDynamicRules(); - const validRegexSet = new Set( - dynamicRules.filter(rule => - rule.condition?.regexFilter && true || false - ).map(rule => - rule.condition.regexFilter - ) - ); + const rejectedRegexRules = []; + + const validateRegex = regex => { + return dnr.isRegexSupported({ regex, isCaseSensitive: false }).then(result => { + const isSupported = result?.isSupported || false; + pruneInvalidRegexRules.validated.set(regex, isSupported); + if ( isSupported ) { return true; } + rejectedRegexRules.push(`\t${regex} ${result?.reason}`); + return false; + }); + }; // Validate regex-based rules const toCheck = []; - const rejectedRegexRules = []; for ( const rule of rulesIn ) { if ( rule.condition?.regexFilter === undefined ) { toCheck.push(true); continue; } - const { - regexFilter: regex, - isUrlFilterCaseSensitive: isCaseSensitive - } = rule.condition; - if ( validRegexSet.has(regex) ) { - toCheck.push(true); - continue; - } - if ( pruneInvalidRegexRules.invalidRegexes.has(regex) ) { - toCheck.push(false); + const { regexFilter } = rule.condition; + if ( pruneInvalidRegexRules.validated.has(regexFilter) ) { + toCheck.push(pruneInvalidRegexRules.validated.get(regexFilter)); continue; } - toCheck.push( - dnr.isRegexSupported({ regex, isCaseSensitive }).then(result => { - if ( result.isSupported ) { return true; } - pruneInvalidRegexRules.invalidRegexes.add(regex); - rejectedRegexRules.push(`\t${regex} ${result.reason}`); - return false; - }) - ); + toCheck.push(validateRegex(regexFilter)); } // Collate results const isValid = await Promise.all(toCheck); if ( rejectedRegexRules.length !== 0 ) { - ubolLog( - `${realm} realm: rejected regexes:\n`, + ubolLog(`${realm} realm: rejected regexes:\n`, rejectedRegexRules.join('\n') ); } return rulesIn.filter((v, i) => isValid[i]); } -pruneInvalidRegexRules.invalidRegexes = new Set(); +pruneInvalidRegexRules.validated = new Map(); /******************************************************************************/ -async function updateRegexRules() { +async function updateRegexRules(toAdd) { const rulesetDetails = await getEnabledRulesetsDetails(); // Fetch regexes for all enabled rulesets @@ -144,69 +134,31 @@ async function updateRegexRules() { // Collate all regexes rules const allRules = []; - let regexRuleId = REGEXES_REALM_START; for ( const rules of regexRulesets ) { if ( Array.isArray(rules) === false ) { continue; } for ( const rule of rules ) { - rule.id = regexRuleId++; + rule.id = dynamicRuleId++; allRules.push(rule); } } + if ( allRules.length === 0 ) { return; } - const validatedRules = await pruneInvalidRegexRules('regexes', allRules); - - // Add validated regex rules to dynamic ruleset without affecting rules - // outside regex rules realm. - const dynamicRuleMap = await getDynamicRules(); - const newRuleMap = new Map(validatedRules.map(rule => [ rule.id, rule ])); - const addRules = []; - const removeRuleIds = []; - - for ( const oldRule of dynamicRuleMap.values() ) { - if ( oldRule.id < REGEXES_REALM_START ) { continue; } - if ( oldRule.id >= REGEXES_REALM_END ) { continue; } - const newRule = newRuleMap.get(oldRule.id); - if ( newRule === undefined ) { - removeRuleIds.push(oldRule.id); - dynamicRuleMap.delete(oldRule.id); - } else if ( JSON.stringify(oldRule) !== JSON.stringify(newRule) ) { - removeRuleIds.push(oldRule.id); - addRules.push(newRule); - dynamicRuleMap.set(oldRule.id, newRule); - } - } - - for ( const newRule of newRuleMap.values() ) { - if ( dynamicRuleMap.has(newRule.id) ) { continue; } - addRules.push(newRule); - dynamicRuleMap.set(newRule.id, newRule); - } + const validRules = await pruneInvalidRegexRules('regexes', allRules); + if ( validRules.length === 0 ) { return; } - if ( addRules.length === 0 && removeRuleIds.length === 0 ) { return; } - - if ( removeRuleIds.length !== 0 ) { - ubolLog(`Remove ${removeRuleIds.length} DNR regex rules`); - } - if ( addRules.length !== 0 ) { - ubolLog(`Add ${addRules.length} DNR regex rules`); - } - - return dnr.updateDynamicRules({ addRules, removeRuleIds }).catch(reason => { - console.error(`updateRegexRules() / ${reason}`); - }); + ubolLog(`Add ${validRules.length} DNR regex rules`); + toAdd.push(...validRules); } /******************************************************************************/ -async function updateRemoveparamRules() { +async function updateRemoveparamRules(toAdd) { const [ hasOmnipotence, rulesetDetails, - dynamicRuleMap, ] = await Promise.all([ browser.permissions.contains({ origins: [ '' ] }), getEnabledRulesetsDetails(), - getDynamicRules(), ]); // Fetch removeparam rules for all enabled rulesets @@ -220,69 +172,32 @@ async function updateRemoveparamRules() { // Removeparam rules can only be enforced with omnipotence const allRules = []; if ( hasOmnipotence ) { - let removeparamRuleId = REMOVEPARAMS_REALM_START; for ( const rules of removeparamRulesets ) { if ( Array.isArray(rules) === false ) { continue; } for ( const rule of rules ) { - rule.id = removeparamRuleId++; + rule.id = dynamicRuleId++; allRules.push(rule); } } } + if ( allRules.length === 0 ) { return; } - const validatedRules = await pruneInvalidRegexRules('removeparam', allRules); - - // Add removeparam rules to dynamic ruleset without affecting rules - // outside removeparam rules realm. - const newRuleMap = new Map(validatedRules.map(rule => [ rule.id, rule ])); - const addRules = []; - const removeRuleIds = []; - - for ( const oldRule of dynamicRuleMap.values() ) { - if ( oldRule.id < REMOVEPARAMS_REALM_START ) { continue; } - if ( oldRule.id >= REMOVEPARAMS_REALM_END ) { continue; } - const newRule = newRuleMap.get(oldRule.id); - if ( newRule === undefined ) { - removeRuleIds.push(oldRule.id); - dynamicRuleMap.delete(oldRule.id); - } else if ( JSON.stringify(oldRule) !== JSON.stringify(newRule) ) { - removeRuleIds.push(oldRule.id); - addRules.push(newRule); - dynamicRuleMap.set(oldRule.id, newRule); - } - } - - for ( const newRule of newRuleMap.values() ) { - if ( dynamicRuleMap.has(newRule.id) ) { continue; } - addRules.push(newRule); - dynamicRuleMap.set(newRule.id, newRule); - } - - if ( addRules.length === 0 && removeRuleIds.length === 0 ) { return; } - - if ( removeRuleIds.length !== 0 ) { - ubolLog(`Remove ${removeRuleIds.length} DNR removeparam rules`); - } - if ( addRules.length !== 0 ) { - ubolLog(`Add ${addRules.length} DNR removeparam rules`); - } + const validRules = await pruneInvalidRegexRules('removeparam', allRules); + if ( validRules.length === 0 ) { return; } - return dnr.updateDynamicRules({ addRules, removeRuleIds }).catch(reason => { - console.error(`updateRemoveparamRules() / ${reason}`); - }); + ubolLog(`Add ${validRules.length} DNR removeparam rules`); + toAdd.push(...validRules); } /******************************************************************************/ -async function updateRedirectRules() { +async function updateRedirectRules(toAdd) { const [ hasOmnipotence, rulesetDetails, - dynamicRuleMap, ] = await Promise.all([ browser.permissions.contains({ origins: [ '' ] }), getEnabledRulesetsDetails(), - getDynamicRules(), ]); // Fetch redirect rules for all enabled rulesets @@ -296,69 +211,32 @@ async function updateRedirectRules() { // Redirect rules can only be enforced with omnipotence const allRules = []; if ( hasOmnipotence ) { - let redirectRuleId = REDIRECT_REALM_START; for ( const rules of redirectRulesets ) { if ( Array.isArray(rules) === false ) { continue; } for ( const rule of rules ) { - rule.id = redirectRuleId++; + rule.id = dynamicRuleId++; allRules.push(rule); } } } + if ( allRules.length === 0 ) { return; } - const validatedRules = await pruneInvalidRegexRules('redirect', allRules); - - // Add redirect rules to dynamic ruleset without affecting rules - // outside redirect rules realm. - const newRuleMap = new Map(validatedRules.map(rule => [ rule.id, rule ])); - const addRules = []; - const removeRuleIds = []; - - for ( const oldRule of dynamicRuleMap.values() ) { - if ( oldRule.id < REDIRECT_REALM_START ) { continue; } - if ( oldRule.id >= REDIRECT_REALM_END ) { continue; } - const newRule = newRuleMap.get(oldRule.id); - if ( newRule === undefined ) { - removeRuleIds.push(oldRule.id); - dynamicRuleMap.delete(oldRule.id); - } else if ( JSON.stringify(oldRule) !== JSON.stringify(newRule) ) { - removeRuleIds.push(oldRule.id); - addRules.push(newRule); - dynamicRuleMap.set(oldRule.id, newRule); - } - } - - for ( const newRule of newRuleMap.values() ) { - if ( dynamicRuleMap.has(newRule.id) ) { continue; } - addRules.push(newRule); - dynamicRuleMap.set(newRule.id, newRule); - } - - if ( addRules.length === 0 && removeRuleIds.length === 0 ) { return; } + const validRules = await pruneInvalidRegexRules('redirect', allRules); + if ( validRules.length === 0 ) { return; } - if ( removeRuleIds.length !== 0 ) { - ubolLog(`Remove ${removeRuleIds.length} DNR redirect rules`); - } - if ( addRules.length !== 0 ) { - ubolLog(`Add ${addRules.length} DNR redirect rules`); - } - - return dnr.updateDynamicRules({ addRules, removeRuleIds }).catch(reason => { - console.error(`updateRedirectRules() / ${reason}`); - }); + ubolLog(`Add ${validRules.length} DNR redirect rules`); + toAdd.push(...validRules); } /******************************************************************************/ -async function updateModifyHeadersRules() { +async function updateModifyHeadersRules(toAdd) { const [ hasOmnipotence, rulesetDetails, - dynamicRuleMap, ] = await Promise.all([ browser.permissions.contains({ origins: [ '' ] }), getEnabledRulesetsDetails(), - getDynamicRules(), ]); // Fetch modifyHeaders rules for all enabled rulesets @@ -372,69 +250,312 @@ async function updateModifyHeadersRules() { // Redirect rules can only be enforced with omnipotence const allRules = []; if ( hasOmnipotence ) { - let ruleId = MODIFYHEADERS_REALM_START; for ( const rules of rulesets ) { if ( Array.isArray(rules) === false ) { continue; } for ( const rule of rules ) { - rule.id = ruleId++; + rule.id = dynamicRuleId++; allRules.push(rule); } } } + if ( allRules.length === 0 ) { return; } - const validatedRules = await pruneInvalidRegexRules('modify-headers', allRules); + const validRules = await pruneInvalidRegexRules('modify-headers', allRules); + if ( validRules.length === 0 ) { return; } - // Add modifyHeaders rules to dynamic ruleset without affecting rules - // outside modifyHeaders realm. - const newRuleMap = new Map(validatedRules.map(rule => [ rule.id, rule ])); - const addRules = []; - const removeRuleIds = []; + ubolLog(`Add ${validRules.length} DNR modify-headers rules`); + toAdd.push(...validRules); +} - for ( const oldRule of dynamicRuleMap.values() ) { - if ( oldRule.id < MODIFYHEADERS_REALM_START ) { continue; } - if ( oldRule.id >= MODIFYHEADERS_REALM_END ) { continue; } - const newRule = newRuleMap.get(oldRule.id); - if ( newRule === undefined ) { - removeRuleIds.push(oldRule.id); - dynamicRuleMap.delete(oldRule.id); - } else if ( JSON.stringify(oldRule) !== JSON.stringify(newRule) ) { - removeRuleIds.push(oldRule.id); - addRules.push(newRule); - dynamicRuleMap.set(oldRule.id, newRule); - } - } +/******************************************************************************/ + +async function updateStrictBlockRules(dynamicRules, sessionRules) { + if ( rulesetConfig.strictBlockMode === false ) { return; } + + const [ + hasOmnipotence, + rulesetDetails, + permanentlyExcluded = [], + temporarilyExcluded = [], + ] = await Promise.all([ + browser.permissions.contains({ origins: [ '' ] }), + getEnabledRulesetsDetails(), + localRead('excludedStrictBlockHostnames'), + sessionRead('excludedStrictBlockHostnames'), + ]); - for ( const newRule of newRuleMap.values() ) { - if ( dynamicRuleMap.has(newRule.id) ) { continue; } - addRules.push(newRule); - dynamicRuleMap.set(newRule.id, newRule); + // Fetch strick-block hostnames + const toFetch = []; + for ( const details of rulesetDetails ) { + if ( details.rules.strictblock === 0 ) { continue; } + toFetch.push(fetchJSON(`/rulesets/strictblock/${details.id}`)); } + const strictblockRulesets = await Promise.all(toFetch); - if ( addRules.length === 0 && removeRuleIds.length === 0 ) { return; } + // Strict-block rules can only be enforced with omnipotence + let toStrictBlock = new Set(); + if ( hasOmnipotence ) { + for ( const hostnames of strictblockRulesets ) { + if ( Array.isArray(hostnames) === false ) { continue; } + toStrictBlock = toStrictBlock.union(new Set(hostnames)); + } + } else { + if ( permanentlyExcluded.length !== 0 ) { + localRemove('excludedStrictBlockHostnames'); + permanentlyExcluded.length = 0; + } + if ( temporarilyExcluded.length !== 0 ) { + sessionRemove('excludedStrictBlockHostnames'); + temporarilyExcluded.length = 0; + } + } + for ( const hn of permanentlyExcluded ) { + toStrictBlock.delete(hn); + } + if ( toStrictBlock.size === 0 ) { return; } + const manifest = runtime.getManifest(); + let strictblockPath = ''; + for ( const war of manifest.web_accessible_resources ) { + if ( war.resources.length !== 1 ) { continue; } + if ( war.resources[0].startsWith('/strictblock.') === false ) { continue; } + strictblockPath = runtime.getURL(war.resources[0]); + break; + } + if ( strictblockPath === '' ) { return; } + const dynamicRule = { + id: STRICTBLOCK_BASE_RULE_ID, + action: { + type: 'redirect', + redirect: { + regexSubstitution: `${strictblockPath}#\\0`, + }, + }, + condition: { + regexFilter: '^https?://.+', + requestDomains: Array.from(toStrictBlock), + resourceTypes: [ 'main_frame' ], + }, + priority: 29, + }; + if ( permanentlyExcluded.length !== 0 ) { + dynamicRule.condition.excludedRequestDomains = permanentlyExcluded; + } + dynamicRules.push(dynamicRule); + ubolLog(`Add 1 DNR dynamic rule with ${toStrictBlock.size} strictblock domains`); + + if ( temporarilyExcluded.length === 0 ) { return; } + sessionRules.push({ + id: STRICTBLOCK_BASE_RULE_ID, + action: { + type: 'allow', + }, + condition: { + requestDomains: temporarilyExcluded, + resourceTypes: [ 'main_frame' ], + }, + priority: 29, + }); + ubolLog(`Add 1 DNR session rule with ${temporarilyExcluded.length} excluded strictblock domains`); +} - if ( removeRuleIds.length !== 0 ) { - ubolLog(`Remove ${removeRuleIds.length} DNR modifyHeaders rules`); +async function commitStrictBlockRules() { + const [ + beforePermanentRules, + beforeTemporaryRules, + ] = await Promise.all([ + dnr.getDynamicRules({ ruleIds: [ STRICTBLOCK_BASE_RULE_ID ] }), + dnr.getSessionRules({ ruleIds: [ STRICTBLOCK_BASE_RULE_ID ] }), + ]); + if ( beforePermanentRules?.length ) { + ubolLog(`Remove 1 DNR dynamic strictblock rule`); } - if ( addRules.length !== 0 ) { - ubolLog(`Add ${addRules.length} DNR modifyHeaders rules`); + if ( beforeTemporaryRules?.length ) { + ubolLog(`Remove 1 DNR session strictblock rule`); } + const afterPermanentRules = []; + const afterTemporaryRules = []; + await updateStrictBlockRules(afterPermanentRules, afterTemporaryRules) + return Promise.all([ + dnr.updateDynamicRules({ + addRules: afterPermanentRules, + removeRuleIds: beforePermanentRules.map(rule => rule.id), + }), + dnr.updateSessionRules({ + addRules: afterTemporaryRules, + removeRuleIds: beforeTemporaryRules.map(rule => rule.id), + }), + ]); +} - return dnr.updateDynamicRules({ addRules, removeRuleIds }).catch(reason => { - console.error(`updateModifyHeadersRules() / ${reason}`); - }); +async function excludeFromStrictBlock(hostname, permanent) { + if ( typeof hostname !== 'string' || hostname === '' ) { return; } + const readFn = permanent ? localRead : sessionRead; + const hostnames = new Set(await readFn('excludedStrictBlockHostnames')); + hostnames.add(hostname); + const writeFn = permanent ? localWrite : sessionWrite; + await writeFn('excludedStrictBlockHostnames', Array.from(hostnames)); + return commitStrictBlockRules(); } -/******************************************************************************/ +async function setStrictBlockMode(state) { + const newState = Boolean(state); + if ( newState === rulesetConfig.strictBlockMode ) { return; } + rulesetConfig.strictBlockMode = newState; + const promises = [ saveRulesetConfig() ]; + if ( newState === false ) { + promises.push( + localRemove('excludedStrictBlockHostnames'), + sessionRemove('excludedStrictBlockHostnames') + ); + } + await Promise.all(promises); + return commitStrictBlockRules(); +} -// TODO: group all omnipotence-related rules into one realm. +/******************************************************************************/ async function updateDynamicRules() { - return Promise.all([ - updateRegexRules(), - updateRemoveparamRules(), - updateRedirectRules(), - updateModifyHeadersRules(), + dynamicRuleId = 1; + const dynamicRules = []; + const sessionRules = []; + const [ + dynamicRuleIds, + sessionRuleIds, + ] = await Promise.all([ + dnr.getDynamicRules().then(rules => + rules.map(rule => rule.id) + .filter(id => id < TRUSTED_DIRECTIVE_BASE_RULE_ID) + ), + dnr.getSessionRules().then(rules => rules.map(rule => rule.id)), + updateRegexRules(dynamicRules), + updateRemoveparamRules(dynamicRules), + updateRedirectRules(dynamicRules), + updateModifyHeadersRules(dynamicRules), + updateStrictBlockRules(dynamicRules, sessionRules), ]); + if ( dynamicRules.length === 0 && dynamicRuleIds.length === 0 ) { return; } + const promises = []; + if ( dynamicRules.length !== 0 || dynamicRuleIds.length !== 0 ) { + promises.push( + dnr.updateDynamicRules({ + addRules: dynamicRules, + removeRuleIds: dynamicRuleIds, + }).then(( ) => { + if ( dynamicRuleIds.length !== 0 ) { + ubolLog(`Remove ${dynamicRuleIds.length} dynamic DNR rules`); + } + if ( dynamicRules.length !== 0 ) { + ubolLog(`Add ${dynamicRules.length} dynamic DNR rules`); + } + }).catch(reason => { + console.error(`updateDynamicRules() / ${reason}`); + }) + ); + } + if ( sessionRules.length !== 0 || sessionRuleIds.length !== 0 ) { + promises.push( + dnr.updateSessionRules({ + addRules: sessionRules, + removeRuleIds: sessionRuleIds, + }).then(( ) => { + if ( sessionRuleIds.length !== 0 ) { + ubolLog(`Remove ${sessionRuleIds.length} session DNR rules`); + } + if ( sessionRules.length !== 0 ) { + ubolLog(`Add ${sessionRules.length} session DNR rules`); + } + }).catch(reason => { + console.error(`updateSessionRules() / ${reason}`); + }) + ); + } + return Promise.all(promises); +} + +/******************************************************************************/ + +async function filteringModesToDNR(modes) { + const trustedRules = await dnr.getDynamicRules({ + ruleIds: [ TRUSTED_DIRECTIVE_BASE_RULE_ID+0 ], + }); + const trustedRule = trustedRules.length !== 0 && trustedRules[0] || undefined; + const beforeRequestDomainSet = new Set(trustedRule?.condition.requestDomains); + const beforeExcludedRrequestDomainSet = new Set(trustedRule?.condition.excludedRequestDomains); + if ( trustedRule !== undefined && beforeRequestDomainSet.size === 0 ) { + beforeRequestDomainSet.add('all-urls'); + } else { + beforeExcludedRrequestDomainSet.add('all-urls'); + } + + const noneHostnames = new Set([ ...modes.none ]); + const notNoneHostnames = new Set([ ...modes.basic, ...modes.optimal, ...modes.complete ]); + let afterRequestDomainSet = new Set(); + let afterExcludedRequestDomainSet = new Set(); + if ( noneHostnames.has('all-urls') ) { + afterRequestDomainSet = new Set([ 'all-urls' ]); + afterExcludedRequestDomainSet = notNoneHostnames; + } else { + afterRequestDomainSet = noneHostnames; + afterExcludedRequestDomainSet = new Set([ 'all-urls' ]); + } + + if ( eqSets(beforeRequestDomainSet, afterRequestDomainSet) ) { + if ( eqSets(beforeExcludedRrequestDomainSet, afterExcludedRequestDomainSet) ) { + return; + } + } + + const removeRuleIds = []; + if ( trustedRule ) { + removeRuleIds.push( + TRUSTED_DIRECTIVE_BASE_RULE_ID+0, + TRUSTED_DIRECTIVE_BASE_RULE_ID+1 + ); + } + + const allowEverywhere = afterRequestDomainSet.delete('all-urls'); + afterExcludedRequestDomainSet.delete('all-urls'); + + const addRules = []; + if ( + allowEverywhere || + afterRequestDomainSet.size !== 0 || + afterExcludedRequestDomainSet.size !== 0 + ) { + const rule0 = { + id: TRUSTED_DIRECTIVE_BASE_RULE_ID+0, + action: { type: 'allowAllRequests' }, + condition: { + resourceTypes: [ 'main_frame' ], + }, + priority: 100, + }; + if ( afterRequestDomainSet.size !== 0 ) { + rule0.condition.requestDomains = Array.from(afterRequestDomainSet); + } else if ( afterExcludedRequestDomainSet.size !== 0 ) { + rule0.condition.excludedRequestDomains = Array.from(afterExcludedRequestDomainSet); + } + addRules.push(rule0); + // https://github.com/uBlockOrigin/uBOL-home/issues/114 + const rule1 = { + id: TRUSTED_DIRECTIVE_BASE_RULE_ID+1, + action: { type: 'allow' }, + condition: { + resourceTypes: [ 'script' ], + }, + priority: 100, + }; + if ( rule0.condition.requestDomains ) { + rule1.condition.initiatorDomains = rule0.condition.requestDomains.slice(); + } else if ( rule0.condition.excludedRequestDomains ) { + rule1.condition.excludedInitiatorDomains = rule0.condition.excludedRequestDomains.slice(); + } + addRules.push(rule1); + } + + if ( addRules.length === 0 && removeRuleIds.length === 0 ) { return; } + + return dnr.updateDynamicRules({ addRules, removeRuleIds }); } /******************************************************************************/ @@ -511,7 +632,7 @@ async function enableRulesets(ids) { } if ( enableRulesetSet.size === 0 && disableRulesetSet.size === 0 ) { - return; + return false; } const enableRulesetIds = Array.from(enableRulesetSet); @@ -525,7 +646,9 @@ async function enableRulesets(ids) { } await dnr.updateEnabledRulesets({ enableRulesetIds, disableRulesetIds }); - return updateDynamicRules(); + await updateDynamicRules(); + + return true; } /******************************************************************************/ @@ -550,11 +673,12 @@ async function getEnabledRulesetsDetails() { /******************************************************************************/ export { - TRUSTED_DIRECTIVE_BASE_RULE_ID, - getRulesetDetails, - getDynamicRules, - enableRulesets, defaultRulesetsFromLanguage, + enableRulesets, + excludeFromStrictBlock, + filteringModesToDNR, + getRulesetDetails, getEnabledRulesetsDetails, + setStrictBlockMode, updateDynamicRules, }; diff --git a/platform/mv3/extension/js/settings.js b/platform/mv3/extension/js/settings.js index 6cbd0e23d08fa..5b73144cb3dc1 100644 --- a/platform/mv3/extension/js/settings.js +++ b/platform/mv3/extension/js/settings.js @@ -67,6 +67,8 @@ function renderWidgets() { } } + qs$('#strictBlockMode input[type="checkbox"]').checked = cachedRulesetData.strictBlockMode; + { dom.prop('#developerMode input[type="checkbox"]', 'checked', Boolean(cachedRulesetData.developerMode) @@ -146,6 +148,13 @@ dom.on('#showBlockedCount input[type="checkbox"]', 'change', ev => { }); }); +dom.on('#strictBlockMode input[type="checkbox"]', 'change', ev => { + sendMessage({ + what: 'setStrictBlockMode', + state: ev.target.checked, + }); +}); + dom.on('#developerMode input[type="checkbox"]', 'change', ev => { sendMessage({ what: 'setDeveloperMode', @@ -240,6 +249,13 @@ listen.onmessage = ev => { } } + if ( message.strictBlockMode !== undefined ) { + if ( message.strictBlockMode !== local.strictBlockMode ) { + local.strictBlockMode = message.strictBlockMode; + render = true; + } + } + if ( message.adminRulesets !== undefined ) { if ( hashFromIterable(message.adminRulesets) !== hashFromIterable(local.adminRulesets) ) { local.adminRulesets = message.adminRulesets; diff --git a/platform/mv3/extension/js/strictblock.js b/platform/mv3/extension/js/strictblock.js new file mode 100644 index 0000000000000..616ef6ddbc32d --- /dev/null +++ b/platform/mv3/extension/js/strictblock.js @@ -0,0 +1,178 @@ +/******************************************************************************* + + uBlock Origin - a comprehensive, efficient content blocker + Copyright (C) 2024-present Raymond Hill + + 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/}. + + Home: https://github.com/gorhill/uBlock +*/ + +import { dom, qs$ } from './dom.js'; +import { i18n$ } from './i18n.js'; +import { sendMessage } from './ext.js'; + +/******************************************************************************/ + +const toURL = new URL('about:blank'); + +function setURL(url) { + try { + toURL.href = url; + } catch(_) { + } +} + +setURL(self.location.hash.slice(1)); + +/******************************************************************************/ + +function urlToFragment(raw) { + try { + const fragment = new DocumentFragment(); + const url = new URL(raw); + const hn = url.hostname; + const i = raw.indexOf(hn); + const b = document.createElement('b'); + b.append(hn); + fragment.append(raw.slice(0,i), b, raw.slice(i+hn.length)); + return fragment; + } catch(_) { + } + return raw; +} + +/******************************************************************************/ + +async function proceed() { + await sendMessage({ + what: 'excludeFromStrictBlock', + hostname: toURL.hostname, + permanent: qs$('#disableWarning').checked, + }); + window.location.replace(toURL.href); +} + +/******************************************************************************/ + +dom.clear('#theURL > p > span:first-of-type'); +qs$('#theURL > p > span:first-of-type').append(urlToFragment(toURL.href)); + +/******************************************************************************/ + +// https://github.com/gorhill/uBlock/issues/691 +// Parse URL to extract as much useful information as possible. This is +// useful to assist the user in deciding whether to navigate to the web page. +(( ) => { + const reURL = /^https?:\/\//; + + const liFromParam = function(name, value) { + if ( value === '' ) { + value = name; + name = ''; + } + const li = dom.create('li'); + let span = dom.create('span'); + dom.text(span, name); + li.appendChild(span); + if ( name !== '' && value !== '' ) { + li.appendChild(document.createTextNode(' = ')); + } + span = dom.create('span'); + if ( reURL.test(value) ) { + const a = dom.create('a'); + dom.attr(a, 'href', value); + dom.text(a, value); + span.appendChild(a); + } else { + dom.text(span, value); + } + li.appendChild(span); + return li; + }; + + // https://github.com/uBlockOrigin/uBlock-issues/issues/1649 + // Limit recursion. + const renderParams = function(parentNode, rawURL, depth = 0) { + let url; + try { + url = new URL(rawURL); + } catch(ex) { + return false; + } + + const search = url.search.slice(1); + if ( search === '' ) { return false; } + + url.search = ''; + const li = liFromParam(i18n$('strictblockNoParamsPrompt'), url.href); + parentNode.appendChild(li); + + const params = new self.URLSearchParams(search); + for ( const [ name, value ] of params ) { + const li = liFromParam(name, value); + if ( depth < 2 && reURL.test(value) ) { + const ul = dom.create('ul'); + renderParams(ul, value, depth + 1); + li.appendChild(ul); + } + parentNode.appendChild(li); + } + + return true; + }; + + if ( renderParams(qs$('#parsed'), toURL.href) === false ) { return; } + + dom.cl.remove('#toggleParse', 'hidden'); + + dom.on('#toggleParse', 'click', ( ) => { + dom.cl.toggle('#theURL', 'collapsed'); + //vAPI.localStorage.setItem( + // 'document-blocked-expand-url', + // (dom.cl.has('#theURL', 'collapsed') === false).toString() + //); + }); + + //vAPI.localStorage.getItemAsync('document-blocked-expand-url').then(value => { + // dom.cl.toggle('#theURL', 'collapsed', value !== 'true' && value !== true); + //}); +})(); + +/******************************************************************************/ + +// https://www.reddit.com/r/uBlockOrigin/comments/breeux/ +if ( window.history.length > 1 ) { + dom.on('#back', 'click', ( ) => { + window.history.back(); + }); + qs$('#bye').style.display = 'none'; +} else { + dom.on('#bye', 'click', ( ) => { + window.close(); + }); + qs$('#back').style.display = 'none'; +} + +dom.on('#disableWarning', 'change', ev => { + const checked = ev.target.checked; + dom.cl.toggle('[data-i18n="strictblockBack"]', 'disabled', checked); + dom.cl.toggle('[data-i18n="strictblockClose"]', 'disabled', checked); +}); + +dom.on('#proceed', 'click', ( ) => { + proceed(); +}); + +/******************************************************************************/ diff --git a/platform/mv3/firefox/manifest.json b/platform/mv3/firefox/manifest.json index e191e3c6be43c..703f652b28ec0 100644 --- a/platform/mv3/firefox/manifest.json +++ b/platform/mv3/firefox/manifest.json @@ -16,10 +16,10 @@ "browser_specific_settings": { "gecko": { "id": "uBOLite@raymondhill.net", - "strict_min_version": "114.0" + "strict_min_version": "127.0" }, "gecko_android": { - "strict_min_version": "114.0" + "strict_min_version": "127.0" } }, "declarative_net_request": { @@ -50,6 +50,5 @@ "storage" ], "short_name": "uBO Lite", - "version": "1.0", - "web_accessible_resources": [] + "version": "1.0" } diff --git a/platform/mv3/make-rulesets.js b/platform/mv3/make-rulesets.js index 04688f15a0ae3..b570067055473 100644 --- a/platform/mv3/make-rulesets.js +++ b/platform/mv3/make-rulesets.js @@ -433,6 +433,33 @@ async function processNetworkFilters(assetDetails, network) { ); } + const strictBlocked = new Set(); + for ( const rule of plainGood ) { + if ( rule.action.type !== 'block' ) { continue; } + if ( rule.condition.domainType ) { continue; } + if ( rule.condition.regexFilter ) { continue; } + if ( rule.condition.urlFilter ) { continue; } + if ( rule.condition.requestMethods ) { continue; } + if ( rule.condition.excludedRequestMethods ) { continue; } + if ( rule.condition.resourceTypes ) { continue; } + if ( rule.condition.excludedResourceTypes ) { continue; } + if ( rule.condition.responseHeaders ) { continue; } + if ( rule.condition.excludedResponseHeaders ) { continue; } + if ( rule.condition.initiatorDomains ) { continue; } + if ( rule.condition.excludedInitiatorDomains ) { continue; } + if ( rule.condition.requestDomains === undefined ) { continue; } + if ( rule.condition.excludedRequestDomains ) { continue; } + for ( const hn of rule.condition.requestDomains ) { + strictBlocked.add(hn); + } + } + if ( strictBlocked.size !== 0 ) { + writeFile( + `${rulesetDir}/strictblock/${assetDetails.id}.json`, + toJSONRuleset(Array.from(strictBlocked)) + ); + } + return { total: rules.length, plain: plainGood.length, @@ -442,6 +469,7 @@ async function processNetworkFilters(assetDetails, network) { removeparam: removeparamsGood.length, redirect: redirects.length, modifyHeaders: modifyHeaders.length, + strictblock: strictBlocked.size, }; } @@ -1085,6 +1113,7 @@ async function rulesetFromURLs(assetDetails) { removeparam: netStats.removeparam, redirect: netStats.redirect, modifyHeaders: netStats.modifyHeaders, + strictblock: netStats.strictblock, discarded: netStats.discarded, rejected: netStats.rejected, }, @@ -1332,6 +1361,15 @@ async function main() { // Patch declarative_net_request key manifest.declarative_net_request = { rule_resources: ruleResources }; // Patch web_accessible_resources key + manifest.web_accessible_resources = manifest.web_accessible_resources || []; + // Strict-block-related resource + const strictblockDocument = `strictblock.${secret}.html`; + copyFile('./strictblock.html', `${outputDir}/${strictblockDocument}`); + manifest.web_accessible_resources.push({ + resources: [ `/${strictblockDocument}` ], + matches: [ '' ], + }); + // Secondary resources const web_accessible_resources = { resources: Array.from(requiredRedirectResources).map(path => `/${path}`), matches: [ '' ], @@ -1339,7 +1377,7 @@ async function main() { if ( platform === 'chromium' ) { web_accessible_resources.use_dynamic_url = true; } - manifest.web_accessible_resources = [ web_accessible_resources ]; + manifest.web_accessible_resources.push(web_accessible_resources); // Patch manifest version property manifest.version = version; diff --git a/platform/mv3/strictblock.html b/platform/mv3/strictblock.html new file mode 100644 index 0000000000000..9572eab09715b --- /dev/null +++ b/platform/mv3/strictblock.html @@ -0,0 +1,43 @@ + + + + + + + + + + + + + +
+ + +
+

_

+ +
+ +
+ +
+ +
+ + + +
+
+ + + + + + + diff --git a/src/document-blocked.html b/src/document-blocked.html index 56d6a95a42b9d..4c69359f1664e 100644 --- a/src/document-blocked.html +++ b/src/document-blocked.html @@ -47,10 +47,12 @@
diff --git a/tools/make-mv3.sh b/tools/make-mv3.sh index c6b02abddfec9..287a7b08b26bd 100755 --- a/tools/make-mv3.sh +++ b/tools/make-mv3.sh @@ -107,6 +107,7 @@ if [ "$QUICK" != "yes" ]; then cp platform/mv3/package.json "$TMPDIR"/ cp platform/mv3/*.js "$TMPDIR"/ cp platform/mv3/*.mjs "$TMPDIR"/ + cp platform/mv3/*.html "$TMPDIR"/ cp platform/mv3/extension/js/utils.js "$TMPDIR"/js/ cp -R "$UBO_DIR"/src/js/resources "$TMPDIR"/js/ cp "$UBO_DIR"/assets/assets.dev.json "$TMPDIR"/ From 784888471a57298d0c2c4f7a9af262d46d1ae113 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Tue, 3 Dec 2024 16:53:50 -0500 Subject: [PATCH 0494/1099] Import translation work from https://crowdin.com/project/ublock --- .../mv3/extension/_locales/ar/messages.json | 36 +++++++++++++++++++ .../mv3/extension/_locales/az/messages.json | 36 +++++++++++++++++++ .../mv3/extension/_locales/be/messages.json | 36 +++++++++++++++++++ .../mv3/extension/_locales/bg/messages.json | 36 +++++++++++++++++++ .../mv3/extension/_locales/bn/messages.json | 36 +++++++++++++++++++ .../extension/_locales/br_FR/messages.json | 36 +++++++++++++++++++ .../mv3/extension/_locales/bs/messages.json | 36 +++++++++++++++++++ .../mv3/extension/_locales/ca/messages.json | 36 +++++++++++++++++++ .../mv3/extension/_locales/cs/messages.json | 36 +++++++++++++++++++ .../mv3/extension/_locales/cv/messages.json | 36 +++++++++++++++++++ .../mv3/extension/_locales/cy/messages.json | 36 +++++++++++++++++++ .../mv3/extension/_locales/da/messages.json | 36 +++++++++++++++++++ .../mv3/extension/_locales/de/messages.json | 36 +++++++++++++++++++ .../mv3/extension/_locales/el/messages.json | 36 +++++++++++++++++++ .../extension/_locales/en_GB/messages.json | 36 +++++++++++++++++++ .../mv3/extension/_locales/eo/messages.json | 36 +++++++++++++++++++ .../mv3/extension/_locales/es/messages.json | 36 +++++++++++++++++++ .../mv3/extension/_locales/et/messages.json | 36 +++++++++++++++++++ .../mv3/extension/_locales/eu/messages.json | 36 +++++++++++++++++++ .../mv3/extension/_locales/fa/messages.json | 36 +++++++++++++++++++ .../mv3/extension/_locales/fi/messages.json | 36 +++++++++++++++++++ .../mv3/extension/_locales/fil/messages.json | 36 +++++++++++++++++++ .../mv3/extension/_locales/fr/messages.json | 36 +++++++++++++++++++ .../mv3/extension/_locales/fy/messages.json | 36 +++++++++++++++++++ .../mv3/extension/_locales/gl/messages.json | 36 +++++++++++++++++++ .../mv3/extension/_locales/gu/messages.json | 36 +++++++++++++++++++ .../mv3/extension/_locales/he/messages.json | 36 +++++++++++++++++++ .../mv3/extension/_locales/hi/messages.json | 36 +++++++++++++++++++ .../mv3/extension/_locales/hr/messages.json | 36 +++++++++++++++++++ .../mv3/extension/_locales/hu/messages.json | 36 +++++++++++++++++++ .../mv3/extension/_locales/hy/messages.json | 36 +++++++++++++++++++ .../mv3/extension/_locales/id/messages.json | 36 +++++++++++++++++++ .../mv3/extension/_locales/it/messages.json | 36 +++++++++++++++++++ .../mv3/extension/_locales/ja/messages.json | 36 +++++++++++++++++++ .../mv3/extension/_locales/ka/messages.json | 36 +++++++++++++++++++ .../mv3/extension/_locales/kk/messages.json | 36 +++++++++++++++++++ .../mv3/extension/_locales/kn/messages.json | 36 +++++++++++++++++++ .../mv3/extension/_locales/ko/messages.json | 36 +++++++++++++++++++ .../mv3/extension/_locales/lt/messages.json | 36 +++++++++++++++++++ .../mv3/extension/_locales/lv/messages.json | 36 +++++++++++++++++++ .../mv3/extension/_locales/mk/messages.json | 36 +++++++++++++++++++ .../mv3/extension/_locales/ml/messages.json | 36 +++++++++++++++++++ .../mv3/extension/_locales/mr/messages.json | 36 +++++++++++++++++++ .../mv3/extension/_locales/ms/messages.json | 36 +++++++++++++++++++ .../mv3/extension/_locales/nb/messages.json | 36 +++++++++++++++++++ .../mv3/extension/_locales/nl/messages.json | 36 +++++++++++++++++++ .../mv3/extension/_locales/oc/messages.json | 36 +++++++++++++++++++ .../mv3/extension/_locales/pa/messages.json | 36 +++++++++++++++++++ .../mv3/extension/_locales/pl/messages.json | 36 +++++++++++++++++++ .../extension/_locales/pt_BR/messages.json | 36 +++++++++++++++++++ .../extension/_locales/pt_PT/messages.json | 36 +++++++++++++++++++ .../mv3/extension/_locales/ro/messages.json | 36 +++++++++++++++++++ .../mv3/extension/_locales/ru/messages.json | 36 +++++++++++++++++++ .../mv3/extension/_locales/si/messages.json | 36 +++++++++++++++++++ .../mv3/extension/_locales/sk/messages.json | 36 +++++++++++++++++++ .../mv3/extension/_locales/sl/messages.json | 36 +++++++++++++++++++ .../mv3/extension/_locales/so/messages.json | 36 +++++++++++++++++++ .../mv3/extension/_locales/sq/messages.json | 36 +++++++++++++++++++ .../mv3/extension/_locales/sr/messages.json | 36 +++++++++++++++++++ .../mv3/extension/_locales/sv/messages.json | 36 +++++++++++++++++++ .../mv3/extension/_locales/sw/messages.json | 36 +++++++++++++++++++ .../mv3/extension/_locales/ta/messages.json | 36 +++++++++++++++++++ .../mv3/extension/_locales/te/messages.json | 36 +++++++++++++++++++ .../mv3/extension/_locales/th/messages.json | 36 +++++++++++++++++++ .../mv3/extension/_locales/tr/messages.json | 36 +++++++++++++++++++ .../mv3/extension/_locales/uk/messages.json | 36 +++++++++++++++++++ .../mv3/extension/_locales/ur/messages.json | 36 +++++++++++++++++++ .../mv3/extension/_locales/vi/messages.json | 36 +++++++++++++++++++ .../extension/_locales/zh_CN/messages.json | 36 +++++++++++++++++++ .../extension/_locales/zh_TW/messages.json | 36 +++++++++++++++++++ 70 files changed, 2520 insertions(+) diff --git a/platform/mv3/extension/_locales/ar/messages.json b/platform/mv3/extension/_locales/ar/messages.json index b925a42e0bfb9..6d3d859d2ea43 100644 --- a/platform/mv3/extension/_locales/ar/messages.json +++ b/platform/mv3/extension/_locales/ar/messages.json @@ -231,8 +231,44 @@ "message": "إظهار عدد الطلبات المحظورة على أيقونة شريط الأدوات", "description": "Label for a checkbox in the options page" }, + "enableStrictBlockLabel": { + "message": "Enable strict blocking", + "description": "Label for a checkbox in the options page" + }, + "enableStrictBlockLegend": { + "message": "Navigation to potentially undesirable sites will be blocked, and you will be offered the option to proceed.", + "description": "Label for a checkbox in the options page" + }, "findListsPlaceholder": { "message": "البحث عن القوائم", "description": "Placeholder for the input field used to find lists" + }, + "strictblockTitle": { + "message": "Page blocked", + "description": "Webpage title for the strict-blocked page" + }, + "strictblockSentence1": { + "message": "uBO Lite has prevented the following page from loading:", + "description": "Sentence used in the strict-blocked page" + }, + "strictblockNoParamsPrompt": { + "message": "without parameters", + "description": "Label to be used for the parameter-less URL" + }, + "strictblockBack": { + "message": "Go back", + "description": "A button to go back to the previous webpage" + }, + "strictblockClose": { + "message": "Close this window", + "description": "A button to close the current tab" + }, + "strictblockDontWarn": { + "message": "Don't warn me again about this site", + "description": "Label for checkbox in document-blocked page" + }, + "strictblockProceed": { + "message": "Proceed", + "description": "A button to navigate to the blocked page" } } diff --git a/platform/mv3/extension/_locales/az/messages.json b/platform/mv3/extension/_locales/az/messages.json index b573b25e394cf..ef0b36678e2d8 100644 --- a/platform/mv3/extension/_locales/az/messages.json +++ b/platform/mv3/extension/_locales/az/messages.json @@ -231,8 +231,44 @@ "message": "Show the number of blocked requests on the toolbar icon", "description": "Label for a checkbox in the options page" }, + "enableStrictBlockLabel": { + "message": "Enable strict blocking", + "description": "Label for a checkbox in the options page" + }, + "enableStrictBlockLegend": { + "message": "Navigation to potentially undesirable sites will be blocked, and you will be offered the option to proceed.", + "description": "Label for a checkbox in the options page" + }, "findListsPlaceholder": { "message": "Find lists", "description": "Placeholder for the input field used to find lists" + }, + "strictblockTitle": { + "message": "Page blocked", + "description": "Webpage title for the strict-blocked page" + }, + "strictblockSentence1": { + "message": "uBO Lite has prevented the following page from loading:", + "description": "Sentence used in the strict-blocked page" + }, + "strictblockNoParamsPrompt": { + "message": "without parameters", + "description": "Label to be used for the parameter-less URL" + }, + "strictblockBack": { + "message": "Go back", + "description": "A button to go back to the previous webpage" + }, + "strictblockClose": { + "message": "Close this window", + "description": "A button to close the current tab" + }, + "strictblockDontWarn": { + "message": "Don't warn me again about this site", + "description": "Label for checkbox in document-blocked page" + }, + "strictblockProceed": { + "message": "Proceed", + "description": "A button to navigate to the blocked page" } } diff --git a/platform/mv3/extension/_locales/be/messages.json b/platform/mv3/extension/_locales/be/messages.json index 3042d674c8b22..a75a324e67a74 100644 --- a/platform/mv3/extension/_locales/be/messages.json +++ b/platform/mv3/extension/_locales/be/messages.json @@ -231,8 +231,44 @@ "message": "Паказваць колькасць заблакаваных запытаў на значку панэлі інструментаў", "description": "Label for a checkbox in the options page" }, + "enableStrictBlockLabel": { + "message": "Enable strict blocking", + "description": "Label for a checkbox in the options page" + }, + "enableStrictBlockLegend": { + "message": "Navigation to potentially undesirable sites will be blocked, and you will be offered the option to proceed.", + "description": "Label for a checkbox in the options page" + }, "findListsPlaceholder": { "message": "Знайсці спісы", "description": "Placeholder for the input field used to find lists" + }, + "strictblockTitle": { + "message": "Page blocked", + "description": "Webpage title for the strict-blocked page" + }, + "strictblockSentence1": { + "message": "uBO Lite has prevented the following page from loading:", + "description": "Sentence used in the strict-blocked page" + }, + "strictblockNoParamsPrompt": { + "message": "without parameters", + "description": "Label to be used for the parameter-less URL" + }, + "strictblockBack": { + "message": "Go back", + "description": "A button to go back to the previous webpage" + }, + "strictblockClose": { + "message": "Close this window", + "description": "A button to close the current tab" + }, + "strictblockDontWarn": { + "message": "Don't warn me again about this site", + "description": "Label for checkbox in document-blocked page" + }, + "strictblockProceed": { + "message": "Proceed", + "description": "A button to navigate to the blocked page" } } diff --git a/platform/mv3/extension/_locales/bg/messages.json b/platform/mv3/extension/_locales/bg/messages.json index 5c2ecb76f4ac9..686b39f1058af 100644 --- a/platform/mv3/extension/_locales/bg/messages.json +++ b/platform/mv3/extension/_locales/bg/messages.json @@ -231,8 +231,44 @@ "message": "Показване на брояч в иконката за блокираните заявки", "description": "Label for a checkbox in the options page" }, + "enableStrictBlockLabel": { + "message": "Enable strict blocking", + "description": "Label for a checkbox in the options page" + }, + "enableStrictBlockLegend": { + "message": "Navigation to potentially undesirable sites will be blocked, and you will be offered the option to proceed.", + "description": "Label for a checkbox in the options page" + }, "findListsPlaceholder": { "message": "Намиране на списъци", "description": "Placeholder for the input field used to find lists" + }, + "strictblockTitle": { + "message": "Page blocked", + "description": "Webpage title for the strict-blocked page" + }, + "strictblockSentence1": { + "message": "uBO Lite has prevented the following page from loading:", + "description": "Sentence used in the strict-blocked page" + }, + "strictblockNoParamsPrompt": { + "message": "without parameters", + "description": "Label to be used for the parameter-less URL" + }, + "strictblockBack": { + "message": "Go back", + "description": "A button to go back to the previous webpage" + }, + "strictblockClose": { + "message": "Close this window", + "description": "A button to close the current tab" + }, + "strictblockDontWarn": { + "message": "Don't warn me again about this site", + "description": "Label for checkbox in document-blocked page" + }, + "strictblockProceed": { + "message": "Proceed", + "description": "A button to navigate to the blocked page" } } diff --git a/platform/mv3/extension/_locales/bn/messages.json b/platform/mv3/extension/_locales/bn/messages.json index f2593fc516db7..a9af81782712f 100644 --- a/platform/mv3/extension/_locales/bn/messages.json +++ b/platform/mv3/extension/_locales/bn/messages.json @@ -231,8 +231,44 @@ "message": "কতগুলো অনুরোধ ব্লক করা হয়েছে তা টুলবার আইকনে দেখাও", "description": "Label for a checkbox in the options page" }, + "enableStrictBlockLabel": { + "message": "Enable strict blocking", + "description": "Label for a checkbox in the options page" + }, + "enableStrictBlockLegend": { + "message": "Navigation to potentially undesirable sites will be blocked, and you will be offered the option to proceed.", + "description": "Label for a checkbox in the options page" + }, "findListsPlaceholder": { "message": "Find lists", "description": "Placeholder for the input field used to find lists" + }, + "strictblockTitle": { + "message": "Page blocked", + "description": "Webpage title for the strict-blocked page" + }, + "strictblockSentence1": { + "message": "uBO Lite has prevented the following page from loading:", + "description": "Sentence used in the strict-blocked page" + }, + "strictblockNoParamsPrompt": { + "message": "without parameters", + "description": "Label to be used for the parameter-less URL" + }, + "strictblockBack": { + "message": "Go back", + "description": "A button to go back to the previous webpage" + }, + "strictblockClose": { + "message": "Close this window", + "description": "A button to close the current tab" + }, + "strictblockDontWarn": { + "message": "Don't warn me again about this site", + "description": "Label for checkbox in document-blocked page" + }, + "strictblockProceed": { + "message": "Proceed", + "description": "A button to navigate to the blocked page" } } diff --git a/platform/mv3/extension/_locales/br_FR/messages.json b/platform/mv3/extension/_locales/br_FR/messages.json index 4db287b541a92..9e61a7f21105b 100644 --- a/platform/mv3/extension/_locales/br_FR/messages.json +++ b/platform/mv3/extension/_locales/br_FR/messages.json @@ -231,8 +231,44 @@ "message": "Diskouez an niver a rekedoù bet stanket war arouez ar varrenn-ostilhoù", "description": "Label for a checkbox in the options page" }, + "enableStrictBlockLabel": { + "message": "Enable strict blocking", + "description": "Label for a checkbox in the options page" + }, + "enableStrictBlockLegend": { + "message": "Navigation to potentially undesirable sites will be blocked, and you will be offered the option to proceed.", + "description": "Label for a checkbox in the options page" + }, "findListsPlaceholder": { "message": "Kavout rolloù", "description": "Placeholder for the input field used to find lists" + }, + "strictblockTitle": { + "message": "Page blocked", + "description": "Webpage title for the strict-blocked page" + }, + "strictblockSentence1": { + "message": "uBO Lite has prevented the following page from loading:", + "description": "Sentence used in the strict-blocked page" + }, + "strictblockNoParamsPrompt": { + "message": "without parameters", + "description": "Label to be used for the parameter-less URL" + }, + "strictblockBack": { + "message": "Go back", + "description": "A button to go back to the previous webpage" + }, + "strictblockClose": { + "message": "Close this window", + "description": "A button to close the current tab" + }, + "strictblockDontWarn": { + "message": "Don't warn me again about this site", + "description": "Label for checkbox in document-blocked page" + }, + "strictblockProceed": { + "message": "Proceed", + "description": "A button to navigate to the blocked page" } } diff --git a/platform/mv3/extension/_locales/bs/messages.json b/platform/mv3/extension/_locales/bs/messages.json index 43466580b2805..a913ad1482aa7 100644 --- a/platform/mv3/extension/_locales/bs/messages.json +++ b/platform/mv3/extension/_locales/bs/messages.json @@ -231,8 +231,44 @@ "message": "Prikažite broj blokiranih zahtjeva na ikoni trake sa alatkama", "description": "Label for a checkbox in the options page" }, + "enableStrictBlockLabel": { + "message": "Enable strict blocking", + "description": "Label for a checkbox in the options page" + }, + "enableStrictBlockLegend": { + "message": "Navigation to potentially undesirable sites will be blocked, and you will be offered the option to proceed.", + "description": "Label for a checkbox in the options page" + }, "findListsPlaceholder": { "message": "Find lists", "description": "Placeholder for the input field used to find lists" + }, + "strictblockTitle": { + "message": "Page blocked", + "description": "Webpage title for the strict-blocked page" + }, + "strictblockSentence1": { + "message": "uBO Lite has prevented the following page from loading:", + "description": "Sentence used in the strict-blocked page" + }, + "strictblockNoParamsPrompt": { + "message": "without parameters", + "description": "Label to be used for the parameter-less URL" + }, + "strictblockBack": { + "message": "Go back", + "description": "A button to go back to the previous webpage" + }, + "strictblockClose": { + "message": "Close this window", + "description": "A button to close the current tab" + }, + "strictblockDontWarn": { + "message": "Don't warn me again about this site", + "description": "Label for checkbox in document-blocked page" + }, + "strictblockProceed": { + "message": "Proceed", + "description": "A button to navigate to the blocked page" } } diff --git a/platform/mv3/extension/_locales/ca/messages.json b/platform/mv3/extension/_locales/ca/messages.json index ac4da9e1d2035..12c826c458189 100644 --- a/platform/mv3/extension/_locales/ca/messages.json +++ b/platform/mv3/extension/_locales/ca/messages.json @@ -231,8 +231,44 @@ "message": "Mostra el nombre de sol·licituds blocades a la icona de la barra d'eines", "description": "Label for a checkbox in the options page" }, + "enableStrictBlockLabel": { + "message": "Enable strict blocking", + "description": "Label for a checkbox in the options page" + }, + "enableStrictBlockLegend": { + "message": "Navigation to potentially undesirable sites will be blocked, and you will be offered the option to proceed.", + "description": "Label for a checkbox in the options page" + }, "findListsPlaceholder": { "message": "Cerca llistes", "description": "Placeholder for the input field used to find lists" + }, + "strictblockTitle": { + "message": "Page blocked", + "description": "Webpage title for the strict-blocked page" + }, + "strictblockSentence1": { + "message": "uBO Lite has prevented the following page from loading:", + "description": "Sentence used in the strict-blocked page" + }, + "strictblockNoParamsPrompt": { + "message": "without parameters", + "description": "Label to be used for the parameter-less URL" + }, + "strictblockBack": { + "message": "Go back", + "description": "A button to go back to the previous webpage" + }, + "strictblockClose": { + "message": "Close this window", + "description": "A button to close the current tab" + }, + "strictblockDontWarn": { + "message": "Don't warn me again about this site", + "description": "Label for checkbox in document-blocked page" + }, + "strictblockProceed": { + "message": "Proceed", + "description": "A button to navigate to the blocked page" } } diff --git a/platform/mv3/extension/_locales/cs/messages.json b/platform/mv3/extension/_locales/cs/messages.json index 5bf5f9d8720f9..ea9251418cf05 100644 --- a/platform/mv3/extension/_locales/cs/messages.json +++ b/platform/mv3/extension/_locales/cs/messages.json @@ -231,8 +231,44 @@ "message": "Zobrazit počet blokovaných požadavků u ikony v panelu nástrojů", "description": "Label for a checkbox in the options page" }, + "enableStrictBlockLabel": { + "message": "Enable strict blocking", + "description": "Label for a checkbox in the options page" + }, + "enableStrictBlockLegend": { + "message": "Navigation to potentially undesirable sites will be blocked, and you will be offered the option to proceed.", + "description": "Label for a checkbox in the options page" + }, "findListsPlaceholder": { "message": "Najít seznamy", "description": "Placeholder for the input field used to find lists" + }, + "strictblockTitle": { + "message": "Page blocked", + "description": "Webpage title for the strict-blocked page" + }, + "strictblockSentence1": { + "message": "uBO Lite has prevented the following page from loading:", + "description": "Sentence used in the strict-blocked page" + }, + "strictblockNoParamsPrompt": { + "message": "without parameters", + "description": "Label to be used for the parameter-less URL" + }, + "strictblockBack": { + "message": "Go back", + "description": "A button to go back to the previous webpage" + }, + "strictblockClose": { + "message": "Close this window", + "description": "A button to close the current tab" + }, + "strictblockDontWarn": { + "message": "Don't warn me again about this site", + "description": "Label for checkbox in document-blocked page" + }, + "strictblockProceed": { + "message": "Proceed", + "description": "A button to navigate to the blocked page" } } diff --git a/platform/mv3/extension/_locales/cv/messages.json b/platform/mv3/extension/_locales/cv/messages.json index 001ac9feaf606..1c200f3653e00 100644 --- a/platform/mv3/extension/_locales/cv/messages.json +++ b/platform/mv3/extension/_locales/cv/messages.json @@ -231,8 +231,44 @@ "message": "Show the number of blocked requests on the toolbar icon", "description": "Label for a checkbox in the options page" }, + "enableStrictBlockLabel": { + "message": "Enable strict blocking", + "description": "Label for a checkbox in the options page" + }, + "enableStrictBlockLegend": { + "message": "Navigation to potentially undesirable sites will be blocked, and you will be offered the option to proceed.", + "description": "Label for a checkbox in the options page" + }, "findListsPlaceholder": { "message": "Find lists", "description": "Placeholder for the input field used to find lists" + }, + "strictblockTitle": { + "message": "Page blocked", + "description": "Webpage title for the strict-blocked page" + }, + "strictblockSentence1": { + "message": "uBO Lite has prevented the following page from loading:", + "description": "Sentence used in the strict-blocked page" + }, + "strictblockNoParamsPrompt": { + "message": "without parameters", + "description": "Label to be used for the parameter-less URL" + }, + "strictblockBack": { + "message": "Go back", + "description": "A button to go back to the previous webpage" + }, + "strictblockClose": { + "message": "Close this window", + "description": "A button to close the current tab" + }, + "strictblockDontWarn": { + "message": "Don't warn me again about this site", + "description": "Label for checkbox in document-blocked page" + }, + "strictblockProceed": { + "message": "Proceed", + "description": "A button to navigate to the blocked page" } } diff --git a/platform/mv3/extension/_locales/cy/messages.json b/platform/mv3/extension/_locales/cy/messages.json index bae4fc9205771..963338c56b06e 100644 --- a/platform/mv3/extension/_locales/cy/messages.json +++ b/platform/mv3/extension/_locales/cy/messages.json @@ -231,8 +231,44 @@ "message": "Show the number of blocked requests on the toolbar icon", "description": "Label for a checkbox in the options page" }, + "enableStrictBlockLabel": { + "message": "Enable strict blocking", + "description": "Label for a checkbox in the options page" + }, + "enableStrictBlockLegend": { + "message": "Navigation to potentially undesirable sites will be blocked, and you will be offered the option to proceed.", + "description": "Label for a checkbox in the options page" + }, "findListsPlaceholder": { "message": "Find lists", "description": "Placeholder for the input field used to find lists" + }, + "strictblockTitle": { + "message": "Page blocked", + "description": "Webpage title for the strict-blocked page" + }, + "strictblockSentence1": { + "message": "uBO Lite has prevented the following page from loading:", + "description": "Sentence used in the strict-blocked page" + }, + "strictblockNoParamsPrompt": { + "message": "without parameters", + "description": "Label to be used for the parameter-less URL" + }, + "strictblockBack": { + "message": "Go back", + "description": "A button to go back to the previous webpage" + }, + "strictblockClose": { + "message": "Close this window", + "description": "A button to close the current tab" + }, + "strictblockDontWarn": { + "message": "Don't warn me again about this site", + "description": "Label for checkbox in document-blocked page" + }, + "strictblockProceed": { + "message": "Proceed", + "description": "A button to navigate to the blocked page" } } diff --git a/platform/mv3/extension/_locales/da/messages.json b/platform/mv3/extension/_locales/da/messages.json index 7d987b92b3102..edddf00153b7f 100644 --- a/platform/mv3/extension/_locales/da/messages.json +++ b/platform/mv3/extension/_locales/da/messages.json @@ -231,8 +231,44 @@ "message": "Vis antallet af blokerede forespørgsler på værktøjsbjælkeikonet", "description": "Label for a checkbox in the options page" }, + "enableStrictBlockLabel": { + "message": "Enable strict blocking", + "description": "Label for a checkbox in the options page" + }, + "enableStrictBlockLegend": { + "message": "Navigation to potentially undesirable sites will be blocked, and you will be offered the option to proceed.", + "description": "Label for a checkbox in the options page" + }, "findListsPlaceholder": { "message": "Find lister", "description": "Placeholder for the input field used to find lists" + }, + "strictblockTitle": { + "message": "Page blocked", + "description": "Webpage title for the strict-blocked page" + }, + "strictblockSentence1": { + "message": "uBO Lite has prevented the following page from loading:", + "description": "Sentence used in the strict-blocked page" + }, + "strictblockNoParamsPrompt": { + "message": "without parameters", + "description": "Label to be used for the parameter-less URL" + }, + "strictblockBack": { + "message": "Go back", + "description": "A button to go back to the previous webpage" + }, + "strictblockClose": { + "message": "Close this window", + "description": "A button to close the current tab" + }, + "strictblockDontWarn": { + "message": "Don't warn me again about this site", + "description": "Label for checkbox in document-blocked page" + }, + "strictblockProceed": { + "message": "Proceed", + "description": "A button to navigate to the blocked page" } } diff --git a/platform/mv3/extension/_locales/de/messages.json b/platform/mv3/extension/_locales/de/messages.json index bdf6c74bb09c1..58ccefdcd6773 100644 --- a/platform/mv3/extension/_locales/de/messages.json +++ b/platform/mv3/extension/_locales/de/messages.json @@ -231,8 +231,44 @@ "message": "Anzahl der blockierten Anfragen auf dem Symbol in der Symbolleiste anzeigen", "description": "Label for a checkbox in the options page" }, + "enableStrictBlockLabel": { + "message": "Enable strict blocking", + "description": "Label for a checkbox in the options page" + }, + "enableStrictBlockLegend": { + "message": "Navigation to potentially undesirable sites will be blocked, and you will be offered the option to proceed.", + "description": "Label for a checkbox in the options page" + }, "findListsPlaceholder": { "message": "Listen suchen", "description": "Placeholder for the input field used to find lists" + }, + "strictblockTitle": { + "message": "Page blocked", + "description": "Webpage title for the strict-blocked page" + }, + "strictblockSentence1": { + "message": "uBO Lite has prevented the following page from loading:", + "description": "Sentence used in the strict-blocked page" + }, + "strictblockNoParamsPrompt": { + "message": "without parameters", + "description": "Label to be used for the parameter-less URL" + }, + "strictblockBack": { + "message": "Go back", + "description": "A button to go back to the previous webpage" + }, + "strictblockClose": { + "message": "Close this window", + "description": "A button to close the current tab" + }, + "strictblockDontWarn": { + "message": "Don't warn me again about this site", + "description": "Label for checkbox in document-blocked page" + }, + "strictblockProceed": { + "message": "Proceed", + "description": "A button to navigate to the blocked page" } } diff --git a/platform/mv3/extension/_locales/el/messages.json b/platform/mv3/extension/_locales/el/messages.json index 45ef7ec9d9cc2..5b11ba4ceca0d 100644 --- a/platform/mv3/extension/_locales/el/messages.json +++ b/platform/mv3/extension/_locales/el/messages.json @@ -231,8 +231,44 @@ "message": "Εμφάνιση του αριθμού των αποκλεισμένων αιτημάτων στο εικονίδιο της γραμμής εργαλείων", "description": "Label for a checkbox in the options page" }, + "enableStrictBlockLabel": { + "message": "Enable strict blocking", + "description": "Label for a checkbox in the options page" + }, + "enableStrictBlockLegend": { + "message": "Navigation to potentially undesirable sites will be blocked, and you will be offered the option to proceed.", + "description": "Label for a checkbox in the options page" + }, "findListsPlaceholder": { "message": "Εύρεση λιστών", "description": "Placeholder for the input field used to find lists" + }, + "strictblockTitle": { + "message": "Page blocked", + "description": "Webpage title for the strict-blocked page" + }, + "strictblockSentence1": { + "message": "uBO Lite has prevented the following page from loading:", + "description": "Sentence used in the strict-blocked page" + }, + "strictblockNoParamsPrompt": { + "message": "without parameters", + "description": "Label to be used for the parameter-less URL" + }, + "strictblockBack": { + "message": "Go back", + "description": "A button to go back to the previous webpage" + }, + "strictblockClose": { + "message": "Close this window", + "description": "A button to close the current tab" + }, + "strictblockDontWarn": { + "message": "Don't warn me again about this site", + "description": "Label for checkbox in document-blocked page" + }, + "strictblockProceed": { + "message": "Proceed", + "description": "A button to navigate to the blocked page" } } diff --git a/platform/mv3/extension/_locales/en_GB/messages.json b/platform/mv3/extension/_locales/en_GB/messages.json index ee80a063a5e79..1ebff08aef9ce 100644 --- a/platform/mv3/extension/_locales/en_GB/messages.json +++ b/platform/mv3/extension/_locales/en_GB/messages.json @@ -231,8 +231,44 @@ "message": "Show the number of blocked requests on the toolbar icon", "description": "Label for a checkbox in the options page" }, + "enableStrictBlockLabel": { + "message": "Enable strict blocking", + "description": "Label for a checkbox in the options page" + }, + "enableStrictBlockLegend": { + "message": "Navigation to potentially undesirable sites will be blocked, and you will be offered the option to proceed.", + "description": "Label for a checkbox in the options page" + }, "findListsPlaceholder": { "message": "Find lists", "description": "Placeholder for the input field used to find lists" + }, + "strictblockTitle": { + "message": "Page blocked", + "description": "Webpage title for the strict-blocked page" + }, + "strictblockSentence1": { + "message": "uBO Lite has prevented the following page from loading:", + "description": "Sentence used in the strict-blocked page" + }, + "strictblockNoParamsPrompt": { + "message": "without parameters", + "description": "Label to be used for the parameter-less URL" + }, + "strictblockBack": { + "message": "Go back", + "description": "A button to go back to the previous webpage" + }, + "strictblockClose": { + "message": "Close this window", + "description": "A button to close the current tab" + }, + "strictblockDontWarn": { + "message": "Don't warn me again about this site", + "description": "Label for checkbox in document-blocked page" + }, + "strictblockProceed": { + "message": "Proceed", + "description": "A button to navigate to the blocked page" } } diff --git a/platform/mv3/extension/_locales/eo/messages.json b/platform/mv3/extension/_locales/eo/messages.json index ec68394744eac..4c75658ccd218 100644 --- a/platform/mv3/extension/_locales/eo/messages.json +++ b/platform/mv3/extension/_locales/eo/messages.json @@ -231,8 +231,44 @@ "message": "Show the number of blocked requests on the toolbar icon", "description": "Label for a checkbox in the options page" }, + "enableStrictBlockLabel": { + "message": "Enable strict blocking", + "description": "Label for a checkbox in the options page" + }, + "enableStrictBlockLegend": { + "message": "Navigation to potentially undesirable sites will be blocked, and you will be offered the option to proceed.", + "description": "Label for a checkbox in the options page" + }, "findListsPlaceholder": { "message": "Find lists", "description": "Placeholder for the input field used to find lists" + }, + "strictblockTitle": { + "message": "Page blocked", + "description": "Webpage title for the strict-blocked page" + }, + "strictblockSentence1": { + "message": "uBO Lite has prevented the following page from loading:", + "description": "Sentence used in the strict-blocked page" + }, + "strictblockNoParamsPrompt": { + "message": "without parameters", + "description": "Label to be used for the parameter-less URL" + }, + "strictblockBack": { + "message": "Go back", + "description": "A button to go back to the previous webpage" + }, + "strictblockClose": { + "message": "Close this window", + "description": "A button to close the current tab" + }, + "strictblockDontWarn": { + "message": "Don't warn me again about this site", + "description": "Label for checkbox in document-blocked page" + }, + "strictblockProceed": { + "message": "Proceed", + "description": "A button to navigate to the blocked page" } } diff --git a/platform/mv3/extension/_locales/es/messages.json b/platform/mv3/extension/_locales/es/messages.json index 89fa0aaddee9d..5fd5d530b0043 100644 --- a/platform/mv3/extension/_locales/es/messages.json +++ b/platform/mv3/extension/_locales/es/messages.json @@ -231,8 +231,44 @@ "message": "Mostrar el número de peticiones bloqueadas en el icono de la barra de herramientas", "description": "Label for a checkbox in the options page" }, + "enableStrictBlockLabel": { + "message": "Enable strict blocking", + "description": "Label for a checkbox in the options page" + }, + "enableStrictBlockLegend": { + "message": "Navigation to potentially undesirable sites will be blocked, and you will be offered the option to proceed.", + "description": "Label for a checkbox in the options page" + }, "findListsPlaceholder": { "message": "Encontrar listas", "description": "Placeholder for the input field used to find lists" + }, + "strictblockTitle": { + "message": "Page blocked", + "description": "Webpage title for the strict-blocked page" + }, + "strictblockSentence1": { + "message": "uBO Lite has prevented the following page from loading:", + "description": "Sentence used in the strict-blocked page" + }, + "strictblockNoParamsPrompt": { + "message": "without parameters", + "description": "Label to be used for the parameter-less URL" + }, + "strictblockBack": { + "message": "Go back", + "description": "A button to go back to the previous webpage" + }, + "strictblockClose": { + "message": "Close this window", + "description": "A button to close the current tab" + }, + "strictblockDontWarn": { + "message": "Don't warn me again about this site", + "description": "Label for checkbox in document-blocked page" + }, + "strictblockProceed": { + "message": "Proceed", + "description": "A button to navigate to the blocked page" } } diff --git a/platform/mv3/extension/_locales/et/messages.json b/platform/mv3/extension/_locales/et/messages.json index 008059cc3c63b..1d8efd99ccdee 100644 --- a/platform/mv3/extension/_locales/et/messages.json +++ b/platform/mv3/extension/_locales/et/messages.json @@ -231,8 +231,44 @@ "message": "Kuva tööriistariba ikoonil blokeeritud elementide arv", "description": "Label for a checkbox in the options page" }, + "enableStrictBlockLabel": { + "message": "Enable strict blocking", + "description": "Label for a checkbox in the options page" + }, + "enableStrictBlockLegend": { + "message": "Navigation to potentially undesirable sites will be blocked, and you will be offered the option to proceed.", + "description": "Label for a checkbox in the options page" + }, "findListsPlaceholder": { "message": "Otsi nimekirju", "description": "Placeholder for the input field used to find lists" + }, + "strictblockTitle": { + "message": "Page blocked", + "description": "Webpage title for the strict-blocked page" + }, + "strictblockSentence1": { + "message": "uBO Lite has prevented the following page from loading:", + "description": "Sentence used in the strict-blocked page" + }, + "strictblockNoParamsPrompt": { + "message": "without parameters", + "description": "Label to be used for the parameter-less URL" + }, + "strictblockBack": { + "message": "Go back", + "description": "A button to go back to the previous webpage" + }, + "strictblockClose": { + "message": "Close this window", + "description": "A button to close the current tab" + }, + "strictblockDontWarn": { + "message": "Don't warn me again about this site", + "description": "Label for checkbox in document-blocked page" + }, + "strictblockProceed": { + "message": "Proceed", + "description": "A button to navigate to the blocked page" } } diff --git a/platform/mv3/extension/_locales/eu/messages.json b/platform/mv3/extension/_locales/eu/messages.json index a477239fc0fbe..4d026da6bc9fe 100644 --- a/platform/mv3/extension/_locales/eu/messages.json +++ b/platform/mv3/extension/_locales/eu/messages.json @@ -231,8 +231,44 @@ "message": "Tresna-barraren ikonoan blokeatutako eskaeren kopurua erakutsi", "description": "Label for a checkbox in the options page" }, + "enableStrictBlockLabel": { + "message": "Enable strict blocking", + "description": "Label for a checkbox in the options page" + }, + "enableStrictBlockLegend": { + "message": "Navigation to potentially undesirable sites will be blocked, and you will be offered the option to proceed.", + "description": "Label for a checkbox in the options page" + }, "findListsPlaceholder": { "message": "Find lists", "description": "Placeholder for the input field used to find lists" + }, + "strictblockTitle": { + "message": "Page blocked", + "description": "Webpage title for the strict-blocked page" + }, + "strictblockSentence1": { + "message": "uBO Lite has prevented the following page from loading:", + "description": "Sentence used in the strict-blocked page" + }, + "strictblockNoParamsPrompt": { + "message": "without parameters", + "description": "Label to be used for the parameter-less URL" + }, + "strictblockBack": { + "message": "Go back", + "description": "A button to go back to the previous webpage" + }, + "strictblockClose": { + "message": "Close this window", + "description": "A button to close the current tab" + }, + "strictblockDontWarn": { + "message": "Don't warn me again about this site", + "description": "Label for checkbox in document-blocked page" + }, + "strictblockProceed": { + "message": "Proceed", + "description": "A button to navigate to the blocked page" } } diff --git a/platform/mv3/extension/_locales/fa/messages.json b/platform/mv3/extension/_locales/fa/messages.json index ebc8ca22f8f43..276405ceb51a7 100644 --- a/platform/mv3/extension/_locales/fa/messages.json +++ b/platform/mv3/extension/_locales/fa/messages.json @@ -231,8 +231,44 @@ "message": "Show the number of blocked requests on the toolbar icon", "description": "Label for a checkbox in the options page" }, + "enableStrictBlockLabel": { + "message": "Enable strict blocking", + "description": "Label for a checkbox in the options page" + }, + "enableStrictBlockLegend": { + "message": "Navigation to potentially undesirable sites will be blocked, and you will be offered the option to proceed.", + "description": "Label for a checkbox in the options page" + }, "findListsPlaceholder": { "message": "Find lists", "description": "Placeholder for the input field used to find lists" + }, + "strictblockTitle": { + "message": "Page blocked", + "description": "Webpage title for the strict-blocked page" + }, + "strictblockSentence1": { + "message": "uBO Lite has prevented the following page from loading:", + "description": "Sentence used in the strict-blocked page" + }, + "strictblockNoParamsPrompt": { + "message": "without parameters", + "description": "Label to be used for the parameter-less URL" + }, + "strictblockBack": { + "message": "Go back", + "description": "A button to go back to the previous webpage" + }, + "strictblockClose": { + "message": "Close this window", + "description": "A button to close the current tab" + }, + "strictblockDontWarn": { + "message": "Don't warn me again about this site", + "description": "Label for checkbox in document-blocked page" + }, + "strictblockProceed": { + "message": "Proceed", + "description": "A button to navigate to the blocked page" } } diff --git a/platform/mv3/extension/_locales/fi/messages.json b/platform/mv3/extension/_locales/fi/messages.json index ecce31bfecf64..e3fe20347cc94 100644 --- a/platform/mv3/extension/_locales/fi/messages.json +++ b/platform/mv3/extension/_locales/fi/messages.json @@ -231,8 +231,44 @@ "message": "Näytä estettyjen pyyntöjen määrä työkalupalkin kuvakkeessa", "description": "Label for a checkbox in the options page" }, + "enableStrictBlockLabel": { + "message": "Enable strict blocking", + "description": "Label for a checkbox in the options page" + }, + "enableStrictBlockLegend": { + "message": "Navigation to potentially undesirable sites will be blocked, and you will be offered the option to proceed.", + "description": "Label for a checkbox in the options page" + }, "findListsPlaceholder": { "message": "Etsi listoja", "description": "Placeholder for the input field used to find lists" + }, + "strictblockTitle": { + "message": "Page blocked", + "description": "Webpage title for the strict-blocked page" + }, + "strictblockSentence1": { + "message": "uBO Lite has prevented the following page from loading:", + "description": "Sentence used in the strict-blocked page" + }, + "strictblockNoParamsPrompt": { + "message": "without parameters", + "description": "Label to be used for the parameter-less URL" + }, + "strictblockBack": { + "message": "Go back", + "description": "A button to go back to the previous webpage" + }, + "strictblockClose": { + "message": "Close this window", + "description": "A button to close the current tab" + }, + "strictblockDontWarn": { + "message": "Don't warn me again about this site", + "description": "Label for checkbox in document-blocked page" + }, + "strictblockProceed": { + "message": "Proceed", + "description": "A button to navigate to the blocked page" } } diff --git a/platform/mv3/extension/_locales/fil/messages.json b/platform/mv3/extension/_locales/fil/messages.json index 6450bb8f60597..3fb3e29541216 100644 --- a/platform/mv3/extension/_locales/fil/messages.json +++ b/platform/mv3/extension/_locales/fil/messages.json @@ -231,8 +231,44 @@ "message": "Show the number of blocked requests on the toolbar icon", "description": "Label for a checkbox in the options page" }, + "enableStrictBlockLabel": { + "message": "Enable strict blocking", + "description": "Label for a checkbox in the options page" + }, + "enableStrictBlockLegend": { + "message": "Navigation to potentially undesirable sites will be blocked, and you will be offered the option to proceed.", + "description": "Label for a checkbox in the options page" + }, "findListsPlaceholder": { "message": "Find lists", "description": "Placeholder for the input field used to find lists" + }, + "strictblockTitle": { + "message": "Page blocked", + "description": "Webpage title for the strict-blocked page" + }, + "strictblockSentence1": { + "message": "uBO Lite has prevented the following page from loading:", + "description": "Sentence used in the strict-blocked page" + }, + "strictblockNoParamsPrompt": { + "message": "without parameters", + "description": "Label to be used for the parameter-less URL" + }, + "strictblockBack": { + "message": "Go back", + "description": "A button to go back to the previous webpage" + }, + "strictblockClose": { + "message": "Close this window", + "description": "A button to close the current tab" + }, + "strictblockDontWarn": { + "message": "Don't warn me again about this site", + "description": "Label for checkbox in document-blocked page" + }, + "strictblockProceed": { + "message": "Proceed", + "description": "A button to navigate to the blocked page" } } diff --git a/platform/mv3/extension/_locales/fr/messages.json b/platform/mv3/extension/_locales/fr/messages.json index 96c47b8e32218..7a9f2f3f31eb9 100644 --- a/platform/mv3/extension/_locales/fr/messages.json +++ b/platform/mv3/extension/_locales/fr/messages.json @@ -231,8 +231,44 @@ "message": "Afficher le nombre de requêtes bloquées sur l'icône", "description": "Label for a checkbox in the options page" }, + "enableStrictBlockLabel": { + "message": "Enable strict blocking", + "description": "Label for a checkbox in the options page" + }, + "enableStrictBlockLegend": { + "message": "Navigation to potentially undesirable sites will be blocked, and you will be offered the option to proceed.", + "description": "Label for a checkbox in the options page" + }, "findListsPlaceholder": { "message": "Trouver des listes", "description": "Placeholder for the input field used to find lists" + }, + "strictblockTitle": { + "message": "Page blocked", + "description": "Webpage title for the strict-blocked page" + }, + "strictblockSentence1": { + "message": "uBO Lite has prevented the following page from loading:", + "description": "Sentence used in the strict-blocked page" + }, + "strictblockNoParamsPrompt": { + "message": "without parameters", + "description": "Label to be used for the parameter-less URL" + }, + "strictblockBack": { + "message": "Go back", + "description": "A button to go back to the previous webpage" + }, + "strictblockClose": { + "message": "Close this window", + "description": "A button to close the current tab" + }, + "strictblockDontWarn": { + "message": "Don't warn me again about this site", + "description": "Label for checkbox in document-blocked page" + }, + "strictblockProceed": { + "message": "Proceed", + "description": "A button to navigate to the blocked page" } } diff --git a/platform/mv3/extension/_locales/fy/messages.json b/platform/mv3/extension/_locales/fy/messages.json index 44d68458821ed..bedb17ef55c4d 100644 --- a/platform/mv3/extension/_locales/fy/messages.json +++ b/platform/mv3/extension/_locales/fy/messages.json @@ -231,8 +231,44 @@ "message": "It tal blokkearre oanfragen op it arkbalkepiktogram toane", "description": "Label for a checkbox in the options page" }, + "enableStrictBlockLabel": { + "message": "Enable strict blocking", + "description": "Label for a checkbox in the options page" + }, + "enableStrictBlockLegend": { + "message": "Navigation to potentially undesirable sites will be blocked, and you will be offered the option to proceed.", + "description": "Label for a checkbox in the options page" + }, "findListsPlaceholder": { "message": "Listen sykje", "description": "Placeholder for the input field used to find lists" + }, + "strictblockTitle": { + "message": "Page blocked", + "description": "Webpage title for the strict-blocked page" + }, + "strictblockSentence1": { + "message": "uBO Lite has prevented the following page from loading:", + "description": "Sentence used in the strict-blocked page" + }, + "strictblockNoParamsPrompt": { + "message": "without parameters", + "description": "Label to be used for the parameter-less URL" + }, + "strictblockBack": { + "message": "Go back", + "description": "A button to go back to the previous webpage" + }, + "strictblockClose": { + "message": "Close this window", + "description": "A button to close the current tab" + }, + "strictblockDontWarn": { + "message": "Don't warn me again about this site", + "description": "Label for checkbox in document-blocked page" + }, + "strictblockProceed": { + "message": "Proceed", + "description": "A button to navigate to the blocked page" } } diff --git a/platform/mv3/extension/_locales/gl/messages.json b/platform/mv3/extension/_locales/gl/messages.json index 53589b6f6cf72..10c74dc99c343 100644 --- a/platform/mv3/extension/_locales/gl/messages.json +++ b/platform/mv3/extension/_locales/gl/messages.json @@ -231,8 +231,44 @@ "message": "Mostrar na icona da barra o número de peticións bloqueadas", "description": "Label for a checkbox in the options page" }, + "enableStrictBlockLabel": { + "message": "Enable strict blocking", + "description": "Label for a checkbox in the options page" + }, + "enableStrictBlockLegend": { + "message": "Navigation to potentially undesirable sites will be blocked, and you will be offered the option to proceed.", + "description": "Label for a checkbox in the options page" + }, "findListsPlaceholder": { "message": "Atopa listas", "description": "Placeholder for the input field used to find lists" + }, + "strictblockTitle": { + "message": "Page blocked", + "description": "Webpage title for the strict-blocked page" + }, + "strictblockSentence1": { + "message": "uBO Lite has prevented the following page from loading:", + "description": "Sentence used in the strict-blocked page" + }, + "strictblockNoParamsPrompt": { + "message": "without parameters", + "description": "Label to be used for the parameter-less URL" + }, + "strictblockBack": { + "message": "Go back", + "description": "A button to go back to the previous webpage" + }, + "strictblockClose": { + "message": "Close this window", + "description": "A button to close the current tab" + }, + "strictblockDontWarn": { + "message": "Don't warn me again about this site", + "description": "Label for checkbox in document-blocked page" + }, + "strictblockProceed": { + "message": "Proceed", + "description": "A button to navigate to the blocked page" } } diff --git a/platform/mv3/extension/_locales/gu/messages.json b/platform/mv3/extension/_locales/gu/messages.json index 001ac9feaf606..1c200f3653e00 100644 --- a/platform/mv3/extension/_locales/gu/messages.json +++ b/platform/mv3/extension/_locales/gu/messages.json @@ -231,8 +231,44 @@ "message": "Show the number of blocked requests on the toolbar icon", "description": "Label for a checkbox in the options page" }, + "enableStrictBlockLabel": { + "message": "Enable strict blocking", + "description": "Label for a checkbox in the options page" + }, + "enableStrictBlockLegend": { + "message": "Navigation to potentially undesirable sites will be blocked, and you will be offered the option to proceed.", + "description": "Label for a checkbox in the options page" + }, "findListsPlaceholder": { "message": "Find lists", "description": "Placeholder for the input field used to find lists" + }, + "strictblockTitle": { + "message": "Page blocked", + "description": "Webpage title for the strict-blocked page" + }, + "strictblockSentence1": { + "message": "uBO Lite has prevented the following page from loading:", + "description": "Sentence used in the strict-blocked page" + }, + "strictblockNoParamsPrompt": { + "message": "without parameters", + "description": "Label to be used for the parameter-less URL" + }, + "strictblockBack": { + "message": "Go back", + "description": "A button to go back to the previous webpage" + }, + "strictblockClose": { + "message": "Close this window", + "description": "A button to close the current tab" + }, + "strictblockDontWarn": { + "message": "Don't warn me again about this site", + "description": "Label for checkbox in document-blocked page" + }, + "strictblockProceed": { + "message": "Proceed", + "description": "A button to navigate to the blocked page" } } diff --git a/platform/mv3/extension/_locales/he/messages.json b/platform/mv3/extension/_locales/he/messages.json index e9b9d50d094c6..ee12c27ad7591 100644 --- a/platform/mv3/extension/_locales/he/messages.json +++ b/platform/mv3/extension/_locales/he/messages.json @@ -231,8 +231,44 @@ "message": "הצגת מספר הבקשות החסומות על הסמל", "description": "Label for a checkbox in the options page" }, + "enableStrictBlockLabel": { + "message": "Enable strict blocking", + "description": "Label for a checkbox in the options page" + }, + "enableStrictBlockLegend": { + "message": "Navigation to potentially undesirable sites will be blocked, and you will be offered the option to proceed.", + "description": "Label for a checkbox in the options page" + }, "findListsPlaceholder": { "message": "חיפוש רשימות", "description": "Placeholder for the input field used to find lists" + }, + "strictblockTitle": { + "message": "Page blocked", + "description": "Webpage title for the strict-blocked page" + }, + "strictblockSentence1": { + "message": "uBO Lite has prevented the following page from loading:", + "description": "Sentence used in the strict-blocked page" + }, + "strictblockNoParamsPrompt": { + "message": "without parameters", + "description": "Label to be used for the parameter-less URL" + }, + "strictblockBack": { + "message": "Go back", + "description": "A button to go back to the previous webpage" + }, + "strictblockClose": { + "message": "Close this window", + "description": "A button to close the current tab" + }, + "strictblockDontWarn": { + "message": "Don't warn me again about this site", + "description": "Label for checkbox in document-blocked page" + }, + "strictblockProceed": { + "message": "Proceed", + "description": "A button to navigate to the blocked page" } } diff --git a/platform/mv3/extension/_locales/hi/messages.json b/platform/mv3/extension/_locales/hi/messages.json index 710ac407d6515..fa5407f5856c2 100644 --- a/platform/mv3/extension/_locales/hi/messages.json +++ b/platform/mv3/extension/_locales/hi/messages.json @@ -231,8 +231,44 @@ "message": "टूलबार आइकन पर अवरुद्ध अनुरोधों की संख्या दिखाएं", "description": "Label for a checkbox in the options page" }, + "enableStrictBlockLabel": { + "message": "Enable strict blocking", + "description": "Label for a checkbox in the options page" + }, + "enableStrictBlockLegend": { + "message": "Navigation to potentially undesirable sites will be blocked, and you will be offered the option to proceed.", + "description": "Label for a checkbox in the options page" + }, "findListsPlaceholder": { "message": "Find lists", "description": "Placeholder for the input field used to find lists" + }, + "strictblockTitle": { + "message": "Page blocked", + "description": "Webpage title for the strict-blocked page" + }, + "strictblockSentence1": { + "message": "uBO Lite has prevented the following page from loading:", + "description": "Sentence used in the strict-blocked page" + }, + "strictblockNoParamsPrompt": { + "message": "without parameters", + "description": "Label to be used for the parameter-less URL" + }, + "strictblockBack": { + "message": "Go back", + "description": "A button to go back to the previous webpage" + }, + "strictblockClose": { + "message": "Close this window", + "description": "A button to close the current tab" + }, + "strictblockDontWarn": { + "message": "Don't warn me again about this site", + "description": "Label for checkbox in document-blocked page" + }, + "strictblockProceed": { + "message": "Proceed", + "description": "A button to navigate to the blocked page" } } diff --git a/platform/mv3/extension/_locales/hr/messages.json b/platform/mv3/extension/_locales/hr/messages.json index d8918586540c5..db09ff164dbf9 100644 --- a/platform/mv3/extension/_locales/hr/messages.json +++ b/platform/mv3/extension/_locales/hr/messages.json @@ -231,8 +231,44 @@ "message": "Prikaži broj blokiranih zahtjeva na ikoni alatne trake", "description": "Label for a checkbox in the options page" }, + "enableStrictBlockLabel": { + "message": "Enable strict blocking", + "description": "Label for a checkbox in the options page" + }, + "enableStrictBlockLegend": { + "message": "Navigation to potentially undesirable sites will be blocked, and you will be offered the option to proceed.", + "description": "Label for a checkbox in the options page" + }, "findListsPlaceholder": { "message": "Pronađi liste", "description": "Placeholder for the input field used to find lists" + }, + "strictblockTitle": { + "message": "Page blocked", + "description": "Webpage title for the strict-blocked page" + }, + "strictblockSentence1": { + "message": "uBO Lite has prevented the following page from loading:", + "description": "Sentence used in the strict-blocked page" + }, + "strictblockNoParamsPrompt": { + "message": "without parameters", + "description": "Label to be used for the parameter-less URL" + }, + "strictblockBack": { + "message": "Go back", + "description": "A button to go back to the previous webpage" + }, + "strictblockClose": { + "message": "Close this window", + "description": "A button to close the current tab" + }, + "strictblockDontWarn": { + "message": "Don't warn me again about this site", + "description": "Label for checkbox in document-blocked page" + }, + "strictblockProceed": { + "message": "Proceed", + "description": "A button to navigate to the blocked page" } } diff --git a/platform/mv3/extension/_locales/hu/messages.json b/platform/mv3/extension/_locales/hu/messages.json index cacd7fee325ad..7fc3f9f07c335 100644 --- a/platform/mv3/extension/_locales/hu/messages.json +++ b/platform/mv3/extension/_locales/hu/messages.json @@ -231,8 +231,44 @@ "message": "Blokkolt kérések számának megjelenítése az eszköztárikonon", "description": "Label for a checkbox in the options page" }, + "enableStrictBlockLabel": { + "message": "Enable strict blocking", + "description": "Label for a checkbox in the options page" + }, + "enableStrictBlockLegend": { + "message": "Navigation to potentially undesirable sites will be blocked, and you will be offered the option to proceed.", + "description": "Label for a checkbox in the options page" + }, "findListsPlaceholder": { "message": "Listák keresése", "description": "Placeholder for the input field used to find lists" + }, + "strictblockTitle": { + "message": "Page blocked", + "description": "Webpage title for the strict-blocked page" + }, + "strictblockSentence1": { + "message": "uBO Lite has prevented the following page from loading:", + "description": "Sentence used in the strict-blocked page" + }, + "strictblockNoParamsPrompt": { + "message": "without parameters", + "description": "Label to be used for the parameter-less URL" + }, + "strictblockBack": { + "message": "Go back", + "description": "A button to go back to the previous webpage" + }, + "strictblockClose": { + "message": "Close this window", + "description": "A button to close the current tab" + }, + "strictblockDontWarn": { + "message": "Don't warn me again about this site", + "description": "Label for checkbox in document-blocked page" + }, + "strictblockProceed": { + "message": "Proceed", + "description": "A button to navigate to the blocked page" } } diff --git a/platform/mv3/extension/_locales/hy/messages.json b/platform/mv3/extension/_locales/hy/messages.json index 9f6b7e347715e..005a83ca2b4cc 100644 --- a/platform/mv3/extension/_locales/hy/messages.json +++ b/platform/mv3/extension/_locales/hy/messages.json @@ -231,8 +231,44 @@ "message": "Ցուցադրել արգելափակված հայտերի քանակը գործիքների վահանակին", "description": "Label for a checkbox in the options page" }, + "enableStrictBlockLabel": { + "message": "Enable strict blocking", + "description": "Label for a checkbox in the options page" + }, + "enableStrictBlockLegend": { + "message": "Navigation to potentially undesirable sites will be blocked, and you will be offered the option to proceed.", + "description": "Label for a checkbox in the options page" + }, "findListsPlaceholder": { "message": "Find lists", "description": "Placeholder for the input field used to find lists" + }, + "strictblockTitle": { + "message": "Page blocked", + "description": "Webpage title for the strict-blocked page" + }, + "strictblockSentence1": { + "message": "uBO Lite has prevented the following page from loading:", + "description": "Sentence used in the strict-blocked page" + }, + "strictblockNoParamsPrompt": { + "message": "without parameters", + "description": "Label to be used for the parameter-less URL" + }, + "strictblockBack": { + "message": "Go back", + "description": "A button to go back to the previous webpage" + }, + "strictblockClose": { + "message": "Close this window", + "description": "A button to close the current tab" + }, + "strictblockDontWarn": { + "message": "Don't warn me again about this site", + "description": "Label for checkbox in document-blocked page" + }, + "strictblockProceed": { + "message": "Proceed", + "description": "A button to navigate to the blocked page" } } diff --git a/platform/mv3/extension/_locales/id/messages.json b/platform/mv3/extension/_locales/id/messages.json index 90b49987c904e..6b05ba7f9d2f2 100644 --- a/platform/mv3/extension/_locales/id/messages.json +++ b/platform/mv3/extension/_locales/id/messages.json @@ -231,8 +231,44 @@ "message": "Tampilkan jumlah permintaan yang diblokir pada ikon toolbar", "description": "Label for a checkbox in the options page" }, + "enableStrictBlockLabel": { + "message": "Enable strict blocking", + "description": "Label for a checkbox in the options page" + }, + "enableStrictBlockLegend": { + "message": "Navigation to potentially undesirable sites will be blocked, and you will be offered the option to proceed.", + "description": "Label for a checkbox in the options page" + }, "findListsPlaceholder": { "message": "Find lists", "description": "Placeholder for the input field used to find lists" + }, + "strictblockTitle": { + "message": "Page blocked", + "description": "Webpage title for the strict-blocked page" + }, + "strictblockSentence1": { + "message": "uBO Lite has prevented the following page from loading:", + "description": "Sentence used in the strict-blocked page" + }, + "strictblockNoParamsPrompt": { + "message": "without parameters", + "description": "Label to be used for the parameter-less URL" + }, + "strictblockBack": { + "message": "Go back", + "description": "A button to go back to the previous webpage" + }, + "strictblockClose": { + "message": "Close this window", + "description": "A button to close the current tab" + }, + "strictblockDontWarn": { + "message": "Don't warn me again about this site", + "description": "Label for checkbox in document-blocked page" + }, + "strictblockProceed": { + "message": "Proceed", + "description": "A button to navigate to the blocked page" } } diff --git a/platform/mv3/extension/_locales/it/messages.json b/platform/mv3/extension/_locales/it/messages.json index 058376269d5d7..2e434a816a359 100644 --- a/platform/mv3/extension/_locales/it/messages.json +++ b/platform/mv3/extension/_locales/it/messages.json @@ -231,8 +231,44 @@ "message": "Mostra il numero delle richieste bloccate sull'icona nella barra degli strumenti", "description": "Label for a checkbox in the options page" }, + "enableStrictBlockLabel": { + "message": "Enable strict blocking", + "description": "Label for a checkbox in the options page" + }, + "enableStrictBlockLegend": { + "message": "Navigation to potentially undesirable sites will be blocked, and you will be offered the option to proceed.", + "description": "Label for a checkbox in the options page" + }, "findListsPlaceholder": { "message": "Trova elenchi", "description": "Placeholder for the input field used to find lists" + }, + "strictblockTitle": { + "message": "Page blocked", + "description": "Webpage title for the strict-blocked page" + }, + "strictblockSentence1": { + "message": "uBO Lite has prevented the following page from loading:", + "description": "Sentence used in the strict-blocked page" + }, + "strictblockNoParamsPrompt": { + "message": "without parameters", + "description": "Label to be used for the parameter-less URL" + }, + "strictblockBack": { + "message": "Go back", + "description": "A button to go back to the previous webpage" + }, + "strictblockClose": { + "message": "Close this window", + "description": "A button to close the current tab" + }, + "strictblockDontWarn": { + "message": "Don't warn me again about this site", + "description": "Label for checkbox in document-blocked page" + }, + "strictblockProceed": { + "message": "Proceed", + "description": "A button to navigate to the blocked page" } } diff --git a/platform/mv3/extension/_locales/ja/messages.json b/platform/mv3/extension/_locales/ja/messages.json index 0efa9bd95eb7b..5c310b1a36c75 100644 --- a/platform/mv3/extension/_locales/ja/messages.json +++ b/platform/mv3/extension/_locales/ja/messages.json @@ -231,8 +231,44 @@ "message": "ブロックしたリクエストの数をツールバーのアイコンに表示", "description": "Label for a checkbox in the options page" }, + "enableStrictBlockLabel": { + "message": "Enable strict blocking", + "description": "Label for a checkbox in the options page" + }, + "enableStrictBlockLegend": { + "message": "Navigation to potentially undesirable sites will be blocked, and you will be offered the option to proceed.", + "description": "Label for a checkbox in the options page" + }, "findListsPlaceholder": { "message": "Find lists", "description": "Placeholder for the input field used to find lists" + }, + "strictblockTitle": { + "message": "Page blocked", + "description": "Webpage title for the strict-blocked page" + }, + "strictblockSentence1": { + "message": "uBO Lite has prevented the following page from loading:", + "description": "Sentence used in the strict-blocked page" + }, + "strictblockNoParamsPrompt": { + "message": "without parameters", + "description": "Label to be used for the parameter-less URL" + }, + "strictblockBack": { + "message": "Go back", + "description": "A button to go back to the previous webpage" + }, + "strictblockClose": { + "message": "Close this window", + "description": "A button to close the current tab" + }, + "strictblockDontWarn": { + "message": "Don't warn me again about this site", + "description": "Label for checkbox in document-blocked page" + }, + "strictblockProceed": { + "message": "Proceed", + "description": "A button to navigate to the blocked page" } } diff --git a/platform/mv3/extension/_locales/ka/messages.json b/platform/mv3/extension/_locales/ka/messages.json index 18931165b03ac..1c874e90178c5 100644 --- a/platform/mv3/extension/_locales/ka/messages.json +++ b/platform/mv3/extension/_locales/ka/messages.json @@ -231,8 +231,44 @@ "message": "აჩვენეთ დაბლოკილი მოთხოვნების რაოდენობა ხელსაწყოთა ზოლის ხატულაზე", "description": "Label for a checkbox in the options page" }, + "enableStrictBlockLabel": { + "message": "Enable strict blocking", + "description": "Label for a checkbox in the options page" + }, + "enableStrictBlockLegend": { + "message": "Navigation to potentially undesirable sites will be blocked, and you will be offered the option to proceed.", + "description": "Label for a checkbox in the options page" + }, "findListsPlaceholder": { "message": "სიების მოძიება", "description": "Placeholder for the input field used to find lists" + }, + "strictblockTitle": { + "message": "Page blocked", + "description": "Webpage title for the strict-blocked page" + }, + "strictblockSentence1": { + "message": "uBO Lite has prevented the following page from loading:", + "description": "Sentence used in the strict-blocked page" + }, + "strictblockNoParamsPrompt": { + "message": "without parameters", + "description": "Label to be used for the parameter-less URL" + }, + "strictblockBack": { + "message": "Go back", + "description": "A button to go back to the previous webpage" + }, + "strictblockClose": { + "message": "Close this window", + "description": "A button to close the current tab" + }, + "strictblockDontWarn": { + "message": "Don't warn me again about this site", + "description": "Label for checkbox in document-blocked page" + }, + "strictblockProceed": { + "message": "Proceed", + "description": "A button to navigate to the blocked page" } } diff --git a/platform/mv3/extension/_locales/kk/messages.json b/platform/mv3/extension/_locales/kk/messages.json index 001ac9feaf606..1c200f3653e00 100644 --- a/platform/mv3/extension/_locales/kk/messages.json +++ b/platform/mv3/extension/_locales/kk/messages.json @@ -231,8 +231,44 @@ "message": "Show the number of blocked requests on the toolbar icon", "description": "Label for a checkbox in the options page" }, + "enableStrictBlockLabel": { + "message": "Enable strict blocking", + "description": "Label for a checkbox in the options page" + }, + "enableStrictBlockLegend": { + "message": "Navigation to potentially undesirable sites will be blocked, and you will be offered the option to proceed.", + "description": "Label for a checkbox in the options page" + }, "findListsPlaceholder": { "message": "Find lists", "description": "Placeholder for the input field used to find lists" + }, + "strictblockTitle": { + "message": "Page blocked", + "description": "Webpage title for the strict-blocked page" + }, + "strictblockSentence1": { + "message": "uBO Lite has prevented the following page from loading:", + "description": "Sentence used in the strict-blocked page" + }, + "strictblockNoParamsPrompt": { + "message": "without parameters", + "description": "Label to be used for the parameter-less URL" + }, + "strictblockBack": { + "message": "Go back", + "description": "A button to go back to the previous webpage" + }, + "strictblockClose": { + "message": "Close this window", + "description": "A button to close the current tab" + }, + "strictblockDontWarn": { + "message": "Don't warn me again about this site", + "description": "Label for checkbox in document-blocked page" + }, + "strictblockProceed": { + "message": "Proceed", + "description": "A button to navigate to the blocked page" } } diff --git a/platform/mv3/extension/_locales/kn/messages.json b/platform/mv3/extension/_locales/kn/messages.json index 7b349ff0a0c16..8d4b80ab44f7e 100644 --- a/platform/mv3/extension/_locales/kn/messages.json +++ b/platform/mv3/extension/_locales/kn/messages.json @@ -231,8 +231,44 @@ "message": "Show the number of blocked requests on the toolbar icon", "description": "Label for a checkbox in the options page" }, + "enableStrictBlockLabel": { + "message": "Enable strict blocking", + "description": "Label for a checkbox in the options page" + }, + "enableStrictBlockLegend": { + "message": "Navigation to potentially undesirable sites will be blocked, and you will be offered the option to proceed.", + "description": "Label for a checkbox in the options page" + }, "findListsPlaceholder": { "message": "Find lists", "description": "Placeholder for the input field used to find lists" + }, + "strictblockTitle": { + "message": "Page blocked", + "description": "Webpage title for the strict-blocked page" + }, + "strictblockSentence1": { + "message": "uBO Lite has prevented the following page from loading:", + "description": "Sentence used in the strict-blocked page" + }, + "strictblockNoParamsPrompt": { + "message": "without parameters", + "description": "Label to be used for the parameter-less URL" + }, + "strictblockBack": { + "message": "Go back", + "description": "A button to go back to the previous webpage" + }, + "strictblockClose": { + "message": "Close this window", + "description": "A button to close the current tab" + }, + "strictblockDontWarn": { + "message": "Don't warn me again about this site", + "description": "Label for checkbox in document-blocked page" + }, + "strictblockProceed": { + "message": "Proceed", + "description": "A button to navigate to the blocked page" } } diff --git a/platform/mv3/extension/_locales/ko/messages.json b/platform/mv3/extension/_locales/ko/messages.json index fbdc2048ef4da..18475960de041 100644 --- a/platform/mv3/extension/_locales/ko/messages.json +++ b/platform/mv3/extension/_locales/ko/messages.json @@ -231,8 +231,44 @@ "message": "차단된 요청 개수를 도구 모음 아이콘에 표시", "description": "Label for a checkbox in the options page" }, + "enableStrictBlockLabel": { + "message": "Enable strict blocking", + "description": "Label for a checkbox in the options page" + }, + "enableStrictBlockLegend": { + "message": "Navigation to potentially undesirable sites will be blocked, and you will be offered the option to proceed.", + "description": "Label for a checkbox in the options page" + }, "findListsPlaceholder": { "message": "목록 찾기", "description": "Placeholder for the input field used to find lists" + }, + "strictblockTitle": { + "message": "Page blocked", + "description": "Webpage title for the strict-blocked page" + }, + "strictblockSentence1": { + "message": "uBO Lite has prevented the following page from loading:", + "description": "Sentence used in the strict-blocked page" + }, + "strictblockNoParamsPrompt": { + "message": "without parameters", + "description": "Label to be used for the parameter-less URL" + }, + "strictblockBack": { + "message": "Go back", + "description": "A button to go back to the previous webpage" + }, + "strictblockClose": { + "message": "Close this window", + "description": "A button to close the current tab" + }, + "strictblockDontWarn": { + "message": "Don't warn me again about this site", + "description": "Label for checkbox in document-blocked page" + }, + "strictblockProceed": { + "message": "Proceed", + "description": "A button to navigate to the blocked page" } } diff --git a/platform/mv3/extension/_locales/lt/messages.json b/platform/mv3/extension/_locales/lt/messages.json index 7e7ae212faeef..8a7b9cb2112e8 100644 --- a/platform/mv3/extension/_locales/lt/messages.json +++ b/platform/mv3/extension/_locales/lt/messages.json @@ -231,8 +231,44 @@ "message": "Show the number of blocked requests on the toolbar icon", "description": "Label for a checkbox in the options page" }, + "enableStrictBlockLabel": { + "message": "Enable strict blocking", + "description": "Label for a checkbox in the options page" + }, + "enableStrictBlockLegend": { + "message": "Navigation to potentially undesirable sites will be blocked, and you will be offered the option to proceed.", + "description": "Label for a checkbox in the options page" + }, "findListsPlaceholder": { "message": "Find lists", "description": "Placeholder for the input field used to find lists" + }, + "strictblockTitle": { + "message": "Page blocked", + "description": "Webpage title for the strict-blocked page" + }, + "strictblockSentence1": { + "message": "uBO Lite has prevented the following page from loading:", + "description": "Sentence used in the strict-blocked page" + }, + "strictblockNoParamsPrompt": { + "message": "without parameters", + "description": "Label to be used for the parameter-less URL" + }, + "strictblockBack": { + "message": "Go back", + "description": "A button to go back to the previous webpage" + }, + "strictblockClose": { + "message": "Close this window", + "description": "A button to close the current tab" + }, + "strictblockDontWarn": { + "message": "Don't warn me again about this site", + "description": "Label for checkbox in document-blocked page" + }, + "strictblockProceed": { + "message": "Proceed", + "description": "A button to navigate to the blocked page" } } diff --git a/platform/mv3/extension/_locales/lv/messages.json b/platform/mv3/extension/_locales/lv/messages.json index 1d470f70f1ee0..cbd9d8b7c9752 100644 --- a/platform/mv3/extension/_locales/lv/messages.json +++ b/platform/mv3/extension/_locales/lv/messages.json @@ -231,8 +231,44 @@ "message": "Rādīt aizturēto pieprasījumu skaitu rīkjoslas ikonā", "description": "Label for a checkbox in the options page" }, + "enableStrictBlockLabel": { + "message": "Enable strict blocking", + "description": "Label for a checkbox in the options page" + }, + "enableStrictBlockLegend": { + "message": "Navigation to potentially undesirable sites will be blocked, and you will be offered the option to proceed.", + "description": "Label for a checkbox in the options page" + }, "findListsPlaceholder": { "message": "Atrast sarakstus", "description": "Placeholder for the input field used to find lists" + }, + "strictblockTitle": { + "message": "Page blocked", + "description": "Webpage title for the strict-blocked page" + }, + "strictblockSentence1": { + "message": "uBO Lite has prevented the following page from loading:", + "description": "Sentence used in the strict-blocked page" + }, + "strictblockNoParamsPrompt": { + "message": "without parameters", + "description": "Label to be used for the parameter-less URL" + }, + "strictblockBack": { + "message": "Go back", + "description": "A button to go back to the previous webpage" + }, + "strictblockClose": { + "message": "Close this window", + "description": "A button to close the current tab" + }, + "strictblockDontWarn": { + "message": "Don't warn me again about this site", + "description": "Label for checkbox in document-blocked page" + }, + "strictblockProceed": { + "message": "Proceed", + "description": "A button to navigate to the blocked page" } } diff --git a/platform/mv3/extension/_locales/mk/messages.json b/platform/mv3/extension/_locales/mk/messages.json index 13a59807181ac..e50658321a032 100644 --- a/platform/mv3/extension/_locales/mk/messages.json +++ b/platform/mv3/extension/_locales/mk/messages.json @@ -231,8 +231,44 @@ "message": "Прикажи го бројот на блокирани барања на иконата во алатникот", "description": "Label for a checkbox in the options page" }, + "enableStrictBlockLabel": { + "message": "Enable strict blocking", + "description": "Label for a checkbox in the options page" + }, + "enableStrictBlockLegend": { + "message": "Navigation to potentially undesirable sites will be blocked, and you will be offered the option to proceed.", + "description": "Label for a checkbox in the options page" + }, "findListsPlaceholder": { "message": "Најди листи", "description": "Placeholder for the input field used to find lists" + }, + "strictblockTitle": { + "message": "Page blocked", + "description": "Webpage title for the strict-blocked page" + }, + "strictblockSentence1": { + "message": "uBO Lite has prevented the following page from loading:", + "description": "Sentence used in the strict-blocked page" + }, + "strictblockNoParamsPrompt": { + "message": "without parameters", + "description": "Label to be used for the parameter-less URL" + }, + "strictblockBack": { + "message": "Go back", + "description": "A button to go back to the previous webpage" + }, + "strictblockClose": { + "message": "Close this window", + "description": "A button to close the current tab" + }, + "strictblockDontWarn": { + "message": "Don't warn me again about this site", + "description": "Label for checkbox in document-blocked page" + }, + "strictblockProceed": { + "message": "Proceed", + "description": "A button to navigate to the blocked page" } } diff --git a/platform/mv3/extension/_locales/ml/messages.json b/platform/mv3/extension/_locales/ml/messages.json index 0862924b4fa66..7db51a3d39f8c 100644 --- a/platform/mv3/extension/_locales/ml/messages.json +++ b/platform/mv3/extension/_locales/ml/messages.json @@ -231,8 +231,44 @@ "message": "Show the number of blocked requests on the toolbar icon", "description": "Label for a checkbox in the options page" }, + "enableStrictBlockLabel": { + "message": "Enable strict blocking", + "description": "Label for a checkbox in the options page" + }, + "enableStrictBlockLegend": { + "message": "Navigation to potentially undesirable sites will be blocked, and you will be offered the option to proceed.", + "description": "Label for a checkbox in the options page" + }, "findListsPlaceholder": { "message": "Find lists", "description": "Placeholder for the input field used to find lists" + }, + "strictblockTitle": { + "message": "Page blocked", + "description": "Webpage title for the strict-blocked page" + }, + "strictblockSentence1": { + "message": "uBO Lite has prevented the following page from loading:", + "description": "Sentence used in the strict-blocked page" + }, + "strictblockNoParamsPrompt": { + "message": "without parameters", + "description": "Label to be used for the parameter-less URL" + }, + "strictblockBack": { + "message": "Go back", + "description": "A button to go back to the previous webpage" + }, + "strictblockClose": { + "message": "Close this window", + "description": "A button to close the current tab" + }, + "strictblockDontWarn": { + "message": "Don't warn me again about this site", + "description": "Label for checkbox in document-blocked page" + }, + "strictblockProceed": { + "message": "Proceed", + "description": "A button to navigate to the blocked page" } } diff --git a/platform/mv3/extension/_locales/mr/messages.json b/platform/mv3/extension/_locales/mr/messages.json index 001ac9feaf606..1c200f3653e00 100644 --- a/platform/mv3/extension/_locales/mr/messages.json +++ b/platform/mv3/extension/_locales/mr/messages.json @@ -231,8 +231,44 @@ "message": "Show the number of blocked requests on the toolbar icon", "description": "Label for a checkbox in the options page" }, + "enableStrictBlockLabel": { + "message": "Enable strict blocking", + "description": "Label for a checkbox in the options page" + }, + "enableStrictBlockLegend": { + "message": "Navigation to potentially undesirable sites will be blocked, and you will be offered the option to proceed.", + "description": "Label for a checkbox in the options page" + }, "findListsPlaceholder": { "message": "Find lists", "description": "Placeholder for the input field used to find lists" + }, + "strictblockTitle": { + "message": "Page blocked", + "description": "Webpage title for the strict-blocked page" + }, + "strictblockSentence1": { + "message": "uBO Lite has prevented the following page from loading:", + "description": "Sentence used in the strict-blocked page" + }, + "strictblockNoParamsPrompt": { + "message": "without parameters", + "description": "Label to be used for the parameter-less URL" + }, + "strictblockBack": { + "message": "Go back", + "description": "A button to go back to the previous webpage" + }, + "strictblockClose": { + "message": "Close this window", + "description": "A button to close the current tab" + }, + "strictblockDontWarn": { + "message": "Don't warn me again about this site", + "description": "Label for checkbox in document-blocked page" + }, + "strictblockProceed": { + "message": "Proceed", + "description": "A button to navigate to the blocked page" } } diff --git a/platform/mv3/extension/_locales/ms/messages.json b/platform/mv3/extension/_locales/ms/messages.json index 853c8ce344b05..784f11dec42ba 100644 --- a/platform/mv3/extension/_locales/ms/messages.json +++ b/platform/mv3/extension/_locales/ms/messages.json @@ -231,8 +231,44 @@ "message": "Tunjukkan bilangan permintaan yang disekat pada ikon bar alat", "description": "Label for a checkbox in the options page" }, + "enableStrictBlockLabel": { + "message": "Enable strict blocking", + "description": "Label for a checkbox in the options page" + }, + "enableStrictBlockLegend": { + "message": "Navigation to potentially undesirable sites will be blocked, and you will be offered the option to proceed.", + "description": "Label for a checkbox in the options page" + }, "findListsPlaceholder": { "message": "Find lists", "description": "Placeholder for the input field used to find lists" + }, + "strictblockTitle": { + "message": "Page blocked", + "description": "Webpage title for the strict-blocked page" + }, + "strictblockSentence1": { + "message": "uBO Lite has prevented the following page from loading:", + "description": "Sentence used in the strict-blocked page" + }, + "strictblockNoParamsPrompt": { + "message": "without parameters", + "description": "Label to be used for the parameter-less URL" + }, + "strictblockBack": { + "message": "Go back", + "description": "A button to go back to the previous webpage" + }, + "strictblockClose": { + "message": "Close this window", + "description": "A button to close the current tab" + }, + "strictblockDontWarn": { + "message": "Don't warn me again about this site", + "description": "Label for checkbox in document-blocked page" + }, + "strictblockProceed": { + "message": "Proceed", + "description": "A button to navigate to the blocked page" } } diff --git a/platform/mv3/extension/_locales/nb/messages.json b/platform/mv3/extension/_locales/nb/messages.json index a977e85326f83..2e553978e3d35 100644 --- a/platform/mv3/extension/_locales/nb/messages.json +++ b/platform/mv3/extension/_locales/nb/messages.json @@ -231,8 +231,44 @@ "message": "Vis antall blokkerte forespørsler på verktøylinjeikonet", "description": "Label for a checkbox in the options page" }, + "enableStrictBlockLabel": { + "message": "Enable strict blocking", + "description": "Label for a checkbox in the options page" + }, + "enableStrictBlockLegend": { + "message": "Navigation to potentially undesirable sites will be blocked, and you will be offered the option to proceed.", + "description": "Label for a checkbox in the options page" + }, "findListsPlaceholder": { "message": "Find lists", "description": "Placeholder for the input field used to find lists" + }, + "strictblockTitle": { + "message": "Page blocked", + "description": "Webpage title for the strict-blocked page" + }, + "strictblockSentence1": { + "message": "uBO Lite has prevented the following page from loading:", + "description": "Sentence used in the strict-blocked page" + }, + "strictblockNoParamsPrompt": { + "message": "without parameters", + "description": "Label to be used for the parameter-less URL" + }, + "strictblockBack": { + "message": "Go back", + "description": "A button to go back to the previous webpage" + }, + "strictblockClose": { + "message": "Close this window", + "description": "A button to close the current tab" + }, + "strictblockDontWarn": { + "message": "Don't warn me again about this site", + "description": "Label for checkbox in document-blocked page" + }, + "strictblockProceed": { + "message": "Proceed", + "description": "A button to navigate to the blocked page" } } diff --git a/platform/mv3/extension/_locales/nl/messages.json b/platform/mv3/extension/_locales/nl/messages.json index 04734466a7481..74e3608a41dfa 100644 --- a/platform/mv3/extension/_locales/nl/messages.json +++ b/platform/mv3/extension/_locales/nl/messages.json @@ -231,8 +231,44 @@ "message": "Het aantal geblokkeerde aanvragen op het werkbalkpictogram tonen", "description": "Label for a checkbox in the options page" }, + "enableStrictBlockLabel": { + "message": "Enable strict blocking", + "description": "Label for a checkbox in the options page" + }, + "enableStrictBlockLegend": { + "message": "Navigation to potentially undesirable sites will be blocked, and you will be offered the option to proceed.", + "description": "Label for a checkbox in the options page" + }, "findListsPlaceholder": { "message": "Lijsten zoeken", "description": "Placeholder for the input field used to find lists" + }, + "strictblockTitle": { + "message": "Page blocked", + "description": "Webpage title for the strict-blocked page" + }, + "strictblockSentence1": { + "message": "uBO Lite has prevented the following page from loading:", + "description": "Sentence used in the strict-blocked page" + }, + "strictblockNoParamsPrompt": { + "message": "without parameters", + "description": "Label to be used for the parameter-less URL" + }, + "strictblockBack": { + "message": "Go back", + "description": "A button to go back to the previous webpage" + }, + "strictblockClose": { + "message": "Close this window", + "description": "A button to close the current tab" + }, + "strictblockDontWarn": { + "message": "Don't warn me again about this site", + "description": "Label for checkbox in document-blocked page" + }, + "strictblockProceed": { + "message": "Proceed", + "description": "A button to navigate to the blocked page" } } diff --git a/platform/mv3/extension/_locales/oc/messages.json b/platform/mv3/extension/_locales/oc/messages.json index 001ac9feaf606..1c200f3653e00 100644 --- a/platform/mv3/extension/_locales/oc/messages.json +++ b/platform/mv3/extension/_locales/oc/messages.json @@ -231,8 +231,44 @@ "message": "Show the number of blocked requests on the toolbar icon", "description": "Label for a checkbox in the options page" }, + "enableStrictBlockLabel": { + "message": "Enable strict blocking", + "description": "Label for a checkbox in the options page" + }, + "enableStrictBlockLegend": { + "message": "Navigation to potentially undesirable sites will be blocked, and you will be offered the option to proceed.", + "description": "Label for a checkbox in the options page" + }, "findListsPlaceholder": { "message": "Find lists", "description": "Placeholder for the input field used to find lists" + }, + "strictblockTitle": { + "message": "Page blocked", + "description": "Webpage title for the strict-blocked page" + }, + "strictblockSentence1": { + "message": "uBO Lite has prevented the following page from loading:", + "description": "Sentence used in the strict-blocked page" + }, + "strictblockNoParamsPrompt": { + "message": "without parameters", + "description": "Label to be used for the parameter-less URL" + }, + "strictblockBack": { + "message": "Go back", + "description": "A button to go back to the previous webpage" + }, + "strictblockClose": { + "message": "Close this window", + "description": "A button to close the current tab" + }, + "strictblockDontWarn": { + "message": "Don't warn me again about this site", + "description": "Label for checkbox in document-blocked page" + }, + "strictblockProceed": { + "message": "Proceed", + "description": "A button to navigate to the blocked page" } } diff --git a/platform/mv3/extension/_locales/pa/messages.json b/platform/mv3/extension/_locales/pa/messages.json index f8ce344bc6e10..6bf60fe983285 100644 --- a/platform/mv3/extension/_locales/pa/messages.json +++ b/platform/mv3/extension/_locales/pa/messages.json @@ -231,8 +231,44 @@ "message": "Show the number of blocked requests on the toolbar icon", "description": "Label for a checkbox in the options page" }, + "enableStrictBlockLabel": { + "message": "Enable strict blocking", + "description": "Label for a checkbox in the options page" + }, + "enableStrictBlockLegend": { + "message": "Navigation to potentially undesirable sites will be blocked, and you will be offered the option to proceed.", + "description": "Label for a checkbox in the options page" + }, "findListsPlaceholder": { "message": "Find lists", "description": "Placeholder for the input field used to find lists" + }, + "strictblockTitle": { + "message": "Page blocked", + "description": "Webpage title for the strict-blocked page" + }, + "strictblockSentence1": { + "message": "uBO Lite has prevented the following page from loading:", + "description": "Sentence used in the strict-blocked page" + }, + "strictblockNoParamsPrompt": { + "message": "without parameters", + "description": "Label to be used for the parameter-less URL" + }, + "strictblockBack": { + "message": "Go back", + "description": "A button to go back to the previous webpage" + }, + "strictblockClose": { + "message": "Close this window", + "description": "A button to close the current tab" + }, + "strictblockDontWarn": { + "message": "Don't warn me again about this site", + "description": "Label for checkbox in document-blocked page" + }, + "strictblockProceed": { + "message": "Proceed", + "description": "A button to navigate to the blocked page" } } diff --git a/platform/mv3/extension/_locales/pl/messages.json b/platform/mv3/extension/_locales/pl/messages.json index 257d2f0f3b680..791eb14d964f4 100644 --- a/platform/mv3/extension/_locales/pl/messages.json +++ b/platform/mv3/extension/_locales/pl/messages.json @@ -231,8 +231,44 @@ "message": "Wyświetlaj liczbę zablokowanych żądań na ikonie paska narzędzi", "description": "Label for a checkbox in the options page" }, + "enableStrictBlockLabel": { + "message": "Enable strict blocking", + "description": "Label for a checkbox in the options page" + }, + "enableStrictBlockLegend": { + "message": "Navigation to potentially undesirable sites will be blocked, and you will be offered the option to proceed.", + "description": "Label for a checkbox in the options page" + }, "findListsPlaceholder": { "message": "Znajdź listy", "description": "Placeholder for the input field used to find lists" + }, + "strictblockTitle": { + "message": "Page blocked", + "description": "Webpage title for the strict-blocked page" + }, + "strictblockSentence1": { + "message": "uBO Lite has prevented the following page from loading:", + "description": "Sentence used in the strict-blocked page" + }, + "strictblockNoParamsPrompt": { + "message": "without parameters", + "description": "Label to be used for the parameter-less URL" + }, + "strictblockBack": { + "message": "Go back", + "description": "A button to go back to the previous webpage" + }, + "strictblockClose": { + "message": "Close this window", + "description": "A button to close the current tab" + }, + "strictblockDontWarn": { + "message": "Don't warn me again about this site", + "description": "Label for checkbox in document-blocked page" + }, + "strictblockProceed": { + "message": "Proceed", + "description": "A button to navigate to the blocked page" } } diff --git a/platform/mv3/extension/_locales/pt_BR/messages.json b/platform/mv3/extension/_locales/pt_BR/messages.json index e539229d5c972..429d7d5d94a9f 100644 --- a/platform/mv3/extension/_locales/pt_BR/messages.json +++ b/platform/mv3/extension/_locales/pt_BR/messages.json @@ -231,8 +231,44 @@ "message": "Mostrar o número de requisições bloqueadas no ícone da barra de ferramentas", "description": "Label for a checkbox in the options page" }, + "enableStrictBlockLabel": { + "message": "Enable strict blocking", + "description": "Label for a checkbox in the options page" + }, + "enableStrictBlockLegend": { + "message": "Navigation to potentially undesirable sites will be blocked, and you will be offered the option to proceed.", + "description": "Label for a checkbox in the options page" + }, "findListsPlaceholder": { "message": "Achar listas", "description": "Placeholder for the input field used to find lists" + }, + "strictblockTitle": { + "message": "Page blocked", + "description": "Webpage title for the strict-blocked page" + }, + "strictblockSentence1": { + "message": "uBO Lite has prevented the following page from loading:", + "description": "Sentence used in the strict-blocked page" + }, + "strictblockNoParamsPrompt": { + "message": "without parameters", + "description": "Label to be used for the parameter-less URL" + }, + "strictblockBack": { + "message": "Go back", + "description": "A button to go back to the previous webpage" + }, + "strictblockClose": { + "message": "Close this window", + "description": "A button to close the current tab" + }, + "strictblockDontWarn": { + "message": "Don't warn me again about this site", + "description": "Label for checkbox in document-blocked page" + }, + "strictblockProceed": { + "message": "Proceed", + "description": "A button to navigate to the blocked page" } } diff --git a/platform/mv3/extension/_locales/pt_PT/messages.json b/platform/mv3/extension/_locales/pt_PT/messages.json index be2ee3752f79c..22d1eaf1e84ea 100644 --- a/platform/mv3/extension/_locales/pt_PT/messages.json +++ b/platform/mv3/extension/_locales/pt_PT/messages.json @@ -231,8 +231,44 @@ "message": "Mostra o número de pedidos bloqueados no ícone da barra de ferramentas", "description": "Label for a checkbox in the options page" }, + "enableStrictBlockLabel": { + "message": "Enable strict blocking", + "description": "Label for a checkbox in the options page" + }, + "enableStrictBlockLegend": { + "message": "Navigation to potentially undesirable sites will be blocked, and you will be offered the option to proceed.", + "description": "Label for a checkbox in the options page" + }, "findListsPlaceholder": { "message": "Encontrar listas", "description": "Placeholder for the input field used to find lists" + }, + "strictblockTitle": { + "message": "Page blocked", + "description": "Webpage title for the strict-blocked page" + }, + "strictblockSentence1": { + "message": "uBO Lite has prevented the following page from loading:", + "description": "Sentence used in the strict-blocked page" + }, + "strictblockNoParamsPrompt": { + "message": "without parameters", + "description": "Label to be used for the parameter-less URL" + }, + "strictblockBack": { + "message": "Go back", + "description": "A button to go back to the previous webpage" + }, + "strictblockClose": { + "message": "Close this window", + "description": "A button to close the current tab" + }, + "strictblockDontWarn": { + "message": "Don't warn me again about this site", + "description": "Label for checkbox in document-blocked page" + }, + "strictblockProceed": { + "message": "Proceed", + "description": "A button to navigate to the blocked page" } } diff --git a/platform/mv3/extension/_locales/ro/messages.json b/platform/mv3/extension/_locales/ro/messages.json index 0b6384fb5a384..6d496312d84ea 100644 --- a/platform/mv3/extension/_locales/ro/messages.json +++ b/platform/mv3/extension/_locales/ro/messages.json @@ -231,8 +231,44 @@ "message": "Arată numărul cererilor blocate pe simbolul extensiei", "description": "Label for a checkbox in the options page" }, + "enableStrictBlockLabel": { + "message": "Enable strict blocking", + "description": "Label for a checkbox in the options page" + }, + "enableStrictBlockLegend": { + "message": "Navigation to potentially undesirable sites will be blocked, and you will be offered the option to proceed.", + "description": "Label for a checkbox in the options page" + }, "findListsPlaceholder": { "message": "Find lists", "description": "Placeholder for the input field used to find lists" + }, + "strictblockTitle": { + "message": "Page blocked", + "description": "Webpage title for the strict-blocked page" + }, + "strictblockSentence1": { + "message": "uBO Lite has prevented the following page from loading:", + "description": "Sentence used in the strict-blocked page" + }, + "strictblockNoParamsPrompt": { + "message": "without parameters", + "description": "Label to be used for the parameter-less URL" + }, + "strictblockBack": { + "message": "Go back", + "description": "A button to go back to the previous webpage" + }, + "strictblockClose": { + "message": "Close this window", + "description": "A button to close the current tab" + }, + "strictblockDontWarn": { + "message": "Don't warn me again about this site", + "description": "Label for checkbox in document-blocked page" + }, + "strictblockProceed": { + "message": "Proceed", + "description": "A button to navigate to the blocked page" } } diff --git a/platform/mv3/extension/_locales/ru/messages.json b/platform/mv3/extension/_locales/ru/messages.json index b2e7be8d3185e..0e84993d58102 100644 --- a/platform/mv3/extension/_locales/ru/messages.json +++ b/platform/mv3/extension/_locales/ru/messages.json @@ -231,8 +231,44 @@ "message": "Показывать количество заблокированных запросов на иконке в панели инструментов", "description": "Label for a checkbox in the options page" }, + "enableStrictBlockLabel": { + "message": "Enable strict blocking", + "description": "Label for a checkbox in the options page" + }, + "enableStrictBlockLegend": { + "message": "Navigation to potentially undesirable sites will be blocked, and you will be offered the option to proceed.", + "description": "Label for a checkbox in the options page" + }, "findListsPlaceholder": { "message": "Найти списки", "description": "Placeholder for the input field used to find lists" + }, + "strictblockTitle": { + "message": "Page blocked", + "description": "Webpage title for the strict-blocked page" + }, + "strictblockSentence1": { + "message": "uBO Lite has prevented the following page from loading:", + "description": "Sentence used in the strict-blocked page" + }, + "strictblockNoParamsPrompt": { + "message": "without parameters", + "description": "Label to be used for the parameter-less URL" + }, + "strictblockBack": { + "message": "Go back", + "description": "A button to go back to the previous webpage" + }, + "strictblockClose": { + "message": "Close this window", + "description": "A button to close the current tab" + }, + "strictblockDontWarn": { + "message": "Don't warn me again about this site", + "description": "Label for checkbox in document-blocked page" + }, + "strictblockProceed": { + "message": "Proceed", + "description": "A button to navigate to the blocked page" } } diff --git a/platform/mv3/extension/_locales/si/messages.json b/platform/mv3/extension/_locales/si/messages.json index cf5b4f4413db1..f8c573375f6b1 100644 --- a/platform/mv3/extension/_locales/si/messages.json +++ b/platform/mv3/extension/_locales/si/messages.json @@ -231,8 +231,44 @@ "message": "මෙවලම් තීරු නිරූපකයේ අවහිර කළ ඉල්ලීම් ගණන පෙන්වන්න", "description": "Label for a checkbox in the options page" }, + "enableStrictBlockLabel": { + "message": "Enable strict blocking", + "description": "Label for a checkbox in the options page" + }, + "enableStrictBlockLegend": { + "message": "Navigation to potentially undesirable sites will be blocked, and you will be offered the option to proceed.", + "description": "Label for a checkbox in the options page" + }, "findListsPlaceholder": { "message": "Find lists", "description": "Placeholder for the input field used to find lists" + }, + "strictblockTitle": { + "message": "Page blocked", + "description": "Webpage title for the strict-blocked page" + }, + "strictblockSentence1": { + "message": "uBO Lite has prevented the following page from loading:", + "description": "Sentence used in the strict-blocked page" + }, + "strictblockNoParamsPrompt": { + "message": "without parameters", + "description": "Label to be used for the parameter-less URL" + }, + "strictblockBack": { + "message": "Go back", + "description": "A button to go back to the previous webpage" + }, + "strictblockClose": { + "message": "Close this window", + "description": "A button to close the current tab" + }, + "strictblockDontWarn": { + "message": "Don't warn me again about this site", + "description": "Label for checkbox in document-blocked page" + }, + "strictblockProceed": { + "message": "Proceed", + "description": "A button to navigate to the blocked page" } } diff --git a/platform/mv3/extension/_locales/sk/messages.json b/platform/mv3/extension/_locales/sk/messages.json index b31acd2fdc324..2f20bb2dca3a7 100644 --- a/platform/mv3/extension/_locales/sk/messages.json +++ b/platform/mv3/extension/_locales/sk/messages.json @@ -231,8 +231,44 @@ "message": "Zobraziť počet zablokovaných požiadaviek na ikone rozšírenia", "description": "Label for a checkbox in the options page" }, + "enableStrictBlockLabel": { + "message": "Enable strict blocking", + "description": "Label for a checkbox in the options page" + }, + "enableStrictBlockLegend": { + "message": "Navigation to potentially undesirable sites will be blocked, and you will be offered the option to proceed.", + "description": "Label for a checkbox in the options page" + }, "findListsPlaceholder": { "message": "Nájsť zoznamy", "description": "Placeholder for the input field used to find lists" + }, + "strictblockTitle": { + "message": "Page blocked", + "description": "Webpage title for the strict-blocked page" + }, + "strictblockSentence1": { + "message": "uBO Lite has prevented the following page from loading:", + "description": "Sentence used in the strict-blocked page" + }, + "strictblockNoParamsPrompt": { + "message": "without parameters", + "description": "Label to be used for the parameter-less URL" + }, + "strictblockBack": { + "message": "Go back", + "description": "A button to go back to the previous webpage" + }, + "strictblockClose": { + "message": "Close this window", + "description": "A button to close the current tab" + }, + "strictblockDontWarn": { + "message": "Don't warn me again about this site", + "description": "Label for checkbox in document-blocked page" + }, + "strictblockProceed": { + "message": "Proceed", + "description": "A button to navigate to the blocked page" } } diff --git a/platform/mv3/extension/_locales/sl/messages.json b/platform/mv3/extension/_locales/sl/messages.json index 001ac9feaf606..1c200f3653e00 100644 --- a/platform/mv3/extension/_locales/sl/messages.json +++ b/platform/mv3/extension/_locales/sl/messages.json @@ -231,8 +231,44 @@ "message": "Show the number of blocked requests on the toolbar icon", "description": "Label for a checkbox in the options page" }, + "enableStrictBlockLabel": { + "message": "Enable strict blocking", + "description": "Label for a checkbox in the options page" + }, + "enableStrictBlockLegend": { + "message": "Navigation to potentially undesirable sites will be blocked, and you will be offered the option to proceed.", + "description": "Label for a checkbox in the options page" + }, "findListsPlaceholder": { "message": "Find lists", "description": "Placeholder for the input field used to find lists" + }, + "strictblockTitle": { + "message": "Page blocked", + "description": "Webpage title for the strict-blocked page" + }, + "strictblockSentence1": { + "message": "uBO Lite has prevented the following page from loading:", + "description": "Sentence used in the strict-blocked page" + }, + "strictblockNoParamsPrompt": { + "message": "without parameters", + "description": "Label to be used for the parameter-less URL" + }, + "strictblockBack": { + "message": "Go back", + "description": "A button to go back to the previous webpage" + }, + "strictblockClose": { + "message": "Close this window", + "description": "A button to close the current tab" + }, + "strictblockDontWarn": { + "message": "Don't warn me again about this site", + "description": "Label for checkbox in document-blocked page" + }, + "strictblockProceed": { + "message": "Proceed", + "description": "A button to navigate to the blocked page" } } diff --git a/platform/mv3/extension/_locales/so/messages.json b/platform/mv3/extension/_locales/so/messages.json index 001ac9feaf606..1c200f3653e00 100644 --- a/platform/mv3/extension/_locales/so/messages.json +++ b/platform/mv3/extension/_locales/so/messages.json @@ -231,8 +231,44 @@ "message": "Show the number of blocked requests on the toolbar icon", "description": "Label for a checkbox in the options page" }, + "enableStrictBlockLabel": { + "message": "Enable strict blocking", + "description": "Label for a checkbox in the options page" + }, + "enableStrictBlockLegend": { + "message": "Navigation to potentially undesirable sites will be blocked, and you will be offered the option to proceed.", + "description": "Label for a checkbox in the options page" + }, "findListsPlaceholder": { "message": "Find lists", "description": "Placeholder for the input field used to find lists" + }, + "strictblockTitle": { + "message": "Page blocked", + "description": "Webpage title for the strict-blocked page" + }, + "strictblockSentence1": { + "message": "uBO Lite has prevented the following page from loading:", + "description": "Sentence used in the strict-blocked page" + }, + "strictblockNoParamsPrompt": { + "message": "without parameters", + "description": "Label to be used for the parameter-less URL" + }, + "strictblockBack": { + "message": "Go back", + "description": "A button to go back to the previous webpage" + }, + "strictblockClose": { + "message": "Close this window", + "description": "A button to close the current tab" + }, + "strictblockDontWarn": { + "message": "Don't warn me again about this site", + "description": "Label for checkbox in document-blocked page" + }, + "strictblockProceed": { + "message": "Proceed", + "description": "A button to navigate to the blocked page" } } diff --git a/platform/mv3/extension/_locales/sq/messages.json b/platform/mv3/extension/_locales/sq/messages.json index 9aa0b63e6b238..2f161843a43b9 100644 --- a/platform/mv3/extension/_locales/sq/messages.json +++ b/platform/mv3/extension/_locales/sq/messages.json @@ -231,8 +231,44 @@ "message": "Shfaq numrin e kërkesave të bllokuara në ikonën e instrumenteve", "description": "Label for a checkbox in the options page" }, + "enableStrictBlockLabel": { + "message": "Enable strict blocking", + "description": "Label for a checkbox in the options page" + }, + "enableStrictBlockLegend": { + "message": "Navigation to potentially undesirable sites will be blocked, and you will be offered the option to proceed.", + "description": "Label for a checkbox in the options page" + }, "findListsPlaceholder": { "message": "Gjej listat", "description": "Placeholder for the input field used to find lists" + }, + "strictblockTitle": { + "message": "Page blocked", + "description": "Webpage title for the strict-blocked page" + }, + "strictblockSentence1": { + "message": "uBO Lite has prevented the following page from loading:", + "description": "Sentence used in the strict-blocked page" + }, + "strictblockNoParamsPrompt": { + "message": "without parameters", + "description": "Label to be used for the parameter-less URL" + }, + "strictblockBack": { + "message": "Go back", + "description": "A button to go back to the previous webpage" + }, + "strictblockClose": { + "message": "Close this window", + "description": "A button to close the current tab" + }, + "strictblockDontWarn": { + "message": "Don't warn me again about this site", + "description": "Label for checkbox in document-blocked page" + }, + "strictblockProceed": { + "message": "Proceed", + "description": "A button to navigate to the blocked page" } } diff --git a/platform/mv3/extension/_locales/sr/messages.json b/platform/mv3/extension/_locales/sr/messages.json index 20c22b359194d..dd4859978c912 100644 --- a/platform/mv3/extension/_locales/sr/messages.json +++ b/platform/mv3/extension/_locales/sr/messages.json @@ -231,8 +231,44 @@ "message": "Прикажи број блокираних захтева на иконици на траци алата", "description": "Label for a checkbox in the options page" }, + "enableStrictBlockLabel": { + "message": "Enable strict blocking", + "description": "Label for a checkbox in the options page" + }, + "enableStrictBlockLegend": { + "message": "Navigation to potentially undesirable sites will be blocked, and you will be offered the option to proceed.", + "description": "Label for a checkbox in the options page" + }, "findListsPlaceholder": { "message": "Пронађи листе", "description": "Placeholder for the input field used to find lists" + }, + "strictblockTitle": { + "message": "Page blocked", + "description": "Webpage title for the strict-blocked page" + }, + "strictblockSentence1": { + "message": "uBO Lite has prevented the following page from loading:", + "description": "Sentence used in the strict-blocked page" + }, + "strictblockNoParamsPrompt": { + "message": "without parameters", + "description": "Label to be used for the parameter-less URL" + }, + "strictblockBack": { + "message": "Go back", + "description": "A button to go back to the previous webpage" + }, + "strictblockClose": { + "message": "Close this window", + "description": "A button to close the current tab" + }, + "strictblockDontWarn": { + "message": "Don't warn me again about this site", + "description": "Label for checkbox in document-blocked page" + }, + "strictblockProceed": { + "message": "Proceed", + "description": "A button to navigate to the blocked page" } } diff --git a/platform/mv3/extension/_locales/sv/messages.json b/platform/mv3/extension/_locales/sv/messages.json index 857cfc707f316..91715d6bac039 100644 --- a/platform/mv3/extension/_locales/sv/messages.json +++ b/platform/mv3/extension/_locales/sv/messages.json @@ -231,8 +231,44 @@ "message": "Visa antalet blockerade förfrågningar på verktygsfältsikonen", "description": "Label for a checkbox in the options page" }, + "enableStrictBlockLabel": { + "message": "Enable strict blocking", + "description": "Label for a checkbox in the options page" + }, + "enableStrictBlockLegend": { + "message": "Navigation to potentially undesirable sites will be blocked, and you will be offered the option to proceed.", + "description": "Label for a checkbox in the options page" + }, "findListsPlaceholder": { "message": "Hitta listor", "description": "Placeholder for the input field used to find lists" + }, + "strictblockTitle": { + "message": "Page blocked", + "description": "Webpage title for the strict-blocked page" + }, + "strictblockSentence1": { + "message": "uBO Lite has prevented the following page from loading:", + "description": "Sentence used in the strict-blocked page" + }, + "strictblockNoParamsPrompt": { + "message": "without parameters", + "description": "Label to be used for the parameter-less URL" + }, + "strictblockBack": { + "message": "Go back", + "description": "A button to go back to the previous webpage" + }, + "strictblockClose": { + "message": "Close this window", + "description": "A button to close the current tab" + }, + "strictblockDontWarn": { + "message": "Don't warn me again about this site", + "description": "Label for checkbox in document-blocked page" + }, + "strictblockProceed": { + "message": "Proceed", + "description": "A button to navigate to the blocked page" } } diff --git a/platform/mv3/extension/_locales/sw/messages.json b/platform/mv3/extension/_locales/sw/messages.json index 001ac9feaf606..1c200f3653e00 100644 --- a/platform/mv3/extension/_locales/sw/messages.json +++ b/platform/mv3/extension/_locales/sw/messages.json @@ -231,8 +231,44 @@ "message": "Show the number of blocked requests on the toolbar icon", "description": "Label for a checkbox in the options page" }, + "enableStrictBlockLabel": { + "message": "Enable strict blocking", + "description": "Label for a checkbox in the options page" + }, + "enableStrictBlockLegend": { + "message": "Navigation to potentially undesirable sites will be blocked, and you will be offered the option to proceed.", + "description": "Label for a checkbox in the options page" + }, "findListsPlaceholder": { "message": "Find lists", "description": "Placeholder for the input field used to find lists" + }, + "strictblockTitle": { + "message": "Page blocked", + "description": "Webpage title for the strict-blocked page" + }, + "strictblockSentence1": { + "message": "uBO Lite has prevented the following page from loading:", + "description": "Sentence used in the strict-blocked page" + }, + "strictblockNoParamsPrompt": { + "message": "without parameters", + "description": "Label to be used for the parameter-less URL" + }, + "strictblockBack": { + "message": "Go back", + "description": "A button to go back to the previous webpage" + }, + "strictblockClose": { + "message": "Close this window", + "description": "A button to close the current tab" + }, + "strictblockDontWarn": { + "message": "Don't warn me again about this site", + "description": "Label for checkbox in document-blocked page" + }, + "strictblockProceed": { + "message": "Proceed", + "description": "A button to navigate to the blocked page" } } diff --git a/platform/mv3/extension/_locales/ta/messages.json b/platform/mv3/extension/_locales/ta/messages.json index 001ac9feaf606..1c200f3653e00 100644 --- a/platform/mv3/extension/_locales/ta/messages.json +++ b/platform/mv3/extension/_locales/ta/messages.json @@ -231,8 +231,44 @@ "message": "Show the number of blocked requests on the toolbar icon", "description": "Label for a checkbox in the options page" }, + "enableStrictBlockLabel": { + "message": "Enable strict blocking", + "description": "Label for a checkbox in the options page" + }, + "enableStrictBlockLegend": { + "message": "Navigation to potentially undesirable sites will be blocked, and you will be offered the option to proceed.", + "description": "Label for a checkbox in the options page" + }, "findListsPlaceholder": { "message": "Find lists", "description": "Placeholder for the input field used to find lists" + }, + "strictblockTitle": { + "message": "Page blocked", + "description": "Webpage title for the strict-blocked page" + }, + "strictblockSentence1": { + "message": "uBO Lite has prevented the following page from loading:", + "description": "Sentence used in the strict-blocked page" + }, + "strictblockNoParamsPrompt": { + "message": "without parameters", + "description": "Label to be used for the parameter-less URL" + }, + "strictblockBack": { + "message": "Go back", + "description": "A button to go back to the previous webpage" + }, + "strictblockClose": { + "message": "Close this window", + "description": "A button to close the current tab" + }, + "strictblockDontWarn": { + "message": "Don't warn me again about this site", + "description": "Label for checkbox in document-blocked page" + }, + "strictblockProceed": { + "message": "Proceed", + "description": "A button to navigate to the blocked page" } } diff --git a/platform/mv3/extension/_locales/te/messages.json b/platform/mv3/extension/_locales/te/messages.json index 7a1cb67fa6a3b..c1933035602e3 100644 --- a/platform/mv3/extension/_locales/te/messages.json +++ b/platform/mv3/extension/_locales/te/messages.json @@ -231,8 +231,44 @@ "message": "Show the number of blocked requests on the toolbar icon", "description": "Label for a checkbox in the options page" }, + "enableStrictBlockLabel": { + "message": "Enable strict blocking", + "description": "Label for a checkbox in the options page" + }, + "enableStrictBlockLegend": { + "message": "Navigation to potentially undesirable sites will be blocked, and you will be offered the option to proceed.", + "description": "Label for a checkbox in the options page" + }, "findListsPlaceholder": { "message": "Find lists", "description": "Placeholder for the input field used to find lists" + }, + "strictblockTitle": { + "message": "Page blocked", + "description": "Webpage title for the strict-blocked page" + }, + "strictblockSentence1": { + "message": "uBO Lite has prevented the following page from loading:", + "description": "Sentence used in the strict-blocked page" + }, + "strictblockNoParamsPrompt": { + "message": "without parameters", + "description": "Label to be used for the parameter-less URL" + }, + "strictblockBack": { + "message": "Go back", + "description": "A button to go back to the previous webpage" + }, + "strictblockClose": { + "message": "Close this window", + "description": "A button to close the current tab" + }, + "strictblockDontWarn": { + "message": "Don't warn me again about this site", + "description": "Label for checkbox in document-blocked page" + }, + "strictblockProceed": { + "message": "Proceed", + "description": "A button to navigate to the blocked page" } } diff --git a/platform/mv3/extension/_locales/th/messages.json b/platform/mv3/extension/_locales/th/messages.json index dd77461de48e0..8ae7f62db2588 100644 --- a/platform/mv3/extension/_locales/th/messages.json +++ b/platform/mv3/extension/_locales/th/messages.json @@ -231,8 +231,44 @@ "message": "แสดงจำนวนคำขอที่ถูกปิดกั้นบนไอคอนแถบเครื่องมือ", "description": "Label for a checkbox in the options page" }, + "enableStrictBlockLabel": { + "message": "Enable strict blocking", + "description": "Label for a checkbox in the options page" + }, + "enableStrictBlockLegend": { + "message": "Navigation to potentially undesirable sites will be blocked, and you will be offered the option to proceed.", + "description": "Label for a checkbox in the options page" + }, "findListsPlaceholder": { "message": "Find lists", "description": "Placeholder for the input field used to find lists" + }, + "strictblockTitle": { + "message": "Page blocked", + "description": "Webpage title for the strict-blocked page" + }, + "strictblockSentence1": { + "message": "uBO Lite has prevented the following page from loading:", + "description": "Sentence used in the strict-blocked page" + }, + "strictblockNoParamsPrompt": { + "message": "without parameters", + "description": "Label to be used for the parameter-less URL" + }, + "strictblockBack": { + "message": "Go back", + "description": "A button to go back to the previous webpage" + }, + "strictblockClose": { + "message": "Close this window", + "description": "A button to close the current tab" + }, + "strictblockDontWarn": { + "message": "Don't warn me again about this site", + "description": "Label for checkbox in document-blocked page" + }, + "strictblockProceed": { + "message": "Proceed", + "description": "A button to navigate to the blocked page" } } diff --git a/platform/mv3/extension/_locales/tr/messages.json b/platform/mv3/extension/_locales/tr/messages.json index 511312a66afdf..5054aca0375b8 100644 --- a/platform/mv3/extension/_locales/tr/messages.json +++ b/platform/mv3/extension/_locales/tr/messages.json @@ -231,8 +231,44 @@ "message": "Engellenen isteklerin sayısını araç çubuğu simgesinde göster", "description": "Label for a checkbox in the options page" }, + "enableStrictBlockLabel": { + "message": "Enable strict blocking", + "description": "Label for a checkbox in the options page" + }, + "enableStrictBlockLegend": { + "message": "Navigation to potentially undesirable sites will be blocked, and you will be offered the option to proceed.", + "description": "Label for a checkbox in the options page" + }, "findListsPlaceholder": { "message": "Liste bul", "description": "Placeholder for the input field used to find lists" + }, + "strictblockTitle": { + "message": "Page blocked", + "description": "Webpage title for the strict-blocked page" + }, + "strictblockSentence1": { + "message": "uBO Lite has prevented the following page from loading:", + "description": "Sentence used in the strict-blocked page" + }, + "strictblockNoParamsPrompt": { + "message": "without parameters", + "description": "Label to be used for the parameter-less URL" + }, + "strictblockBack": { + "message": "Go back", + "description": "A button to go back to the previous webpage" + }, + "strictblockClose": { + "message": "Close this window", + "description": "A button to close the current tab" + }, + "strictblockDontWarn": { + "message": "Don't warn me again about this site", + "description": "Label for checkbox in document-blocked page" + }, + "strictblockProceed": { + "message": "Proceed", + "description": "A button to navigate to the blocked page" } } diff --git a/platform/mv3/extension/_locales/uk/messages.json b/platform/mv3/extension/_locales/uk/messages.json index ea7338db5f6f7..98cac736f3004 100644 --- a/platform/mv3/extension/_locales/uk/messages.json +++ b/platform/mv3/extension/_locales/uk/messages.json @@ -231,8 +231,44 @@ "message": "Показувати кількість заблокованих запитів на піктограмі панелі інструментів", "description": "Label for a checkbox in the options page" }, + "enableStrictBlockLabel": { + "message": "Enable strict blocking", + "description": "Label for a checkbox in the options page" + }, + "enableStrictBlockLegend": { + "message": "Navigation to potentially undesirable sites will be blocked, and you will be offered the option to proceed.", + "description": "Label for a checkbox in the options page" + }, "findListsPlaceholder": { "message": "Знайти списки", "description": "Placeholder for the input field used to find lists" + }, + "strictblockTitle": { + "message": "Page blocked", + "description": "Webpage title for the strict-blocked page" + }, + "strictblockSentence1": { + "message": "uBO Lite has prevented the following page from loading:", + "description": "Sentence used in the strict-blocked page" + }, + "strictblockNoParamsPrompt": { + "message": "without parameters", + "description": "Label to be used for the parameter-less URL" + }, + "strictblockBack": { + "message": "Go back", + "description": "A button to go back to the previous webpage" + }, + "strictblockClose": { + "message": "Close this window", + "description": "A button to close the current tab" + }, + "strictblockDontWarn": { + "message": "Don't warn me again about this site", + "description": "Label for checkbox in document-blocked page" + }, + "strictblockProceed": { + "message": "Proceed", + "description": "A button to navigate to the blocked page" } } diff --git a/platform/mv3/extension/_locales/ur/messages.json b/platform/mv3/extension/_locales/ur/messages.json index 0af9983ebe907..3c4d796af358a 100644 --- a/platform/mv3/extension/_locales/ur/messages.json +++ b/platform/mv3/extension/_locales/ur/messages.json @@ -231,8 +231,44 @@ "message": "ٹول بار کے آئیکن پر بلاک شدہ درخواستوں کی تعداد دکھائیں۔", "description": "Label for a checkbox in the options page" }, + "enableStrictBlockLabel": { + "message": "Enable strict blocking", + "description": "Label for a checkbox in the options page" + }, + "enableStrictBlockLegend": { + "message": "Navigation to potentially undesirable sites will be blocked, and you will be offered the option to proceed.", + "description": "Label for a checkbox in the options page" + }, "findListsPlaceholder": { "message": "Find lists", "description": "Placeholder for the input field used to find lists" + }, + "strictblockTitle": { + "message": "Page blocked", + "description": "Webpage title for the strict-blocked page" + }, + "strictblockSentence1": { + "message": "uBO Lite has prevented the following page from loading:", + "description": "Sentence used in the strict-blocked page" + }, + "strictblockNoParamsPrompt": { + "message": "without parameters", + "description": "Label to be used for the parameter-less URL" + }, + "strictblockBack": { + "message": "Go back", + "description": "A button to go back to the previous webpage" + }, + "strictblockClose": { + "message": "Close this window", + "description": "A button to close the current tab" + }, + "strictblockDontWarn": { + "message": "Don't warn me again about this site", + "description": "Label for checkbox in document-blocked page" + }, + "strictblockProceed": { + "message": "Proceed", + "description": "A button to navigate to the blocked page" } } diff --git a/platform/mv3/extension/_locales/vi/messages.json b/platform/mv3/extension/_locales/vi/messages.json index 5826504b1cea4..2e1c09546aee1 100644 --- a/platform/mv3/extension/_locales/vi/messages.json +++ b/platform/mv3/extension/_locales/vi/messages.json @@ -231,8 +231,44 @@ "message": "Hiện số lượng yêu cầu bị chặn trên biểu tượng thanh công cụ", "description": "Label for a checkbox in the options page" }, + "enableStrictBlockLabel": { + "message": "Enable strict blocking", + "description": "Label for a checkbox in the options page" + }, + "enableStrictBlockLegend": { + "message": "Navigation to potentially undesirable sites will be blocked, and you will be offered the option to proceed.", + "description": "Label for a checkbox in the options page" + }, "findListsPlaceholder": { "message": "Find lists", "description": "Placeholder for the input field used to find lists" + }, + "strictblockTitle": { + "message": "Page blocked", + "description": "Webpage title for the strict-blocked page" + }, + "strictblockSentence1": { + "message": "uBO Lite has prevented the following page from loading:", + "description": "Sentence used in the strict-blocked page" + }, + "strictblockNoParamsPrompt": { + "message": "without parameters", + "description": "Label to be used for the parameter-less URL" + }, + "strictblockBack": { + "message": "Go back", + "description": "A button to go back to the previous webpage" + }, + "strictblockClose": { + "message": "Close this window", + "description": "A button to close the current tab" + }, + "strictblockDontWarn": { + "message": "Don't warn me again about this site", + "description": "Label for checkbox in document-blocked page" + }, + "strictblockProceed": { + "message": "Proceed", + "description": "A button to navigate to the blocked page" } } diff --git a/platform/mv3/extension/_locales/zh_CN/messages.json b/platform/mv3/extension/_locales/zh_CN/messages.json index 05b31c9d07b20..4f01ba20adee8 100644 --- a/platform/mv3/extension/_locales/zh_CN/messages.json +++ b/platform/mv3/extension/_locales/zh_CN/messages.json @@ -231,8 +231,44 @@ "message": "在工具栏图标上显示拦截请求数", "description": "Label for a checkbox in the options page" }, + "enableStrictBlockLabel": { + "message": "Enable strict blocking", + "description": "Label for a checkbox in the options page" + }, + "enableStrictBlockLegend": { + "message": "Navigation to potentially undesirable sites will be blocked, and you will be offered the option to proceed.", + "description": "Label for a checkbox in the options page" + }, "findListsPlaceholder": { "message": "查找列表", "description": "Placeholder for the input field used to find lists" + }, + "strictblockTitle": { + "message": "Page blocked", + "description": "Webpage title for the strict-blocked page" + }, + "strictblockSentence1": { + "message": "uBO Lite has prevented the following page from loading:", + "description": "Sentence used in the strict-blocked page" + }, + "strictblockNoParamsPrompt": { + "message": "without parameters", + "description": "Label to be used for the parameter-less URL" + }, + "strictblockBack": { + "message": "Go back", + "description": "A button to go back to the previous webpage" + }, + "strictblockClose": { + "message": "Close this window", + "description": "A button to close the current tab" + }, + "strictblockDontWarn": { + "message": "Don't warn me again about this site", + "description": "Label for checkbox in document-blocked page" + }, + "strictblockProceed": { + "message": "Proceed", + "description": "A button to navigate to the blocked page" } } diff --git a/platform/mv3/extension/_locales/zh_TW/messages.json b/platform/mv3/extension/_locales/zh_TW/messages.json index 096df7c4e23f3..2aa06e3966568 100644 --- a/platform/mv3/extension/_locales/zh_TW/messages.json +++ b/platform/mv3/extension/_locales/zh_TW/messages.json @@ -231,8 +231,44 @@ "message": "在工具列圖示上顯示被阻擋的連線請求的數量", "description": "Label for a checkbox in the options page" }, + "enableStrictBlockLabel": { + "message": "Enable strict blocking", + "description": "Label for a checkbox in the options page" + }, + "enableStrictBlockLegend": { + "message": "Navigation to potentially undesirable sites will be blocked, and you will be offered the option to proceed.", + "description": "Label for a checkbox in the options page" + }, "findListsPlaceholder": { "message": "尋找清單", "description": "Placeholder for the input field used to find lists" + }, + "strictblockTitle": { + "message": "Page blocked", + "description": "Webpage title for the strict-blocked page" + }, + "strictblockSentence1": { + "message": "uBO Lite has prevented the following page from loading:", + "description": "Sentence used in the strict-blocked page" + }, + "strictblockNoParamsPrompt": { + "message": "without parameters", + "description": "Label to be used for the parameter-less URL" + }, + "strictblockBack": { + "message": "Go back", + "description": "A button to go back to the previous webpage" + }, + "strictblockClose": { + "message": "Close this window", + "description": "A button to close the current tab" + }, + "strictblockDontWarn": { + "message": "Don't warn me again about this site", + "description": "Label for checkbox in document-blocked page" + }, + "strictblockProceed": { + "message": "Proceed", + "description": "A button to navigate to the blocked page" } } From 076e9fa73e4c169dab5c3fb6251fbae5bb51ed56 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Wed, 4 Dec 2024 10:48:55 -0500 Subject: [PATCH 0495/1099] Visually separate scriptlet parameters in active line This makes it easier to see how parameters are parsed internally, in order to make it easier to distinguish commas as separator and literal commas meant to be part of a parameter. Related discussion: https://github.com/uBlockOrigin/uBlock-discussions/discussions/865#discussioncomment-11461980 --- src/css/codemirror.css | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/css/codemirror.css b/src/css/codemirror.css index 9036acc990182..d1ca9fa1008de 100644 --- a/src/css/codemirror.css +++ b/src/css/codemirror.css @@ -134,6 +134,10 @@ .cm-theme-override .cm-s-default .cm-variable { color: var(--sf-variable-ink); } +.cm-theme-override .cm-s-default .CodeMirror-activeline .cm-ext-js .cm-variable { + text-decoration: underline color-mix(in srgb, var(--sf-variable-ink) 30%, transparent) solid 3px; + text-decoration-skip-ink: none; + } .cm-theme-override .cm-s-default .cm-warning { background-color: var(--sf-warning-surface); text-decoration: underline var(--sf-warning-ink); From 48fed03128c59af89d23ba0d0e9f0d8b58e25825 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Wed, 4 Dec 2024 16:55:18 -0500 Subject: [PATCH 0496/1099] Add "RU AdList: Counters" to stable release of uBO Related issue: https://github.com/uBlockOrigin/uBOL-home/issues/229#issuecomment-2408527226 --- assets/assets.json | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/assets/assets.json b/assets/assets.json index 76967068a6e99..6449dbe932434 100644 --- a/assets/assets.json +++ b/assets/assets.json @@ -826,6 +826,7 @@ "RUS-0": { "content": "filters", "group": "regions", + "parent": "🇷🇺ru 🇺🇦ua 🇺🇿uz 🇰🇿kz: RU AdList", "off": true, "title": "🇷🇺ru 🇺🇦ua 🇺🇿uz 🇰🇿kz: RU AdList", "tags": "ads belarusian беларуская kazakh tatar russian русский ukrainian українська uzbek", @@ -839,6 +840,22 @@ "supportURL": "https://forums.lanik.us/viewforum.php?f=102", "instructionURL": "https://forums.lanik.us/viewtopic.php?f=102&t=22512" }, + "RUS-1": { + "content": "filters", + "group": "regions", + "parent": "🇷🇺ru 🇺🇦ua 🇺🇿uz 🇰🇿kz: RU AdList", + "off": true, + "title": "🇷🇺ru 🇺🇦ua 🇺🇿uz 🇰🇿kz: RU AdList: Counters", + "tags": "ads belarusian беларуская kazakh tatar russian русский ukrainian українська uzbek be kk tt ru uk uz", + "contentURL": "https://raw.githubusercontent.com/easylist/ruadlist/master/cntblock.txt", + "cdnURLs": [ + "https://cdn.jsdelivr.net/gh/easylist/ruadlist@master/cntblock.txt", + "https://cdn.statically.io/gh/easylist/ruadlist/master/cntblock.txt", + "https://raw.githubusercontent.com/easylist/ruadlist/master/cntblock.txt" + ], + "supportURL": "https://forums.lanik.us/viewforum.php?f=102", + "instructionURL": "https://forums.lanik.us/viewtopic.php?f=102&t=22512" + }, "spa-0": { "content": "filters", "group": "regions", From ea1b1abaee29e76e0fac84f168e75fb0d31e174d Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Wed, 4 Dec 2024 19:43:40 -0500 Subject: [PATCH 0497/1099] Import translation work from https://crowdin.com/project/ublock --- .../mv3/extension/_locales/ar/messages.json | 16 +++--- .../mv3/extension/_locales/be/messages.json | 18 +++---- .../mv3/extension/_locales/bg/messages.json | 18 +++---- .../extension/_locales/br_FR/messages.json | 20 +++---- .../mv3/extension/_locales/ca/messages.json | 18 +++---- .../mv3/extension/_locales/da/messages.json | 18 +++---- .../mv3/extension/_locales/de/messages.json | 18 +++---- .../mv3/extension/_locales/es/messages.json | 18 +++---- .../mv3/extension/_locales/et/messages.json | 18 +++---- .../mv3/extension/_locales/fi/messages.json | 18 +++---- .../mv3/extension/_locales/fr/messages.json | 18 +++---- .../mv3/extension/_locales/fy/messages.json | 18 +++---- .../mv3/extension/_locales/gl/messages.json | 18 +++---- .../mv3/extension/_locales/hr/messages.json | 18 +++---- .../mv3/extension/_locales/hu/messages.json | 18 +++---- .../mv3/extension/_locales/ja/messages.json | 18 +++---- .../mv3/extension/_locales/lv/messages.json | 18 +++---- .../mv3/extension/_locales/pl/messages.json | 18 +++---- .../extension/_locales/pt_BR/messages.json | 18 +++---- .../mv3/extension/_locales/ru/messages.json | 18 +++---- .../mv3/extension/_locales/sk/messages.json | 18 +++---- .../mv3/extension/_locales/sr/messages.json | 54 +++++++++---------- .../mv3/extension/_locales/sv/messages.json | 18 +++---- .../mv3/extension/_locales/tr/messages.json | 18 +++---- .../extension/_locales/zh_CN/messages.json | 18 +++---- .../extension/_locales/zh_TW/messages.json | 32 +++++------ src/_locales/sr/messages.json | 8 +-- 27 files changed, 263 insertions(+), 263 deletions(-) diff --git a/platform/mv3/extension/_locales/ar/messages.json b/platform/mv3/extension/_locales/ar/messages.json index 6d3d859d2ea43..b4281ab21df17 100644 --- a/platform/mv3/extension/_locales/ar/messages.json +++ b/platform/mv3/extension/_locales/ar/messages.json @@ -232,11 +232,11 @@ "description": "Label for a checkbox in the options page" }, "enableStrictBlockLabel": { - "message": "Enable strict blocking", + "message": "تمكين الحظر الصارم", "description": "Label for a checkbox in the options page" }, "enableStrictBlockLegend": { - "message": "Navigation to potentially undesirable sites will be blocked, and you will be offered the option to proceed.", + "message": "سيتم حظر التنقل إلى المواقع غير المرغوب فيها، وسيتم تقديم خيار لك للمتابعة.", "description": "Label for a checkbox in the options page" }, "findListsPlaceholder": { @@ -244,11 +244,11 @@ "description": "Placeholder for the input field used to find lists" }, "strictblockTitle": { - "message": "Page blocked", + "message": "الصفحة محجوبة", "description": "Webpage title for the strict-blocked page" }, "strictblockSentence1": { - "message": "uBO Lite has prevented the following page from loading:", + "message": "لقد منع uBO Lite تحميل الصفحة التالية:", "description": "Sentence used in the strict-blocked page" }, "strictblockNoParamsPrompt": { @@ -256,19 +256,19 @@ "description": "Label to be used for the parameter-less URL" }, "strictblockBack": { - "message": "Go back", + "message": "رجوع", "description": "A button to go back to the previous webpage" }, "strictblockClose": { - "message": "Close this window", + "message": "إغلاق هذه النافذة", "description": "A button to close the current tab" }, "strictblockDontWarn": { - "message": "Don't warn me again about this site", + "message": "لا تحذرني مرة أخرى من هذا الموقع", "description": "Label for checkbox in document-blocked page" }, "strictblockProceed": { - "message": "Proceed", + "message": "متابعة", "description": "A button to navigate to the blocked page" } } diff --git a/platform/mv3/extension/_locales/be/messages.json b/platform/mv3/extension/_locales/be/messages.json index a75a324e67a74..9ca2a032414e0 100644 --- a/platform/mv3/extension/_locales/be/messages.json +++ b/platform/mv3/extension/_locales/be/messages.json @@ -232,11 +232,11 @@ "description": "Label for a checkbox in the options page" }, "enableStrictBlockLabel": { - "message": "Enable strict blocking", + "message": "Уключыць строгае блакаванне", "description": "Label for a checkbox in the options page" }, "enableStrictBlockLegend": { - "message": "Navigation to potentially undesirable sites will be blocked, and you will be offered the option to proceed.", + "message": "Пераход да патэнцыйна непажаданых сайтаў будзе заблакаваны, і вам будзе прапанавана магчымасць працягнуць.", "description": "Label for a checkbox in the options page" }, "findListsPlaceholder": { @@ -244,31 +244,31 @@ "description": "Placeholder for the input field used to find lists" }, "strictblockTitle": { - "message": "Page blocked", + "message": "Старонка заблакавана", "description": "Webpage title for the strict-blocked page" }, "strictblockSentence1": { - "message": "uBO Lite has prevented the following page from loading:", + "message": "uBO Lite папярэдзіў чытанне наступнай старонкі:", "description": "Sentence used in the strict-blocked page" }, "strictblockNoParamsPrompt": { - "message": "without parameters", + "message": "без параметраў", "description": "Label to be used for the parameter-less URL" }, "strictblockBack": { - "message": "Go back", + "message": "Вярнуцца", "description": "A button to go back to the previous webpage" }, "strictblockClose": { - "message": "Close this window", + "message": "Закрыць гэтае акно", "description": "A button to close the current tab" }, "strictblockDontWarn": { - "message": "Don't warn me again about this site", + "message": "Не папярэджваць больш пра гэты сайт", "description": "Label for checkbox in document-blocked page" }, "strictblockProceed": { - "message": "Proceed", + "message": "Працягнуць", "description": "A button to navigate to the blocked page" } } diff --git a/platform/mv3/extension/_locales/bg/messages.json b/platform/mv3/extension/_locales/bg/messages.json index 686b39f1058af..8ebf6cbd586a2 100644 --- a/platform/mv3/extension/_locales/bg/messages.json +++ b/platform/mv3/extension/_locales/bg/messages.json @@ -232,11 +232,11 @@ "description": "Label for a checkbox in the options page" }, "enableStrictBlockLabel": { - "message": "Enable strict blocking", + "message": "Активиране на строгото блокиране", "description": "Label for a checkbox in the options page" }, "enableStrictBlockLegend": { - "message": "Navigation to potentially undesirable sites will be blocked, and you will be offered the option to proceed.", + "message": "Навигацията към потенциално нежелани сайтове ще бъде блокирана и ще ви бъде предложена възможността да продължите.", "description": "Label for a checkbox in the options page" }, "findListsPlaceholder": { @@ -244,31 +244,31 @@ "description": "Placeholder for the input field used to find lists" }, "strictblockTitle": { - "message": "Page blocked", + "message": "Страницата е блокирана", "description": "Webpage title for the strict-blocked page" }, "strictblockSentence1": { - "message": "uBO Lite has prevented the following page from loading:", + "message": "uBO Lite попречи на зареждането на следната страница:", "description": "Sentence used in the strict-blocked page" }, "strictblockNoParamsPrompt": { - "message": "without parameters", + "message": "без параметри", "description": "Label to be used for the parameter-less URL" }, "strictblockBack": { - "message": "Go back", + "message": "Назад", "description": "A button to go back to the previous webpage" }, "strictblockClose": { - "message": "Close this window", + "message": "Затворяне на прозореца", "description": "A button to close the current tab" }, "strictblockDontWarn": { - "message": "Don't warn me again about this site", + "message": "Не ме предупреждавайте отново за този сайт", "description": "Label for checkbox in document-blocked page" }, "strictblockProceed": { - "message": "Proceed", + "message": "Продължаване", "description": "A button to navigate to the blocked page" } } diff --git a/platform/mv3/extension/_locales/br_FR/messages.json b/platform/mv3/extension/_locales/br_FR/messages.json index 9e61a7f21105b..2b5784900f48f 100644 --- a/platform/mv3/extension/_locales/br_FR/messages.json +++ b/platform/mv3/extension/_locales/br_FR/messages.json @@ -160,7 +160,7 @@ "description": "An entry in the widget used to select the type of issue" }, "supportS6Checkbox1": { - "message": "Label the webpage as “NSFW” (“Not Safe For Work”)", + "message": "Merkañ ar bejenn web evel \"NSFW\" (“Not Safe For Work”, dizereat evit al labour)", "description": "A checkbox to use for NSFW sites" }, "supportReportSpecificButton": { @@ -232,11 +232,11 @@ "description": "Label for a checkbox in the options page" }, "enableStrictBlockLabel": { - "message": "Enable strict blocking", + "message": "Enaouiñ ar stankañ strizh", "description": "Label for a checkbox in the options page" }, "enableStrictBlockLegend": { - "message": "Navigation to potentially undesirable sites will be blocked, and you will be offered the option to proceed.", + "message": "Stanket e vo ar merdeiñ etrezek lec'hiennoù a c'hallfe bezañ dañjerus, ha moaien a vo deoc'h dibab da genderc'hel pe get.", "description": "Label for a checkbox in the options page" }, "findListsPlaceholder": { @@ -244,31 +244,31 @@ "description": "Placeholder for the input field used to find lists" }, "strictblockTitle": { - "message": "Page blocked", + "message": "Pajenn stanket", "description": "Webpage title for the strict-blocked page" }, "strictblockSentence1": { - "message": "uBO Lite has prevented the following page from loading:", + "message": "uBO Lite en deus miret ar bajenn-mañ da gargañ:", "description": "Sentence used in the strict-blocked page" }, "strictblockNoParamsPrompt": { - "message": "without parameters", + "message": "kuit a arventennoù", "description": "Label to be used for the parameter-less URL" }, "strictblockBack": { - "message": "Go back", + "message": "Distreiñ", "description": "A button to go back to the previous webpage" }, "strictblockClose": { - "message": "Close this window", + "message": "Serriñ ar prenestr-mañ", "description": "A button to close the current tab" }, "strictblockDontWarn": { - "message": "Don't warn me again about this site", + "message": "Arabat kemenn din diwar-benn al lec'hienn-mañ en-dro", "description": "Label for checkbox in document-blocked page" }, "strictblockProceed": { - "message": "Proceed", + "message": "Kenderc'hel", "description": "A button to navigate to the blocked page" } } diff --git a/platform/mv3/extension/_locales/ca/messages.json b/platform/mv3/extension/_locales/ca/messages.json index 12c826c458189..96bbd7029e32b 100644 --- a/platform/mv3/extension/_locales/ca/messages.json +++ b/platform/mv3/extension/_locales/ca/messages.json @@ -232,11 +232,11 @@ "description": "Label for a checkbox in the options page" }, "enableStrictBlockLabel": { - "message": "Enable strict blocking", + "message": "Habilita el blocatge estricte", "description": "Label for a checkbox in the options page" }, "enableStrictBlockLegend": { - "message": "Navigation to potentially undesirable sites will be blocked, and you will be offered the option to proceed.", + "message": "Es blocarà la navegació en webs potencialment no desitjables, oferint-vos la possibilitat de continuar.", "description": "Label for a checkbox in the options page" }, "findListsPlaceholder": { @@ -244,31 +244,31 @@ "description": "Placeholder for the input field used to find lists" }, "strictblockTitle": { - "message": "Page blocked", + "message": "Pàgina blocada", "description": "Webpage title for the strict-blocked page" }, "strictblockSentence1": { - "message": "uBO Lite has prevented the following page from loading:", + "message": "L'uBO Lite ha evitat la càrrega d'aquesta pàgina:", "description": "Sentence used in the strict-blocked page" }, "strictblockNoParamsPrompt": { - "message": "without parameters", + "message": "sense paràmetres", "description": "Label to be used for the parameter-less URL" }, "strictblockBack": { - "message": "Go back", + "message": "Enrere", "description": "A button to go back to the previous webpage" }, "strictblockClose": { - "message": "Close this window", + "message": "Tanca aquesta finestra", "description": "A button to close the current tab" }, "strictblockDontWarn": { - "message": "Don't warn me again about this site", + "message": "No em tornis a avisar sobre aquest lloc", "description": "Label for checkbox in document-blocked page" }, "strictblockProceed": { - "message": "Proceed", + "message": "Continua", "description": "A button to navigate to the blocked page" } } diff --git a/platform/mv3/extension/_locales/da/messages.json b/platform/mv3/extension/_locales/da/messages.json index edddf00153b7f..63efbe0d83cd9 100644 --- a/platform/mv3/extension/_locales/da/messages.json +++ b/platform/mv3/extension/_locales/da/messages.json @@ -232,11 +232,11 @@ "description": "Label for a checkbox in the options page" }, "enableStrictBlockLabel": { - "message": "Enable strict blocking", + "message": "Aktivér striks blokering", "description": "Label for a checkbox in the options page" }, "enableStrictBlockLegend": { - "message": "Navigation to potentially undesirable sites will be blocked, and you will be offered the option to proceed.", + "message": "Navigering til potentielt uønskede websteder blokeres, men man tilbydes muligheden for at fortsætte.", "description": "Label for a checkbox in the options page" }, "findListsPlaceholder": { @@ -244,31 +244,31 @@ "description": "Placeholder for the input field used to find lists" }, "strictblockTitle": { - "message": "Page blocked", + "message": "Side blokeret", "description": "Webpage title for the strict-blocked page" }, "strictblockSentence1": { - "message": "uBO Lite has prevented the following page from loading:", + "message": "uBO Lite har forhindret flg. side i at blive indlæst:", "description": "Sentence used in the strict-blocked page" }, "strictblockNoParamsPrompt": { - "message": "without parameters", + "message": "uden parametre", "description": "Label to be used for the parameter-less URL" }, "strictblockBack": { - "message": "Go back", + "message": "Gå tilbage", "description": "A button to go back to the previous webpage" }, "strictblockClose": { - "message": "Close this window", + "message": "Luk dette vindue", "description": "A button to close the current tab" }, "strictblockDontWarn": { - "message": "Don't warn me again about this site", + "message": "Advar ikke igen om dette websted", "description": "Label for checkbox in document-blocked page" }, "strictblockProceed": { - "message": "Proceed", + "message": "Fortsæt", "description": "A button to navigate to the blocked page" } } diff --git a/platform/mv3/extension/_locales/de/messages.json b/platform/mv3/extension/_locales/de/messages.json index 58ccefdcd6773..32c14d1ca49df 100644 --- a/platform/mv3/extension/_locales/de/messages.json +++ b/platform/mv3/extension/_locales/de/messages.json @@ -232,11 +232,11 @@ "description": "Label for a checkbox in the options page" }, "enableStrictBlockLabel": { - "message": "Enable strict blocking", + "message": "Stricktes Blockieren aktivieren", "description": "Label for a checkbox in the options page" }, "enableStrictBlockLegend": { - "message": "Navigation to potentially undesirable sites will be blocked, and you will be offered the option to proceed.", + "message": "Die Navigation zu potenziell unerwünschten Websites wird verhindert, jedoch wird eine Möglichkeit zum Fortfahren angeboten.", "description": "Label for a checkbox in the options page" }, "findListsPlaceholder": { @@ -244,31 +244,31 @@ "description": "Placeholder for the input field used to find lists" }, "strictblockTitle": { - "message": "Page blocked", + "message": "Seite blockiert", "description": "Webpage title for the strict-blocked page" }, "strictblockSentence1": { - "message": "uBO Lite has prevented the following page from loading:", + "message": "uBO Lite hat das Laden der folgenden Seite verhindert:", "description": "Sentence used in the strict-blocked page" }, "strictblockNoParamsPrompt": { - "message": "without parameters", + "message": "ohne Parameter", "description": "Label to be used for the parameter-less URL" }, "strictblockBack": { - "message": "Go back", + "message": "Zurück", "description": "A button to go back to the previous webpage" }, "strictblockClose": { - "message": "Close this window", + "message": "Dieses Fenster schließen", "description": "A button to close the current tab" }, "strictblockDontWarn": { - "message": "Don't warn me again about this site", + "message": "Nicht erneut vor dieser Seite warnen", "description": "Label for checkbox in document-blocked page" }, "strictblockProceed": { - "message": "Proceed", + "message": "Fortfahren", "description": "A button to navigate to the blocked page" } } diff --git a/platform/mv3/extension/_locales/es/messages.json b/platform/mv3/extension/_locales/es/messages.json index 5fd5d530b0043..6bf520e79b2b9 100644 --- a/platform/mv3/extension/_locales/es/messages.json +++ b/platform/mv3/extension/_locales/es/messages.json @@ -232,11 +232,11 @@ "description": "Label for a checkbox in the options page" }, "enableStrictBlockLabel": { - "message": "Enable strict blocking", + "message": "Habilitar bloqueo estricto", "description": "Label for a checkbox in the options page" }, "enableStrictBlockLegend": { - "message": "Navigation to potentially undesirable sites will be blocked, and you will be offered the option to proceed.", + "message": "La navegación a sitios potencialmente no deseados será bloqueada, y se te ofrecerá la opción de continuar.", "description": "Label for a checkbox in the options page" }, "findListsPlaceholder": { @@ -244,31 +244,31 @@ "description": "Placeholder for the input field used to find lists" }, "strictblockTitle": { - "message": "Page blocked", + "message": "Página bloqueada", "description": "Webpage title for the strict-blocked page" }, "strictblockSentence1": { - "message": "uBO Lite has prevented the following page from loading:", + "message": "uBO Lite impidió la carga de la página:", "description": "Sentence used in the strict-blocked page" }, "strictblockNoParamsPrompt": { - "message": "without parameters", + "message": "sin parámetros", "description": "Label to be used for the parameter-less URL" }, "strictblockBack": { - "message": "Go back", + "message": "Regresar", "description": "A button to go back to the previous webpage" }, "strictblockClose": { - "message": "Close this window", + "message": "Cerrar esta ventana", "description": "A button to close the current tab" }, "strictblockDontWarn": { - "message": "Don't warn me again about this site", + "message": "No me adviertas de nuevo sobre este sitio", "description": "Label for checkbox in document-blocked page" }, "strictblockProceed": { - "message": "Proceed", + "message": "Continuar", "description": "A button to navigate to the blocked page" } } diff --git a/platform/mv3/extension/_locales/et/messages.json b/platform/mv3/extension/_locales/et/messages.json index 1d8efd99ccdee..3b7ced5a48785 100644 --- a/platform/mv3/extension/_locales/et/messages.json +++ b/platform/mv3/extension/_locales/et/messages.json @@ -232,11 +232,11 @@ "description": "Label for a checkbox in the options page" }, "enableStrictBlockLabel": { - "message": "Enable strict blocking", + "message": "Luba range tõkestamine", "description": "Label for a checkbox in the options page" }, "enableStrictBlockLegend": { - "message": "Navigation to potentially undesirable sites will be blocked, and you will be offered the option to proceed.", + "message": "Kahtlastele veebilehtedele suunamist takistatakse ja pakutakse võimalust jätkamiseks.", "description": "Label for a checkbox in the options page" }, "findListsPlaceholder": { @@ -244,31 +244,31 @@ "description": "Placeholder for the input field used to find lists" }, "strictblockTitle": { - "message": "Page blocked", + "message": "Lehe sirvimine piiratud", "description": "Webpage title for the strict-blocked page" }, "strictblockSentence1": { - "message": "uBO Lite has prevented the following page from loading:", + "message": "uBO Lite ennetas järgmise veebilehe laadimist:", "description": "Sentence used in the strict-blocked page" }, "strictblockNoParamsPrompt": { - "message": "without parameters", + "message": "ilma näitajateta", "description": "Label to be used for the parameter-less URL" }, "strictblockBack": { - "message": "Go back", + "message": "Tagasi", "description": "A button to go back to the previous webpage" }, "strictblockClose": { - "message": "Close this window", + "message": "Sulge see aken", "description": "A button to close the current tab" }, "strictblockDontWarn": { - "message": "Don't warn me again about this site", + "message": "Edaspidi luba mul seda sirvida", "description": "Label for checkbox in document-blocked page" }, "strictblockProceed": { - "message": "Proceed", + "message": "Jätka", "description": "A button to navigate to the blocked page" } } diff --git a/platform/mv3/extension/_locales/fi/messages.json b/platform/mv3/extension/_locales/fi/messages.json index e3fe20347cc94..963a50c6900ce 100644 --- a/platform/mv3/extension/_locales/fi/messages.json +++ b/platform/mv3/extension/_locales/fi/messages.json @@ -232,11 +232,11 @@ "description": "Label for a checkbox in the options page" }, "enableStrictBlockLabel": { - "message": "Enable strict blocking", + "message": "Käytä tiukkaa estoa", "description": "Label for a checkbox in the options page" }, "enableStrictBlockLegend": { - "message": "Navigation to potentially undesirable sites will be blocked, and you will be offered the option to proceed.", + "message": "Potentiaalisesti ei-toivottujen sivustojen avaaminen estetään mahdollisuudella jatkaa.", "description": "Label for a checkbox in the options page" }, "findListsPlaceholder": { @@ -244,31 +244,31 @@ "description": "Placeholder for the input field used to find lists" }, "strictblockTitle": { - "message": "Page blocked", + "message": "Sivu estettiin", "description": "Webpage title for the strict-blocked page" }, "strictblockSentence1": { - "message": "uBO Lite has prevented the following page from loading:", + "message": "uBO Lite on estänyt seuraavan sivun latauksen:", "description": "Sentence used in the strict-blocked page" }, "strictblockNoParamsPrompt": { - "message": "without parameters", + "message": "ilman parametreja", "description": "Label to be used for the parameter-less URL" }, "strictblockBack": { - "message": "Go back", + "message": "Palaa takaisin", "description": "A button to go back to the previous webpage" }, "strictblockClose": { - "message": "Close this window", + "message": "Sulje tämä ikkuna", "description": "A button to close the current tab" }, "strictblockDontWarn": { - "message": "Don't warn me again about this site", + "message": "Älä varoita minua tästä sivustosta uudelleen", "description": "Label for checkbox in document-blocked page" }, "strictblockProceed": { - "message": "Proceed", + "message": "Jatka", "description": "A button to navigate to the blocked page" } } diff --git a/platform/mv3/extension/_locales/fr/messages.json b/platform/mv3/extension/_locales/fr/messages.json index 7a9f2f3f31eb9..a08099a95f8aa 100644 --- a/platform/mv3/extension/_locales/fr/messages.json +++ b/platform/mv3/extension/_locales/fr/messages.json @@ -232,11 +232,11 @@ "description": "Label for a checkbox in the options page" }, "enableStrictBlockLabel": { - "message": "Enable strict blocking", + "message": "Activer le blocage strict", "description": "Label for a checkbox in the options page" }, "enableStrictBlockLegend": { - "message": "Navigation to potentially undesirable sites will be blocked, and you will be offered the option to proceed.", + "message": "La navigation vers des sites potentiellement indésirables sera bloquée, et vous aurez la possibilité de continuer", "description": "Label for a checkbox in the options page" }, "findListsPlaceholder": { @@ -244,31 +244,31 @@ "description": "Placeholder for the input field used to find lists" }, "strictblockTitle": { - "message": "Page blocked", + "message": "Page bloquée", "description": "Webpage title for the strict-blocked page" }, "strictblockSentence1": { - "message": "uBO Lite has prevented the following page from loading:", + "message": "uBO Lite a empêché le chargement de cette page :", "description": "Sentence used in the strict-blocked page" }, "strictblockNoParamsPrompt": { - "message": "without parameters", + "message": "sans paramètres", "description": "Label to be used for the parameter-less URL" }, "strictblockBack": { - "message": "Go back", + "message": "Précédent", "description": "A button to go back to the previous webpage" }, "strictblockClose": { - "message": "Close this window", + "message": "Fermer cette fenêtre", "description": "A button to close the current tab" }, "strictblockDontWarn": { - "message": "Don't warn me again about this site", + "message": "Ne plus me prévenir pour ce site", "description": "Label for checkbox in document-blocked page" }, "strictblockProceed": { - "message": "Proceed", + "message": "Continuer", "description": "A button to navigate to the blocked page" } } diff --git a/platform/mv3/extension/_locales/fy/messages.json b/platform/mv3/extension/_locales/fy/messages.json index bedb17ef55c4d..f570ed2c0ed19 100644 --- a/platform/mv3/extension/_locales/fy/messages.json +++ b/platform/mv3/extension/_locales/fy/messages.json @@ -232,11 +232,11 @@ "description": "Label for a checkbox in the options page" }, "enableStrictBlockLabel": { - "message": "Enable strict blocking", + "message": "Strikte blokkearring ynskeakelje", "description": "Label for a checkbox in the options page" }, "enableStrictBlockLegend": { - "message": "Navigation to potentially undesirable sites will be blocked, and you will be offered the option to proceed.", + "message": "Navigaasje nei in potinsjeel net-winske websites sil blokkearre wurde, en jo krije in opsje om troch te gean.", "description": "Label for a checkbox in the options page" }, "findListsPlaceholder": { @@ -244,31 +244,31 @@ "description": "Placeholder for the input field used to find lists" }, "strictblockTitle": { - "message": "Page blocked", + "message": "Side blokkearre", "description": "Webpage title for the strict-blocked page" }, "strictblockSentence1": { - "message": "uBO Lite has prevented the following page from loading:", + "message": "uBO Lite hat opkeard dat de folgjende side laden wurd:", "description": "Sentence used in the strict-blocked page" }, "strictblockNoParamsPrompt": { - "message": "without parameters", + "message": "sûnder parameters", "description": "Label to be used for the parameter-less URL" }, "strictblockBack": { - "message": "Go back", + "message": "Tebek", "description": "A button to go back to the previous webpage" }, "strictblockClose": { - "message": "Close this window", + "message": "Slút dit finster", "description": "A button to close the current tab" }, "strictblockDontWarn": { - "message": "Don't warn me again about this site", + "message": "My net mear warskôgje oer dizze website", "description": "Label for checkbox in document-blocked page" }, "strictblockProceed": { - "message": "Proceed", + "message": "Tochgean", "description": "A button to navigate to the blocked page" } } diff --git a/platform/mv3/extension/_locales/gl/messages.json b/platform/mv3/extension/_locales/gl/messages.json index 10c74dc99c343..3054c798c8c3b 100644 --- a/platform/mv3/extension/_locales/gl/messages.json +++ b/platform/mv3/extension/_locales/gl/messages.json @@ -232,11 +232,11 @@ "description": "Label for a checkbox in the options page" }, "enableStrictBlockLabel": { - "message": "Enable strict blocking", + "message": "Activar bloqueo estrito", "description": "Label for a checkbox in the options page" }, "enableStrictBlockLegend": { - "message": "Navigation to potentially undesirable sites will be blocked, and you will be offered the option to proceed.", + "message": "Vaise bloquear a navegación en webs potencialmente non desexables, e ofrecerase a opción de bloquealas.", "description": "Label for a checkbox in the options page" }, "findListsPlaceholder": { @@ -244,31 +244,31 @@ "description": "Placeholder for the input field used to find lists" }, "strictblockTitle": { - "message": "Page blocked", + "message": "Páxina bloqueada", "description": "Webpage title for the strict-blocked page" }, "strictblockSentence1": { - "message": "uBO Lite has prevented the following page from loading:", + "message": "uBO Lite evitou que a seguinte páxina se cargase:", "description": "Sentence used in the strict-blocked page" }, "strictblockNoParamsPrompt": { - "message": "without parameters", + "message": "sen parámetros", "description": "Label to be used for the parameter-less URL" }, "strictblockBack": { - "message": "Go back", + "message": "Volver", "description": "A button to go back to the previous webpage" }, "strictblockClose": { - "message": "Close this window", + "message": "Pechar esta xanela", "description": "A button to close the current tab" }, "strictblockDontWarn": { - "message": "Don't warn me again about this site", + "message": "Non volver avisarme sobre esta web", "description": "Label for checkbox in document-blocked page" }, "strictblockProceed": { - "message": "Proceed", + "message": "Proceder", "description": "A button to navigate to the blocked page" } } diff --git a/platform/mv3/extension/_locales/hr/messages.json b/platform/mv3/extension/_locales/hr/messages.json index db09ff164dbf9..b70b02ca1a81f 100644 --- a/platform/mv3/extension/_locales/hr/messages.json +++ b/platform/mv3/extension/_locales/hr/messages.json @@ -232,11 +232,11 @@ "description": "Label for a checkbox in the options page" }, "enableStrictBlockLabel": { - "message": "Enable strict blocking", + "message": "Omogući strogo blokiranje", "description": "Label for a checkbox in the options page" }, "enableStrictBlockLegend": { - "message": "Navigation to potentially undesirable sites will be blocked, and you will be offered the option to proceed.", + "message": "Navigacija do potencijalno nepoželjnih stranica bit će blokirana i bit će vam ponuđena opcija za nastavak.", "description": "Label for a checkbox in the options page" }, "findListsPlaceholder": { @@ -244,31 +244,31 @@ "description": "Placeholder for the input field used to find lists" }, "strictblockTitle": { - "message": "Page blocked", + "message": "Stranica blokirana", "description": "Webpage title for the strict-blocked page" }, "strictblockSentence1": { - "message": "uBO Lite has prevented the following page from loading:", + "message": "uBO Lite je spriječio učitavanje sljedeće stranice:", "description": "Sentence used in the strict-blocked page" }, "strictblockNoParamsPrompt": { - "message": "without parameters", + "message": "bez parametara", "description": "Label to be used for the parameter-less URL" }, "strictblockBack": { - "message": "Go back", + "message": "Idi natrag", "description": "A button to go back to the previous webpage" }, "strictblockClose": { - "message": "Close this window", + "message": "Zatvori ovaj prozor", "description": "A button to close the current tab" }, "strictblockDontWarn": { - "message": "Don't warn me again about this site", + "message": "Ne upozoravaj me više za ovu web stranicu", "description": "Label for checkbox in document-blocked page" }, "strictblockProceed": { - "message": "Proceed", + "message": "Nastavi", "description": "A button to navigate to the blocked page" } } diff --git a/platform/mv3/extension/_locales/hu/messages.json b/platform/mv3/extension/_locales/hu/messages.json index 7fc3f9f07c335..6a31151a5d6b4 100644 --- a/platform/mv3/extension/_locales/hu/messages.json +++ b/platform/mv3/extension/_locales/hu/messages.json @@ -232,11 +232,11 @@ "description": "Label for a checkbox in the options page" }, "enableStrictBlockLabel": { - "message": "Enable strict blocking", + "message": "Szigorú blokkolás engedélyezése", "description": "Label for a checkbox in the options page" }, "enableStrictBlockLegend": { - "message": "Navigation to potentially undesirable sites will be blocked, and you will be offered the option to proceed.", + "message": "Az esetleges nem kívánatos webhelyekre való navigáció blokkolva lesz, és rákérdez arra, hogy folytatja-e.", "description": "Label for a checkbox in the options page" }, "findListsPlaceholder": { @@ -244,31 +244,31 @@ "description": "Placeholder for the input field used to find lists" }, "strictblockTitle": { - "message": "Page blocked", + "message": "Oldal blokkolva", "description": "Webpage title for the strict-blocked page" }, "strictblockSentence1": { - "message": "uBO Lite has prevented the following page from loading:", + "message": "A uBO Lite megakadályozta a következő oldal betöltését:", "description": "Sentence used in the strict-blocked page" }, "strictblockNoParamsPrompt": { - "message": "without parameters", + "message": "paraméterek nélkül", "description": "Label to be used for the parameter-less URL" }, "strictblockBack": { - "message": "Go back", + "message": "Vissza", "description": "A button to go back to the previous webpage" }, "strictblockClose": { - "message": "Close this window", + "message": "Ablak bezárása", "description": "A button to close the current tab" }, "strictblockDontWarn": { - "message": "Don't warn me again about this site", + "message": "Ne figyelmeztessen többet erről az oldalról", "description": "Label for checkbox in document-blocked page" }, "strictblockProceed": { - "message": "Proceed", + "message": "Folytatás", "description": "A button to navigate to the blocked page" } } diff --git a/platform/mv3/extension/_locales/ja/messages.json b/platform/mv3/extension/_locales/ja/messages.json index 5c310b1a36c75..6da09a7720eaf 100644 --- a/platform/mv3/extension/_locales/ja/messages.json +++ b/platform/mv3/extension/_locales/ja/messages.json @@ -232,11 +232,11 @@ "description": "Label for a checkbox in the options page" }, "enableStrictBlockLabel": { - "message": "Enable strict blocking", + "message": "厳格なブロックを有効化", "description": "Label for a checkbox in the options page" }, "enableStrictBlockLegend": { - "message": "Navigation to potentially undesirable sites will be blocked, and you will be offered the option to proceed.", + "message": "望ましくない可能性のあるサイトへのナビゲーションはブロックされ、次に進むためのオプションが表示されます。", "description": "Label for a checkbox in the options page" }, "findListsPlaceholder": { @@ -244,31 +244,31 @@ "description": "Placeholder for the input field used to find lists" }, "strictblockTitle": { - "message": "Page blocked", + "message": "ブロックしたページ", "description": "Webpage title for the strict-blocked page" }, "strictblockSentence1": { - "message": "uBO Lite has prevented the following page from loading:", + "message": "uBO Lite はこのページの読み込みをブロックしています:", "description": "Sentence used in the strict-blocked page" }, "strictblockNoParamsPrompt": { - "message": "without parameters", + "message": "パラメータを除いた URL", "description": "Label to be used for the parameter-less URL" }, "strictblockBack": { - "message": "Go back", + "message": "戻る", "description": "A button to go back to the previous webpage" }, "strictblockClose": { - "message": "Close this window", + "message": "このウィンドウを閉じる", "description": "A button to close the current tab" }, "strictblockDontWarn": { - "message": "Don't warn me again about this site", + "message": "今後このサイトに関する警告を表示しない", "description": "Label for checkbox in document-blocked page" }, "strictblockProceed": { - "message": "Proceed", + "message": "続行する", "description": "A button to navigate to the blocked page" } } diff --git a/platform/mv3/extension/_locales/lv/messages.json b/platform/mv3/extension/_locales/lv/messages.json index cbd9d8b7c9752..70c7ad1754c61 100644 --- a/platform/mv3/extension/_locales/lv/messages.json +++ b/platform/mv3/extension/_locales/lv/messages.json @@ -232,11 +232,11 @@ "description": "Label for a checkbox in the options page" }, "enableStrictBlockLabel": { - "message": "Enable strict blocking", + "message": "Iespējot stingro aizturēšanu", "description": "Label for a checkbox in the options page" }, "enableStrictBlockLegend": { - "message": "Navigation to potentially undesirable sites will be blocked, and you will be offered the option to proceed.", + "message": "Iespējami nevēlamu vietņu apmeklēšana tiks aizturēta, un tiks piedāvāta iespēja turpināt.", "description": "Label for a checkbox in the options page" }, "findListsPlaceholder": { @@ -244,31 +244,31 @@ "description": "Placeholder for the input field used to find lists" }, "strictblockTitle": { - "message": "Page blocked", + "message": "Lapa aizturēta", "description": "Webpage title for the strict-blocked page" }, "strictblockSentence1": { - "message": "uBO Lite has prevented the following page from loading:", + "message": "uBO Lite novērsa šīs lapas ielādēšanu:", "description": "Sentence used in the strict-blocked page" }, "strictblockNoParamsPrompt": { - "message": "without parameters", + "message": "bez parametriem", "description": "Label to be used for the parameter-less URL" }, "strictblockBack": { - "message": "Go back", + "message": "Doties atpakaļ", "description": "A button to go back to the previous webpage" }, "strictblockClose": { - "message": "Close this window", + "message": "Aizvērt šo logu", "description": "A button to close the current tab" }, "strictblockDontWarn": { - "message": "Don't warn me again about this site", + "message": "Nebrīdināt vairs par šo vietni", "description": "Label for checkbox in document-blocked page" }, "strictblockProceed": { - "message": "Proceed", + "message": "Turpināt", "description": "A button to navigate to the blocked page" } } diff --git a/platform/mv3/extension/_locales/pl/messages.json b/platform/mv3/extension/_locales/pl/messages.json index 791eb14d964f4..b6e9cca38813a 100644 --- a/platform/mv3/extension/_locales/pl/messages.json +++ b/platform/mv3/extension/_locales/pl/messages.json @@ -232,11 +232,11 @@ "description": "Label for a checkbox in the options page" }, "enableStrictBlockLabel": { - "message": "Enable strict blocking", + "message": "Włącz ścisłe blokowanie", "description": "Label for a checkbox in the options page" }, "enableStrictBlockLegend": { - "message": "Navigation to potentially undesirable sites will be blocked, and you will be offered the option to proceed.", + "message": "Nawigacja do potencjalnie niepożądanych witryn zostanie zablokowana i pojawi się opcja kontynuowania.", "description": "Label for a checkbox in the options page" }, "findListsPlaceholder": { @@ -244,31 +244,31 @@ "description": "Placeholder for the input field used to find lists" }, "strictblockTitle": { - "message": "Page blocked", + "message": "Strona zablokowana", "description": "Webpage title for the strict-blocked page" }, "strictblockSentence1": { - "message": "uBO Lite has prevented the following page from loading:", + "message": "uBO Lite nie pozwolił załadować się następującej stronie:", "description": "Sentence used in the strict-blocked page" }, "strictblockNoParamsPrompt": { - "message": "without parameters", + "message": "bez parametrów", "description": "Label to be used for the parameter-less URL" }, "strictblockBack": { - "message": "Go back", + "message": "Wstecz", "description": "A button to go back to the previous webpage" }, "strictblockClose": { - "message": "Close this window", + "message": "Zamknij to okno", "description": "A button to close the current tab" }, "strictblockDontWarn": { - "message": "Don't warn me again about this site", + "message": "Nie ostrzegaj mnie ponownie o tej witrynie", "description": "Label for checkbox in document-blocked page" }, "strictblockProceed": { - "message": "Proceed", + "message": "Kontynuuj", "description": "A button to navigate to the blocked page" } } diff --git a/platform/mv3/extension/_locales/pt_BR/messages.json b/platform/mv3/extension/_locales/pt_BR/messages.json index 429d7d5d94a9f..b64c7e263769d 100644 --- a/platform/mv3/extension/_locales/pt_BR/messages.json +++ b/platform/mv3/extension/_locales/pt_BR/messages.json @@ -232,11 +232,11 @@ "description": "Label for a checkbox in the options page" }, "enableStrictBlockLabel": { - "message": "Enable strict blocking", + "message": "Ativar bloqueio rigoroso", "description": "Label for a checkbox in the options page" }, "enableStrictBlockLegend": { - "message": "Navigation to potentially undesirable sites will be blocked, and you will be offered the option to proceed.", + "message": "A navegação até sites potencialmente indesejáveis ​​será bloqueada e será oferecido a você a opção de prosseguir.", "description": "Label for a checkbox in the options page" }, "findListsPlaceholder": { @@ -244,31 +244,31 @@ "description": "Placeholder for the input field used to find lists" }, "strictblockTitle": { - "message": "Page blocked", + "message": "Página bloqueada", "description": "Webpage title for the strict-blocked page" }, "strictblockSentence1": { - "message": "uBO Lite has prevented the following page from loading:", + "message": "O uBO Lite impediu o carregamento da seguinte página:", "description": "Sentence used in the strict-blocked page" }, "strictblockNoParamsPrompt": { - "message": "without parameters", + "message": "sem parâmetros", "description": "Label to be used for the parameter-less URL" }, "strictblockBack": { - "message": "Go back", + "message": "Voltar", "description": "A button to go back to the previous webpage" }, "strictblockClose": { - "message": "Close this window", + "message": "Fechar esta janela", "description": "A button to close the current tab" }, "strictblockDontWarn": { - "message": "Don't warn me again about this site", + "message": "Não me avise de novo sobre este site", "description": "Label for checkbox in document-blocked page" }, "strictblockProceed": { - "message": "Proceed", + "message": "Prosseguir", "description": "A button to navigate to the blocked page" } } diff --git a/platform/mv3/extension/_locales/ru/messages.json b/platform/mv3/extension/_locales/ru/messages.json index 0e84993d58102..011a910226275 100644 --- a/platform/mv3/extension/_locales/ru/messages.json +++ b/platform/mv3/extension/_locales/ru/messages.json @@ -232,11 +232,11 @@ "description": "Label for a checkbox in the options page" }, "enableStrictBlockLabel": { - "message": "Enable strict blocking", + "message": "Включить строгую блокировку", "description": "Label for a checkbox in the options page" }, "enableStrictBlockLegend": { - "message": "Navigation to potentially undesirable sites will be blocked, and you will be offered the option to proceed.", + "message": "Переход к потенциально нежелательным сайтам будет заблокирован, и будет дана возможность продолжить переход на сайт", "description": "Label for a checkbox in the options page" }, "findListsPlaceholder": { @@ -244,31 +244,31 @@ "description": "Placeholder for the input field used to find lists" }, "strictblockTitle": { - "message": "Page blocked", + "message": "Страница заблокирована", "description": "Webpage title for the strict-blocked page" }, "strictblockSentence1": { - "message": "uBO Lite has prevented the following page from loading:", + "message": "uBO Lite предотвратил загрузку следующей страницы:", "description": "Sentence used in the strict-blocked page" }, "strictblockNoParamsPrompt": { - "message": "without parameters", + "message": "без параметров", "description": "Label to be used for the parameter-less URL" }, "strictblockBack": { - "message": "Go back", + "message": "Назад", "description": "A button to go back to the previous webpage" }, "strictblockClose": { - "message": "Close this window", + "message": "Закрыть это окно", "description": "A button to close the current tab" }, "strictblockDontWarn": { - "message": "Don't warn me again about this site", + "message": "Больше не предупреждать меня об этом сайте", "description": "Label for checkbox in document-blocked page" }, "strictblockProceed": { - "message": "Proceed", + "message": "Продолжить", "description": "A button to navigate to the blocked page" } } diff --git a/platform/mv3/extension/_locales/sk/messages.json b/platform/mv3/extension/_locales/sk/messages.json index 2f20bb2dca3a7..8d7da8002cdfb 100644 --- a/platform/mv3/extension/_locales/sk/messages.json +++ b/platform/mv3/extension/_locales/sk/messages.json @@ -232,11 +232,11 @@ "description": "Label for a checkbox in the options page" }, "enableStrictBlockLabel": { - "message": "Enable strict blocking", + "message": "Povoliť prísne blokovanie", "description": "Label for a checkbox in the options page" }, "enableStrictBlockLegend": { - "message": "Navigation to potentially undesirable sites will be blocked, and you will be offered the option to proceed.", + "message": "Navigácia na potenciálne nežiaduce stránky sa zablokuje a ponúkne sa vám možnosť pokračovať.", "description": "Label for a checkbox in the options page" }, "findListsPlaceholder": { @@ -244,31 +244,31 @@ "description": "Placeholder for the input field used to find lists" }, "strictblockTitle": { - "message": "Page blocked", + "message": "Zablokovaná stránka", "description": "Webpage title for the strict-blocked page" }, "strictblockSentence1": { - "message": "uBO Lite has prevented the following page from loading:", + "message": "uBO Lite zabránil načítaniu nasledujúcej stránky:", "description": "Sentence used in the strict-blocked page" }, "strictblockNoParamsPrompt": { - "message": "without parameters", + "message": "bez parametrov", "description": "Label to be used for the parameter-less URL" }, "strictblockBack": { - "message": "Go back", + "message": "Naspäť", "description": "A button to go back to the previous webpage" }, "strictblockClose": { - "message": "Close this window", + "message": "Zatvoriť toto okno", "description": "A button to close the current tab" }, "strictblockDontWarn": { - "message": "Don't warn me again about this site", + "message": "Už ma na túto stránku neupozorňovať", "description": "Label for checkbox in document-blocked page" }, "strictblockProceed": { - "message": "Proceed", + "message": "Pokračovať", "description": "A button to navigate to the blocked page" } } diff --git a/platform/mv3/extension/_locales/sr/messages.json b/platform/mv3/extension/_locales/sr/messages.json index dd4859978c912..ca3218b7a650b 100644 --- a/platform/mv3/extension/_locales/sr/messages.json +++ b/platform/mv3/extension/_locales/sr/messages.json @@ -32,7 +32,7 @@ "description": "Label in the popup panel for the current filtering mode" }, "popupTipReport": { - "message": "Report an issue on this website", + "message": "Пријавите проблем на овом веб сајту", "description": "Tooltip used for the 'chat' icon in the panel" }, "popupTipDashboard": { @@ -104,67 +104,67 @@ "description": "Shown in the About pane" }, "supportS6H": { - "message": "Report a filter issue", + "message": "Пријављивање проблема са филтером", "description": "Header of 'Report a filter issue' section in Support pane" }, "supportS3P1": { - "message": "Report filter issues with specific websites to the uBlockOrigin/uAssets issue tracker. Requires a GitHub account.", + "message": "Пријавите проблеме са филтерима на одређеним веб сајтовима на uBlockOrigin/uAssets issue tracker. Захтева GitHub налог.", "description": "First paragraph of 'Filter issues' section in Support pane" }, "supportS6P1S1": { - "message": "To avoid burdening volunteers with duplicate reports, please verify that the issue has not already been reported.", + "message": "Да не бисте оптерећивали волонтере дуплим извештајима, проверите да ли је проблем већ пријављен.", "description": "A paragraph in the filter issue reporter section" }, "supportFindSpecificButton": { - "message": "Find similar reports", + "message": "Пронађи сличне извештаје", "description": "A clickable link in the filter issue reporter section" }, "supportS6URL": { - "message": "Address of the webpage:", + "message": "Адреса веб странице:", "description": "Label for the URL of the page" }, "supportS6Select1": { - "message": "The webpage…", + "message": "Веб страница...", "description": "Label for widget to select type of issue" }, "supportS6Select1Option0": { - "message": "-- Pick an entry --", + "message": "", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option1": { - "message": "Shows ads or ad leftovers", + "message": "Приказује рекламе или остатке реклама", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option2": { - "message": "Has overlays or other nuisances", + "message": "Има преклапања или друге сметње", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option3": { - "message": "Detects uBO Lite", + "message": "Детектује uBO Lite", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option4": { - "message": "Has privacy-related issues", + "message": "", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option5": { - "message": "Malfunctions when uBO Lite is enabled", + "message": "Кварови када је uBO Lite омогућен", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option6": { - "message": "Opens unwanted tabs or windows", + "message": "Отвара нежељене картице или прозоре", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option7": { - "message": "Leads to badware, phishing", + "message": "Води до лошег софтвера, „пецања\"", "description": "An entry in the widget used to select the type of issue" }, "supportS6Checkbox1": { - "message": "Label the webpage as “NSFW” (“Not Safe For Work”)", + "message": "Означи веб страницу као „NSFW” („Није безбедно за рад”)", "description": "A checkbox to use for NSFW sites" }, "supportReportSpecificButton": { - "message": "Create new report", + "message": "Креирај нови извештај", "description": "Text for button which open an external webpage in Support pane" }, "firstRunSectionLabel": { @@ -212,7 +212,7 @@ "description": "This describes the 'complete' filtering mode" }, "noFilteringModeDescription": { - "message": "Листа имена хостова за које се неће вршити филтрирање", + "message": "Листа веб сајтова за које се неће вршити филтрирање", "description": "A short description for the editable field which lists trusted sites" }, "noFilteringModePlaceholder": { @@ -232,11 +232,11 @@ "description": "Label for a checkbox in the options page" }, "enableStrictBlockLabel": { - "message": "Enable strict blocking", + "message": "Омогући строго блокирање", "description": "Label for a checkbox in the options page" }, "enableStrictBlockLegend": { - "message": "Navigation to potentially undesirable sites will be blocked, and you will be offered the option to proceed.", + "message": "Навигација до потенцијално непожељних сајтова ће бити блокирана и биће вам понуђена опција да наставите.", "description": "Label for a checkbox in the options page" }, "findListsPlaceholder": { @@ -244,31 +244,31 @@ "description": "Placeholder for the input field used to find lists" }, "strictblockTitle": { - "message": "Page blocked", + "message": "Страница је блокирана", "description": "Webpage title for the strict-blocked page" }, "strictblockSentence1": { - "message": "uBO Lite has prevented the following page from loading:", + "message": "uBO Lite је спречио учитавање следеће странице:", "description": "Sentence used in the strict-blocked page" }, "strictblockNoParamsPrompt": { - "message": "without parameters", + "message": "без параметара", "description": "Label to be used for the parameter-less URL" }, "strictblockBack": { - "message": "Go back", + "message": "Иди назад", "description": "A button to go back to the previous webpage" }, "strictblockClose": { - "message": "Close this window", + "message": "Затвори овај прозор", "description": "A button to close the current tab" }, "strictblockDontWarn": { - "message": "Don't warn me again about this site", + "message": "Не упозоравај ме поново на ову страницу", "description": "Label for checkbox in document-blocked page" }, "strictblockProceed": { - "message": "Proceed", + "message": "Настави", "description": "A button to navigate to the blocked page" } } diff --git a/platform/mv3/extension/_locales/sv/messages.json b/platform/mv3/extension/_locales/sv/messages.json index 91715d6bac039..164a91ba3f326 100644 --- a/platform/mv3/extension/_locales/sv/messages.json +++ b/platform/mv3/extension/_locales/sv/messages.json @@ -232,11 +232,11 @@ "description": "Label for a checkbox in the options page" }, "enableStrictBlockLabel": { - "message": "Enable strict blocking", + "message": "Aktivera strikt blockering", "description": "Label for a checkbox in the options page" }, "enableStrictBlockLegend": { - "message": "Navigation to potentially undesirable sites will be blocked, and you will be offered the option to proceed.", + "message": "Navigering till potentiellt oönskade webbplatser kommer att blockeras och du kommer att erbjudas alternativet att fortsätta.", "description": "Label for a checkbox in the options page" }, "findListsPlaceholder": { @@ -244,31 +244,31 @@ "description": "Placeholder for the input field used to find lists" }, "strictblockTitle": { - "message": "Page blocked", + "message": "Sidan blockerad", "description": "Webpage title for the strict-blocked page" }, "strictblockSentence1": { - "message": "uBO Lite has prevented the following page from loading:", + "message": "uBO Lite har förhindrat följande sida från att läsas in:", "description": "Sentence used in the strict-blocked page" }, "strictblockNoParamsPrompt": { - "message": "without parameters", + "message": "utan parametrar", "description": "Label to be used for the parameter-less URL" }, "strictblockBack": { - "message": "Go back", + "message": "Gå tillbaka", "description": "A button to go back to the previous webpage" }, "strictblockClose": { - "message": "Close this window", + "message": "Stäng det här fönstret", "description": "A button to close the current tab" }, "strictblockDontWarn": { - "message": "Don't warn me again about this site", + "message": "Varna mig inte igen om den här sidan", "description": "Label for checkbox in document-blocked page" }, "strictblockProceed": { - "message": "Proceed", + "message": "Fortsätt", "description": "A button to navigate to the blocked page" } } diff --git a/platform/mv3/extension/_locales/tr/messages.json b/platform/mv3/extension/_locales/tr/messages.json index 5054aca0375b8..a3a6dacf5f140 100644 --- a/platform/mv3/extension/_locales/tr/messages.json +++ b/platform/mv3/extension/_locales/tr/messages.json @@ -232,11 +232,11 @@ "description": "Label for a checkbox in the options page" }, "enableStrictBlockLabel": { - "message": "Enable strict blocking", + "message": "Sıkı engellemeyi kullanıma al", "description": "Label for a checkbox in the options page" }, "enableStrictBlockLegend": { - "message": "Navigation to potentially undesirable sites will be blocked, and you will be offered the option to proceed.", + "message": "İstenmeyebilecek sitelere giriş engellenecek ve devam edip etmeyeceğiniz size sorulacak.", "description": "Label for a checkbox in the options page" }, "findListsPlaceholder": { @@ -244,31 +244,31 @@ "description": "Placeholder for the input field used to find lists" }, "strictblockTitle": { - "message": "Page blocked", + "message": "Sayfa engellendi", "description": "Webpage title for the strict-blocked page" }, "strictblockSentence1": { - "message": "uBO Lite has prevented the following page from loading:", + "message": "uBo Lite aşağıdaki sayfaların yüklenmesini engelledi:", "description": "Sentence used in the strict-blocked page" }, "strictblockNoParamsPrompt": { - "message": "without parameters", + "message": "değişkensiz", "description": "Label to be used for the parameter-less URL" }, "strictblockBack": { - "message": "Go back", + "message": "Geri git", "description": "A button to go back to the previous webpage" }, "strictblockClose": { - "message": "Close this window", + "message": "Bu pencereyi kapat", "description": "A button to close the current tab" }, "strictblockDontWarn": { - "message": "Don't warn me again about this site", + "message": "Bu site için beni bir daha uyarma", "description": "Label for checkbox in document-blocked page" }, "strictblockProceed": { - "message": "Proceed", + "message": "Devam et", "description": "A button to navigate to the blocked page" } } diff --git a/platform/mv3/extension/_locales/zh_CN/messages.json b/platform/mv3/extension/_locales/zh_CN/messages.json index 4f01ba20adee8..9fa3896976ec1 100644 --- a/platform/mv3/extension/_locales/zh_CN/messages.json +++ b/platform/mv3/extension/_locales/zh_CN/messages.json @@ -232,11 +232,11 @@ "description": "Label for a checkbox in the options page" }, "enableStrictBlockLabel": { - "message": "Enable strict blocking", + "message": "开启严格拦截", "description": "Label for a checkbox in the options page" }, "enableStrictBlockLegend": { - "message": "Navigation to potentially undesirable sites will be blocked, and you will be offered the option to proceed.", + "message": "导航至潜在不良网站的操作将被拦截,并且您将可以选择继续前往。", "description": "Label for a checkbox in the options page" }, "findListsPlaceholder": { @@ -244,31 +244,31 @@ "description": "Placeholder for the input field used to find lists" }, "strictblockTitle": { - "message": "Page blocked", + "message": "被拦截的页面", "description": "Webpage title for the strict-blocked page" }, "strictblockSentence1": { - "message": "uBO Lite has prevented the following page from loading:", + "message": "uBO Lite 已阻止了以下页面的加载:", "description": "Sentence used in the strict-blocked page" }, "strictblockNoParamsPrompt": { - "message": "without parameters", + "message": "不含参数", "description": "Label to be used for the parameter-less URL" }, "strictblockBack": { - "message": "Go back", + "message": "返回", "description": "A button to go back to the previous webpage" }, "strictblockClose": { - "message": "Close this window", + "message": "关闭此窗口", "description": "A button to close the current tab" }, "strictblockDontWarn": { - "message": "Don't warn me again about this site", + "message": "不要再警告我关于这个网站了", "description": "Label for checkbox in document-blocked page" }, "strictblockProceed": { - "message": "Proceed", + "message": "继续", "description": "A button to navigate to the blocked page" } } diff --git a/platform/mv3/extension/_locales/zh_TW/messages.json b/platform/mv3/extension/_locales/zh_TW/messages.json index 2aa06e3966568..5061d297175b6 100644 --- a/platform/mv3/extension/_locales/zh_TW/messages.json +++ b/platform/mv3/extension/_locales/zh_TW/messages.json @@ -108,7 +108,7 @@ "description": "Header of 'Report a filter issue' section in Support pane" }, "supportS3P1": { - "message": "回報特定網站的過濾器問題至 uBlockOrigin/uAssets 議題追蹤器需要 GitHub 帳號。", + "message": "將特定網站的過濾器問題回報至 uBlockOrigin/uAssets 議題追蹤器需要 GitHub 帳號。.", "description": "First paragraph of 'Filter issues' section in Support pane" }, "supportS6P1S1": { @@ -172,7 +172,7 @@ "description": "The header text for the welcome message section" }, "firstRunDescription": { - "message": "您剛安裝了 uBO Lite。您可以在此處選擇會套用在所有網站上的預設過濾模式。\n\n預設情況下,將會使用基礎模式,因為其不需要讀取與變更資料的權限。若您信任 uBO Lite,您可以給予其讀取並變更在所有網站上資料的廣泛權限,以便為所有網站啟用更進階的過濾功能。", + "message": "您剛安裝了 uBO Lite。您可以在此處選擇會套用在所有網站上的預設過濾模式。\n\n預設將會使用基礎模式,因為其不需要讀取與變更資料的權限。若您信任 uBO Lite,您可以給予其讀取並變更在所有網站上資料的廣泛權限,以便為所有網站啟用進階過濾功能。", "description": "Descriptive text shown at first install time only " }, "defaultFilteringModeSectionLabel": { @@ -192,7 +192,7 @@ "description": "Name of blocking mode 1" }, "filteringMode2Name": { - "message": "最佳化", + "message": "最佳", "description": "Name of blocking mode 2" }, "filteringMode3Name": { @@ -208,11 +208,11 @@ "description": "This describes the 'optimal' filtering mode" }, "completeFilteringModeDescription": { - "message": "進階網路過濾以及來自選定過濾條件清單的特定與通用擴展過濾條件。\n\n需要在所有網站上讀取並變更資料的廣泛權限。\n\n通用擴展過濾可能會導致更高的網頁資源使用率。", + "message": "進階網路過濾以及來自選定過濾清單的特定與通用擴展過濾條件。\n\n需要在所有網站上讀取並變更資料的廣泛權限。\n\n通用擴展過濾可能會導致使用更多的網頁資源。", "description": "This describes the 'complete' filtering mode" }, "noFilteringModeDescription": { - "message": "不進行過濾的主機名稱列表", + "message": "不會被過濾的網站", "description": "A short description for the editable field which lists trusted sites" }, "noFilteringModePlaceholder": { @@ -224,19 +224,19 @@ "description": "The header text for the 'Behavior' section" }, "autoReloadLabel": { - "message": "變更過濾模式時自動重新載入頁面", + "message": "變更過濾模式時,自動重新載入", "description": "Label for a checkbox in the options page" }, "showBlockedCountLabel": { - "message": "在工具列圖示上顯示被阻擋的連線請求的數量", + "message": "在工具列圖示上顯示被阻擋的網路請求數量", "description": "Label for a checkbox in the options page" }, "enableStrictBlockLabel": { - "message": "Enable strict blocking", + "message": "啟用嚴格阻擋", "description": "Label for a checkbox in the options page" }, "enableStrictBlockLegend": { - "message": "Navigation to potentially undesirable sites will be blocked, and you will be offered the option to proceed.", + "message": "將會阻擋前往潛在不良網站的請求,您可以選擇繼續。", "description": "Label for a checkbox in the options page" }, "findListsPlaceholder": { @@ -244,31 +244,31 @@ "description": "Placeholder for the input field used to find lists" }, "strictblockTitle": { - "message": "Page blocked", + "message": "已封鎖頁面", "description": "Webpage title for the strict-blocked page" }, "strictblockSentence1": { - "message": "uBO Lite has prevented the following page from loading:", + "message": "uBO Lite 已防止下列頁面載入:", "description": "Sentence used in the strict-blocked page" }, "strictblockNoParamsPrompt": { - "message": "without parameters", + "message": "不帶參數", "description": "Label to be used for the parameter-less URL" }, "strictblockBack": { - "message": "Go back", + "message": "返回", "description": "A button to go back to the previous webpage" }, "strictblockClose": { - "message": "Close this window", + "message": "關閉此視窗", "description": "A button to close the current tab" }, "strictblockDontWarn": { - "message": "Don't warn me again about this site", + "message": "不要再顯示關於此網站的警告", "description": "Label for checkbox in document-blocked page" }, "strictblockProceed": { - "message": "Proceed", + "message": "繼續", "description": "A button to navigate to the blocked page" } } diff --git a/src/_locales/sr/messages.json b/src/_locales/sr/messages.json index bf92e441d98e2..1e4f7d6d91df2 100644 --- a/src/_locales/sr/messages.json +++ b/src/_locales/sr/messages.json @@ -928,7 +928,7 @@ "description": "Header of 'Filter issues' section in Support pane" }, "supportS3P1": { - "message": "Пријавите проблеме са филтерима на одређеним веб сајтовима на uBlockOrigin/uAssets, страници за праћење проблема. Неопходан је GitHub налог.", + "message": "Пријавите проблеме са филтерима на одређеним веб сајтовима на uBlockOrigin/uAssets issue tracker. Неопходан је GitHub налог.", "description": "First paragraph of 'Filter issues' section in Support pane" }, "supportS3P2": { @@ -944,7 +944,7 @@ "description": "Header of 'Bug report' section in Support pane" }, "supportS4P1": { - "message": "Пријавите проблеме са самим uBlock Origin-ом на uBlockOrigin/uBlock-issue, страници за праћење проблема. Неопходан је GitHub налог.", + "message": "Пријавите проблеме са самим uBlock Origin-ом на uBlockOrigin/uBlock-issue issue tracker. Неопходан је GitHub налог.", "description": "First paragraph of 'Bug report' section in Support pane" }, "supportS5H": { @@ -1012,7 +1012,7 @@ "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option7": { - "message": "Leads to badware, phishing", + "message": "Води до лошег софтвера, „пецања\"", "description": "An entry in the widget used to select the type of issue" }, "supportS6Checkbox1": { @@ -1192,7 +1192,7 @@ "description": "Button text to navigate to the blocked page" }, "docblockedRedirectPrompt": { - "message": "The blocked page wants to redirect to another site. If you choose to proceed, you will navigate directly to: {{url}}", + "message": "Блокирана страница жели да преусмери на други сајт. Ако одлучите да наставите, ићи ћете директно на: {{url}}", "description": "Text warning about an incoming redirect" }, "cloudPush": { From 1a0331b7c2d5eb4c6ebc99ed383bb61408f8e10a Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Wed, 4 Dec 2024 19:44:23 -0500 Subject: [PATCH 0498/1099] New revision for dev build --- dist/version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dist/version b/dist/version index cf4063a4b0380..867a6909b5fbd 100644 --- a/dist/version +++ b/dist/version @@ -1 +1 @@ -1.61.3.2 \ No newline at end of file +1.61.3.3 \ No newline at end of file From 5eb44d01e50fd55a19bdded64789b3df1096df27 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Wed, 4 Dec 2024 19:45:06 -0500 Subject: [PATCH 0499/1099] Update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index ec88ef78c4ea0..1f406ffd55b87 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,4 @@ +- [Visually separate scriptlet parameters in active line](https://github.com/gorhill/uBlock/commit/076e9fa73e) - [Mitigate potentially delayed execution of scriptlets in Firefox](https://github.com/gorhill/uBlock/commit/b1a00145bd) - [Improve `prevent-setTimeout`/`prevent-setInterval` scriptlets](https://github.com/gorhill/uBlock/commit/3b7fa79a68) - [Improve `trusted-replace-argument` scriptlet](https://github.com/gorhill/uBlock/commit/adced29b5b) From f80143a8ee559befb629217c13b04e3827abacfa Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Wed, 4 Dec 2024 19:50:40 -0500 Subject: [PATCH 0500/1099] Make Firefox dev build auto-update --- dist/firefox/updates.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dist/firefox/updates.json b/dist/firefox/updates.json index 247f6252182f4..d123412a32903 100644 --- a/dist/firefox/updates.json +++ b/dist/firefox/updates.json @@ -3,9 +3,9 @@ "uBlock0@raymondhill.net": { "updates": [ { - "version": "1.61.3.2", + "version": "1.61.3.3", "browser_specific_settings": { "gecko": { "strict_min_version": "78.0" } }, - "update_link": "https://github.com/gorhill/uBlock/releases/download/1.61.3b2/uBlock0_1.61.3b2.firefox.signed.xpi" + "update_link": "https://github.com/gorhill/uBlock/releases/download/1.61.3b3/uBlock0_1.61.3b3.firefox.signed.xpi" } ] } From a86e802afc1f5623f61981fdd45e0d54d583688e Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Thu, 5 Dec 2024 09:04:31 -0500 Subject: [PATCH 0501/1099] Add advanced setting `noScriptingCSP` Related discussion: https://github.com/uBlockOrigin/uBlock-issues/issues/2642#issuecomment-2520096503 Specify which CSP directive to inject when no-scripting switch is toggled on. If this hidden setting is changed, uBO will not try to spoof `noscript` elements. For internal use at the moment, not to be documented. --- src/js/background.js | 2 +- src/js/messaging.js | 5 ++++- src/js/traffic.js | 2 +- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/js/background.js b/src/js/background.js index fa7e3f2e72bd4..01fd3b5d6b1ec 100644 --- a/src/js/background.js +++ b/src/js/background.js @@ -73,6 +73,7 @@ const hiddenSettingsDefault = { loggerPopupType: 'popup', manualUpdateAssetFetchPeriod: 500, modifyWebextFlavor: 'unset', + noScriptingCSP: 'script-src http: https:', popupFontSize: 'unset', popupPanelDisabledSections: 0, popupPanelHeightMode: 0, @@ -254,7 +255,6 @@ const µBlock = { // jshint ignore:line scriptlets: {}, cspNoInlineScript: "script-src 'unsafe-eval' * blob: data:", - cspNoScripting: 'script-src http: https:', cspNoInlineFont: 'font-src *', liveBlockingProfiles: [], diff --git a/src/js/messaging.js b/src/js/messaging.js index d35a1347e782e..16d527c60b48e 100644 --- a/src/js/messaging.js +++ b/src/js/messaging.js @@ -804,6 +804,9 @@ const onMessage = function(request, sender, callback) { case 'shouldRenderNoscriptTags': { if ( pageStore === null ) { break; } + if ( µb.hiddenSettings.noScriptingCSP !== µb.hiddenSettingsDefault.noScriptingCSP ) { + break; + } const fctxt = µb.filteringContext.fromTabId(sender.tabId); if ( pageStore.filterScripting(fctxt, undefined) ) { vAPI.tabs.executeScript(sender.tabId, { @@ -2009,7 +2012,7 @@ const logCSPViolations = function(pageStore, request) { fctxt.type = 'script'; fctxt.filter = undefined; if ( pageStore.filterScripting(fctxt, true) === 1 ) { - cspData.set(µb.cspNoScripting, fctxt.filter); + cspData.set(µb.hiddenSettings.noScriptingCSP, fctxt.filter); } fctxt.type = 'inline-font'; diff --git a/src/js/traffic.js b/src/js/traffic.js index 894554a804f70..df3b09714ff99 100644 --- a/src/js/traffic.js +++ b/src/js/traffic.js @@ -969,7 +969,7 @@ const injectCSP = function(fctxt, pageStore, responseHeaders) { const builtinDirectives = []; if ( pageStore.filterScripting(fctxt, true) === 1 ) { - builtinDirectives.push(µb.cspNoScripting); + builtinDirectives.push(µb.hiddenSettings.noScriptingCSP); if ( logger.enabled ) { fctxt.setRealm('network').setType('scripting').toLogger(); } From 37f7181a52b45beb3ed4e4eca83c1a37a8f54adb Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Thu, 5 Dec 2024 09:08:26 -0500 Subject: [PATCH 0502/1099] New revision for dev build --- dist/version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dist/version b/dist/version index 867a6909b5fbd..8a93a27b73714 100644 --- a/dist/version +++ b/dist/version @@ -1 +1 @@ -1.61.3.3 \ No newline at end of file +1.61.3.4 \ No newline at end of file From 07484ceed1486c9822187e0d2a541d9fb92225f3 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Thu, 5 Dec 2024 09:10:46 -0500 Subject: [PATCH 0503/1099] Import translation work from https://crowdin.com/project/ublock --- .../mv3/extension/_locales/id/messages.json | 20 +++++++++---------- .../mv3/extension/_locales/sq/messages.json | 18 ++++++++--------- .../mv3/extension/_locales/uk/messages.json | 18 ++++++++--------- src/_locales/id/messages.json | 2 +- 4 files changed, 29 insertions(+), 29 deletions(-) diff --git a/platform/mv3/extension/_locales/id/messages.json b/platform/mv3/extension/_locales/id/messages.json index 6b05ba7f9d2f2..f37442b5034a3 100644 --- a/platform/mv3/extension/_locales/id/messages.json +++ b/platform/mv3/extension/_locales/id/messages.json @@ -232,43 +232,43 @@ "description": "Label for a checkbox in the options page" }, "enableStrictBlockLabel": { - "message": "Enable strict blocking", + "message": "Aktifkan pemblokiran ketat", "description": "Label for a checkbox in the options page" }, "enableStrictBlockLegend": { - "message": "Navigation to potentially undesirable sites will be blocked, and you will be offered the option to proceed.", + "message": "Navigasi ke situs yang mungkin tidak diinginkan akan diblokir, dan Anda akan ditawari opsi untuk melanjutkan.", "description": "Label for a checkbox in the options page" }, "findListsPlaceholder": { - "message": "Find lists", + "message": "Temukan daftar", "description": "Placeholder for the input field used to find lists" }, "strictblockTitle": { - "message": "Page blocked", + "message": "Halaman diblokir", "description": "Webpage title for the strict-blocked page" }, "strictblockSentence1": { - "message": "uBO Lite has prevented the following page from loading:", + "message": "uBO Lite telah mencegah pemuatan halaman berikut:", "description": "Sentence used in the strict-blocked page" }, "strictblockNoParamsPrompt": { - "message": "without parameters", + "message": "tanpa parameter", "description": "Label to be used for the parameter-less URL" }, "strictblockBack": { - "message": "Go back", + "message": "Kembali", "description": "A button to go back to the previous webpage" }, "strictblockClose": { - "message": "Close this window", + "message": "Tutup jendela ini", "description": "A button to close the current tab" }, "strictblockDontWarn": { - "message": "Don't warn me again about this site", + "message": "Jangan peringatkan saya lagi tentang situs ini", "description": "Label for checkbox in document-blocked page" }, "strictblockProceed": { - "message": "Proceed", + "message": "Lanjutkan", "description": "A button to navigate to the blocked page" } } diff --git a/platform/mv3/extension/_locales/sq/messages.json b/platform/mv3/extension/_locales/sq/messages.json index 2f161843a43b9..2d881d1cf0d35 100644 --- a/platform/mv3/extension/_locales/sq/messages.json +++ b/platform/mv3/extension/_locales/sq/messages.json @@ -232,11 +232,11 @@ "description": "Label for a checkbox in the options page" }, "enableStrictBlockLabel": { - "message": "Enable strict blocking", + "message": "Aktivizo bllokimin strikt", "description": "Label for a checkbox in the options page" }, "enableStrictBlockLegend": { - "message": "Navigation to potentially undesirable sites will be blocked, and you will be offered the option to proceed.", + "message": "Lundrimi drejt faqeve potencialisht të padëshirueshme do të bllokohet dhe do t'ju ofrohet mundësia për të vazhduar.", "description": "Label for a checkbox in the options page" }, "findListsPlaceholder": { @@ -244,31 +244,31 @@ "description": "Placeholder for the input field used to find lists" }, "strictblockTitle": { - "message": "Page blocked", + "message": "Faqe e bllokuar", "description": "Webpage title for the strict-blocked page" }, "strictblockSentence1": { - "message": "uBO Lite has prevented the following page from loading:", + "message": "uMatrix ka penguar ngarkimin e faqes vijuese:", "description": "Sentence used in the strict-blocked page" }, "strictblockNoParamsPrompt": { - "message": "without parameters", + "message": "pa parametra", "description": "Label to be used for the parameter-less URL" }, "strictblockBack": { - "message": "Go back", + "message": "Mbrapa", "description": "A button to go back to the previous webpage" }, "strictblockClose": { - "message": "Close this window", + "message": "Mbyll këtë dritare", "description": "A button to close the current tab" }, "strictblockDontWarn": { - "message": "Don't warn me again about this site", + "message": "Mos më paralajmëro më për këtë faqe.", "description": "Label for checkbox in document-blocked page" }, "strictblockProceed": { - "message": "Proceed", + "message": "Vazhdo", "description": "A button to navigate to the blocked page" } } diff --git a/platform/mv3/extension/_locales/uk/messages.json b/platform/mv3/extension/_locales/uk/messages.json index 98cac736f3004..fbd534207c1c3 100644 --- a/platform/mv3/extension/_locales/uk/messages.json +++ b/platform/mv3/extension/_locales/uk/messages.json @@ -232,11 +232,11 @@ "description": "Label for a checkbox in the options page" }, "enableStrictBlockLabel": { - "message": "Enable strict blocking", + "message": "Увімкнути суворе блокування", "description": "Label for a checkbox in the options page" }, "enableStrictBlockLegend": { - "message": "Navigation to potentially undesirable sites will be blocked, and you will be offered the option to proceed.", + "message": "Перехід до потенційно небажаних сайтів буде заблоковано, та вам буде запропоновано можливість продовжити", "description": "Label for a checkbox in the options page" }, "findListsPlaceholder": { @@ -244,31 +244,31 @@ "description": "Placeholder for the input field used to find lists" }, "strictblockTitle": { - "message": "Page blocked", + "message": "Сторінку заблоковано", "description": "Webpage title for the strict-blocked page" }, "strictblockSentence1": { - "message": "uBO Lite has prevented the following page from loading:", + "message": "uBO Lite заблокував завантаження наступних сторінок:", "description": "Sentence used in the strict-blocked page" }, "strictblockNoParamsPrompt": { - "message": "without parameters", + "message": "без параметрів", "description": "Label to be used for the parameter-less URL" }, "strictblockBack": { - "message": "Go back", + "message": "Повернутися", "description": "A button to go back to the previous webpage" }, "strictblockClose": { - "message": "Close this window", + "message": "Закрити це вікно", "description": "A button to close the current tab" }, "strictblockDontWarn": { - "message": "Don't warn me again about this site", + "message": "Більше не попереджати мене про цей сайт", "description": "Label for checkbox in document-blocked page" }, "strictblockProceed": { - "message": "Proceed", + "message": "Продовжити", "description": "A button to navigate to the blocked page" } } diff --git a/src/_locales/id/messages.json b/src/_locales/id/messages.json index 493b6210bbe57..38616c1449587 100644 --- a/src/_locales/id/messages.json +++ b/src/_locales/id/messages.json @@ -1192,7 +1192,7 @@ "description": "Button text to navigate to the blocked page" }, "docblockedRedirectPrompt": { - "message": "The blocked page wants to redirect to another site. If you choose to proceed, you will navigate directly to: {{url}}", + "message": "Halaman yang terblokir ingin dialihkan ke situs lain. Jika Anda memilih untuk melanjutkan, Anda akan langsung menuju ke: {{url}}", "description": "Text warning about an incoming redirect" }, "cloudPush": { From fd2ddd3c01c4033eed0d14dbe11ec543e16a3ee2 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Thu, 5 Dec 2024 09:12:28 -0500 Subject: [PATCH 0504/1099] Import translation work from https://crowdin.com/project/ublock --- platform/mv3/extension/_locales/ar/messages.json | 8 ++++++++ platform/mv3/extension/_locales/az/messages.json | 8 ++++++++ platform/mv3/extension/_locales/be/messages.json | 8 ++++++++ platform/mv3/extension/_locales/bg/messages.json | 8 ++++++++ platform/mv3/extension/_locales/bn/messages.json | 8 ++++++++ platform/mv3/extension/_locales/br_FR/messages.json | 8 ++++++++ platform/mv3/extension/_locales/bs/messages.json | 8 ++++++++ platform/mv3/extension/_locales/ca/messages.json | 8 ++++++++ platform/mv3/extension/_locales/cs/messages.json | 8 ++++++++ platform/mv3/extension/_locales/cv/messages.json | 8 ++++++++ platform/mv3/extension/_locales/cy/messages.json | 8 ++++++++ platform/mv3/extension/_locales/da/messages.json | 8 ++++++++ platform/mv3/extension/_locales/de/messages.json | 8 ++++++++ platform/mv3/extension/_locales/el/messages.json | 8 ++++++++ platform/mv3/extension/_locales/en_GB/messages.json | 8 ++++++++ platform/mv3/extension/_locales/eo/messages.json | 8 ++++++++ platform/mv3/extension/_locales/es/messages.json | 8 ++++++++ platform/mv3/extension/_locales/et/messages.json | 8 ++++++++ platform/mv3/extension/_locales/eu/messages.json | 8 ++++++++ platform/mv3/extension/_locales/fa/messages.json | 8 ++++++++ platform/mv3/extension/_locales/fi/messages.json | 8 ++++++++ platform/mv3/extension/_locales/fil/messages.json | 8 ++++++++ platform/mv3/extension/_locales/fr/messages.json | 8 ++++++++ platform/mv3/extension/_locales/fy/messages.json | 8 ++++++++ platform/mv3/extension/_locales/gl/messages.json | 8 ++++++++ platform/mv3/extension/_locales/gu/messages.json | 8 ++++++++ platform/mv3/extension/_locales/he/messages.json | 8 ++++++++ platform/mv3/extension/_locales/hi/messages.json | 8 ++++++++ platform/mv3/extension/_locales/hr/messages.json | 8 ++++++++ platform/mv3/extension/_locales/hu/messages.json | 8 ++++++++ platform/mv3/extension/_locales/hy/messages.json | 8 ++++++++ platform/mv3/extension/_locales/id/messages.json | 8 ++++++++ platform/mv3/extension/_locales/it/messages.json | 8 ++++++++ platform/mv3/extension/_locales/ja/messages.json | 8 ++++++++ platform/mv3/extension/_locales/ka/messages.json | 8 ++++++++ platform/mv3/extension/_locales/kk/messages.json | 8 ++++++++ platform/mv3/extension/_locales/kn/messages.json | 8 ++++++++ platform/mv3/extension/_locales/ko/messages.json | 8 ++++++++ platform/mv3/extension/_locales/lt/messages.json | 8 ++++++++ platform/mv3/extension/_locales/lv/messages.json | 8 ++++++++ platform/mv3/extension/_locales/mk/messages.json | 8 ++++++++ platform/mv3/extension/_locales/ml/messages.json | 8 ++++++++ platform/mv3/extension/_locales/mr/messages.json | 8 ++++++++ platform/mv3/extension/_locales/ms/messages.json | 8 ++++++++ platform/mv3/extension/_locales/nb/messages.json | 8 ++++++++ platform/mv3/extension/_locales/nl/messages.json | 8 ++++++++ platform/mv3/extension/_locales/oc/messages.json | 8 ++++++++ platform/mv3/extension/_locales/pa/messages.json | 8 ++++++++ platform/mv3/extension/_locales/pl/messages.json | 8 ++++++++ platform/mv3/extension/_locales/pt_BR/messages.json | 8 ++++++++ platform/mv3/extension/_locales/pt_PT/messages.json | 8 ++++++++ platform/mv3/extension/_locales/ro/messages.json | 8 ++++++++ platform/mv3/extension/_locales/ru/messages.json | 8 ++++++++ platform/mv3/extension/_locales/si/messages.json | 8 ++++++++ platform/mv3/extension/_locales/sk/messages.json | 8 ++++++++ platform/mv3/extension/_locales/sl/messages.json | 8 ++++++++ platform/mv3/extension/_locales/so/messages.json | 8 ++++++++ platform/mv3/extension/_locales/sq/messages.json | 8 ++++++++ platform/mv3/extension/_locales/sr/messages.json | 8 ++++++++ platform/mv3/extension/_locales/sv/messages.json | 8 ++++++++ platform/mv3/extension/_locales/sw/messages.json | 8 ++++++++ platform/mv3/extension/_locales/ta/messages.json | 8 ++++++++ platform/mv3/extension/_locales/te/messages.json | 8 ++++++++ platform/mv3/extension/_locales/th/messages.json | 8 ++++++++ platform/mv3/extension/_locales/tr/messages.json | 8 ++++++++ platform/mv3/extension/_locales/uk/messages.json | 8 ++++++++ platform/mv3/extension/_locales/ur/messages.json | 8 ++++++++ platform/mv3/extension/_locales/vi/messages.json | 8 ++++++++ platform/mv3/extension/_locales/zh_CN/messages.json | 8 ++++++++ platform/mv3/extension/_locales/zh_TW/messages.json | 8 ++++++++ 70 files changed, 560 insertions(+) diff --git a/platform/mv3/extension/_locales/ar/messages.json b/platform/mv3/extension/_locales/ar/messages.json index b4281ab21df17..6a2aac1b594a3 100644 --- a/platform/mv3/extension/_locales/ar/messages.json +++ b/platform/mv3/extension/_locales/ar/messages.json @@ -251,6 +251,14 @@ "message": "لقد منع uBO Lite تحميل الصفحة التالية:", "description": "Sentence used in the strict-blocked page" }, + "strictblockReasonSentence1": { + "message": "The page was blocked because of a matching filter in {{listname}}.", + "description": "Text informing about what is causing the page to be blocked" + }, + "strictblockRedirectSentence1": { + "message": "The blocked page wants to redirect to another site. If you choose to proceed, you will navigate directly to: {{url}}", + "description": "Text warning about an incoming redirect" + }, "strictblockNoParamsPrompt": { "message": "without parameters", "description": "Label to be used for the parameter-less URL" diff --git a/platform/mv3/extension/_locales/az/messages.json b/platform/mv3/extension/_locales/az/messages.json index ef0b36678e2d8..8172ac24b5bf2 100644 --- a/platform/mv3/extension/_locales/az/messages.json +++ b/platform/mv3/extension/_locales/az/messages.json @@ -251,6 +251,14 @@ "message": "uBO Lite has prevented the following page from loading:", "description": "Sentence used in the strict-blocked page" }, + "strictblockReasonSentence1": { + "message": "The page was blocked because of a matching filter in {{listname}}.", + "description": "Text informing about what is causing the page to be blocked" + }, + "strictblockRedirectSentence1": { + "message": "The blocked page wants to redirect to another site. If you choose to proceed, you will navigate directly to: {{url}}", + "description": "Text warning about an incoming redirect" + }, "strictblockNoParamsPrompt": { "message": "without parameters", "description": "Label to be used for the parameter-less URL" diff --git a/platform/mv3/extension/_locales/be/messages.json b/platform/mv3/extension/_locales/be/messages.json index 9ca2a032414e0..ebed0a2a7309d 100644 --- a/platform/mv3/extension/_locales/be/messages.json +++ b/platform/mv3/extension/_locales/be/messages.json @@ -251,6 +251,14 @@ "message": "uBO Lite папярэдзіў чытанне наступнай старонкі:", "description": "Sentence used in the strict-blocked page" }, + "strictblockReasonSentence1": { + "message": "The page was blocked because of a matching filter in {{listname}}.", + "description": "Text informing about what is causing the page to be blocked" + }, + "strictblockRedirectSentence1": { + "message": "The blocked page wants to redirect to another site. If you choose to proceed, you will navigate directly to: {{url}}", + "description": "Text warning about an incoming redirect" + }, "strictblockNoParamsPrompt": { "message": "без параметраў", "description": "Label to be used for the parameter-less URL" diff --git a/platform/mv3/extension/_locales/bg/messages.json b/platform/mv3/extension/_locales/bg/messages.json index 8ebf6cbd586a2..c312968671465 100644 --- a/platform/mv3/extension/_locales/bg/messages.json +++ b/platform/mv3/extension/_locales/bg/messages.json @@ -251,6 +251,14 @@ "message": "uBO Lite попречи на зареждането на следната страница:", "description": "Sentence used in the strict-blocked page" }, + "strictblockReasonSentence1": { + "message": "The page was blocked because of a matching filter in {{listname}}.", + "description": "Text informing about what is causing the page to be blocked" + }, + "strictblockRedirectSentence1": { + "message": "The blocked page wants to redirect to another site. If you choose to proceed, you will navigate directly to: {{url}}", + "description": "Text warning about an incoming redirect" + }, "strictblockNoParamsPrompt": { "message": "без параметри", "description": "Label to be used for the parameter-less URL" diff --git a/platform/mv3/extension/_locales/bn/messages.json b/platform/mv3/extension/_locales/bn/messages.json index a9af81782712f..9087a0ae719d8 100644 --- a/platform/mv3/extension/_locales/bn/messages.json +++ b/platform/mv3/extension/_locales/bn/messages.json @@ -251,6 +251,14 @@ "message": "uBO Lite has prevented the following page from loading:", "description": "Sentence used in the strict-blocked page" }, + "strictblockReasonSentence1": { + "message": "The page was blocked because of a matching filter in {{listname}}.", + "description": "Text informing about what is causing the page to be blocked" + }, + "strictblockRedirectSentence1": { + "message": "The blocked page wants to redirect to another site. If you choose to proceed, you will navigate directly to: {{url}}", + "description": "Text warning about an incoming redirect" + }, "strictblockNoParamsPrompt": { "message": "without parameters", "description": "Label to be used for the parameter-less URL" diff --git a/platform/mv3/extension/_locales/br_FR/messages.json b/platform/mv3/extension/_locales/br_FR/messages.json index 2b5784900f48f..9fd91dcd3ba6f 100644 --- a/platform/mv3/extension/_locales/br_FR/messages.json +++ b/platform/mv3/extension/_locales/br_FR/messages.json @@ -251,6 +251,14 @@ "message": "uBO Lite en deus miret ar bajenn-mañ da gargañ:", "description": "Sentence used in the strict-blocked page" }, + "strictblockReasonSentence1": { + "message": "The page was blocked because of a matching filter in {{listname}}.", + "description": "Text informing about what is causing the page to be blocked" + }, + "strictblockRedirectSentence1": { + "message": "The blocked page wants to redirect to another site. If you choose to proceed, you will navigate directly to: {{url}}", + "description": "Text warning about an incoming redirect" + }, "strictblockNoParamsPrompt": { "message": "kuit a arventennoù", "description": "Label to be used for the parameter-less URL" diff --git a/platform/mv3/extension/_locales/bs/messages.json b/platform/mv3/extension/_locales/bs/messages.json index a913ad1482aa7..20c51af871ca1 100644 --- a/platform/mv3/extension/_locales/bs/messages.json +++ b/platform/mv3/extension/_locales/bs/messages.json @@ -251,6 +251,14 @@ "message": "uBO Lite has prevented the following page from loading:", "description": "Sentence used in the strict-blocked page" }, + "strictblockReasonSentence1": { + "message": "The page was blocked because of a matching filter in {{listname}}.", + "description": "Text informing about what is causing the page to be blocked" + }, + "strictblockRedirectSentence1": { + "message": "The blocked page wants to redirect to another site. If you choose to proceed, you will navigate directly to: {{url}}", + "description": "Text warning about an incoming redirect" + }, "strictblockNoParamsPrompt": { "message": "without parameters", "description": "Label to be used for the parameter-less URL" diff --git a/platform/mv3/extension/_locales/ca/messages.json b/platform/mv3/extension/_locales/ca/messages.json index 96bbd7029e32b..070b24a5b38bf 100644 --- a/platform/mv3/extension/_locales/ca/messages.json +++ b/platform/mv3/extension/_locales/ca/messages.json @@ -251,6 +251,14 @@ "message": "L'uBO Lite ha evitat la càrrega d'aquesta pàgina:", "description": "Sentence used in the strict-blocked page" }, + "strictblockReasonSentence1": { + "message": "The page was blocked because of a matching filter in {{listname}}.", + "description": "Text informing about what is causing the page to be blocked" + }, + "strictblockRedirectSentence1": { + "message": "The blocked page wants to redirect to another site. If you choose to proceed, you will navigate directly to: {{url}}", + "description": "Text warning about an incoming redirect" + }, "strictblockNoParamsPrompt": { "message": "sense paràmetres", "description": "Label to be used for the parameter-less URL" diff --git a/platform/mv3/extension/_locales/cs/messages.json b/platform/mv3/extension/_locales/cs/messages.json index ea9251418cf05..585475287b5a5 100644 --- a/platform/mv3/extension/_locales/cs/messages.json +++ b/platform/mv3/extension/_locales/cs/messages.json @@ -251,6 +251,14 @@ "message": "uBO Lite has prevented the following page from loading:", "description": "Sentence used in the strict-blocked page" }, + "strictblockReasonSentence1": { + "message": "The page was blocked because of a matching filter in {{listname}}.", + "description": "Text informing about what is causing the page to be blocked" + }, + "strictblockRedirectSentence1": { + "message": "The blocked page wants to redirect to another site. If you choose to proceed, you will navigate directly to: {{url}}", + "description": "Text warning about an incoming redirect" + }, "strictblockNoParamsPrompt": { "message": "without parameters", "description": "Label to be used for the parameter-less URL" diff --git a/platform/mv3/extension/_locales/cv/messages.json b/platform/mv3/extension/_locales/cv/messages.json index 1c200f3653e00..515422f202c23 100644 --- a/platform/mv3/extension/_locales/cv/messages.json +++ b/platform/mv3/extension/_locales/cv/messages.json @@ -251,6 +251,14 @@ "message": "uBO Lite has prevented the following page from loading:", "description": "Sentence used in the strict-blocked page" }, + "strictblockReasonSentence1": { + "message": "The page was blocked because of a matching filter in {{listname}}.", + "description": "Text informing about what is causing the page to be blocked" + }, + "strictblockRedirectSentence1": { + "message": "The blocked page wants to redirect to another site. If you choose to proceed, you will navigate directly to: {{url}}", + "description": "Text warning about an incoming redirect" + }, "strictblockNoParamsPrompt": { "message": "without parameters", "description": "Label to be used for the parameter-less URL" diff --git a/platform/mv3/extension/_locales/cy/messages.json b/platform/mv3/extension/_locales/cy/messages.json index 963338c56b06e..e8f7e1cb5ba9a 100644 --- a/platform/mv3/extension/_locales/cy/messages.json +++ b/platform/mv3/extension/_locales/cy/messages.json @@ -251,6 +251,14 @@ "message": "uBO Lite has prevented the following page from loading:", "description": "Sentence used in the strict-blocked page" }, + "strictblockReasonSentence1": { + "message": "The page was blocked because of a matching filter in {{listname}}.", + "description": "Text informing about what is causing the page to be blocked" + }, + "strictblockRedirectSentence1": { + "message": "The blocked page wants to redirect to another site. If you choose to proceed, you will navigate directly to: {{url}}", + "description": "Text warning about an incoming redirect" + }, "strictblockNoParamsPrompt": { "message": "without parameters", "description": "Label to be used for the parameter-less URL" diff --git a/platform/mv3/extension/_locales/da/messages.json b/platform/mv3/extension/_locales/da/messages.json index 63efbe0d83cd9..25b24263ac287 100644 --- a/platform/mv3/extension/_locales/da/messages.json +++ b/platform/mv3/extension/_locales/da/messages.json @@ -251,6 +251,14 @@ "message": "uBO Lite har forhindret flg. side i at blive indlæst:", "description": "Sentence used in the strict-blocked page" }, + "strictblockReasonSentence1": { + "message": "The page was blocked because of a matching filter in {{listname}}.", + "description": "Text informing about what is causing the page to be blocked" + }, + "strictblockRedirectSentence1": { + "message": "The blocked page wants to redirect to another site. If you choose to proceed, you will navigate directly to: {{url}}", + "description": "Text warning about an incoming redirect" + }, "strictblockNoParamsPrompt": { "message": "uden parametre", "description": "Label to be used for the parameter-less URL" diff --git a/platform/mv3/extension/_locales/de/messages.json b/platform/mv3/extension/_locales/de/messages.json index 32c14d1ca49df..dacd1edcf9793 100644 --- a/platform/mv3/extension/_locales/de/messages.json +++ b/platform/mv3/extension/_locales/de/messages.json @@ -251,6 +251,14 @@ "message": "uBO Lite hat das Laden der folgenden Seite verhindert:", "description": "Sentence used in the strict-blocked page" }, + "strictblockReasonSentence1": { + "message": "The page was blocked because of a matching filter in {{listname}}.", + "description": "Text informing about what is causing the page to be blocked" + }, + "strictblockRedirectSentence1": { + "message": "The blocked page wants to redirect to another site. If you choose to proceed, you will navigate directly to: {{url}}", + "description": "Text warning about an incoming redirect" + }, "strictblockNoParamsPrompt": { "message": "ohne Parameter", "description": "Label to be used for the parameter-less URL" diff --git a/platform/mv3/extension/_locales/el/messages.json b/platform/mv3/extension/_locales/el/messages.json index 5b11ba4ceca0d..1bfb831e66ad6 100644 --- a/platform/mv3/extension/_locales/el/messages.json +++ b/platform/mv3/extension/_locales/el/messages.json @@ -251,6 +251,14 @@ "message": "uBO Lite has prevented the following page from loading:", "description": "Sentence used in the strict-blocked page" }, + "strictblockReasonSentence1": { + "message": "The page was blocked because of a matching filter in {{listname}}.", + "description": "Text informing about what is causing the page to be blocked" + }, + "strictblockRedirectSentence1": { + "message": "The blocked page wants to redirect to another site. If you choose to proceed, you will navigate directly to: {{url}}", + "description": "Text warning about an incoming redirect" + }, "strictblockNoParamsPrompt": { "message": "without parameters", "description": "Label to be used for the parameter-less URL" diff --git a/platform/mv3/extension/_locales/en_GB/messages.json b/platform/mv3/extension/_locales/en_GB/messages.json index 1ebff08aef9ce..ff21a6e83edec 100644 --- a/platform/mv3/extension/_locales/en_GB/messages.json +++ b/platform/mv3/extension/_locales/en_GB/messages.json @@ -251,6 +251,14 @@ "message": "uBO Lite has prevented the following page from loading:", "description": "Sentence used in the strict-blocked page" }, + "strictblockReasonSentence1": { + "message": "The page was blocked because of a matching filter in {{listname}}.", + "description": "Text informing about what is causing the page to be blocked" + }, + "strictblockRedirectSentence1": { + "message": "The blocked page wants to redirect to another site. If you choose to proceed, you will navigate directly to: {{url}}", + "description": "Text warning about an incoming redirect" + }, "strictblockNoParamsPrompt": { "message": "without parameters", "description": "Label to be used for the parameter-less URL" diff --git a/platform/mv3/extension/_locales/eo/messages.json b/platform/mv3/extension/_locales/eo/messages.json index 4c75658ccd218..ed989e8b46d37 100644 --- a/platform/mv3/extension/_locales/eo/messages.json +++ b/platform/mv3/extension/_locales/eo/messages.json @@ -251,6 +251,14 @@ "message": "uBO Lite has prevented the following page from loading:", "description": "Sentence used in the strict-blocked page" }, + "strictblockReasonSentence1": { + "message": "The page was blocked because of a matching filter in {{listname}}.", + "description": "Text informing about what is causing the page to be blocked" + }, + "strictblockRedirectSentence1": { + "message": "The blocked page wants to redirect to another site. If you choose to proceed, you will navigate directly to: {{url}}", + "description": "Text warning about an incoming redirect" + }, "strictblockNoParamsPrompt": { "message": "without parameters", "description": "Label to be used for the parameter-less URL" diff --git a/platform/mv3/extension/_locales/es/messages.json b/platform/mv3/extension/_locales/es/messages.json index 6bf520e79b2b9..027bb7094e644 100644 --- a/platform/mv3/extension/_locales/es/messages.json +++ b/platform/mv3/extension/_locales/es/messages.json @@ -251,6 +251,14 @@ "message": "uBO Lite impidió la carga de la página:", "description": "Sentence used in the strict-blocked page" }, + "strictblockReasonSentence1": { + "message": "The page was blocked because of a matching filter in {{listname}}.", + "description": "Text informing about what is causing the page to be blocked" + }, + "strictblockRedirectSentence1": { + "message": "The blocked page wants to redirect to another site. If you choose to proceed, you will navigate directly to: {{url}}", + "description": "Text warning about an incoming redirect" + }, "strictblockNoParamsPrompt": { "message": "sin parámetros", "description": "Label to be used for the parameter-less URL" diff --git a/platform/mv3/extension/_locales/et/messages.json b/platform/mv3/extension/_locales/et/messages.json index 3b7ced5a48785..9d498685a3a50 100644 --- a/platform/mv3/extension/_locales/et/messages.json +++ b/platform/mv3/extension/_locales/et/messages.json @@ -251,6 +251,14 @@ "message": "uBO Lite ennetas järgmise veebilehe laadimist:", "description": "Sentence used in the strict-blocked page" }, + "strictblockReasonSentence1": { + "message": "The page was blocked because of a matching filter in {{listname}}.", + "description": "Text informing about what is causing the page to be blocked" + }, + "strictblockRedirectSentence1": { + "message": "The blocked page wants to redirect to another site. If you choose to proceed, you will navigate directly to: {{url}}", + "description": "Text warning about an incoming redirect" + }, "strictblockNoParamsPrompt": { "message": "ilma näitajateta", "description": "Label to be used for the parameter-less URL" diff --git a/platform/mv3/extension/_locales/eu/messages.json b/platform/mv3/extension/_locales/eu/messages.json index 4d026da6bc9fe..812e5eada31ca 100644 --- a/platform/mv3/extension/_locales/eu/messages.json +++ b/platform/mv3/extension/_locales/eu/messages.json @@ -251,6 +251,14 @@ "message": "uBO Lite has prevented the following page from loading:", "description": "Sentence used in the strict-blocked page" }, + "strictblockReasonSentence1": { + "message": "The page was blocked because of a matching filter in {{listname}}.", + "description": "Text informing about what is causing the page to be blocked" + }, + "strictblockRedirectSentence1": { + "message": "The blocked page wants to redirect to another site. If you choose to proceed, you will navigate directly to: {{url}}", + "description": "Text warning about an incoming redirect" + }, "strictblockNoParamsPrompt": { "message": "without parameters", "description": "Label to be used for the parameter-less URL" diff --git a/platform/mv3/extension/_locales/fa/messages.json b/platform/mv3/extension/_locales/fa/messages.json index 276405ceb51a7..e9b868325b9e9 100644 --- a/platform/mv3/extension/_locales/fa/messages.json +++ b/platform/mv3/extension/_locales/fa/messages.json @@ -251,6 +251,14 @@ "message": "uBO Lite has prevented the following page from loading:", "description": "Sentence used in the strict-blocked page" }, + "strictblockReasonSentence1": { + "message": "The page was blocked because of a matching filter in {{listname}}.", + "description": "Text informing about what is causing the page to be blocked" + }, + "strictblockRedirectSentence1": { + "message": "The blocked page wants to redirect to another site. If you choose to proceed, you will navigate directly to: {{url}}", + "description": "Text warning about an incoming redirect" + }, "strictblockNoParamsPrompt": { "message": "without parameters", "description": "Label to be used for the parameter-less URL" diff --git a/platform/mv3/extension/_locales/fi/messages.json b/platform/mv3/extension/_locales/fi/messages.json index 963a50c6900ce..761093b972b74 100644 --- a/platform/mv3/extension/_locales/fi/messages.json +++ b/platform/mv3/extension/_locales/fi/messages.json @@ -251,6 +251,14 @@ "message": "uBO Lite on estänyt seuraavan sivun latauksen:", "description": "Sentence used in the strict-blocked page" }, + "strictblockReasonSentence1": { + "message": "The page was blocked because of a matching filter in {{listname}}.", + "description": "Text informing about what is causing the page to be blocked" + }, + "strictblockRedirectSentence1": { + "message": "The blocked page wants to redirect to another site. If you choose to proceed, you will navigate directly to: {{url}}", + "description": "Text warning about an incoming redirect" + }, "strictblockNoParamsPrompt": { "message": "ilman parametreja", "description": "Label to be used for the parameter-less URL" diff --git a/platform/mv3/extension/_locales/fil/messages.json b/platform/mv3/extension/_locales/fil/messages.json index 3fb3e29541216..9e5b7cece38a8 100644 --- a/platform/mv3/extension/_locales/fil/messages.json +++ b/platform/mv3/extension/_locales/fil/messages.json @@ -251,6 +251,14 @@ "message": "uBO Lite has prevented the following page from loading:", "description": "Sentence used in the strict-blocked page" }, + "strictblockReasonSentence1": { + "message": "The page was blocked because of a matching filter in {{listname}}.", + "description": "Text informing about what is causing the page to be blocked" + }, + "strictblockRedirectSentence1": { + "message": "The blocked page wants to redirect to another site. If you choose to proceed, you will navigate directly to: {{url}}", + "description": "Text warning about an incoming redirect" + }, "strictblockNoParamsPrompt": { "message": "without parameters", "description": "Label to be used for the parameter-less URL" diff --git a/platform/mv3/extension/_locales/fr/messages.json b/platform/mv3/extension/_locales/fr/messages.json index a08099a95f8aa..4bba2ac8f412c 100644 --- a/platform/mv3/extension/_locales/fr/messages.json +++ b/platform/mv3/extension/_locales/fr/messages.json @@ -251,6 +251,14 @@ "message": "uBO Lite a empêché le chargement de cette page :", "description": "Sentence used in the strict-blocked page" }, + "strictblockReasonSentence1": { + "message": "The page was blocked because of a matching filter in {{listname}}.", + "description": "Text informing about what is causing the page to be blocked" + }, + "strictblockRedirectSentence1": { + "message": "The blocked page wants to redirect to another site. If you choose to proceed, you will navigate directly to: {{url}}", + "description": "Text warning about an incoming redirect" + }, "strictblockNoParamsPrompt": { "message": "sans paramètres", "description": "Label to be used for the parameter-less URL" diff --git a/platform/mv3/extension/_locales/fy/messages.json b/platform/mv3/extension/_locales/fy/messages.json index f570ed2c0ed19..64b72057bbfb3 100644 --- a/platform/mv3/extension/_locales/fy/messages.json +++ b/platform/mv3/extension/_locales/fy/messages.json @@ -251,6 +251,14 @@ "message": "uBO Lite hat opkeard dat de folgjende side laden wurd:", "description": "Sentence used in the strict-blocked page" }, + "strictblockReasonSentence1": { + "message": "The page was blocked because of a matching filter in {{listname}}.", + "description": "Text informing about what is causing the page to be blocked" + }, + "strictblockRedirectSentence1": { + "message": "The blocked page wants to redirect to another site. If you choose to proceed, you will navigate directly to: {{url}}", + "description": "Text warning about an incoming redirect" + }, "strictblockNoParamsPrompt": { "message": "sûnder parameters", "description": "Label to be used for the parameter-less URL" diff --git a/platform/mv3/extension/_locales/gl/messages.json b/platform/mv3/extension/_locales/gl/messages.json index 3054c798c8c3b..67f783e282dc1 100644 --- a/platform/mv3/extension/_locales/gl/messages.json +++ b/platform/mv3/extension/_locales/gl/messages.json @@ -251,6 +251,14 @@ "message": "uBO Lite evitou que a seguinte páxina se cargase:", "description": "Sentence used in the strict-blocked page" }, + "strictblockReasonSentence1": { + "message": "The page was blocked because of a matching filter in {{listname}}.", + "description": "Text informing about what is causing the page to be blocked" + }, + "strictblockRedirectSentence1": { + "message": "The blocked page wants to redirect to another site. If you choose to proceed, you will navigate directly to: {{url}}", + "description": "Text warning about an incoming redirect" + }, "strictblockNoParamsPrompt": { "message": "sen parámetros", "description": "Label to be used for the parameter-less URL" diff --git a/platform/mv3/extension/_locales/gu/messages.json b/platform/mv3/extension/_locales/gu/messages.json index 1c200f3653e00..515422f202c23 100644 --- a/platform/mv3/extension/_locales/gu/messages.json +++ b/platform/mv3/extension/_locales/gu/messages.json @@ -251,6 +251,14 @@ "message": "uBO Lite has prevented the following page from loading:", "description": "Sentence used in the strict-blocked page" }, + "strictblockReasonSentence1": { + "message": "The page was blocked because of a matching filter in {{listname}}.", + "description": "Text informing about what is causing the page to be blocked" + }, + "strictblockRedirectSentence1": { + "message": "The blocked page wants to redirect to another site. If you choose to proceed, you will navigate directly to: {{url}}", + "description": "Text warning about an incoming redirect" + }, "strictblockNoParamsPrompt": { "message": "without parameters", "description": "Label to be used for the parameter-less URL" diff --git a/platform/mv3/extension/_locales/he/messages.json b/platform/mv3/extension/_locales/he/messages.json index ee12c27ad7591..8a62a294e1745 100644 --- a/platform/mv3/extension/_locales/he/messages.json +++ b/platform/mv3/extension/_locales/he/messages.json @@ -251,6 +251,14 @@ "message": "uBO Lite has prevented the following page from loading:", "description": "Sentence used in the strict-blocked page" }, + "strictblockReasonSentence1": { + "message": "The page was blocked because of a matching filter in {{listname}}.", + "description": "Text informing about what is causing the page to be blocked" + }, + "strictblockRedirectSentence1": { + "message": "The blocked page wants to redirect to another site. If you choose to proceed, you will navigate directly to: {{url}}", + "description": "Text warning about an incoming redirect" + }, "strictblockNoParamsPrompt": { "message": "without parameters", "description": "Label to be used for the parameter-less URL" diff --git a/platform/mv3/extension/_locales/hi/messages.json b/platform/mv3/extension/_locales/hi/messages.json index fa5407f5856c2..7ec113adf70d4 100644 --- a/platform/mv3/extension/_locales/hi/messages.json +++ b/platform/mv3/extension/_locales/hi/messages.json @@ -251,6 +251,14 @@ "message": "uBO Lite has prevented the following page from loading:", "description": "Sentence used in the strict-blocked page" }, + "strictblockReasonSentence1": { + "message": "The page was blocked because of a matching filter in {{listname}}.", + "description": "Text informing about what is causing the page to be blocked" + }, + "strictblockRedirectSentence1": { + "message": "The blocked page wants to redirect to another site. If you choose to proceed, you will navigate directly to: {{url}}", + "description": "Text warning about an incoming redirect" + }, "strictblockNoParamsPrompt": { "message": "without parameters", "description": "Label to be used for the parameter-less URL" diff --git a/platform/mv3/extension/_locales/hr/messages.json b/platform/mv3/extension/_locales/hr/messages.json index b70b02ca1a81f..be7af624f95a7 100644 --- a/platform/mv3/extension/_locales/hr/messages.json +++ b/platform/mv3/extension/_locales/hr/messages.json @@ -251,6 +251,14 @@ "message": "uBO Lite je spriječio učitavanje sljedeće stranice:", "description": "Sentence used in the strict-blocked page" }, + "strictblockReasonSentence1": { + "message": "The page was blocked because of a matching filter in {{listname}}.", + "description": "Text informing about what is causing the page to be blocked" + }, + "strictblockRedirectSentence1": { + "message": "The blocked page wants to redirect to another site. If you choose to proceed, you will navigate directly to: {{url}}", + "description": "Text warning about an incoming redirect" + }, "strictblockNoParamsPrompt": { "message": "bez parametara", "description": "Label to be used for the parameter-less URL" diff --git a/platform/mv3/extension/_locales/hu/messages.json b/platform/mv3/extension/_locales/hu/messages.json index 6a31151a5d6b4..f6663ad3c65aa 100644 --- a/platform/mv3/extension/_locales/hu/messages.json +++ b/platform/mv3/extension/_locales/hu/messages.json @@ -251,6 +251,14 @@ "message": "A uBO Lite megakadályozta a következő oldal betöltését:", "description": "Sentence used in the strict-blocked page" }, + "strictblockReasonSentence1": { + "message": "The page was blocked because of a matching filter in {{listname}}.", + "description": "Text informing about what is causing the page to be blocked" + }, + "strictblockRedirectSentence1": { + "message": "The blocked page wants to redirect to another site. If you choose to proceed, you will navigate directly to: {{url}}", + "description": "Text warning about an incoming redirect" + }, "strictblockNoParamsPrompt": { "message": "paraméterek nélkül", "description": "Label to be used for the parameter-less URL" diff --git a/platform/mv3/extension/_locales/hy/messages.json b/platform/mv3/extension/_locales/hy/messages.json index 005a83ca2b4cc..4d8a874100ec3 100644 --- a/platform/mv3/extension/_locales/hy/messages.json +++ b/platform/mv3/extension/_locales/hy/messages.json @@ -251,6 +251,14 @@ "message": "uBO Lite has prevented the following page from loading:", "description": "Sentence used in the strict-blocked page" }, + "strictblockReasonSentence1": { + "message": "The page was blocked because of a matching filter in {{listname}}.", + "description": "Text informing about what is causing the page to be blocked" + }, + "strictblockRedirectSentence1": { + "message": "The blocked page wants to redirect to another site. If you choose to proceed, you will navigate directly to: {{url}}", + "description": "Text warning about an incoming redirect" + }, "strictblockNoParamsPrompt": { "message": "without parameters", "description": "Label to be used for the parameter-less URL" diff --git a/platform/mv3/extension/_locales/id/messages.json b/platform/mv3/extension/_locales/id/messages.json index f37442b5034a3..4afc32fd7a072 100644 --- a/platform/mv3/extension/_locales/id/messages.json +++ b/platform/mv3/extension/_locales/id/messages.json @@ -251,6 +251,14 @@ "message": "uBO Lite telah mencegah pemuatan halaman berikut:", "description": "Sentence used in the strict-blocked page" }, + "strictblockReasonSentence1": { + "message": "The page was blocked because of a matching filter in {{listname}}.", + "description": "Text informing about what is causing the page to be blocked" + }, + "strictblockRedirectSentence1": { + "message": "The blocked page wants to redirect to another site. If you choose to proceed, you will navigate directly to: {{url}}", + "description": "Text warning about an incoming redirect" + }, "strictblockNoParamsPrompt": { "message": "tanpa parameter", "description": "Label to be used for the parameter-less URL" diff --git a/platform/mv3/extension/_locales/it/messages.json b/platform/mv3/extension/_locales/it/messages.json index 2e434a816a359..1e82bba9fc3d4 100644 --- a/platform/mv3/extension/_locales/it/messages.json +++ b/platform/mv3/extension/_locales/it/messages.json @@ -251,6 +251,14 @@ "message": "uBO Lite has prevented the following page from loading:", "description": "Sentence used in the strict-blocked page" }, + "strictblockReasonSentence1": { + "message": "The page was blocked because of a matching filter in {{listname}}.", + "description": "Text informing about what is causing the page to be blocked" + }, + "strictblockRedirectSentence1": { + "message": "The blocked page wants to redirect to another site. If you choose to proceed, you will navigate directly to: {{url}}", + "description": "Text warning about an incoming redirect" + }, "strictblockNoParamsPrompt": { "message": "without parameters", "description": "Label to be used for the parameter-less URL" diff --git a/platform/mv3/extension/_locales/ja/messages.json b/platform/mv3/extension/_locales/ja/messages.json index 6da09a7720eaf..e94d710cfb7a7 100644 --- a/platform/mv3/extension/_locales/ja/messages.json +++ b/platform/mv3/extension/_locales/ja/messages.json @@ -251,6 +251,14 @@ "message": "uBO Lite はこのページの読み込みをブロックしています:", "description": "Sentence used in the strict-blocked page" }, + "strictblockReasonSentence1": { + "message": "The page was blocked because of a matching filter in {{listname}}.", + "description": "Text informing about what is causing the page to be blocked" + }, + "strictblockRedirectSentence1": { + "message": "The blocked page wants to redirect to another site. If you choose to proceed, you will navigate directly to: {{url}}", + "description": "Text warning about an incoming redirect" + }, "strictblockNoParamsPrompt": { "message": "パラメータを除いた URL", "description": "Label to be used for the parameter-less URL" diff --git a/platform/mv3/extension/_locales/ka/messages.json b/platform/mv3/extension/_locales/ka/messages.json index 1c874e90178c5..c916896c69457 100644 --- a/platform/mv3/extension/_locales/ka/messages.json +++ b/platform/mv3/extension/_locales/ka/messages.json @@ -251,6 +251,14 @@ "message": "uBO Lite has prevented the following page from loading:", "description": "Sentence used in the strict-blocked page" }, + "strictblockReasonSentence1": { + "message": "The page was blocked because of a matching filter in {{listname}}.", + "description": "Text informing about what is causing the page to be blocked" + }, + "strictblockRedirectSentence1": { + "message": "The blocked page wants to redirect to another site. If you choose to proceed, you will navigate directly to: {{url}}", + "description": "Text warning about an incoming redirect" + }, "strictblockNoParamsPrompt": { "message": "without parameters", "description": "Label to be used for the parameter-less URL" diff --git a/platform/mv3/extension/_locales/kk/messages.json b/platform/mv3/extension/_locales/kk/messages.json index 1c200f3653e00..515422f202c23 100644 --- a/platform/mv3/extension/_locales/kk/messages.json +++ b/platform/mv3/extension/_locales/kk/messages.json @@ -251,6 +251,14 @@ "message": "uBO Lite has prevented the following page from loading:", "description": "Sentence used in the strict-blocked page" }, + "strictblockReasonSentence1": { + "message": "The page was blocked because of a matching filter in {{listname}}.", + "description": "Text informing about what is causing the page to be blocked" + }, + "strictblockRedirectSentence1": { + "message": "The blocked page wants to redirect to another site. If you choose to proceed, you will navigate directly to: {{url}}", + "description": "Text warning about an incoming redirect" + }, "strictblockNoParamsPrompt": { "message": "without parameters", "description": "Label to be used for the parameter-less URL" diff --git a/platform/mv3/extension/_locales/kn/messages.json b/platform/mv3/extension/_locales/kn/messages.json index 8d4b80ab44f7e..4eead835febb8 100644 --- a/platform/mv3/extension/_locales/kn/messages.json +++ b/platform/mv3/extension/_locales/kn/messages.json @@ -251,6 +251,14 @@ "message": "uBO Lite has prevented the following page from loading:", "description": "Sentence used in the strict-blocked page" }, + "strictblockReasonSentence1": { + "message": "The page was blocked because of a matching filter in {{listname}}.", + "description": "Text informing about what is causing the page to be blocked" + }, + "strictblockRedirectSentence1": { + "message": "The blocked page wants to redirect to another site. If you choose to proceed, you will navigate directly to: {{url}}", + "description": "Text warning about an incoming redirect" + }, "strictblockNoParamsPrompt": { "message": "without parameters", "description": "Label to be used for the parameter-less URL" diff --git a/platform/mv3/extension/_locales/ko/messages.json b/platform/mv3/extension/_locales/ko/messages.json index 18475960de041..66de739efbc37 100644 --- a/platform/mv3/extension/_locales/ko/messages.json +++ b/platform/mv3/extension/_locales/ko/messages.json @@ -251,6 +251,14 @@ "message": "uBO Lite has prevented the following page from loading:", "description": "Sentence used in the strict-blocked page" }, + "strictblockReasonSentence1": { + "message": "The page was blocked because of a matching filter in {{listname}}.", + "description": "Text informing about what is causing the page to be blocked" + }, + "strictblockRedirectSentence1": { + "message": "The blocked page wants to redirect to another site. If you choose to proceed, you will navigate directly to: {{url}}", + "description": "Text warning about an incoming redirect" + }, "strictblockNoParamsPrompt": { "message": "without parameters", "description": "Label to be used for the parameter-less URL" diff --git a/platform/mv3/extension/_locales/lt/messages.json b/platform/mv3/extension/_locales/lt/messages.json index 8a7b9cb2112e8..abdd54d2d3c21 100644 --- a/platform/mv3/extension/_locales/lt/messages.json +++ b/platform/mv3/extension/_locales/lt/messages.json @@ -251,6 +251,14 @@ "message": "uBO Lite has prevented the following page from loading:", "description": "Sentence used in the strict-blocked page" }, + "strictblockReasonSentence1": { + "message": "The page was blocked because of a matching filter in {{listname}}.", + "description": "Text informing about what is causing the page to be blocked" + }, + "strictblockRedirectSentence1": { + "message": "The blocked page wants to redirect to another site. If you choose to proceed, you will navigate directly to: {{url}}", + "description": "Text warning about an incoming redirect" + }, "strictblockNoParamsPrompt": { "message": "without parameters", "description": "Label to be used for the parameter-less URL" diff --git a/platform/mv3/extension/_locales/lv/messages.json b/platform/mv3/extension/_locales/lv/messages.json index 70c7ad1754c61..4f388fa909693 100644 --- a/platform/mv3/extension/_locales/lv/messages.json +++ b/platform/mv3/extension/_locales/lv/messages.json @@ -251,6 +251,14 @@ "message": "uBO Lite novērsa šīs lapas ielādēšanu:", "description": "Sentence used in the strict-blocked page" }, + "strictblockReasonSentence1": { + "message": "The page was blocked because of a matching filter in {{listname}}.", + "description": "Text informing about what is causing the page to be blocked" + }, + "strictblockRedirectSentence1": { + "message": "The blocked page wants to redirect to another site. If you choose to proceed, you will navigate directly to: {{url}}", + "description": "Text warning about an incoming redirect" + }, "strictblockNoParamsPrompt": { "message": "bez parametriem", "description": "Label to be used for the parameter-less URL" diff --git a/platform/mv3/extension/_locales/mk/messages.json b/platform/mv3/extension/_locales/mk/messages.json index e50658321a032..3a61406f87fec 100644 --- a/platform/mv3/extension/_locales/mk/messages.json +++ b/platform/mv3/extension/_locales/mk/messages.json @@ -251,6 +251,14 @@ "message": "uBO Lite has prevented the following page from loading:", "description": "Sentence used in the strict-blocked page" }, + "strictblockReasonSentence1": { + "message": "The page was blocked because of a matching filter in {{listname}}.", + "description": "Text informing about what is causing the page to be blocked" + }, + "strictblockRedirectSentence1": { + "message": "The blocked page wants to redirect to another site. If you choose to proceed, you will navigate directly to: {{url}}", + "description": "Text warning about an incoming redirect" + }, "strictblockNoParamsPrompt": { "message": "without parameters", "description": "Label to be used for the parameter-less URL" diff --git a/platform/mv3/extension/_locales/ml/messages.json b/platform/mv3/extension/_locales/ml/messages.json index 7db51a3d39f8c..9a5d77e6d00b4 100644 --- a/platform/mv3/extension/_locales/ml/messages.json +++ b/platform/mv3/extension/_locales/ml/messages.json @@ -251,6 +251,14 @@ "message": "uBO Lite has prevented the following page from loading:", "description": "Sentence used in the strict-blocked page" }, + "strictblockReasonSentence1": { + "message": "The page was blocked because of a matching filter in {{listname}}.", + "description": "Text informing about what is causing the page to be blocked" + }, + "strictblockRedirectSentence1": { + "message": "The blocked page wants to redirect to another site. If you choose to proceed, you will navigate directly to: {{url}}", + "description": "Text warning about an incoming redirect" + }, "strictblockNoParamsPrompt": { "message": "without parameters", "description": "Label to be used for the parameter-less URL" diff --git a/platform/mv3/extension/_locales/mr/messages.json b/platform/mv3/extension/_locales/mr/messages.json index 1c200f3653e00..515422f202c23 100644 --- a/platform/mv3/extension/_locales/mr/messages.json +++ b/platform/mv3/extension/_locales/mr/messages.json @@ -251,6 +251,14 @@ "message": "uBO Lite has prevented the following page from loading:", "description": "Sentence used in the strict-blocked page" }, + "strictblockReasonSentence1": { + "message": "The page was blocked because of a matching filter in {{listname}}.", + "description": "Text informing about what is causing the page to be blocked" + }, + "strictblockRedirectSentence1": { + "message": "The blocked page wants to redirect to another site. If you choose to proceed, you will navigate directly to: {{url}}", + "description": "Text warning about an incoming redirect" + }, "strictblockNoParamsPrompt": { "message": "without parameters", "description": "Label to be used for the parameter-less URL" diff --git a/platform/mv3/extension/_locales/ms/messages.json b/platform/mv3/extension/_locales/ms/messages.json index 784f11dec42ba..c90d647d292dc 100644 --- a/platform/mv3/extension/_locales/ms/messages.json +++ b/platform/mv3/extension/_locales/ms/messages.json @@ -251,6 +251,14 @@ "message": "uBO Lite has prevented the following page from loading:", "description": "Sentence used in the strict-blocked page" }, + "strictblockReasonSentence1": { + "message": "The page was blocked because of a matching filter in {{listname}}.", + "description": "Text informing about what is causing the page to be blocked" + }, + "strictblockRedirectSentence1": { + "message": "The blocked page wants to redirect to another site. If you choose to proceed, you will navigate directly to: {{url}}", + "description": "Text warning about an incoming redirect" + }, "strictblockNoParamsPrompt": { "message": "without parameters", "description": "Label to be used for the parameter-less URL" diff --git a/platform/mv3/extension/_locales/nb/messages.json b/platform/mv3/extension/_locales/nb/messages.json index 2e553978e3d35..c65495bd664c5 100644 --- a/platform/mv3/extension/_locales/nb/messages.json +++ b/platform/mv3/extension/_locales/nb/messages.json @@ -251,6 +251,14 @@ "message": "uBO Lite has prevented the following page from loading:", "description": "Sentence used in the strict-blocked page" }, + "strictblockReasonSentence1": { + "message": "The page was blocked because of a matching filter in {{listname}}.", + "description": "Text informing about what is causing the page to be blocked" + }, + "strictblockRedirectSentence1": { + "message": "The blocked page wants to redirect to another site. If you choose to proceed, you will navigate directly to: {{url}}", + "description": "Text warning about an incoming redirect" + }, "strictblockNoParamsPrompt": { "message": "without parameters", "description": "Label to be used for the parameter-less URL" diff --git a/platform/mv3/extension/_locales/nl/messages.json b/platform/mv3/extension/_locales/nl/messages.json index 74e3608a41dfa..d1874b6aed8f0 100644 --- a/platform/mv3/extension/_locales/nl/messages.json +++ b/platform/mv3/extension/_locales/nl/messages.json @@ -251,6 +251,14 @@ "message": "uBO Lite has prevented the following page from loading:", "description": "Sentence used in the strict-blocked page" }, + "strictblockReasonSentence1": { + "message": "The page was blocked because of a matching filter in {{listname}}.", + "description": "Text informing about what is causing the page to be blocked" + }, + "strictblockRedirectSentence1": { + "message": "The blocked page wants to redirect to another site. If you choose to proceed, you will navigate directly to: {{url}}", + "description": "Text warning about an incoming redirect" + }, "strictblockNoParamsPrompt": { "message": "without parameters", "description": "Label to be used for the parameter-less URL" diff --git a/platform/mv3/extension/_locales/oc/messages.json b/platform/mv3/extension/_locales/oc/messages.json index 1c200f3653e00..515422f202c23 100644 --- a/platform/mv3/extension/_locales/oc/messages.json +++ b/platform/mv3/extension/_locales/oc/messages.json @@ -251,6 +251,14 @@ "message": "uBO Lite has prevented the following page from loading:", "description": "Sentence used in the strict-blocked page" }, + "strictblockReasonSentence1": { + "message": "The page was blocked because of a matching filter in {{listname}}.", + "description": "Text informing about what is causing the page to be blocked" + }, + "strictblockRedirectSentence1": { + "message": "The blocked page wants to redirect to another site. If you choose to proceed, you will navigate directly to: {{url}}", + "description": "Text warning about an incoming redirect" + }, "strictblockNoParamsPrompt": { "message": "without parameters", "description": "Label to be used for the parameter-less URL" diff --git a/platform/mv3/extension/_locales/pa/messages.json b/platform/mv3/extension/_locales/pa/messages.json index 6bf60fe983285..dcd08462ffb0f 100644 --- a/platform/mv3/extension/_locales/pa/messages.json +++ b/platform/mv3/extension/_locales/pa/messages.json @@ -251,6 +251,14 @@ "message": "uBO Lite has prevented the following page from loading:", "description": "Sentence used in the strict-blocked page" }, + "strictblockReasonSentence1": { + "message": "The page was blocked because of a matching filter in {{listname}}.", + "description": "Text informing about what is causing the page to be blocked" + }, + "strictblockRedirectSentence1": { + "message": "The blocked page wants to redirect to another site. If you choose to proceed, you will navigate directly to: {{url}}", + "description": "Text warning about an incoming redirect" + }, "strictblockNoParamsPrompt": { "message": "without parameters", "description": "Label to be used for the parameter-less URL" diff --git a/platform/mv3/extension/_locales/pl/messages.json b/platform/mv3/extension/_locales/pl/messages.json index b6e9cca38813a..8a7afcc037e44 100644 --- a/platform/mv3/extension/_locales/pl/messages.json +++ b/platform/mv3/extension/_locales/pl/messages.json @@ -251,6 +251,14 @@ "message": "uBO Lite nie pozwolił załadować się następującej stronie:", "description": "Sentence used in the strict-blocked page" }, + "strictblockReasonSentence1": { + "message": "The page was blocked because of a matching filter in {{listname}}.", + "description": "Text informing about what is causing the page to be blocked" + }, + "strictblockRedirectSentence1": { + "message": "The blocked page wants to redirect to another site. If you choose to proceed, you will navigate directly to: {{url}}", + "description": "Text warning about an incoming redirect" + }, "strictblockNoParamsPrompt": { "message": "bez parametrów", "description": "Label to be used for the parameter-less URL" diff --git a/platform/mv3/extension/_locales/pt_BR/messages.json b/platform/mv3/extension/_locales/pt_BR/messages.json index b64c7e263769d..77ad6cb270579 100644 --- a/platform/mv3/extension/_locales/pt_BR/messages.json +++ b/platform/mv3/extension/_locales/pt_BR/messages.json @@ -251,6 +251,14 @@ "message": "O uBO Lite impediu o carregamento da seguinte página:", "description": "Sentence used in the strict-blocked page" }, + "strictblockReasonSentence1": { + "message": "The page was blocked because of a matching filter in {{listname}}.", + "description": "Text informing about what is causing the page to be blocked" + }, + "strictblockRedirectSentence1": { + "message": "The blocked page wants to redirect to another site. If you choose to proceed, you will navigate directly to: {{url}}", + "description": "Text warning about an incoming redirect" + }, "strictblockNoParamsPrompt": { "message": "sem parâmetros", "description": "Label to be used for the parameter-less URL" diff --git a/platform/mv3/extension/_locales/pt_PT/messages.json b/platform/mv3/extension/_locales/pt_PT/messages.json index 22d1eaf1e84ea..cc744201ba365 100644 --- a/platform/mv3/extension/_locales/pt_PT/messages.json +++ b/platform/mv3/extension/_locales/pt_PT/messages.json @@ -251,6 +251,14 @@ "message": "uBO Lite has prevented the following page from loading:", "description": "Sentence used in the strict-blocked page" }, + "strictblockReasonSentence1": { + "message": "The page was blocked because of a matching filter in {{listname}}.", + "description": "Text informing about what is causing the page to be blocked" + }, + "strictblockRedirectSentence1": { + "message": "The blocked page wants to redirect to another site. If you choose to proceed, you will navigate directly to: {{url}}", + "description": "Text warning about an incoming redirect" + }, "strictblockNoParamsPrompt": { "message": "without parameters", "description": "Label to be used for the parameter-less URL" diff --git a/platform/mv3/extension/_locales/ro/messages.json b/platform/mv3/extension/_locales/ro/messages.json index 6d496312d84ea..8c29e9a458135 100644 --- a/platform/mv3/extension/_locales/ro/messages.json +++ b/platform/mv3/extension/_locales/ro/messages.json @@ -251,6 +251,14 @@ "message": "uBO Lite has prevented the following page from loading:", "description": "Sentence used in the strict-blocked page" }, + "strictblockReasonSentence1": { + "message": "The page was blocked because of a matching filter in {{listname}}.", + "description": "Text informing about what is causing the page to be blocked" + }, + "strictblockRedirectSentence1": { + "message": "The blocked page wants to redirect to another site. If you choose to proceed, you will navigate directly to: {{url}}", + "description": "Text warning about an incoming redirect" + }, "strictblockNoParamsPrompt": { "message": "without parameters", "description": "Label to be used for the parameter-less URL" diff --git a/platform/mv3/extension/_locales/ru/messages.json b/platform/mv3/extension/_locales/ru/messages.json index 011a910226275..53c354a7787b1 100644 --- a/platform/mv3/extension/_locales/ru/messages.json +++ b/platform/mv3/extension/_locales/ru/messages.json @@ -251,6 +251,14 @@ "message": "uBO Lite предотвратил загрузку следующей страницы:", "description": "Sentence used in the strict-blocked page" }, + "strictblockReasonSentence1": { + "message": "The page was blocked because of a matching filter in {{listname}}.", + "description": "Text informing about what is causing the page to be blocked" + }, + "strictblockRedirectSentence1": { + "message": "The blocked page wants to redirect to another site. If you choose to proceed, you will navigate directly to: {{url}}", + "description": "Text warning about an incoming redirect" + }, "strictblockNoParamsPrompt": { "message": "без параметров", "description": "Label to be used for the parameter-less URL" diff --git a/platform/mv3/extension/_locales/si/messages.json b/platform/mv3/extension/_locales/si/messages.json index f8c573375f6b1..e411dbb45a2ca 100644 --- a/platform/mv3/extension/_locales/si/messages.json +++ b/platform/mv3/extension/_locales/si/messages.json @@ -251,6 +251,14 @@ "message": "uBO Lite has prevented the following page from loading:", "description": "Sentence used in the strict-blocked page" }, + "strictblockReasonSentence1": { + "message": "The page was blocked because of a matching filter in {{listname}}.", + "description": "Text informing about what is causing the page to be blocked" + }, + "strictblockRedirectSentence1": { + "message": "The blocked page wants to redirect to another site. If you choose to proceed, you will navigate directly to: {{url}}", + "description": "Text warning about an incoming redirect" + }, "strictblockNoParamsPrompt": { "message": "without parameters", "description": "Label to be used for the parameter-less URL" diff --git a/platform/mv3/extension/_locales/sk/messages.json b/platform/mv3/extension/_locales/sk/messages.json index 8d7da8002cdfb..79cd5c4e57912 100644 --- a/platform/mv3/extension/_locales/sk/messages.json +++ b/platform/mv3/extension/_locales/sk/messages.json @@ -251,6 +251,14 @@ "message": "uBO Lite zabránil načítaniu nasledujúcej stránky:", "description": "Sentence used in the strict-blocked page" }, + "strictblockReasonSentence1": { + "message": "The page was blocked because of a matching filter in {{listname}}.", + "description": "Text informing about what is causing the page to be blocked" + }, + "strictblockRedirectSentence1": { + "message": "The blocked page wants to redirect to another site. If you choose to proceed, you will navigate directly to: {{url}}", + "description": "Text warning about an incoming redirect" + }, "strictblockNoParamsPrompt": { "message": "bez parametrov", "description": "Label to be used for the parameter-less URL" diff --git a/platform/mv3/extension/_locales/sl/messages.json b/platform/mv3/extension/_locales/sl/messages.json index 1c200f3653e00..515422f202c23 100644 --- a/platform/mv3/extension/_locales/sl/messages.json +++ b/platform/mv3/extension/_locales/sl/messages.json @@ -251,6 +251,14 @@ "message": "uBO Lite has prevented the following page from loading:", "description": "Sentence used in the strict-blocked page" }, + "strictblockReasonSentence1": { + "message": "The page was blocked because of a matching filter in {{listname}}.", + "description": "Text informing about what is causing the page to be blocked" + }, + "strictblockRedirectSentence1": { + "message": "The blocked page wants to redirect to another site. If you choose to proceed, you will navigate directly to: {{url}}", + "description": "Text warning about an incoming redirect" + }, "strictblockNoParamsPrompt": { "message": "without parameters", "description": "Label to be used for the parameter-less URL" diff --git a/platform/mv3/extension/_locales/so/messages.json b/platform/mv3/extension/_locales/so/messages.json index 1c200f3653e00..515422f202c23 100644 --- a/platform/mv3/extension/_locales/so/messages.json +++ b/platform/mv3/extension/_locales/so/messages.json @@ -251,6 +251,14 @@ "message": "uBO Lite has prevented the following page from loading:", "description": "Sentence used in the strict-blocked page" }, + "strictblockReasonSentence1": { + "message": "The page was blocked because of a matching filter in {{listname}}.", + "description": "Text informing about what is causing the page to be blocked" + }, + "strictblockRedirectSentence1": { + "message": "The blocked page wants to redirect to another site. If you choose to proceed, you will navigate directly to: {{url}}", + "description": "Text warning about an incoming redirect" + }, "strictblockNoParamsPrompt": { "message": "without parameters", "description": "Label to be used for the parameter-less URL" diff --git a/platform/mv3/extension/_locales/sq/messages.json b/platform/mv3/extension/_locales/sq/messages.json index 2d881d1cf0d35..5d3d191728ab2 100644 --- a/platform/mv3/extension/_locales/sq/messages.json +++ b/platform/mv3/extension/_locales/sq/messages.json @@ -251,6 +251,14 @@ "message": "uMatrix ka penguar ngarkimin e faqes vijuese:", "description": "Sentence used in the strict-blocked page" }, + "strictblockReasonSentence1": { + "message": "The page was blocked because of a matching filter in {{listname}}.", + "description": "Text informing about what is causing the page to be blocked" + }, + "strictblockRedirectSentence1": { + "message": "The blocked page wants to redirect to another site. If you choose to proceed, you will navigate directly to: {{url}}", + "description": "Text warning about an incoming redirect" + }, "strictblockNoParamsPrompt": { "message": "pa parametra", "description": "Label to be used for the parameter-less URL" diff --git a/platform/mv3/extension/_locales/sr/messages.json b/platform/mv3/extension/_locales/sr/messages.json index ca3218b7a650b..329b33f041768 100644 --- a/platform/mv3/extension/_locales/sr/messages.json +++ b/platform/mv3/extension/_locales/sr/messages.json @@ -251,6 +251,14 @@ "message": "uBO Lite је спречио учитавање следеће странице:", "description": "Sentence used in the strict-blocked page" }, + "strictblockReasonSentence1": { + "message": "The page was blocked because of a matching filter in {{listname}}.", + "description": "Text informing about what is causing the page to be blocked" + }, + "strictblockRedirectSentence1": { + "message": "The blocked page wants to redirect to another site. If you choose to proceed, you will navigate directly to: {{url}}", + "description": "Text warning about an incoming redirect" + }, "strictblockNoParamsPrompt": { "message": "без параметара", "description": "Label to be used for the parameter-less URL" diff --git a/platform/mv3/extension/_locales/sv/messages.json b/platform/mv3/extension/_locales/sv/messages.json index 164a91ba3f326..d9c8c7762228a 100644 --- a/platform/mv3/extension/_locales/sv/messages.json +++ b/platform/mv3/extension/_locales/sv/messages.json @@ -251,6 +251,14 @@ "message": "uBO Lite har förhindrat följande sida från att läsas in:", "description": "Sentence used in the strict-blocked page" }, + "strictblockReasonSentence1": { + "message": "The page was blocked because of a matching filter in {{listname}}.", + "description": "Text informing about what is causing the page to be blocked" + }, + "strictblockRedirectSentence1": { + "message": "The blocked page wants to redirect to another site. If you choose to proceed, you will navigate directly to: {{url}}", + "description": "Text warning about an incoming redirect" + }, "strictblockNoParamsPrompt": { "message": "utan parametrar", "description": "Label to be used for the parameter-less URL" diff --git a/platform/mv3/extension/_locales/sw/messages.json b/platform/mv3/extension/_locales/sw/messages.json index 1c200f3653e00..515422f202c23 100644 --- a/platform/mv3/extension/_locales/sw/messages.json +++ b/platform/mv3/extension/_locales/sw/messages.json @@ -251,6 +251,14 @@ "message": "uBO Lite has prevented the following page from loading:", "description": "Sentence used in the strict-blocked page" }, + "strictblockReasonSentence1": { + "message": "The page was blocked because of a matching filter in {{listname}}.", + "description": "Text informing about what is causing the page to be blocked" + }, + "strictblockRedirectSentence1": { + "message": "The blocked page wants to redirect to another site. If you choose to proceed, you will navigate directly to: {{url}}", + "description": "Text warning about an incoming redirect" + }, "strictblockNoParamsPrompt": { "message": "without parameters", "description": "Label to be used for the parameter-less URL" diff --git a/platform/mv3/extension/_locales/ta/messages.json b/platform/mv3/extension/_locales/ta/messages.json index 1c200f3653e00..515422f202c23 100644 --- a/platform/mv3/extension/_locales/ta/messages.json +++ b/platform/mv3/extension/_locales/ta/messages.json @@ -251,6 +251,14 @@ "message": "uBO Lite has prevented the following page from loading:", "description": "Sentence used in the strict-blocked page" }, + "strictblockReasonSentence1": { + "message": "The page was blocked because of a matching filter in {{listname}}.", + "description": "Text informing about what is causing the page to be blocked" + }, + "strictblockRedirectSentence1": { + "message": "The blocked page wants to redirect to another site. If you choose to proceed, you will navigate directly to: {{url}}", + "description": "Text warning about an incoming redirect" + }, "strictblockNoParamsPrompt": { "message": "without parameters", "description": "Label to be used for the parameter-less URL" diff --git a/platform/mv3/extension/_locales/te/messages.json b/platform/mv3/extension/_locales/te/messages.json index c1933035602e3..5f4823359b226 100644 --- a/platform/mv3/extension/_locales/te/messages.json +++ b/platform/mv3/extension/_locales/te/messages.json @@ -251,6 +251,14 @@ "message": "uBO Lite has prevented the following page from loading:", "description": "Sentence used in the strict-blocked page" }, + "strictblockReasonSentence1": { + "message": "The page was blocked because of a matching filter in {{listname}}.", + "description": "Text informing about what is causing the page to be blocked" + }, + "strictblockRedirectSentence1": { + "message": "The blocked page wants to redirect to another site. If you choose to proceed, you will navigate directly to: {{url}}", + "description": "Text warning about an incoming redirect" + }, "strictblockNoParamsPrompt": { "message": "without parameters", "description": "Label to be used for the parameter-less URL" diff --git a/platform/mv3/extension/_locales/th/messages.json b/platform/mv3/extension/_locales/th/messages.json index 8ae7f62db2588..31a0642920e39 100644 --- a/platform/mv3/extension/_locales/th/messages.json +++ b/platform/mv3/extension/_locales/th/messages.json @@ -251,6 +251,14 @@ "message": "uBO Lite has prevented the following page from loading:", "description": "Sentence used in the strict-blocked page" }, + "strictblockReasonSentence1": { + "message": "The page was blocked because of a matching filter in {{listname}}.", + "description": "Text informing about what is causing the page to be blocked" + }, + "strictblockRedirectSentence1": { + "message": "The blocked page wants to redirect to another site. If you choose to proceed, you will navigate directly to: {{url}}", + "description": "Text warning about an incoming redirect" + }, "strictblockNoParamsPrompt": { "message": "without parameters", "description": "Label to be used for the parameter-less URL" diff --git a/platform/mv3/extension/_locales/tr/messages.json b/platform/mv3/extension/_locales/tr/messages.json index a3a6dacf5f140..8fa869b596ef0 100644 --- a/platform/mv3/extension/_locales/tr/messages.json +++ b/platform/mv3/extension/_locales/tr/messages.json @@ -251,6 +251,14 @@ "message": "uBo Lite aşağıdaki sayfaların yüklenmesini engelledi:", "description": "Sentence used in the strict-blocked page" }, + "strictblockReasonSentence1": { + "message": "The page was blocked because of a matching filter in {{listname}}.", + "description": "Text informing about what is causing the page to be blocked" + }, + "strictblockRedirectSentence1": { + "message": "The blocked page wants to redirect to another site. If you choose to proceed, you will navigate directly to: {{url}}", + "description": "Text warning about an incoming redirect" + }, "strictblockNoParamsPrompt": { "message": "değişkensiz", "description": "Label to be used for the parameter-less URL" diff --git a/platform/mv3/extension/_locales/uk/messages.json b/platform/mv3/extension/_locales/uk/messages.json index fbd534207c1c3..51e42d2efb696 100644 --- a/platform/mv3/extension/_locales/uk/messages.json +++ b/platform/mv3/extension/_locales/uk/messages.json @@ -251,6 +251,14 @@ "message": "uBO Lite заблокував завантаження наступних сторінок:", "description": "Sentence used in the strict-blocked page" }, + "strictblockReasonSentence1": { + "message": "The page was blocked because of a matching filter in {{listname}}.", + "description": "Text informing about what is causing the page to be blocked" + }, + "strictblockRedirectSentence1": { + "message": "The blocked page wants to redirect to another site. If you choose to proceed, you will navigate directly to: {{url}}", + "description": "Text warning about an incoming redirect" + }, "strictblockNoParamsPrompt": { "message": "без параметрів", "description": "Label to be used for the parameter-less URL" diff --git a/platform/mv3/extension/_locales/ur/messages.json b/platform/mv3/extension/_locales/ur/messages.json index 3c4d796af358a..d620ff2b57c9e 100644 --- a/platform/mv3/extension/_locales/ur/messages.json +++ b/platform/mv3/extension/_locales/ur/messages.json @@ -251,6 +251,14 @@ "message": "uBO Lite has prevented the following page from loading:", "description": "Sentence used in the strict-blocked page" }, + "strictblockReasonSentence1": { + "message": "The page was blocked because of a matching filter in {{listname}}.", + "description": "Text informing about what is causing the page to be blocked" + }, + "strictblockRedirectSentence1": { + "message": "The blocked page wants to redirect to another site. If you choose to proceed, you will navigate directly to: {{url}}", + "description": "Text warning about an incoming redirect" + }, "strictblockNoParamsPrompt": { "message": "without parameters", "description": "Label to be used for the parameter-less URL" diff --git a/platform/mv3/extension/_locales/vi/messages.json b/platform/mv3/extension/_locales/vi/messages.json index 2e1c09546aee1..069791bb6ec7e 100644 --- a/platform/mv3/extension/_locales/vi/messages.json +++ b/platform/mv3/extension/_locales/vi/messages.json @@ -251,6 +251,14 @@ "message": "uBO Lite has prevented the following page from loading:", "description": "Sentence used in the strict-blocked page" }, + "strictblockReasonSentence1": { + "message": "The page was blocked because of a matching filter in {{listname}}.", + "description": "Text informing about what is causing the page to be blocked" + }, + "strictblockRedirectSentence1": { + "message": "The blocked page wants to redirect to another site. If you choose to proceed, you will navigate directly to: {{url}}", + "description": "Text warning about an incoming redirect" + }, "strictblockNoParamsPrompt": { "message": "without parameters", "description": "Label to be used for the parameter-less URL" diff --git a/platform/mv3/extension/_locales/zh_CN/messages.json b/platform/mv3/extension/_locales/zh_CN/messages.json index 9fa3896976ec1..490bc10672f00 100644 --- a/platform/mv3/extension/_locales/zh_CN/messages.json +++ b/platform/mv3/extension/_locales/zh_CN/messages.json @@ -251,6 +251,14 @@ "message": "uBO Lite 已阻止了以下页面的加载:", "description": "Sentence used in the strict-blocked page" }, + "strictblockReasonSentence1": { + "message": "The page was blocked because of a matching filter in {{listname}}.", + "description": "Text informing about what is causing the page to be blocked" + }, + "strictblockRedirectSentence1": { + "message": "The blocked page wants to redirect to another site. If you choose to proceed, you will navigate directly to: {{url}}", + "description": "Text warning about an incoming redirect" + }, "strictblockNoParamsPrompt": { "message": "不含参数", "description": "Label to be used for the parameter-less URL" diff --git a/platform/mv3/extension/_locales/zh_TW/messages.json b/platform/mv3/extension/_locales/zh_TW/messages.json index 5061d297175b6..003ef30a71f68 100644 --- a/platform/mv3/extension/_locales/zh_TW/messages.json +++ b/platform/mv3/extension/_locales/zh_TW/messages.json @@ -251,6 +251,14 @@ "message": "uBO Lite 已防止下列頁面載入:", "description": "Sentence used in the strict-blocked page" }, + "strictblockReasonSentence1": { + "message": "The page was blocked because of a matching filter in {{listname}}.", + "description": "Text informing about what is causing the page to be blocked" + }, + "strictblockRedirectSentence1": { + "message": "The blocked page wants to redirect to another site. If you choose to proceed, you will navigate directly to: {{url}}", + "description": "Text warning about an incoming redirect" + }, "strictblockNoParamsPrompt": { "message": "不帶參數", "description": "Label to be used for the parameter-less URL" From fb82db34f7c9f207f02b5a69694079bb99607276 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Thu, 5 Dec 2024 09:20:54 -0500 Subject: [PATCH 0505/1099] Make Firefox dev build auto-update --- dist/firefox/updates.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dist/firefox/updates.json b/dist/firefox/updates.json index d123412a32903..67cb51badbed6 100644 --- a/dist/firefox/updates.json +++ b/dist/firefox/updates.json @@ -3,9 +3,9 @@ "uBlock0@raymondhill.net": { "updates": [ { - "version": "1.61.3.3", + "version": "1.61.3.4", "browser_specific_settings": { "gecko": { "strict_min_version": "78.0" } }, - "update_link": "https://github.com/gorhill/uBlock/releases/download/1.61.3b3/uBlock0_1.61.3b3.firefox.signed.xpi" + "update_link": "https://github.com/gorhill/uBlock/releases/download/1.61.3b4/uBlock0_1.61.3b4.firefox.signed.xpi" } ] } From 38390bab9cffe3c2a52de88c130c42d6c2de768e Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Thu, 5 Dec 2024 12:56:25 -0500 Subject: [PATCH 0506/1099] [mv3] Add `urlskip` support for strict-blocked page + extra info Add information about which ruleset caused a page to be strict- blocked. Whenever possible, add ability to URL-skip an incoming redirect in a strict-blocked page. Add new default list: "30-day Phishing Domain List" --- .../mv3/extension/_locales/en/messages.json | 8 + platform/mv3/extension/css/settings.css | 2 +- platform/mv3/extension/css/strictblock.css | 12 ++ platform/mv3/extension/js/background.js | 25 +-- platform/mv3/extension/js/config.js | 5 +- platform/mv3/extension/js/ruleset-manager.js | 44 ++++- platform/mv3/extension/js/strictblock.js | 115 ++++++++++--- platform/mv3/make-rulesets.js | 58 ++++++- platform/mv3/strictblock.html | 10 +- src/js/messaging.js | 6 +- src/js/static-dnr-filtering.js | 1 + src/js/static-net-filtering.js | 158 +++--------------- src/js/urlskip.js | 157 +++++++++++++++++ tools/make-mv3.sh | 1 + tools/make-nodejs.sh | 1 + 15 files changed, 418 insertions(+), 185 deletions(-) create mode 100644 src/js/urlskip.js diff --git a/platform/mv3/extension/_locales/en/messages.json b/platform/mv3/extension/_locales/en/messages.json index 17a5b24cd7935..ad9d26911371e 100644 --- a/platform/mv3/extension/_locales/en/messages.json +++ b/platform/mv3/extension/_locales/en/messages.json @@ -250,6 +250,14 @@ "strictblockSentence1": { "message": "uBO Lite has prevented the following page from loading:", "description": "Sentence used in the strict-blocked page" + }, + "strictblockReasonSentence1": { + "message": "The page was blocked because of a matching filter in {{listname}}.", + "description": "Text informing about what is causing the page to be blocked" + }, + "strictblockRedirectSentence1": { + "message": "The blocked page wants to redirect to another site. If you choose to proceed, you will navigate directly to: {{url}}", + "description": "Text warning about an incoming redirect" }, "strictblockNoParamsPrompt": { "message": "without parameters", diff --git a/platform/mv3/extension/css/settings.css b/platform/mv3/extension/css/settings.css index a8b07d2a1894c..02eb600370a5d 100644 --- a/platform/mv3/extension/css/settings.css +++ b/platform/mv3/extension/css/settings.css @@ -15,7 +15,7 @@ h3 { label + legend { color: color-mix(in srgb, currentColor 69%, transparent); - font-size: small; + font-size: var(--font-size-smaller); margin-inline-start: var(--default-gap-large); } diff --git a/platform/mv3/extension/css/strictblock.css b/platform/mv3/extension/css/strictblock.css index f7534078cca7b..7f1f0e9c0d162 100644 --- a/platform/mv3/extension/css/strictblock.css +++ b/platform/mv3/extension/css/strictblock.css @@ -26,6 +26,9 @@ body { :root.mobile body { padding: var(--default-gap-small); } +body.loading { + visibility: hidden; + } #rootContainer { width: min(100%, 640px); @@ -43,6 +46,9 @@ p { a { text-decoration: none; } +q { + font-weight: bold; + } .code { font-size: 13px; word-break: break-all; @@ -128,6 +134,12 @@ body[dir="rtl"] #toggleParse { font-weight: bold; } +#urlskip a { + display: block; + overflow-y: auto; + word-break: break-all; + } + #actionContainer { display: flex; justify-content: space-between; diff --git a/platform/mv3/extension/js/background.js b/platform/mv3/extension/js/background.js index a58dd70639a48..e250d1025ed96 100644 --- a/platform/mv3/extension/js/background.js +++ b/platform/mv3/extension/js/background.js @@ -50,6 +50,7 @@ import { excludeFromStrictBlock, getEnabledRulesetsDetails, getRulesetDetails, + patchDefaultRulesets, setStrictBlockMode, updateDynamicRules, } from './ruleset-manager.js'; @@ -377,20 +378,24 @@ function onMessage(request, sender, callback) { async function start() { await loadRulesetConfig(); + const currentVersion = getCurrentVersion(); + const isNewVersion = currentVersion !== rulesetConfig.version; + + // The default rulesets may have changed, find out new ruleset to enable, + // obsolete ruleset to remove. + if ( isNewVersion ) { + ubolLog(`Version change: ${rulesetConfig.version} => ${currentVersion}`); + rulesetConfig.version = currentVersion; + await patchDefaultRulesets(); + saveRulesetConfig(); + } + const rulesetsUpdated = process.wakeupRun === false && await enableRulesets(rulesetConfig.enabledRulesets); // We need to update the regex rules only when ruleset version changes. - if ( process.wakeupRun === false ) { - const currentVersion = getCurrentVersion(); - if ( currentVersion !== rulesetConfig.version ) { - ubolLog(`Version change: ${rulesetConfig.version} => ${currentVersion}`); - rulesetConfig.version = currentVersion; - saveRulesetConfig(); - if ( rulesetsUpdated === false ) { - updateDynamicRules(); - } - } + if ( isNewVersion && rulesetsUpdated === false ) { + updateDynamicRules(); } // Permissions may have been removed while the extension was disabled diff --git a/platform/mv3/extension/js/config.js b/platform/mv3/extension/js/config.js index e3859e63fa42f..0c79dd62b18f0 100644 --- a/platform/mv3/extension/js/config.js +++ b/platform/mv3/extension/js/config.js @@ -24,13 +24,11 @@ import { sessionRead, sessionWrite, } from './ext.js'; -import { defaultRulesetsFromLanguage } from './ruleset-manager.js'; - /******************************************************************************/ export const rulesetConfig = { version: '', - enabledRulesets: [ 'default' ], + enabledRulesets: [], autoReload: true, showBlockedCount: true, strictBlockMode: true, @@ -67,7 +65,6 @@ export async function loadRulesetConfig() { sessionWrite('rulesetConfig', rulesetConfig); return; } - rulesetConfig.enabledRulesets = await defaultRulesetsFromLanguage(); sessionWrite('rulesetConfig', rulesetConfig); localWrite('rulesetConfig', rulesetConfig); process.firstRun = true; diff --git a/platform/mv3/extension/js/ruleset-manager.js b/platform/mv3/extension/js/ruleset-manager.js index c9490e6836592..04bfb3b6f241d 100644 --- a/platform/mv3/extension/js/ruleset-manager.js +++ b/platform/mv3/extension/js/ruleset-manager.js @@ -561,8 +561,6 @@ async function filteringModesToDNR(modes) { /******************************************************************************/ async function defaultRulesetsFromLanguage() { - const out = await dnr.getEnabledRulesets(); - const dropCountry = lang => { const pos = lang.indexOf('-'); if ( pos === -1 ) { return lang; } @@ -581,7 +579,12 @@ async function defaultRulesetsFromLanguage() { ); const rulesetDetails = await getRulesetDetails(); + const out = []; for ( const [ id, details ] of rulesetDetails ) { + if ( details.enabled ) { + out.push(id); + continue; + } if ( typeof details.lang !== 'string' ) { continue; } if ( reTargetLang.test(details.lang) === false ) { continue; } out.push(id); @@ -591,6 +594,42 @@ async function defaultRulesetsFromLanguage() { /******************************************************************************/ +async function patchDefaultRulesets() { + const [ + oldDefaultIds = [], + newDefaultIds, + newIds, + ] = await Promise.all([ + localRead('defaultRulesetIds'), + defaultRulesetsFromLanguage(), + getRulesetDetails(), + ]); + const toAdd = []; + const toRemove = []; + for ( const id of newDefaultIds ) { + if ( oldDefaultIds.includes(id) ) { continue; } + toAdd.push(id); + } + for ( const id of oldDefaultIds ) { + if ( newDefaultIds.includes(id) ) { continue; } + toRemove.push(id); + } + for ( const id of rulesetConfig.enabledRulesets ) { + if ( newIds.has(id) ) { continue; } + toRemove.push(id); + } + localWrite('defaultRulesetIds', newDefaultIds); + if ( toAdd.length === 0 && toRemove.length === 0 ) { return; } + const enabledRulesets = new Set(rulesetConfig.enabledRulesets); + toAdd.forEach(id => enabledRulesets.add(id)); + toRemove.forEach(id => enabledRulesets.delete(id)); + const patchedRulesets = Array.from(enabledRulesets); + ubolLog(`Patched rulesets: ${rulesetConfig.enabledRulesets} => ${patchedRulesets}`); + rulesetConfig.enabledRulesets = patchedRulesets; +} + +/******************************************************************************/ + async function enableRulesets(ids) { const afterIds = new Set(ids); const [ beforeIds, adminIds, rulesetDetails ] = await Promise.all([ @@ -679,6 +718,7 @@ export { filteringModesToDNR, getRulesetDetails, getEnabledRulesetsDetails, + patchDefaultRulesets, setStrictBlockMode, updateDynamicRules, }; diff --git a/platform/mv3/extension/js/strictblock.js b/platform/mv3/extension/js/strictblock.js index 616ef6ddbc32d..7600bcfbb9e7f 100644 --- a/platform/mv3/extension/js/strictblock.js +++ b/platform/mv3/extension/js/strictblock.js @@ -20,21 +20,15 @@ */ import { dom, qs$ } from './dom.js'; +import { fetchJSON } from './fetch.js'; +import { getEnabledRulesetsDetails } from './ruleset-manager.js'; import { i18n$ } from './i18n.js'; import { sendMessage } from './ext.js'; +import { urlSkip } from './urlskip.js'; /******************************************************************************/ -const toURL = new URL('about:blank'); - -function setURL(url) { - try { - toURL.href = url; - } catch(_) { - } -} - -setURL(self.location.hash.slice(1)); +const rulesetDetailsPromise = getEnabledRulesetsDetails(); /******************************************************************************/ @@ -66,6 +60,13 @@ async function proceed() { /******************************************************************************/ +const toURL = new URL('about:blank'); + +try { + toURL.href = self.location.hash.slice(1); +} catch(_) { +} + dom.clear('#theURL > p > span:first-of-type'); qs$('#theURL > p > span:first-of-type').append(urlToFragment(toURL.href)); @@ -152,16 +153,92 @@ qs$('#theURL > p > span:first-of-type').append(urlToFragment(toURL.href)); /******************************************************************************/ +// Find which list caused the blocking. +(async ( ) => { + const rulesetDetails = await rulesetDetailsPromise; + let iList = -1; + const searchInList = async i => { + if ( iList !== -1 ) { return; } + const hostnames = new Set( + await fetchJSON(`/rulesets/strictblock/${rulesetDetails[i].id}`) + ); + if ( iList !== -1 ) { return; } + let hn = toURL.hostname; + for (;;) { + if ( hostnames.has(hn) ) { iList = i; break; } + const pos = hn.indexOf('.'); + if ( pos === -1 ) { break; } + hn = hn.slice(pos+1); + } + }; + const toFetch = []; + for ( let i = 0; i < rulesetDetails.length; i++ ) { + if ( rulesetDetails[i].rules.strictblock === 0 ) { continue; } + toFetch.push(searchInList(i)); + } + if ( toFetch.length === 0 ) { return; } + await Promise.all(toFetch); + if ( iList === -1 ) { return; } + + const fragment = new DocumentFragment(); + const text = i18n$('strictblockReasonSentence1'); + const placeholder = '{{listname}}'; + const pos = text.indexOf(placeholder); + if ( pos === -1 ) { return; } + const q = document.createElement('q'); + q.append(rulesetDetails[iList].name); + fragment.append(text.slice(0, pos), q, text.slice(pos + placeholder.length)); + qs$('#reason').append(fragment); + dom.attr('#reason', 'hidden', null); +})(); + +/******************************************************************************/ + +// Offer to skip redirection whenever possible +(async ( ) => { + const rulesetDetails = await rulesetDetailsPromise; + const toFetch = []; + for ( const details of rulesetDetails ) { + if ( details.rules.urlskip === 0 ) { continue; } + toFetch.push(fetchJSON(`/rulesets/urlskip/${details.id}`)); + } + if ( toFetch.length === 0 ) { return; } + const urlskipLists = await Promise.all(toFetch); + for ( const urlskips of urlskipLists ) { + for ( const urlskip of urlskips ) { + const re = new RegExp(urlskip.re, urlskip.c ? undefined : 'i'); + if ( re.test(toURL.href) === false ) { continue; } + const finalURL = urlSkip(toURL.href, false, urlskip.steps); + if ( finalURL === undefined ) { continue; } + const fragment = new DocumentFragment(); + const text = i18n$('strictblockRedirectSentence1'); + const linkPlaceholder = '{{url}}'; + const pos = text.indexOf(linkPlaceholder); + if ( pos === -1 ) { return; } + const link = document.createElement('a'); + link.href = finalURL; + dom.cl.add(link, 'code'); + link.append(urlToFragment(finalURL)); + fragment.append( + text.slice(0, pos), + link, + text.slice(pos + linkPlaceholder.length) + ); + qs$('#urlskip').append(fragment); + dom.attr('#urlskip', 'hidden', null); + return; + } + } +})(); + +/******************************************************************************/ + // https://www.reddit.com/r/uBlockOrigin/comments/breeux/ if ( window.history.length > 1 ) { - dom.on('#back', 'click', ( ) => { - window.history.back(); - }); + dom.on('#back', 'click', ( ) => { window.history.back(); }); qs$('#bye').style.display = 'none'; } else { - dom.on('#bye', 'click', ( ) => { - window.close(); - }); + dom.on('#bye', 'click', ( ) => { window.close(); }); qs$('#back').style.display = 'none'; } @@ -171,8 +248,8 @@ dom.on('#disableWarning', 'change', ev => { dom.cl.toggle('[data-i18n="strictblockClose"]', 'disabled', checked); }); -dom.on('#proceed', 'click', ( ) => { - proceed(); -}); +dom.on('#proceed', 'click', ( ) => { proceed(); }); + +dom.cl.remove(dom.body, 'loading'); /******************************************************************************/ diff --git a/platform/mv3/make-rulesets.js b/platform/mv3/make-rulesets.js index b570067055473..4ea7ae6dba1d0 100644 --- a/platform/mv3/make-rulesets.js +++ b/platform/mv3/make-rulesets.js @@ -225,7 +225,7 @@ const isRegex = rule => rule.condition.regexFilter !== undefined; const isRedirect = rule => { - if ( rule.action === undefined ) { return false; } + if ( isUnsupported(rule) ) { return false; } if ( rule.action.type !== 'redirect' ) { return false; } if ( rule.action.redirect?.extensionPath !== undefined ) { return true; } if ( rule.action.redirect?.transform?.path !== undefined ) { return true; } @@ -233,19 +233,26 @@ const isRedirect = rule => { }; const isModifyHeaders = rule => - rule.action !== undefined && + isUnsupported(rule) === false && rule.action.type === 'modifyHeaders'; const isRemoveparam = rule => - rule.action !== undefined && + isUnsupported(rule) === false && rule.action.type === 'redirect' && rule.action.redirect.transform !== undefined; -const isGood = rule => +const isSafe = rule => + isUnsupported(rule) === false && + rule.action !== undefined && ( + rule.action.type === 'block' || + rule.action.type === 'allow' || + rule.action.type === 'allowAllRequests' + ); + +const isURLSkip = rule => isUnsupported(rule) === false && - isRedirect(rule) === false && - isModifyHeaders(rule) === false && - isRemoveparam(rule) === false; + rule.action !== undefined && + rule.action.type === 'urlskip'; /******************************************************************************/ @@ -357,7 +364,7 @@ async function processNetworkFilters(assetDetails, network) { } } - const plainGood = rules.filter(rule => isGood(rule) && isRegex(rule) === false); + const plainGood = rules.filter(rule => isSafe(rule) && isRegex(rule) === false); log(`\tPlain good: ${plainGood.length}`); log(plainGood .filter(rule => Array.isArray(rule._warning)) @@ -365,7 +372,7 @@ async function processNetworkFilters(assetDetails, network) { .join('\n'), true ); - const regexes = rules.filter(rule => isGood(rule) && isRegex(rule)); + const regexes = rules.filter(rule => isSafe(rule) && isRegex(rule)); log(`\tMaybe good (regexes): ${regexes.length}`); const redirects = rules.filter(rule => @@ -394,6 +401,22 @@ async function processNetworkFilters(assetDetails, network) { ); log(`\tmodifyHeaders=: ${modifyHeaders.length}`); + const urlskips = rules.filter(rule => isURLSkip(rule)).filter(rule => + rule.__modifierAction === 0 && + rule.condition && + rule.condition.regexFilter && + rule.condition.resourceTypes && + rule.condition.resourceTypes.includes('main_frame') + ).map(rule => { + const steps = rule.__modifierValue; + return { + re: rule.condition.regexFilter, + c: rule.condition.isUrlFilterCaseSensitive, + steps: steps.includes(' ') && steps.split(/ +/) || [ steps ], + }; + }); + log(`\turlskip=: ${urlskips.length}`); + const bad = rules.filter(rule => isUnsupported(rule) ); @@ -460,6 +483,13 @@ async function processNetworkFilters(assetDetails, network) { ); } + if ( urlskips.length !== 0 ) { + writeFile( + `${rulesetDir}/urlskip/${assetDetails.id}.json`, + JSON.stringify(urlskips, null, 1) + ); + } + return { total: rules.length, plain: plainGood.length, @@ -470,6 +500,7 @@ async function processNetworkFilters(assetDetails, network) { redirect: redirects.length, modifyHeaders: modifyHeaders.length, strictblock: strictBlocked.size, + urlskip: urlskips.length, }; } @@ -1114,6 +1145,7 @@ async function rulesetFromURLs(assetDetails) { redirect: netStats.redirect, modifyHeaders: netStats.modifyHeaders, strictblock: netStats.strictblock, + urlskip: netStats.urlskip, discarded: netStats.discarded, rejected: netStats.rejected, }, @@ -1264,6 +1296,14 @@ async function main() { }); // Handpicked rulesets from abroad + await rulesetFromURLs({ + id: 'nrd.30day.phishing', + name: '30-day Phishing Domain List', + enabled: true, + urls: [ 'https://raw.githubusercontent.com/xRuffKez/NRD/refs/heads/main/lists/30-day_phishing/domains-only/nrd-phishing-30day.txt' ], + homeURL: 'https://github.com/xRuffKez/NRD?tab=readme-ov-file', + }); + await rulesetFromURLs({ id: 'stevenblack-hosts', name: 'Steven Black’s Unified Hosts (adware + malware)', diff --git a/platform/mv3/strictblock.html b/platform/mv3/strictblock.html index 9572eab09715b..8b6ef2d9be94b 100644 --- a/platform/mv3/strictblock.html +++ b/platform/mv3/strictblock.html @@ -10,20 +10,26 @@ - +
-

_

+

+ + + +
diff --git a/src/js/messaging.js b/src/js/messaging.js index 16d527c60b48e..b24a9a5a27560 100644 --- a/src/js/messaging.js +++ b/src/js/messaging.js @@ -1887,7 +1887,11 @@ const onMessage = function(request, sender, callback) { listPromises.push( io.get(assetKey, { dontCache: true }).then(details => { listNames.push(assetKey); - return { name: assetKey, text: details.content }; + return { + name: assetKey, + text: details.content, + trustedSource: assetKey.startsWith('ublock-'), + }; }) ); } diff --git a/src/js/static-dnr-filtering.js b/src/js/static-dnr-filtering.js index d3e7732a6771f..63639006585c2 100644 --- a/src/js/static-dnr-filtering.js +++ b/src/js/static-dnr-filtering.js @@ -284,6 +284,7 @@ function addToDNR(context, list) { toDNR: true, nativeCssHas: env.includes('native_css_has'), badTypes: [ sfp.NODE_TYPE_NET_OPTION_NAME_REDIRECTRULE ], + trustedSource: list.trustedSource || undefined, }); const compiler = staticNetFilteringEngine.createCompiler(); diff --git a/src/js/static-net-filtering.js b/src/js/static-net-filtering.js index f4769c2c21702..f084180ffccf3 100644 --- a/src/js/static-net-filtering.js +++ b/src/js/static-net-filtering.js @@ -28,6 +28,7 @@ import BidiTrieContainer from './biditrie.js'; import { CompiledListReader } from './static-filtering-io.js'; import { FilteringContext } from './filtering-context.js'; import HNTrieContainer from './hntrie.js'; +import { urlSkip } from './urlskip.js'; /******************************************************************************/ @@ -4679,6 +4680,25 @@ StaticNetFilteringEngine.prototype.dnrFromCompiled = function(op, context, ...ar dnrAddRuleError(rule, `Incompatible with DNR: uritransform=${rule.__modifierValue}`); break; } + case 'urlskip': { + let urlFilter = rule.condition?.urlFilter; + if ( urlFilter === undefined ) { break; } + let anchor = 0b000; + if ( urlFilter.startsWith('||') ) { + anchor |= 0b100; + urlFilter = urlFilter.slice(2); + } else if ( urlFilter.startsWith('|') ) { + anchor |= 0b10; + urlFilter = urlFilter.slice(1); + } + if ( urlFilter.endsWith('|') ) { + anchor |= 0b001; + urlFilter = urlFilter.slice(0, -1); + } + rule.condition.urlFilter = undefined; + rule.condition.regexFilter = restrFromGenericPattern(urlFilter, anchor); + break; + } default: dnrAddRuleError(rule, `Unsupported modifier ${rule.__modifierType}`); break; @@ -5391,57 +5411,6 @@ StaticNetFilteringEngine.prototype.transformRequest = function(fctxt, out = []) return out; }; -/** - * @trustedOption urlskip - * - * @description - * Extract a URL from another URL according to one or more transformation steps, - * thereby skipping over intermediate network request(s) to remote servers. - * Requires a trusted source. - * - * @param steps - * A serie of space-separated directives representing the transformation steps - * to perform to extract the final URL to which a network request should be - * redirected. - * - * Supported directives: - * - * `?name`: extract the value of parameter `name` as the current string. - * - * `&i`: extract the name of the parameter at position `i` as the current - * string. The position is 1-based. - * - * `/.../`: extract the first capture group of a regex as the current string. - * - * `+https`: prepend the current string with `https://`. - * - * `-base64`: decode the current string as a base64-encoded string. - * - * `-safebase64`: decode the current string as a safe base64-encoded string. - * - * `-uricomponent`: decode the current string as a URI encoded string. - * - * `-blocked`: allow the redirection of blocked requests. By default, blocked - * requests can't by urlskip'ed. - * - * At any given step, the currently extracted string may not necessarily be - * a valid URL, and more transformation steps may be needed to obtain a valid - * URL once all the steps are applied. - * - * An unsupported step or a failed step will abort the transformation and no - * redirection will be performed. - * - * The final step is expected to yield a valid URL. If the result is not a - * valid URL, no redirection will be performed. - * - * @example - * ||example.com/path/to/tracker$urlskip=?url - * ||example.com/path/to/tracker$urlskip=?url ?to - * ||pixiv.net/jump.php?$urlskip=&1 - * ||podtrac.com/pts/redirect.mp3/$urlskip=/\/redirect\.mp3\/(.*?\.mp3\b)/ +https - * - * */ - StaticNetFilteringEngine.prototype.urlSkip = function( fctxt, blocked, @@ -5458,7 +5427,7 @@ StaticNetFilteringEngine.prototype.urlSkip = function( const urlin = fctxt.url; const value = directive.value; const steps = value.includes(' ') && value.split(/ +/) || [ value ]; - const urlout = urlSkip(directive, urlin, blocked, steps); + const urlout = urlSkip(urlin, blocked, steps, directive); if ( urlout === undefined ) { continue; } if ( urlout === urlin ) { continue; } fctxt.redirectURL = urlout; @@ -5469,91 +5438,6 @@ StaticNetFilteringEngine.prototype.urlSkip = function( return out; }; -function urlSkip(directive, url, blocked, steps) { - try { - let redirectBlocked = false; - let urlout = url; - for ( const step of steps ) { - const urlin = urlout; - const c0 = step.charCodeAt(0); - // Extract from URL parameter name at position i - if ( c0 === 0x26 ) { // & - const i = (parseInt(step.slice(1)) || 0) - 1; - if ( i < 0 ) { return; } - const url = new URL(urlin); - if ( i >= url.searchParams.size ) { return; } - const params = Array.from(url.searchParams.keys()); - urlout = decodeURIComponent(params[i]); - continue; - } - // Enforce https - if ( c0 === 0x2B && step === '+https' ) { - const s = urlin.replace(/^https?:\/\//, ''); - if ( /^[\w-]:\/\//.test(s) ) { return; } - urlout = `https://${s}`; - continue; - } - // Decode - if ( c0 === 0x2D ) { - // Base64 - if ( step === '-base64' ) { - urlout = self.atob(urlin); - continue; - } - // Safe Base64 - if ( step === '-safebase64' ) { - urlout = urlin.replace(/[-_]/g, safeBase64Replacer); - urlout = self.atob(urlout); - continue; - } - // URI component - if ( step === '-uricomponent' ) { - urlout = self.decodeURIComponent(urlin); - continue; - } - // Enable skip of blocked requests - if ( step === '-blocked' ) { - redirectBlocked = true; - continue; - } - } - // Regex extraction from first capture group - if ( c0 === 0x2F ) { // / - if ( directive.cache === null ) { - directive.cache = new RegExp(step.slice(1, -1)); - } - const match = directive.cache.exec(urlin); - if ( match === null ) { return; } - if ( match.length <= 1 ) { return; } - urlout = match[1]; - continue; - } - // Extract from URL parameter - if ( c0 === 0x3F ) { // ? - urlout = (new URL(urlin)).searchParams.get(step.slice(1)); - if ( urlout === null ) { return; } - if ( urlout.includes(' ') ) { - urlout = urlout.replace(/ /g, '%20'); - } - continue; - } - // Unknown directive - return; - } - const urlfinal = new URL(urlout); - if ( urlfinal.protocol !== 'https:' ) { - if ( urlfinal.protocol !== 'http:' ) { return; } - urlout = urlout.replace('http', 'https'); - } - if ( blocked && redirectBlocked !== true ) { return; } - return urlout; - } catch(x) { - } -} - -const safeBase64Map = { '-': '+', '_': '/' }; -const safeBase64Replacer = s => safeBase64Map[s]; - /******************************************************************************/ // https://github.com/uBlockOrigin/uBlock-issues/issues/1626 diff --git a/src/js/urlskip.js b/src/js/urlskip.js new file mode 100644 index 0000000000000..e4182bbc4be24 --- /dev/null +++ b/src/js/urlskip.js @@ -0,0 +1,157 @@ +/******************************************************************************* + + uBlock Origin - a comprehensive, efficient content blocker + Copyright (C) 2022-present Raymond Hill + + 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/}. + + Home: https://github.com/gorhill/uBlock +*/ + +const safeBase64Map = { '-': '+', '_': '/' }; +const safeBase64Replacer = s => safeBase64Map[s]; + +/** + * @trustedOption urlskip + * + * @description + * Extract a URL from another URL according to one or more transformation steps, + * thereby skipping over intermediate network request(s) to remote servers. + * Requires a trusted source. + * + * @param steps + * A serie of space-separated directives representing the transformation steps + * to perform to extract the final URL to which a network request should be + * redirected. + * + * Supported directives: + * + * `?name`: extract the value of parameter `name` as the current string. + * + * `&i`: extract the name of the parameter at position `i` as the current + * string. The position is 1-based. + * + * `/.../`: extract the first capture group of a regex as the current string. + * + * `+https`: prepend the current string with `https://`. + * + * `-base64`: decode the current string as a base64-encoded string. + * + * `-safebase64`: decode the current string as a safe base64-encoded string. + * + * `-uricomponent`: decode the current string as a URI encoded string. + * + * `-blocked`: allow the redirection of blocked requests. By default, blocked + * requests can't by urlskip'ed. + * + * At any given step, the currently extracted string may not necessarily be + * a valid URL, and more transformation steps may be needed to obtain a valid + * URL once all the steps are applied. + * + * An unsupported step or a failed step will abort the transformation and no + * redirection will be performed. + * + * The final step is expected to yield a valid URL. If the result is not a + * valid URL, no redirection will be performed. + * + * @example + * ||example.com/path/to/tracker$urlskip=?url + * ||example.com/path/to/tracker$urlskip=?url ?to + * ||pixiv.net/jump.php?$urlskip=&1 + * ||podtrac.com/pts/redirect.mp3/$urlskip=/\/redirect\.mp3\/(.*?\.mp3\b)/ +https + * + * */ + +export function urlSkip(url, blocked, steps, directive = {}) { + try { + let redirectBlocked = false; + let urlout = url; + for ( const step of steps ) { + const urlin = urlout; + const c0 = step.charCodeAt(0); + // Extract from URL parameter name at position i + if ( c0 === 0x26 ) { // & + const i = (parseInt(step.slice(1)) || 0) - 1; + if ( i < 0 ) { return; } + const url = new URL(urlin); + if ( i >= url.searchParams.size ) { return; } + const params = Array.from(url.searchParams.keys()); + urlout = decodeURIComponent(params[i]); + continue; + } + // Enforce https + if ( c0 === 0x2B && step === '+https' ) { + const s = urlin.replace(/^https?:\/\//, ''); + if ( /^[\w-]:\/\//.test(s) ) { return; } + urlout = `https://${s}`; + continue; + } + // Decode + if ( c0 === 0x2D ) { + // Base64 + if ( step === '-base64' ) { + urlout = self.atob(urlin); + continue; + } + // Safe Base64 + if ( step === '-safebase64' ) { + urlout = urlin.replace(/[-_]/g, safeBase64Replacer); + urlout = self.atob(urlout); + continue; + } + // URI component + if ( step === '-uricomponent' ) { + urlout = self.decodeURIComponent(urlin); + continue; + } + // Enable skip of blocked requests + if ( step === '-blocked' ) { + redirectBlocked = true; + continue; + } + } + // Regex extraction from first capture group + if ( c0 === 0x2F ) { // / + const re = directive.cache ?? new RegExp(step.slice(1, -1)); + if ( directive.cache === null ) { + directive.cache = re; + } + const match = re.exec(urlin); + if ( match === null ) { return; } + if ( match.length <= 1 ) { return; } + urlout = match[1]; + continue; + } + // Extract from URL parameter + if ( c0 === 0x3F ) { // ? + urlout = (new URL(urlin)).searchParams.get(step.slice(1)); + if ( urlout === null ) { return; } + if ( urlout.includes(' ') ) { + urlout = urlout.replace(/ /g, '%20'); + } + continue; + } + // Unknown directive + return; + } + const urlfinal = new URL(urlout); + if ( urlfinal.protocol !== 'https:' ) { + if ( urlfinal.protocol !== 'http:' ) { return; } + urlout = urlout.replace('http', 'https'); + } + if ( blocked && redirectBlocked !== true ) { return; } + return urlout; + } catch(x) { + } +} diff --git a/tools/make-mv3.sh b/tools/make-mv3.sh index 287a7b08b26bd..5c812ec214dd5 100755 --- a/tools/make-mv3.sh +++ b/tools/make-mv3.sh @@ -76,6 +76,7 @@ cp "$UBO_DIR"/src/css/fa-icons.css "$DES"/css/ cp "$UBO_DIR"/src/js/dom.js "$DES"/js/ cp "$UBO_DIR"/src/js/fa-icons.js "$DES"/js/ cp "$UBO_DIR"/src/js/i18n.js "$DES"/js/ +cp "$UBO_DIR"/src/js/urlskip.js "$DES"/js/ cp "$UBO_DIR"/src/lib/punycode.js "$DES"/js/ cp -R "$UBO_DIR/src/img/flags-of-the-world" "$DES"/img diff --git a/tools/make-nodejs.sh b/tools/make-nodejs.sh index 87e96ddade909..ed8db6cbbf77a 100755 --- a/tools/make-nodejs.sh +++ b/tools/make-nodejs.sh @@ -22,6 +22,7 @@ cp src/js/static-net-filtering.js $DES/js cp src/js/static-filtering-io.js $DES/js cp src/js/tasks.js $DES/js cp src/js/text-utils.js $DES/js +cp src/js/urlskip.js $DES/js cp src/js/uri-utils.js $DES/js cp src/js/url-net-filtering.js $DES/js From b3a51d00180eb352a6424574f46b81fe362fa1b2 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Thu, 5 Dec 2024 13:09:07 -0500 Subject: [PATCH 0507/1099] Import translation work from https://crowdin.com/project/ublock --- .../mv3/extension/_locales/bg/messages.json | 4 ++-- .../extension/_locales/br_FR/messages.json | 4 ++-- .../mv3/extension/_locales/de/messages.json | 4 ++-- .../mv3/extension/_locales/es/messages.json | 4 ++-- .../mv3/extension/_locales/he/messages.json | 22 +++++++++---------- .../mv3/extension/_locales/hi/messages.json | 4 ++-- .../mv3/extension/_locales/hr/messages.json | 4 ++-- .../mv3/extension/_locales/it/messages.json | 4 ++-- .../mv3/extension/_locales/pl/messages.json | 4 ++-- .../extension/_locales/pt_BR/messages.json | 4 ++-- .../mv3/extension/_locales/sk/messages.json | 4 ++-- .../mv3/extension/_locales/sq/messages.json | 4 ++-- .../mv3/extension/_locales/sv/messages.json | 4 ++-- .../mv3/extension/_locales/tr/messages.json | 4 ++-- .../mv3/extension/_locales/uk/messages.json | 4 ++-- 15 files changed, 39 insertions(+), 39 deletions(-) diff --git a/platform/mv3/extension/_locales/bg/messages.json b/platform/mv3/extension/_locales/bg/messages.json index c312968671465..cf834661f3eb5 100644 --- a/platform/mv3/extension/_locales/bg/messages.json +++ b/platform/mv3/extension/_locales/bg/messages.json @@ -252,11 +252,11 @@ "description": "Sentence used in the strict-blocked page" }, "strictblockReasonSentence1": { - "message": "The page was blocked because of a matching filter in {{listname}}.", + "message": "Страницата е блокирана поради съвпадащ филтър в {{listname}}.", "description": "Text informing about what is causing the page to be blocked" }, "strictblockRedirectSentence1": { - "message": "The blocked page wants to redirect to another site. If you choose to proceed, you will navigate directly to: {{url}}", + "message": "Блокираната страница иска да ви пренасочи към друг сайт. Ако изберете да продължите, ще преминете директно към: {{url}}", "description": "Text warning about an incoming redirect" }, "strictblockNoParamsPrompt": { diff --git a/platform/mv3/extension/_locales/br_FR/messages.json b/platform/mv3/extension/_locales/br_FR/messages.json index 9fd91dcd3ba6f..31ae55bb1298b 100644 --- a/platform/mv3/extension/_locales/br_FR/messages.json +++ b/platform/mv3/extension/_locales/br_FR/messages.json @@ -252,11 +252,11 @@ "description": "Sentence used in the strict-blocked page" }, "strictblockReasonSentence1": { - "message": "The page was blocked because of a matching filter in {{listname}}.", + "message": "Stanket eo bet ar bajenn abalamour d'ur sil dereat e-barzh {{listname}}.", "description": "Text informing about what is causing the page to be blocked" }, "strictblockRedirectSentence1": { - "message": "The blocked page wants to redirect to another site. If you choose to proceed, you will navigate directly to: {{url}}", + "message": "Ar bajenn stanket a fell dezhi adkas d'ul lec'hienn all. M'ho peus c'hoant da genderc'hel e vioc'h kaset d'ar chomlec'h-mañ: {{url}}", "description": "Text warning about an incoming redirect" }, "strictblockNoParamsPrompt": { diff --git a/platform/mv3/extension/_locales/de/messages.json b/platform/mv3/extension/_locales/de/messages.json index dacd1edcf9793..af32e26d2f365 100644 --- a/platform/mv3/extension/_locales/de/messages.json +++ b/platform/mv3/extension/_locales/de/messages.json @@ -252,11 +252,11 @@ "description": "Sentence used in the strict-blocked page" }, "strictblockReasonSentence1": { - "message": "The page was blocked because of a matching filter in {{listname}}.", + "message": "Die Seite wurde aufgrund eines übereinstimmenden Filters in {{listname}} blockiert.", "description": "Text informing about what is causing the page to be blocked" }, "strictblockRedirectSentence1": { - "message": "The blocked page wants to redirect to another site. If you choose to proceed, you will navigate directly to: {{url}}", + "message": "Die blockierte Seite möchte zu einer anderen Website weiterleiten. Beim Fortfahren wird folgende Seite aufgerufen: {{url}}", "description": "Text warning about an incoming redirect" }, "strictblockNoParamsPrompt": { diff --git a/platform/mv3/extension/_locales/es/messages.json b/platform/mv3/extension/_locales/es/messages.json index 027bb7094e644..32fe1fc070f3c 100644 --- a/platform/mv3/extension/_locales/es/messages.json +++ b/platform/mv3/extension/_locales/es/messages.json @@ -252,11 +252,11 @@ "description": "Sentence used in the strict-blocked page" }, "strictblockReasonSentence1": { - "message": "The page was blocked because of a matching filter in {{listname}}.", + "message": "La página fue bloqueada debido a un filtro coincidente en {{listname}}.", "description": "Text informing about what is causing the page to be blocked" }, "strictblockRedirectSentence1": { - "message": "The blocked page wants to redirect to another site. If you choose to proceed, you will navigate directly to: {{url}}", + "message": "La página bloqueada quiere redirigir a otro sitio. Si eliges proceder, navegarás directamente a: {{url}}", "description": "Text warning about an incoming redirect" }, "strictblockNoParamsPrompt": { diff --git a/platform/mv3/extension/_locales/he/messages.json b/platform/mv3/extension/_locales/he/messages.json index 8a62a294e1745..2bbc73d76ef81 100644 --- a/platform/mv3/extension/_locales/he/messages.json +++ b/platform/mv3/extension/_locales/he/messages.json @@ -232,11 +232,11 @@ "description": "Label for a checkbox in the options page" }, "enableStrictBlockLabel": { - "message": "Enable strict blocking", + "message": "הפעלת חסימה קפדנית", "description": "Label for a checkbox in the options page" }, "enableStrictBlockLegend": { - "message": "Navigation to potentially undesirable sites will be blocked, and you will be offered the option to proceed.", + "message": "ניווט אפשרי לאתרים לא רצויים יחסם ותהיה אפשרות להחליט להמשיך.", "description": "Label for a checkbox in the options page" }, "findListsPlaceholder": { @@ -244,39 +244,39 @@ "description": "Placeholder for the input field used to find lists" }, "strictblockTitle": { - "message": "Page blocked", + "message": "הדף נחסם", "description": "Webpage title for the strict-blocked page" }, "strictblockSentence1": { - "message": "uBO Lite has prevented the following page from loading:", + "message": "uBO Lite מנע טעינה של הדפים הבאים:", "description": "Sentence used in the strict-blocked page" }, "strictblockReasonSentence1": { - "message": "The page was blocked because of a matching filter in {{listname}}.", + "message": "הדף נחסם בגלל התאמה למסנן מהרשימה {{listname}}", "description": "Text informing about what is causing the page to be blocked" }, "strictblockRedirectSentence1": { - "message": "The blocked page wants to redirect to another site. If you choose to proceed, you will navigate directly to: {{url}}", + "message": "הדף החסום רוצה להעביר אותך לאתר אחר. בחירה להמשיך תעבור ישירות ל {{url}}", "description": "Text warning about an incoming redirect" }, "strictblockNoParamsPrompt": { - "message": "without parameters", + "message": "ללא פרמטרים", "description": "Label to be used for the parameter-less URL" }, "strictblockBack": { - "message": "Go back", + "message": "חזור", "description": "A button to go back to the previous webpage" }, "strictblockClose": { - "message": "Close this window", + "message": "סגור חלון זה", "description": "A button to close the current tab" }, "strictblockDontWarn": { - "message": "Don't warn me again about this site", + "message": "אל תתריע לי שוב על אתר זה", "description": "Label for checkbox in document-blocked page" }, "strictblockProceed": { - "message": "Proceed", + "message": "המשך", "description": "A button to navigate to the blocked page" } } diff --git a/platform/mv3/extension/_locales/hi/messages.json b/platform/mv3/extension/_locales/hi/messages.json index 7ec113adf70d4..66334c281411a 100644 --- a/platform/mv3/extension/_locales/hi/messages.json +++ b/platform/mv3/extension/_locales/hi/messages.json @@ -252,11 +252,11 @@ "description": "Sentence used in the strict-blocked page" }, "strictblockReasonSentence1": { - "message": "The page was blocked because of a matching filter in {{listname}}.", + "message": "पृष्ठ को {{listname}} में मेल खाते फ़िल्टर के कारण अवरुद्ध किया गया था.", "description": "Text informing about what is causing the page to be blocked" }, "strictblockRedirectSentence1": { - "message": "The blocked page wants to redirect to another site. If you choose to proceed, you will navigate directly to: {{url}}", + "message": "ब्लॉक किया गया पेज किसी दूसरी साइट पर रीडायरेक्ट करना चाहता है. अगर आप आगे बढ़ना चुनते हैं, तो आप सीधे इस पर नेविगेट करें: {{url}}", "description": "Text warning about an incoming redirect" }, "strictblockNoParamsPrompt": { diff --git a/platform/mv3/extension/_locales/hr/messages.json b/platform/mv3/extension/_locales/hr/messages.json index be7af624f95a7..116371621df8f 100644 --- a/platform/mv3/extension/_locales/hr/messages.json +++ b/platform/mv3/extension/_locales/hr/messages.json @@ -252,11 +252,11 @@ "description": "Sentence used in the strict-blocked page" }, "strictblockReasonSentence1": { - "message": "The page was blocked because of a matching filter in {{listname}}.", + "message": "Stranica je blokirana zbog odgovarajućeg filtra u {{listname}}.", "description": "Text informing about what is causing the page to be blocked" }, "strictblockRedirectSentence1": { - "message": "The blocked page wants to redirect to another site. If you choose to proceed, you will navigate directly to: {{url}}", + "message": "Blokirana stranica želi preusmjeriti na drugu stranicu. Ako odlučite nastaviti, otići ćete izravno na: {{url}}", "description": "Text warning about an incoming redirect" }, "strictblockNoParamsPrompt": { diff --git a/platform/mv3/extension/_locales/it/messages.json b/platform/mv3/extension/_locales/it/messages.json index 1e82bba9fc3d4..3a7da35ac8aa9 100644 --- a/platform/mv3/extension/_locales/it/messages.json +++ b/platform/mv3/extension/_locales/it/messages.json @@ -252,11 +252,11 @@ "description": "Sentence used in the strict-blocked page" }, "strictblockReasonSentence1": { - "message": "The page was blocked because of a matching filter in {{listname}}.", + "message": "La pagina è stata bloccata a causa di un filtro corrispondente in {{listname}}.", "description": "Text informing about what is causing the page to be blocked" }, "strictblockRedirectSentence1": { - "message": "The blocked page wants to redirect to another site. If you choose to proceed, you will navigate directly to: {{url}}", + "message": "La pagina bloccata vuole reindirizzare a un altro sito. Se scegli di procedere, navigherai direttamente su: {{url}}", "description": "Text warning about an incoming redirect" }, "strictblockNoParamsPrompt": { diff --git a/platform/mv3/extension/_locales/pl/messages.json b/platform/mv3/extension/_locales/pl/messages.json index 8a7afcc037e44..ededd11838d77 100644 --- a/platform/mv3/extension/_locales/pl/messages.json +++ b/platform/mv3/extension/_locales/pl/messages.json @@ -252,11 +252,11 @@ "description": "Sentence used in the strict-blocked page" }, "strictblockReasonSentence1": { - "message": "The page was blocked because of a matching filter in {{listname}}.", + "message": "Strona została zablokowana z powodu pasującego filtra na liście {{listname}}.", "description": "Text informing about what is causing the page to be blocked" }, "strictblockRedirectSentence1": { - "message": "The blocked page wants to redirect to another site. If you choose to proceed, you will navigate directly to: {{url}}", + "message": "Zablokowana strona chce przekierować na inną witrynę. Jeśli zdecydujesz się kontynuować, przejdziesz bezpośrednio do: {{url}}", "description": "Text warning about an incoming redirect" }, "strictblockNoParamsPrompt": { diff --git a/platform/mv3/extension/_locales/pt_BR/messages.json b/platform/mv3/extension/_locales/pt_BR/messages.json index 77ad6cb270579..b92f5b59579eb 100644 --- a/platform/mv3/extension/_locales/pt_BR/messages.json +++ b/platform/mv3/extension/_locales/pt_BR/messages.json @@ -252,11 +252,11 @@ "description": "Sentence used in the strict-blocked page" }, "strictblockReasonSentence1": { - "message": "The page was blocked because of a matching filter in {{listname}}.", + "message": "A página foi bloqueada por causa de um filtro que combina no {{listname}}.", "description": "Text informing about what is causing the page to be blocked" }, "strictblockRedirectSentence1": { - "message": "The blocked page wants to redirect to another site. If you choose to proceed, you will navigate directly to: {{url}}", + "message": "A página bloqueada quer redirecionar para outro site. Se você escolher prosseguir, você navegará diretamente para: {{url}}", "description": "Text warning about an incoming redirect" }, "strictblockNoParamsPrompt": { diff --git a/platform/mv3/extension/_locales/sk/messages.json b/platform/mv3/extension/_locales/sk/messages.json index 79cd5c4e57912..e33e990e4185b 100644 --- a/platform/mv3/extension/_locales/sk/messages.json +++ b/platform/mv3/extension/_locales/sk/messages.json @@ -252,11 +252,11 @@ "description": "Sentence used in the strict-blocked page" }, "strictblockReasonSentence1": { - "message": "The page was blocked because of a matching filter in {{listname}}.", + "message": "Stránka bola zablokovaná z dôvodu zhodného filtra v{{listname}}.", "description": "Text informing about what is causing the page to be blocked" }, "strictblockRedirectSentence1": { - "message": "The blocked page wants to redirect to another site. If you choose to proceed, you will navigate directly to: {{url}}", + "message": "Zablokovaná stránka chce presmerovať na inú stránku. Ak sa rozhodnete pokračovať, prejdete priamo na: {{url}}", "description": "Text warning about an incoming redirect" }, "strictblockNoParamsPrompt": { diff --git a/platform/mv3/extension/_locales/sq/messages.json b/platform/mv3/extension/_locales/sq/messages.json index 5d3d191728ab2..42a8679f0a2bd 100644 --- a/platform/mv3/extension/_locales/sq/messages.json +++ b/platform/mv3/extension/_locales/sq/messages.json @@ -252,11 +252,11 @@ "description": "Sentence used in the strict-blocked page" }, "strictblockReasonSentence1": { - "message": "The page was blocked because of a matching filter in {{listname}}.", + "message": "Faqja u bllokua për shkak të një filtri që përputhet në: {{listname}}.", "description": "Text informing about what is causing the page to be blocked" }, "strictblockRedirectSentence1": { - "message": "The blocked page wants to redirect to another site. If you choose to proceed, you will navigate directly to: {{url}}", + "message": "Faqja e bllokuar do t'ju drejtojë në një uebsajt tjetër. Në rast se pranoni do të shkoni te: {{url}}", "description": "Text warning about an incoming redirect" }, "strictblockNoParamsPrompt": { diff --git a/platform/mv3/extension/_locales/sv/messages.json b/platform/mv3/extension/_locales/sv/messages.json index d9c8c7762228a..e164490906acc 100644 --- a/platform/mv3/extension/_locales/sv/messages.json +++ b/platform/mv3/extension/_locales/sv/messages.json @@ -252,11 +252,11 @@ "description": "Sentence used in the strict-blocked page" }, "strictblockReasonSentence1": { - "message": "The page was blocked because of a matching filter in {{listname}}.", + "message": "Sidan har blockerats på grund av ett matchande filter i {{listname}}.", "description": "Text informing about what is causing the page to be blocked" }, "strictblockRedirectSentence1": { - "message": "The blocked page wants to redirect to another site. If you choose to proceed, you will navigate directly to: {{url}}", + "message": "Den blockerade sidan vill omdirigera till en annan webbplats. Om du väljer att fortsätta skickas du direkt till: {{url}}", "description": "Text warning about an incoming redirect" }, "strictblockNoParamsPrompt": { diff --git a/platform/mv3/extension/_locales/tr/messages.json b/platform/mv3/extension/_locales/tr/messages.json index 8fa869b596ef0..0fb973629fedc 100644 --- a/platform/mv3/extension/_locales/tr/messages.json +++ b/platform/mv3/extension/_locales/tr/messages.json @@ -252,11 +252,11 @@ "description": "Sentence used in the strict-blocked page" }, "strictblockReasonSentence1": { - "message": "The page was blocked because of a matching filter in {{listname}}.", + "message": "Bu sayfa {{listname}} içindeki bir süzgece takıldığı için engellenmiştir.", "description": "Text informing about what is causing the page to be blocked" }, "strictblockRedirectSentence1": { - "message": "The blocked page wants to redirect to another site. If you choose to proceed, you will navigate directly to: {{url}}", + "message": "Engellenen sayfa sizi başka bir siteye yönlendirmek istiyor. Devam etmek isterseniz doğrudan şuraya yönlendirileceksiniz: {{url}}", "description": "Text warning about an incoming redirect" }, "strictblockNoParamsPrompt": { diff --git a/platform/mv3/extension/_locales/uk/messages.json b/platform/mv3/extension/_locales/uk/messages.json index 51e42d2efb696..c6d372980a1e6 100644 --- a/platform/mv3/extension/_locales/uk/messages.json +++ b/platform/mv3/extension/_locales/uk/messages.json @@ -252,11 +252,11 @@ "description": "Sentence used in the strict-blocked page" }, "strictblockReasonSentence1": { - "message": "The page was blocked because of a matching filter in {{listname}}.", + "message": "Сторінку заблоковано, бо вона відповідає фільтру в {{listname}}.", "description": "Text informing about what is causing the page to be blocked" }, "strictblockRedirectSentence1": { - "message": "The blocked page wants to redirect to another site. If you choose to proceed, you will navigate directly to: {{url}}", + "message": "Заблокована сторінка хоче переадресувати на інший сайт. Якщо ви вирішите продовжити, ви перейдете безпосередньо на: {{url}}", "description": "Text warning about an incoming redirect" }, "strictblockNoParamsPrompt": { From 36db7f83274ae219d68e24d4aa477c007050e951 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Fri, 6 Dec 2024 10:15:42 -0500 Subject: [PATCH 0508/1099] Block media elements unconditionally when max size is set to 0 Related feedback: https://old.reddit.com/r/uBlockOrigin/comments/1h7x9nj/ --- src/js/pagestore.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/js/pagestore.js b/src/js/pagestore.js index 44a5674cbe718..7adca8482847e 100644 --- a/src/js/pagestore.js +++ b/src/js/pagestore.js @@ -1063,7 +1063,9 @@ const PageStore = class { this.largeMediaTimer.on(500); } const size = headers.contentLength; - if ( isNaN(size) ) { return 0; } + if ( isNaN(size) ) { + return µb.userSettings.largeMediaSize === 0 ? 1 : 0; + } if ( (size >>> 10) < µb.userSettings.largeMediaSize ) { return 0; } this.largeMediaCount += 1; this.largeMediaTimer.on(500); From 3417fe3d5dcbf2666846761466da1927ead2e9a5 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Fri, 6 Dec 2024 11:53:42 -0500 Subject: [PATCH 0509/1099] Improve `trusted-replace-argument` scriptlet As discussed with filter list maintainers. --- src/js/resources/attribute.js | 4 +-- src/js/resources/cookie.js | 16 +++++++++--- src/js/resources/proxy-apply.js | 4 +-- src/js/resources/replace-argument.js | 32 ++++++++++++++++------- src/js/resources/safe-self.js | 1 + src/js/resources/scriptlets.js | 38 ++++++++++++++++------------ src/js/resources/set-constant.js | 1 + 7 files changed, 63 insertions(+), 33 deletions(-) diff --git a/src/js/resources/attribute.js b/src/js/resources/attribute.js index b6ecdf7a3e597..ed735eecdd70b 100644 --- a/src/js/resources/attribute.js +++ b/src/js/resources/attribute.js @@ -229,7 +229,7 @@ export function removeAttr( if ( rawToken === '' ) { return; } const safe = safeSelf(); const logPrefix = safe.makeLogPrefix('remove-attr', rawToken, rawSelector, behavior); - const tokens = rawToken.split(/\s*\|\s*/); + const tokens = safe.String_split.call(rawToken, /\s*\|\s*/); const selector = tokens .map(a => `${rawSelector}[${CSS.escape(a)}]`) .join(','); @@ -289,7 +289,7 @@ export function removeAttr( subtree: true, }); }; - runAt(( ) => { start(); }, behavior.split(/\s+/)); + runAt(( ) => { start(); }, safe.String_split.call(behavior, /\s+/)); } registerScriptlet(removeAttr, { name: 'remove-attr.js', diff --git a/src/js/resources/cookie.js b/src/js/resources/cookie.js index 3fbeb1858d122..29ad7315cc805 100644 --- a/src/js/resources/cookie.js +++ b/src/js/resources/cookie.js @@ -53,7 +53,8 @@ registerScriptlet(getSafeCookieValuesFn, { /******************************************************************************/ export function getAllCookiesFn() { - return document.cookie.split(/\s*;\s*/).map(s => { + const safe = safeSelf(); + return safe.String_split.call(document.cookie, /\s*;\s*/).map(s => { const pos = s.indexOf('='); if ( pos === 0 ) { return; } if ( pos === -1 ) { return `${s.trim()}=`; } @@ -64,6 +65,9 @@ export function getAllCookiesFn() { } registerScriptlet(getAllCookiesFn, { name: 'get-all-cookies.fn', + dependencies: [ + safeSelf, + ], }); /******************************************************************************/ @@ -71,7 +75,8 @@ registerScriptlet(getAllCookiesFn, { export function getCookieFn( name = '' ) { - for ( const s of document.cookie.split(/\s*;\s*/) ) { + const safe = safeSelf(); + for ( const s of safe.String_split.call(document.cookie, /\s*;\s*/) ) { const pos = s.indexOf('='); if ( pos === -1 ) { continue; } if ( s.slice(0, pos) !== name ) { continue; } @@ -80,6 +85,9 @@ export function getCookieFn( } registerScriptlet(getCookieFn, { name: 'get-cookie.fn', + dependencies: [ + safeSelf, + ], }); /******************************************************************************/ @@ -349,7 +357,7 @@ export function removeCookie( }, ms); }; const remove = ( ) => { - document.cookie.split(';').forEach(cookieStr => { + safe.String_split.call(document.cookie, ';').forEach(cookieStr => { const pos = cookieStr.indexOf('='); if ( pos === -1 ) { return; } const cookieName = cookieStr.slice(0, pos).trim(); @@ -387,7 +395,7 @@ export function removeCookie( window.addEventListener('beforeunload', remove); if ( typeof extraArgs.when !== 'string' ) { return; } const supportedEventTypes = [ 'scroll', 'keydown' ]; - const eventTypes = extraArgs.when.split(/\s/); + const eventTypes = safe.String_split.call(extraArgs.when, /\s/); for ( const type of eventTypes ) { if ( supportedEventTypes.includes(type) === false ) { continue; } document.addEventListener(type, ( ) => { diff --git a/src/js/resources/proxy-apply.js b/src/js/resources/proxy-apply.js index 09d357f9052b6..60326f54617e2 100644 --- a/src/js/resources/proxy-apply.js +++ b/src/js/resources/proxy-apply.js @@ -52,7 +52,7 @@ export function proxyApplyFn( } reflect() { const r = Reflect.construct(this.callFn, this.callArgs); - this.callFn = this.callArgs = undefined; + this.callFn = this.callArgs = this.private = undefined; proxyApplyFn.ctorContexts.push(this); return r; } @@ -75,7 +75,7 @@ export function proxyApplyFn( } reflect() { const r = Reflect.apply(this.callFn, this.thisArg, this.callArgs); - this.callFn = this.thisArg = this.callArgs = undefined; + this.callFn = this.thisArg = this.callArgs = this.private = undefined; proxyApplyFn.applyContexts.push(this); return r; } diff --git a/src/js/resources/replace-argument.js b/src/js/resources/replace-argument.js index 150bf047b8a39..f06938b74d2dd 100644 --- a/src/js/resources/replace-argument.js +++ b/src/js/resources/replace-argument.js @@ -71,25 +71,39 @@ export function trustedReplaceArgument( const reCondition = extraArgs.condition ? safe.patternToRegex(extraArgs.condition) : /^/; - proxyApplyFn(propChain, function(context) { + const getArg = context => { + if ( argposRaw === 'this' ) { return context.thisArg; } const { callArgs } = context; - if ( argposRaw === '' ) { - safe.uboLog(logPrefix, `Arguments:\n${callArgs.join('\n')}`); - return context.reflect(); - } const argpos = argoffset >= 0 ? argoffset : callArgs.length - argoffset; - if ( argpos < 0 || argpos >= callArgs.length ) { + if ( argpos < 0 || argpos >= callArgs.length ) { return; } + context.private = { argpos }; + return callArgs[argpos]; + }; + const setArg = (context, value) => { + if ( argposRaw === 'this' ) { + if ( value !== context.thisArg ) { + context.thisArg = value; + } + } else if ( context.private ) { + context.callArgs[context.private.argpos] = value; + } + }; + proxyApplyFn(propChain, function(context) { + if ( argposRaw === '' ) { + safe.uboLog(logPrefix, `Arguments:\n${context.callArgs.join('\n')}`); return context.reflect(); } - const argBefore = callArgs[argpos]; + const argBefore = getArg(context); if ( safe.RegExp_test.call(reCondition, argBefore) === false ) { return context.reflect(); } const argAfter = replacer && typeof argBefore === 'string' ? argBefore.replace(replacer.re, replacer.replacement) : value; - callArgs[argpos] = argAfter; - safe.uboLog(logPrefix, `Replaced argument:\nBefore: ${JSON.stringify(argBefore)}\nAfter: ${argAfter}`); + if ( argAfter !== argBefore ) { + setArg(context, argAfter); + safe.uboLog(logPrefix, `Replaced argument:\nBefore: ${JSON.stringify(argBefore)}\nAfter: ${argAfter}`); + } return context.reflect(); }); } diff --git a/src/js/resources/safe-self.js b/src/js/resources/safe-self.js index a1840e13b6464..afb98722113fa 100644 --- a/src/js/resources/safe-self.js +++ b/src/js/resources/safe-self.js @@ -51,6 +51,7 @@ export function safeSelf() { 'RegExp_exec': self.RegExp.prototype.exec, 'Request_clone': self.Request.prototype.clone, 'String_fromCharCode': String.fromCharCode, + 'String_split': String.prototype.split, 'XMLHttpRequest': self.XMLHttpRequest, 'addEventListener': self.EventTarget.prototype.addEventListener, 'removeEventListener': self.EventTarget.prototype.removeEventListener, diff --git a/src/js/resources/scriptlets.js b/src/js/resources/scriptlets.js index d1e4c4b58d8e1..f8a761990b946 100644 --- a/src/js/resources/scriptlets.js +++ b/src/js/resources/scriptlets.js @@ -192,7 +192,7 @@ function abortCurrentScriptCore( const reContext = safe.patternToRegex(context); const extraArgs = safe.getExtraArgs(Array.from(arguments), 3); const thisScript = document.currentScript; - const chain = target.split('.'); + const chain = safe.String_split.call(target, '.'); let owner = window; let prop; for (;;) { @@ -406,6 +406,7 @@ builtinScriptlets.push({ dependencies: [ 'matches-stack-trace.fn', 'object-find-owner.fn', + 'safe-self.fn', ], }); // When no "prune paths" argument is provided, the scriptlet is @@ -422,11 +423,12 @@ function objectPruneFn( extraArgs = {} ) { if ( typeof rawPrunePaths !== 'string' ) { return; } + const safe = safeSelf(); const prunePaths = rawPrunePaths !== '' - ? rawPrunePaths.split(/ +/) + ? safe.String_split.call(rawPrunePaths, / +/) : []; const needlePaths = prunePaths.length !== 0 && rawNeedlePaths !== '' - ? rawNeedlePaths.split(/ +/) + ? safe.String_split.call(rawNeedlePaths, / +/) : []; if ( stackNeedleDetails.matchAll !== true ) { if ( matchesStackTrace(stackNeedleDetails, extraArgs.logstack) === false ) { @@ -547,7 +549,7 @@ function matchesStackTrace( // Normalize stack trace const reLine = /(.*?@)?(\S+)(:\d+):\d+\)?$/; const lines = []; - for ( let line of error.stack.split(/[\n\r]+/) ) { + for ( let line of safe.String_split.call(error.stack, /[\n\r]+/) ) { if ( line.includes(exceptionToken) ) { continue; } line = line.trim(); const match = safe.RegExp_exec.call(reLine, line); @@ -594,8 +596,8 @@ function parsePropertiesToMatch(propsToMatch, implicit = '') { const needles = new Map(); if ( propsToMatch === undefined || propsToMatch === '' ) { return needles; } const options = { canNegate: true }; - for ( const needle of propsToMatch.split(/\s+/) ) { - const [ prop, pattern ] = needle.split(':'); + for ( const needle of safe.String_split.call(propsToMatch, /\s+/) ) { + const [ prop, pattern ] = safe.String_split.call(needle, ':'); if ( prop === '' ) { continue; } if ( pattern !== undefined ) { needles.set(prop, safe.initPattern(pattern, options)); @@ -1643,7 +1645,7 @@ function noFetchIf( const safe = safeSelf(); const logPrefix = safe.makeLogPrefix('prevent-fetch', propsToMatch, responseBody, responseType); const needles = []; - for ( const condition of propsToMatch.split(/\s+/) ) { + for ( const condition of safe.String_split.call(propsToMatch, /\s+/) ) { if ( condition === '' ) { continue; } const pos = condition.indexOf(':'); let key, value; @@ -1797,7 +1799,7 @@ function removeClass( if ( rawToken === '' ) { return; } const safe = safeSelf(); const logPrefix = safe.makeLogPrefix('remove-class', rawToken, rawSelector, behavior); - const tokens = rawToken.split(/\s*\|\s*/); + const tokens = safe.String_split.call(rawToken, /\s*\|\s*/); const selector = tokens .map(a => `${rawSelector}.${CSS.escape(a)}`) .join(','); @@ -2510,12 +2512,12 @@ function m3uPrune( } text = before.trim() + '\n' + after.trim(); reM3u.lastIndex = before.length + 1; - toLog.push('Discarding', ...discard.split(/\n+/).map(s => `\t${s}`)); + toLog.push('Discarding', ...safe.String_split.call(discard, /\n+/).map(s => `\t${s}`)); if ( reM3u.global === false ) { break; } } return text; } - const lines = text.split(/\n\r|\n|\r/); + const lines = safe.String_split.call(text, /\n\r|\n|\r/); for ( let i = 0; i < lines.length; i++ ) { if ( lines[i] === undefined ) { continue; } if ( pruneSpliceoutBlock(lines, i) ) { continue; } @@ -2758,13 +2760,17 @@ function hrefSanitizer( builtinScriptlets.push({ name: 'call-nothrow.js', fn: callNothrow, + dependencies: [ + 'safe-self.fn', + ], }); function callNothrow( chain = '' ) { if ( typeof chain !== 'string' ) { return; } if ( chain === '' ) { return; } - const parts = chain.split('.'); + const safe = safeSelf(); + const parts = safe.String_split.call(chain, '.'); let owner = window, prop; for (;;) { prop = parts.shift(); @@ -3095,7 +3101,7 @@ function trustedClickElement( const logPrefix = safe.makeLogPrefix('trusted-click-element', selectors, extraMatch, delay); if ( extraMatch !== '' ) { - const assertions = extraMatch.split(',').map(s => { + const assertions = safe.String_split.call(extraMatch, ',').map(s => { const pos1 = s.indexOf(':'); const s1 = pos1 !== -1 ? s.slice(0, pos1) : s; const not = s1.startsWith('!'); @@ -3163,7 +3169,7 @@ function trustedClickElement( return shadowRoot && querySelectorEx(inside, shadowRoot); }; - const selectorList = selectors.split(/\s*,\s*/) + const selectorList = safe.String_split.call(selectors, /\s*,\s*/) .filter(s => { try { void querySelectorEx(s); @@ -3290,10 +3296,10 @@ function trustedPruneInboundObject( const extraArgs = safe.getExtraArgs(Array.from(arguments), 4); const needlePaths = []; if ( rawPrunePaths !== '' ) { - needlePaths.push(...rawPrunePaths.split(/ +/)); + needlePaths.push(...safe.String_split.call(rawPrunePaths, / +/)); } if ( rawNeedlePaths !== '' ) { - needlePaths.push(...rawNeedlePaths.split(/ +/)); + needlePaths.push(...safe.String_split.call(rawNeedlePaths, / +/)); } const stackNeedle = safe.initPattern(extraArgs.stackToMatch || '', { canNegate: true }); const mustProcess = root => { @@ -3455,7 +3461,7 @@ function trustedSuppressNativeMethod( if ( stack !== '' ) { return; } const safe = safeSelf(); const logPrefix = safe.makeLogPrefix('trusted-suppress-native-method', methodPath, signature, how); - const signatureArgs = signature.split(/\s*\|\s*/).map(v => { + const signatureArgs = safe.String_split.call(signature, /\s*\|\s*/).map(v => { if ( /^".*"$/.test(v) ) { return { type: 'pattern', re: safe.patternToRegex(v.slice(1, -1)) }; } diff --git a/src/js/resources/set-constant.js b/src/js/resources/set-constant.js index 127f27bbbe910..66584fb81d3e6 100644 --- a/src/js/resources/set-constant.js +++ b/src/js/resources/set-constant.js @@ -61,6 +61,7 @@ export function validateConstantFn(trusted, raw, extraArgs = {}) { } else if ( raw.startsWith('{') && raw.endsWith('}') ) { try { value = safe.JSON_parse(raw).value; } catch(ex) { return; } } + return raw; } else { return; } From 0d0754a57bfd65025061237071d262f72bfb08f3 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Fri, 6 Dec 2024 11:56:33 -0500 Subject: [PATCH 0510/1099] Update changelog --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1f406ffd55b87..a2f2928b71c2b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,6 @@ +- [Improve `trusted-replace-argument` scriptlet](https://github.com/gorhill/uBlock/commit/3417fe3d5d) +- [Block media elements unconditionally when max size is set to 0](https://github.com/gorhill/uBlock/commit/36db7f8327) + - Regression from - [Visually separate scriptlet parameters in active line](https://github.com/gorhill/uBlock/commit/076e9fa73e) - [Mitigate potentially delayed execution of scriptlets in Firefox](https://github.com/gorhill/uBlock/commit/b1a00145bd) - [Improve `prevent-setTimeout`/`prevent-setInterval` scriptlets](https://github.com/gorhill/uBlock/commit/3b7fa79a68) From 0701422dcf30c8c0010cd839101ccaa278cd7763 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Fri, 6 Dec 2024 11:56:56 -0500 Subject: [PATCH 0511/1099] New revision for dev build --- dist/version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dist/version b/dist/version index 8a93a27b73714..34da318f7a219 100644 --- a/dist/version +++ b/dist/version @@ -1 +1 @@ -1.61.3.4 \ No newline at end of file +1.61.3.5 \ No newline at end of file From 4ed97bb513e0a9fa229194e4c25d8332198d56d0 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Fri, 6 Dec 2024 12:04:21 -0500 Subject: [PATCH 0512/1099] Update JSDoc documenation --- src/js/resources/replace-argument.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/js/resources/replace-argument.js b/src/js/resources/replace-argument.js index f06938b74d2dd..c5927d2b9dc35 100644 --- a/src/js/resources/replace-argument.js +++ b/src/js/resources/replace-argument.js @@ -38,7 +38,8 @@ import { validateConstantFn } from './set-constant.js'; * * @param argposRaw * The zero-based position of the argument in the argument list. Use a negative - * number for a position relative to the last argument. + * number for a position relative to the last argument. Use literal `this` to + * replace the value used in `prototype`-based methods. * * @param argraw * The replacement value, validated using the same heuristic as with the From 4e7bdff8edff35b862b0b7ecd75a6d462845e46d Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Fri, 6 Dec 2024 12:11:15 -0500 Subject: [PATCH 0513/1099] Make Firefox dev build auto-update --- dist/firefox/updates.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dist/firefox/updates.json b/dist/firefox/updates.json index 67cb51badbed6..f8302e8163f60 100644 --- a/dist/firefox/updates.json +++ b/dist/firefox/updates.json @@ -3,9 +3,9 @@ "uBlock0@raymondhill.net": { "updates": [ { - "version": "1.61.3.4", + "version": "1.61.3.5", "browser_specific_settings": { "gecko": { "strict_min_version": "78.0" } }, - "update_link": "https://github.com/gorhill/uBlock/releases/download/1.61.3b4/uBlock0_1.61.3b4.firefox.signed.xpi" + "update_link": "https://github.com/gorhill/uBlock/releases/download/1.61.3b5/uBlock0_1.61.3b5.firefox.signed.xpi" } ] } From 424fc816281ce053f8e41b0776fcefd73e5fc8a8 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Fri, 6 Dec 2024 12:13:18 -0500 Subject: [PATCH 0514/1099] [mv3] Minor code review --- platform/mv3/extension/css/strictblock.css | 3 +- platform/mv3/extension/js/strictblock.js | 56 +++++++++++++--------- 2 files changed, 35 insertions(+), 24 deletions(-) diff --git a/platform/mv3/extension/css/strictblock.css b/platform/mv3/extension/css/strictblock.css index 7f1f0e9c0d162..5a459446434a3 100644 --- a/platform/mv3/extension/css/strictblock.css +++ b/platform/mv3/extension/css/strictblock.css @@ -50,7 +50,7 @@ q { font-weight: bold; } .code { - font-size: 13px; + font-size: var(--font-size-smaller); word-break: break-all; } #warningSign { @@ -137,7 +137,6 @@ body[dir="rtl"] #toggleParse { #urlskip a { display: block; overflow-y: auto; - word-break: break-all; } #actionContainer { diff --git a/platform/mv3/extension/js/strictblock.js b/platform/mv3/extension/js/strictblock.js index 7600bcfbb9e7f..2e8c74c67e5ea 100644 --- a/platform/mv3/extension/js/strictblock.js +++ b/platform/mv3/extension/js/strictblock.js @@ -72,6 +72,31 @@ qs$('#theURL > p > span:first-of-type').append(urlToFragment(toURL.href)); /******************************************************************************/ +function fragmentFromTemplate(template, placeholder, text, details) { + const fragment = new DocumentFragment(); + const pos = template.indexOf(placeholder); + if ( pos === -1 ) { + fragment.append(template); + return fragment; + } + const elem = document.createElement(details.tag); + const { attributes } = details; + if ( attributes ) { + for ( let i = 0; i < attributes.length; i+= 2 ) { + elem.setAttribute(attributes[i+0], attributes[i+1]); + } + } + elem.append(text); + fragment.append( + template.slice(0, pos), + elem, + template.slice(pos + placeholder.length) + ); + return fragment; +} + +/******************************************************************************/ + // https://github.com/gorhill/uBlock/issues/691 // Parse URL to extract as much useful information as possible. This is // useful to assist the user in deciding whether to navigate to the web page. @@ -179,15 +204,11 @@ qs$('#theURL > p > span:first-of-type').append(urlToFragment(toURL.href)); if ( toFetch.length === 0 ) { return; } await Promise.all(toFetch); if ( iList === -1 ) { return; } - - const fragment = new DocumentFragment(); - const text = i18n$('strictblockReasonSentence1'); - const placeholder = '{{listname}}'; - const pos = text.indexOf(placeholder); - if ( pos === -1 ) { return; } - const q = document.createElement('q'); - q.append(rulesetDetails[iList].name); - fragment.append(text.slice(0, pos), q, text.slice(pos + placeholder.length)); + const fragment = fragmentFromTemplate( + i18n$('strictblockReasonSentence1'), + '{{listname}}', rulesetDetails[iList].name, + { tag: 'q' } + ); qs$('#reason').append(fragment); dom.attr('#reason', 'hidden', null); })(); @@ -210,19 +231,10 @@ qs$('#theURL > p > span:first-of-type').append(urlToFragment(toURL.href)); if ( re.test(toURL.href) === false ) { continue; } const finalURL = urlSkip(toURL.href, false, urlskip.steps); if ( finalURL === undefined ) { continue; } - const fragment = new DocumentFragment(); - const text = i18n$('strictblockRedirectSentence1'); - const linkPlaceholder = '{{url}}'; - const pos = text.indexOf(linkPlaceholder); - if ( pos === -1 ) { return; } - const link = document.createElement('a'); - link.href = finalURL; - dom.cl.add(link, 'code'); - link.append(urlToFragment(finalURL)); - fragment.append( - text.slice(0, pos), - link, - text.slice(pos + linkPlaceholder.length) + const fragment = fragmentFromTemplate( + i18n$('strictblockRedirectSentence1'), + '{{url}}', urlToFragment(finalURL), + { tag: 'a', attributes: [ 'href', finalURL, 'class', 'code' ] } ); qs$('#urlskip').append(fragment); dom.attr('#urlskip', 'hidden', null); From ec5a1b3ab6c7abe6a5f94c44cef5d9e51ffb169d Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Fri, 6 Dec 2024 17:21:30 -0500 Subject: [PATCH 0515/1099] Import translation work from https://crowdin.com/project/ublock --- .../mv3/extension/_locales/ca/messages.json | 4 ++-- .../mv3/extension/_locales/da/messages.json | 4 ++-- .../mv3/extension/_locales/de/messages.json | 2 +- .../mv3/extension/_locales/et/messages.json | 4 ++-- .../mv3/extension/_locales/fi/messages.json | 4 ++-- .../mv3/extension/_locales/fr/messages.json | 4 ++-- .../mv3/extension/_locales/gl/messages.json | 4 ++-- .../mv3/extension/_locales/lv/messages.json | 4 ++-- .../mv3/extension/_locales/nl/messages.json | 22 +++++++++---------- .../mv3/extension/_locales/ru/messages.json | 4 ++-- .../extension/_locales/zh_CN/messages.json | 4 ++-- .../extension/_locales/zh_TW/messages.json | 4 ++-- 12 files changed, 32 insertions(+), 32 deletions(-) diff --git a/platform/mv3/extension/_locales/ca/messages.json b/platform/mv3/extension/_locales/ca/messages.json index 070b24a5b38bf..a11095949897a 100644 --- a/platform/mv3/extension/_locales/ca/messages.json +++ b/platform/mv3/extension/_locales/ca/messages.json @@ -252,11 +252,11 @@ "description": "Sentence used in the strict-blocked page" }, "strictblockReasonSentence1": { - "message": "The page was blocked because of a matching filter in {{listname}}.", + "message": "La pàgina s'ha blocat a causa d'un filtre coincident a {{listname}}.", "description": "Text informing about what is causing the page to be blocked" }, "strictblockRedirectSentence1": { - "message": "The blocked page wants to redirect to another site. If you choose to proceed, you will navigate directly to: {{url}}", + "message": "La pàgina blocada vol redirigir-vos a un altre web diferent. Si continueu, si us reenviarà a: {{url}}", "description": "Text warning about an incoming redirect" }, "strictblockNoParamsPrompt": { diff --git a/platform/mv3/extension/_locales/da/messages.json b/platform/mv3/extension/_locales/da/messages.json index 25b24263ac287..a89b9c535d609 100644 --- a/platform/mv3/extension/_locales/da/messages.json +++ b/platform/mv3/extension/_locales/da/messages.json @@ -252,11 +252,11 @@ "description": "Sentence used in the strict-blocked page" }, "strictblockReasonSentence1": { - "message": "The page was blocked because of a matching filter in {{listname}}.", + "message": "Siden blev blokeret grundet et matchende filter i {{listname}}.", "description": "Text informing about what is causing the page to be blocked" }, "strictblockRedirectSentence1": { - "message": "The blocked page wants to redirect to another site. If you choose to proceed, you will navigate directly to: {{url}}", + "message": "Den blokerede side ønsker at omdirigere til et andet websted. Vælger man at fortsætte, navigeres direkte til: {{url}}", "description": "Text warning about an incoming redirect" }, "strictblockNoParamsPrompt": { diff --git a/platform/mv3/extension/_locales/de/messages.json b/platform/mv3/extension/_locales/de/messages.json index af32e26d2f365..de200b1f5e779 100644 --- a/platform/mv3/extension/_locales/de/messages.json +++ b/platform/mv3/extension/_locales/de/messages.json @@ -232,7 +232,7 @@ "description": "Label for a checkbox in the options page" }, "enableStrictBlockLabel": { - "message": "Stricktes Blockieren aktivieren", + "message": "Striktes Blockieren aktivieren", "description": "Label for a checkbox in the options page" }, "enableStrictBlockLegend": { diff --git a/platform/mv3/extension/_locales/et/messages.json b/platform/mv3/extension/_locales/et/messages.json index 9d498685a3a50..a6dc44ddafb36 100644 --- a/platform/mv3/extension/_locales/et/messages.json +++ b/platform/mv3/extension/_locales/et/messages.json @@ -252,11 +252,11 @@ "description": "Sentence used in the strict-blocked page" }, "strictblockReasonSentence1": { - "message": "The page was blocked because of a matching filter in {{listname}}.", + "message": "Lehe avamine keelati {{listname}} tõttu.", "description": "Text informing about what is causing the page to be blocked" }, "strictblockRedirectSentence1": { - "message": "The blocked page wants to redirect to another site. If you choose to proceed, you will navigate directly to: {{url}}", + "message": "Keelatud leht üritab suunata teisele lehele. Jätkates nõustute avama {{url}}", "description": "Text warning about an incoming redirect" }, "strictblockNoParamsPrompt": { diff --git a/platform/mv3/extension/_locales/fi/messages.json b/platform/mv3/extension/_locales/fi/messages.json index 761093b972b74..905e8319fecb7 100644 --- a/platform/mv3/extension/_locales/fi/messages.json +++ b/platform/mv3/extension/_locales/fi/messages.json @@ -252,11 +252,11 @@ "description": "Sentence used in the strict-blocked page" }, "strictblockReasonSentence1": { - "message": "The page was blocked because of a matching filter in {{listname}}.", + "message": "Sivu estettiin listalla {{listname}} olevan sännön perusteella.", "description": "Text informing about what is causing the page to be blocked" }, "strictblockRedirectSentence1": { - "message": "The blocked page wants to redirect to another site. If you choose to proceed, you will navigate directly to: {{url}}", + "message": "Estetty sivu ohjautuu eri sivustolle. Jos jatkat, siirryt suoraan osoitteeseen {{url}}", "description": "Text warning about an incoming redirect" }, "strictblockNoParamsPrompt": { diff --git a/platform/mv3/extension/_locales/fr/messages.json b/platform/mv3/extension/_locales/fr/messages.json index 4bba2ac8f412c..58cf3803e7f88 100644 --- a/platform/mv3/extension/_locales/fr/messages.json +++ b/platform/mv3/extension/_locales/fr/messages.json @@ -252,11 +252,11 @@ "description": "Sentence used in the strict-blocked page" }, "strictblockReasonSentence1": { - "message": "The page was blocked because of a matching filter in {{listname}}.", + "message": "La page a été bloquée à cause d'un filtre correspondant dans {{listname}}", "description": "Text informing about what is causing the page to be blocked" }, "strictblockRedirectSentence1": { - "message": "The blocked page wants to redirect to another site. If you choose to proceed, you will navigate directly to: {{url}}", + "message": "La page bloquée souhaite rediriger vers un autre site. Si vous choisissez de continuer, vous vous rendrez à l'adresse suivante : {{url}}", "description": "Text warning about an incoming redirect" }, "strictblockNoParamsPrompt": { diff --git a/platform/mv3/extension/_locales/gl/messages.json b/platform/mv3/extension/_locales/gl/messages.json index 67f783e282dc1..9afa714e8a941 100644 --- a/platform/mv3/extension/_locales/gl/messages.json +++ b/platform/mv3/extension/_locales/gl/messages.json @@ -252,11 +252,11 @@ "description": "Sentence used in the strict-blocked page" }, "strictblockReasonSentence1": { - "message": "The page was blocked because of a matching filter in {{listname}}.", + "message": "Bloqueouse a páxina porque concorda cun filtro de {{listname}}.", "description": "Text informing about what is causing the page to be blocked" }, "strictblockRedirectSentence1": { - "message": "The blocked page wants to redirect to another site. If you choose to proceed, you will navigate directly to: {{url}}", + "message": "A páxina bloqueada quere redirixir a outra web. Se elixes continuar vas ir directamente a: {{url}}", "description": "Text warning about an incoming redirect" }, "strictblockNoParamsPrompt": { diff --git a/platform/mv3/extension/_locales/lv/messages.json b/platform/mv3/extension/_locales/lv/messages.json index 4f388fa909693..53b675ac7de02 100644 --- a/platform/mv3/extension/_locales/lv/messages.json +++ b/platform/mv3/extension/_locales/lv/messages.json @@ -252,11 +252,11 @@ "description": "Sentence used in the strict-blocked page" }, "strictblockReasonSentence1": { - "message": "The page was blocked because of a matching filter in {{listname}}.", + "message": "Lapa tika aizturēta, jo atbilst aizturētājam sarakstā {{listname}}.", "description": "Text informing about what is causing the page to be blocked" }, "strictblockRedirectSentence1": { - "message": "The blocked page wants to redirect to another site. If you choose to proceed, you will navigate directly to: {{url}}", + "message": "Aizturētā lapa veic pārvirzīšanu uz citu vietni. Ja izvēlēsies turpināt, nonāksi uzreiz šeit: {{url}}", "description": "Text warning about an incoming redirect" }, "strictblockNoParamsPrompt": { diff --git a/platform/mv3/extension/_locales/nl/messages.json b/platform/mv3/extension/_locales/nl/messages.json index d1874b6aed8f0..a170351aa536e 100644 --- a/platform/mv3/extension/_locales/nl/messages.json +++ b/platform/mv3/extension/_locales/nl/messages.json @@ -232,11 +232,11 @@ "description": "Label for a checkbox in the options page" }, "enableStrictBlockLabel": { - "message": "Enable strict blocking", + "message": "Strenge blokkering inschakelen", "description": "Label for a checkbox in the options page" }, "enableStrictBlockLegend": { - "message": "Navigation to potentially undesirable sites will be blocked, and you will be offered the option to proceed.", + "message": "Navigatie naar mogelijk ongewenste websites wordt geblokkeerd, en nu krijgt de mogelijkheid om door te gaan.", "description": "Label for a checkbox in the options page" }, "findListsPlaceholder": { @@ -244,39 +244,39 @@ "description": "Placeholder for the input field used to find lists" }, "strictblockTitle": { - "message": "Page blocked", + "message": "Pagina geblokkeerd", "description": "Webpage title for the strict-blocked page" }, "strictblockSentence1": { - "message": "uBO Lite has prevented the following page from loading:", + "message": "uBO Lite heeft het laden van de volgende pagina voorkomen:", "description": "Sentence used in the strict-blocked page" }, "strictblockReasonSentence1": { - "message": "The page was blocked because of a matching filter in {{listname}}.", + "message": "De pagina is geblokkeerd vanwege een overeenkomend filter in {{listname}}.", "description": "Text informing about what is causing the page to be blocked" }, "strictblockRedirectSentence1": { - "message": "The blocked page wants to redirect to another site. If you choose to proceed, you will navigate directly to: {{url}}", + "message": "De geblokkeerde pagina wil u omleiden naar een andere website. Als u doorgaat, navigeert u rechtstreeks naar: {{url}}", "description": "Text warning about an incoming redirect" }, "strictblockNoParamsPrompt": { - "message": "without parameters", + "message": "zonder parameters", "description": "Label to be used for the parameter-less URL" }, "strictblockBack": { - "message": "Go back", + "message": "Teruggaan", "description": "A button to go back to the previous webpage" }, "strictblockClose": { - "message": "Close this window", + "message": "Dit venster sluiten", "description": "A button to close the current tab" }, "strictblockDontWarn": { - "message": "Don't warn me again about this site", + "message": "Mij niet meer waarschuwen over deze website", "description": "Label for checkbox in document-blocked page" }, "strictblockProceed": { - "message": "Proceed", + "message": "Doorgaan", "description": "A button to navigate to the blocked page" } } diff --git a/platform/mv3/extension/_locales/ru/messages.json b/platform/mv3/extension/_locales/ru/messages.json index 53c354a7787b1..bd89af9f8d8a6 100644 --- a/platform/mv3/extension/_locales/ru/messages.json +++ b/platform/mv3/extension/_locales/ru/messages.json @@ -252,11 +252,11 @@ "description": "Sentence used in the strict-blocked page" }, "strictblockReasonSentence1": { - "message": "The page was blocked because of a matching filter in {{listname}}.", + "message": "Веб-страница была заблокирована из-за попадания в фильтр из {{listname}}.", "description": "Text informing about what is causing the page to be blocked" }, "strictblockRedirectSentence1": { - "message": "The blocked page wants to redirect to another site. If you choose to proceed, you will navigate directly to: {{url}}", + "message": "Заблокированная страница собирается перенаправить вас на другой сайт. Если вы решите продолжить, вы перейдете непосредственно на: {{url}}", "description": "Text warning about an incoming redirect" }, "strictblockNoParamsPrompt": { diff --git a/platform/mv3/extension/_locales/zh_CN/messages.json b/platform/mv3/extension/_locales/zh_CN/messages.json index 490bc10672f00..136a6284673da 100644 --- a/platform/mv3/extension/_locales/zh_CN/messages.json +++ b/platform/mv3/extension/_locales/zh_CN/messages.json @@ -252,11 +252,11 @@ "description": "Sentence used in the strict-blocked page" }, "strictblockReasonSentence1": { - "message": "The page was blocked because of a matching filter in {{listname}}.", + "message": "该页面由于 {{listName}} 中的匹配过滤器而被阻止。", "description": "Text informing about what is causing the page to be blocked" }, "strictblockRedirectSentence1": { - "message": "The blocked page wants to redirect to another site. If you choose to proceed, you will navigate directly to: {{url}}", + "message": "被屏蔽的页面希望重定向到另一个网站。如果您选择继续,将直接导航到:{{url}}", "description": "Text warning about an incoming redirect" }, "strictblockNoParamsPrompt": { diff --git a/platform/mv3/extension/_locales/zh_TW/messages.json b/platform/mv3/extension/_locales/zh_TW/messages.json index 003ef30a71f68..7a80c28a11484 100644 --- a/platform/mv3/extension/_locales/zh_TW/messages.json +++ b/platform/mv3/extension/_locales/zh_TW/messages.json @@ -252,11 +252,11 @@ "description": "Sentence used in the strict-blocked page" }, "strictblockReasonSentence1": { - "message": "The page was blocked because of a matching filter in {{listname}}.", + "message": "由於在 {{listname}} 中有符合的過濾條件,因此該頁面被封鎖。", "description": "Text informing about what is causing the page to be blocked" }, "strictblockRedirectSentence1": { - "message": "The blocked page wants to redirect to another site. If you choose to proceed, you will navigate directly to: {{url}}", + "message": "被封鎖的網頁想要重新導向至其他網站。如果您選擇繼續,則會直接前往:{{url}}", "description": "Text warning about an incoming redirect" }, "strictblockNoParamsPrompt": { From 56ba93700c092c00f869fc85129046fe096c1c00 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sun, 8 Dec 2024 08:37:29 -0500 Subject: [PATCH 0516/1099] [mv3] Ensure no generic cosmetic filters end up in specific realm Related issue: https://github.com/uBlockOrigin/uBOL-home/issues/254 Cosmetic filters with only negated hostnames would end up being classified as specific, while in reality the filter is generic, with specific exceptions. This commit fixes the improper classification. Enforcing specific exception filters in the generic realm is still an issue to address. --- platform/mv3/make-rulesets.js | 13 +---- src/js/static-dnr-filtering.js | 97 +++++++++++++++++++--------------- 2 files changed, 55 insertions(+), 55 deletions(-) diff --git a/platform/mv3/make-rulesets.js b/platform/mv3/make-rulesets.js index 4ea7ae6dba1d0..9df90963cc7be 100644 --- a/platform/mv3/make-rulesets.js +++ b/platform/mv3/make-rulesets.js @@ -83,11 +83,6 @@ const uidint32 = (s) => { return parseInt(h,16) & 0x7FFFFFFF; }; -const hnSort = (a, b) => - a.split('.').reverse().join('.').localeCompare( - b.split('.').reverse().join('.') - ); - /******************************************************************************/ const stdOutput = []; @@ -681,14 +676,10 @@ function groupHostnamesBySelectors(arrayin) { const out = Array.from(contentMap).map(a => [ a[0], { a: a[1].a, - y: a[1].y ? Array.from(a[1].y).sort(hnSort) : '*', + y: a[1].y ? Array.from(a[1].y) : undefined, n: a[1].n ? Array.from(a[1].n) : undefined, } - ]).sort((a, b) => { - const ha = Array.isArray(a[1].y) ? a[1].y[0] : '*'; - const hb = Array.isArray(b[1].y) ? b[1].y[0] : '*'; - return hnSort(ha, hb); - }); + ]); return out; } diff --git a/src/js/static-dnr-filtering.js b/src/js/static-dnr-filtering.js index 63639006585c2..9935fd161e45c 100644 --- a/src/js/static-dnr-filtering.js +++ b/src/js/static-dnr-filtering.js @@ -90,6 +90,39 @@ const keyFromSelector = selector => { /******************************************************************************/ +function addGenericCosmeticFilter(context, selector, isException) { + if ( selector === undefined ) { return; } + if ( selector.length <= 1 ) { return; } + if ( isException ) { + if ( context.genericCosmeticExceptions === undefined ) { + context.genericCosmeticExceptions = new Set(); + } + context.genericCosmeticExceptions.add(selector); + return; + } + if ( selector.charCodeAt(0) === 0x7B /* '{' */ ) { return; } + const key = keyFromSelector(selector); + if ( key === undefined ) { + if ( context.genericHighCosmeticFilters === undefined ) { + context.genericHighCosmeticFilters = new Set(); + } + context.genericHighCosmeticFilters.add(selector); + return; + } + const type = key.charCodeAt(0); + const hash = hashFromStr(type, key.slice(1)); + if ( context.genericCosmeticFilters === undefined ) { + context.genericCosmeticFilters = new Map(); + } + let bucket = context.genericCosmeticFilters.get(hash); + if ( bucket === undefined ) { + context.genericCosmeticFilters.set(hash, bucket = []); + } + bucket.push(selector); +} + +/******************************************************************************/ + function addExtendedToDNR(context, parser) { if ( parser.isExtendedFilter() === false ) { return false; } @@ -195,35 +228,8 @@ function addExtendedToDNR(context, parser) { // Generic cosmetic filtering if ( parser.hasOptions() === false ) { - const { compiled } = parser.result; - if ( compiled === undefined ) { return; } - if ( compiled.length <= 1 ) { return; } - if ( parser.isException() ) { - if ( context.genericCosmeticExceptions === undefined ) { - context.genericCosmeticExceptions = new Set(); - } - context.genericCosmeticExceptions.add(compiled); - return; - } - if ( compiled.charCodeAt(0) === 0x7B /* '{' */ ) { return; } - const key = keyFromSelector(compiled); - if ( key === undefined ) { - if ( context.genericHighCosmeticFilters === undefined ) { - context.genericHighCosmeticFilters = new Set(); - } - context.genericHighCosmeticFilters.add(compiled); - return; - } - const type = key.charCodeAt(0); - const hash = hashFromStr(type, key.slice(1)); - if ( context.genericCosmeticFilters === undefined ) { - context.genericCosmeticFilters = new Map(); - } - let bucket = context.genericCosmeticFilters.get(hash); - if ( bucket === undefined ) { - context.genericCosmeticFilters.set(hash, bucket = []); - } - bucket.push(compiled); + const { compiled, exception } = parser.result; + addGenericCosmeticFilter(context, compiled, exception); return; } @@ -234,26 +240,22 @@ function addExtendedToDNR(context, parser) { if ( context.specificCosmeticFilters === undefined ) { context.specificCosmeticFilters = new Map(); } + const { compiled, exception, raw } = parser.result; + if ( compiled === undefined ) { + context.specificCosmeticFilters.set(`Invalid filter: ...##${raw}`, { + rejected: true + }); + return; + } + let details = context.specificCosmeticFilters.get(compiled); for ( const { hn, not, bad } of parser.getExtFilterDomainIterator() ) { if ( bad ) { continue; } + if ( not && exception ) { continue; } if ( isRegex(hn) ) { continue; } - let { compiled, exception, raw } = parser.result; - if ( exception ) { continue; } - let rejected; - if ( compiled === undefined ) { - rejected = `Invalid filter: ${hn}##${raw}`; - } - if ( rejected ) { - compiled = rejected; - } - let details = context.specificCosmeticFilters.get(compiled); if ( details === undefined ) { - details = {}; - if ( rejected ) { details.rejected = true; } - context.specificCosmeticFilters.set(compiled, details); + context.specificCosmeticFilters.set(compiled, details = {}); } - if ( rejected ) { continue; } - if ( not ) { + if ( exception ) { if ( details.excludeMatches === undefined ) { details.excludeMatches = []; } @@ -270,6 +272,13 @@ function addExtendedToDNR(context, parser) { } details.matches.push(hn); } + if ( details === undefined ) { return; } + if ( exception ) { return; } + if ( compiled.startsWith('{') ) { return; } + if ( details.matches === undefined || details.matches.includes('*') ) { + addGenericCosmeticFilter(context, compiled, false); + details.matches = undefined; + } } /******************************************************************************/ From 6c6ac6c7a7bd6e08ba4e70fbd102e7f41af544ef Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sun, 8 Dec 2024 16:58:23 -0500 Subject: [PATCH 0517/1099] Import translation work from https://crowdin.com/project/ublock --- platform/mv3/description/webstore.zh_TW.txt | 14 ++++----- .../mv3/extension/_locales/el/messages.json | 4 +-- .../mv3/extension/_locales/es/messages.json | 4 +-- .../mv3/extension/_locales/hu/messages.json | 4 +-- .../mv3/extension/_locales/ka/messages.json | 22 +++++++------- .../mv3/extension/_locales/ko/messages.json | 22 +++++++------- .../extension/_locales/pt_PT/messages.json | 30 +++++++++---------- .../mv3/extension/_locales/sq/messages.json | 18 +++++------ .../extension/_locales/zh_TW/messages.json | 18 +++++------ src/_locales/hu/messages.json | 2 +- src/_locales/pt_PT/messages.json | 4 +-- 11 files changed, 71 insertions(+), 71 deletions(-) diff --git a/platform/mv3/description/webstore.zh_TW.txt b/platform/mv3/description/webstore.zh_TW.txt index 8af192573f3b8..ddc587c6f9762 100644 --- a/platform/mv3/description/webstore.zh_TW.txt +++ b/platform/mv3/description/webstore.zh_TW.txt @@ -1,6 +1,6 @@ uBO Lite (uBOL) 是款以 MV3 為基礎的「免權限」內容阻擋器。 -預設規則集對應了 uBlock Origin 的預設過濾集: +預設規則集對應着 uBlock Origin 的預設過濾集: - uBlock Origin 內建的過濾器清單 - EasyList @@ -9,19 +9,19 @@ uBO Lite (uBOL) 是款以 MV3 為基礎的「免權限」內容阻擋器。 您可以前往選項頁面(按下彈出面板的 **齒輪** 按鈕)啟用更多規則集。 -uBOL 是完全宣告式的,意即過濾過程中不需要持續性的 uBOL 處理程序參與,且以 CSS/JS 注入為基礎進行的內容過濾由可靠的瀏覽器執行,而非是擴充功能。 這就代表 uBOL 在內容阻擋過程不會佔用 CPU 和記憶體資源——除了和彈出面板或選項頁面互動的場景外,都不需要 uBOL 的 Service Worker 程序。 +uBOL 是完全宣告式的,意即過濾過程中不需要持續性的 uBOL 處理程序參與,且以 CSS/JS 注入為基礎的內容過濾由可靠的瀏覽器執行,而非是擴充功能。 這就代表 uBOL 在內容阻擋過程不會佔用 CPU 和記憶體資源——除了和彈出面板或選項頁面互動的場景外,都不需要 uBOL 的 Service Worker 程序。 -uBOL 在安裝期間不需要氾濫的「讀取與修改資料」權限,因此它出廠時的功能和 uBlock Origin 或其他在安裝期間要求「讀取與修改資料」權限的內容阻擋程式相比,會相對受限。 +uBOL 在安裝期間不需要廣泛的「讀取與修改資料」權限,因此它出廠時的功能和 uBlock Origin 或其他在安裝期間要求該權限的內容阻擋程式相比,相對受限。 -不過 uBOL 能讓你 **明確地** 在自選的特定網站授予額外的權限,使其在這些網站的過濾效果可以在元素過濾及 scripetlet 注入的加持下得到提升。 +不過 uBOL 能讓你 **明確地** 在自選的特定網站授予額外的權限,使其在這些網站的過濾效果可以在元素過濾及 scripetlet 注入的加持下提升。 -若要授予指定網站延伸權限,請開啟對話框並選擇更高的過濾模式,如「最佳化」或「完整」。 +若要授予指定網站延伸權限,請開啟彈出面板並選擇更佳的過濾模式,如「最佳」或「完整」。 -瀏覽器接著會警告您授予擴充功能請求的額外權限會帶來的後果,而你需要告訴瀏覽器要同意還是拒絕請求。 +瀏覽器接著會警告您授予擴充功能請求的額外權限所帶來的後果,而你需要告訴瀏覽器要同意還是拒絕。 如果你接受 uBOL 在目前網站請求的額外權限,其在這個網站的過濾效果將會更好。 -您可以在 uBOL 的選項頁面設定預設的過濾模式。 如果您選擇「最佳化」或「完整」為預設的模式,您需要授予 uBOL 讀取與修改所有網站資料的權限。 +您可以在 uBOL 的選項頁面設定預設的過濾模式。 如果您選擇「最佳」或「完整」為預設模式,您需要授予 uBOL 讀取與修改所有網站資料的權限。 注意這尚未完工,最終目標有: diff --git a/platform/mv3/extension/_locales/el/messages.json b/platform/mv3/extension/_locales/el/messages.json index 1bfb831e66ad6..66f33670c7be0 100644 --- a/platform/mv3/extension/_locales/el/messages.json +++ b/platform/mv3/extension/_locales/el/messages.json @@ -252,11 +252,11 @@ "description": "Sentence used in the strict-blocked page" }, "strictblockReasonSentence1": { - "message": "The page was blocked because of a matching filter in {{listname}}.", + "message": "Η σελίδα αποκλείστηκε λόγω αντιστοίχισης φίλτρου στη {{listname}}.", "description": "Text informing about what is causing the page to be blocked" }, "strictblockRedirectSentence1": { - "message": "The blocked page wants to redirect to another site. If you choose to proceed, you will navigate directly to: {{url}}", + "message": "Η αποκλεισμένη σελίδα θέλει να κάνει ανακατεύθυνση σε άλλο ιστότοπο. Αν επιλέξετε να συνεχίσετε, θα μεταβείτε απευθείας στο: {{url}}", "description": "Text warning about an incoming redirect" }, "strictblockNoParamsPrompt": { diff --git a/platform/mv3/extension/_locales/es/messages.json b/platform/mv3/extension/_locales/es/messages.json index 32fe1fc070f3c..10eec6a0d1a76 100644 --- a/platform/mv3/extension/_locales/es/messages.json +++ b/platform/mv3/extension/_locales/es/messages.json @@ -252,11 +252,11 @@ "description": "Sentence used in the strict-blocked page" }, "strictblockReasonSentence1": { - "message": "La página fue bloqueada debido a un filtro coincidente en {{listname}}.", + "message": "La página ha sido bloqueada como resultado de un filtro coincidente en {{listname}}.", "description": "Text informing about what is causing the page to be blocked" }, "strictblockRedirectSentence1": { - "message": "La página bloqueada quiere redirigir a otro sitio. Si eliges proceder, navegarás directamente a: {{url}}", + "message": "La página bloqueada quiere redirigir a otro sitio. Si eliges continuar, navegarás directamente a: {{url}}", "description": "Text warning about an incoming redirect" }, "strictblockNoParamsPrompt": { diff --git a/platform/mv3/extension/_locales/hu/messages.json b/platform/mv3/extension/_locales/hu/messages.json index f6663ad3c65aa..9583d57488d17 100644 --- a/platform/mv3/extension/_locales/hu/messages.json +++ b/platform/mv3/extension/_locales/hu/messages.json @@ -252,11 +252,11 @@ "description": "Sentence used in the strict-blocked page" }, "strictblockReasonSentence1": { - "message": "The page was blocked because of a matching filter in {{listname}}.", + "message": "Az oldal a(z) {{listname}} listában lévő illeszkedő szűrő miatt blokkolva lett.", "description": "Text informing about what is causing the page to be blocked" }, "strictblockRedirectSentence1": { - "message": "The blocked page wants to redirect to another site. If you choose to proceed, you will navigate directly to: {{url}}", + "message": "A blokkolt oldal egy másik webhelyre akarja átirányítani. Ha a folytatást választja, akkor közvetlenül ide fog navigálni: {{url}}", "description": "Text warning about an incoming redirect" }, "strictblockNoParamsPrompt": { diff --git a/platform/mv3/extension/_locales/ka/messages.json b/platform/mv3/extension/_locales/ka/messages.json index c916896c69457..6b1e1608fb41f 100644 --- a/platform/mv3/extension/_locales/ka/messages.json +++ b/platform/mv3/extension/_locales/ka/messages.json @@ -232,11 +232,11 @@ "description": "Label for a checkbox in the options page" }, "enableStrictBlockLabel": { - "message": "Enable strict blocking", + "message": "მკაცრი შეზღუდვის ჩართვა", "description": "Label for a checkbox in the options page" }, "enableStrictBlockLegend": { - "message": "Navigation to potentially undesirable sites will be blocked, and you will be offered the option to proceed.", + "message": "შეიზღუდა სავარაუდოდ არასასურველ გვერდებზე გადასვლა, საშუალება გეძლევათ, მაინც განაგრძოთ.", "description": "Label for a checkbox in the options page" }, "findListsPlaceholder": { @@ -244,39 +244,39 @@ "description": "Placeholder for the input field used to find lists" }, "strictblockTitle": { - "message": "Page blocked", + "message": "შეზღუდული გვერდი", "description": "Webpage title for the strict-blocked page" }, "strictblockSentence1": { - "message": "uBO Lite has prevented the following page from loading:", + "message": "uBO Lite ზღუდავს მოცემული გვერდის ჩატვირთვას:", "description": "Sentence used in the strict-blocked page" }, "strictblockReasonSentence1": { - "message": "The page was blocked because of a matching filter in {{listname}}.", + "message": "გვერდი შეიზღუდა ფილტრით {{listname}}.", "description": "Text informing about what is causing the page to be blocked" }, "strictblockRedirectSentence1": { - "message": "The blocked page wants to redirect to another site. If you choose to proceed, you will navigate directly to: {{url}}", + "message": "ეს შეზღუდული გვერდი ცდილობს თქვენს სხვა საიტზე გადაყვანას. თუ განაგრძობთ, გაიხსნება: {{url}}", "description": "Text warning about an incoming redirect" }, "strictblockNoParamsPrompt": { - "message": "without parameters", + "message": "პარამეტრების გარეშე", "description": "Label to be used for the parameter-less URL" }, "strictblockBack": { - "message": "Go back", + "message": "უკან დაბრუნება", "description": "A button to go back to the previous webpage" }, "strictblockClose": { - "message": "Close this window", + "message": "ამ ფანჯრის დახურვა", "description": "A button to close the current tab" }, "strictblockDontWarn": { - "message": "Don't warn me again about this site", + "message": "აღარ მეცნობოს ამ საიტზე", "description": "Label for checkbox in document-blocked page" }, "strictblockProceed": { - "message": "Proceed", + "message": "გაგრძელება", "description": "A button to navigate to the blocked page" } } diff --git a/platform/mv3/extension/_locales/ko/messages.json b/platform/mv3/extension/_locales/ko/messages.json index 66de739efbc37..a8a6767a081ed 100644 --- a/platform/mv3/extension/_locales/ko/messages.json +++ b/platform/mv3/extension/_locales/ko/messages.json @@ -232,11 +232,11 @@ "description": "Label for a checkbox in the options page" }, "enableStrictBlockLabel": { - "message": "Enable strict blocking", + "message": "엄격한 차단 켜기", "description": "Label for a checkbox in the options page" }, "enableStrictBlockLegend": { - "message": "Navigation to potentially undesirable sites will be blocked, and you will be offered the option to proceed.", + "message": "잠재적으로 좋지 않은 사이트로의 접속을 차단하고, 사용자가 진행할지 선택하도록 합니다.", "description": "Label for a checkbox in the options page" }, "findListsPlaceholder": { @@ -244,39 +244,39 @@ "description": "Placeholder for the input field used to find lists" }, "strictblockTitle": { - "message": "Page blocked", + "message": "페이지 차단됨", "description": "Webpage title for the strict-blocked page" }, "strictblockSentence1": { - "message": "uBO Lite has prevented the following page from loading:", + "message": "uBO Lite가 다음 페이지를 불러오지 못하게 했습니다.", "description": "Sentence used in the strict-blocked page" }, "strictblockReasonSentence1": { - "message": "The page was blocked because of a matching filter in {{listname}}.", + "message": "이 페이지가 {{listname}} 내 필터링 항목에 해당하여 차단되었습니다.", "description": "Text informing about what is causing the page to be blocked" }, "strictblockRedirectSentence1": { - "message": "The blocked page wants to redirect to another site. If you choose to proceed, you will navigate directly to: {{url}}", + "message": "차단된 페이지에서 다른 사이트로 이동하려 합니다. 계속하시면, {{url}} 주소로 바로 이동합니다.", "description": "Text warning about an incoming redirect" }, "strictblockNoParamsPrompt": { - "message": "without parameters", + "message": "매개변수 없음", "description": "Label to be used for the parameter-less URL" }, "strictblockBack": { - "message": "Go back", + "message": "뒤로", "description": "A button to go back to the previous webpage" }, "strictblockClose": { - "message": "Close this window", + "message": "창 닫기", "description": "A button to close the current tab" }, "strictblockDontWarn": { - "message": "Don't warn me again about this site", + "message": "다시는 이 사이트에 대해 경고하지 않기", "description": "Label for checkbox in document-blocked page" }, "strictblockProceed": { - "message": "Proceed", + "message": "계속", "description": "A button to navigate to the blocked page" } } diff --git a/platform/mv3/extension/_locales/pt_PT/messages.json b/platform/mv3/extension/_locales/pt_PT/messages.json index cc744201ba365..9be55f5d94ae5 100644 --- a/platform/mv3/extension/_locales/pt_PT/messages.json +++ b/platform/mv3/extension/_locales/pt_PT/messages.json @@ -112,7 +112,7 @@ "description": "First paragraph of 'Filter issues' section in Support pane" }, "supportS6P1S1": { - "message": "Para evitar sobrecarregar os voluntários com relatórios duplicados, por favor verifique se o problema ainda não foi relatado.", + "message": "Para evitar sobrecarregar os voluntários com relatórios duplicados, verifique se o problema ainda não foi relatado.", "description": "A paragraph in the filter issue reporter section" }, "supportFindSpecificButton": { @@ -124,7 +124,7 @@ "description": "Label for the URL of the page" }, "supportS6Select1": { - "message": "A página web...", + "message": "A página web…", "description": "Label for widget to select type of issue" }, "supportS6Select1Option0": { @@ -140,7 +140,7 @@ "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option3": { - "message": "Deteta uBO Lite", + "message": "Deteta o uBO Lite", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option4": { @@ -148,7 +148,7 @@ "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option5": { - "message": "Falha quando o uBO Lite é ativado", + "message": "Falha quando o uBO Lite está ativado", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option6": { @@ -232,11 +232,11 @@ "description": "Label for a checkbox in the options page" }, "enableStrictBlockLabel": { - "message": "Enable strict blocking", + "message": "Ativar bloqueio estrito", "description": "Label for a checkbox in the options page" }, "enableStrictBlockLegend": { - "message": "Navigation to potentially undesirable sites will be blocked, and you will be offered the option to proceed.", + "message": "A navegação para sites potencialmente indesejáveis será bloqueada e ser-lhe-á dada a opção de proceder.", "description": "Label for a checkbox in the options page" }, "findListsPlaceholder": { @@ -244,39 +244,39 @@ "description": "Placeholder for the input field used to find lists" }, "strictblockTitle": { - "message": "Page blocked", + "message": "Página bloqueada", "description": "Webpage title for the strict-blocked page" }, "strictblockSentence1": { - "message": "uBO Lite has prevented the following page from loading:", + "message": "O uBO Lite impediu a seguinte página de ser carregada:", "description": "Sentence used in the strict-blocked page" }, "strictblockReasonSentence1": { - "message": "The page was blocked because of a matching filter in {{listname}}.", + "message": "A página foi bloqueada devido a um filtro correspondente em {{listname}}.", "description": "Text informing about what is causing the page to be blocked" }, "strictblockRedirectSentence1": { - "message": "The blocked page wants to redirect to another site. If you choose to proceed, you will navigate directly to: {{url}}", + "message": "A página bloqueada pretende redirecionar para outro site. Se optar por proceder, navegará diretamente para: {{url}}", "description": "Text warning about an incoming redirect" }, "strictblockNoParamsPrompt": { - "message": "without parameters", + "message": "sem parâmetros", "description": "Label to be used for the parameter-less URL" }, "strictblockBack": { - "message": "Go back", + "message": "Voltar", "description": "A button to go back to the previous webpage" }, "strictblockClose": { - "message": "Close this window", + "message": "Fechar esta janela", "description": "A button to close the current tab" }, "strictblockDontWarn": { - "message": "Don't warn me again about this site", + "message": "Não me avisar novamente acerca deste site", "description": "Label for checkbox in document-blocked page" }, "strictblockProceed": { - "message": "Proceed", + "message": "Proceder", "description": "A button to navigate to the blocked page" } } diff --git a/platform/mv3/extension/_locales/sq/messages.json b/platform/mv3/extension/_locales/sq/messages.json index 42a8679f0a2bd..9019b355488de 100644 --- a/platform/mv3/extension/_locales/sq/messages.json +++ b/platform/mv3/extension/_locales/sq/messages.json @@ -232,11 +232,11 @@ "description": "Label for a checkbox in the options page" }, "enableStrictBlockLabel": { - "message": "Aktivizo bllokimin strikt", + "message": "Aktivizoj bllokimin strikt", "description": "Label for a checkbox in the options page" }, "enableStrictBlockLegend": { - "message": "Lundrimi drejt faqeve potencialisht të padëshirueshme do të bllokohet dhe do t'ju ofrohet mundësia për të vazhduar.", + "message": "Uebsajtet potencialisht të padëshirueshme do të bllokohen dhe do t'ju jepet mundësia për të vazhduar.", "description": "Label for a checkbox in the options page" }, "findListsPlaceholder": { @@ -248,15 +248,15 @@ "description": "Webpage title for the strict-blocked page" }, "strictblockSentence1": { - "message": "uMatrix ka penguar ngarkimin e faqes vijuese:", + "message": "uBO Lite pengoi hapjen e faqes vijuese:", "description": "Sentence used in the strict-blocked page" }, "strictblockReasonSentence1": { - "message": "Faqja u bllokua për shkak të një filtri që përputhet në: {{listname}}.", + "message": "Faqja u bllokua për shkak se përputhet me filtrin në {{listname}}.", "description": "Text informing about what is causing the page to be blocked" }, "strictblockRedirectSentence1": { - "message": "Faqja e bllokuar do t'ju drejtojë në një uebsajt tjetër. Në rast se pranoni do të shkoni te: {{url}}", + "message": "Faqja e bllokuar do t'ju drejtojë në një uebsajt tjetër. Në rast se pranoni do të shkoni direkt te: {{url}}", "description": "Text warning about an incoming redirect" }, "strictblockNoParamsPrompt": { @@ -264,19 +264,19 @@ "description": "Label to be used for the parameter-less URL" }, "strictblockBack": { - "message": "Mbrapa", + "message": "Kthehem", "description": "A button to go back to the previous webpage" }, "strictblockClose": { - "message": "Mbyll këtë dritare", + "message": "Mbyll dritaren", "description": "A button to close the current tab" }, "strictblockDontWarn": { - "message": "Mos më paralajmëro më për këtë faqe.", + "message": "Mos më lajmëro për këtë faqen", "description": "Label for checkbox in document-blocked page" }, "strictblockProceed": { - "message": "Vazhdo", + "message": "Vazhdoj", "description": "A button to navigate to the blocked page" } } diff --git a/platform/mv3/extension/_locales/zh_TW/messages.json b/platform/mv3/extension/_locales/zh_TW/messages.json index 7a80c28a11484..d76680e6644f1 100644 --- a/platform/mv3/extension/_locales/zh_TW/messages.json +++ b/platform/mv3/extension/_locales/zh_TW/messages.json @@ -4,7 +4,7 @@ "description": "extension name." }, "extShortDesc": { - "message": "無須權限的內容阻擋器。安裝即可阻擋廣告、追蹤器、挖礦程式等網頁內容。", + "message": "一個無須任何權限的內容阻擋器。安裝即可阻擋廣告、追蹤器、挖礦程式等網頁內容。", "description": "this will be in the Chrome web store: must be 132 characters or less" }, "perRulesetStats": { @@ -112,7 +112,7 @@ "description": "First paragraph of 'Filter issues' section in Support pane" }, "supportS6P1S1": { - "message": "為免給志願者帶來額外負擔,請先檢查問題是否被回報過,以免重複。", + "message": "為免增加志願者的負擔,請先確認該問題尚未被報告。", "description": "A paragraph in the filter issue reporter section" }, "supportFindSpecificButton": { @@ -144,11 +144,11 @@ "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option4": { - "message": "有隱私權問題", + "message": "存在隱私問題", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option5": { - "message": "開啟 uBO Lite 的時候運作不正常", + "message": "開啟 uBO Lite 的時候運作異常", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option6": { @@ -172,7 +172,7 @@ "description": "The header text for the welcome message section" }, "firstRunDescription": { - "message": "您剛安裝了 uBO Lite。您可以在此處選擇會套用在所有網站上的預設過濾模式。\n\n預設將會使用基礎模式,因為其不需要讀取與變更資料的權限。若您信任 uBO Lite,您可以給予其讀取並變更在所有網站上資料的廣泛權限,以便為所有網站啟用進階過濾功能。", + "message": "您剛安裝了 uBO Lite。您可以在此處選擇會套用在所有網站上的預設過濾模式。\n\n預設情況下,會使用基礎模式,因為其不需要讀取與變更資料的權限。若您信任 uBO Lite,您可以授予其讀取並變更在所有網站上資料的廣泛權限,以便為所有網站啟用進階過濾功能。", "description": "Descriptive text shown at first install time only " }, "defaultFilteringModeSectionLabel": { @@ -180,7 +180,7 @@ "description": "The header text for the default filtering mode section" }, "defaultFilteringModeDescription": { - "message": "預設過濾模式將被每個網站的過濾模式覆寫。您可以根據在該網站上最有效的模式為任何指定的網站調整過濾模式。每種模式都有其優缺點。", + "message": "預設過濾模式將被每個網站的過濾模式覆寫。你可以根據每個網站的情況,調整該網站的過濾模式,以選擇最適合的模式。每種模式都有其優缺點。", "description": "This describes the default filtering mode setting" }, "filteringMode0Name": { @@ -204,11 +204,11 @@ "description": "This describes the 'basic' filtering mode" }, "optimalFilteringModeDescription": { - "message": "進階網路過濾以及來自選定過濾條件清單的特定擴展過濾條件。\n\n需要在所有網站上讀取並變更資料的廣泛權限。", + "message": "進階網路過濾以及來自選定過濾條件清單的特定擴展過濾。\n\n需要廣泛的權限以讀取和修改所有網站的數據。", "description": "This describes the 'optimal' filtering mode" }, "completeFilteringModeDescription": { - "message": "進階網路過濾以及來自選定過濾清單的特定與通用擴展過濾條件。\n\n需要在所有網站上讀取並變更資料的廣泛權限。\n\n通用擴展過濾可能會導致使用更多的網頁資源。", + "message": "進階網路過濾加上來自選定過濾清單的特定及通用擴展過濾。\n\n需要廣泛的權限以讀取和修改所有網站的數據。\n\n通用擴展過濾可能導致網頁資源使用量增加。", "description": "This describes the 'complete' filtering mode" }, "noFilteringModeDescription": { @@ -256,7 +256,7 @@ "description": "Text informing about what is causing the page to be blocked" }, "strictblockRedirectSentence1": { - "message": "被封鎖的網頁想要重新導向至其他網站。如果您選擇繼續,則會直接前往:{{url}}", + "message": "被阻擋的頁面試圖重定向到另一個網站。如果您選擇繼續,將直接導航至:{{url}}", "description": "Text warning about an incoming redirect" }, "strictblockNoParamsPrompt": { diff --git a/src/_locales/hu/messages.json b/src/_locales/hu/messages.json index 062d1dae1c9c2..472701531c979 100644 --- a/src/_locales/hu/messages.json +++ b/src/_locales/hu/messages.json @@ -1192,7 +1192,7 @@ "description": "Button text to navigate to the blocked page" }, "docblockedRedirectPrompt": { - "message": "A blokkolt oldal egy másik webhelyre akarja átirányítani. Ha a folytatást választja, akkor követlenül ide fog navigálni: {{url}}", + "message": "A blokkolt oldal egy másik webhelyre akarja átirányítani. Ha a folytatást választja, akkor közvetlenül ide fog navigálni: {{url}}", "description": "Text warning about an incoming redirect" }, "cloudPush": { diff --git a/src/_locales/pt_PT/messages.json b/src/_locales/pt_PT/messages.json index 5298956affd17..0dd6035ce486e 100644 --- a/src/_locales/pt_PT/messages.json +++ b/src/_locales/pt_PT/messages.json @@ -1004,7 +1004,7 @@ "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option5": { - "message": "Falha quando o uBlock Origin é ativado", + "message": "Falha quando o uBlock Origin está ativado", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option6": { @@ -1192,7 +1192,7 @@ "description": "Button text to navigate to the blocked page" }, "docblockedRedirectPrompt": { - "message": "A página bloqueada pretende redirecionar para outro site. Se optar por continuar, navegará diretamente para: {{url}}", + "message": "A página bloqueada pretende redirecionar para outro site. Se optar por proceder, navegará diretamente para: {{url}}", "description": "Text warning about an incoming redirect" }, "cloudPush": { From b7441bb9c0954eb595cc6b3462e3b018ca6cebac Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Mon, 9 Dec 2024 08:05:47 -0500 Subject: [PATCH 0518/1099] Fix regression in `set-constant`scriptlet Regression from https://github.com/gorhill/uBlock/commit/3417fe3d5d --- platform/mv3/extension/js/debug.js | 3 ++- platform/mv3/extension/js/ruleset-manager.js | 15 ++++++++++----- src/js/resources/set-constant.js | 1 - 3 files changed, 12 insertions(+), 7 deletions(-) diff --git a/platform/mv3/extension/js/debug.js b/platform/mv3/extension/js/debug.js index 0a6ecaffe70ee..4a0d54d81ca68 100644 --- a/platform/mv3/extension/js/debug.js +++ b/platform/mv3/extension/js/debug.js @@ -20,6 +20,7 @@ */ import { dnr } from './ext.js'; +import { getDynamicRules } from './ext-compat.js'; /******************************************************************************/ @@ -53,7 +54,7 @@ const getRuleset = async rulesetId => { } let rules; if ( rulesetId === dnr.DYNAMIC_RULESET_ID ) { - rules = await dnr.getDynamicRules().catch(( ) => undefined); + rules = await getDynamicRules().catch(( ) => undefined); } else { const response = await fetch(`/rulesets/main/${rulesetId}.json`).catch(( ) => undefined); if ( response === undefined ) { return; } diff --git a/platform/mv3/extension/js/ruleset-manager.js b/platform/mv3/extension/js/ruleset-manager.js index 04bfb3b6f241d..ca8f1239bfe2a 100644 --- a/platform/mv3/extension/js/ruleset-manager.js +++ b/platform/mv3/extension/js/ruleset-manager.js @@ -26,6 +26,11 @@ import { runtime, } from './ext.js'; +import { + getDynamicRules, + getSessionRules, +} from './ext-compat.js'; + import { localRead, localRemove, localWrite, sessionRead, sessionRemove, sessionWrite, @@ -363,8 +368,8 @@ async function commitStrictBlockRules() { beforePermanentRules, beforeTemporaryRules, ] = await Promise.all([ - dnr.getDynamicRules({ ruleIds: [ STRICTBLOCK_BASE_RULE_ID ] }), - dnr.getSessionRules({ ruleIds: [ STRICTBLOCK_BASE_RULE_ID ] }), + getDynamicRules({ ruleIds: [ STRICTBLOCK_BASE_RULE_ID ] }), + getSessionRules({ ruleIds: [ STRICTBLOCK_BASE_RULE_ID ] }), ]); if ( beforePermanentRules?.length ) { ubolLog(`Remove 1 DNR dynamic strictblock rule`); @@ -422,11 +427,11 @@ async function updateDynamicRules() { dynamicRuleIds, sessionRuleIds, ] = await Promise.all([ - dnr.getDynamicRules().then(rules => + getDynamicRules().then(rules => rules.map(rule => rule.id) .filter(id => id < TRUSTED_DIRECTIVE_BASE_RULE_ID) ), - dnr.getSessionRules().then(rules => rules.map(rule => rule.id)), + getSessionRules().then(rules => rules.map(rule => rule.id)), updateRegexRules(dynamicRules), updateRemoveparamRules(dynamicRules), updateRedirectRules(dynamicRules), @@ -475,7 +480,7 @@ async function updateDynamicRules() { /******************************************************************************/ async function filteringModesToDNR(modes) { - const trustedRules = await dnr.getDynamicRules({ + const trustedRules = await getDynamicRules({ ruleIds: [ TRUSTED_DIRECTIVE_BASE_RULE_ID+0 ], }); const trustedRule = trustedRules.length !== 0 && trustedRules[0] || undefined; diff --git a/src/js/resources/set-constant.js b/src/js/resources/set-constant.js index 66584fb81d3e6..127f27bbbe910 100644 --- a/src/js/resources/set-constant.js +++ b/src/js/resources/set-constant.js @@ -61,7 +61,6 @@ export function validateConstantFn(trusted, raw, extraArgs = {}) { } else if ( raw.startsWith('{') && raw.endsWith('}') ) { try { value = safe.JSON_parse(raw).value; } catch(ex) { return; } } - return raw; } else { return; } From fa87e6d8307b50ca59b6955c9f6db9d9f61f447b Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Mon, 9 Dec 2024 08:08:43 -0500 Subject: [PATCH 0519/1099] New revision for dev build --- dist/version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dist/version b/dist/version index 34da318f7a219..0ddc5cb2449d2 100644 --- a/dist/version +++ b/dist/version @@ -1 +1 @@ -1.61.3.5 \ No newline at end of file +1.61.3.6 \ No newline at end of file From 7daa45e49f10f53c72a16d7c248b8e601c2e68f1 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Mon, 9 Dec 2024 08:19:43 -0500 Subject: [PATCH 0520/1099] Revert "Fix regression in `set-constant`scriptlet" This reverts commit b7441bb9c0954eb595cc6b3462e3b018ca6cebac. --- platform/mv3/extension/js/debug.js | 3 +-- platform/mv3/extension/js/ruleset-manager.js | 15 +++++---------- src/js/resources/set-constant.js | 1 + 3 files changed, 7 insertions(+), 12 deletions(-) diff --git a/platform/mv3/extension/js/debug.js b/platform/mv3/extension/js/debug.js index 4a0d54d81ca68..0a6ecaffe70ee 100644 --- a/platform/mv3/extension/js/debug.js +++ b/platform/mv3/extension/js/debug.js @@ -20,7 +20,6 @@ */ import { dnr } from './ext.js'; -import { getDynamicRules } from './ext-compat.js'; /******************************************************************************/ @@ -54,7 +53,7 @@ const getRuleset = async rulesetId => { } let rules; if ( rulesetId === dnr.DYNAMIC_RULESET_ID ) { - rules = await getDynamicRules().catch(( ) => undefined); + rules = await dnr.getDynamicRules().catch(( ) => undefined); } else { const response = await fetch(`/rulesets/main/${rulesetId}.json`).catch(( ) => undefined); if ( response === undefined ) { return; } diff --git a/platform/mv3/extension/js/ruleset-manager.js b/platform/mv3/extension/js/ruleset-manager.js index ca8f1239bfe2a..04bfb3b6f241d 100644 --- a/platform/mv3/extension/js/ruleset-manager.js +++ b/platform/mv3/extension/js/ruleset-manager.js @@ -26,11 +26,6 @@ import { runtime, } from './ext.js'; -import { - getDynamicRules, - getSessionRules, -} from './ext-compat.js'; - import { localRead, localRemove, localWrite, sessionRead, sessionRemove, sessionWrite, @@ -368,8 +363,8 @@ async function commitStrictBlockRules() { beforePermanentRules, beforeTemporaryRules, ] = await Promise.all([ - getDynamicRules({ ruleIds: [ STRICTBLOCK_BASE_RULE_ID ] }), - getSessionRules({ ruleIds: [ STRICTBLOCK_BASE_RULE_ID ] }), + dnr.getDynamicRules({ ruleIds: [ STRICTBLOCK_BASE_RULE_ID ] }), + dnr.getSessionRules({ ruleIds: [ STRICTBLOCK_BASE_RULE_ID ] }), ]); if ( beforePermanentRules?.length ) { ubolLog(`Remove 1 DNR dynamic strictblock rule`); @@ -427,11 +422,11 @@ async function updateDynamicRules() { dynamicRuleIds, sessionRuleIds, ] = await Promise.all([ - getDynamicRules().then(rules => + dnr.getDynamicRules().then(rules => rules.map(rule => rule.id) .filter(id => id < TRUSTED_DIRECTIVE_BASE_RULE_ID) ), - getSessionRules().then(rules => rules.map(rule => rule.id)), + dnr.getSessionRules().then(rules => rules.map(rule => rule.id)), updateRegexRules(dynamicRules), updateRemoveparamRules(dynamicRules), updateRedirectRules(dynamicRules), @@ -480,7 +475,7 @@ async function updateDynamicRules() { /******************************************************************************/ async function filteringModesToDNR(modes) { - const trustedRules = await getDynamicRules({ + const trustedRules = await dnr.getDynamicRules({ ruleIds: [ TRUSTED_DIRECTIVE_BASE_RULE_ID+0 ], }); const trustedRule = trustedRules.length !== 0 && trustedRules[0] || undefined; diff --git a/src/js/resources/set-constant.js b/src/js/resources/set-constant.js index 127f27bbbe910..66584fb81d3e6 100644 --- a/src/js/resources/set-constant.js +++ b/src/js/resources/set-constant.js @@ -61,6 +61,7 @@ export function validateConstantFn(trusted, raw, extraArgs = {}) { } else if ( raw.startsWith('{') && raw.endsWith('}') ) { try { value = safe.JSON_parse(raw).value; } catch(ex) { return; } } + return raw; } else { return; } From 2ccb01973ee009e6a2e414cd178e2b9dfce6a230 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Mon, 9 Dec 2024 08:22:21 -0500 Subject: [PATCH 0521/1099] Fix regression in `set-constant` scriptlet Regression from 3417fe3d5d --- src/js/resources/set-constant.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/js/resources/set-constant.js b/src/js/resources/set-constant.js index 66584fb81d3e6..127f27bbbe910 100644 --- a/src/js/resources/set-constant.js +++ b/src/js/resources/set-constant.js @@ -61,7 +61,6 @@ export function validateConstantFn(trusted, raw, extraArgs = {}) { } else if ( raw.startsWith('{') && raw.endsWith('}') ) { try { value = safe.JSON_parse(raw).value; } catch(ex) { return; } } - return raw; } else { return; } From 51ef43c0de1bf7f57b61a085c0d6ae1c3024d168 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Mon, 9 Dec 2024 08:38:29 -0500 Subject: [PATCH 0522/1099] Make Firefox dev build auto-update --- dist/firefox/updates.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dist/firefox/updates.json b/dist/firefox/updates.json index f8302e8163f60..d01a946110734 100644 --- a/dist/firefox/updates.json +++ b/dist/firefox/updates.json @@ -3,9 +3,9 @@ "uBlock0@raymondhill.net": { "updates": [ { - "version": "1.61.3.5", + "version": "1.61.3.6", "browser_specific_settings": { "gecko": { "strict_min_version": "78.0" } }, - "update_link": "https://github.com/gorhill/uBlock/releases/download/1.61.3b5/uBlock0_1.61.3b5.firefox.signed.xpi" + "update_link": "https://github.com/gorhill/uBlock/releases/download/1.61.3b6/uBlock0_1.61.3b6.firefox.signed.xpi" } ] } From 20a570ebe8e4a7573e71ac854563124657e39408 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Mon, 9 Dec 2024 09:42:46 -0500 Subject: [PATCH 0523/1099] [mv3] Make strict-blocking checkbox dependent on filtering mode Related feedback: https://github.com/uBlockOrigin/uBOL-home/issues/214#issuecomment-2528072997 --- platform/mv3/extension/css/settings.css | 5 +++-- platform/mv3/extension/dashboard.html | 4 ++-- platform/mv3/extension/js/settings.js | 7 ++++++- 3 files changed, 11 insertions(+), 5 deletions(-) diff --git a/platform/mv3/extension/css/settings.css b/platform/mv3/extension/css/settings.css index 02eb600370a5d..36f9c3d6f7516 100644 --- a/platform/mv3/extension/css/settings.css +++ b/platform/mv3/extension/css/settings.css @@ -30,8 +30,9 @@ body[data-forbid~="filteringMode"] section[data-pane="settings"] > div:has(> h3[ display: none; } -#showBlockedCount:has(input[type="checkbox"][disabled]) { - opacity: 0.5; +label:has(input[type="checkbox"][disabled]), +label:has(input[type="checkbox"][disabled]) + legend { + filter: var(--checkbox-disabled-filter); } #defaultFilteringMode { diff --git a/platform/mv3/extension/dashboard.html b/platform/mv3/extension/dashboard.html index 5ac63f3d06d0f..8ef9799041a2e 100644 --- a/platform/mv3/extension/dashboard.html +++ b/platform/mv3/extension/dashboard.html @@ -31,8 +31,6 @@

-

-

@@ -90,6 +88,8 @@

+

+

diff --git a/platform/mv3/extension/js/settings.js b/platform/mv3/extension/js/settings.js index 5b73144cb3dc1..d924d6f59ae70 100644 --- a/platform/mv3/extension/js/settings.js +++ b/platform/mv3/extension/js/settings.js @@ -67,7 +67,12 @@ function renderWidgets() { } } - qs$('#strictBlockMode input[type="checkbox"]').checked = cachedRulesetData.strictBlockMode; + { + const input = qs$('#strictBlockMode input[type="checkbox"]'); + const canStrictBlock = cachedRulesetData.defaultFilteringMode > 1; + input.checked = canStrictBlock && cachedRulesetData.strictBlockMode; + dom.attr(input, 'disabled', canStrictBlock ? null : ''); + } { dom.prop('#developerMode input[type="checkbox"]', 'checked', From 09554384efc7cffcc0cdc42888595eae5015830b Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Mon, 9 Dec 2024 10:48:24 -0500 Subject: [PATCH 0524/1099] [mv3] Replace "30-day Phishing Domain List" with "Malicious URL Blocklist" As per team feedback. --- platform/mv3/make-rulesets.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/platform/mv3/make-rulesets.js b/platform/mv3/make-rulesets.js index 9df90963cc7be..220a723f21487 100644 --- a/platform/mv3/make-rulesets.js +++ b/platform/mv3/make-rulesets.js @@ -1288,11 +1288,11 @@ async function main() { // Handpicked rulesets from abroad await rulesetFromURLs({ - id: 'nrd.30day.phishing', - name: '30-day Phishing Domain List', + id: 'urlhaus.full', + name: 'Malicious URL Blocklist', enabled: true, - urls: [ 'https://raw.githubusercontent.com/xRuffKez/NRD/refs/heads/main/lists/30-day_phishing/domains-only/nrd-phishing-30day.txt' ], - homeURL: 'https://github.com/xRuffKez/NRD?tab=readme-ov-file', + urls: [ 'https://malware-filter.gitlab.io/malware-filter/urlhaus-filter-domains.txt' ], + homeURL: 'https://gitlab.com/malware-filter/urlhaus-filter', }); await rulesetFromURLs({ From 738f93da28ccec836eb160805732e314c5d02d5c Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Mon, 9 Dec 2024 10:52:07 -0500 Subject: [PATCH 0525/1099] Import translation work from https://crowdin.com/project/ublock --- platform/mv3/extension/_locales/ar/messages.json | 2 +- platform/mv3/extension/_locales/fy/messages.json | 12 ++++++------ platform/mv3/extension/_locales/nl/messages.json | 2 +- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/platform/mv3/extension/_locales/ar/messages.json b/platform/mv3/extension/_locales/ar/messages.json index 6a2aac1b594a3..62bc98175dd32 100644 --- a/platform/mv3/extension/_locales/ar/messages.json +++ b/platform/mv3/extension/_locales/ar/messages.json @@ -256,7 +256,7 @@ "description": "Text informing about what is causing the page to be blocked" }, "strictblockRedirectSentence1": { - "message": "The blocked page wants to redirect to another site. If you choose to proceed, you will navigate directly to: {{url}}", + "message": "الصفحة المحظورة تريد إعادة التوجيه إلى موقع آخر. إذا اخترت المتابعة، فسوف تنتقل مباشرة إلى: {{url}}", "description": "Text warning about an incoming redirect" }, "strictblockNoParamsPrompt": { diff --git a/platform/mv3/extension/_locales/fy/messages.json b/platform/mv3/extension/_locales/fy/messages.json index 64b72057bbfb3..2c09835ec4d26 100644 --- a/platform/mv3/extension/_locales/fy/messages.json +++ b/platform/mv3/extension/_locales/fy/messages.json @@ -232,11 +232,11 @@ "description": "Label for a checkbox in the options page" }, "enableStrictBlockLabel": { - "message": "Strikte blokkearring ynskeakelje", + "message": "Strange blokkearring ynskeakelje", "description": "Label for a checkbox in the options page" }, "enableStrictBlockLegend": { - "message": "Navigaasje nei in potinsjeel net-winske websites sil blokkearre wurde, en jo krije in opsje om troch te gean.", + "message": "Navigaasje nei potinsjeel net-winske websites wurdt blokkearre, en jo krije de opsje om troch te gean.", "description": "Label for a checkbox in the options page" }, "findListsPlaceholder": { @@ -248,15 +248,15 @@ "description": "Webpage title for the strict-blocked page" }, "strictblockSentence1": { - "message": "uBO Lite hat opkeard dat de folgjende side laden wurd:", + "message": "uBO Lite hat it laden fan de folgjende side opkeard:", "description": "Sentence used in the strict-blocked page" }, "strictblockReasonSentence1": { - "message": "The page was blocked because of a matching filter in {{listname}}.", + "message": "De side is blokkearre fanwegen in oerienkommend filter yn {{listname}}.", "description": "Text informing about what is causing the page to be blocked" }, "strictblockRedirectSentence1": { - "message": "The blocked page wants to redirect to another site. If you choose to proceed, you will navigate directly to: {{url}}", + "message": "De blokkearre side wol jo omliede nei in oare website. As jo trochgean, navigearje jo streekrjocht nei: {{url}}", "description": "Text warning about an incoming redirect" }, "strictblockNoParamsPrompt": { @@ -268,7 +268,7 @@ "description": "A button to go back to the previous webpage" }, "strictblockClose": { - "message": "Slút dit finster", + "message": "Dit finster slute", "description": "A button to close the current tab" }, "strictblockDontWarn": { diff --git a/platform/mv3/extension/_locales/nl/messages.json b/platform/mv3/extension/_locales/nl/messages.json index a170351aa536e..c567d2d6704ed 100644 --- a/platform/mv3/extension/_locales/nl/messages.json +++ b/platform/mv3/extension/_locales/nl/messages.json @@ -236,7 +236,7 @@ "description": "Label for a checkbox in the options page" }, "enableStrictBlockLegend": { - "message": "Navigatie naar mogelijk ongewenste websites wordt geblokkeerd, en nu krijgt de mogelijkheid om door te gaan.", + "message": "Navigatie naar mogelijk ongewenste websites wordt geblokkeerd, en u krijgt de mogelijkheid om door te gaan.", "description": "Label for a checkbox in the options page" }, "findListsPlaceholder": { From 0e1800e68df8c5e82bd5cd93309b2c31064468c6 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Tue, 10 Dec 2024 10:16:35 -0500 Subject: [PATCH 0526/1099] [mv3] Fix using Optimal as default mode when hosts permission allows it Related issue: https://github.com/uBlockOrigin/uBOL-home/discussions/161 --- platform/mv3/extension/js/background.js | 18 ++++++++++++------ platform/mv3/extension/js/mode-manager.js | 5 +++-- 2 files changed, 15 insertions(+), 8 deletions(-) diff --git a/platform/mv3/extension/js/background.js b/platform/mv3/extension/js/background.js index e250d1025ed96..ce8c0ea93bd10 100644 --- a/platform/mv3/extension/js/background.js +++ b/platform/mv3/extension/js/background.js @@ -93,7 +93,11 @@ async function hasGreatPowers(origin) { }); } -function hasOmnipotence() { +async function hasOmnipotence() { + const manifest = runtime.getManifest(); + const hasOmnipotence = Array.isArray(manifest.host_permissions) && + manifest.host_permissions.includes(''); + if ( hasOmnipotence ) { return true; } return browser.permissions.contains({ origins: [ '' ], }); @@ -436,13 +440,15 @@ async function start() { if ( afterLevel === MODE_OPTIMAL ) { updateDynamicRules(); registerInjectables(); + process.firstRun = false; } - } - const disableFirstRunPage = await adminRead('disableFirstRunPage'); - if ( disableFirstRunPage !== true ) { - runtime.openOptionsPage(); } else { - process.firstRun = false; + const disableFirstRunPage = await adminRead('disableFirstRunPage'); + if ( disableFirstRunPage !== true ) { + runtime.openOptionsPage(); + } else { + process.firstRun = false; + } } } diff --git a/platform/mv3/extension/js/mode-manager.js b/platform/mv3/extension/js/mode-manager.js index 3630925caad9a..a8af4bbb930fe 100644 --- a/platform/mv3/extension/js/mode-manager.js +++ b/platform/mv3/extension/js/mode-manager.js @@ -252,10 +252,11 @@ async function writeFilteringModeDetails(afterDetails) { localWrite('filteringModeDetails', data); sessionWrite('filteringModeDetails', data); readFilteringModeDetails.cache = unserializeModeDetails(data); - - Promise.all([ + return Promise.all([ getDefaultFilteringMode(), getTrustedSites(), + localWrite('filteringModeDetails', data), + sessionWrite('filteringModeDetails', data), ]).then(results => { broadcastMessage({ defaultFilteringMode: results[0], From f14257d813e548cd11aedc29a2919ef3f461f63e Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Tue, 10 Dec 2024 10:22:38 -0500 Subject: [PATCH 0527/1099] [mv3] Minor visual fix in dashboard's Welcome section --- platform/mv3/extension/css/settings.css | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/platform/mv3/extension/css/settings.css b/platform/mv3/extension/css/settings.css index 36f9c3d6f7516..237fb8191984e 100644 --- a/platform/mv3/extension/css/settings.css +++ b/platform/mv3/extension/css/settings.css @@ -9,6 +9,12 @@ body.firstRun .firstRun { display: block; padding: 8px; } +body .firstRun > *:first-child { + margin-top: 0; + } +body .firstRun > *:last-child { + margin-bottom: 0; + } h3 { margin: 1em 0; } From 7ed34708441c6b6629bab80f87df24884b5b1d57 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Wed, 11 Dec 2024 08:47:10 -0500 Subject: [PATCH 0528/1099] Improve `trusted-suppress-native-method` scriptlet Add support for the `stack` parameter. --- src/js/resources/scriptlets.js | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/src/js/resources/scriptlets.js b/src/js/resources/scriptlets.js index f8a761990b946..99d55c12c54e0 100644 --- a/src/js/resources/scriptlets.js +++ b/src/js/resources/scriptlets.js @@ -431,7 +431,7 @@ function objectPruneFn( ? safe.String_split.call(rawNeedlePaths, / +/) : []; if ( stackNeedleDetails.matchAll !== true ) { - if ( matchesStackTrace(stackNeedleDetails, extraArgs.logstack) === false ) { + if ( matchesStackTraceFn(stackNeedleDetails, extraArgs.logstack) === false ) { return; } } @@ -531,13 +531,13 @@ function objectFindOwnerFn( builtinScriptlets.push({ name: 'matches-stack-trace.fn', - fn: matchesStackTrace, + fn: matchesStackTraceFn, dependencies: [ 'get-exception-token.fn', 'safe-self.fn', ], }); -function matchesStackTrace( +function matchesStackTraceFn( needleDetails, logLevel = '' ) { @@ -1169,13 +1169,13 @@ function abortOnStackTrace( let v = owner[chain]; Object.defineProperty(owner, chain, { get: function() { - if ( matchesStackTrace(needleDetails, extraArgs.log) ) { + if ( matchesStackTraceFn(needleDetails, extraArgs.log) ) { throw new ReferenceError(getExceptionToken()); } return v; }, set: function(a) { - if ( matchesStackTrace(needleDetails, extraArgs.log) ) { + if ( matchesStackTraceFn(needleDetails, extraArgs.log) ) { throw new ReferenceError(getExceptionToken()); } v = a; @@ -3447,6 +3447,7 @@ builtinScriptlets.push({ requiresTrust: true, fn: trustedSuppressNativeMethod, dependencies: [ + 'matches-stack-trace.fn', 'proxy-apply.fn', 'safe-self.fn', ], @@ -3458,9 +3459,8 @@ function trustedSuppressNativeMethod( stack = '' ) { if ( methodPath === '' ) { return; } - if ( stack !== '' ) { return; } const safe = safeSelf(); - const logPrefix = safe.makeLogPrefix('trusted-suppress-native-method', methodPath, signature, how); + const logPrefix = safe.makeLogPrefix('trusted-suppress-native-method', methodPath, signature, how, stack); const signatureArgs = safe.String_split.call(signature, /\s*\|\s*/).map(v => { if ( /^".*"$/.test(v) ) { return { type: 'pattern', re: safe.patternToRegex(v.slice(1, -1)) }; @@ -3478,6 +3478,7 @@ function trustedSuppressNativeMethod( return { type: 'exact', value: undefined }; } }); + const stackNeedle = safe.initPattern(stack, { canNegate: true }); proxyApplyFn(methodPath, function(context) { const { callArgs } = context; if ( signature === '' ) { @@ -3499,6 +3500,11 @@ function trustedSuppressNativeMethod( } } } + if ( stackNeedle.matchAll !== true ) { + if ( matchesStackTraceFn(stackNeedle) === false ) { + return context.reflect(); + } + } if ( how === 'debug' ) { debugger; // eslint-disable-line no-debugger return context.reflect(); From 2d400b566126a80a13f9197546fb6a2e2061a94d Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Wed, 11 Dec 2024 08:49:44 -0500 Subject: [PATCH 0529/1099] Update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index a2f2928b71c2b..7f0bac98aae50 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,4 @@ +- [Improve `trusted-suppress-native-method` scriptlet](https://github.com/gorhill/uBlock/commit/7ed3470844) - [Improve `trusted-replace-argument` scriptlet](https://github.com/gorhill/uBlock/commit/3417fe3d5d) - [Block media elements unconditionally when max size is set to 0](https://github.com/gorhill/uBlock/commit/36db7f8327) - Regression from From d193ae7d2f1fdc0dbfcdecf0fb3de63b6611e09b Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Wed, 11 Dec 2024 08:50:30 -0500 Subject: [PATCH 0530/1099] New revision for dev build --- dist/version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dist/version b/dist/version index 0ddc5cb2449d2..5b477a8fa08cf 100644 --- a/dist/version +++ b/dist/version @@ -1 +1 @@ -1.61.3.6 \ No newline at end of file +1.61.3.7 \ No newline at end of file From 42c23f10d42212a6a11e937e7d0c433b2b8c720a Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Wed, 11 Dec 2024 09:05:37 -0500 Subject: [PATCH 0531/1099] Make Firefox dev build auto-update --- dist/firefox/updates.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dist/firefox/updates.json b/dist/firefox/updates.json index d01a946110734..bf1ef1e0896a5 100644 --- a/dist/firefox/updates.json +++ b/dist/firefox/updates.json @@ -3,9 +3,9 @@ "uBlock0@raymondhill.net": { "updates": [ { - "version": "1.61.3.6", + "version": "1.61.3.7", "browser_specific_settings": { "gecko": { "strict_min_version": "78.0" } }, - "update_link": "https://github.com/gorhill/uBlock/releases/download/1.61.3b6/uBlock0_1.61.3b6.firefox.signed.xpi" + "update_link": "https://github.com/gorhill/uBlock/releases/download/1.61.3b7/uBlock0_1.61.3b7.firefox.signed.xpi" } ] } From 9b2cf0169a2f7e7c1096b56a70914695d5af585d Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Thu, 12 Dec 2024 10:18:42 -0500 Subject: [PATCH 0532/1099] Improve `trusted-suppress-native-method` scriptlet Add ability to log stack trace when verbose mode is enabled. --- src/js/resources/scriptlets.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/js/resources/scriptlets.js b/src/js/resources/scriptlets.js index 99d55c12c54e0..aeb0f096180c4 100644 --- a/src/js/resources/scriptlets.js +++ b/src/js/resources/scriptlets.js @@ -3501,7 +3501,8 @@ function trustedSuppressNativeMethod( } } if ( stackNeedle.matchAll !== true ) { - if ( matchesStackTraceFn(stackNeedle) === false ) { + const logLevel = safe.logLevel > 1 ? 'all' : ''; + if ( matchesStackTraceFn(stackNeedle, logLevel) === false ) { return context.reflect(); } } From 25a4433e2a21c87871a17344cd94c4a74b6f651c Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Thu, 12 Dec 2024 10:50:32 -0500 Subject: [PATCH 0533/1099] New revision for dev build --- dist/version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dist/version b/dist/version index 5b477a8fa08cf..8e0b6eb3d6b63 100644 --- a/dist/version +++ b/dist/version @@ -1 +1 @@ -1.61.3.7 \ No newline at end of file +1.61.3.8 \ No newline at end of file From d51a01f0f8f344d0d4cb86daf6a766eaa5af4c36 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Thu, 12 Dec 2024 12:34:04 -0500 Subject: [PATCH 0534/1099] Add regional list for UKR Related discussion: https://github.com/uBlockOrigin/uBlock-issues/issues/2692 --- assets/assets.dev.json | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/assets/assets.dev.json b/assets/assets.dev.json index a51bc714c3933..6681c5c979e02 100644 --- a/assets/assets.dev.json +++ b/assets/assets.dev.json @@ -830,7 +830,7 @@ "off": true, "title": "🇷🇺ru 🇺🇦ua 🇺🇿uz 🇰🇿kz: RU AdList", "tags": "ads belarusian беларуская kazakh tatar russian русский ukrainian українська uzbek", - "lang": "be kk tt ru uk uz", + "lang": "be kk tt ru uz", "contentURL": "https://raw.githubusercontent.com/easylist/ruadlist/master/RuAdList-uBO.txt", "cdnURLs": [ "https://cdn.jsdelivr.net/gh/dimisa-RUAdList/RUAdListCDN@main/lists/ruadlist.ubo.min.txt", @@ -922,6 +922,16 @@ "supportURL": "https://github.com/AdguardTeam/AdguardFilters#adguard-filters", "instructionURL": "https://adguard.com/kb/general/ad-filtering/adguard-filters/" }, + "UKR-0": { + "content": "filters", + "group": "regions", + "off": true, + "title": "🇺🇦ua: Ukrainian Ad List", + "tags": "ads ukraine україна", + "lang": "uk", + "contentURL": "https://raw.githubusercontent.com/ukrainianfilters/lists/main/ads/ads.txt", + "supportURL": "https://github.com/ukrainianfilters/lists" + }, "VIE-1": { "content": "filters", "group": "regions", From bdc67b1e7f77b604fdc7690169fb2acc544b344c Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Thu, 12 Dec 2024 12:40:33 -0500 Subject: [PATCH 0535/1099] Make Firefox dev build auto-update --- dist/firefox/updates.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dist/firefox/updates.json b/dist/firefox/updates.json index bf1ef1e0896a5..211c105732f59 100644 --- a/dist/firefox/updates.json +++ b/dist/firefox/updates.json @@ -3,9 +3,9 @@ "uBlock0@raymondhill.net": { "updates": [ { - "version": "1.61.3.7", + "version": "1.61.3.8", "browser_specific_settings": { "gecko": { "strict_min_version": "78.0" } }, - "update_link": "https://github.com/gorhill/uBlock/releases/download/1.61.3b7/uBlock0_1.61.3b7.firefox.signed.xpi" + "update_link": "https://github.com/gorhill/uBlock/releases/download/1.61.3b8/uBlock0_1.61.3b8.firefox.signed.xpi" } ] } From b7bacc0fc115d5d9cc977e7479207ed0157a5737 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Thu, 12 Dec 2024 19:47:48 -0500 Subject: [PATCH 0536/1099] Fix bad filter in urlhaus Related issue: https://github.com/uBlockOrigin/uBOL-home/issues/256 --- platform/mv3/make-rulesets.js | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/platform/mv3/make-rulesets.js b/platform/mv3/make-rulesets.js index 220a723f21487..1dc121d813889 100644 --- a/platform/mv3/make-rulesets.js +++ b/platform/mv3/make-rulesets.js @@ -1291,7 +1291,13 @@ async function main() { id: 'urlhaus.full', name: 'Malicious URL Blocklist', enabled: true, - urls: [ 'https://malware-filter.gitlab.io/malware-filter/urlhaus-filter-domains.txt' ], + urls: [ + 'https://malware-filter.gitlab.io/malware-filter/urlhaus-filter-domains.txt', + ], + filters: [ + // https://github.com/uBlockOrigin/uBOL-home/issues/256 + '192.168.1.1$badfilter', + ], homeURL: 'https://gitlab.com/malware-filter/urlhaus-filter', }); From 54ebea31f773497afdd5a235979195edfc2d73c2 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Thu, 12 Dec 2024 20:00:08 -0500 Subject: [PATCH 0537/1099] Use HOSTS version of "Malicious URL Blocklist" Related issue: https://github.com/uBlockOrigin/uBOL-home/issues/256 --- assets/assets.json | 12 +++++++++++- platform/mv3/make-rulesets.js | 4 +--- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/assets/assets.json b/assets/assets.json index 6449dbe932434..82b4505df0c81 100644 --- a/assets/assets.json +++ b/assets/assets.json @@ -830,7 +830,7 @@ "off": true, "title": "🇷🇺ru 🇺🇦ua 🇺🇿uz 🇰🇿kz: RU AdList", "tags": "ads belarusian беларуская kazakh tatar russian русский ukrainian українська uzbek", - "lang": "be kk tt ru uk uz", + "lang": "be kk tt ru uz", "contentURL": "https://raw.githubusercontent.com/easylist/ruadlist/master/RuAdList-uBO.txt", "cdnURLs": [ "https://cdn.jsdelivr.net/gh/dimisa-RUAdList/RUAdListCDN@main/lists/ruadlist.ubo.min.txt", @@ -922,6 +922,16 @@ "supportURL": "https://github.com/AdguardTeam/AdguardFilters#adguard-filters", "instructionURL": "https://adguard.com/kb/general/ad-filtering/adguard-filters/" }, + "UKR-0": { + "content": "filters", + "group": "regions", + "off": true, + "title": "🇺🇦ua: Ukrainian Ad List", + "tags": "ads ukraine україна", + "lang": "uk", + "contentURL": "https://raw.githubusercontent.com/ukrainianfilters/lists/main/ads/ads.txt", + "supportURL": "https://github.com/ukrainianfilters/lists" + }, "VIE-1": { "content": "filters", "group": "regions", diff --git a/platform/mv3/make-rulesets.js b/platform/mv3/make-rulesets.js index 1dc121d813889..e23de3e44a1a6 100644 --- a/platform/mv3/make-rulesets.js +++ b/platform/mv3/make-rulesets.js @@ -1292,11 +1292,9 @@ async function main() { name: 'Malicious URL Blocklist', enabled: true, urls: [ - 'https://malware-filter.gitlab.io/malware-filter/urlhaus-filter-domains.txt', + 'https://malware-filter.gitlab.io/malware-filter/urlhaus-filter-hosts.txt', ], filters: [ - // https://github.com/uBlockOrigin/uBOL-home/issues/256 - '192.168.1.1$badfilter', ], homeURL: 'https://gitlab.com/malware-filter/urlhaus-filter', }); From b1936524f79c33b3d59cf9d561744409832b0f38 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Thu, 12 Dec 2024 20:06:15 -0500 Subject: [PATCH 0538/1099] Import translation work from https://crowdin.com/project/ublock --- platform/mv3/description/webstore.be.txt | 6 +++--- platform/mv3/extension/_locales/be/messages.json | 6 +++--- platform/mv3/extension/_locales/pt_PT/messages.json | 6 +++--- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/platform/mv3/description/webstore.be.txt b/platform/mv3/description/webstore.be.txt index c23daa3fb6dd8..d9749c59cca75 100644 --- a/platform/mv3/description/webstore.be.txt +++ b/platform/mv3/description/webstore.be.txt @@ -1,11 +1,11 @@ uBO Лайт (uBOL) гэта блакіроўшчык кантэнту з меншымі патрабаваннямі да дазволаў заснаваны на MV3 -The default ruleset corresponds to uBlock Origin's default filterset: +Прадвызначаны набор правіл адпавядае тыпавому набору фільтраў uBlock Origin: -- uBlock Origin's built-in filter lists +- Убудаваныя спісы фільтраў uBlock Origin - EasyList - EasyPrivacy -- Peter Lowe’s Ad and tracking server list +- Спіс сервераў рэкламы ды адсочвання ад Peter Lowe You can enable more rulesets by visiting the options page -- click the _Cogs_ icon in the popup panel. diff --git a/platform/mv3/extension/_locales/be/messages.json b/platform/mv3/extension/_locales/be/messages.json index ebed0a2a7309d..0063666a5c310 100644 --- a/platform/mv3/extension/_locales/be/messages.json +++ b/platform/mv3/extension/_locales/be/messages.json @@ -244,7 +244,7 @@ "description": "Placeholder for the input field used to find lists" }, "strictblockTitle": { - "message": "Старонка заблакавана", + "message": "Старонка заблакаваная", "description": "Webpage title for the strict-blocked page" }, "strictblockSentence1": { @@ -252,11 +252,11 @@ "description": "Sentence used in the strict-blocked page" }, "strictblockReasonSentence1": { - "message": "The page was blocked because of a matching filter in {{listname}}.", + "message": "Старонка была заблакаваная, бо трапіла ў фільтр праз {{listname}}.", "description": "Text informing about what is causing the page to be blocked" }, "strictblockRedirectSentence1": { - "message": "The blocked page wants to redirect to another site. If you choose to proceed, you will navigate directly to: {{url}}", + "message": "Заблакаваная старонка мае намер перанакіраваць на іншы сайт. Калі вырашыце працягнуць, вы пяройдзеце непасрэдна на: {{url}}", "description": "Text warning about an incoming redirect" }, "strictblockNoParamsPrompt": { diff --git a/platform/mv3/extension/_locales/pt_PT/messages.json b/platform/mv3/extension/_locales/pt_PT/messages.json index 9be55f5d94ae5..d02147916eecf 100644 --- a/platform/mv3/extension/_locales/pt_PT/messages.json +++ b/platform/mv3/extension/_locales/pt_PT/messages.json @@ -108,7 +108,7 @@ "description": "Header of 'Report a filter issue' section in Support pane" }, "supportS3P1": { - "message": "Relate problemas de filtros em websites específicos no rastreador de problemas uBlockOrigin/uAssets. Requer uma conta no GitHub.", + "message": "Relate problemas de filtros em websites específicos no controlador de problemas uBlockOrigin/uAssets. Requer uma conta GitHub.", "description": "First paragraph of 'Filter issues' section in Support pane" }, "supportS6P1S1": { @@ -224,11 +224,11 @@ "description": "The header text for the 'Behavior' section" }, "autoReloadLabel": { - "message": "Recarrega automaticamente a página ao mudar o modo de filtragem", + "message": "Recarregar automaticamente a página ao mudar o modo de filtragem", "description": "Label for a checkbox in the options page" }, "showBlockedCountLabel": { - "message": "Mostra o número de pedidos bloqueados no ícone da barra de ferramentas", + "message": "Mostrar o número de pedidos bloqueados no ícone da barra de ferramentas", "description": "Label for a checkbox in the options page" }, "enableStrictBlockLabel": { From 2b6d67b29a1270695a280ccd1e2e0dd68faee64e Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Fri, 13 Dec 2024 08:30:27 -0500 Subject: [PATCH 0539/1099] Import translation work from https://crowdin.com/project/ublock --- platform/mv3/extension/_locales/el/messages.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/platform/mv3/extension/_locales/el/messages.json b/platform/mv3/extension/_locales/el/messages.json index 66f33670c7be0..5c69bd869de45 100644 --- a/platform/mv3/extension/_locales/el/messages.json +++ b/platform/mv3/extension/_locales/el/messages.json @@ -264,19 +264,19 @@ "description": "Label to be used for the parameter-less URL" }, "strictblockBack": { - "message": "Go back", + "message": "Επιστροφή", "description": "A button to go back to the previous webpage" }, "strictblockClose": { - "message": "Close this window", + "message": "Κλείσιμο του παραθύρου", "description": "A button to close the current tab" }, "strictblockDontWarn": { - "message": "Don't warn me again about this site", + "message": "Να μην προειδοποιηθώ ξανά για αυτόν τον ιστότοπο", "description": "Label for checkbox in document-blocked page" }, "strictblockProceed": { - "message": "Proceed", + "message": "Συνέχεια", "description": "A button to navigate to the blocked page" } } From 8ba71f09d756b96f081f423efb5006ce6b628974 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Fri, 13 Dec 2024 10:54:01 -0500 Subject: [PATCH 0540/1099] Improve quote usage in filter options and scriptlets Related feedback: https://github.com/uBlockOrigin/uBlock-issues/issues/760#issuecomment-2540436382 Using quotes in filter option values is meant to remove ambiguity when the value contains special characters. This was not working when the value started with `$`. For example, fixes usage of quotes in: $removeparam='$deep_link' Also, fixed logger output for scriptlets using empty parameters in quotes. --- src/js/scriptlet-filtering-core.js | 2 +- src/js/static-filtering-parser.js | 19 ++++++++----------- src/js/static-net-filtering.js | 2 +- 3 files changed, 10 insertions(+), 13 deletions(-) diff --git a/src/js/scriptlet-filtering-core.js b/src/js/scriptlet-filtering-core.js index 907844fbc198a..0fec05c2ba7a2 100644 --- a/src/js/scriptlet-filtering-core.js +++ b/src/js/scriptlet-filtering-core.js @@ -99,7 +99,7 @@ const patchScriptlet = (content, arglist) => { }; const requote = s => { - if ( /^(["'`]).+\1$|,/.test(s) === false ) { return s; } + if ( /^(["'`]).*\1$|,|^$/.test(s) === false ) { return s; } if ( s.includes("'") === false ) { return `'${s}'`; } if ( s.includes('"') === false ) { return `"${s}"`; } if ( s.includes('`') === false ) { return `\`${s}\``; } diff --git a/src/js/static-filtering-parser.js b/src/js/static-filtering-parser.js index 2be165eb107ef..d3b4ca48416cf 100644 --- a/src/js/static-filtering-parser.js +++ b/src/js/static-filtering-parser.js @@ -1477,18 +1477,15 @@ export class AstFilterParser { if ( j === -1 ) { return end; } if ( (j+1) === end ) { return end; } for (;;) { - const before = s.charCodeAt(j-1); - if ( j !== start && before === 0x24 /* $ */ ) { return -1; } - const after = s.charCodeAt(j+1); - if ( - after !== 0x29 /* ) */ && - after !== 0x2F /* / */ && - after !== 0x7C /* | */ && - before !== 0x5C /* \ */ - ) { - return j; + const before = s.charAt(j-1); + if ( before === '$' ) { return -1; } + const after = s.charAt(j+1); + if ( ')/|'.includes(after) === false ) { + if ( before === '' || '"\'\\`'.includes(before) === false ) { + return j; + } } - if ( j <= start ) { break; } + if ( j === start ) { break; } j = s.lastIndexOf('$', j-1); if ( j === -1 ) { break; } } diff --git a/src/js/static-net-filtering.js b/src/js/static-net-filtering.js index f084180ffccf3..58b6570705c2d 100644 --- a/src/js/static-net-filtering.js +++ b/src/js/static-net-filtering.js @@ -419,7 +419,7 @@ class LogData { } static requote(s) { - if ( /^(["'`]).+\1$|,/.test(s) === false ) { return s; } + if ( /^\$|^(["'`]).*\1$|,/.test(s) === false ) { return s; } if ( s.includes("'") === false ) { return `'${s}'`; } if ( s.includes('"') === false ) { return `"${s}"`; } if ( s.includes('`') === false ) { return `\`${s}\``; } From 439d608d03cfc4c4d5f0a2c0fdebe72b10d36508 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Fri, 13 Dec 2024 11:15:36 -0500 Subject: [PATCH 0541/1099] Revert "Use HOSTS version of "Malicious URL Blocklist"" This reverts commit 54ebea31f773497afdd5a235979195edfc2d73c2. --- assets/assets.json | 12 +----------- platform/mv3/make-rulesets.js | 4 +++- 2 files changed, 4 insertions(+), 12 deletions(-) diff --git a/assets/assets.json b/assets/assets.json index 82b4505df0c81..6449dbe932434 100644 --- a/assets/assets.json +++ b/assets/assets.json @@ -830,7 +830,7 @@ "off": true, "title": "🇷🇺ru 🇺🇦ua 🇺🇿uz 🇰🇿kz: RU AdList", "tags": "ads belarusian беларуская kazakh tatar russian русский ukrainian українська uzbek", - "lang": "be kk tt ru uz", + "lang": "be kk tt ru uk uz", "contentURL": "https://raw.githubusercontent.com/easylist/ruadlist/master/RuAdList-uBO.txt", "cdnURLs": [ "https://cdn.jsdelivr.net/gh/dimisa-RUAdList/RUAdListCDN@main/lists/ruadlist.ubo.min.txt", @@ -922,16 +922,6 @@ "supportURL": "https://github.com/AdguardTeam/AdguardFilters#adguard-filters", "instructionURL": "https://adguard.com/kb/general/ad-filtering/adguard-filters/" }, - "UKR-0": { - "content": "filters", - "group": "regions", - "off": true, - "title": "🇺🇦ua: Ukrainian Ad List", - "tags": "ads ukraine україна", - "lang": "uk", - "contentURL": "https://raw.githubusercontent.com/ukrainianfilters/lists/main/ads/ads.txt", - "supportURL": "https://github.com/ukrainianfilters/lists" - }, "VIE-1": { "content": "filters", "group": "regions", diff --git a/platform/mv3/make-rulesets.js b/platform/mv3/make-rulesets.js index e23de3e44a1a6..1dc121d813889 100644 --- a/platform/mv3/make-rulesets.js +++ b/platform/mv3/make-rulesets.js @@ -1292,9 +1292,11 @@ async function main() { name: 'Malicious URL Blocklist', enabled: true, urls: [ - 'https://malware-filter.gitlab.io/malware-filter/urlhaus-filter-hosts.txt', + 'https://malware-filter.gitlab.io/malware-filter/urlhaus-filter-domains.txt', ], filters: [ + // https://github.com/uBlockOrigin/uBOL-home/issues/256 + '192.168.1.1$badfilter', ], homeURL: 'https://gitlab.com/malware-filter/urlhaus-filter', }); From 0f373b7639215738f17a76ce88b9fa0b5abe5cc9 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Fri, 13 Dec 2024 11:18:18 -0500 Subject: [PATCH 0542/1099] Use HOSTS version of "Malicious URL Blocklist" Related issue: https://github.com/uBlockOrigin/uBOL-home/issues/256 --- platform/mv3/make-rulesets.js | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/platform/mv3/make-rulesets.js b/platform/mv3/make-rulesets.js index 1dc121d813889..e23de3e44a1a6 100644 --- a/platform/mv3/make-rulesets.js +++ b/platform/mv3/make-rulesets.js @@ -1292,11 +1292,9 @@ async function main() { name: 'Malicious URL Blocklist', enabled: true, urls: [ - 'https://malware-filter.gitlab.io/malware-filter/urlhaus-filter-domains.txt', + 'https://malware-filter.gitlab.io/malware-filter/urlhaus-filter-hosts.txt', ], filters: [ - // https://github.com/uBlockOrigin/uBOL-home/issues/256 - '192.168.1.1$badfilter', ], homeURL: 'https://gitlab.com/malware-filter/urlhaus-filter', }); From 65f64a58095de962dc13d5b1edb72be6d68508a8 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Fri, 13 Dec 2024 11:29:59 -0500 Subject: [PATCH 0543/1099] Update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7f0bac98aae50..95caa55a11a3c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,4 @@ +- [Improve quote usage in filter options and scriptlets](https://github.com/gorhill/uBlock/commit/8ba71f09d7) - [Improve `trusted-suppress-native-method` scriptlet](https://github.com/gorhill/uBlock/commit/7ed3470844) - [Improve `trusted-replace-argument` scriptlet](https://github.com/gorhill/uBlock/commit/3417fe3d5d) - [Block media elements unconditionally when max size is set to 0](https://github.com/gorhill/uBlock/commit/36db7f8327) From 73b85efae2fa839f068c4237af0179fc1935f4f3 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Fri, 13 Dec 2024 11:31:31 -0500 Subject: [PATCH 0544/1099] New revision for dev build --- dist/version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dist/version b/dist/version index 8e0b6eb3d6b63..08ceb89e82319 100644 --- a/dist/version +++ b/dist/version @@ -1 +1 @@ -1.61.3.8 \ No newline at end of file +1.61.3.9 \ No newline at end of file From fbe203c101b2dd5dd1fe3d4274827f95b80b1b13 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Fri, 13 Dec 2024 11:36:20 -0500 Subject: [PATCH 0545/1099] Make Firefox dev build auto-update --- dist/firefox/updates.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dist/firefox/updates.json b/dist/firefox/updates.json index 211c105732f59..3d1dabb89b96e 100644 --- a/dist/firefox/updates.json +++ b/dist/firefox/updates.json @@ -3,9 +3,9 @@ "uBlock0@raymondhill.net": { "updates": [ { - "version": "1.61.3.8", + "version": "1.61.3.9", "browser_specific_settings": { "gecko": { "strict_min_version": "78.0" } }, - "update_link": "https://github.com/gorhill/uBlock/releases/download/1.61.3b8/uBlock0_1.61.3b8.firefox.signed.xpi" + "update_link": "https://github.com/gorhill/uBlock/releases/download/1.61.3b9/uBlock0_1.61.3b9.firefox.signed.xpi" } ] } From ac9e12e17c722bffe885343647d74a366d7c78b9 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sat, 14 Dec 2024 08:45:17 -0500 Subject: [PATCH 0546/1099] [mv3] Add "Malware protection, security" section in "Filter lists" pane Additionally, add "Openphish Domain Blocklist" as stock list. --- .../mv3/extension/_locales/en/messages.json | 2 +- platform/mv3/extension/js/filter-lists.js | 5 +++ platform/mv3/make-rulesets.js | 36 ++++++++++++------- 3 files changed, 30 insertions(+), 13 deletions(-) diff --git a/platform/mv3/extension/_locales/en/messages.json b/platform/mv3/extension/_locales/en/messages.json index ad9d26911371e..c40e17428ec7c 100644 --- a/platform/mv3/extension/_locales/en/messages.json +++ b/platform/mv3/extension/_locales/en/messages.json @@ -60,7 +60,7 @@ "description": "Header for a ruleset section in 'Filter lists pane'" }, "3pGroupMalware": { - "message": "Malware domains", + "message": "Malware protection, security", "description": "Header for a ruleset section in 'Filter lists pane'" }, "3pGroupAnnoyances": { diff --git a/platform/mv3/extension/js/filter-lists.js b/platform/mv3/extension/js/filter-lists.js index daa647c5e28c9..2356d57ce13d7 100644 --- a/platform/mv3/extension/js/filter-lists.js +++ b/platform/mv3/extension/js/filter-lists.js @@ -218,6 +218,11 @@ export function renderFilterLists(rulesetData) { rulesetDetails.filter(ruleset => ruleset.id === 'default' ), + ], [ + 'malware', + rulesetDetails.filter(ruleset => + ruleset.group === 'malware' + ), ], [ 'annoyances', rulesetDetails.filter(ruleset => diff --git a/platform/mv3/make-rulesets.js b/platform/mv3/make-rulesets.js index e23de3e44a1a6..f2c62671f359e 100644 --- a/platform/mv3/make-rulesets.js +++ b/platform/mv3/make-rulesets.js @@ -1186,23 +1186,22 @@ async function main() { log(`Secret: ${secret}`); // Assemble all default lists as the default ruleset - const contentURLs = [ - 'https://ublockorigin.github.io/uAssets/filters/filters.min.txt', - 'https://ublockorigin.github.io/uAssets/filters/badware.min.txt', - 'https://ublockorigin.github.io/uAssets/filters/privacy.min.txt', - 'https://ublockorigin.github.io/uAssets/filters/unbreak.min.txt', - 'https://ublockorigin.github.io/uAssets/filters/quick-fixes.min.txt', - 'https://ublockorigin.github.io/uAssets/filters/ubol-filters.txt', - 'https://ublockorigin.github.io/uAssets/thirdparties/easylist.txt', - 'https://ublockorigin.github.io/uAssets/thirdparties/easyprivacy.txt', - 'https://pgl.yoyo.org/adservers/serverlist.php?hostformat=hosts&showintro=1&mimetype=plaintext', - ]; await rulesetFromURLs({ id: 'default', name: 'Ads, trackers, miners, and more' , enabled: true, secret, - urls: contentURLs, + urls: [ + 'https://ublockorigin.github.io/uAssets/filters/filters.min.txt', + 'https://ublockorigin.github.io/uAssets/filters/badware.min.txt', + 'https://ublockorigin.github.io/uAssets/filters/privacy.min.txt', + 'https://ublockorigin.github.io/uAssets/filters/unbreak.min.txt', + 'https://ublockorigin.github.io/uAssets/filters/quick-fixes.min.txt', + 'https://ublockorigin.github.io/uAssets/filters/ubol-filters.txt', + 'https://ublockorigin.github.io/uAssets/thirdparties/easylist.txt', + 'https://ublockorigin.github.io/uAssets/thirdparties/easyprivacy.txt', + 'https://pgl.yoyo.org/adservers/serverlist.php?hostformat=hosts&showintro=1&mimetype=plaintext', + ], dnrURL: 'https://ublockorigin.github.io/uAssets/dnr/default.json', homeURL: 'https://github.com/uBlockOrigin/uAssets', filters: [ @@ -1290,6 +1289,7 @@ async function main() { await rulesetFromURLs({ id: 'urlhaus.full', name: 'Malicious URL Blocklist', + group: 'malware', enabled: true, urls: [ 'https://malware-filter.gitlab.io/malware-filter/urlhaus-filter-hosts.txt', @@ -1298,6 +1298,18 @@ async function main() { ], homeURL: 'https://gitlab.com/malware-filter/urlhaus-filter', }); + await rulesetFromURLs({ + id: 'openphish.domains', + name: 'Openphish Domain Blocklist', + group: 'malware', + enabled: false, + urls: [ + 'https://raw.githubusercontent.com/stephenhawk8054/openphish-adblock/refs/heads/main/filters_init_domains.txt', + ], + filters: [ + ], + homeURL: 'https://github.com/stephenhawk8054/openphish-adblock', + }); await rulesetFromURLs({ id: 'stevenblack-hosts', From 5e0802fd6778641a615a618cf33a66dc5c8dd3b4 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sat, 14 Dec 2024 08:53:15 -0500 Subject: [PATCH 0547/1099] Import translation work from https://crowdin.com/project/ublock --- .../mv3/extension/_locales/cs/messages.json | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/platform/mv3/extension/_locales/cs/messages.json b/platform/mv3/extension/_locales/cs/messages.json index 585475287b5a5..3de9ca455861c 100644 --- a/platform/mv3/extension/_locales/cs/messages.json +++ b/platform/mv3/extension/_locales/cs/messages.json @@ -232,11 +232,11 @@ "description": "Label for a checkbox in the options page" }, "enableStrictBlockLabel": { - "message": "Enable strict blocking", + "message": "Povolit přísné blokování", "description": "Label for a checkbox in the options page" }, "enableStrictBlockLegend": { - "message": "Navigation to potentially undesirable sites will be blocked, and you will be offered the option to proceed.", + "message": "Navigace na potenciálně nežádoucí stránky bude zablokována a bude vám nabídnuta možnost pokračovat.", "description": "Label for a checkbox in the options page" }, "findListsPlaceholder": { @@ -244,39 +244,39 @@ "description": "Placeholder for the input field used to find lists" }, "strictblockTitle": { - "message": "Page blocked", + "message": "Stránka zablokována", "description": "Webpage title for the strict-blocked page" }, "strictblockSentence1": { - "message": "uBO Lite has prevented the following page from loading:", + "message": "uBO Lite zabránil načtení následující stránky:", "description": "Sentence used in the strict-blocked page" }, "strictblockReasonSentence1": { - "message": "The page was blocked because of a matching filter in {{listname}}.", + "message": "Stránka byla zablokována z důvodu shodného filtru v {{listname}}.", "description": "Text informing about what is causing the page to be blocked" }, "strictblockRedirectSentence1": { - "message": "The blocked page wants to redirect to another site. If you choose to proceed, you will navigate directly to: {{url}}", + "message": "Zablokovaná stránka vás chce přesměrovat na jiný web. Pokud se rozhodnete pokračovat, přejdete přímo na: {{url}}", "description": "Text warning about an incoming redirect" }, "strictblockNoParamsPrompt": { - "message": "without parameters", + "message": "bez parametrů", "description": "Label to be used for the parameter-less URL" }, "strictblockBack": { - "message": "Go back", + "message": "Zpět", "description": "A button to go back to the previous webpage" }, "strictblockClose": { - "message": "Close this window", + "message": "Zavřít toto okno", "description": "A button to close the current tab" }, "strictblockDontWarn": { - "message": "Don't warn me again about this site", + "message": "Nevarujte mě znovu o této stránce", "description": "Label for checkbox in document-blocked page" }, "strictblockProceed": { - "message": "Proceed", + "message": "Pokračovat", "description": "A button to navigate to the blocked page" } } From c2a4b72feed326aa84499827c1fd254e905ac487 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sat, 14 Dec 2024 08:57:22 -0500 Subject: [PATCH 0548/1099] Import translation work from https://crowdin.com/project/ublock --- platform/mv3/extension/_locales/az/messages.json | 2 +- platform/mv3/extension/_locales/br_FR/messages.json | 2 +- platform/mv3/extension/_locales/cv/messages.json | 2 +- platform/mv3/extension/_locales/cy/messages.json | 2 +- platform/mv3/extension/_locales/eo/messages.json | 2 +- platform/mv3/extension/_locales/fa/messages.json | 2 +- platform/mv3/extension/_locales/gl/messages.json | 2 +- platform/mv3/extension/_locales/gu/messages.json | 2 +- platform/mv3/extension/_locales/kk/messages.json | 2 +- platform/mv3/extension/_locales/kn/messages.json | 2 +- platform/mv3/extension/_locales/lt/messages.json | 2 +- platform/mv3/extension/_locales/mk/messages.json | 2 +- platform/mv3/extension/_locales/ml/messages.json | 2 +- platform/mv3/extension/_locales/mr/messages.json | 2 +- platform/mv3/extension/_locales/ms/messages.json | 2 +- platform/mv3/extension/_locales/oc/messages.json | 2 +- platform/mv3/extension/_locales/pa/messages.json | 2 +- platform/mv3/extension/_locales/sl/messages.json | 2 +- platform/mv3/extension/_locales/so/messages.json | 2 +- platform/mv3/extension/_locales/sw/messages.json | 2 +- platform/mv3/extension/_locales/ta/messages.json | 2 +- platform/mv3/extension/_locales/te/messages.json | 2 +- platform/mv3/extension/_locales/th/messages.json | 2 +- platform/mv3/extension/_locales/ur/messages.json | 2 +- 24 files changed, 24 insertions(+), 24 deletions(-) diff --git a/platform/mv3/extension/_locales/az/messages.json b/platform/mv3/extension/_locales/az/messages.json index 8172ac24b5bf2..b0b311a8bb5af 100644 --- a/platform/mv3/extension/_locales/az/messages.json +++ b/platform/mv3/extension/_locales/az/messages.json @@ -60,7 +60,7 @@ "description": "Header for a ruleset section in 'Filter lists pane'" }, "3pGroupMalware": { - "message": "Malware domains", + "message": "Malware protection, security", "description": "Header for a ruleset section in 'Filter lists pane'" }, "3pGroupAnnoyances": { diff --git a/platform/mv3/extension/_locales/br_FR/messages.json b/platform/mv3/extension/_locales/br_FR/messages.json index 31ae55bb1298b..1b3d92a2c42d5 100644 --- a/platform/mv3/extension/_locales/br_FR/messages.json +++ b/platform/mv3/extension/_locales/br_FR/messages.json @@ -60,7 +60,7 @@ "description": "Header for a ruleset section in 'Filter lists pane'" }, "3pGroupMalware": { - "message": "Malware domains", + "message": "Malware protection, security", "description": "Header for a ruleset section in 'Filter lists pane'" }, "3pGroupAnnoyances": { diff --git a/platform/mv3/extension/_locales/cv/messages.json b/platform/mv3/extension/_locales/cv/messages.json index 515422f202c23..b5d2ec8498ea7 100644 --- a/platform/mv3/extension/_locales/cv/messages.json +++ b/platform/mv3/extension/_locales/cv/messages.json @@ -60,7 +60,7 @@ "description": "Header for a ruleset section in 'Filter lists pane'" }, "3pGroupMalware": { - "message": "Malware domains", + "message": "Malware protection, security", "description": "Header for a ruleset section in 'Filter lists pane'" }, "3pGroupAnnoyances": { diff --git a/platform/mv3/extension/_locales/cy/messages.json b/platform/mv3/extension/_locales/cy/messages.json index e8f7e1cb5ba9a..3e444d525379e 100644 --- a/platform/mv3/extension/_locales/cy/messages.json +++ b/platform/mv3/extension/_locales/cy/messages.json @@ -60,7 +60,7 @@ "description": "Header for a ruleset section in 'Filter lists pane'" }, "3pGroupMalware": { - "message": "Malware domains", + "message": "Malware protection, security", "description": "Header for a ruleset section in 'Filter lists pane'" }, "3pGroupAnnoyances": { diff --git a/platform/mv3/extension/_locales/eo/messages.json b/platform/mv3/extension/_locales/eo/messages.json index ed989e8b46d37..90e4a10343dfd 100644 --- a/platform/mv3/extension/_locales/eo/messages.json +++ b/platform/mv3/extension/_locales/eo/messages.json @@ -60,7 +60,7 @@ "description": "Header for a ruleset section in 'Filter lists pane'" }, "3pGroupMalware": { - "message": "Malware domains", + "message": "Malware protection, security", "description": "Header for a ruleset section in 'Filter lists pane'" }, "3pGroupAnnoyances": { diff --git a/platform/mv3/extension/_locales/fa/messages.json b/platform/mv3/extension/_locales/fa/messages.json index e9b868325b9e9..743018c21a4cd 100644 --- a/platform/mv3/extension/_locales/fa/messages.json +++ b/platform/mv3/extension/_locales/fa/messages.json @@ -60,7 +60,7 @@ "description": "Header for a ruleset section in 'Filter lists pane'" }, "3pGroupMalware": { - "message": "Malware domains", + "message": "Malware protection, security", "description": "Header for a ruleset section in 'Filter lists pane'" }, "3pGroupAnnoyances": { diff --git a/platform/mv3/extension/_locales/gl/messages.json b/platform/mv3/extension/_locales/gl/messages.json index 9afa714e8a941..c6e3cc2daa151 100644 --- a/platform/mv3/extension/_locales/gl/messages.json +++ b/platform/mv3/extension/_locales/gl/messages.json @@ -60,7 +60,7 @@ "description": "Header for a ruleset section in 'Filter lists pane'" }, "3pGroupMalware": { - "message": "Malware domains", + "message": "Malware protection, security", "description": "Header for a ruleset section in 'Filter lists pane'" }, "3pGroupAnnoyances": { diff --git a/platform/mv3/extension/_locales/gu/messages.json b/platform/mv3/extension/_locales/gu/messages.json index 515422f202c23..b5d2ec8498ea7 100644 --- a/platform/mv3/extension/_locales/gu/messages.json +++ b/platform/mv3/extension/_locales/gu/messages.json @@ -60,7 +60,7 @@ "description": "Header for a ruleset section in 'Filter lists pane'" }, "3pGroupMalware": { - "message": "Malware domains", + "message": "Malware protection, security", "description": "Header for a ruleset section in 'Filter lists pane'" }, "3pGroupAnnoyances": { diff --git a/platform/mv3/extension/_locales/kk/messages.json b/platform/mv3/extension/_locales/kk/messages.json index 515422f202c23..b5d2ec8498ea7 100644 --- a/platform/mv3/extension/_locales/kk/messages.json +++ b/platform/mv3/extension/_locales/kk/messages.json @@ -60,7 +60,7 @@ "description": "Header for a ruleset section in 'Filter lists pane'" }, "3pGroupMalware": { - "message": "Malware domains", + "message": "Malware protection, security", "description": "Header for a ruleset section in 'Filter lists pane'" }, "3pGroupAnnoyances": { diff --git a/platform/mv3/extension/_locales/kn/messages.json b/platform/mv3/extension/_locales/kn/messages.json index 4eead835febb8..94a0f5288d013 100644 --- a/platform/mv3/extension/_locales/kn/messages.json +++ b/platform/mv3/extension/_locales/kn/messages.json @@ -60,7 +60,7 @@ "description": "Header for a ruleset section in 'Filter lists pane'" }, "3pGroupMalware": { - "message": "Malware domains", + "message": "Malware protection, security", "description": "Header for a ruleset section in 'Filter lists pane'" }, "3pGroupAnnoyances": { diff --git a/platform/mv3/extension/_locales/lt/messages.json b/platform/mv3/extension/_locales/lt/messages.json index abdd54d2d3c21..964c21d1f5bb8 100644 --- a/platform/mv3/extension/_locales/lt/messages.json +++ b/platform/mv3/extension/_locales/lt/messages.json @@ -60,7 +60,7 @@ "description": "Header for a ruleset section in 'Filter lists pane'" }, "3pGroupMalware": { - "message": "Malware domains", + "message": "Malware protection, security", "description": "Header for a ruleset section in 'Filter lists pane'" }, "3pGroupAnnoyances": { diff --git a/platform/mv3/extension/_locales/mk/messages.json b/platform/mv3/extension/_locales/mk/messages.json index 3a61406f87fec..39a5b44a517e6 100644 --- a/platform/mv3/extension/_locales/mk/messages.json +++ b/platform/mv3/extension/_locales/mk/messages.json @@ -60,7 +60,7 @@ "description": "Header for a ruleset section in 'Filter lists pane'" }, "3pGroupMalware": { - "message": "Malware domains", + "message": "Malware protection, security", "description": "Header for a ruleset section in 'Filter lists pane'" }, "3pGroupAnnoyances": { diff --git a/platform/mv3/extension/_locales/ml/messages.json b/platform/mv3/extension/_locales/ml/messages.json index 9a5d77e6d00b4..2dd452d2c989f 100644 --- a/platform/mv3/extension/_locales/ml/messages.json +++ b/platform/mv3/extension/_locales/ml/messages.json @@ -60,7 +60,7 @@ "description": "Header for a ruleset section in 'Filter lists pane'" }, "3pGroupMalware": { - "message": "Malware domains", + "message": "Malware protection, security", "description": "Header for a ruleset section in 'Filter lists pane'" }, "3pGroupAnnoyances": { diff --git a/platform/mv3/extension/_locales/mr/messages.json b/platform/mv3/extension/_locales/mr/messages.json index 515422f202c23..b5d2ec8498ea7 100644 --- a/platform/mv3/extension/_locales/mr/messages.json +++ b/platform/mv3/extension/_locales/mr/messages.json @@ -60,7 +60,7 @@ "description": "Header for a ruleset section in 'Filter lists pane'" }, "3pGroupMalware": { - "message": "Malware domains", + "message": "Malware protection, security", "description": "Header for a ruleset section in 'Filter lists pane'" }, "3pGroupAnnoyances": { diff --git a/platform/mv3/extension/_locales/ms/messages.json b/platform/mv3/extension/_locales/ms/messages.json index c90d647d292dc..828adc67eef3d 100644 --- a/platform/mv3/extension/_locales/ms/messages.json +++ b/platform/mv3/extension/_locales/ms/messages.json @@ -60,7 +60,7 @@ "description": "Header for a ruleset section in 'Filter lists pane'" }, "3pGroupMalware": { - "message": "Malware domains", + "message": "Malware protection, security", "description": "Header for a ruleset section in 'Filter lists pane'" }, "3pGroupAnnoyances": { diff --git a/platform/mv3/extension/_locales/oc/messages.json b/platform/mv3/extension/_locales/oc/messages.json index 515422f202c23..b5d2ec8498ea7 100644 --- a/platform/mv3/extension/_locales/oc/messages.json +++ b/platform/mv3/extension/_locales/oc/messages.json @@ -60,7 +60,7 @@ "description": "Header for a ruleset section in 'Filter lists pane'" }, "3pGroupMalware": { - "message": "Malware domains", + "message": "Malware protection, security", "description": "Header for a ruleset section in 'Filter lists pane'" }, "3pGroupAnnoyances": { diff --git a/platform/mv3/extension/_locales/pa/messages.json b/platform/mv3/extension/_locales/pa/messages.json index dcd08462ffb0f..fbf91d2ece372 100644 --- a/platform/mv3/extension/_locales/pa/messages.json +++ b/platform/mv3/extension/_locales/pa/messages.json @@ -60,7 +60,7 @@ "description": "Header for a ruleset section in 'Filter lists pane'" }, "3pGroupMalware": { - "message": "Malware domains", + "message": "Malware protection, security", "description": "Header for a ruleset section in 'Filter lists pane'" }, "3pGroupAnnoyances": { diff --git a/platform/mv3/extension/_locales/sl/messages.json b/platform/mv3/extension/_locales/sl/messages.json index 515422f202c23..b5d2ec8498ea7 100644 --- a/platform/mv3/extension/_locales/sl/messages.json +++ b/platform/mv3/extension/_locales/sl/messages.json @@ -60,7 +60,7 @@ "description": "Header for a ruleset section in 'Filter lists pane'" }, "3pGroupMalware": { - "message": "Malware domains", + "message": "Malware protection, security", "description": "Header for a ruleset section in 'Filter lists pane'" }, "3pGroupAnnoyances": { diff --git a/platform/mv3/extension/_locales/so/messages.json b/platform/mv3/extension/_locales/so/messages.json index 515422f202c23..b5d2ec8498ea7 100644 --- a/platform/mv3/extension/_locales/so/messages.json +++ b/platform/mv3/extension/_locales/so/messages.json @@ -60,7 +60,7 @@ "description": "Header for a ruleset section in 'Filter lists pane'" }, "3pGroupMalware": { - "message": "Malware domains", + "message": "Malware protection, security", "description": "Header for a ruleset section in 'Filter lists pane'" }, "3pGroupAnnoyances": { diff --git a/platform/mv3/extension/_locales/sw/messages.json b/platform/mv3/extension/_locales/sw/messages.json index 515422f202c23..b5d2ec8498ea7 100644 --- a/platform/mv3/extension/_locales/sw/messages.json +++ b/platform/mv3/extension/_locales/sw/messages.json @@ -60,7 +60,7 @@ "description": "Header for a ruleset section in 'Filter lists pane'" }, "3pGroupMalware": { - "message": "Malware domains", + "message": "Malware protection, security", "description": "Header for a ruleset section in 'Filter lists pane'" }, "3pGroupAnnoyances": { diff --git a/platform/mv3/extension/_locales/ta/messages.json b/platform/mv3/extension/_locales/ta/messages.json index 515422f202c23..b5d2ec8498ea7 100644 --- a/platform/mv3/extension/_locales/ta/messages.json +++ b/platform/mv3/extension/_locales/ta/messages.json @@ -60,7 +60,7 @@ "description": "Header for a ruleset section in 'Filter lists pane'" }, "3pGroupMalware": { - "message": "Malware domains", + "message": "Malware protection, security", "description": "Header for a ruleset section in 'Filter lists pane'" }, "3pGroupAnnoyances": { diff --git a/platform/mv3/extension/_locales/te/messages.json b/platform/mv3/extension/_locales/te/messages.json index 5f4823359b226..1dbee4ca9a2e2 100644 --- a/platform/mv3/extension/_locales/te/messages.json +++ b/platform/mv3/extension/_locales/te/messages.json @@ -60,7 +60,7 @@ "description": "Header for a ruleset section in 'Filter lists pane'" }, "3pGroupMalware": { - "message": "Malware domains", + "message": "Malware protection, security", "description": "Header for a ruleset section in 'Filter lists pane'" }, "3pGroupAnnoyances": { diff --git a/platform/mv3/extension/_locales/th/messages.json b/platform/mv3/extension/_locales/th/messages.json index 31a0642920e39..fea659106cd36 100644 --- a/platform/mv3/extension/_locales/th/messages.json +++ b/platform/mv3/extension/_locales/th/messages.json @@ -60,7 +60,7 @@ "description": "Header for a ruleset section in 'Filter lists pane'" }, "3pGroupMalware": { - "message": "Malware domains", + "message": "Malware protection, security", "description": "Header for a ruleset section in 'Filter lists pane'" }, "3pGroupAnnoyances": { diff --git a/platform/mv3/extension/_locales/ur/messages.json b/platform/mv3/extension/_locales/ur/messages.json index d620ff2b57c9e..97cf4fd512537 100644 --- a/platform/mv3/extension/_locales/ur/messages.json +++ b/platform/mv3/extension/_locales/ur/messages.json @@ -60,7 +60,7 @@ "description": "Header for a ruleset section in 'Filter lists pane'" }, "3pGroupMalware": { - "message": "Malware domains", + "message": "Malware protection, security", "description": "Header for a ruleset section in 'Filter lists pane'" }, "3pGroupAnnoyances": { From c279cded17051ec9dcb92afbb829aee68df52ac6 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sat, 14 Dec 2024 09:01:23 -0500 Subject: [PATCH 0549/1099] [mv3] Minor code review --- platform/mv3/extension/js/ruleset-manager.js | 22 ++++++++++++-------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/platform/mv3/extension/js/ruleset-manager.js b/platform/mv3/extension/js/ruleset-manager.js index 04bfb3b6f241d..4e3ab50b2e382 100644 --- a/platform/mv3/extension/js/ruleset-manager.js +++ b/platform/mv3/extension/js/ruleset-manager.js @@ -23,11 +23,8 @@ import { browser, dnr, i18n, - runtime, -} from './ext.js'; - -import { localRead, localRemove, localWrite, + runtime, sessionRead, sessionRemove, sessionWrite, } from './ext.js'; @@ -578,13 +575,17 @@ async function defaultRulesetsFromLanguage() { `\\b(${Array.from(langSet).join('|')})\\b` ); + const manifest = runtime.getManifest(); + const rulesets = manifest.declarative_net_request.rule_resources; const rulesetDetails = await getRulesetDetails(); const out = []; - for ( const [ id, details ] of rulesetDetails ) { - if ( details.enabled ) { + for ( const ruleset of rulesets ) { + const { id, enabled } = ruleset; + if ( enabled ) { out.push(id); continue; } + const details = rulesetDetails.get(id); if ( typeof details.lang !== 'string' ) { continue; } if ( reTargetLang.test(details.lang) === false ) { continue; } out.push(id); @@ -598,12 +599,15 @@ async function patchDefaultRulesets() { const [ oldDefaultIds = [], newDefaultIds, - newIds, ] = await Promise.all([ localRead('defaultRulesetIds'), defaultRulesetsFromLanguage(), - getRulesetDetails(), ]); + + const manifest = runtime.getManifest(); + const validIds = new Set( + manifest.declarative_net_request.rule_resources.map(r => r.id) + ); const toAdd = []; const toRemove = []; for ( const id of newDefaultIds ) { @@ -615,7 +619,7 @@ async function patchDefaultRulesets() { toRemove.push(id); } for ( const id of rulesetConfig.enabledRulesets ) { - if ( newIds.has(id) ) { continue; } + if ( validIds.has(id) ) { continue; } toRemove.push(id); } localWrite('defaultRulesetIds', newDefaultIds); From 9c75814525cad2ae4e963f0cb305b1c676a64308 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sat, 14 Dec 2024 09:03:17 -0500 Subject: [PATCH 0550/1099] Use code quotes for filters in logger export feature --- src/js/logger-ui.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/js/logger-ui.js b/src/js/logger-ui.js index e76586a50f854..0b9c31dd99d57 100644 --- a/src/js/logger-ui.js +++ b/src/js/logger-ui.js @@ -2779,7 +2779,7 @@ const loggerStats = (( ) => { const outputOne = []; for ( let i = 0; i < fields.length; i++ ) { const field = fields[i]; - let code = /\b(?:www\.|https?:\/\/)/.test(field) ? '`' : ''; + const code = i === 1 || /\b(?:www\.|https?:\/\/)/.test(field) ? '`' : ''; outputOne.push(` ${code}${field.replace(/\|/g, '\\|')}${code} `); } outputAll.push(outputOne.join('|')); From d47876c54710e57dd099d522e6aeebc782392506 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sat, 14 Dec 2024 11:00:21 -0500 Subject: [PATCH 0551/1099] Use combined list for UKR As per feedback: https://github.com/uBlockOrigin/uBlock-issues/issues/2692#issuecomment-2543153158 --- assets/assets.dev.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/assets/assets.dev.json b/assets/assets.dev.json index 6681c5c979e02..aa529d23614af 100644 --- a/assets/assets.dev.json +++ b/assets/assets.dev.json @@ -926,10 +926,10 @@ "content": "filters", "group": "regions", "off": true, - "title": "🇺🇦ua: Ukrainian Ad List", + "title": "🇺🇦ua: Ukrainian Filters", "tags": "ads ukraine україна", "lang": "uk", - "contentURL": "https://raw.githubusercontent.com/ukrainianfilters/lists/main/ads/ads.txt", + "contentURL": "https://raw.githubusercontent.com/ukrainianfilters/lists/main/combined/uBO/uBO.txt", "supportURL": "https://github.com/ukrainianfilters/lists" }, "VIE-1": { From 9fbc23abfca5ed42caff357c12398ba4c7ec6181 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sun, 15 Dec 2024 15:38:40 -0500 Subject: [PATCH 0552/1099] Import translation work from https://crowdin.com/project/ublock --- .../mv3/extension/_locales/ar/messages.json | 2 +- .../mv3/extension/_locales/de/messages.json | 2 +- .../mv3/extension/_locales/es/messages.json | 2 +- .../mv3/extension/_locales/it/messages.json | 18 +++++++++--------- src/_locales/br_FR/messages.json | 2 +- 5 files changed, 13 insertions(+), 13 deletions(-) diff --git a/platform/mv3/extension/_locales/ar/messages.json b/platform/mv3/extension/_locales/ar/messages.json index 62bc98175dd32..c4fd37b0e2c92 100644 --- a/platform/mv3/extension/_locales/ar/messages.json +++ b/platform/mv3/extension/_locales/ar/messages.json @@ -252,7 +252,7 @@ "description": "Sentence used in the strict-blocked page" }, "strictblockReasonSentence1": { - "message": "The page was blocked because of a matching filter in {{listname}}.", + "message": "تم حظر الصفحة بسبب وجود فلتر مطابق في {{listname}}.", "description": "Text informing about what is causing the page to be blocked" }, "strictblockRedirectSentence1": { diff --git a/platform/mv3/extension/_locales/de/messages.json b/platform/mv3/extension/_locales/de/messages.json index de200b1f5e779..22b4bb99bb3cb 100644 --- a/platform/mv3/extension/_locales/de/messages.json +++ b/platform/mv3/extension/_locales/de/messages.json @@ -60,7 +60,7 @@ "description": "Header for a ruleset section in 'Filter lists pane'" }, "3pGroupMalware": { - "message": "Domains mit Schadsoftware", + "message": "Schutz vor Schadsoftware, Sicherheit", "description": "Header for a ruleset section in 'Filter lists pane'" }, "3pGroupAnnoyances": { diff --git a/platform/mv3/extension/_locales/es/messages.json b/platform/mv3/extension/_locales/es/messages.json index 10eec6a0d1a76..e17dfe967f5e4 100644 --- a/platform/mv3/extension/_locales/es/messages.json +++ b/platform/mv3/extension/_locales/es/messages.json @@ -60,7 +60,7 @@ "description": "Header for a ruleset section in 'Filter lists pane'" }, "3pGroupMalware": { - "message": "Dominios de malware", + "message": "Protección de malware, seguridad", "description": "Header for a ruleset section in 'Filter lists pane'" }, "3pGroupAnnoyances": { diff --git a/platform/mv3/extension/_locales/it/messages.json b/platform/mv3/extension/_locales/it/messages.json index 3a7da35ac8aa9..e454824ab0d3b 100644 --- a/platform/mv3/extension/_locales/it/messages.json +++ b/platform/mv3/extension/_locales/it/messages.json @@ -232,11 +232,11 @@ "description": "Label for a checkbox in the options page" }, "enableStrictBlockLabel": { - "message": "Enable strict blocking", + "message": "Abilita il blocco rigido", "description": "Label for a checkbox in the options page" }, "enableStrictBlockLegend": { - "message": "Navigation to potentially undesirable sites will be blocked, and you will be offered the option to proceed.", + "message": "La navigazione verso siti potenzialmente indesiderati verrà bloccata e ti verrà offerta la possibilità di procedere.", "description": "Label for a checkbox in the options page" }, "findListsPlaceholder": { @@ -244,11 +244,11 @@ "description": "Placeholder for the input field used to find lists" }, "strictblockTitle": { - "message": "Page blocked", + "message": "Pagina bloccata", "description": "Webpage title for the strict-blocked page" }, "strictblockSentence1": { - "message": "uBO Lite has prevented the following page from loading:", + "message": "uBO Lite ha impedito alla seguente pagina di caricarsi:", "description": "Sentence used in the strict-blocked page" }, "strictblockReasonSentence1": { @@ -260,23 +260,23 @@ "description": "Text warning about an incoming redirect" }, "strictblockNoParamsPrompt": { - "message": "without parameters", + "message": "senza parametri", "description": "Label to be used for the parameter-less URL" }, "strictblockBack": { - "message": "Go back", + "message": "Torna indietro", "description": "A button to go back to the previous webpage" }, "strictblockClose": { - "message": "Close this window", + "message": "Chiudi questa finestra", "description": "A button to close the current tab" }, "strictblockDontWarn": { - "message": "Don't warn me again about this site", + "message": "Non avvisarmi di nuovo riguardo questo sito", "description": "Label for checkbox in document-blocked page" }, "strictblockProceed": { - "message": "Proceed", + "message": "Procedi", "description": "A button to navigate to the blocked page" } } diff --git a/src/_locales/br_FR/messages.json b/src/_locales/br_FR/messages.json index abddf0af58f94..53d3ce41e38c9 100644 --- a/src/_locales/br_FR/messages.json +++ b/src/_locales/br_FR/messages.json @@ -1160,7 +1160,7 @@ "description": "label to be used for the parameter-less URL: https://cloud.githubusercontent.com/assets/585534/9832014/bfb1b8f0-593b-11e5-8a27-fba472a5529a.png" }, "docblockedFoundIn": { - "message": "Hag a vez kavet e-barzh:", + "message": "Hag a zo kavet e-barzh:", "description": "English: List of filter list names follows" }, "docblockedBack": { From 791a2b08e1a15c169c0a86130de769822628c81d Mon Sep 17 00:00:00 2001 From: Fanboynz Date: Tue, 17 Dec 2024 02:26:36 +1300 Subject: [PATCH 0553/1099] Add all/none in set-local/cookie (#3928) * Add all/none in set-local/cookie * Add functional cookie value --- src/js/resources/cookie.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/js/resources/cookie.js b/src/js/resources/cookie.js index 29ad7315cc805..df02ca2e93403 100644 --- a/src/js/resources/cookie.js +++ b/src/js/resources/cookie.js @@ -44,6 +44,7 @@ export function getSafeCookieValuesFn() { 'on', 'off', 'true', 't', 'false', 'f', 'yes', 'y', 'no', 'n', + 'all', 'none', 'functional', ]; } registerScriptlet(getSafeCookieValuesFn, { From 9bf8d53ebe7cab20ac8491741c5b99cf9d35d643 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Fri, 20 Dec 2024 10:07:40 -0500 Subject: [PATCH 0554/1099] Improve `href-sanitizer` scriptlet Related discussion: https://github.com/uBlockOrigin/uBlock-issues/discussions/3487 Add support for `urlskip=` syntax by internally reusing `urlskip=` code. --- src/js/resources/href-sanitizer.js | 174 +++++++++++++++++++++++++++++ src/js/resources/scriptlets.js | 158 +------------------------- src/js/urlskip.js | 9 +- 3 files changed, 180 insertions(+), 161 deletions(-) create mode 100644 src/js/resources/href-sanitizer.js diff --git a/src/js/resources/href-sanitizer.js b/src/js/resources/href-sanitizer.js new file mode 100644 index 0000000000000..c445f024cd83f --- /dev/null +++ b/src/js/resources/href-sanitizer.js @@ -0,0 +1,174 @@ +/******************************************************************************* + + uBlock Origin - a comprehensive, efficient content blocker + Copyright (C) 2019-present Raymond Hill + + 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/}. + + Home: https://github.com/gorhill/uBlock + +*/ + +import { registerScriptlet } from './base.js'; +import { runAt } from './run-at.js'; +import { safeSelf } from './safe-self.js'; +import { urlSkip } from '../urlskip.js'; + +/******************************************************************************/ + +registerScriptlet(urlSkip, { + name: 'urlskip.fn', +}); + +/** + * @scriptlet href-sanitizer + * + * @description + * Set the `href` attribute to a value found in the DOM at, or below the + * targeted `a` element, and optionally with transformation steps. + * + * @param selector + * A plain CSS selector for elements which `href` property must be sanitized. + * + * @param source + * One or more tokens to lookup the source of the `href` property, and + * optionally the transformation steps to perform: + * - `text`: Use the text content of the element as the URL + * - `[name]`: Use the value of the attribute `name` as the URL + * - Transformation steps: see `urlskip` documentation + * + * If `text` or `[name]` is not present, the URL will be the value of `href` + * attribute. + * + * @example + * `example.org##+js(href-sanitizer, a)` + * `example.org##+js(href-sanitizer, a[title], [title])` + * `example.org##+js(href-sanitizer, a[href*="/away.php?to="], ?to)` + * `example.org##+js(href-sanitizer, a[href*="/redirect"], ?url ?url -base64)` + * + * */ + +function hrefSanitizer( + selector = '', + source = '' +) { + if ( typeof selector !== 'string' ) { return; } + if ( selector === '' ) { return; } + const safe = safeSelf(); + const logPrefix = safe.makeLogPrefix('href-sanitizer', selector, source); + if ( source === '' ) { source = 'text'; } + const sanitizeCopycats = (href, text) => { + let elems = []; + try { + elems = document.querySelectorAll(`a[href="${href}"`); + } + catch(ex) { + } + for ( const elem of elems ) { + elem.setAttribute('href', text); + } + return elems.length; + }; + const validateURL = text => { + if ( typeof text !== 'string' ) { return ''; } + if ( text === '' ) { return ''; } + if ( /[\x00-\x20\x7f]/.test(text) ) { return ''; } + try { + const url = new URL(text, document.location); + return url.href; + } catch(ex) { + } + return ''; + }; + const extractURL = (elem, source) => { + if ( /^\[.*\]$/.test(source) ) { + return elem.getAttribute(source.slice(1,-1).trim()) || ''; + } + if ( source === 'text' ) { + return elem.textContent + .replace(/^[^\x21-\x7e]+/, '') // remove leading invalid characters + .replace(/[^\x21-\x7e]+$/, '') // remove trailing invalid characters + ; + } + if ( source.startsWith('?') ) { + const steps = source.replace(/(\S)\?/g, '\\1?').split(/\s+/); + const url = urlSkip(elem.href, false, steps); + if ( url === undefined ) { return; } + return url.replace(/ /g, '%20'); + } + return ''; + }; + const sanitize = ( ) => { + let elems = []; + try { + elems = document.querySelectorAll(selector); + } + catch(ex) { + return false; + } + for ( const elem of elems ) { + if ( elem.localName !== 'a' ) { continue; } + if ( elem.hasAttribute('href') === false ) { continue; } + const href = elem.getAttribute('href'); + const text = extractURL(elem, source); + const hrefAfter = validateURL(text); + if ( hrefAfter === '' ) { continue; } + if ( hrefAfter === href ) { continue; } + elem.setAttribute('href', hrefAfter); + const count = sanitizeCopycats(href, hrefAfter); + safe.uboLog(logPrefix, `Sanitized ${count+1} links to\n${hrefAfter}`); + } + return true; + }; + let observer, timer; + const onDomChanged = mutations => { + if ( timer !== undefined ) { return; } + let shouldSanitize = false; + for ( const mutation of mutations ) { + if ( mutation.addedNodes.length === 0 ) { continue; } + for ( const node of mutation.addedNodes ) { + if ( node.nodeType !== 1 ) { continue; } + shouldSanitize = true; + break; + } + if ( shouldSanitize ) { break; } + } + if ( shouldSanitize === false ) { return; } + timer = safe.onIdle(( ) => { + timer = undefined; + sanitize(); + }); + }; + const start = ( ) => { + if ( sanitize() === false ) { return; } + observer = new MutationObserver(onDomChanged); + observer.observe(document.body, { + subtree: true, + childList: true, + }); + }; + runAt(( ) => { start(); }, 'interactive'); +} +registerScriptlet(hrefSanitizer, { + name: 'href-sanitizer.js', + world: 'ISOLATED', + aliases: [ + 'urlskip.js', + ], + dependencies: [ + runAt, + safeSelf, + urlSkip, + ], +}); diff --git a/src/js/resources/scriptlets.js b/src/js/resources/scriptlets.js index aeb0f096180c4..a460f3fb7533f 100644 --- a/src/js/resources/scriptlets.js +++ b/src/js/resources/scriptlets.js @@ -21,6 +21,7 @@ */ import './attribute.js'; +import './href-sanitizer.js'; import './replace-argument.js'; import './spoof-css.js'; import './prevent-settimeout.js'; @@ -2576,163 +2577,6 @@ function m3uPrune( }); } -/******************************************************************************* - * - * @scriptlet href-sanitizer - * - * @description - * Set the `href` attribute to a value found in the DOM at, or below the - * targeted `a` element. - * - * ### Syntax - * - * ```text - * example.org##+js(href-sanitizer, selector [, source]) - * ``` - * - * - `selector`: required, CSS selector, specifies `a` elements for which the - * `href` attribute must be overridden. - * - `source`: optional, default to `text`, specifies from where to get the - * value which will override the `href` attribute. - * - `text`: the value will be the first valid URL found in the text - * content of the targeted `a` element. - * - `[attr]`: the value will be the attribute _attr_ of the targeted `a` - * element. - * - `?param`: the value will be the query parameter _param_ of the URL - * found in the `href` attribute of the targeted `a` element. - * - * ### Examples - * - * example.org##+js(href-sanitizer, a) - * example.org##+js(href-sanitizer, a[title], [title]) - * example.org##+js(href-sanitizer, a[href*="/away.php?to="], ?to) - * - * */ - -builtinScriptlets.push({ - name: 'href-sanitizer.js', - fn: hrefSanitizer, - world: 'ISOLATED', - dependencies: [ - 'run-at.fn', - 'safe-self.fn', - ], -}); -function hrefSanitizer( - selector = '', - source = '' -) { - if ( typeof selector !== 'string' ) { return; } - if ( selector === '' ) { return; } - const safe = safeSelf(); - const logPrefix = safe.makeLogPrefix('href-sanitizer', selector, source); - if ( source === '' ) { source = 'text'; } - const sanitizeCopycats = (href, text) => { - let elems = []; - try { - elems = document.querySelectorAll(`a[href="${href}"`); - } - catch(ex) { - } - for ( const elem of elems ) { - elem.setAttribute('href', text); - } - return elems.length; - }; - const validateURL = text => { - if ( text === '' ) { return ''; } - if ( /[\x00-\x20\x7f]/.test(text) ) { return ''; } - try { - const url = new URL(text, document.location); - return url.href; - } catch(ex) { - } - return ''; - }; - const extractParam = (href, source) => { - if ( Boolean(source) === false ) { return href; } - const recursive = source.includes('?', 1); - const end = recursive ? source.indexOf('?', 1) : source.length; - try { - const url = new URL(href, document.location); - let value = url.searchParams.get(source.slice(1, end)); - if ( value === null ) { return href } - if ( recursive ) { return extractParam(value, source.slice(end)); } - if ( value.includes(' ') ) { - value = value.replace(/ /g, '%20'); - } - return value; - } catch(x) { - } - return href; - }; - const extractText = (elem, source) => { - if ( /^\[.*\]$/.test(source) ) { - return elem.getAttribute(source.slice(1,-1).trim()) || ''; - } - if ( source.startsWith('?') ) { - return extractParam(elem.href, source); - } - if ( source === 'text' ) { - return elem.textContent - .replace(/^[^\x21-\x7e]+/, '') // remove leading invalid characters - .replace(/[^\x21-\x7e]+$/, '') // remove trailing invalid characters - ; - } - return ''; - }; - const sanitize = ( ) => { - let elems = []; - try { - elems = document.querySelectorAll(selector); - } - catch(ex) { - return false; - } - for ( const elem of elems ) { - if ( elem.localName !== 'a' ) { continue; } - if ( elem.hasAttribute('href') === false ) { continue; } - const href = elem.getAttribute('href'); - const text = extractText(elem, source); - const hrefAfter = validateURL(text); - if ( hrefAfter === '' ) { continue; } - if ( hrefAfter === href ) { continue; } - elem.setAttribute('href', hrefAfter); - const count = sanitizeCopycats(href, hrefAfter); - safe.uboLog(logPrefix, `Sanitized ${count+1} links to\n${hrefAfter}`); - } - return true; - }; - let observer, timer; - const onDomChanged = mutations => { - if ( timer !== undefined ) { return; } - let shouldSanitize = false; - for ( const mutation of mutations ) { - if ( mutation.addedNodes.length === 0 ) { continue; } - for ( const node of mutation.addedNodes ) { - if ( node.nodeType !== 1 ) { continue; } - shouldSanitize = true; - break; - } - if ( shouldSanitize ) { break; } - } - if ( shouldSanitize === false ) { return; } - timer = safe.onIdle(( ) => { - timer = undefined; - sanitize(); - }); - }; - const start = ( ) => { - if ( sanitize() === false ) { return; } - observer = new MutationObserver(onDomChanged); - observer.observe(document.body, { - subtree: true, - childList: true, - }); - }; - runAt(( ) => { start(); }, 'interactive'); -} - /******************************************************************************* * * @scriptlet call-nothrow diff --git a/src/js/urlskip.js b/src/js/urlskip.js index e4182bbc4be24..31af869ad680b 100644 --- a/src/js/urlskip.js +++ b/src/js/urlskip.js @@ -19,9 +19,6 @@ Home: https://github.com/gorhill/uBlock */ -const safeBase64Map = { '-': '+', '_': '/' }; -const safeBase64Replacer = s => safeBase64Map[s]; - /** * @trustedOption urlskip * @@ -106,7 +103,11 @@ export function urlSkip(url, blocked, steps, directive = {}) { } // Safe Base64 if ( step === '-safebase64' ) { - urlout = urlin.replace(/[-_]/g, safeBase64Replacer); + if ( urlSkip.safeBase64Replacer === undefined ) { + urlSkip.safeBase64Map = { '-': '+', '_': '/' }; + urlSkip.safeBase64Replacer = s => urlSkip.safeBase64Map[s]; + } + urlout = urlin.replace(/[-_]/g, urlSkip.safeBase64Replacer); urlout = self.atob(urlout); continue; } From 5ddde5a36a5beec3ac88a8b56cc34cf81bc18402 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Fri, 20 Dec 2024 10:14:11 -0500 Subject: [PATCH 0555/1099] Update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 95caa55a11a3c..53f0c70d5cf9c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,4 @@ +- [Improve `href-sanitizer` scriptlet](https://github.com/gorhill/uBlock/commit/9bf8d53ebe) - [Improve quote usage in filter options and scriptlets](https://github.com/gorhill/uBlock/commit/8ba71f09d7) - [Improve `trusted-suppress-native-method` scriptlet](https://github.com/gorhill/uBlock/commit/7ed3470844) - [Improve `trusted-replace-argument` scriptlet](https://github.com/gorhill/uBlock/commit/3417fe3d5d) From 743a9076921395992360382db2a84f99f22a5604 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Fri, 20 Dec 2024 10:14:31 -0500 Subject: [PATCH 0556/1099] New revision for dev build --- dist/version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dist/version b/dist/version index 08ceb89e82319..ac220a20dcbdb 100644 --- a/dist/version +++ b/dist/version @@ -1 +1 @@ -1.61.3.9 \ No newline at end of file +1.61.3.10 \ No newline at end of file From 143cc0280ad93071bf3223f0618763d994661cbe Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Fri, 20 Dec 2024 10:26:16 -0500 Subject: [PATCH 0557/1099] Make Firefox dev build auto-update --- dist/firefox/updates.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dist/firefox/updates.json b/dist/firefox/updates.json index 3d1dabb89b96e..ec5113be34d48 100644 --- a/dist/firefox/updates.json +++ b/dist/firefox/updates.json @@ -3,9 +3,9 @@ "uBlock0@raymondhill.net": { "updates": [ { - "version": "1.61.3.9", + "version": "1.61.3.10", "browser_specific_settings": { "gecko": { "strict_min_version": "78.0" } }, - "update_link": "https://github.com/gorhill/uBlock/releases/download/1.61.3b9/uBlock0_1.61.3b9.firefox.signed.xpi" + "update_link": "https://github.com/gorhill/uBlock/releases/download/1.61.3b10/uBlock0_1.61.3b10.firefox.signed.xpi" } ] } From c311315daa6eeae23a272db84044019ad7346254 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Fri, 20 Dec 2024 13:55:48 -0500 Subject: [PATCH 0558/1099] [mv3] Fix undue blocking of network requests for unfiltered sites Related issue: https://github.com/uBlockOrigin/uBOL-home/issues/247 Use session rules with `TAB_ID_NONE` to properly exclude network requests from service workers potentially used by trusted websites. --- platform/mv3/extension/js/ext.js | 1 + platform/mv3/extension/js/ruleset-manager.js | 112 +++++++++++-------- 2 files changed, 68 insertions(+), 45 deletions(-) diff --git a/platform/mv3/extension/js/ext.js b/platform/mv3/extension/js/ext.js index 0a5a98f4f59a2..cd63abc674688 100644 --- a/platform/mv3/extension/js/ext.js +++ b/platform/mv3/extension/js/ext.js @@ -28,6 +28,7 @@ export const browser = export const dnr = browser.declarativeNetRequest; export const i18n = browser.i18n; export const runtime = browser.runtime; +export const TAB_ID_NONE = browser.tabs.TAB_ID_NONE; export const windows = browser.windows; /******************************************************************************/ diff --git a/platform/mv3/extension/js/ruleset-manager.js b/platform/mv3/extension/js/ruleset-manager.js index 4e3ab50b2e382..935cf2dc6b2c3 100644 --- a/platform/mv3/extension/js/ruleset-manager.js +++ b/platform/mv3/extension/js/ruleset-manager.js @@ -20,6 +20,7 @@ */ import { + TAB_ID_NONE, browser, dnr, i18n, @@ -47,19 +48,6 @@ let dynamicRuleId = 1; /******************************************************************************/ -const eqSets = (setBefore, setAfter) => { - if ( setBefore.size !== setAfter.size ) { return false; } - for ( const hn of setAfter ) { - if ( setBefore.has(hn) === false ) { return false; } - } - for ( const hn of setBefore ) { - if ( setAfter.has(hn) === false ) { return false; } - } - return true; -}; - -/******************************************************************************/ - function getRulesetDetails() { if ( getRulesetDetails.rulesetDetailsPromise !== undefined ) { return getRulesetDetails.rulesetDetailsPromise; @@ -472,13 +460,17 @@ async function updateDynamicRules() { /******************************************************************************/ async function filteringModesToDNR(modes) { - const trustedRules = await dnr.getDynamicRules({ - ruleIds: [ TRUSTED_DIRECTIVE_BASE_RULE_ID+0 ], - }); - const trustedRule = trustedRules.length !== 0 && trustedRules[0] || undefined; - const beforeRequestDomainSet = new Set(trustedRule?.condition.requestDomains); - const beforeExcludedRrequestDomainSet = new Set(trustedRule?.condition.excludedRequestDomains); - if ( trustedRule !== undefined && beforeRequestDomainSet.size === 0 ) { + const [ + dynamicRules, + sessionRules, + ] = await Promise.all([ + dnr.getDynamicRules({ ruleIds: [ TRUSTED_DIRECTIVE_BASE_RULE_ID+0 ] }), + dnr.getSessionRules({ ruleIds: [ TRUSTED_DIRECTIVE_BASE_RULE_ID+1 ] }), + ]); + const dynamicRule = dynamicRules?.length && dynamicRules[0] || undefined; + const beforeRequestDomainSet = new Set(dynamicRule?.condition.requestDomains); + const beforeExcludedRrequestDomainSet = new Set(dynamicRule?.condition.excludedRequestDomains); + if ( dynamicRule !== undefined && beforeRequestDomainSet.size === 0 ) { beforeRequestDomainSet.add('all-urls'); } else { beforeExcludedRrequestDomainSet.add('all-urls'); @@ -493,27 +485,19 @@ async function filteringModesToDNR(modes) { afterExcludedRequestDomainSet = notNoneHostnames; } else { afterRequestDomainSet = noneHostnames; - afterExcludedRequestDomainSet = new Set([ 'all-urls' ]); + afterExcludedRequestDomainSet = new Set(); } - if ( eqSets(beforeRequestDomainSet, afterRequestDomainSet) ) { - if ( eqSets(beforeExcludedRrequestDomainSet, afterExcludedRequestDomainSet) ) { - return; - } - } - - const removeRuleIds = []; - if ( trustedRule ) { - removeRuleIds.push( - TRUSTED_DIRECTIVE_BASE_RULE_ID+0, - TRUSTED_DIRECTIVE_BASE_RULE_ID+1 - ); + const removeDynamicRuleIds = []; + const removeSessionRuleIds = []; + if ( dynamicRule ) { + removeDynamicRuleIds.push(TRUSTED_DIRECTIVE_BASE_RULE_ID+0); + removeSessionRuleIds.push(TRUSTED_DIRECTIVE_BASE_RULE_ID+1); } const allowEverywhere = afterRequestDomainSet.delete('all-urls'); - afterExcludedRequestDomainSet.delete('all-urls'); - - const addRules = []; + const addDynamicRules = []; + const addSessionRules = []; if ( allowEverywhere || afterRequestDomainSet.size !== 0 || @@ -528,33 +512,71 @@ async function filteringModesToDNR(modes) { priority: 100, }; if ( afterRequestDomainSet.size !== 0 ) { - rule0.condition.requestDomains = Array.from(afterRequestDomainSet); + rule0.condition.requestDomains = + Array.from(afterRequestDomainSet).sort(); } else if ( afterExcludedRequestDomainSet.size !== 0 ) { - rule0.condition.excludedRequestDomains = Array.from(afterExcludedRequestDomainSet); + rule0.condition.excludedRequestDomains = + Array.from(afterExcludedRequestDomainSet).sort(); } - addRules.push(rule0); + addDynamicRules.push(rule0); // https://github.com/uBlockOrigin/uBOL-home/issues/114 + // https://github.com/uBlockOrigin/uBOL-home/issues/247 const rule1 = { id: TRUSTED_DIRECTIVE_BASE_RULE_ID+1, action: { type: 'allow' }, condition: { - resourceTypes: [ 'script' ], + tabIds: [ TAB_ID_NONE ], }, priority: 100, }; if ( rule0.condition.requestDomains ) { - rule1.condition.initiatorDomains = rule0.condition.requestDomains.slice(); + rule1.condition.initiatorDomains = + rule0.condition.requestDomains.slice(); } else if ( rule0.condition.excludedRequestDomains ) { - rule1.condition.excludedInitiatorDomains = rule0.condition.excludedRequestDomains.slice(); + rule1.condition.excludedInitiatorDomains = + rule0.condition.excludedRequestDomains.slice(); } - addRules.push(rule1); + addSessionRules.push(rule1); } - if ( addRules.length === 0 && removeRuleIds.length === 0 ) { return; } + const noneCount = noneHostnames.has('all-urls') + ? -notNoneHostnames.size + : noneHostnames.size; - return dnr.updateDynamicRules({ addRules, removeRuleIds }); + const promises = []; + if ( isDifferentAllowRules(addDynamicRules, dynamicRules) ) { + promises.push(dnr.updateDynamicRules({ + addRules: addDynamicRules, + removeRuleIds: removeDynamicRuleIds, + })); + ubolLog(`Add "allowAllRequests" dynamic rule for ${noneCount} sites`); + } + if ( isDifferentAllowRules(addSessionRules, sessionRules) ) { + promises.push(dnr.updateSessionRules({ + addRules: addSessionRules, + removeRuleIds: removeSessionRuleIds, + })); + ubolLog(`Add "allow" session rule for ${noneCount} sites`); + } + if ( promises.length === 0 ) { return; } + return Promise.all(promises); } +const isDifferentAllowRules = (a, b) => { + const pp = [ + 'requestDomains', + 'excludedRequestDomains', + 'initiatorDomains', + 'excludedInitiatorDomains', + ]; + for ( const p of pp ) { + const ac = a?.length && a[0].condition[p] || []; + const bc = b?.length && b[0].condition[p] || []; + if ( ac.join() !== bc.join() ) { return true; } + } + return false; +}; + /******************************************************************************/ async function defaultRulesetsFromLanguage() { From a17a8ac04b06e1a7b1b56647b4ae2a675d5e8042 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Fri, 20 Dec 2024 14:28:23 -0500 Subject: [PATCH 0559/1099] Import translation work from https://crowdin.com/project/ublock --- .../mv3/extension/_locales/ar/messages.json | 4 ++-- .../mv3/extension/_locales/fr/messages.json | 2 +- .../mv3/extension/_locales/id/messages.json | 4 ++-- .../mv3/extension/_locales/vi/messages.json | 24 +++++++++---------- src/_locales/ar/messages.json | 4 ++-- src/_locales/oc/messages.json | 2 +- 6 files changed, 20 insertions(+), 20 deletions(-) diff --git a/platform/mv3/extension/_locales/ar/messages.json b/platform/mv3/extension/_locales/ar/messages.json index c4fd37b0e2c92..41275c0d99691 100644 --- a/platform/mv3/extension/_locales/ar/messages.json +++ b/platform/mv3/extension/_locales/ar/messages.json @@ -108,7 +108,7 @@ "description": "Header of 'Report a filter issue' section in Support pane" }, "supportS3P1": { - "message": "Report filter issues with specific websites to the uBlockOrigin/uAssets issue tracker. Requires a GitHub account.", + "message": "الإبلاغ عن مشكلات الفلترة الخاصة بمواقع الويب المحددة إلىuBlockOrigin/uAssetsمتتبع المشكلةيتطلب حساب GitHub", "description": "First paragraph of 'Filter issues' section in Support pane" }, "supportS6P1S1": { @@ -260,7 +260,7 @@ "description": "Text warning about an incoming redirect" }, "strictblockNoParamsPrompt": { - "message": "without parameters", + "message": "دون معلمات", "description": "Label to be used for the parameter-less URL" }, "strictblockBack": { diff --git a/platform/mv3/extension/_locales/fr/messages.json b/platform/mv3/extension/_locales/fr/messages.json index 58cf3803e7f88..8ba96a1067a94 100644 --- a/platform/mv3/extension/_locales/fr/messages.json +++ b/platform/mv3/extension/_locales/fr/messages.json @@ -60,7 +60,7 @@ "description": "Header for a ruleset section in 'Filter lists pane'" }, "3pGroupMalware": { - "message": "Domaines malveillants", + "message": "Protection contre les logiciels malveillants, sécurité", "description": "Header for a ruleset section in 'Filter lists pane'" }, "3pGroupAnnoyances": { diff --git a/platform/mv3/extension/_locales/id/messages.json b/platform/mv3/extension/_locales/id/messages.json index 4afc32fd7a072..15ef69acc1f7d 100644 --- a/platform/mv3/extension/_locales/id/messages.json +++ b/platform/mv3/extension/_locales/id/messages.json @@ -252,11 +252,11 @@ "description": "Sentence used in the strict-blocked page" }, "strictblockReasonSentence1": { - "message": "The page was blocked because of a matching filter in {{listname}}.", + "message": "Halaman diblokir karena cocok dengan filter yang ada di dalam {{listname}}.", "description": "Text informing about what is causing the page to be blocked" }, "strictblockRedirectSentence1": { - "message": "The blocked page wants to redirect to another site. If you choose to proceed, you will navigate directly to: {{url}}", + "message": "Halaman yang diblokir ingin beralih ke situs yang lain. Jika Anda memilih untuk melanjutkan, Anda akan langsung diarahkan ke: {{url}}", "description": "Text warning about an incoming redirect" }, "strictblockNoParamsPrompt": { diff --git a/platform/mv3/extension/_locales/vi/messages.json b/platform/mv3/extension/_locales/vi/messages.json index 069791bb6ec7e..2ce652036b083 100644 --- a/platform/mv3/extension/_locales/vi/messages.json +++ b/platform/mv3/extension/_locales/vi/messages.json @@ -232,51 +232,51 @@ "description": "Label for a checkbox in the options page" }, "enableStrictBlockLabel": { - "message": "Enable strict blocking", + "message": "Bật chặn nghiêm ngặt", "description": "Label for a checkbox in the options page" }, "enableStrictBlockLegend": { - "message": "Navigation to potentially undesirable sites will be blocked, and you will be offered the option to proceed.", + "message": "Việc điều hướng đến các trang web có khả năng không mong muốn sẽ bị chặn và bạn sẽ được cung cấp tùy chọn để tiếp tục.", "description": "Label for a checkbox in the options page" }, "findListsPlaceholder": { - "message": "Find lists", + "message": "Tìm danh sách", "description": "Placeholder for the input field used to find lists" }, "strictblockTitle": { - "message": "Page blocked", + "message": "Trang bị chặn", "description": "Webpage title for the strict-blocked page" }, "strictblockSentence1": { - "message": "uBO Lite has prevented the following page from loading:", + "message": "uBO Lite đã ngăn tải trang sau:", "description": "Sentence used in the strict-blocked page" }, "strictblockReasonSentence1": { - "message": "The page was blocked because of a matching filter in {{listname}}.", + "message": "Trang này đã bị chặn vì bộ lọc phù hợp trong {{listname}}.", "description": "Text informing about what is causing the page to be blocked" }, "strictblockRedirectSentence1": { - "message": "The blocked page wants to redirect to another site. If you choose to proceed, you will navigate directly to: {{url}}", + "message": "Trang đã chặn muốn chuyển hướng sang trang khác. Nếu đồng ý, bạn sẽ được chuyển hướng sang {{url}}", "description": "Text warning about an incoming redirect" }, "strictblockNoParamsPrompt": { - "message": "without parameters", + "message": "không có thông số", "description": "Label to be used for the parameter-less URL" }, "strictblockBack": { - "message": "Go back", + "message": "Quay lại", "description": "A button to go back to the previous webpage" }, "strictblockClose": { - "message": "Close this window", + "message": "Đóng cửa sổ này", "description": "A button to close the current tab" }, "strictblockDontWarn": { - "message": "Don't warn me again about this site", + "message": "Đừng cảnh báo lại cho tôi về trang web này", "description": "Label for checkbox in document-blocked page" }, "strictblockProceed": { - "message": "Proceed", + "message": "Tiến hành", "description": "A button to navigate to the blocked page" } } diff --git a/src/_locales/ar/messages.json b/src/_locales/ar/messages.json index 3d1023f561df5..0b62e5e1f6a01 100644 --- a/src/_locales/ar/messages.json +++ b/src/_locales/ar/messages.json @@ -104,7 +104,7 @@ "description": "For the new mobile-friendly popup design" }, "popupBlockedSinceInstall_v2": { - "message": "حُجِب منذ التنصيب", + "message": "تم حظره منذ التثبيت", "description": "For the new mobile-friendly popup design" }, "popupDomainsConnected_v2": { @@ -128,7 +128,7 @@ "description": "Tooltip used for the logger icon in the panel" }, "popupTipReport": { - "message": "التقرير عن مُشكلة في الموقع", + "message": "الإبلاغ عن مشكلة في هذا الموقع", "description": "Tooltip used for the 'chat' icon in the panel" }, "popupTipNoPopups": { diff --git a/src/_locales/oc/messages.json b/src/_locales/oc/messages.json index 1b4b4c0d251de..d1a3fa188d502 100644 --- a/src/_locales/oc/messages.json +++ b/src/_locales/oc/messages.json @@ -1192,7 +1192,7 @@ "description": "Button text to navigate to the blocked page" }, "docblockedRedirectPrompt": { - "message": "The blocked page wants to redirect to another site. If you choose to proceed, you will navigate directly to: {{url}}", + "message": "La pagina blocada vòl redirigir cap a un autre site. Se causissètz de perseguir, aniretz dirèctament a : {{url}}", "description": "Text warning about an incoming redirect" }, "cloudPush": { From 89e44131a0a450f3ecbbfb2bf82887ea69fc6e44 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sat, 21 Dec 2024 08:57:12 -0500 Subject: [PATCH 0560/1099] [mv3] Enable OpenPhish ruleset by default As discussed with filter list maintainers. --- platform/mv3/make-rulesets.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/platform/mv3/make-rulesets.js b/platform/mv3/make-rulesets.js index f2c62671f359e..0e527f413eb70 100644 --- a/platform/mv3/make-rulesets.js +++ b/platform/mv3/make-rulesets.js @@ -1300,9 +1300,9 @@ async function main() { }); await rulesetFromURLs({ id: 'openphish.domains', - name: 'Openphish Domain Blocklist', + name: 'OpenPhish Domain Blocklist', group: 'malware', - enabled: false, + enabled: true, urls: [ 'https://raw.githubusercontent.com/stephenhawk8054/openphish-adblock/refs/heads/main/filters_init_domains.txt', ], From 7494eaf621c60dc8ddb901deacc74af5cc329380 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sat, 21 Dec 2024 11:27:03 -0500 Subject: [PATCH 0561/1099] Improve `parse-properties-to-match` scriptlet helper Related issue: https://github.com/uBlockOrigin/uBlock-discussions/discussions/831#discussioncomment-11637436 If the property name contains unexpected characters, assume that the `:` is not a separator. --- src/js/resources/scriptlets.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/js/resources/scriptlets.js b/src/js/resources/scriptlets.js index a460f3fb7533f..36d52192346d6 100644 --- a/src/js/resources/scriptlets.js +++ b/src/js/resources/scriptlets.js @@ -598,8 +598,12 @@ function parsePropertiesToMatch(propsToMatch, implicit = '') { if ( propsToMatch === undefined || propsToMatch === '' ) { return needles; } const options = { canNegate: true }; for ( const needle of safe.String_split.call(propsToMatch, /\s+/) ) { - const [ prop, pattern ] = safe.String_split.call(needle, ':'); + let [ prop, pattern ] = safe.String_split.call(needle, ':'); if ( prop === '' ) { continue; } + if ( pattern !== undefined && /[^$\w -]/.test(prop) ) { + prop = `${prop}:${pattern}`; + pattern = undefined; + } if ( pattern !== undefined ) { needles.set(prop, safe.initPattern(pattern, options)); } else if ( implicit !== '' ) { From 43d157112dae9f81799326d54a1d26c450353f27 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sat, 21 Dec 2024 11:31:33 -0500 Subject: [PATCH 0562/1099] Update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 53f0c70d5cf9c..53baa32a5eb4e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,4 @@ +- [Improve `parse-properties-to-match` scriptlet helper](https://github.com/gorhill/uBlock/commit/7494eaf621) - [Improve `href-sanitizer` scriptlet](https://github.com/gorhill/uBlock/commit/9bf8d53ebe) - [Improve quote usage in filter options and scriptlets](https://github.com/gorhill/uBlock/commit/8ba71f09d7) - [Improve `trusted-suppress-native-method` scriptlet](https://github.com/gorhill/uBlock/commit/7ed3470844) From dd281d9fc5f288f077dee4686cc9dd75e12948e0 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sat, 21 Dec 2024 11:32:03 -0500 Subject: [PATCH 0563/1099] New revision for dev build --- dist/version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dist/version b/dist/version index ac220a20dcbdb..428c38dd31ea8 100644 --- a/dist/version +++ b/dist/version @@ -1 +1 @@ -1.61.3.10 \ No newline at end of file +1.61.3.11 \ No newline at end of file From 6c228a8bfdcfc14140cdd3967270df28598c1aaf Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sat, 21 Dec 2024 11:46:44 -0500 Subject: [PATCH 0564/1099] Make Firefox dev build auto-update --- dist/firefox/updates.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dist/firefox/updates.json b/dist/firefox/updates.json index ec5113be34d48..911f60e54eaf5 100644 --- a/dist/firefox/updates.json +++ b/dist/firefox/updates.json @@ -3,9 +3,9 @@ "uBlock0@raymondhill.net": { "updates": [ { - "version": "1.61.3.10", + "version": "1.61.3.11", "browser_specific_settings": { "gecko": { "strict_min_version": "78.0" } }, - "update_link": "https://github.com/gorhill/uBlock/releases/download/1.61.3b10/uBlock0_1.61.3b10.firefox.signed.xpi" + "update_link": "https://github.com/gorhill/uBlock/releases/download/1.61.3b11/uBlock0_1.61.3b11.firefox.signed.xpi" } ] } From fd481a37bffa705455711718df96127d83fc1313 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sun, 22 Dec 2024 10:36:47 -0500 Subject: [PATCH 0565/1099] Import translation work from https://crowdin.com/project/ublock --- platform/mv3/description/webstore.el.txt | 2 +- platform/mv3/extension/_locales/el/messages.json | 14 +++++++------- src/_locales/el/messages.json | 4 ++-- src/_locales/zh_TW/messages.json | 12 ++++++------ 4 files changed, 16 insertions(+), 16 deletions(-) diff --git a/platform/mv3/description/webstore.el.txt b/platform/mv3/description/webstore.el.txt index 73922f351e7e0..28dcd568e049d 100644 --- a/platform/mv3/description/webstore.el.txt +++ b/platform/mv3/description/webstore.el.txt @@ -1,4 +1,4 @@ -Το uBO Lite (uBOL) είναι ένα πρόσθετο φραγής περιεχομένου που *δεν απαιτεί δικαιώματα* και βασίζεται στο MV3. +Το uBO Lite (uBOL) είναι πρόσθετο φραγής περιεχομένου που *δεν απαιτεί δικαιώματα* και βασίζεται στο MV3. Το προεπιλεγμένο σύνολο κανόνων αντιστοιχεί στο προεπιλεγμένο σύνολο φίλτρων του uBlock Origin: diff --git a/platform/mv3/extension/_locales/el/messages.json b/platform/mv3/extension/_locales/el/messages.json index 5c69bd869de45..9e4a7414687d4 100644 --- a/platform/mv3/extension/_locales/el/messages.json +++ b/platform/mv3/extension/_locales/el/messages.json @@ -36,7 +36,7 @@ "description": "Tooltip used for the 'chat' icon in the panel" }, "popupTipDashboard": { - "message": "Ανοίξτε τον πίνακα ελέγχου", + "message": "Άνοιγμα του πίνακα εργαλείων", "description": "English: Click to open the dashboard" }, "popupMoreButton": { @@ -168,7 +168,7 @@ "description": "Text for button which open an external webpage in Support pane" }, "firstRunSectionLabel": { - "message": "Καλώς ήρθατε", + "message": "Καλώς ορίσατε", "description": "The header text for the welcome message section" }, "firstRunDescription": { @@ -232,11 +232,11 @@ "description": "Label for a checkbox in the options page" }, "enableStrictBlockLabel": { - "message": "Enable strict blocking", + "message": "Ενεργοποίηση αυστηρής φραγής", "description": "Label for a checkbox in the options page" }, "enableStrictBlockLegend": { - "message": "Navigation to potentially undesirable sites will be blocked, and you will be offered the option to proceed.", + "message": "Θα απετραπεί η πρόσβαση σε πιθανά ανεπιθύμητους ιστοτόπους. Θα σας προσφερθεί η επιλογή να συνεχίσετε.", "description": "Label for a checkbox in the options page" }, "findListsPlaceholder": { @@ -244,11 +244,11 @@ "description": "Placeholder for the input field used to find lists" }, "strictblockTitle": { - "message": "Page blocked", + "message": "Αποκλησμένη σελίδα", "description": "Webpage title for the strict-blocked page" }, "strictblockSentence1": { - "message": "uBO Lite has prevented the following page from loading:", + "message": "Το uBO Lite εμπόδισε τη φόρτωση στης παρακάτω σελίδας:", "description": "Sentence used in the strict-blocked page" }, "strictblockReasonSentence1": { @@ -260,7 +260,7 @@ "description": "Text warning about an incoming redirect" }, "strictblockNoParamsPrompt": { - "message": "without parameters", + "message": "χωρίς παραμέτρους", "description": "Label to be used for the parameter-less URL" }, "strictblockBack": { diff --git a/src/_locales/el/messages.json b/src/_locales/el/messages.json index aefa2c6a580ad..9ce246bf5d627 100644 --- a/src/_locales/el/messages.json +++ b/src/_locales/el/messages.json @@ -960,7 +960,7 @@ "description": "Second paragraph of 'Troubleshooting Information' section in Support pane" }, "supportS6H": { - "message": "Αναφέρετε ένα πρόβλημα φίλτρου", + "message": "Αναφέρετε πρόβλημα φίλτρου", "description": "Header of 'Report a filter issue' section in Support pane" }, "supportS6P1S1": { @@ -1064,7 +1064,7 @@ "description": "Shown in the About pane" }, "aboutCDNsInfo": { - "message": "Ένα τυχαία επιλεγμένο CDN χρησιμοποιείται όταν πρέπει να ενημερωθεί μια λίστα φίλτρων", + "message": "Χρησιμοποιείται τυχαία-επιλεγμένο CDN όταν πρέπει να ενημερωθεί μια λίστα φίλτρων", "description": "Shown in the About pane" }, "aboutBackupDataButton": { diff --git a/src/_locales/zh_TW/messages.json b/src/_locales/zh_TW/messages.json index 92923a9e122c8..ce98855f5a1b5 100644 --- a/src/_locales/zh_TW/messages.json +++ b/src/_locales/zh_TW/messages.json @@ -468,7 +468,7 @@ "description": "English: Apply changes" }, "3pGroupDefault": { - "message": "內置", + "message": "內建", "description": "Filter lists section name" }, "3pGroupAds": { @@ -480,7 +480,7 @@ "description": "Filter lists section name" }, "3pGroupMalware": { - "message": "惡意軟體保護、保安", + "message": "惡意軟體防護及安全性", "description": "Filter lists section name" }, "3pGroupSocial": { @@ -940,7 +940,7 @@ "description": "Third paragraph of 'Filter issues' section in Support pane" }, "supportS4H": { - "message": "錯誤報告", + "message": "錯誤回報", "description": "Header of 'Bug report' section in Support pane" }, "supportS4P1": { @@ -1036,7 +1036,7 @@ "description": "" }, "aboutCode": { - "message": "源碼(GPLv3)", + "message": "原始碼(GPLv3)", "description": "English: Source code (GPLv3)" }, "aboutContributors": { @@ -1044,7 +1044,7 @@ "description": "English: Contributors" }, "aboutSourceCode": { - "message": "源碼", + "message": "原始碼", "description": "Link text to source code repo" }, "aboutTranslations": { @@ -1248,7 +1248,7 @@ "description": "A context menu entry, present when large media elements have been blocked on the current site" }, "contextMenuViewSource": { - "message": "檢視源碼…", + "message": "檢視原始碼…", "description": "A context menu entry, to view the source code of the target resource" }, "shortcutCapturePlaceholder": { From 27a72b8eef6f1527f1e6b3c712f643c530044c6f Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Mon, 23 Dec 2024 09:57:57 -0500 Subject: [PATCH 0566/1099] [mv3] Hopefully this fixes Edge submission issue Submission to Edge Addons store fails with: ---- The following checks failed: Package acceptance validation error: Manifest file reference 'urlhaus.full' does not exist in the zip archive. (Note: File locations are case-sensitive) Package acceptance validation error: Manifest file reference 'openphish.domains' does not exist in the zip archive. (Note: File locations are case-sensitive) ---- Guessing the use of period in ruleset file path is causing the issue. --- platform/mv3/make-rulesets.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/platform/mv3/make-rulesets.js b/platform/mv3/make-rulesets.js index 0e527f413eb70..2d9c2d380c4f7 100644 --- a/platform/mv3/make-rulesets.js +++ b/platform/mv3/make-rulesets.js @@ -1287,7 +1287,7 @@ async function main() { // Handpicked rulesets from abroad await rulesetFromURLs({ - id: 'urlhaus.full', + id: 'urlhaus-full', name: 'Malicious URL Blocklist', group: 'malware', enabled: true, @@ -1299,7 +1299,7 @@ async function main() { homeURL: 'https://gitlab.com/malware-filter/urlhaus-filter', }); await rulesetFromURLs({ - id: 'openphish.domains', + id: 'openphish-domains', name: 'OpenPhish Domain Blocklist', group: 'malware', enabled: true, From a7aa755f1884acbf4a34e947c90129a3ddd0fa2f Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Tue, 24 Dec 2024 08:59:28 -0500 Subject: [PATCH 0567/1099] Improve `urlskip=` filter option New step: `#`, to extract the hash part of a URL. Example, URL: https://example.com/#aHR0cHM6Ly9naXRodWIuY29tL3VCbG9ja09yaWdpbi8= Filter: ||example.com^$urlskip=# -base64 As a result, navigate to https://github.com/uBlockOrigin/ --- src/js/urlskip.js | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/js/urlskip.js b/src/js/urlskip.js index 31af869ad680b..66109f301afa9 100644 --- a/src/js/urlskip.js +++ b/src/js/urlskip.js @@ -39,6 +39,8 @@ * `&i`: extract the name of the parameter at position `i` as the current * string. The position is 1-based. * + * `#`: extract the hash as the current string. + * * `/.../`: extract the first capture group of a regex as the current string. * * `+https`: prepend the current string with `https://`. @@ -77,6 +79,12 @@ export function urlSkip(url, blocked, steps, directive = {}) { for ( const step of steps ) { const urlin = urlout; const c0 = step.charCodeAt(0); + // Extract from hash + if ( c0 === 0x23 && step === '#' ) { // # + const pos = urlin.indexOf('#'); + urlout = pos !== -1 ? urlin.slice(pos+1) : ''; + continue; + } // Extract from URL parameter name at position i if ( c0 === 0x26 ) { // & const i = (parseInt(step.slice(1)) || 0) - 1; @@ -88,14 +96,14 @@ export function urlSkip(url, blocked, steps, directive = {}) { continue; } // Enforce https - if ( c0 === 0x2B && step === '+https' ) { + if ( c0 === 0x2B && step === '+https' ) { // + const s = urlin.replace(/^https?:\/\//, ''); if ( /^[\w-]:\/\//.test(s) ) { return; } urlout = `https://${s}`; continue; } // Decode - if ( c0 === 0x2D ) { + if ( c0 === 0x2D ) { // - // Base64 if ( step === '-base64' ) { urlout = self.atob(urlin); From 23134c2e45e4390e65e0cfa005dda4ec9158164c Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Tue, 24 Dec 2024 09:10:04 -0500 Subject: [PATCH 0568/1099] Update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 53baa32a5eb4e..83b9c3e8d9f20 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,4 @@ +- [Improve `urlskip=` filter option](https://github.com/gorhill/uBlock/commit/a7aa755f18) - [Improve `parse-properties-to-match` scriptlet helper](https://github.com/gorhill/uBlock/commit/7494eaf621) - [Improve `href-sanitizer` scriptlet](https://github.com/gorhill/uBlock/commit/9bf8d53ebe) - [Improve quote usage in filter options and scriptlets](https://github.com/gorhill/uBlock/commit/8ba71f09d7) From d4194353f89f3487f5cfed6f8c7635aca1df2d22 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Tue, 24 Dec 2024 09:10:31 -0500 Subject: [PATCH 0569/1099] New revision for release candidate --- dist/version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dist/version b/dist/version index 428c38dd31ea8..6812cb56f7cfa 100644 --- a/dist/version +++ b/dist/version @@ -1 +1 @@ -1.61.3.11 \ No newline at end of file +1.61.3.100 \ No newline at end of file From 14bed5679a6d7191458f894bf6c04afaa946c6d9 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Tue, 24 Dec 2024 09:16:16 -0500 Subject: [PATCH 0570/1099] Make Firefox dev build auto-update --- dist/firefox/updates.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dist/firefox/updates.json b/dist/firefox/updates.json index 911f60e54eaf5..e3975f1514178 100644 --- a/dist/firefox/updates.json +++ b/dist/firefox/updates.json @@ -3,9 +3,9 @@ "uBlock0@raymondhill.net": { "updates": [ { - "version": "1.61.3.11", + "version": "1.61.3.100", "browser_specific_settings": { "gecko": { "strict_min_version": "78.0" } }, - "update_link": "https://github.com/gorhill/uBlock/releases/download/1.61.3b11/uBlock0_1.61.3b11.firefox.signed.xpi" + "update_link": "https://github.com/gorhill/uBlock/releases/download/1.61.3rc0/uBlock0_1.61.3rc0.firefox.signed.xpi" } ] } From 3df22b1791b1a43b5070470ef3a542ecf49a0184 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Tue, 24 Dec 2024 11:30:13 -0500 Subject: [PATCH 0571/1099] [mv3] Change extension id --- platform/mv3/firefox/manifest.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform/mv3/firefox/manifest.json b/platform/mv3/firefox/manifest.json index 703f652b28ec0..75e0f028676ad 100644 --- a/platform/mv3/firefox/manifest.json +++ b/platform/mv3/firefox/manifest.json @@ -15,7 +15,7 @@ }, "browser_specific_settings": { "gecko": { - "id": "uBOLite@raymondhill.net", + "id": "uBOLiteRedux@raymondhill.net", "strict_min_version": "127.0" }, "gecko_android": { From f7fa3139af3c0aa2b3b39c278282593865b38d05 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Tue, 24 Dec 2024 12:43:53 -0500 Subject: [PATCH 0572/1099] Need to name as per validator --- platform/mv3/firefox/manifest.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform/mv3/firefox/manifest.json b/platform/mv3/firefox/manifest.json index 75e0f028676ad..2ee26567352ad 100644 --- a/platform/mv3/firefox/manifest.json +++ b/platform/mv3/firefox/manifest.json @@ -35,7 +35,7 @@ "128": "img/icon_128.png" }, "manifest_version": 3, - "name": "__MSG_extName__", + "name": "uBO Lite", "options_ui": { "open_in_tab": true, "page": "dashboard.html" From 42638f0e446e4535272ed6d46911e3320336fc84 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Wed, 25 Dec 2024 14:26:00 -0500 Subject: [PATCH 0573/1099] Fix regression in `href-sanitizer` scriptlet Related feedback: https://github.com/uBlockOrigin/uBlock-issues/issues/2531#issuecomment-2561968581 Regression from: https://github.com/gorhill/uBlock/commit/9bf8d53ebe --- src/js/resources/href-sanitizer.js | 28 +++++++++++++++++++++------- 1 file changed, 21 insertions(+), 7 deletions(-) diff --git a/src/js/resources/href-sanitizer.js b/src/js/resources/href-sanitizer.js index c445f024cd83f..82a9a95c831a4 100644 --- a/src/js/resources/href-sanitizer.js +++ b/src/js/resources/href-sanitizer.js @@ -91,6 +91,20 @@ function hrefSanitizer( } return ''; }; + const extractParam = (href, source) => { + if ( Boolean(source) === false ) { return href; } + const recursive = source.includes('?', 1); + const end = recursive ? source.indexOf('?', 1) : source.length; + try { + const url = new URL(href, document.location); + let value = url.searchParams.get(source.slice(1, end)); + if ( value === null ) { return href } + if ( recursive ) { return extractParam(value, source.slice(end)); } + return value; + } catch(x) { + } + return href; + }; const extractURL = (elem, source) => { if ( /^\[.*\]$/.test(source) ) { return elem.getAttribute(source.slice(1,-1).trim()) || ''; @@ -101,13 +115,13 @@ function hrefSanitizer( .replace(/[^\x21-\x7e]+$/, '') // remove trailing invalid characters ; } - if ( source.startsWith('?') ) { - const steps = source.replace(/(\S)\?/g, '\\1?').split(/\s+/); - const url = urlSkip(elem.href, false, steps); - if ( url === undefined ) { return; } - return url.replace(/ /g, '%20'); - } - return ''; + if ( source.startsWith('?') === false ) { return ''; } + const steps = source.replace(/(\S)\?/g, '\\1?').split(/\s+/); + const url = steps.length === 1 + ? extractParam(elem.href, source) + : urlSkip(elem.href, false, steps); + if ( url === undefined ) { return; } + return url.replace(/ /g, '%20'); }; const sanitize = ( ) => { let elems = []; From a4677f160fad1e7a1879e2ac226e74c7c3fdcdd8 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Wed, 25 Dec 2024 14:28:24 -0500 Subject: [PATCH 0574/1099] New revision for release candidate --- dist/version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dist/version b/dist/version index 6812cb56f7cfa..57d9230511f76 100644 --- a/dist/version +++ b/dist/version @@ -1 +1 @@ -1.61.3.100 \ No newline at end of file +1.61.3.101 \ No newline at end of file From dd5a79e6eb82c7557a256f61567a8c504649e332 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Wed, 25 Dec 2024 14:30:48 -0500 Subject: [PATCH 0575/1099] Make Firefox dev build auto-update --- dist/firefox/updates.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dist/firefox/updates.json b/dist/firefox/updates.json index e3975f1514178..ea0bba569d199 100644 --- a/dist/firefox/updates.json +++ b/dist/firefox/updates.json @@ -3,9 +3,9 @@ "uBlock0@raymondhill.net": { "updates": [ { - "version": "1.61.3.100", + "version": "1.61.3.101", "browser_specific_settings": { "gecko": { "strict_min_version": "78.0" } }, - "update_link": "https://github.com/gorhill/uBlock/releases/download/1.61.3rc0/uBlock0_1.61.3rc0.firefox.signed.xpi" + "update_link": "https://github.com/gorhill/uBlock/releases/download/1.61.3rc1/uBlock0_1.61.3rc1.firefox.signed.xpi" } ] } From 3ae28423b9fb7bec3ea01a7d858524699de15174 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Thu, 26 Dec 2024 08:05:10 -0500 Subject: [PATCH 0576/1099] Remove pointless `instructionURL` from AdGuard's lists There is no special extra steps to be taken when enabling an AdGuard lists, there is no point to have `instructionURL` for these. Related issue: https://github.com/uBlockOrigin/uBlock-issues/issues/3502 --- assets/assets.dev.json | 35 ++++++++++++----------------------- assets/assets.json | 41 ++++++++++++++--------------------------- 2 files changed, 26 insertions(+), 50 deletions(-) diff --git a/assets/assets.dev.json b/assets/assets.dev.json index aa529d23614af..fcdffc291d27b 100644 --- a/assets/assets.dev.json +++ b/assets/assets.dev.json @@ -136,8 +136,7 @@ "title": "AdGuard – Ads", "tags": "ads", "contentURL": "https://filters.adtidy.org/extension/ublock/filters/2_without_easylist.txt", - "supportURL": "https://github.com/AdguardTeam/AdguardFilters#adguard-filters", - "instructionURL": "https://adguard.com/kb/general/ad-filtering/adguard-filters/" + "supportURL": "https://github.com/AdguardTeam/AdguardFilters#adguard-filters" }, "adguard-mobile": { "content": "filters", @@ -147,8 +146,7 @@ "tags": "ads mobile", "ua": "mobile", "contentURL": "https://filters.adtidy.org/extension/ublock/filters/11.txt", - "supportURL": "https://github.com/AdguardTeam/AdguardFilters#adguard-filters", - "instructionURL": "https://adguard.com/kb/general/ad-filtering/adguard-filters/" + "supportURL": "https://github.com/AdguardTeam/AdguardFilters#adguard-filters" }, "easylist": { "content": "filters", @@ -174,8 +172,7 @@ "title": "AdGuard URL Tracking Protection", "tags": "privacy", "contentURL": "https://filters.adtidy.org/extension/ublock/filters/17.txt", - "supportURL": "https://github.com/AdguardTeam/AdguardFilters#adguard-filters", - "instructionURL": "https://adguard.com/kb/general/ad-filtering/adguard-filters/" + "supportURL": "https://github.com/AdguardTeam/AdguardFilters#adguard-filters" }, "adguard-spyware": { "content": "filters", @@ -183,8 +180,7 @@ "off": true, "title": "AdGuard Tracking Protection", "contentURL": "https://filters.adtidy.org/extension/ublock/filters/3.txt", - "supportURL": "https://github.com/AdguardTeam/AdguardFilters#adguard-filters", - "instructionURL": "https://adguard.com/kb/general/ad-filtering/adguard-filters/" + "supportURL": "https://github.com/AdguardTeam/AdguardFilters#adguard-filters" }, "block-lan": { "content": "filters", @@ -255,8 +251,7 @@ "title": "AdGuard – Cookie Notices", "tags": "annoyances cookies", "contentURL": "https://filters.adtidy.org/extension/ublock/filters/18.txt", - "supportURL": "https://github.com/AdguardTeam/AdguardFilters#adguard-filters", - "instructionURL": "https://adguard.com/kb/general/ad-filtering/adguard-filters/" + "supportURL": "https://github.com/AdguardTeam/AdguardFilters#adguard-filters" }, "ublock-cookies-adguard": { "content": "filters", @@ -324,8 +319,7 @@ "title": "AdGuard – Social Widgets", "tags": "annoyances social", "contentURL": "https://filters.adtidy.org/extension/ublock/filters/4.txt", - "supportURL": "https://github.com/AdguardTeam/AdguardFilters#adguard-filters", - "instructionURL": "https://adguard.com/kb/general/ad-filtering/adguard-filters/" + "supportURL": "https://github.com/AdguardTeam/AdguardFilters#adguard-filters" }, "fanboy-social": { "content": "filters", @@ -367,8 +361,7 @@ "title": "AdGuard – Popup Overlays", "tags": "annoyances", "contentURL": "https://filters.adtidy.org/extension/ublock/filters/19.txt", - "supportURL": "https://github.com/AdguardTeam/AdguardFilters#adguard-filters", - "instructionURL": "https://adguard.com/kb/general/ad-filtering/adguard-filters/" + "supportURL": "https://github.com/AdguardTeam/AdguardFilters#adguard-filters" }, "adguard-mobile-app-banners": { "content": "filters", @@ -378,8 +371,7 @@ "title": "AdGuard – Mobile App Banners", "tags": "annoyances mobile", "contentURL": "https://filters.adtidy.org/extension/ublock/filters/20.txt", - "supportURL": "https://github.com/AdguardTeam/AdguardFilters#adguard-filters", - "instructionURL": "https://adguard.com/kb/general/ad-filtering/adguard-filters/" + "supportURL": "https://github.com/AdguardTeam/AdguardFilters#adguard-filters" }, "adguard-other-annoyances": { "content": "filters", @@ -389,8 +381,7 @@ "title": "AdGuard – Other Annoyances", "tags": "annoyances", "contentURL": "https://filters.adtidy.org/extension/ublock/filters/21.txt", - "supportURL": "https://github.com/AdguardTeam/AdguardFilters#adguard-filters", - "instructionURL": "https://adguard.com/kb/general/ad-filtering/adguard-filters/" + "supportURL": "https://github.com/AdguardTeam/AdguardFilters#adguard-filters" }, "adguard-widgets": { "content": "filters", @@ -400,8 +391,7 @@ "title": "AdGuard – Widgets", "tags": "annoyances", "contentURL": "https://filters.adtidy.org/extension/ublock/filters/22.txt", - "supportURL": "https://github.com/AdguardTeam/AdguardFilters#adguard-filters", - "instructionURL": "https://adguard.com/kb/general/ad-filtering/adguard-filters/" + "supportURL": "https://github.com/AdguardTeam/AdguardFilters#adguard-filters" }, "easylist-annoyances": { "content": "filters", @@ -772,7 +762,7 @@ "lang": "af fy nl", "contentURL": "https://filters.adtidy.org/extension/ublock/filters/8.txt", "cdnURLs": null, - "supportURL": "https://adguard.com/kb/general/ad-filtering/adguard-filters/" + "supportURL": "https://github.com/AdguardTeam/AdguardFilters#adguard-filters" }, "NOR-0": { "content": "filters", @@ -874,8 +864,7 @@ "tags": "ads aragonese basque catalan spanish español galician guarani portuguese português", "lang": "an ast ca cak es eu gl gn trs pt quz", "contentURL": "https://filters.adtidy.org/extension/ublock/filters/9.txt", - "supportURL": "https://github.com/AdguardTeam/AdguardFilters#adguard-filters", - "instructionURL": "https://adguard.com/kb/general/ad-filtering/adguard-filters/" + "supportURL": "https://github.com/AdguardTeam/AdguardFilters#adguard-filters" }, "SVN-0": { "content": "filters", diff --git a/assets/assets.json b/assets/assets.json index 6449dbe932434..6cd042ee2b783 100644 --- a/assets/assets.json +++ b/assets/assets.json @@ -136,8 +136,7 @@ "title": "AdGuard – Ads", "tags": "ads", "contentURL": "https://filters.adtidy.org/extension/ublock/filters/2_without_easylist.txt", - "supportURL": "https://github.com/AdguardTeam/AdguardFilters#adguard-filters", - "instructionURL": "https://adguard.com/kb/general/ad-filtering/adguard-filters/" + "supportURL": "https://github.com/AdguardTeam/AdguardFilters#adguard-filters" }, "adguard-mobile": { "content": "filters", @@ -147,8 +146,7 @@ "tags": "ads mobile", "ua": "mobile", "contentURL": "https://filters.adtidy.org/extension/ublock/filters/11.txt", - "supportURL": "https://github.com/AdguardTeam/AdguardFilters#adguard-filters", - "instructionURL": "https://adguard.com/kb/general/ad-filtering/adguard-filters/" + "supportURL": "https://github.com/AdguardTeam/AdguardFilters#adguard-filters" }, "easylist": { "content": "filters", @@ -174,8 +172,7 @@ "title": "AdGuard URL Tracking Protection", "tags": "privacy", "contentURL": "https://filters.adtidy.org/extension/ublock/filters/17.txt", - "supportURL": "https://github.com/AdguardTeam/AdguardFilters#adguard-filters", - "instructionURL": "https://adguard.com/kb/general/ad-filtering/adguard-filters/" + "supportURL": "https://github.com/AdguardTeam/AdguardFilters#adguard-filters" }, "adguard-spyware": { "content": "filters", @@ -183,8 +180,7 @@ "off": true, "title": "AdGuard Tracking Protection", "contentURL": "https://filters.adtidy.org/extension/ublock/filters/3.txt", - "supportURL": "https://github.com/AdguardTeam/AdguardFilters#adguard-filters", - "instructionURL": "https://adguard.com/kb/general/ad-filtering/adguard-filters/" + "supportURL": "https://github.com/AdguardTeam/AdguardFilters#adguard-filters" }, "block-lan": { "content": "filters", @@ -255,8 +251,7 @@ "title": "AdGuard – Cookie Notices", "tags": "annoyances cookies", "contentURL": "https://filters.adtidy.org/extension/ublock/filters/18.txt", - "supportURL": "https://github.com/AdguardTeam/AdguardFilters#adguard-filters", - "instructionURL": "https://adguard.com/kb/general/ad-filtering/adguard-filters/" + "supportURL": "https://github.com/AdguardTeam/AdguardFilters#adguard-filters" }, "ublock-cookies-adguard": { "content": "filters", @@ -324,8 +319,7 @@ "title": "AdGuard – Social Widgets", "tags": "annoyances social", "contentURL": "https://filters.adtidy.org/extension/ublock/filters/4.txt", - "supportURL": "https://github.com/AdguardTeam/AdguardFilters#adguard-filters", - "instructionURL": "https://adguard.com/kb/general/ad-filtering/adguard-filters/" + "supportURL": "https://github.com/AdguardTeam/AdguardFilters#adguard-filters" }, "fanboy-social": { "content": "filters", @@ -367,8 +361,7 @@ "title": "AdGuard – Popup Overlays", "tags": "annoyances", "contentURL": "https://filters.adtidy.org/extension/ublock/filters/19.txt", - "supportURL": "https://github.com/AdguardTeam/AdguardFilters#adguard-filters", - "instructionURL": "https://adguard.com/kb/general/ad-filtering/adguard-filters/" + "supportURL": "https://github.com/AdguardTeam/AdguardFilters#adguard-filters" }, "adguard-mobile-app-banners": { "content": "filters", @@ -378,8 +371,7 @@ "title": "AdGuard – Mobile App Banners", "tags": "annoyances mobile", "contentURL": "https://filters.adtidy.org/extension/ublock/filters/20.txt", - "supportURL": "https://github.com/AdguardTeam/AdguardFilters#adguard-filters", - "instructionURL": "https://adguard.com/kb/general/ad-filtering/adguard-filters/" + "supportURL": "https://github.com/AdguardTeam/AdguardFilters#adguard-filters" }, "adguard-other-annoyances": { "content": "filters", @@ -389,8 +381,7 @@ "title": "AdGuard – Other Annoyances", "tags": "annoyances", "contentURL": "https://filters.adtidy.org/extension/ublock/filters/21.txt", - "supportURL": "https://github.com/AdguardTeam/AdguardFilters#adguard-filters", - "instructionURL": "https://adguard.com/kb/general/ad-filtering/adguard-filters/" + "supportURL": "https://github.com/AdguardTeam/AdguardFilters#adguard-filters" }, "adguard-widgets": { "content": "filters", @@ -400,8 +391,7 @@ "title": "AdGuard – Widgets", "tags": "annoyances", "contentURL": "https://filters.adtidy.org/extension/ublock/filters/22.txt", - "supportURL": "https://github.com/AdguardTeam/AdguardFilters#adguard-filters", - "instructionURL": "https://adguard.com/kb/general/ad-filtering/adguard-filters/" + "supportURL": "https://github.com/AdguardTeam/AdguardFilters#adguard-filters" }, "easylist-annoyances": { "content": "filters", @@ -716,8 +706,7 @@ "tags": "ads japanese 日本語", "lang": "ja", "contentURL": "https://filters.adtidy.org/extension/ublock/filters/7.txt", - "supportURL": "https://github.com/AdguardTeam/AdguardFilters#adguard-filters", - "instructionURL": "https://adguard.com/kb/general/ad-filtering/adguard-filters/" + "supportURL": "https://github.com/AdguardTeam/AdguardFilters#adguard-filters" }, "KOR-1": { "content": "filters", @@ -772,7 +761,7 @@ "lang": "af fy nl", "contentURL": "https://filters.adtidy.org/extension/ublock/filters/8.txt", "cdnURLs": null, - "supportURL": "https://adguard.com/kb/general/ad-filtering/adguard-filters/" + "supportURL": "https://github.com/AdguardTeam/AdguardFilters#adguard-filters" }, "NOR-0": { "content": "filters", @@ -874,8 +863,7 @@ "tags": "ads aragonese basque catalan spanish español galician guarani portuguese português", "lang": "an ast ca cak es eu gl gn trs pt quz", "contentURL": "https://filters.adtidy.org/extension/ublock/filters/9.txt", - "supportURL": "https://github.com/AdguardTeam/AdguardFilters#adguard-filters", - "instructionURL": "https://adguard.com/kb/general/ad-filtering/adguard-filters/" + "supportURL": "https://github.com/AdguardTeam/AdguardFilters#adguard-filters" }, "SVN-0": { "content": "filters", @@ -919,8 +907,7 @@ "tags": "ads turkish türkçe", "lang": "tr", "contentURL": "https://filters.adtidy.org/extension/ublock/filters/13.txt", - "supportURL": "https://github.com/AdguardTeam/AdguardFilters#adguard-filters", - "instructionURL": "https://adguard.com/kb/general/ad-filtering/adguard-filters/" + "supportURL": "https://github.com/AdguardTeam/AdguardFilters#adguard-filters" }, "VIE-1": { "content": "filters", From cb6c11ab6f2a8580737c278ada9d55446af285fa Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Thu, 26 Dec 2024 08:10:13 -0500 Subject: [PATCH 0577/1099] Improve `trusted-suppress-native-method` scriptlet As per AdGuard's documentation `/.../` is a valid matcher, to match a string argument against a regex pattern. --- src/js/resources/scriptlets.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/js/resources/scriptlets.js b/src/js/resources/scriptlets.js index 36d52192346d6..75beb5450e7f9 100644 --- a/src/js/resources/scriptlets.js +++ b/src/js/resources/scriptlets.js @@ -3313,6 +3313,9 @@ function trustedSuppressNativeMethod( if ( /^".*"$/.test(v) ) { return { type: 'pattern', re: safe.patternToRegex(v.slice(1, -1)) }; } + if ( /^\/.+\/$/.test(v) ) { + return { type: 'pattern', re: safe.patternToRegex(v) }; + } if ( v === 'false' ) { return { type: 'exact', value: false }; } From 19f22c438e6aa0086305a71c75f4101079bf24e3 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Thu, 26 Dec 2024 11:58:34 -0500 Subject: [PATCH 0578/1099] Remove pointless instructionURL from AdGuard's lists There is no special extra steps to be taken when enabling an AdGuard lists, there is no point to have `instructionURL` for these. Related issue: https://github.com/uBlockOrigin/uBlock-issues/issues/3502 --- assets/assets.dev.json | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/assets/assets.dev.json b/assets/assets.dev.json index fcdffc291d27b..669289b0824f5 100644 --- a/assets/assets.dev.json +++ b/assets/assets.dev.json @@ -908,8 +908,7 @@ "tags": "ads turkish türkçe", "lang": "tr", "contentURL": "https://filters.adtidy.org/extension/ublock/filters/13.txt", - "supportURL": "https://github.com/AdguardTeam/AdguardFilters#adguard-filters", - "instructionURL": "https://adguard.com/kb/general/ad-filtering/adguard-filters/" + "supportURL": "https://github.com/AdguardTeam/AdguardFilters#adguard-filters" }, "UKR-0": { "content": "filters", From dd40dbdd963d06a6bebff4daa2071d2bd9c7e797 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Thu, 26 Dec 2024 18:46:00 -0500 Subject: [PATCH 0579/1099] Update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 83b9c3e8d9f20..8a6956ae41e15 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,4 @@ +- [Improve `trusted-suppress-native-method` scriptlet](https://github.com/gorhill/uBlock/commit/cb6c11ab6f) - [Improve `urlskip=` filter option](https://github.com/gorhill/uBlock/commit/a7aa755f18) - [Improve `parse-properties-to-match` scriptlet helper](https://github.com/gorhill/uBlock/commit/7494eaf621) - [Improve `href-sanitizer` scriptlet](https://github.com/gorhill/uBlock/commit/9bf8d53ebe) From 9032afef102287feb4915d8f6a699e382118fbb9 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Thu, 26 Dec 2024 18:46:24 -0500 Subject: [PATCH 0580/1099] New revision for dev build --- dist/version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dist/version b/dist/version index 57d9230511f76..bf3d825d534fa 100644 --- a/dist/version +++ b/dist/version @@ -1 +1 @@ -1.61.3.101 \ No newline at end of file +1.61.3.102 \ No newline at end of file From 4015e7f7727e40e0802c1acf60fb05e230cf93af Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Thu, 26 Dec 2024 18:50:45 -0500 Subject: [PATCH 0581/1099] Make Firefox dev build auto-update --- dist/firefox/updates.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dist/firefox/updates.json b/dist/firefox/updates.json index ea0bba569d199..944a20ab79a93 100644 --- a/dist/firefox/updates.json +++ b/dist/firefox/updates.json @@ -3,9 +3,9 @@ "uBlock0@raymondhill.net": { "updates": [ { - "version": "1.61.3.101", + "version": "1.61.3.102", "browser_specific_settings": { "gecko": { "strict_min_version": "78.0" } }, - "update_link": "https://github.com/gorhill/uBlock/releases/download/1.61.3rc1/uBlock0_1.61.3rc1.firefox.signed.xpi" + "update_link": "https://github.com/gorhill/uBlock/releases/download/1.61.3rc2/uBlock0_1.61.3rc2.firefox.signed.xpi" } ] } From c92a5182185ba11dd54c2d03d409694a2838cf78 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Thu, 26 Dec 2024 19:07:26 -0500 Subject: [PATCH 0582/1099] Fix deserialization of ArrayBuffer shared by multiple TypedArrays Deserialization failed to restore proper ArrayBuffer when used by multiple TypedArrays. This didn't affect uBO since this specific code path was never taken. --- src/js/s14e-serializer.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/js/s14e-serializer.js b/src/js/s14e-serializer.js index 8b1850f136fe9..f88e5b5e2f593 100644 --- a/src/js/s14e-serializer.js +++ b/src/js/s14e-serializer.js @@ -851,10 +851,11 @@ const _deserialize = ( ) => { case I_DATAVIEW: { const byteOffset = deserializeLargeUint(); const length = deserializeLargeUint(); + const ref = refCounter++; const arrayBuffer = _deserialize(); const ctor = toArrayBufferViewConstructor[`${type}`]; const out = new ctor(arrayBuffer, byteOffset, length); - readRefs.set(refCounter++, out); + readRefs.set(ref, out); return out; } default: From 05c5f369211a95f904c4a4d7dcf8ad7e5c516fe6 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Thu, 26 Dec 2024 19:18:44 -0500 Subject: [PATCH 0583/1099] Update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8a6956ae41e15..cd568c622a2a7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,4 @@ +- [Fix deserialization of ArrayBuffer shared by multiple TypedArrays](https://github.com/gorhill/uBlock/commit/c92a518218) - [Improve `trusted-suppress-native-method` scriptlet](https://github.com/gorhill/uBlock/commit/cb6c11ab6f) - [Improve `urlskip=` filter option](https://github.com/gorhill/uBlock/commit/a7aa755f18) - [Improve `parse-properties-to-match` scriptlet helper](https://github.com/gorhill/uBlock/commit/7494eaf621) From e3757e32a5b18eb6e41eedbd8fa2882a26b6f572 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Thu, 26 Dec 2024 19:19:06 -0500 Subject: [PATCH 0584/1099] New revision for release candidate --- dist/version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dist/version b/dist/version index bf3d825d534fa..403a62b48e450 100644 --- a/dist/version +++ b/dist/version @@ -1 +1 @@ -1.61.3.102 \ No newline at end of file +1.61.3.103 \ No newline at end of file From b856a2767d1c9acaa6e3803df819fbfd01d47b74 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Thu, 26 Dec 2024 19:26:19 -0500 Subject: [PATCH 0585/1099] Make Firefox dev build auto-update --- dist/firefox/updates.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dist/firefox/updates.json b/dist/firefox/updates.json index 944a20ab79a93..47f87e3b71a08 100644 --- a/dist/firefox/updates.json +++ b/dist/firefox/updates.json @@ -3,9 +3,9 @@ "uBlock0@raymondhill.net": { "updates": [ { - "version": "1.61.3.102", + "version": "1.61.3.103", "browser_specific_settings": { "gecko": { "strict_min_version": "78.0" } }, - "update_link": "https://github.com/gorhill/uBlock/releases/download/1.61.3rc2/uBlock0_1.61.3rc2.firefox.signed.xpi" + "update_link": "https://github.com/gorhill/uBlock/releases/download/1.61.3rc3/uBlock0_1.61.3rc3.firefox.signed.xpi" } ] } From b8678d22eaefde33d3ad22c73f3752fcbba6ea41 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Fri, 27 Dec 2024 08:34:41 -0500 Subject: [PATCH 0586/1099] Remove pointless instructionURL from AdGuard's lists There is no special extra steps to be taken when enabling an AdGuard lists, there is no point to have `instructionURL` for these. Related issue: https://github.com/uBlockOrigin/uBlock-issues/issues/3502 --- assets/assets.dev.json | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/assets/assets.dev.json b/assets/assets.dev.json index 669289b0824f5..861a99ebe3857 100644 --- a/assets/assets.dev.json +++ b/assets/assets.dev.json @@ -706,8 +706,7 @@ "tags": "ads japanese 日本語", "lang": "ja", "contentURL": "https://filters.adtidy.org/extension/ublock/filters/7.txt", - "supportURL": "https://github.com/AdguardTeam/AdguardFilters#adguard-filters", - "instructionURL": "https://adguard.com/kb/general/ad-filtering/adguard-filters/" + "supportURL": "https://github.com/AdguardTeam/AdguardFilters#adguard-filters" }, "KOR-1": { "content": "filters", From 61922da24b15a5f9e727e20f51675556d07acf2f Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sun, 29 Dec 2024 16:38:56 -0500 Subject: [PATCH 0587/1099] [mv3] Extend strict-blocking coverage; improve URL-skip behavior This extends coverage of strict-blocking to pattern-based filters with `doc` filter option. When proceeding with a URL-skip URL present, no temporary bypass will be created when the "Don't warn me again about this site" is left unchecked. The idea is to avoid the intermediate redirects if we navigate again on the same strict-blocked site, while a temporary bypass would prevent this. uBO's "Badware risks" list has been spinned off as its own list. The idea is that should a site be strict-blocked from that list, we would want to know the strict-block is due to the "Badware risks" list. --- platform/mv3/chromium/manifest.json | 13 +- platform/mv3/extension/js/background.js | 9 +- platform/mv3/extension/js/filter-lists.js | 3 +- platform/mv3/extension/js/ruleset-manager.js | 318 +++++++++--------- platform/mv3/extension/js/strictblock.js | 84 +++-- platform/mv3/{ => extension}/strictblock.html | 0 platform/mv3/firefox/manifest.json | 12 +- platform/mv3/make-rulesets.js | 258 ++++++++++---- src/js/static-dnr-filtering.js | 174 +++++----- 9 files changed, 531 insertions(+), 340 deletions(-) rename platform/mv3/{ => extension}/strictblock.html (100%) diff --git a/platform/mv3/chromium/manifest.json b/platform/mv3/chromium/manifest.json index 72760301c5a32..c4d8e96a3147e 100644 --- a/platform/mv3/chromium/manifest.json +++ b/platform/mv3/chromium/manifest.json @@ -41,5 +41,16 @@ "storage": { "managed_schema": "managed_storage.json" }, - "version": "1.0" + "version": "1.0", + "web_accessible_resources": [ + { + "resources": [ + "/strictblock.html" + ], + "matches": [ + "" + ], + "use_dynamic_url": true + } + ] } diff --git a/platform/mv3/extension/js/background.js b/platform/mv3/extension/js/background.js index ce8c0ea93bd10..9577eeea6cade 100644 --- a/platform/mv3/extension/js/background.js +++ b/platform/mv3/extension/js/background.js @@ -53,6 +53,7 @@ import { patchDefaultRulesets, setStrictBlockMode, updateDynamicRules, + updateSessionRules, } from './ruleset-manager.js'; import { @@ -398,8 +399,12 @@ async function start() { await enableRulesets(rulesetConfig.enabledRulesets); // We need to update the regex rules only when ruleset version changes. - if ( isNewVersion && rulesetsUpdated === false ) { - updateDynamicRules(); + if ( rulesetsUpdated === false ) { + if ( isNewVersion ) { + updateDynamicRules(); + } else if ( process.wakeupRun === false ) { + updateSessionRules(); + } } // Permissions may have been removed while the extension was disabled diff --git a/platform/mv3/extension/js/filter-lists.js b/platform/mv3/extension/js/filter-lists.js index 2356d57ce13d7..b0e85e148d24f 100644 --- a/platform/mv3/extension/js/filter-lists.js +++ b/platform/mv3/extension/js/filter-lists.js @@ -216,7 +216,8 @@ export function renderFilterLists(rulesetData) { [ 'default', rulesetDetails.filter(ruleset => - ruleset.id === 'default' + ruleset.id === 'default' || + ruleset.group === 'default' ), ], [ 'malware', diff --git a/platform/mv3/extension/js/ruleset-manager.js b/platform/mv3/extension/js/ruleset-manager.js index 935cf2dc6b2c3..6667b6deaef61 100644 --- a/platform/mv3/extension/js/ruleset-manager.js +++ b/platform/mv3/extension/js/ruleset-manager.js @@ -41,10 +41,18 @@ import { ubolLog } from './debug.js'; /******************************************************************************/ -const STRICTBLOCK_BASE_RULE_ID = 7000000; const TRUSTED_DIRECTIVE_BASE_RULE_ID = 8000000; +const STRICTBLOCK_PRIORITY = 29; -let dynamicRuleId = 1; +/******************************************************************************/ + +const isStrictBlockRule = rule => { + if ( rule.priority !== STRICTBLOCK_PRIORITY ) { return false; } + if ( rule.action.type !== 'redirect' ) { return false; } + const substitution = rule.action.redirect.regexSubstitution; + return substitution !== undefined && + substitution.includes('/strictblock.'); +}; /******************************************************************************/ @@ -106,7 +114,15 @@ pruneInvalidRegexRules.validated = new Map(); /******************************************************************************/ -async function updateRegexRules(toAdd) { +async function updateRegexRules(currentRules, addRules, removeRuleIds) { + // Remove existing regex-related block rules + for ( const rule of currentRules ) { + const { type } = rule.action; + if ( type !== 'block' && type !== 'allow' ) { continue; } + if ( rule.condition.regexFilter === undefined ) { continue; } + removeRuleIds.push(rule.id); + } + const rulesetDetails = await getEnabledRulesetsDetails(); // Fetch regexes for all enabled rulesets @@ -122,7 +138,6 @@ async function updateRegexRules(toAdd) { for ( const rules of regexRulesets ) { if ( Array.isArray(rules) === false ) { continue; } for ( const rule of rules ) { - rule.id = dynamicRuleId++; allRules.push(rule); } } @@ -132,12 +147,19 @@ async function updateRegexRules(toAdd) { if ( validRules.length === 0 ) { return; } ubolLog(`Add ${validRules.length} DNR regex rules`); - toAdd.push(...validRules); + addRules.push(...validRules); } /******************************************************************************/ -async function updateRemoveparamRules(toAdd) { +async function updateRemoveparamRules(currentRules, addRules, removeRuleIds) { + // Remove existing removeparam-related rules + for ( const rule of currentRules ) { + if ( rule.action.type !== 'redirect' ) { continue; } + if ( rule.action.redirect.transform === undefined ) { continue; } + removeRuleIds.push(rule.id); + } + const [ hasOmnipotence, rulesetDetails, @@ -160,7 +182,6 @@ async function updateRemoveparamRules(toAdd) { for ( const rules of removeparamRulesets ) { if ( Array.isArray(rules) === false ) { continue; } for ( const rule of rules ) { - rule.id = dynamicRuleId++; allRules.push(rule); } } @@ -171,12 +192,19 @@ async function updateRemoveparamRules(toAdd) { if ( validRules.length === 0 ) { return; } ubolLog(`Add ${validRules.length} DNR removeparam rules`); - toAdd.push(...validRules); + addRules.push(...validRules); } /******************************************************************************/ -async function updateRedirectRules(toAdd) { +async function updateRedirectRules(currentRules, addRules, removeRuleIds) { + // Remove existing redirect-related rules + for ( const rule of currentRules ) { + if ( rule.action.type !== 'redirect' ) { continue; } + if ( rule.action.redirect.extensionPath === undefined ) { continue; } + removeRuleIds.push(rule.id); + } + const [ hasOmnipotence, rulesetDetails, @@ -199,7 +227,6 @@ async function updateRedirectRules(toAdd) { for ( const rules of redirectRulesets ) { if ( Array.isArray(rules) === false ) { continue; } for ( const rule of rules ) { - rule.id = dynamicRuleId++; allRules.push(rule); } } @@ -210,12 +237,18 @@ async function updateRedirectRules(toAdd) { if ( validRules.length === 0 ) { return; } ubolLog(`Add ${validRules.length} DNR redirect rules`); - toAdd.push(...validRules); + addRules.push(...validRules); } /******************************************************************************/ -async function updateModifyHeadersRules(toAdd) { +async function updateModifyHeadersRules(currentRules, addRules, removeRuleIds) { + // Remove existing header modification-related rules + for ( const rule of currentRules ) { + if ( rule.action.type !== 'modifyHeaders' ) { continue; } + removeRuleIds.push(rule.id); + } + const [ hasOmnipotence, rulesetDetails, @@ -238,7 +271,6 @@ async function updateModifyHeadersRules(toAdd) { for ( const rules of rulesets ) { if ( Array.isArray(rules) === false ) { continue; } for ( const rule of rules ) { - rule.id = dynamicRuleId++; allRules.push(rule); } } @@ -249,12 +281,65 @@ async function updateModifyHeadersRules(toAdd) { if ( validRules.length === 0 ) { return; } ubolLog(`Add ${validRules.length} DNR modify-headers rules`); - toAdd.push(...validRules); + addRules.push(...validRules); +} + +/******************************************************************************/ + +async function updateDynamicRules() { + const currentRules = await dnr.getDynamicRules(); + const addRules = []; + const removeRuleIds = []; + + // Remove potentially left-over strict-block rules from previous version + for ( const rule of currentRules ) { + if ( isStrictBlockRule(rule) === false ) { continue; } + removeRuleIds.push(rule.id); + } + + await Promise.all([ + updateRegexRules(currentRules, addRules, removeRuleIds), + updateRemoveparamRules(currentRules, addRules, removeRuleIds), + updateRedirectRules(currentRules, addRules, removeRuleIds), + updateModifyHeadersRules(currentRules, addRules, removeRuleIds), + ]); + if ( addRules.length === 0 && removeRuleIds.length === 0 ) { return; } + + const maxRegexRuleCount = dnr.MAX_NUMBER_OF_REGEX_RULES; + let regexRuleCount = 0; + let ruleId = 1; + for ( const rule of addRules ) { + if ( rule?.condition.regexFilter ) { regexRuleCount += 1; } + if ( (rule.id || 0) >= TRUSTED_DIRECTIVE_BASE_RULE_ID ) { continue; } + rule.id = ruleId++; + } + if ( regexRuleCount !== 0 ) { + ubolLog(`Using ${regexRuleCount}/${maxRegexRuleCount} dynamic regex-based DNR rules`); + } + return Promise.all([ + dnr.updateDynamicRules({ addRules, removeRuleIds }).then(( ) => { + if ( removeRuleIds.length !== 0 ) { + ubolLog(`Remove ${removeRuleIds.length} dynamic DNR rules`); + } + if ( addRules.length !== 0 ) { + ubolLog(`Add ${addRules.length} dynamic DNR rules`); + } + }).catch(reason => { + console.error(`updateDynamicRules() / ${reason}`); + }), + updateSessionRules(), + ]); } /******************************************************************************/ -async function updateStrictBlockRules(dynamicRules, sessionRules) { +async function updateStrictBlockRules(currentRules, addRules, removeRuleIds) { + // Remove existing strictblock-related rules + for ( const rule of currentRules ) { + if ( isStrictBlockRule(rule) === false ) { continue; } + removeRuleIds.push(rule.id); + } + if ( rulesetConfig.strictBlockMode === false ) { return; } const [ @@ -269,107 +354,49 @@ async function updateStrictBlockRules(dynamicRules, sessionRules) { sessionRead('excludedStrictBlockHostnames'), ]); - // Fetch strick-block hostnames + // Strict-block rules can only be enforced with omnipotence + if ( hasOmnipotence === false ) { + localRemove('excludedStrictBlockHostnames'); + sessionRemove('excludedStrictBlockHostnames'); + return; + } + + // Fetch strick-block rules const toFetch = []; for ( const details of rulesetDetails ) { if ( details.rules.strictblock === 0 ) { continue; } toFetch.push(fetchJSON(`/rulesets/strictblock/${details.id}`)); } - const strictblockRulesets = await Promise.all(toFetch); + const rulesets = await Promise.all(toFetch); - // Strict-block rules can only be enforced with omnipotence - let toStrictBlock = new Set(); - if ( hasOmnipotence ) { - for ( const hostnames of strictblockRulesets ) { - if ( Array.isArray(hostnames) === false ) { continue; } - toStrictBlock = toStrictBlock.union(new Set(hostnames)); - } - } else { - if ( permanentlyExcluded.length !== 0 ) { - localRemove('excludedStrictBlockHostnames'); - permanentlyExcluded.length = 0; - } - if ( temporarilyExcluded.length !== 0 ) { - sessionRemove('excludedStrictBlockHostnames'); - temporarilyExcluded.length = 0; + const substitution = `${runtime.getURL('/strictblock.html')}#\\0`; + const allRules = []; + for ( const rules of rulesets ) { + if ( Array.isArray(rules) === false ) { continue; } + for ( const rule of rules ) { + rule.action.redirect.regexSubstitution = substitution; + allRules.push(rule); } } - for ( const hn of permanentlyExcluded ) { - toStrictBlock.delete(hn); - } - if ( toStrictBlock.size === 0 ) { return; } - const manifest = runtime.getManifest(); - let strictblockPath = ''; - for ( const war of manifest.web_accessible_resources ) { - if ( war.resources.length !== 1 ) { continue; } - if ( war.resources[0].startsWith('/strictblock.') === false ) { continue; } - strictblockPath = runtime.getURL(war.resources[0]); - break; - } - if ( strictblockPath === '' ) { return; } - const dynamicRule = { - id: STRICTBLOCK_BASE_RULE_ID, - action: { - type: 'redirect', - redirect: { - regexSubstitution: `${strictblockPath}#\\0`, - }, - }, - condition: { - regexFilter: '^https?://.+', - requestDomains: Array.from(toStrictBlock), - resourceTypes: [ 'main_frame' ], - }, - priority: 29, - }; - if ( permanentlyExcluded.length !== 0 ) { - dynamicRule.condition.excludedRequestDomains = permanentlyExcluded; + + const validRules = await pruneInvalidRegexRules('strictblock', allRules); + if ( validRules.length === 0 ) { return; } + ubolLog(`Add ${validRules.length} DNR strictblock rules`); + for ( const rule of validRules ) { + addRules.push(rule); } - dynamicRules.push(dynamicRule); - ubolLog(`Add 1 DNR dynamic rule with ${toStrictBlock.size} strictblock domains`); - if ( temporarilyExcluded.length === 0 ) { return; } - sessionRules.push({ - id: STRICTBLOCK_BASE_RULE_ID, - action: { - type: 'allow', - }, + const allExcluded = permanentlyExcluded.concat(temporarilyExcluded); + if ( allExcluded.length === 0 ) { return; } + addRules.push({ + action: { type: 'allow' }, condition: { - requestDomains: temporarilyExcluded, + requestDomains: allExcluded, resourceTypes: [ 'main_frame' ], }, - priority: 29, + priority: STRICTBLOCK_PRIORITY, }); - ubolLog(`Add 1 DNR session rule with ${temporarilyExcluded.length} excluded strictblock domains`); -} - -async function commitStrictBlockRules() { - const [ - beforePermanentRules, - beforeTemporaryRules, - ] = await Promise.all([ - dnr.getDynamicRules({ ruleIds: [ STRICTBLOCK_BASE_RULE_ID ] }), - dnr.getSessionRules({ ruleIds: [ STRICTBLOCK_BASE_RULE_ID ] }), - ]); - if ( beforePermanentRules?.length ) { - ubolLog(`Remove 1 DNR dynamic strictblock rule`); - } - if ( beforeTemporaryRules?.length ) { - ubolLog(`Remove 1 DNR session strictblock rule`); - } - const afterPermanentRules = []; - const afterTemporaryRules = []; - await updateStrictBlockRules(afterPermanentRules, afterTemporaryRules) - return Promise.all([ - dnr.updateDynamicRules({ - addRules: afterPermanentRules, - removeRuleIds: beforePermanentRules.map(rule => rule.id), - }), - dnr.updateSessionRules({ - addRules: afterTemporaryRules, - removeRuleIds: beforeTemporaryRules.map(rule => rule.id), - }), - ]); + ubolLog(`Add 1 DNR session rule with ${allExcluded.length} for excluded strict-block domains`); } async function excludeFromStrictBlock(hostname, permanent) { @@ -379,7 +406,7 @@ async function excludeFromStrictBlock(hostname, permanent) { hostnames.add(hostname); const writeFn = permanent ? localWrite : sessionWrite; await writeFn('excludedStrictBlockHostnames', Array.from(hostnames)); - return commitStrictBlockRules(); + return updateSessionRules(); } async function setStrictBlockMode(state) { @@ -394,67 +421,37 @@ async function setStrictBlockMode(state) { ); } await Promise.all(promises); - return commitStrictBlockRules(); + return updateSessionRules(); } /******************************************************************************/ -async function updateDynamicRules() { - dynamicRuleId = 1; - const dynamicRules = []; - const sessionRules = []; - const [ - dynamicRuleIds, - sessionRuleIds, - ] = await Promise.all([ - dnr.getDynamicRules().then(rules => - rules.map(rule => rule.id) - .filter(id => id < TRUSTED_DIRECTIVE_BASE_RULE_ID) - ), - dnr.getSessionRules().then(rules => rules.map(rule => rule.id)), - updateRegexRules(dynamicRules), - updateRemoveparamRules(dynamicRules), - updateRedirectRules(dynamicRules), - updateModifyHeadersRules(dynamicRules), - updateStrictBlockRules(dynamicRules, sessionRules), - ]); - if ( dynamicRules.length === 0 && dynamicRuleIds.length === 0 ) { return; } - const promises = []; - if ( dynamicRules.length !== 0 || dynamicRuleIds.length !== 0 ) { - promises.push( - dnr.updateDynamicRules({ - addRules: dynamicRules, - removeRuleIds: dynamicRuleIds, - }).then(( ) => { - if ( dynamicRuleIds.length !== 0 ) { - ubolLog(`Remove ${dynamicRuleIds.length} dynamic DNR rules`); - } - if ( dynamicRules.length !== 0 ) { - ubolLog(`Add ${dynamicRules.length} dynamic DNR rules`); - } - }).catch(reason => { - console.error(`updateDynamicRules() / ${reason}`); - }) - ); - } - if ( sessionRules.length !== 0 || sessionRuleIds.length !== 0 ) { - promises.push( - dnr.updateSessionRules({ - addRules: sessionRules, - removeRuleIds: sessionRuleIds, - }).then(( ) => { - if ( sessionRuleIds.length !== 0 ) { - ubolLog(`Remove ${sessionRuleIds.length} session DNR rules`); - } - if ( sessionRules.length !== 0 ) { - ubolLog(`Add ${sessionRules.length} session DNR rules`); - } - }).catch(reason => { - console.error(`updateSessionRules() / ${reason}`); - }) - ); - } - return Promise.all(promises); +async function updateSessionRules() { + const addRules = []; + const removeRuleIds = []; + const currentRules = await dnr.getSessionRules(); + await updateStrictBlockRules(currentRules, addRules, removeRuleIds); + if ( addRules.length === 0 && removeRuleIds.length === 0 ) { return; } + const maxRegexRuleCount = dnr.MAX_NUMBER_OF_REGEX_RULES; + let regexRuleCount = 0; + let ruleId = 1; + for ( const rule of addRules ) { + if ( rule?.condition.regexFilter ) { regexRuleCount += 1; } + rule.id = ruleId++; + } + if ( regexRuleCount !== 0 ) { + ubolLog(`Using ${regexRuleCount}/${maxRegexRuleCount} session regex-based DNR rules`); + } + return dnr.updateSessionRules({ addRules, removeRuleIds }).then(( ) => { + if ( removeRuleIds.length !== 0 ) { + ubolLog(`Remove ${removeRuleIds.length} session DNR rules`); + } + if ( addRules.length !== 0 ) { + ubolLog(`Add ${addRules.length} session DNR rules`); + } + }).catch(reason => { + console.error(`updateSessionRules() / ${reason}`); + }); } /******************************************************************************/ @@ -747,4 +744,5 @@ export { patchDefaultRulesets, setStrictBlockMode, updateDynamicRules, + updateSessionRules, }; diff --git a/platform/mv3/extension/js/strictblock.js b/platform/mv3/extension/js/strictblock.js index 2e8c74c67e5ea..3ba92aae703f6 100644 --- a/platform/mv3/extension/js/strictblock.js +++ b/platform/mv3/extension/js/strictblock.js @@ -49,21 +49,12 @@ function urlToFragment(raw) { /******************************************************************************/ -async function proceed() { - await sendMessage({ - what: 'excludeFromStrictBlock', - hostname: toURL.hostname, - permanent: qs$('#disableWarning').checked, - }); - window.location.replace(toURL.href); -} - -/******************************************************************************/ - const toURL = new URL('about:blank'); +const toFinalURL = new URL('about:blank'); try { toURL.href = self.location.hash.slice(1); + toFinalURL.href = toURL.href; } catch(_) { } @@ -72,6 +63,25 @@ qs$('#theURL > p > span:first-of-type').append(urlToFragment(toURL.href)); /******************************************************************************/ +async function proceed() { + const permanent = qs$('#disableWarning').checked; + // Do not exclude current hostname from strict-block ruleset if a urlskip + // directive to another site is in effect. + // TODO: what if the urlskip directive leads to a different subdomain on + // same site? + if ( toFinalURL.hostname !== toURL.hostname && permanent !== true ) { + return window.location.replace(toFinalURL.href); + } + await sendMessage({ + what: 'excludeFromStrictBlock', + hostname: toURL.hostname, + permanent, + }); + window.location.replace(toURL.href); +} + +/******************************************************************************/ + function fragmentFromTemplate(template, placeholder, text, details) { const fragment = new DocumentFragment(); const pos = template.indexOf(placeholder); @@ -165,15 +175,7 @@ function fragmentFromTemplate(template, placeholder, text, details) { dom.on('#toggleParse', 'click', ( ) => { dom.cl.toggle('#theURL', 'collapsed'); - //vAPI.localStorage.setItem( - // 'document-blocked-expand-url', - // (dom.cl.has('#theURL', 'collapsed') === false).toString() - //); }); - - //vAPI.localStorage.getItemAsync('document-blocked-expand-url').then(value => { - // dom.cl.toggle('#theURL', 'collapsed', value !== 'true' && value !== true); - //}); })(); /******************************************************************************/ @@ -184,16 +186,30 @@ function fragmentFromTemplate(template, placeholder, text, details) { let iList = -1; const searchInList = async i => { if ( iList !== -1 ) { return; } - const hostnames = new Set( - await fetchJSON(`/rulesets/strictblock/${rulesetDetails[i].id}`) - ); + const rules = await fetchJSON(`/rulesets/strictblock/${rulesetDetails[i].id}`); if ( iList !== -1 ) { return; } - let hn = toURL.hostname; - for (;;) { - if ( hostnames.has(hn) ) { iList = i; break; } - const pos = hn.indexOf('.'); - if ( pos === -1 ) { break; } - hn = hn.slice(pos+1); + const toHref = toURL.href; + for ( const rule of rules ) { + const { regexFilter, requestDomains } = rule.condition; + let matchesDomain = requestDomains === undefined; + if ( requestDomains ) { + let hn = toURL.hostname; + for (;;) { + if ( requestDomains.includes(hn) ) { + matchesDomain = true; + break; + } + const pos = hn.indexOf('.'); + if ( pos === -1 ) { break; } + hn = hn.slice(pos+1); + } + if ( matchesDomain === false ) { continue; } + } + const re = new RegExp(regexFilter); + const matchesRegex = re.test(toHref); + if ( matchesDomain && matchesRegex ) { + iList = i; + } } }; const toFetch = []; @@ -225,12 +241,24 @@ function fragmentFromTemplate(template, placeholder, text, details) { } if ( toFetch.length === 0 ) { return; } const urlskipLists = await Promise.all(toFetch); + const toHn = toURL.hostname; + const matchesHn = hn => { + if ( hn.endsWith(toHn) === false ) { return false; } + if ( hn.length === toHn.length ) { return true; } + return toHn.charAt(toHn.length - hn.length - 1) === '.'; + }; for ( const urlskips of urlskipLists ) { for ( const urlskip of urlskips ) { const re = new RegExp(urlskip.re, urlskip.c ? undefined : 'i'); if ( re.test(toURL.href) === false ) { continue; } + if ( urlskip.hostnames ) { + if ( urlskip.hostnames.some(hn => matchesHn(hn)) === false ) { + continue; + } + } const finalURL = urlSkip(toURL.href, false, urlskip.steps); if ( finalURL === undefined ) { continue; } + toFinalURL.href = finalURL; const fragment = fragmentFromTemplate( i18n$('strictblockRedirectSentence1'), '{{url}}', urlToFragment(finalURL), diff --git a/platform/mv3/strictblock.html b/platform/mv3/extension/strictblock.html similarity index 100% rename from platform/mv3/strictblock.html rename to platform/mv3/extension/strictblock.html diff --git a/platform/mv3/firefox/manifest.json b/platform/mv3/firefox/manifest.json index 2ee26567352ad..7847e3ad523f3 100644 --- a/platform/mv3/firefox/manifest.json +++ b/platform/mv3/firefox/manifest.json @@ -50,5 +50,15 @@ "storage" ], "short_name": "uBO Lite", - "version": "1.0" + "version": "1.0", + "web_accessible_resources": [ + { + "resources": [ + "/strictblock.html" + ], + "matches": [ + "" + ] + } + ] } diff --git a/platform/mv3/make-rulesets.js b/platform/mv3/make-rulesets.js index 2d9c2d380c4f7..b3495483ee329 100644 --- a/platform/mv3/make-rulesets.js +++ b/platform/mv3/make-rulesets.js @@ -22,9 +22,15 @@ import * as makeScriptlet from './make-scriptlets.js'; import * as sfp from './js/static-filtering-parser.js'; -import { createHash, randomBytes } from 'crypto'; +import { + createHash, + randomBytes, +} from 'crypto'; +import { + dnrRulesetFromRawLists, + mergeRules, +} from './js/static-dnr-filtering.js'; -import { dnrRulesetFromRawLists } from './js/static-dnr-filtering.js'; import fs from 'fs/promises'; import https from 'https'; import path from 'path'; @@ -158,6 +164,52 @@ const scriptletStats = new Map(); const genericDetails = new Map(); const requiredRedirectResources = new Set(); +// This will be used to sign our inserted `!#trusted on` directives +const secret = createHash('sha256').update(randomBytes(16)).digest('hex').slice(0,16); +log(`Secret: ${secret}`); + +/******************************************************************************/ + +const restrSeparator = '(?:[^%.0-9a-z_-]|$)'; + +const rePatternFromUrlFilter = s => { + let anchor = 0b000; + if ( s.startsWith('||') ) { + anchor = 0b100; + s = s.slice(2); + } else if ( s.startsWith('|') ) { + anchor = 0b010; + s = s.slice(1); + } + if ( s.endsWith('|') ) { + anchor |= 0b001; + s = s.slice(0, -1); + } + let reStr = s.replace(rePatternFromUrlFilter.rePlainChars, '\\$&') + .replace(rePatternFromUrlFilter.reSeparators, restrSeparator) + .replace(rePatternFromUrlFilter.reDanglingAsterisks, '') + .replace(rePatternFromUrlFilter.reAsterisks, '\\S*?'); + if ( anchor & 0b100 ) { + reStr = ( + reStr.startsWith('\\.') ? + rePatternFromUrlFilter.restrHostnameAnchor2 : + rePatternFromUrlFilter.restrHostnameAnchor1 + ) + reStr; + } else if ( anchor & 0b010 ) { + reStr = '^' + reStr; + } + if ( anchor & 0b001 ) { + reStr += '$'; + } + return reStr; +}; +rePatternFromUrlFilter.rePlainChars = /[.+?${}()|[\]\\]/g; +rePatternFromUrlFilter.reSeparators = /\^/g; +rePatternFromUrlFilter.reDanglingAsterisks = /^\*+|\*+$/g; +rePatternFromUrlFilter.reAsterisks = /\*+/g; +rePatternFromUrlFilter.restrHostnameAnchor1 = '^[a-z-]+://(?:[^/?#]+\\.)?'; +rePatternFromUrlFilter.restrHostnameAnchor2 = '^[a-z-]+://(?:[^/?#]+)?'; + /******************************************************************************/ async function fetchList(assetDetails) { @@ -179,7 +231,7 @@ async function fetchList(assetDetails) { } fetchedURLs.add(part.url); if ( part.url.startsWith('https://ublockorigin.github.io/uAssets/filters/') ) { - newParts.push(`!#trusted on ${assetDetails.secret}`); + newParts.push(`!#trusted on ${secret}`); } newParts.push( fetchText(part.url, cacheDir).then(details => { @@ -197,7 +249,7 @@ async function fetchList(assetDetails) { return { url, content: '' }; }) ); - newParts.push(`!#trusted off ${assetDetails.secret}`); + newParts.push(`!#trusted off ${secret}`); } parts = await Promise.all(newParts); parts = sfp.utils.preparser.expandIncludes(parts, env); @@ -330,6 +382,68 @@ function toJSONRuleset(ruleset) { /******************************************************************************/ +function toStrictBlockRule(rule, out) { + if ( rule.action.type !== 'block' ) { return; } + const { condition } = rule; + if ( condition === undefined ) { return; } + if ( condition.domainType ) { return; } + if ( condition.excludedResourceTypes ) { return; } + if ( condition.requestMethods ) { return; } + if ( condition.excludedRequestMethods ) { return; } + if ( condition.responseHeaders ) { return; } + if ( condition.excludedResponseHeaders ) { return; } + if ( condition.initiatorDomains ) { return; } + if ( condition.excludedInitiatorDomains ) { return; } + if ( condition.excludedRequestDomains ) { return; } + const { resourceTypes } = condition; + if ( resourceTypes === undefined ) { + if ( condition.requestDomains === undefined ) { return; } + } else { + if ( resourceTypes.length !== 1 ) { return; } + if ( resourceTypes[0] !== 'main_frame' ) { return; } + } + let regexFilter; + if ( condition.urlFilter ) { + regexFilter = rePatternFromUrlFilter(condition.urlFilter); + } else if ( condition.regexFilter ) { + regexFilter = condition.regexFilter; + } else { + regexFilter = '^https?://.*'; + } + if ( + regexFilter.startsWith('^') === false + ) { + regexFilter = `^.*${regexFilter}`; + } + if ( + regexFilter.endsWith('$') === false && + regexFilter.endsWith('.*') === false && + regexFilter.endsWith('.+') === false + ) { + regexFilter = `${regexFilter}.*`; + } + const strictBlockRule = { + action: { + type: 'redirect', + redirect: { + regexSubstitution: `/strictblock.html#\\0`, + }, + }, + condition: { + regexFilter, + resourceTypes: [ 'main_frame' ], + }, + priority: 29, + }; + if ( condition.requestDomains ) { + strictBlockRule.condition.requestDomains = condition.requestDomains.slice(); + } + out.set(toStrictBlockRule.ruleId++, strictBlockRule); +} +toStrictBlockRule.ruleId = 1; + +/******************************************************************************/ + async function processNetworkFilters(assetDetails, network) { const { ruleset: rules } = network; log(`Input filter count: ${network.filterCount}`); @@ -396,21 +510,46 @@ async function processNetworkFilters(assetDetails, network) { ); log(`\tmodifyHeaders=: ${modifyHeaders.length}`); - const urlskips = rules.filter(rule => isURLSkip(rule)).filter(rule => - rule.__modifierAction === 0 && - rule.condition && - rule.condition.regexFilter && - rule.condition.resourceTypes && - rule.condition.resourceTypes.includes('main_frame') - ).map(rule => { - const steps = rule.__modifierValue; - return { - re: rule.condition.regexFilter, - c: rule.condition.isUrlFilterCaseSensitive, - steps: steps.includes(' ') && steps.split(/ +/) || [ steps ], - }; - }); - log(`\turlskip=: ${urlskips.length}`); + const urlskips = new Map(); + for ( const rule of rules ) { + if ( isURLSkip(rule) === false ) { continue; } + if ( rule.__modifierAction !== 0 ) { continue; } + const { condition } = rule; + if ( condition.resourceTypes ) { + if ( condition.resourceTypes.includes('main_frame') === false ) { + continue; + } + } + const { urlFilter, regexFilter, requestDomains } = condition; + let re; + if ( urlFilter !== undefined ) { + re = rePatternFromUrlFilter(urlFilter); + } else if ( regexFilter !== undefined ) { + re = regexFilter; + } else { + re = '^'; + } + const rawSteps = rule.__modifierValue; + const steps = rawSteps.includes(' ') && rawSteps.split(/ +/) || [ rawSteps ]; + const keyEntry = { + re, + c: condition.isUrlFilterCaseSensitive, + steps, + } + const key = JSON.stringify(keyEntry); + let actualEntry = urlskips.get(key); + if ( actualEntry === undefined ) { + urlskips.set(key, keyEntry); + actualEntry = keyEntry; + } + if ( requestDomains !== undefined ) { + if ( actualEntry.hostnames === undefined ) { + actualEntry.hostnames = []; + } + actualEntry.hostnames.push(...requestDomains); + } + } + log(`\turlskip=: ${urlskips.size}`); const bad = rules.filter(rule => isUnsupported(rule) @@ -451,37 +590,26 @@ async function processNetworkFilters(assetDetails, network) { ); } - const strictBlocked = new Set(); + const strictBlocked = new Map(); for ( const rule of plainGood ) { - if ( rule.action.type !== 'block' ) { continue; } - if ( rule.condition.domainType ) { continue; } - if ( rule.condition.regexFilter ) { continue; } - if ( rule.condition.urlFilter ) { continue; } - if ( rule.condition.requestMethods ) { continue; } - if ( rule.condition.excludedRequestMethods ) { continue; } - if ( rule.condition.resourceTypes ) { continue; } - if ( rule.condition.excludedResourceTypes ) { continue; } - if ( rule.condition.responseHeaders ) { continue; } - if ( rule.condition.excludedResponseHeaders ) { continue; } - if ( rule.condition.initiatorDomains ) { continue; } - if ( rule.condition.excludedInitiatorDomains ) { continue; } - if ( rule.condition.requestDomains === undefined ) { continue; } - if ( rule.condition.excludedRequestDomains ) { continue; } - for ( const hn of rule.condition.requestDomains ) { - strictBlocked.add(hn); - } + toStrictBlockRule(rule, strictBlocked); } if ( strictBlocked.size !== 0 ) { + mergeRules(strictBlocked, 'requestDomains'); + let id = 1; + for ( const rule of strictBlocked.values() ) { + rule.id = id++; + } writeFile( `${rulesetDir}/strictblock/${assetDetails.id}.json`, - toJSONRuleset(Array.from(strictBlocked)) + toJSONRuleset(Array.from(strictBlocked.values())) ); } - if ( urlskips.length !== 0 ) { + if ( urlskips.size !== 0 ) { writeFile( `${rulesetDir}/urlskip/${assetDetails.id}.json`, - JSON.stringify(urlskips, null, 1) + JSON.stringify(Array.from(urlskips.values()), null, 1) ); } @@ -495,7 +623,7 @@ async function processNetworkFilters(assetDetails, network) { redirect: redirects.length, modifyHeaders: modifyHeaders.length, strictblock: strictBlocked.size, - urlskip: urlskips.length, + urlskip: urlskips.size, }; } @@ -1018,16 +1146,25 @@ async function rulesetFromURLs(assetDetails) { log('============================'); log(`Listset for '${assetDetails.id}':`); - if ( assetDetails.text === undefined ) { + if ( assetDetails.text === undefined && assetDetails.urls.length !== 0 ) { const text = await fetchList(assetDetails); - if ( text === '' ) { return; } assetDetails.text = text; + } else { + assetDetails.text = ''; } - if ( Array.isArray(assetDetails.filters) ) { - assetDetails.text += '\n' + assetDetails.filters.join('\n'); + if ( Array.isArray(assetDetails.filters) && assetDetails.filters.length ) { + const extra = [ + `!#trusted on ${secret}`, + ...assetDetails.filters, + `!#trusted off ${secret}`, + assetDetails.text, + ]; + assetDetails.text = extra.join('\n').trim(); } + if ( assetDetails.text === '' ) { return; } + const extensionPaths = []; for ( const [ fname, details ] of redirectResourcesMap ) { const path = `/web_accessible_resources/${fname}`; @@ -1045,7 +1182,7 @@ async function rulesetFromURLs(assetDetails) { const results = await dnrRulesetFromRawLists( [ { name: assetDetails.id, text: assetDetails.text } ], - { env, extensionPaths, secret: assetDetails.secret } + { env, extensionPaths, secret } ); const netStats = await processNetworkFilters( @@ -1181,19 +1318,13 @@ async function main() { JSON.parse(text) ); - // This will be used to sign our inserted `!#trusted on` directives - const secret = createHash('sha256').update(randomBytes(16)).digest('hex').slice(0,16); - log(`Secret: ${secret}`); - // Assemble all default lists as the default ruleset await rulesetFromURLs({ id: 'default', name: 'Ads, trackers, miners, and more' , enabled: true, - secret, urls: [ 'https://ublockorigin.github.io/uAssets/filters/filters.min.txt', - 'https://ublockorigin.github.io/uAssets/filters/badware.min.txt', 'https://ublockorigin.github.io/uAssets/filters/privacy.min.txt', 'https://ublockorigin.github.io/uAssets/filters/unbreak.min.txt', 'https://ublockorigin.github.io/uAssets/filters/quick-fixes.min.txt', @@ -1208,6 +1339,19 @@ async function main() { ], }); + await rulesetFromURLs({ + id: 'badware', + name: 'Badware risks' , + group: 'default', + enabled: true, + urls: [ + 'https://ublockorigin.github.io/uAssets/filters/badware.min.txt', + ], + homeURL: 'https://github.com/uBlockOrigin/uAssets', + filters: [ + ], + }); + // Handpicked rulesets from assets.json const handpicked = [ 'block-lan', @@ -1235,7 +1379,6 @@ async function main() { name: 'EasyList/uBO – Cookie Notices', group: 'annoyances', enabled: false, - secret, urls: [ 'https://ublockorigin.github.io/uAssets/thirdparties/easylist-cookies.txt', 'https://ublockorigin.github.io/uAssets/filters/annoyances-cookies.txt', @@ -1247,7 +1390,6 @@ async function main() { name: 'EasyList/uBO – Overlay Notices', group: 'annoyances', enabled: false, - secret, urls: [ 'https://ublockorigin.github.io/uAssets/thirdparties/easylist-newsletters.txt', 'https://ublockorigin.github.io/uAssets/filters/annoyances-others.txt', @@ -1409,14 +1551,6 @@ async function main() { manifest.declarative_net_request = { rule_resources: ruleResources }; // Patch web_accessible_resources key manifest.web_accessible_resources = manifest.web_accessible_resources || []; - // Strict-block-related resource - const strictblockDocument = `strictblock.${secret}.html`; - copyFile('./strictblock.html', `${outputDir}/${strictblockDocument}`); - manifest.web_accessible_resources.push({ - resources: [ `/${strictblockDocument}` ], - matches: [ '' ], - }); - // Secondary resources const web_accessible_resources = { resources: Array.from(requiredRedirectResources).map(path => `/${path}`), matches: [ '' ], diff --git a/src/js/static-dnr-filtering.js b/src/js/static-dnr-filtering.js index 9935fd161e45c..2e75633ebf8fd 100644 --- a/src/js/static-dnr-filtering.js +++ b/src/js/static-dnr-filtering.js @@ -354,101 +354,105 @@ function addToDNR(context, list) { /******************************************************************************/ -function finalizeRuleset(context, network) { - const ruleset = network.ruleset; - - // Assign rule ids - const rulesetMap = new Map(); - { - let ruleId = 1; - for ( const rule of ruleset ) { - rulesetMap.set(ruleId++, rule); +// Merge rules where possible by merging arrays of a specific property. +// +// https://github.com/uBlockOrigin/uBOL-home/issues/10#issuecomment-1304822579 +// Do not merge rules which have errors. + +function mergeRules(rulesetMap, mergeTarget) { + const sorter = (_, v) => { + if ( Array.isArray(v) ) { + return typeof v[0] === 'string' ? v.sort() : v; } - } - // Merge rules where possible by merging arrays of a specific property. - // - // https://github.com/uBlockOrigin/uBOL-home/issues/10#issuecomment-1304822579 - // Do not merge rules which have errors. - const mergeRules = (rulesetMap, mergeTarget) => { - const mergeMap = new Map(); - const sorter = (_, v) => { - if ( Array.isArray(v) ) { - return typeof v[0] === 'string' ? v.sort() : v; + if ( v instanceof Object ) { + const sorted = {}; + for ( const kk of Object.keys(v).sort() ) { + sorted[kk] = v[kk]; } + return sorted; + } + return v; + }; + const ruleHasher = (rule, target) => { + return JSON.stringify(rule, (k, v) => { + if ( k.startsWith('_') ) { return; } + if ( k === target ) { return; } + return sorter(k, v); + }); + }; + const extractTargetValue = (obj, target) => { + for ( const [ k, v ] of Object.entries(obj) ) { + if ( Array.isArray(v) && k === target ) { return v; } if ( v instanceof Object ) { - const sorted = {}; - for ( const kk of Object.keys(v).sort() ) { - sorted[kk] = v[kk]; - } - return sorted; - } - return v; - }; - const ruleHasher = (rule, target) => { - return JSON.stringify(rule, (k, v) => { - if ( k.startsWith('_') ) { return; } - if ( k === target ) { return; } - return sorter(k, v); - }); - }; - const extractTargetValue = (obj, target) => { - for ( const [ k, v ] of Object.entries(obj) ) { - if ( Array.isArray(v) && k === target ) { return v; } - if ( v instanceof Object ) { - const r = extractTargetValue(v, target); - if ( r !== undefined ) { return r; } - } + const r = extractTargetValue(v, target); + if ( r !== undefined ) { return r; } } - }; - const extractTargetOwner = (obj, target) => { - for ( const [ k, v ] of Object.entries(obj) ) { - if ( Array.isArray(v) && k === target ) { return obj; } - if ( v instanceof Object ) { - const r = extractTargetOwner(v, target); - if ( r !== undefined ) { return r; } - } - } - }; - for ( const [ id, rule ] of rulesetMap ) { - if ( rule._error !== undefined ) { continue; } - const hash = ruleHasher(rule, mergeTarget); - if ( mergeMap.has(hash) === false ) { - mergeMap.set(hash, []); + } + }; + const extractTargetOwner = (obj, target) => { + for ( const [ k, v ] of Object.entries(obj) ) { + if ( Array.isArray(v) && k === target ) { return obj; } + if ( v instanceof Object ) { + const r = extractTargetOwner(v, target); + if ( r !== undefined ) { return r; } } - mergeMap.get(hash).push(id); } - for ( const ids of mergeMap.values() ) { - if ( ids.length === 1 ) { continue; } - const leftHand = rulesetMap.get(ids[0]); - const leftHandSet = new Set( - extractTargetValue(leftHand, mergeTarget) || [] - ); - for ( let i = 1; i < ids.length; i++ ) { - const rightHandId = ids[i]; - const rightHand = rulesetMap.get(rightHandId); - const rightHandArray = extractTargetValue(rightHand, mergeTarget); - if ( rightHandArray !== undefined ) { - if ( leftHandSet.size !== 0 ) { - for ( const item of rightHandArray ) { - leftHandSet.add(item); - } + }; + const mergeMap = new Map(); + for ( const [ id, rule ] of rulesetMap ) { + if ( rule._error !== undefined ) { continue; } + const hash = ruleHasher(rule, mergeTarget); + if ( mergeMap.has(hash) === false ) { + mergeMap.set(hash, []); + } + mergeMap.get(hash).push(id); + } + for ( const ids of mergeMap.values() ) { + if ( ids.length === 1 ) { continue; } + const leftHand = rulesetMap.get(ids[0]); + const leftHandSet = new Set( + extractTargetValue(leftHand, mergeTarget) || [] + ); + for ( let i = 1; i < ids.length; i++ ) { + const rightHandId = ids[i]; + const rightHand = rulesetMap.get(rightHandId); + const rightHandArray = extractTargetValue(rightHand, mergeTarget); + if ( rightHandArray !== undefined ) { + if ( leftHandSet.size !== 0 ) { + for ( const item of rightHandArray ) { + leftHandSet.add(item); } - } else { - leftHandSet.clear(); } - rulesetMap.delete(rightHandId); + } else { + leftHandSet.clear(); } - const leftHandOwner = extractTargetOwner(leftHand, mergeTarget); - if ( leftHandSet.size > 1 ) { - //if ( leftHandOwner === undefined ) { debugger; } - leftHandOwner[mergeTarget] = Array.from(leftHandSet).sort(); - } else if ( leftHandSet.size === 0 ) { - if ( leftHandOwner !== undefined ) { - leftHandOwner[mergeTarget] = undefined; - } + rulesetMap.delete(rightHandId); + } + const leftHandOwner = extractTargetOwner(leftHand, mergeTarget); + if ( leftHandSet.size > 1 ) { + //if ( leftHandOwner === undefined ) { debugger; } + leftHandOwner[mergeTarget] = Array.from(leftHandSet).sort(); + } else if ( leftHandSet.size === 0 ) { + if ( leftHandOwner !== undefined ) { + leftHandOwner[mergeTarget] = undefined; } } - }; + } +} + +/******************************************************************************/ + +function finalizeRuleset(context, network) { + const ruleset = network.ruleset; + + // Assign rule ids + const rulesetMap = new Map(); + { + let ruleId = 1; + for ( const rule of ruleset ) { + rulesetMap.set(ruleId++, rule); + } + } mergeRules(rulesetMap, 'resourceTypes'); mergeRules(rulesetMap, 'removeParams'); mergeRules(rulesetMap, 'initiatorDomains'); @@ -508,4 +512,4 @@ async function dnrRulesetFromRawLists(lists, options = {}) { /******************************************************************************/ -export { dnrRulesetFromRawLists }; +export { dnrRulesetFromRawLists, mergeRules }; From c04d4f41873bebeb8756d04a9e6ff8e16353a3fb Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sun, 29 Dec 2024 17:06:26 -0500 Subject: [PATCH 0588/1099] [mv3] Fix build script --- tools/make-mv3.sh | 1 - 1 file changed, 1 deletion(-) diff --git a/tools/make-mv3.sh b/tools/make-mv3.sh index 5c812ec214dd5..60eba71ae476d 100755 --- a/tools/make-mv3.sh +++ b/tools/make-mv3.sh @@ -108,7 +108,6 @@ if [ "$QUICK" != "yes" ]; then cp platform/mv3/package.json "$TMPDIR"/ cp platform/mv3/*.js "$TMPDIR"/ cp platform/mv3/*.mjs "$TMPDIR"/ - cp platform/mv3/*.html "$TMPDIR"/ cp platform/mv3/extension/js/utils.js "$TMPDIR"/js/ cp -R "$UBO_DIR"/src/js/resources "$TMPDIR"/js/ cp "$UBO_DIR"/assets/assets.dev.json "$TMPDIR"/ From 62178de75682a9e7e8fc7d1bedda2c7f476b5b66 Mon Sep 17 00:00:00 2001 From: Fanboynz Date: Tue, 31 Dec 2024 02:27:50 +1300 Subject: [PATCH 0589/1099] Add granted and done cookie values (#3932) --- src/js/resources/cookie.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/js/resources/cookie.js b/src/js/resources/cookie.js index df02ca2e93403..88bc10e92126b 100644 --- a/src/js/resources/cookie.js +++ b/src/js/resources/cookie.js @@ -45,6 +45,7 @@ export function getSafeCookieValuesFn() { 'true', 't', 'false', 'f', 'yes', 'y', 'no', 'n', 'all', 'none', 'functional', + 'granted', 'done', ]; } registerScriptlet(getSafeCookieValuesFn, { From 217d001d71b9f0e063e7018bfb4209adffa1e018 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Mon, 30 Dec 2024 13:39:46 -0500 Subject: [PATCH 0590/1099] [mv3] Minor changes --- platform/mv3/extension/css/settings.css | 3 +++ platform/mv3/make-rulesets.js | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/platform/mv3/extension/css/settings.css b/platform/mv3/extension/css/settings.css index 237fb8191984e..df73535140e71 100644 --- a/platform/mv3/extension/css/settings.css +++ b/platform/mv3/extension/css/settings.css @@ -52,6 +52,9 @@ label:has(input[type="checkbox"][disabled]) + legend { display: flex; flex-direction: column; } +.filteringModeCard:hover { + background-color: color-mix(in hsl, var(--surface-1) 75%, var(--surface-0) 25%); + } .filteringModeCard:has(.radio > [type="radio"]:checked) { background-color: var(--surface-0); } diff --git a/platform/mv3/make-rulesets.js b/platform/mv3/make-rulesets.js index b3495483ee329..3c4660f0897c4 100644 --- a/platform/mv3/make-rulesets.js +++ b/platform/mv3/make-rulesets.js @@ -1342,7 +1342,7 @@ async function main() { await rulesetFromURLs({ id: 'badware', name: 'Badware risks' , - group: 'default', + group: 'malware', enabled: true, urls: [ 'https://ublockorigin.github.io/uAssets/filters/badware.min.txt', From 7c530198c886e9884acc35b2544945e3a878a103 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Wed, 1 Jan 2025 08:20:45 -0500 Subject: [PATCH 0591/1099] Import translation work from https://crowdin.com/project/ublock --- platform/mv3/description/webstore.tr.txt | 2 +- .../extension/_locales/zh_TW/messages.json | 42 +++++++++---------- src/_locales/vi/messages.json | 12 +++--- src/_locales/zh_TW/messages.json | 14 +++---- 4 files changed, 35 insertions(+), 35 deletions(-) diff --git a/platform/mv3/description/webstore.tr.txt b/platform/mv3/description/webstore.tr.txt index 05bc267cfd5b7..bf186ac42412a 100644 --- a/platform/mv3/description/webstore.tr.txt +++ b/platform/mv3/description/webstore.tr.txt @@ -7,7 +7,7 @@ Varsayılan kural seti, uBlock Origin'in varsayılan filtre setine karşılık g - EasyPrivacy - Peter Lowe'un Reklam ve izleme sunucusu listesi -Seçenekler ekranına uğrayarak daha çok kuralı uygulatabilirsiniz, bunun için açılır paneldeki _dişli_ simgesine tıklayın. +Seçenekler ekranına uğrayarak daha fazla kuralı aktif hale getirebilirsiniz, bunun için açılır paneldeki _dişli_ simgesine tıklayın. uBOL tamamen bildirimseldir, yani filtrelemenin gerçekleşmesi için kalıcı bir uBOL işlemine gerek yoktur, içerik filtreleme eklenti yerine tarayıcının kendisi tarafından CSS/JS yerleştirerek gerçekleştirilir. Bu, içerik engelleme devam ederken uBOL'nin kendisinin CPU/bellek kaynaklarını tüketmediği anlamına gelir -- uBOL'un hizmet çalışanı işlemi, _only_ açılan panel veya seçenek sayfalarıyla etkileşim kurduğunuzda gereklidir. diff --git a/platform/mv3/extension/_locales/zh_TW/messages.json b/platform/mv3/extension/_locales/zh_TW/messages.json index d76680e6644f1..54bc3173bf4ef 100644 --- a/platform/mv3/extension/_locales/zh_TW/messages.json +++ b/platform/mv3/extension/_locales/zh_TW/messages.json @@ -4,11 +4,11 @@ "description": "extension name." }, "extShortDesc": { - "message": "一個無須任何權限的內容阻擋器。安裝即可阻擋廣告、追蹤器、挖礦程式等網頁內容。", + "message": "一個無須授權的內容封鎖器。安裝後即可立即封鎖廣告、追蹤器、挖礦程式等等。", "description": "this will be in the Chrome web store: must be 132 characters or less" }, "perRulesetStats": { - "message": "{{ruleCount}} 條規則,轉換自 {{filterCount}} 條網路過濾規則", + "message": "{{ruleCount}} 條規則,由 {{filterCount}} 條網路過濾規則轉換而來", "description": "Appears aside each filter list in the _3rd-party filters_ pane" }, "dashboardName": { @@ -32,7 +32,7 @@ "description": "Label in the popup panel for the current filtering mode" }, "popupTipReport": { - "message": "回報網站問題", + "message": "回報此網站的問題", "description": "Tooltip used for the 'chat' icon in the panel" }, "popupTipDashboard": { @@ -112,7 +112,7 @@ "description": "First paragraph of 'Filter issues' section in Support pane" }, "supportS6P1S1": { - "message": "為免增加志願者的負擔,請先確認該問題尚未被報告。", + "message": "為避免增加志願者的負擔,請先確認該問題被尚未被回報過。", "description": "A paragraph in the filter issue reporter section" }, "supportFindSpecificButton": { @@ -128,15 +128,15 @@ "description": "Label for widget to select type of issue" }, "supportS6Select1Option0": { - "message": "-- 挑選一種情況 --", + "message": "-- 選擇一項 --", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option1": { - "message": "會顯示廣告或殘留空位", + "message": "會顯示廣告或廣告殘留物", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option2": { - "message": "有覆蓋物或其他滋擾物", + "message": "有覆蓋層或其他干擾", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option3": { @@ -144,19 +144,19 @@ "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option4": { - "message": "存在隱私問題", + "message": "有隱私相關問題", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option5": { - "message": "開啟 uBO Lite 的時候運作異常", + "message": "在 uBO Lite 啟用時運作異常", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option6": { - "message": "會開啟不需要的分頁或視窗", + "message": "開啟不需要的分頁或視窗", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option7": { - "message": "導致惡意軟體、網路釣魚", + "message": "導向惡意軟體、釣魚網站", "description": "An entry in the widget used to select the type of issue" }, "supportS6Checkbox1": { @@ -172,7 +172,7 @@ "description": "The header text for the welcome message section" }, "firstRunDescription": { - "message": "您剛安裝了 uBO Lite。您可以在此處選擇會套用在所有網站上的預設過濾模式。\n\n預設情況下,會使用基礎模式,因為其不需要讀取與變更資料的權限。若您信任 uBO Lite,您可以授予其讀取並變更在所有網站上資料的廣泛權限,以便為所有網站啟用進階過濾功能。", + "message": "您剛安裝了 uBO Lite。您可以在此處選擇會在所有網站上套用的預設過濾模式。\n\n預設情況下,選擇了基礎模式,因為其不需要讀取與變更資料的權限。若您信任 uBO Lite,您可以授予其讀取並變更在所有網站上資料的廣泛權限,以便為所有網站啟用進階過濾功能。", "description": "Descriptive text shown at first install time only " }, "defaultFilteringModeSectionLabel": { @@ -204,15 +204,15 @@ "description": "This describes the 'basic' filtering mode" }, "optimalFilteringModeDescription": { - "message": "進階網路過濾以及來自選定過濾條件清單的特定擴展過濾。\n\n需要廣泛的權限以讀取和修改所有網站的數據。", + "message": "進階的網路過濾功能,並結合選定過濾清單中的特定延伸過濾。 \n\n需要授予讀取和修改所有網站資料的廣泛權限。", "description": "This describes the 'optimal' filtering mode" }, "completeFilteringModeDescription": { - "message": "進階網路過濾加上來自選定過濾清單的特定及通用擴展過濾。\n\n需要廣泛的權限以讀取和修改所有網站的數據。\n\n通用擴展過濾可能導致網頁資源使用量增加。", + "message": "進階網路過濾,加上從選定過濾清單中進行特定與通用延伸過濾。\n\n需要授予讀取和修改所有網站資料的廣泛權限。\n\n通用延伸過濾可能會導致較高的網頁資源使用量。", "description": "This describes the 'complete' filtering mode" }, "noFilteringModeDescription": { - "message": "不會被過濾的網站", + "message": "白名單", "description": "A short description for the editable field which lists trusted sites" }, "noFilteringModePlaceholder": { @@ -224,11 +224,11 @@ "description": "The header text for the 'Behavior' section" }, "autoReloadLabel": { - "message": "變更過濾模式時,自動重新載入", + "message": "變更過濾模式時自動重新載入", "description": "Label for a checkbox in the options page" }, "showBlockedCountLabel": { - "message": "在工具列圖示上顯示被阻擋的網路請求數量", + "message": "在工具列圖示上顯示已封鎖請求的數量。", "description": "Label for a checkbox in the options page" }, "enableStrictBlockLabel": { @@ -236,7 +236,7 @@ "description": "Label for a checkbox in the options page" }, "enableStrictBlockLegend": { - "message": "將會阻擋前往潛在不良網站的請求,您可以選擇繼續。", + "message": "前往潛在不良網站的導航將被阻止,您可以選擇是否繼續前往。", "description": "Label for a checkbox in the options page" }, "findListsPlaceholder": { @@ -252,11 +252,11 @@ "description": "Sentence used in the strict-blocked page" }, "strictblockReasonSentence1": { - "message": "由於在 {{listname}} 中有符合的過濾條件,因此該頁面被封鎖。", + "message": "由於符合 {{listname}} 中的過濾規則,此頁面已被封鎖。", "description": "Text informing about what is causing the page to be blocked" }, "strictblockRedirectSentence1": { - "message": "被阻擋的頁面試圖重定向到另一個網站。如果您選擇繼續,將直接導航至:{{url}}", + "message": "被阻擋的頁面想要重定向到另一個網站。如果您選擇繼續,將直接導航至:{{url}}", "description": "Text warning about an incoming redirect" }, "strictblockNoParamsPrompt": { @@ -272,7 +272,7 @@ "description": "A button to close the current tab" }, "strictblockDontWarn": { - "message": "不要再顯示關於此網站的警告", + "message": "不再就此網站警告我", "description": "Label for checkbox in document-blocked page" }, "strictblockProceed": { diff --git a/src/_locales/vi/messages.json b/src/_locales/vi/messages.json index bfea4310a0f38..f27c7b3c360d5 100644 --- a/src/_locales/vi/messages.json +++ b/src/_locales/vi/messages.json @@ -104,7 +104,7 @@ "description": "For the new mobile-friendly popup design" }, "popupBlockedSinceInstall_v2": { - "message": "Đã chặn từ khi cài đặt", + "message": "Bị chặn kể từ khi cài đặt", "description": "For the new mobile-friendly popup design" }, "popupDomainsConnected_v2": { @@ -140,7 +140,7 @@ "description": "Tooltip for the no-popups per-site switch" }, "popupTipNoPopups2": { - "message": "Nhấp để bỏ chặn tất cả cửa sổ bật lên trên trang này", + "message": "Bấm để bỏ chặn tất cả cửa sổ bật lên trên trang này", "description": "Tooltip for the no-popups per-site switch" }, "popupTipNoLargeMedia": { @@ -176,7 +176,7 @@ "description": "Tooltip for the no-remote-fonts per-site switch" }, "popupTipNoRemoteFonts2": { - "message": "Nhấp để không chặn phông tải về trên trang này", + "message": "Bấm để ngừng chặn remote fonts trên trang này", "description": "Tooltip for the no-remote-fonts per-site switch" }, "popupTipNoScripting1": { @@ -200,7 +200,7 @@ "description": "Caption for the no-cosmetic-filtering per-site switch" }, "popupNoRemoteFonts_v2": { - "message": "Phông tải về", + "message": "Remote fonts", "description": "Caption for the no-remote-fonts per-site switch" }, "popupNoScripting_v2": { @@ -380,7 +380,7 @@ "description": "" }, "settingsNoLargeMediaPrompt": { - "message": "Chặn yếu tố đa phương tiện lớn hơn {{input}} KB", + "message": "Chặn phần tử đa phương tiện kich thước lớn hơn {{input}} KB", "description": "" }, "settingsNoRemoteFontsPrompt": { @@ -404,7 +404,7 @@ "description": "Section for controlling advanced-user settings" }, "settingsAdvancedSynopsis": { - "message": "Những tính năng dành cho người dùng nâng cao", + "message": "Các tính năng chỉ phù hợp với kỹ thuật viên", "description": "Description of section controlling advanced-user settings" }, "settingsAdvancedUserSettings": { diff --git a/src/_locales/zh_TW/messages.json b/src/_locales/zh_TW/messages.json index ce98855f5a1b5..346ab1e1aa5b5 100644 --- a/src/_locales/zh_TW/messages.json +++ b/src/_locales/zh_TW/messages.json @@ -16,7 +16,7 @@ "description": "A warning in the dashboard when navigating away from unsaved changes" }, "dashboardUnsavedWarningStay": { - "message": "留下", + "message": "留在這裡", "description": "Label for button to prevent navigating away from unsaved changes" }, "dashboardUnsavedWarningIgnore": { @@ -68,7 +68,7 @@ "description": "Title for the advanced settings page" }, "popupPowerSwitchInfo": { - "message": "點擊:在此網站 停用/啟用 uBlock₀ 。\n\nCtrl + 點擊:僅在此頁面停用 uBlock₀ 。", + "message": "點擊:在這個網站停用/啟用 uBlock₀。\n\nCtrl + 點擊:僅在這個頁面停用 uBlock₀。", "description": "English: Click: disable/enable uBlock₀ for this site.\n\nCtrl+click: disable uBlock₀ only on this page." }, "popupPowerSwitchInfo1": { @@ -100,11 +100,11 @@ "description": "English: or" }, "popupBlockedOnThisPage_v2": { - "message": "此頁面已阻擋", + "message": "已在這個頁面攔截", "description": "For the new mobile-friendly popup design" }, "popupBlockedSinceInstall_v2": { - "message": "安裝後已阻擋", + "message": "安裝後已攔截", "description": "For the new mobile-friendly popup design" }, "popupDomainsConnected_v2": { @@ -224,7 +224,7 @@ "description": "Tooltip when hovering the top-most cell of the local-rules column." }, "popupTipSaveRules": { - "message": "點擊此處讓變更永久生效。", + "message": "點擊以永久儲存您的變更。", "description": "Tooltip when hovering over the padlock in the dynamic filtering pane." }, "popupTipRevertRules": { @@ -364,7 +364,7 @@ "description": "English: " }, "settingsWebRTCIPAddressHiddenPrompt": { - "message": "防止「網頁即時通訊」洩漏本地 IP 位址", + "message": "防止 WebRTC 洩漏本機 IP 位址", "description": "English: " }, "settingPerSiteSwitchGroup": { @@ -536,7 +536,7 @@ "description": "used as a tooltip for error icon beside a list" }, "1pTrustWarning": { - "message": "切勿添加來歷不明的過濾規則。", + "message": "切勿加入來歷不明的過濾規則。", "description": "Warning against copy-pasting filters from random sources" }, "1pEnableMyFiltersLabel": { From 7f78d1959704cc447d1ae7a1642b6a76f9a05a1e Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Wed, 1 Jan 2025 08:22:32 -0500 Subject: [PATCH 0592/1099] New stable release version --- dist/version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dist/version b/dist/version index 403a62b48e450..59790a701844b 100644 --- a/dist/version +++ b/dist/version @@ -1 +1 @@ -1.61.3.103 \ No newline at end of file +1.62.0 \ No newline at end of file From 551c6bc6eb744debc9aa15246c0120e85dfce38a Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sat, 4 Jan 2025 20:23:53 -0500 Subject: [PATCH 0593/1099] Improve `href-sanitizer` scriptlet Related issue: https://github.com/uBlockOrigin/uAssets/issues/26709#issuecomment-2569993803 --- src/js/urlskip.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/js/urlskip.js b/src/js/urlskip.js index 66109f301afa9..b0d396623bbc7 100644 --- a/src/js/urlskip.js +++ b/src/js/urlskip.js @@ -121,7 +121,7 @@ export function urlSkip(url, blocked, steps, directive = {}) { } // URI component if ( step === '-uricomponent' ) { - urlout = self.decodeURIComponent(urlin); + urlout = decodeURIComponent(urlin); continue; } // Enable skip of blocked requests From b617926c1c5076f16aa95a75d331512802d66a5b Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sat, 4 Jan 2025 20:29:19 -0500 Subject: [PATCH 0594/1099] Improve `abort-on-stack-trace` scriptlet Related feedback: https://github.com/uBlockOrigin/uAssets/issues/26704#issuecomment-2569381462 --- src/js/resources/scriptlets.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) mode change 100644 => 100755 src/js/resources/scriptlets.js diff --git a/src/js/resources/scriptlets.js b/src/js/resources/scriptlets.js old mode 100644 new mode 100755 index 75beb5450e7f9..37a332425e4ff --- a/src/js/resources/scriptlets.js +++ b/src/js/resources/scriptlets.js @@ -1174,13 +1174,15 @@ function abortOnStackTrace( let v = owner[chain]; Object.defineProperty(owner, chain, { get: function() { - if ( matchesStackTraceFn(needleDetails, extraArgs.log) ) { + const log = safe.logLevel > 1 ? 'all' : 'match'; + if ( matchesStackTraceFn(needleDetails, log) ) { throw new ReferenceError(getExceptionToken()); } return v; }, set: function(a) { - if ( matchesStackTraceFn(needleDetails, extraArgs.log) ) { + const log = safe.logLevel > 1 ? 'all' : 'match'; + if ( matchesStackTraceFn(needleDetails, log) ) { throw new ReferenceError(getExceptionToken()); } v = a; From e192e40a818d388e6f3ddcd6bab14bc064b9d652 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sat, 4 Jan 2025 20:39:06 -0500 Subject: [PATCH 0595/1099] Update CHANGELOG --- CHANGELOG.md | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index cd568c622a2a7..116331334a359 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,11 @@ -- [Fix deserialization of ArrayBuffer shared by multiple TypedArrays](https://github.com/gorhill/uBlock/commit/c92a518218) +- [Improve `abort-on-stack-trace` scriptlet](https://github.com/gorhill/uBlock/commit/b617926c1c) +- [Improve `href-sanitizer` scriptlet](https://github.com/gorhill/uBlock/commit/551c6bc6eb) + +---------- + +# 1.62.0 + +## Fixes / changes- [Fix deserialization of ArrayBuffer shared by multiple TypedArrays](https://github.com/gorhill/uBlock/commit/c92a518218) - [Improve `trusted-suppress-native-method` scriptlet](https://github.com/gorhill/uBlock/commit/cb6c11ab6f) - [Improve `urlskip=` filter option](https://github.com/gorhill/uBlock/commit/a7aa755f18) - [Improve `parse-properties-to-match` scriptlet helper](https://github.com/gorhill/uBlock/commit/7494eaf621) From af8f11eb1426f710d680439a5a50e47bb9f3bc17 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sat, 4 Jan 2025 20:39:38 -0500 Subject: [PATCH 0596/1099] New revision for dev build --- dist/version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dist/version b/dist/version index 59790a701844b..a9d7dad2232bb 100644 --- a/dist/version +++ b/dist/version @@ -1 +1 @@ -1.62.0 \ No newline at end of file +1.62.1.0 From 25030e6cb99e3cfe8742d90fb5e7541a01343445 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sat, 4 Jan 2025 20:41:28 -0500 Subject: [PATCH 0597/1099] Update .gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 2c0e571513d19..0f4c6e0c4b582 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,4 @@ __pycache__/ node_modules/ /dist/build/ /tmp/ +.DS_Store From 987f41c85ddd95ab24706904f8f4e226bc61046c Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sat, 4 Jan 2025 21:05:46 -0500 Subject: [PATCH 0598/1099] Make Firefox dev build auto-update --- dist/firefox/updates.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dist/firefox/updates.json b/dist/firefox/updates.json index 47f87e3b71a08..47c9c7932af88 100644 --- a/dist/firefox/updates.json +++ b/dist/firefox/updates.json @@ -3,9 +3,9 @@ "uBlock0@raymondhill.net": { "updates": [ { - "version": "1.61.3.103", + "version": "1.62.1.0", "browser_specific_settings": { "gecko": { "strict_min_version": "78.0" } }, - "update_link": "https://github.com/gorhill/uBlock/releases/download/1.61.3rc3/uBlock0_1.61.3rc3.firefox.signed.xpi" + "update_link": "https://github.com/gorhill/uBlock/releases/download/1.62.1b0/uBlock0_1.62.1b0.firefox.signed.xpi" } ] } From 22664733367879172934fd25aa8e3a45d0a7eaa1 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Mon, 6 Jan 2025 20:04:22 -0500 Subject: [PATCH 0599/1099] Fix regression in `trusted-replace-argument` scritplet Related feedback: https://github.com/uBlockOrigin/uBlock-discussions/discussions/859#discussioncomment-11751928 Regression from: https://github.com/gorhill/uBlock/commit/adced29b5bd26165ca89c4562a048c7e73a667f6 --- src/js/resources/replace-argument.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/js/resources/replace-argument.js b/src/js/resources/replace-argument.js index c5927d2b9dc35..a53991790e3e6 100644 --- a/src/js/resources/replace-argument.js +++ b/src/js/resources/replace-argument.js @@ -68,7 +68,7 @@ export function trustedReplaceArgument( const replacer = argraw.startsWith('repl:/') && parseReplaceFn(argraw.slice(5)) || undefined; const value = replacer === undefined && - validateConstantFn(true, argraw, extraArgs) || undefined; + validateConstantFn(true, argraw, extraArgs); const reCondition = extraArgs.condition ? safe.patternToRegex(extraArgs.condition) : /^/; From e0c4bce9317102c6186bdd109d456f67c03599bf Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Tue, 7 Jan 2025 13:58:10 -0500 Subject: [PATCH 0600/1099] Fix changelog --- CHANGELOG.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 116331334a359..dce6539cfda88 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,7 +5,9 @@ # 1.62.0 -## Fixes / changes- [Fix deserialization of ArrayBuffer shared by multiple TypedArrays](https://github.com/gorhill/uBlock/commit/c92a518218) +## Fixes / changes + +- [Fix deserialization of ArrayBuffer shared by multiple TypedArrays](https://github.com/gorhill/uBlock/commit/c92a518218) - [Improve `trusted-suppress-native-method` scriptlet](https://github.com/gorhill/uBlock/commit/cb6c11ab6f) - [Improve `urlskip=` filter option](https://github.com/gorhill/uBlock/commit/a7aa755f18) - [Improve `parse-properties-to-match` scriptlet helper](https://github.com/gorhill/uBlock/commit/7494eaf621) From 4b12247da106249a4d1e1143fd1c30d97656de73 Mon Sep 17 00:00:00 2001 From: Fanboynz Date: Sat, 11 Jan 2025 03:21:19 +1300 Subject: [PATCH 0601/1099] Add decline value to cookie.js (#3933) --- src/js/resources/cookie.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/js/resources/cookie.js b/src/js/resources/cookie.js index 88bc10e92126b..571514ed8201b 100644 --- a/src/js/resources/cookie.js +++ b/src/js/resources/cookie.js @@ -46,6 +46,7 @@ export function getSafeCookieValuesFn() { 'yes', 'y', 'no', 'n', 'all', 'none', 'functional', 'granted', 'done', + 'decline', 'declined', ]; } registerScriptlet(getSafeCookieValuesFn, { From f8975e3a6a67af2818eee09671b02655ce2a851b Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Thu, 9 Jan 2025 09:33:22 -0500 Subject: [PATCH 0602/1099] Better integrate latest eslint version --- .eslintrc.yml | 30 - .jshintrc | 22 - Makefile | 7 +- eslint.config.mjs | 51 + package-lock.json | 1146 +++++++++++++++++ package.json | 32 + platform/browser/main.js | 123 -- platform/browser/test.html | 71 - platform/chromium/is-webrtc-supported.html | 2 +- platform/chromium/is-webrtc-supported.js | 6 +- platform/chromium/vapi-background-ext.js | 2 +- platform/common/vapi-background.js | 46 +- platform/common/vapi-client.js | 2 +- platform/dig/snfe.js | 14 +- platform/firefox/vapi-background-ext.js | 4 +- platform/firefox/webext.js | 2 - platform/mv3/make-rulesets.js | 2 +- platform/mv3/safe-replace.js | 4 - platform/nodejs/build.js | 5 - platform/nodejs/index.js | 6 +- platform/npm/.eslintrc.json | 38 - platform/npm/demo.js | 2 +- platform/npm/package.json | 2 - platform/npm/test.js | 7 +- src/js/about.js | 2 - src/js/advanced-settings.js | 2 - src/js/asset-viewer.js | 6 +- src/js/assets.js | 6 +- src/js/base64-custom.js | 2 - src/js/benchmarks.js | 4 +- src/js/cachestorage.js | 2 +- src/js/click2load.js | 2 - src/js/cloud-ui.js | 6 +- src/js/code-viewer.js | 44 +- src/js/codemirror/search-thread.js | 9 +- src/js/codemirror/search.js | 2 +- src/js/codemirror/ubo-dynamic-filtering.js | 4 +- src/js/commands.js | 6 +- src/js/console.js | 2 - src/js/contentscript-extra.js | 2 +- src/js/contentscript.js | 8 +- src/js/contextmenu.js | 2 +- src/js/dashboard-common.js | 2 - src/js/dashboard.js | 4 +- src/js/diff-updater.js | 7 +- src/js/document-blocked.js | 4 +- src/js/dom-inspector.js | 31 +- src/js/dyna-rules.js | 19 +- src/js/dynamic-net-filtering.js | 12 +- src/js/fa-icons.js | 2 - src/js/filtering-engines.js | 4 - src/js/hnswitches.js | 11 +- src/js/html-filtering.js | 10 +- src/js/httpheader-filtering.js | 12 +- src/js/logger-ui-inspector.js | 2 +- src/js/logger-ui.js | 4 +- src/js/logger.js | 4 - src/js/lz4.js | 4 - src/js/messaging.js | 2 +- src/js/mrucache.js | 2 - src/js/pagestore.js | 2 +- src/js/popup-fenix.js | 96 +- src/js/redirect-engine.js | 6 +- src/js/resources/attribute.js | 4 +- src/js/resources/cookie.js | 2 +- src/js/resources/href-sanitizer.js | 8 +- src/js/resources/localstorage.js | 2 +- src/js/resources/parse-replace.js | 2 +- src/js/resources/replace-argument.js | 6 +- src/js/resources/safe-self.js | 4 +- src/js/resources/scriptlets.js | 32 +- src/js/resources/set-constant.js | 4 +- src/js/reverselookup-worker.js | 3 +- src/js/reverselookup.js | 13 +- src/js/scriptlet-filtering-core.js | 6 +- src/js/scriptlet-filtering.js | 2 +- src/js/scriptlets/cosmetic-logger.js | 10 +- src/js/scriptlets/cosmetic-off.js | 2 - src/js/scriptlets/cosmetic-on.js | 2 - src/js/scriptlets/cosmetic-report.js | 6 +- src/js/scriptlets/dom-inspector.js | 6 +- src/js/scriptlets/dom-survey-elements.js | 2 - src/js/scriptlets/dom-survey-scripts.js | 2 - src/js/scriptlets/epicker.js | 10 +- src/js/scriptlets/load-3p-css.js | 4 +- src/js/scriptlets/load-large-media-all.js | 2 - src/js/scriptlets/noscript-spoof.js | 10 +- src/js/scriptlets/scriptlet-loglevel-1.js | 2 - src/js/scriptlets/scriptlet-loglevel-2.js | 2 - .../scriptlets/should-inject-contentscript.js | 2 +- src/js/scriptlets/subscriber.js | 6 +- src/js/scriptlets/updater.js | 6 +- src/js/settings.js | 6 +- src/js/start.js | 34 +- src/js/static-ext-filtering-db.js | 2 - src/js/static-ext-filtering.js | 6 +- src/js/static-filtering-io.js | 2 - src/js/static-filtering-parser.js | 26 +- src/js/static-net-filtering.js | 4 +- src/js/storage.js | 2 +- src/js/support.js | 6 +- src/js/tab.js | 35 +- src/js/tasks.js | 6 - src/js/text-encode.js | 98 +- src/js/text-utils.js | 2 - src/js/theme.js | 6 +- src/js/ublock.js | 95 +- src/js/uri-utils.js | 10 +- src/js/url-net-filtering.js | 4 - src/js/urlskip.js | 2 +- src/js/utils.js | 6 +- src/js/whitelist.js | 6 +- 112 files changed, 1617 insertions(+), 880 deletions(-) delete mode 100644 .eslintrc.yml delete mode 100644 .jshintrc create mode 100644 eslint.config.mjs create mode 100644 package-lock.json create mode 100644 package.json delete mode 100644 platform/browser/main.js delete mode 100644 platform/browser/test.html delete mode 100644 platform/npm/.eslintrc.json diff --git a/.eslintrc.yml b/.eslintrc.yml deleted file mode 100644 index d7db6e4e4f8bc..0000000000000 --- a/.eslintrc.yml +++ /dev/null @@ -1,30 +0,0 @@ -env: - browser: true - es2022: true -extends: eslint:recommended -parserOptions: - sourceType: module -rules: - eqeqeq: - - warn - - always - indent: - - error - - 4 - - ignoredNodes: - - Program > BlockStatement - - Program > ExpressionStatement > CallExpression > ArrowFunctionExpression > BlockStatement - - Program > ExpressionStatement > CallExpression > FunctionExpression > BlockStatement - - Program > IfStatement > BlockStatement - - Program > VariableDeclaration > VariableDeclarator > CallExpression > ArrowFunctionExpression > BlockStatement - - CallExpression > MemberExpression - - ArrayExpression > * - - ObjectExpression > * - no-control-regex: off - no-empty: off - sort-imports: warn - strict: warn -globals: - browser: readonly - chrome: readonly - vAPI: readonly diff --git a/.jshintrc b/.jshintrc deleted file mode 100644 index 46c0d2e572cf2..0000000000000 --- a/.jshintrc +++ /dev/null @@ -1,22 +0,0 @@ -{ - "browser": true, - "devel": true, - "eqeqeq": true, - "esversion": 11, - "globals": { - "chrome": false, // global variable in Chromium, Chrome, Opera - "globalThis": false, - "self": false, - "vAPI": false, - "URLSearchParams": false, - "WebAssembly": false - }, - "laxbreak": true, - "newcap": false, - "nonew": false, - "strict": "global", - "sub": true, - "undef": true, - "unused": true, - "validthis": true -} diff --git a/Makefile b/Makefile index b308dc4be1e0b..0ff5e3306c8cd 100644 --- a/Makefile +++ b/Makefile @@ -32,11 +32,12 @@ firefox: dist/build/uBlock0.firefox dist/build/uBlock0.npm: tools/make-nodejs.sh $(sources) $(platform) $(assets) tools/make-npm.sh -# Build the Node.js package. -npm: dist/build/uBlock0.npm +# Dev tools +npm: node_modules/ + npm install lint: npm - cd dist/build/uBlock0.npm && npm run lint + npm run lint test: npm cd dist/build/uBlock0.npm && npm run test diff --git a/eslint.config.mjs b/eslint.config.mjs new file mode 100644 index 0000000000000..f9bdca1ca9cd2 --- /dev/null +++ b/eslint.config.mjs @@ -0,0 +1,51 @@ +import js from "@eslint/js"; +import globals from "globals"; +import json from "@eslint/json"; + +import { includeIgnoreFile } from "@eslint/compat"; +import path from "node:path"; +import { fileURLToPath } from "node:url"; + +const __filename = fileURLToPath(import.meta.url); +const __dirname = path.dirname(__filename); +const gitignorePath = path.resolve(__dirname, ".gitignore"); + +export default [ includeIgnoreFile(gitignorePath), { + files: ["**/*.js", "**/*.mjs"], + ...js.configs.recommended, +}, { + files: ["**/*.js", "**/*.mjs"], + languageOptions: { + globals: { + ...globals.browser, + browser: "readonly", + chrome: "readonly", + vAPI: "readonly", + }, + sourceType: "module", + }, + rules: { + eqeqeq: ["warn", "always"], + indent: ["error", 4, { + ignoredNodes: [ + "Program > BlockStatement", + "Program > ExpressionStatement > CallExpression > ArrowFunctionExpression > BlockStatement", + "Program > ExpressionStatement > CallExpression > FunctionExpression > BlockStatement", + "Program > IfStatement > BlockStatement", + "Program > VariableDeclaration > VariableDeclarator > CallExpression > ArrowFunctionExpression > BlockStatement", + "CallExpression > MemberExpression", + "ArrayExpression > *", + "ObjectExpression > *", + ], + }], + "no-control-regex": "off", + "no-empty": "off", + "sort-imports": "error", + "strict": "error", + }, +}, { + files: ["**/*.json"], + ignores: ["package-lock.json"], + language: "json/json", + ...json.configs.recommended, +} ]; diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000000000..18b1c50b91002 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,1146 @@ +{ + "name": "uBlock", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "uBlock", + "version": "1.0.0", + "license": "GPLv3", + "devDependencies": { + "@eslint/compat": "^1.2.4", + "@eslint/js": "^9.17.0", + "@eslint/json": "^0.9.0", + "eslint": "^9.17.0", + "eslint-formatter-compact": "^8.40.0", + "globals": "^15.14.0" + }, + "engines": { + "node": ">=22", + "npm": ">=11" + } + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.1.tgz", + "integrity": "sha512-s3O3waFUrMV8P/XaF/+ZTp1X9XBZW1a4B97ZnjQF2KYWaFD2A8KyFBsrsfSjEmjn3RGWAIuvlneuZm3CUK3jbA==", + "dev": true, + "license": "MIT", + "dependencies": { + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.12.1", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.1.tgz", + "integrity": "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/compat": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@eslint/compat/-/compat-1.2.4.tgz", + "integrity": "sha512-S8ZdQj/N69YAtuqFt7653jwcvuUj131+6qGLUyDqfDg1OIoBQ66OCuXC473YQfO2AaxITTutiRQiDwoo7ZLYyg==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "peerDependencies": { + "eslint": "^9.10.0" + }, + "peerDependenciesMeta": { + "eslint": { + "optional": true + } + } + }, + "node_modules/@eslint/config-array": { + "version": "0.19.1", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.19.1.tgz", + "integrity": "sha512-fo6Mtm5mWyKjA/Chy1BYTdn5mGJoDNjC7C64ug20ADsRDGrA85bN3uK3MaKbeRkRuuIEAR5N33Jr1pbm411/PA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/object-schema": "^2.1.5", + "debug": "^4.3.1", + "minimatch": "^3.1.2" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/core": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.9.1.tgz", + "integrity": "sha512-GuUdqkyyzQI5RMIWkHhvTWLCyLo1jNK3vzkSyaExH5kHPDHcuL2VOpHjmMY+y3+NC69qAKToBqldTBgYeLSr9Q==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@types/json-schema": "^7.0.15" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.2.0.tgz", + "integrity": "sha512-grOjVNN8P3hjJn/eIETF1wwd12DdnwFDoyceUJLYYdkpbwq3nLi+4fqrTAONx7XDALqlL220wC/RHSC/QTI/0w==", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^10.0.1", + "globals": "^14.0.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/eslintrc/node_modules/globals": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", + "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@eslint/js": { + "version": "9.17.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.17.0.tgz", + "integrity": "sha512-Sxc4hqcs1kTu0iID3kcZDW3JHq2a77HO9P8CP6YEA/FpH3Ll8UXE2r/86Rz9YJLKme39S9vU5OWNjC6Xl0Cr3w==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/json": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/@eslint/json/-/json-0.9.0.tgz", + "integrity": "sha512-PTLD0Kp7+BKhTthodns+hFbuZZ+hjb3lc/iVAg7mtBAnW5hLJhkST9O4m21oMkxG94GR2+GGZQNNurG9KP8pNA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/plugin-kit": "^0.2.3", + "@humanwhocodes/momoa": "^3.3.4" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/object-schema": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.5.tgz", + "integrity": "sha512-o0bhxnL89h5Bae5T318nFoFzGy+YE5i/gGkoPAgkmTVdRKTiv3p8JHevPiPaMwoloKfEiiaHlawCqaZMqRm+XQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/plugin-kit": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.2.4.tgz", + "integrity": "sha512-zSkKow6H5Kdm0ZUQUB2kV5JIXqoG0+uH5YADhaEHswm664N9Db8dXSi0nMJpacpMf+MyyglF1vnZohpEg5yUtg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "levn": "^0.4.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@humanfs/core": { + "version": "0.19.1", + "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", + "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/node": { + "version": "0.16.6", + "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.6.tgz", + "integrity": "sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@humanfs/core": "^0.19.1", + "@humanwhocodes/retry": "^0.3.0" + }, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/node/node_modules/@humanwhocodes/retry": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.3.1.tgz", + "integrity": "sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/momoa": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/@humanwhocodes/momoa/-/momoa-3.3.6.tgz", + "integrity": "sha512-7/sAGm3YsT6xG1bDkTSHvOpQB+cR4I2InfMVw110nuOCrxZvOQHgRqBMxSoTeUQrk9RS4OU9Aw2MBMZVJgLZMg==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18" + } + }, + "node_modules/@humanwhocodes/retry": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.1.tgz", + "integrity": "sha512-c7hNEllBlenFTHBky65mhq8WD2kbN9Q6gk0bTk8lSBvc554jpXSkST1iePudpt7+A/AQvuHs9EMqjHDXMY1lrA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@types/estree": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz", + "integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "dev": true, + "license": "MIT" + }, + "node_modules/acorn": { + "version": "8.14.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz", + "integrity": "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==", + "dev": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true, + "license": "Python-2.0" + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "license": "MIT" + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true, + "license": "MIT" + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/debug": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", + "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint": { + "version": "9.17.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.17.0.tgz", + "integrity": "sha512-evtlNcpJg+cZLcnVKwsai8fExnqjGPicK7gnUtlNuzu+Fv9bI0aLpND5T44VLQtoMEnI57LoXO9XAkIXwohKrA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.12.1", + "@eslint/config-array": "^0.19.0", + "@eslint/core": "^0.9.0", + "@eslint/eslintrc": "^3.2.0", + "@eslint/js": "9.17.0", + "@eslint/plugin-kit": "^0.2.3", + "@humanfs/node": "^0.16.6", + "@humanwhocodes/module-importer": "^1.0.1", + "@humanwhocodes/retry": "^0.4.1", + "@types/estree": "^1.0.6", + "@types/json-schema": "^7.0.15", + "ajv": "^6.12.4", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.6", + "debug": "^4.3.2", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^8.2.0", + "eslint-visitor-keys": "^4.2.0", + "espree": "^10.3.0", + "esquery": "^1.5.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^8.0.0", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "jiti": "*" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + } + } + }, + "node_modules/eslint-formatter-compact": { + "version": "8.40.0", + "resolved": "https://registry.npmjs.org/eslint-formatter-compact/-/eslint-formatter-compact-8.40.0.tgz", + "integrity": "sha512-cwGUs113TgmTQXecx5kfRjB7m0y2wkDLSadPTE2pK6M/wO4N8PjmUaoWOFNCP9MHgsiZwgqd5bZFnDCnszC56Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/eslint-scope": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.2.0.tgz", + "integrity": "sha512-PHlWUfG6lvPc3yvP5A4PNyBL1W8fkDUccmI21JUu/+GKZBoH/W5u6usENXUrWFRsyoW5ACUjFGgAFQp5gUlb/A==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz", + "integrity": "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/espree": { + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-10.3.0.tgz", + "integrity": "sha512-0QYC8b24HWY8zjRnDTL6RiHfDbAWn63qb4LMj1Z4b076A4une81+z03Kg7l7mn/48PUTqoLptSXez8oknU8Clg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "acorn": "^8.14.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^4.2.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esquery": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", + "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true, + "license": "MIT" + }, + "node_modules/file-entry-cache": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", + "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "flat-cache": "^4.0.0" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat-cache": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", + "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", + "dev": true, + "license": "MIT", + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.4" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/flatted": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.2.tgz", + "integrity": "sha512-AiwGJM8YcNOaobumgtng+6NHuOqC3A7MixFeDafM3X9cIUM+xUXoS5Vfgf+OihAYe20fxqNM9yPBXJzRtZ/4eA==", + "dev": true, + "license": "ISC" + }, + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/globals": { + "version": "15.14.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-15.14.0.tgz", + "integrity": "sha512-OkToC372DtlQeje9/zHIo5CT8lRP/FUgEOKBEhU4e0abL7J7CD24fD9ohiLN5hagG/kWCYj4K5oaxxtj2Z0Dig==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/import-fresh": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", + "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "dev": true, + "license": "MIT", + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true, + "license": "ISC" + }, + "node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true, + "license": "MIT" + }, + "node_modules/optionator": { + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.5" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "license": "MIT", + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/word-wrap": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000000000..5e992ba9d0f61 --- /dev/null +++ b/package.json @@ -0,0 +1,32 @@ +{ + "name": "uBlock", + "version": "1.0.0", + "description": "npm dev tools", + "main": "index.js", + "scripts": { + "lint": "eslint ./src/js/*.js ./src/js/**/*.js ./**/*.json ./platform/**/*.js ", + "test": "echo \"Error: no test specified\" && exit 1" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/gorhill/uBlock.git" + }, + "author": "Raymond Hill", + "license": "GPLv3", + "bugs": { + "url": "https://github.com/gorhill/uBlock/issues" + }, + "homepage": "https://github.com/gorhill/uBlock#readme", + "engines": { + "node": ">=22", + "npm": ">=11" + }, + "devDependencies": { + "@eslint/compat": "^1.2.4", + "@eslint/js": "^9.17.0", + "@eslint/json": "^0.9.0", + "eslint": "^9.17.0", + "eslint-formatter-compact": "^8.40.0", + "globals": "^15.14.0" + } +} diff --git a/platform/browser/main.js b/platform/browser/main.js deleted file mode 100644 index d6f6acb9c69b1..0000000000000 --- a/platform/browser/main.js +++ /dev/null @@ -1,123 +0,0 @@ -/******************************************************************************* - - uBlock Origin - a browser extension to block requests. - Copyright (C) 2014-present Raymond Hill - - 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/}. - - Home: https://github.com/gorhill/uBlock -*/ - -'use strict'; - -/******************************************************************************/ - -import publicSuffixList from './lib/publicsuffixlist/publicsuffixlist.js'; -import punycode from './lib/punycode.js'; - -import staticNetFilteringEngine from './js/static-net-filtering.js'; -import { FilteringContext } from './js/filtering-context.js'; -import { LineIterator } from './js/text-utils.js'; -import * as sfp from './js/static-filtering-parser.js'; - -import { - CompiledListReader, - CompiledListWriter -} from './js/static-filtering-io.js'; - -/******************************************************************************/ - -function compileList(rawText, writer) { - const lineIter = new LineIterator(rawText); - const parser = new sfp.AstFilterParser({ - interactive: true, - maxTokenLength: staticNetFilteringEngine.MAX_TOKEN_LENGTH, - }); - const compiler = staticNetFilteringEngine.createCompiler(); - - while ( lineIter.eot() === false ) { - let line = lineIter.next(); - - while ( line.endsWith(' \\') ) { - if ( lineIter.peek(4) !== ' ' ) { break; } - line = line.slice(0, -2).trim() + lineIter.next().trim(); - } - parser.parse(line); - - if ( parser.isFilter() === false ) { continue; } - if ( parser.isNetworkFilter() === false ) { continue; } - if ( compiler.compile(parser, writer) ) { continue; } - if ( compiler.error !== undefined ) { - console.info(JSON.stringify({ - realm: 'message', - type: 'error', - text: compiler.error - })); - } - } - - return writer.toString(); -} - -function applyList(name, raw) { - const writer = new CompiledListWriter(); - writer.properties.set('name', name); - const compiled = compileList(raw, writer); - const reader = new CompiledListReader(compiled); - staticNetFilteringEngine.fromCompiled(reader); -} - -function enableWASM(path) { - return Promise.all([ - publicSuffixList.enableWASM(`${path}/lib/publicsuffixlist`), - staticNetFilteringEngine.enableWASM(`${path}/js`), - ]); -} - -function pslInit(raw) { - if ( typeof raw !== 'string' || raw.trim() === '' ) { - console.info('Unable to populate public suffix list'); - return; - } - publicSuffixList.parse(raw, punycode.toASCII); - console.info('Public suffix list populated'); -} - -function restart(lists) { - // Remove all filters - reset(); - - if ( Array.isArray(lists) && lists.length !== 0 ) { - // Populate filtering engine with filter lists - for ( const { name, raw } of lists ) { - applyList(name, raw); - } - // Commit changes - staticNetFilteringEngine.freeze(); - staticNetFilteringEngine.optimize(); - } - - return staticNetFilteringEngine; -} - -function reset() { - staticNetFilteringEngine.reset(); -} - -export { - FilteringContext, - enableWASM, - pslInit, - restart, -}; diff --git a/platform/browser/test.html b/platform/browser/test.html deleted file mode 100644 index 32b1aba8e1a2c..0000000000000 --- a/platform/browser/test.html +++ /dev/null @@ -1,71 +0,0 @@ - - - - -uBO Static Network Filtering Engine - - - - - diff --git a/platform/chromium/is-webrtc-supported.html b/platform/chromium/is-webrtc-supported.html index d30b674b1b38d..58238b9469250 100644 --- a/platform/chromium/is-webrtc-supported.html +++ b/platform/chromium/is-webrtc-supported.html @@ -3,7 +3,7 @@ - + diff --git a/platform/chromium/is-webrtc-supported.js b/platform/chromium/is-webrtc-supported.js index 8841370a1050c..263bb686bf99a 100644 --- a/platform/chromium/is-webrtc-supported.js +++ b/platform/chromium/is-webrtc-supported.js @@ -30,11 +30,9 @@ // collected. (function() { - 'use strict'; - - var pc = null; + let pc = null; try { - var PC = self.RTCPeerConnection || self.webkitRTCPeerConnection; + const PC = self.RTCPeerConnection || self.webkitRTCPeerConnection; if ( PC ) { pc = new PC(null); } diff --git a/platform/chromium/vapi-background-ext.js b/platform/chromium/vapi-background-ext.js index acbdc3e9d7b74..34367a76b23fa 100644 --- a/platform/chromium/vapi-background-ext.js +++ b/platform/chromium/vapi-background-ext.js @@ -224,7 +224,7 @@ vAPI.scriptletsInjector = (( ) => { script.appendChild(doc.createTextNode(details.scriptlets)); (doc.head || doc.documentElement).appendChild(script); self.uBO_scriptletsInjected = details.filters; - } catch (ex) { + } catch { } if ( script ) { script.remove(); diff --git a/platform/common/vapi-background.js b/platform/common/vapi-background.js index fac5121c00082..5fcff01f87311 100644 --- a/platform/common/vapi-background.js +++ b/platform/common/vapi-background.js @@ -320,7 +320,7 @@ vAPI.Tabs = class { try { result = await webext.tabs.executeScript(...args); } - catch(reason) { + catch { } return Array.isArray(result) ? result : []; } @@ -334,7 +334,7 @@ vAPI.Tabs = class { try { tab = await webext.tabs.get(tabId); } - catch(reason) { + catch { } return tab instanceof Object ? tab : null; } @@ -351,7 +351,7 @@ vAPI.Tabs = class { try { await webext.tabs.insertCSS(...arguments); } - catch(reason) { + catch { } } @@ -360,7 +360,7 @@ vAPI.Tabs = class { try { tabs = await webext.tabs.query(queryInfo); } - catch(reason) { + catch { } return Array.isArray(tabs) ? tabs : []; } @@ -372,7 +372,7 @@ vAPI.Tabs = class { try { await webext.tabs.removeCSS(...arguments); } - catch(reason) { + catch { } } @@ -530,7 +530,7 @@ vAPI.Tabs = class { try { tab = await webext.tabs.update(...arguments); } - catch (reason) { + catch { } return tab instanceof Object ? tab : null; } @@ -556,7 +556,7 @@ vAPI.Tabs = class { try { await webext.tabs.remove(tabId); } - catch (reason) { + catch { } } @@ -569,7 +569,7 @@ vAPI.Tabs = class { { bypassCache: bypassCache === true } ); } - catch (reason) { + catch { } } @@ -668,7 +668,7 @@ if ( webext.windows instanceof Object ) { try { win = await webext.windows.get(...arguments); } - catch (reason) { + catch { } return win instanceof Object ? win : null; }, @@ -677,7 +677,7 @@ if ( webext.windows instanceof Object ) { try { win = await webext.windows.create(...arguments); } - catch (reason) { + catch { } return win instanceof Object ? win : null; }, @@ -686,7 +686,7 @@ if ( webext.windows instanceof Object ) { try { win = await webext.windows.update(...arguments); } - catch (reason) { + catch { } return win instanceof Object ? win : null; }, @@ -702,7 +702,7 @@ if ( webext.browserAction instanceof Object ) { try { await webext.browserAction.setTitle(...arguments); } - catch (reason) { + catch { } }, }; @@ -712,28 +712,28 @@ if ( webext.browserAction instanceof Object ) { try { await webext.browserAction.setBadgeTextColor(...arguments); } - catch (reason) { + catch { } }; vAPI.browserAction.setBadgeBackgroundColor = async function() { try { await webext.browserAction.setBadgeBackgroundColor(...arguments); } - catch (reason) { + catch { } }; vAPI.browserAction.setBadgeText = async function() { try { await webext.browserAction.setBadgeText(...arguments); } - catch (reason) { + catch { } }; vAPI.browserAction.setIcon = async function() { try { await webext.browserAction.setIcon(...arguments); } - catch (reason) { + catch { } }; } @@ -807,7 +807,7 @@ if ( webext.browserAction instanceof Object ) { let data; try { data = ctx.getImageData(0, 0, w, h); - } catch(ex) { + } catch { } return data; }; @@ -1084,7 +1084,7 @@ vAPI.messaging = { msgId: this.msgId, msg: response !== undefined ? response : null, }); - } catch (ex) { + } catch { this.messaging.onPortDisconnect(this.port); } // Store for reuse @@ -1473,7 +1473,7 @@ vAPI.adminStorage = (( ) => { let store; try { store = await webext.storage.managed.get(); - } catch(ex) { + } catch { } vAPI.storage.set({ cachedManagedStorage: store || {} }); }; @@ -1488,7 +1488,7 @@ vAPI.adminStorage = (( ) => { } else { bin = bin.cachedManagedStorage; } - } catch(ex) { + } catch { bin = {}; } cacheManagedStorage(); @@ -1688,7 +1688,7 @@ vAPI.cloud = (( ) => { // operation to fail. try { await deleteChunks(datakey, chunkCount + 1); - } catch (reason) { + } catch { } // Push the data to browser-provided cloud storage. @@ -1742,7 +1742,7 @@ vAPI.cloud = (( ) => { if ( typeof entry === 'string' ) { entry = JSON.parse(entry); } - } catch(_) { + } catch { } return entry; }; @@ -1763,7 +1763,7 @@ vAPI.cloud = (( ) => { webext.storage.sync.getBytesInUse(keys), webext.storage.sync.getBytesInUse(null), ]); - } catch(ex) { + } catch { } if ( Array.isArray(results) === false ) { return; } return { used: results[0], total: results[1], max: QUOTA_BYTES }; diff --git a/platform/common/vapi-client.js b/platform/common/vapi-client.js index a6d46c6259078..afa939d1a73bd 100644 --- a/platform/common/vapi-client.js +++ b/platform/common/vapi-client.js @@ -165,7 +165,7 @@ vAPI.messaging = { } try { this.port = browser.runtime.connect({name: vAPI.sessionId}) || null; - } catch (ex) { + } catch { this.port = null; } // Not having a valid port at this point means the main process is diff --git a/platform/dig/snfe.js b/platform/dig/snfe.js index 70908285bf33b..f0b4e55ab2277 100644 --- a/platform/dig/snfe.js +++ b/platform/dig/snfe.js @@ -19,22 +19,12 @@ Home: https://github.com/gorhill/uBlock */ -/* eslint-disable-next-line no-redeclare */ /* globals process */ -'use strict'; - -/******************************************************************************/ - -import { strict as assert } from 'assert'; +import { StaticNetFilteringEngine, enableWASM } from './index.js'; +import { mkdir, readFile, writeFile } from 'fs/promises'; import { createRequire } from 'module'; -import { readFile, writeFile, mkdir } from 'fs/promises'; import { dirname } from 'path'; -import { fileURLToPath } from 'url'; - -const __dirname = dirname(fileURLToPath(import.meta.url)); - -import { enableWASM, StaticNetFilteringEngine } from './index.js'; /******************************************************************************/ diff --git a/platform/firefox/vapi-background-ext.js b/platform/firefox/vapi-background-ext.js index 5710224a34adb..9e0d8d357c297 100644 --- a/platform/firefox/vapi-background-ext.js +++ b/platform/firefox/vapi-background-ext.js @@ -373,7 +373,7 @@ vAPI.scriptletsInjector = (( ) => { script = doc.createElement('script'); script.appendChild(doc.createTextNode(code)); (doc.head || doc.documentElement).appendChild(script); - } catch (ex) { + } catch { } if ( script ) { script.remove(); @@ -400,7 +400,7 @@ vAPI.scriptletsInjector = (( ) => { script.src = url; (doc.head || doc.documentElement || doc).append(script); self.uBO_scriptletsInjected = details.filters; - } catch (ex) { + } catch { } if ( url ) { if ( script ) { script.remove(); } diff --git a/platform/firefox/webext.js b/platform/firefox/webext.js index e9bff76305024..67ad05dd3789e 100644 --- a/platform/firefox/webext.js +++ b/platform/firefox/webext.js @@ -19,6 +19,4 @@ Home: https://github.com/gorhill/uBlock */ -'use strict'; - export default browser; diff --git a/platform/mv3/make-rulesets.js b/platform/mv3/make-rulesets.js index 3c4660f0897c4..6df89f91891f8 100644 --- a/platform/mv3/make-rulesets.js +++ b/platform/mv3/make-rulesets.js @@ -125,7 +125,7 @@ const fetchText = (url, cacheDir) => { const content = data.join(''); try { writeFile(`${cacheDir}/${fname}`, content); - } catch (ex) { + } catch { } resolve({ url, content }); }); diff --git a/platform/mv3/safe-replace.js b/platform/mv3/safe-replace.js index b56e5e5af5781..6f417b20b733c 100644 --- a/platform/mv3/safe-replace.js +++ b/platform/mv3/safe-replace.js @@ -19,10 +19,6 @@ Home: https://github.com/gorhill/uBlock */ -'use strict'; - -/******************************************************************************/ - export function safeReplace(text, pattern, replacement, count = 1) { const rePattern = typeof pattern === 'string' ? new RegExp(pattern.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')) diff --git a/platform/nodejs/build.js b/platform/nodejs/build.js index dbc08437c6dd1..eb22693ff8bf0 100644 --- a/platform/nodejs/build.js +++ b/platform/nodejs/build.js @@ -19,12 +19,7 @@ Home: https://github.com/gorhill/uBlock */ -'use strict'; - -/******************************************************************************/ - import fs from 'fs'; - import { pslInit } from './index.js'; /******************************************************************************/ diff --git a/platform/nodejs/index.js b/platform/nodejs/index.js index 56aa74f42c67c..3b82cb9ab383e 100644 --- a/platform/nodejs/index.js +++ b/platform/nodejs/index.js @@ -19,6 +19,8 @@ Home: https://github.com/gorhill/uBlock */ +/* globals process */ + import * as s14e from './js/s14e-serializer.js'; import * as sfp from './js/static-filtering-parser.js'; @@ -188,7 +190,7 @@ async function useLists(lists, options = {}) { useLists.promise = Promise.all(promises); await useLists.promise; - useLists.promise = null; // eslint-disable-line require-atomic-updates + useLists.promise = null; // Commit changes snfe.freeze(); @@ -283,7 +285,7 @@ class StaticNetFilteringEngine { // module.exports. Once all included files are written like ES modules, using // export statements, this should no longer be necessary. if ( typeof module !== 'undefined' && typeof exports !== 'undefined' ) { - module.exports = exports; + module.exports = exports; // eslint-disable-line no-undef } export { diff --git a/platform/npm/.eslintrc.json b/platform/npm/.eslintrc.json deleted file mode 100644 index 5f7c6b58c5ccf..0000000000000 --- a/platform/npm/.eslintrc.json +++ /dev/null @@ -1,38 +0,0 @@ -{ - "root": true, - "env": { - "es2021": true, - "node": true - }, - "extends": "eslint:recommended", - "parserOptions": { - "ecmaVersion": 12, - "sourceType": "module" - }, - "rules": { - "eqeqeq": [ "warn", "always" ], - "indent": [ - "warn", - 4, - { - "ArrayExpression": "first", - "CallExpression": { "arguments": "first" }, - "MemberExpression": "off", - "ObjectExpression": "off", - "ignoreComments": true, - "ignoredNodes": [ - "AssignmentExpression:has(Literal)" - ] - } - ], - "getter-return": "off", - "no-control-regex": "off", - "no-empty": [ "error", { "allowEmptyCatch": true } ], - "no-promise-executor-return": [ "error" ], - "no-template-curly-in-string": [ "error" ], - "no-unreachable-loop": [ "error" ], - "no-useless-backreference": [ "error" ], - "no-useless-escape": "off", - "require-atomic-updates": [ "warn" ] - } -} diff --git a/platform/npm/demo.js b/platform/npm/demo.js index bc0ca87a1f7e3..8baec24e47c52 100644 --- a/platform/npm/demo.js +++ b/platform/npm/demo.js @@ -31,8 +31,8 @@ * * */ -import fs from 'fs/promises'; import { StaticNetFilteringEngine } from '@gorhill/ubo-core'; +import fs from 'fs/promises'; /******************************************************************************/ diff --git a/platform/npm/package.json b/platform/npm/package.json index 6430bec8e52c9..190d7e8577cde 100644 --- a/platform/npm/package.json +++ b/platform/npm/package.json @@ -6,7 +6,6 @@ "main": "index.js", "scripts": { "build": "node build.js", - "lint": "eslint js/ *.js tests/*.js", "test": "c8 --include=index.js --include=js/**/*.js node test.js --mocha", "test-full-battery": "c8 --include=index.js --include=js/**/*.js node test.js --mocha --full-battery", "check-leaks": "mocha --check-leaks tests/leaks.js" @@ -36,7 +35,6 @@ }, "devDependencies": { "c8": "^7.12.0", - "eslint": "^8.29.0", "esm-world": "0.1.3", "mocha": "^10.2.0", "scaling-palm-tree": "github:mjethani/scaling-palm-tree#15cf1ab37e038771e1ff8005edc46d95f176739f" diff --git a/platform/npm/test.js b/platform/npm/test.js index 5b4401e5945d6..25ff9e74302ec 100644 --- a/platform/npm/test.js +++ b/platform/npm/test.js @@ -19,15 +19,10 @@ Home: https://github.com/gorhill/uBlock */ -/* eslint-disable-next-line no-redeclare */ /* globals process */ -'use strict'; - -/******************************************************************************/ - -import { spawn } from "child_process"; import { promisify } from 'util'; +import { spawn } from "child_process"; /******************************************************************************/ diff --git a/src/js/about.js b/src/js/about.js index 680fab146f895..202f3489bb3c8 100644 --- a/src/js/about.js +++ b/src/js/about.js @@ -19,8 +19,6 @@ Home: https://github.com/gorhill/uBlock */ -'use strict'; - import { dom } from './dom.js'; /******************************************************************************/ diff --git a/src/js/advanced-settings.js b/src/js/advanced-settings.js index c21346f58efee..de7da56b25b69 100644 --- a/src/js/advanced-settings.js +++ b/src/js/advanced-settings.js @@ -21,8 +21,6 @@ /* global CodeMirror, uBlockDashboard */ -'use strict'; - import { dom, qs$ } from './dom.js'; /******************************************************************************/ diff --git a/src/js/asset-viewer.js b/src/js/asset-viewer.js index 351531b62b6de..62f90d2795013 100644 --- a/src/js/asset-viewer.js +++ b/src/js/asset-viewer.js @@ -21,12 +21,8 @@ /* global CodeMirror, uBlockDashboard */ -'use strict'; - -/******************************************************************************/ - -import { dom, qs$ } from './dom.js'; import './codemirror/ubo-static-filtering.js'; +import { dom, qs$ } from './dom.js'; /******************************************************************************/ diff --git a/src/js/assets.js b/src/js/assets.js index 4f02700f8c46e..778af87f03510 100644 --- a/src/js/assets.js +++ b/src/js/assets.js @@ -319,7 +319,7 @@ assets.fetch = function(url, options = {}) { xhr.responseType = options.responseType || 'text'; xhr.send(); timeoutTimer.on({ sec: timeoutAfter }); - } catch (e) { + } catch { onErrorEvent.call(xhr); } @@ -396,7 +396,7 @@ assets.fetchFilterList = async function(mainlistURL) { const toParsedURL = url => { try { return new URL(url.trim()); - } catch (ex) { + } catch { } }; @@ -622,7 +622,7 @@ function updateAssetSourceRegistry(json, silent = false) { Array.from(Object.entries(newDict)) .filter(a => a[1].content === 'filters' && a[1].off === undefined) .map(a => a[0]); - } catch (ex) { + } catch { } if ( newDict instanceof Object === false ) { return; } diff --git a/src/js/base64-custom.js b/src/js/base64-custom.js index 0d9a43fa2275d..9d15d7182822f 100644 --- a/src/js/base64-custom.js +++ b/src/js/base64-custom.js @@ -19,8 +19,6 @@ Home: https://github.com/gorhill/uBlock */ -'use strict'; - /******************************************************************************/ // Custom base64 codecs. These codecs are meant to encode/decode typed arrays diff --git a/src/js/benchmarks.js b/src/js/benchmarks.js index 59996b0f63746..f9875694e4039 100644 --- a/src/js/benchmarks.js +++ b/src/js/benchmarks.js @@ -89,7 +89,7 @@ const loadBenchmarkDataset = (( ) => { if ( details.content.startsWith('[') ) { try { requests = JSON.parse(details.content); - } catch(ex) { + } catch { } } else { const lineIter = new LineIterator(details.content); @@ -99,7 +99,7 @@ const loadBenchmarkDataset = (( ) => { if ( line === '' ) { continue; } try { parsed.push(JSON.parse(line)); - } catch(ex) { + } catch { parsed.length = 0; break; } diff --git a/src/js/cachestorage.js b/src/js/cachestorage.js index f946bc73b92ff..97b8b6f053d10 100644 --- a/src/js/cachestorage.js +++ b/src/js/cachestorage.js @@ -480,7 +480,7 @@ const idbStorage = (( ) => { try { const db = ev.target.result; db.createObjectStore(STORAGE_NAME, { keyPath: 'key' }); - } catch(ex) { + } catch { req.onerror(); } }; diff --git a/src/js/click2load.js b/src/js/click2load.js index b441d973e8845..4a9f489eba503 100644 --- a/src/js/click2load.js +++ b/src/js/click2load.js @@ -19,8 +19,6 @@ Home: https://github.com/gorhill/uBlock */ -'use strict'; - /******************************************************************************/ /******************************************************************************/ diff --git a/src/js/cloud-ui.js b/src/js/cloud-ui.js index 228f1140b6519..26bcd03a33ce6 100644 --- a/src/js/cloud-ui.js +++ b/src/js/cloud-ui.js @@ -19,12 +19,8 @@ Home: https://github.com/gorhill/uBlock */ -/* global faIconsInit */ - -'use strict'; - -import { i18n, i18n$ } from './i18n.js'; import { dom, qs$ } from './dom.js'; +import { i18n, i18n$ } from './i18n.js'; import { faIconsInit } from './fa-icons.js'; /******************************************************************************/ diff --git a/src/js/code-viewer.js b/src/js/code-viewer.js index f11289ad0b755..83d5de98a7270 100644 --- a/src/js/code-viewer.js +++ b/src/js/code-viewer.js @@ -21,10 +21,6 @@ /* globals CodeMirror, uBlockDashboard, beautifier */ -'use strict'; - -/******************************************************************************/ - import { dom, qs$ } from './dom.js'; import { getActualTheme } from './theme.js'; @@ -117,25 +113,25 @@ async function fetchResource(url) { } }; switch ( mime ) { - case 'text/css': - text = beautifier.css(text, beautifierOptions); - break; - case 'text/html': - case 'application/xhtml+xml': - case 'application/xml': - case 'image/svg+xml': - text = beautifier.html(text, beautifierOptions); - break; - case 'text/javascript': - case 'application/javascript': - case 'application/x-javascript': - text = beautifier.js(text, beautifierOptions); - break; - case 'application/json': - text = beautifier.js(text, beautifierOptions); - break; - default: - break; + case 'text/css': + text = beautifier.css(text, beautifierOptions); + break; + case 'text/html': + case 'application/xhtml+xml': + case 'application/xml': + case 'image/svg+xml': + text = beautifier.html(text, beautifierOptions); + break; + case 'text/javascript': + case 'application/javascript': + case 'application/x-javascript': + text = beautifier.js(text, beautifierOptions); + break; + case 'application/json': + text = beautifier.js(text, beautifierOptions); + break; + default: + break; } return { mime, text }; } @@ -182,7 +178,7 @@ async function setURL(resourceURL) { const url = new URL(resourceURL, currentURL || undefined); url.hash = ''; afterURL = url.href; - } catch(ex) { + } catch { } if ( afterURL === undefined ) { return; } } else { diff --git a/src/js/codemirror/search-thread.js b/src/js/codemirror/search-thread.js index 3a4416ff8beba..6697d508c600c 100644 --- a/src/js/codemirror/search-thread.js +++ b/src/js/codemirror/search-thread.js @@ -19,8 +19,6 @@ Home: https://github.com/gorhill/uBlock */ -'use strict'; - /******************************************************************************/ (( ) => { @@ -43,7 +41,7 @@ if ( let reSearch; try { reSearch = new RegExp(details.pattern, details.flags); - } catch(ex) { + } catch { return; } @@ -88,11 +86,14 @@ if ( content = msg.content; break; - case 'doSearch': + case 'doSearch': { const response = doSearch(msg); self.postMessage({ id: msg.id, response }); break; } + default: + break; + } }; return; diff --git a/src/js/codemirror/search.js b/src/js/codemirror/search.js index 7ee5b33899f05..dabc5c191b4fe 100644 --- a/src/js/codemirror/search.js +++ b/src/js/codemirror/search.js @@ -226,7 +226,7 @@ import { i18n$ } from '../i18n.js'; query = re.source; flags = re.flags; } - catch (e) { + catch { reParsed = null; } } diff --git a/src/js/codemirror/ubo-dynamic-filtering.js b/src/js/codemirror/ubo-dynamic-filtering.js index d0709a4f871eb..6a6e18ac901b7 100644 --- a/src/js/codemirror/ubo-dynamic-filtering.js +++ b/src/js/codemirror/ubo-dynamic-filtering.js @@ -21,8 +21,6 @@ /* global CodeMirror */ -'use strict'; - CodeMirror.defineMode('ubo-dynamic-filtering', ( ) => { const validSwitches = new Set([ @@ -69,7 +67,7 @@ CodeMirror.defineMode('ubo-dynamic-filtering', ( ) => { hnValidator.hostname = '_'; try { hnValidator.hostname = hnin; - } catch(_) { + } catch { return false; } const hnout = hnValidator.hostname; diff --git a/src/js/commands.js b/src/js/commands.js index 2f29b23d91741..34e6312f580e7 100644 --- a/src/js/commands.js +++ b/src/js/commands.js @@ -19,12 +19,8 @@ Home: https://github.com/gorhill/uBlock */ -'use strict'; - -/******************************************************************************/ - -import µb from './background.js'; import { hostnameFromURI } from './uri-utils.js'; +import µb from './background.js'; /******************************************************************************/ diff --git a/src/js/console.js b/src/js/console.js index 410abbd841a63..255fb63834de9 100644 --- a/src/js/console.js +++ b/src/js/console.js @@ -19,8 +19,6 @@ Home: https://github.com/gorhill/uBlock */ -'use strict'; - /******************************************************************************/ function ubologSet(state = false) { diff --git a/src/js/contentscript-extra.js b/src/js/contentscript-extra.js index 21feb1d390b32..9a60dabf782bf 100644 --- a/src/js/contentscript-extra.js +++ b/src/js/contentscript-extra.js @@ -508,7 +508,7 @@ class PSelectorRoot extends PSelector { prime(input) { try { return super.prime(input); - } catch (ex) { + } catch { } return []; } diff --git a/src/js/contentscript.js b/src/js/contentscript.js index 46df837bee235..1f6a97111666a 100644 --- a/src/js/contentscript.js +++ b/src/js/contentscript.js @@ -125,7 +125,7 @@ vAPI.contentScript = true; ) { context = context.parent; } - } catch(ex) { + } catch { } vAPI.effectiveSelf = context; } @@ -360,7 +360,7 @@ vAPI.SafeAnimationFrame = class { if ( addedNodes.length === 0 && removedNodes === false ) { return; } for ( const listener of getListenerIterator() ) { try { listener.onDOMChanged(addedNodes, removedNodes); } - catch (ex) { } + catch { } } addedNodes.length = 0; removedNodes = false; @@ -422,7 +422,7 @@ vAPI.SafeAnimationFrame = class { listenerIteratorDirty = true; if ( domLayoutObserver === undefined ) { return; } try { listener.onDOMCreated(); } - catch (ex) { } + catch { } startMutationObserver(); }; @@ -450,7 +450,7 @@ vAPI.SafeAnimationFrame = class { const start = function() { for ( const listener of getListenerIterator() ) { try { listener.onDOMCreated(); } - catch (ex) { } + catch { } } startMutationObserver(); }; diff --git a/src/js/contextmenu.js b/src/js/contextmenu.js index a2b01e9a1f9c3..a158f2408af86 100644 --- a/src/js/contextmenu.js +++ b/src/js/contextmenu.js @@ -91,7 +91,7 @@ const onSubscribeToList = function(details) { try { parsedURL = new URL(details.linkUrl); } - catch(ex) { + catch { } if ( parsedURL instanceof URL === false ) { return; } const url = parsedURL.searchParams.get('location'); diff --git a/src/js/dashboard-common.js b/src/js/dashboard-common.js index feceb1f623d77..b761fa9b9b0cd 100644 --- a/src/js/dashboard-common.js +++ b/src/js/dashboard-common.js @@ -19,8 +19,6 @@ Home: https://github.com/gorhill/uBlock */ -'use strict'; - import { dom } from './dom.js'; /******************************************************************************/ diff --git a/src/js/dashboard.js b/src/js/dashboard.js index 3ba16f0e4dc82..3975a039ed43a 100644 --- a/src/js/dashboard.js +++ b/src/js/dashboard.js @@ -19,8 +19,6 @@ Home: https://github.com/gorhill/uBlock */ -'use strict'; - import { dom, qs$ } from './dom.js'; /******************************************************************************/ @@ -118,7 +116,7 @@ if ( self.location.hash.slice(1) === 'no-dashboard.html' ) { if ( iframe.src !== '' ) { iframe.src = ''; } - } catch(ex) { + } catch { } vAPI.defer.once(250).then(( ) => check()); }; diff --git a/src/js/diff-updater.js b/src/js/diff-updater.js index 4e6ece1556b1c..8a3c5e9e31fb1 100644 --- a/src/js/diff-updater.js +++ b/src/js/diff-updater.js @@ -19,15 +19,13 @@ Home: https://github.com/gorhill/uBlock */ -'use strict'; - // This module can be dynamically loaded or spun off as a worker. /******************************************************************************/ const patches = new Map(); const encoder = new TextEncoder(); -const reFileName = /([^\/]+?)(?:#.+)?$/; +const reFileName = /([^/]+?)(?:#.+)?$/; const EMPTYLINE = ''; /******************************************************************************/ @@ -50,8 +48,7 @@ const basename = url => { const resolveURL = (path, url) => { try { return new URL(path, url); - } - catch(_) { + } catch { } }; diff --git a/src/js/document-blocked.js b/src/js/document-blocked.js index 0bccb06a208e8..2d7f28a7ae113 100644 --- a/src/js/document-blocked.js +++ b/src/js/document-blocked.js @@ -85,7 +85,7 @@ const urlToFragment = raw => { b.append(hn); fragment.append(raw.slice(0,i), b, raw.slice(i+hn.length)); return fragment; - } catch(_) { + } catch { } return raw; }; @@ -157,7 +157,7 @@ if ( typeof details.to === 'string' && details.to.length !== 0 ) { let url; try { url = new URL(rawURL); - } catch(ex) { + } catch { return false; } diff --git a/src/js/dom-inspector.js b/src/js/dom-inspector.js index a0d334b762a35..a6ef775708e54 100644 --- a/src/js/dom-inspector.js +++ b/src/js/dom-inspector.js @@ -19,9 +19,6 @@ Home: https://github.com/gorhill/uBlock */ -'use strict'; - -/******************************************************************************/ /******************************************************************************/ const svgRoot = document.querySelector('svg'); @@ -36,20 +33,20 @@ const shutdown = ( ) => { const contentInspectorChannel = ev => { const msg = ev.data || {}; switch ( msg.what ) { - case 'quitInspector': { - shutdown(); - break; - } - case 'svgPaths': { - const paths = svgRoot.children; - paths[0].setAttribute('d', msg.paths[0]); - paths[1].setAttribute('d', msg.paths[1]); - paths[2].setAttribute('d', msg.paths[2]); - paths[3].setAttribute('d', msg.paths[3]); - break; - } - default: - break; + case 'quitInspector': { + shutdown(); + break; + } + case 'svgPaths': { + const paths = svgRoot.children; + paths[0].setAttribute('d', msg.paths[0]); + paths[1].setAttribute('d', msg.paths[1]); + paths[2].setAttribute('d', msg.paths[2]); + paths[3].setAttribute('d', msg.paths[3]); + break; + } + default: + break; } }; diff --git a/src/js/dyna-rules.js b/src/js/dyna-rules.js index 69eef8519bfff..1bb1d097b0669 100644 --- a/src/js/dyna-rules.js +++ b/src/js/dyna-rules.js @@ -21,15 +21,11 @@ /* global CodeMirror, diff_match_patch, uBlockDashboard */ -'use strict'; - -import publicSuffixList from '../lib/publicsuffixlist/publicsuffixlist.js'; - +import './codemirror/ubo-dynamic-filtering.js'; +import { dom, qs$, qsa$ } from './dom.js'; import { hostnameFromURI } from './uri-utils.js'; import { i18n$ } from './i18n.js'; -import { dom, qs$, qsa$ } from './dom.js'; - -import './codemirror/ubo-dynamic-filtering.js'; +import publicSuffixList from '../lib/publicsuffixlist/publicsuffixlist.js'; /******************************************************************************/ @@ -327,7 +323,7 @@ function handleImportFilePicker() { // https://github.com/chrisaljoudi/uBlock/issues/757 // Support RequestPolicy rule syntax let result = this.result; - let matches = /\[origins-to-destinations\]([^\[]+)/.exec(result); + let matches = /\[origins-to-destinations\]([^[]+)/.exec(result); if ( matches && matches.length === 2 ) { result = matches[1].trim() .replace(/\|/g, ' ') @@ -459,13 +455,12 @@ const onPresentationChanged = (( ) => { thePanes.orig.modified.join('\n'), thePanes.edit.modified.join('\n') ); - const ll = []; let il = 0, lellipsis = false; - const rr = []; let ir = 0, rellipsis = false; + const ll = []; let lellipsis = false; + const rr = []; let rellipsis = false; for ( let i = 0; i < diffs.length; i++ ) { const diff = diffs[i]; if ( diff[0] === 0 ) { lellipsis = rellipsis = true; - il += 1; ir += 1; continue; } if ( diff[0] < 0 ) { @@ -475,7 +470,6 @@ const onPresentationChanged = (( ) => { lellipsis = rellipsis = false; } ll.push(diff[1].trim()); - il += 1; continue; } /* diff[0] > 0 */ @@ -485,7 +479,6 @@ const onPresentationChanged = (( ) => { lellipsis = rellipsis = false; } rr.push(diff[1].trim()); - ir += 1; } if ( lellipsis ) { ll.push('...'); } if ( rellipsis ) { rr.push('...'); } diff --git a/src/js/dynamic-net-filtering.js b/src/js/dynamic-net-filtering.js index ec7a7c906bf3b..4bc53e1a7544b 100644 --- a/src/js/dynamic-net-filtering.js +++ b/src/js/dynamic-net-filtering.js @@ -19,18 +19,12 @@ Home: https://github.com/gorhill/uBlock */ -'use strict'; - -/******************************************************************************/ - -import punycode from '../lib/punycode.js'; - -import { LineIterator } from './text-utils.js'; - import { decomposeHostname, domainFromHostname, } from './uri-utils.js'; +import { LineIterator } from './text-utils.js'; +import punycode from '../lib/punycode.js'; /******************************************************************************/ @@ -73,7 +67,7 @@ const intToActionMap = new Map([ ]); // For performance purpose, as simple tests as possible -const reBadHostname = /[^0-9a-z_.\[\]:%-]/; +const reBadHostname = /[^0-9a-z_.[\]:%-]/; const reNotASCII = /[^\x20-\x7F]/; const decomposedSource = []; const decomposedDestination = []; diff --git a/src/js/fa-icons.js b/src/js/fa-icons.js index 08fee85578d79..82ec1510e988a 100644 --- a/src/js/fa-icons.js +++ b/src/js/fa-icons.js @@ -19,8 +19,6 @@ Home: https://github.com/gorhill/uMatrix */ -'use strict'; - /******************************************************************************/ export const faIconsInit = (( ) => { diff --git a/src/js/filtering-engines.js b/src/js/filtering-engines.js index d72ff9d6451fe..64198b9f3fdba 100644 --- a/src/js/filtering-engines.js +++ b/src/js/filtering-engines.js @@ -19,10 +19,6 @@ Home: https://github.com/gorhill/uBlock */ -'use strict'; - -/******************************************************************************/ - import DynamicHostRuleFiltering from './dynamic-net-filtering.js'; import DynamicSwitchRuleFiltering from './hnswitches.js'; import DynamicURLRuleFiltering from './url-net-filtering.js'; diff --git a/src/js/hnswitches.js b/src/js/hnswitches.js index 9e94a8ee093e3..126b7e4a66528 100644 --- a/src/js/hnswitches.js +++ b/src/js/hnswitches.js @@ -19,16 +19,9 @@ Home: https://github.com/gorhill/uBlock */ -/* jshint bitwise: false */ - -'use strict'; - -/******************************************************************************/ - -import punycode from '../lib/punycode.js'; - -import { decomposeHostname } from './uri-utils.js'; import { LineIterator } from './text-utils.js'; +import { decomposeHostname } from './uri-utils.js'; +import punycode from '../lib/punycode.js'; /******************************************************************************/ diff --git a/src/js/html-filtering.js b/src/js/html-filtering.js index 81d66ee1d9871..ac6dee23cd418 100644 --- a/src/js/html-filtering.js +++ b/src/js/html-filtering.js @@ -19,15 +19,11 @@ Home: https://github.com/gorhill/uBlock */ -'use strict'; - -/******************************************************************************/ - -import logger from './logger.js'; -import µb from './background.js'; -import { sessionFirewall } from './filtering-engines.js'; import { StaticExtFilteringHostnameDB } from './static-ext-filtering-db.js'; import { entityFromDomain } from './uri-utils.js'; +import logger from './logger.js'; +import { sessionFirewall } from './filtering-engines.js'; +import µb from './background.js'; /******************************************************************************/ diff --git a/src/js/httpheader-filtering.js b/src/js/httpheader-filtering.js index 522ea2157d239..58781fb61bf3c 100644 --- a/src/js/httpheader-filtering.js +++ b/src/js/httpheader-filtering.js @@ -19,16 +19,12 @@ Home: https://github.com/gorhill/uBlock */ -'use strict'; - -/******************************************************************************/ - -import logger from './logger.js'; -import µb from './background.js'; +import * as sfp from './static-filtering-parser.js'; +import { StaticExtFilteringHostnameDB } from './static-ext-filtering-db.js'; import { entityFromDomain } from './uri-utils.js'; +import logger from './logger.js'; import { sessionFirewall } from './filtering-engines.js'; -import { StaticExtFilteringHostnameDB } from './static-ext-filtering-db.js'; -import * as sfp from './static-filtering-parser.js'; +import µb from './background.js'; /******************************************************************************/ diff --git a/src/js/logger-ui-inspector.js b/src/js/logger-ui-inspector.js index d90d44a435d41..a45fc578956b6 100644 --- a/src/js/logger-ui-inspector.js +++ b/src/js/logger-ui-inspector.js @@ -88,7 +88,7 @@ const contentInspectorChannel = (( ) => { toContentPort = browser.tabs.connect(tabId, { frameId }); toContentPort.onMessage.addListener(onContentMessage); toContentPort.onDisconnect.addListener(onContentDisconnect); - } catch(_) { + } catch { } }; diff --git a/src/js/logger-ui.js b/src/js/logger-ui.js index 0b9c31dd99d57..19270a2be26a8 100644 --- a/src/js/logger-ui.js +++ b/src/js/logger-ui.js @@ -2156,7 +2156,7 @@ const rowFilterer = (( ) => { reStr = rawPart.slice(1, -1); try { new RegExp(reStr); - } catch(ex) { + } catch { reStr = ''; } } @@ -2937,7 +2937,7 @@ const loggerSettings = (( ) => { if ( Array.isArray(stored.columns) ) { settings.columns = stored.columns; } - } catch(_) { + } catch { } }); diff --git a/src/js/logger.js b/src/js/logger.js index 766188e547542..aa6755d5c0103 100644 --- a/src/js/logger.js +++ b/src/js/logger.js @@ -19,10 +19,6 @@ Home: https://github.com/gorhill/uBlock */ -'use strict'; - -/******************************************************************************/ - import { broadcast, broadcastToAll } from './broadcast.js'; /******************************************************************************/ diff --git a/src/js/lz4.js b/src/js/lz4.js index 608cdd853ceaa..f89d7436f8288 100644 --- a/src/js/lz4.js +++ b/src/js/lz4.js @@ -21,10 +21,6 @@ /* global lz4BlockCodec */ -'use strict'; - -/******************************************************************************/ - import µb from './background.js'; /******************************************************************************* diff --git a/src/js/messaging.js b/src/js/messaging.js index b24a9a5a27560..2a5e7da9529ab 100644 --- a/src/js/messaging.js +++ b/src/js/messaging.js @@ -918,7 +918,7 @@ const fromBase64 = function(encoded) { let u8array; try { u8array = denseBase64.decode(encoded); - } catch(ex) { + } catch { } return Promise.resolve(u8array !== undefined ? u8array : encoded); }; diff --git a/src/js/mrucache.js b/src/js/mrucache.js index 9a16047fa80b2..266783adf24fc 100644 --- a/src/js/mrucache.js +++ b/src/js/mrucache.js @@ -19,8 +19,6 @@ Home: https://github.com/gorhill/uBlock */ -'use strict'; - export class MRUCache { constructor(maxSize) { this.maxSize = maxSize; diff --git a/src/js/pagestore.js b/src/js/pagestore.js index 7adca8482847e..5d67f86f128bd 100644 --- a/src/js/pagestore.js +++ b/src/js/pagestore.js @@ -549,7 +549,7 @@ const PageStore = class { entries = await webext.webNavigation.getAllFrames({ tabId: this.tabId }); - } catch(ex) { + } catch { } if ( Array.isArray(entries) === false ) { return; } const toKeep = new Set(); diff --git a/src/js/popup-fenix.js b/src/js/popup-fenix.js index 9f2af084da0f1..c4ffc7fd79ea9 100644 --- a/src/js/popup-fenix.js +++ b/src/js/popup-fenix.js @@ -19,11 +19,9 @@ Home: https://github.com/gorhill/uBlock */ -'use strict'; - -import punycode from '../lib/punycode.js'; -import { i18n$ } from './i18n.js'; import { dom, qs$, qsa$ } from './dom.js'; +import { i18n$ } from './i18n.js'; +import punycode from '../lib/punycode.js'; /******************************************************************************/ @@ -178,11 +176,11 @@ const formatNumber = function(count) { // a poor's man compact form, which unfortunately is not i18n-friendly. count /= 1000000; if ( count >= 100 ) { - count = Math.floor(count * 10) / 10; + count = Math.floor(count * 10) / 10; } else if ( count > 10 ) { - count = Math.floor(count * 100) / 100; + count = Math.floor(count * 100) / 100; } else { - count = Math.floor(count * 1000) / 1000; + count = Math.floor(count * 1000) / 1000; } return (count).toLocaleString(undefined) + '\u2009M'; }; @@ -469,27 +467,27 @@ function filterFirewallRows() { for ( const elem of elems ) { const on = dom.cl.has(elem, 'on'); switch ( elem.dataset.expr ) { - case 'not': - not = on; - break; - case 'blocked': - dom.cl.toggle(firewallElem, 'showBlocked', !not && on); - dom.cl.toggle(firewallElem, 'hideBlocked', not && on); - break; - case 'allowed': - dom.cl.toggle(firewallElem, 'showAllowed', !not && on); - dom.cl.toggle(firewallElem, 'hideAllowed', not && on); - break; - case 'script': - dom.cl.toggle(firewallElem, 'show3pScript', !not && on); - dom.cl.toggle(firewallElem, 'hide3pScript', not && on); - break; - case 'frame': - dom.cl.toggle(firewallElem, 'show3pFrame', !not && on); - dom.cl.toggle(firewallElem, 'hide3pFrame', not && on); - break; - default: - break; + case 'not': + not = on; + break; + case 'blocked': + dom.cl.toggle(firewallElem, 'showBlocked', !not && on); + dom.cl.toggle(firewallElem, 'hideBlocked', not && on); + break; + case 'allowed': + dom.cl.toggle(firewallElem, 'showAllowed', !not && on); + dom.cl.toggle(firewallElem, 'hideAllowed', not && on); + break; + case 'script': + dom.cl.toggle(firewallElem, 'show3pScript', !not && on); + dom.cl.toggle(firewallElem, 'hide3pScript', not && on); + break; + case 'frame': + dom.cl.toggle(firewallElem, 'show3pFrame', !not && on); + dom.cl.toggle(firewallElem, 'hide3pFrame', not && on); + break; + default: + break; } } } @@ -498,14 +496,14 @@ dom.on('#firewall .filterExpressions', 'click', 'span[data-expr]', ev => { const target = ev.target; dom.cl.toggle(target, 'on'); switch ( target.dataset.expr ) { - case 'blocked': - if ( dom.cl.has(target, 'on') === false ) { break; } - dom.cl.remove('#firewall .filterExpressions span[data-expr="allowed"]', 'on'); - break; - case 'allowed': - if ( dom.cl.has(target, 'on') === false ) { break; } - dom.cl.remove('#firewall .filterExpressions span[data-expr="blocked"]', 'on'); - break; + case 'blocked': + if ( dom.cl.has(target, 'on') === false ) { break; } + dom.cl.remove('#firewall .filterExpressions span[data-expr="allowed"]', 'on'); + break; + case 'allowed': + if ( dom.cl.has(target, 'on') === false ) { break; } + dom.cl.remove('#firewall .filterExpressions span[data-expr="blocked"]', 'on'); + break; } filterFirewallRows(); const elems = qsa$('#firewall .filterExpressions span[data-expr]'); @@ -1194,18 +1192,18 @@ dom.on(document, 'keydown', ev => { if ( ev.isComposing ) { return; } let bypassCache = false; switch ( ev.key ) { - case 'F5': - bypassCache = ev.ctrlKey || ev.metaKey || ev.shiftKey; - break; - case 'r': - if ( (ev.ctrlKey || ev.metaKey) !== true ) { return; } - break; - case 'R': - if ( (ev.ctrlKey || ev.metaKey) !== true ) { return; } - bypassCache = true; - break; - default: - return; + case 'F5': + bypassCache = ev.ctrlKey || ev.metaKey || ev.shiftKey; + break; + case 'r': + if ( (ev.ctrlKey || ev.metaKey) !== true ) { return; } + break; + case 'R': + if ( (ev.ctrlKey || ev.metaKey) !== true ) { return; } + bypassCache = true; + break; + default: + return; } reloadTab(bypassCache); ev.preventDefault(); @@ -1223,7 +1221,7 @@ vAPI.localStorage.getItemAsync('popupExpandExceptions').then(exceptions => { expandExceptions.add(exception); } } - catch(ex) { + catch { } }); diff --git a/src/js/redirect-engine.js b/src/js/redirect-engine.js index d768db546f537..5e7a55884d283 100644 --- a/src/js/redirect-engine.js +++ b/src/js/redirect-engine.js @@ -19,12 +19,8 @@ Home: https://github.com/gorhill/uBlock */ -'use strict'; - -/******************************************************************************/ - -import redirectableResources from './redirect-resources.js'; import { LineIterator, orphanizeString } from './text-utils.js'; +import redirectableResources from './redirect-resources.js'; /******************************************************************************/ diff --git a/src/js/resources/attribute.js b/src/js/resources/attribute.js index ed735eecdd70b..3b33a5061a07e 100644 --- a/src/js/resources/attribute.js +++ b/src/js/resources/attribute.js @@ -49,7 +49,7 @@ export function setAttrFn( let elems; try { elems = document.querySelectorAll(selector); - } catch(_) { + } catch { return false; } for ( const elem of elems ) { @@ -259,7 +259,7 @@ export function removeAttr( safe.uboLog(logPrefix, `Removed attribute '${attr}'`); } } - } catch(ex) { + } catch { } }; const mutationHandler = mutations => { diff --git a/src/js/resources/cookie.js b/src/js/resources/cookie.js index 571514ed8201b..8a7a97076403f 100644 --- a/src/js/resources/cookie.js +++ b/src/js/resources/cookie.js @@ -142,7 +142,7 @@ export function setCookieFn( try { document.cookie = cookieParts.join(''); - } catch(_) { + } catch { } const done = getCookieFn(name) === value; diff --git a/src/js/resources/href-sanitizer.js b/src/js/resources/href-sanitizer.js index 82a9a95c831a4..eb88c27c30bcc 100644 --- a/src/js/resources/href-sanitizer.js +++ b/src/js/resources/href-sanitizer.js @@ -73,7 +73,7 @@ function hrefSanitizer( try { elems = document.querySelectorAll(`a[href="${href}"`); } - catch(ex) { + catch { } for ( const elem of elems ) { elem.setAttribute('href', text); @@ -87,7 +87,7 @@ function hrefSanitizer( try { const url = new URL(text, document.location); return url.href; - } catch(ex) { + } catch { } return ''; }; @@ -101,7 +101,7 @@ function hrefSanitizer( if ( value === null ) { return href } if ( recursive ) { return extractParam(value, source.slice(end)); } return value; - } catch(x) { + } catch { } return href; }; @@ -128,7 +128,7 @@ function hrefSanitizer( try { elems = document.querySelectorAll(selector); } - catch(ex) { + catch { return false; } for ( const elem of elems ) { diff --git a/src/js/resources/localstorage.js b/src/js/resources/localstorage.js index 73e41546e135d..8a6b7629323c6 100644 --- a/src/js/resources/localstorage.js +++ b/src/js/resources/localstorage.js @@ -102,7 +102,7 @@ export function setLocalStorageItemFn( } else { storage.setItem(key, `${value}`); } - } catch(ex) { + } catch { } } registerScriptlet(setLocalStorageItemFn, { diff --git a/src/js/resources/parse-replace.js b/src/js/resources/parse-replace.js index da638735f7140..e7fd488e9d7a2 100644 --- a/src/js/resources/parse-replace.js +++ b/src/js/resources/parse-replace.js @@ -43,7 +43,7 @@ export function parseReplaceFn(s) { const flags = s.slice(parser.separatorEnd); try { return { re: new RegExp(pattern, flags), replacement }; - } catch(_) { + } catch { } } registerScriptlet(parseReplaceFn, { diff --git a/src/js/resources/replace-argument.js b/src/js/resources/replace-argument.js index a53991790e3e6..638eaeee6155b 100644 --- a/src/js/resources/replace-argument.js +++ b/src/js/resources/replace-argument.js @@ -95,8 +95,10 @@ export function trustedReplaceArgument( return context.reflect(); } const argBefore = getArg(context); - if ( safe.RegExp_test.call(reCondition, argBefore) === false ) { - return context.reflect(); + if ( extraArgs.condition !== undefined ) { + if ( safe.RegExp_test.call(reCondition, argBefore) === false ) { + return context.reflect(); + } } const argAfter = replacer && typeof argBefore === 'string' ? argBefore.replace(replacer.re, replacer.replacement) diff --git a/src/js/resources/safe-self.js b/src/js/resources/safe-self.js index afb98722113fa..3766f3e194e40 100644 --- a/src/js/resources/safe-self.js +++ b/src/js/resources/safe-self.js @@ -127,7 +127,7 @@ export function safeSelf() { try { return new RegExp(match[1], match[2] || undefined); } - catch(ex) { + catch { } return /^/; }, @@ -205,7 +205,7 @@ export function safeSelf() { } }; bc.postMessage('areyouready?'); - } catch(_) { + } catch { safe.sendToLogger = (type, ...args) => { const text = safe.toLogText(type, ...args); if ( text === undefined ) { return; } diff --git a/src/js/resources/scriptlets.js b/src/js/resources/scriptlets.js index 37a332425e4ff..20a34f30e26ce 100755 --- a/src/js/resources/scriptlets.js +++ b/src/js/resources/scriptlets.js @@ -231,7 +231,7 @@ function abortCurrentScriptCore( text = self.decodeURIComponent(content); break; } - } catch(ex) { + } catch { } scriptTexts.set(elem, text); return text; @@ -644,7 +644,7 @@ function matchObjectProperties(propNeedles, ...objs) { if ( value === undefined ) { continue; } if ( typeof value !== 'string' ) { try { value = safe.JSON_stringify(value); } - catch(ex) { } + catch { } if ( typeof value !== 'string' ) { continue; } } if ( safe.testPattern(details, value) ) { continue; } @@ -853,7 +853,7 @@ function preventXhrFn( const safeDispatchEvent = (xhr, type) => { try { xhr.dispatchEvent(new Event(type)); - } catch(_) { + } catch { } }; const XHRBefore = XMLHttpRequest.prototype; @@ -1295,7 +1295,7 @@ function addEventListenerDefuser( } else { h = String(callArgs[1]); } - } catch(ex) { + } catch { } if ( type === '' && pattern === '' ) { safe.uboLog(logPrefix, `Called: ${t}\n${h}\n${elementDetails(thisArg)}`); @@ -1429,7 +1429,7 @@ function jsonPruneXhrResponse( } else if ( typeof innerResponse === 'string' ) { try { objBefore = safe.JSON_parse(innerResponse); - } catch(ex) { + } catch { } } if ( typeof objBefore !== 'object' ) { @@ -1681,7 +1681,7 @@ function noFetchIf( responseProps[p] = { value: v }; }); } - catch(ex) {} + catch { } } else if ( responseType !== '' ) { if ( validResponseProps.type.includes(responseType) ) { responseProps.type = { value: responseType }; @@ -1699,7 +1699,7 @@ function noFetchIf( let v = details[prop]; if ( typeof v !== 'string' ) { try { v = safe.JSON_stringify(v); } - catch(ex) { } + catch { } } if ( typeof v !== 'string' ) { continue; } props.set(prop, v); @@ -1721,7 +1721,7 @@ function noFetchIf( break; } } - } catch(ex) { + } catch { } if ( proceed ) { return context.reflect(); @@ -1823,7 +1823,7 @@ function removeClass( node.classList.remove(...tokens); safe.uboLog(logPrefix, 'Removed class(es)'); } - } catch(ex) { + } catch { } if ( mustStay ) { return; } if ( document.readyState !== 'complete' ) { return; } @@ -2342,7 +2342,7 @@ function xmlPrune( pruneFromDoc(xmlDoc); const serializer = new XMLSerializer(); text = serializer.serializeToString(xmlDoc); - } catch(ex) { + } catch { } return text; }; @@ -2636,7 +2636,7 @@ function callNothrow( let r; try { r = Reflect.apply(...args); - } catch(ex) { + } catch { } return r; }, @@ -3023,7 +3023,7 @@ function trustedClickElement( .filter(s => { try { void querySelectorEx(s); - } catch(_) { + } catch { return false; } return true; @@ -3170,7 +3170,7 @@ function trustedPruneInboundObject( if ( extraArgs.dontOverwrite ) { try { objBefore = safe.JSON_parse(safe.JSON_stringify(targetArg)); - } catch(_) { + } catch { objBefore = undefined; } } @@ -3255,7 +3255,7 @@ function trustedReplaceOutboundText( let textBefore = encodedTextBefore; if ( extraArgs.encoding === 'base64' ) { try { textBefore = self.atob(encodedTextBefore); } - catch(ex) { return encodedTextBefore; } + catch { return encodedTextBefore; } } if ( rawPattern === '' ) { safe.uboLog(logPrefix, 'Decoded outbound text:\n', textBefore); @@ -3456,7 +3456,7 @@ function trustedPreventDomBypass( Object.defineProperty(elem, 'contentWindow', { value: self }); } safe.uboLog(logPrefix, 'Bypass prevented'); - } catch(_) { + } catch { } } return r; @@ -3511,7 +3511,7 @@ function trustedOverrideElementMethod( const { thisArg } = context; try { override = thisArg.closest(selector) === thisArg; - } catch(_) { + } catch { } } if ( override === false ) { diff --git a/src/js/resources/set-constant.js b/src/js/resources/set-constant.js index 127f27bbbe910..9ee985914d5f7 100644 --- a/src/js/resources/set-constant.js +++ b/src/js/resources/set-constant.js @@ -57,9 +57,9 @@ export function validateConstantFn(trusted, raw, extraArgs = {}) { if ( Math.abs(raw) > 0x7FFF ) { return; } } else if ( trusted ) { if ( raw.startsWith('json:') ) { - try { value = safe.JSON_parse(raw.slice(5)); } catch(ex) { return; } + try { value = safe.JSON_parse(raw.slice(5)); } catch { return; } } else if ( raw.startsWith('{') && raw.endsWith('}') ) { - try { value = safe.JSON_parse(raw).value; } catch(ex) { return; } + try { value = safe.JSON_parse(raw).value; } catch { return; } } } else { return; diff --git a/src/js/reverselookup-worker.js b/src/js/reverselookup-worker.js index 37b8b652ad875..ffdf5668680ef 100644 --- a/src/js/reverselookup-worker.js +++ b/src/js/reverselookup-worker.js @@ -19,8 +19,6 @@ Home: https://github.com/gorhill/uBlock */ -'use strict'; - /******************************************************************************/ let listEntries = Object.create(null); @@ -212,6 +210,7 @@ const fromExtendedFilter = function(details) { case 8: // HTML filtering // Response header filtering + /* fallthrough */ case 64: { if ( exception !== ((fargs[2] & 0b001) !== 0) ) { break; } const isProcedural = (fargs[2] & 0b010) !== 0; diff --git a/src/js/reverselookup.js b/src/js/reverselookup.js index e7bf24e94aa35..e1fa006d41dbb 100644 --- a/src/js/reverselookup.js +++ b/src/js/reverselookup.js @@ -19,20 +19,15 @@ Home: https://github.com/gorhill/uBlock */ -'use strict'; - -/******************************************************************************/ - -import staticNetFilteringEngine from './static-net-filtering.js'; -import µb from './background.js'; -import { CompiledListWriter } from './static-filtering-io.js'; -import { i18n$ } from './i18n.js'; import * as sfp from './static-filtering-parser.js'; - import { domainFromHostname, hostnameFromURI, } from './uri-utils.js'; +import { CompiledListWriter } from './static-filtering-io.js'; +import { i18n$ } from './i18n.js'; +import staticNetFilteringEngine from './static-net-filtering.js'; +import µb from './background.js'; /******************************************************************************/ diff --git a/src/js/scriptlet-filtering-core.js b/src/js/scriptlet-filtering-core.js index 0fec05c2ba7a2..a585f119ebf6e 100644 --- a/src/js/scriptlet-filtering-core.js +++ b/src/js/scriptlet-filtering-core.js @@ -19,12 +19,8 @@ Home: https://github.com/gorhill/uBlock */ -'use strict'; - -/******************************************************************************/ - -import { redirectEngine as reng } from './redirect-engine.js'; import { StaticExtFilteringHostnameDB } from './static-ext-filtering-db.js'; +import { redirectEngine as reng } from './redirect-engine.js'; /******************************************************************************/ diff --git a/src/js/scriptlet-filtering.js b/src/js/scriptlet-filtering.js index b7a0617722838..722a6fe34402b 100644 --- a/src/js/scriptlet-filtering.js +++ b/src/js/scriptlet-filtering.js @@ -166,7 +166,7 @@ const onScriptletMessageInjector = (( ) => { }; bcSecret.postMessage('iamready!'); self.uBO_bcSecret = bcSecret; - } catch(_) { + } catch { } }.toString(), ')(', diff --git a/src/js/scriptlets/cosmetic-logger.js b/src/js/scriptlets/cosmetic-logger.js index 5d1f1b99af82d..912c18eb70dea 100644 --- a/src/js/scriptlets/cosmetic-logger.js +++ b/src/js/scriptlets/cosmetic-logger.js @@ -19,10 +19,6 @@ Home: https://github.com/gorhill/uBlock */ -/* globals browser */ - -'use strict'; - /******************************************************************************/ (( ) => { @@ -53,7 +49,7 @@ function hasSelector(selector, context = document) { try { return context.querySelector(selector) !== null; } - catch(ex) { + catch { } return false; } @@ -65,7 +61,7 @@ function safeMatchSelector(selector, context) { try { return context.matches(safeSelector); } - catch(ex) { + catch { } return false; } @@ -77,7 +73,7 @@ function safeQuerySelector(selector, context = document) { try { return context.querySelector(safeSelector); } - catch(ex) { + catch { } return null; } diff --git a/src/js/scriptlets/cosmetic-off.js b/src/js/scriptlets/cosmetic-off.js index f1301e2599a81..e38de4efe5f93 100644 --- a/src/js/scriptlets/cosmetic-off.js +++ b/src/js/scriptlets/cosmetic-off.js @@ -19,8 +19,6 @@ Home: https://github.com/gorhill/uBlock */ -'use strict'; - /******************************************************************************/ if ( typeof vAPI === 'object' && vAPI.domFilterer ) { diff --git a/src/js/scriptlets/cosmetic-on.js b/src/js/scriptlets/cosmetic-on.js index 7b3097613db9d..cb10270cffaee 100644 --- a/src/js/scriptlets/cosmetic-on.js +++ b/src/js/scriptlets/cosmetic-on.js @@ -19,8 +19,6 @@ Home: https://github.com/gorhill/uBlock */ -'use strict'; - /******************************************************************************/ if ( typeof vAPI === 'object' && vAPI.domFilterer ) { diff --git a/src/js/scriptlets/cosmetic-report.js b/src/js/scriptlets/cosmetic-report.js index a968d4de0285b..83b8b62bf4327 100644 --- a/src/js/scriptlets/cosmetic-report.js +++ b/src/js/scriptlets/cosmetic-report.js @@ -19,8 +19,6 @@ Home: https://github.com/gorhill/uBlock */ -'use strict'; - /******************************************************************************/ (( ) => { @@ -40,7 +38,7 @@ const hasSelector = selector => { try { return document.querySelector(selector) !== null; } - catch(ex) { + catch { } return false; }; @@ -52,7 +50,7 @@ const safeQuerySelector = selector => { try { return document.querySelector(safeSelector); } - catch(ex) { + catch { } return null; }; diff --git a/src/js/scriptlets/dom-inspector.js b/src/js/scriptlets/dom-inspector.js index a1be150057239..1536d8ac4dee2 100644 --- a/src/js/scriptlets/dom-inspector.js +++ b/src/js/scriptlets/dom-inspector.js @@ -431,7 +431,7 @@ const elementsFromSelector = function(selector, context) { // plain CSS selector try { return context.querySelectorAll(selector); - } catch (ex) { + } catch { } return []; }; @@ -443,7 +443,7 @@ const elementsFromSpecialSelector = function(selector) { let nodes; try { nodes = document.querySelectorAll(matches[1]); - } catch(ex) { + } catch { nodes = []; } for ( const node of nodes ) { @@ -772,7 +772,7 @@ const contentInspectorChannel = (( ) => { if ( toLoggerPort === undefined ) { return; } try { toLoggerPort.postMessage(msg); - } catch(_) { + } catch { shutdownInspector(); } }; diff --git a/src/js/scriptlets/dom-survey-elements.js b/src/js/scriptlets/dom-survey-elements.js index 15825967f7d4f..85270ce62b0da 100644 --- a/src/js/scriptlets/dom-survey-elements.js +++ b/src/js/scriptlets/dom-survey-elements.js @@ -19,8 +19,6 @@ Home: https://github.com/gorhill/uBlock */ -'use strict'; - /******************************************************************************/ // https://github.com/uBlockOrigin/uBlock-issues/issues/756 diff --git a/src/js/scriptlets/dom-survey-scripts.js b/src/js/scriptlets/dom-survey-scripts.js index e5300ffdce755..30ef66e46b96f 100644 --- a/src/js/scriptlets/dom-survey-scripts.js +++ b/src/js/scriptlets/dom-survey-scripts.js @@ -19,8 +19,6 @@ Home: https://github.com/gorhill/uBlock */ -'use strict'; - /******************************************************************************/ // Scriptlets to count the number of script tags in a document. diff --git a/src/js/scriptlets/epicker.js b/src/js/scriptlets/epicker.js index 106941a2ba662..5dd23da718fe8 100644 --- a/src/js/scriptlets/epicker.js +++ b/src/js/scriptlets/epicker.js @@ -52,7 +52,7 @@ const safeQuerySelectorAll = function(node, selector) { if ( node !== null ) { try { return node.querySelectorAll(selector); - } catch (e) { + } catch { } } return []; @@ -672,8 +672,7 @@ const filterToDOMInterface = (( ) => { let reFilter = null; try { reFilter = new RegExp(reStr, 'i'); - } - catch (e) { + } catch { return out; } @@ -727,8 +726,7 @@ const filterToDOMInterface = (( ) => { elems = document.querySelectorAll( raw.replace(rePseudoElements, '') ); - } - catch (e) { + } catch { return; } const out = []; @@ -762,7 +760,7 @@ const filterToDOMInterface = (( ) => { default: break; } - } catch(ex) { + } catch { return; } if ( !elems ) { return; } diff --git a/src/js/scriptlets/load-3p-css.js b/src/js/scriptlets/load-3p-css.js index bb7d542dc56a5..315e17c65c7fd 100644 --- a/src/js/scriptlets/load-3p-css.js +++ b/src/js/scriptlets/load-3p-css.js @@ -19,8 +19,6 @@ Home: https://github.com/gorhill/uBlock */ -'use strict'; - /******************************************************************************/ (( ) => { @@ -34,7 +32,7 @@ let loaded = false; try { loaded = sheet.rules.length !== 0; - } catch(ex) { + } catch { } if ( loaded ) { continue; } const link = sheet.ownerNode || null; diff --git a/src/js/scriptlets/load-large-media-all.js b/src/js/scriptlets/load-large-media-all.js index a44539e702182..cf375774e12d3 100644 --- a/src/js/scriptlets/load-large-media-all.js +++ b/src/js/scriptlets/load-large-media-all.js @@ -19,8 +19,6 @@ Home: https://github.com/gorhill/uBlock */ -'use strict'; - /******************************************************************************/ (( ) => { diff --git a/src/js/scriptlets/noscript-spoof.js b/src/js/scriptlets/noscript-spoof.js index 49e9093b92072..a897df3c5885b 100644 --- a/src/js/scriptlets/noscript-spoof.js +++ b/src/js/scriptlets/noscript-spoof.js @@ -22,8 +22,6 @@ // Code below has been imported from uMatrix and modified to fit uBO: // https://github.com/gorhill/uMatrix/blob/3f8794dd899a05e066c24066c6c0a2515d5c60d2/src/js/contentscript.js#L464-L531 -'use strict'; - /******************************************************************************/ // https://github.com/gorhill/uMatrix/issues/232 @@ -46,15 +44,13 @@ let url; try { url = new URL(refreshURL, document.baseURI); - } catch(ex) { + } catch { return; } if ( reSafeURL.test(url.href) === false ) { return; } redirectTimer = setTimeout(( ) => { - location.assign(url.href); - }, - parseInt(match[1], 10) * 1000 + 1 - ); + location.assign(url.href); + }, parseInt(match[1], 10) * 1000 + 1); meta.parentNode.removeChild(meta); }; diff --git a/src/js/scriptlets/scriptlet-loglevel-1.js b/src/js/scriptlets/scriptlet-loglevel-1.js index bc5f4bb28a904..ad84ffae8c781 100644 --- a/src/js/scriptlets/scriptlet-loglevel-1.js +++ b/src/js/scriptlets/scriptlet-loglevel-1.js @@ -19,8 +19,6 @@ Home: https://github.com/gorhill/uBlock */ -'use strict'; - /******************************************************************************/ (( ) => { diff --git a/src/js/scriptlets/scriptlet-loglevel-2.js b/src/js/scriptlets/scriptlet-loglevel-2.js index d8afefdcfe164..6047919a09cc4 100644 --- a/src/js/scriptlets/scriptlet-loglevel-2.js +++ b/src/js/scriptlets/scriptlet-loglevel-2.js @@ -19,8 +19,6 @@ Home: https://github.com/gorhill/uBlock */ -'use strict'; - /******************************************************************************/ (( ) => { diff --git a/src/js/scriptlets/should-inject-contentscript.js b/src/js/scriptlets/should-inject-contentscript.js index 2317e20688777..1b678733808d4 100644 --- a/src/js/scriptlets/should-inject-contentscript.js +++ b/src/js/scriptlets/should-inject-contentscript.js @@ -32,7 +32,7 @@ self.requestIdleCallback(( ) => vAPI?.bootstrap?.()); } return status; - } catch(ex) { + } catch { } return true; })(); diff --git a/src/js/scriptlets/subscriber.js b/src/js/scriptlets/subscriber.js index ea7b20946c32a..5d9234d08f435 100644 --- a/src/js/scriptlets/subscriber.js +++ b/src/js/scriptlets/subscriber.js @@ -19,10 +19,6 @@ Home: https://github.com/gorhill/uBlock */ -/* global HTMLDocument */ - -'use strict'; - /******************************************************************************/ // Injected into specific web pages, those which have been pre-selected @@ -70,7 +66,7 @@ const onMaybeSubscriptionLinkClicked = function(target) { title, }); return true; - } catch (_) { + } catch { } }; diff --git a/src/js/scriptlets/updater.js b/src/js/scriptlets/updater.js index 006b663a05951..908121c3aecfd 100644 --- a/src/js/scriptlets/updater.js +++ b/src/js/scriptlets/updater.js @@ -19,10 +19,6 @@ Home: https://github.com/gorhill/uBlock */ -/* global HTMLDocument */ - -'use strict'; - /******************************************************************************/ // Injected into specific webpages, those which have been pre-selected @@ -75,7 +71,7 @@ function updateStockLists(target) { auto, }); return true; - } catch (_) { + } catch { } } diff --git a/src/js/settings.js b/src/js/settings.js index fc0ea684b445e..521dad44b8363 100644 --- a/src/js/settings.js +++ b/src/js/settings.js @@ -19,11 +19,9 @@ Home: https://github.com/gorhill/uBlock */ -'use strict'; - -import { i18n$ } from './i18n.js'; import { dom, qs$, qsa$ } from './dom.js'; import { setAccentColor, setTheme } from './theme.js'; +import { i18n$ } from './i18n.js'; /******************************************************************************/ @@ -69,7 +67,7 @@ function handleImportFilePicker() { throw 'Invalid'; } } - catch (e) { + catch { userData = undefined; } if ( userData === undefined ) { diff --git a/src/js/start.js b/src/js/start.js index 18e7dbdb49467..df7ce05287d49 100644 --- a/src/js/start.js +++ b/src/js/start.js @@ -19,12 +19,6 @@ Home: https://github.com/gorhill/uBlock */ -/* globals browser */ - -'use strict'; - -/******************************************************************************/ - import './vapi-common.js'; import './vapi-background.js'; import './vapi-background-ext.js'; @@ -39,27 +33,27 @@ import './tab.js'; import './ublock.js'; import './utils.js'; -import io from './assets.js'; -import µb from './background.js'; -import { filteringBehaviorChanged } from './broadcast.js'; -import cacheStorage from './cachestorage.js'; -import { ubolog } from './console.js'; -import contextMenu from './contextmenu.js'; -import { redirectEngine } from './redirect-engine.js'; -import staticFilteringReverseLookup from './reverselookup.js'; -import staticExtFilteringEngine from './static-ext-filtering.js'; -import staticNetFilteringEngine from './static-net-filtering.js'; -import webRequest from './traffic.js'; - import { permanentFirewall, - sessionFirewall, permanentSwitches, - sessionSwitches, permanentURLFiltering, + sessionFirewall, + sessionSwitches, sessionURLFiltering, } from './filtering-engines.js'; +import cacheStorage from './cachestorage.js'; +import contextMenu from './contextmenu.js'; +import { filteringBehaviorChanged } from './broadcast.js'; +import io from './assets.js'; +import { redirectEngine } from './redirect-engine.js'; +import staticExtFilteringEngine from './static-ext-filtering.js'; +import staticFilteringReverseLookup from './reverselookup.js'; +import staticNetFilteringEngine from './static-net-filtering.js'; +import { ubolog } from './console.js'; +import webRequest from './traffic.js'; +import µb from './background.js'; + /******************************************************************************/ let lastVersionInt = 0; diff --git a/src/js/static-ext-filtering-db.js b/src/js/static-ext-filtering-db.js index e669c1e11a3ce..2d25c83bd2b3d 100644 --- a/src/js/static-ext-filtering-db.js +++ b/src/js/static-ext-filtering-db.js @@ -19,8 +19,6 @@ Home: https://github.com/gorhill/uBlock */ -'use strict'; - /******************************************************************************/ const StaticExtFilteringHostnameDB = class { diff --git a/src/js/static-ext-filtering.js b/src/js/static-ext-filtering.js index e616e6350e927..8a33093222b6a 100644 --- a/src/js/static-ext-filtering.js +++ b/src/js/static-ext-filtering.js @@ -19,15 +19,11 @@ Home: https://github.com/gorhill/uBlock */ -'use strict'; - -/******************************************************************************/ - import cosmeticFilteringEngine from './cosmetic-filtering.js'; import htmlFilteringEngine from './html-filtering.js'; import httpheaderFilteringEngine from './httpheader-filtering.js'; -import scriptletFilteringEngine from './scriptlet-filtering.js'; import logger from './logger.js'; +import scriptletFilteringEngine from './scriptlet-filtering.js'; /******************************************************************************* diff --git a/src/js/static-filtering-io.js b/src/js/static-filtering-io.js index 3f016ab0e69df..db8478f6235c3 100644 --- a/src/js/static-filtering-io.js +++ b/src/js/static-filtering-io.js @@ -19,8 +19,6 @@ Home: https://github.com/gorhill/uBlock */ -'use strict'; - /******************************************************************************/ // https://www.reddit.com/r/uBlockOrigin/comments/oq6kt5/ubo_loads_generic_filter_instead_of_specific/ diff --git a/src/js/static-filtering-parser.js b/src/js/static-filtering-parser.js index d3b4ca48416cf..73507cc6b4ec8 100644 --- a/src/js/static-filtering-parser.js +++ b/src/js/static-filtering-parser.js @@ -19,8 +19,6 @@ Home: https://github.com/gorhill/uBlock */ -/******************************************************************************/ - import * as cssTree from '../lib/csstree/css-tree.js'; import { ArglistParser } from './arglist-parser.js'; import Regex from '../lib/regexanalyzer/regex.js'; @@ -1315,7 +1313,7 @@ export class AstFilterParser { const value = this.getNetOptionValue(NODE_TYPE_NET_OPTION_NAME_IPADDRESS); if ( /^\/.+\/$/.test(value) ) { try { void new RegExp(value); } - catch(_) { realBad = true; } + catch { realBad = true; } } break; } @@ -1849,7 +1847,7 @@ export class AstFilterParser { normal = normal.replace(this.reUnicodeChars, s => encodeURIComponent(s).toLowerCase() ); - } catch (ex) { + } catch { return; } return normal; @@ -2827,7 +2825,7 @@ export class AstFilterParser { try { this.punycoder.hostname = hn; hn = this.punycoder.hostname; - } catch (_) { + } catch { return ''; } if ( hn === '_' || hn === '' ) { return ''; } @@ -2951,7 +2949,7 @@ export function parseQueryPruneValue(arg) { try { out.re = new RegExp(match[1], match[2] || ''); } - catch(ex) { + catch { out.bad = true; } return out; @@ -2960,7 +2958,7 @@ export function parseQueryPruneValue(arg) { if ( s.startsWith('|') ) { try { out.re = new RegExp('^' + s.slice(1), 'i'); - } catch(ex) { + } catch { out.bad = true; } return out; @@ -2990,7 +2988,7 @@ export function parseHeaderValue(arg) { try { out.re = new RegExp(match[1], match[2] || ''); } - catch(ex) { + catch { out.bad = true; } } @@ -3022,7 +3020,7 @@ export function parseReplaceValue(s) { const flags = s.slice(parser.separatorEnd); try { return { re: new RegExp(pattern, flags), replacement }; - } catch(_) { + } catch { } } @@ -4056,7 +4054,7 @@ class ExtSelectorCompiler { try { const expr = doc.createExpression(r.s, null); expr.evaluate(doc, XPathResult.ANY_UNORDERED_NODE_TYPE); - } catch (e) { + } catch { return; } return r.s; @@ -4212,7 +4210,7 @@ export const utils = (( ) => { regexAnalyzer(reStr, false).tree() ); } - } catch(ex) { + } catch { return false; } return true; @@ -4223,7 +4221,7 @@ export const utils = (( ) => { let tree; try { tree = regexAnalyzer(reStr, false).tree(); - } catch(ex) { + } catch { return; } const isRE2 = node => { @@ -4254,7 +4252,7 @@ export const utils = (( ) => { s = this.tokenizableStrFromNode( regexAnalyzer(reStr, false).tree() ); - } catch(ex) { + } catch { } // Process optional sequences const reOptional = /[\x02\x03]+/; @@ -4316,7 +4314,7 @@ export const utils = (( ) => { const toURL = url => { try { return new URL(url.trim()); - } catch (ex) { + } catch { } }; diff --git a/src/js/static-net-filtering.js b/src/js/static-net-filtering.js index 58b6570705c2d..34909d92c5ddd 100644 --- a/src/js/static-net-filtering.js +++ b/src/js/static-net-filtering.js @@ -3453,7 +3453,7 @@ class FilterCompiler { try { const re = new RegExp(s); return re.source; - } catch (ex) { + } catch { } return ''; } @@ -5503,7 +5503,7 @@ StaticNetFilteringEngine.prototype.filterQuery = function(fctxt, out = []) { for ( const [ key, raw ] of params ) { let value = raw; try { value = decodeURIComponent(value); } - catch(ex) { } + catch { } if ( re.test(`${key}=${value}`) === not ) { continue; } if ( isException === false ) { params.delete(key); } filtered = true; diff --git a/src/js/storage.js b/src/js/storage.js index c3b541bd3c159..8d1696515a2ec 100644 --- a/src/js/storage.js +++ b/src/js/storage.js @@ -435,7 +435,7 @@ onBroadcast(msg => { try { const url = new URL(prefix); if ( url.hostname.length > 0 ) { return url.href; } - } catch(_) { + } catch { } }).filter(prefix => prefix !== undefined); } diff --git a/src/js/support.js b/src/js/support.js index 9bfd7cbe3b3c2..c6b45d4f1d924 100644 --- a/src/js/support.js +++ b/src/js/support.js @@ -21,10 +21,8 @@ /* global CodeMirror, uBlockDashboard */ -'use strict'; - -import { onBroadcast } from './broadcast.js'; import { dom, qs$ } from './dom.js'; +import { onBroadcast } from './broadcast.js'; /******************************************************************************/ @@ -220,7 +218,7 @@ const reportedPage = (( ) => { hostname: parsedURL.hostname.replace(/^(m|mobile|www)\./, ''), popupPanel: JSON.parse(url.searchParams.get('popupPanel')), }; - } catch(ex) { + } catch { } return null; })(); diff --git a/src/js/tab.js b/src/js/tab.js index c505e5abc317a..eeba9a528abe8 100644 --- a/src/js/tab.js +++ b/src/js/tab.js @@ -19,18 +19,11 @@ Home: https://github.com/gorhill/uBlock */ -'use strict'; - -/******************************************************************************/ - -import contextMenu from './contextmenu.js'; -import logger from './logger.js'; -import scriptletFilteringEngine from './scriptlet-filtering.js'; -import staticNetFilteringEngine from './static-net-filtering.js'; -import µb from './background.js'; -import webext from './webext.js'; -import { PageStore } from './pagestore.js'; -import { i18n$ } from './i18n.js'; +import { + domainFromHostname, + hostnameFromURI, + originFromURI, +} from './uri-utils.js'; import { sessionFirewall, @@ -38,11 +31,14 @@ import { sessionURLFiltering, } from './filtering-engines.js'; -import { - domainFromHostname, - hostnameFromURI, - originFromURI, -} from './uri-utils.js'; +import { PageStore } from './pagestore.js'; +import contextMenu from './contextmenu.js'; +import { i18n$ } from './i18n.js'; +import logger from './logger.js'; +import scriptletFilteringEngine from './scriptlet-filtering.js'; +import staticNetFilteringEngine from './static-net-filtering.js'; +import webext from './webext.js'; +import µb from './background.js'; /******************************************************************************/ /******************************************************************************/ @@ -65,7 +61,7 @@ import { } try { tabURLNormalizer.href = tabURL; - } catch(ex) { + } catch { return tabURL; } const protocol = tabURLNormalizer.protocol.slice(0, -1); @@ -574,8 +570,7 @@ housekeep itself. frameId: sourceFrameId, }), ]); - } - catch (reason) { + } catch { return; } if ( diff --git a/src/js/tasks.js b/src/js/tasks.js index 8358fd883507e..ece860621847d 100644 --- a/src/js/tasks.js +++ b/src/js/tasks.js @@ -19,12 +19,6 @@ Home: https://github.com/gorhill/uBlock */ -/* globals requestIdleCallback, cancelIdleCallback */ - -'use strict'; - -/******************************************************************************/ - export function queueTask(func, timeout = 5000) { if ( typeof requestIdleCallback === 'undefined' ) { return setTimeout(func, 1); diff --git a/src/js/text-encode.js b/src/js/text-encode.js index 06c7b2c9a680a..8e5ad81ae9290 100644 --- a/src/js/text-encode.js +++ b/src/js/text-encode.js @@ -19,10 +19,6 @@ Home: https://github.com/gorhill/uBlock */ -'use strict'; - -/******************************************************************************/ - import µb from './background.js'; /******************************************************************************/ @@ -67,64 +63,64 @@ const textEncode = (( ) => { // http://www.unicode.org/Public/MAPPINGS/VENDORS/MICSFT/WINDOWS/CP1250.TXT const cp1250_range0 = new Uint8Array([ - /* 0x0100 */ 0x00, 0x00, 0xC3, 0xE3, 0xA5, 0xB9, 0xC6, 0xE6, - /* 0x0108 */ 0x00, 0x00, 0x00, 0x00, 0xC8, 0xE8, 0xCF, 0xEF, - /* 0x0110 */ 0xD0, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - /* 0x0118 */ 0xCA, 0xEA, 0xCC, 0xEC, 0x00, 0x00, 0x00, 0x00, - /* 0x0120 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - /* 0x0128 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - /* 0x0130 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - /* 0x0138 */ 0x00, 0xC5, 0xE5, 0x00, 0x00, 0xBC, 0xBE, 0x00, - /* 0x0140 */ 0x00, 0xA3, 0xB3, 0xD1, 0xF1, 0x00, 0x00, 0xD2, - /* 0x0148 */ 0xF2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - /* 0x0150 */ 0xD5, 0xF5, 0x00, 0x00, 0xC0, 0xE0, 0x00, 0x00, - /* 0x0158 */ 0xD8, 0xF8, 0x8C, 0x9C, 0x00, 0x00, 0xAA, 0xBA, - /* 0x0160 */ 0x8A, 0x9A, 0xDE, 0xFE, 0x8D, 0x9D, 0x00, 0x00, - /* 0x0168 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xD9, 0xF9, - /* 0x0170 */ 0xDB, 0xFB, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - /* 0x0178 */ 0x00, 0x8F, 0x9F, 0xAF, 0xBF, 0x8E, 0x9E, 0x00 + /* 0x0100 */ 0x00, 0x00, 0xC3, 0xE3, 0xA5, 0xB9, 0xC6, 0xE6, + /* 0x0108 */ 0x00, 0x00, 0x00, 0x00, 0xC8, 0xE8, 0xCF, 0xEF, + /* 0x0110 */ 0xD0, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /* 0x0118 */ 0xCA, 0xEA, 0xCC, 0xEC, 0x00, 0x00, 0x00, 0x00, + /* 0x0120 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /* 0x0128 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /* 0x0130 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /* 0x0138 */ 0x00, 0xC5, 0xE5, 0x00, 0x00, 0xBC, 0xBE, 0x00, + /* 0x0140 */ 0x00, 0xA3, 0xB3, 0xD1, 0xF1, 0x00, 0x00, 0xD2, + /* 0x0148 */ 0xF2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /* 0x0150 */ 0xD5, 0xF5, 0x00, 0x00, 0xC0, 0xE0, 0x00, 0x00, + /* 0x0158 */ 0xD8, 0xF8, 0x8C, 0x9C, 0x00, 0x00, 0xAA, 0xBA, + /* 0x0160 */ 0x8A, 0x9A, 0xDE, 0xFE, 0x8D, 0x9D, 0x00, 0x00, + /* 0x0168 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xD9, 0xF9, + /* 0x0170 */ 0xDB, 0xFB, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /* 0x0178 */ 0x00, 0x8F, 0x9F, 0xAF, 0xBF, 0x8E, 0x9E, 0x00 ]); // http://www.unicode.org/Public/MAPPINGS/VENDORS/MICSFT/WINDOWS/CP1251.TXT const cp1251_range0 = new Uint8Array([ - /* 0x0400 */ 0x00, 0xA8, 0x80, 0x81, 0xAA, 0xBD, 0xB2, 0xAF, - /* 0x0408 */ 0xA3, 0x8A, 0x8C, 0x8E, 0x8D, 0x00, 0xA1, 0x8F, - /* 0x0410 */ 0xC0, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, - /* 0x0418 */ 0xC8, 0xC9, 0xCA, 0xCB, 0xCC, 0xCD, 0xCE, 0xCF, - /* 0x0420 */ 0xD0, 0xD1, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, - /* 0x0428 */ 0xD8, 0xD9, 0xDA, 0xDB, 0xDC, 0xDD, 0xDE, 0xDF, - /* 0x0430 */ 0xE0, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, - /* 0x0438 */ 0xE8, 0xE9, 0xEA, 0xEB, 0xEC, 0xED, 0xEE, 0xEF, - /* 0x0440 */ 0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, - /* 0x0448 */ 0xF8, 0xF9, 0xFA, 0xFB, 0xFC, 0xFD, 0xFE, 0xFF, - /* 0x0450 */ 0x00, 0xB8, 0x90, 0x83, 0xBA, 0xBE, 0xB3, 0xBF, - /* 0x0458 */ 0xBC, 0x9A, 0x9C, 0x9E, 0x9D, 0x00, 0xA2, 0x9F, - /* 0x0460 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - /* 0x0468 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - /* 0x0470 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - /* 0x0478 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - /* 0x0480 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - /* 0x0488 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - /* 0x0490 */ 0xA5, 0xB4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + /* 0x0400 */ 0x00, 0xA8, 0x80, 0x81, 0xAA, 0xBD, 0xB2, 0xAF, + /* 0x0408 */ 0xA3, 0x8A, 0x8C, 0x8E, 0x8D, 0x00, 0xA1, 0x8F, + /* 0x0410 */ 0xC0, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, + /* 0x0418 */ 0xC8, 0xC9, 0xCA, 0xCB, 0xCC, 0xCD, 0xCE, 0xCF, + /* 0x0420 */ 0xD0, 0xD1, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, + /* 0x0428 */ 0xD8, 0xD9, 0xDA, 0xDB, 0xDC, 0xDD, 0xDE, 0xDF, + /* 0x0430 */ 0xE0, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, + /* 0x0438 */ 0xE8, 0xE9, 0xEA, 0xEB, 0xEC, 0xED, 0xEE, 0xEF, + /* 0x0440 */ 0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, + /* 0x0448 */ 0xF8, 0xF9, 0xFA, 0xFB, 0xFC, 0xFD, 0xFE, 0xFF, + /* 0x0450 */ 0x00, 0xB8, 0x90, 0x83, 0xBA, 0xBE, 0xB3, 0xBF, + /* 0x0458 */ 0xBC, 0x9A, 0x9C, 0x9E, 0x9D, 0x00, 0xA2, 0x9F, + /* 0x0460 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /* 0x0468 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /* 0x0470 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /* 0x0478 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /* 0x0480 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /* 0x0488 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /* 0x0490 */ 0xA5, 0xB4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 ]); // https://www.unicode.org/Public/MAPPINGS/VENDORS/MICSFT/WINDOWS/CP1252.TXT const cp1252_range0 = new Uint8Array([ - /* 0x0150 */ 0x00, 0x00, 0x8C, 0x9C, 0x00, 0x00, 0x00, 0x00, - /* 0x0158 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - /* 0x0160 */ 0x8A, 0x9A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - /* 0x0168 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - /* 0x0170 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - /* 0x0178 */ 0x9F, 0x00, 0x00, 0x00, 0x00, 0x8E, 0x9E, 0x00 + /* 0x0150 */ 0x00, 0x00, 0x8C, 0x9C, 0x00, 0x00, 0x00, 0x00, + /* 0x0158 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /* 0x0160 */ 0x8A, 0x9A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /* 0x0168 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /* 0x0170 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /* 0x0178 */ 0x9F, 0x00, 0x00, 0x00, 0x00, 0x8E, 0x9E, 0x00 ]); const cp125x_range0 = new Uint8Array([ - /* 0x2010 */ 0x00, 0x00, 0x00, 0x96, 0x97, 0x00, 0x00, 0x00, - /* 0x2018 */ 0x91, 0x92, 0x82, 0x00, 0x93, 0x94, 0x84, 0x00, - /* 0x2020 */ 0x86, 0x87, 0x95, 0x00, 0x00, 0x00, 0x85, 0x00, - /* 0x2028 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - /* 0x2030 */ 0x89, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - /* 0x2038 */ 0x00, 0x8B, 0x9B, 0x00, 0x00, 0x00, 0x00, 0x00 + /* 0x2010 */ 0x00, 0x00, 0x00, 0x96, 0x97, 0x00, 0x00, 0x00, + /* 0x2018 */ 0x91, 0x92, 0x82, 0x00, 0x93, 0x94, 0x84, 0x00, + /* 0x2020 */ 0x86, 0x87, 0x95, 0x00, 0x00, 0x00, 0x85, 0x00, + /* 0x2028 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /* 0x2030 */ 0x89, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /* 0x2038 */ 0x00, 0x8B, 0x9B, 0x00, 0x00, 0x00, 0x00, 0x00 ]); const encoders = { diff --git a/src/js/text-utils.js b/src/js/text-utils.js index 198a433dd0090..8b2bb52653b20 100644 --- a/src/js/text-utils.js +++ b/src/js/text-utils.js @@ -19,8 +19,6 @@ Home: https://github.com/gorhill/uBlock */ -'use strict'; - /******************************************************************************/ // https://bugs.chromium.org/p/v8/issues/detail?id=2869 diff --git a/src/js/theme.js b/src/js/theme.js index d3f9b0009aa48..111272f104dc6 100644 --- a/src/js/theme.js +++ b/src/js/theme.js @@ -19,8 +19,6 @@ Home: https://github.com/gorhill/uBlock */ -'use strict'; - function getActualTheme(nominalTheme) { let theme = nominalTheme || 'light'; if ( nominalTheme === 'auto' ) { @@ -51,7 +49,7 @@ function setTheme(theme, propagate = false) { if ( propagate === false ) { break; } if ( w === w.parent ) { break; } w = w.parent; - try { void w.document; } catch(ex) { return; } + try { void w.document; } catch { return; } } } @@ -110,7 +108,7 @@ function setAccentColor( if ( propagate === false ) { break; } if ( w === w.parent ) { break; } w = w.parent; - try { void w.document; } catch(ex) { break; } + try { void w.document; } catch { break; } } } diff --git a/src/js/ublock.js b/src/js/ublock.js index cfc63497a28ac..0ac05d08d8d04 100644 --- a/src/js/ublock.js +++ b/src/js/ublock.js @@ -19,27 +19,28 @@ Home: https://github.com/gorhill/uBlock */ -'use strict'; - -/******************************************************************************/ - -import io from './assets.js'; -import µb from './background.js'; -import { broadcast, filteringBehaviorChanged, onBroadcast } from './broadcast.js'; -import contextMenu from './contextmenu.js'; -import cosmeticFilteringEngine from './cosmetic-filtering.js'; -import { redirectEngine } from './redirect-engine.js'; -import { hostnameFromURI } from './uri-utils.js'; +import { + broadcast, + filteringBehaviorChanged, + onBroadcast, +} from './broadcast.js'; import { permanentFirewall, - sessionFirewall, permanentSwitches, - sessionSwitches, permanentURLFiltering, + sessionFirewall, + sessionSwitches, sessionURLFiltering, } from './filtering-engines.js'; +import contextMenu from './contextmenu.js'; +import cosmeticFilteringEngine from './cosmetic-filtering.js'; +import { hostnameFromURI } from './uri-utils.js'; +import io from './assets.js'; +import { redirectEngine } from './redirect-engine.js'; +import µb from './background.js'; + /******************************************************************************/ /******************************************************************************/ @@ -47,7 +48,7 @@ import { // Be more flexible with whitelist syntax // Any special regexp char will be escaped -const whitelistDirectiveEscape = /[-\/\\^$+?.()|[\]{}]/g; +const whitelistDirectiveEscape = /[-/\\^$+?.()|[\]{}]/g; // All `*` will be expanded into `.*` const whitelistDirectiveEscapeAsterisk = /\*/g; @@ -254,7 +255,7 @@ const matchBucket = function(url, hostname, bucket, start) { try { const re = new RegExp(directive.slice(1, -1)); directiveToRegexpMap.set(directive, re); - } catch(ex) { + } catch { key = '#'; directive = '# ' + line; } @@ -294,8 +295,8 @@ const matchBucket = function(url, hostname, bucket, start) { }; // https://github.com/gorhill/uBlock/issues/3717 -µb.reWhitelistBadHostname = /[^a-z0-9.\-_\[\]:]/; -µb.reWhitelistHostnameExtractor = /([a-z0-9.\-_\[\]]+)(?::[\d*]+)?\/(?:[^\x00-\x20\/]|$)[^\x00-\x20]*$/; +µb.reWhitelistBadHostname = /[^a-z0-9.\-_[\]:]/; +µb.reWhitelistHostnameExtractor = /([a-z0-9.\-_[\]]+)(?::[\d*]+)?\/(?:[^\x00-\x20/]|$)[^\x00-\x20]*$/; /******************************************************************************/ @@ -369,7 +370,7 @@ const matchBucket = function(url, hostname, bucket, start) { case 'noLargeMedia': case 'noRemoteFonts': case 'noScripting': - case 'noCSPReports': + case 'noCSPReports': { let switchName; switch ( name ) { case 'noCosmeticFiltering': @@ -392,6 +393,7 @@ const matchBucket = function(url, hostname, bucket, start) { this.saveHostnameSwitches(); } break; + } case 'prefetchingDisabled': if ( this.privacySettingsSupported ) { vAPI.browserSettings.set({ 'prefetching': !value }); @@ -588,39 +590,40 @@ const matchBucket = function(url, hostname, bucket, start) { // Take per-switch action if needed switch ( details.name ) { - case 'no-scripting': - this.updateToolbarIcon(details.tabId, 0b100); - break; - case 'no-cosmetic-filtering': { - const scriptlet = newState ? 'cosmetic-off' : 'cosmetic-on'; - vAPI.tabs.executeScript(details.tabId, { - file: `/js/scriptlets/${scriptlet}.js`, - allFrames: true, - }); - break; + case 'no-scripting': + this.updateToolbarIcon(details.tabId, 0b100); + break; + case 'no-cosmetic-filtering': { + const scriptlet = newState ? 'cosmetic-off' : 'cosmetic-on'; + vAPI.tabs.executeScript(details.tabId, { + file: `/js/scriptlets/${scriptlet}.js`, + allFrames: true, + }); + break; + } + case 'no-large-media': { + const pageStore = this.pageStoreFromTabId(details.tabId); + if ( pageStore !== null ) { + pageStore.temporarilyAllowLargeMediaElements(!newState); } - case 'no-large-media': - const pageStore = this.pageStoreFromTabId(details.tabId); - if ( pageStore !== null ) { - pageStore.temporarilyAllowLargeMediaElements(!newState); - } - break; - default: - break; + break; + } + default: + break; } // Flush caches if needed if ( newState ) { switch ( details.name ) { - case 'no-scripting': - case 'no-remote-fonts': - filteringBehaviorChanged({ - direction: details.state ? 1 : 0, - hostname: details.hostname, - }); - break; - default: - break; + case 'no-scripting': + case 'no-remote-fonts': + filteringBehaviorChanged({ + direction: details.state ? 1 : 0, + hostname: details.hostname, + }); + break; + default: + break; } } @@ -691,7 +694,7 @@ const matchBucket = function(url, hostname, bucket, start) { try { const url = new URL(pageURL); return JSON.parse(url.searchParams.get('details')).url; - } catch(ex) { + } catch { } } return pageURL; diff --git a/src/js/uri-utils.js b/src/js/uri-utils.js index 273b1511f1db7..aa9dca934ced4 100644 --- a/src/js/uri-utils.js +++ b/src/js/uri-utils.js @@ -19,10 +19,6 @@ Home: https://github.com/gorhill/uBlock */ -'use strict'; - -/******************************************************************************/ - import publicSuffixList from '../lib/publicsuffixlist/publicsuffixlist.js'; import punycode from '../lib/punycode.js'; @@ -34,7 +30,7 @@ import punycode from '../lib/punycode.js'; const reHostnameFromCommonURL = /^https:\/\/[0-9a-z._-]+[0-9a-z]\//; const reAuthorityFromURI = - /^(?:[^:\/?#]+:)?(\/\/[^\/?#]+)/; + /^(?:[^:/?#]+:)?(\/\/[^/?#]+)/; const reHostFromNakedAuthority = /^[0-9a-z._-]+[0-9a-z]$/i; const reHostFromAuthority = @@ -44,7 +40,7 @@ const reIPv6FromAuthority = const reMustNormalizeHostname = /[^0-9a-z._-]/; const reOriginFromURI = - /^[^:\/?#]+:\/\/[^\/?#]+/; + /^[^:/?#]+:\/\/[^/?#]+/; const reHostnameFromNetworkURL = /^(?:http|ws|ftp)s?:\/\/([0-9a-z_][0-9a-z._-]*[0-9a-z])(?::\d+)?\//; const reIPAddressNaive = @@ -54,7 +50,7 @@ const reNetworkURI = // For performance purpose, as simple tests as possible const reIPv4VeryCoarse = /\.\d+$/; -const reHostnameVeryCoarse = /[g-z_\-]/; +const reHostnameVeryCoarse = /[g-z_-]/; /******************************************************************************/ diff --git a/src/js/url-net-filtering.js b/src/js/url-net-filtering.js index 39befc7dce2ca..43831cf523bb3 100644 --- a/src/js/url-net-filtering.js +++ b/src/js/url-net-filtering.js @@ -19,10 +19,6 @@ Home: https://github.com/gorhill/uBlock */ -'use strict'; - -/******************************************************************************/ - import { LineIterator } from './text-utils.js'; import { decomposeHostname } from './uri-utils.js'; diff --git a/src/js/urlskip.js b/src/js/urlskip.js index b0d396623bbc7..85c396e67748d 100644 --- a/src/js/urlskip.js +++ b/src/js/urlskip.js @@ -161,6 +161,6 @@ export function urlSkip(url, blocked, steps, directive = {}) { } if ( blocked && redirectBlocked !== true ) { return; } return urlout; - } catch(x) { + } catch { } } diff --git a/src/js/utils.js b/src/js/utils.js index e48e963e184b7..8b12244fdfe2b 100644 --- a/src/js/utils.js +++ b/src/js/utils.js @@ -19,10 +19,6 @@ Home: https://github.com/gorhill/uBlock */ -'use strict'; - -/******************************************************************************/ - import µb from './background.js'; /******************************************************************************/ @@ -72,7 +68,7 @@ import µb from './background.js'; popupLoggerBox = JSON.parse( vAPI.localStorage.getItem('popupLoggerBox') ); - } catch(ex) { + } catch { } if ( popupLoggerBox !== undefined ) { details.box = popupLoggerBox; diff --git a/src/js/whitelist.js b/src/js/whitelist.js index b8f0eaaa55e98..6d9e951c1be7f 100644 --- a/src/js/whitelist.js +++ b/src/js/whitelist.js @@ -21,10 +21,8 @@ /* global CodeMirror, uBlockDashboard */ -'use strict'; - -import { i18n$ } from './i18n.js'; import { dom, qs$ } from './dom.js'; +import { i18n$ } from './i18n.js'; /******************************************************************************/ @@ -62,7 +60,7 @@ CodeMirror.defineMode("ubo-whitelist-directives", function() { if ( reRegex.test(line) ) { try { new RegExp(line.slice(1, -1)); - } catch(ex) { + } catch { return 'error'; } return null; From ab0f4ba0d4bc88b8772748b8e078409ddc67e4cc Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Fri, 10 Jan 2025 10:23:54 -0500 Subject: [PATCH 0603/1099] [mv3] Fix conversion of filters with `important` option Related issue: https://github.com/uBlockOrigin/uBOL-home/issues/266 --- src/js/static-net-filtering.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/js/static-net-filtering.js b/src/js/static-net-filtering.js index 34909d92c5ddd..af5f5691c376e 100644 --- a/src/js/static-net-filtering.js +++ b/src/js/static-net-filtering.js @@ -769,10 +769,6 @@ class FilterImportant { return filterDataAlloc(args[0]); } - static dnrFromCompiled(args, rule) { - rule.priority = (rule.priority || 0) + 30; - } - static keyFromArgs() { } @@ -4463,13 +4459,17 @@ StaticNetFilteringEngine.prototype.dnrFromCompiled = function(op, context, ...ar const realms = new Map([ [ BLOCK_REALM, { type: 'block', priority: 10 } ], + [ BLOCK_REALM | IMPORTANT_REALM, { type: 'block', priority: 40 } ], [ ALLOW_REALM, { type: 'allow', priority: 30 } ], [ REDIRECT_REALM, { type: 'redirect', priority: 11 } ], + [ REDIRECT_REALM | IMPORTANT_REALM, { type: 'redirect', priority: 41 } ], [ REMOVEPARAM_REALM, { type: 'removeparam', priority: 0 } ], [ CSP_REALM, { type: 'csp', priority: 0 } ], [ PERMISSIONS_REALM, { type: 'permissions', priority: 0 } ], [ URLTRANSFORM_REALM, { type: 'uritransform', priority: 0 } ], [ HEADERS_REALM, { type: 'block', priority: 10 } ], + [ HEADERS_REALM | ALLOW_REALM, { type: 'allow', priority: 30 } ], + [ HEADERS_REALM | IMPORTANT_REALM, { type: 'allow', priority: 40 } ], [ URLSKIP_REALM, { type: 'urlskip', priority: 0 } ], ]); const partyness = new Map([ From d2ff26a49de81befca301d137afad149999b6781 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Fri, 10 Jan 2025 10:28:41 -0500 Subject: [PATCH 0604/1099] Update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index dce6539cfda88..041c6f6b97905 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,4 @@ +- [Add `decline` value to `set-cookie` scriptlet](https://github.com/gorhill/uBlock/commit/4b12247da1) - [Improve `abort-on-stack-trace` scriptlet](https://github.com/gorhill/uBlock/commit/b617926c1c) - [Improve `href-sanitizer` scriptlet](https://github.com/gorhill/uBlock/commit/551c6bc6eb) From 2c64da4d03e1b6f6463db5e6a9bd339f298296c5 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Fri, 10 Jan 2025 10:29:03 -0500 Subject: [PATCH 0605/1099] New revision for dev build --- dist/version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dist/version b/dist/version index a9d7dad2232bb..4f03d10d18d20 100644 --- a/dist/version +++ b/dist/version @@ -1 +1 @@ -1.62.1.0 +1.62.1.1 From b2c568b3d62dea89aedc5600ab0bc3489c05c3a0 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Fri, 10 Jan 2025 10:46:32 -0500 Subject: [PATCH 0606/1099] Fix workflow --- .github/workflows/main.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 1d34c0242d012..d6570608659e5 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -30,6 +30,11 @@ jobs: tools/make-chromium.sh ${{ env.VERSION }} tools/make-firefox.sh ${{ env.VERSION }} tools/make-thunderbird.sh ${{ env.VERSION }} + - name: Build NPM package + uses: actions/setup-node@v4 + with: + node-version: 22 + run: | tools/make-npm.sh ${{ env.VERSION }} - name: Assemble release notes run: | From 149228c388f0fccf91edac9cd21d5387c1ab22bc Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Fri, 10 Jan 2025 10:52:36 -0500 Subject: [PATCH 0607/1099] Fix workflow --- .github/workflows/main.yml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index d6570608659e5..03c128df464a1 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -15,6 +15,10 @@ jobs: runs-on: ubuntu-latest if: startsWith(github.ref, 'refs/tags/') steps: + - name: Setup nodejs + uses: actions/setup-node@v4 + with: + node-version: 22 - name: Clone repository uses: actions/checkout@v4 with: @@ -31,9 +35,6 @@ jobs: tools/make-firefox.sh ${{ env.VERSION }} tools/make-thunderbird.sh ${{ env.VERSION }} - name: Build NPM package - uses: actions/setup-node@v4 - with: - node-version: 22 run: | tools/make-npm.sh ${{ env.VERSION }} - name: Assemble release notes From 3685a18fe9b8659f594a2eb111118fe07d04d2d8 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Fri, 10 Jan 2025 11:08:54 -0500 Subject: [PATCH 0608/1099] Fix workflow and makefile --- .github/workflows/main.yml | 8 -------- Makefile | 6 ++++-- 2 files changed, 4 insertions(+), 10 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 03c128df464a1..90ebc5e554fb8 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -15,10 +15,6 @@ jobs: runs-on: ubuntu-latest if: startsWith(github.ref, 'refs/tags/') steps: - - name: Setup nodejs - uses: actions/setup-node@v4 - with: - node-version: 22 - name: Clone repository uses: actions/checkout@v4 with: @@ -34,9 +30,6 @@ jobs: tools/make-chromium.sh ${{ env.VERSION }} tools/make-firefox.sh ${{ env.VERSION }} tools/make-thunderbird.sh ${{ env.VERSION }} - - name: Build NPM package - run: | - tools/make-npm.sh ${{ env.VERSION }} - name: Assemble release notes run: | > release.body.txt @@ -57,4 +50,3 @@ jobs: dist/build/uBlock0_${{ env.VERSION }}.chromium.zip dist/build/uBlock0_${{ env.VERSION }}.firefox.xpi dist/build/uBlock0_${{ env.VERSION }}.thunderbird.xpi - dist/build/uBlock0_${{ env.VERSION }}.npm.tgz diff --git a/Makefile b/Makefile index 0ff5e3306c8cd..53075785fd5d0 100644 --- a/Makefile +++ b/Makefile @@ -33,9 +33,11 @@ dist/build/uBlock0.npm: tools/make-nodejs.sh $(sources) $(platform) $(assets) tools/make-npm.sh # Dev tools -npm: node_modules/ +node_modules: npm install +npm: node_modules + lint: npm npm run lint @@ -77,7 +79,7 @@ dist/build/uAssets: tools/pull-assets.sh clean: - rm -rf dist/build tmp/node_modules + rm -rf dist/build tmp/node_modules node_modules cleanassets: rm -rf dist/build/mv3-data dist/build/uAssets From 7cdf56f69aae61a792d33091d0fb34c8c4108c44 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Fri, 10 Jan 2025 11:16:10 -0500 Subject: [PATCH 0609/1099] Make Firefox dev build auto-update --- dist/firefox/updates.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dist/firefox/updates.json b/dist/firefox/updates.json index 47c9c7932af88..929c3cc9fbc50 100644 --- a/dist/firefox/updates.json +++ b/dist/firefox/updates.json @@ -3,9 +3,9 @@ "uBlock0@raymondhill.net": { "updates": [ { - "version": "1.62.1.0", + "version": "1.62.1.1", "browser_specific_settings": { "gecko": { "strict_min_version": "78.0" } }, - "update_link": "https://github.com/gorhill/uBlock/releases/download/1.62.1b0/uBlock0_1.62.1b0.firefox.signed.xpi" + "update_link": "https://github.com/gorhill/uBlock/releases/download/1.62.1b1/uBlock0_1.62.1b1.firefox.signed.xpi" } ] } From efd1d7d171eab505b22a7036fa51a4fcc4e7ad2f Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sat, 11 Jan 2025 10:53:36 -0500 Subject: [PATCH 0610/1099] Fix makefile --- Makefile | 6 ++++-- package.json | 2 +- tools/make-npm.sh | 1 - 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/Makefile b/Makefile index 53075785fd5d0..826dd546c68fe 100644 --- a/Makefile +++ b/Makefile @@ -32,13 +32,15 @@ firefox: dist/build/uBlock0.firefox dist/build/uBlock0.npm: tools/make-nodejs.sh $(sources) $(platform) $(assets) tools/make-npm.sh +npm: dist/build/uBlock0.npm + # Dev tools node_modules: npm install -npm: node_modules +init: node_modules -lint: npm +lint: init npm run lint test: npm diff --git a/package.json b/package.json index 5e992ba9d0f61..63e5b7da353b1 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,7 @@ "description": "npm dev tools", "main": "index.js", "scripts": { - "lint": "eslint ./src/js/*.js ./src/js/**/*.js ./**/*.json ./platform/**/*.js ", + "lint": "eslint --no-warn-ignored -- ./src/js/*.js ./src/js/**/*.js ./**/*.json ./platform/**/*.js ", "test": "echo \"Error: no test specified\" && exit 1" }, "repository": { diff --git a/tools/make-npm.sh b/tools/make-npm.sh index 8a17b0e3c1e51..56830751c0561 100755 --- a/tools/make-npm.sh +++ b/tools/make-npm.sh @@ -17,7 +17,6 @@ rm -rf $DES # Target-specific cp platform/npm/.npmignore $DES/ cp platform/npm/*.json $DES/ -cp platform/npm/.*.json $DES/ cp platform/npm/*.js $DES/ cp -R platform/npm/tests $DES/ cp platform/npm/README.md $DES/ From 2843aa1c90b4862f400ac43444e2ee46e5524e2a Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Wed, 15 Jan 2025 09:13:18 -0500 Subject: [PATCH 0611/1099] [mv3] Inject highly generic cosmetic filters in all frames Related issue: https://github.com/uBlockOrigin/uAssets/issues/26839 --- platform/mv3/extension/js/scripting-manager.js | 1 + 1 file changed, 1 insertion(+) diff --git a/platform/mv3/extension/js/scripting-manager.js b/platform/mv3/extension/js/scripting-manager.js index 23a6444ea0acd..6bd20a7e89da2 100644 --- a/platform/mv3/extension/js/scripting-manager.js +++ b/platform/mv3/extension/js/scripting-manager.js @@ -139,6 +139,7 @@ function registerHighGeneric(context, genericDetails) { const directive = { id: 'css-generichigh', css, + allFrames: true, matches, excludeMatches, runAt: 'document_end', From 60ed584fc181b5d8dd935d60c32d2592d3674188 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Wed, 15 Jan 2025 10:06:48 -0500 Subject: [PATCH 0612/1099] Import translation work from https://crowdin.com/project/ublock --- src/_locales/ar/messages.json | 12 ++--- src/_locales/zh_TW/messages.json | 90 ++++++++++++++++---------------- 2 files changed, 51 insertions(+), 51 deletions(-) diff --git a/src/_locales/ar/messages.json b/src/_locales/ar/messages.json index 0b62e5e1f6a01..2d37ccf3f9630 100644 --- a/src/_locales/ar/messages.json +++ b/src/_locales/ar/messages.json @@ -104,7 +104,7 @@ "description": "For the new mobile-friendly popup design" }, "popupBlockedSinceInstall_v2": { - "message": "تم حظره منذ التثبيت", + "message": "حُجِب منذ التنصيب", "description": "For the new mobile-friendly popup design" }, "popupDomainsConnected_v2": { @@ -188,7 +188,7 @@ "description": "Tooltip for the no-scripting per-site switch" }, "popupNoPopups_v2": { - "message": "الإطارات المنبثقة", + "message": "نوافذ منبثقة", "description": "Caption for the no-popups per-site switch" }, "popupNoLargeMedia_v2": { @@ -196,7 +196,7 @@ "description": "Caption for the no-large-media per-site switch" }, "popupNoCosmeticFiltering_v2": { - "message": "إعادات الفلاتر التجميلية العمومية", + "message": "المرشحات التجميلية", "description": "Caption for the no-cosmetic-filtering per-site switch" }, "popupNoRemoteFonts_v2": { @@ -272,11 +272,11 @@ "description": "appears in popup" }, "popupVersion": { - "message": "نسخة", + "message": "الإصدار", "description": "Example of use: Version 1.26.4" }, "popup3pScriptFilter": { - "message": "سكربت", + "message": "برنامَج نصي", "description": "Appears as an option to filter out firewall rows" }, "popup3pFrameFilter": { @@ -340,7 +340,7 @@ "description": "Section for controlling user interface appearance" }, "settingsThemeLabel": { - "message": "المظاهر", + "message": "السمة", "description": "Label for checkbox to enable a custom dark theme" }, "settingsThemeAccent0Label": { diff --git a/src/_locales/zh_TW/messages.json b/src/_locales/zh_TW/messages.json index 346ab1e1aa5b5..5b56a89fc7e6a 100644 --- a/src/_locales/zh_TW/messages.json +++ b/src/_locales/zh_TW/messages.json @@ -4,7 +4,7 @@ "description": "extension name." }, "extShortDesc": { - "message": "終於,有一款僅使用少量 CPU 及記憶體的高效能攔截器。", + "message": "一款基於Chromium瀏覽器的高效率廣告封鎖工具,只需要超低的CPU與記憶體使用量。", "description": "this will be in the Chrome web store: must be 132 characters or less" }, "dashboardName": { @@ -16,7 +16,7 @@ "description": "A warning in the dashboard when navigating away from unsaved changes" }, "dashboardUnsavedWarningStay": { - "message": "留在這裡", + "message": "不離開", "description": "Label for button to prevent navigating away from unsaved changes" }, "dashboardUnsavedWarningIgnore": { @@ -40,7 +40,7 @@ "description": "appears as tab name in dashboard" }, "whitelistPageName": { - "message": "白名單", + "message": "受信任網站", "description": "appears as tab name in dashboard" }, "shortcutsPageName": { @@ -72,11 +72,11 @@ "description": "English: Click: disable/enable uBlock₀ for this site.\n\nCtrl+click: disable uBlock₀ only on this page." }, "popupPowerSwitchInfo1": { - "message": "點擊:在此網站停用 uBlock₀ 。\n\nCtrl + 點擊:僅在此頁面停用 uBlock₀ 。", + "message": "點擊:在這個網站停用 uBlock₀ 。\n\nCtrl + 點擊:僅在這個頁面停用 uBlock₀ 。", "description": "Message to be read by screen readers" }, "popupPowerSwitchInfo2": { - "message": "點擊以在此網站啟用 uBlock₀ 。", + "message": "點擊以在這個網站啟用 uBlock₀。", "description": "Message to be read by screen readers" }, "popupBlockedRequestPrompt": { @@ -136,11 +136,11 @@ "description": "Tooltip for the no-popups per-site switch" }, "popupTipNoPopups1": { - "message": "點擊以封鎖此網站的所有彈出式視窗", + "message": "點擊以封鎖這個網站的所有彈出式視窗", "description": "Tooltip for the no-popups per-site switch" }, "popupTipNoPopups2": { - "message": "點擊以解除封鎖此網站的所有彈出式視窗", + "message": "點擊以解除封鎖這個網站的所有彈出式視窗", "description": "Tooltip for the no-popups per-site switch" }, "popupTipNoLargeMedia": { @@ -164,7 +164,7 @@ "description": "Tooltip for the no-cosmetic-filtering per-site switch" }, "popupTipNoCosmeticFiltering2": { - "message": "點擊以啟用此網站的網頁元素過濾", + "message": "點擊以啟用這個網站的網頁元素過濾", "description": "Tooltip for the no-cosmetic-filtering per-site switch" }, "popupTipNoRemoteFonts": { @@ -176,15 +176,15 @@ "description": "Tooltip for the no-remote-fonts per-site switch" }, "popupTipNoRemoteFonts2": { - "message": "點擊以解除封鎖此網站的遠端字型", + "message": "點擊以解除封鎖這個網站的遠端字型", "description": "Tooltip for the no-remote-fonts per-site switch" }, "popupTipNoScripting1": { - "message": "點擊以停用此網站的 JavaScript", + "message": "點擊以停用這個網站的 JavaScript", "description": "Tooltip for the no-scripting per-site switch" }, "popupTipNoScripting2": { - "message": "點擊以重新啟用此網站的 JavaScript ", + "message": "點擊以重新啟用這個網站的 JavaScript ", "description": "Tooltip for the no-scripting per-site switch" }, "popupNoPopups_v2": { @@ -444,7 +444,7 @@ "description": "English: Parse and enforce Adblock+ element hiding filters." }, "3pParseAllABPHideFiltersInfo": { - "message": "「網頁元素過濾規則」用來隱藏網頁中被認為礙眼,且不能被以網路請求為基礎之過濾引擎所阻擋的元素。", + "message": "「網頁元素過濾規則」負責隱藏網頁中被認為礙眼,且不能被網路請求過濾引擎阻擋的元素。", "description": "Describes the purpose of the 'Parse and enforce cosmetic filters' feature." }, "3pIgnoreGenericCosmeticFilters": { @@ -480,11 +480,11 @@ "description": "Filter lists section name" }, "3pGroupMalware": { - "message": "惡意軟體防護及安全性", + "message": "惡意軟體保護及保安", "description": "Filter lists section name" }, "3pGroupSocial": { - "message": "社交小工具", + "message": "社交媒體小工具", "description": "Filter lists section name" }, "3pGroupCookies": { @@ -492,7 +492,7 @@ "description": "Filter lists section name" }, "3pGroupAnnoyances": { - "message": "嫌惡元素", + "message": "騷擾", "description": "Filter lists section name" }, "3pGroupMultipurpose": { @@ -512,7 +512,7 @@ "description": "The label for the checkbox used to import external filter lists" }, "3pExternalListsHint": { - "message": "每行一個網址。無效的網址將被忽略。", + "message": "每行一個網址。以「!」開頭的行將被忽略。無效的網址也將被忽略。", "description": "Short information about how to use the textarea to import external filter lists by URL" }, "3pExternalListObsolete": { @@ -524,7 +524,7 @@ "description": "used as a tooltip for eye icon beside a list" }, "3pLastUpdate": { - "message": "上次更新:{{ago}}。\n點擊此處以強制更新。", + "message": "最後更新:{{ago}}。", "description": "used as a tooltip for the clock icon beside a list" }, "3pUpdating": { @@ -536,11 +536,11 @@ "description": "used as a tooltip for error icon beside a list" }, "1pTrustWarning": { - "message": "切勿加入來歷不明的過濾規則。", + "message": "切勿添加來自不可信來源的過濾規則。", "description": "Warning against copy-pasting filters from random sources" }, "1pEnableMyFiltersLabel": { - "message": "啟用自訂過濾器", + "message": "啟用我的自訂過濾規則", "description": "Label for the checkbox use to enable/disable 'My filters' list" }, "1pTrustMyFiltersLabel": { @@ -624,7 +624,7 @@ "description": "English: a sort option for list of rules." }, "rulesSortByDestination": { - "message": "目標", + "message": "目的地", "description": "English: a sort option for list of rules." }, "whitelistPrompt": { @@ -760,15 +760,15 @@ "description": "Label to identify a rule field" }, "loggerEntryDetailsContext": { - "message": "上下文", + "message": "來源", "description": "Label to identify a context field (typically a hostname)" }, "loggerEntryDetailsRootContext": { - "message": "根上下文", + "message": "主內容", "description": "Label to identify a root context field (typically a hostname)" }, "loggerEntryDetailsPartyness": { - "message": "第一方/第三方", + "message": "第一方/第三方", "description": "Label to identify a field providing partyness information" }, "loggerEntryDetailsType": { @@ -832,15 +832,15 @@ "description": "Used in the static filtering wizard" }, "loggerStaticFilteringFinderSentence1": { - "message": "在下列清單中找到靜態過濾規則 {{filter}}:", + "message": "在下列清單中找到靜態過濾規則 {{filter}}:", "description": "Below this sentence, the filter list(s) in which the filter was found" }, "loggerStaticFilteringFinderSentence2": { - "message": "無法在任何目前已啟用的過濾規則清單中找到靜態過濾規則", + "message": "無法在任何啟用的過濾清單中,找到靜態過濾規則 {{filter}}", "description": "Message to show when a filter cannot be found in any filter lists" }, "loggerSettingDiscardPrompt": { - "message": "未符合以下所有條件的記錄將會被自動捨棄:", + "message": "不符合以下任一狀況的記錄將會被自動清除:", "description": "Logger setting: A sentence to describe the purpose of the settings below" }, "loggerSettingPerEntryMaxAge": { @@ -848,7 +848,7 @@ "description": "A logger setting" }, "loggerSettingPerTabMaxLoads": { - "message": "每個分頁最多保留 {{input}} 次重新載入該頁所產生的記錄", + "message": "每個分頁最多保留 {{input}} 次內容加載產生的記錄", "description": "A logger setting" }, "loggerSettingPerTabMaxEntries": { @@ -856,7 +856,7 @@ "description": "A logger setting" }, "loggerSettingPerEntryLineCount": { - "message": "在垂直延展模式中每條記錄顯示 {{input}} 行", + "message": "在垂直延展的模式中每個項目顯示 {{input}} 行", "description": "A logger setting" }, "loggerSettingHideColumnsPrompt": { @@ -900,7 +900,7 @@ "description": "Text for button which open an external webpage in Support pane" }, "supportReportSpecificButton": { - "message": "建立新報告", + "message": "發出新報告", "description": "Text for button which open an external webpage in Support pane" }, "supportFindSpecificButton": { @@ -924,7 +924,7 @@ "description": "First paragraph of 'Questions and support' section in Support pane" }, "supportS3H": { - "message": "過濾器問題 / 網站被破壞", + "message": "過濾器問題 / 網站被搞壞", "description": "Header of 'Filter issues' section in Support pane" }, "supportS3P1": { @@ -936,7 +936,7 @@ "description": "Second paragraph of 'Filter issues' section in Support pane" }, "supportS3P3": { - "message": "小提示:請確定您的過濾器清單已經更新至最新版本。我們主要用「記錄器」來分析過濾器相關問題。", + "message": "小提示:請確定您的過濾器清單已經更新至最新版本。我們主要用 記錄器 來分析過濾器相關問題。", "description": "Third paragraph of 'Filter issues' section in Support pane" }, "supportS4H": { @@ -964,11 +964,11 @@ "description": "Header of 'Report a filter issue' section in Support pane" }, "supportS6P1S1": { - "message": "為免給志願者帶來額外負擔,請先檢查問題有沒有被回報過,避免重複回報。", + "message": "為了避免太多人發出重複的報告拖垮志工,請先確認是否已經有人回報過您打算報告的問題。", "description": "A paragraph in the filter issue reporter section" }, "supportS6P2S1": { - "message": "過濾器清單每天更新,請確保問題尚未在最新版本中解決。", + "message": "過濾器清單每天更新。請確認您的問題無法用最新的過濾器清單解決。", "description": "A paragraph in the filter issue reporter section" }, "supportS6P2S2": { @@ -992,11 +992,11 @@ "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option2": { - "message": "含有覆蓋物或其他滋擾物", + "message": "會覆蓋內容或有其他煩人的內容", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option3": { - "message": "偵測到 uBlock Origin", + "message": "會偵測到您已安裝 uBlock Origin", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option4": { @@ -1004,11 +1004,11 @@ "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option5": { - "message": "開啟 uBlock Origin 的時候運作不正常", + "message": "開啟 uBlock Origin 的時候網頁運作不正常", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option6": { - "message": "會開啟不需要的分頁或視窗", + "message": "會開啟不想要的分頁或視窗", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option7": { @@ -1036,7 +1036,7 @@ "description": "" }, "aboutCode": { - "message": "原始碼(GPLv3)", + "message": "源碼(GPLv3)", "description": "English: Source code (GPLv3)" }, "aboutContributors": { @@ -1044,7 +1044,7 @@ "description": "English: Contributors" }, "aboutSourceCode": { - "message": "原始碼", + "message": "源碼", "description": "Link text to source code repo" }, "aboutTranslations": { @@ -1056,11 +1056,11 @@ "description": "Link text to uBO's own filter lists repo" }, "aboutDependencies": { - "message": "外部相依套件(與 GPLv3 相容):", + "message": "外部相依套件(與通用公眾授權條款第三版相容):", "description": "Shown in the About pane" }, "aboutCDNs": { - "message": "uBO 自家的過濾規則清單由下列 CDN 免費代管:", + "message": "uBO 自家的過濾規則清單代管於下列 CDN 中:", "description": "Shown in the About pane" }, "aboutCDNsInfo": { @@ -1096,7 +1096,7 @@ "description": "Message asking user to confirm reset" }, "errorCantConnectTo": { - "message": "網路錯誤:{{msg}}", + "message": "無法連線至 {{url}}", "description": "English: Network error: {{msg}}" }, "subscriberConfirm": { @@ -1144,7 +1144,7 @@ "description": "Firefox-specific: appears as 'uBlock₀ (off)'" }, "docblockedTitle": { - "message": "已封鎖頁面", + "message": "頁面已阻擋", "description": "Used as a title for the document-blocked page" }, "docblockedPrompt1": { @@ -1240,7 +1240,7 @@ "description": "An entry in the browser's contextual menu" }, "contextMenuSubscribeToList": { - "message": "訂閱過濾規則清單…", + "message": "訂閱過濾器清單⋯", "description": "An entry in the browser's contextual menu" }, "contextMenuTemporarilyAllowLargeMediaElements": { @@ -1304,7 +1304,7 @@ "description": "Summary of number of errors as reported by the linter " }, "unprocessedRequestTooltip": { - "message": "無法在瀏覽器啟動時正確過濾頁面。請重新載入以確保過濾正常。", + "message": "無法在瀏覽器啟動的時候正確過濾。\n請重新載入頁面來確保過濾正確。", "description": "A warning which will appear in the popup panel if needed" }, "dummy": { From 8629f07138749e7c6088fbfda84a381f2cd3bc66 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sun, 19 Jan 2025 09:35:51 -0500 Subject: [PATCH 0613/1099] Fix "make lint" command; fix more lint errors --- package.json | 2 +- platform/mv3/extension/js/ext.js | 6 +++--- platform/mv3/extension/js/fetch.js | 4 ---- platform/mv3/extension/js/popup.js | 8 ++++---- platform/mv3/extension/js/report.js | 2 +- platform/mv3/extension/js/scripting/css-declarative.js | 8 +------- platform/mv3/extension/js/scripting/css-procedural.js | 10 +++------- platform/mv3/extension/js/scripting/css-specific.js | 8 +------- platform/mv3/extension/js/settings.js | 4 ++-- platform/mv3/extension/js/strictblock.js | 6 +++--- platform/mv3/extension/js/theme.js | 4 ---- platform/mv3/extension/js/utils.js | 2 +- platform/mv3/scriptlets/css-declarative.template.js | 6 ------ platform/mv3/scriptlets/css-generic.template.js | 8 +------- platform/mv3/scriptlets/css-procedural.template.js | 6 ------ platform/mv3/scriptlets/css-specific.template.js | 6 ------ platform/mv3/scriptlets/scriptlet.template.js | 6 ++---- 17 files changed, 23 insertions(+), 73 deletions(-) diff --git a/package.json b/package.json index 63e5b7da353b1..fd65ac4cb4861 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,7 @@ "description": "npm dev tools", "main": "index.js", "scripts": { - "lint": "eslint --no-warn-ignored -- ./src/js/*.js ./src/js/**/*.js ./**/*.json ./platform/**/*.js ", + "lint": "eslint --no-warn-ignored -- \"./src/js/*.js\" \"./src/js/**/*.js\" \"./**/*.json\" \"./platform/**/*.js\"", "test": "echo \"Error: no test specified\" && exit 1" }, "repository": { diff --git a/platform/mv3/extension/js/ext.js b/platform/mv3/extension/js/ext.js index cd63abc674688..2f3285388d62f 100644 --- a/platform/mv3/extension/js/ext.js +++ b/platform/mv3/extension/js/ext.js @@ -64,7 +64,7 @@ export async function localRead(key) { const bin = await browser.storage.local.get(key); if ( bin instanceof Object === false ) { return; } return bin[key] ?? undefined; - } catch(ex) { + } catch { } } @@ -89,7 +89,7 @@ export async function sessionRead(key) { const bin = await browser.storage.session.get(key); if ( bin instanceof Object === false ) { return; } return bin[key] ?? undefined; - } catch(ex) { + } catch { } } @@ -114,7 +114,7 @@ export async function adminRead(key) { const bin = await browser.storage.managed.get(key); if ( bin instanceof Object === false ) { return; } return bin[key] ?? undefined; - } catch(ex) { + } catch { } } diff --git a/platform/mv3/extension/js/fetch.js b/platform/mv3/extension/js/fetch.js index 5570159fd717a..675a37eba9c01 100644 --- a/platform/mv3/extension/js/fetch.js +++ b/platform/mv3/extension/js/fetch.js @@ -19,10 +19,6 @@ Home: https://github.com/gorhill/uBlock */ -/* jshint esversion:11 */ - -'use strict'; - /******************************************************************************/ function fetchJSON(path) { diff --git a/platform/mv3/extension/js/popup.js b/platform/mv3/extension/js/popup.js index 98b4d54ce1434..761f82032f166 100644 --- a/platform/mv3/extension/js/popup.js +++ b/platform/mv3/extension/js/popup.js @@ -79,7 +79,7 @@ async function commitFilteringMode() { granted = await browser.permissions.request({ origins: [ `*://*.${targetHostname}/*` ], }); - } catch(ex) { + } catch { } if ( granted !== true ) { setFilteringMode(beforeLevel); @@ -287,7 +287,7 @@ dom.on('[data-i18n-title="popupTipReport"]', 'click', ev => { let url; try { url = new URL(currentTab.url); - } catch(_) { + } catch { } if ( url === undefined ) { return; } const reportURL = new URL(runtime.getURL('/report.html')); @@ -325,7 +325,7 @@ async function init() { url = new URL(url.hash.slice(1)); } tabURL.href = url.href || ''; - } catch(ex) { + } catch { } if ( url !== undefined ) { @@ -393,7 +393,7 @@ async function init() { async function tryInit() { try { await init(); - } catch(ex) { + } catch { setTimeout(tryInit, 100); } } diff --git a/platform/mv3/extension/js/report.js b/platform/mv3/extension/js/report.js index 35c3a85094100..3ce29bad1705e 100644 --- a/platform/mv3/extension/js/report.js +++ b/platform/mv3/extension/js/report.js @@ -52,7 +52,7 @@ const reportedPage = (( ) => { hostname: parsedURL.hostname.replace(/^(m|mobile|www)\./, ''), mode: url.searchParams.get('mode'), }; - } catch(ex) { + } catch { } return null; })(); diff --git a/platform/mv3/extension/js/scripting/css-declarative.js b/platform/mv3/extension/js/scripting/css-declarative.js index d5c55552f7869..539c97d51070f 100644 --- a/platform/mv3/extension/js/scripting/css-declarative.js +++ b/platform/mv3/extension/js/scripting/css-declarative.js @@ -19,12 +19,6 @@ Home: https://github.com/gorhill/uBlock */ -/* jshint esversion:11 */ - -'use strict'; - -/******************************************************************************/ - // Important! // Isolate from global scope (function uBOL_cssDeclarative() { @@ -39,7 +33,7 @@ delete self.declarativeImports; const hnParts = []; try { hnParts.push(...document.location.hostname.split('.')); } -catch(ex) { } +catch { } const hnpartslen = hnParts.length; if ( hnpartslen === 0 ) { return; } diff --git a/platform/mv3/extension/js/scripting/css-procedural.js b/platform/mv3/extension/js/scripting/css-procedural.js index 58fd4d6d7efe6..da27a6d22dc92 100644 --- a/platform/mv3/extension/js/scripting/css-procedural.js +++ b/platform/mv3/extension/js/scripting/css-procedural.js @@ -19,10 +19,6 @@ Home: https://github.com/gorhill/uBlock */ -/* jshint esversion:11 */ - -/******************************************************************************/ - // Important! // Isolate from global scope (function uBOL_cssProcedural() { @@ -37,7 +33,7 @@ delete self.proceduralImports; const hnParts = []; try { hnParts.push(...document.location.hostname.split('.')); } -catch(ex) { } +catch { } const hnpartslen = hnParts.length; if ( hnpartslen === 0 ) { return; } @@ -621,14 +617,14 @@ class PSelectorRoot extends PSelector { prime(input) { try { return super.prime(input); - } catch (ex) { + } catch { } return []; } exec(input) { try { return super.exec(input); - } catch (ex) { + } catch { } return []; } diff --git a/platform/mv3/extension/js/scripting/css-specific.js b/platform/mv3/extension/js/scripting/css-specific.js index faf997cc05f00..c58ebd9aca0e8 100644 --- a/platform/mv3/extension/js/scripting/css-specific.js +++ b/platform/mv3/extension/js/scripting/css-specific.js @@ -19,12 +19,6 @@ Home: https://github.com/gorhill/uBlock */ -/* jshint esversion:11 */ - -'use strict'; - -/******************************************************************************/ - // Important! // Isolate from global scope (function uBOL_cssSpecific() { @@ -39,7 +33,7 @@ delete self.specificImports; const hnParts = []; try { hnParts.push(...document.location.hostname.split('.')); } -catch(ex) { } +catch { } const hnpartslen = hnParts.length; if ( hnpartslen === 0 ) { return; } diff --git a/platform/mv3/extension/js/settings.js b/platform/mv3/extension/js/settings.js index d924d6f59ae70..51ba0414af34d 100644 --- a/platform/mv3/extension/js/settings.js +++ b/platform/mv3/extension/js/settings.js @@ -195,7 +195,7 @@ function getStagedTrustedSites() { return punycode.toASCII( (new URL(`https://${hn}/`)).hostname ); - } catch(_) { + } catch { } return ''; }).filter(hn => hn !== ''); @@ -292,7 +292,7 @@ sendMessage({ renderFilterLists(cachedRulesetData); renderWidgets(); dom.cl.remove(dom.body, 'loading'); - } catch(ex) { + } catch { } listen(); }).catch(reason => { diff --git a/platform/mv3/extension/js/strictblock.js b/platform/mv3/extension/js/strictblock.js index 3ba92aae703f6..eb4c253714a95 100644 --- a/platform/mv3/extension/js/strictblock.js +++ b/platform/mv3/extension/js/strictblock.js @@ -42,7 +42,7 @@ function urlToFragment(raw) { b.append(hn); fragment.append(raw.slice(0,i), b, raw.slice(i+hn.length)); return fragment; - } catch(_) { + } catch { } return raw; } @@ -55,7 +55,7 @@ const toFinalURL = new URL('about:blank'); try { toURL.href = self.location.hash.slice(1); toFinalURL.href = toURL.href; -} catch(_) { +} catch { } dom.clear('#theURL > p > span:first-of-type'); @@ -144,7 +144,7 @@ function fragmentFromTemplate(template, placeholder, text, details) { let url; try { url = new URL(rawURL); - } catch(ex) { + } catch { return false; } diff --git a/platform/mv3/extension/js/theme.js b/platform/mv3/extension/js/theme.js index a61384e4aa9d2..bab0fbfd068a5 100644 --- a/platform/mv3/extension/js/theme.js +++ b/platform/mv3/extension/js/theme.js @@ -19,10 +19,6 @@ Home: https://github.com/gorhill/uBlock */ -/* jshint esversion:11 */ - -'use strict'; - import { dom } from './dom.js'; /******************************************************************************/ diff --git a/platform/mv3/extension/js/utils.js b/platform/mv3/extension/js/utils.js index 7bf6e13503dfd..dfbe235f33777 100644 --- a/platform/mv3/extension/js/utils.js +++ b/platform/mv3/extension/js/utils.js @@ -24,7 +24,7 @@ function parsedURLromOrigin(origin) { try { return new URL(origin); - } catch(ex) { + } catch { } } diff --git a/platform/mv3/scriptlets/css-declarative.template.js b/platform/mv3/scriptlets/css-declarative.template.js index c1f480fab6eb9..a4ac2a28080de 100644 --- a/platform/mv3/scriptlets/css-declarative.template.js +++ b/platform/mv3/scriptlets/css-declarative.template.js @@ -19,14 +19,8 @@ Home: https://github.com/gorhill/uBlock */ -/* jshint esversion:11 */ - -'use strict'; - // ruleset: $rulesetId$ -/******************************************************************************/ - // Important! // Isolate from global scope (function uBOL_cssDeclarativeImport() { diff --git a/platform/mv3/scriptlets/css-generic.template.js b/platform/mv3/scriptlets/css-generic.template.js index a1f1d6c047604..5c06ead0c2bda 100644 --- a/platform/mv3/scriptlets/css-generic.template.js +++ b/platform/mv3/scriptlets/css-generic.template.js @@ -19,11 +19,7 @@ Home: https://github.com/gorhill/uBlock */ -/* jshint esversion:11 */ - -'use strict'; - -/******************************************************************************/ +// $rulesetId$ // Important! // Isolate from global scope @@ -31,8 +27,6 @@ /******************************************************************************/ -// $rulesetId$ - const toImport = self.$genericSelectorMap$; const genericSelectorMap = self.genericSelectorMap || new Map(); diff --git a/platform/mv3/scriptlets/css-procedural.template.js b/platform/mv3/scriptlets/css-procedural.template.js index 61c95e60695d4..f9f5682d51640 100644 --- a/platform/mv3/scriptlets/css-procedural.template.js +++ b/platform/mv3/scriptlets/css-procedural.template.js @@ -19,14 +19,8 @@ Home: https://github.com/gorhill/uBlock */ -/* jshint esversion:11 */ - -'use strict'; - // ruleset: $rulesetId$ -/******************************************************************************/ - // Important! // Isolate from global scope (function uBOL_cssProceduralImport() { diff --git a/platform/mv3/scriptlets/css-specific.template.js b/platform/mv3/scriptlets/css-specific.template.js index 68589312fffa5..e164e2e58f7de 100644 --- a/platform/mv3/scriptlets/css-specific.template.js +++ b/platform/mv3/scriptlets/css-specific.template.js @@ -19,14 +19,8 @@ Home: https://github.com/gorhill/uBlock */ -/* jshint esversion:11 */ - -'use strict'; - // ruleset: $rulesetId$ -/******************************************************************************/ - // Important! // Isolate from global scope (function uBOL_cssSpecificImports() { diff --git a/platform/mv3/scriptlets/scriptlet.template.js b/platform/mv3/scriptlets/scriptlet.template.js index 19560c8aedbef..4cef174f6b2f2 100644 --- a/platform/mv3/scriptlets/scriptlet.template.js +++ b/platform/mv3/scriptlets/scriptlet.template.js @@ -24,8 +24,6 @@ // ruleset: $rulesetId$ -/******************************************************************************/ - // Important! // Isolate from global scope @@ -66,8 +64,8 @@ try { const pos = origin.lastIndexOf('://'); if ( pos === -1 ) { return; } hnParts.push(...origin.slice(pos+3).split('.')); +} catch { } -catch(ex) { } const hnpartslen = hnParts.length; if ( hnpartslen === 0 ) { return; } @@ -124,7 +122,7 @@ if ( entitiesMap.size !== 0 ) { // Apply scriplets for ( const i of todoIndices ) { try { $scriptletName$(...argsList[i]); } - catch(ex) {} + catch { } } argsList.length = 0; From 35a47d674b47b14fed18691ce5ede6a7adddb4a1 Mon Sep 17 00:00:00 2001 From: Fanboynz Date: Thu, 20 Feb 2025 01:36:02 +1300 Subject: [PATCH 0614/1099] Add "closed","next", "mandatory", "agree/disagree" values to cookies.js (#3934) * Add closed and next values to cookies.js * Add mandatory to cookie value * Add disagree/aggree --- src/js/resources/cookie.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/js/resources/cookie.js b/src/js/resources/cookie.js index 8a7a97076403f..517fa9c044f03 100644 --- a/src/js/resources/cookie.js +++ b/src/js/resources/cookie.js @@ -47,6 +47,8 @@ export function getSafeCookieValuesFn() { 'all', 'none', 'functional', 'granted', 'done', 'decline', 'declined', + 'closed', 'next', 'mandatory', + 'disagree', 'agree', ]; } registerScriptlet(getSafeCookieValuesFn, { From f12351688ad32674102db77c9ef33b3d6c3c6005 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Wed, 19 Feb 2025 07:51:43 -0500 Subject: [PATCH 0615/1099] Import translation work from https://crowdin.com/project/ublock --- platform/mv3/description/webstore.bn.txt | 2 +- platform/mv3/description/webstore.hy.txt | 2 - platform/mv3/description/webstore.ro.txt | 4 +- .../mv3/extension/_locales/bn/messages.json | 60 +++++++++---------- .../mv3/extension/_locales/eu/messages.json | 6 +- .../mv3/extension/_locales/hy/messages.json | 10 ++-- .../mv3/extension/_locales/ro/messages.json | 60 +++++++++---------- .../mv3/extension/_locales/uk/messages.json | 2 +- src/_locales/bn/messages.json | 4 +- src/_locales/hy/messages.json | 2 +- src/_locales/ro/messages.json | 10 ++-- src/_locales/sk/messages.json | 6 +- src/_locales/zh_TW/messages.json | 4 +- 13 files changed, 85 insertions(+), 87 deletions(-) diff --git a/platform/mv3/description/webstore.bn.txt b/platform/mv3/description/webstore.bn.txt index ba1fcbdc5e9d3..25e841c21f389 100644 --- a/platform/mv3/description/webstore.bn.txt +++ b/platform/mv3/description/webstore.bn.txt @@ -7,7 +7,7 @@ uBO Lite (uBOL) হল একটি *অনুমতি-হীন* MV3-ভিত - সহজ গোপনীয়তা - পিটার লো এর বিজ্ঞাপন এবং ট্র্যাকিং সার্ভার তালিকা -You can enable more rulesets by visiting the options page -- click the _Cogs_ icon in the popup panel. +তুমি অপশন পাতায় গিয়ে আরও নিয়ম যোগ করতে পারো -- পপআপ প্যানেলে _গিয়ার_ আইকনে ক্লিক করে। uBOL সম্পূর্ণরূপে ঘোষণামূলক, অর্থাৎ ফিল্টারিং করতে একটি স্থায়ী uBOL প্রক্রিয়ার প্রয়োজন নেই, এবং CSS/JS ইনজেকশন-ভিত্তিক বিষয়বস্তু ফিল্টারিং এক্সটেনশনের পরিবর্তে ব্রাউজার নিজেই নির্ভরযোগ্যভাবে এই কাজ করে থাকে। এর মানে হল যে কন্টেন্ট ব্লকিং চলমান থাকা অবস্থায় uBOL নিজেই CPU/মেমরি রিসোর্স ব্যবহার করে না -- uBOL-এর পরিষেবার প্রক্রিয়ার প্রয়োজন শুধুমাত্র_ যখন আপনি পপআপ প্যানেল বা অপশন পেজগুলির সাথে ইন্টারঅ্যাক্ট করেন। diff --git a/platform/mv3/description/webstore.hy.txt b/platform/mv3/description/webstore.hy.txt index cf9ca080ecd5c..15d2d726b7e44 100644 --- a/platform/mv3/description/webstore.hy.txt +++ b/platform/mv3/description/webstore.hy.txt @@ -17,8 +17,6 @@ uBOL-ը տեղադրման ժամանակ «տվյելները լիովին ը Այնուամենայնիվ, uBOL-ը թույլ է տալիս *դիտմամբ* տրամադրել ընդլայնված թույլտվություններ Ձեր ընտրած կայքերի համար, որպեսզի այն կարողանա էլ ավելի լավ զտել այդ կայքերը՝ օգտագործելով կոսմետիկ զտումը և սցենարների արմատավորումները։ -Для предоставления расширенных разрешений на текущем сайте - откройте всплывающую панель и выберите повышенный режим фильтрации: Оптимальный или Полный. - Ընթացիկ կայքում ընդլայնված թույլտվություններ տրամադրելու համար բացեք դուրս լողացող վահանակը և ընտրեք ընդլայնված զտման ռեժիմ՝ Գերադասելի կամ Ամբողջական։ Այնուհետև զննիչը կզգուշացնի Ձեզ ընթացիկ կայքում ընդլայնման կողմից պահանջվող լրացուցիչ թույլտվությունների տրամադրման հետևանքների մասին, և Դուք պետք է ընտրեք՝ ընդունում եք, թե մերժում եք հայտը։ diff --git a/platform/mv3/description/webstore.ro.txt b/platform/mv3/description/webstore.ro.txt index 61c81d41a5d24..8f64fad1f5832 100644 --- a/platform/mv3/description/webstore.ro.txt +++ b/platform/mv3/description/webstore.ro.txt @@ -1,4 +1,4 @@ -uBO Lite (uBOL) este blocant de conținut experimental *fără permisiuni* bazat pe MV3. +uBO Lite (uBOL) este blocant experimental de conținut *fără permisiuni* bazat pe MV3. Setul de reguli implicit corespunde setului de filtre implicit al uBlock Origin: @@ -7,7 +7,7 @@ Listele de filtre încorporate de uBlock Origin - EasyPrivacy - Oglas Peter Lowe i lista servera za praćenje -You can enable more rulesets by visiting the options page -- click the _Cogs_ icon in the popup panel. +Puteți activa mai multe seturi de reguli accesând pagina de opțiuni - faceți clic pe pictograma _Cogs_ din panoul pop-up. uBOL este în întregime declarativ, ceea ce înseamnă că nu este nevoie de un proces uBOL permanent pentru ca filtrarea să aibă loc, iar filtrarea conținutului pe bază de injecție CSS/JS este realizată în mod sigur de browser în sine, mai degrabă decât de extensie. Aceasta înseamnă că uBOL în sine nu consumă resurse CPU/memorie în timp ce blocarea conținutului este în desfășurare -- procesul de lucru al serviciului uBOL este necesar _doar_ atunci când interacționați cu panoul pop-up sau cu paginile de opțiuni. diff --git a/platform/mv3/extension/_locales/bn/messages.json b/platform/mv3/extension/_locales/bn/messages.json index 9087a0ae719d8..5bbf52a761bb5 100644 --- a/platform/mv3/extension/_locales/bn/messages.json +++ b/platform/mv3/extension/_locales/bn/messages.json @@ -32,7 +32,7 @@ "description": "Label in the popup panel for the current filtering mode" }, "popupTipReport": { - "message": "Report an issue on this website", + "message": "এই ওয়েবসাইট নিয়ে সমস্যা জানাও", "description": "Tooltip used for the 'chat' icon in the panel" }, "popupTipDashboard": { @@ -104,67 +104,67 @@ "description": "Shown in the About pane" }, "supportS6H": { - "message": "Report a filter issue", + "message": "ছাঁকনি নিয়ে সমস্যা জানাও", "description": "Header of 'Report a filter issue' section in Support pane" }, "supportS3P1": { - "message": "Report filter issues with specific websites to the uBlockOrigin/uAssets issue tracker. Requires a GitHub account.", + "message": "নির্দিষ্ট ছাঁকনি বিষয়ক সমস্যা এখানে জানাও uBlockOrigin/uAssets সমস্যা ট্র্যাকার. গিটহাব অ্যাকাউন্ট প্রয়োজন।", "description": "First paragraph of 'Filter issues' section in Support pane" }, "supportS6P1S1": { - "message": "To avoid burdening volunteers with duplicate reports, please verify that the issue has not already been reported.", + "message": "একই প্রতিবেদন দুইবার এড়াতে ও স্বেচ্ছাসেবকদের বোঝা কমাতে, অনুগ্রহ করে যাচাই করো যে সমস্যাটি ইতিমধ্যে জানানো হয়নি।", "description": "A paragraph in the filter issue reporter section" }, "supportFindSpecificButton": { - "message": "Find similar reports", + "message": "একই রকম অভিযোগ দেখ", "description": "A clickable link in the filter issue reporter section" }, "supportS6URL": { - "message": "Address of the webpage:", + "message": "ওয়েবপৃষ্ঠার ঠিকানা:", "description": "Label for the URL of the page" }, "supportS6Select1": { - "message": "The webpage…", + "message": "ওয়েবপৃষ্ঠা…", "description": "Label for widget to select type of issue" }, "supportS6Select1Option0": { - "message": "-- Pick an entry --", + "message": "-- একটি ভুক্তি নির্বাচন করো --", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option1": { - "message": "Shows ads or ad leftovers", + "message": "বিজ্ঞাপন বা বিজ্ঞাপনের অবশিষ্টাংশ দেখায়", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option2": { - "message": "Has overlays or other nuisances", + "message": "ওভারলে বা অন্যান্য উপদ্রব আছে", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option3": { - "message": "Detects uBO Lite", + "message": "ইউবিও লাইট শনাক্ত করে", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option4": { - "message": "Has privacy-related issues", + "message": "গোপনীয়তা-সম্পর্কিত সমস্যা আছে", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option5": { - "message": "Malfunctions when uBO Lite is enabled", + "message": "ইউবিও লাইট চালু করলে সমস্যা হয়", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option6": { - "message": "Opens unwanted tabs or windows", + "message": "অবাঞ্ছিত ট্যাব বা জানালা খুলে", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option7": { - "message": "Leads to badware, phishing", + "message": "ব্যাডওয়্যার, প্রতারণমূলক জায়গায় নেয়", "description": "An entry in the widget used to select the type of issue" }, "supportS6Checkbox1": { - "message": "Label the webpage as “NSFW” (“Not Safe For Work”)", + "message": "ওয়েব পৃষ্ঠাটিকে “NSFW” হিসাবে চিহ্নিত করুন (“Not Safe For Work”)", "description": "A checkbox to use for NSFW sites" }, "supportReportSpecificButton": { - "message": "Create new report", + "message": "নতুন অভিযোগ", "description": "Text for button which open an external webpage in Support pane" }, "firstRunSectionLabel": { @@ -228,55 +228,55 @@ "description": "Label for a checkbox in the options page" }, "showBlockedCountLabel": { - "message": "কতগুলো অনুরোধ ব্লক করা হয়েছে তা টুলবার আইকনে দেখাও", + "message": "কতগুলো অনুরোধ অবরুদ্ধ করা হয়েছে তা টুলবার আইকনে দেখাও", "description": "Label for a checkbox in the options page" }, "enableStrictBlockLabel": { - "message": "Enable strict blocking", + "message": "কঠোর অবরোধ চালু করো", "description": "Label for a checkbox in the options page" }, "enableStrictBlockLegend": { - "message": "Navigation to potentially undesirable sites will be blocked, and you will be offered the option to proceed.", + "message": "সম্ভাব্য অযাচিত ওয়েবসাইট অবরুদ্ধ করা হবে, আর সেখানে আগানোর উপায় দেওয়া থাকবে।", "description": "Label for a checkbox in the options page" }, "findListsPlaceholder": { - "message": "Find lists", + "message": "তালিকা খুঁজো", "description": "Placeholder for the input field used to find lists" }, "strictblockTitle": { - "message": "Page blocked", + "message": "পাতা অবরুদ্ধ", "description": "Webpage title for the strict-blocked page" }, "strictblockSentence1": { - "message": "uBO Lite has prevented the following page from loading:", + "message": "ইউবিও লাইট এই পাতা লোড হওয়া আটকিয়েছে:", "description": "Sentence used in the strict-blocked page" }, "strictblockReasonSentence1": { - "message": "The page was blocked because of a matching filter in {{listname}}.", + "message": "{{listname}} এর একটি ছাঁকনির জন্য এই পাতাটি অবরুদ্ধ করা হয়েছে।", "description": "Text informing about what is causing the page to be blocked" }, "strictblockRedirectSentence1": { - "message": "The blocked page wants to redirect to another site. If you choose to proceed, you will navigate directly to: {{url}}", + "message": "এই অবরুদ্ধ পাতাটি আরেকটি ওয়েবসাইটে নেয়। এগিয়ে যেতে চাইলে, সরাসরি এই জায়গায় যাবে: {{url}}", "description": "Text warning about an incoming redirect" }, "strictblockNoParamsPrompt": { - "message": "without parameters", + "message": "প্যারামিটার নেই", "description": "Label to be used for the parameter-less URL" }, "strictblockBack": { - "message": "Go back", + "message": "ফিরে যাও", "description": "A button to go back to the previous webpage" }, "strictblockClose": { - "message": "Close this window", + "message": "জানালা বন্ধ করো", "description": "A button to close the current tab" }, "strictblockDontWarn": { - "message": "Don't warn me again about this site", + "message": "এই সাইট নিয়ে পুনরায় সতর্ক করবে না", "description": "Label for checkbox in document-blocked page" }, "strictblockProceed": { - "message": "Proceed", + "message": "এগিয়ে যাও", "description": "A button to navigate to the blocked page" } } diff --git a/platform/mv3/extension/_locales/eu/messages.json b/platform/mv3/extension/_locales/eu/messages.json index 812e5eada31ca..65fce033338d2 100644 --- a/platform/mv3/extension/_locales/eu/messages.json +++ b/platform/mv3/extension/_locales/eu/messages.json @@ -32,7 +32,7 @@ "description": "Label in the popup panel for the current filtering mode" }, "popupTipReport": { - "message": "Report an issue on this website", + "message": "Webgune honetan arazo baten berri eman", "description": "Tooltip used for the 'chat' icon in the panel" }, "popupTipDashboard": { @@ -104,7 +104,7 @@ "description": "Shown in the About pane" }, "supportS6H": { - "message": "Report a filter issue", + "message": "iragazkiko arazo baten berria eman", "description": "Header of 'Report a filter issue' section in Support pane" }, "supportS3P1": { @@ -140,7 +140,7 @@ "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option3": { - "message": "Detects uBO Lite", + "message": "uBO Lite detektatzen da", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option4": { diff --git a/platform/mv3/extension/_locales/hy/messages.json b/platform/mv3/extension/_locales/hy/messages.json index 4d8a874100ec3..4c2a5d01124f5 100644 --- a/platform/mv3/extension/_locales/hy/messages.json +++ b/platform/mv3/extension/_locales/hy/messages.json @@ -32,7 +32,7 @@ "description": "Label in the popup panel for the current filtering mode" }, "popupTipReport": { - "message": "Report an issue on this website", + "message": "Զեկուցել կայքի հետ խնդրի մասին", "description": "Tooltip used for the 'chat' icon in the panel" }, "popupTipDashboard": { @@ -124,7 +124,7 @@ "description": "Label for the URL of the page" }, "supportS6Select1": { - "message": "The webpage…", + "message": "Վեբկայքը՝", "description": "Label for widget to select type of issue" }, "supportS6Select1Option0": { @@ -264,11 +264,11 @@ "description": "Label to be used for the parameter-less URL" }, "strictblockBack": { - "message": "Go back", + "message": "Հետ գնալ", "description": "A button to go back to the previous webpage" }, "strictblockClose": { - "message": "Close this window", + "message": "Փակել այս պատուհանը", "description": "A button to close the current tab" }, "strictblockDontWarn": { @@ -276,7 +276,7 @@ "description": "Label for checkbox in document-blocked page" }, "strictblockProceed": { - "message": "Proceed", + "message": "Շարունակել", "description": "A button to navigate to the blocked page" } } diff --git a/platform/mv3/extension/_locales/ro/messages.json b/platform/mv3/extension/_locales/ro/messages.json index 8c29e9a458135..d94f30209ac87 100644 --- a/platform/mv3/extension/_locales/ro/messages.json +++ b/platform/mv3/extension/_locales/ro/messages.json @@ -32,7 +32,7 @@ "description": "Label in the popup panel for the current filtering mode" }, "popupTipReport": { - "message": "Report an issue on this website", + "message": "Raportează o eroare pe acest site web", "description": "Tooltip used for the 'chat' icon in the panel" }, "popupTipDashboard": { @@ -104,67 +104,67 @@ "description": "Shown in the About pane" }, "supportS6H": { - "message": "Report a filter issue", + "message": "Raportează o problemă cu filtrele", "description": "Header of 'Report a filter issue' section in Support pane" }, "supportS3P1": { - "message": "Report filter issues with specific websites to the uBlockOrigin/uAssets issue tracker. Requires a GitHub account.", + "message": "Raportează aici o eroare cu filtrele pentru un site specific uBlockOrigin/uAssets issue tracker. Este necesar un cont GitHub.", "description": "First paragraph of 'Filter issues' section in Support pane" }, "supportS6P1S1": { - "message": "To avoid burdening volunteers with duplicate reports, please verify that the issue has not already been reported.", + "message": "Pentru a evita încărcarea voluntarilor cu rapoarte duplicate, vă rugăm să verificați dacă problema nu a fost deja raportată.", "description": "A paragraph in the filter issue reporter section" }, "supportFindSpecificButton": { - "message": "Find similar reports", + "message": "Găsiți rapoarte similare", "description": "A clickable link in the filter issue reporter section" }, "supportS6URL": { - "message": "Address of the webpage:", + "message": "Adresa paginii web:", "description": "Label for the URL of the page" }, "supportS6Select1": { - "message": "The webpage…", + "message": "Pagina web…", "description": "Label for widget to select type of issue" }, "supportS6Select1Option0": { - "message": "-- Pick an entry --", + "message": "-- Alege o intrare --", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option1": { - "message": "Shows ads or ad leftovers", + "message": "Arată reclame sau resturi de reclame", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option2": { - "message": "Has overlays or other nuisances", + "message": "Are suprapuneri sau alte inconveniente", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option3": { - "message": "Detects uBO Lite", + "message": "Detectează uBO Lite", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option4": { - "message": "Has privacy-related issues", + "message": "Are probleme privind confidențialitatea", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option5": { - "message": "Malfunctions when uBO Lite is enabled", + "message": "Defecțiuni atunci când uBO Lite este activat", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option6": { - "message": "Opens unwanted tabs or windows", + "message": "Deschide file sau ferestre nedorite", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option7": { - "message": "Leads to badware, phishing", + "message": "Duce la programe dăunătoare, phishing", "description": "An entry in the widget used to select the type of issue" }, "supportS6Checkbox1": { - "message": "Label the webpage as “NSFW” (“Not Safe For Work”)", + "message": "Etichetați pagina web ca “NSFW” (“Nu este sigur la locul de muncă”)", "description": "A checkbox to use for NSFW sites" }, "supportReportSpecificButton": { - "message": "Create new report", + "message": "Creează o nouă sesizare", "description": "Text for button which open an external webpage in Support pane" }, "firstRunSectionLabel": { @@ -216,7 +216,7 @@ "description": "A short description for the editable field which lists trusted sites" }, "noFilteringModePlaceholder": { - "message": "[hostnames only]\nexample.com\ngames.example\n...", + "message": "[doar hostnames]\nexample.com\ngames.example\n...", "description": "Default text for in edit field" }, "behaviorSectionLabel": { @@ -232,51 +232,51 @@ "description": "Label for a checkbox in the options page" }, "enableStrictBlockLabel": { - "message": "Enable strict blocking", + "message": "Activați blocarea strictă", "description": "Label for a checkbox in the options page" }, "enableStrictBlockLegend": { - "message": "Navigation to potentially undesirable sites will be blocked, and you will be offered the option to proceed.", + "message": "Navigarea către site-uri potențial nedorite va fi blocată și vi se va oferi opțiunea de a continua.", "description": "Label for a checkbox in the options page" }, "findListsPlaceholder": { - "message": "Find lists", + "message": "Căutați liste", "description": "Placeholder for the input field used to find lists" }, "strictblockTitle": { - "message": "Page blocked", + "message": "Pagină blocată", "description": "Webpage title for the strict-blocked page" }, "strictblockSentence1": { - "message": "uBO Lite has prevented the following page from loading:", + "message": "uBO Lite a împiedicat încărcarea următoarei pagini:", "description": "Sentence used in the strict-blocked page" }, "strictblockReasonSentence1": { - "message": "The page was blocked because of a matching filter in {{listname}}.", + "message": "Pagina a fost blocată din cauza unui filtru din {{listname}}.", "description": "Text informing about what is causing the page to be blocked" }, "strictblockRedirectSentence1": { - "message": "The blocked page wants to redirect to another site. If you choose to proceed, you will navigate directly to: {{url}}", + "message": "Pagina blocată dorește să redirecționeze către un alt site. Dacă alegeți să continuați, veți naviga direct către: {{url}}", "description": "Text warning about an incoming redirect" }, "strictblockNoParamsPrompt": { - "message": "without parameters", + "message": "fără parametri", "description": "Label to be used for the parameter-less URL" }, "strictblockBack": { - "message": "Go back", + "message": "Înapoi", "description": "A button to go back to the previous webpage" }, "strictblockClose": { - "message": "Close this window", + "message": "Închide fereastra", "description": "A button to close the current tab" }, "strictblockDontWarn": { - "message": "Don't warn me again about this site", + "message": "Nu mă avertiza din nou despre acest site", "description": "Label for checkbox in document-blocked page" }, "strictblockProceed": { - "message": "Proceed", + "message": "Continuă", "description": "A button to navigate to the blocked page" } } diff --git a/platform/mv3/extension/_locales/uk/messages.json b/platform/mv3/extension/_locales/uk/messages.json index c6d372980a1e6..58ebe4b686abd 100644 --- a/platform/mv3/extension/_locales/uk/messages.json +++ b/platform/mv3/extension/_locales/uk/messages.json @@ -248,7 +248,7 @@ "description": "Webpage title for the strict-blocked page" }, "strictblockSentence1": { - "message": "uBO Lite заблокував завантаження наступних сторінок:", + "message": "uBO Lite заблокував завантаження таких сторінок:", "description": "Sentence used in the strict-blocked page" }, "strictblockReasonSentence1": { diff --git a/src/_locales/bn/messages.json b/src/_locales/bn/messages.json index 13aa3ac653e1f..82efd870ba1fa 100644 --- a/src/_locales/bn/messages.json +++ b/src/_locales/bn/messages.json @@ -1012,7 +1012,7 @@ "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option7": { - "message": "Leads to badware, phishing", + "message": "ব্যাডওয়্যার, প্রতারণমূলক জায়গায় নেয়", "description": "An entry in the widget used to select the type of issue" }, "supportS6Checkbox1": { @@ -1192,7 +1192,7 @@ "description": "Button text to navigate to the blocked page" }, "docblockedRedirectPrompt": { - "message": "The blocked page wants to redirect to another site. If you choose to proceed, you will navigate directly to: {{url}}", + "message": "ব্লককৃত পাতাটি আরেকটি ওয়েবসাইটে নেয়। এগিয়ে যেতে চাইলে, সরাসরি এই জায়গায় যাবে: {{url}}", "description": "Text warning about an incoming redirect" }, "cloudPush": { diff --git a/src/_locales/hy/messages.json b/src/_locales/hy/messages.json index e76041419c9c7..3fc81ce27c730 100644 --- a/src/_locales/hy/messages.json +++ b/src/_locales/hy/messages.json @@ -1192,7 +1192,7 @@ "description": "Button text to navigate to the blocked page" }, "docblockedRedirectPrompt": { - "message": "The blocked page wants to redirect to another site. If you choose to proceed, you will navigate directly to: {{url}}", + "message": "Արգելափակված կայքը ցանկանում է տեղափոխել Ձեզ ուրիշ կայք։ Եթե ցանկանում եք շարունակել, Դուք անմիջապես կտեղափոխվեք {{url}}", "description": "Text warning about an incoming redirect" }, "cloudPush": { diff --git a/src/_locales/ro/messages.json b/src/_locales/ro/messages.json index 5aacc0ff2048a..75b712aea7956 100644 --- a/src/_locales/ro/messages.json +++ b/src/_locales/ro/messages.json @@ -488,7 +488,7 @@ "description": "Filter lists section name" }, "3pGroupCookies": { - "message": "Cookie notices", + "message": "Notificări privind cookie-urile", "description": "Filter lists section name" }, "3pGroupAnnoyances": { @@ -540,11 +540,11 @@ "description": "Warning against copy-pasting filters from random sources" }, "1pEnableMyFiltersLabel": { - "message": "Enable my custom filters", + "message": "Activați filtrele mele personalizate", "description": "Label for the checkbox use to enable/disable 'My filters' list" }, "1pTrustMyFiltersLabel": { - "message": "Allow custom filters requiring trust", + "message": "Permiteți filtre personalizate care necesită încredere", "description": "Label for the checkbox use to trust the content of 'My filters' list" }, "1pImport": { @@ -1012,7 +1012,7 @@ "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option7": { - "message": "Leads to badware, phishing", + "message": "Duce la programe dăunătoare, phishing", "description": "An entry in the widget used to select the type of issue" }, "supportS6Checkbox1": { @@ -1192,7 +1192,7 @@ "description": "Button text to navigate to the blocked page" }, "docblockedRedirectPrompt": { - "message": "The blocked page wants to redirect to another site. If you choose to proceed, you will navigate directly to: {{url}}", + "message": "Pagina blocată dorește să redirecționeze către un alt site. Dacă alegeți să continuați, veți naviga direct către: {{url}}", "description": "Text warning about an incoming redirect" }, "cloudPush": { diff --git a/src/_locales/sk/messages.json b/src/_locales/sk/messages.json index 04d46777ce198..331c1721c5437 100644 --- a/src/_locales/sk/messages.json +++ b/src/_locales/sk/messages.json @@ -340,11 +340,11 @@ "description": "Section for controlling user interface appearance" }, "settingsThemeLabel": { - "message": "Téma", + "message": "Motív", "description": "Label for checkbox to enable a custom dark theme" }, "settingsThemeAccent0Label": { - "message": "Vlastná farba témy", + "message": "Vlastná farba motívu", "description": "Label for checkbox to pick an accent color" }, "settingsCloudStorageEnabledPrompt": { @@ -372,7 +372,7 @@ "description": "" }, "settingPerSiteSwitchGroupSynopsis": { - "message": "Tieto predvolené správania môžu byť prepísané u jednotlivých stránkach", + "message": "Tieto predvolené správania môžu byť prepísané na jednotlivých stránkach", "description": "" }, "settingsNoCosmeticFilteringPrompt": { diff --git a/src/_locales/zh_TW/messages.json b/src/_locales/zh_TW/messages.json index 5b56a89fc7e6a..67996a8b17a05 100644 --- a/src/_locales/zh_TW/messages.json +++ b/src/_locales/zh_TW/messages.json @@ -4,7 +4,7 @@ "description": "extension name." }, "extShortDesc": { - "message": "一款基於Chromium瀏覽器的高效率廣告封鎖工具,只需要超低的CPU與記憶體使用量。", + "message": "終於有一款高效能的封鎖工具。對 CPU 和記憶體的佔用極低。", "description": "this will be in the Chrome web store: must be 132 characters or less" }, "dashboardName": { @@ -364,7 +364,7 @@ "description": "English: " }, "settingsWebRTCIPAddressHiddenPrompt": { - "message": "防止 WebRTC 洩漏本機 IP 位址", + "message": "防止 WebRTC 洩漏本地 IP 位址", "description": "English: " }, "settingPerSiteSwitchGroup": { From 17c66030fe4a2c8db49d16fd63749bb12412f0a7 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Wed, 19 Feb 2025 08:41:22 -0500 Subject: [PATCH 0616/1099] [mv3] Fix incorrect hostname matching in urlskip-related code Related feedback: https://github.com/uBlockOrigin/uBOL-home/issues/278#issuecomment-2664971115 --- platform/mv3/extension/js/strictblock.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform/mv3/extension/js/strictblock.js b/platform/mv3/extension/js/strictblock.js index eb4c253714a95..23e1372a2cd9f 100644 --- a/platform/mv3/extension/js/strictblock.js +++ b/platform/mv3/extension/js/strictblock.js @@ -243,7 +243,7 @@ function fragmentFromTemplate(template, placeholder, text, details) { const urlskipLists = await Promise.all(toFetch); const toHn = toURL.hostname; const matchesHn = hn => { - if ( hn.endsWith(toHn) === false ) { return false; } + if ( toHn.endsWith(hn) === false ) { return false; } if ( hn.length === toHn.length ) { return true; } return toHn.charAt(toHn.length - hn.length - 1) === '.'; }; From 0df7faffac4b7080b1c66b71bffd8a26aebc41bd Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Wed, 19 Feb 2025 14:01:27 -0500 Subject: [PATCH 0617/1099] Improve `noeval-if` scriptlet Related feedback: https://github.com/uBlockOrigin/uBlock-issues/issues/2907#issuecomment-2660051167 --- src/js/resources/noeval.js | 58 ++++++++++++++++++++++++++++++++++ src/js/resources/scriptlets.js | 35 +------------------- 2 files changed, 59 insertions(+), 34 deletions(-) create mode 100644 src/js/resources/noeval.js diff --git a/src/js/resources/noeval.js b/src/js/resources/noeval.js new file mode 100644 index 0000000000000..d66db532b0f83 --- /dev/null +++ b/src/js/resources/noeval.js @@ -0,0 +1,58 @@ +/******************************************************************************* + + uBlock Origin - a comprehensive, efficient content blocker + Copyright (C) 2019-present Raymond Hill + + 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/}. + + Home: https://github.com/gorhill/uBlock + +*/ + +import { proxyApplyFn } from './proxy-apply.js'; +import { registerScriptlet } from './base.js'; +import { safeSelf } from './safe-self.js'; + +/******************************************************************************/ + +function noEvalIf( + needle = '' +) { + if ( typeof needle !== 'string' ) { return; } + const safe = safeSelf(); + const logPrefix = safe.makeLogPrefix('noeval-if', needle); + const reNeedle = safe.patternToRegex(needle); + proxyApplyFn('eval', function(context) { + const { callArgs } = context; + const a = String(callArgs[0]); + if ( needle !== '' && reNeedle.test(a) ) { + safe.uboLog(logPrefix, 'Prevented:\n', a); + return; + } + if ( needle === '' || safe.logLevel > 1 ) { + safe.uboLog(logPrefix, 'Not prevented:\n', a); + } + return context.reflect(); + }); +} +registerScriptlet(noEvalIf, { + name: 'noeval-if.js', + aliases: [ + 'prevent-eval-if.js', + ], + dependencies: [ + proxyApplyFn, + safeSelf, + ], +}); diff --git a/src/js/resources/scriptlets.js b/src/js/resources/scriptlets.js index 20a34f30e26ce..10a89d0832d1c 100755 --- a/src/js/resources/scriptlets.js +++ b/src/js/resources/scriptlets.js @@ -22,6 +22,7 @@ import './attribute.js'; import './href-sanitizer.js'; +import './noeval.js'; import './replace-argument.js'; import './spoof-css.js'; import './prevent-settimeout.js'; @@ -1598,40 +1599,6 @@ function adjustSetTimeout( /******************************************************************************/ -builtinScriptlets.push({ - name: 'noeval-if.js', - aliases: [ - 'prevent-eval-if.js', - ], - fn: noEvalIf, - dependencies: [ - 'safe-self.fn', - ], -}); -function noEvalIf( - needle = '' -) { - if ( typeof needle !== 'string' ) { return; } - const safe = safeSelf(); - const logPrefix = safe.makeLogPrefix('noeval-if', needle); - const reNeedle = safe.patternToRegex(needle); - window.eval = new Proxy(window.eval, { // jshint ignore: line - apply: function(target, thisArg, args) { - const a = String(args[0]); - if ( needle !== '' && reNeedle.test(a) ) { - safe.uboLog(logPrefix, 'Prevented:\n', a); - return; - } - if ( needle === '' || safe.logLevel > 1 ) { - safe.uboLog(logPrefix, 'Not prevented:\n', a); - } - return Reflect.apply(target, thisArg, args); - } - }); -} - -/******************************************************************************/ - builtinScriptlets.push({ name: 'prevent-fetch.js', aliases: [ From 807ed382e235692a2c020f87cd670b2082cb146f Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Wed, 19 Feb 2025 14:02:57 -0500 Subject: [PATCH 0618/1099] New revision for dev build --- dist/version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dist/version b/dist/version index 4f03d10d18d20..9d2329f3314e6 100644 --- a/dist/version +++ b/dist/version @@ -1 +1 @@ -1.62.1.1 +1.62.1.2 From d2dbb098a275bd962a2e5395d4280a7a6f4ff273 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Wed, 19 Feb 2025 14:26:37 -0500 Subject: [PATCH 0619/1099] Update changelog --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 041c6f6b97905..70f392e5ca79f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,5 @@ +- [Improve `noeval-if` scriptlet](https://github.com/gorhill/uBlock/commit/0df7faffac) +- [Add "closed","next", "mandatory", "agree/disagree" values to `trusted-set-cookie` scriptlet](https://github.com/gorhill/uBlock/commit/35a47d674b) - [Add `decline` value to `set-cookie` scriptlet](https://github.com/gorhill/uBlock/commit/4b12247da1) - [Improve `abort-on-stack-trace` scriptlet](https://github.com/gorhill/uBlock/commit/b617926c1c) - [Improve `href-sanitizer` scriptlet](https://github.com/gorhill/uBlock/commit/551c6bc6eb) From 516ff356e66eaa79e25042e3d79e0ef745077a7b Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Wed, 19 Feb 2025 14:31:28 -0500 Subject: [PATCH 0620/1099] Make Firefox dev build auto-update --- dist/firefox/updates.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dist/firefox/updates.json b/dist/firefox/updates.json index 929c3cc9fbc50..4ce0a42aa59a8 100644 --- a/dist/firefox/updates.json +++ b/dist/firefox/updates.json @@ -3,9 +3,9 @@ "uBlock0@raymondhill.net": { "updates": [ { - "version": "1.62.1.1", + "version": "1.62.1.2", "browser_specific_settings": { "gecko": { "strict_min_version": "78.0" } }, - "update_link": "https://github.com/gorhill/uBlock/releases/download/1.62.1b1/uBlock0_1.62.1b1.firefox.signed.xpi" + "update_link": "https://github.com/gorhill/uBlock/releases/download/1.62.1b2/uBlock0_1.62.1b2.firefox.signed.xpi" } ] } From 344539d79347424af731cd1a924d0abcf39d0a06 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Thu, 20 Feb 2025 08:06:50 -0500 Subject: [PATCH 0621/1099] Comply with Mozilla's "User Consent and Control" Make clear that the hostname of the page with issue is being sent to GitHub's server when searching existing reports or creating a new report. --- platform/mv3/extension/_locales/en/messages.json | 6 +++--- src/_locales/en/messages.json | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/platform/mv3/extension/_locales/en/messages.json b/platform/mv3/extension/_locales/en/messages.json index c40e17428ec7c..e4ac1c56c8c1c 100644 --- a/platform/mv3/extension/_locales/en/messages.json +++ b/platform/mv3/extension/_locales/en/messages.json @@ -112,11 +112,11 @@ "description": "First paragraph of 'Filter issues' section in Support pane" }, "supportS6P1S1": { - "message": "To avoid burdening volunteers with duplicate reports, please verify that the issue has not already been reported.", + "message": "To avoid burdening volunteers with duplicate reports, please verify that the issue has not already been reported. Note: clicking the button will cause the page's origin to be sent to GitHub.", "description": "A paragraph in the filter issue reporter section" }, "supportFindSpecificButton": { - "message": "Find similar reports", + "message": "Find similar reports on GitHub", "description": "A clickable link in the filter issue reporter section" }, "supportS6URL": { @@ -164,7 +164,7 @@ "description": "A checkbox to use for NSFW sites" }, "supportReportSpecificButton": { - "message": "Create new report", + "message": "Create new report on GitHub", "description": "Text for button which open an external webpage in Support pane" }, "firstRunSectionLabel": { diff --git a/src/_locales/en/messages.json b/src/_locales/en/messages.json index de0a8af1500eb..1a5fc098eb803 100644 --- a/src/_locales/en/messages.json +++ b/src/_locales/en/messages.json @@ -901,11 +901,11 @@ "description": "Text for button which open an external webpage in Support pane" }, "supportReportSpecificButton": { - "message": "Create new report", + "message": "Create new report on GitHub", "description": "Text for button which open an external webpage in Support pane" }, "supportFindSpecificButton": { - "message": "Find similar reports", + "message": "Find similar reports on GitHub", "description": "A clickable link in the filter issue reporter section" }, "supportS1H": { @@ -965,7 +965,7 @@ "description": "Header of 'Report a filter issue' section in Support pane" }, "supportS6P1S1": { - "message": "To avoid burdening volunteers with duplicate reports, please verify that the issue has not already been reported.", + "message": "To avoid burdening volunteers with duplicate reports, please verify that the issue has not already been reported. Note: clicking the button will cause the page's origin to be sent to GitHub.", "description": "A paragraph in the filter issue reporter section" }, "supportS6P2S1": { From 4d0ecbae3de205cf9b8723b63853c2c865425a6f Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Thu, 20 Feb 2025 08:44:31 -0500 Subject: [PATCH 0622/1099] Import translation work from https://crowdin.com/project/ublock --- platform/mv3/extension/_locales/ja/messages.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/platform/mv3/extension/_locales/ja/messages.json b/platform/mv3/extension/_locales/ja/messages.json index e94d710cfb7a7..30bc6cd7179f4 100644 --- a/platform/mv3/extension/_locales/ja/messages.json +++ b/platform/mv3/extension/_locales/ja/messages.json @@ -240,7 +240,7 @@ "description": "Label for a checkbox in the options page" }, "findListsPlaceholder": { - "message": "Find lists", + "message": "リストを検索", "description": "Placeholder for the input field used to find lists" }, "strictblockTitle": { @@ -252,11 +252,11 @@ "description": "Sentence used in the strict-blocked page" }, "strictblockReasonSentence1": { - "message": "The page was blocked because of a matching filter in {{listname}}.", + "message": "このページは、{{listname}} のフィルターに一致したためブロックされました。", "description": "Text informing about what is causing the page to be blocked" }, "strictblockRedirectSentence1": { - "message": "The blocked page wants to redirect to another site. If you choose to proceed, you will navigate directly to: {{url}}", + "message": "ブロックしたページは別のサイトへリダイレクトしようとしています。続行すると次の URL へ移動します: {{url}}", "description": "Text warning about an incoming redirect" }, "strictblockNoParamsPrompt": { From 9f27b9feddc86bc7f49fa046e25d8d67d6d56861 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Thu, 20 Feb 2025 09:02:06 -0500 Subject: [PATCH 0623/1099] Import translation work from https://crowdin.com/project/ublock --- platform/mv3/extension/_locales/az/messages.json | 6 +++--- platform/mv3/extension/_locales/bs/messages.json | 6 +++--- platform/mv3/extension/_locales/cv/messages.json | 6 +++--- platform/mv3/extension/_locales/cy/messages.json | 6 +++--- platform/mv3/extension/_locales/de/messages.json | 2 +- platform/mv3/extension/_locales/en_GB/messages.json | 2 +- platform/mv3/extension/_locales/eo/messages.json | 6 +++--- platform/mv3/extension/_locales/es/messages.json | 2 +- platform/mv3/extension/_locales/et/messages.json | 2 +- platform/mv3/extension/_locales/eu/messages.json | 6 +++--- platform/mv3/extension/_locales/fa/messages.json | 6 +++--- platform/mv3/extension/_locales/fil/messages.json | 6 +++--- platform/mv3/extension/_locales/gu/messages.json | 6 +++--- platform/mv3/extension/_locales/hi/messages.json | 6 +++--- platform/mv3/extension/_locales/hy/messages.json | 6 +++--- platform/mv3/extension/_locales/it/messages.json | 2 +- platform/mv3/extension/_locales/kk/messages.json | 6 +++--- platform/mv3/extension/_locales/kn/messages.json | 6 +++--- platform/mv3/extension/_locales/lt/messages.json | 6 +++--- platform/mv3/extension/_locales/ml/messages.json | 6 +++--- platform/mv3/extension/_locales/mr/messages.json | 6 +++--- platform/mv3/extension/_locales/ms/messages.json | 6 +++--- platform/mv3/extension/_locales/nb/messages.json | 6 +++--- platform/mv3/extension/_locales/oc/messages.json | 6 +++--- platform/mv3/extension/_locales/pa/messages.json | 2 +- platform/mv3/extension/_locales/pl/messages.json | 2 +- platform/mv3/extension/_locales/si/messages.json | 2 +- platform/mv3/extension/_locales/sl/messages.json | 6 +++--- platform/mv3/extension/_locales/so/messages.json | 6 +++--- platform/mv3/extension/_locales/sv/messages.json | 8 ++++---- platform/mv3/extension/_locales/sw/messages.json | 6 +++--- platform/mv3/extension/_locales/ta/messages.json | 6 +++--- platform/mv3/extension/_locales/te/messages.json | 6 +++--- platform/mv3/extension/_locales/th/messages.json | 4 ++-- platform/mv3/extension/_locales/ur/messages.json | 6 +++--- src/_locales/cv/messages.json | 6 +++--- src/_locales/cy/messages.json | 2 +- src/_locales/eo/messages.json | 2 +- src/_locales/es/messages.json | 2 +- src/_locales/gu/messages.json | 6 +++--- src/_locales/he/messages.json | 2 +- src/_locales/kk/messages.json | 6 +++--- src/_locales/kn/messages.json | 6 +++--- src/_locales/lt/messages.json | 2 +- src/_locales/ml/messages.json | 2 +- src/_locales/mr/messages.json | 6 +++--- src/_locales/oc/messages.json | 6 +++--- src/_locales/ru/messages.json | 6 +++--- src/_locales/si/messages.json | 2 +- src/_locales/so/messages.json | 6 +++--- src/_locales/sv/messages.json | 6 +++--- src/_locales/ta/messages.json | 2 +- src/_locales/te/messages.json | 2 +- src/_locales/th/messages.json | 4 ++-- src/_locales/tr/messages.json | 2 +- src/_locales/ur/messages.json | 6 +++--- 56 files changed, 131 insertions(+), 131 deletions(-) diff --git a/platform/mv3/extension/_locales/az/messages.json b/platform/mv3/extension/_locales/az/messages.json index b0b311a8bb5af..3037727fa355a 100644 --- a/platform/mv3/extension/_locales/az/messages.json +++ b/platform/mv3/extension/_locales/az/messages.json @@ -112,11 +112,11 @@ "description": "First paragraph of 'Filter issues' section in Support pane" }, "supportS6P1S1": { - "message": "To avoid burdening volunteers with duplicate reports, please verify that the issue has not already been reported.", + "message": "To avoid burdening volunteers with duplicate reports, please verify that the issue has not already been reported. Note: clicking the button will cause the page's origin to be sent to GitHub.", "description": "A paragraph in the filter issue reporter section" }, "supportFindSpecificButton": { - "message": "Find similar reports", + "message": "Find similar reports on GitHub", "description": "A clickable link in the filter issue reporter section" }, "supportS6URL": { @@ -164,7 +164,7 @@ "description": "A checkbox to use for NSFW sites" }, "supportReportSpecificButton": { - "message": "Create new report", + "message": "Create new report on GitHub", "description": "Text for button which open an external webpage in Support pane" }, "firstRunSectionLabel": { diff --git a/platform/mv3/extension/_locales/bs/messages.json b/platform/mv3/extension/_locales/bs/messages.json index 20c51af871ca1..f75505801010f 100644 --- a/platform/mv3/extension/_locales/bs/messages.json +++ b/platform/mv3/extension/_locales/bs/messages.json @@ -112,11 +112,11 @@ "description": "First paragraph of 'Filter issues' section in Support pane" }, "supportS6P1S1": { - "message": "To avoid burdening volunteers with duplicate reports, please verify that the issue has not already been reported.", + "message": "To avoid burdening volunteers with duplicate reports, please verify that the issue has not already been reported. Note: clicking the button will cause the page's origin to be sent to GitHub.", "description": "A paragraph in the filter issue reporter section" }, "supportFindSpecificButton": { - "message": "Find similar reports", + "message": "Find similar reports on GitHub", "description": "A clickable link in the filter issue reporter section" }, "supportS6URL": { @@ -164,7 +164,7 @@ "description": "A checkbox to use for NSFW sites" }, "supportReportSpecificButton": { - "message": "Create new report", + "message": "Create new report on GitHub", "description": "Text for button which open an external webpage in Support pane" }, "firstRunSectionLabel": { diff --git a/platform/mv3/extension/_locales/cv/messages.json b/platform/mv3/extension/_locales/cv/messages.json index b5d2ec8498ea7..8fc526ab35263 100644 --- a/platform/mv3/extension/_locales/cv/messages.json +++ b/platform/mv3/extension/_locales/cv/messages.json @@ -112,11 +112,11 @@ "description": "First paragraph of 'Filter issues' section in Support pane" }, "supportS6P1S1": { - "message": "To avoid burdening volunteers with duplicate reports, please verify that the issue has not already been reported.", + "message": "To avoid burdening volunteers with duplicate reports, please verify that the issue has not already been reported. Note: clicking the button will cause the page's origin to be sent to GitHub.", "description": "A paragraph in the filter issue reporter section" }, "supportFindSpecificButton": { - "message": "Find similar reports", + "message": "Find similar reports on GitHub", "description": "A clickable link in the filter issue reporter section" }, "supportS6URL": { @@ -164,7 +164,7 @@ "description": "A checkbox to use for NSFW sites" }, "supportReportSpecificButton": { - "message": "Create new report", + "message": "Create new report on GitHub", "description": "Text for button which open an external webpage in Support pane" }, "firstRunSectionLabel": { diff --git a/platform/mv3/extension/_locales/cy/messages.json b/platform/mv3/extension/_locales/cy/messages.json index 3e444d525379e..6b5cfee00c452 100644 --- a/platform/mv3/extension/_locales/cy/messages.json +++ b/platform/mv3/extension/_locales/cy/messages.json @@ -112,11 +112,11 @@ "description": "First paragraph of 'Filter issues' section in Support pane" }, "supportS6P1S1": { - "message": "To avoid burdening volunteers with duplicate reports, please verify that the issue has not already been reported.", + "message": "To avoid burdening volunteers with duplicate reports, please verify that the issue has not already been reported. Note: clicking the button will cause the page's origin to be sent to GitHub.", "description": "A paragraph in the filter issue reporter section" }, "supportFindSpecificButton": { - "message": "Find similar reports", + "message": "Find similar reports on GitHub", "description": "A clickable link in the filter issue reporter section" }, "supportS6URL": { @@ -164,7 +164,7 @@ "description": "A checkbox to use for NSFW sites" }, "supportReportSpecificButton": { - "message": "Create new report", + "message": "Create new report on GitHub", "description": "Text for button which open an external webpage in Support pane" }, "firstRunSectionLabel": { diff --git a/platform/mv3/extension/_locales/de/messages.json b/platform/mv3/extension/_locales/de/messages.json index 22b4bb99bb3cb..df439a59080b2 100644 --- a/platform/mv3/extension/_locales/de/messages.json +++ b/platform/mv3/extension/_locales/de/messages.json @@ -112,7 +112,7 @@ "description": "First paragraph of 'Filter issues' section in Support pane" }, "supportS6P1S1": { - "message": "Um die Freiwilligen nicht mit doppelten Meldungen zu belasten, vergewissern Sie sich bitte, dass das Problem nicht schon einmal gemeldet wurde.", + "message": "Um die Helfer nicht mit doppelten Meldungen zu belasten, vergewissern Sie sich bitte, dass das Problem nicht schon einmal gemeldet wurde. Hinweis: Wenn Sie auf die Schaltfläche klicken, wird der Ursprung der Seite an GitHub gesendet.", "description": "A paragraph in the filter issue reporter section" }, "supportFindSpecificButton": { diff --git a/platform/mv3/extension/_locales/en_GB/messages.json b/platform/mv3/extension/_locales/en_GB/messages.json index ff21a6e83edec..fd6916d750635 100644 --- a/platform/mv3/extension/_locales/en_GB/messages.json +++ b/platform/mv3/extension/_locales/en_GB/messages.json @@ -112,7 +112,7 @@ "description": "First paragraph of 'Filter issues' section in Support pane" }, "supportS6P1S1": { - "message": "To avoid burdening volunteers with duplicate reports, please verify that the issue has not already been reported.", + "message": "To avoid burdening volunteers with duplicate reports, please verify that the issue has not already been reported. Note: clicking the button will cause the page's origin to be sent to GitHub.", "description": "A paragraph in the filter issue reporter section" }, "supportFindSpecificButton": { diff --git a/platform/mv3/extension/_locales/eo/messages.json b/platform/mv3/extension/_locales/eo/messages.json index 90e4a10343dfd..6c00013391e9e 100644 --- a/platform/mv3/extension/_locales/eo/messages.json +++ b/platform/mv3/extension/_locales/eo/messages.json @@ -112,11 +112,11 @@ "description": "First paragraph of 'Filter issues' section in Support pane" }, "supportS6P1S1": { - "message": "To avoid burdening volunteers with duplicate reports, please verify that the issue has not already been reported.", + "message": "To avoid burdening volunteers with duplicate reports, please verify that the issue has not already been reported. Note: clicking the button will cause the page's origin to be sent to GitHub.", "description": "A paragraph in the filter issue reporter section" }, "supportFindSpecificButton": { - "message": "Find similar reports", + "message": "Find similar reports on GitHub", "description": "A clickable link in the filter issue reporter section" }, "supportS6URL": { @@ -164,7 +164,7 @@ "description": "A checkbox to use for NSFW sites" }, "supportReportSpecificButton": { - "message": "Create new report", + "message": "Create new report on GitHub", "description": "Text for button which open an external webpage in Support pane" }, "firstRunSectionLabel": { diff --git a/platform/mv3/extension/_locales/es/messages.json b/platform/mv3/extension/_locales/es/messages.json index e17dfe967f5e4..15bc85cabb6d7 100644 --- a/platform/mv3/extension/_locales/es/messages.json +++ b/platform/mv3/extension/_locales/es/messages.json @@ -164,7 +164,7 @@ "description": "A checkbox to use for NSFW sites" }, "supportReportSpecificButton": { - "message": "Crear nuevo reporte", + "message": "Crear informe nuevo", "description": "Text for button which open an external webpage in Support pane" }, "firstRunSectionLabel": { diff --git a/platform/mv3/extension/_locales/et/messages.json b/platform/mv3/extension/_locales/et/messages.json index a6dc44ddafb36..37c23f0164af3 100644 --- a/platform/mv3/extension/_locales/et/messages.json +++ b/platform/mv3/extension/_locales/et/messages.json @@ -112,7 +112,7 @@ "description": "First paragraph of 'Filter issues' section in Support pane" }, "supportS6P1S1": { - "message": "Vabatahtlike koormamise vältimiseks samade teadetega veenduge, et keegi pole selle murega juba varem pöördunud.", + "message": "Vabatahtlike koormamise vältimiseks samade teadetega veenduge, et keegi pole selle murega juba varem pöördunud. Märge! Nupule klõpsamisega saadetakse GitHubile lehekülje aadress.", "description": "A paragraph in the filter issue reporter section" }, "supportFindSpecificButton": { diff --git a/platform/mv3/extension/_locales/eu/messages.json b/platform/mv3/extension/_locales/eu/messages.json index 65fce033338d2..73e6c1c470c0e 100644 --- a/platform/mv3/extension/_locales/eu/messages.json +++ b/platform/mv3/extension/_locales/eu/messages.json @@ -112,11 +112,11 @@ "description": "First paragraph of 'Filter issues' section in Support pane" }, "supportS6P1S1": { - "message": "To avoid burdening volunteers with duplicate reports, please verify that the issue has not already been reported.", + "message": "To avoid burdening volunteers with duplicate reports, please verify that the issue has not already been reported. Note: clicking the button will cause the page's origin to be sent to GitHub.", "description": "A paragraph in the filter issue reporter section" }, "supportFindSpecificButton": { - "message": "Find similar reports", + "message": "Find similar reports on GitHub", "description": "A clickable link in the filter issue reporter section" }, "supportS6URL": { @@ -164,7 +164,7 @@ "description": "A checkbox to use for NSFW sites" }, "supportReportSpecificButton": { - "message": "Create new report", + "message": "Create new report on GitHub", "description": "Text for button which open an external webpage in Support pane" }, "firstRunSectionLabel": { diff --git a/platform/mv3/extension/_locales/fa/messages.json b/platform/mv3/extension/_locales/fa/messages.json index 743018c21a4cd..5d58678db08b6 100644 --- a/platform/mv3/extension/_locales/fa/messages.json +++ b/platform/mv3/extension/_locales/fa/messages.json @@ -112,11 +112,11 @@ "description": "First paragraph of 'Filter issues' section in Support pane" }, "supportS6P1S1": { - "message": "To avoid burdening volunteers with duplicate reports, please verify that the issue has not already been reported.", + "message": "To avoid burdening volunteers with duplicate reports, please verify that the issue has not already been reported. Note: clicking the button will cause the page's origin to be sent to GitHub.", "description": "A paragraph in the filter issue reporter section" }, "supportFindSpecificButton": { - "message": "Find similar reports", + "message": "Find similar reports on GitHub", "description": "A clickable link in the filter issue reporter section" }, "supportS6URL": { @@ -164,7 +164,7 @@ "description": "A checkbox to use for NSFW sites" }, "supportReportSpecificButton": { - "message": "Create new report", + "message": "Create new report on GitHub", "description": "Text for button which open an external webpage in Support pane" }, "firstRunSectionLabel": { diff --git a/platform/mv3/extension/_locales/fil/messages.json b/platform/mv3/extension/_locales/fil/messages.json index 9e5b7cece38a8..937990762817f 100644 --- a/platform/mv3/extension/_locales/fil/messages.json +++ b/platform/mv3/extension/_locales/fil/messages.json @@ -112,11 +112,11 @@ "description": "First paragraph of 'Filter issues' section in Support pane" }, "supportS6P1S1": { - "message": "To avoid burdening volunteers with duplicate reports, please verify that the issue has not already been reported.", + "message": "To avoid burdening volunteers with duplicate reports, please verify that the issue has not already been reported. Note: clicking the button will cause the page's origin to be sent to GitHub.", "description": "A paragraph in the filter issue reporter section" }, "supportFindSpecificButton": { - "message": "Find similar reports", + "message": "Find similar reports on GitHub", "description": "A clickable link in the filter issue reporter section" }, "supportS6URL": { @@ -164,7 +164,7 @@ "description": "A checkbox to use for NSFW sites" }, "supportReportSpecificButton": { - "message": "Create new report", + "message": "Create new report on GitHub", "description": "Text for button which open an external webpage in Support pane" }, "firstRunSectionLabel": { diff --git a/platform/mv3/extension/_locales/gu/messages.json b/platform/mv3/extension/_locales/gu/messages.json index b5d2ec8498ea7..8fc526ab35263 100644 --- a/platform/mv3/extension/_locales/gu/messages.json +++ b/platform/mv3/extension/_locales/gu/messages.json @@ -112,11 +112,11 @@ "description": "First paragraph of 'Filter issues' section in Support pane" }, "supportS6P1S1": { - "message": "To avoid burdening volunteers with duplicate reports, please verify that the issue has not already been reported.", + "message": "To avoid burdening volunteers with duplicate reports, please verify that the issue has not already been reported. Note: clicking the button will cause the page's origin to be sent to GitHub.", "description": "A paragraph in the filter issue reporter section" }, "supportFindSpecificButton": { - "message": "Find similar reports", + "message": "Find similar reports on GitHub", "description": "A clickable link in the filter issue reporter section" }, "supportS6URL": { @@ -164,7 +164,7 @@ "description": "A checkbox to use for NSFW sites" }, "supportReportSpecificButton": { - "message": "Create new report", + "message": "Create new report on GitHub", "description": "Text for button which open an external webpage in Support pane" }, "firstRunSectionLabel": { diff --git a/platform/mv3/extension/_locales/hi/messages.json b/platform/mv3/extension/_locales/hi/messages.json index 66334c281411a..621fff4a660da 100644 --- a/platform/mv3/extension/_locales/hi/messages.json +++ b/platform/mv3/extension/_locales/hi/messages.json @@ -112,11 +112,11 @@ "description": "First paragraph of 'Filter issues' section in Support pane" }, "supportS6P1S1": { - "message": "To avoid burdening volunteers with duplicate reports, please verify that the issue has not already been reported.", + "message": "To avoid burdening volunteers with duplicate reports, please verify that the issue has not already been reported. Note: clicking the button will cause the page's origin to be sent to GitHub.", "description": "A paragraph in the filter issue reporter section" }, "supportFindSpecificButton": { - "message": "Find similar reports", + "message": "Find similar reports on GitHub", "description": "A clickable link in the filter issue reporter section" }, "supportS6URL": { @@ -164,7 +164,7 @@ "description": "A checkbox to use for NSFW sites" }, "supportReportSpecificButton": { - "message": "Create new report", + "message": "Create new report on GitHub", "description": "Text for button which open an external webpage in Support pane" }, "firstRunSectionLabel": { diff --git a/platform/mv3/extension/_locales/hy/messages.json b/platform/mv3/extension/_locales/hy/messages.json index 4c2a5d01124f5..413665e45ed2a 100644 --- a/platform/mv3/extension/_locales/hy/messages.json +++ b/platform/mv3/extension/_locales/hy/messages.json @@ -112,11 +112,11 @@ "description": "First paragraph of 'Filter issues' section in Support pane" }, "supportS6P1S1": { - "message": "To avoid burdening volunteers with duplicate reports, please verify that the issue has not already been reported.", + "message": "To avoid burdening volunteers with duplicate reports, please verify that the issue has not already been reported. Note: clicking the button will cause the page's origin to be sent to GitHub.", "description": "A paragraph in the filter issue reporter section" }, "supportFindSpecificButton": { - "message": "Find similar reports", + "message": "Find similar reports on GitHub", "description": "A clickable link in the filter issue reporter section" }, "supportS6URL": { @@ -164,7 +164,7 @@ "description": "A checkbox to use for NSFW sites" }, "supportReportSpecificButton": { - "message": "Create new report", + "message": "Create new report on GitHub", "description": "Text for button which open an external webpage in Support pane" }, "firstRunSectionLabel": { diff --git a/platform/mv3/extension/_locales/it/messages.json b/platform/mv3/extension/_locales/it/messages.json index e454824ab0d3b..9e0626b6f12ab 100644 --- a/platform/mv3/extension/_locales/it/messages.json +++ b/platform/mv3/extension/_locales/it/messages.json @@ -112,7 +112,7 @@ "description": "First paragraph of 'Filter issues' section in Support pane" }, "supportS6P1S1": { - "message": "Per evitare di gravare sui volontari con segnalazioni doppie, verifica che il problema non sia già stato segnalato.", + "message": "Per evitare di gravare sui volontari con doppie segnalazioni, verificate che il problema non sia già stato segnalato. Nota: facendo clic sul pulsante, l'origine della pagina verrà inviata a GitHub.", "description": "A paragraph in the filter issue reporter section" }, "supportFindSpecificButton": { diff --git a/platform/mv3/extension/_locales/kk/messages.json b/platform/mv3/extension/_locales/kk/messages.json index b5d2ec8498ea7..8fc526ab35263 100644 --- a/platform/mv3/extension/_locales/kk/messages.json +++ b/platform/mv3/extension/_locales/kk/messages.json @@ -112,11 +112,11 @@ "description": "First paragraph of 'Filter issues' section in Support pane" }, "supportS6P1S1": { - "message": "To avoid burdening volunteers with duplicate reports, please verify that the issue has not already been reported.", + "message": "To avoid burdening volunteers with duplicate reports, please verify that the issue has not already been reported. Note: clicking the button will cause the page's origin to be sent to GitHub.", "description": "A paragraph in the filter issue reporter section" }, "supportFindSpecificButton": { - "message": "Find similar reports", + "message": "Find similar reports on GitHub", "description": "A clickable link in the filter issue reporter section" }, "supportS6URL": { @@ -164,7 +164,7 @@ "description": "A checkbox to use for NSFW sites" }, "supportReportSpecificButton": { - "message": "Create new report", + "message": "Create new report on GitHub", "description": "Text for button which open an external webpage in Support pane" }, "firstRunSectionLabel": { diff --git a/platform/mv3/extension/_locales/kn/messages.json b/platform/mv3/extension/_locales/kn/messages.json index 94a0f5288d013..4238ead0daa30 100644 --- a/platform/mv3/extension/_locales/kn/messages.json +++ b/platform/mv3/extension/_locales/kn/messages.json @@ -112,11 +112,11 @@ "description": "First paragraph of 'Filter issues' section in Support pane" }, "supportS6P1S1": { - "message": "To avoid burdening volunteers with duplicate reports, please verify that the issue has not already been reported.", + "message": "To avoid burdening volunteers with duplicate reports, please verify that the issue has not already been reported. Note: clicking the button will cause the page's origin to be sent to GitHub.", "description": "A paragraph in the filter issue reporter section" }, "supportFindSpecificButton": { - "message": "Find similar reports", + "message": "Find similar reports on GitHub", "description": "A clickable link in the filter issue reporter section" }, "supportS6URL": { @@ -164,7 +164,7 @@ "description": "A checkbox to use for NSFW sites" }, "supportReportSpecificButton": { - "message": "Create new report", + "message": "Create new report on GitHub", "description": "Text for button which open an external webpage in Support pane" }, "firstRunSectionLabel": { diff --git a/platform/mv3/extension/_locales/lt/messages.json b/platform/mv3/extension/_locales/lt/messages.json index 964c21d1f5bb8..feffe2456d52e 100644 --- a/platform/mv3/extension/_locales/lt/messages.json +++ b/platform/mv3/extension/_locales/lt/messages.json @@ -112,11 +112,11 @@ "description": "First paragraph of 'Filter issues' section in Support pane" }, "supportS6P1S1": { - "message": "To avoid burdening volunteers with duplicate reports, please verify that the issue has not already been reported.", + "message": "To avoid burdening volunteers with duplicate reports, please verify that the issue has not already been reported. Note: clicking the button will cause the page's origin to be sent to GitHub.", "description": "A paragraph in the filter issue reporter section" }, "supportFindSpecificButton": { - "message": "Find similar reports", + "message": "Find similar reports on GitHub", "description": "A clickable link in the filter issue reporter section" }, "supportS6URL": { @@ -164,7 +164,7 @@ "description": "A checkbox to use for NSFW sites" }, "supportReportSpecificButton": { - "message": "Create new report", + "message": "Create new report on GitHub", "description": "Text for button which open an external webpage in Support pane" }, "firstRunSectionLabel": { diff --git a/platform/mv3/extension/_locales/ml/messages.json b/platform/mv3/extension/_locales/ml/messages.json index 2dd452d2c989f..c0374388ac12c 100644 --- a/platform/mv3/extension/_locales/ml/messages.json +++ b/platform/mv3/extension/_locales/ml/messages.json @@ -112,11 +112,11 @@ "description": "First paragraph of 'Filter issues' section in Support pane" }, "supportS6P1S1": { - "message": "To avoid burdening volunteers with duplicate reports, please verify that the issue has not already been reported.", + "message": "To avoid burdening volunteers with duplicate reports, please verify that the issue has not already been reported. Note: clicking the button will cause the page's origin to be sent to GitHub.", "description": "A paragraph in the filter issue reporter section" }, "supportFindSpecificButton": { - "message": "Find similar reports", + "message": "Find similar reports on GitHub", "description": "A clickable link in the filter issue reporter section" }, "supportS6URL": { @@ -164,7 +164,7 @@ "description": "A checkbox to use for NSFW sites" }, "supportReportSpecificButton": { - "message": "Create new report", + "message": "Create new report on GitHub", "description": "Text for button which open an external webpage in Support pane" }, "firstRunSectionLabel": { diff --git a/platform/mv3/extension/_locales/mr/messages.json b/platform/mv3/extension/_locales/mr/messages.json index b5d2ec8498ea7..8fc526ab35263 100644 --- a/platform/mv3/extension/_locales/mr/messages.json +++ b/platform/mv3/extension/_locales/mr/messages.json @@ -112,11 +112,11 @@ "description": "First paragraph of 'Filter issues' section in Support pane" }, "supportS6P1S1": { - "message": "To avoid burdening volunteers with duplicate reports, please verify that the issue has not already been reported.", + "message": "To avoid burdening volunteers with duplicate reports, please verify that the issue has not already been reported. Note: clicking the button will cause the page's origin to be sent to GitHub.", "description": "A paragraph in the filter issue reporter section" }, "supportFindSpecificButton": { - "message": "Find similar reports", + "message": "Find similar reports on GitHub", "description": "A clickable link in the filter issue reporter section" }, "supportS6URL": { @@ -164,7 +164,7 @@ "description": "A checkbox to use for NSFW sites" }, "supportReportSpecificButton": { - "message": "Create new report", + "message": "Create new report on GitHub", "description": "Text for button which open an external webpage in Support pane" }, "firstRunSectionLabel": { diff --git a/platform/mv3/extension/_locales/ms/messages.json b/platform/mv3/extension/_locales/ms/messages.json index 828adc67eef3d..06c10f84d0445 100644 --- a/platform/mv3/extension/_locales/ms/messages.json +++ b/platform/mv3/extension/_locales/ms/messages.json @@ -112,11 +112,11 @@ "description": "First paragraph of 'Filter issues' section in Support pane" }, "supportS6P1S1": { - "message": "To avoid burdening volunteers with duplicate reports, please verify that the issue has not already been reported.", + "message": "To avoid burdening volunteers with duplicate reports, please verify that the issue has not already been reported. Note: clicking the button will cause the page's origin to be sent to GitHub.", "description": "A paragraph in the filter issue reporter section" }, "supportFindSpecificButton": { - "message": "Find similar reports", + "message": "Find similar reports on GitHub", "description": "A clickable link in the filter issue reporter section" }, "supportS6URL": { @@ -164,7 +164,7 @@ "description": "A checkbox to use for NSFW sites" }, "supportReportSpecificButton": { - "message": "Create new report", + "message": "Create new report on GitHub", "description": "Text for button which open an external webpage in Support pane" }, "firstRunSectionLabel": { diff --git a/platform/mv3/extension/_locales/nb/messages.json b/platform/mv3/extension/_locales/nb/messages.json index c65495bd664c5..c7571d0ea991b 100644 --- a/platform/mv3/extension/_locales/nb/messages.json +++ b/platform/mv3/extension/_locales/nb/messages.json @@ -112,11 +112,11 @@ "description": "First paragraph of 'Filter issues' section in Support pane" }, "supportS6P1S1": { - "message": "To avoid burdening volunteers with duplicate reports, please verify that the issue has not already been reported.", + "message": "To avoid burdening volunteers with duplicate reports, please verify that the issue has not already been reported. Note: clicking the button will cause the page's origin to be sent to GitHub.", "description": "A paragraph in the filter issue reporter section" }, "supportFindSpecificButton": { - "message": "Find similar reports", + "message": "Find similar reports on GitHub", "description": "A clickable link in the filter issue reporter section" }, "supportS6URL": { @@ -164,7 +164,7 @@ "description": "A checkbox to use for NSFW sites" }, "supportReportSpecificButton": { - "message": "Create new report", + "message": "Create new report on GitHub", "description": "Text for button which open an external webpage in Support pane" }, "firstRunSectionLabel": { diff --git a/platform/mv3/extension/_locales/oc/messages.json b/platform/mv3/extension/_locales/oc/messages.json index b5d2ec8498ea7..8fc526ab35263 100644 --- a/platform/mv3/extension/_locales/oc/messages.json +++ b/platform/mv3/extension/_locales/oc/messages.json @@ -112,11 +112,11 @@ "description": "First paragraph of 'Filter issues' section in Support pane" }, "supportS6P1S1": { - "message": "To avoid burdening volunteers with duplicate reports, please verify that the issue has not already been reported.", + "message": "To avoid burdening volunteers with duplicate reports, please verify that the issue has not already been reported. Note: clicking the button will cause the page's origin to be sent to GitHub.", "description": "A paragraph in the filter issue reporter section" }, "supportFindSpecificButton": { - "message": "Find similar reports", + "message": "Find similar reports on GitHub", "description": "A clickable link in the filter issue reporter section" }, "supportS6URL": { @@ -164,7 +164,7 @@ "description": "A checkbox to use for NSFW sites" }, "supportReportSpecificButton": { - "message": "Create new report", + "message": "Create new report on GitHub", "description": "Text for button which open an external webpage in Support pane" }, "firstRunSectionLabel": { diff --git a/platform/mv3/extension/_locales/pa/messages.json b/platform/mv3/extension/_locales/pa/messages.json index fbf91d2ece372..8a6f7440f8d70 100644 --- a/platform/mv3/extension/_locales/pa/messages.json +++ b/platform/mv3/extension/_locales/pa/messages.json @@ -112,7 +112,7 @@ "description": "First paragraph of 'Filter issues' section in Support pane" }, "supportS6P1S1": { - "message": "To avoid burdening volunteers with duplicate reports, please verify that the issue has not already been reported.", + "message": "To avoid burdening volunteers with duplicate reports, please verify that the issue has not already been reported. Note: clicking the button will cause the page's origin to be sent to GitHub.", "description": "A paragraph in the filter issue reporter section" }, "supportFindSpecificButton": { diff --git a/platform/mv3/extension/_locales/pl/messages.json b/platform/mv3/extension/_locales/pl/messages.json index ededd11838d77..089d4c10a1a06 100644 --- a/platform/mv3/extension/_locales/pl/messages.json +++ b/platform/mv3/extension/_locales/pl/messages.json @@ -112,7 +112,7 @@ "description": "First paragraph of 'Filter issues' section in Support pane" }, "supportS6P1S1": { - "message": "Aby uniknąć obciążania wolontariuszy zduplikowanymi zgłoszeniami, sprawdź, czy problem nie został już zgłoszony.", + "message": "Sprawdź, czy problem nie został już zgłoszony, aby uniknąć obciążania wolontariuszy duplikatami raportów. Uwaga: kliknięcie przycisku spowoduje wysłanie źródła strony do serwisu GitHub.", "description": "A paragraph in the filter issue reporter section" }, "supportFindSpecificButton": { diff --git a/platform/mv3/extension/_locales/si/messages.json b/platform/mv3/extension/_locales/si/messages.json index e411dbb45a2ca..73ef6db16270b 100644 --- a/platform/mv3/extension/_locales/si/messages.json +++ b/platform/mv3/extension/_locales/si/messages.json @@ -112,7 +112,7 @@ "description": "First paragraph of 'Filter issues' section in Support pane" }, "supportS6P1S1": { - "message": "To avoid burdening volunteers with duplicate reports, please verify that the issue has not already been reported.", + "message": "To avoid burdening volunteers with duplicate reports, please verify that the issue has not already been reported. Note: clicking the button will cause the page's origin to be sent to GitHub.", "description": "A paragraph in the filter issue reporter section" }, "supportFindSpecificButton": { diff --git a/platform/mv3/extension/_locales/sl/messages.json b/platform/mv3/extension/_locales/sl/messages.json index b5d2ec8498ea7..8fc526ab35263 100644 --- a/platform/mv3/extension/_locales/sl/messages.json +++ b/platform/mv3/extension/_locales/sl/messages.json @@ -112,11 +112,11 @@ "description": "First paragraph of 'Filter issues' section in Support pane" }, "supportS6P1S1": { - "message": "To avoid burdening volunteers with duplicate reports, please verify that the issue has not already been reported.", + "message": "To avoid burdening volunteers with duplicate reports, please verify that the issue has not already been reported. Note: clicking the button will cause the page's origin to be sent to GitHub.", "description": "A paragraph in the filter issue reporter section" }, "supportFindSpecificButton": { - "message": "Find similar reports", + "message": "Find similar reports on GitHub", "description": "A clickable link in the filter issue reporter section" }, "supportS6URL": { @@ -164,7 +164,7 @@ "description": "A checkbox to use for NSFW sites" }, "supportReportSpecificButton": { - "message": "Create new report", + "message": "Create new report on GitHub", "description": "Text for button which open an external webpage in Support pane" }, "firstRunSectionLabel": { diff --git a/platform/mv3/extension/_locales/so/messages.json b/platform/mv3/extension/_locales/so/messages.json index b5d2ec8498ea7..8fc526ab35263 100644 --- a/platform/mv3/extension/_locales/so/messages.json +++ b/platform/mv3/extension/_locales/so/messages.json @@ -112,11 +112,11 @@ "description": "First paragraph of 'Filter issues' section in Support pane" }, "supportS6P1S1": { - "message": "To avoid burdening volunteers with duplicate reports, please verify that the issue has not already been reported.", + "message": "To avoid burdening volunteers with duplicate reports, please verify that the issue has not already been reported. Note: clicking the button will cause the page's origin to be sent to GitHub.", "description": "A paragraph in the filter issue reporter section" }, "supportFindSpecificButton": { - "message": "Find similar reports", + "message": "Find similar reports on GitHub", "description": "A clickable link in the filter issue reporter section" }, "supportS6URL": { @@ -164,7 +164,7 @@ "description": "A checkbox to use for NSFW sites" }, "supportReportSpecificButton": { - "message": "Create new report", + "message": "Create new report on GitHub", "description": "Text for button which open an external webpage in Support pane" }, "firstRunSectionLabel": { diff --git a/platform/mv3/extension/_locales/sv/messages.json b/platform/mv3/extension/_locales/sv/messages.json index e164490906acc..202049b8e5504 100644 --- a/platform/mv3/extension/_locales/sv/messages.json +++ b/platform/mv3/extension/_locales/sv/messages.json @@ -60,7 +60,7 @@ "description": "Header for a ruleset section in 'Filter lists pane'" }, "3pGroupMalware": { - "message": "Domäner med skadlig kod (malware)", + "message": "Skydd mot skadlig programvara, säkerhet", "description": "Header for a ruleset section in 'Filter lists pane'" }, "3pGroupAnnoyances": { @@ -112,11 +112,11 @@ "description": "First paragraph of 'Filter issues' section in Support pane" }, "supportS6P1S1": { - "message": "För att undvika att belasta volontärer med dubbletter av rapporter, kontrollera att problemet inte redan har rapporterats.", + "message": "För att undvika att belasta volontärer med dubbletter av rapporter, kontrollera att problemet inte redan har rapporterats. Observera: om du klickar på knappen kommer sidans ursprung att skickas till GitHub.", "description": "A paragraph in the filter issue reporter section" }, "supportFindSpecificButton": { - "message": "Hitta liknande rapporter", + "message": "Hitta liknande rapporter på GitHub", "description": "A clickable link in the filter issue reporter section" }, "supportS6URL": { @@ -164,7 +164,7 @@ "description": "A checkbox to use for NSFW sites" }, "supportReportSpecificButton": { - "message": "Skapa ny rapport", + "message": "Skapa ny rapport på GitHub", "description": "Text for button which open an external webpage in Support pane" }, "firstRunSectionLabel": { diff --git a/platform/mv3/extension/_locales/sw/messages.json b/platform/mv3/extension/_locales/sw/messages.json index b5d2ec8498ea7..8fc526ab35263 100644 --- a/platform/mv3/extension/_locales/sw/messages.json +++ b/platform/mv3/extension/_locales/sw/messages.json @@ -112,11 +112,11 @@ "description": "First paragraph of 'Filter issues' section in Support pane" }, "supportS6P1S1": { - "message": "To avoid burdening volunteers with duplicate reports, please verify that the issue has not already been reported.", + "message": "To avoid burdening volunteers with duplicate reports, please verify that the issue has not already been reported. Note: clicking the button will cause the page's origin to be sent to GitHub.", "description": "A paragraph in the filter issue reporter section" }, "supportFindSpecificButton": { - "message": "Find similar reports", + "message": "Find similar reports on GitHub", "description": "A clickable link in the filter issue reporter section" }, "supportS6URL": { @@ -164,7 +164,7 @@ "description": "A checkbox to use for NSFW sites" }, "supportReportSpecificButton": { - "message": "Create new report", + "message": "Create new report on GitHub", "description": "Text for button which open an external webpage in Support pane" }, "firstRunSectionLabel": { diff --git a/platform/mv3/extension/_locales/ta/messages.json b/platform/mv3/extension/_locales/ta/messages.json index b5d2ec8498ea7..8fc526ab35263 100644 --- a/platform/mv3/extension/_locales/ta/messages.json +++ b/platform/mv3/extension/_locales/ta/messages.json @@ -112,11 +112,11 @@ "description": "First paragraph of 'Filter issues' section in Support pane" }, "supportS6P1S1": { - "message": "To avoid burdening volunteers with duplicate reports, please verify that the issue has not already been reported.", + "message": "To avoid burdening volunteers with duplicate reports, please verify that the issue has not already been reported. Note: clicking the button will cause the page's origin to be sent to GitHub.", "description": "A paragraph in the filter issue reporter section" }, "supportFindSpecificButton": { - "message": "Find similar reports", + "message": "Find similar reports on GitHub", "description": "A clickable link in the filter issue reporter section" }, "supportS6URL": { @@ -164,7 +164,7 @@ "description": "A checkbox to use for NSFW sites" }, "supportReportSpecificButton": { - "message": "Create new report", + "message": "Create new report on GitHub", "description": "Text for button which open an external webpage in Support pane" }, "firstRunSectionLabel": { diff --git a/platform/mv3/extension/_locales/te/messages.json b/platform/mv3/extension/_locales/te/messages.json index 1dbee4ca9a2e2..f3e48a9d10a18 100644 --- a/platform/mv3/extension/_locales/te/messages.json +++ b/platform/mv3/extension/_locales/te/messages.json @@ -112,11 +112,11 @@ "description": "First paragraph of 'Filter issues' section in Support pane" }, "supportS6P1S1": { - "message": "To avoid burdening volunteers with duplicate reports, please verify that the issue has not already been reported.", + "message": "To avoid burdening volunteers with duplicate reports, please verify that the issue has not already been reported. Note: clicking the button will cause the page's origin to be sent to GitHub.", "description": "A paragraph in the filter issue reporter section" }, "supportFindSpecificButton": { - "message": "Find similar reports", + "message": "Find similar reports on GitHub", "description": "A clickable link in the filter issue reporter section" }, "supportS6URL": { @@ -164,7 +164,7 @@ "description": "A checkbox to use for NSFW sites" }, "supportReportSpecificButton": { - "message": "Create new report", + "message": "Create new report on GitHub", "description": "Text for button which open an external webpage in Support pane" }, "firstRunSectionLabel": { diff --git a/platform/mv3/extension/_locales/th/messages.json b/platform/mv3/extension/_locales/th/messages.json index fea659106cd36..e29ef0cb06652 100644 --- a/platform/mv3/extension/_locales/th/messages.json +++ b/platform/mv3/extension/_locales/th/messages.json @@ -112,11 +112,11 @@ "description": "First paragraph of 'Filter issues' section in Support pane" }, "supportS6P1S1": { - "message": "To avoid burdening volunteers with duplicate reports, please verify that the issue has not already been reported.", + "message": "To avoid burdening volunteers with duplicate reports, please verify that the issue has not already been reported. Note: clicking the button will cause the page's origin to be sent to GitHub.", "description": "A paragraph in the filter issue reporter section" }, "supportFindSpecificButton": { - "message": "Find similar reports", + "message": "Find similar reports on GitHub", "description": "A clickable link in the filter issue reporter section" }, "supportS6URL": { diff --git a/platform/mv3/extension/_locales/ur/messages.json b/platform/mv3/extension/_locales/ur/messages.json index 97cf4fd512537..67fef2a531e4c 100644 --- a/platform/mv3/extension/_locales/ur/messages.json +++ b/platform/mv3/extension/_locales/ur/messages.json @@ -112,11 +112,11 @@ "description": "First paragraph of 'Filter issues' section in Support pane" }, "supportS6P1S1": { - "message": "To avoid burdening volunteers with duplicate reports, please verify that the issue has not already been reported.", + "message": "To avoid burdening volunteers with duplicate reports, please verify that the issue has not already been reported. Note: clicking the button will cause the page's origin to be sent to GitHub.", "description": "A paragraph in the filter issue reporter section" }, "supportFindSpecificButton": { - "message": "Find similar reports", + "message": "Find similar reports on GitHub", "description": "A clickable link in the filter issue reporter section" }, "supportS6URL": { @@ -164,7 +164,7 @@ "description": "A checkbox to use for NSFW sites" }, "supportReportSpecificButton": { - "message": "Create new report", + "message": "Create new report on GitHub", "description": "Text for button which open an external webpage in Support pane" }, "firstRunSectionLabel": { diff --git a/src/_locales/cv/messages.json b/src/_locales/cv/messages.json index b648059a7aed4..657e5c988a742 100644 --- a/src/_locales/cv/messages.json +++ b/src/_locales/cv/messages.json @@ -900,11 +900,11 @@ "description": "Text for button which open an external webpage in Support pane" }, "supportReportSpecificButton": { - "message": "Create new report", + "message": "Create new report on GitHub", "description": "Text for button which open an external webpage in Support pane" }, "supportFindSpecificButton": { - "message": "Find similar reports", + "message": "Find similar reports on GitHub", "description": "A clickable link in the filter issue reporter section" }, "supportS1H": { @@ -964,7 +964,7 @@ "description": "Header of 'Report a filter issue' section in Support pane" }, "supportS6P1S1": { - "message": "To avoid burdening volunteers with duplicate reports, please verify that the issue has not already been reported.", + "message": "To avoid burdening volunteers with duplicate reports, please verify that the issue has not already been reported. Note: clicking the button will cause the page's origin to be sent to GitHub.", "description": "A paragraph in the filter issue reporter section" }, "supportS6P2S1": { diff --git a/src/_locales/cy/messages.json b/src/_locales/cy/messages.json index 02cfc802feb66..d5e6a33f5196f 100644 --- a/src/_locales/cy/messages.json +++ b/src/_locales/cy/messages.json @@ -964,7 +964,7 @@ "description": "Header of 'Report a filter issue' section in Support pane" }, "supportS6P1S1": { - "message": "To avoid burdening volunteers with duplicate reports, please verify that the issue has not already been reported.", + "message": "To avoid burdening volunteers with duplicate reports, please verify that the issue has not already been reported. Note: clicking the button will cause the page's origin to be sent to GitHub.", "description": "A paragraph in the filter issue reporter section" }, "supportS6P2S1": { diff --git a/src/_locales/eo/messages.json b/src/_locales/eo/messages.json index 1f00db737c90e..25175c14c53c6 100644 --- a/src/_locales/eo/messages.json +++ b/src/_locales/eo/messages.json @@ -964,7 +964,7 @@ "description": "Header of 'Report a filter issue' section in Support pane" }, "supportS6P1S1": { - "message": "To avoid burdening volunteers with duplicate reports, please verify that the issue has not already been reported.", + "message": "To avoid burdening volunteers with duplicate reports, please verify that the issue has not already been reported. Note: clicking the button will cause the page's origin to be sent to GitHub.", "description": "A paragraph in the filter issue reporter section" }, "supportS6P2S1": { diff --git a/src/_locales/es/messages.json b/src/_locales/es/messages.json index a4a418d0b45fc..ae5109fad7f99 100644 --- a/src/_locales/es/messages.json +++ b/src/_locales/es/messages.json @@ -904,7 +904,7 @@ "description": "Text for button which open an external webpage in Support pane" }, "supportFindSpecificButton": { - "message": "Encontrar reportes similares", + "message": "Buscar reportes similares", "description": "A clickable link in the filter issue reporter section" }, "supportS1H": { diff --git a/src/_locales/gu/messages.json b/src/_locales/gu/messages.json index ef8377fd70c8f..ff78823b0c12d 100644 --- a/src/_locales/gu/messages.json +++ b/src/_locales/gu/messages.json @@ -900,11 +900,11 @@ "description": "Text for button which open an external webpage in Support pane" }, "supportReportSpecificButton": { - "message": "Create new report", + "message": "Create new report on GitHub", "description": "Text for button which open an external webpage in Support pane" }, "supportFindSpecificButton": { - "message": "Find similar reports", + "message": "Find similar reports on GitHub", "description": "A clickable link in the filter issue reporter section" }, "supportS1H": { @@ -964,7 +964,7 @@ "description": "Header of 'Report a filter issue' section in Support pane" }, "supportS6P1S1": { - "message": "To avoid burdening volunteers with duplicate reports, please verify that the issue has not already been reported.", + "message": "To avoid burdening volunteers with duplicate reports, please verify that the issue has not already been reported. Note: clicking the button will cause the page's origin to be sent to GitHub.", "description": "A paragraph in the filter issue reporter section" }, "supportS6P2S1": { diff --git a/src/_locales/he/messages.json b/src/_locales/he/messages.json index ca9073ecb951b..1bb59f599f156 100644 --- a/src/_locales/he/messages.json +++ b/src/_locales/he/messages.json @@ -904,7 +904,7 @@ "description": "Text for button which open an external webpage in Support pane" }, "supportFindSpecificButton": { - "message": "מצא דוחות דומים", + "message": "חיפוש דוחות דומים", "description": "A clickable link in the filter issue reporter section" }, "supportS1H": { diff --git a/src/_locales/kk/messages.json b/src/_locales/kk/messages.json index a352345886b29..a9a535d0265a3 100644 --- a/src/_locales/kk/messages.json +++ b/src/_locales/kk/messages.json @@ -900,11 +900,11 @@ "description": "Text for button which open an external webpage in Support pane" }, "supportReportSpecificButton": { - "message": "Create new report", + "message": "Create new report on GitHub", "description": "Text for button which open an external webpage in Support pane" }, "supportFindSpecificButton": { - "message": "Find similar reports", + "message": "Find similar reports on GitHub", "description": "A clickable link in the filter issue reporter section" }, "supportS1H": { @@ -964,7 +964,7 @@ "description": "Header of 'Report a filter issue' section in Support pane" }, "supportS6P1S1": { - "message": "To avoid burdening volunteers with duplicate reports, please verify that the issue has not already been reported.", + "message": "To avoid burdening volunteers with duplicate reports, please verify that the issue has not already been reported. Note: clicking the button will cause the page's origin to be sent to GitHub.", "description": "A paragraph in the filter issue reporter section" }, "supportS6P2S1": { diff --git a/src/_locales/kn/messages.json b/src/_locales/kn/messages.json index 84394e4656a4f..f250c57019635 100644 --- a/src/_locales/kn/messages.json +++ b/src/_locales/kn/messages.json @@ -900,11 +900,11 @@ "description": "Text for button which open an external webpage in Support pane" }, "supportReportSpecificButton": { - "message": "Create new report", + "message": "Create new report on GitHub", "description": "Text for button which open an external webpage in Support pane" }, "supportFindSpecificButton": { - "message": "Find similar reports", + "message": "Find similar reports on GitHub", "description": "A clickable link in the filter issue reporter section" }, "supportS1H": { @@ -964,7 +964,7 @@ "description": "Header of 'Report a filter issue' section in Support pane" }, "supportS6P1S1": { - "message": "To avoid burdening volunteers with duplicate reports, please verify that the issue has not already been reported.", + "message": "To avoid burdening volunteers with duplicate reports, please verify that the issue has not already been reported. Note: clicking the button will cause the page's origin to be sent to GitHub.", "description": "A paragraph in the filter issue reporter section" }, "supportS6P2S1": { diff --git a/src/_locales/lt/messages.json b/src/_locales/lt/messages.json index 263a9d8fe6e9a..578506919a7e7 100644 --- a/src/_locales/lt/messages.json +++ b/src/_locales/lt/messages.json @@ -964,7 +964,7 @@ "description": "Header of 'Report a filter issue' section in Support pane" }, "supportS6P1S1": { - "message": "To avoid burdening volunteers with duplicate reports, please verify that the issue has not already been reported.", + "message": "To avoid burdening volunteers with duplicate reports, please verify that the issue has not already been reported. Note: clicking the button will cause the page's origin to be sent to GitHub.", "description": "A paragraph in the filter issue reporter section" }, "supportS6P2S1": { diff --git a/src/_locales/ml/messages.json b/src/_locales/ml/messages.json index 1bb8d1e866f65..84596d2a3c5c5 100644 --- a/src/_locales/ml/messages.json +++ b/src/_locales/ml/messages.json @@ -964,7 +964,7 @@ "description": "Header of 'Report a filter issue' section in Support pane" }, "supportS6P1S1": { - "message": "To avoid burdening volunteers with duplicate reports, please verify that the issue has not already been reported.", + "message": "To avoid burdening volunteers with duplicate reports, please verify that the issue has not already been reported. Note: clicking the button will cause the page's origin to be sent to GitHub.", "description": "A paragraph in the filter issue reporter section" }, "supportS6P2S1": { diff --git a/src/_locales/mr/messages.json b/src/_locales/mr/messages.json index 56407af1acfa3..0bbe951da70ad 100644 --- a/src/_locales/mr/messages.json +++ b/src/_locales/mr/messages.json @@ -900,11 +900,11 @@ "description": "Text for button which open an external webpage in Support pane" }, "supportReportSpecificButton": { - "message": "Create new report", + "message": "Create new report on GitHub", "description": "Text for button which open an external webpage in Support pane" }, "supportFindSpecificButton": { - "message": "Find similar reports", + "message": "Find similar reports on GitHub", "description": "A clickable link in the filter issue reporter section" }, "supportS1H": { @@ -964,7 +964,7 @@ "description": "Header of 'Report a filter issue' section in Support pane" }, "supportS6P1S1": { - "message": "To avoid burdening volunteers with duplicate reports, please verify that the issue has not already been reported.", + "message": "To avoid burdening volunteers with duplicate reports, please verify that the issue has not already been reported. Note: clicking the button will cause the page's origin to be sent to GitHub.", "description": "A paragraph in the filter issue reporter section" }, "supportS6P2S1": { diff --git a/src/_locales/oc/messages.json b/src/_locales/oc/messages.json index d1a3fa188d502..83616706d628e 100644 --- a/src/_locales/oc/messages.json +++ b/src/_locales/oc/messages.json @@ -900,11 +900,11 @@ "description": "Text for button which open an external webpage in Support pane" }, "supportReportSpecificButton": { - "message": "Create new report", + "message": "Create new report on GitHub", "description": "Text for button which open an external webpage in Support pane" }, "supportFindSpecificButton": { - "message": "Find similar reports", + "message": "Find similar reports on GitHub", "description": "A clickable link in the filter issue reporter section" }, "supportS1H": { @@ -964,7 +964,7 @@ "description": "Header of 'Report a filter issue' section in Support pane" }, "supportS6P1S1": { - "message": "To avoid burdening volunteers with duplicate reports, please verify that the issue has not already been reported.", + "message": "To avoid burdening volunteers with duplicate reports, please verify that the issue has not already been reported. Note: clicking the button will cause the page's origin to be sent to GitHub.", "description": "A paragraph in the filter issue reporter section" }, "supportS6P2S1": { diff --git a/src/_locales/ru/messages.json b/src/_locales/ru/messages.json index 8cd58ef040c49..fe186f6c3c05c 100644 --- a/src/_locales/ru/messages.json +++ b/src/_locales/ru/messages.json @@ -900,11 +900,11 @@ "description": "Text for button which open an external webpage in Support pane" }, "supportReportSpecificButton": { - "message": "Создать новый отчет", + "message": "Создать новый отчёт", "description": "Text for button which open an external webpage in Support pane" }, "supportFindSpecificButton": { - "message": "Найти похожие отчеты", + "message": "Найти похожие отчёты", "description": "A clickable link in the filter issue reporter section" }, "supportS1H": { @@ -964,7 +964,7 @@ "description": "Header of 'Report a filter issue' section in Support pane" }, "supportS6P1S1": { - "message": "Чтобы не обременять волонтеров дублированными отчетами, пожалуйста, убедитесь, что о данной проблеме еще не сообщали", + "message": "Чтобы не обременять добровольцев дублированными отчётами, пожалуйста, убедитесь, что о данной проблеме еще не сообщали.", "description": "A paragraph in the filter issue reporter section" }, "supportS6P2S1": { diff --git a/src/_locales/si/messages.json b/src/_locales/si/messages.json index eeafed6f67220..71ab9a720f98b 100644 --- a/src/_locales/si/messages.json +++ b/src/_locales/si/messages.json @@ -964,7 +964,7 @@ "description": "Header of 'Report a filter issue' section in Support pane" }, "supportS6P1S1": { - "message": "To avoid burdening volunteers with duplicate reports, please verify that the issue has not already been reported.", + "message": "To avoid burdening volunteers with duplicate reports, please verify that the issue has not already been reported. Note: clicking the button will cause the page's origin to be sent to GitHub.", "description": "A paragraph in the filter issue reporter section" }, "supportS6P2S1": { diff --git a/src/_locales/so/messages.json b/src/_locales/so/messages.json index c0ba2c7c70356..3a247c8ce4638 100644 --- a/src/_locales/so/messages.json +++ b/src/_locales/so/messages.json @@ -900,11 +900,11 @@ "description": "Text for button which open an external webpage in Support pane" }, "supportReportSpecificButton": { - "message": "Create new report", + "message": "Create new report on GitHub", "description": "Text for button which open an external webpage in Support pane" }, "supportFindSpecificButton": { - "message": "Find similar reports", + "message": "Find similar reports on GitHub", "description": "A clickable link in the filter issue reporter section" }, "supportS1H": { @@ -964,7 +964,7 @@ "description": "Header of 'Report a filter issue' section in Support pane" }, "supportS6P1S1": { - "message": "To avoid burdening volunteers with duplicate reports, please verify that the issue has not already been reported.", + "message": "To avoid burdening volunteers with duplicate reports, please verify that the issue has not already been reported. Note: clicking the button will cause the page's origin to be sent to GitHub.", "description": "A paragraph in the filter issue reporter section" }, "supportS6P2S1": { diff --git a/src/_locales/sv/messages.json b/src/_locales/sv/messages.json index e1dbccb445bb8..22c291e256c4b 100644 --- a/src/_locales/sv/messages.json +++ b/src/_locales/sv/messages.json @@ -900,11 +900,11 @@ "description": "Text for button which open an external webpage in Support pane" }, "supportReportSpecificButton": { - "message": "Skapa ny rapport", + "message": "Skapa ny rapport på GitHub", "description": "Text for button which open an external webpage in Support pane" }, "supportFindSpecificButton": { - "message": "Hitta liknande rapporter", + "message": "Hitta liknande rapporter på GitHub", "description": "A clickable link in the filter issue reporter section" }, "supportS1H": { @@ -964,7 +964,7 @@ "description": "Header of 'Report a filter issue' section in Support pane" }, "supportS6P1S1": { - "message": "För att undvika att belasta volontärer med dubbletter av rapporter, kontrollera att problemet inte redan har rapporterats.", + "message": "För att undvika att belasta volontärer med dubbletter av rapporter, kontrollera att problemet inte redan har rapporterats. Observera: om du klickar på knappen kommer sidans ursprung att skickas till GitHub.", "description": "A paragraph in the filter issue reporter section" }, "supportS6P2S1": { diff --git a/src/_locales/ta/messages.json b/src/_locales/ta/messages.json index 262a988ed4256..add26031b824e 100644 --- a/src/_locales/ta/messages.json +++ b/src/_locales/ta/messages.json @@ -964,7 +964,7 @@ "description": "Header of 'Report a filter issue' section in Support pane" }, "supportS6P1S1": { - "message": "To avoid burdening volunteers with duplicate reports, please verify that the issue has not already been reported.", + "message": "To avoid burdening volunteers with duplicate reports, please verify that the issue has not already been reported. Note: clicking the button will cause the page's origin to be sent to GitHub.", "description": "A paragraph in the filter issue reporter section" }, "supportS6P2S1": { diff --git a/src/_locales/te/messages.json b/src/_locales/te/messages.json index 68ebfc80a0801..e678d64f66561 100644 --- a/src/_locales/te/messages.json +++ b/src/_locales/te/messages.json @@ -964,7 +964,7 @@ "description": "Header of 'Report a filter issue' section in Support pane" }, "supportS6P1S1": { - "message": "To avoid burdening volunteers with duplicate reports, please verify that the issue has not already been reported.", + "message": "To avoid burdening volunteers with duplicate reports, please verify that the issue has not already been reported. Note: clicking the button will cause the page's origin to be sent to GitHub.", "description": "A paragraph in the filter issue reporter section" }, "supportS6P2S1": { diff --git a/src/_locales/th/messages.json b/src/_locales/th/messages.json index 55ba15a03f0a6..f420860563809 100644 --- a/src/_locales/th/messages.json +++ b/src/_locales/th/messages.json @@ -904,7 +904,7 @@ "description": "Text for button which open an external webpage in Support pane" }, "supportFindSpecificButton": { - "message": "Find similar reports", + "message": "Find similar reports on GitHub", "description": "A clickable link in the filter issue reporter section" }, "supportS1H": { @@ -964,7 +964,7 @@ "description": "Header of 'Report a filter issue' section in Support pane" }, "supportS6P1S1": { - "message": "To avoid burdening volunteers with duplicate reports, please verify that the issue has not already been reported.", + "message": "To avoid burdening volunteers with duplicate reports, please verify that the issue has not already been reported. Note: clicking the button will cause the page's origin to be sent to GitHub.", "description": "A paragraph in the filter issue reporter section" }, "supportS6P2S1": { diff --git a/src/_locales/tr/messages.json b/src/_locales/tr/messages.json index f3d2276db5950..78435caab57ee 100644 --- a/src/_locales/tr/messages.json +++ b/src/_locales/tr/messages.json @@ -904,7 +904,7 @@ "description": "Text for button which open an external webpage in Support pane" }, "supportFindSpecificButton": { - "message": "Benzer raporları bul", + "message": "Benzer raporlar bul", "description": "A clickable link in the filter issue reporter section" }, "supportS1H": { diff --git a/src/_locales/ur/messages.json b/src/_locales/ur/messages.json index ec794cbfb966e..fa79bed9b1a52 100644 --- a/src/_locales/ur/messages.json +++ b/src/_locales/ur/messages.json @@ -900,11 +900,11 @@ "description": "Text for button which open an external webpage in Support pane" }, "supportReportSpecificButton": { - "message": "Create new report", + "message": "Create new report on GitHub", "description": "Text for button which open an external webpage in Support pane" }, "supportFindSpecificButton": { - "message": "Find similar reports", + "message": "Find similar reports on GitHub", "description": "A clickable link in the filter issue reporter section" }, "supportS1H": { @@ -964,7 +964,7 @@ "description": "Header of 'Report a filter issue' section in Support pane" }, "supportS6P1S1": { - "message": "To avoid burdening volunteers with duplicate reports, please verify that the issue has not already been reported.", + "message": "To avoid burdening volunteers with duplicate reports, please verify that the issue has not already been reported. Note: clicking the button will cause the page's origin to be sent to GitHub.", "description": "A paragraph in the filter issue reporter section" }, "supportS6P2S1": { From c778f339cd55b07f5c03ff324784558bf1adad2a Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Thu, 20 Feb 2025 09:02:25 -0500 Subject: [PATCH 0624/1099] Update changelog --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 70f392e5ca79f..5f4ac56a31a88 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,5 @@ - [Improve `noeval-if` scriptlet](https://github.com/gorhill/uBlock/commit/0df7faffac) -- [Add "closed","next", "mandatory", "agree/disagree" values to `trusted-set-cookie` scriptlet](https://github.com/gorhill/uBlock/commit/35a47d674b) +- [Add "closed","next", "mandatory", "agree/disagree" values to `trusted-set-cookie` scriptlet](https://github.com/gorhill/uBlock/commit/35a47d674b) (by @ryanbr) - [Add `decline` value to `set-cookie` scriptlet](https://github.com/gorhill/uBlock/commit/4b12247da1) - [Improve `abort-on-stack-trace` scriptlet](https://github.com/gorhill/uBlock/commit/b617926c1c) - [Improve `href-sanitizer` scriptlet](https://github.com/gorhill/uBlock/commit/551c6bc6eb) From 9bb1a2baaf0063099c361ff2e24b8f312a6a9546 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Thu, 20 Feb 2025 09:47:36 -0500 Subject: [PATCH 0625/1099] Improve `evaldata-prune` scriptlet --- src/js/resources/scriptlets.js | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/src/js/resources/scriptlets.js b/src/js/resources/scriptlets.js index 10a89d0832d1c..8ee5603b5fa4d 100755 --- a/src/js/resources/scriptlets.js +++ b/src/js/resources/scriptlets.js @@ -1472,21 +1472,19 @@ builtinScriptlets.push({ fn: evaldataPrune, dependencies: [ 'object-prune.fn', + 'proxy-apply.fn', ], }); function evaldataPrune( rawPrunePaths = '', rawNeedlePaths = '' ) { - self.eval = new Proxy(self.eval, { - apply(target, thisArg, args) { - const before = Reflect.apply(target, thisArg, args); - if ( typeof before === 'object' ) { - const after = objectPruneFn(before, rawPrunePaths, rawNeedlePaths); - return after || before; - } - return before; - } + proxyApplyFn('eval', function(context) { + const before = context.reflect(); + if ( typeof before !== 'object' ) { return before; } + if ( before === null ) { return null; } + const after = objectPruneFn(before, rawPrunePaths, rawNeedlePaths); + return after || before; }); } From 5921e50e03bf2f86550ef16b0a72bf6c0bbda8f3 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Thu, 20 Feb 2025 15:11:36 -0500 Subject: [PATCH 0626/1099] Fix reverse lookup of `##^responseheader(...)` filters Related issue: https://github.com/uBlockOrigin/uBlock-issues/issues/3544 --- src/js/httpheader-filtering.js | 4 +--- src/js/reverselookup-worker.js | 12 ++++++++++-- src/js/reverselookup.js | 10 ++++++---- src/js/static-filtering-parser.js | 6 ++++++ 4 files changed, 23 insertions(+), 9 deletions(-) diff --git a/src/js/httpheader-filtering.js b/src/js/httpheader-filtering.js index 58781fb61bf3c..20f83219b4f2b 100644 --- a/src/js/httpheader-filtering.js +++ b/src/js/httpheader-filtering.js @@ -19,7 +19,6 @@ Home: https://github.com/gorhill/uBlock */ -import * as sfp from './static-filtering-parser.js'; import { StaticExtFilteringHostnameDB } from './static-ext-filtering-db.js'; import { entityFromDomain } from './uri-utils.js'; import logger from './logger.js'; @@ -83,8 +82,7 @@ httpheaderFilteringEngine.compile = function(parser, writer) { writer.select('HTTPHEADER_FILTERS'); const isException = parser.isException(); - const root = parser.getBranchFromType(sfp.NODE_TYPE_EXT_PATTERN_RESPONSEHEADER); - const headerName = parser.getNodeString(root); + const headerName = parser.getResponseheaderName(); // Tokenless is meaningful only for exception filters. if ( headerName === '' && isException === false ) { return; } diff --git a/src/js/reverselookup-worker.js b/src/js/reverselookup-worker.js index ffdf5668680ef..1cad3cf0218ee 100644 --- a/src/js/reverselookup-worker.js +++ b/src/js/reverselookup-worker.js @@ -120,7 +120,7 @@ const fromExtendedFilter = function(details) { // The longer the needle, the lower the number of false positives. // https://github.com/uBlockOrigin/uBlock-issues/issues/1139 // Mind that there is no guarantee a selector has `\w` characters. - const needle = selector.match(/\w+|\*/g).reduce(function(a, b) { + const needle = (details.needle || selector).match(/\w+|\*/g).reduce(function(a, b) { return a.length > b.length ? a : b; }); @@ -213,6 +213,12 @@ const fromExtendedFilter = function(details) { /* fallthrough */ case 64: { if ( exception !== ((fargs[2] & 0b001) !== 0) ) { break; } + if ( /^responseheader\(.+\)$/.test(selector) ) { + if ( fargs[3] !== needle ) { break; } + if ( hostnameMatches(fargs[1]) === false ) { break; } + found = fargs[1] + prefix + selector; + break; + } const isProcedural = (fargs[2] & 0b010) !== 0; if ( isProcedural === false && fargs[3] !== selector || @@ -236,11 +242,13 @@ const fromExtendedFilter = function(details) { // Scriptlet injection case 32: if ( exception !== ((fargs[2] & 0b001) !== 0) ) { break; } - if ( fargs[3] !== details.compiled ) { break; } + if ( fargs[3] !== details.needle ) { break; } if ( hostnameMatches(fargs[1]) ) { found = fargs[1] + prefix + selector; } break; + default: + break; } if ( found !== undefined ) { if ( response[found] === undefined ) { diff --git a/src/js/reverselookup.js b/src/js/reverselookup.js index e1fa006d41dbb..7a0bfb04c34e2 100644 --- a/src/js/reverselookup.js +++ b/src/js/reverselookup.js @@ -93,7 +93,7 @@ const initWorker = function() { }; for ( const listKey in µb.availableFilterLists ) { - if ( µb.availableFilterLists.hasOwnProperty(listKey) === false ) { + if ( Object.prototype.hasOwnProperty.call(µb.availableFilterLists, listKey) === false ) { continue; } const entry = µb.availableFilterLists[listKey]; @@ -167,9 +167,11 @@ const fromExtendedFilter = async function(details) { nativeCssHas: vAPI.webextFlavor.env.includes('native_css_has'), }); parser.parse(details.rawFilter); - let compiled; + let needle; if ( parser.isScriptletFilter() ) { - compiled = JSON.stringify(parser.getScriptletArgs()); + needle = JSON.stringify(parser.getScriptletArgs()); + } else if ( parser.isResponseheaderFilter() ) { + needle = parser.getResponseheaderName(); } worker.postMessage({ @@ -188,7 +190,7 @@ const fromExtendedFilter = async function(details) { details.url ) === 2, rawFilter: details.rawFilter, - compiled, + needle, }); return new Promise(resolve => { diff --git a/src/js/static-filtering-parser.js b/src/js/static-filtering-parser.js index 73507cc6b4ec8..9315460a6f8f9 100644 --- a/src/js/static-filtering-parser.js +++ b/src/js/static-filtering-parser.js @@ -2494,6 +2494,12 @@ export class AstFilterParser { return head; } + getResponseheaderName() { + if ( this.isResponseheaderFilter() === false ) { return ''; } + const root = this.getBranchFromType(NODE_TYPE_EXT_PATTERN_RESPONSEHEADER); + return this.getNodeString(root); + } + parseExtPatternHtml(parent) { const beg = this.nodes[parent+NODE_BEG_INDEX]; const end = this.nodes[parent+NODE_END_INDEX]; From 839d1c31b3833247401572907a9ebe205e5ae1cb Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Thu, 20 Feb 2025 15:15:51 -0500 Subject: [PATCH 0627/1099] Update changelog --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5f4ac56a31a88..15e29485718dd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,6 @@ +- [Fix reverse lookup of `##^responseheader(...)` filters](https://github.com/gorhill/uBlock/commit/5921e50e03) +- [Improve `evaldata-prune` scriptlet](https://github.com/gorhill/uBlock/commit/9bb1a2baaf) +- [Comply with Mozilla's "User Consent and Control"](https://github.com/gorhill/uBlock/commit/344539d793) - [Improve `noeval-if` scriptlet](https://github.com/gorhill/uBlock/commit/0df7faffac) - [Add "closed","next", "mandatory", "agree/disagree" values to `trusted-set-cookie` scriptlet](https://github.com/gorhill/uBlock/commit/35a47d674b) (by @ryanbr) - [Add `decline` value to `set-cookie` scriptlet](https://github.com/gorhill/uBlock/commit/4b12247da1) From 80a04efefe92051f85c5676cb9388897df3d2f6a Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Thu, 20 Feb 2025 15:16:11 -0500 Subject: [PATCH 0628/1099] New revision for dev build --- dist/version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dist/version b/dist/version index 9d2329f3314e6..2d2375905baa8 100644 --- a/dist/version +++ b/dist/version @@ -1 +1 @@ -1.62.1.2 +1.62.1.3 From ce724d5c47bb147627c5cab13b472a64a44d304c Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Thu, 20 Feb 2025 15:19:57 -0500 Subject: [PATCH 0629/1099] Import translation work from https://crowdin.com/project/ublock --- platform/mv3/extension/_locales/ca/messages.json | 2 +- platform/mv3/extension/_locales/da/messages.json | 6 +++--- platform/mv3/extension/_locales/de/messages.json | 2 +- platform/mv3/extension/_locales/el/messages.json | 6 +++--- platform/mv3/extension/_locales/es/messages.json | 2 +- platform/mv3/extension/_locales/fr/messages.json | 6 +++--- platform/mv3/extension/_locales/hu/messages.json | 2 +- platform/mv3/extension/_locales/it/messages.json | 2 +- platform/mv3/extension/_locales/pt_BR/messages.json | 2 +- platform/mv3/extension/_locales/ru/messages.json | 6 +++--- platform/mv3/extension/_locales/sk/messages.json | 2 +- platform/mv3/extension/_locales/tr/messages.json | 2 +- platform/mv3/extension/_locales/uk/messages.json | 2 +- platform/mv3/extension/_locales/zh_TW/messages.json | 2 +- src/_locales/da/messages.json | 6 +++--- src/_locales/de/messages.json | 2 +- src/_locales/fr/messages.json | 6 +++--- src/_locales/hr/messages.json | 4 ++-- src/_locales/ru/messages.json | 6 +++--- 19 files changed, 34 insertions(+), 34 deletions(-) diff --git a/platform/mv3/extension/_locales/ca/messages.json b/platform/mv3/extension/_locales/ca/messages.json index a11095949897a..82eb7f454fa49 100644 --- a/platform/mv3/extension/_locales/ca/messages.json +++ b/platform/mv3/extension/_locales/ca/messages.json @@ -112,7 +112,7 @@ "description": "First paragraph of 'Filter issues' section in Support pane" }, "supportS6P1S1": { - "message": "Per evitar carregar els voluntaris amb informes duplicats, verifiqueu que el problema encara no s'hagi notificat.", + "message": "Per a evitar la sobrecàrrega del nostre voluntariat amb informes duplicats, verifiqueu abans que el problema encara no s'ha notificat. Nota: En fer clic, enviareu la pàgina causant al nostre GitHub.", "description": "A paragraph in the filter issue reporter section" }, "supportFindSpecificButton": { diff --git a/platform/mv3/extension/_locales/da/messages.json b/platform/mv3/extension/_locales/da/messages.json index a89b9c535d609..9b679860c3ece 100644 --- a/platform/mv3/extension/_locales/da/messages.json +++ b/platform/mv3/extension/_locales/da/messages.json @@ -112,11 +112,11 @@ "description": "First paragraph of 'Filter issues' section in Support pane" }, "supportS6P1S1": { - "message": "For at undgå at belaste frivillige med dubletanmeldelser, så tjek venligst, at problemet ikke allerede er anmeldt.", + "message": "For at undgå at bebyrde frivillige med dubletanmeldelser, så tjek venligst, at problematikken ikke allerede er anmeldt. Bemærk: Ved at klikke på knappen, sendes sidens oprindelse til GitHub.", "description": "A paragraph in the filter issue reporter section" }, "supportFindSpecificButton": { - "message": "Find lign. anmeldelser", + "message": "Find lign. anmeldelser på GitHub", "description": "A clickable link in the filter issue reporter section" }, "supportS6URL": { @@ -164,7 +164,7 @@ "description": "A checkbox to use for NSFW sites" }, "supportReportSpecificButton": { - "message": "Opret ny anmeldelse", + "message": "Opret ny anmeldelse på GitHub", "description": "Text for button which open an external webpage in Support pane" }, "firstRunSectionLabel": { diff --git a/platform/mv3/extension/_locales/de/messages.json b/platform/mv3/extension/_locales/de/messages.json index df439a59080b2..47e88a65f892d 100644 --- a/platform/mv3/extension/_locales/de/messages.json +++ b/platform/mv3/extension/_locales/de/messages.json @@ -112,7 +112,7 @@ "description": "First paragraph of 'Filter issues' section in Support pane" }, "supportS6P1S1": { - "message": "Um die Helfer nicht mit doppelten Meldungen zu belasten, vergewissern Sie sich bitte, dass das Problem nicht schon einmal gemeldet wurde. Hinweis: Wenn Sie auf die Schaltfläche klicken, wird der Ursprung der Seite an GitHub gesendet.", + "message": "Um die Freiwilligen nicht mit doppelten Meldungen zu überlasten, vergewissern Sie sich bitte, dass das Problem noch nicht gemeldet wurde. Hinweis: Das Anklicken der Schaltfläche übermittelt den Ursprung der Seite an GitHub.", "description": "A paragraph in the filter issue reporter section" }, "supportFindSpecificButton": { diff --git a/platform/mv3/extension/_locales/el/messages.json b/platform/mv3/extension/_locales/el/messages.json index 9e4a7414687d4..e45da59756a6a 100644 --- a/platform/mv3/extension/_locales/el/messages.json +++ b/platform/mv3/extension/_locales/el/messages.json @@ -104,15 +104,15 @@ "description": "Shown in the About pane" }, "supportS6H": { - "message": "Αναφορά ζητήματος φίλτρου", + "message": "Αναφορά προβλήματος φίλτρου", "description": "Header of 'Report a filter issue' section in Support pane" }, "supportS3P1": { - "message": "Αναφέρετε ζητήματα φίλτρου για συγκεκριμένους ιστοτόπους στο εργαλείο παρακολούθησης ζητημάτων του uBlockOrigin/uAssets. Απαιτείται λογαριασμός GitHub.", + "message": "Αναφέρετε προβλήμα φίλτρου για συγκεκριμένους ιστοτόπους στο εργαλείο παρακολούθησης ζητημάτων του uBlockOrigin/uAssets. Απαιτείται λογαριασμός GitHub.", "description": "First paragraph of 'Filter issues' section in Support pane" }, "supportS6P1S1": { - "message": "Για να αποφύγετε την επιβάρυνση των εθελοντών με διπλές αναφορές, βεβαιωθείτε ότι το ζήτημα δεν έχει ήδη αναφερθεί.", + "message": "Για να μην επιβαρυνθούν οι εθελοντών με διπλές αναφορές, βεβαιωθείτε ότι το ζήτημα δεν έχει ήδη αναφερθεί.Σημείωση: Με το πάτημα του κουμπιού, θα σταλεί η σελίδα προέλευσης στο GitHub.", "description": "A paragraph in the filter issue reporter section" }, "supportFindSpecificButton": { diff --git a/platform/mv3/extension/_locales/es/messages.json b/platform/mv3/extension/_locales/es/messages.json index 15bc85cabb6d7..b4feca746e9fd 100644 --- a/platform/mv3/extension/_locales/es/messages.json +++ b/platform/mv3/extension/_locales/es/messages.json @@ -112,7 +112,7 @@ "description": "First paragraph of 'Filter issues' section in Support pane" }, "supportS6P1S1": { - "message": "Para evitar sobrecargar a voluntarios con reportes duplicados, verifica que el problema no haya sido reportado.", + "message": "Para evitar sobrecargar a voluntarios con reportes duplicados, por favor verifica que el problema no haya sido reportado. Nota: cliqueando el botón causará que el origen de la página sea enviado a GitHub.", "description": "A paragraph in the filter issue reporter section" }, "supportFindSpecificButton": { diff --git a/platform/mv3/extension/_locales/fr/messages.json b/platform/mv3/extension/_locales/fr/messages.json index 8ba96a1067a94..f321673813da6 100644 --- a/platform/mv3/extension/_locales/fr/messages.json +++ b/platform/mv3/extension/_locales/fr/messages.json @@ -112,11 +112,11 @@ "description": "First paragraph of 'Filter issues' section in Support pane" }, "supportS6P1S1": { - "message": "Pour éviter d'encombrer les contributeurs avec des rapports en double, veuillez vérifier que le problème n'a pas déjà été rapporté.", + "message": "Pour éviter d'encombrer les contributeurs avec des rapports en double, veuillez vérifier que le problème n'a pas déjà été rapporté.\nNote : Cliquer sur le bouton entraînera l'envoi de la page d'origine à GitHub.", "description": "A paragraph in the filter issue reporter section" }, "supportFindSpecificButton": { - "message": "Trouver des rapports similaires", + "message": "Trouver des rapports similaires sur GitHub", "description": "A clickable link in the filter issue reporter section" }, "supportS6URL": { @@ -164,7 +164,7 @@ "description": "A checkbox to use for NSFW sites" }, "supportReportSpecificButton": { - "message": "Créer un nouveau rapport", + "message": "Créer un nouveau rapport sur GitHub", "description": "Text for button which open an external webpage in Support pane" }, "firstRunSectionLabel": { diff --git a/platform/mv3/extension/_locales/hu/messages.json b/platform/mv3/extension/_locales/hu/messages.json index 9583d57488d17..b74005aa174f1 100644 --- a/platform/mv3/extension/_locales/hu/messages.json +++ b/platform/mv3/extension/_locales/hu/messages.json @@ -112,7 +112,7 @@ "description": "First paragraph of 'Filter issues' section in Support pane" }, "supportS6P1S1": { - "message": "Az önkéntesek terhelésének csökkentése érdekében győződjön meg róla, hogy a hiba még nem lett jelentve.", + "message": "Az önkéntesek terhelésének csökkentése érdekében győződjön meg róla, hogy a hiba még nem lett jelentve. Megjegyzés: a gombra kattintás azt okozza, hogy a lap eredete el lesz küldve a GitHub részére.", "description": "A paragraph in the filter issue reporter section" }, "supportFindSpecificButton": { diff --git a/platform/mv3/extension/_locales/it/messages.json b/platform/mv3/extension/_locales/it/messages.json index 9e0626b6f12ab..7d09d3ea8a7c0 100644 --- a/platform/mv3/extension/_locales/it/messages.json +++ b/platform/mv3/extension/_locales/it/messages.json @@ -112,7 +112,7 @@ "description": "First paragraph of 'Filter issues' section in Support pane" }, "supportS6P1S1": { - "message": "Per evitare di gravare sui volontari con doppie segnalazioni, verificate che il problema non sia già stato segnalato. Nota: facendo clic sul pulsante, l'origine della pagina verrà inviata a GitHub.", + "message": "Per evitare di gravare sui volontari con segnalazioni duplicate, verifica che il problema non sia già stato segnalato. Nota: cliccando il pulsante l'origine della pagina sarà inviata a GitHub.", "description": "A paragraph in the filter issue reporter section" }, "supportFindSpecificButton": { diff --git a/platform/mv3/extension/_locales/pt_BR/messages.json b/platform/mv3/extension/_locales/pt_BR/messages.json index b92f5b59579eb..00493556d6ac6 100644 --- a/platform/mv3/extension/_locales/pt_BR/messages.json +++ b/platform/mv3/extension/_locales/pt_BR/messages.json @@ -112,7 +112,7 @@ "description": "First paragraph of 'Filter issues' section in Support pane" }, "supportS6P1S1": { - "message": "Pra evitar sobrecarregar os voluntários com relatórios duplicados por favor verifique se o problema já não foi reportado.", + "message": "Para evitar sobrecarregar os voluntários com relatórios duplicados por favor verifique se o problema já não foi reportado.\nObservação: clicar no botão fará com que a origem da página seja enviada ao GitHub.", "description": "A paragraph in the filter issue reporter section" }, "supportFindSpecificButton": { diff --git a/platform/mv3/extension/_locales/ru/messages.json b/platform/mv3/extension/_locales/ru/messages.json index bd89af9f8d8a6..ed0b191b0b6d6 100644 --- a/platform/mv3/extension/_locales/ru/messages.json +++ b/platform/mv3/extension/_locales/ru/messages.json @@ -112,11 +112,11 @@ "description": "First paragraph of 'Filter issues' section in Support pane" }, "supportS6P1S1": { - "message": "Чтобы не обременять волонтеров повторяющимися отчетами, пожалуйста, убедитесь, что о данной проблеме еще не сообщали.", + "message": "Чтобы не обременять добровольцев повторяющимися отчётами, пожалуйста, убедитесь, что об этой проблеме ещё не сообщали. Примечание: щелчок по кнопке приведёт к отправке исходного текста страницы в GitHub.", "description": "A paragraph in the filter issue reporter section" }, "supportFindSpecificButton": { - "message": "Найти похожие отчеты", + "message": "Найти похожие отчёты в GitHub", "description": "A clickable link in the filter issue reporter section" }, "supportS6URL": { @@ -164,7 +164,7 @@ "description": "A checkbox to use for NSFW sites" }, "supportReportSpecificButton": { - "message": "Создать новый отчет", + "message": "Создать новый отчёт в GitHub", "description": "Text for button which open an external webpage in Support pane" }, "firstRunSectionLabel": { diff --git a/platform/mv3/extension/_locales/sk/messages.json b/platform/mv3/extension/_locales/sk/messages.json index e33e990e4185b..715977fc2b85b 100644 --- a/platform/mv3/extension/_locales/sk/messages.json +++ b/platform/mv3/extension/_locales/sk/messages.json @@ -112,7 +112,7 @@ "description": "First paragraph of 'Filter issues' section in Support pane" }, "supportS6P1S1": { - "message": "Aby ste dobrovoľníkov nezaťažovali duplicitnými hláseniami, overte si, či už problém nebol nahlásený.", + "message": "Aby ste dobrovoľníkov nezaťažovali duplicitnými hláseniami, overte si, či už problém nebol nahlásený. Poznámka: kliknutím na tlačidlo sa odošle pôvodná stránka na GitHub.", "description": "A paragraph in the filter issue reporter section" }, "supportFindSpecificButton": { diff --git a/platform/mv3/extension/_locales/tr/messages.json b/platform/mv3/extension/_locales/tr/messages.json index 0fb973629fedc..c43374d7194f6 100644 --- a/platform/mv3/extension/_locales/tr/messages.json +++ b/platform/mv3/extension/_locales/tr/messages.json @@ -112,7 +112,7 @@ "description": "First paragraph of 'Filter issues' section in Support pane" }, "supportS6P1S1": { - "message": "Gönüllüleri benzer raporlar ile bezdirmemek için sorunun zaten bildirilip bildirilmediğine bakın.", + "message": "Gönüllüleri benzer raporlar ile bezdirmemek için sorunun zaten bildirilip bildirilmediğine bakın. Not: düğmeye tıklamak sayfanın kaynağının GitHub üstüne gönderilmesine neden olur.", "description": "A paragraph in the filter issue reporter section" }, "supportFindSpecificButton": { diff --git a/platform/mv3/extension/_locales/uk/messages.json b/platform/mv3/extension/_locales/uk/messages.json index 58ebe4b686abd..6a64ee42e4deb 100644 --- a/platform/mv3/extension/_locales/uk/messages.json +++ b/platform/mv3/extension/_locales/uk/messages.json @@ -112,7 +112,7 @@ "description": "First paragraph of 'Filter issues' section in Support pane" }, "supportS6P1S1": { - "message": "Щоб не обтяжувати волонтерів повторюваними звітами, переконайтеся, що про проблему ще не повідомлялося.", + "message": "Щоб не обтяжувати волонтерів повторюваними звітами, переконайтеся, що про проблему ще не повідомлялося.Зауваження: натискання на кнопку призведе до надсилання походження сторінки на GitHub.", "description": "A paragraph in the filter issue reporter section" }, "supportFindSpecificButton": { diff --git a/platform/mv3/extension/_locales/zh_TW/messages.json b/platform/mv3/extension/_locales/zh_TW/messages.json index 54bc3173bf4ef..d3dbb78428e3f 100644 --- a/platform/mv3/extension/_locales/zh_TW/messages.json +++ b/platform/mv3/extension/_locales/zh_TW/messages.json @@ -112,7 +112,7 @@ "description": "First paragraph of 'Filter issues' section in Support pane" }, "supportS6P1S1": { - "message": "為避免增加志願者的負擔,請先確認該問題被尚未被回報過。", + "message": "為避免增加志願者的負擔,請先確認此問題是否已被回報過。請注意:點選按鈕會將本頁的來源傳送到 GitHub。", "description": "A paragraph in the filter issue reporter section" }, "supportFindSpecificButton": { diff --git a/src/_locales/da/messages.json b/src/_locales/da/messages.json index b737ac367952e..90a33df731f5a 100644 --- a/src/_locales/da/messages.json +++ b/src/_locales/da/messages.json @@ -900,11 +900,11 @@ "description": "Text for button which open an external webpage in Support pane" }, "supportReportSpecificButton": { - "message": "Opret ny anmeldelse", + "message": "Opret ny anmeldelse på GitHub", "description": "Text for button which open an external webpage in Support pane" }, "supportFindSpecificButton": { - "message": "Find lign. anmeldelser", + "message": "Find lign. anmeldelser på GitHub", "description": "A clickable link in the filter issue reporter section" }, "supportS1H": { @@ -964,7 +964,7 @@ "description": "Header of 'Report a filter issue' section in Support pane" }, "supportS6P1S1": { - "message": "For at undgå at belaste frivillige med dubletanmeldelser, så tjek venligst, at problemet ikke allerede er anmeldt.", + "message": "For at undgå at bebyrde frivillige med dubletanmeldelser, så tjek venligst, at problematikken ikke allerede er anmeldt. Bemærk: Ved at klikke på knappen, sendes sidens oprindelse til GitHub.", "description": "A paragraph in the filter issue reporter section" }, "supportS6P2S1": { diff --git a/src/_locales/de/messages.json b/src/_locales/de/messages.json index 60289ec911790..8e0c518ec77e0 100644 --- a/src/_locales/de/messages.json +++ b/src/_locales/de/messages.json @@ -964,7 +964,7 @@ "description": "Header of 'Report a filter issue' section in Support pane" }, "supportS6P1S1": { - "message": "Um die Freiwilligen nicht mit doppelten Meldungen zu belasten, vergewissern Sie sich bitte, dass das Problem nicht schon einmal gemeldet wurde.", + "message": "Um die Freiwilligen nicht mit doppelten Meldungen zu überlasten, vergewissern Sie sich bitte, dass das Problem noch nicht gemeldet wurde. Hinweis: Das Anklicken der Schaltfläche übermittelt den Ursprung der Seite an GitHub.", "description": "A paragraph in the filter issue reporter section" }, "supportS6P2S1": { diff --git a/src/_locales/fr/messages.json b/src/_locales/fr/messages.json index 309bba8600080..002bde7936253 100644 --- a/src/_locales/fr/messages.json +++ b/src/_locales/fr/messages.json @@ -900,11 +900,11 @@ "description": "Text for button which open an external webpage in Support pane" }, "supportReportSpecificButton": { - "message": "Créer un rapport", + "message": "Créer un nouveau rapport sur GitHub", "description": "Text for button which open an external webpage in Support pane" }, "supportFindSpecificButton": { - "message": "Trouver des rapports similaires", + "message": "Trouver des rapports similaires sur GitHub", "description": "A clickable link in the filter issue reporter section" }, "supportS1H": { @@ -964,7 +964,7 @@ "description": "Header of 'Report a filter issue' section in Support pane" }, "supportS6P1S1": { - "message": "Pour éviter d'encombrer les contributeurs avec des rapports en double, veuillez vérifier que le problème n'a pas déjà été rapporté.", + "message": "Pour éviter d'encombrer les contributeurs avec des rapports en double, veuillez vérifier que le problème n'a pas déjà été rapporté.\nNote : Cliquer sur le bouton entraînera l'envoi de la page d'origine à GitHub.", "description": "A paragraph in the filter issue reporter section" }, "supportS6P2S1": { diff --git a/src/_locales/hr/messages.json b/src/_locales/hr/messages.json index 68e37c5dd8eb2..b0647cdfd89a1 100644 --- a/src/_locales/hr/messages.json +++ b/src/_locales/hr/messages.json @@ -900,11 +900,11 @@ "description": "Text for button which open an external webpage in Support pane" }, "supportReportSpecificButton": { - "message": "Napravi novu prijavu", + "message": "Napravi novu prijavu na GitHub-u", "description": "Text for button which open an external webpage in Support pane" }, "supportFindSpecificButton": { - "message": "Nađi slične prijave", + "message": "Nađi slične prijave na GitHub-u", "description": "A clickable link in the filter issue reporter section" }, "supportS1H": { diff --git a/src/_locales/ru/messages.json b/src/_locales/ru/messages.json index fe186f6c3c05c..eae7c39c2c1a9 100644 --- a/src/_locales/ru/messages.json +++ b/src/_locales/ru/messages.json @@ -900,11 +900,11 @@ "description": "Text for button which open an external webpage in Support pane" }, "supportReportSpecificButton": { - "message": "Создать новый отчёт", + "message": "Создать новый отчёт в GitHub", "description": "Text for button which open an external webpage in Support pane" }, "supportFindSpecificButton": { - "message": "Найти похожие отчёты", + "message": "Найти похожие отчёты в GitHub", "description": "A clickable link in the filter issue reporter section" }, "supportS1H": { @@ -964,7 +964,7 @@ "description": "Header of 'Report a filter issue' section in Support pane" }, "supportS6P1S1": { - "message": "Чтобы не обременять добровольцев дублированными отчётами, пожалуйста, убедитесь, что о данной проблеме еще не сообщали.", + "message": "Чтобы не обременять добровольцев повторяющимися отчётами, пожалуйста, убедитесь, что об этой проблеме ещё не сообщали. Примечание: щелчок по кнопке приведёт к отправке исходного текста страницы в GitHub.", "description": "A paragraph in the filter issue reporter section" }, "supportS6P2S1": { From 128083c203830942acecfc42f718ed7c7c08f895 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Thu, 20 Feb 2025 15:31:23 -0500 Subject: [PATCH 0630/1099] Make Firefox dev build auto-update --- dist/firefox/updates.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dist/firefox/updates.json b/dist/firefox/updates.json index 4ce0a42aa59a8..8e203a5bc2f2f 100644 --- a/dist/firefox/updates.json +++ b/dist/firefox/updates.json @@ -3,9 +3,9 @@ "uBlock0@raymondhill.net": { "updates": [ { - "version": "1.62.1.2", + "version": "1.62.1.3", "browser_specific_settings": { "gecko": { "strict_min_version": "78.0" } }, - "update_link": "https://github.com/gorhill/uBlock/releases/download/1.62.1b2/uBlock0_1.62.1b2.firefox.signed.xpi" + "update_link": "https://github.com/gorhill/uBlock/releases/download/1.62.1b3/uBlock0_1.62.1b3.firefox.signed.xpi" } ] } From 1d2378e74eada69fbce27d7baba1df2fdc22dbcc Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Fri, 21 Feb 2025 08:23:27 -0500 Subject: [PATCH 0631/1099] [mv3] Workaround for permissions dialog preventing proper mode change This is a Chromium-specific issue. Related issue: https://github.com/uBlockOrigin/uBOL-home/issues/280 --- platform/mv3/extension/js/background.js | 43 ++++++++++++++++++++++--- platform/mv3/extension/js/popup.js | 10 ++++++ platform/mv3/make-rulesets.js | 10 +++--- 3 files changed, 53 insertions(+), 10 deletions(-) diff --git a/platform/mv3/extension/js/background.js b/platform/mv3/extension/js/background.js index 9577eeea6cade..7874b3d0130f4 100644 --- a/platform/mv3/extension/js/background.js +++ b/platform/mv3/extension/js/background.js @@ -45,6 +45,11 @@ import { getAdminRulesets, } from './admin.js'; +import { + broadcastMessage, + hostnamesFromMatches, +} from './utils.js'; + import { enableRulesets, excludeFromStrictBlock, @@ -70,7 +75,6 @@ import { saveRulesetConfig, } from './config.js'; -import { broadcastMessage } from './utils.js'; import { registerInjectables } from './scripting-manager.js'; /******************************************************************************/ @@ -79,6 +83,8 @@ const UBOL_ORIGIN = runtime.getURL('').replace(/\/$/, ''); const canShowBlockedCount = typeof dnr.setExtensionActionOptions === 'function'; +let pendingPermissionRequest; + /******************************************************************************/ function getCurrentVersion() { @@ -116,6 +122,30 @@ async function onPermissionsRemoved() { return true; } +// https://github.com/uBlockOrigin/uBOL-home/issues/280 +async function onPermissionsAdded(permissions) { + const details = pendingPermissionRequest; + pendingPermissionRequest = undefined; + if ( details === undefined ) { return; } + const defaultMode = await getDefaultFilteringMode(); + if ( defaultMode >= MODE_OPTIMAL ) { return; } + if ( Array.isArray(permissions.origins) === false ) { return; } + const hostnames = hostnamesFromMatches(permissions.origins); + if ( hostnames.includes(details.hostname) === false ) { return; } + const beforeLevel = await getFilteringMode(details.hostname); + if ( beforeLevel === details.afterLevel ) { return; } + const afterLevel = await setFilteringMode(details.hostname, details.afterLevel); + if ( afterLevel !== details.afterLevel ) { return; } + await registerInjectables(); + if ( rulesetConfig.autoReload ) { + self.setTimeout(( ) => { + browser.tabs.update(details.tabId, { + url: details.url, + }); + }, 437); + } +} + /******************************************************************************/ async function gotoURL(url, type) { @@ -312,6 +342,10 @@ function onMessage(request, sender, callback) { return true; } + case 'setPendingFilteringMode': + pendingPermissionRequest = request; + break; + case 'getDefaultFilteringMode': { getDefaultFilteringMode().then(level => { callback(level); @@ -434,10 +468,6 @@ async function start() { runtime.onMessage.addListener(onMessage); - browser.permissions.onRemoved.addListener( - ( ) => { onPermissionsRemoved(); } - ); - if ( process.firstRun ) { const enableOptimal = await hasOmnipotence(); if ( enableOptimal ) { @@ -463,6 +493,9 @@ async function start() { if ( process.wakeupRun === false ) { adminReadEx('disabledFeatures'); } + + browser.permissions.onRemoved.addListener(onPermissionsRemoved); + browser.permissions.onAdded.addListener(onPermissionsAdded); } // https://github.com/uBlockOrigin/uBOL-home/issues/199 diff --git a/platform/mv3/extension/js/popup.js b/platform/mv3/extension/js/popup.js index 761f82032f166..d0eefb1bb0f09 100644 --- a/platform/mv3/extension/js/popup.js +++ b/platform/mv3/extension/js/popup.js @@ -74,6 +74,16 @@ async function commitFilteringMode() { const afterLevel = parseInt(modeSlider.dataset.level, 10); const beforeLevel = parseInt(modeSlider.dataset.levelBefore, 10); if ( afterLevel > 1 ) { + if ( beforeLevel <= 1 ) { + sendMessage({ + what: 'setPendingFilteringMode', + tabId: currentTab.id, + url: tabURL.href, + hostname: targetHostname, + beforeLevel, + afterLevel, + }); + } let granted = false; try { granted = await browser.permissions.request({ diff --git a/platform/mv3/make-rulesets.js b/platform/mv3/make-rulesets.js index 6df89f91891f8..9279ea6febb63 100644 --- a/platform/mv3/make-rulesets.js +++ b/platform/mv3/make-rulesets.js @@ -93,7 +93,7 @@ const uidint32 = (s) => { const stdOutput = []; -const log = (text, silent = false) => { +const log = (text, silent = true) => { stdOutput.push(text); if ( silent === false ) { console.log(text); @@ -166,7 +166,7 @@ const requiredRedirectResources = new Set(); // This will be used to sign our inserted `!#trusted on` directives const secret = createHash('sha256').update(randomBytes(16)).digest('hex').slice(0,16); -log(`Secret: ${secret}`); +log(`Secret: ${secret}`, false); /******************************************************************************/ @@ -245,7 +245,7 @@ async function fetchList(assetDetails) { return { url, content }; } } - log(`No valid content for ${details.name}`); + log(`No valid content for ${details.name}`, false); return { url, content: '' }; }) ); @@ -257,7 +257,7 @@ async function fetchList(assetDetails) { const text = parts.join('\n'); if ( text === '' ) { - log('No filterset found'); + log('No filterset found', false); } return text; } @@ -1308,7 +1308,7 @@ async function main() { const minutePart = Math.floor(now.getUTCMinutes()); version = `${yearPart}.${monthPart}.${dayPart}.${hourPart * 60 + minutePart}`; } - log(`Version: ${version}`); + log(`Version: ${version}`, false); // Get assets.json content const assets = await fs.readFile( From 6ca28a5b3f0988d41a494736e33bb139d036d64a Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sat, 22 Feb 2025 07:38:12 -0500 Subject: [PATCH 0632/1099] [mv3] Improve output og log.txt --- platform/mv3/make-rulesets.js | 7 +++++-- platform/mv3/make-scriptlets.js | 6 +++--- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/platform/mv3/make-rulesets.js b/platform/mv3/make-rulesets.js index 9279ea6febb63..9c62718eef583 100644 --- a/platform/mv3/make-rulesets.js +++ b/platform/mv3/make-rulesets.js @@ -91,15 +91,18 @@ const uidint32 = (s) => { /******************************************************************************/ +const consoleLog = console.log; const stdOutput = []; const log = (text, silent = true) => { stdOutput.push(text); if ( silent === false ) { - console.log(text); + consoleLog(text); } }; +console.log = log; + /******************************************************************************/ const urlToFileName = url => { @@ -1126,7 +1129,7 @@ async function processScriptletFilters(assetDetails, mapin) { makeScriptlet.init(); for ( const details of mapin.values() ) { - makeScriptlet.compile(details); + makeScriptlet.compile(assetDetails, details); } const stats = await makeScriptlet.commit( assetDetails.id, diff --git a/platform/mv3/make-scriptlets.js b/platform/mv3/make-scriptlets.js index 27dc5d247921b..8cc651ff2e240 100644 --- a/platform/mv3/make-scriptlets.js +++ b/platform/mv3/make-scriptlets.js @@ -75,7 +75,7 @@ export function reset() { /******************************************************************************/ -export function compile(details) { +export function compile(assetDetails, details) { if ( details.args[0].endsWith('.js') === false ) { details.args[0] += '.js'; } @@ -85,8 +85,9 @@ export function compile(details) { const scriptletToken = details.args[0]; const resourceEntry = resourceDetails.get(scriptletToken); if ( resourceEntry === undefined ) { return; } + const argsToken = JSON.stringify(details.args.slice(1)); if ( resourceEntry.requiresTrust && details.trustedSource !== true ) { - console.log(`Rejecting ${scriptletToken}: source is not trusted`); + console.log(`Rejecting +js(${scriptletToken},${argsToken.slice(1,-1)}): ${assetDetails.id} is not trusted`); return; } if ( scriptletFiles.has(scriptletToken) === false ) { @@ -102,7 +103,6 @@ export function compile(details) { }); } const scriptletDetails = scriptletFiles.get(scriptletToken); - const argsToken = JSON.stringify(details.args.slice(1)); if ( scriptletDetails.args.has(argsToken) === false ) { scriptletDetails.args.set(argsToken, scriptletDetails.args.size); } From 0013e04fd39cec8d87c7595b413817cf3bcf3c80 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sat, 22 Feb 2025 07:47:33 -0500 Subject: [PATCH 0633/1099] Import translation work from https://crowdin.com/project/ublock --- platform/mv3/extension/_locales/de/messages.json | 4 ++-- platform/mv3/extension/_locales/es/messages.json | 6 +++--- platform/mv3/extension/_locales/gl/messages.json | 2 +- platform/mv3/extension/_locales/hr/messages.json | 8 ++++---- platform/mv3/extension/_locales/ja/messages.json | 2 +- platform/mv3/extension/_locales/ka/messages.json | 2 +- platform/mv3/extension/_locales/lv/messages.json | 2 +- platform/mv3/extension/_locales/tr/messages.json | 8 ++++---- src/_locales/de/messages.json | 4 ++-- src/_locales/es/messages.json | 6 +++--- src/_locales/hr/messages.json | 2 +- 11 files changed, 23 insertions(+), 23 deletions(-) diff --git a/platform/mv3/extension/_locales/de/messages.json b/platform/mv3/extension/_locales/de/messages.json index 47e88a65f892d..56381893e5df7 100644 --- a/platform/mv3/extension/_locales/de/messages.json +++ b/platform/mv3/extension/_locales/de/messages.json @@ -116,7 +116,7 @@ "description": "A paragraph in the filter issue reporter section" }, "supportFindSpecificButton": { - "message": "Ähnliche Meldungen finden", + "message": "Ähnliche Meldungen auf GitHub finden", "description": "A clickable link in the filter issue reporter section" }, "supportS6URL": { @@ -164,7 +164,7 @@ "description": "A checkbox to use for NSFW sites" }, "supportReportSpecificButton": { - "message": "Neue Meldung erstellen", + "message": "Neue Meldung auf GitHub erstellen", "description": "Text for button which open an external webpage in Support pane" }, "firstRunSectionLabel": { diff --git a/platform/mv3/extension/_locales/es/messages.json b/platform/mv3/extension/_locales/es/messages.json index b4feca746e9fd..f4a01210801ce 100644 --- a/platform/mv3/extension/_locales/es/messages.json +++ b/platform/mv3/extension/_locales/es/messages.json @@ -112,11 +112,11 @@ "description": "First paragraph of 'Filter issues' section in Support pane" }, "supportS6P1S1": { - "message": "Para evitar sobrecargar a voluntarios con reportes duplicados, por favor verifica que el problema no haya sido reportado. Nota: cliqueando el botón causará que el origen de la página sea enviado a GitHub.", + "message": "Para evitar sobrecargar a voluntarios con reportes duplicados, verifica que el problema no haya sido reportado. Nota: al hacer clic en el botón, hará que el origen de la página se envíe a GitHub.", "description": "A paragraph in the filter issue reporter section" }, "supportFindSpecificButton": { - "message": "Encontrar reportes similares", + "message": "Encontrar reportes similares en GitHub", "description": "A clickable link in the filter issue reporter section" }, "supportS6URL": { @@ -164,7 +164,7 @@ "description": "A checkbox to use for NSFW sites" }, "supportReportSpecificButton": { - "message": "Crear informe nuevo", + "message": "Crear nuevo reporte en GitHub", "description": "Text for button which open an external webpage in Support pane" }, "firstRunSectionLabel": { diff --git a/platform/mv3/extension/_locales/gl/messages.json b/platform/mv3/extension/_locales/gl/messages.json index c6e3cc2daa151..cf4399b0aa435 100644 --- a/platform/mv3/extension/_locales/gl/messages.json +++ b/platform/mv3/extension/_locales/gl/messages.json @@ -112,7 +112,7 @@ "description": "First paragraph of 'Filter issues' section in Support pane" }, "supportS6P1S1": { - "message": "Para evitar a sobrecarga de traballo para as persoas voluntarias con duplicados dos problemas, comproba que aínda non se informou acerca do problema.", + "message": "Para evitar a sobrecarga de traballo para as persoas voluntarias con duplicados dos problemas, comproba que aínda non se informou sobre o problema. Nota: ao premer no botón enviarás a orixe da páxina a GitHub.", "description": "A paragraph in the filter issue reporter section" }, "supportFindSpecificButton": { diff --git a/platform/mv3/extension/_locales/hr/messages.json b/platform/mv3/extension/_locales/hr/messages.json index 116371621df8f..b99ddaeb07e65 100644 --- a/platform/mv3/extension/_locales/hr/messages.json +++ b/platform/mv3/extension/_locales/hr/messages.json @@ -60,7 +60,7 @@ "description": "Header for a ruleset section in 'Filter lists pane'" }, "3pGroupMalware": { - "message": "Zloćudne domene", + "message": "Zaštita od zlonamjernog softvera, sigurnost", "description": "Header for a ruleset section in 'Filter lists pane'" }, "3pGroupAnnoyances": { @@ -112,11 +112,11 @@ "description": "First paragraph of 'Filter issues' section in Support pane" }, "supportS6P1S1": { - "message": "Kako biste izbjegli opterećivanje volontera duplim prijavama, provjerite nije li problem već prijavljen.", + "message": "Kako biste izbjegli opterećivanje volontera duplim prijavama, provjerite nije li problem već prijavljen. Napomena: klik na gumb uzrokovat će slanje izvorne stranice na GitHub.", "description": "A paragraph in the filter issue reporter section" }, "supportFindSpecificButton": { - "message": "Nađi slične prijave", + "message": "Nađi slične prijave na GitHub-u", "description": "A clickable link in the filter issue reporter section" }, "supportS6URL": { @@ -164,7 +164,7 @@ "description": "A checkbox to use for NSFW sites" }, "supportReportSpecificButton": { - "message": "Napravi novu prijavu", + "message": "Napravi novu prijavu na GitHub-u", "description": "Text for button which open an external webpage in Support pane" }, "firstRunSectionLabel": { diff --git a/platform/mv3/extension/_locales/ja/messages.json b/platform/mv3/extension/_locales/ja/messages.json index 30bc6cd7179f4..110d5b4df3548 100644 --- a/platform/mv3/extension/_locales/ja/messages.json +++ b/platform/mv3/extension/_locales/ja/messages.json @@ -112,7 +112,7 @@ "description": "First paragraph of 'Filter issues' section in Support pane" }, "supportS6P1S1": { - "message": "重複した報告によってボランティアに負担をかけないように、問題がすでに報告されていないか確認してください。", + "message": "重複した報告によってボランティアに負担をかけないように、問題がすでに報告されていないか確認してください。 注意: ボタンをクリックすると、ページのオリジンが GitHub に送信されます。", "description": "A paragraph in the filter issue reporter section" }, "supportFindSpecificButton": { diff --git a/platform/mv3/extension/_locales/ka/messages.json b/platform/mv3/extension/_locales/ka/messages.json index 6b1e1608fb41f..c921dde829351 100644 --- a/platform/mv3/extension/_locales/ka/messages.json +++ b/platform/mv3/extension/_locales/ka/messages.json @@ -112,7 +112,7 @@ "description": "First paragraph of 'Filter issues' section in Support pane" }, "supportS6P1S1": { - "message": "მოხალისეები რომ არ მოცდნენ ერთნაირი მოსხენებების ნახვით, გთოხვთ გადაამოწმოთ, უკვე ხომ არაა გაგზავნილი საჩივარი ამ ხარვეზზე.", + "message": "მოხალისეები რომ არ მოცდნენ ერთნაირი მოსხენებების გაცნობით, გთხოვთ გადაამოწმოთ, უკვე ხომ არაა გაგზავნილი საჩივარი ამ ხარვეზზე. გაითვალისწინეთ: ღილაკზე დაწკაპების შედეგად გვერდის მონაცემები გაიგზავნება GitHub-ზე.", "description": "A paragraph in the filter issue reporter section" }, "supportFindSpecificButton": { diff --git a/platform/mv3/extension/_locales/lv/messages.json b/platform/mv3/extension/_locales/lv/messages.json index 53b675ac7de02..e7eae54c0bfae 100644 --- a/platform/mv3/extension/_locales/lv/messages.json +++ b/platform/mv3/extension/_locales/lv/messages.json @@ -112,7 +112,7 @@ "description": "First paragraph of 'Filter issues' section in Support pane" }, "supportS6P1S1": { - "message": "Lai izvairītos no brīvprātīgo noslogošanas ar ziņojumiem, kas atkārtojas, lūgums pārbaudīt, ka par šādu nepilnību jau nav ziņots.", + "message": "Lai izvairītos no brīvprātīgo noslogošanas ar ziņojumiem, kas atkārtojas, lūgums pārbaudīt, ka par šādu nepilnību jau nav ziņots. Piebilde: klikšķināšana uz pogas izraisīs arī lapas izcelsmes nosūtīšanu uz GitHub.", "description": "A paragraph in the filter issue reporter section" }, "supportFindSpecificButton": { diff --git a/platform/mv3/extension/_locales/tr/messages.json b/platform/mv3/extension/_locales/tr/messages.json index c43374d7194f6..d10acdfb246fe 100644 --- a/platform/mv3/extension/_locales/tr/messages.json +++ b/platform/mv3/extension/_locales/tr/messages.json @@ -112,7 +112,7 @@ "description": "First paragraph of 'Filter issues' section in Support pane" }, "supportS6P1S1": { - "message": "Gönüllüleri benzer raporlar ile bezdirmemek için sorunun zaten bildirilip bildirilmediğine bakın. Not: düğmeye tıklamak sayfanın kaynağının GitHub üstüne gönderilmesine neden olur.", + "message": "Gönüllüleri benzer raporlar ile bezdirmemek için sorunun zaten bildirilip bildirilmediğine bakın. Not: Butona tıklamak sayfanın temel adresini GitHub'a gönderir.", "description": "A paragraph in the filter issue reporter section" }, "supportFindSpecificButton": { @@ -216,7 +216,7 @@ "description": "A short description for the editable field which lists trusted sites" }, "noFilteringModePlaceholder": { - "message": "[sadece ana bilgisayar adları]\nörnek.com\noyunlar.site\n…", + "message": "[sadece alan adları]\nexample.com\ngames.example\n…", "description": "Default text for in edit field" }, "behaviorSectionLabel": { @@ -232,7 +232,7 @@ "description": "Label for a checkbox in the options page" }, "enableStrictBlockLabel": { - "message": "Sıkı engellemeyi kullanıma al", + "message": "Sıkı engellemeyi etkinleştir", "description": "Label for a checkbox in the options page" }, "enableStrictBlockLegend": { @@ -252,7 +252,7 @@ "description": "Sentence used in the strict-blocked page" }, "strictblockReasonSentence1": { - "message": "Bu sayfa {{listname}} içindeki bir süzgece takıldığı için engellenmiştir.", + "message": "Bu sayfa {{listname}} içindeki bir filtreye takıldığı için engellenmiştir.", "description": "Text informing about what is causing the page to be blocked" }, "strictblockRedirectSentence1": { diff --git a/src/_locales/de/messages.json b/src/_locales/de/messages.json index 8e0c518ec77e0..39ffd4e739cae 100644 --- a/src/_locales/de/messages.json +++ b/src/_locales/de/messages.json @@ -900,11 +900,11 @@ "description": "Text for button which open an external webpage in Support pane" }, "supportReportSpecificButton": { - "message": "Neue Meldung erstellen", + "message": "Neue Meldung auf GitHub erstellen", "description": "Text for button which open an external webpage in Support pane" }, "supportFindSpecificButton": { - "message": "Ähnliche Meldungen finden", + "message": "Ähnliche Meldungen auf GitHub finden", "description": "A clickable link in the filter issue reporter section" }, "supportS1H": { diff --git a/src/_locales/es/messages.json b/src/_locales/es/messages.json index ae5109fad7f99..390aba48dd65f 100644 --- a/src/_locales/es/messages.json +++ b/src/_locales/es/messages.json @@ -900,11 +900,11 @@ "description": "Text for button which open an external webpage in Support pane" }, "supportReportSpecificButton": { - "message": "Crear nuevo reporte", + "message": "Crear nuevo reporte en GitHub", "description": "Text for button which open an external webpage in Support pane" }, "supportFindSpecificButton": { - "message": "Buscar reportes similares", + "message": "Encontrar reportes similares en GitHub", "description": "A clickable link in the filter issue reporter section" }, "supportS1H": { @@ -964,7 +964,7 @@ "description": "Header of 'Report a filter issue' section in Support pane" }, "supportS6P1S1": { - "message": "Para evitar sobrecargar a voluntarios con reportes duplicados, verifica que el problema no haya sido reportado.", + "message": "Para evitar sobrecargar a voluntarios con reportes duplicados, verifica que el problema no haya sido reportado. Nota: al hacer clic en el botón, hará que el origen de la página se envíe a GitHub.", "description": "A paragraph in the filter issue reporter section" }, "supportS6P2S1": { diff --git a/src/_locales/hr/messages.json b/src/_locales/hr/messages.json index b0647cdfd89a1..ed4c16f4c256c 100644 --- a/src/_locales/hr/messages.json +++ b/src/_locales/hr/messages.json @@ -964,7 +964,7 @@ "description": "Header of 'Report a filter issue' section in Support pane" }, "supportS6P1S1": { - "message": "Kako biste izbjegli opterećivanje volontera duplim prijavama, provjerite nije li problem već prijavljen.", + "message": "Kako biste izbjegli opterećivanje volontera duplim prijavama, provjerite nije li problem već prijavljen. Napomena: klik na gumb uzrokovat će slanje izvorne stranice na GitHub.", "description": "A paragraph in the filter issue reporter section" }, "supportS6P2S1": { From 02b78fb717e531ec169d2d39108927753db7d750 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sat, 22 Feb 2025 11:07:58 -0500 Subject: [PATCH 0634/1099] [mv3] Remove obsolete Firefox workaround code Related commit: https://github.com/gorhill/uBlock/commit/2e745f9bfb2cf7e5e241160b2a20edf5edfcad92 --- platform/mv3/extension/js/scripting-manager.js | 11 ++--------- platform/mv3/firefox/manifest.json | 4 ++-- 2 files changed, 4 insertions(+), 11 deletions(-) diff --git a/platform/mv3/extension/js/scripting-manager.js b/platform/mv3/extension/js/scripting-manager.js index 6bd20a7e89da2..6b28dc5b9d51c 100644 --- a/platform/mv3/extension/js/scripting-manager.js +++ b/platform/mv3/extension/js/scripting-manager.js @@ -29,8 +29,6 @@ import { ubolLog } from './debug.js'; /******************************************************************************/ -const isGecko = browser.runtime.getURL('').startsWith('moz-extension://'); - const resourceDetailPromises = new Map(); function getScriptletDetails() { @@ -467,16 +465,11 @@ function registerScriptlet(context, scriptletDetails) { allFrames: true, matches, excludeMatches, + matchOriginAsFallback: true, runAt: 'document_start', + world: 'MAIN', }; - // https://bugzilla.mozilla.org/show_bug.cgi?id=1736575 - // `MAIN` world not yet supported in Firefox - if ( isGecko === false ) { - directive.world = 'MAIN'; - directive.matchOriginAsFallback = true; - } - // register if ( registered === undefined ) { context.toAdd.push(directive); diff --git a/platform/mv3/firefox/manifest.json b/platform/mv3/firefox/manifest.json index 7847e3ad523f3..1ecaad2ec23de 100644 --- a/platform/mv3/firefox/manifest.json +++ b/platform/mv3/firefox/manifest.json @@ -16,10 +16,10 @@ "browser_specific_settings": { "gecko": { "id": "uBOLiteRedux@raymondhill.net", - "strict_min_version": "127.0" + "strict_min_version": "128.0" }, "gecko_android": { - "strict_min_version": "127.0" + "strict_min_version": "128.0" } }, "declarative_net_request": { From e56ca0f6128a1c8e95a52d4bb5cf4e8d7c5bfa11 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sun, 23 Feb 2025 12:20:17 -0500 Subject: [PATCH 0635/1099] Import translation from https://crowdin.com/project/ublock --- platform/mv3/extension/_locales/fi/messages.json | 8 ++++---- src/_locales/fi/messages.json | 6 +++--- src/_locales/tr/messages.json | 6 +++--- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/platform/mv3/extension/_locales/fi/messages.json b/platform/mv3/extension/_locales/fi/messages.json index 905e8319fecb7..eb23edb1d2fa3 100644 --- a/platform/mv3/extension/_locales/fi/messages.json +++ b/platform/mv3/extension/_locales/fi/messages.json @@ -112,11 +112,11 @@ "description": "First paragraph of 'Filter issues' section in Support pane" }, "supportS6P1S1": { - "message": "Välttääksesi vapaaehtoisten kuormittamisen ylimääräisillä ilmoituksilla, tarkista ensin onko ongelmasta jo ilmoitettu.", + "message": "Välttääksesi vapaaehtoisten kuormittamisen ylimääräisillä ilmoituksilla, tarkasta ensin onko ongelmasta jo ilmoitettu. Huomioi: painikkeen painalluksen seurauksena sivun osoite lähetetään GitHubiin.", "description": "A paragraph in the filter issue reporter section" }, "supportFindSpecificButton": { - "message": "Etsi samankaltaisia ilmoituksia", + "message": "Etsi GitHubista vastaavia ilmoituksia", "description": "A clickable link in the filter issue reporter section" }, "supportS6URL": { @@ -164,7 +164,7 @@ "description": "A checkbox to use for NSFW sites" }, "supportReportSpecificButton": { - "message": "Luo uusi ilmoitus", + "message": "Luo GitHubiin uusi ilmoitus", "description": "Text for button which open an external webpage in Support pane" }, "firstRunSectionLabel": { @@ -260,7 +260,7 @@ "description": "Text warning about an incoming redirect" }, "strictblockNoParamsPrompt": { - "message": "ilman parametreja", + "message": "parametritön", "description": "Label to be used for the parameter-less URL" }, "strictblockBack": { diff --git a/src/_locales/fi/messages.json b/src/_locales/fi/messages.json index dbdc0638b3e7f..05bcd595474c6 100644 --- a/src/_locales/fi/messages.json +++ b/src/_locales/fi/messages.json @@ -900,11 +900,11 @@ "description": "Text for button which open an external webpage in Support pane" }, "supportReportSpecificButton": { - "message": "Luo uusi ilmoitus", + "message": "Luo GitHubiin uusi ilmoitus", "description": "Text for button which open an external webpage in Support pane" }, "supportFindSpecificButton": { - "message": "Etsi samankaltaisia ilmoituksia", + "message": "Etsi GitHubista vastaavia ilmoituksia", "description": "A clickable link in the filter issue reporter section" }, "supportS1H": { @@ -964,7 +964,7 @@ "description": "Header of 'Report a filter issue' section in Support pane" }, "supportS6P1S1": { - "message": "Välttääksesi vapaaehtoisten kuormittamisen ylimääräisillä ilmoituksilla, tarkista ensin onko ongelmasta jo ilmoitettu.", + "message": "Välttääksesi vapaaehtoisten kuormittamisen ylimääräisillä ilmoituksilla, tarkasta ensin onko ongelmasta jo ilmoitettu. Huomioi: painikkeen painalluksen seurauksena sivun osoite lähetetään GitHubiin.", "description": "A paragraph in the filter issue reporter section" }, "supportS6P2S1": { diff --git a/src/_locales/tr/messages.json b/src/_locales/tr/messages.json index 78435caab57ee..c6bad17d536c5 100644 --- a/src/_locales/tr/messages.json +++ b/src/_locales/tr/messages.json @@ -632,7 +632,7 @@ "description": "A concise description of the 'Trusted sites' pane." }, "whitelistImport": { - "message": "İçe aktar ve ekle", + "message": "İçe aktar ve ekle…", "description": "Button in the 'Trusted sites' pane" }, "whitelistExport": { @@ -900,7 +900,7 @@ "description": "Text for button which open an external webpage in Support pane" }, "supportReportSpecificButton": { - "message": "Yeni rapor oluştur", + "message": "GitHub üzerinde yeni rapor oluştur", "description": "Text for button which open an external webpage in Support pane" }, "supportFindSpecificButton": { @@ -1272,7 +1272,7 @@ "description": "Label for keyboard shortcut used to toggle cosmetic filtering" }, "toggleJavascript": { - "message": "JavaScript'i Aç/Kapa", + "message": "JavaScript'i Aç/Kapat", "description": "Label for keyboard shortcut used to toggle no-scripting switch" }, "relaxBlockingMode": { From 8a6b12a319255263937e01e70497fb70a23fa853 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Mon, 24 Feb 2025 10:11:13 -0500 Subject: [PATCH 0636/1099] [mv3] Inject scriptlets in their intended target world --- platform/mv3/extension/js/scripting-manager.js | 12 ++++++------ platform/mv3/make-scriptlets.js | 8 ++++++-- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/platform/mv3/extension/js/scripting-manager.js b/platform/mv3/extension/js/scripting-manager.js index 6b28dc5b9d51c..2e5c9c6fdff78 100644 --- a/platform/mv3/extension/js/scripting-manager.js +++ b/platform/mv3/extension/js/scripting-manager.js @@ -430,7 +430,7 @@ function registerScriptlet(context, scriptletDetails) { const scriptletList = scriptletDetails.get(rulesetId); if ( scriptletList === undefined ) { continue; } - for ( const [ token, scriptletHostnames ] of scriptletList ) { + for ( const [ token, details ] of scriptletList ) { const id = `${rulesetId}.${token}`; const registered = before.get(id); @@ -439,17 +439,17 @@ function registerScriptlet(context, scriptletDetails) { let targetHostnames = []; if ( hasBroadHostPermission ) { excludeMatches.push(...permissionRevokedMatches); - if ( scriptletHostnames.length > 100 ) { + if ( details.hostnames.length > 100 ) { targetHostnames = [ '*' ]; } else { - targetHostnames = scriptletHostnames; + targetHostnames = details.hostnames; } } else if ( permissionGrantedHostnames.length !== 0 ) { - if ( scriptletHostnames.includes('*') ) { + if ( details.hostnames.includes('*') ) { targetHostnames = permissionGrantedHostnames; } else { targetHostnames = ut.intersectHostnameIters( - scriptletHostnames, + details.hostnames, permissionGrantedHostnames ); } @@ -467,7 +467,7 @@ function registerScriptlet(context, scriptletDetails) { excludeMatches, matchOriginAsFallback: true, runAt: 'document_start', - world: 'MAIN', + world: details.world, }; // register diff --git a/platform/mv3/make-scriptlets.js b/platform/mv3/make-scriptlets.js index 8cc651ff2e240..c2fc6d69fd13a 100644 --- a/platform/mv3/make-scriptlets.js +++ b/platform/mv3/make-scriptlets.js @@ -163,7 +163,6 @@ export async function commit(rulesetId, path, writeFn) { ); content = safeReplace(content, /\$rulesetId\$/, rulesetId, 0); content = safeReplace(content, /\$scriptletName\$/, details.name, 0); - content = safeReplace(content, '$world$', details.world); content = safeReplace(content, 'self.$argsList$', JSON.stringify(Array.from(details.args.keys()).map(a => JSON.parse(a))) @@ -181,7 +180,12 @@ export async function commit(rulesetId, path, writeFn) { JSON.stringify(Array.from(details.exceptions)) ); writeFn(`${path}/${rulesetId}.${name}`, content); - scriptletStats.push([ name.slice(0, -3), Array.from(details.matches).sort() ]); + scriptletStats.push([ + name.slice(0, -3), { + hostnames: Array.from(details.matches).sort(), + world: details.world, + } + ]); } return scriptletStats; } From 9c26a07b539b42bf5eab04e81e75dc3eb691ead0 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Mon, 24 Feb 2025 12:47:54 -0500 Subject: [PATCH 0637/1099] Improve `prevent-addEventListener` scriptlet Related issue: https://github.com/uBlockOrigin/uBlock-issues/issues/3360 --- src/js/resources/scriptlets.js | 46 ++++++++++++++++++---------------- 1 file changed, 24 insertions(+), 22 deletions(-) diff --git a/src/js/resources/scriptlets.js b/src/js/resources/scriptlets.js index 8ee5603b5fa4d..3bea30b9e8a26 100755 --- a/src/js/resources/scriptlets.js +++ b/src/js/resources/scriptlets.js @@ -1281,30 +1281,32 @@ function addEventListenerDefuser( } return matchesBoth; }; - runAt(( ) => { - proxyApplyFn('EventTarget.prototype.addEventListener', function(context) { - const { callArgs, thisArg } = context; - let t, h; - try { - t = String(callArgs[0]); - if ( typeof callArgs[1] === 'function' ) { - h = String(safe.Function_toString(callArgs[1])); - } else if ( typeof callArgs[1] === 'object' && callArgs[1] !== null ) { - if ( typeof callArgs[1].handleEvent === 'function' ) { - h = String(safe.Function_toString(callArgs[1].handleEvent)); - } - } else { - h = String(callArgs[1]); + const proxyFn = function(context) { + const { callArgs, thisArg } = context; + let t, h; + try { + t = String(callArgs[0]); + if ( typeof callArgs[1] === 'function' ) { + h = String(safe.Function_toString(callArgs[1])); + } else if ( typeof callArgs[1] === 'object' && callArgs[1] !== null ) { + if ( typeof callArgs[1].handleEvent === 'function' ) { + h = String(safe.Function_toString(callArgs[1].handleEvent)); } - } catch { - } - if ( type === '' && pattern === '' ) { - safe.uboLog(logPrefix, `Called: ${t}\n${h}\n${elementDetails(thisArg)}`); - } else if ( shouldPrevent(thisArg, t, h) ) { - return safe.uboLog(logPrefix, `Prevented: ${t}\n${h}\n${elementDetails(thisArg)}`); + } else { + h = String(callArgs[1]); } - return context.reflect(); - }); + } catch { + } + if ( type === '' && pattern === '' ) { + safe.uboLog(logPrefix, `Called: ${t}\n${h}\n${elementDetails(thisArg)}`); + } else if ( shouldPrevent(thisArg, t, h) ) { + return safe.uboLog(logPrefix, `Prevented: ${t}\n${h}\n${elementDetails(thisArg)}`); + } + return context.reflect(); + }; + runAt(( ) => { + proxyApplyFn('EventTarget.prototype.addEventListener', proxyFn); + proxyApplyFn('document.addEventListener', proxyFn); }, extraArgs.runAt); } From 8237dfc4f7900842ce2dd924354c9dd9f1ce6195 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Mon, 24 Feb 2025 12:50:33 -0500 Subject: [PATCH 0638/1099] Update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 15e29485718dd..eb6e2bf07a7ec 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,4 @@ +- [Improve `prevent-addEventListener` scriptlet](https://github.com/gorhill/uBlock/commit/9c26a07b53) - [Fix reverse lookup of `##^responseheader(...)` filters](https://github.com/gorhill/uBlock/commit/5921e50e03) - [Improve `evaldata-prune` scriptlet](https://github.com/gorhill/uBlock/commit/9bb1a2baaf) - [Comply with Mozilla's "User Consent and Control"](https://github.com/gorhill/uBlock/commit/344539d793) From 18a502c4e313642c2437868c58e5afc5985dd59a Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Mon, 24 Feb 2025 12:50:56 -0500 Subject: [PATCH 0639/1099] New revision for dev build --- dist/version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dist/version b/dist/version index 2d2375905baa8..f1b1062314c05 100644 --- a/dist/version +++ b/dist/version @@ -1 +1 @@ -1.62.1.3 +1.62.1.4 From 7fb656b6538ea5a32e2a6125089dcf9790787b7a Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Mon, 24 Feb 2025 13:05:32 -0500 Subject: [PATCH 0640/1099] Make Firefox dev build auto-update --- dist/firefox/updates.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dist/firefox/updates.json b/dist/firefox/updates.json index 8e203a5bc2f2f..16c4b4dcc12b8 100644 --- a/dist/firefox/updates.json +++ b/dist/firefox/updates.json @@ -3,9 +3,9 @@ "uBlock0@raymondhill.net": { "updates": [ { - "version": "1.62.1.3", + "version": "1.62.1.4", "browser_specific_settings": { "gecko": { "strict_min_version": "78.0" } }, - "update_link": "https://github.com/gorhill/uBlock/releases/download/1.62.1b3/uBlock0_1.62.1b3.firefox.signed.xpi" + "update_link": "https://github.com/gorhill/uBlock/releases/download/1.62.1b4/uBlock0_1.62.1b4.firefox.signed.xpi" } ] } From d41989e62a78380cac32962311fdc14b5e1e67cb Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Tue, 25 Feb 2025 13:02:44 -0500 Subject: [PATCH 0641/1099] Improve `disable-newtab-links` scriptlet Related discussion: https://github.com/uBlockOrigin/uBlock-issues/discussions/3551 --- src/js/resources/scriptlets.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/js/resources/scriptlets.js b/src/js/resources/scriptlets.js index 3bea30b9e8a26..105bdb75e5b06 100755 --- a/src/js/resources/scriptlets.js +++ b/src/js/resources/scriptlets.js @@ -2222,8 +2222,8 @@ builtinScriptlets.push({ }); // https://github.com/uBlockOrigin/uAssets/issues/913 function disableNewtabLinks() { - document.addEventListener('click', function(ev) { - var target = ev.target; + document.addEventListener('click', ev => { + let target = ev.target; while ( target !== null ) { if ( target.localName === 'a' && target.hasAttribute('target') ) { ev.stopPropagation(); @@ -2232,7 +2232,7 @@ function disableNewtabLinks() { } target = target.parentNode; } - }); + }, { capture: true }); } /******************************************************************************/ From 95b99ef4ac6f0dc92978a066781e9147a0fe4c0a Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Tue, 25 Feb 2025 13:05:21 -0500 Subject: [PATCH 0642/1099] New revision for dev build --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index eb6e2bf07a7ec..b4178292c6fcd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,4 @@ +- [Improve `disable-newtab-links` scriptlet](https://github.com/gorhill/uBlock/commit/d41989e62a) - [Improve `prevent-addEventListener` scriptlet](https://github.com/gorhill/uBlock/commit/9c26a07b53) - [Fix reverse lookup of `##^responseheader(...)` filters](https://github.com/gorhill/uBlock/commit/5921e50e03) - [Improve `evaldata-prune` scriptlet](https://github.com/gorhill/uBlock/commit/9bb1a2baaf) From ea8853cda3efd2faddc8bdbfe15bd74fcf44f903 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Fri, 28 Feb 2025 17:18:48 -0500 Subject: [PATCH 0643/1099] Use onmessage/postMessage instead of BroadcastChannel in diff updater --- src/js/assets.js | 12 +++++------- src/js/diff-updater.js | 10 +++++----- 2 files changed, 10 insertions(+), 12 deletions(-) diff --git a/src/js/assets.js b/src/js/assets.js index 778af87f03510..ba7c4cf705dc4 100644 --- a/src/js/assets.js +++ b/src/js/assets.js @@ -1244,10 +1244,8 @@ async function diffUpdater() { ubolog('Diff updater: cycle start'); return new Promise(resolve => { let pendingOps = 0; - const bc = new globalThis.BroadcastChannel('diffUpdater'); const terminate = error => { worker.terminate(); - bc.close(); resolve(); if ( typeof error !== 'string' ) { return; } ubolog(`Diff updater: terminate because ${error}`); @@ -1260,14 +1258,15 @@ async function diffUpdater() { if ( metadata.diffPath === data.patchPath ) { return; } assetCacheSetDetails(data.assetKey, metadata); }; - bc.onmessage = ev => { + const worker = new Worker('js/diff-updater.js'); + worker.onmessage = ev => { const data = ev.data || {}; if ( data.what === 'ready' ) { ubolog('Diff updater: hard updating', toHardUpdate.map(v => v.assetKey).join()); while ( toHardUpdate.length !== 0 ) { const assetDetails = toHardUpdate.shift(); assetDetails.fetch = true; - bc.postMessage(assetDetails); + worker.postMessage(assetDetails); pendingOps += 1; } return; @@ -1284,7 +1283,7 @@ async function diffUpdater() { data.text = result.content || ''; data.status = undefined; checkAndCorrectDiffPath(data); - bc.postMessage(data); + worker.postMessage(data); }); return; } @@ -1320,7 +1319,7 @@ async function diffUpdater() { if ( pendingOps === 0 && toSoftUpdate.length !== 0 ) { ubolog('Diff updater: soft updating', toSoftUpdate.map(v => v.assetKey).join()); while ( toSoftUpdate.length !== 0 ) { - bc.postMessage(toSoftUpdate.shift()); + worker.postMessage(toSoftUpdate.shift()); pendingOps += 1; } } @@ -1328,7 +1327,6 @@ async function diffUpdater() { ubolog('Diff updater: cycle complete'); terminate(); }; - const worker = new Worker('js/diff-updater.js'); }).catch(reason => { ubolog(`Diff updater: ${reason}`); }); diff --git a/src/js/diff-updater.js b/src/js/diff-updater.js index 8a3c5e9e31fb1..b9e906bd688ef 100644 --- a/src/js/diff-updater.js +++ b/src/js/diff-updater.js @@ -265,21 +265,21 @@ async function fetchAndApplyAllPatches(assetDetails) { /******************************************************************************/ -const bc = new globalThis.BroadcastChannel('diffUpdater'); +const self = globalThis; -bc.onmessage = ev => { +self.onmessage = ev => { const message = ev.data || {}; switch ( message.what ) { case 'update': fetchAndApplyAllPatches(message).then(response => { - bc.postMessage(response); + self.postMessage(response); }).catch(error => { - bc.postMessage({ what: 'broken', error }); + self.postMessage({ what: 'broken', error }); }); break; } }; -bc.postMessage({ what: 'ready' }); +self.postMessage({ what: 'ready' }); /******************************************************************************/ From cff88d547f35d5396c54a59cbfd0aeed0da7221c Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sat, 1 Mar 2025 09:26:44 -0500 Subject: [PATCH 0644/1099] Import translation work from https://crowdin.com/project/ublock --- platform/mv3/description/webstore.be.txt | 4 +-- platform/mv3/description/webstore.ro.txt | 2 +- platform/mv3/description/webstore.zh_CN.txt | 2 +- .../mv3/extension/_locales/be/messages.json | 6 ++-- .../extension/_locales/br_FR/messages.json | 2 +- .../mv3/extension/_locales/ru/messages.json | 2 +- src/_locales/be/messages.json | 30 +++++++++---------- src/_locales/ru/messages.json | 2 +- 8 files changed, 25 insertions(+), 25 deletions(-) diff --git a/platform/mv3/description/webstore.be.txt b/platform/mv3/description/webstore.be.txt index d9749c59cca75..4740e0df62a1f 100644 --- a/platform/mv3/description/webstore.be.txt +++ b/platform/mv3/description/webstore.be.txt @@ -5,7 +5,7 @@ uBO Лайт (uBOL) гэта блакіроўшчык кантэнту з мен - Убудаваныя спісы фільтраў uBlock Origin - EasyList - EasyPrivacy -- Спіс сервераў рэкламы ды адсочвання ад Peter Lowe +- Спіс сервераў з рэкламай і адсочвання ад Peter Lowe You can enable more rulesets by visiting the options page -- click the _Cogs_ icon in the popup panel. @@ -25,6 +25,6 @@ You can set the default filtering mode from uBOL's options page. If you pick the Keep in mind this is still a work in progress, with these end goals: -- No broad host permissions at install time -- extended permissions are granted explicitly by the user on a per-site basis. +- Адсутнасць шырокіх дазволаў на вузел падчас усталявання. Пашыраныя дазволы выдаюцца карыстальнікам для кожнага сайта асобна. - Цалкам дэкларатыўная ацэнка надзейнасці і эфектыўнасці работы працэсара/памяці. diff --git a/platform/mv3/description/webstore.ro.txt b/platform/mv3/description/webstore.ro.txt index 8f64fad1f5832..fe70da85660f5 100644 --- a/platform/mv3/description/webstore.ro.txt +++ b/platform/mv3/description/webstore.ro.txt @@ -5,7 +5,7 @@ Setul de reguli implicit corespunde setului de filtre implicit al uBlock Origin: Listele de filtre încorporate de uBlock Origin - EasyList - EasyPrivacy -- Oglas Peter Lowe i lista servera za praćenje +- -Lista de servere de anunț și de urmărire a lui Peter Lowe Puteți activa mai multe seturi de reguli accesând pagina de opțiuni - faceți clic pe pictograma _Cogs_ din panoul pop-up. diff --git a/platform/mv3/description/webstore.zh_CN.txt b/platform/mv3/description/webstore.zh_CN.txt index e0ae72dd5eb33..4cc39b7192141 100644 --- a/platform/mv3/description/webstore.zh_CN.txt +++ b/platform/mv3/description/webstore.zh_CN.txt @@ -5,7 +5,7 @@ uBO Lite (uBOL) 是一个基于最新浏览器扩展接口(Manifest Version 3 - uBlock Origin 内置过滤规则列表 - EasyList - EasyPrivacy -- Peter Lowe’s Ad and tracking server list +- Peter Lowe 的广告和跟踪服务器列表 访问选项页面,点击弹出面板中的齿轮图标,即可启用更多规则集。 diff --git a/platform/mv3/extension/_locales/be/messages.json b/platform/mv3/extension/_locales/be/messages.json index 0063666a5c310..deeb4c4be9d08 100644 --- a/platform/mv3/extension/_locales/be/messages.json +++ b/platform/mv3/extension/_locales/be/messages.json @@ -8,7 +8,7 @@ "description": "this will be in the Chrome web store: must be 132 characters or less" }, "perRulesetStats": { - "message": "{{ruleCount}} правілаў, пераўтвораных з {{filterCount}} сеткавых фільтраў", + "message": "Правілы (колькасць: {{ruleCount}}), якія пераўтвораны з сеткавых фільтраў (колькасць: {{filterCount}})", "description": "Appears aside each filter list in the _3rd-party filters_ pane" }, "dashboardName": { @@ -128,7 +128,7 @@ "description": "Label for widget to select type of issue" }, "supportS6Select1Option0": { - "message": "-- Выберыце тэму --", + "message": "-- Выберыце праблему --", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option1": { @@ -216,7 +216,7 @@ "description": "A short description for the editable field which lists trusted sites" }, "noFilteringModePlaceholder": { - "message": "[толькі назвы хостаў]\nexample.com\ngames.example\n...", + "message": "[толькі назвы вузлоў]\nexample.com\ngames.example\n...", "description": "Default text for in edit field" }, "behaviorSectionLabel": { diff --git a/platform/mv3/extension/_locales/br_FR/messages.json b/platform/mv3/extension/_locales/br_FR/messages.json index 1b3d92a2c42d5..ee4ea9c58575b 100644 --- a/platform/mv3/extension/_locales/br_FR/messages.json +++ b/platform/mv3/extension/_locales/br_FR/messages.json @@ -112,7 +112,7 @@ "description": "First paragraph of 'Filter issues' section in Support pane" }, "supportS6P1S1": { - "message": "Evit nompas sammañ ar genlabourerien a-youl vat gant meur a zanevell heñvel, gwiriit ma n'eo ket bet danevellet ho kudenn en ar-raok mar plij.", + "message": "Evit nompas sammañ ar genlabourerien a-youl vat gant meur a zanevell heñvel, gwiriit ma n'eo ket bet danevellet ho kudenn en ar-raok mar plij. Notenn: ma klikit ar bouton e vo kaset anv herberc'hier ar bajenn da c'h-GitHub.", "description": "A paragraph in the filter issue reporter section" }, "supportFindSpecificButton": { diff --git a/platform/mv3/extension/_locales/ru/messages.json b/platform/mv3/extension/_locales/ru/messages.json index ed0b191b0b6d6..fcb58bdabccf7 100644 --- a/platform/mv3/extension/_locales/ru/messages.json +++ b/platform/mv3/extension/_locales/ru/messages.json @@ -112,7 +112,7 @@ "description": "First paragraph of 'Filter issues' section in Support pane" }, "supportS6P1S1": { - "message": "Чтобы не обременять добровольцев повторяющимися отчётами, пожалуйста, убедитесь, что об этой проблеме ещё не сообщали. Примечание: щелчок по кнопке приведёт к отправке исходного текста страницы в GitHub.", + "message": "Чтобы не обременять добровольцев повторяющимися отчётами, пожалуйста, убедитесь, что об этой проблеме ещё не сообщали. Примечание: щелчок по кнопке приведёт к отправке адреса посещенной страницы GitHub'у.", "description": "A paragraph in the filter issue reporter section" }, "supportFindSpecificButton": { diff --git a/src/_locales/be/messages.json b/src/_locales/be/messages.json index 43aef84b7a2ff..95be96c0ba038 100644 --- a/src/_locales/be/messages.json +++ b/src/_locales/be/messages.json @@ -228,7 +228,7 @@ "description": "Tooltip when hovering over the padlock in the dynamic filtering pane." }, "popupTipRevertRules": { - "message": "Націсніце, каб скасаваць змены.", + "message": "Націсніце, каб вярнуць змены.", "description": "Tooltip when hovering over the eraser in the dynamic filtering pane." }, "popupAnyRulePrompt": { @@ -252,7 +252,7 @@ "description": "" }, "popup1pScriptRulePrompt": { - "message": "уласныя скрыпты", + "message": "Асноўныя скрыпты", "description": "" }, "popup3pScriptRulePrompt": { @@ -260,7 +260,7 @@ "description": "" }, "popup3pFrameRulePrompt": { - "message": "староннія рамкі", + "message": "староннія фрэймы", "description": "" }, "popupHitDomainCountPrompt": { @@ -424,7 +424,7 @@ "description": "Appears at the top of the _3rd-party filters_ pane" }, "3pListsOfBlockedHostsPerListStats": { - "message": "{{used}} выкарыстана з {{total}}", + "message": "Выкарыстана: {{used}} з {{total}}", "description": "Appears aside each filter list in the _3rd-party filters_ pane" }, "3pAutoUpdatePrompt1": { @@ -740,7 +740,7 @@ "description": "A keyword in the built-in row filtering expression" }, "loggerRowFiltererBuiltin3p": { - "message": "старонні", + "message": "староннія", "description": "A keyword in the built-in row filtering expression" }, "loggerEntryDetailsHeader": { @@ -796,7 +796,7 @@ "description": "Small header to identify the static filtering section" }, "loggerStaticFilteringSentence": { - "message": "{{action}} сеткавыя запыты {{type}}, {{br}}URL-адрас якіх супадае з {{url}} {{br}}і паходзяць з {{origin}},{{br}}{{importance}} ёсць адпаведны фільтр-вынятак.", + "message": "{{action}} сеткавыя запыты {{type}}, {{br}}URL-адрас якіх супадае з {{url}} {{br}}і якія паходзяць з {{origin}},{{br}}{{importance}} з'яўляюцца адпаведным фільтрам выключэння.", "description": "Used in the static filtering wizard" }, "loggerStaticFilteringSentencePartBlock": { @@ -932,11 +932,11 @@ "description": "First paragraph of 'Filter issues' section in Support pane" }, "supportS3P2": { - "message": "Важна: Пазбягайце выкарыстання іншых блакавальнікаў аналагічнага прызначэння разам з uBlock Origin, паколькі гэта можа прывесці да праблем з фільтрамі на пэўных сайтах.", + "message": "Важна: Пазбягайце выкарыстання іншых блакавальнікаў падобнага прызначэння разам з uBlock Origin, паколькі гэта можа прывесці да праблем з фільтрамі на пэўных сайтах.", "description": "Second paragraph of 'Filter issues' section in Support pane" }, "supportS3P3": { - "message": "Парада: Упэўніцеся, што вашы спісы фільтраў абноўленыя. Логер — галоўны інструмент для дыягностыкі праблем, звязаных з фільтрамі.", + "message": "Парада: Пераканайцеся, што вашы спісы фільтраў знаходзяцца ў актуальным стане. Рэгістратар — гэта галоўны інструмент для дыягностыкі праблем, якія звязаны з фільтрамі.", "description": "Third paragraph of 'Filter issues' section in Support pane" }, "supportS4H": { @@ -956,7 +956,7 @@ "description": "First paragraph of 'Troubleshooting Information' section in Support pane" }, "supportS5P2": { - "message": "Важна: Патэнцыйна прыватная або адчувальная інфармацыя тыпова рэдагуецца. Адрэдагаваная інфармацыя можа зрабіць цяжэйшым вырашэнне праблемы.", + "message": "Важна: Патэнцыйна прыватная або канфідэнцыяльныя зветскі прадвызначана рэдагуюцца. Адрэдагаваныя звесткі могуць ускладніць вырашэнне праблемы.", "description": "Second paragraph of 'Troubleshooting Information' section in Support pane" }, "supportS6H": { @@ -984,7 +984,7 @@ "description": "Label for widget to select type of issue" }, "supportS6Select1Option0": { - "message": "-- Выберыце запіс праблемы --", + "message": "-- Выберыце праблему --", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option1": { @@ -1064,7 +1064,7 @@ "description": "Shown in the About pane" }, "aboutCDNsInfo": { - "message": "Калі спіс фільтраў патрабуе абнаўлення, выкарыстоўваецца выпадкова выбраная сетка CDN", + "message": "Пры абнаўленні спіса фільтраў выкарыстоўваецца CDN, які выбіраецца выпадковым чынам", "description": "Shown in the About pane" }, "aboutBackupDataButton": { @@ -1100,7 +1100,7 @@ "description": "English: Network error: {{msg}}" }, "subscriberConfirm": { - "message": "Дадаць наступны URL-адрас да вашага спісу фільтраў?\n\nНазва: \"{{title}}\"\nURL: {{url}}", + "message": "Дадаць наступны URL-адрас да вашага спіса фільтраў?\n\nНазва: \"{{title}}\"\nURL-адрас: {{url}}", "description": "No longer used" }, "subscribeButton": { @@ -1112,7 +1112,7 @@ "description": "English: a minute ago" }, "elapsedManyMinutesAgo": { - "message": "{{value}} хвілін(ы) таму", + "message": "Хвілін таму: {{value}}", "description": "English: {{value}} minutes ago" }, "elapsedOneHourAgo": { @@ -1120,7 +1120,7 @@ "description": "English: an hour ago" }, "elapsedManyHoursAgo": { - "message": "{{value}} гадзін(ы) таму", + "message": "Гадзін таму: {{value}}", "description": "English: {{value}} hours ago" }, "elapsedOneDayAgo": { @@ -1128,7 +1128,7 @@ "description": "English: a day ago" }, "elapsedManyDaysAgo": { - "message": "{{value}} дні таму", + "message": "Дзён таму: {{value}}", "description": "English: {{value}} days ago" }, "showDashboardButton": { diff --git a/src/_locales/ru/messages.json b/src/_locales/ru/messages.json index eae7c39c2c1a9..cca5c0f5af434 100644 --- a/src/_locales/ru/messages.json +++ b/src/_locales/ru/messages.json @@ -964,7 +964,7 @@ "description": "Header of 'Report a filter issue' section in Support pane" }, "supportS6P1S1": { - "message": "Чтобы не обременять добровольцев повторяющимися отчётами, пожалуйста, убедитесь, что об этой проблеме ещё не сообщали. Примечание: щелчок по кнопке приведёт к отправке исходного текста страницы в GitHub.", + "message": "Чтобы не обременять добровольцев повторяющимися отчётами, пожалуйста, убедитесь, что об этой проблеме ещё не сообщали. Примечание: щелчок по кнопке приведёт к отправке адреса посещенной страницы GitHub'у.", "description": "A paragraph in the filter issue reporter section" }, "supportS6P2S1": { From 36404543e4071b0c84c0a0e8a38ee155fb325797 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sat, 1 Mar 2025 14:48:51 -0500 Subject: [PATCH 0645/1099] Increase URL buffer size to 8192 (from 2048) Related: https://github.com/easylist/easylist/commit/777d7ba9 --- src/devtools.html | 2 +- src/js/background.js | 2 +- src/js/biditrie.js | 76 ++++++++++++++++----------------- src/js/devtools.js | 1 + src/js/s14e-serializer.js | 10 ++++- src/js/static-net-filtering.js | 29 +++---------- src/js/wasm/README.md | 2 +- src/js/wasm/biditrie.wasm | Bin 990 -> 999 bytes src/js/wasm/biditrie.wat | 38 ++++++++--------- 9 files changed, 74 insertions(+), 86 deletions(-) diff --git a/src/devtools.html b/src/devtools.html index 8f66827bfaca8..561037661c2c5 100644 --- a/src/devtools.html +++ b/src/devtools.html @@ -33,7 +33,7 @@
-
+
diff --git a/src/js/background.js b/src/js/background.js index 01fd3b5d6b1ec..b648ee9c05ad6 100644 --- a/src/js/background.js +++ b/src/js/background.js @@ -181,7 +181,7 @@ const µBlock = { // jshint ignore:line // Read-only systemSettings: { compiledMagic: 57, // Increase when compiled format changes - selfieMagic: 58, // Increase when selfie format changes + selfieMagic: 59, // Increase when selfie format changes }, // https://github.com/uBlockOrigin/uBlock-issues/issues/759#issuecomment-546654501 diff --git a/src/js/biditrie.js b/src/js/biditrie.js index 560d5918a2276..33e6a1f7531a3 100644 --- a/src/js/biditrie.js +++ b/src/js/biditrie.js @@ -24,13 +24,16 @@ A BidiTrieContainer is mostly a large buffer in which distinct but related tries are stored. The memory layout of the buffer is as follow: - 0-2047: haystack section - 2048-2051: number of significant characters in the haystack - 2052-2055: offset to start of trie data section (=> trie0) - 2056-2059: offset to end of trie data section (=> trie1) - 2060-2063: offset to start of character data section (=> char0) - 2064-2067: offset to end of character data section (=> char1) - 2068: start of trie data section + 0-8192: haystack section + 8192-8195: number of significant characters in the haystack + 8196-8199: offset to start of trie data section (=> trie0) + 8200-8203: offset to end of trie data section (=> trie1) + 8204-8207: offset to start of character data section (=> char0) + 8208-8211: offset to end of character data section (=> char1) + 8212-8215: offset to left index result (=> result_l) + 8216-8219: offset to right index result (=> result_r) + 8220-8223: offset to extra unit result (=> result_iu) + 8224: start of trie data section +--------------+ Normal cell: | And | If "Segment info" matches: @@ -93,18 +96,19 @@ */ +const VERSION = 2; const PAGE_SIZE = 65536*2; const HAYSTACK_START = 0; -const HAYSTACK_SIZE = 2048; // i32 / i8 -const HAYSTACK_SIZE_SLOT = HAYSTACK_SIZE >>> 2; // 512 / 2048 -const TRIE0_SLOT = HAYSTACK_SIZE_SLOT + 1; // 513 / 2052 -const TRIE1_SLOT = HAYSTACK_SIZE_SLOT + 2; // 514 / 2056 -const CHAR0_SLOT = HAYSTACK_SIZE_SLOT + 3; // 515 / 2060 -const CHAR1_SLOT = HAYSTACK_SIZE_SLOT + 4; // 516 / 2064 -const RESULT_L_SLOT = HAYSTACK_SIZE_SLOT + 5; // 517 / 2068 -const RESULT_R_SLOT = HAYSTACK_SIZE_SLOT + 6; // 518 / 2072 -const RESULT_IU_SLOT = HAYSTACK_SIZE_SLOT + 7; // 519 / 2076 -const TRIE0_START = HAYSTACK_SIZE_SLOT + 8 << 2; // 2080 +const HAYSTACK_SIZE = 8192; // i32 / i8 +const HAYSTACK_SIZE_SLOT = HAYSTACK_SIZE >>> 2; // 2048 / 8192 +const TRIE0_SLOT = HAYSTACK_SIZE_SLOT + 1; // 2049 / 8196 +const TRIE1_SLOT = HAYSTACK_SIZE_SLOT + 2; // 2050 / 8200 +const CHAR0_SLOT = HAYSTACK_SIZE_SLOT + 3; // 2051 / 8204 +const CHAR1_SLOT = HAYSTACK_SIZE_SLOT + 4; // 2052 / 8208 +const RESULT_L_SLOT = HAYSTACK_SIZE_SLOT + 5; // 2053 / 8212 +const RESULT_R_SLOT = HAYSTACK_SIZE_SLOT + 6; // 2054 / 8216 +const RESULT_IU_SLOT = HAYSTACK_SIZE_SLOT + 7; // 2055 / 8220 +const TRIE0_START = HAYSTACK_SIZE_SLOT + 8 << 2; // 8224 const CELL_BYTE_LENGTH = 12; const MIN_FREE_CELL_BYTE_LENGTH = CELL_BYTE_LENGTH * 8; @@ -156,28 +160,21 @@ class BidiTrieContainer { // Public methods //-------------------------------------------------------------------------- - get haystackLen() { + getHaystackLen() { return this.buf32[HAYSTACK_SIZE_SLOT]; } - set haystackLen(v) { + setHaystackLen(v) { + if ( v > HAYSTACK_SIZE ) { + v = HAYSTACK_SIZE; + } this.buf32[HAYSTACK_SIZE_SLOT] = v; + return v; } - reset(details) { - if ( - details instanceof Object && - typeof details.byteLength === 'number' && - typeof details.char0 === 'number' - ) { - if ( details.byteLength > this.buf8.byteLength ) { - this.reallocateBuf(details.byteLength); - } - this.buf32[CHAR0_SLOT] = details.char0; - } + reset() { this.buf32[TRIE1_SLOT] = this.buf32[TRIE0_SLOT]; this.buf32[CHAR1_SLOT] = this.buf32[CHAR0_SLOT]; - this.lastStored = ''; this.lastStoredLen = this.lastStoredIndex = 0; } @@ -571,23 +568,22 @@ class BidiTrieContainer { this.buf32[iboundary+BCELL_EXTRA] = v; } - optimize(shrink = false) { - if ( shrink ) { - this.shrinkBuf(); - } - return { - byteLength: this.buf8.byteLength, - char0: this.buf32[CHAR0_SLOT], - }; + optimize() { + this.shrinkBuf(); } toSelfie() { const buf32 = this.buf32.subarray(0, this.buf32[CHAR1_SLOT] + 3 >>> 2); - return { buf32, checksum: i32Checksum(buf32) }; + return { + version: VERSION, + buf32, + checksum: i32Checksum(buf32), + }; } fromSelfie(selfie) { if ( typeof selfie !== 'object' || selfie === null ) { return false; } + if ( selfie.version !== VERSION ) { return false; } if ( selfie.buf32 instanceof Uint32Array === false ) { return false; } if ( selfie.checksum !== i32Checksum(selfie.buf32) ) { return false; } const byteLength = selfie.buf32.length << 2; diff --git a/src/js/devtools.js b/src/js/devtools.js index 92cd078940f80..5183d2192d939 100644 --- a/src/js/devtools.js +++ b/src/js/devtools.js @@ -331,6 +331,7 @@ cmEditor.on('beforeChange', (cm, details) => { const fields = line.slice(5).split(/\s+/); const query = {}; for ( const field of fields ) { + if ( field === '' ) { continue; } if ( /[/.]/.test(field) ) { if ( query.url === undefined ) { query.url = field; diff --git a/src/js/s14e-serializer.js b/src/js/s14e-serializer.js index f88e5b5e2f593..db40388e9648a 100644 --- a/src/js/s14e-serializer.js +++ b/src/js/s14e-serializer.js @@ -1422,8 +1422,14 @@ if ( isInstanceOf(globalThis, 'DedicatedWorkerGlobalScope') ) { break; } case THREAD_DESERIALIZE: { - const result = deserialize(msg.data); - globalThis.postMessage({ id: msg.id, size: msg.size, result }); + let result; + try { + result = deserialize(msg.data); + } catch(ex) { + console.error(ex); + } finally { + globalThis.postMessage({ id: msg.id, size: msg.size, result }); + } break; } default: diff --git a/src/js/static-net-filtering.js b/src/js/static-net-filtering.js index af5f5691c376e..b1a56d2326fcb 100644 --- a/src/js/static-net-filtering.js +++ b/src/js/static-net-filtering.js @@ -550,14 +550,6 @@ const bidiTrieMatchExtra = (l, r, ix) => { const bidiTrie = new BidiTrieContainer(bidiTrieMatchExtra); -const bidiTriePrime = ( ) => { - bidiTrie.reset(keyvalStore.getItem('SNFE.bidiTrie')); -}; - -const bidiTrieOptimize = (shrink = false) => { - keyvalStore.setItem('SNFE.bidiTrie', bidiTrie.optimize(shrink)); -}; - /******************************************************************************* Each filter class will register itself in the map. @@ -800,7 +792,7 @@ class FilterPatternPlain { if ( bidiTrie.startsWith( left, - bidiTrie.haystackLen, + bidiTrie.getHaystackLen(), filterData[idata+1], n ) === 0 @@ -877,7 +869,7 @@ class FilterPatternPlain1 extends FilterPatternPlain { if ( bidiTrie.startsWith( left, - bidiTrie.haystackLen, + bidiTrie.getHaystackLen(), filterData[idata+1], n ) === 0 @@ -900,7 +892,7 @@ class FilterPatternPlainX extends FilterPatternPlain { if ( bidiTrie.startsWith( left, - bidiTrie.haystackLen, + bidiTrie.getHaystackLen(), filterData[idata+1], n ) === 0 @@ -1035,7 +1027,7 @@ class FilterAnchorHnLeft { lastBeg = len !== 0 ? haystackCodes.indexOf(0x3A) : -1; if ( lastBeg !== -1 ) { if ( - lastBeg >= bidiTrie.haystackLen || + lastBeg >= bidiTrie.getHaystackLen() || haystackCodes[lastBeg+1] !== 0x2F || haystackCodes[lastBeg+2] !== 0x2F ) { @@ -3199,14 +3191,9 @@ const urlTokenizer = new (class { _tokenize(encodeInto) { const tokens = this._tokens; - let url = this._urlOut; - let l = url.length; + const url = this._urlOut; + const l = encodeInto.setHaystackLen(url.length); if ( l === 0 ) { return 0; } - if ( l > 2048 ) { - url = url.slice(0, 2048); - l = 2048; - } - encodeInto.haystackLen = l; let j = 0; let hasq = -1; mainLoop: { @@ -4197,7 +4184,6 @@ StaticNetFilteringEngine.prototype.prime = function() { destHNTrieContainer.reset( keyvalStore.getItem('SNFE.destHNTrieContainer.trieDetails') ); - bidiTriePrime(); }; /******************************************************************************/ @@ -4793,7 +4779,6 @@ StaticNetFilteringEngine.prototype.optimize = function(throttle = 0) { 'SNFE.destHNTrieContainer.trieDetails', destHNTrieContainer.optimize() ); - bidiTrieOptimize(); filterDataShrink(); }; @@ -4801,7 +4786,7 @@ StaticNetFilteringEngine.prototype.optimize = function(throttle = 0) { StaticNetFilteringEngine.prototype.toSelfie = function() { this.optimize(0); - bidiTrieOptimize(true); + bidiTrie.optimize(); keyvalStore.setItem('SNFE.origHNTrieContainer.trieDetails', origHNTrieContainer.optimize() ); diff --git a/src/js/wasm/README.md b/src/js/wasm/README.md index 32aef076f0170..01041b421a981 100644 --- a/src/js/wasm/README.md +++ b/src/js/wasm/README.md @@ -13,7 +13,7 @@ Assuming: ### `wat2wasm` tool The `wat2wasm` tool can be downloaded from an official WebAssembly project: -. + ### `wat2wasm` tool online diff --git a/src/js/wasm/biditrie.wasm b/src/js/wasm/biditrie.wasm index 5bfc6b7d0480ef5fefe921bcb3f7b4afd571e778..1a133986798515f18e9d482c8dae44d6ae467cdb 100644 GIT binary patch delta 153 zcmcb|{+xY6hs<3zwm*!FZ1oBZj!Y$r430eq7&Mp|6qy_w4lqny^px`oBO_})NOW>P z<5@=XPY^4x2omNrnliXAhHwd=Vo{ rJ(B~Ig95WFFHo5RbG8zr0)q!HqXHAyTp{KV#)`@5%nBe49n4_>P-rEl delta 144 zcmaFPevf@Zhtw4|wqJ~lZ1oBZj!Y$r430el8cYm|OpXl#6PG;YxXj4NS`QSPJc;ov zqvPZRCS@A|1!glQ2FED^3Jf56hJXSikmhz|myasV2~;K9qNz@WgK nrNHP7WHEt^Im~3iSUC9)lMJK4WGUtl#`4Me%nCsEMCLF6uazJE diff --git a/src/js/wasm/biditrie.wat b/src/js/wasm/biditrie.wat index a6c80baf1c874..7a775009145f1 100644 --- a/src/js/wasm/biditrie.wat +++ b/src/js/wasm/biditrie.wat @@ -32,16 +32,16 @@ ;; ;; Memory layout, byte offset: ;; const HAYSTACK_START = 0; -;; const HAYSTACK_SIZE = 2048; // i32 / i8 -;; const HAYSTACK_SIZE_SLOT = HAYSTACK_SIZE >>> 2; // 512 / 2048 -;; const TRIE0_SLOT = HAYSTACK_SIZE_SLOT + 1; // 513 / 2052 -;; const TRIE1_SLOT = HAYSTACK_SIZE_SLOT + 2; // 514 / 2056 -;; const CHAR0_SLOT = HAYSTACK_SIZE_SLOT + 3; // 515 / 2060 -;; const CHAR1_SLOT = HAYSTACK_SIZE_SLOT + 4; // 516 / 2064 -;; const RESULT_L_SLOT = HAYSTACK_SIZE_SLOT + 5; // 517 / 2068 -;; const RESULT_R_SLOT = HAYSTACK_SIZE_SLOT + 6; // 518 / 2072 -;; const RESULT_IU_SLOT = HAYSTACK_SIZE_SLOT + 7; // 519 / 2076 -;; const TRIE0_START = HAYSTACK_SIZE_SLOT + 8 << 2; // 2080 +;; const HAYSTACK_SIZE = 8192; // i32 / i8 +;; const HAYSTACK_SIZE_SLOT = HAYSTACK_SIZE >>> 2; // 2048 / 8192 +;; const TRIE0_SLOT = HAYSTACK_SIZE_SLOT + 1; // 2049 / 8196 +;; const TRIE1_SLOT = HAYSTACK_SIZE_SLOT + 2; // 2050 / 8200 +;; const CHAR0_SLOT = HAYSTACK_SIZE_SLOT + 3; // 2051 / 8204 +;; const CHAR1_SLOT = HAYSTACK_SIZE_SLOT + 4; // 2052 / 8208 +;; const RESULT_L_SLOT = HAYSTACK_SIZE_SLOT + 5; // 2053 / 8212 +;; const RESULT_R_SLOT = HAYSTACK_SIZE_SLOT + 6; // 2054 / 8216 +;; const RESULT_IU_SLOT = HAYSTACK_SIZE_SLOT + 7; // 2055 / 8220 +;; const TRIE0_START = HAYSTACK_SIZE_SLOT + 8 << 2; // 8224 ;; ;; @@ -71,11 +71,11 @@ ;; const buf32 = this.buf32; ;; const buf8 = this.buf8; ;; const char0 = buf32[CHAR0_SLOT]; - i32.const 2060 + i32.const 8204 i32.load align=4 local.set $char0 ;; const aR = buf32[HAYSTACK_SIZE_SLOT]; - i32.const 2048 + i32.const 8192 i32.load align=4 local.set $aR ;; let al = ai; @@ -253,7 +253,7 @@ ;; const buf32 = this.buf32; ;; const buf8 = this.buf8; ;; const char0 = buf32[CHAR0_SLOT]; - i32.const 2060 + i32.const 8204 i32.load align=4 local.set $char0 block $matchFound @@ -433,15 +433,15 @@ ;; } end ;; this.buf32[RESULT_IU_SLOT] = iu; - i32.const 2076 + i32.const 8220 local.get $iu i32.store align=4 ;; this.buf32[RESULT_L_SLOT] = l; - i32.const 2068 + i32.const 8212 local.get $l i32.store align=4 ;; this.buf32[RESULT_R_SLOT] = r; - i32.const 2072 + i32.const 8216 local.get $r i32.store align=4 end ;; $succeed @@ -483,7 +483,7 @@ ;; const charCodes = this.buf8; ;; needleLeft += this.buf32[CHAR0_SLOT]; local.get $needleLeft - i32.const 2060 ;; CHAR0_SLOT memory address + i32.const 8204 ;; CHAR0_SLOT memory address i32.load align=4 ;; CHAR0 memory address i32.add ;; needle memory address local.tee $needleLeft @@ -559,7 +559,7 @@ br_if $fail ;; needleLeft += this.buf32[CHAR0_SLOT]; local.get $needleLeft - i32.const 2060 ;; CHAR0_SLOT memory address + i32.const 8204 ;; CHAR0_SLOT memory address i32.load align=4 ;; CHAR0 memory address i32.add ;; needle memory address local.tee $needleLeft @@ -659,7 +659,7 @@ br_if $fail ;; needleLeft += this.buf32[CHAR0_SLOT]; local.get $needleLeft - i32.const 2060 ;; CHAR0_SLOT memory address + i32.const 8204 ;; CHAR0_SLOT memory address i32.load align=4 ;; CHAR0 memory address i32.add ;; needle memory address local.tee $needleLeft From 0913d5d1b41bc7d7de0a9038799f1e36d9b88ad4 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sat, 1 Mar 2025 14:51:48 -0500 Subject: [PATCH 0646/1099] New revision for dev build --- dist/version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dist/version b/dist/version index f1b1062314c05..dafefec0a7d11 100644 --- a/dist/version +++ b/dist/version @@ -1 +1 @@ -1.62.1.4 +1.62.1.5 From 2505b9f7607f4b85817c9df80ba25e325174e943 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sat, 1 Mar 2025 14:53:48 -0500 Subject: [PATCH 0647/1099] Update changelog --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index b4178292c6fcd..2cdf73a68a1d1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,5 @@ +- [Increase URL buffer size to 8192 (from 2048)](https://github.com/gorhill/uBlock/commit/36404543e4) +- [Use onmessage/postMessage instead of BroadcastChannel in diff updater](https://github.com/gorhill/uBlock/commit/ea8853cda3) - [Improve `disable-newtab-links` scriptlet](https://github.com/gorhill/uBlock/commit/d41989e62a) - [Improve `prevent-addEventListener` scriptlet](https://github.com/gorhill/uBlock/commit/9c26a07b53) - [Fix reverse lookup of `##^responseheader(...)` filters](https://github.com/gorhill/uBlock/commit/5921e50e03) From d7a103aa1c67ab749123b6a8c8eab8c381b58738 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sat, 1 Mar 2025 15:00:44 -0500 Subject: [PATCH 0648/1099] Make Firefox dev build auto-update --- dist/firefox/updates.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dist/firefox/updates.json b/dist/firefox/updates.json index 16c4b4dcc12b8..2351d438fe746 100644 --- a/dist/firefox/updates.json +++ b/dist/firefox/updates.json @@ -3,9 +3,9 @@ "uBlock0@raymondhill.net": { "updates": [ { - "version": "1.62.1.4", + "version": "1.62.1.5", "browser_specific_settings": { "gecko": { "strict_min_version": "78.0" } }, - "update_link": "https://github.com/gorhill/uBlock/releases/download/1.62.1b4/uBlock0_1.62.1b4.firefox.signed.xpi" + "update_link": "https://github.com/gorhill/uBlock/releases/download/1.62.1b5/uBlock0_1.62.1b5.firefox.signed.xpi" } ] } From 34df044808a50080cbc4ec75b4d8d32e2372a7f1 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sat, 1 Mar 2025 16:26:46 -0500 Subject: [PATCH 0649/1099] Add filter option synonyms for `strict1p`/`strict3p` Related issue: https://github.com/uBlockOrigin/uBlock-issues/issues/3554 --- src/js/static-filtering-parser.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/js/static-filtering-parser.js b/src/js/static-filtering-parser.js index 9315460a6f8f9..a5d8b3b5790b4 100644 --- a/src/js/static-filtering-parser.js +++ b/src/js/static-filtering-parser.js @@ -222,9 +222,11 @@ export const nodeTypeFromOptionName = new Map([ [ '1p', NODE_TYPE_NET_OPTION_NAME_1P ], /* synonym */ [ 'first-party', NODE_TYPE_NET_OPTION_NAME_1P ], [ 'strict1p', NODE_TYPE_NET_OPTION_NAME_STRICT1P ], + /* synonym */ [ 'strict-first-party', NODE_TYPE_NET_OPTION_NAME_STRICT1P ], [ '3p', NODE_TYPE_NET_OPTION_NAME_3P ], /* synonym */ [ 'third-party', NODE_TYPE_NET_OPTION_NAME_3P ], [ 'strict3p', NODE_TYPE_NET_OPTION_NAME_STRICT3P ], + /* synonym */ [ 'strict-third-party', NODE_TYPE_NET_OPTION_NAME_STRICT3P ], [ 'all', NODE_TYPE_NET_OPTION_NAME_ALL ], [ 'badfilter', NODE_TYPE_NET_OPTION_NAME_BADFILTER ], [ 'cname', NODE_TYPE_NET_OPTION_NAME_CNAME ], @@ -3036,9 +3038,11 @@ export const netOptionTokenDescriptors = new Map([ [ '1p', { canNegate: true } ], /* synonym */ [ 'first-party', { canNegate: true } ], [ 'strict1p', { } ], + /* synonym */ [ 'strict-first-party', { } ], [ '3p', { canNegate: true } ], /* synonym */ [ 'third-party', { canNegate: true } ], [ 'strict3p', { } ], + /* synonym */ [ 'strict-third-party', { } ], [ 'all', { } ], [ 'badfilter', { } ], [ 'cname', { allowOnly: true } ], From 8467e1b018e48e53d82a91b9d0c42ee3c6121194 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sun, 2 Mar 2025 12:27:45 -0500 Subject: [PATCH 0650/1099] Re-wording --- RELEASE.HEAD.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/RELEASE.HEAD.md b/RELEASE.HEAD.md index 986319b72bd8c..328d4e75f307d 100644 --- a/RELEASE.HEAD.md +++ b/RELEASE.HEAD.md @@ -3,7 +3,7 @@ #### How to Install the Developer Build: - **Firefox**: Download the build from [uBlock0_%version%.firefox.signed.xpi](https://github.com/gorhill/uBlock/releases/download/%version%/uBlock0_%version%.firefox.signed.xpi). - - uBO works best on Firefox, check out [why](https://github.com/gorhill/uBlock/wiki/uBlock-Origin-works-best-on-Firefox). + - uBO works best on Gecko-based browsers, check out [why](https://github.com/gorhill/uBlock/wiki/uBlock-Origin-works-best-on-Firefox). - **Chromium**: Install directly from the [Chrome Web Store](https://chromewebstore.google.com/detail/ublock-origin-development/cgbcahbpdhpcegmbfconppldiemgcoii). From b29ac9809441c0c5d4c6f906aa50c0214389456d Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Mon, 3 Mar 2025 08:27:37 -0500 Subject: [PATCH 0651/1099] [mv3] Inject surveyor in frames Related issue: https://github.com/uBlockOrigin/uBOL-home/issues/290 --- platform/mv3/extension/js/scripting-manager.js | 1 + 1 file changed, 1 insertion(+) diff --git a/platform/mv3/extension/js/scripting-manager.js b/platform/mv3/extension/js/scripting-manager.js index 2e5c9c6fdff78..b11a2f6a68a97 100644 --- a/platform/mv3/extension/js/scripting-manager.js +++ b/platform/mv3/extension/js/scripting-manager.js @@ -209,6 +209,7 @@ function registerGeneric(context, genericDetails) { const directive = { id: 'css-generic', js, + allFrames: true, matches, excludeMatches, runAt: 'document_idle', From 1dbd280ba337e751cea3468b644fc083af4ab076 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Mon, 3 Mar 2025 11:45:54 -0500 Subject: [PATCH 0652/1099] [mv3] Convert `domain=` to `to=` for `csp=`/`permissions=` options Related discussion: https://github.com/uBlockOrigin/uBOL-home/issues/156#issuecomment-2694741399 --- platform/chromium/manifest.json | 2 +- platform/firefox/manifest.json | 2 +- platform/opera/manifest.json | 2 +- src/js/static-net-filtering.js | 18 ++++++++++++++++++ 4 files changed, 21 insertions(+), 3 deletions(-) diff --git a/platform/chromium/manifest.json b/platform/chromium/manifest.json index ba860b6e36bd7..b2945f90f6e53 100644 --- a/platform/chromium/manifest.json +++ b/platform/chromium/manifest.json @@ -89,7 +89,7 @@ }, "incognito": "split", "manifest_version": 2, - "minimum_chrome_version": "80.0", + "minimum_chrome_version": "85.0", "name": "uBlock Origin", "options_ui": { "page": "dashboard.html", diff --git a/platform/firefox/manifest.json b/platform/firefox/manifest.json index bd347e5eeadd8..b5d28c1116956 100644 --- a/platform/firefox/manifest.json +++ b/platform/firefox/manifest.json @@ -17,7 +17,7 @@ "browser_specific_settings": { "gecko": { "id": "uBlock0@raymondhill.net", - "strict_min_version": "78.0" + "strict_min_version": "79.0" }, "gecko_android": { "strict_min_version": "79.0" diff --git a/platform/opera/manifest.json b/platform/opera/manifest.json index 38015c3959979..6b9f0e015c453 100644 --- a/platform/opera/manifest.json +++ b/platform/opera/manifest.json @@ -88,7 +88,7 @@ }, "incognito": "split", "manifest_version": 2, - "minimum_opera_version": "67.0", + "minimum_opera_version": "79.0", "name": "uBlock Origin", "options_page": "dashboard.html", "permissions": [ diff --git a/src/js/static-net-filtering.js b/src/js/static-net-filtering.js index b1a56d2326fcb..e6796d89a37e0 100644 --- a/src/js/static-net-filtering.js +++ b/src/js/static-net-filtering.js @@ -4565,6 +4565,7 @@ StaticNetFilteringEngine.prototype.dnrFromCompiled = function(op, context, ...ar // Patch modifier filters for ( const rule of ruleset ) { if ( rule.__modifierType === undefined ) { continue; } + let patchDomainOption = false; switch ( rule.__modifierType ) { case 'csp': rule.action.type = 'modifyHeaders'; @@ -4576,6 +4577,7 @@ StaticNetFilteringEngine.prototype.dnrFromCompiled = function(op, context, ...ar if ( rule.__modifierAction === ALLOW_REALM ) { dnrAddRuleError(rule, `Unsupported csp exception: ${rule.__modifierValue}`); } + patchDomainOption = true; break; case 'permissions': rule.action.type = 'modifyHeaders'; @@ -4587,6 +4589,7 @@ StaticNetFilteringEngine.prototype.dnrFromCompiled = function(op, context, ...ar if ( rule.__modifierAction === ALLOW_REALM ) { dnrAddRuleError(rule, `Unsupported permissions exception: ${rule.__modifierValue}`); } + patchDomainOption = true; break; case 'redirect-rule': { let token = rule.__modifierValue; @@ -4689,6 +4692,21 @@ StaticNetFilteringEngine.prototype.dnrFromCompiled = function(op, context, ...ar dnrAddRuleError(rule, `Unsupported modifier ${rule.__modifierType}`); break; } + + // Some modifiers only work on document resources + // Related issue: https://github.com/uBlockOrigin/uBOL-home/issues/156 + if ( patchDomainOption ) { + const domains = rule.condition.initiatorDomains; + if ( Array.isArray(domains) && domains.length !== 0 ) { + rule.condition.requestDomains ||= []; + rule.condition.requestDomains.push(...domains); + } + const notDomains = rule.condition.excludedInitiatorDomains; + if ( Array.isArray(notDomains) && notDomains.length !== 0 ) { + rule.condition.excludedRequestDomains ||= []; + rule.condition.excludedRequestDomains.push(...notDomains); + } + } } return { From 987ddad1fd96f57fea0c1dd581ee27cf68020eae Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Tue, 4 Mar 2025 07:02:41 -0500 Subject: [PATCH 0653/1099] [mv3] Adjust as per feedback Related discussion: https://github.com/uBlockOrigin/uBOL-home/issues/156#issuecomment-2696060398 --- src/js/static-net-filtering.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/js/static-net-filtering.js b/src/js/static-net-filtering.js index e6796d89a37e0..b24f0ffa0f000 100644 --- a/src/js/static-net-filtering.js +++ b/src/js/static-net-filtering.js @@ -4577,7 +4577,6 @@ StaticNetFilteringEngine.prototype.dnrFromCompiled = function(op, context, ...ar if ( rule.__modifierAction === ALLOW_REALM ) { dnrAddRuleError(rule, `Unsupported csp exception: ${rule.__modifierValue}`); } - patchDomainOption = true; break; case 'permissions': rule.action.type = 'modifyHeaders'; From e636c32f2a56070f74de74ab55d1c4bfbca497f2 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Tue, 4 Mar 2025 07:12:56 -0500 Subject: [PATCH 0654/1099] Fix range parser in `prevent-setTimeout` scriptlet Related feedback: https://github.com/uBlockOrigin/uBlock-discussions/discussions/925#discussioncomment-11940830 --- src/js/resources/prevent-settimeout.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/js/resources/prevent-settimeout.js b/src/js/resources/prevent-settimeout.js index d8cfefad739d6..c137f0dc70476 100644 --- a/src/js/resources/prevent-settimeout.js +++ b/src/js/resources/prevent-settimeout.js @@ -36,7 +36,7 @@ class RangeParser { this.min = this.max = parseInt(s, 10) || 0; } if ( pos !== -1 ) { - this.max = parseInt(s.slice(1), 10) || Number.MAX_SAFE_INTEGER; + this.max = parseInt(s.slice(pos + 1), 10) || Number.MAX_SAFE_INTEGER; } } unbound() { From a483f7955fc8c9c820684c3f55c9f982a7abb2d3 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Thu, 6 Mar 2025 08:57:27 -0500 Subject: [PATCH 0655/1099] Add ability to inject scriptlets according to origin of ancestor contexts New syntax for scriptlet-based filtering: it is now possible to inject a scriptlet in a context according to whether an ancestor origin matches a specific hostname. Example: example.com>>##+js(set, iAmEmbeddedInExampleDotCom, true) The new `>>` specifier means that the scriptlet will not be injected in `example.com`, but will be injected in all direct and indirect embedded contexts in pages loaded from `example.com` The new ancestor domain specifier also works for entity directives: example.*##+js(set, iAmEmbeddedInExampleDotEffectiveTLD, true) This is an experimental feature, to be further evaluated and discussed by filter list maintainers. This is not a complete implementation, by choice. Do not open issues regarding this new syntax, the current shortcomings are known. The new syntax is being discussed internally at: https://github.com/uBlockOrigin/uBlock-discussions/discussions/244 --- src/js/benchmarks.js | 1 - src/js/cosmetic-filtering.js | 6 ++- src/js/messaging.js | 3 +- src/js/pagestore.js | 15 ++++++ src/js/scriptlet-filtering-core.js | 21 ++++++-- src/js/scriptlet-filtering.js | 3 +- src/js/static-filtering-parser.js | 84 +++++++++++++++++++----------- src/js/tab.js | 1 + src/js/traffic.js | 1 + src/js/uri-utils.js | 10 ++++ 10 files changed, 102 insertions(+), 43 deletions(-) diff --git a/src/js/benchmarks.js b/src/js/benchmarks.js index f9875694e4039..7e24d39bd4073 100644 --- a/src/js/benchmarks.js +++ b/src/js/benchmarks.js @@ -321,7 +321,6 @@ export async function benchmarkCosmeticFiltering() { frameId: undefined, hostname: '', domain: '', - entity: '', }; const options = { noSpecificCosmeticFiltering: false, diff --git a/src/js/cosmetic-filtering.js b/src/js/cosmetic-filtering.js index 9a078095c5951..663fa0df9afb2 100644 --- a/src/js/cosmetic-filtering.js +++ b/src/js/cosmetic-filtering.js @@ -23,6 +23,7 @@ import { MRUCache } from './mrucache.js'; import { StaticExtFilteringHostnameDB } from './static-ext-filtering-db.js'; +import { entityFromHostname } from './uri-utils.js'; import logger from './logger.js'; import µb from './background.js'; @@ -818,9 +819,10 @@ CosmeticFilteringEngine.prototype.retrieveSpecificSelectors = function( 3 ); // Retrieve filters with a entity-based hostname value - if ( request.entity !== '' ) { + const entity = entityFromHostname(hostname, request.domain); + if ( entity !== '' ) { this.specificFilters.retrieve( - `${hostname.slice(0, -request.domain.length)}${request.entity}`, + entity, options.noSpecificCosmeticFiltering ? discardSets : retrieveSets, 1 ); diff --git a/src/js/messaging.js b/src/js/messaging.js index 2a5e7da9529ab..668138c3b423c 100644 --- a/src/js/messaging.js +++ b/src/js/messaging.js @@ -25,7 +25,6 @@ import * as sfp from './static-filtering-parser.js'; import { domainFromHostname, domainFromURI, - entityFromDomain, hostnameFromURI, isNetworkURI, } from './uri-utils.js'; @@ -684,7 +683,7 @@ const retrieveContentScriptParameters = async function(sender, request) { request.frameId = frameId; request.hostname = hostnameFromURI(request.url); request.domain = domainFromHostname(request.hostname); - request.entity = entityFromDomain(request.domain); + request.ancestors = pageStore.getFrameAncestorDetails(frameId); const scf = response.specificCosmeticFilters = cosmeticFilteringEngine.retrieveSpecificSelectors(request, response); diff --git a/src/js/pagestore.js b/src/js/pagestore.js index 5d67f86f128bd..cfb9974753da0 100644 --- a/src/js/pagestore.js +++ b/src/js/pagestore.js @@ -535,6 +535,21 @@ const PageStore = class { return sender.frameURL; } + getFrameAncestorDetails(frameId) { + if ( frameId === 0 ) { return []; } + const out = []; + for (;;) { + const frameStore = this.getFrameStore(frameId); + if ( frameStore === null ) { break; } + const { domain, hostname } = frameStore; + if ( hostname !== undefined ) { + out.push({ domain, hostname }); + } + frameId = frameStore.parentId; + } + return out.slice(1); + } + // There is no event to tell us a specific subframe has been removed from // the main document. The code below will remove subframes which are no // longer present in the root document. Removing obsolete subframes is diff --git a/src/js/scriptlet-filtering-core.js b/src/js/scriptlet-filtering-core.js index a585f119ebf6e..89b06ca05f0d8 100644 --- a/src/js/scriptlet-filtering-core.js +++ b/src/js/scriptlet-filtering-core.js @@ -20,6 +20,7 @@ */ import { StaticExtFilteringHostnameDB } from './static-ext-filtering-db.js'; +import { entityFromHostname } from './uri-utils.js'; import { redirectEngine as reng } from './redirect-engine.js'; /******************************************************************************/ @@ -208,13 +209,23 @@ export class ScriptletFilteringEngine { $scriptlets.clear(); $exceptions.clear(); - const { hostname } = request; + const { ancestors = [], domain, hostname } = request; this.scriptletDB.retrieve(hostname, [ $scriptlets, $exceptions ]); - const entity = request.entity !== '' - ? `${hostname.slice(0, -request.domain.length)}${request.entity}` - : '*'; - this.scriptletDB.retrieve(entity, [ $scriptlets, $exceptions ], 1); + const entity = entityFromHostname(hostname, domain); + if ( entity !== '' ) { + this.scriptletDB.retrieve(entity, [ $scriptlets, $exceptions ], 1); + } else { + this.scriptletDB.retrieve('*', [ $scriptlets, $exceptions ], 1); + } + for ( const ancestor of ancestors ) { + const { domain, hostname } = ancestor; + this.scriptletDB.retrieve(`${hostname}>>`, [ $scriptlets, $exceptions ], 1); + const entity = entityFromHostname(hostname, domain); + if ( entity !== '' ) { + this.scriptletDB.retrieve(`${entity}>>`, [ $scriptlets, $exceptions ], 1); + } + } if ( $scriptlets.size === 0 ) { return; } // Wholly disable scriptlet injection? diff --git a/src/js/scriptlet-filtering.js b/src/js/scriptlet-filtering.js index 722a6fe34402b..0d127f6a56207 100644 --- a/src/js/scriptlet-filtering.js +++ b/src/js/scriptlet-filtering.js @@ -23,7 +23,6 @@ import { domainFromHostname, - entityFromDomain, hostnameFromURI, } from './uri-utils.js'; @@ -335,7 +334,7 @@ export class ScriptletFilteringEngineEx extends ScriptletFilteringEngine { url: details.url, hostname, domain, - entity: entityFromDomain(domain), + ancestors: details.ancestors, }); if ( scriptletDetails === undefined ) { contentScriptRegisterer.unregister(hostname); diff --git a/src/js/static-filtering-parser.js b/src/js/static-filtering-parser.js index a5d8b3b5790b4..4b649d7d93e5d 100644 --- a/src/js/static-filtering-parser.js +++ b/src/js/static-filtering-parser.js @@ -335,6 +335,27 @@ export const nodeNameFromNodeType = new Map([ /******************************************************************************/ +// Local constants + +const DOMAIN_CAN_USE_WILDCARD = 0b000001; +const DOMAIN_CAN_USE_ENTITY = 0b000010; +const DOMAIN_CAN_USE_SINGLE_WILDCARD = 0b000100; +const DOMAIN_CAN_BE_NEGATED = 0b001000; +const DOMAIN_CAN_BE_REGEX = 0b010000; +const DOMAIN_CAN_BE_ANCESTOR = 0b100000; + +const DOMAIN_FROM_FROMTO_LIST = DOMAIN_CAN_USE_ENTITY | + DOMAIN_CAN_BE_NEGATED | + DOMAIN_CAN_BE_REGEX; +const DOMAIN_FROM_DENYALLOW_LIST = 0; +const DOMAIN_FROM_EXT_LIST = DOMAIN_CAN_USE_ENTITY | + DOMAIN_CAN_USE_SINGLE_WILDCARD | + DOMAIN_CAN_BE_NEGATED | + DOMAIN_CAN_BE_REGEX | + DOMAIN_CAN_BE_ANCESTOR; + +/******************************************************************************/ + // Precomputed AST layouts for most common filters. const astTemplates = { @@ -1839,7 +1860,7 @@ export class AstFilterParser { const hn = match[0].replace(this.reHostnameLabel, s => { if ( this.reHasUnicodeChar.test(s) === false ) { return s; } if ( s.charCodeAt(0) === 0x2D /* - */ ) { s = '*' + s; } - return this.normalizeHostnameValue(s, 0b0001) || s; + return this.normalizeHostnameValue(s, DOMAIN_CAN_USE_WILDCARD) || s; }); normal = hn + normal.slice(match.index + match[0].length); } @@ -2018,11 +2039,11 @@ export class AstFilterParser { } switch ( nodeOptionType ) { case NODE_TYPE_NET_OPTION_NAME_DENYALLOW: - this.linkDown(next, this.parseDomainList(next, '|'), 0b00000); + this.linkDown(next, this.parseDomainList(next, '|'), DOMAIN_FROM_DENYALLOW_LIST); break; case NODE_TYPE_NET_OPTION_NAME_FROM: case NODE_TYPE_NET_OPTION_NAME_TO: - this.linkDown(next, this.parseDomainList(next, '|', 0b11010)); + this.linkDown(next, this.parseDomainList(next, '|', DOMAIN_FROM_FROMTO_LIST)); break; default: break; @@ -2054,7 +2075,7 @@ export class AstFilterParser { return this.getNodeTransform(valueNode); } - parseDomainList(parent, separator, mode = 0b00000) { + parseDomainList(parent, separator, mode = 0) { const parentBeg = this.nodes[parent+NODE_BEG_INDEX]; const parentEnd = this.nodes[parent+NODE_END_INDEX]; const containerNode = this.allocTypedNode( @@ -2128,7 +2149,7 @@ export class AstFilterParser { if ( not ) { this.addNodeFlags(parent, NODE_FLAG_IS_NEGATED); head = this.allocTypedNode(NODE_TYPE_OPTION_VALUE_NOT, beg, beg + 1); - if ( (parseDetails.mode & 0b1000) === 0 ) { + if ( (parseDetails.mode & DOMAIN_CAN_BE_NEGATED) === 0 ) { this.addNodeFlags(parent, NODE_FLAG_ERROR); } beg += 1; @@ -2173,23 +2194,29 @@ export class AstFilterParser { parseDetails.len = end - parentBeg; } - // mode bits: - // 0b00001: can use wildcard at any position - // 0b00010: can use entity-based hostnames - // 0b00100: can use single wildcard - // 0b01000: can be negated - // 0b10000: can be a regex normalizeDomainValue(node, type, modeBits) { - const s = this.getNodeString(node); + const raw = this.getNodeString(node); + const isAncestor = raw.endsWith('>>'); + if ( isAncestor ) { + if ( (modeBits & DOMAIN_CAN_BE_ANCESTOR) === 0 ) { return ''; } + } + const before = isAncestor ? raw.slice(0, -2) : raw; + let after; if ( type === 0 ) { - return this.normalizeHostnameValue(s, modeBits); + after = this.normalizeHostnameValue(before, modeBits) ?? before; + if ( after === '' ) { return ''; } + } else { + if ( (modeBits & DOMAIN_CAN_BE_REGEX) === 0 ) { return ''; } + const regex = type === 1 ? before : `/${before.slice(10, -2)}/`; + const source = this.normalizeRegexPattern(regex); + if ( source === '' ) { return ''; } + after = type === 2 || source !== regex ? `/${source}/` : before; + } + if ( isAncestor ) { + after = `${after}>>`; } - if ( (modeBits & 0b10000) === 0 ) { return ''; } - const regex = type === 1 ? s : `/${s.slice(10, -2)}/`; - const source = this.normalizeRegexPattern(regex); - if ( source === '' ) { return ''; } - if ( type === 1 && source === regex ) { return; } - return `/${source}/`; + if ( after === raw ) { return; } + return after; } parseExt(parent, anchorBeg, anchorLen) { @@ -2207,7 +2234,8 @@ export class AstFilterParser { ); this.addFlags(AST_FLAG_HAS_OPTIONS); this.addNodeToRegister(NODE_TYPE_EXT_OPTIONS, next); - this.linkDown(next, this.parseDomainList(next, ',', 0b11110)); + const down = this.parseDomainList(next, ',', DOMAIN_FROM_EXT_LIST); + this.linkDown(next, down); prev = this.linkRight(prev, next); } next = this.allocTypedNode( @@ -2800,17 +2828,11 @@ export class AstFilterParser { // Ultimately, let the browser API do the hostname normalization, after // making some other trivial checks. // - // mode bits: - // 0b00001: can use wildcard at any position - // 0b00010: can use entity-based hostnames - // 0b00100: can use single wildcard - // 0b01000: can be negated - // // returns: // undefined: no normalization needed, use original hostname // empty string: hostname is invalid // non-empty string: normalized hostname - normalizeHostnameValue(s, modeBits = 0b00000) { + normalizeHostnameValue(s, modeBits = 0) { if ( this.reHostnameAscii.test(s) ) { return; } if ( this.reBadHostnameChars.test(s) ) { return ''; } let hn = s; @@ -2818,13 +2840,13 @@ export class AstFilterParser { if ( hasWildcard ) { if ( modeBits === 0 ) { return ''; } if ( hn.length === 1 ) { - if ( (modeBits & 0b0100) === 0 ) { return ''; } + if ( (modeBits & DOMAIN_CAN_USE_SINGLE_WILDCARD) === 0 ) { return ''; } return; } - if ( (modeBits & 0b0010) !== 0 ) { + if ( (modeBits & DOMAIN_CAN_USE_ENTITY) !== 0 ) { if ( this.rePlainEntity.test(hn) ) { return; } if ( this.reIsEntity.test(hn) === false ) { return ''; } - } else if ( (modeBits & 0b0001) === 0 ) { + } else if ( (modeBits & DOMAIN_CAN_USE_WILDCARD) === 0 ) { return ''; } hn = hn.replace(/\*/g, '__asterisk__'); @@ -2841,7 +2863,7 @@ export class AstFilterParser { hn = this.punycoder.hostname.replace(/__asterisk__/g, '*'); } if ( - (modeBits & 0b0001) === 0 && ( + (modeBits & DOMAIN_CAN_USE_WILDCARD) === 0 && ( hn.charCodeAt(0) === 0x2E /* . */ || exCharCodeAt(hn, -1) === 0x2E /* . */ ) diff --git a/src/js/tab.js b/src/js/tab.js index eeba9a528abe8..1bf65912db647 100644 --- a/src/js/tab.js +++ b/src/js/tab.js @@ -928,6 +928,7 @@ vAPI.Tabs = class extends vAPI.Tabs { if ( pageStore === null ) { return; } pageStore.setFrameURL(details); if ( pageStore.getNetFilteringSwitch() ) { + details.ancestors = pageStore.getFrameAncestorDetails(frameId); scriptletFilteringEngine.injectNow(details); } } diff --git a/src/js/traffic.js b/src/js/traffic.js index df3b09714ff99..6e4b064b23123 100644 --- a/src/js/traffic.js +++ b/src/js/traffic.js @@ -1256,6 +1256,7 @@ const webRequest = { const pageStore = µb.pageStoreFromTabId(details.tabId); if ( pageStore === null ) { return; } if ( pageStore.getNetFilteringSwitch() === false ) { return; } + details.ancestors = pageStore.getFrameAncestorDetails(details.frameId); scriptletFilteringEngine.injectNow(details); }, { diff --git a/src/js/uri-utils.js b/src/js/uri-utils.js index aa9dca934ced4..aded30947a30b 100644 --- a/src/js/uri-utils.js +++ b/src/js/uri-utils.js @@ -70,6 +70,15 @@ function entityFromDomain(domain) { return pos !== -1 ? domain.slice(0, pos) + '.*' : ''; } +function entityFromHostname(hostname, domain) { + if ( domain === undefined ) { + domain = domainFromHostname(hostname); + } + const entity = entityFromDomain(domain); + if ( entity === '' ) { return ''; } + return `${hostname.slice(0, -domain.length)}${entity}` +} + function hostnameFromURI(uri) { let match = reHostnameFromCommonURL.exec(uri); if ( match !== null ) { return match[0].slice(8, -1); } @@ -164,6 +173,7 @@ export { domainFromHostname, domainFromURI, entityFromDomain, + entityFromHostname, hostnameFromNetworkURL, hostnameFromURI, isNetworkURI, From 202165867af1aa4244d2bba6c3ee4c47c80da77e Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Thu, 6 Mar 2025 09:44:32 -0500 Subject: [PATCH 0656/1099] Update changelog --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2cdf73a68a1d1..a44e00c9d3e9f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,6 @@ +- [Add ability to inject scriptlets according to origin of ancestor contexts](https://github.com/gorhill/uBlock/commit/a483f7955f) +- [Fix range parser in prevent-setTimeout scriptlet](https://github.com/gorhill/uBlock/commit/e636c32f2a) +- [Add filter option synonyms for `strict1p`/`strict3p`](https://github.com/gorhill/uBlock/commit/34df044808) - [Increase URL buffer size to 8192 (from 2048)](https://github.com/gorhill/uBlock/commit/36404543e4) - [Use onmessage/postMessage instead of BroadcastChannel in diff updater](https://github.com/gorhill/uBlock/commit/ea8853cda3) - [Improve `disable-newtab-links` scriptlet](https://github.com/gorhill/uBlock/commit/d41989e62a) From 4a5c2723ff01d26fa56bd22a50cbc0fe5e5e6b2e Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Thu, 6 Mar 2025 09:44:50 -0500 Subject: [PATCH 0657/1099] New revision for dev build --- dist/version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dist/version b/dist/version index dafefec0a7d11..40ec5833684ee 100644 --- a/dist/version +++ b/dist/version @@ -1 +1 @@ -1.62.1.5 +1.62.1.6 From fc231998b9357b74c46450856535f948145b884a Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Thu, 6 Mar 2025 09:50:37 -0500 Subject: [PATCH 0658/1099] Improve `overlay-buster` scriptlet Related discussion: https://github.com/uBlockOrigin/uBlock-issues/discussions/3470 --- src/js/resources/scriptlets.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/js/resources/scriptlets.js b/src/js/resources/scriptlets.js index 105bdb75e5b06..f182a961a33af 100755 --- a/src/js/resources/scriptlets.js +++ b/src/js/resources/scriptlets.js @@ -2103,8 +2103,8 @@ builtinScriptlets.push({ // Experimental: Generic nuisance overlay buster. // if this works well and proves to be useful, this may end up // as a stock tool in uBO's popup panel. -function overlayBuster() { - if ( window !== window.top ) { return; } +function overlayBuster(allFrames) { + if ( allFrame === '' && window !== window.top ) { return; } var tstart; var ttl = 30000; var delay = 0; From 90a99073a61f1efa370d82489e51b339fc30eff8 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Thu, 6 Mar 2025 09:54:38 -0500 Subject: [PATCH 0659/1099] Update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index a44e00c9d3e9f..946a83fa98fc7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,4 @@ +- [Improve `overlay-buster` scriptlet](https://github.com/gorhill/uBlock/commit/fc231998b9) - [Add ability to inject scriptlets according to origin of ancestor contexts](https://github.com/gorhill/uBlock/commit/a483f7955f) - [Fix range parser in prevent-setTimeout scriptlet](https://github.com/gorhill/uBlock/commit/e636c32f2a) - [Add filter option synonyms for `strict1p`/`strict3p`](https://github.com/gorhill/uBlock/commit/34df044808) From 03fb6ee05980ec02bde674abdf98ad2e58b8eb05 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Thu, 6 Mar 2025 09:59:35 -0500 Subject: [PATCH 0660/1099] Import translation work from https://crowdin.com/project/ublock --- platform/mv3/description/webstore.ar.txt | 8 +-- platform/mv3/description/webstore.nb.txt | 2 +- .../mv3/extension/_locales/nb/messages.json | 60 +++++++++---------- .../mv3/extension/_locales/sq/messages.json | 6 +- src/_locales/nb/messages.json | 6 +- src/_locales/sq/messages.json | 6 +- 6 files changed, 41 insertions(+), 47 deletions(-) diff --git a/platform/mv3/description/webstore.ar.txt b/platform/mv3/description/webstore.ar.txt index 9053a997ad161..d1fb665879d2a 100644 --- a/platform/mv3/description/webstore.ar.txt +++ b/platform/mv3/description/webstore.ar.txt @@ -9,26 +9,20 @@ يمكنك تفعيل المزيد من مجموعات القواعد من خلال زيارة صفحة الخيارات - انقر على أيقونة _الترس_ في لوحة الإشعارات. -uBOL صريح تمامًا، مما يعني أنه لا تحتاج إلى uBOL بشكل دائم لحدوث تصفية المحتوى، يتم إجراء تصفية المحتوى من خلال إضافة CSS/JS بشكل موثوق به بواسطة المتصفح نفسه بدلًا من الإضافة. - هذا يعني أن uBOL نفسه لا يستهلك موارد وحدة المعالجة المركزية/الذاكرة أثناء استمراره في حظر المحتوى. عملية عامل الخدمة في uBOL مطلوبة _فقط_ عند التفاعل مع اللوحة المنبثقة أو صفحة الخيارات. +uBOL صريح تمامًا، مما يعني أنه لا تحتاج إلى uBOL بشكل دائم لحدوث تصفية المحتوى، يتم إجراء تصفية المحتوى من خلال إضافة CSS/JS بشكل موثوق به بواسطة المتصفح نفسه بدلًا من الإضافة. هذا يعني أن uBOL نفسه لا يستهلك موارد وحدة المعالجة المركزية/الذاكرة أثناء استمراره في حظر المحتوى. لا يتطلب uBOL صلاحية واسعة «لقراءة البيانات وتعديلها» في وقت التثبيت، وبالتالي فإن قدراته محدودة مقارنة بـ uBlock Origin أو إضافات حظر الإعلانات الأخرى التي تتطلب صلاحية واسعة «قراءة البيانات وتعديلها» في وقت التثبيت. - ومع ذلك، يسمح لك uBOL "بوضوح" بمنح صلاحيات موسعة على مواقع محددة من اختيارك حتى يتمكن من التصفية بشكل أفضل على تلك المواقع باستخدام التصفية التجميلية وإضافة النص. - لمنح صلاحيات موسعة على موقع معين، افتح اللوحة المنبثقة واختر وضع التصفية إما الأمثل أو الكامل. سيحذرك المتصفح من مخاطر منح صلاحيات إضافية التي يطلبها الامتداد على الموقع الحالي، وسيتعين عليك إختيار بما إذا كنت تقبل الطلب أو ترفضه. - إذا قبلت طلب uBOL بالحصول على صلاحيات إضافية على الموقع الحالي، فستتمكن من تصفية المحتوى بشكل أفضل للموقع الحالي. - بإمكانك اختيار وضع التصفية الافتراضية من خلال صفحة خيارات uBOL. إذا اخترت الوضع الأمثل أو الكامل باعتباره الوضع الافتراضي، فستحتاج إلى منح uBOL الإذن لقراءة البيانات وتعديلها على جميع مواقع الويب. - ضع في اعتبارك أن هذا لا يزال عملًا قيد التنفيذ، هذه هي الأهداف النهائية: لا يمكنك تحديد الأذونات المستخدمة لاحقًا في التثبيت، تحديدك للأذونات سيكون خلال زيارتك لكل موقع. diff --git a/platform/mv3/description/webstore.nb.txt b/platform/mv3/description/webstore.nb.txt index 756166aba704d..c45f9a527c080 100644 --- a/platform/mv3/description/webstore.nb.txt +++ b/platform/mv3/description/webstore.nb.txt @@ -7,7 +7,7 @@ Standardregelsettet tilsvarer standardfiltersettet til uBlock Origin: - EasyPrivacy - Peter Lowe’s Ad and tracking server list -You can enable more rulesets by visiting the options page -- click the _Cogs_ icon in the popup panel. +Du kan legge til flere regelsett ved å gå til innstillingssiden -- klikk _Tannhjul_-ikonet i oppsprettspanelet. uBOL er fullstendig deklarativ, noe som betyr at det ikke er behov for en permanent uBOL-prosess for at filtreringen skal skje, og CSS/JS-injeksjonsbasert innholdsfiltrering utføres pålitelig av nettleseren selv i stedet for av utvidelsen. Dette betyr at uBOL selv ikke bruker CPU/minneressurser mens innholdsblokkering pågår -- uBOL's service worker-prosess kreves _bare_ når du samhandler med oppsprettspanelet eller innstillingssidene. diff --git a/platform/mv3/extension/_locales/nb/messages.json b/platform/mv3/extension/_locales/nb/messages.json index c7571d0ea991b..52a4bd4d9e448 100644 --- a/platform/mv3/extension/_locales/nb/messages.json +++ b/platform/mv3/extension/_locales/nb/messages.json @@ -32,7 +32,7 @@ "description": "Label in the popup panel for the current filtering mode" }, "popupTipReport": { - "message": "Report an issue on this website", + "message": "Rapporter et problem på dette nettstedet", "description": "Tooltip used for the 'chat' icon in the panel" }, "popupTipDashboard": { @@ -104,67 +104,67 @@ "description": "Shown in the About pane" }, "supportS6H": { - "message": "Report a filter issue", + "message": "Rapporter om filterproblem", "description": "Header of 'Report a filter issue' section in Support pane" }, "supportS3P1": { - "message": "Report filter issues with specific websites to the uBlockOrigin/uAssets issue tracker. Requires a GitHub account.", + "message": "Rapporter filterproblemer med bestemte nettsteder til uBlockOrigin/uAssets problemsporing. Krever en GitHub-konto.", "description": "First paragraph of 'Filter issues' section in Support pane" }, "supportS6P1S1": { - "message": "To avoid burdening volunteers with duplicate reports, please verify that the issue has not already been reported. Note: clicking the button will cause the page's origin to be sent to GitHub.", + "message": "For å unngå å belaste de frivillige med dobbeltrapporter, må du kontrollere at problemet ikke allerede har blitt rapportert. Noter: ved å klikke på knappen vil sidens opprinnelse bli sendt til GitHub..", "description": "A paragraph in the filter issue reporter section" }, "supportFindSpecificButton": { - "message": "Find similar reports on GitHub", + "message": "Finn lignende rapporter på GitHub", "description": "A clickable link in the filter issue reporter section" }, "supportS6URL": { - "message": "Address of the webpage:", + "message": "Nettsidens adresse:", "description": "Label for the URL of the page" }, "supportS6Select1": { - "message": "The webpage…", + "message": "Nettsiden...", "description": "Label for widget to select type of issue" }, "supportS6Select1Option0": { - "message": "-- Pick an entry --", + "message": "-- Velg en oppføring --", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option1": { - "message": "Shows ads or ad leftovers", + "message": "Viser reklame eller reklamerester", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option2": { - "message": "Has overlays or other nuisances", + "message": "Har overlegg eller andre ulemper", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option3": { - "message": "Detects uBO Lite", + "message": "Oppdager uBO Lite", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option4": { - "message": "Has privacy-related issues", + "message": "Har personvernrelaterte problemer", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option5": { - "message": "Malfunctions when uBO Lite is enabled", + "message": "Feilfunksjoner når uBO Lite er aktivert", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option6": { - "message": "Opens unwanted tabs or windows", + "message": "Åpner uønskede faner eller vinduer", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option7": { - "message": "Leads to badware, phishing", + "message": "Fører til skadelig programvare og/eller phishing", "description": "An entry in the widget used to select the type of issue" }, "supportS6Checkbox1": { - "message": "Label the webpage as “NSFW” (“Not Safe For Work”)", + "message": "Merk nettsiden som «NSFW» (“Not Safe For Work”)", "description": "A checkbox to use for NSFW sites" }, "supportReportSpecificButton": { - "message": "Create new report on GitHub", + "message": "Opprett ny rapport på GitHub", "description": "Text for button which open an external webpage in Support pane" }, "firstRunSectionLabel": { @@ -216,7 +216,7 @@ "description": "A short description for the editable field which lists trusted sites" }, "noFilteringModePlaceholder": { - "message": "[hostnames only]\nexample.com\ngames.example\n...", + "message": "[kun vertsnavn]\nexample.com\ngames.example\n...", "description": "Default text for in edit field" }, "behaviorSectionLabel": { @@ -232,51 +232,51 @@ "description": "Label for a checkbox in the options page" }, "enableStrictBlockLabel": { - "message": "Enable strict blocking", + "message": "Aktiver streng blokkering", "description": "Label for a checkbox in the options page" }, "enableStrictBlockLegend": { - "message": "Navigation to potentially undesirable sites will be blocked, and you will be offered the option to proceed.", + "message": "Navigering til potensielt uønskede nettsteder blir blokkert, og du får tilbud om å fortsette.", "description": "Label for a checkbox in the options page" }, "findListsPlaceholder": { - "message": "Find lists", + "message": "Finn lister", "description": "Placeholder for the input field used to find lists" }, "strictblockTitle": { - "message": "Page blocked", + "message": "Side blokkert", "description": "Webpage title for the strict-blocked page" }, "strictblockSentence1": { - "message": "uBO Lite has prevented the following page from loading:", + "message": "uBO Lite har forhindret innlasting av følgende side:", "description": "Sentence used in the strict-blocked page" }, "strictblockReasonSentence1": { - "message": "The page was blocked because of a matching filter in {{listname}}.", + "message": "Siden ble blokkert på grunn av et matchende filter i {{listname}}.", "description": "Text informing about what is causing the page to be blocked" }, "strictblockRedirectSentence1": { - "message": "The blocked page wants to redirect to another site. If you choose to proceed, you will navigate directly to: {{url}}", + "message": "Den blokkerte siden ønsker å omdirigere til et annet nettsted. Hvis du velger å fortsette, vil du navigere direkte til: {{url}}", "description": "Text warning about an incoming redirect" }, "strictblockNoParamsPrompt": { - "message": "without parameters", + "message": "uten parametere", "description": "Label to be used for the parameter-less URL" }, "strictblockBack": { - "message": "Go back", + "message": "Gå tilbake", "description": "A button to go back to the previous webpage" }, "strictblockClose": { - "message": "Close this window", + "message": "Lukk dette vinduet", "description": "A button to close the current tab" }, "strictblockDontWarn": { - "message": "Don't warn me again about this site", + "message": "Ikke varsle igjen om dette nettstedet", "description": "Label for checkbox in document-blocked page" }, "strictblockProceed": { - "message": "Proceed", + "message": "Fortsett", "description": "A button to navigate to the blocked page" } } diff --git a/platform/mv3/extension/_locales/sq/messages.json b/platform/mv3/extension/_locales/sq/messages.json index 9019b355488de..2db5f40998339 100644 --- a/platform/mv3/extension/_locales/sq/messages.json +++ b/platform/mv3/extension/_locales/sq/messages.json @@ -112,11 +112,11 @@ "description": "First paragraph of 'Filter issues' section in Support pane" }, "supportS6P1S1": { - "message": "Verifikoni a është raportuar më parë problemi që të mos i lodhni vullnetarët e tjerë me të njëjtat gjëra.", + "message": "Verifikoni a është raportuar edhe më parë që të mos i lodhni vullnetarët e tjerë me të njëjtat probleme. Shënim: kur klikoni butonin, origjina e faqes do të dërgohet në GitHub.", "description": "A paragraph in the filter issue reporter section" }, "supportFindSpecificButton": { - "message": "Gjej raporte të ngjashme", + "message": "Gjej raporte të ngjashme në GitHub", "description": "A clickable link in the filter issue reporter section" }, "supportS6URL": { @@ -164,7 +164,7 @@ "description": "A checkbox to use for NSFW sites" }, "supportReportSpecificButton": { - "message": "Krijoj raport të ri", + "message": "Krijoj raport të ri në GitHub", "description": "Text for button which open an external webpage in Support pane" }, "firstRunSectionLabel": { diff --git a/src/_locales/nb/messages.json b/src/_locales/nb/messages.json index 912dbf4d127b6..a9ce75ecedeb4 100644 --- a/src/_locales/nb/messages.json +++ b/src/_locales/nb/messages.json @@ -484,7 +484,7 @@ "description": "Filter lists section name" }, "3pGroupSocial": { - "message": "Sosiale mediers moduler", + "message": "Sosiale moduler", "description": "Filter lists section name" }, "3pGroupCookies": { @@ -1012,7 +1012,7 @@ "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option7": { - "message": "Leads to badware, phishing", + "message": "Fører til skadelig programvare og/eller phishing", "description": "An entry in the widget used to select the type of issue" }, "supportS6Checkbox1": { @@ -1192,7 +1192,7 @@ "description": "Button text to navigate to the blocked page" }, "docblockedRedirectPrompt": { - "message": "The blocked page wants to redirect to another site. If you choose to proceed, you will navigate directly to: {{url}}", + "message": "Den blokkerte siden ønsker å omdirigere til et annet nettsted. Hvis du velger å fortsette, vil du navigere direkte til: {{url}}", "description": "Text warning about an incoming redirect" }, "cloudPush": { diff --git a/src/_locales/sq/messages.json b/src/_locales/sq/messages.json index 8fa200e0bb16b..4a5b3aebe6712 100644 --- a/src/_locales/sq/messages.json +++ b/src/_locales/sq/messages.json @@ -900,11 +900,11 @@ "description": "Text for button which open an external webpage in Support pane" }, "supportReportSpecificButton": { - "message": "Krijoj raport të ri", + "message": "Krijoj raport të ri në GitHub", "description": "Text for button which open an external webpage in Support pane" }, "supportFindSpecificButton": { - "message": "Gjej raporte të ngjashme", + "message": "Gjej raporte të ngjashme në GitHub", "description": "A clickable link in the filter issue reporter section" }, "supportS1H": { @@ -964,7 +964,7 @@ "description": "Header of 'Report a filter issue' section in Support pane" }, "supportS6P1S1": { - "message": "Verifikoni a është raportuar më parë problemi që të mos i lodhni vullnetarët e tjerë me të njëjtat gjëra.", + "message": "Verifikoni a është raportuar edhe më parë që të mos i lodhni vullnetarët e tjerë me të njëjtat probleme. Shënim: kur klikoni butonin, origjina e faqes do të dërgohet në GitHub.", "description": "A paragraph in the filter issue reporter section" }, "supportS6P2S1": { From 83df04a53d12be169fbca4aa1046548f27834a2a Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Thu, 6 Mar 2025 10:31:21 -0500 Subject: [PATCH 0661/1099] Make Firefox dev build auto-update --- dist/firefox/updates.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/dist/firefox/updates.json b/dist/firefox/updates.json index 2351d438fe746..0d6692f428d26 100644 --- a/dist/firefox/updates.json +++ b/dist/firefox/updates.json @@ -3,9 +3,9 @@ "uBlock0@raymondhill.net": { "updates": [ { - "version": "1.62.1.5", - "browser_specific_settings": { "gecko": { "strict_min_version": "78.0" } }, - "update_link": "https://github.com/gorhill/uBlock/releases/download/1.62.1b5/uBlock0_1.62.1b5.firefox.signed.xpi" + "version": "1.62.1.6", + "browser_specific_settings": { "gecko": { "strict_min_version": "79.0" } }, + "update_link": "https://github.com/gorhill/uBlock/releases/download/1.62.1b6/uBlock0_1.62.1b6.firefox.signed.xpi" } ] } From 31d82c494b196a37a8f30765b465acbfa4871f34 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Thu, 6 Mar 2025 14:15:02 -0500 Subject: [PATCH 0662/1099] Fix typo in variable name --- src/js/resources/scriptlets.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/js/resources/scriptlets.js b/src/js/resources/scriptlets.js index f182a961a33af..f370e50b29001 100755 --- a/src/js/resources/scriptlets.js +++ b/src/js/resources/scriptlets.js @@ -2104,7 +2104,7 @@ builtinScriptlets.push({ // if this works well and proves to be useful, this may end up // as a stock tool in uBO's popup panel. function overlayBuster(allFrames) { - if ( allFrame === '' && window !== window.top ) { return; } + if ( allFrames === '' && window !== window.top ) { return; } var tstart; var ttl = 30000; var delay = 0; From 536f0fba25b442a04d762f91e6b57e2ab796a212 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Thu, 6 Mar 2025 17:39:15 -0500 Subject: [PATCH 0663/1099] [mv3] Add test suite list to available rulesets Microsoft Edge for Android requires a "link to the core function test cases for your Edge Android extension" to verify that the extension function properly. --- platform/mv3/make-rulesets.js | 18 ++++++++++++++++-- platform/mv3/scriptlets/scriptlet.template.js | 11 +++++++---- 2 files changed, 23 insertions(+), 6 deletions(-) diff --git a/platform/mv3/make-rulesets.js b/platform/mv3/make-rulesets.js index 9c62718eef583..7fb24351b5353 100644 --- a/platform/mv3/make-rulesets.js +++ b/platform/mv3/make-rulesets.js @@ -233,7 +233,10 @@ async function fetchList(assetDetails) { continue; } fetchedURLs.add(part.url); - if ( part.url.startsWith('https://ublockorigin.github.io/uAssets/filters/') ) { + if ( + assetDetails.trustedSource || + part.url.startsWith('https://ublockorigin.github.io/uAssets/filters/') + ) { newParts.push(`!#trusted on ${secret}`); } newParts.push( @@ -248,7 +251,7 @@ async function fetchList(assetDetails) { return { url, content }; } } - log(`No valid content for ${details.name}`, false); + log(`No valid content for ${url}`, false); return { url, content: '' }; }) ); @@ -1464,6 +1467,17 @@ async function main() { homeURL: 'https://github.com/StevenBlack/hosts#readme', }); + await rulesetFromURLs({ + id: 'ubol-tests', + name: 'uBO Lite Test Filters', + enabled: false, + trusted: true, + urls: [ 'https://ublockorigin.github.io/uBOL-home/tests/test-filters.txt' ], + homeURL: 'https://ublockorigin.github.io/uBOL-home/tests/test-filters.html', + filters: [ + ], + }); + // Regional rulesets const excludedLists = [ 'ara-0', diff --git a/platform/mv3/scriptlets/scriptlet.template.js b/platform/mv3/scriptlets/scriptlet.template.js index 4cef174f6b2f2..f43fb6ad2c332 100644 --- a/platform/mv3/scriptlets/scriptlet.template.js +++ b/platform/mv3/scriptlets/scriptlet.template.js @@ -55,15 +55,18 @@ const hnParts = []; try { let origin = document.location.origin; if ( origin === 'null' ) { - const origins = document.location.ancestorOrigins; + const origins = document.location.ancestorOrigins || []; for ( let i = 0; i < origins.length; i++ ) { origin = origins[i]; if ( origin !== 'null' ) { break; } } } - const pos = origin.lastIndexOf('://'); - if ( pos === -1 ) { return; } - hnParts.push(...origin.slice(pos+3).split('.')); + const beg = origin.lastIndexOf('://'); + if ( beg === -1 ) { return; } + let hn = origin.slice(beg+3) + const end = hn.indexOf(':'); + if ( end !== -1 ) { hn = hn.slice(0, end); } + hnParts.push(...hn.split('.')); } catch { } const hnpartslen = hnParts.length; From d006fd06e7bb82c1a0d2c60e33163c8cc73036af Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Fri, 7 Mar 2025 17:04:02 -0500 Subject: [PATCH 0664/1099] [mv3] Add support for ancestor context syntax in scriptlets Related commit: https://github.com/gorhill/uBlock/commit/a483f7955fc8c9c820684c3f55c9f982a7abb2d3 --- platform/mv3/make-rulesets.js | 4 +- platform/mv3/make-scriptlets.js | 37 ++--- platform/mv3/scriptlets/scriptlet.template.js | 144 +++++++----------- 3 files changed, 78 insertions(+), 107 deletions(-) diff --git a/platform/mv3/make-rulesets.js b/platform/mv3/make-rulesets.js index 7fb24351b5353..d6efcd77ef05d 100644 --- a/platform/mv3/make-rulesets.js +++ b/platform/mv3/make-rulesets.js @@ -1526,8 +1526,8 @@ async function main() { await rulesetFromURLs({ id: 'est-0', - group: 'regions', - lang: 'et', + group: 'regions', + lang: 'et', name: '🇪🇪ee: Eesti saitidele kohandatud filter', enabled: false, urls: [ 'https://ubol-et.adblock.ee/list.txt' ], diff --git a/platform/mv3/make-scriptlets.js b/platform/mv3/make-scriptlets.js index c2fc6d69fd13a..cf356c5ea2160 100644 --- a/platform/mv3/make-scriptlets.js +++ b/platform/mv3/make-scriptlets.js @@ -97,8 +97,9 @@ export function compile(assetDetails, details) { world: resourceEntry.world, args: new Map(), hostnames: new Map(), - entities: new Map(), exceptions: new Map(), + hasEntities: false, + hasAncestors: false, matches: new Set(), }); } @@ -109,23 +110,21 @@ export function compile(assetDetails, details) { const iArgs = scriptletDetails.args.get(argsToken); if ( details.matches ) { for ( const hn of details.matches ) { - if ( hn.endsWith('.*') ) { + const isEntity = hn.endsWith('.*') || hn.endsWith('.*>>'); + scriptletDetails.hasEntities ||= isEntity; + const isAncestor = hn.endsWith('>>') + scriptletDetails.hasAncestors ||= isAncestor; + if ( isEntity || isAncestor ) { scriptletDetails.matches.clear(); scriptletDetails.matches.add('*'); - const entity = hn.slice(0, -2); - if ( scriptletDetails.entities.has(entity) === false ) { - scriptletDetails.entities.set(entity, new Set()); - } - scriptletDetails.entities.get(entity).add(iArgs); - } else { - if ( scriptletDetails.matches.has('*') === false ) { - scriptletDetails.matches.add(hn); - } - if ( scriptletDetails.hostnames.has(hn) === false ) { - scriptletDetails.hostnames.set(hn, new Set()); - } - scriptletDetails.hostnames.get(hn).add(iArgs); } + if ( scriptletDetails.matches.has('*') === false ) { + scriptletDetails.matches.add(hn); + } + if ( scriptletDetails.hostnames.has(hn) === false ) { + scriptletDetails.hostnames.set(hn, new Set()); + } + scriptletDetails.hostnames.get(hn).add(iArgs); } } else { scriptletDetails.matches.add('*'); @@ -172,8 +171,12 @@ export async function commit(rulesetId, path, writeFn) { JSON.stringify(patchHnMap(details.hostnames)) ); content = safeReplace(content, - 'self.$entitiesMap$', - JSON.stringify(patchHnMap(details.entities)) + 'self.$hasEntities$', + JSON.stringify(details.hasEntities) + ); + content = safeReplace(content, + 'self.$hasAncestors$', + JSON.stringify(details.hasAncestors) ); content = safeReplace(content, 'self.$exceptionsMap$', diff --git a/platform/mv3/scriptlets/scriptlet.template.js b/platform/mv3/scriptlets/scriptlet.template.js index f43fb6ad2c332..fb5ace81870f0 100644 --- a/platform/mv3/scriptlets/scriptlet.template.js +++ b/platform/mv3/scriptlets/scriptlet.template.js @@ -20,129 +20,97 @@ */ -/* eslint-disable indent */ - // ruleset: $rulesetId$ // Important! // Isolate from global scope // Start of local scope -(( ) => { +(function uBOL_$scriptletName$() { /******************************************************************************/ -// Start of code to inject -const uBOL_$scriptletName$ = function() { +function $scriptletName$(){} -const scriptletGlobals = {}; // eslint-disable-line +/******************************************************************************/ +const scriptletGlobals = {}; // eslint-disable-line const argsList = self.$argsList$; - const hostnamesMap = new Map(self.$hostnamesMap$); - -const entitiesMap = new Map(self.$entitiesMap$); - const exceptionsMap = new Map(self.$exceptionsMap$); +const hasEntities = self.$hasEntities$; +const hasAncestors = self.$hasAncestors$; -/******************************************************************************/ - -function $scriptletName$(){} - -/******************************************************************************/ - -const hnParts = []; -try { - let origin = document.location.origin; - if ( origin === 'null' ) { - const origins = document.location.ancestorOrigins || []; - for ( let i = 0; i < origins.length; i++ ) { - origin = origins[i]; - if ( origin !== 'null' ) { break; } +const collectArgIndices = (hn, map, out) => { + let argsIndices = map.get(hn); + if ( argsIndices === undefined ) { return; } + if ( typeof argsIndices !== 'number' ) { + for ( const argsIndex of argsIndices ) { + out.add(argsIndex); } + } else { + out.add(argsIndices); } - const beg = origin.lastIndexOf('://'); - if ( beg === -1 ) { return; } - let hn = origin.slice(beg+3) - const end = hn.indexOf(':'); - if ( end !== -1 ) { hn = hn.slice(0, end); } - hnParts.push(...hn.split('.')); -} catch { -} -const hnpartslen = hnParts.length; -if ( hnpartslen === 0 ) { return; } - -const todoIndices = new Set(); -const tonotdoIndices = []; +}; -// Exceptions -if ( exceptionsMap.size !== 0 ) { +const indicesFromHostname = (hostname, suffix = '') => { + const hnParts = hostname.split('.'); + const hnpartslen = hnParts.length; + if ( hnpartslen === 0 ) { return; } for ( let i = 0; i < hnpartslen; i++ ) { - const hn = hnParts.slice(i).join('.'); - const excepted = exceptionsMap.get(hn); - if ( excepted ) { tonotdoIndices.push(...excepted); } + const hn = `${hnParts.slice(i).join('.')}${suffix}`; + collectArgIndices(hn, hostnamesMap, todoIndices); + collectArgIndices(hn, exceptionsMap, tonotdoIndices); } - exceptionsMap.clear(); -} - -// Hostname-based -if ( hostnamesMap.size !== 0 ) { - const collectArgIndices = hn => { - let argsIndices = hostnamesMap.get(hn); - if ( argsIndices === undefined ) { return; } - if ( typeof argsIndices === 'number' ) { argsIndices = [ argsIndices ]; } - for ( const argsIndex of argsIndices ) { - if ( tonotdoIndices.includes(argsIndex) ) { continue; } - todoIndices.add(argsIndex); + if ( hasEntities ) { + const n = hnpartslen - 1; + for ( let i = 0; i < n; i++ ) { + for ( let j = n; j > i; j-- ) { + const en = `${hnParts.slice(i,j).join('.')}.*${suffix}`; + collectArgIndices(en, hostnamesMap, todoIndices); + collectArgIndices(en, exceptionsMap, tonotdoIndices); + } } - }; - for ( let i = 0; i < hnpartslen; i++ ) { - const hn = hnParts.slice(i).join('.'); - collectArgIndices(hn); } - collectArgIndices('*'); - hostnamesMap.clear(); -} +}; -// Entity-based -if ( entitiesMap.size !== 0 ) { - const n = hnpartslen - 1; - for ( let i = 0; i < n; i++ ) { - for ( let j = n; j > i; j-- ) { - const en = hnParts.slice(i,j).join('.'); - let argsIndices = entitiesMap.get(en); - if ( argsIndices === undefined ) { continue; } - if ( typeof argsIndices === 'number' ) { argsIndices = [ argsIndices ]; } - for ( const argsIndex of argsIndices ) { - if ( tonotdoIndices.includes(argsIndex) ) { continue; } - todoIndices.add(argsIndex); - } - } +const entries = (( ) => { + const docloc = document.location; + const origins = [ docloc.origin ]; + if ( docloc.ancestorOrigins ) { + origins.push(...docloc.ancestorOrigins); + } + return origins.map((origin, i) => { + const beg = origin.lastIndexOf('://'); + if ( beg === -1 ) { return; } + const hn = origin.slice(beg+3) + const end = hn.indexOf(':'); + return { hn: end === -1 ? hn : hn.slice(0, end), i }; + }).filter(a => a !== undefined); +})(); +if ( entries.length === 0 ) { return; } + +const todoIndices = new Set(); +const tonotdoIndices = new Set(); + +indicesFromHostname(entries[0].hn); +if ( hasAncestors ) { + for ( const entry of entries ) { + if ( entry.i === 0 ) { continue; } + indicesFromHostname(entry.hn, '>>'); } - entitiesMap.clear(); } // Apply scriplets for ( const i of todoIndices ) { + if ( tonotdoIndices.has(i) ) { continue; } try { $scriptletName$(...argsList[i]); } catch { } } -argsList.length = 0; - -/******************************************************************************/ - -}; -// End of code to inject - -/******************************************************************************/ - -uBOL_$scriptletName$(); /******************************************************************************/ // End of local scope })(); -/******************************************************************************/ - void 0; From 2b93a9128e3be5e6e9af7a1723c19cc3aebb99c7 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Fri, 7 Mar 2025 17:06:21 -0500 Subject: [PATCH 0665/1099] Update changelog --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 946a83fa98fc7..65bec0b8c5fcb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,7 +10,7 @@ - [Improve `evaldata-prune` scriptlet](https://github.com/gorhill/uBlock/commit/9bb1a2baaf) - [Comply with Mozilla's "User Consent and Control"](https://github.com/gorhill/uBlock/commit/344539d793) - [Improve `noeval-if` scriptlet](https://github.com/gorhill/uBlock/commit/0df7faffac) -- [Add "closed","next", "mandatory", "agree/disagree" values to `trusted-set-cookie` scriptlet](https://github.com/gorhill/uBlock/commit/35a47d674b) (by @ryanbr) +- [Add "closed","next", "mandatory", "agree/disagree" values to `set-cookie` scriptlet](https://github.com/gorhill/uBlock/commit/35a47d674b) (by @ryanbr) - [Add `decline` value to `set-cookie` scriptlet](https://github.com/gorhill/uBlock/commit/4b12247da1) - [Improve `abort-on-stack-trace` scriptlet](https://github.com/gorhill/uBlock/commit/b617926c1c) - [Improve `href-sanitizer` scriptlet](https://github.com/gorhill/uBlock/commit/551c6bc6eb) From c569f663ee3cef7284990c43e86bd01b85d7201a Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sat, 8 Mar 2025 10:07:33 -0500 Subject: [PATCH 0666/1099] Add scripts to build for MV3 Edge --- tools/make-edge.mjs | 54 +++++++++++++++++++++++++++++++++++++++++++++ tools/make-edge.sh | 24 ++++++++++++++++++++ 2 files changed, 78 insertions(+) create mode 100644 tools/make-edge.mjs create mode 100755 tools/make-edge.sh diff --git a/tools/make-edge.mjs b/tools/make-edge.mjs new file mode 100644 index 0000000000000..45bf7d7d7f827 --- /dev/null +++ b/tools/make-edge.mjs @@ -0,0 +1,54 @@ +/******************************************************************************* + + uBlock Origin Lite - a comprehensive, MV3-compliant content blocker + Copyright (C) 2022-present Raymond Hill + + 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/}. + + Home: https://github.com/gorhill/uBlock +*/ + +'use strict'; + +/******************************************************************************/ + +import fs from 'fs/promises'; + +/******************************************************************************/ + +async function main() { + const manifestPath = 'dist/build/uBOLite.edge/manifest.json'; + + // Get manifest content + const manifest = await fs.readFile(manifestPath, { encoding: 'utf8' + }).then(text => + JSON.parse(text) + ); + + // https://learn.microsoft.com/answers/questions/918426/cant-update-extension-with-declarative-net-request + // Set all ruleset path to package root + for ( const ruleset of manifest.declarative_net_request.rule_resources ) { + const pos = ruleset.path.lastIndexOf('/'); + if ( pos === -1 ) { continue; } + ruleset.path = ruleset.path.slice(pos + 1); + } + // Commit changes + await fs.writeFile(manifestPath, + JSON.stringify(manifest, null, 2) + '\n' + ); +} + +main(); + +/******************************************************************************/ diff --git a/tools/make-edge.sh b/tools/make-edge.sh new file mode 100755 index 0000000000000..c5c4ff4c4ad00 --- /dev/null +++ b/tools/make-edge.sh @@ -0,0 +1,24 @@ +#!/usr/bin/env bash +# +# This script assumes a linux environment + +set -e + +echo "*** uBOLite.edge: Creating web store package" + +DES=dist/build/uBOLite.edge +rm -rf $DES +mkdir -p $DES + +echo "*** uBOLite.edge: Copying reference chromium-based files" +cp -R dist/build/uBOLite.chromium/* $DES/ + +# Edge store requires that all DNR rulesets are at the root of the package +# https://learn.microsoft.com/answers/questions/918426/cant-update-extension-with-declarative-net-request +echo "*** uBOLite.edge: Modify reference implementation for Edge compatibility" +mv $DES/rulesets/main/* $DES/ +rmdir $DES/rulesets/main +# Patch manifest.json +node tools/make-edge.mjs + +echo "*** uBOLite.edge: Package done." From e102a5ee06c95f25aed0c327d3be1f6f5c8ccaf2 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sat, 8 Mar 2025 10:43:59 -0500 Subject: [PATCH 0667/1099] Use "make mv3-edge" to build Edge package --- Makefile | 14 ++++----- tools/make-edge.sh | 24 --------------- tools/make-mv3.sh | 76 ++++++++++++++++++++++++---------------------- 3 files changed, 47 insertions(+), 67 deletions(-) delete mode 100755 tools/make-edge.sh diff --git a/Makefile b/Makefile index 826dd546c68fe..773c93f5c4a1b 100644 --- a/Makefile +++ b/Makefile @@ -2,12 +2,13 @@ run_options := $(filter-out $@,$(MAKECMDGOALS)) .PHONY: all clean cleanassets test lint chromium opera firefox npm dig \ - mv3 mv3-quick mv3-chromium mv3-firefox \ + mv3-chromium mv3-firefox mv3-edge \ compare maxcost medcost mincost modifiers record wasm sources := $(wildcard assets/* assets/*/* dist/version src/* src/*/* src/*/*/* src/*/*/*/*) platform := $(wildcard platform/* platform/*/* platform/*/*/* platform/*/*/*/* platform/*/*/*/*/*) assets := dist/build/uAssets +mv3-assets := dist/build/mv3-data/* all: chromium firefox npm @@ -61,21 +62,20 @@ dig: dist/build/uBlock0.dig dig-snfe: dig cd dist/build/uBlock0.dig && npm run snfe $(run_options) -dist/build/uBOLite.chromium: tools/make-mv3.sh $(sources) $(platform) +dist/build/uBOLite.chromium: tools/make-mv3.sh $(sources) $(platform) $(mv3-assets) tools/make-mv3.sh chromium mv3-chromium: dist/build/uBOLite.chromium -dist/build/uBOLite.firefox: tools/make-mv3.sh $(sources) $(platform) +dist/build/uBOLite.firefox: tools/make-mv3.sh $(sources) $(platform) $(mv3-assets) tools/make-mv3.sh firefox mv3-firefox: dist/build/uBOLite.firefox -mv3-quick: tools/make-mv3.sh $(sources) $(platform) - tools/make-mv3.sh quick +dist/build/uBOLite.edge: tools/make-mv3.sh tools/make-edge.mjs $(sources) $(platform) $(mv3-assets) + tools/make-mv3.sh edge -mv3-full: tools/make-mv3.sh $(sources) $(platform) - tools/make-mv3.sh full +mv3-edge: dist/build/uBOLite.edge dist/build/uAssets: tools/pull-assets.sh diff --git a/tools/make-edge.sh b/tools/make-edge.sh deleted file mode 100755 index c5c4ff4c4ad00..0000000000000 --- a/tools/make-edge.sh +++ /dev/null @@ -1,24 +0,0 @@ -#!/usr/bin/env bash -# -# This script assumes a linux environment - -set -e - -echo "*** uBOLite.edge: Creating web store package" - -DES=dist/build/uBOLite.edge -rm -rf $DES -mkdir -p $DES - -echo "*** uBOLite.edge: Copying reference chromium-based files" -cp -R dist/build/uBOLite.chromium/* $DES/ - -# Edge store requires that all DNR rulesets are at the root of the package -# https://learn.microsoft.com/answers/questions/918426/cant-update-extension-with-declarative-net-request -echo "*** uBOLite.edge: Modify reference implementation for Edge compatibility" -mv $DES/rulesets/main/* $DES/ -rmdir $DES/rulesets/main -# Patch manifest.json -node tools/make-edge.mjs - -echo "*** uBOLite.edge: Package done." diff --git a/tools/make-mv3.sh b/tools/make-mv3.sh index 60eba71ae476d..c985ae154df88 100755 --- a/tools/make-mv3.sh +++ b/tools/make-mv3.sh @@ -11,9 +11,6 @@ PLATFORM="chromium" for i in "$@"; do case $i in - quick) - QUICK="yes" - ;; full) FULL="yes" ;; @@ -23,6 +20,9 @@ for i in "$@"; do chromium) PLATFORM="chromium" ;; + edge) + PLATFORM="edge" + ;; uBOLite_+([0-9]).+([0-9]).+([0-9]).+([0-9])) TAGNAME="$i" FULL="yes" @@ -39,9 +39,7 @@ echo "BEFORE=$BEFORE" DES="dist/build/uBOLite.$PLATFORM" -if [ "$QUICK" != "yes" ]; then - rm -rf $DES -fi +rm -rf $DES mkdir -p $DES cd $DES @@ -95,38 +93,44 @@ cp platform/mv3/extension/img/* "$DES"/img/ cp -R platform/mv3/extension/_locales "$DES"/ cp platform/mv3/README.md "$DES/" -if [ "$QUICK" != "yes" ]; then - echo "*** uBOLite.mv3: Generating rulesets" - TMPDIR=$(mktemp -d) - mkdir -p "$TMPDIR" - if [ "$PLATFORM" = "chromium" ]; then - cp platform/mv3/chromium/manifest.json "$DES"/ - elif [ "$PLATFORM" = "firefox" ]; then - cp platform/mv3/firefox/manifest.json "$DES"/ - fi - ./tools/make-nodejs.sh "$TMPDIR" - cp platform/mv3/package.json "$TMPDIR"/ - cp platform/mv3/*.js "$TMPDIR"/ - cp platform/mv3/*.mjs "$TMPDIR"/ - cp platform/mv3/extension/js/utils.js "$TMPDIR"/js/ - cp -R "$UBO_DIR"/src/js/resources "$TMPDIR"/js/ - cp "$UBO_DIR"/assets/assets.dev.json "$TMPDIR"/ - cp -R platform/mv3/scriptlets "$TMPDIR"/ - mkdir -p "$TMPDIR"/web_accessible_resources - cp "$UBO_DIR"/src/web_accessible_resources/* "$TMPDIR"/web_accessible_resources/ - cd "$TMPDIR" - node --no-warnings make-rulesets.js output="$DES" platform="$PLATFORM" - if [ -n "$BEFORE" ]; then - echo "*** uBOLite.mv3: salvaging rule ids to minimize diff size" - echo " before=$BEFORE/$PLATFORM" - echo " after=$DES" - node salvage-ruleids.mjs before="$BEFORE"/"$PLATFORM" after="$DES" - fi - cd - > /dev/null - rm -rf "$TMPDIR" +echo "*** uBOLite.mv3: Generating rulesets" +TMPDIR=$(mktemp -d) +mkdir -p "$TMPDIR" +if [ "$PLATFORM" = "chromium" ] || [ "$PLATFORM" = "edge" ]; then + cp platform/mv3/chromium/manifest.json "$DES"/ +elif [ "$PLATFORM" = "firefox" ]; then + cp platform/mv3/firefox/manifest.json "$DES"/ +fi +./tools/make-nodejs.sh "$TMPDIR" +cp platform/mv3/package.json "$TMPDIR"/ +cp platform/mv3/*.js "$TMPDIR"/ +cp platform/mv3/*.mjs "$TMPDIR"/ +cp platform/mv3/extension/js/utils.js "$TMPDIR"/js/ +cp -R "$UBO_DIR"/src/js/resources "$TMPDIR"/js/ +cp "$UBO_DIR"/assets/assets.dev.json "$TMPDIR"/ +cp -R platform/mv3/scriptlets "$TMPDIR"/ +mkdir -p "$TMPDIR"/web_accessible_resources +cp "$UBO_DIR"/src/web_accessible_resources/* "$TMPDIR"/web_accessible_resources/ +cd "$TMPDIR" +node --no-warnings make-rulesets.js output="$DES" platform="$PLATFORM" +if [ -n "$BEFORE" ]; then + echo "*** uBOLite.mv3: salvaging rule ids to minimize diff size" + echo " before=$BEFORE/$PLATFORM" + echo " after=$DES" + node salvage-ruleids.mjs before="$BEFORE"/"$PLATFORM" after="$DES" +fi +cd - > /dev/null +rm -rf "$TMPDIR" + +# For Edge, declared rulesets must be at package root +if [ "$PLATFORM" = "edge" ]; then + echo "*** uBOLite.edge: Modify reference implementation for Edge compatibility" + mv "$DES"/rulesets/main/* "$DES/" + rmdir "$DES/rulesets/main" + node tools/make-edge.mjs fi -echo "*** uBOLite.mv3: extension ready" +echo "*** uBOLite.$PLATFORM: extension ready" echo "Extension location: $DES/" # Local build From 94db43c4ad1f9783054b8a513557f0c2d0a37acb Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sat, 8 Mar 2025 11:31:31 -0500 Subject: [PATCH 0668/1099] [mv3] Minor changes to account for Edge build --- platform/mv3/make-rulesets.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/platform/mv3/make-rulesets.js b/platform/mv3/make-rulesets.js index d6efcd77ef05d..9e5c93908bdd3 100644 --- a/platform/mv3/make-rulesets.js +++ b/platform/mv3/make-rulesets.js @@ -64,14 +64,15 @@ const rulesetDir = `${outputDir}/rulesets`; const scriptletDir = `${rulesetDir}/scripting`; const env = [ platform, + 'native_css_has', 'mv3', 'ublock', 'ubol', 'user_stylesheet', ]; -if ( platform !== 'firefox' ) { - env.push('native_css_has'); +if ( platform === 'edge' ) { + env.push('chromium'); } /******************************************************************************/ @@ -1572,7 +1573,7 @@ async function main() { resources: Array.from(requiredRedirectResources).map(path => `/${path}`), matches: [ '' ], }; - if ( platform === 'chromium' ) { + if ( env.includes('chromium') ) { web_accessible_resources.use_dynamic_url = true; } manifest.web_accessible_resources.push(web_accessible_resources); From 26d1283583b2c3462e2ffe0d6a6e832670ef2f11 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sat, 8 Mar 2025 13:39:53 -0500 Subject: [PATCH 0669/1099] Fix mv3 build commands --- Makefile | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/Makefile b/Makefile index 773c93f5c4a1b..84b389c0c06b2 100644 --- a/Makefile +++ b/Makefile @@ -8,7 +8,6 @@ run_options := $(filter-out $@,$(MAKECMDGOALS)) sources := $(wildcard assets/* assets/*/* dist/version src/* src/*/* src/*/*/* src/*/*/*/*) platform := $(wildcard platform/* platform/*/* platform/*/*/* platform/*/*/*/* platform/*/*/*/*/*) assets := dist/build/uAssets -mv3-assets := dist/build/mv3-data/* all: chromium firefox npm @@ -62,17 +61,20 @@ dig: dist/build/uBlock0.dig dig-snfe: dig cd dist/build/uBlock0.dig && npm run snfe $(run_options) -dist/build/uBOLite.chromium: tools/make-mv3.sh $(sources) $(platform) $(mv3-assets) +mv3-assets: + mkdir dist/build/mv3-data + +dist/build/uBOLite.chromium: tools/make-mv3.sh $(sources) $(platform) mv3-assets tools/make-mv3.sh chromium mv3-chromium: dist/build/uBOLite.chromium -dist/build/uBOLite.firefox: tools/make-mv3.sh $(sources) $(platform) $(mv3-assets) +dist/build/uBOLite.firefox: tools/make-mv3.sh $(sources) $(platform) mv3-assets tools/make-mv3.sh firefox mv3-firefox: dist/build/uBOLite.firefox -dist/build/uBOLite.edge: tools/make-mv3.sh tools/make-edge.mjs $(sources) $(platform) $(mv3-assets) +dist/build/uBOLite.edge: tools/make-mv3.sh tools/make-edge.mjs $(sources) $(platform) mv3-assets tools/make-mv3.sh edge mv3-edge: dist/build/uBOLite.edge From bf9549115c9c7e2f8525f92991153f77e7707dc3 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sat, 8 Mar 2025 17:22:46 -0500 Subject: [PATCH 0670/1099] Import translation work from https://crowdin.com/project/ublock --- platform/mv3/extension/_locales/th/messages.json | 12 ++++++------ src/_locales/th/messages.json | 10 +++++----- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/platform/mv3/extension/_locales/th/messages.json b/platform/mv3/extension/_locales/th/messages.json index e29ef0cb06652..ad19d5dd0097f 100644 --- a/platform/mv3/extension/_locales/th/messages.json +++ b/platform/mv3/extension/_locales/th/messages.json @@ -144,7 +144,7 @@ "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option4": { - "message": "Has privacy-related issues", + "message": "มีปัญหาเกี่ยวกับความเป็นส่วนตัว", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option5": { @@ -240,7 +240,7 @@ "description": "Label for a checkbox in the options page" }, "findListsPlaceholder": { - "message": "Find lists", + "message": "หารายการ", "description": "Placeholder for the input field used to find lists" }, "strictblockTitle": { @@ -264,19 +264,19 @@ "description": "Label to be used for the parameter-less URL" }, "strictblockBack": { - "message": "Go back", + "message": "ย้อนกลับ", "description": "A button to go back to the previous webpage" }, "strictblockClose": { - "message": "Close this window", + "message": "ปิดหน้าต่างนี้", "description": "A button to close the current tab" }, "strictblockDontWarn": { - "message": "Don't warn me again about this site", + "message": "อย่าเตือนเกี่ยวกับเว็บไซต์นี้ให้กับฉันอีก", "description": "Label for checkbox in document-blocked page" }, "strictblockProceed": { - "message": "Proceed", + "message": "ดำเนินต่อ", "description": "A button to navigate to the blocked page" } } diff --git a/src/_locales/th/messages.json b/src/_locales/th/messages.json index f420860563809..52a7d883c534b 100644 --- a/src/_locales/th/messages.json +++ b/src/_locales/th/messages.json @@ -552,7 +552,7 @@ "description": "Button in the 'My filters' pane" }, "1pExport": { - "message": "Export…", + "message": "ส่งออก", "description": "Button in the 'My filters' pane" }, "1pExportFilename": { @@ -784,7 +784,7 @@ "description": "Small header to identify the dynamic URL filtering section" }, "loggerURLFilteringContextLabel": { - "message": "Context:", + "message": "บริบท:", "description": "Label for the context selector" }, "loggerURLFilteringTypeLabel": { @@ -824,7 +824,7 @@ "description": "Used in the static filtering wizard" }, "loggerStaticFilteringSentencePartNotImportant": { - "message": "except when", + "message": "ยกเว้นเมื่อ", "description": "Used in the static filtering wizard" }, "loggerStaticFilteringSentencePartImportant": { @@ -1080,7 +1080,7 @@ "description": "English: Restore from file..." }, "aboutResetDataButton": { - "message": "Reset to default settings…", + "message": "ปรับเป็นการตั้งค่าเริ่มต้น", "description": "English: Reset to default settings..." }, "aboutRestoreDataConfirm": { @@ -1260,7 +1260,7 @@ "description": "Tooltip for the button used to lock scrolling between the views in the 'My rules' pane" }, "genericCopyToClipboard": { - "message": "Copy to clipboard", + "message": "คัดลอกไปยังคลิปบอร์ด", "description": "Label for buttons used to copy something to the clipboard" }, "genericSelectAll": { From 3f850db1a89bfe526a33a456f04a6e58b9c3b9c4 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sun, 9 Mar 2025 08:41:28 -0400 Subject: [PATCH 0671/1099] Fix makefile --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 84b389c0c06b2..d568b9dc06a9a 100644 --- a/Makefile +++ b/Makefile @@ -62,7 +62,7 @@ dig-snfe: dig cd dist/build/uBlock0.dig && npm run snfe $(run_options) mv3-assets: - mkdir dist/build/mv3-data + mkdir -p dist/build/mv3-data dist/build/uBOLite.chromium: tools/make-mv3.sh $(sources) $(platform) mv3-assets tools/make-mv3.sh chromium From b9a5726672c4d8cad7fdaf98579c7f02de2f3da0 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Mon, 10 Mar 2025 09:24:26 -0400 Subject: [PATCH 0672/1099] Rebuild MV3 extension if mv3-data content changes --- Makefile | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/Makefile b/Makefile index d568b9dc06a9a..c9d92a35c730e 100644 --- a/Makefile +++ b/Makefile @@ -8,6 +8,7 @@ run_options := $(filter-out $@,$(MAKECMDGOALS)) sources := $(wildcard assets/* assets/*/* dist/version src/* src/*/* src/*/*/* src/*/*/*/*) platform := $(wildcard platform/* platform/*/* platform/*/*/* platform/*/*/*/* platform/*/*/*/*/*) assets := dist/build/uAssets +mv3-data := $(wildcard dist/build/mv3-data/*) all: chromium firefox npm @@ -61,20 +62,20 @@ dig: dist/build/uBlock0.dig dig-snfe: dig cd dist/build/uBlock0.dig && npm run snfe $(run_options) -mv3-assets: +dist/build/mv3-data: mkdir -p dist/build/mv3-data -dist/build/uBOLite.chromium: tools/make-mv3.sh $(sources) $(platform) mv3-assets +dist/build/uBOLite.chromium: tools/make-mv3.sh $(sources) $(platform) $(mv3-data) dist/build/mv3-data tools/make-mv3.sh chromium mv3-chromium: dist/build/uBOLite.chromium -dist/build/uBOLite.firefox: tools/make-mv3.sh $(sources) $(platform) mv3-assets +dist/build/uBOLite.firefox: tools/make-mv3.sh $(sources) $(platform) $(mv3-data) dist/build/mv3-data tools/make-mv3.sh firefox mv3-firefox: dist/build/uBOLite.firefox -dist/build/uBOLite.edge: tools/make-mv3.sh tools/make-edge.mjs $(sources) $(platform) mv3-assets +dist/build/uBOLite.edge: tools/make-mv3.sh tools/make-edge.mjs $(sources) $(platform) $(mv3-data) dist/build/mv3-data tools/make-mv3.sh edge mv3-edge: dist/build/uBOLite.edge From 1ea56a04cd58ea976d16ff1fb6b624dd99981e27 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Mon, 10 Mar 2025 11:44:15 -0400 Subject: [PATCH 0673/1099] [mv3] Explicit filtering modes in troubleshooting data --- platform/mv3/extension/js/report.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/platform/mv3/extension/js/report.js b/platform/mv3/extension/js/report.js index 3ce29bad1705e..a7e29658fb78c 100644 --- a/platform/mv3/extension/js/report.js +++ b/platform/mv3/extension/js/report.js @@ -113,7 +113,10 @@ async function reportSpecificFilterIssue() { const modes = [ 'no filtering', 'basic', 'optimal', 'complete' ]; const config = { version: `uBOL ${manifest.version}`, - mode: `${modes[reportedPage.mode]} / ${modes[defaultMode]}`, + filtering: { + 'site': `${modes[reportedPage.mode]}`, + 'default': `${modes[defaultMode]}`, + }, rulesets, }; const configBody = [ From 9f55694c8a11363cd454eb6211694515594e8e49 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Tue, 11 Mar 2025 09:13:58 -0400 Subject: [PATCH 0674/1099] New revision for release candidate --- dist/version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dist/version b/dist/version index 40ec5833684ee..b6bab7b18d4ae 100644 --- a/dist/version +++ b/dist/version @@ -1 +1 @@ -1.62.1.6 +1.62.1.100 From 7db84ef13deee2d4b8408bc04dbea4e2049d979f Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Tue, 11 Mar 2025 09:15:40 -0400 Subject: [PATCH 0675/1099] Do not blindly force https in urlskip Related discussion: https://github.com/uBlockOrigin/uBlock-issues/issues/3206#issuecomment-2708584507 --- src/js/urlskip.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/js/urlskip.js b/src/js/urlskip.js index 85c396e67748d..360a3dd7b3a9f 100644 --- a/src/js/urlskip.js +++ b/src/js/urlskip.js @@ -157,7 +157,6 @@ export function urlSkip(url, blocked, steps, directive = {}) { const urlfinal = new URL(urlout); if ( urlfinal.protocol !== 'https:' ) { if ( urlfinal.protocol !== 'http:' ) { return; } - urlout = urlout.replace('http', 'https'); } if ( blocked && redirectBlocked !== true ) { return; } return urlout; From 22fdf8fc1d6db0616aa2ccbcbbd6913039162d2b Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Tue, 11 Mar 2025 09:51:19 -0400 Subject: [PATCH 0676/1099] Make Firefox dev build auto-update --- dist/firefox/updates.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dist/firefox/updates.json b/dist/firefox/updates.json index 0d6692f428d26..95cb3297b9154 100644 --- a/dist/firefox/updates.json +++ b/dist/firefox/updates.json @@ -3,9 +3,9 @@ "uBlock0@raymondhill.net": { "updates": [ { - "version": "1.62.1.6", + "version": "1.62.1.100", "browser_specific_settings": { "gecko": { "strict_min_version": "79.0" } }, - "update_link": "https://github.com/gorhill/uBlock/releases/download/1.62.1b6/uBlock0_1.62.1b6.firefox.signed.xpi" + "update_link": "https://github.com/gorhill/uBlock/releases/download/1.62.1rc0/uBlock0_1.62.1rc0.firefox.signed.xpi" } ] } From a009623d975415ec5a4f2ff973290479118850be Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Thu, 13 Mar 2025 13:15:27 -0400 Subject: [PATCH 0677/1099] [mv3] Improve generic cosmetic filtering Specifically, properly exclude generic cosmetic filters according to specific cosmetic exceptions. Related issue: https://github.com/uBlockOrigin/uBOL-home/issues/181 --- .../mv3/extension/js/scripting-manager.js | 4 + .../extension/js/scripting/css-declarative.js | 93 ++---- .../mv3/extension/js/scripting/css-generic.js | 47 ++- .../extension/js/scripting/css-procedural.js | 94 ++---- .../extension/js/scripting/css-specific.js | 90 ++---- .../extension/js/scripting/isolated-api.js | 80 +++++ platform/mv3/make-rulesets.js | 303 +++++++++--------- .../scriptlets/css-declarative.template.js | 9 +- .../mv3/scriptlets/css-generic.template.js | 47 ++- .../mv3/scriptlets/css-procedural.template.js | 9 +- .../mv3/scriptlets/css-specific.template.js | 9 +- src/js/static-dnr-filtering.js | 46 +-- 12 files changed, 415 insertions(+), 416 deletions(-) create mode 100644 platform/mv3/extension/js/scripting/isolated-api.js diff --git a/platform/mv3/extension/js/scripting-manager.js b/platform/mv3/extension/js/scripting-manager.js index b11a2f6a68a97..1ca2528ea09f8 100644 --- a/platform/mv3/extension/js/scripting-manager.js +++ b/platform/mv3/extension/js/scripting-manager.js @@ -179,6 +179,7 @@ function registerGeneric(context, genericDetails) { if ( js.length === 0 ) { return; } + js.unshift('/js/scripting/isolated-api.js'); js.push('/js/scripting/css-generic.js'); const { none, basic, optimal, complete } = filteringModeDetails; @@ -252,6 +253,7 @@ function registerProcedural(context) { ]; if ( matches.length === 0 ) { return; } + js.unshift('/js/scripting/isolated-api.js'); js.push('/js/scripting/css-procedural.js'); const excludeMatches = []; @@ -311,6 +313,7 @@ function registerDeclarative(context) { ]; if ( matches.length === 0 ) { return; } + js.unshift('/js/scripting/isolated-api.js'); js.push('/js/scripting/css-declarative.js'); const excludeMatches = []; @@ -370,6 +373,7 @@ function registerSpecific(context) { ]; if ( matches.length === 0 ) { return; } + js.unshift('/js/scripting/isolated-api.js'); js.push('/js/scripting/css-specific.js'); const excludeMatches = []; diff --git a/platform/mv3/extension/js/scripting/css-declarative.js b/platform/mv3/extension/js/scripting/css-declarative.js index 539c97d51070f..9f63fe5585483 100644 --- a/platform/mv3/extension/js/scripting/css-declarative.js +++ b/platform/mv3/extension/js/scripting/css-declarative.js @@ -27,73 +27,41 @@ const declarativeImports = self.declarativeImports || []; self.declarativeImports = undefined; -delete self.declarativeImports; /******************************************************************************/ -const hnParts = []; -try { hnParts.push(...document.location.hostname.split('.')); } -catch { } -const hnpartslen = hnParts.length; -if ( hnpartslen === 0 ) { return; } - const selectors = []; - -for ( const { argsList, exceptionsMap, hostnamesMap, entitiesMap } of declarativeImports ) { - const todoIndices = new Set(); - const tonotdoIndices = []; - // Exceptions - if ( exceptionsMap.size !== 0 ) { - for ( let i = 0; i < hnpartslen; i++ ) { - const hn = hnParts.slice(i).join('.'); - const excepted = exceptionsMap.get(hn); - if ( excepted ) { tonotdoIndices.push(...excepted); } - } - exceptionsMap.clear(); - } - // Hostname-based - if ( hostnamesMap.size !== 0 ) { - const collectArgIndices = hn => { - let argsIndices = hostnamesMap.get(hn); - if ( argsIndices === undefined ) { return; } - if ( typeof argsIndices === 'number' ) { argsIndices = [ argsIndices ]; } - for ( const argsIndex of argsIndices ) { - if ( tonotdoIndices.includes(argsIndex) ) { continue; } - todoIndices.add(argsIndex); - } - }; - for ( let i = 0; i < hnpartslen; i++ ) { - const hn = hnParts.slice(i).join('.'); - collectArgIndices(hn); - } - collectArgIndices('*'); - hostnamesMap.clear(); - } - // Entity-based - if ( entitiesMap.size !== 0 ) { - const n = hnpartslen - 1; - for ( let i = 0; i < n; i++ ) { - for ( let j = n; j > i; j-- ) { - const en = hnParts.slice(i,j).join('.'); - let argsIndices = entitiesMap.get(en); - if ( argsIndices === undefined ) { continue; } - if ( typeof argsIndices === 'number' ) { argsIndices = [ argsIndices ]; } - for ( const argsIndex of argsIndices ) { - if ( tonotdoIndices.includes(argsIndex) ) { continue; } - todoIndices.add(argsIndex); - } - } - } - entitiesMap.clear(); +const exceptions = []; + +const lookupHostname = (hostname, details, out) => { + let seqi = details.hostnamesMap.get(hostname); + if ( seqi === undefined ) { return; } + const { argsList, argsSeqs } = details; + for (;;) { + const argi = argsSeqs[seqi++]; + const done = argi > 0; + out.push(...argsList[done ? argi : -argi].split('\n')); + if ( done ) { break; } } - for ( const i of todoIndices ) { - selectors.push(...argsList[i].map(json => JSON.parse(json))); +}; + +const lookupAll = hostname => { + for ( const details of declarativeImports ) { + lookupHostname(hostname, details, selectors); + lookupHostname(`~${hostname}`, details, exceptions); } - argsList.length = 0; -} +}; + +self.isolatedAPI.forEachHostname(lookupAll, { + hasEntities: declarativeImports.some(a => a.hasEntities) +}); + declarativeImports.length = 0; -if ( selectors.length === 0 ) { return; } +const exceptedSelectors = exceptions.length !== 0 + ? selectors.filter(a => exceptions.includes(a) === false) + : selectors; +if ( exceptedSelectors.length === 0 ) { return; } /******************************************************************************/ @@ -126,8 +94,9 @@ const cssRuleFromProcedural = details => { }; const sheetText = []; -for ( const selector of selectors ) { - const ruleText = cssRuleFromProcedural(selector); +for ( const selector of exceptedSelectors ) { + const details = JSON.parse(selector); + const ruleText = cssRuleFromProcedural(details); if ( ruleText === undefined ) { continue; } sheetText.push(ruleText); } @@ -138,7 +107,7 @@ if ( sheetText.length === 0 ) { return; } chrome.runtime.sendMessage({ what: 'insertCSS', css }).catch(( ) => { count -= 1; if ( count === 0 ) { return; } - uBOL_injectCSS(css, count - 1); + uBOL_injectCSS(css, count); }); })(sheetText.join('\n')); diff --git a/platform/mv3/extension/js/scripting/css-generic.js b/platform/mv3/extension/js/scripting/css-generic.js index b00ea03bd4c89..5206e92a7503b 100644 --- a/platform/mv3/extension/js/scripting/css-generic.js +++ b/platform/mv3/extension/js/scripting/css-generic.js @@ -26,10 +26,12 @@ (function uBOL_cssGeneric() { const genericSelectorMap = self.genericSelectorMap || new Map(); -delete self.genericSelectorMap; - +self.genericSelectorMap = undefined; if ( genericSelectorMap.size === 0 ) { return; } +const genericExceptionMap = self.genericExceptionMap || new Map(); +self.genericExceptionMap = undefined; + /******************************************************************************/ const maxSurveyTimeSlice = 4; @@ -76,6 +78,7 @@ const uBOL_idFromNode = (node, out) => { const selectorList = genericSelectorMap.get(hash); if ( selectorList === undefined ) { return; } genericSelectorMap.delete(hash); + if ( isExcepted(hash) ) { return; } out.push(selectorList); }; @@ -96,10 +99,20 @@ const uBOL_classesFromNode = (node, out) => { const selectorList = genericSelectorMap.get(hash); if ( selectorList === undefined ) { continue; } genericSelectorMap.delete(hash); + if ( isExcepted(hash) ) { continue; } out.push(selectorList); } }; +const isExcepted = hash => { + const hostnames = genericExceptionMap.get(hash); + if ( hostnames === undefined ) { return; } + const hasEntities = hostnames.includes('.*'); + return self.isolatedAPI.forEachHostname((hostname) => { + if ( hostnames.includes(` ${hostname} `) ) { return true; } + }, { hasEntities }); +} + /******************************************************************************/ const pendingNodes = { @@ -191,12 +204,26 @@ const uBOL_injectCSS = (css, count = 10) => { chrome.runtime.sendMessage({ what: 'insertCSS', css }).catch(( ) => { count -= 1; if ( count === 0 ) { return; } - uBOL_injectCSS(css, count - 1); + uBOL_injectCSS(css, count); }); }; /******************************************************************************/ +const stopAll = reason => { + if ( domChangeTimer !== undefined ) { + self.clearTimeout(domChangeTimer); + domChangeTimer = undefined; + } + domMutationObserver.disconnect(); + domMutationObserver.takeRecords(); + domMutationObserver = undefined; + genericSelectorMap.clear(); + console.info(`uBOL: Generic cosmetic filtering stopped because ${reason}`); +}; + +/******************************************************************************/ + pendingNodes.add(document); uBOL_processNodes(); @@ -219,20 +246,6 @@ needDomChangeObserver(); /******************************************************************************/ -const stopAll = reason => { - if ( domChangeTimer !== undefined ) { - self.clearTimeout(domChangeTimer); - domChangeTimer = undefined; - } - domMutationObserver.disconnect(); - domMutationObserver.takeRecords(); - domMutationObserver = undefined; - genericSelectorMap.clear(); - console.info(`uBOL: Generic cosmetic filtering stopped because ${reason}`); -}; - -/******************************************************************************/ - })(); /******************************************************************************/ diff --git a/platform/mv3/extension/js/scripting/css-procedural.js b/platform/mv3/extension/js/scripting/css-procedural.js index da27a6d22dc92..101aa7ee77185 100644 --- a/platform/mv3/extension/js/scripting/css-procedural.js +++ b/platform/mv3/extension/js/scripting/css-procedural.js @@ -27,73 +27,41 @@ const proceduralImports = self.proceduralImports || []; self.proceduralImports = undefined; -delete self.proceduralImports; /******************************************************************************/ -const hnParts = []; -try { hnParts.push(...document.location.hostname.split('.')); } -catch { } -const hnpartslen = hnParts.length; -if ( hnpartslen === 0 ) { return; } - const selectors = []; - -for ( const { argsList, exceptionsMap, hostnamesMap, entitiesMap } of proceduralImports ) { - const todoIndices = new Set(); - const tonotdoIndices = []; - // Exceptions - if ( exceptionsMap.size !== 0 ) { - for ( let i = 0; i < hnpartslen; i++ ) { - const hn = hnParts.slice(i).join('.'); - const excepted = exceptionsMap.get(hn); - if ( excepted ) { tonotdoIndices.push(...excepted); } - } - exceptionsMap.clear(); - } - // Hostname-based - if ( hostnamesMap.size !== 0 ) { - const collectArgIndices = hn => { - let argsIndices = hostnamesMap.get(hn); - if ( argsIndices === undefined ) { return; } - if ( typeof argsIndices === 'number' ) { argsIndices = [ argsIndices ]; } - for ( const argsIndex of argsIndices ) { - if ( tonotdoIndices.includes(argsIndex) ) { continue; } - todoIndices.add(argsIndex); - } - }; - for ( let i = 0; i < hnpartslen; i++ ) { - const hn = hnParts.slice(i).join('.'); - collectArgIndices(hn); - } - collectArgIndices('*'); - hostnamesMap.clear(); - } - // Entity-based - if ( entitiesMap.size !== 0 ) { - const n = hnpartslen - 1; - for ( let i = 0; i < n; i++ ) { - for ( let j = n; j > i; j-- ) { - const en = hnParts.slice(i,j).join('.'); - let argsIndices = entitiesMap.get(en); - if ( argsIndices === undefined ) { continue; } - if ( typeof argsIndices === 'number' ) { argsIndices = [ argsIndices ]; } - for ( const argsIndex of argsIndices ) { - if ( tonotdoIndices.includes(argsIndex) ) { continue; } - todoIndices.add(argsIndex); - } - } - } - entitiesMap.clear(); +const exceptions = []; + +const lookupHostname = (hostname, details, out) => { + let seqi = details.hostnamesMap.get(hostname); + if ( seqi === undefined ) { return; } + const { argsList, argsSeqs } = details; + for (;;) { + const argi = argsSeqs[seqi++]; + const done = argi > 0; + out.push(...argsList[done ? argi : -argi]); + if ( done ) { break; } } - for ( const i of todoIndices ) { - selectors.push(...argsList[i].map(json => JSON.parse(json))); +}; + +const lookupAll = hostname => { + for ( const details of proceduralImports ) { + lookupHostname(hostname, details, selectors); + lookupHostname(`~${hostname}`, details, exceptions); } - argsList.length = 0; -} +}; + +self.isolatedAPI.forEachHostname(lookupAll, { + hasEntities: proceduralImports.some(a => a.hasEntities) +}); + proceduralImports.length = 0; -if ( selectors.length === 0 ) { return; } +const exceptedSelectors = exceptions.length !== 0 + ? selectors.filter(a => exceptions.includes(a) === false) + : selectors; +if ( exceptedSelectors.length === 0 ) { return; } /******************************************************************************/ @@ -101,7 +69,7 @@ const uBOL_injectCSS = (css, count = 10) => { chrome.runtime.sendMessage({ what: 'insertCSS', css }).catch(( ) => { count -= 1; if ( count === 0 ) { return; } - uBOL_injectCSS(css, count - 1); + uBOL_injectCSS(css, count); }); }; @@ -646,7 +614,7 @@ class ProceduralFilterer { this.uBOL_commitNow(); } - addSelectors() { + addSelectors(selectors) { for ( const selector of selectors ) { const pselector = new PSelectorRoot(selector); this.primeProceduralSelector(pselector); @@ -787,7 +755,9 @@ class ProceduralFilterer { /******************************************************************************/ -const proceduralFilterer = new ProceduralFilterer(selectors); +const proceduralFilterer = new ProceduralFilterer( + exceptedSelectors.map(a => JSON.parse(a)) +); const observer = new MutationObserver(mutations => { let domChanged = false; diff --git a/platform/mv3/extension/js/scripting/css-specific.js b/platform/mv3/extension/js/scripting/css-specific.js index c58ebd9aca0e8..661cca61605ca 100644 --- a/platform/mv3/extension/js/scripting/css-specific.js +++ b/platform/mv3/extension/js/scripting/css-specific.js @@ -27,73 +27,41 @@ const specificImports = self.specificImports || []; self.specificImports = undefined; -delete self.specificImports; /******************************************************************************/ -const hnParts = []; -try { hnParts.push(...document.location.hostname.split('.')); } -catch { } -const hnpartslen = hnParts.length; -if ( hnpartslen === 0 ) { return; } - const selectors = []; - -for ( const { argsList, exceptionsMap, hostnamesMap, entitiesMap } of specificImports ) { - const todoIndices = new Set(); - const tonotdoIndices = []; - // Exceptions - if ( exceptionsMap.size !== 0 ) { - for ( let i = 0; i < hnpartslen; i++ ) { - const hn = hnParts.slice(i).join('.'); - const excepted = exceptionsMap.get(hn); - if ( excepted ) { tonotdoIndices.push(...excepted); } - } - exceptionsMap.clear(); - } - // Hostname-based - if ( hostnamesMap.size !== 0 ) { - const collectArgIndices = hn => { - let argsIndices = hostnamesMap.get(hn); - if ( argsIndices === undefined ) { return; } - if ( typeof argsIndices === 'number' ) { argsIndices = [ argsIndices ]; } - for ( const argsIndex of argsIndices ) { - if ( tonotdoIndices.includes(argsIndex) ) { continue; } - todoIndices.add(argsIndex); - } - }; - for ( let i = 0; i < hnpartslen; i++ ) { - const hn = hnParts.slice(i).join('.'); - collectArgIndices(hn); - } - collectArgIndices('*'); - hostnamesMap.clear(); - } - // Entity-based - if ( entitiesMap.size !== 0 ) { - const n = hnpartslen - 1; - for ( let i = 0; i < n; i++ ) { - for ( let j = n; j > i; j-- ) { - const en = hnParts.slice(i,j).join('.'); - let argsIndices = entitiesMap.get(en); - if ( argsIndices === undefined ) { continue; } - if ( typeof argsIndices === 'number' ) { argsIndices = [ argsIndices ]; } - for ( const argsIndex of argsIndices ) { - if ( tonotdoIndices.includes(argsIndex) ) { continue; } - todoIndices.add(argsIndex); - } - } - } - entitiesMap.clear(); +const exceptions = []; + +const lookupHostname = (hostname, details, out) => { + let seqi = details.hostnamesMap.get(hostname); + if ( seqi === undefined ) { return; } + const { argsList, argsSeqs } = details; + for (;;) { + const argi = argsSeqs[seqi++]; + const done = argi > 0; + out.push(...argsList[done ? argi : -argi].split('\n')); + if ( done ) { break; } } - for ( const i of todoIndices ) { - selectors.push(argsList[i]); +}; + +const lookupAll = hostname => { + for ( const details of specificImports ) { + lookupHostname(hostname, details, selectors); + lookupHostname(`~${hostname}`, details, exceptions); } - argsList.length = 0; -} +}; + +self.isolatedAPI.forEachHostname(lookupAll, { + hasEntities: specificImports.some(a => a.hasEntities) +}); + specificImports.length = 0; -if ( selectors.length === 0 ) { return; } +const exceptedSelectors = exceptions.length !== 0 + ? selectors.filter(a => exceptions.includes(a) === false) + : selectors; +if ( exceptedSelectors.length === 0 ) { return; } /******************************************************************************/ @@ -101,9 +69,9 @@ if ( selectors.length === 0 ) { return; } chrome.runtime.sendMessage({ what: 'insertCSS', css }).catch(( ) => { count -= 1; if ( count === 0 ) { return; } - uBOL_injectCSS(css, count - 1); + uBOL_injectCSS(css, count); }); -})(`${selectors.join(',')}{display:none!important;}`); +})(`${exceptedSelectors.join(',')}{display:none!important;}`); /******************************************************************************/ diff --git a/platform/mv3/extension/js/scripting/isolated-api.js b/platform/mv3/extension/js/scripting/isolated-api.js new file mode 100644 index 0000000000000..9c867bc412fc5 --- /dev/null +++ b/platform/mv3/extension/js/scripting/isolated-api.js @@ -0,0 +1,80 @@ +/******************************************************************************* + + uBlock Origin Lite - a comprehensive, MV3-compliant content blocker + Copyright (C) 2022-present Raymond Hill + + 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/}. + + Home: https://github.com/gorhill/uBlock/ + +*/ + +/******************************************************************************/ + +(api => { + if ( typeof api === 'object' ) { return; } + + const isolatedAPI = self.isolatedAPI = {}; + + const hostnameStack = (( ) => { + const docloc = document.location; + const origins = [ docloc.origin ]; + if ( docloc.ancestorOrigins ) { + origins.push(...docloc.ancestorOrigins); + } + return origins.map((origin, i) => { + const beg = origin.lastIndexOf('://'); + if ( beg === -1 ) { return; } + const hn1 = origin.slice(beg+3) + const end = hn1.indexOf(':'); + const hn2 = end === -1 ? hn1 : hn1.slice(0, end); + return { hnparts: hn2.split('.'), i }; + }).filter(a => a !== undefined); + })(); + + const forEachHostname = (entry, callback, details) => { + const hnparts = entry.hnparts; + const hnpartslen = hnparts.length; + if ( hnpartslen === 0 ) { return; } + for ( let i = 0; i < hnpartslen; i++ ) { + const r = callback(`${hnparts.slice(i).join('.')}`, details); + if ( r !== undefined ) { return r; } + } + if ( details.hasEntities !== true ) { return; } + const n = hnpartslen - 1; + for ( let i = 0; i < n; i++ ) { + for ( let j = n; j > i; j-- ) { + const r = callback(`${hnparts.slice(i,j).join('.')}.*`, details); + if ( r !== undefined ) { return r; } + } + } + }; + + isolatedAPI.forEachHostname = (callback, details) => { + if ( hostnameStack.length === 0 ) { return; } + return forEachHostname(hostnameStack[0], callback, details); + }; + + isolatedAPI.forEachHostnameAncestors = (callback, details) => { + for ( const entry of hostnameStack ) { + if ( entry.i === 0 ) { continue; } + const r = forEachHostname(entry, callback, details); + if ( r !== undefined ) { return r; } + } + }; +})(self.isolatedAPI); + +/******************************************************************************/ + +void 0; \ No newline at end of file diff --git a/platform/mv3/make-rulesets.js b/platform/mv3/make-rulesets.js index 9e5c93908bdd3..f4e3808869c06 100644 --- a/platform/mv3/make-rulesets.js +++ b/platform/mv3/make-rulesets.js @@ -675,36 +675,69 @@ function loadAllSourceScriptlets() { /******************************************************************************/ -async function processGenericCosmeticFilters(assetDetails, bucketsMap, exceptionSet) { - if ( bucketsMap === undefined ) { return 0; } - if ( exceptionSet ) { - for ( const [ hash, selectors ] of bucketsMap ) { - let i = selectors.length; - while ( i-- ) { - const selector = selectors[i]; - if ( exceptionSet.has(selector) === false ) { continue; } - selectors.splice(i, 1); - //log(`\tRemoving excepted generic filter ##${selector}`); - } - if ( selectors.length === 0 ) { - bucketsMap.delete(hash); +// http://www.cse.yorku.ca/~oz/hash.html#djb2 +// Must mirror content script surveyor's version + +async function processGenericCosmeticFilters( + assetDetails, + selectorList, + exceptionList, + declarativeMap +) { + const exceptionSet = new Set( + exceptionList && + exceptionList.filter(a => a.key !== undefined).map(a => a.selector) + ); + + const genericSelectorMap = new Map(); + if ( selectorList ) { + for ( const { key, selector } of selectorList ) { + if ( key === undefined ) { continue; } + if ( exceptionSet.has(selector) ) { continue; } + const type = key.charCodeAt(0); + const hash = hashFromStr(type, key.slice(1)); + const selectors = genericSelectorMap.get(hash); + if ( selectors === undefined ) { + genericSelectorMap.set(hash, selector) + } else { + genericSelectorMap.set(hash, `${selectors},${selector}`) } } } - if ( bucketsMap.size === 0 ) { return 0; } - const bucketsList = Array.from(bucketsMap); - const count = bucketsList.reduce((a, v) => a += v[1].length, 0); - if ( count === 0 ) { return 0; } - const selectorLists = bucketsList.map(v => [ v[0], v[1].join(',') ]); - const originalScriptletMap = await loadAllSourceScriptlets(); + // Specific exceptions + const genericExceptionMap = new Map(); + if ( declarativeMap ) { + for ( const details of declarativeMap.values() ) { + if ( details.rejected ) { continue; } + if ( details.key === undefined ) { continue; } + if ( details.matches !== undefined ) { continue; } + if ( details.excludeMatches === undefined ) { continue; } + const type = details.key.charCodeAt(0); + const hash = hashFromStr(type, details.key.slice(1)); + const hostnames = genericExceptionMap.get(hash) ?? ''; + genericExceptionMap.set(hash, + `${hostnames} ${details.excludeMatches.join(' ')} ` + ); + } + } + + if ( genericSelectorMap.size === 0 ) { + if ( genericExceptionMap.size === 0 ) { return 0; } + } + + const originalScriptletMap = await loadAllSourceScriptlets(); let patchedScriptlet = originalScriptletMap.get('css-generic').replace( '$rulesetId$', assetDetails.id ); patchedScriptlet = safeReplace(patchedScriptlet, /\bself\.\$genericSelectorMap\$/, - `${JSON.stringify(selectorLists, scriptletJsonReplacer)}` + `${JSON.stringify(genericSelectorMap, scriptletJsonReplacer)}` + ); + patchedScriptlet = safeReplace(patchedScriptlet, + /\bself\.\$genericExceptionMap\$/, + `${JSON.stringify(genericExceptionMap, scriptletJsonReplacer)}` ); writeFile( @@ -712,24 +745,49 @@ async function processGenericCosmeticFilters(assetDetails, bucketsMap, exception patchedScriptlet ); - log(`CSS-generic: ${count} plain CSS selectors`); + log(`CSS-generic: ${genericSelectorMap.size} plain CSS selectors`); + log(`CSS-generic: ${genericExceptionMap.size} specific CSS exceptions`); - return count; + return genericSelectorMap.size + genericExceptionMap.size; } +const hashFromStr = (type, s) => { + const len = s.length; + const step = len + 7 >>> 3; + let hash = (type << 5) + type ^ len; + for ( let i = 0; i < len; i += step ) { + hash = (hash << 5) + hash ^ s.charCodeAt(i); + } + return hash & 0xFFFFFF; +}; + /******************************************************************************/ -async function processGenericHighCosmeticFilters(assetDetails, selectorSet, exceptionSet) { - if ( selectorSet === undefined ) { return 0; } - if ( exceptionSet ) { - for ( const selector of selectorSet ) { - if ( exceptionSet.has(selector) === false ) { continue; } - selectorSet.delete(selector); - //log(`\tRemoving excepted generic filter ##${selector}`); +async function processGenericHighCosmeticFilters( + assetDetails, + genericSelectorList, + genericExceptionList +) { + if ( genericSelectorList === undefined ) { return 0; } + const genericSelectorSet = new Set( + genericSelectorList + .filter(a => a.key === undefined) + .map(a => a.selector) + ); + if ( genericExceptionList ) { + const genericExceptionSet = new Set( + genericExceptionList + .filter(a => a.key === undefined) + .map(a => a.selector) + ); + for ( const selector of genericExceptionSet ) { + if ( genericSelectorSet.has(selector) === false ) { continue; } + genericSelectorSet.delete(selector); + log(`\tRemoving excepted highly generic filter ##${selector}`); } } - if ( selectorSet.size === 0 ) { return 0; } - const selectorLists = Array.from(selectorSet).sort().join(',\n'); + if ( genericSelectorSet.size === 0 ) { return 0; } + const selectorLists = Array.from(genericSelectorSet).sort().join(',\n'); const originalScriptletMap = await loadAllSourceScriptlets(); let patchedScriptlet = originalScriptletMap.get('css-generichigh').replace( @@ -746,9 +804,9 @@ async function processGenericHighCosmeticFilters(assetDetails, selectorSet, exce patchedScriptlet ); - log(`CSS-generic-high: ${selectorSet.size} plain CSS selectors`); + log(`CSS-generic-high: ${genericSelectorSet.size} plain CSS selectors`); - return selectorSet.size; + return genericSelectorSet.size; } /******************************************************************************/ @@ -846,22 +904,33 @@ const scriptletJsonReplacer = (k, v) => { /******************************************************************************/ function argsMap2List(argsMap, hostnamesMap) { - const argsList = []; + const argsList = [ '' ]; const indexMap = new Map(); for ( const [ id, details ] of argsMap ) { indexMap.set(id, argsList.length); argsList.push(details); } + const argsSeqs = [ 0 ]; + const argsSeqsIndices = new Map(); for ( const [ hn, ids ] of hostnamesMap ) { + const seqKey = JSON.stringify(ids); + if ( argsSeqsIndices.has(seqKey) ) { + hostnamesMap.set(hn, argsSeqsIndices.get(seqKey)); + continue; + } + const seqIndex = argsSeqs.length; + argsSeqsIndices.set(seqKey, seqIndex); + hostnamesMap.set(hn, seqIndex); if ( typeof ids === 'number' ) { - hostnamesMap.set(hn, indexMap.get(ids)); + argsSeqs.push(indexMap.get(ids)); continue; } for ( let i = 0; i < ids.length; i++ ) { - ids[i] = indexMap.get(ids[i]); + argsSeqs.push(-indexMap.get(ids[i])); } + argsSeqs[argsSeqs.length-1] = -argsSeqs[argsSeqs.length-1]; } - return argsList; + return { argsList, argsSeqs }; } /******************************************************************************/ @@ -885,38 +954,21 @@ async function processCosmeticFilters(assetDetails, mapin) { const argsMap = domainBasedEntries.map(entry => [ entry[0], - { - a: entry[1].a ? entry[1].a.join(',\n') : undefined, - n: entry[1].n - } + entry[1].a ? entry[1].a.join('\n') : undefined, ]); const hostnamesMap = new Map(); + let hasEntities = false; for ( const [ id, details ] of domainBasedEntries ) { - if ( details.y === undefined ) { continue; } - scriptletHostnameToIdMap(details.y, id, hostnamesMap); - } - const argsList = argsMap2List(argsMap, hostnamesMap); - const entitiesMap = new Map(); - for ( const [ hn, details ] of hostnamesMap ) { - if ( hn.endsWith('.*') === false ) { continue; } - hostnamesMap.delete(hn); - entitiesMap.set(hn.slice(0, -2), details); - } - - // Extract exceptions from argsList, simplify argsList entries - const exceptionsMap = new Map(); - for ( let i = 0; i < argsList.length; i++ ) { - const details = argsList[i]; + if ( details.y ) { + scriptletHostnameToIdMap(details.y, id, hostnamesMap); + hasEntities ||= details.y.some(a => a.endsWith('.*')); + } if ( details.n ) { - for ( const hn of details.n ) { - if ( exceptionsMap.has(hn) === false ) { - exceptionsMap.set(hn, []); - } - exceptionsMap.get(hn).push(i); - } + scriptletHostnameToIdMap(details.n.map(a => `~${a}`), id, hostnamesMap); + hasEntities ||= details.n.some(a => a.endsWith('.*')); } - argsList[i] = details.a; } + const { argsList, argsSeqs } = argsMap2List(argsMap, hostnamesMap); const originalScriptletMap = await loadAllSourceScriptlets(); let patchedScriptlet = originalScriptletMap.get('css-specific').replace( @@ -928,16 +980,16 @@ async function processCosmeticFilters(assetDetails, mapin) { `${JSON.stringify(argsList, scriptletJsonReplacer)}` ); patchedScriptlet = safeReplace(patchedScriptlet, - /\bself\.\$hostnamesMap\$/, - `${JSON.stringify(hostnamesMap, scriptletJsonReplacer)}` + /\bself\.\$argsSeqs\$/, + `${JSON.stringify(argsSeqs, scriptletJsonReplacer)}` ); patchedScriptlet = safeReplace(patchedScriptlet, - /\bself\.\$entitiesMap\$/, - `${JSON.stringify(entitiesMap, scriptletJsonReplacer)}` + /\bself\.\$hostnamesMap\$/, + `${JSON.stringify(hostnamesMap, scriptletJsonReplacer)}` ); patchedScriptlet = safeReplace(patchedScriptlet, - /\bself\.\$exceptionsMap\$/, - `${JSON.stringify(exceptionsMap, scriptletJsonReplacer)}` + 'self.$hasEntities$', + JSON.stringify(hasEntities) ); writeFile(`${scriptletDir}/specific/${assetDetails.id}.js`, patchedScriptlet); generatedFiles.push(`${assetDetails.id}`); @@ -945,10 +997,9 @@ async function processCosmeticFilters(assetDetails, mapin) { if ( generatedFiles.length !== 0 ) { log(`CSS-specific: ${mapin.size} distinct filters`); log(`\tCombined into ${hostnamesMap.size} distinct hostnames`); - log(`\tCombined into ${entitiesMap.size} distinct entities`); } - return hostnamesMap.size + entitiesMap.size; + return hostnamesMap.size; } /******************************************************************************/ @@ -973,38 +1024,21 @@ async function processDeclarativeCosmeticFilters(assetDetails, mapin) { const argsMap = contentArray.map(entry => [ entry[0], - { - a: entry[1].a, - n: entry[1].n, - } + entry[1].a ? entry[1].a.join('\n') : undefined, ]); const hostnamesMap = new Map(); + let hasEntities = false; for ( const [ id, details ] of contentArray ) { - if ( details.y === undefined ) { continue; } - scriptletHostnameToIdMap(details.y, id, hostnamesMap); - } - const argsList = argsMap2List(argsMap, hostnamesMap); - const entitiesMap = new Map(); - for ( const [ hn, details ] of hostnamesMap ) { - if ( hn.endsWith('.*') === false ) { continue; } - hostnamesMap.delete(hn); - entitiesMap.set(hn.slice(0, -2), details); - } - - // Extract exceptions from argsList, simplify argsList entries - const exceptionsMap = new Map(); - for ( let i = 0; i < argsList.length; i++ ) { - const details = argsList[i]; + if ( details.y ) { + scriptletHostnameToIdMap(details.y, id, hostnamesMap); + hasEntities ||= details.y.some(a => a.endsWith('.*')); + } if ( details.n ) { - for ( const hn of details.n ) { - if ( exceptionsMap.has(hn) === false ) { - exceptionsMap.set(hn, []); - } - exceptionsMap.get(hn).push(i); - } + scriptletHostnameToIdMap(details.n.map(a => `~${a}`), id, hostnamesMap); + hasEntities ||= details.n.some(a => a.endsWith('.*')); } - argsList[i] = details.a; } + const { argsList, argsSeqs } = argsMap2List(argsMap, hostnamesMap); const originalScriptletMap = await loadAllSourceScriptlets(); let patchedScriptlet = originalScriptletMap.get('css-declarative').replace( @@ -1016,26 +1050,25 @@ async function processDeclarativeCosmeticFilters(assetDetails, mapin) { `${JSON.stringify(argsList, scriptletJsonReplacer)}` ); patchedScriptlet = safeReplace(patchedScriptlet, - /\bself\.\$hostnamesMap\$/, - `${JSON.stringify(hostnamesMap, scriptletJsonReplacer)}` + /\bself\.\$argsSeqs\$/, + `${JSON.stringify(argsSeqs, scriptletJsonReplacer)}` ); patchedScriptlet = safeReplace(patchedScriptlet, - /\bself\.\$entitiesMap\$/, - `${JSON.stringify(entitiesMap, scriptletJsonReplacer)}` + /\bself\.\$hostnamesMap\$/, + `${JSON.stringify(hostnamesMap, scriptletJsonReplacer)}` ); patchedScriptlet = safeReplace(patchedScriptlet, - /\bself\.\$exceptionsMap\$/, - `${JSON.stringify(exceptionsMap, scriptletJsonReplacer)}` + 'self.$hasEntities$', + JSON.stringify(hasEntities) ); writeFile(`${scriptletDir}/declarative/${assetDetails.id}.js`, patchedScriptlet); if ( contentArray.length !== 0 ) { log(`CSS-declarative: ${declaratives.size} distinct filters`); log(`\tCombined into ${hostnamesMap.size} distinct hostnames`); - log(`\tCombined into ${entitiesMap.size} distinct entities`); } - return hostnamesMap.size + entitiesMap.size; + return hostnamesMap.size; } /******************************************************************************/ @@ -1059,39 +1092,21 @@ async function processProceduralCosmeticFilters(assetDetails, mapin) { const argsMap = contentArray.map(entry => [ entry[0], - { - a: entry[1].a, - n: entry[1].n, - } + entry[1].a, ]); const hostnamesMap = new Map(); + let hasEntities = false; for ( const [ id, details ] of contentArray ) { - if ( details.y === undefined ) { continue; } - scriptletHostnameToIdMap(details.y, id, hostnamesMap); - } - const argsList = argsMap2List(argsMap, hostnamesMap); - const entitiesMap = new Map(); - for ( const [ hn, details ] of hostnamesMap ) { - if ( hn.endsWith('.*') === false ) { continue; } - hostnamesMap.delete(hn); - entitiesMap.set(hn.slice(0, -2), details); - } - - // Extract exceptions from argsList, simplify argsList entries - const exceptionsMap = new Map(); - for ( let i = 0; i < argsList.length; i++ ) { - const details = argsList[i]; + if ( details.y ) { + scriptletHostnameToIdMap(details.y, id, hostnamesMap); + hasEntities ||= details.y.some(a => a.endsWith('.*')); + } if ( details.n ) { - for ( const hn of details.n ) { - if ( exceptionsMap.has(hn) === false ) { - exceptionsMap.set(hn, []); - } - exceptionsMap.get(hn).push(i); - } + scriptletHostnameToIdMap(details.n.map(a => `~${a}`), id, hostnamesMap); + hasEntities ||= details.n.some(a => a.endsWith('.*')); } - argsList[i] = details.a; } - + const { argsList, argsSeqs } = argsMap2List(argsMap, hostnamesMap); const originalScriptletMap = await loadAllSourceScriptlets(); let patchedScriptlet = originalScriptletMap.get('css-procedural').replace( '$rulesetId$', @@ -1102,26 +1117,25 @@ async function processProceduralCosmeticFilters(assetDetails, mapin) { `${JSON.stringify(argsList, scriptletJsonReplacer)}` ); patchedScriptlet = safeReplace(patchedScriptlet, - /\bself\.\$hostnamesMap\$/, - `${JSON.stringify(hostnamesMap, scriptletJsonReplacer)}` + /\bself\.\$argsSeqs\$/, + `${JSON.stringify(argsSeqs, scriptletJsonReplacer)}` ); patchedScriptlet = safeReplace(patchedScriptlet, - /\bself\.\$entitiesMap\$/, - `${JSON.stringify(entitiesMap, scriptletJsonReplacer)}` + /\bself\.\$hostnamesMap\$/, + `${JSON.stringify(hostnamesMap, scriptletJsonReplacer)}` ); patchedScriptlet = safeReplace(patchedScriptlet, - /\bself\.\$exceptionsMap\$/, - `${JSON.stringify(exceptionsMap, scriptletJsonReplacer)}` + 'self.$hasEntities$', + JSON.stringify(hasEntities) ); writeFile(`${scriptletDir}/procedural/${assetDetails.id}.js`, patchedScriptlet); if ( contentArray.length !== 0 ) { log(`Procedural-related distinct filters: ${procedurals.size} distinct combined selectors`); log(`\tCombined into ${hostnamesMap.size} distinct hostnames`); - log(`\tCombined into ${entitiesMap.size} distinct entities`); } - return hostnamesMap.size + entitiesMap.size; + return hostnamesMap.size; } /******************************************************************************/ @@ -1233,13 +1247,14 @@ async function rulesetFromURLs(assetDetails) { const genericCosmeticStats = await processGenericCosmeticFilters( assetDetails, - results.genericCosmetic, - results.genericCosmeticExceptions + results.genericCosmeticFilters, + results.genericCosmeticExceptions, + declarativeCosmetic ); const genericHighCosmeticStats = await processGenericHighCosmeticFilters( assetDetails, - results.genericHighCosmetic, - results.genericCosmeticExceptions + results.genericCosmeticFilters, + results.genericCosmeticExceptions, ); const specificCosmeticStats = await processCosmeticFilters( assetDetails, diff --git a/platform/mv3/scriptlets/css-declarative.template.js b/platform/mv3/scriptlets/css-declarative.template.js index a4ac2a28080de..f7d467600af7f 100644 --- a/platform/mv3/scriptlets/css-declarative.template.js +++ b/platform/mv3/scriptlets/css-declarative.template.js @@ -28,15 +28,12 @@ /******************************************************************************/ const argsList = self.$argsList$; - +const argsSeqs = self.$argsSeqs$; const hostnamesMap = new Map(self.$hostnamesMap$); - -const entitiesMap = new Map(self.$entitiesMap$); - -const exceptionsMap = new Map(self.$exceptionsMap$); +const hasEntities = self.$hasEntities$; self.declarativeImports = self.declarativeImports || []; -self.declarativeImports.push({ argsList, hostnamesMap, entitiesMap, exceptionsMap }); +self.declarativeImports.push({ argsList, argsSeqs, hostnamesMap, hasEntities }); /******************************************************************************/ diff --git a/platform/mv3/scriptlets/css-generic.template.js b/platform/mv3/scriptlets/css-generic.template.js index 5c06ead0c2bda..4b535e98181a3 100644 --- a/platform/mv3/scriptlets/css-generic.template.js +++ b/platform/mv3/scriptlets/css-generic.template.js @@ -27,27 +27,40 @@ /******************************************************************************/ -const toImport = self.$genericSelectorMap$; - -const genericSelectorMap = self.genericSelectorMap || new Map(); - -if ( genericSelectorMap.size === 0 ) { - self.genericSelectorMap = new Map(toImport); - return; +const selectorsToImport = self.$genericSelectorMap$; +const exceptionsToImport = self.$genericExceptionMap$; + +if ( selectorsToImport ) { + const map = self.genericSelectorMap = + self.genericSelectorMap || new Map(); + + if ( map.size !== 0 ) { + for ( const entry of selectorsToImport ) { + const before = map.get(entry[0]); + map.set(entry[0], + before === undefined ? entry[1] : `${before},${entry[1]}` + ); + } + } else { + self.genericSelectorMap = new Map(selectorsToImport); + } + selectorsToImport.length = 0; } -for ( const toImportEntry of toImport ) { - const existing = genericSelectorMap.get(toImportEntry[0]); - genericSelectorMap.set( - toImportEntry[0], - existing === undefined - ? toImportEntry[1] - : `${existing},${toImportEntry[1]}` - ); +if ( exceptionsToImport ) { + const map = self.genericExceptionMap = + self.genericExceptionMap || new Map(); + + if ( map.size !== 0 ) { + for ( const entry of exceptionsToImport ) { + map.set(entry[0], `${map.get(entry[0]) || ''}${entry[1]}`); + } + } else { + self.genericExceptionMap = new Map(exceptionsToImport); + } + exceptionsToImport.length = 0; } -self.genericSelectorMap = genericSelectorMap; - /******************************************************************************/ })(); diff --git a/platform/mv3/scriptlets/css-procedural.template.js b/platform/mv3/scriptlets/css-procedural.template.js index f9f5682d51640..1928abdd488cc 100644 --- a/platform/mv3/scriptlets/css-procedural.template.js +++ b/platform/mv3/scriptlets/css-procedural.template.js @@ -28,15 +28,12 @@ /******************************************************************************/ const argsList = self.$argsList$; - +const argsSeqs = self.$argsSeqs$; const hostnamesMap = new Map(self.$hostnamesMap$); - -const entitiesMap = new Map(self.$entitiesMap$); - -const exceptionsMap = new Map(self.$exceptionsMap$); +const hasEntities = self.$hasEntities$; self.proceduralImports = self.proceduralImports || []; -self.proceduralImports.push({ argsList, hostnamesMap, entitiesMap, exceptionsMap }); +self.proceduralImports.push({ argsList, argsSeqs, hostnamesMap, hasEntities }); /******************************************************************************/ diff --git a/platform/mv3/scriptlets/css-specific.template.js b/platform/mv3/scriptlets/css-specific.template.js index e164e2e58f7de..f4c93cdf0f752 100644 --- a/platform/mv3/scriptlets/css-specific.template.js +++ b/platform/mv3/scriptlets/css-specific.template.js @@ -28,15 +28,12 @@ /******************************************************************************/ const argsList = self.$argsList$; - +const argsSeqs = self.$argsSeqs$; const hostnamesMap = new Map(self.$hostnamesMap$); - -const entitiesMap = new Map(self.$entitiesMap$); - -const exceptionsMap = new Map(self.$exceptionsMap$); +const hasEntities = self.$hasEntities$; self.specificImports = self.specificImports || []; -self.specificImports.push({ argsList, hostnamesMap, entitiesMap, exceptionsMap }); +self.specificImports.push({ argsList, argsSeqs, hostnamesMap, hasEntities }); /******************************************************************************/ diff --git a/src/js/static-dnr-filtering.js b/src/js/static-dnr-filtering.js index 2e75633ebf8fd..3f8081c7e28e5 100644 --- a/src/js/static-dnr-filtering.js +++ b/src/js/static-dnr-filtering.js @@ -31,19 +31,6 @@ import staticNetFilteringEngine from './static-net-filtering.js'; /******************************************************************************/ -// http://www.cse.yorku.ca/~oz/hash.html#djb2 -// Must mirror content script surveyor's version - -const hashFromStr = (type, s) => { - const len = s.length; - const step = len + 7 >>> 3; - let hash = (type << 5) + type ^ len; - for ( let i = 0; i < len; i += step ) { - hash = (hash << 5) + hash ^ s.charCodeAt(i); - } - return hash & 0xFFFFFF; -}; - const isRegex = hn => hn.startsWith('/') && hn.endsWith('/'); /******************************************************************************/ @@ -93,32 +80,19 @@ const keyFromSelector = selector => { function addGenericCosmeticFilter(context, selector, isException) { if ( selector === undefined ) { return; } if ( selector.length <= 1 ) { return; } - if ( isException ) { - if ( context.genericCosmeticExceptions === undefined ) { - context.genericCosmeticExceptions = new Set(); - } - context.genericCosmeticExceptions.add(selector); - return; - } if ( selector.charCodeAt(0) === 0x7B /* '{' */ ) { return; } const key = keyFromSelector(selector); - if ( key === undefined ) { - if ( context.genericHighCosmeticFilters === undefined ) { - context.genericHighCosmeticFilters = new Set(); + if ( isException ) { + if ( context.genericCosmeticExceptions === undefined ) { + context.genericCosmeticExceptions = []; } - context.genericHighCosmeticFilters.add(selector); + context.genericCosmeticExceptions.push({ key, selector }); return; } - const type = key.charCodeAt(0); - const hash = hashFromStr(type, key.slice(1)); if ( context.genericCosmeticFilters === undefined ) { - context.genericCosmeticFilters = new Map(); - } - let bucket = context.genericCosmeticFilters.get(hash); - if ( bucket === undefined ) { - context.genericCosmeticFilters.set(hash, bucket = []); + context.genericCosmeticFilters = []; } - bucket.push(selector); + context.genericCosmeticFilters.push({ key, selector }); } /******************************************************************************/ @@ -255,7 +229,10 @@ function addExtendedToDNR(context, parser) { if ( details === undefined ) { context.specificCosmeticFilters.set(compiled, details = {}); } - if ( exception ) { + if ( compiled.startsWith('{') === false ) { + details.key = keyFromSelector(compiled); + } + if ( exception || not ) { if ( details.excludeMatches === undefined ) { details.excludeMatches = []; } @@ -497,8 +474,7 @@ async function dnrRulesetFromRawLists(lists, options = {}) { await Promise.all(toLoad); const result = { network: staticNetFilteringEngine.dnrFromCompiled('end', context), - genericCosmetic: context.genericCosmeticFilters, - genericHighCosmetic: context.genericHighCosmeticFilters, + genericCosmeticFilters: context.genericCosmeticFilters, genericCosmeticExceptions: context.genericCosmeticExceptions, specificCosmetic: context.specificCosmeticFilters, scriptlet: context.scriptletFilters, From 4032ba01bf23a2319be1856ad7b3dd44f19dbad1 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Thu, 13 Mar 2025 13:27:21 -0400 Subject: [PATCH 0678/1099] [mv3] Separate name from version in report To make it easier to spot reports from forks of uBOL. --- platform/mv3/extension/js/report.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/platform/mv3/extension/js/report.js b/platform/mv3/extension/js/report.js index a7e29658fb78c..39fd60f671e09 100644 --- a/platform/mv3/extension/js/report.js +++ b/platform/mv3/extension/js/report.js @@ -112,7 +112,8 @@ async function reportSpecificFilterIssue() { const defaultMode = await sendMessage({ what: 'getDefaultFilteringMode' }); const modes = [ 'no filtering', 'basic', 'optimal', 'complete' ]; const config = { - version: `uBOL ${manifest.version}`, + name: manifest.name, + version: manifest.version, filtering: { 'site': `${modes[reportedPage.mode]}`, 'default': `${modes[defaultMode]}`, From 68962453ff6eec7ff109615a738beb8699b9844a Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Thu, 13 Mar 2025 19:02:20 -0400 Subject: [PATCH 0679/1099] [mv3] Eliminate false positives in new generic cosmetic exception code Related commit: https://github.com/gorhill/uBlock/commit/a009623d975415ec5a4f2ff973290479118850be --- .../mv3/extension/js/scripting/css-generic.js | 36 +++++++++---- platform/mv3/make-rulesets.js | 29 ++++++++--- .../mv3/scriptlets/css-generic.template.js | 51 +++++++++++++------ 3 files changed, 81 insertions(+), 35 deletions(-) diff --git a/platform/mv3/extension/js/scripting/css-generic.js b/platform/mv3/extension/js/scripting/css-generic.js index 5206e92a7503b..f242b417b650f 100644 --- a/platform/mv3/extension/js/scripting/css-generic.js +++ b/platform/mv3/extension/js/scripting/css-generic.js @@ -29,6 +29,9 @@ const genericSelectorMap = self.genericSelectorMap || new Map(); self.genericSelectorMap = undefined; if ( genericSelectorMap.size === 0 ) { return; } +const genericExceptionSieve = self.genericExceptionSieve || new Set(); +self.genericExceptionSieve = undefined; + const genericExceptionMap = self.genericExceptionMap || new Map(); self.genericExceptionMap = undefined; @@ -78,8 +81,11 @@ const uBOL_idFromNode = (node, out) => { const selectorList = genericSelectorMap.get(hash); if ( selectorList === undefined ) { return; } genericSelectorMap.delete(hash); - if ( isExcepted(hash) ) { return; } - out.push(selectorList); + if ( genericExceptionSieve.has(hash) ) { + applyExceptions(selectorList, out); + } else { + out.push(selectorList); + } }; // https://github.com/uBlockOrigin/uBlock-issues/discussions/2076 @@ -99,18 +105,26 @@ const uBOL_classesFromNode = (node, out) => { const selectorList = genericSelectorMap.get(hash); if ( selectorList === undefined ) { continue; } genericSelectorMap.delete(hash); - if ( isExcepted(hash) ) { continue; } - out.push(selectorList); + if ( genericExceptionSieve.has(hash) ) { + applyExceptions(selectorList, out); + } else { + out.push(selectorList); + } } }; -const isExcepted = hash => { - const hostnames = genericExceptionMap.get(hash); - if ( hostnames === undefined ) { return; } - const hasEntities = hostnames.includes('.*'); - return self.isolatedAPI.forEachHostname((hostname) => { - if ( hostnames.includes(` ${hostname} `) ) { return true; } - }, { hasEntities }); +const applyExceptions = (selectorList, out) => { + const selectors = new Set(selectorList.split(',\n')); + self.isolatedAPI.forEachHostname(hostname => { + const exceptions = genericExceptionMap.get(hostname); + if ( exceptions === undefined ) { return; } + for ( const exception of exceptions.split('\n') ) { + selectors.delete(exception); + } + if ( selectors.size === 0 ) { return true; } + }, { hasEntities: true }); + if ( selectors.size === 0 ) { return; } + out.push(Array.from(selectors).join(',\n')); } /******************************************************************************/ diff --git a/platform/mv3/make-rulesets.js b/platform/mv3/make-rulesets.js index f4e3808869c06..eb1ec62e52c98 100644 --- a/platform/mv3/make-rulesets.js +++ b/platform/mv3/make-rulesets.js @@ -700,25 +700,31 @@ async function processGenericCosmeticFilters( if ( selectors === undefined ) { genericSelectorMap.set(hash, selector) } else { - genericSelectorMap.set(hash, `${selectors},${selector}`) + genericSelectorMap.set(hash, `${selectors},\n${selector}`) } } } // Specific exceptions + const genericExceptionSieve = new Set(); const genericExceptionMap = new Map(); if ( declarativeMap ) { - for ( const details of declarativeMap.values() ) { + for ( const [ exception, details ] of declarativeMap ) { if ( details.rejected ) { continue; } if ( details.key === undefined ) { continue; } if ( details.matches !== undefined ) { continue; } if ( details.excludeMatches === undefined ) { continue; } const type = details.key.charCodeAt(0); const hash = hashFromStr(type, details.key.slice(1)); - const hostnames = genericExceptionMap.get(hash) ?? ''; - genericExceptionMap.set(hash, - `${hostnames} ${details.excludeMatches.join(' ')} ` - ); + genericExceptionSieve.add(hash); + for ( const hn of details.excludeMatches ) { + const exceptions = genericExceptionMap.get(hn); + if ( exceptions === undefined ) { + genericExceptionMap.set(hn, exception); + } else { + genericExceptionMap.set(hn, `${exceptions}\n${exception}`); + } + } } } @@ -735,6 +741,10 @@ async function processGenericCosmeticFilters( /\bself\.\$genericSelectorMap\$/, `${JSON.stringify(genericSelectorMap, scriptletJsonReplacer)}` ); + patchedScriptlet = safeReplace(patchedScriptlet, + /\bself\.\$genericExceptionSieve\$/, + `${JSON.stringify(genericExceptionSieve, scriptletJsonReplacer)}` + ); patchedScriptlet = safeReplace(patchedScriptlet, /\bself\.\$genericExceptionMap\$/, `${JSON.stringify(genericExceptionMap, scriptletJsonReplacer)}` @@ -745,10 +755,10 @@ async function processGenericCosmeticFilters( patchedScriptlet ); + log(`CSS-generic: ${genericExceptionSieve.size} specific CSS exceptions`); log(`CSS-generic: ${genericSelectorMap.size} plain CSS selectors`); - log(`CSS-generic: ${genericExceptionMap.size} specific CSS exceptions`); - return genericSelectorMap.size + genericExceptionMap.size; + return genericSelectorMap.size + genericExceptionSieve.size; } const hashFromStr = (type, s) => { @@ -1491,6 +1501,9 @@ async function main() { urls: [ 'https://ublockorigin.github.io/uBOL-home/tests/test-filters.txt' ], homeURL: 'https://ublockorigin.github.io/uBOL-home/tests/test-filters.html', filters: [ + '###gcf #gcf1 .fail', + '###gcf #gcf2 .fail', + 'ublockorigin.github.io,localhost#@##gcf #gcf2 .fail', ], }); diff --git a/platform/mv3/scriptlets/css-generic.template.js b/platform/mv3/scriptlets/css-generic.template.js index 4b535e98181a3..a353d58544016 100644 --- a/platform/mv3/scriptlets/css-generic.template.js +++ b/platform/mv3/scriptlets/css-generic.template.js @@ -27,38 +27,57 @@ /******************************************************************************/ -const selectorsToImport = self.$genericSelectorMap$; -const exceptionsToImport = self.$genericExceptionMap$; +const genericSelectorMap = self.$genericSelectorMap$; +const genericExceptionSieve = self.$genericExceptionSieve$; +const genericExceptionMap = self.$genericExceptionMap$; -if ( selectorsToImport ) { +if ( genericSelectorMap ) { const map = self.genericSelectorMap = self.genericSelectorMap || new Map(); - if ( map.size !== 0 ) { - for ( const entry of selectorsToImport ) { + for ( const entry of genericSelectorMap ) { const before = map.get(entry[0]); - map.set(entry[0], - before === undefined ? entry[1] : `${before},${entry[1]}` - ); + if ( before === undefined ) { + map.set(entry[0], entry[1]); + } else { + map.set(entry[0], `${before},\n${entry[1]}`); + } + } + } else { + self.genericSelectorMap = new Map(genericSelectorMap); + } + genericSelectorMap.length = 0; +} + +if ( genericExceptionSieve ) { + const hashes = self.genericExceptionSieve = + self.genericExceptionSieve || new Set(); + if ( hashes.size !== 0 ) { + for ( const hash of genericExceptionSieve ) { + hashes.add(hash); } } else { - self.genericSelectorMap = new Map(selectorsToImport); + self.genericExceptionSieve = new Set(genericExceptionSieve); } - selectorsToImport.length = 0; + genericExceptionSieve.length = 0; } -if ( exceptionsToImport ) { +if ( genericExceptionMap ) { const map = self.genericExceptionMap = self.genericExceptionMap || new Map(); - if ( map.size !== 0 ) { - for ( const entry of exceptionsToImport ) { - map.set(entry[0], `${map.get(entry[0]) || ''}${entry[1]}`); + for ( const entry of genericExceptionMap ) { + const before = map.get(entry[0]); + if ( before === undefined ) { + map.set(entry[0], entry[1]); + } else { + map.set(entry[0], `${before}\n${entry[1]}`); + } } } else { - self.genericExceptionMap = new Map(exceptionsToImport); + self.genericExceptionMap = new Map(genericExceptionMap); } - exceptionsToImport.length = 0; + genericExceptionMap.length = 0; } /******************************************************************************/ From 34cea709246a7f8c476f85d2fe34e5af7811298b Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Fri, 14 Mar 2025 10:47:14 -0400 Subject: [PATCH 0680/1099] Disable obsolete cache-control workaround for Firefox Related issue: https://github.com/uBlockOrigin/uBlock-issues/issues/3576 This is the first step, a trivial code change which disable the injection of `Cache-Control` header in Firefox. In the next dev cycle, the second step will be to remove all the code related to `Cache-Control` injection. --- src/js/background.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/js/background.js b/src/js/background.js index b648ee9c05ad6..058eee5e5debe 100644 --- a/src/js/background.js +++ b/src/js/background.js @@ -53,7 +53,7 @@ const hiddenSettingsDefault = { cacheStorageCompression: true, cacheStorageCompressionThreshold: 65536, cacheStorageMultithread: 2, - cacheControlForFirefox1376932: 'no-cache, no-store, must-revalidate', + cacheControlForFirefox1376932: 'unset', cloudStorageCompression: true, cnameIgnoreList: 'unset', cnameIgnore1stParty: true, From 592a79341df9c1a6cf6046a209a4af952c5294ef Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Fri, 14 Mar 2025 10:51:23 -0400 Subject: [PATCH 0681/1099] New revision for release candidate --- dist/version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dist/version b/dist/version index b6bab7b18d4ae..71dbb4137d678 100644 --- a/dist/version +++ b/dist/version @@ -1 +1 @@ -1.62.1.100 +1.62.1.101 From 69601b5c95af57b99402f42fa2dcfee297fee30b Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Fri, 14 Mar 2025 10:52:27 -0400 Subject: [PATCH 0682/1099] Update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 65bec0b8c5fcb..a5a8da1e91657 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,4 @@ +- [Disable obsolete cache-control workaround for Firefox](https://github.com/gorhill/uBlock/commit/34cea70924) - [Improve `overlay-buster` scriptlet](https://github.com/gorhill/uBlock/commit/fc231998b9) - [Add ability to inject scriptlets according to origin of ancestor contexts](https://github.com/gorhill/uBlock/commit/a483f7955f) - [Fix range parser in prevent-setTimeout scriptlet](https://github.com/gorhill/uBlock/commit/e636c32f2a) From db7210a24a59564bdda7c4daec49734e717c9096 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Fri, 14 Mar 2025 10:59:26 -0400 Subject: [PATCH 0683/1099] Import translation work from https://crowdin.com/project/ublock --- platform/mv3/extension/_locales/nb/messages.json | 6 +++--- src/_locales/nb/messages.json | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/platform/mv3/extension/_locales/nb/messages.json b/platform/mv3/extension/_locales/nb/messages.json index 52a4bd4d9e448..107513af638b5 100644 --- a/platform/mv3/extension/_locales/nb/messages.json +++ b/platform/mv3/extension/_locales/nb/messages.json @@ -136,7 +136,7 @@ "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option2": { - "message": "Har overlegg eller andre ulemper", + "message": "Har overlegg eller andre plager", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option3": { @@ -148,7 +148,7 @@ "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option5": { - "message": "Feilfunksjoner når uBO Lite er aktivert", + "message": "Fungerer ikke når uBO Lite er aktivert", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option6": { @@ -212,7 +212,7 @@ "description": "This describes the 'complete' filtering mode" }, "noFilteringModeDescription": { - "message": "Liste over vertsnavn der ingen filtrering vil finne sted", + "message": "Liste over nettsider der ingen filtrering vil finne sted.", "description": "A short description for the editable field which lists trusted sites" }, "noFilteringModePlaceholder": { diff --git a/src/_locales/nb/messages.json b/src/_locales/nb/messages.json index a9ce75ecedeb4..1eca2d5992419 100644 --- a/src/_locales/nb/messages.json +++ b/src/_locales/nb/messages.json @@ -900,11 +900,11 @@ "description": "Text for button which open an external webpage in Support pane" }, "supportReportSpecificButton": { - "message": "Opprett ny rapport", + "message": "Opprett ny rapport på GitHub", "description": "Text for button which open an external webpage in Support pane" }, "supportFindSpecificButton": { - "message": "Finn lignende rapporter", + "message": "Finn lignende rapporter på GitHub", "description": "A clickable link in the filter issue reporter section" }, "supportS1H": { @@ -964,7 +964,7 @@ "description": "Header of 'Report a filter issue' section in Support pane" }, "supportS6P1S1": { - "message": "For å unngå å belaste frivillige med duplikate rapporter, kontroller at problemet ikke allerede er rapportert.", + "message": "For å unngå å belaste de frivillige med dobbeltrapporter, må du kontrollere at problemet ikke allerede har blitt rapportert. Noter: ved å klikke på knappen vil sidens opprinnelse bli sendt til GitHub.", "description": "A paragraph in the filter issue reporter section" }, "supportS6P2S1": { From e84d3cd7a3d743a891716bab1177548947505d53 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Fri, 14 Mar 2025 11:06:11 -0400 Subject: [PATCH 0684/1099] Make Firefox dev build auto-update --- dist/firefox/updates.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dist/firefox/updates.json b/dist/firefox/updates.json index 95cb3297b9154..3e79897f76463 100644 --- a/dist/firefox/updates.json +++ b/dist/firefox/updates.json @@ -3,9 +3,9 @@ "uBlock0@raymondhill.net": { "updates": [ { - "version": "1.62.1.100", + "version": "1.62.1.101", "browser_specific_settings": { "gecko": { "strict_min_version": "79.0" } }, - "update_link": "https://github.com/gorhill/uBlock/releases/download/1.62.1rc0/uBlock0_1.62.1rc0.firefox.signed.xpi" + "update_link": "https://github.com/gorhill/uBlock/releases/download/1.62.1rc1/uBlock0_1.62.1rc1.firefox.signed.xpi" } ] } From b98a8d5c02c76b369b69031d128636f5500f21c3 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Fri, 14 Mar 2025 12:29:01 -0400 Subject: [PATCH 0685/1099] [mv3] Remove stray test filters --- platform/mv3/make-rulesets.js | 3 --- 1 file changed, 3 deletions(-) diff --git a/platform/mv3/make-rulesets.js b/platform/mv3/make-rulesets.js index eb1ec62e52c98..93a0758963c8d 100644 --- a/platform/mv3/make-rulesets.js +++ b/platform/mv3/make-rulesets.js @@ -1501,9 +1501,6 @@ async function main() { urls: [ 'https://ublockorigin.github.io/uBOL-home/tests/test-filters.txt' ], homeURL: 'https://ublockorigin.github.io/uBOL-home/tests/test-filters.html', filters: [ - '###gcf #gcf1 .fail', - '###gcf #gcf2 .fail', - 'ublockorigin.github.io,localhost#@##gcf #gcf2 .fail', ], }); From 9e946ce0c3cbbaf9c147c3d5e023f8d376a92632 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sat, 15 Mar 2025 07:50:28 -0400 Subject: [PATCH 0686/1099] Improve `trusted-override-element-method` scriptlet As discussed with filter list maintainers: https://github.com/uBlockOrigin/uBlock-discussions/discussions/933 --- src/js/resources/scriptlets.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/js/resources/scriptlets.js b/src/js/resources/scriptlets.js index f370e50b29001..c580f4e7b537e 100755 --- a/src/js/resources/scriptlets.js +++ b/src/js/resources/scriptlets.js @@ -3472,6 +3472,7 @@ function trustedOverrideElementMethod( if ( methodPath === '' ) { return; } const safe = safeSelf(); const logPrefix = safe.makeLogPrefix('trusted-override-element-method', methodPath, selector, disposition); + const extraArgs = safe.getExtraArgs(Array.from(arguments), 3); proxyApplyFn(methodPath, function(context) { let override = selector === ''; if ( override === false ) { @@ -3492,7 +3493,7 @@ function trustedOverrideElementMethod( if ( disposition === 'throw' ) { throw new ReferenceError(); } - return validateConstantFn(false, disposition); + return validateConstantFn(true, disposition, extraArgs); }); } From 1e2a7072d81d51a5ea21f738d95f51ebbaf1392f Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sat, 15 Mar 2025 07:53:02 -0400 Subject: [PATCH 0687/1099] Update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index a5a8da1e91657..7f4aaf8db0ce3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,4 @@ +- [Improve `trusted-override-element-method` scriptlet](https://github.com/gorhill/uBlock/commit/9e946ce0c3) - [Disable obsolete cache-control workaround for Firefox](https://github.com/gorhill/uBlock/commit/34cea70924) - [Improve `overlay-buster` scriptlet](https://github.com/gorhill/uBlock/commit/fc231998b9) - [Add ability to inject scriptlets according to origin of ancestor contexts](https://github.com/gorhill/uBlock/commit/a483f7955f) From 19cc3b127a403c3b17215f1575a1a4199dc54c14 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sat, 15 Mar 2025 07:53:37 -0400 Subject: [PATCH 0688/1099] New revision for release candidate --- dist/version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dist/version b/dist/version index 71dbb4137d678..50772059adb15 100644 --- a/dist/version +++ b/dist/version @@ -1 +1 @@ -1.62.1.101 +1.62.1.102 From 4d12ac788cb2b8c8ea63fee154805fe3447cdea6 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sat, 15 Mar 2025 08:06:22 -0400 Subject: [PATCH 0689/1099] Make Firefox dev build auto-update --- dist/firefox/updates.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dist/firefox/updates.json b/dist/firefox/updates.json index 3e79897f76463..4d9f672bf9004 100644 --- a/dist/firefox/updates.json +++ b/dist/firefox/updates.json @@ -3,9 +3,9 @@ "uBlock0@raymondhill.net": { "updates": [ { - "version": "1.62.1.101", + "version": "1.62.1.102", "browser_specific_settings": { "gecko": { "strict_min_version": "79.0" } }, - "update_link": "https://github.com/gorhill/uBlock/releases/download/1.62.1rc1/uBlock0_1.62.1rc1.firefox.signed.xpi" + "update_link": "https://github.com/gorhill/uBlock/releases/download/1.62.1rc2/uBlock0_1.62.1rc2.firefox.signed.xpi" } ] } From c3187d85e8136a8279b0f18bd3e93a6b2c1a34e1 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sat, 15 Mar 2025 13:29:26 -0400 Subject: [PATCH 0690/1099] [mv3] Avoid re-entrance when registering content scripts --- platform/mv3/extension/js/scripting-manager.js | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/platform/mv3/extension/js/scripting-manager.js b/platform/mv3/extension/js/scripting-manager.js index 1ca2528ea09f8..d387622e1d770 100644 --- a/platform/mv3/extension/js/scripting-manager.js +++ b/platform/mv3/extension/js/scripting-manager.js @@ -495,11 +495,12 @@ function registerScriptlet(context, scriptletDetails) { /******************************************************************************/ -async function registerInjectables(origins) { - void origins; - +async function registerInjectables() { if ( browser.scripting === undefined ) { return false; } + if ( registerInjectables.barrier ) { return true; } + registerInjectables.barrier = true; + const [ filteringModeDetails, rulesetsDetails, @@ -548,6 +549,8 @@ async function registerInjectables(origins) { .catch(reason => { console.info(reason); }); } + registerInjectables.barrier = false; + return true; } From fba662421d01ced3099e28cfcf8987d6e58eca93 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sat, 15 Mar 2025 15:06:51 -0400 Subject: [PATCH 0691/1099] [mv3] Make it easier to add filters for testing purpose --- platform/mv3/make-rulesets.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/platform/mv3/make-rulesets.js b/platform/mv3/make-rulesets.js index 93a0758963c8d..04799a23f71a3 100644 --- a/platform/mv3/make-rulesets.js +++ b/platform/mv3/make-rulesets.js @@ -1500,8 +1500,8 @@ async function main() { trusted: true, urls: [ 'https://ublockorigin.github.io/uBOL-home/tests/test-filters.txt' ], homeURL: 'https://ublockorigin.github.io/uBOL-home/tests/test-filters.html', - filters: [ - ], + filters: [` + `], }); // Regional rulesets From 4aebdbb0a9ce9708e2ebbcc2bd0baa29eaf36abd Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sun, 16 Mar 2025 05:50:53 -0400 Subject: [PATCH 0692/1099] Add quit button to element zapper mode Related issue: https://github.com/uBlockOrigin/uBlock-issues/issues/1968 Given the availability of this new quit button, the element zapper will now default to stick around after zapping an element. --- src/css/epicker-ui.css | 16 +++++++++++++--- src/js/epicker-ui.js | 5 +++-- 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/src/css/epicker-ui.css b/src/css/epicker-ui.css index 1923142b2f7d5..7edbfe84524cf 100644 --- a/src/css/epicker-ui.css +++ b/src/css/epicker-ui.css @@ -16,7 +16,7 @@ html#ublock0-epicker, border: 1px solid var(--border-2); box-sizing: border-box; cursor: default; - display: none; + display: flex; flex-direction: column; max-width: min(32rem, 100vw - 4px); min-width: min(24rem, 100vw - 4px); @@ -25,8 +25,18 @@ html#ublock0-epicker, width: min(32rem, 100vw - 4px); z-index: 100; } -#ublock0-epicker:not(.zap) aside { - display: flex; +#ublock0-epicker.zap aside { + min-width: unset !important; + width: unset !important; +} +#ublock0-epicker.zap aside > section, +#ublock0-epicker.zap aside > ul, +#ublock0-epicker.zap aside > #windowbar > div:not(#quit) { + display: none; +} +#ublock0-epicker.zap aside > #windowbar > #quit { + height: 2.5em; + width: 2.5em; } #ublock0-epicker:not(.paused) aside, #ublock0-epicker.minimized aside { diff --git a/src/js/epicker-ui.js b/src/js/epicker-ui.js index d2b14df65dce0..d3235e92182f6 100644 --- a/src/js/epicker-ui.js +++ b/src/js/epicker-ui.js @@ -331,7 +331,7 @@ const onSvgClicked = function(ev) { mx: ev.clientX, my: ev.clientY, options: { - stay: ev.shiftKey || ev.type === 'touch', + stay: true, highlight: ev.target !== svgIslands, }, }); @@ -822,6 +822,8 @@ const startPicker = function() { unpausePicker(); + $id('quit').addEventListener('click', onQuitClicked); + if ( pickerRoot.classList.contains('zap') ) { return; } cmEditor.on('changes', onCandidateChanged); @@ -837,7 +839,6 @@ const startPicker = function() { dom.cl.toggle(pickerRoot, 'minimized'); } }); - $id('quit').addEventListener('click', onQuitClicked); $id('move').addEventListener('mousedown', onStartMoving); $id('move').addEventListener('touchstart', onStartMoving); $id('candidateFilters').addEventListener('click', onCandidateClicked); From 2d559c43db15e3f5e624dab27d209043e0c5730b Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sun, 16 Mar 2025 05:56:17 -0400 Subject: [PATCH 0693/1099] Update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7f4aaf8db0ce3..34a566d0ea3ad 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,4 @@ +- [Add quit button to element zapper mode](https://github.com/gorhill/uBlock/commit/4aebdbb0a9) - [Improve `trusted-override-element-method` scriptlet](https://github.com/gorhill/uBlock/commit/9e946ce0c3) - [Disable obsolete cache-control workaround for Firefox](https://github.com/gorhill/uBlock/commit/34cea70924) - [Improve `overlay-buster` scriptlet](https://github.com/gorhill/uBlock/commit/fc231998b9) From 99cb84fab6bec0acb881f1808221119cb789b776 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sun, 16 Mar 2025 05:56:45 -0400 Subject: [PATCH 0694/1099] New revision for release candidate --- dist/version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dist/version b/dist/version index 50772059adb15..50e28817fc987 100644 --- a/dist/version +++ b/dist/version @@ -1 +1 @@ -1.62.1.102 +1.62.1.103 From 0e8de98411fd62c9aac1537056b50306b5e6c501 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sun, 16 Mar 2025 06:01:17 -0400 Subject: [PATCH 0695/1099] Make Firefox dev build auto-update --- dist/firefox/updates.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dist/firefox/updates.json b/dist/firefox/updates.json index 4d9f672bf9004..8d818becb2e34 100644 --- a/dist/firefox/updates.json +++ b/dist/firefox/updates.json @@ -3,9 +3,9 @@ "uBlock0@raymondhill.net": { "updates": [ { - "version": "1.62.1.102", + "version": "1.62.1.103", "browser_specific_settings": { "gecko": { "strict_min_version": "79.0" } }, - "update_link": "https://github.com/gorhill/uBlock/releases/download/1.62.1rc2/uBlock0_1.62.1rc2.firefox.signed.xpi" + "update_link": "https://github.com/gorhill/uBlock/releases/download/1.62.1rc3/uBlock0_1.62.1rc3.firefox.signed.xpi" } ] } From d36ea89a0288f55f3c68d60db7bb81fa0c7f9ed5 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Mon, 17 Mar 2025 08:02:30 -0400 Subject: [PATCH 0696/1099] Improve `prevent-set[Timeout|Interval]` scriptlets --- src/js/resources/prevent-settimeout.js | 12 ++++++------ src/js/resources/safe-self.js | 1 + 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/js/resources/prevent-settimeout.js b/src/js/resources/prevent-settimeout.js index c137f0dc70476..289c4465974c1 100644 --- a/src/js/resources/prevent-settimeout.js +++ b/src/js/resources/prevent-settimeout.js @@ -95,8 +95,8 @@ export function preventSetTimeout( proxyApplyFn('setTimeout', function(context) { const { callArgs } = context; const a = callArgs[0] instanceof Function - ? String(safe.Function_toString(callArgs[0])) - : String(callArgs[0]); + ? safe.String(safe.Function_toString(callArgs[0])) + : safe.String(callArgs[0]); const b = callArgs[1]; if ( needleRaw === '' && range.unbound() ) { safe.uboLog(logPrefix, `Called:\n${a}\n${b}`); @@ -159,8 +159,8 @@ export function preventSetInterval( proxyApplyFn('setInterval', function(context) { const { callArgs } = context; const a = callArgs[0] instanceof Function - ? String(safe.Function_toString(callArgs[0])) - : String(callArgs[0]); + ? safe.String(safe.Function_toString(callArgs[0])) + : safe.String(callArgs[0]); const b = callArgs[1]; if ( needleRaw === '' && range.unbound() ) { safe.uboLog(logPrefix, `Called:\n${a}\n${b}`); @@ -212,8 +212,8 @@ export function preventRequestAnimationFrame( proxyApplyFn('requestAnimationFrame', function(context) { const { callArgs } = context; const a = callArgs[0] instanceof Function - ? String(safe.Function_toString(callArgs[0])) - : String(callArgs[0]); + ? safe.String(safe.Function_toString(callArgs[0])) + : safe.String(callArgs[0]); if ( needleRaw === '' ) { safe.uboLog(logPrefix, `Called:\n${a}`); } else if ( reNeedle.test(a) !== needleNot ) { diff --git a/src/js/resources/safe-self.js b/src/js/resources/safe-self.js index 3766f3e194e40..6b6d72eeba26c 100644 --- a/src/js/resources/safe-self.js +++ b/src/js/resources/safe-self.js @@ -50,6 +50,7 @@ export function safeSelf() { 'RegExp_test': self.RegExp.prototype.test, 'RegExp_exec': self.RegExp.prototype.exec, 'Request_clone': self.Request.prototype.clone, + 'String': self.String, 'String_fromCharCode': String.fromCharCode, 'String_split': String.prototype.split, 'XMLHttpRequest': self.XMLHttpRequest, From dfe3d48fa3ea5e2d18f637c0416984e76314f9e9 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Mon, 17 Mar 2025 08:06:03 -0400 Subject: [PATCH 0697/1099] Update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 34a566d0ea3ad..0b137f44c0d73 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,4 @@ +- [Improve `prevent-set[Timeout|Interval]` scriptlets](https://github.com/gorhill/uBlock/commit/d36ea89a02) - [Add quit button to element zapper mode](https://github.com/gorhill/uBlock/commit/4aebdbb0a9) - [Improve `trusted-override-element-method` scriptlet](https://github.com/gorhill/uBlock/commit/9e946ce0c3) - [Disable obsolete cache-control workaround for Firefox](https://github.com/gorhill/uBlock/commit/34cea70924) From 839e240ec12849d97e9b4425ea96593b02a727ac Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Mon, 17 Mar 2025 16:22:53 -0400 Subject: [PATCH 0698/1099] New stable release version --- dist/version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dist/version b/dist/version index 50e28817fc987..af92bdd9f58d1 100644 --- a/dist/version +++ b/dist/version @@ -1 +1 @@ -1.62.1.103 +1.63.0 From 98b011f64c01442556b273eba8561e8479a743fa Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Tue, 18 Mar 2025 17:15:35 -0400 Subject: [PATCH 0699/1099] [mv3] Add support for explicit `generichide` filter option `generichide` option is implicitly enforced on all sites unless an exception overrides `generichide`. Though rare, sometimes a `generichide` exception needs to be overridden so that generic cosmetic filtering is made possible on a specific site. This commit is to add support for restoring generic cosmetic filtering on sites which were excluded through a `generichide` exception. Concretely, this is needed to ensure the test suite can properly verify that generic cosmetic filtering is working when the filtering mode is set to "complete": ||ublockorigin.github.io^$generichide,important --- .../mv3/extension/js/scripting-manager.js | 144 +++++++++++++----- platform/mv3/extension/js/utils.js | 11 +- platform/mv3/make-rulesets.js | 23 ++- src/js/static-net-filtering.js | 22 +++ 4 files changed, 146 insertions(+), 54 deletions(-) diff --git a/platform/mv3/extension/js/scripting-manager.js b/platform/mv3/extension/js/scripting-manager.js index d387622e1d770..e22a7c53092d8 100644 --- a/platform/mv3/extension/js/scripting-manager.js +++ b/platform/mv3/extension/js/scripting-manager.js @@ -66,6 +66,15 @@ const arrayEq = (a = [], b = [], sort = true) => { /******************************************************************************/ +const normalizeMatches = matches => { + if ( matches.length <= 1 ) { return; } + if ( matches.includes('') === false ) { return; } + matches.length = 0; + matches.push(''); +}; + +/******************************************************************************/ + // The extensions API does not always return exactly what we fed it, so we // need to normalize some entries to be sure we properly detect changes when // comparing registered entries vs. entries to register. @@ -93,11 +102,17 @@ function registerHighGeneric(context, genericDetails) { const { before, filteringModeDetails, rulesetsDetails } = context; const excludeHostnames = []; + const includeHostnames = []; const css = []; for ( const details of rulesetsDetails ) { const hostnames = genericDetails.get(details.id); - if ( hostnames !== undefined ) { - excludeHostnames.push(...hostnames); + if ( hostnames ) { + if ( hostnames.unhide ) { + excludeHostnames.push(...hostnames.unhide); + } + if ( hostnames.hide ) { + includeHostnames.push(...hostnames.hide); + } } const count = details.css?.generichigh || 0; if ( count === 0 ) { continue; } @@ -165,12 +180,18 @@ function registerHighGeneric(context, genericDetails) { function registerGeneric(context, genericDetails) { const { before, filteringModeDetails, rulesetsDetails } = context; - const excludeHostnames = []; + const excludedByFilter = []; + const includedByFilter = []; const js = []; for ( const details of rulesetsDetails ) { const hostnames = genericDetails.get(details.id); - if ( hostnames !== undefined ) { - excludeHostnames.push(...hostnames); + if ( hostnames ) { + if ( hostnames.unhide ) { + excludedByFilter.push(...hostnames.unhide); + } + if ( hostnames.hide ) { + includedByFilter.push(...hostnames.hide); + } } const count = details.css?.generic || 0; if ( count === 0 ) { continue; } @@ -183,53 +204,86 @@ function registerGeneric(context, genericDetails) { js.push('/js/scripting/css-generic.js'); const { none, basic, optimal, complete } = filteringModeDetails; - const matches = []; - const excludeMatches = []; - if ( complete.has('all-urls') ) { - excludeMatches.push(...ut.matchesFromHostnames(none)); - excludeMatches.push(...ut.matchesFromHostnames(basic)); - excludeMatches.push(...ut.matchesFromHostnames(optimal)); - excludeMatches.push(...ut.matchesFromHostnames(excludeHostnames)); - matches.push(''); - } else { - matches.push( + const includedByMode = [ ...complete ]; + const excludedByMode = [ ...none, ...basic, ...optimal ]; + + if ( complete.has('all-urls') === false ) { + const matches = [ ...ut.matchesFromHostnames( - ut.subtractHostnameIters( - Array.from(complete), - excludeHostnames - ) - ) - ); + ut.subtractHostnameIters(includedByMode, excludedByFilter) + ), + ...ut.matchesFromHostnames( + ut.intersectHostnameIters(includedByMode, includedByFilter) + ), + ]; + if ( matches.length === 0 ) { return; } + const registered = before.get('css-generic-some'); + before.delete('css-generic-some'); // Important! + const directive = { + id: 'css-generic-some', + js, + allFrames: true, + matches, + runAt: 'document_idle', + }; + if ( registered === undefined ) { // register + context.toAdd.push(directive); + } else if ( // update + arrayEq(registered.js, js, false) === false || + arrayEq(registered.matches, directive.matches) === false + ) { + context.toRemove.push('css-generic-some'); + context.toAdd.push(directive); + } + return; } - if ( matches.length === 0 ) { return; } - - const registered = before.get('css-generic'); - before.delete('css-generic'); // Important! - - const directive = { - id: 'css-generic', + const excludeMatches = [ + ...ut.matchesFromHostnames(excludedByMode), + ...ut.matchesFromHostnames(excludedByFilter), + ]; + const registeredAll = before.get('css-generic-all'); + before.delete('css-generic-all'); // Important! + const directiveAll = { + id: 'css-generic-all', js, allFrames: true, - matches, + matches: [ '' ], excludeMatches, runAt: 'document_idle', }; - - // register - if ( registered === undefined ) { - context.toAdd.push(directive); - return; + if ( registeredAll === undefined ) { // register + context.toAdd.push(directiveAll); + } else if ( // update + arrayEq(registeredAll.js, js, false) === false || + arrayEq(registeredAll.excludeMatches, directiveAll.excludeMatches) === false + ) { + context.toRemove.push('css-generic-all'); + context.toAdd.push(directiveAll); } - - // update - if ( - arrayEq(registered.js, js, false) === false || - arrayEq(registered.matches, matches) === false || - arrayEq(registered.excludeMatches, excludeMatches) === false + const matches = [ + ...ut.matchesFromHostnames( + ut.subtractHostnameIters(includedByFilter, excludedByMode) + ), + ]; + if ( matches.length === 0 ) { return; } + const registeredSome = before.get('css-generic-some'); + before.delete('css-generic-some'); // Important! + const directiveSome = { + id: 'css-generic-some', + js, + allFrames: true, + matches, + runAt: 'document_idle', + }; + if ( registeredSome === undefined ) { // register + context.toAdd.push(directiveSome); + } else if ( // update + arrayEq(registeredSome.js, js, false) === false || + arrayEq(registeredSome.matches, directiveSome.matches) === false ) { - context.toRemove.push('css-generic'); - context.toAdd.push(directive); + context.toRemove.push('css-generic-some'); + context.toAdd.push(directiveSome); } } @@ -253,6 +307,8 @@ function registerProcedural(context) { ]; if ( matches.length === 0 ) { return; } + normalizeMatches(matches); + js.unshift('/js/scripting/isolated-api.js'); js.push('/js/scripting/css-procedural.js'); @@ -313,6 +369,8 @@ function registerDeclarative(context) { ]; if ( matches.length === 0 ) { return; } + normalizeMatches(matches); + js.unshift('/js/scripting/isolated-api.js'); js.push('/js/scripting/css-declarative.js'); @@ -373,6 +431,8 @@ function registerSpecific(context) { ]; if ( matches.length === 0 ) { return; } + normalizeMatches(matches); + js.unshift('/js/scripting/isolated-api.js'); js.push('/js/scripting/css-specific.js'); diff --git a/platform/mv3/extension/js/utils.js b/platform/mv3/extension/js/utils.js index dfbe235f33777..f6f58fb90dce9 100644 --- a/platform/mv3/extension/js/utils.js +++ b/platform/mv3/extension/js/utils.js @@ -100,15 +100,13 @@ const subtractHostnameIters = (itera, iterb) => { /******************************************************************************/ +const matchFromHostname = hn => + hn === '*' || hn === 'all-urls' ? '' : `*://*.${hn}/*`; + const matchesFromHostnames = hostnames => { const out = []; for ( const hn of hostnames ) { - if ( hn === '*' || hn === 'all-urls' ) { - out.length = 0; - out.push(''); - break; - } - out.push(`*://*.${hn}/*`); + out.push(matchFromHostname(hn)); } return out; }; @@ -144,6 +142,7 @@ export { isDescendantHostnameOfIter, intersectHostnameIters, subtractHostnameIters, + matchFromHostname, matchesFromHostnames, hostnamesFromMatches, }; diff --git a/platform/mv3/make-rulesets.js b/platform/mv3/make-rulesets.js index 04799a23f71a3..0a8e34c82626d 100644 --- a/platform/mv3/make-rulesets.js +++ b/platform/mv3/make-rulesets.js @@ -1245,14 +1245,25 @@ async function rulesetFromURLs(assetDetails) { log(rejectedCosmetic.map(line => `\t${line}`).join('\n'), true); } + const genericDetailsForRuleset = {}; if ( Array.isArray(results.network.generichideExclusions) && results.network.generichideExclusions.length !== 0 ) { - genericDetails.set( - assetDetails.id, - results.network.generichideExclusions.filter(hn => hn.endsWith('.*') === false).sort() - ); + genericDetailsForRuleset.unhide = results.network.generichideExclusions + .filter(hn => hn.endsWith('.*') === false) + .sort(); + } + if ( + Array.isArray(results.network.generichideInclusions) && + results.network.generichideInclusions.length !== 0 + ) { + genericDetailsForRuleset.hide = results.network.generichideInclusions + .filter(hn => hn.endsWith('.*') === false) + .sort(); + } + if ( genericDetailsForRuleset.unhide || genericDetailsForRuleset.hide ) { + genericDetails.set(assetDetails.id, genericDetailsForRuleset); } const genericCosmeticStats = await processGenericCosmeticFilters( @@ -1367,8 +1378,8 @@ async function main() { ], dnrURL: 'https://ublockorigin.github.io/uAssets/dnr/default.json', homeURL: 'https://github.com/uBlockOrigin/uAssets', - filters: [ - ], + filters: [` + `], }); await rulesetFromURLs({ diff --git a/src/js/static-net-filtering.js b/src/js/static-net-filtering.js index b24f0ffa0f000..acfb5d14452f6 100644 --- a/src/js/static-net-filtering.js +++ b/src/js/static-net-filtering.js @@ -4511,6 +4511,7 @@ StaticNetFilteringEngine.prototype.dnrFromCompiled = function(op, context, ...ar // Collect generichide filters const generichideExclusions = []; + const generichideInclusions = []; { const bucket = buckets.get(ALLOW_REALM | typeNameToTypeValue['generichide']); if ( bucket ) { @@ -4522,6 +4523,26 @@ StaticNetFilteringEngine.prototype.dnrFromCompiled = function(op, context, ...ar } else if ( rule.condition.requestDomains ) { generichideExclusions.push(...rule.condition.requestDomains); } + if ( rule.condition.excludedInitiatorDomains ) { + generichideInclusions.push(...rule.condition.excludedInitiatorDomains); + } else if ( rule.condition.excludedRequestDomains ) { + generichideInclusions.push(...rule.condition.excludedRequestDomains); + } + } + } + } + } + { + const bucket = buckets.get(BLOCKIMPORTANT_REALM | typeNameToTypeValue['generichide']); + if ( bucket ) { + for ( const rules of bucket.values() ) { + for ( const rule of rules ) { + if ( rule.condition === undefined ) { continue; } + if ( rule.condition.initiatorDomains ) { + generichideInclusions.push(...rule.condition.initiatorDomains); + } else if ( rule.condition.requestDomains ) { + generichideInclusions.push(...rule.condition.requestDomains); + } } } } @@ -4714,6 +4735,7 @@ StaticNetFilteringEngine.prototype.dnrFromCompiled = function(op, context, ...ar acceptedFilterCount: context.acceptedFilterCount, rejectedFilterCount: context.rejectedFilterCount, generichideExclusions: Array.from(new Set(generichideExclusions)), + generichideInclusions: Array.from(new Set(generichideInclusions)), }; }; From fe744816f1df077fae764e15e5bb5bd0f1c1edc1 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Wed, 19 Mar 2025 12:34:03 -0400 Subject: [PATCH 0700/1099] Add `prevent-innerHTML` scriptlet @description Conditionally prevent assignment to `innerHTML` property. @param [selector] Optional. The element must matches `selector` for the prevention to take place. @param [pattern] Optional. A pattern to match against the assigned value. The pattern can be a plain string, or a regex. Prepend with `!` to reverse the match condition. As discussed with filter list volunteers. --- src/js/resources/prevent-innerHTML.js | 79 +++++++++++++++++++++++++++ src/js/resources/scriptlets.js | 3 +- 2 files changed, 81 insertions(+), 1 deletion(-) create mode 100644 src/js/resources/prevent-innerHTML.js diff --git a/src/js/resources/prevent-innerHTML.js b/src/js/resources/prevent-innerHTML.js new file mode 100644 index 0000000000000..0ab40a11e7ef8 --- /dev/null +++ b/src/js/resources/prevent-innerHTML.js @@ -0,0 +1,79 @@ +/******************************************************************************* + + uBlock Origin - a comprehensive, efficient content blocker + Copyright (C) 2025-present Raymond Hill + + 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/}. + + Home: https://github.com/gorhill/uBlock + +*/ + +import { registerScriptlet } from './base.js'; +import { safeSelf } from './safe-self.js'; + +/** + * @scriptlet prevent-innerHTML + * + * @description + * Conditionally prevent assignment to `innerHTML` property. + * + * @param [selector] + * Optional. The element must matches `selector` for the prevention to take + * place. + * + * @param [pattern] + * Optional. A pattern to match against the assigned value. The pattern can be + * a plain string, or a regex. Prepend with `!` to reverse the match condition. + * + * */ + +export function preventInnerHTML( + selector = '', + pattern = '' +) { + const safe = safeSelf(); + const logPrefix = safe.makeLogPrefix('prevent-innerHTML', selector, pattern); + const matcher = safe.initPattern(pattern, { canNegate: true }); + const current = safe.Object_getOwnPropertyDescriptor(Element.prototype, 'innerHTML'); + if ( current === undefined ) { return; } + const shouldPreventSet = a => { + if ( selector !== '' ) { + if ( typeof this.matches === 'function' === false ) { return false; } + if ( this.matches(selector) === false ) { return false; } + } + return safe.testPattern(matcher, a); + }; + Object.defineProperty(Element.prototype, 'innerHTML', { + get: function() { + return current.get + ? current.get.call(this) + : current.value; + }, + set: function(a) { + if ( shouldPreventSet(a) ) { + safe.uboLog(logPrefix, 'Prevented'); + } else if ( current.set ) { + current.set.call(this, a); + } + current.value = a; + }, + }); +} +registerScriptlet(preventInnerHTML, { + name: 'prevent-innerHTML.js', + dependencies: [ + safeSelf, + ], +}); diff --git a/src/js/resources/scriptlets.js b/src/js/resources/scriptlets.js index c580f4e7b537e..1f4a8de67b2b6 100755 --- a/src/js/resources/scriptlets.js +++ b/src/js/resources/scriptlets.js @@ -23,9 +23,10 @@ import './attribute.js'; import './href-sanitizer.js'; import './noeval.js'; +import './prevent-innerHTML.js'; +import './prevent-settimeout.js'; import './replace-argument.js'; import './spoof-css.js'; -import './prevent-settimeout.js'; import { runAt, runAtHtmlElementFn } from './run-at.js'; From ebec5a1865f5317a70f70f8e3eb6df7522b9d7e0 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Wed, 19 Mar 2025 12:36:21 -0400 Subject: [PATCH 0701/1099] New revision for dev build --- dist/version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dist/version b/dist/version index af92bdd9f58d1..32e128e50ae6c 100644 --- a/dist/version +++ b/dist/version @@ -1 +1 @@ -1.63.0 +1.63.1.0 From eda245a1a6eefdea67816deed359a6719ba3cf82 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Wed, 19 Mar 2025 12:37:52 -0400 Subject: [PATCH 0702/1099] Update changelog --- CHANGELOG.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0b137f44c0d73..adfcebbdce0e6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,11 @@ +* [Add prevent-innerHTML scriptlet](https://github.com/gorhill/uBlock/commit/fe744816f1) + +---------- + +# 1.63.0 + +## Fixes / changes + - [Improve `prevent-set[Timeout|Interval]` scriptlets](https://github.com/gorhill/uBlock/commit/d36ea89a02) - [Add quit button to element zapper mode](https://github.com/gorhill/uBlock/commit/4aebdbb0a9) - [Improve `trusted-override-element-method` scriptlet](https://github.com/gorhill/uBlock/commit/9e946ce0c3) From 18349292a6273fb452064996a2c03055e3b60e79 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Wed, 19 Mar 2025 12:43:36 -0400 Subject: [PATCH 0703/1099] Improve logging ability in new `prevent-innerHTML` scriptlet --- src/js/resources/prevent-innerHTML.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/js/resources/prevent-innerHTML.js b/src/js/resources/prevent-innerHTML.js index 0ab40a11e7ef8..e3d3b1f5b5496 100644 --- a/src/js/resources/prevent-innerHTML.js +++ b/src/js/resources/prevent-innerHTML.js @@ -67,6 +67,9 @@ export function preventInnerHTML( } else if ( current.set ) { current.set.call(this, a); } + if ( safe.logLevel > 1 ) { + safe.uboLog(logPrefix, `Assigned:\n${a}`); + } current.value = a; }, }); From 94f73015ed9110ad4539207c958e6b772928f71a Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Wed, 19 Mar 2025 12:51:26 -0400 Subject: [PATCH 0704/1099] Make Firefox dev build auto-update --- dist/firefox/updates.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dist/firefox/updates.json b/dist/firefox/updates.json index 8d818becb2e34..daefd17193091 100644 --- a/dist/firefox/updates.json +++ b/dist/firefox/updates.json @@ -3,9 +3,9 @@ "uBlock0@raymondhill.net": { "updates": [ { - "version": "1.62.1.103", + "version": "1.63.1.0", "browser_specific_settings": { "gecko": { "strict_min_version": "79.0" } }, - "update_link": "https://github.com/gorhill/uBlock/releases/download/1.62.1rc3/uBlock0_1.62.1rc3.firefox.signed.xpi" + "update_link": "https://github.com/gorhill/uBlock/releases/download/1.63.1b0/uBlock0_1.63.1b0.firefox.signed.xpi" } ] } From 76b80baaeabc1732bbe970708a142dbad86509df Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Thu, 20 Mar 2025 07:14:38 -0400 Subject: [PATCH 0705/1099] Fix TypedArray overflow Related issue: https://github.com/uBlockOrigin/uBlock-issues/issues/3582 --- src/js/static-net-filtering.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/js/static-net-filtering.js b/src/js/static-net-filtering.js index acfb5d14452f6..a5973f525c1ac 100644 --- a/src/js/static-net-filtering.js +++ b/src/js/static-net-filtering.js @@ -3100,7 +3100,7 @@ const urlTokenizer = new (class { this._hasQuery = 0; // https://www.reddit.com/r/uBlockOrigin/comments/dzw57l/ // Remember: 1 token needs two slots - this._tokens = new Uint32Array(2064); + this._tokens = new Uint32Array(bidiTrie.haystack.length + 16); this.knownTokens = new Uint8Array(65536); this.resetKnownTokens(); From b93871117ccb8a9f707977bb42bd799c900714a9 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Thu, 20 Mar 2025 07:22:50 -0400 Subject: [PATCH 0706/1099] New revision for dev build --- dist/version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dist/version b/dist/version index 32e128e50ae6c..fba35fe9b1b23 100644 --- a/dist/version +++ b/dist/version @@ -1 +1 @@ -1.63.1.0 +1.63.1.1 From d8cda00cd344c14888758dc8e99e108014a7486c Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Thu, 20 Mar 2025 07:23:48 -0400 Subject: [PATCH 0707/1099] Update changelog --- CHANGELOG.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index adfcebbdce0e6..9f4ed009cf460 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,5 @@ -* [Add prevent-innerHTML scriptlet](https://github.com/gorhill/uBlock/commit/fe744816f1) +- [Fix TypedArray overflow](https://github.com/gorhill/uBlock/commit/76b80baaea) +- [Add prevent-innerHTML scriptlet](https://github.com/gorhill/uBlock/commit/fe744816f1) ---------- From e7651f73f42d6d341420db8511ef914e03103d7d Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Thu, 20 Mar 2025 07:36:34 -0400 Subject: [PATCH 0708/1099] Make Firefox dev build auto-update --- dist/firefox/updates.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dist/firefox/updates.json b/dist/firefox/updates.json index daefd17193091..b3a4c53608122 100644 --- a/dist/firefox/updates.json +++ b/dist/firefox/updates.json @@ -3,9 +3,9 @@ "uBlock0@raymondhill.net": { "updates": [ { - "version": "1.63.1.0", + "version": "1.63.1.1", "browser_specific_settings": { "gecko": { "strict_min_version": "79.0" } }, - "update_link": "https://github.com/gorhill/uBlock/releases/download/1.63.1b0/uBlock0_1.63.1b0.firefox.signed.xpi" + "update_link": "https://github.com/gorhill/uBlock/releases/download/1.63.1b1/uBlock0_1.63.1b1.firefox.signed.xpi" } ] } From 7dba69dc48d2114648ca237e4b69031d5db8626b Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Thu, 20 Mar 2025 07:37:44 -0400 Subject: [PATCH 0709/1099] New revision for statble release --- dist/version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dist/version b/dist/version index fba35fe9b1b23..590653d9447be 100644 --- a/dist/version +++ b/dist/version @@ -1 +1 @@ -1.63.1.1 +1.63.2 From 5b81369fc2458b9b3b9aa672fc29d1ce9e84fbe0 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Thu, 20 Mar 2025 12:03:17 -0400 Subject: [PATCH 0710/1099] `bidiTrie` is hardcoded Thus no need to threat it as if it was a changing parameter. --- src/js/static-net-filtering.js | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/js/static-net-filtering.js b/src/js/static-net-filtering.js index a5973f525c1ac..c6b4f00c625c7 100644 --- a/src/js/static-net-filtering.js +++ b/src/js/static-net-filtering.js @@ -3130,9 +3130,9 @@ const urlTokenizer = new (class { } // Tokenize on demand. - getTokens(encodeInto) { + getTokens() { if ( this._tokenized ) { return this._tokens; } - let i = this._tokenize(encodeInto); + let i = this._tokenize(); this._tokens[i+0] = ANY_TOKEN_HASH; this._tokens[i+1] = 0; i += 2; @@ -3189,17 +3189,17 @@ const urlTokenizer = new (class { // https://github.com/chrisaljoudi/uBlock/issues/1118 // We limit to a maximum number of tokens. - _tokenize(encodeInto) { + _tokenize() { const tokens = this._tokens; const url = this._urlOut; - const l = encodeInto.setHaystackLen(url.length); + const l = bidiTrie.setHaystackLen(url.length); if ( l === 0 ) { return 0; } let j = 0; let hasq = -1; mainLoop: { const knownTokens = this.knownTokens; const vtc = this._validTokenChars; - const charCodes = encodeInto.haystack; + const charCodes = bidiTrie.haystack; let i = 0, n = 0, ti = 0, th = 0; for (;;) { for (;;) { @@ -4968,7 +4968,7 @@ StaticNetFilteringEngine.prototype.matchAndFetchModifiers = function( results, }; - const tokenHashes = urlTokenizer.getTokens(bidiTrie); + const tokenHashes = urlTokenizer.getTokens(); let i = 0; let th = 0, iunit = 0; for (;;) { @@ -5158,7 +5158,7 @@ StaticNetFilteringEngine.prototype.realmMatchString = function( } // Pattern-based filters else { - const tokenHashes = urlTokenizer.getTokens(bidiTrie); + const tokenHashes = urlTokenizer.getTokens(); let i = 0; for (;;) { tokenHash = tokenHashes[i]; From 451e1c24a45b5f3908c7eacf602626e1571ed09c Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Thu, 20 Mar 2025 12:05:37 -0400 Subject: [PATCH 0711/1099] New revision for dev build --- dist/version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dist/version b/dist/version index 590653d9447be..44072f506a3ac 100644 --- a/dist/version +++ b/dist/version @@ -1 +1 @@ -1.63.2 +1.63.3.0 From 88fa550a96b3934689349c3adbd8e80be7844b8c Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Thu, 20 Mar 2025 12:06:58 -0400 Subject: [PATCH 0712/1099] Improve `[json-prune|trusted-replace]-fetch-response` scriptlets Output more information to the logger in verbose mode. --- src/js/resources/scriptlets.js | 66 +++++++++++++--------------------- 1 file changed, 24 insertions(+), 42 deletions(-) diff --git a/src/js/resources/scriptlets.js b/src/js/resources/scriptlets.js index 1f4a8de67b2b6..73b534bc7382b 100755 --- a/src/js/resources/scriptlets.js +++ b/src/js/resources/scriptlets.js @@ -625,34 +625,23 @@ builtinScriptlets.push({ ], }); function matchObjectProperties(propNeedles, ...objs) { - if ( matchObjectProperties.extractProperties === undefined ) { - matchObjectProperties.extractProperties = (src, des, props) => { - for ( const p of props ) { - const v = src[p]; - if ( v === undefined ) { continue; } - des[p] = src[p]; - } - }; - } const safe = safeSelf(); - const haystack = {}; - const props = safe.Array_from(propNeedles.keys()); + const matched = []; for ( const obj of objs ) { if ( obj instanceof Object === false ) { continue; } - matchObjectProperties.extractProperties(obj, haystack, props); - } - for ( const [ prop, details ] of propNeedles ) { - let value = haystack[prop]; - if ( value === undefined ) { continue; } - if ( typeof value !== 'string' ) { - try { value = safe.JSON_stringify(value); } - catch { } - if ( typeof value !== 'string' ) { continue; } - } - if ( safe.testPattern(details, value) ) { continue; } - return false; + for ( const [ prop, details ] of propNeedles ) { + let value = obj[prop]; + if ( value === undefined ) { continue; } + if ( typeof value !== 'string' ) { + try { value = safe.JSON_stringify(value); } + catch { } + if ( typeof value !== 'string' ) { continue; } + } + if ( safe.testPattern(details, value) === false ) { return; } + matched.push(`${prop}: ${value}`); + } } - return true; + return matched; } /******************************************************************************/ @@ -679,7 +668,6 @@ function jsonPruneFetchResponseFn( const logall = rawPrunePaths === ''; const applyHandler = function(target, thisArg, args) { const fetchPromise = Reflect.apply(target, thisArg, args); - let outcome = logall ? 'nomatch' : 'match'; if ( propNeedles.size !== 0 ) { const objs = [ args[0] instanceof Object ? args[0] : { url: args[0] } ]; if ( objs[0] instanceof Request ) { @@ -692,14 +680,12 @@ function jsonPruneFetchResponseFn( if ( args[1] instanceof Object ) { objs.push(args[1]); } - if ( matchObjectProperties(propNeedles, ...objs) === false ) { - outcome = 'nomatch'; + const matched = matchObjectProperties(propNeedles, ...objs); + if ( matched === undefined ) { return fetchPromise; } + if ( safe.logLevel > 1 ) { + safe.uboLog(logPrefix, `Matched "propsToMatch":\n\t${matched.join('\n\t')}`); } } - if ( logall === false && outcome === 'nomatch' ) { return fetchPromise; } - if ( safe.logLevel > 1 && outcome !== 'nomatch' && propNeedles.size !== 0 ) { - safe.uboLog(logPrefix, `Matched optional "propsToMatch"\n${extraArgs.propsToMatch}`); - } return fetchPromise.then(responseBefore => { const response = responseBefore.clone(); return response.json().then(objBefore => { @@ -772,7 +758,6 @@ function replaceFetchResponseFn( apply: function(target, thisArg, args) { const fetchPromise = Reflect.apply(target, thisArg, args); if ( pattern === '' ) { return fetchPromise; } - let outcome = 'match'; if ( propNeedles.size !== 0 ) { const objs = [ args[0] instanceof Object ? args[0] : { url: args[0] } ]; if ( objs[0] instanceof Request ) { @@ -786,14 +771,12 @@ function replaceFetchResponseFn( if ( args[1] instanceof Object ) { objs.push(args[1]); } - if ( matchObjectProperties(propNeedles, ...objs) === false ) { - outcome = 'nomatch'; + const matched = matchObjectProperties(propNeedles, ...objs); + if ( matched === undefined ) { return fetchPromise; } + if ( safe.logLevel > 1 ) { + safe.uboLog(logPrefix, `Matched "propsToMatch":\n\t${matched.join('\n\t')}`); } } - if ( outcome === 'nomatch' ) { return fetchPromise; } - if ( safe.logLevel > 1 ) { - safe.uboLog(logPrefix, `Matched "propsToMatch"\n${propsToMatch}`); - } return fetchPromise.then(responseBefore => { const response = responseBefore.clone(); return response.text().then(textBefore => { @@ -801,8 +784,7 @@ function replaceFetchResponseFn( return responseBefore; } const textAfter = textBefore.replace(rePattern, replacement); - const outcome = textAfter !== textBefore ? 'match' : 'nomatch'; - if ( outcome === 'nomatch' ) { return responseBefore; } + if ( textAfter === textBefore ) { return responseBefore; } safe.uboLog(logPrefix, 'Replaced'); const responseAfter = new Response(textAfter, { status: responseBefore.status, @@ -1399,7 +1381,7 @@ function jsonPruneXhrResponse( const xhrDetails = { method, url }; let outcome = 'match'; if ( propNeedles.size !== 0 ) { - if ( matchObjectProperties(propNeedles, xhrDetails) === false ) { + if ( matchObjectProperties(propNeedles, xhrDetails) === undefined ) { outcome = 'nomatch'; } } @@ -2838,7 +2820,7 @@ function trustedReplaceXhrResponse( const xhrDetails = { method, url }; let outcome = 'match'; if ( propNeedles.size !== 0 ) { - if ( matchObjectProperties(propNeedles, xhrDetails) === false ) { + if ( matchObjectProperties(propNeedles, xhrDetails) === undefined ) { outcome = 'nomatch'; } } From 2774dfe38f1016ee5710a80adae7750ee7f997a9 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Thu, 20 Mar 2025 12:11:05 -0400 Subject: [PATCH 0713/1099] Update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9f4ed009cf460..4e5db1bf68047 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,4 @@ +- [Improve `[json-prune|trusted-replace]-fetch-response` scriptlets](https://github.com/gorhill/uBlock/commit/88fa550a96) - [Fix TypedArray overflow](https://github.com/gorhill/uBlock/commit/76b80baaea) - [Add prevent-innerHTML scriptlet](https://github.com/gorhill/uBlock/commit/fe744816f1) From a88594599f346e1960f9dd88f4b29ef40af6262f Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Thu, 20 Mar 2025 12:13:58 -0400 Subject: [PATCH 0714/1099] Update changelog --- CHANGELOG.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4e5db1bf68047..0b58104f7d1ca 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,11 @@ - [Improve `[json-prune|trusted-replace]-fetch-response` scriptlets](https://github.com/gorhill/uBlock/commit/88fa550a96) + +---------- + +# 1.63.2 + +## Fixes / changes + - [Fix TypedArray overflow](https://github.com/gorhill/uBlock/commit/76b80baaea) - [Add prevent-innerHTML scriptlet](https://github.com/gorhill/uBlock/commit/fe744816f1) From 0f2a374585f2df44ea3336f9f08192e25957cbd4 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Thu, 20 Mar 2025 12:20:46 -0400 Subject: [PATCH 0715/1099] Make Firefox dev build auto-update --- dist/firefox/updates.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dist/firefox/updates.json b/dist/firefox/updates.json index b3a4c53608122..b23a446aa1873 100644 --- a/dist/firefox/updates.json +++ b/dist/firefox/updates.json @@ -3,9 +3,9 @@ "uBlock0@raymondhill.net": { "updates": [ { - "version": "1.63.1.1", + "version": "1.63.3.0", "browser_specific_settings": { "gecko": { "strict_min_version": "79.0" } }, - "update_link": "https://github.com/gorhill/uBlock/releases/download/1.63.1b1/uBlock0_1.63.1b1.firefox.signed.xpi" + "update_link": "https://github.com/gorhill/uBlock/releases/download/1.63.3b0/uBlock0_1.63.3b0.firefox.signed.xpi" } ] } From ab458b492a187bf5e702a04f6db78bbc43d5cd8d Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Fri, 21 Mar 2025 13:23:54 -0400 Subject: [PATCH 0716/1099] [mv3] Bring back element zapper --- platform/mv3/chromium/manifest.json | 13 + .../mv3/extension/_locales/en/messages.json | 8 + platform/mv3/extension/css/popup.css | 6 +- platform/mv3/extension/css/zapper-ui.css | 50 +++ platform/mv3/extension/js/background.js | 102 ++++- platform/mv3/extension/js/popup.js | 18 +- platform/mv3/extension/js/scripting/zapper.js | 409 ++++++++++++++++++ platform/mv3/extension/js/theme.js | 19 +- platform/mv3/extension/js/zapper-ui.js | 226 ++++++++++ platform/mv3/extension/popup.html | 4 +- platform/mv3/extension/zapper-ui.html | 25 ++ platform/mv3/firefox/manifest.json | 13 + 12 files changed, 856 insertions(+), 37 deletions(-) create mode 100644 platform/mv3/extension/css/zapper-ui.css create mode 100644 platform/mv3/extension/js/scripting/zapper.js create mode 100644 platform/mv3/extension/js/zapper-ui.js create mode 100644 platform/mv3/extension/zapper-ui.html diff --git a/platform/mv3/chromium/manifest.json b/platform/mv3/chromium/manifest.json index c4d8e96a3147e..a5ff0306f487a 100644 --- a/platform/mv3/chromium/manifest.json +++ b/platform/mv3/chromium/manifest.json @@ -12,6 +12,11 @@ "service_worker": "/js/background.js", "type": "module" }, + "commands": { + "enter-zapper-mode": { + "description": "__MSG_zapperTipEnter__" + } + }, "declarative_net_request": { "rule_resources": [ ] @@ -51,6 +56,14 @@ "" ], "use_dynamic_url": true + }, + { + "resources": [ + "/zapper-ui.html" + ], + "matches": [ + "" + ] } ] } diff --git a/platform/mv3/extension/_locales/en/messages.json b/platform/mv3/extension/_locales/en/messages.json index e4ac1c56c8c1c..741e6eb649309 100644 --- a/platform/mv3/extension/_locales/en/messages.json +++ b/platform/mv3/extension/_locales/en/messages.json @@ -278,5 +278,13 @@ "strictblockProceed": { "message": "Proceed", "description": "A button to navigate to the blocked page" + }, + "zapperTipEnter": { + "message": "Enter element zapper mode", + "description": "Tooltip for the button used to enter zapper mode" + }, + "zapperTipQuit": { + "message": "Exit element zapper mode", + "description": "Tooltip for the button used to exit zapper mode" } } diff --git a/platform/mv3/extension/css/popup.css b/platform/mv3/extension/css/popup.css index cc6824674a18a..6ccc8f8147c04 100644 --- a/platform/mv3/extension/css/popup.css +++ b/platform/mv3/extension/css/popup.css @@ -155,13 +155,13 @@ body.needReload #refresh { } .toolRibbon { - align-items: center; + align-items: start; background-color: var(--popup-toolbar-surface); display: grid; grid-auto-columns: 1fr; grid-auto-flow: column; - grid-template: auto / repeat(5, 1fr); - justify-items: center; + grid-template: auto / repeat(4, 1fr); + justify-items: stretch; white-space: normal; } .toolRibbon .tool { diff --git a/platform/mv3/extension/css/zapper-ui.css b/platform/mv3/extension/css/zapper-ui.css new file mode 100644 index 0000000000000..cf729ac4791e9 --- /dev/null +++ b/platform/mv3/extension/css/zapper-ui.css @@ -0,0 +1,50 @@ +html#ubol-zapper, +#ubol-zapper body { + background: transparent; + cursor: not-allowed; + height: 100vh; + height: 100svh; + margin: 0; + overflow: hidden; + width: 100vw; +} +#ubol-zapper :focus { + outline: none; +} +#ubol-zapper aside { + background-color: var(--surface-1); + box-sizing: border-box; + cursor: default; + display: flex; + flex-direction: column; + position: fixed; + z-index: 100; +} +#ubol-zapper aside > #quit { + fill: none; + height: 4em; + stroke: var(--ink-1); + stroke-width: 3px; + width: 4em; +} +#ubol-zapper svg#sea { + cursor: crosshair; + box-sizing: border-box; + height: 100%; + left: 0; + position: absolute; + top: 0; + width: 100%; +} +#ubol-zapper svg#sea > path:first-child { + fill: rgba(0,0,0,0.5); + fill-rule: evenodd; +} +#ubol-zapper svg#sea > path + path { + stroke: #FF0; + stroke-width: 0.5px; + fill: rgba(255,255,63,0.20); +} +#ubol-zapper #quit:hover { + background-color: var(--surface-2) +} diff --git a/platform/mv3/extension/js/background.js b/platform/mv3/extension/js/background.js index 7874b3d0130f4..374f771d253c5 100644 --- a/platform/mv3/extension/js/background.js +++ b/platform/mv3/extension/js/background.js @@ -198,6 +198,20 @@ function onMessage(request, sender, callback) { return false; } + case 'removeCSS': { + const tabId = sender?.tab?.id ?? false; + const frameId = sender?.frameId ?? false; + if ( tabId === false || frameId === false ) { return; } + browser.scripting.removeCSS({ + css: request.css, + origin: 'USER', + target: { tabId, frameIds: [ frameId ] }, + }).catch(reason => { + console.log(reason); + }); + return false; + } + default: break; } @@ -414,6 +428,23 @@ function onMessage(request, sender, callback) { /******************************************************************************/ +function onCommand(command, tab) { + switch ( command ) { + case 'enter-zapper-mode': { + if ( browser.scripting === undefined ) { return; } + browser.scripting.executeScript({ + files: [ '/js/scripting/zapper.js' ], + target: { tabId: tab.id }, + }); + break; + } + default: + break; + } +} + +/******************************************************************************/ + async function start() { await loadRulesetConfig(); @@ -466,8 +497,6 @@ async function start() { }); } - runtime.onMessage.addListener(onMessage); - if ( process.firstRun ) { const enableOptimal = await hasOmnipotence(); if ( enableOptimal ) { @@ -493,27 +522,56 @@ async function start() { if ( process.wakeupRun === false ) { adminReadEx('disabledFeatures'); } - - browser.permissions.onRemoved.addListener(onPermissionsRemoved); - browser.permissions.onAdded.addListener(onPermissionsAdded); } -// https://github.com/uBlockOrigin/uBOL-home/issues/199 -// Force a restart of the extension once when an "internal error" occurs -start().then(( ) => { - localRemove('goodStart'); - return false; -}).catch(reason => { - console.trace(reason); - if ( process.wakeupRun ) { return; } - return localRead('goodStart').then(goodStart => { - if ( goodStart === false ) { - localRemove('goodStart'); - return false; - } - return localWrite('goodStart', false).then(( ) => true); +/******************************************************************************/ + +const isFullyInitialized = new Promise(resolve => { + // https://github.com/uBlockOrigin/uBOL-home/issues/199 + // Force a restart of the extension once when an "internal error" occurs + start().then(( ) => { + localRemove('goodStart'); + return false; + }).catch(reason => { + console.trace(reason); + if ( process.wakeupRun ) { return; } + return localRead('goodStart').then(goodStart => { + if ( goodStart === false ) { + localRemove('goodStart'); + return false; + } + return localWrite('goodStart', false).then(( ) => true); + }); + }).then(restart => { + if ( restart !== true ) { return; } + runtime.reload(); + }).finally(( ) => { + resolve(true); + }); +}); + +runtime.onMessage.addListener((request, sender, callback) => { + isFullyInitialized.then(( ) => { + const r = onMessage(request, sender, callback); + if ( r !== true ) { callback(); } + }); + return true; +}); + +browser.permissions.onRemoved.addListener((...args) => { + isFullyInitialized.then(( ) => { + onPermissionsRemoved(...args); + }); +}); + +browser.permissions.onAdded.addListener((...args) => { + isFullyInitialized.then(( ) => { + onPermissionsAdded(...args); + }); +}); + +browser.commands.onCommand.addListener((...args) => { + isFullyInitialized.then(( ) => { + onCommand(...args); }); -}).then(restart => { - if ( restart !== true ) { return; } - runtime.reload(); }); diff --git a/platform/mv3/extension/js/popup.js b/platform/mv3/extension/js/popup.js index d0eefb1bb0f09..a50c889b6ac52 100644 --- a/platform/mv3/extension/js/popup.js +++ b/platform/mv3/extension/js/popup.js @@ -319,6 +319,17 @@ dom.on('[data-i18n-title="popupTipDashboard"]', 'click', ev => { /******************************************************************************/ +dom.on('#gotoZapper', 'click', ( ) => { + if ( browser.scripting === undefined ) { return; } + browser.scripting.executeScript({ + files: [ '/js/scripting/zapper.js' ], + target: { tabId: currentTab.id }, + }); + self.close(); +}); + +/******************************************************************************/ + async function init() { const [ tab ] = await browser.tabs.query({ active: true, @@ -362,9 +373,10 @@ async function init() { isNaN(currentTab.id) === false ); - dom.cl.toggle('#reportFilterIssue', 'enabled', - /^https?:\/\//.test(url?.href) - ); + const isNetworkPage = url.protocol === 'http:' || url.protocol === 'https:'; + + dom.cl.toggle('#reportFilterIssue', 'enabled', isNetworkPage ); + dom.cl.toggle('#gotoZapper', 'enabled', isNetworkPage); const parent = qs$('#rulesetStats'); for ( const details of popupPanelData.rulesetDetails || [] ) { diff --git a/platform/mv3/extension/js/scripting/zapper.js b/platform/mv3/extension/js/scripting/zapper.js new file mode 100644 index 0000000000000..038ea1601d761 --- /dev/null +++ b/platform/mv3/extension/js/scripting/zapper.js @@ -0,0 +1,409 @@ +/******************************************************************************* + + uBlock Origin - a comprehensive, efficient content blocker + Copyright (C) 2025-present Raymond Hill + + 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/}. + + Home: https://github.com/gorhill/uBlock +*/ + +(async ( ) => { + +/******************************************************************************/ + +const zapper = self.uBOLZapper = self.uBOLZapper || {}; + +if ( zapper.injected ) { return; } +zapper.injected = true; + +/******************************************************************************/ + +const sendMessage = msg => { + try { + chrome.runtime.sendMessage(msg).catch(( ) => { }); + } catch { + } +}; + +/******************************************************************************/ + +const zapperSecret = (( ) => { + let secret = String.fromCharCode((Math.random() * 26) + 97); + do { + secret += (Math.floor(Math.random() * 2147483647) + 2147483647) + .toString(36) + .slice(2); + } while ( secret.length < 8 ); + return secret; +})(); + +const zapperCSSStyle = [ + 'background: transparent', + 'border: 0', + 'border-radius: 0', + 'box-shadow: none', + 'color-scheme: light dark', + 'display: block', + 'filter: none', + 'height: 100vh', + ' height: 100svh', + 'left: 0', + 'margin: 0', + 'max-height: none', + 'max-width: none', + 'min-height: unset', + 'min-width: unset', + 'opacity: 1', + 'outline: 0', + 'padding: 0', + 'pointer-events: auto', + 'position: fixed', + 'top: 0', + 'transform: none', + 'visibility: hidden', + 'width: 100%', + 'z-index: 2147483647', + '' +].join(' !important;\n'); + +const zapperCSS = ` +:root > [${zapperSecret}] { + ${zapperCSSStyle} +} +:root > [${zapperSecret}-loaded] { + visibility: visible !important; +} +:root [${zapperSecret}-click] { + pointer-events: none !important; +} +`; + +sendMessage({ what: 'insertCSS', css: zapperCSS }); + +/******************************************************************************/ + +const getElementBoundingClientRect = function(elem) { + let rect = typeof elem.getBoundingClientRect === 'function' + ? elem.getBoundingClientRect() + : { height: 0, left: 0, top: 0, width: 0 }; + + // https://github.com/gorhill/uBlock/issues/1024 + // Try not returning an empty bounding rect. + if ( rect.width !== 0 && rect.height !== 0 ) { + return rect; + } + if ( elem.shadowRoot instanceof DocumentFragment ) { + return getElementBoundingClientRect(elem.shadowRoot); + } + + let left = rect.left, + right = left + rect.width, + top = rect.top, + bottom = top + rect.height; + + for ( const child of elem.children ) { + rect = getElementBoundingClientRect(child); + if ( rect.width === 0 || rect.height === 0 ) { continue; } + if ( rect.left < left ) { left = rect.left; } + if ( rect.right > right ) { right = rect.right; } + if ( rect.top < top ) { top = rect.top; } + if ( rect.bottom > bottom ) { bottom = rect.bottom; } + } + + return { + bottom, + height: bottom - top, + left, + right, + top, + width: right - left + }; +}; + +/******************************************************************************/ + +const highlightElement = function(elem) { + if ( elem !== true ) { + if ( elem !== undefined ) { + if ( elem === highlightElement.current ) { return; } + if ( elem !== zapperFrame ) { + highlightElement.current = elem; + } + } + } + elem = highlightElement.current; + + const ow = self.innerWidth; + const oh = self.innerHeight; + const islands = []; + + if ( elem !== null ) { + const rect = getElementBoundingClientRect(elem); + // Ignore offscreen areas + if ( + rect.left <= ow && rect.top <= oh && + rect.left + rect.width >= 0 && rect.top + rect.height >= 0 + ) { + islands.push( + `M${rect.left} ${rect.top}h${rect.width}v${rect.height}h-${rect.width}z` + ); + } + } + + zapperFramePort.postMessage({ + what: 'svgPaths', + ocean: `M0 0h${ow}v${oh}h-${ow}z`, + islands: islands.join(''), + }); +}; +highlightElement.current = null; + +/******************************************************************************/ + +const elementFromPoint = (( ) => { + let lastX, lastY; + + return (x, y) => { + if ( x !== undefined ) { + lastX = x; lastY = y; + } else if ( lastX !== undefined ) { + x = lastX; y = lastY; + } else { + return null; + } + if ( !zapperFrame ) { return null; } + const magicAttr = `${zapperSecret}-click`; + zapperFrame.setAttribute(magicAttr, ''); + let elem = document.elementFromPoint(x, y); + if ( elem === document.body || elem === document.documentElement ) { + elem = null; + } + // https://github.com/uBlockOrigin/uBlock-issues/issues/380 + zapperFrame.removeAttribute(magicAttr); + return elem; + }; +})(); + +/******************************************************************************/ + +const highlightElementAtPoint = function(mx, my) { + const elem = elementFromPoint(mx, my); + highlightElement(elem); +}; + +/******************************************************************************/ + +// https://www.reddit.com/r/uBlockOrigin/comments/bktxtb/scrolling_doesnt_work/emn901o +// Override 'fixed' position property on body element if present. + +// With touch-driven devices, first highlight the element and remove only +// when tapping again the highlighted area. + +const zapElementAtPoint = function(mx, my, options) { + if ( options.highlight ) { + const elem = elementFromPoint(mx, my); + if ( elem ) { + highlightElement(elem); + } + return; + } + + let elemToRemove = highlightElement.current; + if ( elemToRemove === null && mx !== undefined ) { + elemToRemove = elementFromPoint(mx, my); + } + + if ( elemToRemove instanceof Element === false ) { return; } + + const getStyleValue = (elem, prop) => { + const style = window.getComputedStyle(elem); + return style ? style[prop] : ''; + }; + + // Heuristic to detect scroll-locking: remove such lock when detected. + let maybeScrollLocked = elemToRemove.shadowRoot instanceof DocumentFragment; + if ( maybeScrollLocked === false ) { + let elem = elemToRemove; + do { + maybeScrollLocked = + parseInt(getStyleValue(elem, 'zIndex'), 10) >= 1000 || + getStyleValue(elem, 'position') === 'fixed'; + elem = elem.parentElement; + } while ( elem !== null && maybeScrollLocked === false ); + } + if ( maybeScrollLocked ) { + const doc = document; + if ( getStyleValue(doc.body, 'overflowY') === 'hidden' ) { + doc.body.style.setProperty('overflow', 'auto', 'important'); + } + if ( getStyleValue(doc.body, 'position') === 'fixed' ) { + doc.body.style.setProperty('position', 'initial', 'important'); + } + if ( getStyleValue(doc.documentElement, 'position') === 'fixed' ) { + doc.documentElement.style.setProperty('position', 'initial', 'important'); + } + if ( getStyleValue(doc.documentElement, 'overflowY') === 'hidden' ) { + doc.documentElement.style.setProperty('overflow', 'auto', 'important'); + } + } + elemToRemove.remove(); + highlightElementAtPoint(mx, my); +}; + +/******************************************************************************/ + +const onKeyPressed = function(ev) { + // Delete + if ( + (ev.key === 'Delete' || ev.key === 'Backspace') ) { + ev.stopPropagation(); + ev.preventDefault(); + zapElementAtPoint(); + return; + } + // Esc + if ( ev.key === 'Escape' || ev.which === 27 ) { + ev.stopPropagation(); + ev.preventDefault(); + quitZapper(); + return; + } +}; + +/******************************************************************************/ + +// https://github.com/chrisaljoudi/uBlock/issues/190 +// May need to dynamically adjust the height of the overlay + new position +// of highlighted elements. + +const onViewportChanged = function() { + highlightElement(true); +}; + +/******************************************************************************/ + +const startZapper = function() { + zapperFrame.focus(); + self.addEventListener('scroll', onViewportChanged, { passive: true }); + self.addEventListener('resize', onViewportChanged, { passive: true }); + self.addEventListener('keydown', onKeyPressed, true); +}; + +/******************************************************************************/ + +const quitZapper = function() { + self.removeEventListener('scroll', onViewportChanged, { passive: true }); + self.removeEventListener('resize', onViewportChanged, { passive: true }); + self.removeEventListener('keydown', onKeyPressed, true); + if ( zapperFramePort ) { + zapperFramePort.close(); + zapperFramePort.onmessage = null; + zapperFramePort.onmessageerror = null; + zapperFramePort = null; + } + if ( zapperFrame ) { + zapperFrame.remove(); + zapperFrame = null; + } + sendMessage({ what: 'removeCSS', css: zapperCSS }); + zapper.injected = false; +}; + +/******************************************************************************/ + +const onFrameMessage = function(msg) { + switch ( msg.what ) { + case 'start': + startZapper(); + if ( Boolean(zapperFramePort) === false ) { break; } + highlightElement(true); + break; + case 'quitZapper': + quitZapper(); + break; + case 'highlightElementAtPoint': + highlightElementAtPoint(msg.mx, msg.my); + break; + case 'unhighlight': + highlightElement(null); + break; + case 'zapElementAtPoint': + zapElementAtPoint(msg.mx, msg.my, msg.options); + if ( msg.options.highlight !== true && msg.options.stay !== true ) { + quitZapper(); + } + break; + default: + break; + } +}; + +/******************************************************************************/ + +// zapper-ui.html will be injected in the page through an iframe, and +// is a sandboxed so as to prevent the page from interfering with its +// content and behavior. +// +// The purpose of zapper.js is to install the zapper UI, and wait for the +// component to establish a direct communication channel. +// +// When the zapper is installed on a page, the only change the page sees is an +// iframe with a random attribute. The page can't see the content of the +// iframe, and cannot interfere with its style properties. However the page +// can remove the iframe. + +const bootstrap = async ( ) => { + const url = new URL(chrome.runtime.getURL('/zapper-ui.html')); + return new Promise(resolve => { + const frame = document.createElement('iframe'); + frame.setAttribute(zapperSecret, ''); + document.documentElement.append(frame); + frame.onload = ( ) => { + frame.onload = null; + frame.setAttribute(`${zapperSecret}-loaded`, ''); + const channel = new MessageChannel(); + const port = channel.port1; + port.onmessage = ev => { + onFrameMessage(ev.data || {}); + }; + port.onmessageerror = ( ) => { + quitZapper(); + }; + frame.contentWindow.postMessage( + { what: 'zapperStart' }, + url.origin, + [ channel.port2 ] + ); + frame.contentWindow.focus(); + resolve({ + zapperFrame: frame, + zapperFramePort: port, + }); + }; + frame.contentWindow.location = url.href; + }); +}; + +let { zapperFrame, zapperFramePort } = await bootstrap(); +if ( zapperFrame && zapperFramePort ) { return; } + +quitZapper(); + +/******************************************************************************/ + +})(); + + +void 0; diff --git a/platform/mv3/extension/js/theme.js b/platform/mv3/extension/js/theme.js index bab0fbfd068a5..a20d459f01600 100644 --- a/platform/mv3/extension/js/theme.js +++ b/platform/mv3/extension/js/theme.js @@ -23,9 +23,16 @@ import { dom } from './dom.js'; /******************************************************************************/ -const mql = self.matchMedia('(prefers-color-scheme: dark)'); -const theme = mql instanceof Object && mql.matches === true - ? 'dark' - : 'light'; -dom.cl.toggle(dom.html, 'dark', theme === 'dark'); -dom.cl.toggle(dom.html, 'light', theme !== 'dark'); +{ + const mql = self.matchMedia('(prefers-color-scheme: dark)'); + const theme = mql instanceof Object && mql.matches === true + ? 'dark' + : 'light'; + dom.cl.toggle(dom.html, 'dark', theme === 'dark'); + dom.cl.toggle(dom.html, 'light', theme !== 'dark'); +} + +{ + const mql = self.matchMedia('(hover: hover)'); + dom.cl.toggle(dom.html, 'mobile', mql.matches !== true); +} diff --git a/platform/mv3/extension/js/zapper-ui.js b/platform/mv3/extension/js/zapper-ui.js new file mode 100644 index 0000000000000..46bf3217abe6f --- /dev/null +++ b/platform/mv3/extension/js/zapper-ui.js @@ -0,0 +1,226 @@ +/******************************************************************************* + + uBlock Origin - a comprehensive, efficient content blocker + Copyright (C) 2025-present Raymond Hill + + 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/}. + + Home: https://github.com/gorhill/uBlock +*/ + +/******************************************************************************/ + +const $id = id => document.getElementById(id); +const $stor = selector => document.querySelector(selector); + +const svgRoot = $stor('svg#sea'); +const svgOcean = svgRoot.children[0]; +const svgIslands = svgRoot.children[1]; +const NoPaths = 'M0 0'; + +let zapperScriptPort; + +/******************************************************************************/ + +const onSvgClicked = function(ev) { + // If zap mode, highlight element under mouse, this makes the zapper usable + // on touch screens. + zapperScriptPort.postMessage({ + what: 'zapElementAtPoint', + mx: ev.clientX, + my: ev.clientY, + options: { + stay: true, + highlight: ev.target !== svgIslands, + }, + }); +}; + +/******************************************************************************* + + Swipe right: + Remove current highlight + +*/ + +const onSvgTouch = (( ) => { + let startX = 0, startY = 0; + let t0 = 0; + return ev => { + if ( ev.type === 'touchstart' ) { + startX = ev.touches[0].screenX; + startY = ev.touches[0].screenY; + t0 = ev.timeStamp; + return; + } + if ( startX === undefined ) { return; } + const stopX = ev.changedTouches[0].screenX; + const stopY = ev.changedTouches[0].screenY; + const angle = Math.abs(Math.atan2(stopY - startY, stopX - startX)); + const distance = Math.sqrt( + Math.pow(stopX - startX, 2) + + Math.pow(stopY - startY, 2) + ); + // Interpret touch events as a tap if: + // - Swipe is not valid; and + // - The time between start and stop was less than 200ms. + const duration = ev.timeStamp - t0; + if ( distance < 32 && duration < 200 ) { + onSvgClicked({ + type: 'touch', + target: ev.target, + clientX: ev.changedTouches[0].pageX, + clientY: ev.changedTouches[0].pageY, + }); + ev.preventDefault(); + return; + } + if ( distance < 64 ) { return; } + const angleUpperBound = Math.PI * 0.25 * 0.5; + const swipeRight = angle < angleUpperBound; + if ( swipeRight === false ) { return; } + if ( ev.cancelable ) { + ev.preventDefault(); + } + // Swipe right. + if ( svgIslands.getAttribute('d') === NoPaths ) { return; } + zapperScriptPort.postMessage({ + what: 'unhighlight' + }); + }; +})(); + +/******************************************************************************/ + +const svgListening = (( ) => { + let on = false; + let timer; + let mx = 0, my = 0; + + const onTimer = ( ) => { + timer = undefined; + zapperScriptPort.postMessage({ + what: 'highlightElementAtPoint', + mx, + my, + }); + }; + + const onHover = ev => { + mx = ev.clientX; + my = ev.clientY; + if ( timer === undefined ) { + timer = self.requestAnimationFrame(onTimer); + } + }; + + return state => { + if ( state === on ) { return; } + on = state; + if ( on ) { + document.addEventListener('mousemove', onHover, { passive: true }); + return; + } + document.removeEventListener('mousemove', onHover, { passive: true }); + if ( timer !== undefined ) { + self.cancelAnimationFrame(timer); + timer = undefined; + } + }; +})(); + +/******************************************************************************/ + +const onKeyPressed = function(ev) { + // Delete + if ( ev.key === 'Delete' || ev.key === 'Backspace' ) { + zapperScriptPort.postMessage({ + what: 'zapElementAtPoint', + options: { stay: true }, + }); + return; + } + // Esc + if ( ev.key === 'Escape' || ev.which === 27 ) { + quitZapper(); + return; + } +}; + +/******************************************************************************/ + +const onScriptMessage = function(msg) { + switch ( msg.what ) { + case 'svgPaths': { + let { ocean, islands } = msg; + ocean += islands; + svgOcean.setAttribute('d', ocean); + svgIslands.setAttribute('d', islands || NoPaths); + break; + } + default: + break; + } +}; + +/******************************************************************************/ + +const startZapper = function(port) { + zapperScriptPort = port; + zapperScriptPort.onmessage = ev => { + onScriptMessage(ev.data || {}); + }; + zapperScriptPort.onmessageerror = ( ) => { + quitZapper(); + }; + zapperScriptPort.postMessage({ what: 'start' }); + self.addEventListener('keydown', onKeyPressed, true); + $stor('svg#sea').addEventListener('click', onSvgClicked); + $stor('svg#sea').addEventListener('touchstart', onSvgTouch, { passive: true }); + $stor('svg#sea').addEventListener('touchend', onSvgTouch); + $id('quit').addEventListener('click', quitZapper ); + svgListening(true); +}; + +/******************************************************************************/ + +const quitZapper = function() { + self.removeEventListener('keydown', onKeyPressed, true); + $stor('svg#sea').removeEventListener('click', onSvgClicked); + $stor('svg#sea').removeEventListener('touchstart', onSvgTouch, { passive: true }); + $stor('svg#sea').removeEventListener('touchend', onSvgTouch); + $id('quit').removeEventListener('click', quitZapper ); + svgListening(false); + if ( zapperScriptPort ) { + zapperScriptPort.postMessage({ what: 'quitZapper' }); + zapperScriptPort.close(); + zapperScriptPort.onmessage = null; + zapperScriptPort.onmessageerror = null; + zapperScriptPort = null; + } +}; + +/******************************************************************************/ + +// Wait for the content script to establish communication + +globalThis.addEventListener('message', ev => { + const msg = ev.data || {}; + if ( msg.what !== 'zapperStart' ) { return; } + if ( Array.isArray(ev.ports) === false ) { return; } + if ( ev.ports.length === 0 ) { return; } + startZapper(ev.ports[0]); +}, { once: true }); + +/******************************************************************************/ diff --git a/platform/mv3/extension/popup.html b/platform/mv3/extension/popup.html index fc2307a99c623..bd1c00d9182ed 100644 --- a/platform/mv3/extension/popup.html +++ b/platform/mv3/extension/popup.html @@ -27,9 +27,7 @@

_
- - - + bolt list-altShow matched rules comment-alt cogs diff --git a/platform/mv3/extension/zapper-ui.html b/platform/mv3/extension/zapper-ui.html new file mode 100644 index 0000000000000..3e0ef51f80d1d --- /dev/null +++ b/platform/mv3/extension/zapper-ui.html @@ -0,0 +1,25 @@ + + + + + + +uBO Lite Zapper + + + + + + + + + + + + + + diff --git a/platform/mv3/firefox/manifest.json b/platform/mv3/firefox/manifest.json index 1ecaad2ec23de..0bd10629ae648 100644 --- a/platform/mv3/firefox/manifest.json +++ b/platform/mv3/firefox/manifest.json @@ -22,6 +22,11 @@ "strict_min_version": "128.0" } }, + "commands": { + "enter-zapper-mode": { + "description": "__MSG_zapperTipEnter__" + } + }, "declarative_net_request": { "rule_resources": [ ] @@ -59,6 +64,14 @@ "matches": [ "" ] + }, + { + "resources": [ + "/zapper-ui.html" + ], + "matches": [ + "" + ] } ] } From 7a2f4960d0ff49be2ecbb8f5be74b8a4b6a3bb82 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Fri, 21 Mar 2025 13:29:30 -0400 Subject: [PATCH 0717/1099] Import translation work from https://crowdin.com/project/ublock --- platform/mv3/description/webstore.th.txt | 30 +-- .../extension/_locales/pt_PT/messages.json | 8 +- .../mv3/extension/_locales/th/messages.json | 90 +++---- src/_locales/pt_PT/messages.json | 6 +- src/_locales/th/messages.json | 232 +++++++++--------- 5 files changed, 183 insertions(+), 183 deletions(-) diff --git a/platform/mv3/description/webstore.th.txt b/platform/mv3/description/webstore.th.txt index 7669a5939f98c..ab6de1987b72f 100644 --- a/platform/mv3/description/webstore.th.txt +++ b/platform/mv3/description/webstore.th.txt @@ -1,30 +1,30 @@ -uBO Lite (uBOL) is a *permission-less* MV3-based content blocker. +uBO Lite (uBOL) เป็นตัวบล๊อกเนื้อหาที่ *ไม่ต้องขออนุญาต* แบบ MV3-based -The default ruleset corresponds to uBlock Origin's default filterset: +ชุดเงื่อนไขเริ่มต้นสอดคล้องกันกับค่าตัวกรองเริ่มต้นของ uBlock Origin: -- uBlock Origin's built-in filter lists +- รายการตัวกรองภายใน uBlock Origin - EasyList - EasyPrivacy -- Peter Lowe’s Ad and tracking server list +- รายการติดตามเซิร์ฟเวอร์โฆษณาของ Peter Lowe -You can enable more rulesets by visiting the options page -- click the _Cogs_ icon in the popup panel. +คุณสามารถเปิดใช้งานชุดเงื่อนไขเพิ่มเติมโดยไปที่เพจทางเลือก -- คลิก the _Cogs_ icon ที่แผงป๊อปอัพ -uBOL is entirely declarative, meaning there is no need for a permanent uBOL process for the filtering to occur, and CSS/JS injection-based content filtering is performed reliably by the browser itself rather than by the extension. This means that uBOL itself does not consume CPU/memory resources while content blocking is ongoing -- uBOL's service worker process is required _only_ when you interact with the popup panel or the option pages. +uBOL มีการประกาศอย่างชัดเจน ซึ่งหมายความว่าไม่ต้องมีกระบวนการ uBOL ถาวรในการคัดกรอง และการคัดกรองเนื้อหา CSS/JS injection-based จะดำเนินการได้อย่างน่าเชื่อถือด้วยตัวเบราว์เซอร์เองแทนที่จะเป็นส่วนขยาย สิ่งนี้หมายถึงตัว uBOL เองไม่ต้องผลาญทรัพยากรซีพียู/หน่วยความจำในขณะที่ทำการบล๊อก -- กระบวนการทำงานของ uBOL ใช้ required _only_ เมื่อคุณโต้ตอบกับแผงป๊อปอัพหรือเพจตัวเลือก -uBOL does not require broad "read and modify data" permission at install time, hence its limited capabilities out of the box compared to uBlock Origin or other content blockers requiring broad "read and modify data" permissions at install time. +uBOL ไม่จำเป็นต้องขออนุญาตแบบรวมในการ "อ่านและปรับแต่งข้อมูล" เมื่อติดตั้ง ดังนั้นความสามารถที่จำกัดตอนเริ่มใช้งานครั้งแรกเมื่อเทียบกับ uBlock Origin หรือตัวบล๊อกเนื้อหาอื่น ๆ ที่ต้องการการขออนุญาตแบบรวมในการ "อ่านและปรับแต่งข้อมูล" ตั้งแต่ตอนติดตั้ง -However, uBOL allows you to *explicitly* grant extended permissions on specific sites of your choice so that it can better filter on those sites using cosmetic filtering and scriptlet injections. +อย่างไรก็ตาม uBOL อนุญาตให้คุณให้สิทธิ์เพิ่มเติมแบบ *ชัดเจน* ในแต่ละเว็บไซต์ตามความต้องการของคุณเอง ดังนั้น มันจะเป็นการคัดกรองที่ดีกว่าบนเว็บไซต์เหล่านั้นด้วยการคัดกรองที่ตกแต่ง และ scriptlet injections -To grant extended permissions on a given site, open the popup panel and pick a higher filtering mode such as Optimal or Complete. +ในการให้สิทธิ์เพิ่มเติมสำหรับเว็บไซต์ที่กำหนด ให้เปิดแผงป๊อปอัพ แล้วเลือกโหมดการกรองที่สูงขึ้น เช่น เหมาะสมที่สุด หรือ สมบูรณ์ -The browser will then warn you about the effects of granting the additional permissions requested by the extension on the current site, and you will have to tell the browser whether you accept or decline the request. +เบราว์เซอร์จะแจ้งเตือนเกี่ยวกับผลกระทบในการให้สิทธิ์เพิ่มเติมที่ร้องขอโดยส่วนขยายบนเว็บไซต์ปัจจุบัน และคุณจะต้องแจ้งกับยอมรับหรือปฏิเสธกับเบราว์เซอร์ -If you accept uBOL's request for additional permissions on the current site, it will be able to better filter content for the current site. +หากคุณยอมรับการร้องขอจาก uBOL ในการขออนุญาตเพิ่มเติมบนเว็บไซต์ปัจจุบัน มันจะสามารถคัดกรองเนื้อหาที่ดีขึ้น -You can set the default filtering mode from uBOL's options page. If you pick the Optimal or Complete mode as the default one, you will need to grant uBOL the permission to read and modify data on all websites. +คุณสามารถตั้งโหมดการคัดกรองเริ่มต้นจากเพจตัวเลือกของ uBOL หากคุณเลือกโหมดแบบ เหมาะสมที่สุด หรือ สมบูรณ์ เป็นค่า่เริ่มต้น คุณจำเป็นต้องให้สิทธิ์กับ uBOL ในการอ่านและปรับแต่งข้อมูลบนทุกเว็บไซต์ -Keep in mind this is still a work in progress, with these end goals: +โปรดเข้าใจว่ายังอยู่ในกระบวนการทำงานด้วยเป้าหมายเหล่านี้: -- No broad host permissions at install time -- extended permissions are granted explicitly by the user on a per-site basis. +- ไม่ต้องขออนุญาตแบบรวมในตอนติดตีั้ง -- การอนุญาตเพิ่มเติมจะเป็นการให้สิทธิ์โดยเฉพาะโดยผู้ใช้ตามแต่ละเว็บไซต์ -- Entirely declarative for reliability and CPU/memory efficiency. +- ยืนยันถึงความน่าเชื่อถือและประสิทธิภาพของซีพียู/หน่วยความจำได้อย่างมีประสิทธิภาพ diff --git a/platform/mv3/extension/_locales/pt_PT/messages.json b/platform/mv3/extension/_locales/pt_PT/messages.json index d02147916eecf..4a16597c28ad5 100644 --- a/platform/mv3/extension/_locales/pt_PT/messages.json +++ b/platform/mv3/extension/_locales/pt_PT/messages.json @@ -112,11 +112,11 @@ "description": "First paragraph of 'Filter issues' section in Support pane" }, "supportS6P1S1": { - "message": "Para evitar sobrecarregar os voluntários com relatórios duplicados, verifique se o problema ainda não foi relatado.", + "message": "Para evitar sobrecarregar os voluntários com relatórios duplicados, verifique se o problema ainda não foi relatado. Nota: clicar no botão fará com que a origem da página seja enviada para o GitHub.", "description": "A paragraph in the filter issue reporter section" }, "supportFindSpecificButton": { - "message": "Encontrar relatórios semelhantes", + "message": "Encontrar relatórios semelhantes no GitHub", "description": "A clickable link in the filter issue reporter section" }, "supportS6URL": { @@ -160,11 +160,11 @@ "description": "An entry in the widget used to select the type of issue" }, "supportS6Checkbox1": { - "message": "Classifica a página web como “NSFW” (“Não segura para o trabalho”)", + "message": "Classificar a página web como “NSFW” (“Não segura para o trabalho”)", "description": "A checkbox to use for NSFW sites" }, "supportReportSpecificButton": { - "message": "Criar novo relatório", + "message": "Criar novo relatório no GitHub", "description": "Text for button which open an external webpage in Support pane" }, "firstRunSectionLabel": { diff --git a/platform/mv3/extension/_locales/th/messages.json b/platform/mv3/extension/_locales/th/messages.json index ad19d5dd0097f..8bd35902e8c8b 100644 --- a/platform/mv3/extension/_locales/th/messages.json +++ b/platform/mv3/extension/_locales/th/messages.json @@ -4,15 +4,15 @@ "description": "extension name." }, "extShortDesc": { - "message": "A permission-less content blocker. Blocks ads, trackers, miners, and more immediately upon installation.", + "message": "ตัวบล็อกเนื้อหาที่ไม่ต้องขออนุญาต บล็อกโฆษณา ตัวติดตาม ตัวขุด และอื่น ๆ ทันทีหลังจากติดตั้ง", "description": "this will be in the Chrome web store: must be 132 characters or less" }, "perRulesetStats": { - "message": "{{ruleCount}} rules, converted from {{filterCount}} network filters", + "message": "{{ruleCount}} เงื่อนไข, แปลงมาจาก {{filterCount}} ตัวกรองเครือข่าย", "description": "Appears aside each filter list in the _3rd-party filters_ pane" }, "dashboardName": { - "message": "uBO Lite — Dashboard", + "message": "uBO Lite — แดชบอร์ด", "description": "English: uBO Lite — Dashboard" }, "settingsPageName": { @@ -32,23 +32,23 @@ "description": "Label in the popup panel for the current filtering mode" }, "popupTipReport": { - "message": "Report an issue on this website", + "message": "รายงานปัญหาที่เกิดบนเว็บไซต์นี้", "description": "Tooltip used for the 'chat' icon in the panel" }, "popupTipDashboard": { - "message": "Open the dashboard", + "message": "เปิดแดชบอร์ด", "description": "English: Click to open the dashboard" }, "popupMoreButton": { - "message": "More", + "message": "ขยาย", "description": "Label to be used to show popup panel sections" }, "popupLessButton": { - "message": "Less", + "message": "ย่อลง ", "description": "Label to be used to hide popup panel sections" }, "3pGroupDefault": { - "message": "Default", + "message": "ค่าเริ่มต้น", "description": "Header for a ruleset section in 'Filter lists pane'" }, "3pGroupAds": { @@ -56,7 +56,7 @@ "description": "Header for a ruleset section in 'Filter lists pane'" }, "3pGroupPrivacy": { - "message": "Privacy", + "message": "ความเป็นส่วนตัว", "description": "Header for a ruleset section in 'Filter lists pane'" }, "3pGroupMalware": { @@ -64,15 +64,15 @@ "description": "Header for a ruleset section in 'Filter lists pane'" }, "3pGroupAnnoyances": { - "message": "Annoyances", + "message": "ความรำคาญ", "description": "Header for a ruleset section in 'Filter lists pane'" }, "3pGroupMisc": { - "message": "Miscellaneous", + "message": "เบ็ดเตล็ด", "description": "Header for a ruleset section in 'Filter lists pane'" }, "3pGroupRegions": { - "message": "Regions, languages", + "message": "ภูมิภาค, ภาษา", "description": "Header for a ruleset section in 'Filter lists pane'" }, "aboutChangelog": { @@ -80,15 +80,15 @@ "description": "" }, "aboutCode": { - "message": "Source code (GPLv3)", + "message": "ซอร์สโค้ด (GPLv3)", "description": "English: Source code (GPLv3)" }, "aboutContributors": { - "message": "Contributors", + "message": "ผู้สนับสนุน", "description": "English: Contributors" }, "aboutSourceCode": { - "message": "Source code", + "message": "ซอร์สโค้ด", "description": "Link text to source code repo" }, "aboutTranslations": { @@ -96,7 +96,7 @@ "description": "Link text to translations repo" }, "aboutFilterLists": { - "message": "Filter lists", + "message": "รายการตัวกรอง", "description": "Link text to uBO's own filter lists repo" }, "aboutDependencies": { @@ -104,43 +104,43 @@ "description": "Shown in the About pane" }, "supportS6H": { - "message": "Report a filter issue", + "message": "รายงานปัญหาตัวกรอง", "description": "Header of 'Report a filter issue' section in Support pane" }, "supportS3P1": { - "message": "Report filter issues with specific websites to the uBlockOrigin/uAssets issue tracker. Requires a GitHub account.", + "message": "รายงานปัญหาตัวกรองผ่านเว็บไซต์เฉพาะที่ uBlockOrigin/uAssets ปัญหาตัวติดตาม. ต้องใช้บัญชี GitHub", "description": "First paragraph of 'Filter issues' section in Support pane" }, "supportS6P1S1": { - "message": "To avoid burdening volunteers with duplicate reports, please verify that the issue has not already been reported. Note: clicking the button will cause the page's origin to be sent to GitHub.", + "message": "เพื่อเป็นการลดภาระอาสาสมัครจากการรายงานซ้ำซ้อน โปรดตรวจสอบก่อนว่าปัญหาดังกล่าวได้รับการรายงานไปแล้วหรือยัง หมายเหตุ: คลิกที่ปุ่มจะเป็นการส่งต้นทางของหน้าเว็บไปยัง GitHub", "description": "A paragraph in the filter issue reporter section" }, "supportFindSpecificButton": { - "message": "Find similar reports on GitHub", + "message": "ค้นหารายงานที่คล้ายกันบน GitHub", "description": "A clickable link in the filter issue reporter section" }, "supportS6URL": { - "message": "Address of the webpage:", + "message": "ที่อยู่ของเว็บเพจ:", "description": "Label for the URL of the page" }, "supportS6Select1": { - "message": "The webpage…", + "message": "เว็บเพจ…", "description": "Label for widget to select type of issue" }, "supportS6Select1Option0": { - "message": "-- Pick an entry --", + "message": "-- เลือกรายการ --", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option1": { - "message": "Shows ads or ad leftovers", + "message": "แสดงโฆษณาหรือสิ่งที่ตกค้างอยู่", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option2": { - "message": "Has overlays or other nuisances", + "message": "มีโอเวอร์เลย์หรือสิ่งอื่น ๆ ที่รบกวน", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option3": { - "message": "Detects uBO Lite", + "message": "ตรวจหา uBlock Origin", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option4": { @@ -148,19 +148,19 @@ "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option5": { - "message": "Malfunctions when uBO Lite is enabled", + "message": "ความผิดปกติเมื่อเปิดใช้งาน uBO Lite", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option6": { - "message": "Opens unwanted tabs or windows", + "message": "เปิดแท็บหรือหน้าต่างที่ไม่ต้องการ", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option7": { - "message": "Leads to badware, phishing", + "message": "นำไปสู่แบดแวร์ ฟิชชิ่ง", "description": "An entry in the widget used to select the type of issue" }, "supportS6Checkbox1": { - "message": "Label the webpage as “NSFW” (“Not Safe For Work”)", + "message": "ติดป้ายเว็บเพจว่าเป็น “NSFW” (“ไม่ปลอดภัยกับงาน (Not Safe For Work)”)", "description": "A checkbox to use for NSFW sites" }, "supportReportSpecificButton": { @@ -180,11 +180,11 @@ "description": "The header text for the default filtering mode section" }, "defaultFilteringModeDescription": { - "message": "The default filtering mode will be overridden by per-website filtering modes. You can adjust the filtering mode on any given website according to whichever mode works best on that website. Each mode has its advantages and disadvantages.", + "message": "โหมดการคัดกรองเริ่มต้นจะถูกแทนที่ด้วยโหมดการคัดกรองเฉพาะเว็บไซต์ คุณสามารถปรับแต่งโหมดคัดกรองสำหรับเว็บไซต์ที่มีตามโหมดใดก็ได้ที่ทำงานได้ดีที่สุดบนเว็บไซต์นั้น ซึ่งแต่ละโหมดก็มีข้อดีข้อเสีย", "description": "This describes the default filtering mode setting" }, "filteringMode0Name": { - "message": "no filtering", + "message": "ไม่มีการคัดกรอง", "description": "Name of blocking mode 0" }, "filteringMode1Name": { @@ -192,27 +192,27 @@ "description": "Name of blocking mode 1" }, "filteringMode2Name": { - "message": "optimal", + "message": "เหมาะสมที่สุด", "description": "Name of blocking mode 2" }, "filteringMode3Name": { - "message": "complete", + "message": "สมบูรณ์", "description": "Name of blocking mode 3" }, "basicFilteringModeDescription": { - "message": "Basic network filtering from selected filter lists.\n\nDoes not require permission to read and modify data on websites.", + "message": "การคัดกรองเครือข่ายเบื้องต้นจากรายการที่เลือก\n\nไม่ต้องขออนุญาตในการอ่านและปรับแต่งข้อมูลบนเว็บไซต์", "description": "This describes the 'basic' filtering mode" }, "optimalFilteringModeDescription": { - "message": "Advanced network filtering plus specific extended filtering from selected filter lists.\n\nRequires broad permission to read and modify data on all websites.", + "message": "การคัดกรองเครือข่ายขั้นสูงและการคัดกรองเพิ่มเติมโดยเฉพาะจากรายการที่เลือก\n\nต้องขออนุญาตแบบรวมในการอ่านและปรับแต่งข้อมูลบนทุกเว็บไซต์", "description": "This describes the 'optimal' filtering mode" }, "completeFilteringModeDescription": { - "message": "Advanced network filtering plus specific and generic extended filtering from selected filter lists.\n\nRequires broad permission to read and modify data on all websites.\n\nGeneric extended filtering may cause higher webpage resources usage.", + "message": "การคัดกรองเครือข่ายขั้นสูงและการคัดกรองทั่วไปเพิ่มเติม และโดยเฉพาะจากรายการที่เลือก\n\nต้องขออนุญาตแบบรวมในการอ่านและปรับแต่งข้อมูลบนทุกเว็บไซต์\n\nการคัดกรองทั่วไปเพิ่มเติมอาจะทำให้มีการใช้งานทรัพยากรเว็บเพจสูงขึ้น", "description": "This describes the 'complete' filtering mode" }, "noFilteringModeDescription": { - "message": "List of websites for which no filtering will take place.", + "message": "รายการเว็บไซต์ซึ่งไม่มีการคัดกรอง", "description": "A short description for the editable field which lists trusted sites" }, "noFilteringModePlaceholder": { @@ -232,11 +232,11 @@ "description": "Label for a checkbox in the options page" }, "enableStrictBlockLabel": { - "message": "Enable strict blocking", + "message": "เปิดใช้งานการบล๊อกเข็มงวด", "description": "Label for a checkbox in the options page" }, "enableStrictBlockLegend": { - "message": "Navigation to potentially undesirable sites will be blocked, and you will be offered the option to proceed.", + "message": "การนำทางไปยังเว็บไซต์ที่ไม่พึงประสงค์จะถูกบล็อก และคุณจะได้รับตัวเลือกเพื่อดำเนินการต่อ", "description": "Label for a checkbox in the options page" }, "findListsPlaceholder": { @@ -244,23 +244,23 @@ "description": "Placeholder for the input field used to find lists" }, "strictblockTitle": { - "message": "Page blocked", + "message": "บล๊อกเพจ", "description": "Webpage title for the strict-blocked page" }, "strictblockSentence1": { - "message": "uBO Lite has prevented the following page from loading:", + "message": "uBO Lite ได้ป้องกันเพจเหล่านี้จากการโหลด:", "description": "Sentence used in the strict-blocked page" }, "strictblockReasonSentence1": { - "message": "The page was blocked because of a matching filter in {{listname}}.", + "message": "เว็บเพจถูกบล๊อก เพราะตรงกับรายการคัดกรองใน {{listname}}.", "description": "Text informing about what is causing the page to be blocked" }, "strictblockRedirectSentence1": { - "message": "The blocked page wants to redirect to another site. If you choose to proceed, you will navigate directly to: {{url}}", + "message": "เว็บเพจที่บล๊อกต้องการเด้งไปเว็บไซต์อื่น หากคุณต้องการดำเนินการต่อ คุณจะถูกนำทางโดยตรงไปที่: {{url}}", "description": "Text warning about an incoming redirect" }, "strictblockNoParamsPrompt": { - "message": "without parameters", + "message": "ไม่ระบุพารามิเตอร์", "description": "Label to be used for the parameter-less URL" }, "strictblockBack": { diff --git a/src/_locales/pt_PT/messages.json b/src/_locales/pt_PT/messages.json index 0dd6035ce486e..26be572860377 100644 --- a/src/_locales/pt_PT/messages.json +++ b/src/_locales/pt_PT/messages.json @@ -900,11 +900,11 @@ "description": "Text for button which open an external webpage in Support pane" }, "supportReportSpecificButton": { - "message": "Criar novo relatório", + "message": "Criar novo relatório no GitHub", "description": "Text for button which open an external webpage in Support pane" }, "supportFindSpecificButton": { - "message": "Encontrar relatórios semelhantes", + "message": "Encontrar relatórios semelhantes no GitHub", "description": "A clickable link in the filter issue reporter section" }, "supportS1H": { @@ -964,7 +964,7 @@ "description": "Header of 'Report a filter issue' section in Support pane" }, "supportS6P1S1": { - "message": "Para evitar sobrecarregar os voluntários com relatórios duplicados, por favor verifique se o problema ainda não foi relatado.", + "message": "Para evitar sobrecarregar os voluntários com relatórios duplicados, verifique se o problema ainda não foi relatado. Nota: clicar no botão fará com que a origem da página seja enviada para o GitHub.", "description": "A paragraph in the filter issue reporter section" }, "supportS6P2S1": { diff --git a/src/_locales/th/messages.json b/src/_locales/th/messages.json index 52a7d883c534b..9cad465f24826 100644 --- a/src/_locales/th/messages.json +++ b/src/_locales/th/messages.json @@ -28,7 +28,7 @@ "description": "appears as tab name in dashboard" }, "3pPageName": { - "message": "ตัวกรองจากที่อื่น", + "message": "รายการตัวกรอง", "description": "appears as tab name in dashboard" }, "1pPageName": { @@ -36,19 +36,19 @@ "description": "appears as tab name in dashboard" }, "rulesPageName": { - "message": "กฎของฉัน", + "message": "เงื่อนไขของฉัน", "description": "appears as tab name in dashboard" }, "whitelistPageName": { - "message": "รายการยกเว้น", + "message": "เว็บไซต์ที่เชื่อถือได้", "description": "appears as tab name in dashboard" }, "shortcutsPageName": { - "message": "Shortcuts", + "message": "ทางลัด", "description": "appears as tab name in dashboard" }, "statsPageName": { - "message": "uBlock₀ — ประวัติ", + "message": "uBlock₀ — บันทึก", "description": "Title for the logger window" }, "aboutPageName": { @@ -56,11 +56,11 @@ "description": "appears as tab name in dashboard" }, "supportPageName": { - "message": "ช่วยเหลือ", + "message": "สนับสนุน", "description": "appears as tab name in dashboard" }, "assetViewerPageName": { - "message": "uBlock₀ — Asset viewer", + "message": "uBlock₀ — โปรแกรมดูสิ่งที่ถือครอง", "description": "Title for the asset viewer page" }, "advancedSettingsPageName": { @@ -68,7 +68,7 @@ "description": "Title for the advanced settings page" }, "popupPowerSwitchInfo": { - "message": "Click: เปิด/ปิดการทำงานของ uBlock₀ สำหรับเว็บไซต์นี้ \nCtrl+click: ปิดการทำงานของ uBlock₀ เฉพาะหน้าเว็บนี้", + "message": "คลิก: ปิด/เปิดการทำงานของ uBlock₀ สำหรับเว็บไซต์นี้ \nCtrl+คลิก: ปิดการทำงานของ uBlock₀ เฉพาะหน้าเว็บนี้", "description": "English: Click: disable/enable uBlock₀ for this site.\n\nCtrl+click: disable uBlock₀ only on this page." }, "popupPowerSwitchInfo1": { @@ -168,7 +168,7 @@ "description": "Tooltip for the no-cosmetic-filtering per-site switch" }, "popupTipNoRemoteFonts": { - "message": "Toggle the blocking of remote fonts for this site", + "message": "สลับเป็นฟอนต์เครื่องที่บล็อกสำหรับเว็บนี้", "description": "Tooltip for the no-remote-fonts per-site switch" }, "popupTipNoRemoteFonts1": { @@ -384,7 +384,7 @@ "description": "" }, "settingsNoRemoteFontsPrompt": { - "message": "Block remote fonts", + "message": "ฟอนต์เครื่องที่บล็อก", "description": "" }, "settingsNoScriptingPrompt": { @@ -396,7 +396,7 @@ "description": "background information: https://github.com/gorhill/uBlock/issues/3150" }, "settingsUncloakCnamePrompt": { - "message": "Uncloak canonical names", + "message": "เปิดเผยชื่อตามหลักเกณฑ์", "description": "background information: https://github.com/uBlockOrigin/uBlock-issues/issues/1513" }, "settingsAdvanced": { @@ -420,11 +420,11 @@ "description": "English: Last backup:" }, "3pListsOfBlockedHostsPrompt": { - "message": "{{netFilterCount}} network filters + {{cosmeticFilterCount}} cosmetic filters from:", + "message": "{{netFilterCount}} ตัวกรองเครือข่าย + {{cosmeticFilterCount}} ตกแต่งตัวกรองจาก:", "description": "Appears at the top of the _3rd-party filters_ pane" }, "3pListsOfBlockedHostsPerListStats": { - "message": "{{used}} used out of {{total}}", + "message": "{{used}} ใช้ไปแล้วจาก {{total}}", "description": "Appears aside each filter list in the _3rd-party filters_ pane" }, "3pAutoUpdatePrompt1": { @@ -484,11 +484,11 @@ "description": "Filter lists section name" }, "3pGroupSocial": { - "message": "Social widgets", + "message": "วิดเจ็ตโซเชียล", "description": "Filter lists section name" }, "3pGroupCookies": { - "message": "Cookie notices", + "message": "คำประกาศการใช้คุ้กกี้", "description": "Filter lists section name" }, "3pGroupAnnoyances": { @@ -532,23 +532,23 @@ "description": "used as a tooltip for the spinner icon beside a list" }, "3pNetworkError": { - "message": "A network error prevented the resource from being updated.", + "message": "ความผิดพลาดของเครือข่ายขวางกั้นการอัปเดต", "description": "used as a tooltip for error icon beside a list" }, "1pTrustWarning": { - "message": "Do not add filters from untrusted sources.", + "message": "ไม่อนุญาตให้เพิ่มตัวกรองจากแหล่งที่มาที่ไม่น่าเชื่อถือ", "description": "Warning against copy-pasting filters from random sources" }, "1pEnableMyFiltersLabel": { - "message": "Enable my custom filters", + "message": "เปิดใช้งานการปรับแต่งตัวกรองของฉัน", "description": "Label for the checkbox use to enable/disable 'My filters' list" }, "1pTrustMyFiltersLabel": { - "message": "Allow custom filters requiring trust", + "message": "อนุญาตการปรับแต่งตัวกรองที่ต้องมีความน่าเชื่อถือ", "description": "Label for the checkbox use to trust the content of 'My filters' list" }, "1pImport": { - "message": "Import and append…", + "message": "นำเข้าและเพิ่มต่อท้าย…", "description": "Button in the 'My filters' pane" }, "1pExport": { @@ -560,7 +560,7 @@ "description": "English: my-ublock-static-filters_{{datetime}}.txt" }, "1pApplyChanges": { - "message": "Apply changes", + "message": "นำไปใช้", "description": "English: Apply changes" }, "rulesPermanentHeader": { @@ -576,7 +576,7 @@ "description": "This will remove all temporary rules" }, "rulesCommit": { - "message": "Commit", + "message": "ยืนยัน", "description": "This will persist temporary rules" }, "rulesEdit": { @@ -604,11 +604,11 @@ "description": "default file name to use" }, "rulesHint": { - "message": "List of your dynamic filtering rules.", + "message": "รายการเงื่อนไขตัวกรองแบบไดนามิกของคุณ", "description": "English: List of your dynamic filtering rules." }, "rulesFormatHint": { - "message": "Rule syntax: source destination type action (full documentation).", + "message": "ข้อกำหนดเงื่อนไข: การดำเนินการประเภทแหล่งปลายทาง (เอกสารฉบับเต็ม)", "description": "English: dynamic rule syntax and full documentation." }, "rulesSort": { @@ -668,7 +668,7 @@ "description": "Appears in the logger's tab selector" }, "logBehindTheScene": { - "message": "Tabless", + "message": "ตาราง", "description": "Pretty name for behind-the-scene network requests" }, "loggerCurrentTab": { @@ -680,19 +680,19 @@ "description": "Tooltip for the reload button in the logger page" }, "loggerDomInspectorTip": { - "message": "Toggle the DOM inspector", + "message": "สลับตัวตรวจสอบ DOM", "description": "Tooltip for the DOM inspector button in the logger page" }, "loggerPopupPanelTip": { - "message": "Toggle the popup panel", + "message": "สลับแผงป๊อปอัป", "description": "Tooltip for the popup panel button in the logger page" }, "loggerInfoTip": { - "message": "uBlock Origin wiki: The logger", + "message": "uBlock Origin wiki: ตัวลงบันทึก", "description": "Tooltip for the top-right info label in the logger page" }, "loggerClearTip": { - "message": "Clear logger", + "message": "ล้างบันทึก", "description": "Tooltip for the eraser in the logger page; used to blank the content of the logger" }, "loggerPauseTip": { @@ -700,23 +700,23 @@ "description": "Tooltip for the pause button in the logger page" }, "loggerUnpauseTip": { - "message": "Unpause logger", + "message": "ยกเลิกการหยุดบันทึกชั่วคราว", "description": "Tooltip for the play button in the logger page" }, "loggerRowFiltererButtonTip": { - "message": "Toggle logger filtering", + "message": "สลับตัวกรองบันทึก", "description": "Tooltip for the row filterer button in the logger page" }, "logFilterPrompt": { - "message": "filter logger content", + "message": "ตัวกรองเนื้อหาบันทึก", "description": "Placeholder string for logger output filtering input field" }, "loggerRowFiltererBuiltinTip": { - "message": "Logger filtering options", + "message": "ตัวเลือกตัวกรองบันทึก", "description": "Tooltip for the button to bring up logger output filtering options" }, "loggerRowFiltererBuiltinNot": { - "message": "Not", + "message": "ไม่", "description": "A keyword in the built-in row filtering expression" }, "loggerRowFiltererBuiltinEventful": { @@ -724,19 +724,19 @@ "description": "A keyword in the built-in row filtering expression: all items corresponding to uBO doing something (blocked, allowed, redirected, etc.)" }, "loggerRowFiltererBuiltinBlocked": { - "message": "blocked", + "message": "บล๊อกอยู่", "description": "A keyword in the built-in row filtering expression" }, "loggerRowFiltererBuiltinAllowed": { - "message": "allowed", + "message": "อนุญาต", "description": "A keyword in the built-in row filtering expression" }, "loggerRowFiltererBuiltinModified": { - "message": "modified", + "message": "แก้ไขแล้ว", "description": "A keyword in the built-in row filtering expression" }, "loggerRowFiltererBuiltin1p": { - "message": "1st-party", + "message": "ฝ่ายที่ 1", "description": "A keyword in the built-in row filtering expression" }, "loggerRowFiltererBuiltin3p": { @@ -760,19 +760,19 @@ "description": "Label to identify a rule field" }, "loggerEntryDetailsContext": { - "message": "Context", + "message": "บริบท", "description": "Label to identify a context field (typically a hostname)" }, "loggerEntryDetailsRootContext": { - "message": "Root context", + "message": "ปฐมบท", "description": "Label to identify a root context field (typically a hostname)" }, "loggerEntryDetailsPartyness": { - "message": "Partyness", + "message": "การรวมกลุ่ม", "description": "Label to identify a field providing partyness information" }, "loggerEntryDetailsType": { - "message": "Type", + "message": "ประเภท", "description": "Label to identify the type of an entry" }, "loggerEntryDetailsURL": { @@ -780,7 +780,7 @@ "description": "Label to identify the URL of an entry" }, "loggerURLFilteringHeader": { - "message": "URL rule", + "message": "เงื่อนไข URL", "description": "Small header to identify the dynamic URL filtering section" }, "loggerURLFilteringContextLabel": { @@ -792,11 +792,11 @@ "description": "Label for the type selector" }, "loggerStaticFilteringHeader": { - "message": "Static filter", + "message": "ตัวกรองแบบคงที่", "description": "Small header to identify the static filtering section" }, "loggerStaticFilteringSentence": { - "message": "{{action}} network requests of {{type}} {{br}}which URL address matches {{url}} {{br}}and which originates {{origin}},{{br}}{{importance}} there is a matching exception filter.", + "message": "{{action}} การร้องขอเครือข่ายของ {{type}} {{br}}ซึ่ง URL ตรงกันกับ {{url}} {{br}}และมีต้นทางที่ {{origin}},{{br}}{{importance}} มีตัวกรองที่ยกเว้นตรงกัน", "description": "Used in the static filtering wizard" }, "loggerStaticFilteringSentencePartBlock": { @@ -808,19 +808,19 @@ "description": "Used in the static filtering wizard" }, "loggerStaticFilteringSentencePartType": { - "message": "type “{{type}}”", + "message": "ประเภท “{{type}}”", "description": "Used in the static filtering wizard" }, "loggerStaticFilteringSentencePartAnyType": { - "message": "any type", + "message": "ประเภทใดก็ได้", "description": "Used in the static filtering wizard" }, "loggerStaticFilteringSentencePartOrigin": { - "message": "from “{{origin}}”", + "message": "จาก “{{origin}}”", "description": "Used in the static filtering wizard" }, "loggerStaticFilteringSentencePartAnyOrigin": { - "message": "from anywhere", + "message": "จากที่ใดก็ได้", "description": "Used in the static filtering wizard" }, "loggerStaticFilteringSentencePartNotImportant": { @@ -832,31 +832,31 @@ "description": "Used in the static filtering wizard" }, "loggerStaticFilteringFinderSentence1": { - "message": "Static filter {{filter}} found in:", + "message": "ตัวกรองแบบคงที่ {{filter}} พบใน:", "description": "Below this sentence, the filter list(s) in which the filter was found" }, "loggerStaticFilteringFinderSentence2": { - "message": "Static filter could not be found in any of the currently enabled filter lists", + "message": "ตัวกรองแบบคงที่ไม่พบในรายการตัวกรองที่เปิดอยู่", "description": "Message to show when a filter cannot be found in any filter lists" }, "loggerSettingDiscardPrompt": { - "message": "Logger entries which do not fulfill all three conditions below will be automatically discarded:", + "message": "รายการบันทึกที่ไม่ครบถ้วนทั้ง 3 เงื่อนไขตามด้านล่างจะถูกละทิ้งโดยอัตโนมัติ", "description": "Logger setting: A sentence to describe the purpose of the settings below" }, "loggerSettingPerEntryMaxAge": { - "message": "Preserve entries from the last {{input}} minutes", + "message": "สงวนรายการ {{input}} นาที", "description": "A logger setting" }, "loggerSettingPerTabMaxLoads": { - "message": "Preserve at most {{input}} page loads per tab", + "message": "สงวนจำนวนมากที่สุด {{input}} หน้าโหลดต่อแท็บ", "description": "A logger setting" }, "loggerSettingPerTabMaxEntries": { - "message": "Preserve at most {{input}} entries per tab", + "message": "สงวนจำนวนมากที่สุด {{input}} รายการต่อแท็บ", "description": "A logger setting" }, "loggerSettingPerEntryLineCount": { - "message": "Use {{input}} lines per entry in vertically expanded mode", + "message": "ใช้ {{input}} บรรทัดต่อรายการในโหมดการขยายแนวตั้ง", "description": "A logger setting" }, "loggerSettingHideColumnsPrompt": { @@ -872,11 +872,11 @@ "description": "A label for the filter or rule column" }, "loggerSettingHideColumnContext": { - "message": "{{input}} Context", + "message": "{{input}} บริบท", "description": "A label for the context column" }, "loggerSettingHideColumnPartyness": { - "message": "{{input}} Partyness", + "message": "{{input}} การจัดกลุ่ม", "description": "A label for the partyness column" }, "loggerExportFormatList": { @@ -888,11 +888,11 @@ "description": "Label for radio-button to pick export format" }, "loggerExportEncodePlain": { - "message": "Plain", + "message": "ธรรมดา", "description": "Label for radio-button to pick export text format" }, "loggerExportEncodeMarkdown": { - "message": "Markdown", + "message": "ทำเครื่องหมายลง", "description": "Label for radio-button to pick export text format" }, "supportOpenButton": { @@ -904,7 +904,7 @@ "description": "Text for button which open an external webpage in Support pane" }, "supportFindSpecificButton": { - "message": "Find similar reports on GitHub", + "message": "ค้นหารายงานคล้ายกันบน GitHub", "description": "A clickable link in the filter issue reporter section" }, "supportS1H": { @@ -912,31 +912,31 @@ "description": "Header of 'Documentation' section in Support pane" }, "supportS1P1": { - "message": "Read the documentation at uBlock/wiki to learn about all of uBlock Origin's features.", + "message": "อ่านเอกสารข้อมูลที่ uBlock/wiki เพื่อเรียนรู้ฟีเจอร์ทั้งหมดของ uBlock Origin", "description": "First paragraph of 'Documentation' section in Support pane" }, "supportS2H": { - "message": "Questions and support", + "message": "คำถามและการสนับสนุน", "description": "Header of 'Questions and support' section in Support pane" }, "supportS2P1": { - "message": "Answers to questions and other kinds of help support is provided on the subreddit /r/uBlockOrigin.", + "message": "การตอบคำถามและการสนับสนุนอื่น ๆ ที่เตรียมไว้ให้บน subreddit /r/uBlockOrigin", "description": "First paragraph of 'Questions and support' section in Support pane" }, "supportS3H": { - "message": "Filter issues/website is broken", + "message": "คัดกรองปัญหา/เว็บไซต์ที่ไม่สมบูรณ์", "description": "Header of 'Filter issues' section in Support pane" }, "supportS3P1": { - "message": "Report filter issues with specific websites to the uBlockOrigin/uAssets issue tracker. Requires a GitHub account.", + "message": "รายงานปัญหาการกรองด้วยเว็บไซต์เฉพาะที่ uBlockOrigin/uAssets ตัวติดตามปัญหา. ต้องใช้บัญชี GitHub", "description": "First paragraph of 'Filter issues' section in Support pane" }, "supportS3P2": { - "message": "Important: Avoid using other similarly-purposed blockers along with uBlock Origin, as this may cause filter issues on specific websites.", + "message": "สำคัญ: หลีกเลี่ยงการใช้ตัวบล๊อกอื่น ๆ ร่วมกันกับ uBlock Origin ซึ่งอาจทำให้เกิดปัญหาในการกรองของหน้าเว็บไซต์ที่ต้องการ", "description": "Second paragraph of 'Filter issues' section in Support pane" }, "supportS3P3": { - "message": "Tips: Be sure your filter lists are up to date. The logger is the primary tool to diagnose filter-related issues.", + "message": "เคล็ดลับ: ให้แน่ใจว่ารายการตัวกรองของคุณได้รับการอัปเดตแล้ว ตัวบันทึก เป็นเครื่องมือหลักในการวินิจฉัยปัญหาที่เกี่ยวเนื่อง", "description": "Third paragraph of 'Filter issues' section in Support pane" }, "supportS4H": { @@ -944,43 +944,43 @@ "description": "Header of 'Bug report' section in Support pane" }, "supportS4P1": { - "message": "Report issues with uBlock Origin itself to the uBlockOrigin/uBlock-issue issue tracker. Requires a GitHub account.", + "message": "รายงานปัญหาด้วย uBlock Origin เอง uBlockOrigin/uBlock-issue ปัญหาตัวติดตาม. ต้องใช้บัญชี GitHub", "description": "First paragraph of 'Bug report' section in Support pane" }, "supportS5H": { - "message": "Troubleshooting Information", + "message": "ข้อมูลการแก้ไขปัญหา", "description": "Header of 'Troubleshooting Information' section in Support pane" }, "supportS5P1": { - "message": "Below is technical information that might be useful when volunteers are trying to help you solve a problem.", + "message": "ด้านล่างนี้เป็นข้อมูลทางเทคนิคซึ่งอาจเป็นประโยชน์ในกรณีที่อาสาสมัครพยายามให้การช่วยเหลือในการแก้ไขปัญหาให้คุณ", "description": "First paragraph of 'Troubleshooting Information' section in Support pane" }, "supportS5P2": { - "message": "Important: Potentially private or sensitive information is redacted by default. Redacted information may make it more difficult to solve a problem.", + "message": "สำคัญ: ข้อมูลที่อาจเป็นความลับหรือละเอียดอ่อนจะถูกแก้ไขตามค่าเริ่มต้น ข้อมูลที่แก้ไขอาจะทำให้การแก้ไขปัญหายากขึ้น", "description": "Second paragraph of 'Troubleshooting Information' section in Support pane" }, "supportS6H": { - "message": "Report a filter issue", + "message": "รายงานปัญหาตัวกรอง", "description": "Header of 'Report a filter issue' section in Support pane" }, "supportS6P1S1": { - "message": "To avoid burdening volunteers with duplicate reports, please verify that the issue has not already been reported. Note: clicking the button will cause the page's origin to be sent to GitHub.", + "message": "เพื่อเป็นการลดภาระอาสาสมัครจากการรายงานซ้ำซ้อน โปรดตรวจสอบก่อนว่าปัญหาดังกล่าวได้รับการรายงานไปแล้วหรือยัง หมายเหตุ: คลิกที่ปุ่มจะเป็นการส่งต้นทางของหน้าเว็บไปยัง GitHub", "description": "A paragraph in the filter issue reporter section" }, "supportS6P2S1": { - "message": "Filter lists are updated daily. Be sure your issue has not already been addressed in the most recent filter lists.", + "message": "มีการอัปเดตรายการตัวกรองประจำวัน ให้แน่ใจว่าปัญหาของคุณยังไม่ได้ระบุไว้ในรายการตัวกรองล่าสุด", "description": "A paragraph in the filter issue reporter section" }, "supportS6P2S2": { - "message": "Verify that the issue still exists after reloading the problematic webpage.", + "message": "ตรวจสอบว่าปัญหายังคงอยู่หลังจากมีการโหลดหน้าเว็บเพจที่มีปัญหาซ้ำแล้ว", "description": "A paragraph in the filter issue reporter section" }, "supportS6URL": { - "message": "Address of the web page:", + "message": "ที่อยู่ของเว็บเพจ:", "description": "Label for the URL of the page" }, "supportS6Select1": { - "message": "The web page…", + "message": "เว็บเพจ…", "description": "Label for widget to select type of issue" }, "supportS6Select1Option0": { @@ -988,35 +988,35 @@ "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option1": { - "message": "Shows ads or ad leftovers", + "message": "แสดงโฆษณาหรือสิ่งที่ตกค้างอยู่", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option2": { - "message": "Has overlays or other nuisances", + "message": "มีโอเวอร์เลย์หรือสิ่งอื่น ๆ ที่รบกวน", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option3": { - "message": "Detects uBlock Origin", + "message": "ตรวจหา uBlock Origin", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option4": { - "message": "Has privacy-related issues", + "message": "มีปัญหาที่เกี่ยวเนื่องกับความเป็นส่วนตัว", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option5": { - "message": "Malfunctions when uBlock Origin is enabled", + "message": "ความผิดปกติเมื่อเปิดใช้งาน uBlock Origin", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option6": { - "message": "Opens unwanted tabs or windows", + "message": "เปิดแท็บหรือหน้าต่างที่ไม่ต้องการ", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option7": { - "message": "Leads to badware, phishing", + "message": "นำไปสู่แบดแวร์ ฟิชชิ่ง", "description": "An entry in the widget used to select the type of issue" }, "supportS6Checkbox1": { - "message": "Label the web page as “NSFW” (“Not Safe For Work”)", + "message": "ติดป้ายเว็บเพจว่าเป็น “NSFW” (“ไม่ปลอดภัยกับงาน (Not Safe For Work)”)", "description": "A checkbox to use for NSFW sites" }, "supportRedact": { @@ -1032,11 +1032,11 @@ "description": "Link to privacy policy on GitHub (English)" }, "aboutChangelog": { - "message": "Changelog", + "message": "บันทึกการเปลี่ยนแปลง", "description": "" }, "aboutCode": { - "message": "Source code (GPLv3)", + "message": "ซอร์สโค้ด (GPLv3)", "description": "English: Source code (GPLv3)" }, "aboutContributors": { @@ -1044,7 +1044,7 @@ "description": "English: Contributors" }, "aboutSourceCode": { - "message": "Source code", + "message": "ซอร์สโค้ด", "description": "Link text to source code repo" }, "aboutTranslations": { @@ -1052,7 +1052,7 @@ "description": "Link text to translations repo" }, "aboutFilterLists": { - "message": "Filter lists", + "message": "รายการตัวกรอง", "description": "Link text to uBO's own filter lists repo" }, "aboutDependencies": { @@ -1060,7 +1060,7 @@ "description": "Shown in the About pane" }, "aboutCDNs": { - "message": "uBO's own filter lists are freely hosted on the following CDNs:", + "message": "uBO's ถือครองรายการตัวกรองที่เก็บไว้ที่ CDNs:", "description": "Shown in the About pane" }, "aboutCDNsInfo": { @@ -1068,7 +1068,7 @@ "description": "Shown in the About pane" }, "aboutBackupDataButton": { - "message": "Back up to file…", + "message": "แบ็กอัพไปยังไฟล์…", "description": "Text for button to create a backup of all settings" }, "aboutBackupFilename": { @@ -1076,7 +1076,7 @@ "description": "English: my-ublock-backup_{{datetime}}.txt" }, "aboutRestoreDataButton": { - "message": "Restore from file…", + "message": "คืนค่าจากไฟล์…", "description": "English: Restore from file..." }, "aboutResetDataButton": { @@ -1084,19 +1084,19 @@ "description": "English: Reset to default settings..." }, "aboutRestoreDataConfirm": { - "message": "All your settings will be overwritten using data backed up on {{time}}, and uBlock₀ will restart.\n\nOverwrite all existing settings using backed up data?", + "message": "การตั้งค่าทั้งหมดของคุณจะถูกเขียนทับโดยใช้ข้อมูลที่แบ็กอัพไว้บน {{time}} และ uBlock₀ จะรีสตาร์ท\n\nเขียนทับการตั้งค่าทั้งหมดด้วยข้อมูลที่แบ็กอัพหรือไม่?", "description": "Message asking user to confirm restore" }, "aboutRestoreDataError": { - "message": "The data could not be read or is invalid", + "message": "ไม่สามารถอ่านข้อมูลได้หรือข้อมูลไม่ถูกต้อง", "description": "Message to display when an error occurred during restore" }, "aboutResetDataConfirm": { - "message": "All your settings will be removed, and uBlock₀ will restart.\n\nReset uBlock₀ to factory settings?", + "message": "การตั้งค่าทั้งหมดของคุณจะถูกลบทิ้ง และ uBlock₀ จะรีสตาร์ท\n\nรีเซ็ต uBlock₀ เป็นค่าเริ่มต้นโรงงาน?", "description": "Message asking user to confirm reset" }, "errorCantConnectTo": { - "message": "Network error: {{msg}}", + "message": "เครือข่ายผิดพลาด: {{msg}}", "description": "English: Network error: {{msg}}" }, "subscriberConfirm": { @@ -1132,11 +1132,11 @@ "description": "English: {{value}} days ago" }, "showDashboardButton": { - "message": "Show Dashboard", + "message": "แสดงแดชบอร์ด", "description": "Firefox/Fennec-specific: Show Dashboard" }, "showNetworkLogButton": { - "message": "Show Logger", + "message": "แสดงบันทึก", "description": "Firefox/Fennec-specific: Show Logger" }, "fennecMenuItemBlockingOff": { @@ -1156,7 +1156,7 @@ "description": "Used in the strict-blocking page" }, "docblockedNoParamsPrompt": { - "message": "without parameters", + "message": "ไม่ระบุพารามิเตอร์", "description": "label to be used for the parameter-less URL: https://cloud.githubusercontent.com/assets/585534/9832014/bfb1b8f0-593b-11e5-8a27-fba472a5529a.png" }, "docblockedFoundIn": { @@ -1172,7 +1172,7 @@ "description": "English: Close this window" }, "docblockedDontWarn": { - "message": "Don't warn me again about this site", + "message": "ไม่ต้องแจ้งเตือนอีกเกี่ยวกับเว็บไซต์นี้", "description": "Label for checkbox in document-blocked page" }, "docblockedProceed": { @@ -1192,19 +1192,19 @@ "description": "Button text to navigate to the blocked page" }, "docblockedRedirectPrompt": { - "message": "The blocked page wants to redirect to another site. If you choose to proceed, you will navigate directly to: {{url}}", + "message": "เว็บเพจที่บล๊อกต้องการเด้งไปเว็บไซต์อื่น หากคุณต้องการดำเนินการต่อ คุณจะถูกนำทางโดยตรงไปที่: {{url}}", "description": "Text warning about an incoming redirect" }, "cloudPush": { - "message": "Export to cloud storage", + "message": "ส่งออกไปที่เก็บบนคลาวด์", "description": "tooltip" }, "cloudPull": { - "message": "Import from cloud storage", + "message": "นำเข้าจากที่เก็บบนคลาวด์", "description": "tooltip" }, "cloudPullAndMerge": { - "message": "Import from cloud storage and merge with current settings", + "message": "นำเข้าจากที่เก็บบนคลาวด์และผสานเข้ากับการตั้งค่าปัจจุบัน", "description": "tooltip" }, "cloudNoData": { @@ -1220,15 +1220,15 @@ "description": "A warning to users at the top of 'Advanced settings' page" }, "genericSubmit": { - "message": "Submit", + "message": "ยืนยัน", "description": "for generic 'Submit' buttons" }, "genericApplyChanges": { - "message": "Apply changes", + "message": "ใช้การเปลี่ยนแปลง", "description": "for generic 'Apply changes' buttons" }, "genericRevert": { - "message": "Revert", + "message": "คืนค่ากลับ", "description": "for generic 'Revert' buttons" }, "genericBytes": { @@ -1236,7 +1236,7 @@ "description": "" }, "contextMenuBlockElementInFrame": { - "message": "Block element in frame…", + "message": "บล๊อกองค์ประกอบในเฟรม…", "description": "An entry in the browser's contextual menu" }, "contextMenuSubscribeToList": { @@ -1252,11 +1252,11 @@ "description": "A context menu entry, to view the source code of the target resource" }, "shortcutCapturePlaceholder": { - "message": "Type a shortcut", + "message": "พิมพ์ทางลัด", "description": "Placeholder string for input field used to capture a keyboard shortcut" }, "genericMergeViewScrollLock": { - "message": "Toggle locked scrolling", + "message": "สลับการล็อกการเลื่อน", "description": "Tooltip for the button used to lock scrolling between the views in the 'My rules' pane" }, "genericCopyToClipboard": { @@ -1268,15 +1268,15 @@ "description": "Label for buttons used to select all text in editor" }, "toggleCosmeticFiltering": { - "message": "Toggle cosmetic filtering", + "message": "สลับตัวกรองการตกแต่ง", "description": "Label for keyboard shortcut used to toggle cosmetic filtering" }, "toggleJavascript": { - "message": "Toggle JavaScript", + "message": "สลับ JavaScript", "description": "Label for keyboard shortcut used to toggle no-scripting switch" }, "relaxBlockingMode": { - "message": "Relax blocking mode", + "message": "ผ่อนปรนโหมดการบล๊อก", "description": "Label for keyboard shortcut used to relax blocking mode" }, "storageUsed": { @@ -1300,11 +1300,11 @@ "description": "Message used in frame placeholders" }, "linterMainReport": { - "message": "Errors: {{count}}", + "message": "ข้อผิดพลาด: {{count}}", "description": "Summary of number of errors as reported by the linter " }, "unprocessedRequestTooltip": { - "message": "Could not filter properly at browser launch. Reload the page to ensure proper filtering.", + "message": "ไม่สามารถกรองได้สมบูรณ์เมื่อตอนเริ่มเบราว์เซอร์ โหลดหน้าเว็บใหม่เพื่อให้การกรองสมบูรณ์", "description": "A warning which will appear in the popup panel if needed" }, "dummy": { From 27e2d6a513db331b9b4dc6363d3333d8f46471c3 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Fri, 21 Mar 2025 13:31:40 -0400 Subject: [PATCH 0718/1099] Import translation work from https://crowdin.com/project/ublock --- platform/mv3/extension/_locales/ar/messages.json | 8 ++++++++ platform/mv3/extension/_locales/az/messages.json | 8 ++++++++ platform/mv3/extension/_locales/be/messages.json | 8 ++++++++ platform/mv3/extension/_locales/bg/messages.json | 8 ++++++++ platform/mv3/extension/_locales/bn/messages.json | 8 ++++++++ platform/mv3/extension/_locales/br_FR/messages.json | 8 ++++++++ platform/mv3/extension/_locales/bs/messages.json | 8 ++++++++ platform/mv3/extension/_locales/ca/messages.json | 8 ++++++++ platform/mv3/extension/_locales/cs/messages.json | 8 ++++++++ platform/mv3/extension/_locales/cv/messages.json | 8 ++++++++ platform/mv3/extension/_locales/cy/messages.json | 8 ++++++++ platform/mv3/extension/_locales/da/messages.json | 8 ++++++++ platform/mv3/extension/_locales/de/messages.json | 8 ++++++++ platform/mv3/extension/_locales/el/messages.json | 8 ++++++++ platform/mv3/extension/_locales/en_GB/messages.json | 8 ++++++++ platform/mv3/extension/_locales/eo/messages.json | 8 ++++++++ platform/mv3/extension/_locales/es/messages.json | 8 ++++++++ platform/mv3/extension/_locales/et/messages.json | 8 ++++++++ platform/mv3/extension/_locales/eu/messages.json | 8 ++++++++ platform/mv3/extension/_locales/fa/messages.json | 8 ++++++++ platform/mv3/extension/_locales/fi/messages.json | 8 ++++++++ platform/mv3/extension/_locales/fil/messages.json | 8 ++++++++ platform/mv3/extension/_locales/fr/messages.json | 8 ++++++++ platform/mv3/extension/_locales/fy/messages.json | 8 ++++++++ platform/mv3/extension/_locales/gl/messages.json | 8 ++++++++ platform/mv3/extension/_locales/gu/messages.json | 8 ++++++++ platform/mv3/extension/_locales/he/messages.json | 8 ++++++++ platform/mv3/extension/_locales/hi/messages.json | 8 ++++++++ platform/mv3/extension/_locales/hr/messages.json | 8 ++++++++ platform/mv3/extension/_locales/hu/messages.json | 8 ++++++++ platform/mv3/extension/_locales/hy/messages.json | 8 ++++++++ platform/mv3/extension/_locales/id/messages.json | 8 ++++++++ platform/mv3/extension/_locales/it/messages.json | 8 ++++++++ platform/mv3/extension/_locales/ja/messages.json | 8 ++++++++ platform/mv3/extension/_locales/ka/messages.json | 8 ++++++++ platform/mv3/extension/_locales/kk/messages.json | 8 ++++++++ platform/mv3/extension/_locales/kn/messages.json | 8 ++++++++ platform/mv3/extension/_locales/ko/messages.json | 8 ++++++++ platform/mv3/extension/_locales/lt/messages.json | 8 ++++++++ platform/mv3/extension/_locales/lv/messages.json | 8 ++++++++ platform/mv3/extension/_locales/mk/messages.json | 8 ++++++++ platform/mv3/extension/_locales/ml/messages.json | 8 ++++++++ platform/mv3/extension/_locales/mr/messages.json | 8 ++++++++ platform/mv3/extension/_locales/ms/messages.json | 8 ++++++++ platform/mv3/extension/_locales/nb/messages.json | 8 ++++++++ platform/mv3/extension/_locales/nl/messages.json | 8 ++++++++ platform/mv3/extension/_locales/oc/messages.json | 8 ++++++++ platform/mv3/extension/_locales/pa/messages.json | 8 ++++++++ platform/mv3/extension/_locales/pl/messages.json | 8 ++++++++ platform/mv3/extension/_locales/pt_BR/messages.json | 8 ++++++++ platform/mv3/extension/_locales/pt_PT/messages.json | 8 ++++++++ platform/mv3/extension/_locales/ro/messages.json | 8 ++++++++ platform/mv3/extension/_locales/ru/messages.json | 8 ++++++++ platform/mv3/extension/_locales/si/messages.json | 8 ++++++++ platform/mv3/extension/_locales/sk/messages.json | 8 ++++++++ platform/mv3/extension/_locales/sl/messages.json | 8 ++++++++ platform/mv3/extension/_locales/so/messages.json | 8 ++++++++ platform/mv3/extension/_locales/sq/messages.json | 8 ++++++++ platform/mv3/extension/_locales/sr/messages.json | 8 ++++++++ platform/mv3/extension/_locales/sv/messages.json | 8 ++++++++ platform/mv3/extension/_locales/sw/messages.json | 8 ++++++++ platform/mv3/extension/_locales/ta/messages.json | 8 ++++++++ platform/mv3/extension/_locales/te/messages.json | 8 ++++++++ platform/mv3/extension/_locales/th/messages.json | 8 ++++++++ platform/mv3/extension/_locales/tr/messages.json | 8 ++++++++ platform/mv3/extension/_locales/uk/messages.json | 8 ++++++++ platform/mv3/extension/_locales/ur/messages.json | 8 ++++++++ platform/mv3/extension/_locales/vi/messages.json | 8 ++++++++ platform/mv3/extension/_locales/zh_CN/messages.json | 8 ++++++++ platform/mv3/extension/_locales/zh_TW/messages.json | 8 ++++++++ 70 files changed, 560 insertions(+) diff --git a/platform/mv3/extension/_locales/ar/messages.json b/platform/mv3/extension/_locales/ar/messages.json index 41275c0d99691..36b7b67475145 100644 --- a/platform/mv3/extension/_locales/ar/messages.json +++ b/platform/mv3/extension/_locales/ar/messages.json @@ -278,5 +278,13 @@ "strictblockProceed": { "message": "متابعة", "description": "A button to navigate to the blocked page" + }, + "zapperTipEnter": { + "message": "Enter element zapper mode", + "description": "Tooltip for the button used to enter zapper mode" + }, + "zapperTipQuit": { + "message": "Exit element zapper mode", + "description": "Tooltip for the button used to exit zapper mode" } } diff --git a/platform/mv3/extension/_locales/az/messages.json b/platform/mv3/extension/_locales/az/messages.json index 3037727fa355a..626fec5b9edc7 100644 --- a/platform/mv3/extension/_locales/az/messages.json +++ b/platform/mv3/extension/_locales/az/messages.json @@ -278,5 +278,13 @@ "strictblockProceed": { "message": "Proceed", "description": "A button to navigate to the blocked page" + }, + "zapperTipEnter": { + "message": "Enter element zapper mode", + "description": "Tooltip for the button used to enter zapper mode" + }, + "zapperTipQuit": { + "message": "Exit element zapper mode", + "description": "Tooltip for the button used to exit zapper mode" } } diff --git a/platform/mv3/extension/_locales/be/messages.json b/platform/mv3/extension/_locales/be/messages.json index deeb4c4be9d08..7dff1f259f107 100644 --- a/platform/mv3/extension/_locales/be/messages.json +++ b/platform/mv3/extension/_locales/be/messages.json @@ -278,5 +278,13 @@ "strictblockProceed": { "message": "Працягнуць", "description": "A button to navigate to the blocked page" + }, + "zapperTipEnter": { + "message": "Enter element zapper mode", + "description": "Tooltip for the button used to enter zapper mode" + }, + "zapperTipQuit": { + "message": "Exit element zapper mode", + "description": "Tooltip for the button used to exit zapper mode" } } diff --git a/platform/mv3/extension/_locales/bg/messages.json b/platform/mv3/extension/_locales/bg/messages.json index cf834661f3eb5..ecacd4f5ea743 100644 --- a/platform/mv3/extension/_locales/bg/messages.json +++ b/platform/mv3/extension/_locales/bg/messages.json @@ -278,5 +278,13 @@ "strictblockProceed": { "message": "Продължаване", "description": "A button to navigate to the blocked page" + }, + "zapperTipEnter": { + "message": "Enter element zapper mode", + "description": "Tooltip for the button used to enter zapper mode" + }, + "zapperTipQuit": { + "message": "Exit element zapper mode", + "description": "Tooltip for the button used to exit zapper mode" } } diff --git a/platform/mv3/extension/_locales/bn/messages.json b/platform/mv3/extension/_locales/bn/messages.json index 5bbf52a761bb5..7d87026243c84 100644 --- a/platform/mv3/extension/_locales/bn/messages.json +++ b/platform/mv3/extension/_locales/bn/messages.json @@ -278,5 +278,13 @@ "strictblockProceed": { "message": "এগিয়ে যাও", "description": "A button to navigate to the blocked page" + }, + "zapperTipEnter": { + "message": "Enter element zapper mode", + "description": "Tooltip for the button used to enter zapper mode" + }, + "zapperTipQuit": { + "message": "Exit element zapper mode", + "description": "Tooltip for the button used to exit zapper mode" } } diff --git a/platform/mv3/extension/_locales/br_FR/messages.json b/platform/mv3/extension/_locales/br_FR/messages.json index ee4ea9c58575b..43f4696c545d0 100644 --- a/platform/mv3/extension/_locales/br_FR/messages.json +++ b/platform/mv3/extension/_locales/br_FR/messages.json @@ -278,5 +278,13 @@ "strictblockProceed": { "message": "Kenderc'hel", "description": "A button to navigate to the blocked page" + }, + "zapperTipEnter": { + "message": "Enter element zapper mode", + "description": "Tooltip for the button used to enter zapper mode" + }, + "zapperTipQuit": { + "message": "Exit element zapper mode", + "description": "Tooltip for the button used to exit zapper mode" } } diff --git a/platform/mv3/extension/_locales/bs/messages.json b/platform/mv3/extension/_locales/bs/messages.json index f75505801010f..00f06f12d4f64 100644 --- a/platform/mv3/extension/_locales/bs/messages.json +++ b/platform/mv3/extension/_locales/bs/messages.json @@ -278,5 +278,13 @@ "strictblockProceed": { "message": "Proceed", "description": "A button to navigate to the blocked page" + }, + "zapperTipEnter": { + "message": "Enter element zapper mode", + "description": "Tooltip for the button used to enter zapper mode" + }, + "zapperTipQuit": { + "message": "Exit element zapper mode", + "description": "Tooltip for the button used to exit zapper mode" } } diff --git a/platform/mv3/extension/_locales/ca/messages.json b/platform/mv3/extension/_locales/ca/messages.json index 82eb7f454fa49..2acb47141928d 100644 --- a/platform/mv3/extension/_locales/ca/messages.json +++ b/platform/mv3/extension/_locales/ca/messages.json @@ -278,5 +278,13 @@ "strictblockProceed": { "message": "Continua", "description": "A button to navigate to the blocked page" + }, + "zapperTipEnter": { + "message": "Enter element zapper mode", + "description": "Tooltip for the button used to enter zapper mode" + }, + "zapperTipQuit": { + "message": "Exit element zapper mode", + "description": "Tooltip for the button used to exit zapper mode" } } diff --git a/platform/mv3/extension/_locales/cs/messages.json b/platform/mv3/extension/_locales/cs/messages.json index 3de9ca455861c..8b71344f39a23 100644 --- a/platform/mv3/extension/_locales/cs/messages.json +++ b/platform/mv3/extension/_locales/cs/messages.json @@ -278,5 +278,13 @@ "strictblockProceed": { "message": "Pokračovat", "description": "A button to navigate to the blocked page" + }, + "zapperTipEnter": { + "message": "Enter element zapper mode", + "description": "Tooltip for the button used to enter zapper mode" + }, + "zapperTipQuit": { + "message": "Exit element zapper mode", + "description": "Tooltip for the button used to exit zapper mode" } } diff --git a/platform/mv3/extension/_locales/cv/messages.json b/platform/mv3/extension/_locales/cv/messages.json index 8fc526ab35263..4021b8b2f4cdf 100644 --- a/platform/mv3/extension/_locales/cv/messages.json +++ b/platform/mv3/extension/_locales/cv/messages.json @@ -278,5 +278,13 @@ "strictblockProceed": { "message": "Proceed", "description": "A button to navigate to the blocked page" + }, + "zapperTipEnter": { + "message": "Enter element zapper mode", + "description": "Tooltip for the button used to enter zapper mode" + }, + "zapperTipQuit": { + "message": "Exit element zapper mode", + "description": "Tooltip for the button used to exit zapper mode" } } diff --git a/platform/mv3/extension/_locales/cy/messages.json b/platform/mv3/extension/_locales/cy/messages.json index 6b5cfee00c452..645649f6562e5 100644 --- a/platform/mv3/extension/_locales/cy/messages.json +++ b/platform/mv3/extension/_locales/cy/messages.json @@ -278,5 +278,13 @@ "strictblockProceed": { "message": "Proceed", "description": "A button to navigate to the blocked page" + }, + "zapperTipEnter": { + "message": "Enter element zapper mode", + "description": "Tooltip for the button used to enter zapper mode" + }, + "zapperTipQuit": { + "message": "Exit element zapper mode", + "description": "Tooltip for the button used to exit zapper mode" } } diff --git a/platform/mv3/extension/_locales/da/messages.json b/platform/mv3/extension/_locales/da/messages.json index 9b679860c3ece..9902eec2ad25c 100644 --- a/platform/mv3/extension/_locales/da/messages.json +++ b/platform/mv3/extension/_locales/da/messages.json @@ -278,5 +278,13 @@ "strictblockProceed": { "message": "Fortsæt", "description": "A button to navigate to the blocked page" + }, + "zapperTipEnter": { + "message": "Enter element zapper mode", + "description": "Tooltip for the button used to enter zapper mode" + }, + "zapperTipQuit": { + "message": "Exit element zapper mode", + "description": "Tooltip for the button used to exit zapper mode" } } diff --git a/platform/mv3/extension/_locales/de/messages.json b/platform/mv3/extension/_locales/de/messages.json index 56381893e5df7..2f617ab19a2ee 100644 --- a/platform/mv3/extension/_locales/de/messages.json +++ b/platform/mv3/extension/_locales/de/messages.json @@ -278,5 +278,13 @@ "strictblockProceed": { "message": "Fortfahren", "description": "A button to navigate to the blocked page" + }, + "zapperTipEnter": { + "message": "Enter element zapper mode", + "description": "Tooltip for the button used to enter zapper mode" + }, + "zapperTipQuit": { + "message": "Exit element zapper mode", + "description": "Tooltip for the button used to exit zapper mode" } } diff --git a/platform/mv3/extension/_locales/el/messages.json b/platform/mv3/extension/_locales/el/messages.json index e45da59756a6a..b1dd6c723993a 100644 --- a/platform/mv3/extension/_locales/el/messages.json +++ b/platform/mv3/extension/_locales/el/messages.json @@ -278,5 +278,13 @@ "strictblockProceed": { "message": "Συνέχεια", "description": "A button to navigate to the blocked page" + }, + "zapperTipEnter": { + "message": "Enter element zapper mode", + "description": "Tooltip for the button used to enter zapper mode" + }, + "zapperTipQuit": { + "message": "Exit element zapper mode", + "description": "Tooltip for the button used to exit zapper mode" } } diff --git a/platform/mv3/extension/_locales/en_GB/messages.json b/platform/mv3/extension/_locales/en_GB/messages.json index fd6916d750635..02b3dcff66df7 100644 --- a/platform/mv3/extension/_locales/en_GB/messages.json +++ b/platform/mv3/extension/_locales/en_GB/messages.json @@ -278,5 +278,13 @@ "strictblockProceed": { "message": "Proceed", "description": "A button to navigate to the blocked page" + }, + "zapperTipEnter": { + "message": "Enter element zapper mode", + "description": "Tooltip for the button used to enter zapper mode" + }, + "zapperTipQuit": { + "message": "Exit element zapper mode", + "description": "Tooltip for the button used to exit zapper mode" } } diff --git a/platform/mv3/extension/_locales/eo/messages.json b/platform/mv3/extension/_locales/eo/messages.json index 6c00013391e9e..8c2c3e7ce4291 100644 --- a/platform/mv3/extension/_locales/eo/messages.json +++ b/platform/mv3/extension/_locales/eo/messages.json @@ -278,5 +278,13 @@ "strictblockProceed": { "message": "Proceed", "description": "A button to navigate to the blocked page" + }, + "zapperTipEnter": { + "message": "Enter element zapper mode", + "description": "Tooltip for the button used to enter zapper mode" + }, + "zapperTipQuit": { + "message": "Exit element zapper mode", + "description": "Tooltip for the button used to exit zapper mode" } } diff --git a/platform/mv3/extension/_locales/es/messages.json b/platform/mv3/extension/_locales/es/messages.json index f4a01210801ce..a9e679383ca66 100644 --- a/platform/mv3/extension/_locales/es/messages.json +++ b/platform/mv3/extension/_locales/es/messages.json @@ -278,5 +278,13 @@ "strictblockProceed": { "message": "Continuar", "description": "A button to navigate to the blocked page" + }, + "zapperTipEnter": { + "message": "Enter element zapper mode", + "description": "Tooltip for the button used to enter zapper mode" + }, + "zapperTipQuit": { + "message": "Exit element zapper mode", + "description": "Tooltip for the button used to exit zapper mode" } } diff --git a/platform/mv3/extension/_locales/et/messages.json b/platform/mv3/extension/_locales/et/messages.json index 37c23f0164af3..576851abfb5c3 100644 --- a/platform/mv3/extension/_locales/et/messages.json +++ b/platform/mv3/extension/_locales/et/messages.json @@ -278,5 +278,13 @@ "strictblockProceed": { "message": "Jätka", "description": "A button to navigate to the blocked page" + }, + "zapperTipEnter": { + "message": "Enter element zapper mode", + "description": "Tooltip for the button used to enter zapper mode" + }, + "zapperTipQuit": { + "message": "Exit element zapper mode", + "description": "Tooltip for the button used to exit zapper mode" } } diff --git a/platform/mv3/extension/_locales/eu/messages.json b/platform/mv3/extension/_locales/eu/messages.json index 73e6c1c470c0e..00e67b773df5a 100644 --- a/platform/mv3/extension/_locales/eu/messages.json +++ b/platform/mv3/extension/_locales/eu/messages.json @@ -278,5 +278,13 @@ "strictblockProceed": { "message": "Proceed", "description": "A button to navigate to the blocked page" + }, + "zapperTipEnter": { + "message": "Enter element zapper mode", + "description": "Tooltip for the button used to enter zapper mode" + }, + "zapperTipQuit": { + "message": "Exit element zapper mode", + "description": "Tooltip for the button used to exit zapper mode" } } diff --git a/platform/mv3/extension/_locales/fa/messages.json b/platform/mv3/extension/_locales/fa/messages.json index 5d58678db08b6..078a22bed8b02 100644 --- a/platform/mv3/extension/_locales/fa/messages.json +++ b/platform/mv3/extension/_locales/fa/messages.json @@ -278,5 +278,13 @@ "strictblockProceed": { "message": "Proceed", "description": "A button to navigate to the blocked page" + }, + "zapperTipEnter": { + "message": "Enter element zapper mode", + "description": "Tooltip for the button used to enter zapper mode" + }, + "zapperTipQuit": { + "message": "Exit element zapper mode", + "description": "Tooltip for the button used to exit zapper mode" } } diff --git a/platform/mv3/extension/_locales/fi/messages.json b/platform/mv3/extension/_locales/fi/messages.json index eb23edb1d2fa3..b21d56c681351 100644 --- a/platform/mv3/extension/_locales/fi/messages.json +++ b/platform/mv3/extension/_locales/fi/messages.json @@ -278,5 +278,13 @@ "strictblockProceed": { "message": "Jatka", "description": "A button to navigate to the blocked page" + }, + "zapperTipEnter": { + "message": "Enter element zapper mode", + "description": "Tooltip for the button used to enter zapper mode" + }, + "zapperTipQuit": { + "message": "Exit element zapper mode", + "description": "Tooltip for the button used to exit zapper mode" } } diff --git a/platform/mv3/extension/_locales/fil/messages.json b/platform/mv3/extension/_locales/fil/messages.json index 937990762817f..ef499364b27c7 100644 --- a/platform/mv3/extension/_locales/fil/messages.json +++ b/platform/mv3/extension/_locales/fil/messages.json @@ -278,5 +278,13 @@ "strictblockProceed": { "message": "Proceed", "description": "A button to navigate to the blocked page" + }, + "zapperTipEnter": { + "message": "Enter element zapper mode", + "description": "Tooltip for the button used to enter zapper mode" + }, + "zapperTipQuit": { + "message": "Exit element zapper mode", + "description": "Tooltip for the button used to exit zapper mode" } } diff --git a/platform/mv3/extension/_locales/fr/messages.json b/platform/mv3/extension/_locales/fr/messages.json index f321673813da6..fbdc060a5c961 100644 --- a/platform/mv3/extension/_locales/fr/messages.json +++ b/platform/mv3/extension/_locales/fr/messages.json @@ -278,5 +278,13 @@ "strictblockProceed": { "message": "Continuer", "description": "A button to navigate to the blocked page" + }, + "zapperTipEnter": { + "message": "Enter element zapper mode", + "description": "Tooltip for the button used to enter zapper mode" + }, + "zapperTipQuit": { + "message": "Exit element zapper mode", + "description": "Tooltip for the button used to exit zapper mode" } } diff --git a/platform/mv3/extension/_locales/fy/messages.json b/platform/mv3/extension/_locales/fy/messages.json index 2c09835ec4d26..77ce2b2368e46 100644 --- a/platform/mv3/extension/_locales/fy/messages.json +++ b/platform/mv3/extension/_locales/fy/messages.json @@ -278,5 +278,13 @@ "strictblockProceed": { "message": "Tochgean", "description": "A button to navigate to the blocked page" + }, + "zapperTipEnter": { + "message": "Enter element zapper mode", + "description": "Tooltip for the button used to enter zapper mode" + }, + "zapperTipQuit": { + "message": "Exit element zapper mode", + "description": "Tooltip for the button used to exit zapper mode" } } diff --git a/platform/mv3/extension/_locales/gl/messages.json b/platform/mv3/extension/_locales/gl/messages.json index cf4399b0aa435..cb950eee7dcb0 100644 --- a/platform/mv3/extension/_locales/gl/messages.json +++ b/platform/mv3/extension/_locales/gl/messages.json @@ -278,5 +278,13 @@ "strictblockProceed": { "message": "Proceder", "description": "A button to navigate to the blocked page" + }, + "zapperTipEnter": { + "message": "Enter element zapper mode", + "description": "Tooltip for the button used to enter zapper mode" + }, + "zapperTipQuit": { + "message": "Exit element zapper mode", + "description": "Tooltip for the button used to exit zapper mode" } } diff --git a/platform/mv3/extension/_locales/gu/messages.json b/platform/mv3/extension/_locales/gu/messages.json index 8fc526ab35263..4021b8b2f4cdf 100644 --- a/platform/mv3/extension/_locales/gu/messages.json +++ b/platform/mv3/extension/_locales/gu/messages.json @@ -278,5 +278,13 @@ "strictblockProceed": { "message": "Proceed", "description": "A button to navigate to the blocked page" + }, + "zapperTipEnter": { + "message": "Enter element zapper mode", + "description": "Tooltip for the button used to enter zapper mode" + }, + "zapperTipQuit": { + "message": "Exit element zapper mode", + "description": "Tooltip for the button used to exit zapper mode" } } diff --git a/platform/mv3/extension/_locales/he/messages.json b/platform/mv3/extension/_locales/he/messages.json index 2bbc73d76ef81..f699c126691d3 100644 --- a/platform/mv3/extension/_locales/he/messages.json +++ b/platform/mv3/extension/_locales/he/messages.json @@ -278,5 +278,13 @@ "strictblockProceed": { "message": "המשך", "description": "A button to navigate to the blocked page" + }, + "zapperTipEnter": { + "message": "Enter element zapper mode", + "description": "Tooltip for the button used to enter zapper mode" + }, + "zapperTipQuit": { + "message": "Exit element zapper mode", + "description": "Tooltip for the button used to exit zapper mode" } } diff --git a/platform/mv3/extension/_locales/hi/messages.json b/platform/mv3/extension/_locales/hi/messages.json index 621fff4a660da..920c94cc8691b 100644 --- a/platform/mv3/extension/_locales/hi/messages.json +++ b/platform/mv3/extension/_locales/hi/messages.json @@ -278,5 +278,13 @@ "strictblockProceed": { "message": "Proceed", "description": "A button to navigate to the blocked page" + }, + "zapperTipEnter": { + "message": "Enter element zapper mode", + "description": "Tooltip for the button used to enter zapper mode" + }, + "zapperTipQuit": { + "message": "Exit element zapper mode", + "description": "Tooltip for the button used to exit zapper mode" } } diff --git a/platform/mv3/extension/_locales/hr/messages.json b/platform/mv3/extension/_locales/hr/messages.json index b99ddaeb07e65..1c11f6b8749b0 100644 --- a/platform/mv3/extension/_locales/hr/messages.json +++ b/platform/mv3/extension/_locales/hr/messages.json @@ -278,5 +278,13 @@ "strictblockProceed": { "message": "Nastavi", "description": "A button to navigate to the blocked page" + }, + "zapperTipEnter": { + "message": "Enter element zapper mode", + "description": "Tooltip for the button used to enter zapper mode" + }, + "zapperTipQuit": { + "message": "Exit element zapper mode", + "description": "Tooltip for the button used to exit zapper mode" } } diff --git a/platform/mv3/extension/_locales/hu/messages.json b/platform/mv3/extension/_locales/hu/messages.json index b74005aa174f1..f0cbe38ee4915 100644 --- a/platform/mv3/extension/_locales/hu/messages.json +++ b/platform/mv3/extension/_locales/hu/messages.json @@ -278,5 +278,13 @@ "strictblockProceed": { "message": "Folytatás", "description": "A button to navigate to the blocked page" + }, + "zapperTipEnter": { + "message": "Enter element zapper mode", + "description": "Tooltip for the button used to enter zapper mode" + }, + "zapperTipQuit": { + "message": "Exit element zapper mode", + "description": "Tooltip for the button used to exit zapper mode" } } diff --git a/platform/mv3/extension/_locales/hy/messages.json b/platform/mv3/extension/_locales/hy/messages.json index 413665e45ed2a..3ecdead07186c 100644 --- a/platform/mv3/extension/_locales/hy/messages.json +++ b/platform/mv3/extension/_locales/hy/messages.json @@ -278,5 +278,13 @@ "strictblockProceed": { "message": "Շարունակել", "description": "A button to navigate to the blocked page" + }, + "zapperTipEnter": { + "message": "Enter element zapper mode", + "description": "Tooltip for the button used to enter zapper mode" + }, + "zapperTipQuit": { + "message": "Exit element zapper mode", + "description": "Tooltip for the button used to exit zapper mode" } } diff --git a/platform/mv3/extension/_locales/id/messages.json b/platform/mv3/extension/_locales/id/messages.json index 15ef69acc1f7d..c1427e018072b 100644 --- a/platform/mv3/extension/_locales/id/messages.json +++ b/platform/mv3/extension/_locales/id/messages.json @@ -278,5 +278,13 @@ "strictblockProceed": { "message": "Lanjutkan", "description": "A button to navigate to the blocked page" + }, + "zapperTipEnter": { + "message": "Enter element zapper mode", + "description": "Tooltip for the button used to enter zapper mode" + }, + "zapperTipQuit": { + "message": "Exit element zapper mode", + "description": "Tooltip for the button used to exit zapper mode" } } diff --git a/platform/mv3/extension/_locales/it/messages.json b/platform/mv3/extension/_locales/it/messages.json index 7d09d3ea8a7c0..03f4cf4d210eb 100644 --- a/platform/mv3/extension/_locales/it/messages.json +++ b/platform/mv3/extension/_locales/it/messages.json @@ -278,5 +278,13 @@ "strictblockProceed": { "message": "Procedi", "description": "A button to navigate to the blocked page" + }, + "zapperTipEnter": { + "message": "Enter element zapper mode", + "description": "Tooltip for the button used to enter zapper mode" + }, + "zapperTipQuit": { + "message": "Exit element zapper mode", + "description": "Tooltip for the button used to exit zapper mode" } } diff --git a/platform/mv3/extension/_locales/ja/messages.json b/platform/mv3/extension/_locales/ja/messages.json index 110d5b4df3548..18396880c4080 100644 --- a/platform/mv3/extension/_locales/ja/messages.json +++ b/platform/mv3/extension/_locales/ja/messages.json @@ -278,5 +278,13 @@ "strictblockProceed": { "message": "続行する", "description": "A button to navigate to the blocked page" + }, + "zapperTipEnter": { + "message": "Enter element zapper mode", + "description": "Tooltip for the button used to enter zapper mode" + }, + "zapperTipQuit": { + "message": "Exit element zapper mode", + "description": "Tooltip for the button used to exit zapper mode" } } diff --git a/platform/mv3/extension/_locales/ka/messages.json b/platform/mv3/extension/_locales/ka/messages.json index c921dde829351..eda9df6a87c57 100644 --- a/platform/mv3/extension/_locales/ka/messages.json +++ b/platform/mv3/extension/_locales/ka/messages.json @@ -278,5 +278,13 @@ "strictblockProceed": { "message": "გაგრძელება", "description": "A button to navigate to the blocked page" + }, + "zapperTipEnter": { + "message": "Enter element zapper mode", + "description": "Tooltip for the button used to enter zapper mode" + }, + "zapperTipQuit": { + "message": "Exit element zapper mode", + "description": "Tooltip for the button used to exit zapper mode" } } diff --git a/platform/mv3/extension/_locales/kk/messages.json b/platform/mv3/extension/_locales/kk/messages.json index 8fc526ab35263..4021b8b2f4cdf 100644 --- a/platform/mv3/extension/_locales/kk/messages.json +++ b/platform/mv3/extension/_locales/kk/messages.json @@ -278,5 +278,13 @@ "strictblockProceed": { "message": "Proceed", "description": "A button to navigate to the blocked page" + }, + "zapperTipEnter": { + "message": "Enter element zapper mode", + "description": "Tooltip for the button used to enter zapper mode" + }, + "zapperTipQuit": { + "message": "Exit element zapper mode", + "description": "Tooltip for the button used to exit zapper mode" } } diff --git a/platform/mv3/extension/_locales/kn/messages.json b/platform/mv3/extension/_locales/kn/messages.json index 4238ead0daa30..03944eccd3d51 100644 --- a/platform/mv3/extension/_locales/kn/messages.json +++ b/platform/mv3/extension/_locales/kn/messages.json @@ -278,5 +278,13 @@ "strictblockProceed": { "message": "Proceed", "description": "A button to navigate to the blocked page" + }, + "zapperTipEnter": { + "message": "Enter element zapper mode", + "description": "Tooltip for the button used to enter zapper mode" + }, + "zapperTipQuit": { + "message": "Exit element zapper mode", + "description": "Tooltip for the button used to exit zapper mode" } } diff --git a/platform/mv3/extension/_locales/ko/messages.json b/platform/mv3/extension/_locales/ko/messages.json index a8a6767a081ed..9f93fbca5c085 100644 --- a/platform/mv3/extension/_locales/ko/messages.json +++ b/platform/mv3/extension/_locales/ko/messages.json @@ -278,5 +278,13 @@ "strictblockProceed": { "message": "계속", "description": "A button to navigate to the blocked page" + }, + "zapperTipEnter": { + "message": "Enter element zapper mode", + "description": "Tooltip for the button used to enter zapper mode" + }, + "zapperTipQuit": { + "message": "Exit element zapper mode", + "description": "Tooltip for the button used to exit zapper mode" } } diff --git a/platform/mv3/extension/_locales/lt/messages.json b/platform/mv3/extension/_locales/lt/messages.json index feffe2456d52e..8c6bfe86c0ba3 100644 --- a/platform/mv3/extension/_locales/lt/messages.json +++ b/platform/mv3/extension/_locales/lt/messages.json @@ -278,5 +278,13 @@ "strictblockProceed": { "message": "Proceed", "description": "A button to navigate to the blocked page" + }, + "zapperTipEnter": { + "message": "Enter element zapper mode", + "description": "Tooltip for the button used to enter zapper mode" + }, + "zapperTipQuit": { + "message": "Exit element zapper mode", + "description": "Tooltip for the button used to exit zapper mode" } } diff --git a/platform/mv3/extension/_locales/lv/messages.json b/platform/mv3/extension/_locales/lv/messages.json index e7eae54c0bfae..769de4c3ae793 100644 --- a/platform/mv3/extension/_locales/lv/messages.json +++ b/platform/mv3/extension/_locales/lv/messages.json @@ -278,5 +278,13 @@ "strictblockProceed": { "message": "Turpināt", "description": "A button to navigate to the blocked page" + }, + "zapperTipEnter": { + "message": "Enter element zapper mode", + "description": "Tooltip for the button used to enter zapper mode" + }, + "zapperTipQuit": { + "message": "Exit element zapper mode", + "description": "Tooltip for the button used to exit zapper mode" } } diff --git a/platform/mv3/extension/_locales/mk/messages.json b/platform/mv3/extension/_locales/mk/messages.json index 39a5b44a517e6..6353c7e7ec445 100644 --- a/platform/mv3/extension/_locales/mk/messages.json +++ b/platform/mv3/extension/_locales/mk/messages.json @@ -278,5 +278,13 @@ "strictblockProceed": { "message": "Proceed", "description": "A button to navigate to the blocked page" + }, + "zapperTipEnter": { + "message": "Enter element zapper mode", + "description": "Tooltip for the button used to enter zapper mode" + }, + "zapperTipQuit": { + "message": "Exit element zapper mode", + "description": "Tooltip for the button used to exit zapper mode" } } diff --git a/platform/mv3/extension/_locales/ml/messages.json b/platform/mv3/extension/_locales/ml/messages.json index c0374388ac12c..80824c8d07ecd 100644 --- a/platform/mv3/extension/_locales/ml/messages.json +++ b/platform/mv3/extension/_locales/ml/messages.json @@ -278,5 +278,13 @@ "strictblockProceed": { "message": "Proceed", "description": "A button to navigate to the blocked page" + }, + "zapperTipEnter": { + "message": "Enter element zapper mode", + "description": "Tooltip for the button used to enter zapper mode" + }, + "zapperTipQuit": { + "message": "Exit element zapper mode", + "description": "Tooltip for the button used to exit zapper mode" } } diff --git a/platform/mv3/extension/_locales/mr/messages.json b/platform/mv3/extension/_locales/mr/messages.json index 8fc526ab35263..4021b8b2f4cdf 100644 --- a/platform/mv3/extension/_locales/mr/messages.json +++ b/platform/mv3/extension/_locales/mr/messages.json @@ -278,5 +278,13 @@ "strictblockProceed": { "message": "Proceed", "description": "A button to navigate to the blocked page" + }, + "zapperTipEnter": { + "message": "Enter element zapper mode", + "description": "Tooltip for the button used to enter zapper mode" + }, + "zapperTipQuit": { + "message": "Exit element zapper mode", + "description": "Tooltip for the button used to exit zapper mode" } } diff --git a/platform/mv3/extension/_locales/ms/messages.json b/platform/mv3/extension/_locales/ms/messages.json index 06c10f84d0445..47cf813c35eca 100644 --- a/platform/mv3/extension/_locales/ms/messages.json +++ b/platform/mv3/extension/_locales/ms/messages.json @@ -278,5 +278,13 @@ "strictblockProceed": { "message": "Proceed", "description": "A button to navigate to the blocked page" + }, + "zapperTipEnter": { + "message": "Enter element zapper mode", + "description": "Tooltip for the button used to enter zapper mode" + }, + "zapperTipQuit": { + "message": "Exit element zapper mode", + "description": "Tooltip for the button used to exit zapper mode" } } diff --git a/platform/mv3/extension/_locales/nb/messages.json b/platform/mv3/extension/_locales/nb/messages.json index 107513af638b5..f28df0a2c1a90 100644 --- a/platform/mv3/extension/_locales/nb/messages.json +++ b/platform/mv3/extension/_locales/nb/messages.json @@ -278,5 +278,13 @@ "strictblockProceed": { "message": "Fortsett", "description": "A button to navigate to the blocked page" + }, + "zapperTipEnter": { + "message": "Enter element zapper mode", + "description": "Tooltip for the button used to enter zapper mode" + }, + "zapperTipQuit": { + "message": "Exit element zapper mode", + "description": "Tooltip for the button used to exit zapper mode" } } diff --git a/platform/mv3/extension/_locales/nl/messages.json b/platform/mv3/extension/_locales/nl/messages.json index c567d2d6704ed..f437b433d55c7 100644 --- a/platform/mv3/extension/_locales/nl/messages.json +++ b/platform/mv3/extension/_locales/nl/messages.json @@ -278,5 +278,13 @@ "strictblockProceed": { "message": "Doorgaan", "description": "A button to navigate to the blocked page" + }, + "zapperTipEnter": { + "message": "Enter element zapper mode", + "description": "Tooltip for the button used to enter zapper mode" + }, + "zapperTipQuit": { + "message": "Exit element zapper mode", + "description": "Tooltip for the button used to exit zapper mode" } } diff --git a/platform/mv3/extension/_locales/oc/messages.json b/platform/mv3/extension/_locales/oc/messages.json index 8fc526ab35263..4021b8b2f4cdf 100644 --- a/platform/mv3/extension/_locales/oc/messages.json +++ b/platform/mv3/extension/_locales/oc/messages.json @@ -278,5 +278,13 @@ "strictblockProceed": { "message": "Proceed", "description": "A button to navigate to the blocked page" + }, + "zapperTipEnter": { + "message": "Enter element zapper mode", + "description": "Tooltip for the button used to enter zapper mode" + }, + "zapperTipQuit": { + "message": "Exit element zapper mode", + "description": "Tooltip for the button used to exit zapper mode" } } diff --git a/platform/mv3/extension/_locales/pa/messages.json b/platform/mv3/extension/_locales/pa/messages.json index 8a6f7440f8d70..3be9de68071d6 100644 --- a/platform/mv3/extension/_locales/pa/messages.json +++ b/platform/mv3/extension/_locales/pa/messages.json @@ -278,5 +278,13 @@ "strictblockProceed": { "message": "Proceed", "description": "A button to navigate to the blocked page" + }, + "zapperTipEnter": { + "message": "Enter element zapper mode", + "description": "Tooltip for the button used to enter zapper mode" + }, + "zapperTipQuit": { + "message": "Exit element zapper mode", + "description": "Tooltip for the button used to exit zapper mode" } } diff --git a/platform/mv3/extension/_locales/pl/messages.json b/platform/mv3/extension/_locales/pl/messages.json index 089d4c10a1a06..3dcfb382498d7 100644 --- a/platform/mv3/extension/_locales/pl/messages.json +++ b/platform/mv3/extension/_locales/pl/messages.json @@ -278,5 +278,13 @@ "strictblockProceed": { "message": "Kontynuuj", "description": "A button to navigate to the blocked page" + }, + "zapperTipEnter": { + "message": "Enter element zapper mode", + "description": "Tooltip for the button used to enter zapper mode" + }, + "zapperTipQuit": { + "message": "Exit element zapper mode", + "description": "Tooltip for the button used to exit zapper mode" } } diff --git a/platform/mv3/extension/_locales/pt_BR/messages.json b/platform/mv3/extension/_locales/pt_BR/messages.json index 00493556d6ac6..6b034c1b580dc 100644 --- a/platform/mv3/extension/_locales/pt_BR/messages.json +++ b/platform/mv3/extension/_locales/pt_BR/messages.json @@ -278,5 +278,13 @@ "strictblockProceed": { "message": "Prosseguir", "description": "A button to navigate to the blocked page" + }, + "zapperTipEnter": { + "message": "Enter element zapper mode", + "description": "Tooltip for the button used to enter zapper mode" + }, + "zapperTipQuit": { + "message": "Exit element zapper mode", + "description": "Tooltip for the button used to exit zapper mode" } } diff --git a/platform/mv3/extension/_locales/pt_PT/messages.json b/platform/mv3/extension/_locales/pt_PT/messages.json index 4a16597c28ad5..0563513f1ca6f 100644 --- a/platform/mv3/extension/_locales/pt_PT/messages.json +++ b/platform/mv3/extension/_locales/pt_PT/messages.json @@ -278,5 +278,13 @@ "strictblockProceed": { "message": "Proceder", "description": "A button to navigate to the blocked page" + }, + "zapperTipEnter": { + "message": "Enter element zapper mode", + "description": "Tooltip for the button used to enter zapper mode" + }, + "zapperTipQuit": { + "message": "Exit element zapper mode", + "description": "Tooltip for the button used to exit zapper mode" } } diff --git a/platform/mv3/extension/_locales/ro/messages.json b/platform/mv3/extension/_locales/ro/messages.json index d94f30209ac87..4084828126d56 100644 --- a/platform/mv3/extension/_locales/ro/messages.json +++ b/platform/mv3/extension/_locales/ro/messages.json @@ -278,5 +278,13 @@ "strictblockProceed": { "message": "Continuă", "description": "A button to navigate to the blocked page" + }, + "zapperTipEnter": { + "message": "Enter element zapper mode", + "description": "Tooltip for the button used to enter zapper mode" + }, + "zapperTipQuit": { + "message": "Exit element zapper mode", + "description": "Tooltip for the button used to exit zapper mode" } } diff --git a/platform/mv3/extension/_locales/ru/messages.json b/platform/mv3/extension/_locales/ru/messages.json index fcb58bdabccf7..c7d7b51e65eb0 100644 --- a/platform/mv3/extension/_locales/ru/messages.json +++ b/platform/mv3/extension/_locales/ru/messages.json @@ -278,5 +278,13 @@ "strictblockProceed": { "message": "Продолжить", "description": "A button to navigate to the blocked page" + }, + "zapperTipEnter": { + "message": "Enter element zapper mode", + "description": "Tooltip for the button used to enter zapper mode" + }, + "zapperTipQuit": { + "message": "Exit element zapper mode", + "description": "Tooltip for the button used to exit zapper mode" } } diff --git a/platform/mv3/extension/_locales/si/messages.json b/platform/mv3/extension/_locales/si/messages.json index 73ef6db16270b..d91403abae1cd 100644 --- a/platform/mv3/extension/_locales/si/messages.json +++ b/platform/mv3/extension/_locales/si/messages.json @@ -278,5 +278,13 @@ "strictblockProceed": { "message": "Proceed", "description": "A button to navigate to the blocked page" + }, + "zapperTipEnter": { + "message": "Enter element zapper mode", + "description": "Tooltip for the button used to enter zapper mode" + }, + "zapperTipQuit": { + "message": "Exit element zapper mode", + "description": "Tooltip for the button used to exit zapper mode" } } diff --git a/platform/mv3/extension/_locales/sk/messages.json b/platform/mv3/extension/_locales/sk/messages.json index 715977fc2b85b..9bb02c0662b70 100644 --- a/platform/mv3/extension/_locales/sk/messages.json +++ b/platform/mv3/extension/_locales/sk/messages.json @@ -278,5 +278,13 @@ "strictblockProceed": { "message": "Pokračovať", "description": "A button to navigate to the blocked page" + }, + "zapperTipEnter": { + "message": "Enter element zapper mode", + "description": "Tooltip for the button used to enter zapper mode" + }, + "zapperTipQuit": { + "message": "Exit element zapper mode", + "description": "Tooltip for the button used to exit zapper mode" } } diff --git a/platform/mv3/extension/_locales/sl/messages.json b/platform/mv3/extension/_locales/sl/messages.json index 8fc526ab35263..4021b8b2f4cdf 100644 --- a/platform/mv3/extension/_locales/sl/messages.json +++ b/platform/mv3/extension/_locales/sl/messages.json @@ -278,5 +278,13 @@ "strictblockProceed": { "message": "Proceed", "description": "A button to navigate to the blocked page" + }, + "zapperTipEnter": { + "message": "Enter element zapper mode", + "description": "Tooltip for the button used to enter zapper mode" + }, + "zapperTipQuit": { + "message": "Exit element zapper mode", + "description": "Tooltip for the button used to exit zapper mode" } } diff --git a/platform/mv3/extension/_locales/so/messages.json b/platform/mv3/extension/_locales/so/messages.json index 8fc526ab35263..4021b8b2f4cdf 100644 --- a/platform/mv3/extension/_locales/so/messages.json +++ b/platform/mv3/extension/_locales/so/messages.json @@ -278,5 +278,13 @@ "strictblockProceed": { "message": "Proceed", "description": "A button to navigate to the blocked page" + }, + "zapperTipEnter": { + "message": "Enter element zapper mode", + "description": "Tooltip for the button used to enter zapper mode" + }, + "zapperTipQuit": { + "message": "Exit element zapper mode", + "description": "Tooltip for the button used to exit zapper mode" } } diff --git a/platform/mv3/extension/_locales/sq/messages.json b/platform/mv3/extension/_locales/sq/messages.json index 2db5f40998339..236278611fcba 100644 --- a/platform/mv3/extension/_locales/sq/messages.json +++ b/platform/mv3/extension/_locales/sq/messages.json @@ -278,5 +278,13 @@ "strictblockProceed": { "message": "Vazhdoj", "description": "A button to navigate to the blocked page" + }, + "zapperTipEnter": { + "message": "Enter element zapper mode", + "description": "Tooltip for the button used to enter zapper mode" + }, + "zapperTipQuit": { + "message": "Exit element zapper mode", + "description": "Tooltip for the button used to exit zapper mode" } } diff --git a/platform/mv3/extension/_locales/sr/messages.json b/platform/mv3/extension/_locales/sr/messages.json index 329b33f041768..06c4c847b9741 100644 --- a/platform/mv3/extension/_locales/sr/messages.json +++ b/platform/mv3/extension/_locales/sr/messages.json @@ -278,5 +278,13 @@ "strictblockProceed": { "message": "Настави", "description": "A button to navigate to the blocked page" + }, + "zapperTipEnter": { + "message": "Enter element zapper mode", + "description": "Tooltip for the button used to enter zapper mode" + }, + "zapperTipQuit": { + "message": "Exit element zapper mode", + "description": "Tooltip for the button used to exit zapper mode" } } diff --git a/platform/mv3/extension/_locales/sv/messages.json b/platform/mv3/extension/_locales/sv/messages.json index 202049b8e5504..926c0cf583b63 100644 --- a/platform/mv3/extension/_locales/sv/messages.json +++ b/platform/mv3/extension/_locales/sv/messages.json @@ -278,5 +278,13 @@ "strictblockProceed": { "message": "Fortsätt", "description": "A button to navigate to the blocked page" + }, + "zapperTipEnter": { + "message": "Enter element zapper mode", + "description": "Tooltip for the button used to enter zapper mode" + }, + "zapperTipQuit": { + "message": "Exit element zapper mode", + "description": "Tooltip for the button used to exit zapper mode" } } diff --git a/platform/mv3/extension/_locales/sw/messages.json b/platform/mv3/extension/_locales/sw/messages.json index 8fc526ab35263..4021b8b2f4cdf 100644 --- a/platform/mv3/extension/_locales/sw/messages.json +++ b/platform/mv3/extension/_locales/sw/messages.json @@ -278,5 +278,13 @@ "strictblockProceed": { "message": "Proceed", "description": "A button to navigate to the blocked page" + }, + "zapperTipEnter": { + "message": "Enter element zapper mode", + "description": "Tooltip for the button used to enter zapper mode" + }, + "zapperTipQuit": { + "message": "Exit element zapper mode", + "description": "Tooltip for the button used to exit zapper mode" } } diff --git a/platform/mv3/extension/_locales/ta/messages.json b/platform/mv3/extension/_locales/ta/messages.json index 8fc526ab35263..4021b8b2f4cdf 100644 --- a/platform/mv3/extension/_locales/ta/messages.json +++ b/platform/mv3/extension/_locales/ta/messages.json @@ -278,5 +278,13 @@ "strictblockProceed": { "message": "Proceed", "description": "A button to navigate to the blocked page" + }, + "zapperTipEnter": { + "message": "Enter element zapper mode", + "description": "Tooltip for the button used to enter zapper mode" + }, + "zapperTipQuit": { + "message": "Exit element zapper mode", + "description": "Tooltip for the button used to exit zapper mode" } } diff --git a/platform/mv3/extension/_locales/te/messages.json b/platform/mv3/extension/_locales/te/messages.json index f3e48a9d10a18..9a368701a2a12 100644 --- a/platform/mv3/extension/_locales/te/messages.json +++ b/platform/mv3/extension/_locales/te/messages.json @@ -278,5 +278,13 @@ "strictblockProceed": { "message": "Proceed", "description": "A button to navigate to the blocked page" + }, + "zapperTipEnter": { + "message": "Enter element zapper mode", + "description": "Tooltip for the button used to enter zapper mode" + }, + "zapperTipQuit": { + "message": "Exit element zapper mode", + "description": "Tooltip for the button used to exit zapper mode" } } diff --git a/platform/mv3/extension/_locales/th/messages.json b/platform/mv3/extension/_locales/th/messages.json index 8bd35902e8c8b..4c57881a1c7e8 100644 --- a/platform/mv3/extension/_locales/th/messages.json +++ b/platform/mv3/extension/_locales/th/messages.json @@ -278,5 +278,13 @@ "strictblockProceed": { "message": "ดำเนินต่อ", "description": "A button to navigate to the blocked page" + }, + "zapperTipEnter": { + "message": "Enter element zapper mode", + "description": "Tooltip for the button used to enter zapper mode" + }, + "zapperTipQuit": { + "message": "Exit element zapper mode", + "description": "Tooltip for the button used to exit zapper mode" } } diff --git a/platform/mv3/extension/_locales/tr/messages.json b/platform/mv3/extension/_locales/tr/messages.json index d10acdfb246fe..5eb43483dd7a7 100644 --- a/platform/mv3/extension/_locales/tr/messages.json +++ b/platform/mv3/extension/_locales/tr/messages.json @@ -278,5 +278,13 @@ "strictblockProceed": { "message": "Devam et", "description": "A button to navigate to the blocked page" + }, + "zapperTipEnter": { + "message": "Enter element zapper mode", + "description": "Tooltip for the button used to enter zapper mode" + }, + "zapperTipQuit": { + "message": "Exit element zapper mode", + "description": "Tooltip for the button used to exit zapper mode" } } diff --git a/platform/mv3/extension/_locales/uk/messages.json b/platform/mv3/extension/_locales/uk/messages.json index 6a64ee42e4deb..ecc9c8caa842b 100644 --- a/platform/mv3/extension/_locales/uk/messages.json +++ b/platform/mv3/extension/_locales/uk/messages.json @@ -278,5 +278,13 @@ "strictblockProceed": { "message": "Продовжити", "description": "A button to navigate to the blocked page" + }, + "zapperTipEnter": { + "message": "Enter element zapper mode", + "description": "Tooltip for the button used to enter zapper mode" + }, + "zapperTipQuit": { + "message": "Exit element zapper mode", + "description": "Tooltip for the button used to exit zapper mode" } } diff --git a/platform/mv3/extension/_locales/ur/messages.json b/platform/mv3/extension/_locales/ur/messages.json index 67fef2a531e4c..fbb65b01ed8b1 100644 --- a/platform/mv3/extension/_locales/ur/messages.json +++ b/platform/mv3/extension/_locales/ur/messages.json @@ -278,5 +278,13 @@ "strictblockProceed": { "message": "Proceed", "description": "A button to navigate to the blocked page" + }, + "zapperTipEnter": { + "message": "Enter element zapper mode", + "description": "Tooltip for the button used to enter zapper mode" + }, + "zapperTipQuit": { + "message": "Exit element zapper mode", + "description": "Tooltip for the button used to exit zapper mode" } } diff --git a/platform/mv3/extension/_locales/vi/messages.json b/platform/mv3/extension/_locales/vi/messages.json index 2ce652036b083..fda7516f49d3b 100644 --- a/platform/mv3/extension/_locales/vi/messages.json +++ b/platform/mv3/extension/_locales/vi/messages.json @@ -278,5 +278,13 @@ "strictblockProceed": { "message": "Tiến hành", "description": "A button to navigate to the blocked page" + }, + "zapperTipEnter": { + "message": "Enter element zapper mode", + "description": "Tooltip for the button used to enter zapper mode" + }, + "zapperTipQuit": { + "message": "Exit element zapper mode", + "description": "Tooltip for the button used to exit zapper mode" } } diff --git a/platform/mv3/extension/_locales/zh_CN/messages.json b/platform/mv3/extension/_locales/zh_CN/messages.json index 136a6284673da..7eafea6f84045 100644 --- a/platform/mv3/extension/_locales/zh_CN/messages.json +++ b/platform/mv3/extension/_locales/zh_CN/messages.json @@ -278,5 +278,13 @@ "strictblockProceed": { "message": "继续", "description": "A button to navigate to the blocked page" + }, + "zapperTipEnter": { + "message": "Enter element zapper mode", + "description": "Tooltip for the button used to enter zapper mode" + }, + "zapperTipQuit": { + "message": "Exit element zapper mode", + "description": "Tooltip for the button used to exit zapper mode" } } diff --git a/platform/mv3/extension/_locales/zh_TW/messages.json b/platform/mv3/extension/_locales/zh_TW/messages.json index d3dbb78428e3f..c1d853ad21a97 100644 --- a/platform/mv3/extension/_locales/zh_TW/messages.json +++ b/platform/mv3/extension/_locales/zh_TW/messages.json @@ -278,5 +278,13 @@ "strictblockProceed": { "message": "繼續", "description": "A button to navigate to the blocked page" + }, + "zapperTipEnter": { + "message": "Enter element zapper mode", + "description": "Tooltip for the button used to enter zapper mode" + }, + "zapperTipQuit": { + "message": "Exit element zapper mode", + "description": "Tooltip for the button used to exit zapper mode" } } From 95a3be9d56c4a5264e5346dc4717682b9fd8fd11 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sat, 22 Mar 2025 16:01:43 -0400 Subject: [PATCH 0719/1099] Add `jsonl-prune-xhr-response`/`jsonl-prune-fetch-response` scriptlets As discussed internally with filter list volunteers. --- platform/chromium/manifest.json | 2 +- platform/firefox/manifest.json | 4 +- src/js/resources/json-prune.js | 299 ++++++++++++ src/js/resources/jsonl-prune.js | 274 +++++++++++ src/js/resources/object-prune.js | 272 +++++++++++ src/js/resources/safe-self.js | 1 + src/js/resources/scriptlets.js | 755 +------------------------------ src/js/resources/stack-trace.js | 148 ++++++ src/js/resources/utils.js | 117 +++++ 9 files changed, 1134 insertions(+), 738 deletions(-) create mode 100644 src/js/resources/json-prune.js create mode 100644 src/js/resources/jsonl-prune.js create mode 100644 src/js/resources/object-prune.js create mode 100644 src/js/resources/stack-trace.js create mode 100644 src/js/resources/utils.js diff --git a/platform/chromium/manifest.json b/platform/chromium/manifest.json index b2945f90f6e53..0b4095a049bee 100644 --- a/platform/chromium/manifest.json +++ b/platform/chromium/manifest.json @@ -89,7 +89,7 @@ }, "incognito": "split", "manifest_version": 2, - "minimum_chrome_version": "85.0", + "minimum_chrome_version": "93.0", "name": "uBlock Origin", "options_ui": { "page": "dashboard.html", diff --git a/platform/firefox/manifest.json b/platform/firefox/manifest.json index b5d28c1116956..888dced282b82 100644 --- a/platform/firefox/manifest.json +++ b/platform/firefox/manifest.json @@ -17,10 +17,10 @@ "browser_specific_settings": { "gecko": { "id": "uBlock0@raymondhill.net", - "strict_min_version": "79.0" + "strict_min_version": "92.0" }, "gecko_android": { - "strict_min_version": "79.0" + "strict_min_version": "92.0" } }, "commands": { diff --git a/src/js/resources/json-prune.js b/src/js/resources/json-prune.js new file mode 100644 index 0000000000000..62f134473827d --- /dev/null +++ b/src/js/resources/json-prune.js @@ -0,0 +1,299 @@ +/******************************************************************************* + + uBlock Origin - a comprehensive, efficient content blocker + Copyright (C) 2019-present Raymond Hill + + 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/}. + + Home: https://github.com/gorhill/uBlock + +*/ + +import { + matchObjectPropertiesFn, + parsePropertiesToMatchFn, +} from './utils.js'; + +import { objectPruneFn } from './object-prune.js'; +import { proxyApplyFn } from './proxy-apply.js'; +import { registerScriptlet } from './base.js'; +import { safeSelf } from './safe-self.js'; + +/******************************************************************************/ + +function jsonPrune( + rawPrunePaths = '', + rawNeedlePaths = '', + stackNeedle = '' +) { + const safe = safeSelf(); + const logPrefix = safe.makeLogPrefix('json-prune', rawPrunePaths, rawNeedlePaths, stackNeedle); + const stackNeedleDetails = safe.initPattern(stackNeedle, { canNegate: true }); + const extraArgs = safe.getExtraArgs(Array.from(arguments), 3); + JSON.parse = new Proxy(JSON.parse, { + apply: function(target, thisArg, args) { + const objBefore = Reflect.apply(target, thisArg, args); + if ( rawPrunePaths === '' ) { + safe.uboLog(logPrefix, safe.JSON_stringify(objBefore, null, 2)); + } + const objAfter = objectPruneFn( + objBefore, + rawPrunePaths, + rawNeedlePaths, + stackNeedleDetails, + extraArgs + ); + if ( objAfter === undefined ) { return objBefore; } + safe.uboLog(logPrefix, 'Pruned'); + if ( safe.logLevel > 1 ) { + safe.uboLog(logPrefix, `After pruning:\n${safe.JSON_stringify(objAfter, null, 2)}`); + } + return objAfter; + }, + }); +} +registerScriptlet(jsonPrune, { + name: 'json-prune.js', + dependencies: [ + objectPruneFn, + safeSelf, + ], +}); + +/******************************************************************************/ + +function jsonPruneFetchResponseFn( + rawPrunePaths = '', + rawNeedlePaths = '' +) { + const safe = safeSelf(); + const logPrefix = safe.makeLogPrefix('json-prune-fetch-response', rawPrunePaths, rawNeedlePaths); + const extraArgs = safe.getExtraArgs(Array.from(arguments), 2); + const propNeedles = parsePropertiesToMatchFn(extraArgs.propsToMatch, 'url'); + const stackNeedle = safe.initPattern(extraArgs.stackToMatch || '', { canNegate: true }); + const logall = rawPrunePaths === ''; + const applyHandler = function(target, thisArg, args) { + const fetchPromise = Reflect.apply(target, thisArg, args); + if ( propNeedles.size !== 0 ) { + const objs = [ args[0] instanceof Object ? args[0] : { url: args[0] } ]; + if ( objs[0] instanceof Request ) { + try { + objs[0] = safe.Request_clone.call(objs[0]); + } catch(ex) { + safe.uboErr(logPrefix, 'Error:', ex); + } + } + if ( args[1] instanceof Object ) { + objs.push(args[1]); + } + const matched = matchObjectPropertiesFn(propNeedles, ...objs); + if ( matched === undefined ) { return fetchPromise; } + if ( safe.logLevel > 1 ) { + safe.uboLog(logPrefix, `Matched "propsToMatch":\n\t${matched.join('\n\t')}`); + } + } + return fetchPromise.then(responseBefore => { + const response = responseBefore.clone(); + return response.json().then(objBefore => { + if ( typeof objBefore !== 'object' ) { return responseBefore; } + if ( logall ) { + safe.uboLog(logPrefix, safe.JSON_stringify(objBefore, null, 2)); + return responseBefore; + } + const objAfter = objectPruneFn( + objBefore, + rawPrunePaths, + rawNeedlePaths, + stackNeedle, + extraArgs + ); + if ( typeof objAfter !== 'object' ) { return responseBefore; } + safe.uboLog(logPrefix, 'Pruned'); + const responseAfter = Response.json(objAfter, { + status: responseBefore.status, + statusText: responseBefore.statusText, + headers: responseBefore.headers, + }); + Object.defineProperties(responseAfter, { + ok: { value: responseBefore.ok }, + redirected: { value: responseBefore.redirected }, + type: { value: responseBefore.type }, + url: { value: responseBefore.url }, + }); + return responseAfter; + }).catch(reason => { + safe.uboErr(logPrefix, 'Error:', reason); + return responseBefore; + }); + }).catch(reason => { + safe.uboErr(logPrefix, 'Error:', reason); + return fetchPromise; + }); + }; + self.fetch = new Proxy(self.fetch, { + apply: applyHandler + }); +} +registerScriptlet(jsonPruneFetchResponseFn, { + name: 'json-prune-fetch-response.fn', + dependencies: [ + matchObjectPropertiesFn, + objectPruneFn, + parsePropertiesToMatchFn, + safeSelf, + ], +}); + +/******************************************************************************/ + +function jsonPruneFetchResponse(...args) { + jsonPruneFetchResponseFn(...args); +} +registerScriptlet(jsonPruneFetchResponse, { + name: 'json-prune-fetch-response.js', + dependencies: [ + jsonPruneFetchResponseFn, + ], +}); + +/******************************************************************************/ + +function jsonPruneXhrResponseFn( + rawPrunePaths = '', + rawNeedlePaths = '' +) { + const safe = safeSelf(); + const logPrefix = safe.makeLogPrefix('json-prune-xhr-response', rawPrunePaths, rawNeedlePaths); + const xhrInstances = new WeakMap(); + const extraArgs = safe.getExtraArgs(Array.from(arguments), 2); + const propNeedles = parsePropertiesToMatchFn(extraArgs.propsToMatch, 'url'); + const stackNeedle = safe.initPattern(extraArgs.stackToMatch || '', { canNegate: true }); + self.XMLHttpRequest = class extends self.XMLHttpRequest { + open(method, url, ...args) { + const xhrDetails = { method, url }; + let outcome = 'match'; + if ( propNeedles.size !== 0 ) { + if ( matchObjectPropertiesFn(propNeedles, xhrDetails) === undefined ) { + outcome = 'nomatch'; + } + } + if ( outcome === 'match' ) { + if ( safe.logLevel > 1 ) { + safe.uboLog(logPrefix, `Matched optional "propsToMatch", "${extraArgs.propsToMatch}"`); + } + xhrInstances.set(this, xhrDetails); + } + return super.open(method, url, ...args); + } + get response() { + const innerResponse = super.response; + const xhrDetails = xhrInstances.get(this); + if ( xhrDetails === undefined ) { + return innerResponse; + } + const responseLength = typeof innerResponse === 'string' + ? innerResponse.length + : undefined; + if ( xhrDetails.lastResponseLength !== responseLength ) { + xhrDetails.response = undefined; + xhrDetails.lastResponseLength = responseLength; + } + if ( xhrDetails.response !== undefined ) { + return xhrDetails.response; + } + let objBefore; + if ( typeof innerResponse === 'object' ) { + objBefore = innerResponse; + } else if ( typeof innerResponse === 'string' ) { + try { + objBefore = safe.JSON_parse(innerResponse); + } catch { + } + } + if ( typeof objBefore !== 'object' ) { + return (xhrDetails.response = innerResponse); + } + const objAfter = objectPruneFn( + objBefore, + rawPrunePaths, + rawNeedlePaths, + stackNeedle, + extraArgs + ); + let outerResponse; + if ( typeof objAfter === 'object' ) { + outerResponse = typeof innerResponse === 'string' + ? safe.JSON_stringify(objAfter) + : objAfter; + safe.uboLog(logPrefix, 'Pruned'); + } else { + outerResponse = innerResponse; + } + return (xhrDetails.response = outerResponse); + } + get responseText() { + const response = this.response; + return typeof response !== 'string' + ? super.responseText + : response; + } + }; +} +registerScriptlet(jsonPruneXhrResponseFn, { + name: 'json-prune-xhr-response.fn', + dependencies: [ + matchObjectPropertiesFn, + objectPruneFn, + parsePropertiesToMatchFn, + safeSelf, + ], +}); + +/******************************************************************************/ + +function jsonPruneXhrResponse(...args) { + jsonPruneXhrResponseFn(...args); +} +registerScriptlet(jsonPruneXhrResponse, { + name: 'json-prune-xhr-response.js', + dependencies: [ + jsonPruneXhrResponseFn, + ], +}); + +/******************************************************************************/ + +// There is still code out there which uses `eval` in lieu of `JSON.parse`. + +function evaldataPrune( + rawPrunePaths = '', + rawNeedlePaths = '' +) { + proxyApplyFn('eval', function(context) { + const before = context.reflect(); + if ( typeof before !== 'object' ) { return before; } + if ( before === null ) { return null; } + const after = objectPruneFn(before, rawPrunePaths, rawNeedlePaths); + return after || before; + }); +} +registerScriptlet(evaldataPrune, { + name: 'evaldata-prune.js', + dependencies: [ + objectPruneFn, + proxyApplyFn, + ], +}); + +/******************************************************************************/ diff --git a/src/js/resources/jsonl-prune.js b/src/js/resources/jsonl-prune.js new file mode 100644 index 0000000000000..16d540d7783a2 --- /dev/null +++ b/src/js/resources/jsonl-prune.js @@ -0,0 +1,274 @@ +/******************************************************************************* + + uBlock Origin - a comprehensive, efficient content blocker + Copyright (C) 2019-present Raymond Hill + + 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/}. + + Home: https://github.com/gorhill/uBlock + +*/ + +import { + matchObjectPropertiesFn, + parsePropertiesToMatchFn, +} from './utils.js'; + +import { objectPruneFn } from './object-prune.js'; +import { registerScriptlet } from './base.js'; +import { safeSelf } from './safe-self.js'; + +/******************************************************************************/ + +function jsonlPruneFn( + text = '', + rawPrunePaths = '', + rawNeedlePaths = '' +) { + const safe = safeSelf(); + const linesBefore = text.split(/\n+/); + const linesAfter = []; + for ( const lineBefore of linesBefore ) { + let objBefore; + try { + objBefore = safe.JSON_parse(lineBefore); + } catch { + } + if ( typeof objBefore !== 'object' ) { + linesAfter.push(lineBefore); + continue; + } + const objAfter = objectPruneFn(objBefore, rawPrunePaths, rawNeedlePaths); + if ( typeof objAfter !== 'object' ) { + linesAfter.push(lineBefore); + continue; + } + linesAfter.push(safe.JSON_stringifyFn(objAfter)); + } + return linesAfter.join('\n'); +} +registerScriptlet(jsonlPruneFn, { + name: 'jsonl-prune.fn', + dependencies: [ + objectPruneFn, + safeSelf, + ], +}); + +/******************************************************************************/ + +/** + * @scriptlet jsonl-prune-xhr-response.js + * + * @description + * Prune the objects found in a JSONL resource fetched through a XHR instance. + * + * @param rawPrunePaths + * The property to remove from the objects. + * + * @param rawNeedlePaths + * A property which must be present for the pruning to take effect. + * + * @param [propsToMatch, value] + * An optional vararg detailing the arguments to match when xhr.open() is + * called. + * + * */ + +function jsonlPruneXhrResponseFn( + rawPrunePaths = '', + rawNeedlePaths = '' +) { + const safe = safeSelf(); + const logPrefix = safe.makeLogPrefix('jsonl-prune-xhr-response', rawPrunePaths, rawNeedlePaths); + const xhrInstances = new WeakMap(); + const extraArgs = safe.getExtraArgs(Array.from(arguments), 2); + const propNeedles = parsePropertiesToMatchFn(extraArgs.propsToMatch, 'url'); + self.XMLHttpRequest = class extends self.XMLHttpRequest { + open(method, url, ...args) { + const xhrDetails = { method, url }; + const matched = propNeedles.size === 0 || + matchObjectPropertiesFn(propNeedles, xhrDetails); + if ( matched ) { + if ( safe.logLevel > 1 && Array.isArray(matched) ) { + safe.uboLog(logPrefix, `Matched "propsToMatch":\n\t${matched.join('\n\t')}`); + } + xhrInstances.set(this, xhrDetails); + } + return super.open(method, url, ...args); + } + get response() { + const innerResponse = super.response; + const xhrDetails = xhrInstances.get(this); + if ( xhrDetails === undefined ) { + return innerResponse; + } + const responseLength = typeof innerResponse === 'string' + ? innerResponse.length + : undefined; + if ( xhrDetails.lastResponseLength !== responseLength ) { + xhrDetails.response = undefined; + xhrDetails.lastResponseLength = responseLength; + } + if ( xhrDetails.response !== undefined ) { + return xhrDetails.response; + } + if ( typeof innerResponse !== 'string' ) { + return (xhrDetails.response = innerResponse); + } + const outerResponse = jsonlPruneFn(innerResponse, rawPrunePaths, rawNeedlePaths); + if ( outerResponse !== innerResponse ) { + safe.uboLog(logPrefix, 'Pruned'); + } + return (xhrDetails.response = outerResponse); + } + get responseText() { + const response = this.response; + return typeof response !== 'string' + ? super.responseText + : response; + } + }; +} +registerScriptlet(jsonlPruneXhrResponseFn, { + name: 'jsonl-prune-xhr-response.fn', + dependencies: [ + jsonlPruneFn, + matchObjectPropertiesFn, + parsePropertiesToMatchFn, + safeSelf, + ], +}); + +/******************************************************************************/ + +function jsonlPruneXhrResponse(...args) { + jsonlPruneXhrResponseFn(...args); +} +registerScriptlet(jsonlPruneXhrResponse, { + name: 'jsonl-prune-xhr-response.js', + dependencies: [ + jsonlPruneXhrResponseFn, + ], +}); + +/******************************************************************************/ + +/** + * @scriptlet jsonl-prune-fetch-response.js + * + * @description + * Prune the objects found in a JSONL resource fetched through the fetch API. + * Once the pruning is performed. + * + * @param rawPrunePaths + * The property to remove from the objects. + * + * @param rawNeedlePaths + * A property which must be present for the pruning to take effect. + * + * @param [propsToMatch, value] + * An optional vararg detailing the arguments to match when xhr.open() is + * called. + * + * */ + +function jsonlPruneFetchResponseFn( + rawPrunePaths = '', + rawNeedlePaths = '' +) { + const safe = safeSelf(); + const logPrefix = safe.makeLogPrefix('jsonl-prune-fetch-response', rawPrunePaths, rawNeedlePaths); + const extraArgs = safe.getExtraArgs(Array.from(arguments), 2); + const propNeedles = parsePropertiesToMatchFn(extraArgs.propsToMatch, 'url'); + const logall = rawPrunePaths === ''; + const applyHandler = function(target, thisArg, args) { + const fetchPromise = Reflect.apply(target, thisArg, args); + if ( propNeedles.size !== 0 ) { + const objs = [ args[0] instanceof Object ? args[0] : { url: args[0] } ]; + if ( objs[0] instanceof Request ) { + try { + objs[0] = safe.Request_clone.call(objs[0]); + } catch(ex) { + safe.uboErr(logPrefix, 'Error:', ex); + } + } + if ( args[1] instanceof Object ) { + objs.push(args[1]); + } + const matched = matchObjectPropertiesFn(propNeedles, ...objs); + if ( matched === undefined ) { return fetchPromise; } + if ( safe.logLevel > 1 ) { + safe.uboLog(logPrefix, `Matched "propsToMatch":\n\t${matched.join('\n\t')}`); + } + } + return fetchPromise.then(responseBefore => { + const response = responseBefore.clone(); + return response.text().then(textBefore => { + if ( typeof textBefore !== 'string' ) { return textBefore; } + if ( logall ) { + safe.uboLog(logPrefix, textBefore); + return responseBefore; + } + const textAfter = jsonlPruneFn(textBefore, rawPrunePaths, rawNeedlePaths); + if ( textAfter === textBefore ) { return responseBefore; } + safe.uboLog(logPrefix, 'Pruned'); + const responseAfter = new Response(textAfter, { + status: responseBefore.status, + statusText: responseBefore.statusText, + headers: responseBefore.headers, + }); + Object.defineProperties(responseAfter, { + ok: { value: responseBefore.ok }, + redirected: { value: responseBefore.redirected }, + type: { value: responseBefore.type }, + url: { value: responseBefore.url }, + }); + return responseAfter; + }).catch(reason => { + safe.uboErr(logPrefix, 'Error:', reason); + return responseBefore; + }); + }).catch(reason => { + safe.uboErr(logPrefix, 'Error:', reason); + return fetchPromise; + }); + }; + self.fetch = new Proxy(self.fetch, { + apply: applyHandler + }); +} +registerScriptlet(jsonlPruneFetchResponseFn, { + name: 'jsonl-prune-fetch-response.fn', + dependencies: [ + jsonlPruneFn, + matchObjectPropertiesFn, + parsePropertiesToMatchFn, + safeSelf, + ], +}); + +/******************************************************************************/ + +function jsonlPruneFetchResponse(...args) { + jsonlPruneFetchResponseFn(...args); +} +registerScriptlet(jsonlPruneFetchResponse, { + name: 'jsonl-prune-fetch-response.js', + dependencies: [ + jsonlPruneFetchResponseFn, + ], +}); + +/******************************************************************************/ diff --git a/src/js/resources/object-prune.js b/src/js/resources/object-prune.js new file mode 100644 index 0000000000000..50256fe52828b --- /dev/null +++ b/src/js/resources/object-prune.js @@ -0,0 +1,272 @@ +/******************************************************************************* + + uBlock Origin - a comprehensive, efficient content blocker + Copyright (C) 2019-present Raymond Hill + + 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/}. + + Home: https://github.com/gorhill/uBlock + +*/ + +import { matchesStackTraceFn } from './stack-trace.js'; +import { proxyApplyFn } from './proxy-apply.js'; +import { registerScriptlet } from './base.js'; +import { safeSelf } from './safe-self.js'; + +/******************************************************************************/ + +function objectFindOwnerFn( + root, + path, + prune = false +) { + const safe = safeSelf(); + let owner = root; + let chain = path; + for (;;) { + if ( typeof owner !== 'object' || owner === null ) { return false; } + const pos = chain.indexOf('.'); + if ( pos === -1 ) { + if ( prune === false ) { + return safe.Object_hasOwn(owner, chain); + } + let modified = false; + if ( chain === '*' ) { + for ( const key in owner ) { + if ( safe.Object_hasOwn(owner, key) === false ) { continue; } + delete owner[key]; + modified = true; + } + } else if ( safe.Object_hasOwn(owner, chain) ) { + delete owner[chain]; + modified = true; + } + return modified; + } + const prop = chain.slice(0, pos); + const next = chain.slice(pos + 1); + let found = false; + if ( prop === '[-]' && Array.isArray(owner) ) { + let i = owner.length; + while ( i-- ) { + if ( objectFindOwnerFn(owner[i], next) === false ) { continue; } + owner.splice(i, 1); + found = true; + } + return found; + } + if ( prop === '{-}' && owner instanceof Object ) { + for ( const key of Object.keys(owner) ) { + if ( objectFindOwnerFn(owner[key], next) === false ) { continue; } + delete owner[key]; + found = true; + } + return found; + } + if ( + prop === '[]' && Array.isArray(owner) || + prop === '{}' && owner instanceof Object || + prop === '*' && owner instanceof Object + ) { + for ( const key of Object.keys(owner) ) { + if (objectFindOwnerFn(owner[key], next, prune) === false ) { continue; } + found = true; + } + return found; + } + if ( safe.Object_hasOwn(owner, prop) === false ) { return false; } + owner = owner[prop]; + chain = chain.slice(pos + 1); + } +} +registerScriptlet(objectFindOwnerFn, { + name: 'object-find-owner.fn', + dependencies: [ + safeSelf, + ], +}); + +/******************************************************************************/ + +// When no "prune paths" argument is provided, the scriptlet is +// used for logging purpose and the "needle paths" argument is +// used to filter logging output. +// +// https://github.com/uBlockOrigin/uBlock-issues/issues/1545 +// - Add support for "remove everything if needle matches" case + +export function objectPruneFn( + obj, + rawPrunePaths, + rawNeedlePaths, + stackNeedleDetails = { matchAll: true }, + extraArgs = {} +) { + if ( typeof rawPrunePaths !== 'string' ) { return; } + const safe = safeSelf(); + const prunePaths = rawPrunePaths !== '' + ? safe.String_split.call(rawPrunePaths, / +/) + : []; + const needlePaths = prunePaths.length !== 0 && rawNeedlePaths !== '' + ? safe.String_split.call(rawNeedlePaths, / +/) + : []; + if ( stackNeedleDetails.matchAll !== true ) { + if ( matchesStackTraceFn(stackNeedleDetails, extraArgs.logstack) === false ) { + return; + } + } + if ( objectPruneFn.mustProcess === undefined ) { + objectPruneFn.mustProcess = (root, needlePaths) => { + for ( const needlePath of needlePaths ) { + if ( objectFindOwnerFn(root, needlePath) === false ) { + return false; + } + } + return true; + }; + } + if ( prunePaths.length === 0 ) { return; } + let outcome = 'nomatch'; + if ( objectPruneFn.mustProcess(obj, needlePaths) ) { + for ( const path of prunePaths ) { + if ( objectFindOwnerFn(obj, path, true) ) { + outcome = 'match'; + } + } + } + if ( outcome === 'match' ) { return obj; } +} +registerScriptlet(objectPruneFn, { + name: 'object-prune.fn', + dependencies: [ + matchesStackTraceFn, + objectFindOwnerFn, + safeSelf, + ], +}); + +/******************************************************************************/ + +function trustedPruneInboundObject( + entryPoint = '', + argPos = '', + rawPrunePaths = '', + rawNeedlePaths = '' +) { + if ( entryPoint === '' ) { return; } + let context = globalThis; + let prop = entryPoint; + for (;;) { + const pos = prop.indexOf('.'); + if ( pos === -1 ) { break; } + context = context[prop.slice(0, pos)]; + if ( context instanceof Object === false ) { return; } + prop = prop.slice(pos+1); + } + if ( typeof context[prop] !== 'function' ) { return; } + const argIndex = parseInt(argPos); + if ( isNaN(argIndex) ) { return; } + if ( argIndex < 1 ) { return; } + const safe = safeSelf(); + const extraArgs = safe.getExtraArgs(Array.from(arguments), 4); + const needlePaths = []; + if ( rawPrunePaths !== '' ) { + needlePaths.push(...safe.String_split.call(rawPrunePaths, / +/)); + } + if ( rawNeedlePaths !== '' ) { + needlePaths.push(...safe.String_split.call(rawNeedlePaths, / +/)); + } + const stackNeedle = safe.initPattern(extraArgs.stackToMatch || '', { canNegate: true }); + const mustProcess = root => { + for ( const needlePath of needlePaths ) { + if ( objectFindOwnerFn(root, needlePath) === false ) { + return false; + } + } + return true; + }; + context[prop] = new Proxy(context[prop], { + apply: function(target, thisArg, args) { + const targetArg = argIndex <= args.length + ? args[argIndex-1] + : undefined; + if ( targetArg instanceof Object && mustProcess(targetArg) ) { + let objBefore = targetArg; + if ( extraArgs.dontOverwrite ) { + try { + objBefore = safe.JSON_parse(safe.JSON_stringify(targetArg)); + } catch { + objBefore = undefined; + } + } + if ( objBefore !== undefined ) { + const objAfter = objectPruneFn( + objBefore, + rawPrunePaths, + rawNeedlePaths, + stackNeedle, + extraArgs + ); + args[argIndex-1] = objAfter || objBefore; + } + } + return Reflect.apply(target, thisArg, args); + }, + }); +} +registerScriptlet(trustedPruneInboundObject, { + name: 'trusted-prune-inbound-object.js', + requiresTrust: true, + dependencies: [ + objectFindOwnerFn, + objectPruneFn, + safeSelf, + ], +}); + +/******************************************************************************/ + +function trustedPruneOutboundObject( + propChain = '', + rawPrunePaths = '', + rawNeedlePaths = '' +) { + if ( propChain === '' ) { return; } + const safe = safeSelf(); + const extraArgs = safe.getExtraArgs(Array.from(arguments), 3); + proxyApplyFn(propChain, function(context) { + const objBefore = context.reflect(); + if ( objBefore instanceof Object === false ) { return objBefore; } + const objAfter = objectPruneFn( + objBefore, + rawPrunePaths, + rawNeedlePaths, + { matchAll: true }, + extraArgs + ); + return objAfter || objBefore; + }); +} +registerScriptlet(trustedPruneOutboundObject, { + name: 'trusted-prune-outbound-object.js', + requiresTrust: true, + dependencies: [ + objectPruneFn, + proxyApplyFn, + safeSelf, + ], +}); + +/******************************************************************************/ diff --git a/src/js/resources/safe-self.js b/src/js/resources/safe-self.js index 6b6d72eeba26c..43aec1d94affa 100644 --- a/src/js/resources/safe-self.js +++ b/src/js/resources/safe-self.js @@ -46,6 +46,7 @@ export function safeSelf() { 'Object_defineProperties': Object.defineProperties.bind(Object), 'Object_fromEntries': Object.fromEntries.bind(Object), 'Object_getOwnPropertyDescriptor': Object.getOwnPropertyDescriptor.bind(Object), + 'Object_hasOwn': Object.hasOwn.bind(Object), 'RegExp': self.RegExp, 'RegExp_test': self.RegExp.prototype.test, 'RegExp_exec': self.RegExp.prototype.exec, diff --git a/src/js/resources/scriptlets.js b/src/js/resources/scriptlets.js index 73b534bc7382b..ba196f85ab606 100755 --- a/src/js/resources/scriptlets.js +++ b/src/js/resources/scriptlets.js @@ -22,16 +22,26 @@ import './attribute.js'; import './href-sanitizer.js'; +import './json-prune.js'; +import './jsonl-prune.js'; import './noeval.js'; +import './object-prune.js'; import './prevent-innerHTML.js'; import './prevent-settimeout.js'; import './replace-argument.js'; import './spoof-css.js'; +import { + getExceptionTokenFn, + getRandomTokenFn, + matchObjectPropertiesFn, + parsePropertiesToMatchFn, +} from './utils.js'; import { runAt, runAtHtmlElementFn } from './run-at.js'; import { getAllCookiesFn } from './cookie.js'; import { getAllLocalStorageFn } from './localstorage.js'; +import { matchesStackTraceFn } from './stack-trace.js'; import { proxyApplyFn } from './proxy-apply.js'; import { registeredScriptlets } from './base.js'; import { safeSelf } from './safe-self.js'; @@ -52,41 +62,6 @@ export const builtinScriptlets = registeredScriptlets; *******************************************************************************/ -builtinScriptlets.push({ - name: 'get-random-token.fn', - fn: getRandomToken, - dependencies: [ - 'safe-self.fn', - ], -}); -function getRandomToken() { - const safe = safeSelf(); - return safe.String_fromCharCode(Date.now() % 26 + 97) + - safe.Math_floor(safe.Math_random() * 982451653 + 982451653).toString(36); -} -/******************************************************************************/ - -builtinScriptlets.push({ - name: 'get-exception-token.fn', - fn: getExceptionToken, - dependencies: [ - 'get-random-token.fn', - ], -}); -function getExceptionToken() { - const token = getRandomToken(); - const oe = self.onerror; - self.onerror = function(msg, ...args) { - if ( typeof msg === 'string' && msg.includes(token) ) { return true; } - if ( oe instanceof Function ) { - return oe.call(this, msg, ...args); - } - }.bind(); - return token; -} - -/******************************************************************************/ - builtinScriptlets.push({ name: 'should-debug.fn', fn: shouldDebug, @@ -215,7 +190,7 @@ function abortCurrentScriptCore( desc = undefined; } const debug = shouldDebug(extraArgs); - const exceptionToken = getExceptionToken(); + const exceptionToken = getExceptionTokenFn(); const scriptTexts = new WeakMap(); const getScriptText = elem => { let text = elem.textContent; @@ -330,7 +305,7 @@ function replaceNodeTextFn( if ( tt instanceof Object ) { if ( typeof tt.getPropertyType === 'function' ) { if ( tt.getPropertyType('script', 'textContent') === 'TrustedScript' ) { - return tt.createPolicy(getRandomToken(), out); + return tt.createPolicy(getRandomTokenFn(), out); } } } @@ -403,334 +378,6 @@ function replaceNodeTextFn( /******************************************************************************/ -builtinScriptlets.push({ - name: 'object-prune.fn', - fn: objectPruneFn, - dependencies: [ - 'matches-stack-trace.fn', - 'object-find-owner.fn', - 'safe-self.fn', - ], -}); -// When no "prune paths" argument is provided, the scriptlet is -// used for logging purpose and the "needle paths" argument is -// used to filter logging output. -// -// https://github.com/uBlockOrigin/uBlock-issues/issues/1545 -// - Add support for "remove everything if needle matches" case -function objectPruneFn( - obj, - rawPrunePaths, - rawNeedlePaths, - stackNeedleDetails = { matchAll: true }, - extraArgs = {} -) { - if ( typeof rawPrunePaths !== 'string' ) { return; } - const safe = safeSelf(); - const prunePaths = rawPrunePaths !== '' - ? safe.String_split.call(rawPrunePaths, / +/) - : []; - const needlePaths = prunePaths.length !== 0 && rawNeedlePaths !== '' - ? safe.String_split.call(rawNeedlePaths, / +/) - : []; - if ( stackNeedleDetails.matchAll !== true ) { - if ( matchesStackTraceFn(stackNeedleDetails, extraArgs.logstack) === false ) { - return; - } - } - if ( objectPruneFn.mustProcess === undefined ) { - objectPruneFn.mustProcess = (root, needlePaths) => { - for ( const needlePath of needlePaths ) { - if ( objectFindOwnerFn(root, needlePath) === false ) { - return false; - } - } - return true; - }; - } - if ( prunePaths.length === 0 ) { return; } - let outcome = 'nomatch'; - if ( objectPruneFn.mustProcess(obj, needlePaths) ) { - for ( const path of prunePaths ) { - if ( objectFindOwnerFn(obj, path, true) ) { - outcome = 'match'; - } - } - } - if ( outcome === 'match' ) { return obj; } -} - -/******************************************************************************/ - -builtinScriptlets.push({ - name: 'object-find-owner.fn', - fn: objectFindOwnerFn, -}); -function objectFindOwnerFn( - root, - path, - prune = false -) { - let owner = root; - let chain = path; - for (;;) { - if ( typeof owner !== 'object' || owner === null ) { return false; } - const pos = chain.indexOf('.'); - if ( pos === -1 ) { - if ( prune === false ) { - return owner.hasOwnProperty(chain); - } - let modified = false; - if ( chain === '*' ) { - for ( const key in owner ) { - if ( owner.hasOwnProperty(key) === false ) { continue; } - delete owner[key]; - modified = true; - } - } else if ( owner.hasOwnProperty(chain) ) { - delete owner[chain]; - modified = true; - } - return modified; - } - const prop = chain.slice(0, pos); - const next = chain.slice(pos + 1); - let found = false; - if ( prop === '[-]' && Array.isArray(owner) ) { - let i = owner.length; - while ( i-- ) { - if ( objectFindOwnerFn(owner[i], next) === false ) { continue; } - owner.splice(i, 1); - found = true; - } - return found; - } - if ( prop === '{-}' && owner instanceof Object ) { - for ( const key of Object.keys(owner) ) { - if ( objectFindOwnerFn(owner[key], next) === false ) { continue; } - delete owner[key]; - found = true; - } - return found; - } - if ( - prop === '[]' && Array.isArray(owner) || - prop === '{}' && owner instanceof Object || - prop === '*' && owner instanceof Object - ) { - for ( const key of Object.keys(owner) ) { - if (objectFindOwnerFn(owner[key], next, prune) === false ) { continue; } - found = true; - } - return found; - } - if ( owner.hasOwnProperty(prop) === false ) { return false; } - owner = owner[prop]; - chain = chain.slice(pos + 1); - } -} - -/******************************************************************************/ - -builtinScriptlets.push({ - name: 'matches-stack-trace.fn', - fn: matchesStackTraceFn, - dependencies: [ - 'get-exception-token.fn', - 'safe-self.fn', - ], -}); -function matchesStackTraceFn( - needleDetails, - logLevel = '' -) { - const safe = safeSelf(); - const exceptionToken = getExceptionToken(); - const error = new safe.Error(exceptionToken); - const docURL = new URL(self.location.href); - docURL.hash = ''; - // Normalize stack trace - const reLine = /(.*?@)?(\S+)(:\d+):\d+\)?$/; - const lines = []; - for ( let line of safe.String_split.call(error.stack, /[\n\r]+/) ) { - if ( line.includes(exceptionToken) ) { continue; } - line = line.trim(); - const match = safe.RegExp_exec.call(reLine, line); - if ( match === null ) { continue; } - let url = match[2]; - if ( url.startsWith('(') ) { url = url.slice(1); } - if ( url === docURL.href ) { - url = 'inlineScript'; - } else if ( url.startsWith('') ) { - url = 'injectedScript'; - } - let fn = match[1] !== undefined - ? match[1].slice(0, -1) - : line.slice(0, match.index).trim(); - if ( fn.startsWith('at') ) { fn = fn.slice(2).trim(); } - let rowcol = match[3]; - lines.push(' ' + `${fn} ${url}${rowcol}:1`.trim()); - } - lines[0] = `stackDepth:${lines.length-1}`; - const stack = lines.join('\t'); - const r = needleDetails.matchAll !== true && - safe.testPattern(needleDetails, stack); - if ( - logLevel === 'all' || - logLevel === 'match' && r || - logLevel === 'nomatch' && !r - ) { - safe.uboLog(stack.replace(/\t/g, '\n')); - } - return r; -} - -/******************************************************************************/ - -builtinScriptlets.push({ - name: 'parse-properties-to-match.fn', - fn: parsePropertiesToMatch, - dependencies: [ - 'safe-self.fn', - ], -}); -function parsePropertiesToMatch(propsToMatch, implicit = '') { - const safe = safeSelf(); - const needles = new Map(); - if ( propsToMatch === undefined || propsToMatch === '' ) { return needles; } - const options = { canNegate: true }; - for ( const needle of safe.String_split.call(propsToMatch, /\s+/) ) { - let [ prop, pattern ] = safe.String_split.call(needle, ':'); - if ( prop === '' ) { continue; } - if ( pattern !== undefined && /[^$\w -]/.test(prop) ) { - prop = `${prop}:${pattern}`; - pattern = undefined; - } - if ( pattern !== undefined ) { - needles.set(prop, safe.initPattern(pattern, options)); - } else if ( implicit !== '' ) { - needles.set(implicit, safe.initPattern(prop, options)); - } - } - return needles; -} - -/******************************************************************************/ - -builtinScriptlets.push({ - name: 'match-object-properties.fn', - fn: matchObjectProperties, - dependencies: [ - 'safe-self.fn', - ], -}); -function matchObjectProperties(propNeedles, ...objs) { - const safe = safeSelf(); - const matched = []; - for ( const obj of objs ) { - if ( obj instanceof Object === false ) { continue; } - for ( const [ prop, details ] of propNeedles ) { - let value = obj[prop]; - if ( value === undefined ) { continue; } - if ( typeof value !== 'string' ) { - try { value = safe.JSON_stringify(value); } - catch { } - if ( typeof value !== 'string' ) { continue; } - } - if ( safe.testPattern(details, value) === false ) { return; } - matched.push(`${prop}: ${value}`); - } - } - return matched; -} - -/******************************************************************************/ - -builtinScriptlets.push({ - name: 'json-prune-fetch-response.fn', - fn: jsonPruneFetchResponseFn, - dependencies: [ - 'match-object-properties.fn', - 'object-prune.fn', - 'parse-properties-to-match.fn', - 'safe-self.fn', - ], -}); -function jsonPruneFetchResponseFn( - rawPrunePaths = '', - rawNeedlePaths = '' -) { - const safe = safeSelf(); - const logPrefix = safe.makeLogPrefix('json-prune-fetch-response', rawPrunePaths, rawNeedlePaths); - const extraArgs = safe.getExtraArgs(Array.from(arguments), 2); - const propNeedles = parsePropertiesToMatch(extraArgs.propsToMatch, 'url'); - const stackNeedle = safe.initPattern(extraArgs.stackToMatch || '', { canNegate: true }); - const logall = rawPrunePaths === ''; - const applyHandler = function(target, thisArg, args) { - const fetchPromise = Reflect.apply(target, thisArg, args); - if ( propNeedles.size !== 0 ) { - const objs = [ args[0] instanceof Object ? args[0] : { url: args[0] } ]; - if ( objs[0] instanceof Request ) { - try { - objs[0] = safe.Request_clone.call(objs[0]); - } catch(ex) { - safe.uboErr(logPrefix, 'Error:', ex); - } - } - if ( args[1] instanceof Object ) { - objs.push(args[1]); - } - const matched = matchObjectProperties(propNeedles, ...objs); - if ( matched === undefined ) { return fetchPromise; } - if ( safe.logLevel > 1 ) { - safe.uboLog(logPrefix, `Matched "propsToMatch":\n\t${matched.join('\n\t')}`); - } - } - return fetchPromise.then(responseBefore => { - const response = responseBefore.clone(); - return response.json().then(objBefore => { - if ( typeof objBefore !== 'object' ) { return responseBefore; } - if ( logall ) { - safe.uboLog(logPrefix, safe.JSON_stringify(objBefore, null, 2)); - return responseBefore; - } - const objAfter = objectPruneFn( - objBefore, - rawPrunePaths, - rawNeedlePaths, - stackNeedle, - extraArgs - ); - if ( typeof objAfter !== 'object' ) { return responseBefore; } - safe.uboLog(logPrefix, 'Pruned'); - const responseAfter = Response.json(objAfter, { - status: responseBefore.status, - statusText: responseBefore.statusText, - headers: responseBefore.headers, - }); - Object.defineProperties(responseAfter, { - ok: { value: responseBefore.ok }, - redirected: { value: responseBefore.redirected }, - type: { value: responseBefore.type }, - url: { value: responseBefore.url }, - }); - return responseAfter; - }).catch(reason => { - safe.uboErr(logPrefix, 'Error:', reason); - return responseBefore; - }); - }).catch(reason => { - safe.uboErr(logPrefix, 'Error:', reason); - return fetchPromise; - }); - }; - self.fetch = new Proxy(self.fetch, { - apply: applyHandler - }); -} - -/******************************************************************************/ - builtinScriptlets.push({ name: 'replace-fetch-response.fn', fn: replaceFetchResponseFn, @@ -751,7 +398,7 @@ function replaceFetchResponseFn( const logPrefix = safe.makeLogPrefix('replace-fetch-response', pattern, replacement, propsToMatch); if ( pattern === '*' ) { pattern = '.*'; } const rePattern = safe.patternToRegex(pattern); - const propNeedles = parsePropertiesToMatch(propsToMatch, 'url'); + const propNeedles = parsePropertiesToMatchFn(propsToMatch, 'url'); const extraArgs = safe.getExtraArgs(Array.from(arguments), 4); const reIncludes = extraArgs.includes ? safe.patternToRegex(extraArgs.includes) : null; self.fetch = new Proxy(self.fetch, { @@ -771,7 +418,7 @@ function replaceFetchResponseFn( if ( args[1] instanceof Object ) { objs.push(args[1]); } - const matched = matchObjectProperties(propNeedles, ...objs); + const matched = matchObjectPropertiesFn(propNeedles, ...objs); if ( matched === undefined ) { return fetchPromise; } if ( safe.logLevel > 1 ) { safe.uboLog(logPrefix, `Matched "propsToMatch":\n\t${matched.join('\n\t')}`); @@ -832,7 +479,7 @@ function preventXhrFn( const scriptletName = trusted ? 'trusted-prevent-xhr' : 'prevent-xhr'; const logPrefix = safe.makeLogPrefix(scriptletName, propsToMatch, directive); const xhrInstances = new WeakMap(); - const propNeedles = parsePropertiesToMatch(propsToMatch, 'url'); + const propNeedles = parsePropertiesToMatchFn(propsToMatch, 'url'); const warOrigin = scriptletGlobals.warOrigin; const safeDispatchEvent = (xhr, type) => { try { @@ -852,7 +499,7 @@ function preventXhrFn( safe.uboLog(logPrefix, `Called: ${safe.JSON_stringify(haystack, null, 2)}`); return super.open(method, url, ...args); } - if ( matchObjectProperties(propNeedles, haystack) ) { + if ( matchObjectPropertiesFn(propNeedles, haystack) ) { const xhrDetails = Object.assign(haystack, { xhr: this, defer: args.length === 0 || !!args[0], @@ -1051,7 +698,7 @@ function abortOnPropertyRead( if ( chain === '' ) { return; } const safe = safeSelf(); const logPrefix = safe.makeLogPrefix('abort-on-property-read', chain); - const exceptionToken = getExceptionToken(); + const exceptionToken = getExceptionTokenFn(); const abort = function() { safe.uboLog(logPrefix, 'Aborted'); throw new ReferenceError(exceptionToken); @@ -1111,7 +758,7 @@ function abortOnPropertyWrite( if ( prop === '' ) { return; } const safe = safeSelf(); const logPrefix = safe.makeLogPrefix('abort-on-property-write', prop); - const exceptionToken = getExceptionToken(); + const exceptionToken = getExceptionTokenFn(); let owner = window; for (;;) { const pos = prop.indexOf('.'); @@ -1131,74 +778,6 @@ function abortOnPropertyWrite( /******************************************************************************/ -builtinScriptlets.push({ - name: 'abort-on-stack-trace.js', - aliases: [ - 'aost.js', - ], - fn: abortOnStackTrace, - dependencies: [ - 'get-exception-token.fn', - 'matches-stack-trace.fn', - 'safe-self.fn', - ], -}); -function abortOnStackTrace( - chain = '', - needle = '' -) { - if ( typeof chain !== 'string' ) { return; } - const safe = safeSelf(); - const needleDetails = safe.initPattern(needle, { canNegate: true }); - const extraArgs = safe.getExtraArgs(Array.from(arguments), 2); - if ( needle === '' ) { extraArgs.log = 'all'; } - const makeProxy = function(owner, chain) { - const pos = chain.indexOf('.'); - if ( pos === -1 ) { - let v = owner[chain]; - Object.defineProperty(owner, chain, { - get: function() { - const log = safe.logLevel > 1 ? 'all' : 'match'; - if ( matchesStackTraceFn(needleDetails, log) ) { - throw new ReferenceError(getExceptionToken()); - } - return v; - }, - set: function(a) { - const log = safe.logLevel > 1 ? 'all' : 'match'; - if ( matchesStackTraceFn(needleDetails, log) ) { - throw new ReferenceError(getExceptionToken()); - } - v = a; - }, - }); - return; - } - const prop = chain.slice(0, pos); - let v = owner[prop]; - chain = chain.slice(pos + 1); - if ( v ) { - makeProxy(v, chain); - return; - } - const desc = Object.getOwnPropertyDescriptor(owner, prop); - if ( desc && desc.set !== undefined ) { return; } - Object.defineProperty(owner, prop, { - get: function() { return v; }, - set: function(a) { - v = a; - if ( a instanceof Object ) { - makeProxy(a, chain); - } - } - }); - }; - const owner = window; - makeProxy(owner, chain); -} - -/******************************************************************************/ - builtinScriptlets.push({ name: 'addEventListener-defuser.js', aliases: [ @@ -1295,186 +874,6 @@ function addEventListenerDefuser( /******************************************************************************/ -builtinScriptlets.push({ - name: 'json-prune.js', - fn: jsonPrune, - dependencies: [ - 'object-prune.fn', - 'safe-self.fn', - ], -}); -function jsonPrune( - rawPrunePaths = '', - rawNeedlePaths = '', - stackNeedle = '' -) { - const safe = safeSelf(); - const logPrefix = safe.makeLogPrefix('json-prune', rawPrunePaths, rawNeedlePaths, stackNeedle); - const stackNeedleDetails = safe.initPattern(stackNeedle, { canNegate: true }); - const extraArgs = safe.getExtraArgs(Array.from(arguments), 3); - JSON.parse = new Proxy(JSON.parse, { - apply: function(target, thisArg, args) { - const objBefore = Reflect.apply(target, thisArg, args); - if ( rawPrunePaths === '' ) { - safe.uboLog(logPrefix, safe.JSON_stringify(objBefore, null, 2)); - } - const objAfter = objectPruneFn( - objBefore, - rawPrunePaths, - rawNeedlePaths, - stackNeedleDetails, - extraArgs - ); - if ( objAfter === undefined ) { return objBefore; } - safe.uboLog(logPrefix, 'Pruned'); - if ( safe.logLevel > 1 ) { - safe.uboLog(logPrefix, `After pruning:\n${safe.JSON_stringify(objAfter, null, 2)}`); - } - return objAfter; - }, - }); -} - -/******************************************************************************* - * - * json-prune-fetch-response.js - * - * Prune JSON response of fetch requests. - * - **/ - -builtinScriptlets.push({ - name: 'json-prune-fetch-response.js', - fn: jsonPruneFetchResponse, - dependencies: [ - 'json-prune-fetch-response.fn', - ], -}); -function jsonPruneFetchResponse(...args) { - jsonPruneFetchResponseFn(...args); -} - -/******************************************************************************/ - -builtinScriptlets.push({ - name: 'json-prune-xhr-response.js', - fn: jsonPruneXhrResponse, - dependencies: [ - 'match-object-properties.fn', - 'object-prune.fn', - 'parse-properties-to-match.fn', - 'safe-self.fn', - ], -}); -function jsonPruneXhrResponse( - rawPrunePaths = '', - rawNeedlePaths = '' -) { - const safe = safeSelf(); - const logPrefix = safe.makeLogPrefix('json-prune-xhr-response', rawPrunePaths, rawNeedlePaths); - const xhrInstances = new WeakMap(); - const extraArgs = safe.getExtraArgs(Array.from(arguments), 2); - const propNeedles = parsePropertiesToMatch(extraArgs.propsToMatch, 'url'); - const stackNeedle = safe.initPattern(extraArgs.stackToMatch || '', { canNegate: true }); - self.XMLHttpRequest = class extends self.XMLHttpRequest { - open(method, url, ...args) { - const xhrDetails = { method, url }; - let outcome = 'match'; - if ( propNeedles.size !== 0 ) { - if ( matchObjectProperties(propNeedles, xhrDetails) === undefined ) { - outcome = 'nomatch'; - } - } - if ( outcome === 'match' ) { - if ( safe.logLevel > 1 ) { - safe.uboLog(logPrefix, `Matched optional "propsToMatch", "${extraArgs.propsToMatch}"`); - } - xhrInstances.set(this, xhrDetails); - } - return super.open(method, url, ...args); - } - get response() { - const innerResponse = super.response; - const xhrDetails = xhrInstances.get(this); - if ( xhrDetails === undefined ) { - return innerResponse; - } - const responseLength = typeof innerResponse === 'string' - ? innerResponse.length - : undefined; - if ( xhrDetails.lastResponseLength !== responseLength ) { - xhrDetails.response = undefined; - xhrDetails.lastResponseLength = responseLength; - } - if ( xhrDetails.response !== undefined ) { - return xhrDetails.response; - } - let objBefore; - if ( typeof innerResponse === 'object' ) { - objBefore = innerResponse; - } else if ( typeof innerResponse === 'string' ) { - try { - objBefore = safe.JSON_parse(innerResponse); - } catch { - } - } - if ( typeof objBefore !== 'object' ) { - return (xhrDetails.response = innerResponse); - } - const objAfter = objectPruneFn( - objBefore, - rawPrunePaths, - rawNeedlePaths, - stackNeedle, - extraArgs - ); - let outerResponse; - if ( typeof objAfter === 'object' ) { - outerResponse = typeof innerResponse === 'string' - ? safe.JSON_stringify(objAfter) - : objAfter; - safe.uboLog(logPrefix, 'Pruned'); - } else { - outerResponse = innerResponse; - } - return (xhrDetails.response = outerResponse); - } - get responseText() { - const response = this.response; - return typeof response !== 'string' - ? super.responseText - : response; - } - }; -} - -/******************************************************************************/ - -// There is still code out there which uses `eval` in lieu of `JSON.parse`. - -builtinScriptlets.push({ - name: 'evaldata-prune.js', - fn: evaldataPrune, - dependencies: [ - 'object-prune.fn', - 'proxy-apply.fn', - ], -}); -function evaldataPrune( - rawPrunePaths = '', - rawNeedlePaths = '' -) { - proxyApplyFn('eval', function(context) { - const before = context.reflect(); - if ( typeof before !== 'object' ) { return before; } - if ( before === null ) { return null; } - const after = objectPruneFn(before, rawPrunePaths, rawNeedlePaths); - return after || before; - }); -} - -/******************************************************************************/ - builtinScriptlets.push({ name: 'adjust-setInterval.js', aliases: [ @@ -2811,7 +2210,7 @@ function trustedReplaceXhrResponse( const xhrInstances = new WeakMap(); if ( pattern === '*' ) { pattern = '.*'; } const rePattern = safe.patternToRegex(pattern); - const propNeedles = parsePropertiesToMatch(propsToMatch, 'url'); + const propNeedles = parsePropertiesToMatchFn(propsToMatch, 'url'); const extraArgs = safe.getExtraArgs(Array.from(arguments), 3); const reIncludes = extraArgs.includes ? safe.patternToRegex(extraArgs.includes) : null; self.XMLHttpRequest = class extends self.XMLHttpRequest { @@ -2820,7 +2219,7 @@ function trustedReplaceXhrResponse( const xhrDetails = { method, url }; let outcome = 'match'; if ( propNeedles.size !== 0 ) { - if ( matchObjectProperties(propNeedles, xhrDetails) === undefined ) { + if ( matchObjectPropertiesFn(propNeedles, xhrDetails) === undefined ) { outcome = 'nomatch'; } } @@ -3062,120 +2461,6 @@ function trustedClickElement( /******************************************************************************/ -builtinScriptlets.push({ - name: 'trusted-prune-inbound-object.js', - requiresTrust: true, - fn: trustedPruneInboundObject, - dependencies: [ - 'object-find-owner.fn', - 'object-prune.fn', - 'safe-self.fn', - ], -}); -function trustedPruneInboundObject( - entryPoint = '', - argPos = '', - rawPrunePaths = '', - rawNeedlePaths = '' -) { - if ( entryPoint === '' ) { return; } - let context = globalThis; - let prop = entryPoint; - for (;;) { - const pos = prop.indexOf('.'); - if ( pos === -1 ) { break; } - context = context[prop.slice(0, pos)]; - if ( context instanceof Object === false ) { return; } - prop = prop.slice(pos+1); - } - if ( typeof context[prop] !== 'function' ) { return; } - const argIndex = parseInt(argPos); - if ( isNaN(argIndex) ) { return; } - if ( argIndex < 1 ) { return; } - const safe = safeSelf(); - const extraArgs = safe.getExtraArgs(Array.from(arguments), 4); - const needlePaths = []; - if ( rawPrunePaths !== '' ) { - needlePaths.push(...safe.String_split.call(rawPrunePaths, / +/)); - } - if ( rawNeedlePaths !== '' ) { - needlePaths.push(...safe.String_split.call(rawNeedlePaths, / +/)); - } - const stackNeedle = safe.initPattern(extraArgs.stackToMatch || '', { canNegate: true }); - const mustProcess = root => { - for ( const needlePath of needlePaths ) { - if ( objectFindOwnerFn(root, needlePath) === false ) { - return false; - } - } - return true; - }; - context[prop] = new Proxy(context[prop], { - apply: function(target, thisArg, args) { - const targetArg = argIndex <= args.length - ? args[argIndex-1] - : undefined; - if ( targetArg instanceof Object && mustProcess(targetArg) ) { - let objBefore = targetArg; - if ( extraArgs.dontOverwrite ) { - try { - objBefore = safe.JSON_parse(safe.JSON_stringify(targetArg)); - } catch { - objBefore = undefined; - } - } - if ( objBefore !== undefined ) { - const objAfter = objectPruneFn( - objBefore, - rawPrunePaths, - rawNeedlePaths, - stackNeedle, - extraArgs - ); - args[argIndex-1] = objAfter || objBefore; - } - } - return Reflect.apply(target, thisArg, args); - }, - }); -} - -/******************************************************************************/ - -builtinScriptlets.push({ - name: 'trusted-prune-outbound-object.js', - requiresTrust: true, - fn: trustedPruneOutboundObject, - dependencies: [ - 'object-prune.fn', - 'proxy-apply.fn', - 'safe-self.fn', - ], -}); -function trustedPruneOutboundObject( - propChain = '', - rawPrunePaths = '', - rawNeedlePaths = '' -) { - if ( propChain === '' ) { return; } - const safe = safeSelf(); - const extraArgs = safe.getExtraArgs(Array.from(arguments), 3); - proxyApplyFn(propChain, function(context) { - const objBefore = context.reflect(); - if ( objBefore instanceof Object === false ) { return objBefore; } - const objAfter = objectPruneFn( - objBefore, - rawPrunePaths, - rawNeedlePaths, - { matchAll: true }, - extraArgs - ); - return objAfter || objBefore; - }); -} - -/******************************************************************************/ - builtinScriptlets.push({ name: 'trusted-replace-outbound-text.js', requiresTrust: true, diff --git a/src/js/resources/stack-trace.js b/src/js/resources/stack-trace.js new file mode 100644 index 0000000000000..c2e535847ee32 --- /dev/null +++ b/src/js/resources/stack-trace.js @@ -0,0 +1,148 @@ +/******************************************************************************* + + uBlock Origin - a comprehensive, efficient content blocker + Copyright (C) 2019-present Raymond Hill + + 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/}. + + Home: https://github.com/gorhill/uBlock + +*/ + +import { getExceptionTokenFn } from './utils.js'; +import { registerScriptlet } from './base.js'; +import { safeSelf } from './safe-self.js'; + +/******************************************************************************/ + +export function matchesStackTraceFn( + needleDetails, + logLevel = '' +) { + const safe = safeSelf(); + const exceptionToken = getExceptionTokenFn(); + const error = new safe.Error(exceptionToken); + const docURL = new URL(self.location.href); + docURL.hash = ''; + // Normalize stack trace + const reLine = /(.*?@)?(\S+)(:\d+):\d+\)?$/; + const lines = []; + for ( let line of safe.String_split.call(error.stack, /[\n\r]+/) ) { + if ( line.includes(exceptionToken) ) { continue; } + line = line.trim(); + const match = safe.RegExp_exec.call(reLine, line); + if ( match === null ) { continue; } + let url = match[2]; + if ( url.startsWith('(') ) { url = url.slice(1); } + if ( url === docURL.href ) { + url = 'inlineScript'; + } else if ( url.startsWith('') ) { + url = 'injectedScript'; + } + let fn = match[1] !== undefined + ? match[1].slice(0, -1) + : line.slice(0, match.index).trim(); + if ( fn.startsWith('at') ) { fn = fn.slice(2).trim(); } + let rowcol = match[3]; + lines.push(' ' + `${fn} ${url}${rowcol}:1`.trim()); + } + lines[0] = `stackDepth:${lines.length-1}`; + const stack = lines.join('\t'); + const r = needleDetails.matchAll !== true && + safe.testPattern(needleDetails, stack); + if ( + logLevel === 'all' || + logLevel === 'match' && r || + logLevel === 'nomatch' && !r + ) { + safe.uboLog(stack.replace(/\t/g, '\n')); + } + return r; +} +registerScriptlet(matchesStackTraceFn, { + name: 'matches-stack-trace.fn', + dependencies: [ + getExceptionTokenFn, + safeSelf, + ], +}); + +/******************************************************************************/ + +function abortOnStackTrace( + chain = '', + needle = '' +) { + if ( typeof chain !== 'string' ) { return; } + const safe = safeSelf(); + const needleDetails = safe.initPattern(needle, { canNegate: true }); + const extraArgs = safe.getExtraArgs(Array.from(arguments), 2); + if ( needle === '' ) { extraArgs.log = 'all'; } + const makeProxy = function(owner, chain) { + const pos = chain.indexOf('.'); + if ( pos === -1 ) { + let v = owner[chain]; + Object.defineProperty(owner, chain, { + get: function() { + const log = safe.logLevel > 1 ? 'all' : 'match'; + if ( matchesStackTraceFn(needleDetails, log) ) { + throw new ReferenceError(getExceptionTokenFn()); + } + return v; + }, + set: function(a) { + const log = safe.logLevel > 1 ? 'all' : 'match'; + if ( matchesStackTraceFn(needleDetails, log) ) { + throw new ReferenceError(getExceptionTokenFn()); + } + v = a; + }, + }); + return; + } + const prop = chain.slice(0, pos); + let v = owner[prop]; + chain = chain.slice(pos + 1); + if ( v ) { + makeProxy(v, chain); + return; + } + const desc = Object.getOwnPropertyDescriptor(owner, prop); + if ( desc && desc.set !== undefined ) { return; } + Object.defineProperty(owner, prop, { + get: function() { return v; }, + set: function(a) { + v = a; + if ( a instanceof Object ) { + makeProxy(a, chain); + } + } + }); + }; + const owner = window; + makeProxy(owner, chain); +} +registerScriptlet(abortOnStackTrace, { + name: 'abort-on-stack-trace.js', + aliases: [ + 'aost.js', + ], + dependencies: [ + getExceptionTokenFn, + matchesStackTraceFn, + safeSelf, + ], +}); + +/******************************************************************************/ diff --git a/src/js/resources/utils.js b/src/js/resources/utils.js new file mode 100644 index 0000000000000..86e55e9917fee --- /dev/null +++ b/src/js/resources/utils.js @@ -0,0 +1,117 @@ +/******************************************************************************* + + uBlock Origin - a comprehensive, efficient content blocker + Copyright (C) 2019-present Raymond Hill + + 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/}. + + Home: https://github.com/gorhill/uBlock + +*/ + +import { registerScriptlet } from './base.js'; +import { safeSelf } from './safe-self.js'; + +/******************************************************************************/ + +export function getRandomTokenFn() { + const safe = safeSelf(); + return safe.String_fromCharCode(Date.now() % 26 + 97) + + safe.Math_floor(safe.Math_random() * 982451653 + 982451653).toString(36); +} +registerScriptlet(getRandomTokenFn, { + name: 'get-random-token.fn', + dependencies: [ + safeSelf, + ], +}); + +/******************************************************************************/ + +export function getExceptionTokenFn() { + const token = getRandomTokenFn(); + const oe = self.onerror; + self.onerror = function(msg, ...args) { + if ( typeof msg === 'string' && msg.includes(token) ) { return true; } + if ( oe instanceof Function ) { + return oe.call(this, msg, ...args); + } + }.bind(); + return token; +} +registerScriptlet(getExceptionTokenFn, { + name: 'get-exception-token.fn', + dependencies: [ + getRandomTokenFn, + ], +}); + +/******************************************************************************/ + +export function parsePropertiesToMatchFn(propsToMatch, implicit = '') { + const safe = safeSelf(); + const needles = new Map(); + if ( propsToMatch === undefined || propsToMatch === '' ) { return needles; } + const options = { canNegate: true }; + for ( const needle of safe.String_split.call(propsToMatch, /\s+/) ) { + let [ prop, pattern ] = safe.String_split.call(needle, ':'); + if ( prop === '' ) { continue; } + if ( pattern !== undefined && /[^$\w -]/.test(prop) ) { + prop = `${prop}:${pattern}`; + pattern = undefined; + } + if ( pattern !== undefined ) { + needles.set(prop, safe.initPattern(pattern, options)); + } else if ( implicit !== '' ) { + needles.set(implicit, safe.initPattern(prop, options)); + } + } + return needles; +} +registerScriptlet(parsePropertiesToMatchFn, { + name: 'parse-properties-to-match.fn', + dependencies: [ + safeSelf, + ], +}); + +/******************************************************************************/ + +export function matchObjectPropertiesFn(propNeedles, ...objs) { + const safe = safeSelf(); + const matched = []; + for ( const obj of objs ) { + if ( obj instanceof Object === false ) { continue; } + for ( const [ prop, details ] of propNeedles ) { + let value = obj[prop]; + if ( value === undefined ) { continue; } + if ( typeof value !== 'string' ) { + try { value = safe.JSON_stringify(value); } + catch { } + if ( typeof value !== 'string' ) { continue; } + } + if ( safe.testPattern(details, value) === false ) { return; } + matched.push(`${prop}: ${value}`); + } + } + return matched; +} +registerScriptlet(matchObjectPropertiesFn, { + name: 'match-object-properties.fn', + dependencies: [ + safeSelf, + ], +}); + +/******************************************************************************/ From a07d01285cd6849012d9b4e291c1531ac2a707a7 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sat, 22 Mar 2025 16:06:37 -0400 Subject: [PATCH 0720/1099] Update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0b58104f7d1ca..d9978e7b3e94f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,4 @@ +- [Add jsonl-prune-xhr-response/jsonl-prune-fetch-response scriptlets](https://github.com/gorhill/uBlock/commit/95a3be9d56) - [Improve `[json-prune|trusted-replace]-fetch-response` scriptlets](https://github.com/gorhill/uBlock/commit/88fa550a96) ---------- From 7bded976a881a919b408f5996391f72ef095fb6a Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sat, 22 Mar 2025 16:06:50 -0400 Subject: [PATCH 0721/1099] New revision for dev build --- dist/version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dist/version b/dist/version index 44072f506a3ac..1d280851adba6 100644 --- a/dist/version +++ b/dist/version @@ -1 +1 @@ -1.63.3.0 +1.63.3.1 From 8f78faf98080424584399bab51116df494d41185 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sat, 22 Mar 2025 16:11:10 -0400 Subject: [PATCH 0722/1099] Import translation work from https://crowdin.com/project/ublock --- platform/mv3/extension/_locales/bg/messages.json | 4 ++-- platform/mv3/extension/_locales/ca/messages.json | 4 ++-- platform/mv3/extension/_locales/cs/messages.json | 4 ++-- platform/mv3/extension/_locales/da/messages.json | 4 ++-- platform/mv3/extension/_locales/de/messages.json | 4 ++-- platform/mv3/extension/_locales/el/messages.json | 4 ++-- platform/mv3/extension/_locales/es/messages.json | 4 ++-- platform/mv3/extension/_locales/fr/messages.json | 4 ++-- platform/mv3/extension/_locales/gl/messages.json | 4 ++-- platform/mv3/extension/_locales/hr/messages.json | 4 ++-- platform/mv3/extension/_locales/it/messages.json | 4 ++-- platform/mv3/extension/_locales/ja/messages.json | 4 ++-- platform/mv3/extension/_locales/ko/messages.json | 4 ++-- platform/mv3/extension/_locales/lv/messages.json | 4 ++-- platform/mv3/extension/_locales/nl/messages.json | 4 ++-- platform/mv3/extension/_locales/pl/messages.json | 4 ++-- platform/mv3/extension/_locales/pt_BR/messages.json | 4 ++-- platform/mv3/extension/_locales/ru/messages.json | 4 ++-- platform/mv3/extension/_locales/sk/messages.json | 4 ++-- platform/mv3/extension/_locales/sq/messages.json | 4 ++-- platform/mv3/extension/_locales/sv/messages.json | 4 ++-- platform/mv3/extension/_locales/tr/messages.json | 4 ++-- platform/mv3/extension/_locales/uk/messages.json | 4 ++-- platform/mv3/extension/_locales/zh_TW/messages.json | 4 ++-- src/_locales/ca/messages.json | 6 +++--- src/_locales/el/messages.json | 2 +- src/_locales/fr/messages.json | 2 +- src/_locales/sv/messages.json | 2 +- src/_locales/zh_TW/messages.json | 4 ++-- 29 files changed, 56 insertions(+), 56 deletions(-) diff --git a/platform/mv3/extension/_locales/bg/messages.json b/platform/mv3/extension/_locales/bg/messages.json index ecacd4f5ea743..0ade0d081a037 100644 --- a/platform/mv3/extension/_locales/bg/messages.json +++ b/platform/mv3/extension/_locales/bg/messages.json @@ -280,11 +280,11 @@ "description": "A button to navigate to the blocked page" }, "zapperTipEnter": { - "message": "Enter element zapper mode", + "message": "Влизане в режима на временно скриване на елемента", "description": "Tooltip for the button used to enter zapper mode" }, "zapperTipQuit": { - "message": "Exit element zapper mode", + "message": "Излизане от режима на временно скриване на елемента", "description": "Tooltip for the button used to exit zapper mode" } } diff --git a/platform/mv3/extension/_locales/ca/messages.json b/platform/mv3/extension/_locales/ca/messages.json index 2acb47141928d..0a0e3423461f5 100644 --- a/platform/mv3/extension/_locales/ca/messages.json +++ b/platform/mv3/extension/_locales/ca/messages.json @@ -280,11 +280,11 @@ "description": "A button to navigate to the blocked page" }, "zapperTipEnter": { - "message": "Enter element zapper mode", + "message": "Accedeix al mode d'eliminació d'elements", "description": "Tooltip for the button used to enter zapper mode" }, "zapperTipQuit": { - "message": "Exit element zapper mode", + "message": "Surt del mode d'eliminació d'elements", "description": "Tooltip for the button used to exit zapper mode" } } diff --git a/platform/mv3/extension/_locales/cs/messages.json b/platform/mv3/extension/_locales/cs/messages.json index 8b71344f39a23..aa21a94475faa 100644 --- a/platform/mv3/extension/_locales/cs/messages.json +++ b/platform/mv3/extension/_locales/cs/messages.json @@ -280,11 +280,11 @@ "description": "A button to navigate to the blocked page" }, "zapperTipEnter": { - "message": "Enter element zapper mode", + "message": "Přejít do režimu dočasného skrytí prvků", "description": "Tooltip for the button used to enter zapper mode" }, "zapperTipQuit": { - "message": "Exit element zapper mode", + "message": "Opustit režim dočasného skrytí prvků", "description": "Tooltip for the button used to exit zapper mode" } } diff --git a/platform/mv3/extension/_locales/da/messages.json b/platform/mv3/extension/_locales/da/messages.json index 9902eec2ad25c..e0ce22a0849c7 100644 --- a/platform/mv3/extension/_locales/da/messages.json +++ b/platform/mv3/extension/_locales/da/messages.json @@ -280,11 +280,11 @@ "description": "A button to navigate to the blocked page" }, "zapperTipEnter": { - "message": "Enter element zapper mode", + "message": "Gå til elementdræber­tilstand", "description": "Tooltip for the button used to enter zapper mode" }, "zapperTipQuit": { - "message": "Exit element zapper mode", + "message": "Forlad elementdræber­tilstand", "description": "Tooltip for the button used to exit zapper mode" } } diff --git a/platform/mv3/extension/_locales/de/messages.json b/platform/mv3/extension/_locales/de/messages.json index 2f617ab19a2ee..30419df3d2ab7 100644 --- a/platform/mv3/extension/_locales/de/messages.json +++ b/platform/mv3/extension/_locales/de/messages.json @@ -280,11 +280,11 @@ "description": "A button to navigate to the blocked page" }, "zapperTipEnter": { - "message": "Enter element zapper mode", + "message": "Element temporär entfernen", "description": "Tooltip for the button used to enter zapper mode" }, "zapperTipQuit": { - "message": "Exit element zapper mode", + "message": "Temporären Modus beenden", "description": "Tooltip for the button used to exit zapper mode" } } diff --git a/platform/mv3/extension/_locales/el/messages.json b/platform/mv3/extension/_locales/el/messages.json index b1dd6c723993a..5a94f2ffea253 100644 --- a/platform/mv3/extension/_locales/el/messages.json +++ b/platform/mv3/extension/_locales/el/messages.json @@ -280,11 +280,11 @@ "description": "A button to navigate to the blocked page" }, "zapperTipEnter": { - "message": "Enter element zapper mode", + "message": "Είσοδος σε λειτουργία αφαίρεσης στοιχείων", "description": "Tooltip for the button used to enter zapper mode" }, "zapperTipQuit": { - "message": "Exit element zapper mode", + "message": "Έξοδος από λειτουργία αφαίρεσης στοιχείων", "description": "Tooltip for the button used to exit zapper mode" } } diff --git a/platform/mv3/extension/_locales/es/messages.json b/platform/mv3/extension/_locales/es/messages.json index a9e679383ca66..a2f41cbf28178 100644 --- a/platform/mv3/extension/_locales/es/messages.json +++ b/platform/mv3/extension/_locales/es/messages.json @@ -280,11 +280,11 @@ "description": "A button to navigate to the blocked page" }, "zapperTipEnter": { - "message": "Enter element zapper mode", + "message": "Entrar al modo eliminación de elementos", "description": "Tooltip for the button used to enter zapper mode" }, "zapperTipQuit": { - "message": "Exit element zapper mode", + "message": "Salir del modo eliminación de elementos", "description": "Tooltip for the button used to exit zapper mode" } } diff --git a/platform/mv3/extension/_locales/fr/messages.json b/platform/mv3/extension/_locales/fr/messages.json index fbdc060a5c961..215b28eec1d3a 100644 --- a/platform/mv3/extension/_locales/fr/messages.json +++ b/platform/mv3/extension/_locales/fr/messages.json @@ -280,11 +280,11 @@ "description": "A button to navigate to the blocked page" }, "zapperTipEnter": { - "message": "Enter element zapper mode", + "message": "Entrer en mode Zappeur d'élément", "description": "Tooltip for the button used to enter zapper mode" }, "zapperTipQuit": { - "message": "Exit element zapper mode", + "message": "Quitter le mode Zappeur d'élément", "description": "Tooltip for the button used to exit zapper mode" } } diff --git a/platform/mv3/extension/_locales/gl/messages.json b/platform/mv3/extension/_locales/gl/messages.json index cb950eee7dcb0..45e126a0456e3 100644 --- a/platform/mv3/extension/_locales/gl/messages.json +++ b/platform/mv3/extension/_locales/gl/messages.json @@ -280,11 +280,11 @@ "description": "A button to navigate to the blocked page" }, "zapperTipEnter": { - "message": "Enter element zapper mode", + "message": "Entrar no modo eliminador de elementos", "description": "Tooltip for the button used to enter zapper mode" }, "zapperTipQuit": { - "message": "Exit element zapper mode", + "message": "Sair do modo eliminador de elementos", "description": "Tooltip for the button used to exit zapper mode" } } diff --git a/platform/mv3/extension/_locales/hr/messages.json b/platform/mv3/extension/_locales/hr/messages.json index 1c11f6b8749b0..9119a14047df5 100644 --- a/platform/mv3/extension/_locales/hr/messages.json +++ b/platform/mv3/extension/_locales/hr/messages.json @@ -280,11 +280,11 @@ "description": "A button to navigate to the blocked page" }, "zapperTipEnter": { - "message": "Enter element zapper mode", + "message": "Ulaz u način rada uklanjanja elementa", "description": "Tooltip for the button used to enter zapper mode" }, "zapperTipQuit": { - "message": "Exit element zapper mode", + "message": "Zatvori način rada uklanjanja elementa", "description": "Tooltip for the button used to exit zapper mode" } } diff --git a/platform/mv3/extension/_locales/it/messages.json b/platform/mv3/extension/_locales/it/messages.json index 03f4cf4d210eb..5a459af58af00 100644 --- a/platform/mv3/extension/_locales/it/messages.json +++ b/platform/mv3/extension/_locales/it/messages.json @@ -280,11 +280,11 @@ "description": "A button to navigate to the blocked page" }, "zapperTipEnter": { - "message": "Enter element zapper mode", + "message": "Accedi alla modalità blocco rapido", "description": "Tooltip for the button used to enter zapper mode" }, "zapperTipQuit": { - "message": "Exit element zapper mode", + "message": "Esci dalla modalità blocco rapido", "description": "Tooltip for the button used to exit zapper mode" } } diff --git a/platform/mv3/extension/_locales/ja/messages.json b/platform/mv3/extension/_locales/ja/messages.json index 18396880c4080..22d0787f8fe9c 100644 --- a/platform/mv3/extension/_locales/ja/messages.json +++ b/platform/mv3/extension/_locales/ja/messages.json @@ -280,11 +280,11 @@ "description": "A button to navigate to the blocked page" }, "zapperTipEnter": { - "message": "Enter element zapper mode", + "message": "要素抹消モードを開始", "description": "Tooltip for the button used to enter zapper mode" }, "zapperTipQuit": { - "message": "Exit element zapper mode", + "message": "要素抹消モードを終了", "description": "Tooltip for the button used to exit zapper mode" } } diff --git a/platform/mv3/extension/_locales/ko/messages.json b/platform/mv3/extension/_locales/ko/messages.json index 9f93fbca5c085..f0ac82033018a 100644 --- a/platform/mv3/extension/_locales/ko/messages.json +++ b/platform/mv3/extension/_locales/ko/messages.json @@ -280,11 +280,11 @@ "description": "A button to navigate to the blocked page" }, "zapperTipEnter": { - "message": "Enter element zapper mode", + "message": "구성 요소 제거 모드로 진입", "description": "Tooltip for the button used to enter zapper mode" }, "zapperTipQuit": { - "message": "Exit element zapper mode", + "message": "구성 요소 제거 모드 종료", "description": "Tooltip for the button used to exit zapper mode" } } diff --git a/platform/mv3/extension/_locales/lv/messages.json b/platform/mv3/extension/_locales/lv/messages.json index 769de4c3ae793..b280efb2d5390 100644 --- a/platform/mv3/extension/_locales/lv/messages.json +++ b/platform/mv3/extension/_locales/lv/messages.json @@ -280,11 +280,11 @@ "description": "A button to navigate to the blocked page" }, "zapperTipEnter": { - "message": "Enter element zapper mode", + "message": "Pārslēgties uz elementu iznīcināšanu", "description": "Tooltip for the button used to enter zapper mode" }, "zapperTipQuit": { - "message": "Exit element zapper mode", + "message": "Iziet no elementu iznīcināšanas", "description": "Tooltip for the button used to exit zapper mode" } } diff --git a/platform/mv3/extension/_locales/nl/messages.json b/platform/mv3/extension/_locales/nl/messages.json index f437b433d55c7..6c087425fecab 100644 --- a/platform/mv3/extension/_locales/nl/messages.json +++ b/platform/mv3/extension/_locales/nl/messages.json @@ -280,11 +280,11 @@ "description": "A button to navigate to the blocked page" }, "zapperTipEnter": { - "message": "Enter element zapper mode", + "message": "Element­wisser­modus openen", "description": "Tooltip for the button used to enter zapper mode" }, "zapperTipQuit": { - "message": "Exit element zapper mode", + "message": "Element­wisser­modus sluiten", "description": "Tooltip for the button used to exit zapper mode" } } diff --git a/platform/mv3/extension/_locales/pl/messages.json b/platform/mv3/extension/_locales/pl/messages.json index 3dcfb382498d7..51948aee99de9 100644 --- a/platform/mv3/extension/_locales/pl/messages.json +++ b/platform/mv3/extension/_locales/pl/messages.json @@ -280,11 +280,11 @@ "description": "A button to navigate to the blocked page" }, "zapperTipEnter": { - "message": "Enter element zapper mode", + "message": "Przejdź do trybu usuwania elementów", "description": "Tooltip for the button used to enter zapper mode" }, "zapperTipQuit": { - "message": "Exit element zapper mode", + "message": "Wyjdź z trybu usuwania elementów", "description": "Tooltip for the button used to exit zapper mode" } } diff --git a/platform/mv3/extension/_locales/pt_BR/messages.json b/platform/mv3/extension/_locales/pt_BR/messages.json index 6b034c1b580dc..0a58de024bb6b 100644 --- a/platform/mv3/extension/_locales/pt_BR/messages.json +++ b/platform/mv3/extension/_locales/pt_BR/messages.json @@ -280,11 +280,11 @@ "description": "A button to navigate to the blocked page" }, "zapperTipEnter": { - "message": "Enter element zapper mode", + "message": "Entrar no modo do elemento zapper", "description": "Tooltip for the button used to enter zapper mode" }, "zapperTipQuit": { - "message": "Exit element zapper mode", + "message": "Sair do modo do elemento zapper", "description": "Tooltip for the button used to exit zapper mode" } } diff --git a/platform/mv3/extension/_locales/ru/messages.json b/platform/mv3/extension/_locales/ru/messages.json index c7d7b51e65eb0..07336f7ec8dab 100644 --- a/platform/mv3/extension/_locales/ru/messages.json +++ b/platform/mv3/extension/_locales/ru/messages.json @@ -280,11 +280,11 @@ "description": "A button to navigate to the blocked page" }, "zapperTipEnter": { - "message": "Enter element zapper mode", + "message": "Войти в режим временного скрытия элемента", "description": "Tooltip for the button used to enter zapper mode" }, "zapperTipQuit": { - "message": "Exit element zapper mode", + "message": "Покинуть режим временного скрытия элемента", "description": "Tooltip for the button used to exit zapper mode" } } diff --git a/platform/mv3/extension/_locales/sk/messages.json b/platform/mv3/extension/_locales/sk/messages.json index 9bb02c0662b70..ad4fc321ba56b 100644 --- a/platform/mv3/extension/_locales/sk/messages.json +++ b/platform/mv3/extension/_locales/sk/messages.json @@ -280,11 +280,11 @@ "description": "A button to navigate to the blocked page" }, "zapperTipEnter": { - "message": "Enter element zapper mode", + "message": "Prejsť do režimu dočasného skrytia prvkov", "description": "Tooltip for the button used to enter zapper mode" }, "zapperTipQuit": { - "message": "Exit element zapper mode", + "message": "Ukončiť režim dočasného skrytia prvkov", "description": "Tooltip for the button used to exit zapper mode" } } diff --git a/platform/mv3/extension/_locales/sq/messages.json b/platform/mv3/extension/_locales/sq/messages.json index 236278611fcba..88cd9fd76ffd7 100644 --- a/platform/mv3/extension/_locales/sq/messages.json +++ b/platform/mv3/extension/_locales/sq/messages.json @@ -280,11 +280,11 @@ "description": "A button to navigate to the blocked page" }, "zapperTipEnter": { - "message": "Enter element zapper mode", + "message": "Aktivizoj asgjësuesin e elementeve", "description": "Tooltip for the button used to enter zapper mode" }, "zapperTipQuit": { - "message": "Exit element zapper mode", + "message": "Mbyll asgjësuesin e elementeve", "description": "Tooltip for the button used to exit zapper mode" } } diff --git a/platform/mv3/extension/_locales/sv/messages.json b/platform/mv3/extension/_locales/sv/messages.json index 926c0cf583b63..65f43d4de0fbc 100644 --- a/platform/mv3/extension/_locales/sv/messages.json +++ b/platform/mv3/extension/_locales/sv/messages.json @@ -280,11 +280,11 @@ "description": "A button to navigate to the blocked page" }, "zapperTipEnter": { - "message": "Enter element zapper mode", + "message": "Gå in i elementzapperläge", "description": "Tooltip for the button used to enter zapper mode" }, "zapperTipQuit": { - "message": "Exit element zapper mode", + "message": "Lämna elementzapperläge", "description": "Tooltip for the button used to exit zapper mode" } } diff --git a/platform/mv3/extension/_locales/tr/messages.json b/platform/mv3/extension/_locales/tr/messages.json index 5eb43483dd7a7..f9940ae9049e7 100644 --- a/platform/mv3/extension/_locales/tr/messages.json +++ b/platform/mv3/extension/_locales/tr/messages.json @@ -280,11 +280,11 @@ "description": "A button to navigate to the blocked page" }, "zapperTipEnter": { - "message": "Enter element zapper mode", + "message": "Öge silme moduna gir", "description": "Tooltip for the button used to enter zapper mode" }, "zapperTipQuit": { - "message": "Exit element zapper mode", + "message": "Öge silme modundan çık", "description": "Tooltip for the button used to exit zapper mode" } } diff --git a/platform/mv3/extension/_locales/uk/messages.json b/platform/mv3/extension/_locales/uk/messages.json index ecc9c8caa842b..21e463a6b0684 100644 --- a/platform/mv3/extension/_locales/uk/messages.json +++ b/platform/mv3/extension/_locales/uk/messages.json @@ -280,11 +280,11 @@ "description": "A button to navigate to the blocked page" }, "zapperTipEnter": { - "message": "Enter element zapper mode", + "message": "Перейти в режим тимчасового приховування елементів", "description": "Tooltip for the button used to enter zapper mode" }, "zapperTipQuit": { - "message": "Exit element zapper mode", + "message": "Вийти з режиму тимчасового приховування елементів", "description": "Tooltip for the button used to exit zapper mode" } } diff --git a/platform/mv3/extension/_locales/zh_TW/messages.json b/platform/mv3/extension/_locales/zh_TW/messages.json index c1d853ad21a97..3488118a765b1 100644 --- a/platform/mv3/extension/_locales/zh_TW/messages.json +++ b/platform/mv3/extension/_locales/zh_TW/messages.json @@ -280,11 +280,11 @@ "description": "A button to navigate to the blocked page" }, "zapperTipEnter": { - "message": "Enter element zapper mode", + "message": "進入元素臨時移除模式", "description": "Tooltip for the button used to enter zapper mode" }, "zapperTipQuit": { - "message": "Exit element zapper mode", + "message": "離開元素臨時移除模式", "description": "Tooltip for the button used to exit zapper mode" } } diff --git a/src/_locales/ca/messages.json b/src/_locales/ca/messages.json index e2f4623328e4f..642ca40020159 100644 --- a/src/_locales/ca/messages.json +++ b/src/_locales/ca/messages.json @@ -900,11 +900,11 @@ "description": "Text for button which open an external webpage in Support pane" }, "supportReportSpecificButton": { - "message": "Crea un informe nou", + "message": "Crea un informe nou al GitHub", "description": "Text for button which open an external webpage in Support pane" }, "supportFindSpecificButton": { - "message": "Cerca d'informes semblants", + "message": "Cerca informes similars al GitHub", "description": "A clickable link in the filter issue reporter section" }, "supportS1H": { @@ -964,7 +964,7 @@ "description": "Header of 'Report a filter issue' section in Support pane" }, "supportS6P1S1": { - "message": "Per evitar carregar els voluntaris amb informes duplicats, verifiqueu que el problema encara no s'hagi notificat.", + "message": "Per a evitar la sobrecàrrega del nostre voluntariat amb informes duplicats, verifiqueu abans que el problema encara no s'ha notificat. Nota: en fer clic, enviareu la pàgina causant al nostre GitHub.", "description": "A paragraph in the filter issue reporter section" }, "supportS6P2S1": { diff --git a/src/_locales/el/messages.json b/src/_locales/el/messages.json index 9ce246bf5d627..b6c745952d8d3 100644 --- a/src/_locales/el/messages.json +++ b/src/_locales/el/messages.json @@ -116,7 +116,7 @@ "description": "English: Click to open the dashboard" }, "popupTipZapper": { - "message": "Είσοδος σε λειτουργία εκτέλεσης στοιχείων", + "message": "Είσοδος σε λειτουργία αφαίρεσης στοιχείων", "description": "Tooltip for the element-zapper icon in the popup panel" }, "popupTipPicker": { diff --git a/src/_locales/fr/messages.json b/src/_locales/fr/messages.json index 002bde7936253..e6dc2af66b57f 100644 --- a/src/_locales/fr/messages.json +++ b/src/_locales/fr/messages.json @@ -116,7 +116,7 @@ "description": "English: Click to open the dashboard" }, "popupTipZapper": { - "message": "Entrer en mode Zappeur", + "message": "Entrer en mode Zappeur d'élément", "description": "Tooltip for the element-zapper icon in the popup panel" }, "popupTipPicker": { diff --git a/src/_locales/sv/messages.json b/src/_locales/sv/messages.json index 22c291e256c4b..e6320fb28bd56 100644 --- a/src/_locales/sv/messages.json +++ b/src/_locales/sv/messages.json @@ -116,7 +116,7 @@ "description": "English: Click to open the dashboard" }, "popupTipZapper": { - "message": "Gå till elementzapperläge", + "message": "Gå in i elementzapperläge", "description": "Tooltip for the element-zapper icon in the popup panel" }, "popupTipPicker": { diff --git a/src/_locales/zh_TW/messages.json b/src/_locales/zh_TW/messages.json index 67996a8b17a05..45999bd46495f 100644 --- a/src/_locales/zh_TW/messages.json +++ b/src/_locales/zh_TW/messages.json @@ -16,7 +16,7 @@ "description": "A warning in the dashboard when navigating away from unsaved changes" }, "dashboardUnsavedWarningStay": { - "message": "不離開", + "message": "留在這裡", "description": "Label for button to prevent navigating away from unsaved changes" }, "dashboardUnsavedWarningIgnore": { @@ -44,7 +44,7 @@ "description": "appears as tab name in dashboard" }, "shortcutsPageName": { - "message": "快速鍵", + "message": "快捷鍵", "description": "appears as tab name in dashboard" }, "statsPageName": { From f09aeef6d9109cd74e9016ec2292af015a2e5a9f Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sat, 22 Mar 2025 16:13:52 -0400 Subject: [PATCH 0723/1099] Update changelog --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d9978e7b3e94f..ec90fec1a550d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,4 @@ -- [Add jsonl-prune-xhr-response/jsonl-prune-fetch-response scriptlets](https://github.com/gorhill/uBlock/commit/95a3be9d56) +- [Add `jsonl-prune-xhr-response`/`jsonl-prune-fetch-response` scriptlets](https://github.com/gorhill/uBlock/commit/95a3be9d56) - [Improve `[json-prune|trusted-replace]-fetch-response` scriptlets](https://github.com/gorhill/uBlock/commit/88fa550a96) ---------- From fbabd4eaac034f4622798d8ce53994794ba1ea5f Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sat, 22 Mar 2025 16:15:48 -0400 Subject: [PATCH 0724/1099] Make Firefox dev build auto-update --- dist/firefox/updates.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/dist/firefox/updates.json b/dist/firefox/updates.json index b23a446aa1873..d74fcf57e3e3a 100644 --- a/dist/firefox/updates.json +++ b/dist/firefox/updates.json @@ -3,9 +3,9 @@ "uBlock0@raymondhill.net": { "updates": [ { - "version": "1.63.3.0", - "browser_specific_settings": { "gecko": { "strict_min_version": "79.0" } }, - "update_link": "https://github.com/gorhill/uBlock/releases/download/1.63.3b0/uBlock0_1.63.3b0.firefox.signed.xpi" + "version": "1.63.3.1", + "browser_specific_settings": { "gecko": { "strict_min_version": "92.0" } }, + "update_link": "https://github.com/gorhill/uBlock/releases/download/1.63.3b1/uBlock0_1.63.3b1.firefox.signed.xpi" } ] } From 07a4a6a35de8bc777f323eacf7ed56e4c2d5afff Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sun, 23 Mar 2025 16:37:57 -0400 Subject: [PATCH 0725/1099] Import translation work from https://crowdin.com/project/ublock --- platform/mv3/extension/_locales/et/messages.json | 4 ++-- platform/mv3/extension/_locales/ka/messages.json | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/platform/mv3/extension/_locales/et/messages.json b/platform/mv3/extension/_locales/et/messages.json index 576851abfb5c3..71c81d7ffbf39 100644 --- a/platform/mv3/extension/_locales/et/messages.json +++ b/platform/mv3/extension/_locales/et/messages.json @@ -280,11 +280,11 @@ "description": "A button to navigate to the blocked page" }, "zapperTipEnter": { - "message": "Enter element zapper mode", + "message": "Sisene elemendi hävitusrežiimi", "description": "Tooltip for the button used to enter zapper mode" }, "zapperTipQuit": { - "message": "Exit element zapper mode", + "message": "Välju elemendi hävitusrežiimist", "description": "Tooltip for the button used to exit zapper mode" } } diff --git a/platform/mv3/extension/_locales/ka/messages.json b/platform/mv3/extension/_locales/ka/messages.json index eda9df6a87c57..ab8aa5da010f7 100644 --- a/platform/mv3/extension/_locales/ka/messages.json +++ b/platform/mv3/extension/_locales/ka/messages.json @@ -280,11 +280,11 @@ "description": "A button to navigate to the blocked page" }, "zapperTipEnter": { - "message": "Enter element zapper mode", + "message": "ნაწილების ამოჭრის რეჟიმში გადასვლა", "description": "Tooltip for the button used to enter zapper mode" }, "zapperTipQuit": { - "message": "Exit element zapper mode", + "message": "ნაწილების ამოჭრის რეჟიმიდან გასვლა", "description": "Tooltip for the button used to exit zapper mode" } } From 0c8de6b55043dcaa2a1948bd9c2b81685f918b0b Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Mon, 24 Mar 2025 07:33:21 -0400 Subject: [PATCH 0726/1099] Fix `prevent-innerHTML` scriptlet --- src/js/resources/prevent-innerHTML.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/js/resources/prevent-innerHTML.js b/src/js/resources/prevent-innerHTML.js index e3d3b1f5b5496..587ea20ae80e4 100644 --- a/src/js/resources/prevent-innerHTML.js +++ b/src/js/resources/prevent-innerHTML.js @@ -48,10 +48,10 @@ export function preventInnerHTML( const matcher = safe.initPattern(pattern, { canNegate: true }); const current = safe.Object_getOwnPropertyDescriptor(Element.prototype, 'innerHTML'); if ( current === undefined ) { return; } - const shouldPreventSet = a => { + const shouldPreventSet = (elem, a) => { if ( selector !== '' ) { - if ( typeof this.matches === 'function' === false ) { return false; } - if ( this.matches(selector) === false ) { return false; } + if ( typeof elem.matches !== 'function' ) { return false; } + if ( elem.matches(selector) === false ) { return false; } } return safe.testPattern(matcher, a); }; @@ -62,7 +62,7 @@ export function preventInnerHTML( : current.value; }, set: function(a) { - if ( shouldPreventSet(a) ) { + if ( shouldPreventSet(this, a) ) { safe.uboLog(logPrefix, 'Prevented'); } else if ( current.set ) { current.set.call(this, a); From 0fce659bb07d4d04f8f2c2da8a717f26263ad1b8 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Mon, 24 Mar 2025 08:15:09 -0400 Subject: [PATCH 0727/1099] Use `Object.hasOwn` instead of `Object.prototype.hasOwnProperty` Reference: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/hasOwn --- platform/common/vapi-background.js | 7 ++---- src/js/3p-filters.js | 8 ++----- src/js/advanced-settings.js | 2 +- src/js/assets.js | 5 +--- src/js/cachestorage.js | 12 ++++------ src/js/document-blocked.js | 2 +- src/js/dyna-rules.js | 2 +- src/js/dynamic-net-filtering.js | 2 +- src/js/hnswitches.js | 2 +- src/js/i18n.js | 2 +- src/js/logger-ui.js | 13 ++++------ src/js/messaging.js | 9 +++---- src/js/popup-fenix.js | 9 +++---- src/js/resources/run-at.js | 2 +- src/js/reverselookup.js | 2 +- src/js/s14e-serializer.js | 5 +--- src/js/start.js | 6 ++--- src/js/storage.js | 38 +++++++++++++----------------- src/js/text-encode.js | 2 +- src/js/ublock.js | 2 +- src/js/utils.js | 4 ++-- 21 files changed, 52 insertions(+), 84 deletions(-) diff --git a/platform/common/vapi-background.js b/platform/common/vapi-background.js index 5fcff01f87311..d292341ec3327 100644 --- a/platform/common/vapi-background.js +++ b/platform/common/vapi-background.js @@ -43,9 +43,6 @@ if ( vAPI.canWASM === false ) { vAPI.supportsUserStylesheets = vAPI.webextFlavor.soup.has('user_stylesheet'); -const hasOwnProperty = (o, p) => - Object.prototype.hasOwnProperty.call(o, p); - /******************************************************************************/ vAPI.app = { @@ -190,7 +187,7 @@ vAPI.browserSettings = (( ) => { set: function(details) { for ( const setting in details ) { - if ( hasOwnProperty(details, setting) === false ) { continue; } + if ( Object.hasOwn(details, setting) === false ) { continue; } switch ( setting ) { case 'prefetching': { const enabled = !!details[setting]; @@ -1220,7 +1217,7 @@ vAPI.Net = class { { const wrrt = browser.webRequest.ResourceType; for ( const typeKey in wrrt ) { - if ( hasOwnProperty(wrrt, typeKey) ) { + if ( Object.hasOwn(wrrt, typeKey) ) { this.validTypes.add(wrrt[typeKey]); } } diff --git a/src/js/3p-filters.js b/src/js/3p-filters.js index e551dcfd0f32e..fed3154212b2d 100644 --- a/src/js/3p-filters.js +++ b/src/js/3p-filters.js @@ -30,10 +30,6 @@ const obsoleteTemplateString = i18n$('3pExternalListObsolete'); const reValidExternalList = /^[a-z-]+:\/\/(?:\S+\/\S*|\/\S+)/m; const recentlyUpdated = 1 * 60 * 60 * 1000; // 1 hour -// https://eslint.org/docs/latest/rules/no-prototype-builtins -const hasOwnProperty = (o, p) => - Object.prototype.hasOwnProperty.call(o, p); - let listsetDetails = {}; /******************************************************************************/ @@ -246,7 +242,7 @@ const renderFilterLists = ( ) => { } for ( const [ listkey, listDetails ] of Object.entries(response.available) ) { let groupkey = listDetails.group2 || listDetails.group; - if ( hasOwnProperty(listTree, groupkey) === false ) { + if ( Object.hasOwn(listTree, groupkey) === false ) { groupkey = 'unknown'; } const groupDetails = listTree[groupkey]; @@ -603,7 +599,7 @@ const selectFilterLists = async ( ) => { const toRemove = []; for ( const liEntry of qsa$('#lists .listEntry[data-role="leaf"]') ) { const listkey = liEntry.dataset.key; - if ( hasOwnProperty(listsetDetails.available, listkey) === false ) { + if ( Object.hasOwn(listsetDetails.available, listkey) === false ) { continue; } const listDetails = listsetDetails.available[listkey]; diff --git a/src/js/advanced-settings.js b/src/js/advanced-settings.js index de7da56b25b69..d40ab76302293 100644 --- a/src/js/advanced-settings.js +++ b/src/js/advanced-settings.js @@ -88,7 +88,7 @@ const hashFromAdvancedSettings = function(raw) { const arrayFromObject = function(o) { const out = []; for ( const k in o ) { - if ( o.hasOwnProperty(k) === false ) { continue; } + if ( Object.hasOwn(o, k) === false ) { continue; } out.push([ k, `${o[k]}` ]); } return out; diff --git a/src/js/assets.js b/src/js/assets.js index ba7c4cf705dc4..a9d07cf0a25b2 100644 --- a/src/js/assets.js +++ b/src/js/assets.js @@ -47,9 +47,6 @@ let remoteServerFriendly = false; /******************************************************************************/ -const hasOwnProperty = (o, p) => - Object.prototype.hasOwnProperty.call(o, p); - const stringIsNotEmpty = s => typeof s === 'string' && s !== ''; const parseExpires = s => { @@ -735,7 +732,7 @@ async function assetCacheRead(assetKey, updateReadTime = false) { } if ( bin instanceof Object === false ) { return reportBack(''); } - if ( hasOwnProperty(bin, internalKey) === false ) { return reportBack(''); } + if ( Object.hasOwn(bin, internalKey) === false ) { return reportBack(''); } const entry = assetCacheRegistry[assetKey]; if ( entry === undefined ) { return reportBack(''); } diff --git a/src/js/cachestorage.js b/src/js/cachestorage.js index 97b8b6f053d10..91578f6674cdd 100644 --- a/src/js/cachestorage.js +++ b/src/js/cachestorage.js @@ -44,10 +44,6 @@ const keysFromGetArg = arg => { let fastCache = 'indexedDB'; -// https://eslint.org/docs/latest/rules/no-prototype-builtins -const hasOwnProperty = (o, p) => - Object.prototype.hasOwnProperty.call(o, p); - /******************************************************************************* * * Extension storage @@ -76,7 +72,7 @@ const cacheStorage = (( ) => { if ( found.length === wanted.length ) { return; } const missing = []; for ( const key of wanted ) { - if ( hasOwnProperty(outbin, key) ) { continue; } + if ( Object.hasOwn(outbin, key) ) { continue; } missing.push(key); } return missing; @@ -118,7 +114,7 @@ const cacheStorage = (( ) => { if ( argbin instanceof Object === false ) { return; } if ( Array.isArray(argbin) ) { return; } for ( const key of wanted ) { - if ( hasOwnProperty(argbin, key) === false ) { continue; } + if ( Object.hasOwn(argbin, key) === false ) { continue; } outbin[key] = argbin[key]; } }).then(( ) => { @@ -187,7 +183,7 @@ const cacheStorage = (( ) => { }, select(api) { - if ( hasOwnProperty(cacheAPIs, api) === false ) { return fastCache; } + if ( Object.hasOwn(cacheAPIs, api) === false ) { return fastCache; } fastCache = api; for ( const k of Object.keys(cacheAPIs) ) { if ( k === api ) { continue; } @@ -672,7 +668,7 @@ const idbStorage = (( ) => { } if ( argbin instanceof Object && Array.isArray(argbin) === false ) { for ( const key of keys ) { - if ( hasOwnProperty(outbin, key) ) { continue; } + if ( Object.hasOwn(outbin, key) ) { continue; } outbin[key] = argbin[key]; } } diff --git a/src/js/document-blocked.js b/src/js/document-blocked.js index 2d7f28a7ae113..aabcf2eb361b7 100644 --- a/src/js/document-blocked.js +++ b/src/js/document-blocked.js @@ -45,7 +45,7 @@ let details = {}; let lists; for ( const rawFilter in response ) { - if ( Object.prototype.hasOwnProperty.call(response, rawFilter) ) { + if ( Object.hasOwn(response, rawFilter) ) { lists = response[rawFilter]; break; } diff --git a/src/js/dyna-rules.js b/src/js/dyna-rules.js index 1bb1d097b0669..6d6e778420903 100644 --- a/src/js/dyna-rules.js +++ b/src/js/dyna-rules.js @@ -177,7 +177,7 @@ function rulesToDoc(clearHistory) { edit.startOperation(); for ( const key in thePanes ) { - if ( thePanes.hasOwnProperty(key) === false ) { continue; } + if ( Object.hasOwn(thePanes, key) === false ) { continue; } const doc = thePanes[key].doc; const rules = filterRules(key); if ( diff --git a/src/js/dynamic-net-filtering.js b/src/js/dynamic-net-filtering.js index 4bc53e1a7544b..f921d93c2d9de 100644 --- a/src/js/dynamic-net-filtering.js +++ b/src/js/dynamic-net-filtering.js @@ -30,7 +30,7 @@ import punycode from '../lib/punycode.js'; // Object.create(null) is used below to eliminate worries about unexpected // property names in prototype chain -- and this way we don't have to use -// hasOwnProperty() to avoid this. +// Object.hasOwn() to avoid this. const supportedDynamicTypes = Object.create(null); Object.assign(supportedDynamicTypes, { diff --git a/src/js/hnswitches.js b/src/js/hnswitches.js index 126b7e4a66528..4e531cf30f6ec 100644 --- a/src/js/hnswitches.js +++ b/src/js/hnswitches.js @@ -29,7 +29,7 @@ const decomposedSource = []; // Object.create(null) is used below to eliminate worries about unexpected // property names in prototype chain -- and this way we don't have to use -// hasOwnProperty() to avoid this. +// Object.hasOwn() to avoid this. const switchBitOffsets = Object.create(null); Object.assign(switchBitOffsets, { diff --git a/src/js/i18n.js b/src/js/i18n.js index 6ce3b5f96284c..2f6f7dc013fa0 100644 --- a/src/js/i18n.js +++ b/src/js/i18n.js @@ -173,7 +173,7 @@ if ( isBackgroundProcess !== true ) { } textout += textin.slice(0, match.index); let prop = match[0].slice(2, -2); - if ( Object.prototype.hasOwnProperty.call(dict, prop) ) { + if ( Object.hasOwn(dict, prop) ) { textout += dict[prop].replace(//g, '>'); } else { diff --git a/src/js/logger-ui.js b/src/js/logger-ui.js index 19270a2be26a8..4a62b92ad16ae 100644 --- a/src/js/logger-ui.js +++ b/src/js/logger-ui.js @@ -70,9 +70,6 @@ const tabIdFromAttribute = function(elem) { return isNaN(tabId) ? 0 : tabId; }; -const hasOwnProperty = (o, p) => - Object.prototype.hasOwnProperty.call(o, p); - const dispatchTabidChange = vAPI.defer.create(( ) => { document.dispatchEvent(new Event('tabIdChanged')); }); @@ -253,7 +250,7 @@ class LogEntry { this.voided = false; if ( details instanceof Object === false ) { return; } for ( const prop in this ) { - if ( hasOwnProperty(details, prop) === false ) { continue; } + if ( Object.hasOwn(details, prop) === false ) { continue; } this[prop] = details[prop]; } if ( details.aliasURL !== undefined ) { @@ -1224,7 +1221,7 @@ dom.on(document, 'keydown', ev => { const onColorsReady = function(response) { dom.cl.toggle(dom.body, 'dirty', response.dirty); for ( const url in response.colors ) { - if ( hasOwnProperty(response.colors, url) === false ) { continue; } + if ( Object.hasOwn(response.colors, url) === false ) { continue; } const colorEntry = response.colors[url]; const node = qs$(dialog, `.dynamic .entry .action[data-url="${url}"]`); if ( node === null ) { continue; } @@ -1291,7 +1288,7 @@ dom.on(document, 'keydown', ev => { dom.cl.toggle( qs$(dialog, '#createStaticFilter'), 'disabled', - hasOwnProperty(createdStaticFilters, value) || value === '' + Object.hasOwn(createdStaticFilters, value) || value === '' ); }; @@ -1332,7 +1329,7 @@ dom.on(document, 'keydown', ev => { const value = staticFilterNode().value .replace(/^((?:@@)?\/.+\/)(\$|$)/, '$1*$2'); // Avoid duplicates - if ( hasOwnProperty(createdStaticFilters, value) ) { return; } + if ( Object.hasOwn(createdStaticFilters, value) ) { return; } createdStaticFilters[value] = true; // https://github.com/uBlockOrigin/uBlock-issues/issues/1281#issuecomment-704217175 // TODO: @@ -2835,7 +2832,7 @@ const loggerStats = (( ) => { }; const setRadioButton = function(group, value) { - if ( hasOwnProperty(options, group) === false ) { return; } + if ( Object.hasOwn(options, group) === false ) { return; } const groupEl = qs$(dialog, `[data-radio="${group}"]`); const buttonEls = qsa$(groupEl, '[data-radio-item]'); for ( const buttonEl of buttonEls ) { diff --git a/src/js/messaging.js b/src/js/messaging.js index 668138c3b423c..d7c862d107ae9 100644 --- a/src/js/messaging.js +++ b/src/js/messaging.js @@ -58,9 +58,6 @@ import µb from './background.js'; /******************************************************************************/ -const hasOwnProperty = (o, p) => - Object.prototype.hasOwnProperty.call(o, p); - // https://github.com/uBlockOrigin/uBlock-issues/issues/710 // Listeners have a name and a "privileged" status. // The nameless default handler is always deemed "privileged". @@ -1095,7 +1092,7 @@ const restoreUserData = async function(request) { // Discard unknown setting or setting with default value. for ( const key in hiddenSettings ) { if ( - hasOwnProperty(µb.hiddenSettingsDefault, key) === false || + Object.hasOwn(µb.hiddenSettingsDefault, key) === false || hiddenSettings[key] === µb.hiddenSettingsDefault[key] ) { delete hiddenSettings[key]; @@ -1147,7 +1144,7 @@ const resetUserData = async function() { // Filter lists const prepListEntries = function(entries) { for ( const k in entries ) { - if ( hasOwnProperty(entries, k) === false ) { continue; } + if ( Object.hasOwn(entries, k) === false ) { continue; } const entry = entries[k]; if ( typeof entry.supportURL === 'string' && entry.supportURL !== '' ) { entry.supportName = hostnameFromURI(entry.supportURL); @@ -1335,7 +1332,7 @@ const getSupportData = async function() { let addedListset = {}; let removedListset = {}; for ( const listKey in lists ) { - if ( hasOwnProperty(lists, listKey) === false ) { continue; } + if ( Object.hasOwn(lists, listKey) === false ) { continue; } const list = lists[listKey]; if ( list.content !== 'filters' ) { continue; } const used = µb.selectedFilterLists.includes(listKey); diff --git a/src/js/popup-fenix.js b/src/js/popup-fenix.js index c4ffc7fd79ea9..298afe28f71a1 100644 --- a/src/js/popup-fenix.js +++ b/src/js/popup-fenix.js @@ -68,9 +68,6 @@ let cachedPopupHash = ''; const reCyrillicNonAmbiguous = /[\u0400-\u042b\u042d-\u042f\u0431\u0432\u0434\u0436-\u043d\u0442\u0444\u0446-\u0449\u044b-\u0454\u0457\u0459-\u0460\u0462-\u0474\u0476-\u04ba\u04bc\u04be-\u04ce\u04d0-\u0500\u0502-\u051a\u051c\u051e-\u052f]/; const reCyrillicAmbiguous = /[\u042c\u0430\u0433\u0435\u043e\u043f\u0440\u0441\u0443\u0445\u044a\u0455\u0456\u0458\u0461\u0475\u04bb\u04bd\u04cf\u0501\u051b\u051d]/; -const hasOwnProperty = (o, p) => - Object.prototype.hasOwnProperty.call(o, p); - /******************************************************************************/ const cachePopupData = function(data) { @@ -89,7 +86,7 @@ const cachePopupData = function(data) { return popupData; } for ( const hostname in hostnameDict ) { - if ( hasOwnProperty(hostnameDict, hostname) === false ) { continue; } + if ( Object.hasOwn(hostnameDict, hostname) === false ) { continue; } let domain = hostnameDict[hostname].domain; let prefix = hostname.slice(0, 0 - domain.length - 1); // Prefix with space char for 1st-party hostnames: this ensure these @@ -161,7 +158,7 @@ const formatNumber = function(count) { }); if ( intl.resolvedOptions instanceof Function && - hasOwnProperty(intl.resolvedOptions(), 'notation') + Object.hasOwn(intl.resolvedOptions(), 'notation') ) { intlNumberFormat = intl; } @@ -546,7 +543,7 @@ const renderPrivacyExposure = function() { if ( des === '*' || desHostnameDone.has(des) ) { continue; } const hnDetails = hostnameDict[des]; const { domain, counts } = hnDetails; - if ( hasOwnProperty(allDomains, domain) === false ) { + if ( Object.hasOwn(allDomains, domain) === false ) { allDomains[domain] = false; allDomainCount += 1; } diff --git a/src/js/resources/run-at.js b/src/js/resources/run-at.js index 545324dcf1160..35036d2bab5f6 100644 --- a/src/js/resources/run-at.js +++ b/src/js/resources/run-at.js @@ -53,7 +53,7 @@ export function runAt(fn, when) { const tokens = Array.isArray(state) ? state : [ state ]; for ( const token of tokens ) { const prop = `${token}`; - if ( targets.hasOwnProperty(prop) === false ) { continue; } + if ( Object.hasOwn(targets, prop) === false ) { continue; } return targets[prop]; } return 0; diff --git a/src/js/reverselookup.js b/src/js/reverselookup.js index 7a0bfb04c34e2..2d2709c1ecab0 100644 --- a/src/js/reverselookup.js +++ b/src/js/reverselookup.js @@ -93,7 +93,7 @@ const initWorker = function() { }; for ( const listKey in µb.availableFilterLists ) { - if ( Object.prototype.hasOwnProperty.call(µb.availableFilterLists, listKey) === false ) { + if ( Object.hasOwn(µb.availableFilterLists, listKey) === false ) { continue; } const entry = µb.availableFilterLists[listKey]; diff --git a/src/js/s14e-serializer.js b/src/js/s14e-serializer.js index db40388e9648a..c74a4f2e303e2 100644 --- a/src/js/s14e-serializer.js +++ b/src/js/s14e-serializer.js @@ -306,9 +306,6 @@ const shouldCompress = (s, options) => options.compressThreshold <= s.length ); -const hasOwnProperty = (o, p) => - Object.prototype.hasOwnProperty.call(o, p); - /******************************************************************************* * * A large Uint is always a positive integer (can be zero), assumed to be @@ -1155,7 +1152,7 @@ export const getConfig = ( ) => Object.assign({}, currentConfig); export const setConfig = config => { for ( const key in Object.keys(config) ) { - if ( hasOwnProperty(defaultConfig, key) === false ) { continue; } + if ( Object.hasOwn(defaultConfig, key) === false ) { continue; } const val = config[key]; if ( typeof val !== typeof defaultConfig[key] ) { continue; } if ( (validateConfig[key])(val) === false ) { continue; } diff --git a/src/js/start.js b/src/js/start.js index df7ce05287d49..bb6b1850e3a05 100644 --- a/src/js/start.js +++ b/src/js/start.js @@ -357,15 +357,15 @@ const onFirstFetchReady = (fetched, adminExtra) => { const toFetch = (from, fetched) => { for ( const k in from ) { - if ( from.hasOwnProperty(k) === false ) { continue; } + if ( Object.hasOwn(from, k) === false ) { continue; } fetched[k] = from[k]; } }; const fromFetch = (to, fetched) => { for ( const k in to ) { - if ( to.hasOwnProperty(k) === false ) { continue; } - if ( fetched.hasOwnProperty(k) === false ) { continue; } + if ( Object.hasOwn(to, k) === false ) { continue; } + if ( Object.hasOwn(fetched, k) === false ) { continue; } to[k] = fetched[k]; } }; diff --git a/src/js/storage.js b/src/js/storage.js index 8d1696515a2ec..7dc6e863806a3 100644 --- a/src/js/storage.js +++ b/src/js/storage.js @@ -48,12 +48,6 @@ import µb from './background.js'; /******************************************************************************/ -// https://eslint.org/docs/latest/rules/no-prototype-builtins -const hasOwnProperty = (o, p) => - Object.prototype.hasOwnProperty.call(o, p); - -/******************************************************************************/ - µb.getBytesInUse = async function() { const promises = []; let bytesInUse; @@ -186,7 +180,7 @@ const hasOwnProperty = (o, p) => for ( const entry of adminSettings ) { if ( entry.length < 1 ) { continue; } const name = entry[0]; - if ( hasOwnProperty(usDefault, name) === false ) { continue; } + if ( Object.hasOwn(usDefault, name) === false ) { continue; } const value = entry.length < 2 ? usDefault[name] : this.settingValueFromString(usDefault, name, entry[1]); @@ -215,8 +209,8 @@ const hasOwnProperty = (o, p) => const toRemove = []; for ( const key in this.userSettings ) { - if ( hasOwnProperty(this.userSettings, key) === false ) { continue; } - if ( hasOwnProperty(toSave, key) ) { continue; } + if ( Object.hasOwn(this.userSettings, key) === false ) { continue; } + if ( Object.hasOwn(toSave, key) ) { continue; } toRemove.push(key); } if ( toRemove.length !== 0 ) { @@ -253,7 +247,7 @@ const hasOwnProperty = (o, p) => for ( const entry of advancedSettings ) { if ( entry.length < 1 ) { continue; } const name = entry[0]; - if ( hasOwnProperty(hsDefault, name) === false ) { continue; } + if ( Object.hasOwn(hsDefault, name) === false ) { continue; } const value = entry.length < 2 ? hsDefault[name] : this.hiddenSettingValueFromString(name, entry[1]); @@ -287,8 +281,8 @@ const hasOwnProperty = (o, p) => } for ( const key in hsDefault ) { - if ( hasOwnProperty(hsDefault, key) === false ) { continue; } - if ( hasOwnProperty(hsAdmin, name) ) { continue; } + if ( Object.hasOwn(hsDefault, key) === false ) { continue; } + if ( Object.hasOwn(hsAdmin, name) ) { continue; } if ( typeof hs[key] !== typeof hsDefault[key] ) { continue; } this.hiddenSettings[key] = hs[key]; } @@ -334,8 +328,8 @@ onBroadcast(msg => { const matches = /^\s*(\S+)\s+(.+)$/.exec(line); if ( matches === null || matches.length !== 3 ) { continue; } const name = matches[1]; - if ( hasOwnProperty(out, name) === false ) { continue; } - if ( hasOwnProperty(this.hiddenSettingsAdmin, name) ) { continue; } + if ( Object.hasOwn(out, name) === false ) { continue; } + if ( Object.hasOwn(this.hiddenSettingsAdmin, name) ) { continue; } const value = this.hiddenSettingValueFromString(name, matches[2]); if ( value !== undefined ) { out[name] = value; @@ -347,7 +341,7 @@ onBroadcast(msg => { µb.hiddenSettingValueFromString = function(name, value) { if ( typeof name !== 'string' || typeof value !== 'string' ) { return; } const hsDefault = this.hiddenSettingsDefault; - if ( hasOwnProperty(hsDefault, name) === false ) { return; } + if ( Object.hasOwn(hsDefault, name) === false ) { return; } let r; switch ( typeof hsDefault[name] ) { case 'boolean': @@ -688,7 +682,7 @@ onBroadcast(msg => { µb.autoSelectRegionalFilterLists = function(lists) { const selectedListKeys = [ this.userFiltersPath ]; for ( const key in lists ) { - if ( hasOwnProperty(lists, key) === false ) { continue; } + if ( Object.hasOwn(lists, key) === false ) { continue; } const list = lists[key]; if ( list.content !== 'filters' ) { continue; } if ( list.off !== true ) { @@ -941,7 +935,7 @@ onBroadcast(msg => { let acceptedCount = snfe.acceptedCount + sxfe.acceptedCount; let discardedCount = snfe.discardedCount + sxfe.discardedCount; µb.applyCompiledFilters(compiled, assetKey === µb.userFiltersPath); - if ( hasOwnProperty(µb.availableFilterLists, assetKey) ) { + if ( Object.hasOwn(µb.availableFilterLists, assetKey) ) { const entry = µb.availableFilterLists[assetKey]; entry.entryCount = snfe.acceptedCount + sxfe.acceptedCount - acceptedCount; @@ -977,7 +971,7 @@ onBroadcast(msg => { // content. const toLoad = []; for ( const assetKey in lists ) { - if ( hasOwnProperty(lists, assetKey) === false ) { continue; } + if ( Object.hasOwn(lists, assetKey) === false ) { continue; } if ( lists[assetKey].off ) { continue; } toLoad.push( µb.getCompiledFilterList(assetKey).then(details => { @@ -1428,8 +1422,8 @@ onBroadcast(msg => { const µbus = this.userSettings; const adminus = data.userSettings; for ( const name in µbus ) { - if ( hasOwnProperty(µbus, name) === false ) { continue; } - if ( hasOwnProperty(adminus, name) === false ) { continue; } + if ( Object.hasOwn(µbus, name) === false ) { continue; } + if ( Object.hasOwn(adminus, name) === false ) { continue; } bin[name] = adminus[name]; binNotEmpty = true; } @@ -1600,7 +1594,7 @@ onBroadcast(msg => { if ( topic === 'before-asset-updated' ) { if ( details.type === 'filters' ) { if ( - hasOwnProperty(this.availableFilterLists, details.assetKey) === false || + Object.hasOwn(this.availableFilterLists, details.assetKey) === false || this.selectedFilterLists.indexOf(details.assetKey) === -1 || this.badLists.get(details.assetKey) ) { @@ -1615,7 +1609,7 @@ onBroadcast(msg => { // Skip selfie-related content. if ( details.assetKey.startsWith('selfie/') ) { return; } const cached = typeof details.content === 'string' && details.content !== ''; - if ( hasOwnProperty(this.availableFilterLists, details.assetKey) ) { + if ( Object.hasOwn(this.availableFilterLists, details.assetKey) ) { if ( cached ) { if ( this.selectedFilterLists.indexOf(details.assetKey) !== -1 ) { this.extractFilterListMetadata( diff --git a/src/js/text-encode.js b/src/js/text-encode.js index 8e5ad81ae9290..b1e850b29c7f6 100644 --- a/src/js/text-encode.js +++ b/src/js/text-encode.js @@ -251,7 +251,7 @@ const textEncode = (( ) => { return { encode: function(charset, buf) { - return encoders.hasOwnProperty(charset) ? + return Object.hasOwn(encoders, charset) ? encoders[charset](buf) : buf; }, diff --git a/src/js/ublock.js b/src/js/ublock.js index 0ac05d08d8d04..b3a453c818693 100644 --- a/src/js/ublock.js +++ b/src/js/ublock.js @@ -333,7 +333,7 @@ const matchBucket = function(url, hostname, bucket, start) { } // Change -- but only if the user setting actually exists. - const mustSave = us.hasOwnProperty(name) && value !== us[name]; + const mustSave = Object.hasOwn(us, name) && value !== us[name]; if ( mustSave ) { us[name] = value; } diff --git a/src/js/utils.js b/src/js/utils.js index 8b12244fdfe2b..da7e1599581da 100644 --- a/src/js/utils.js +++ b/src/js/utils.js @@ -93,7 +93,7 @@ import µb from './background.js'; µb.getModifiedSettings = function(edit, orig = {}) { const out = {}; for ( const prop in edit ) { - if ( orig.hasOwnProperty(prop) && edit[prop] !== orig[prop] ) { + if ( Object.hasOwn(orig, prop) && edit[prop] !== orig[prop] ) { out[prop] = edit[prop]; } } @@ -102,7 +102,7 @@ import µb from './background.js'; µb.settingValueFromString = function(orig, name, s) { if ( typeof name !== 'string' || typeof s !== 'string' ) { return; } - if ( orig.hasOwnProperty(name) === false ) { return; } + if ( Object.hasOwn(orig, name) === false ) { return; } let r; switch ( typeof orig[name] ) { case 'boolean': From 484ae6852894c9cc9fc7b20ffd1b66e5de7c5ff1 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Mon, 24 Mar 2025 08:17:38 -0400 Subject: [PATCH 0728/1099] Import translation work from https://crowdin.com/project/ublock --- platform/mv3/extension/_locales/fy/messages.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/platform/mv3/extension/_locales/fy/messages.json b/platform/mv3/extension/_locales/fy/messages.json index 77ce2b2368e46..56b3a3a203f71 100644 --- a/platform/mv3/extension/_locales/fy/messages.json +++ b/platform/mv3/extension/_locales/fy/messages.json @@ -280,11 +280,11 @@ "description": "A button to navigate to the blocked page" }, "zapperTipEnter": { - "message": "Enter element zapper mode", + "message": "Elemint­wisker­modus iepenje", "description": "Tooltip for the button used to enter zapper mode" }, "zapperTipQuit": { - "message": "Exit element zapper mode", + "message": "Elemint­wisker­modus slute", "description": "Tooltip for the button used to exit zapper mode" } } From d18311213dd419a9000a5f815cae776fa101ffe3 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Mon, 24 Mar 2025 12:16:10 -0400 Subject: [PATCH 0729/1099] Improve `jsonl-prune-...` scriptlets --- src/js/resources/jsonl-prune.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/js/resources/jsonl-prune.js b/src/js/resources/jsonl-prune.js index 16d540d7783a2..d6268233246a7 100644 --- a/src/js/resources/jsonl-prune.js +++ b/src/js/resources/jsonl-prune.js @@ -54,7 +54,7 @@ function jsonlPruneFn( linesAfter.push(lineBefore); continue; } - linesAfter.push(safe.JSON_stringifyFn(objAfter)); + linesAfter.push(safe.JSON_stringify(objAfter).replace(/\//g, '\\/')); } return linesAfter.join('\n'); } From 3520c2fd9e1b3db0d5a4bcf16d30e6dfd23e6dbe Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Mon, 24 Mar 2025 12:23:59 -0400 Subject: [PATCH 0730/1099] New revision for dev build --- dist/version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dist/version b/dist/version index 1d280851adba6..6b7717df3af83 100644 --- a/dist/version +++ b/dist/version @@ -1 +1 @@ -1.63.3.1 +1.63.3.2 From 620678fc1790d18c0007a1221c647574a7530cc2 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Mon, 24 Mar 2025 12:32:08 -0400 Subject: [PATCH 0731/1099] Make Firefox dev build auto-update --- dist/firefox/updates.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dist/firefox/updates.json b/dist/firefox/updates.json index d74fcf57e3e3a..117490dc224d1 100644 --- a/dist/firefox/updates.json +++ b/dist/firefox/updates.json @@ -3,9 +3,9 @@ "uBlock0@raymondhill.net": { "updates": [ { - "version": "1.63.3.1", + "version": "1.63.3.2", "browser_specific_settings": { "gecko": { "strict_min_version": "92.0" } }, - "update_link": "https://github.com/gorhill/uBlock/releases/download/1.63.3b1/uBlock0_1.63.3b1.firefox.signed.xpi" + "update_link": "https://github.com/gorhill/uBlock/releases/download/1.63.3b2/uBlock0_1.63.3b2.firefox.signed.xpi" } ] } From e100f9e85c5c18981f7d493418c09bc64460df47 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Tue, 25 Mar 2025 08:20:43 -0400 Subject: [PATCH 0732/1099] Import translation work from https://crowdin.com/project/ublock --- platform/mv3/extension/_locales/eu/messages.json | 16 ++++++++-------- platform/mv3/extension/_locales/hu/messages.json | 4 ++-- src/_locales/eu/messages.json | 2 +- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/platform/mv3/extension/_locales/eu/messages.json b/platform/mv3/extension/_locales/eu/messages.json index 00e67b773df5a..6183330b5ce62 100644 --- a/platform/mv3/extension/_locales/eu/messages.json +++ b/platform/mv3/extension/_locales/eu/messages.json @@ -244,7 +244,7 @@ "description": "Placeholder for the input field used to find lists" }, "strictblockTitle": { - "message": "Page blocked", + "message": "Blokeatutako orrialdea", "description": "Webpage title for the strict-blocked page" }, "strictblockSentence1": { @@ -260,31 +260,31 @@ "description": "Text warning about an incoming redirect" }, "strictblockNoParamsPrompt": { - "message": "without parameters", + "message": "parametrorik gabe", "description": "Label to be used for the parameter-less URL" }, "strictblockBack": { - "message": "Go back", + "message": "Joan atzera", "description": "A button to go back to the previous webpage" }, "strictblockClose": { - "message": "Close this window", + "message": "Itxi leiho hau", "description": "A button to close the current tab" }, "strictblockDontWarn": { - "message": "Don't warn me again about this site", + "message": "Ez esan ezer berriz ere orrialde honi buruz", "description": "Label for checkbox in document-blocked page" }, "strictblockProceed": { - "message": "Proceed", + "message": "Aurrera", "description": "A button to navigate to the blocked page" }, "zapperTipEnter": { - "message": "Enter element zapper mode", + "message": "Sartu elementua zapper moduan", "description": "Tooltip for the button used to enter zapper mode" }, "zapperTipQuit": { - "message": "Exit element zapper mode", + "message": "Irten elementua zapper moduan", "description": "Tooltip for the button used to exit zapper mode" } } diff --git a/platform/mv3/extension/_locales/hu/messages.json b/platform/mv3/extension/_locales/hu/messages.json index f0cbe38ee4915..5b18c0a4f2482 100644 --- a/platform/mv3/extension/_locales/hu/messages.json +++ b/platform/mv3/extension/_locales/hu/messages.json @@ -280,11 +280,11 @@ "description": "A button to navigate to the blocked page" }, "zapperTipEnter": { - "message": "Enter element zapper mode", + "message": "Belépés az elemeltávolító módba", "description": "Tooltip for the button used to enter zapper mode" }, "zapperTipQuit": { - "message": "Exit element zapper mode", + "message": "Kilépés az elemeltávolító módból", "description": "Tooltip for the button used to exit zapper mode" } } diff --git a/src/_locales/eu/messages.json b/src/_locales/eu/messages.json index c99740bb9436d..f2fda34ea0ee3 100644 --- a/src/_locales/eu/messages.json +++ b/src/_locales/eu/messages.json @@ -1192,7 +1192,7 @@ "description": "Button text to navigate to the blocked page" }, "docblockedRedirectPrompt": { - "message": "The blocked page wants to redirect to another site. If you choose to proceed, you will navigate directly to: {{url}}", + "message": "Blokeatutako orrialdeak beste helbide batera berbideratu nahi zaitu. Jarraitu nahi baduzu, zuzenean helbide honetara joango zara: {{url}}", "description": "Text warning about an incoming redirect" }, "cloudPush": { From d0c028386a6d8ad23175c4255c397b86894bbe06 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Tue, 25 Mar 2025 08:23:27 -0400 Subject: [PATCH 0733/1099] Import translation work from https://crowdin.com/project/ublock --- platform/mv3/extension/_locales/ar/messages.json | 4 ++++ platform/mv3/extension/_locales/az/messages.json | 4 ++++ platform/mv3/extension/_locales/be/messages.json | 4 ++++ platform/mv3/extension/_locales/bg/messages.json | 4 ++++ platform/mv3/extension/_locales/bn/messages.json | 4 ++++ platform/mv3/extension/_locales/br_FR/messages.json | 4 ++++ platform/mv3/extension/_locales/bs/messages.json | 4 ++++ platform/mv3/extension/_locales/ca/messages.json | 4 ++++ platform/mv3/extension/_locales/cs/messages.json | 4 ++++ platform/mv3/extension/_locales/cv/messages.json | 4 ++++ platform/mv3/extension/_locales/cy/messages.json | 4 ++++ platform/mv3/extension/_locales/da/messages.json | 4 ++++ platform/mv3/extension/_locales/de/messages.json | 4 ++++ platform/mv3/extension/_locales/el/messages.json | 4 ++++ platform/mv3/extension/_locales/en/messages.json | 4 ++++ platform/mv3/extension/_locales/en_GB/messages.json | 4 ++++ platform/mv3/extension/_locales/eo/messages.json | 4 ++++ platform/mv3/extension/_locales/es/messages.json | 4 ++++ platform/mv3/extension/_locales/et/messages.json | 4 ++++ platform/mv3/extension/_locales/eu/messages.json | 4 ++++ platform/mv3/extension/_locales/fa/messages.json | 4 ++++ platform/mv3/extension/_locales/fi/messages.json | 4 ++++ platform/mv3/extension/_locales/fil/messages.json | 4 ++++ platform/mv3/extension/_locales/fr/messages.json | 4 ++++ platform/mv3/extension/_locales/fy/messages.json | 4 ++++ platform/mv3/extension/_locales/gl/messages.json | 4 ++++ platform/mv3/extension/_locales/gu/messages.json | 4 ++++ platform/mv3/extension/_locales/he/messages.json | 4 ++++ platform/mv3/extension/_locales/hi/messages.json | 4 ++++ platform/mv3/extension/_locales/hr/messages.json | 4 ++++ platform/mv3/extension/_locales/hu/messages.json | 4 ++++ platform/mv3/extension/_locales/hy/messages.json | 4 ++++ platform/mv3/extension/_locales/id/messages.json | 4 ++++ platform/mv3/extension/_locales/it/messages.json | 4 ++++ platform/mv3/extension/_locales/ja/messages.json | 4 ++++ platform/mv3/extension/_locales/ka/messages.json | 4 ++++ platform/mv3/extension/_locales/kk/messages.json | 4 ++++ platform/mv3/extension/_locales/kn/messages.json | 4 ++++ platform/mv3/extension/_locales/ko/messages.json | 4 ++++ platform/mv3/extension/_locales/lt/messages.json | 4 ++++ platform/mv3/extension/_locales/lv/messages.json | 4 ++++ platform/mv3/extension/_locales/mk/messages.json | 4 ++++ platform/mv3/extension/_locales/ml/messages.json | 4 ++++ platform/mv3/extension/_locales/mr/messages.json | 4 ++++ platform/mv3/extension/_locales/ms/messages.json | 4 ++++ platform/mv3/extension/_locales/nb/messages.json | 4 ++++ platform/mv3/extension/_locales/nl/messages.json | 4 ++++ platform/mv3/extension/_locales/oc/messages.json | 4 ++++ platform/mv3/extension/_locales/pa/messages.json | 4 ++++ platform/mv3/extension/_locales/pl/messages.json | 4 ++++ platform/mv3/extension/_locales/pt_BR/messages.json | 4 ++++ platform/mv3/extension/_locales/pt_PT/messages.json | 4 ++++ platform/mv3/extension/_locales/ro/messages.json | 4 ++++ platform/mv3/extension/_locales/ru/messages.json | 4 ++++ platform/mv3/extension/_locales/si/messages.json | 4 ++++ platform/mv3/extension/_locales/sk/messages.json | 4 ++++ platform/mv3/extension/_locales/sl/messages.json | 4 ++++ platform/mv3/extension/_locales/so/messages.json | 4 ++++ platform/mv3/extension/_locales/sq/messages.json | 4 ++++ platform/mv3/extension/_locales/sr/messages.json | 4 ++++ platform/mv3/extension/_locales/sv/messages.json | 4 ++++ platform/mv3/extension/_locales/sw/messages.json | 4 ++++ platform/mv3/extension/_locales/ta/messages.json | 4 ++++ platform/mv3/extension/_locales/te/messages.json | 4 ++++ platform/mv3/extension/_locales/th/messages.json | 4 ++++ platform/mv3/extension/_locales/tr/messages.json | 4 ++++ platform/mv3/extension/_locales/uk/messages.json | 4 ++++ platform/mv3/extension/_locales/ur/messages.json | 4 ++++ platform/mv3/extension/_locales/vi/messages.json | 4 ++++ platform/mv3/extension/_locales/zh_CN/messages.json | 4 ++++ platform/mv3/extension/_locales/zh_TW/messages.json | 4 ++++ 71 files changed, 284 insertions(+) diff --git a/platform/mv3/extension/_locales/ar/messages.json b/platform/mv3/extension/_locales/ar/messages.json index 36b7b67475145..de7c2b2041a17 100644 --- a/platform/mv3/extension/_locales/ar/messages.json +++ b/platform/mv3/extension/_locales/ar/messages.json @@ -111,6 +111,10 @@ "message": "الإبلاغ عن مشكلات الفلترة الخاصة بمواقع الويب المحددة إلىuBlockOrigin/uAssetsمتتبع المشكلةيتطلب حساب GitHub", "description": "First paragraph of 'Filter issues' section in Support pane" }, + "supportS5H": { + "message": "Troubleshooting information", + "description": "Label of 'Troubleshooting information' section in 'Report a filter issue' page" + }, "supportS6P1S1": { "message": "لتجنب تحميل المتطوعين بتقارير مكررة، يرجى التأكد من أن المشكلة لم يتم الإبلاغ عنها بالفعل.", "description": "A paragraph in the filter issue reporter section" diff --git a/platform/mv3/extension/_locales/az/messages.json b/platform/mv3/extension/_locales/az/messages.json index 626fec5b9edc7..83af2753dc3cc 100644 --- a/platform/mv3/extension/_locales/az/messages.json +++ b/platform/mv3/extension/_locales/az/messages.json @@ -111,6 +111,10 @@ "message": "Report filter issues with specific websites to the uBlockOrigin/uAssets issue tracker. Requires a GitHub account.", "description": "First paragraph of 'Filter issues' section in Support pane" }, + "supportS5H": { + "message": "Troubleshooting information", + "description": "Label of 'Troubleshooting information' section in 'Report a filter issue' page" + }, "supportS6P1S1": { "message": "To avoid burdening volunteers with duplicate reports, please verify that the issue has not already been reported. Note: clicking the button will cause the page's origin to be sent to GitHub.", "description": "A paragraph in the filter issue reporter section" diff --git a/platform/mv3/extension/_locales/be/messages.json b/platform/mv3/extension/_locales/be/messages.json index 7dff1f259f107..a0c4b891f7376 100644 --- a/platform/mv3/extension/_locales/be/messages.json +++ b/platform/mv3/extension/_locales/be/messages.json @@ -111,6 +111,10 @@ "message": "Паведамляйце пра праблемы з фільтрамі, датычныя канкрэтных вэб-сайтаў, праз трэкер праблемuBlockOrigin/uAssets . Патрэбны ўліковы запіс GitHub.", "description": "First paragraph of 'Filter issues' section in Support pane" }, + "supportS5H": { + "message": "Troubleshooting information", + "description": "Label of 'Troubleshooting information' section in 'Report a filter issue' page" + }, "supportS6P1S1": { "message": "Каб не абцяжарваць добраахвотнікаў дубляванымі справаздачамі, калі ласка, пераканайцеся, што пра гэтую праблему не паведамлялі раней.", "description": "A paragraph in the filter issue reporter section" diff --git a/platform/mv3/extension/_locales/bg/messages.json b/platform/mv3/extension/_locales/bg/messages.json index 0ade0d081a037..8301ba3084fc9 100644 --- a/platform/mv3/extension/_locales/bg/messages.json +++ b/platform/mv3/extension/_locales/bg/messages.json @@ -111,6 +111,10 @@ "message": "Докладвайте за проблеми с филтрирането на конкретни уебсайтове в uBlockOrigin/uAssets за проследяване на проблеми. Изисква се акаунт в GitHub.", "description": "First paragraph of 'Filter issues' section in Support pane" }, + "supportS5H": { + "message": "Troubleshooting information", + "description": "Label of 'Troubleshooting information' section in 'Report a filter issue' page" + }, "supportS6P1S1": { "message": "За да се избегне натоварването на доброволците с дублиращи се доклади, моля, проверете дали проблемът вече не е докладван.", "description": "A paragraph in the filter issue reporter section" diff --git a/platform/mv3/extension/_locales/bn/messages.json b/platform/mv3/extension/_locales/bn/messages.json index 7d87026243c84..58bb0cbfc7b83 100644 --- a/platform/mv3/extension/_locales/bn/messages.json +++ b/platform/mv3/extension/_locales/bn/messages.json @@ -111,6 +111,10 @@ "message": "নির্দিষ্ট ছাঁকনি বিষয়ক সমস্যা এখানে জানাও uBlockOrigin/uAssets সমস্যা ট্র্যাকার. গিটহাব অ্যাকাউন্ট প্রয়োজন।", "description": "First paragraph of 'Filter issues' section in Support pane" }, + "supportS5H": { + "message": "Troubleshooting information", + "description": "Label of 'Troubleshooting information' section in 'Report a filter issue' page" + }, "supportS6P1S1": { "message": "একই প্রতিবেদন দুইবার এড়াতে ও স্বেচ্ছাসেবকদের বোঝা কমাতে, অনুগ্রহ করে যাচাই করো যে সমস্যাটি ইতিমধ্যে জানানো হয়নি।", "description": "A paragraph in the filter issue reporter section" diff --git a/platform/mv3/extension/_locales/br_FR/messages.json b/platform/mv3/extension/_locales/br_FR/messages.json index 43f4696c545d0..bcfebb11d9717 100644 --- a/platform/mv3/extension/_locales/br_FR/messages.json +++ b/platform/mv3/extension/_locales/br_FR/messages.json @@ -111,6 +111,10 @@ "message": "Report filter issues with specific websites to the uBlockOrigin/uAssets issue tracker. Requires a GitHub account.", "description": "First paragraph of 'Filter issues' section in Support pane" }, + "supportS5H": { + "message": "Troubleshooting information", + "description": "Label of 'Troubleshooting information' section in 'Report a filter issue' page" + }, "supportS6P1S1": { "message": "Evit nompas sammañ ar genlabourerien a-youl vat gant meur a zanevell heñvel, gwiriit ma n'eo ket bet danevellet ho kudenn en ar-raok mar plij. Notenn: ma klikit ar bouton e vo kaset anv herberc'hier ar bajenn da c'h-GitHub.", "description": "A paragraph in the filter issue reporter section" diff --git a/platform/mv3/extension/_locales/bs/messages.json b/platform/mv3/extension/_locales/bs/messages.json index 00f06f12d4f64..8f1c60fb52490 100644 --- a/platform/mv3/extension/_locales/bs/messages.json +++ b/platform/mv3/extension/_locales/bs/messages.json @@ -111,6 +111,10 @@ "message": "Report filter issues with specific websites to the uBlockOrigin/uAssets issue tracker. Requires a GitHub account.", "description": "First paragraph of 'Filter issues' section in Support pane" }, + "supportS5H": { + "message": "Troubleshooting information", + "description": "Label of 'Troubleshooting information' section in 'Report a filter issue' page" + }, "supportS6P1S1": { "message": "To avoid burdening volunteers with duplicate reports, please verify that the issue has not already been reported. Note: clicking the button will cause the page's origin to be sent to GitHub.", "description": "A paragraph in the filter issue reporter section" diff --git a/platform/mv3/extension/_locales/ca/messages.json b/platform/mv3/extension/_locales/ca/messages.json index 0a0e3423461f5..1f3ef5f418d0b 100644 --- a/platform/mv3/extension/_locales/ca/messages.json +++ b/platform/mv3/extension/_locales/ca/messages.json @@ -111,6 +111,10 @@ "message": "Informeu d'un problema en llocs web específics mitjançant el uBlockOrigin/uAssets seguiment d'errors. Cal un compte al GitHub.", "description": "First paragraph of 'Filter issues' section in Support pane" }, + "supportS5H": { + "message": "Troubleshooting information", + "description": "Label of 'Troubleshooting information' section in 'Report a filter issue' page" + }, "supportS6P1S1": { "message": "Per a evitar la sobrecàrrega del nostre voluntariat amb informes duplicats, verifiqueu abans que el problema encara no s'ha notificat. Nota: En fer clic, enviareu la pàgina causant al nostre GitHub.", "description": "A paragraph in the filter issue reporter section" diff --git a/platform/mv3/extension/_locales/cs/messages.json b/platform/mv3/extension/_locales/cs/messages.json index aa21a94475faa..e2697983ebd73 100644 --- a/platform/mv3/extension/_locales/cs/messages.json +++ b/platform/mv3/extension/_locales/cs/messages.json @@ -111,6 +111,10 @@ "message": "Nahlaste problémy s filtrem u učitých webových stránek do sledovače problémů uBlockOrigin/uAssets. Vyžaduje účet GitHub.", "description": "First paragraph of 'Filter issues' section in Support pane" }, + "supportS5H": { + "message": "Troubleshooting information", + "description": "Label of 'Troubleshooting information' section in 'Report a filter issue' page" + }, "supportS6P1S1": { "message": "Abyste dobrovolníky nezatěžovali duplicitními hlášeními, ověřte si, zda již problém nebyl nahlášen.", "description": "A paragraph in the filter issue reporter section" diff --git a/platform/mv3/extension/_locales/cv/messages.json b/platform/mv3/extension/_locales/cv/messages.json index 4021b8b2f4cdf..ff00c75cff2cc 100644 --- a/platform/mv3/extension/_locales/cv/messages.json +++ b/platform/mv3/extension/_locales/cv/messages.json @@ -111,6 +111,10 @@ "message": "Report filter issues with specific websites to the uBlockOrigin/uAssets issue tracker. Requires a GitHub account.", "description": "First paragraph of 'Filter issues' section in Support pane" }, + "supportS5H": { + "message": "Troubleshooting information", + "description": "Label of 'Troubleshooting information' section in 'Report a filter issue' page" + }, "supportS6P1S1": { "message": "To avoid burdening volunteers with duplicate reports, please verify that the issue has not already been reported. Note: clicking the button will cause the page's origin to be sent to GitHub.", "description": "A paragraph in the filter issue reporter section" diff --git a/platform/mv3/extension/_locales/cy/messages.json b/platform/mv3/extension/_locales/cy/messages.json index 645649f6562e5..3a8b27662057f 100644 --- a/platform/mv3/extension/_locales/cy/messages.json +++ b/platform/mv3/extension/_locales/cy/messages.json @@ -111,6 +111,10 @@ "message": "Report filter issues with specific websites to the uBlockOrigin/uAssets issue tracker. Requires a GitHub account.", "description": "First paragraph of 'Filter issues' section in Support pane" }, + "supportS5H": { + "message": "Troubleshooting information", + "description": "Label of 'Troubleshooting information' section in 'Report a filter issue' page" + }, "supportS6P1S1": { "message": "To avoid burdening volunteers with duplicate reports, please verify that the issue has not already been reported. Note: clicking the button will cause the page's origin to be sent to GitHub.", "description": "A paragraph in the filter issue reporter section" diff --git a/platform/mv3/extension/_locales/da/messages.json b/platform/mv3/extension/_locales/da/messages.json index e0ce22a0849c7..21382d36e318c 100644 --- a/platform/mv3/extension/_locales/da/messages.json +++ b/platform/mv3/extension/_locales/da/messages.json @@ -111,6 +111,10 @@ "message": "Anmeld filterproblemer med bestemte websteder til uBlockOrigin/uAssets-problemsporingen. Kræver en GitHub-konto.", "description": "First paragraph of 'Filter issues' section in Support pane" }, + "supportS5H": { + "message": "Troubleshooting information", + "description": "Label of 'Troubleshooting information' section in 'Report a filter issue' page" + }, "supportS6P1S1": { "message": "For at undgå at bebyrde frivillige med dubletanmeldelser, så tjek venligst, at problematikken ikke allerede er anmeldt. Bemærk: Ved at klikke på knappen, sendes sidens oprindelse til GitHub.", "description": "A paragraph in the filter issue reporter section" diff --git a/platform/mv3/extension/_locales/de/messages.json b/platform/mv3/extension/_locales/de/messages.json index 30419df3d2ab7..64d64f36fff65 100644 --- a/platform/mv3/extension/_locales/de/messages.json +++ b/platform/mv3/extension/_locales/de/messages.json @@ -111,6 +111,10 @@ "message": "Bitte melden Sie Filterprobleme mit bestimmten Websites an den uBlockOrigin/uAssets Issue Tracker. Erfordert ein GitHub-Konto.", "description": "First paragraph of 'Filter issues' section in Support pane" }, + "supportS5H": { + "message": "Troubleshooting information", + "description": "Label of 'Troubleshooting information' section in 'Report a filter issue' page" + }, "supportS6P1S1": { "message": "Um die Freiwilligen nicht mit doppelten Meldungen zu überlasten, vergewissern Sie sich bitte, dass das Problem noch nicht gemeldet wurde. Hinweis: Das Anklicken der Schaltfläche übermittelt den Ursprung der Seite an GitHub.", "description": "A paragraph in the filter issue reporter section" diff --git a/platform/mv3/extension/_locales/el/messages.json b/platform/mv3/extension/_locales/el/messages.json index 5a94f2ffea253..a662270516eae 100644 --- a/platform/mv3/extension/_locales/el/messages.json +++ b/platform/mv3/extension/_locales/el/messages.json @@ -111,6 +111,10 @@ "message": "Αναφέρετε προβλήμα φίλτρου για συγκεκριμένους ιστοτόπους στο εργαλείο παρακολούθησης ζητημάτων του uBlockOrigin/uAssets. Απαιτείται λογαριασμός GitHub.", "description": "First paragraph of 'Filter issues' section in Support pane" }, + "supportS5H": { + "message": "Troubleshooting information", + "description": "Label of 'Troubleshooting information' section in 'Report a filter issue' page" + }, "supportS6P1S1": { "message": "Για να μην επιβαρυνθούν οι εθελοντών με διπλές αναφορές, βεβαιωθείτε ότι το ζήτημα δεν έχει ήδη αναφερθεί.Σημείωση: Με το πάτημα του κουμπιού, θα σταλεί η σελίδα προέλευσης στο GitHub.", "description": "A paragraph in the filter issue reporter section" diff --git a/platform/mv3/extension/_locales/en/messages.json b/platform/mv3/extension/_locales/en/messages.json index 741e6eb649309..d3ef6a391209e 100644 --- a/platform/mv3/extension/_locales/en/messages.json +++ b/platform/mv3/extension/_locales/en/messages.json @@ -111,6 +111,10 @@ "message": "Report filter issues with specific websites to the uBlockOrigin/uAssets issue tracker. Requires a GitHub account.", "description": "First paragraph of 'Filter issues' section in Support pane" }, + "supportS5H": { + "message": "Troubleshooting information", + "description": "Label of 'Troubleshooting information' section in 'Report a filter issue' page" + }, "supportS6P1S1": { "message": "To avoid burdening volunteers with duplicate reports, please verify that the issue has not already been reported. Note: clicking the button will cause the page's origin to be sent to GitHub.", "description": "A paragraph in the filter issue reporter section" diff --git a/platform/mv3/extension/_locales/en_GB/messages.json b/platform/mv3/extension/_locales/en_GB/messages.json index 02b3dcff66df7..2a7bd0548dd6b 100644 --- a/platform/mv3/extension/_locales/en_GB/messages.json +++ b/platform/mv3/extension/_locales/en_GB/messages.json @@ -111,6 +111,10 @@ "message": "Report filter issues with specific websites to the uBlockOrigin/uAssets issue tracker. Requires a GitHub account.", "description": "First paragraph of 'Filter issues' section in Support pane" }, + "supportS5H": { + "message": "Troubleshooting information", + "description": "Label of 'Troubleshooting information' section in 'Report a filter issue' page" + }, "supportS6P1S1": { "message": "To avoid burdening volunteers with duplicate reports, please verify that the issue has not already been reported. Note: clicking the button will cause the page's origin to be sent to GitHub.", "description": "A paragraph in the filter issue reporter section" diff --git a/platform/mv3/extension/_locales/eo/messages.json b/platform/mv3/extension/_locales/eo/messages.json index 8c2c3e7ce4291..b038d08a9cc9c 100644 --- a/platform/mv3/extension/_locales/eo/messages.json +++ b/platform/mv3/extension/_locales/eo/messages.json @@ -111,6 +111,10 @@ "message": "Report filter issues with specific websites to the uBlockOrigin/uAssets issue tracker. Requires a GitHub account.", "description": "First paragraph of 'Filter issues' section in Support pane" }, + "supportS5H": { + "message": "Troubleshooting information", + "description": "Label of 'Troubleshooting information' section in 'Report a filter issue' page" + }, "supportS6P1S1": { "message": "To avoid burdening volunteers with duplicate reports, please verify that the issue has not already been reported. Note: clicking the button will cause the page's origin to be sent to GitHub.", "description": "A paragraph in the filter issue reporter section" diff --git a/platform/mv3/extension/_locales/es/messages.json b/platform/mv3/extension/_locales/es/messages.json index a2f41cbf28178..8eac5dbe0fcbd 100644 --- a/platform/mv3/extension/_locales/es/messages.json +++ b/platform/mv3/extension/_locales/es/messages.json @@ -111,6 +111,10 @@ "message": "Reportar problemas de filtros con sitios web específicos en el registro de problemas uBlockOrigin/uAssets. Requiere una cuenta en GitHub.", "description": "First paragraph of 'Filter issues' section in Support pane" }, + "supportS5H": { + "message": "Troubleshooting information", + "description": "Label of 'Troubleshooting information' section in 'Report a filter issue' page" + }, "supportS6P1S1": { "message": "Para evitar sobrecargar a voluntarios con reportes duplicados, verifica que el problema no haya sido reportado. Nota: al hacer clic en el botón, hará que el origen de la página se envíe a GitHub.", "description": "A paragraph in the filter issue reporter section" diff --git a/platform/mv3/extension/_locales/et/messages.json b/platform/mv3/extension/_locales/et/messages.json index 71c81d7ffbf39..9db0bc5141bb5 100644 --- a/platform/mv3/extension/_locales/et/messages.json +++ b/platform/mv3/extension/_locales/et/messages.json @@ -111,6 +111,10 @@ "message": "Teavita vigasest filtrist kindlate veebilehtedega uBlockOrigin/uAssets vigade andmebaasi kaudu. Nõuab GitHubi kontot.", "description": "First paragraph of 'Filter issues' section in Support pane" }, + "supportS5H": { + "message": "Troubleshooting information", + "description": "Label of 'Troubleshooting information' section in 'Report a filter issue' page" + }, "supportS6P1S1": { "message": "Vabatahtlike koormamise vältimiseks samade teadetega veenduge, et keegi pole selle murega juba varem pöördunud. Märge! Nupule klõpsamisega saadetakse GitHubile lehekülje aadress.", "description": "A paragraph in the filter issue reporter section" diff --git a/platform/mv3/extension/_locales/eu/messages.json b/platform/mv3/extension/_locales/eu/messages.json index 6183330b5ce62..62691d7cc8d90 100644 --- a/platform/mv3/extension/_locales/eu/messages.json +++ b/platform/mv3/extension/_locales/eu/messages.json @@ -111,6 +111,10 @@ "message": "Report filter issues with specific websites to the uBlockOrigin/uAssets issue tracker. Requires a GitHub account.", "description": "First paragraph of 'Filter issues' section in Support pane" }, + "supportS5H": { + "message": "Troubleshooting information", + "description": "Label of 'Troubleshooting information' section in 'Report a filter issue' page" + }, "supportS6P1S1": { "message": "To avoid burdening volunteers with duplicate reports, please verify that the issue has not already been reported. Note: clicking the button will cause the page's origin to be sent to GitHub.", "description": "A paragraph in the filter issue reporter section" diff --git a/platform/mv3/extension/_locales/fa/messages.json b/platform/mv3/extension/_locales/fa/messages.json index 078a22bed8b02..6f49128513293 100644 --- a/platform/mv3/extension/_locales/fa/messages.json +++ b/platform/mv3/extension/_locales/fa/messages.json @@ -111,6 +111,10 @@ "message": "Report filter issues with specific websites to the uBlockOrigin/uAssets issue tracker. Requires a GitHub account.", "description": "First paragraph of 'Filter issues' section in Support pane" }, + "supportS5H": { + "message": "Troubleshooting information", + "description": "Label of 'Troubleshooting information' section in 'Report a filter issue' page" + }, "supportS6P1S1": { "message": "To avoid burdening volunteers with duplicate reports, please verify that the issue has not already been reported. Note: clicking the button will cause the page's origin to be sent to GitHub.", "description": "A paragraph in the filter issue reporter section" diff --git a/platform/mv3/extension/_locales/fi/messages.json b/platform/mv3/extension/_locales/fi/messages.json index b21d56c681351..78387f1a67a84 100644 --- a/platform/mv3/extension/_locales/fi/messages.json +++ b/platform/mv3/extension/_locales/fi/messages.json @@ -111,6 +111,10 @@ "message": "Ilmoita sivustokohtaisista suodatinongelmista uBlockOrigin/uAssets -ongelmaseurantaan. Vaatii GitHub-tilin.", "description": "First paragraph of 'Filter issues' section in Support pane" }, + "supportS5H": { + "message": "Troubleshooting information", + "description": "Label of 'Troubleshooting information' section in 'Report a filter issue' page" + }, "supportS6P1S1": { "message": "Välttääksesi vapaaehtoisten kuormittamisen ylimääräisillä ilmoituksilla, tarkasta ensin onko ongelmasta jo ilmoitettu. Huomioi: painikkeen painalluksen seurauksena sivun osoite lähetetään GitHubiin.", "description": "A paragraph in the filter issue reporter section" diff --git a/platform/mv3/extension/_locales/fil/messages.json b/platform/mv3/extension/_locales/fil/messages.json index ef499364b27c7..c3ce112251daa 100644 --- a/platform/mv3/extension/_locales/fil/messages.json +++ b/platform/mv3/extension/_locales/fil/messages.json @@ -111,6 +111,10 @@ "message": "Report filter issues with specific websites to the uBlockOrigin/uAssets issue tracker. Requires a GitHub account.", "description": "First paragraph of 'Filter issues' section in Support pane" }, + "supportS5H": { + "message": "Troubleshooting information", + "description": "Label of 'Troubleshooting information' section in 'Report a filter issue' page" + }, "supportS6P1S1": { "message": "To avoid burdening volunteers with duplicate reports, please verify that the issue has not already been reported. Note: clicking the button will cause the page's origin to be sent to GitHub.", "description": "A paragraph in the filter issue reporter section" diff --git a/platform/mv3/extension/_locales/fr/messages.json b/platform/mv3/extension/_locales/fr/messages.json index 215b28eec1d3a..58676ba7eafa7 100644 --- a/platform/mv3/extension/_locales/fr/messages.json +++ b/platform/mv3/extension/_locales/fr/messages.json @@ -111,6 +111,10 @@ "message": "Rapportez des problèmes de filtre sur des sites Web spécifiques dans le uBlockOrigin/uAssets suivi des problèmes. Nécessite un compte GitHub.", "description": "First paragraph of 'Filter issues' section in Support pane" }, + "supportS5H": { + "message": "Troubleshooting information", + "description": "Label of 'Troubleshooting information' section in 'Report a filter issue' page" + }, "supportS6P1S1": { "message": "Pour éviter d'encombrer les contributeurs avec des rapports en double, veuillez vérifier que le problème n'a pas déjà été rapporté.\nNote : Cliquer sur le bouton entraînera l'envoi de la page d'origine à GitHub.", "description": "A paragraph in the filter issue reporter section" diff --git a/platform/mv3/extension/_locales/fy/messages.json b/platform/mv3/extension/_locales/fy/messages.json index 56b3a3a203f71..7b17476fce91d 100644 --- a/platform/mv3/extension/_locales/fy/messages.json +++ b/platform/mv3/extension/_locales/fy/messages.json @@ -111,6 +111,10 @@ "message": "Meld filterproblemen mei spesifike websites yn de uBlockOrigin/uAssets-probleemtracker. Fereasket in GitHub-account.", "description": "First paragraph of 'Filter issues' section in Support pane" }, + "supportS5H": { + "message": "Troubleshooting information", + "description": "Label of 'Troubleshooting information' section in 'Report a filter issue' page" + }, "supportS6P1S1": { "message": "Kontrolearje oft it probleem net earder meld is om foar te kommen dat frijwilligers mei dûbele meldingen belêst wurde.", "description": "A paragraph in the filter issue reporter section" diff --git a/platform/mv3/extension/_locales/gl/messages.json b/platform/mv3/extension/_locales/gl/messages.json index 45e126a0456e3..0496a611ba6c9 100644 --- a/platform/mv3/extension/_locales/gl/messages.json +++ b/platform/mv3/extension/_locales/gl/messages.json @@ -111,6 +111,10 @@ "message": "Informa de problemas cos filtros en webs concretas no seguimento de problemas en uBlockOrigin/uAssets. Require unha conta GitHub.", "description": "First paragraph of 'Filter issues' section in Support pane" }, + "supportS5H": { + "message": "Troubleshooting information", + "description": "Label of 'Troubleshooting information' section in 'Report a filter issue' page" + }, "supportS6P1S1": { "message": "Para evitar a sobrecarga de traballo para as persoas voluntarias con duplicados dos problemas, comproba que aínda non se informou sobre o problema. Nota: ao premer no botón enviarás a orixe da páxina a GitHub.", "description": "A paragraph in the filter issue reporter section" diff --git a/platform/mv3/extension/_locales/gu/messages.json b/platform/mv3/extension/_locales/gu/messages.json index 4021b8b2f4cdf..ff00c75cff2cc 100644 --- a/platform/mv3/extension/_locales/gu/messages.json +++ b/platform/mv3/extension/_locales/gu/messages.json @@ -111,6 +111,10 @@ "message": "Report filter issues with specific websites to the uBlockOrigin/uAssets issue tracker. Requires a GitHub account.", "description": "First paragraph of 'Filter issues' section in Support pane" }, + "supportS5H": { + "message": "Troubleshooting information", + "description": "Label of 'Troubleshooting information' section in 'Report a filter issue' page" + }, "supportS6P1S1": { "message": "To avoid burdening volunteers with duplicate reports, please verify that the issue has not already been reported. Note: clicking the button will cause the page's origin to be sent to GitHub.", "description": "A paragraph in the filter issue reporter section" diff --git a/platform/mv3/extension/_locales/he/messages.json b/platform/mv3/extension/_locales/he/messages.json index f699c126691d3..067d6127ae1d7 100644 --- a/platform/mv3/extension/_locales/he/messages.json +++ b/platform/mv3/extension/_locales/he/messages.json @@ -111,6 +111,10 @@ "message": "לדיווח על בעיות באתרים ספציפים יש לפתוח דיווח חדש במעקב הדיווחים של uBlockOrigin/uAssets. נדרש חשבון ב GitHub.", "description": "First paragraph of 'Filter issues' section in Support pane" }, + "supportS5H": { + "message": "Troubleshooting information", + "description": "Label of 'Troubleshooting information' section in 'Report a filter issue' page" + }, "supportS6P1S1": { "message": "כדי להימנע מהכבדה על מתנדבים בדווחים כפולים, נא לודא שבעיה דומה טרם דווחה.", "description": "A paragraph in the filter issue reporter section" diff --git a/platform/mv3/extension/_locales/hi/messages.json b/platform/mv3/extension/_locales/hi/messages.json index 920c94cc8691b..fc87194e5ba47 100644 --- a/platform/mv3/extension/_locales/hi/messages.json +++ b/platform/mv3/extension/_locales/hi/messages.json @@ -111,6 +111,10 @@ "message": "Report filter issues with specific websites to the uBlockOrigin/uAssets issue tracker. Requires a GitHub account.", "description": "First paragraph of 'Filter issues' section in Support pane" }, + "supportS5H": { + "message": "Troubleshooting information", + "description": "Label of 'Troubleshooting information' section in 'Report a filter issue' page" + }, "supportS6P1S1": { "message": "To avoid burdening volunteers with duplicate reports, please verify that the issue has not already been reported. Note: clicking the button will cause the page's origin to be sent to GitHub.", "description": "A paragraph in the filter issue reporter section" diff --git a/platform/mv3/extension/_locales/hr/messages.json b/platform/mv3/extension/_locales/hr/messages.json index 9119a14047df5..704ab9e7ddba0 100644 --- a/platform/mv3/extension/_locales/hr/messages.json +++ b/platform/mv3/extension/_locales/hr/messages.json @@ -111,6 +111,10 @@ "message": "Prijavite probleme s filtrima s određenim web-lokacijama uBlockOrigin/uAssets alatu za praćenje problema. Potreban je GitHub račun.", "description": "First paragraph of 'Filter issues' section in Support pane" }, + "supportS5H": { + "message": "Troubleshooting information", + "description": "Label of 'Troubleshooting information' section in 'Report a filter issue' page" + }, "supportS6P1S1": { "message": "Kako biste izbjegli opterećivanje volontera duplim prijavama, provjerite nije li problem već prijavljen. Napomena: klik na gumb uzrokovat će slanje izvorne stranice na GitHub.", "description": "A paragraph in the filter issue reporter section" diff --git a/platform/mv3/extension/_locales/hu/messages.json b/platform/mv3/extension/_locales/hu/messages.json index 5b18c0a4f2482..1a50d68f51af2 100644 --- a/platform/mv3/extension/_locales/hu/messages.json +++ b/platform/mv3/extension/_locales/hu/messages.json @@ -111,6 +111,10 @@ "message": "Az adott webhelyeket érintő szűrőhibákat a uBlockOrigin/uAssets hibakövetőjében jelentse. Ehhez GitHub-fiók szükséges.", "description": "First paragraph of 'Filter issues' section in Support pane" }, + "supportS5H": { + "message": "Troubleshooting information", + "description": "Label of 'Troubleshooting information' section in 'Report a filter issue' page" + }, "supportS6P1S1": { "message": "Az önkéntesek terhelésének csökkentése érdekében győződjön meg róla, hogy a hiba még nem lett jelentve. Megjegyzés: a gombra kattintás azt okozza, hogy a lap eredete el lesz küldve a GitHub részére.", "description": "A paragraph in the filter issue reporter section" diff --git a/platform/mv3/extension/_locales/hy/messages.json b/platform/mv3/extension/_locales/hy/messages.json index 3ecdead07186c..f58262d2cd319 100644 --- a/platform/mv3/extension/_locales/hy/messages.json +++ b/platform/mv3/extension/_locales/hy/messages.json @@ -111,6 +111,10 @@ "message": "Report filter issues with specific websites to the uBlockOrigin/uAssets issue tracker. Requires a GitHub account.", "description": "First paragraph of 'Filter issues' section in Support pane" }, + "supportS5H": { + "message": "Troubleshooting information", + "description": "Label of 'Troubleshooting information' section in 'Report a filter issue' page" + }, "supportS6P1S1": { "message": "To avoid burdening volunteers with duplicate reports, please verify that the issue has not already been reported. Note: clicking the button will cause the page's origin to be sent to GitHub.", "description": "A paragraph in the filter issue reporter section" diff --git a/platform/mv3/extension/_locales/id/messages.json b/platform/mv3/extension/_locales/id/messages.json index c1427e018072b..79f269d6ce46d 100644 --- a/platform/mv3/extension/_locales/id/messages.json +++ b/platform/mv3/extension/_locales/id/messages.json @@ -111,6 +111,10 @@ "message": "Laporkan masalah filter situs web tertentu ke pelacak masalah uBlockOrigin/uAssets. Membutuhkan akun GitHub.", "description": "First paragraph of 'Filter issues' section in Support pane" }, + "supportS5H": { + "message": "Troubleshooting information", + "description": "Label of 'Troubleshooting information' section in 'Report a filter issue' page" + }, "supportS6P1S1": { "message": "Untuk menghindari membebani sukarelawan dengan laporan duplikat, harap verifikasi bahwa masalah tersebut belum dilaporkan.", "description": "A paragraph in the filter issue reporter section" diff --git a/platform/mv3/extension/_locales/it/messages.json b/platform/mv3/extension/_locales/it/messages.json index 5a459af58af00..1fa37dd4dff85 100644 --- a/platform/mv3/extension/_locales/it/messages.json +++ b/platform/mv3/extension/_locales/it/messages.json @@ -111,6 +111,10 @@ "message": "Segnala i problemi di filtraggio con siti web specifici su uBlockOrigin/uAssets issue tracker. Richiede un account GitHub.", "description": "First paragraph of 'Filter issues' section in Support pane" }, + "supportS5H": { + "message": "Troubleshooting information", + "description": "Label of 'Troubleshooting information' section in 'Report a filter issue' page" + }, "supportS6P1S1": { "message": "Per evitare di gravare sui volontari con segnalazioni duplicate, verifica che il problema non sia già stato segnalato. Nota: cliccando il pulsante l'origine della pagina sarà inviata a GitHub.", "description": "A paragraph in the filter issue reporter section" diff --git a/platform/mv3/extension/_locales/ja/messages.json b/platform/mv3/extension/_locales/ja/messages.json index 22d0787f8fe9c..fca925a9e7bda 100644 --- a/platform/mv3/extension/_locales/ja/messages.json +++ b/platform/mv3/extension/_locales/ja/messages.json @@ -111,6 +111,10 @@ "message": "uBlockOrigin/uAssets issue tracker で特定のウェブサイトでのフィルターの問題を報告します。GitHub アカウントが必要です", "description": "First paragraph of 'Filter issues' section in Support pane" }, + "supportS5H": { + "message": "Troubleshooting information", + "description": "Label of 'Troubleshooting information' section in 'Report a filter issue' page" + }, "supportS6P1S1": { "message": "重複した報告によってボランティアに負担をかけないように、問題がすでに報告されていないか確認してください。 注意: ボタンをクリックすると、ページのオリジンが GitHub に送信されます。", "description": "A paragraph in the filter issue reporter section" diff --git a/platform/mv3/extension/_locales/ka/messages.json b/platform/mv3/extension/_locales/ka/messages.json index ab8aa5da010f7..12762611e04a6 100644 --- a/platform/mv3/extension/_locales/ka/messages.json +++ b/platform/mv3/extension/_locales/ka/messages.json @@ -111,6 +111,10 @@ "message": "ცალკეულ საიტზე ფილტრების ხარვეზების მოსახსენებლად გამოიყენეთ uBlockOrigin/uAssets ხარვეზების აღსარიცხავი. დაგჭირდებათ GitHub-ანგარიში.", "description": "First paragraph of 'Filter issues' section in Support pane" }, + "supportS5H": { + "message": "Troubleshooting information", + "description": "Label of 'Troubleshooting information' section in 'Report a filter issue' page" + }, "supportS6P1S1": { "message": "მოხალისეები რომ არ მოცდნენ ერთნაირი მოსხენებების გაცნობით, გთხოვთ გადაამოწმოთ, უკვე ხომ არაა გაგზავნილი საჩივარი ამ ხარვეზზე. გაითვალისწინეთ: ღილაკზე დაწკაპების შედეგად გვერდის მონაცემები გაიგზავნება GitHub-ზე.", "description": "A paragraph in the filter issue reporter section" diff --git a/platform/mv3/extension/_locales/kk/messages.json b/platform/mv3/extension/_locales/kk/messages.json index 4021b8b2f4cdf..ff00c75cff2cc 100644 --- a/platform/mv3/extension/_locales/kk/messages.json +++ b/platform/mv3/extension/_locales/kk/messages.json @@ -111,6 +111,10 @@ "message": "Report filter issues with specific websites to the uBlockOrigin/uAssets issue tracker. Requires a GitHub account.", "description": "First paragraph of 'Filter issues' section in Support pane" }, + "supportS5H": { + "message": "Troubleshooting information", + "description": "Label of 'Troubleshooting information' section in 'Report a filter issue' page" + }, "supportS6P1S1": { "message": "To avoid burdening volunteers with duplicate reports, please verify that the issue has not already been reported. Note: clicking the button will cause the page's origin to be sent to GitHub.", "description": "A paragraph in the filter issue reporter section" diff --git a/platform/mv3/extension/_locales/kn/messages.json b/platform/mv3/extension/_locales/kn/messages.json index 03944eccd3d51..14fae1956c7e6 100644 --- a/platform/mv3/extension/_locales/kn/messages.json +++ b/platform/mv3/extension/_locales/kn/messages.json @@ -111,6 +111,10 @@ "message": "Report filter issues with specific websites to the uBlockOrigin/uAssets issue tracker. Requires a GitHub account.", "description": "First paragraph of 'Filter issues' section in Support pane" }, + "supportS5H": { + "message": "Troubleshooting information", + "description": "Label of 'Troubleshooting information' section in 'Report a filter issue' page" + }, "supportS6P1S1": { "message": "To avoid burdening volunteers with duplicate reports, please verify that the issue has not already been reported. Note: clicking the button will cause the page's origin to be sent to GitHub.", "description": "A paragraph in the filter issue reporter section" diff --git a/platform/mv3/extension/_locales/ko/messages.json b/platform/mv3/extension/_locales/ko/messages.json index f0ac82033018a..dd9442c5f00b2 100644 --- a/platform/mv3/extension/_locales/ko/messages.json +++ b/platform/mv3/extension/_locales/ko/messages.json @@ -111,6 +111,10 @@ "message": "특정 웹사이트에서 발생하는 필터 이슈를 uBlockOrigin/uAssets 이슈 트래커에 신고할 수 있습니다. GitHub 계정이 필요합니다.", "description": "First paragraph of 'Filter issues' section in Support pane" }, + "supportS5H": { + "message": "Troubleshooting information", + "description": "Label of 'Troubleshooting information' section in 'Report a filter issue' page" + }, "supportS6P1S1": { "message": "봉사자들이 중복 신고로 인해 부담을 겪지 않도록, 해당 이슈가 이미 신고되지는 않았는지 확인해주시기 바랍니다.", "description": "A paragraph in the filter issue reporter section" diff --git a/platform/mv3/extension/_locales/lt/messages.json b/platform/mv3/extension/_locales/lt/messages.json index 8c6bfe86c0ba3..8f9c5653fe02a 100644 --- a/platform/mv3/extension/_locales/lt/messages.json +++ b/platform/mv3/extension/_locales/lt/messages.json @@ -111,6 +111,10 @@ "message": "Report filter issues with specific websites to the uBlockOrigin/uAssets issue tracker. Requires a GitHub account.", "description": "First paragraph of 'Filter issues' section in Support pane" }, + "supportS5H": { + "message": "Troubleshooting information", + "description": "Label of 'Troubleshooting information' section in 'Report a filter issue' page" + }, "supportS6P1S1": { "message": "To avoid burdening volunteers with duplicate reports, please verify that the issue has not already been reported. Note: clicking the button will cause the page's origin to be sent to GitHub.", "description": "A paragraph in the filter issue reporter section" diff --git a/platform/mv3/extension/_locales/lv/messages.json b/platform/mv3/extension/_locales/lv/messages.json index b280efb2d5390..c18dffd242d3f 100644 --- a/platform/mv3/extension/_locales/lv/messages.json +++ b/platform/mv3/extension/_locales/lv/messages.json @@ -111,6 +111,10 @@ "message": "Ziņot par aizturēšanas filtru nepilnībām noteiktās vietnēs uBlockOrigin/uAssets pieteikumu izsekotājā. Nepieciešams GitHub konts.", "description": "First paragraph of 'Filter issues' section in Support pane" }, + "supportS5H": { + "message": "Troubleshooting information", + "description": "Label of 'Troubleshooting information' section in 'Report a filter issue' page" + }, "supportS6P1S1": { "message": "Lai izvairītos no brīvprātīgo noslogošanas ar ziņojumiem, kas atkārtojas, lūgums pārbaudīt, ka par šādu nepilnību jau nav ziņots. Piebilde: klikšķināšana uz pogas izraisīs arī lapas izcelsmes nosūtīšanu uz GitHub.", "description": "A paragraph in the filter issue reporter section" diff --git a/platform/mv3/extension/_locales/mk/messages.json b/platform/mv3/extension/_locales/mk/messages.json index 6353c7e7ec445..15a10cd569171 100644 --- a/platform/mv3/extension/_locales/mk/messages.json +++ b/platform/mv3/extension/_locales/mk/messages.json @@ -111,6 +111,10 @@ "message": "Пријавете проблеми со филтерите за специфични веб-страници до uBlockOrigin/uAssets issue tracker. Потребен е GitHub профил.", "description": "First paragraph of 'Filter issues' section in Support pane" }, + "supportS5H": { + "message": "Troubleshooting information", + "description": "Label of 'Troubleshooting information' section in 'Report a filter issue' page" + }, "supportS6P1S1": { "message": "За да се избегне оптоварување на волонтерите со дупликат пријави, ве молиме проверете дали проблемот веќе не е пријавен.", "description": "A paragraph in the filter issue reporter section" diff --git a/platform/mv3/extension/_locales/ml/messages.json b/platform/mv3/extension/_locales/ml/messages.json index 80824c8d07ecd..35afa1072ad6e 100644 --- a/platform/mv3/extension/_locales/ml/messages.json +++ b/platform/mv3/extension/_locales/ml/messages.json @@ -111,6 +111,10 @@ "message": "Report filter issues with specific websites to the uBlockOrigin/uAssets issue tracker. Requires a GitHub account.", "description": "First paragraph of 'Filter issues' section in Support pane" }, + "supportS5H": { + "message": "Troubleshooting information", + "description": "Label of 'Troubleshooting information' section in 'Report a filter issue' page" + }, "supportS6P1S1": { "message": "To avoid burdening volunteers with duplicate reports, please verify that the issue has not already been reported. Note: clicking the button will cause the page's origin to be sent to GitHub.", "description": "A paragraph in the filter issue reporter section" diff --git a/platform/mv3/extension/_locales/mr/messages.json b/platform/mv3/extension/_locales/mr/messages.json index 4021b8b2f4cdf..ff00c75cff2cc 100644 --- a/platform/mv3/extension/_locales/mr/messages.json +++ b/platform/mv3/extension/_locales/mr/messages.json @@ -111,6 +111,10 @@ "message": "Report filter issues with specific websites to the uBlockOrigin/uAssets issue tracker. Requires a GitHub account.", "description": "First paragraph of 'Filter issues' section in Support pane" }, + "supportS5H": { + "message": "Troubleshooting information", + "description": "Label of 'Troubleshooting information' section in 'Report a filter issue' page" + }, "supportS6P1S1": { "message": "To avoid burdening volunteers with duplicate reports, please verify that the issue has not already been reported. Note: clicking the button will cause the page's origin to be sent to GitHub.", "description": "A paragraph in the filter issue reporter section" diff --git a/platform/mv3/extension/_locales/ms/messages.json b/platform/mv3/extension/_locales/ms/messages.json index 47cf813c35eca..66e9a94fb5c95 100644 --- a/platform/mv3/extension/_locales/ms/messages.json +++ b/platform/mv3/extension/_locales/ms/messages.json @@ -111,6 +111,10 @@ "message": "Report filter issues with specific websites to the uBlockOrigin/uAssets issue tracker. Requires a GitHub account.", "description": "First paragraph of 'Filter issues' section in Support pane" }, + "supportS5H": { + "message": "Troubleshooting information", + "description": "Label of 'Troubleshooting information' section in 'Report a filter issue' page" + }, "supportS6P1S1": { "message": "To avoid burdening volunteers with duplicate reports, please verify that the issue has not already been reported. Note: clicking the button will cause the page's origin to be sent to GitHub.", "description": "A paragraph in the filter issue reporter section" diff --git a/platform/mv3/extension/_locales/nb/messages.json b/platform/mv3/extension/_locales/nb/messages.json index f28df0a2c1a90..60116609c15d9 100644 --- a/platform/mv3/extension/_locales/nb/messages.json +++ b/platform/mv3/extension/_locales/nb/messages.json @@ -111,6 +111,10 @@ "message": "Rapporter filterproblemer med bestemte nettsteder til uBlockOrigin/uAssets problemsporing. Krever en GitHub-konto.", "description": "First paragraph of 'Filter issues' section in Support pane" }, + "supportS5H": { + "message": "Troubleshooting information", + "description": "Label of 'Troubleshooting information' section in 'Report a filter issue' page" + }, "supportS6P1S1": { "message": "For å unngå å belaste de frivillige med dobbeltrapporter, må du kontrollere at problemet ikke allerede har blitt rapportert. Noter: ved å klikke på knappen vil sidens opprinnelse bli sendt til GitHub..", "description": "A paragraph in the filter issue reporter section" diff --git a/platform/mv3/extension/_locales/nl/messages.json b/platform/mv3/extension/_locales/nl/messages.json index 6c087425fecab..65e7d6c78fbfd 100644 --- a/platform/mv3/extension/_locales/nl/messages.json +++ b/platform/mv3/extension/_locales/nl/messages.json @@ -111,6 +111,10 @@ "message": "Meld filterproblemen met specifieke websites in de uBlockOrigin/uAssets-probleemtracker. Vereist een GitHub-account.", "description": "First paragraph of 'Filter issues' section in Support pane" }, + "supportS5H": { + "message": "Troubleshooting information", + "description": "Label of 'Troubleshooting information' section in 'Report a filter issue' page" + }, "supportS6P1S1": { "message": "Controleer of het probleem niet eerder is gemeld om te voorkomen dat vrijwilligers met dubbele meldingen worden belast.", "description": "A paragraph in the filter issue reporter section" diff --git a/platform/mv3/extension/_locales/oc/messages.json b/platform/mv3/extension/_locales/oc/messages.json index 4021b8b2f4cdf..ff00c75cff2cc 100644 --- a/platform/mv3/extension/_locales/oc/messages.json +++ b/platform/mv3/extension/_locales/oc/messages.json @@ -111,6 +111,10 @@ "message": "Report filter issues with specific websites to the uBlockOrigin/uAssets issue tracker. Requires a GitHub account.", "description": "First paragraph of 'Filter issues' section in Support pane" }, + "supportS5H": { + "message": "Troubleshooting information", + "description": "Label of 'Troubleshooting information' section in 'Report a filter issue' page" + }, "supportS6P1S1": { "message": "To avoid burdening volunteers with duplicate reports, please verify that the issue has not already been reported. Note: clicking the button will cause the page's origin to be sent to GitHub.", "description": "A paragraph in the filter issue reporter section" diff --git a/platform/mv3/extension/_locales/pa/messages.json b/platform/mv3/extension/_locales/pa/messages.json index 3be9de68071d6..764ce7110f818 100644 --- a/platform/mv3/extension/_locales/pa/messages.json +++ b/platform/mv3/extension/_locales/pa/messages.json @@ -111,6 +111,10 @@ "message": "Report filter issues with specific websites to the uBlockOrigin/uAssets issue tracker. Requires a GitHub account.", "description": "First paragraph of 'Filter issues' section in Support pane" }, + "supportS5H": { + "message": "Troubleshooting information", + "description": "Label of 'Troubleshooting information' section in 'Report a filter issue' page" + }, "supportS6P1S1": { "message": "To avoid burdening volunteers with duplicate reports, please verify that the issue has not already been reported. Note: clicking the button will cause the page's origin to be sent to GitHub.", "description": "A paragraph in the filter issue reporter section" diff --git a/platform/mv3/extension/_locales/pl/messages.json b/platform/mv3/extension/_locales/pl/messages.json index 51948aee99de9..39d5455687d7f 100644 --- a/platform/mv3/extension/_locales/pl/messages.json +++ b/platform/mv3/extension/_locales/pl/messages.json @@ -111,6 +111,10 @@ "message": "Zgłoś problemy z filtrami dotyczące konkretnych witryn internetowych do systemu śledzenia problemów uBlockOrigin/uAssets. Wymagane jest konto GitHub.", "description": "First paragraph of 'Filter issues' section in Support pane" }, + "supportS5H": { + "message": "Troubleshooting information", + "description": "Label of 'Troubleshooting information' section in 'Report a filter issue' page" + }, "supportS6P1S1": { "message": "Sprawdź, czy problem nie został już zgłoszony, aby uniknąć obciążania wolontariuszy duplikatami raportów. Uwaga: kliknięcie przycisku spowoduje wysłanie źródła strony do serwisu GitHub.", "description": "A paragraph in the filter issue reporter section" diff --git a/platform/mv3/extension/_locales/pt_BR/messages.json b/platform/mv3/extension/_locales/pt_BR/messages.json index 0a58de024bb6b..1f73def3ce140 100644 --- a/platform/mv3/extension/_locales/pt_BR/messages.json +++ b/platform/mv3/extension/_locales/pt_BR/messages.json @@ -111,6 +111,10 @@ "message": "Reporte problemas dos filtros com sites da web específicos no rastreador de problemas uBlockOrigin/uAssets. Requer uma conta no GitHub.", "description": "First paragraph of 'Filter issues' section in Support pane" }, + "supportS5H": { + "message": "Troubleshooting information", + "description": "Label of 'Troubleshooting information' section in 'Report a filter issue' page" + }, "supportS6P1S1": { "message": "Para evitar sobrecarregar os voluntários com relatórios duplicados por favor verifique se o problema já não foi reportado.\nObservação: clicar no botão fará com que a origem da página seja enviada ao GitHub.", "description": "A paragraph in the filter issue reporter section" diff --git a/platform/mv3/extension/_locales/pt_PT/messages.json b/platform/mv3/extension/_locales/pt_PT/messages.json index 0563513f1ca6f..18223f89b4523 100644 --- a/platform/mv3/extension/_locales/pt_PT/messages.json +++ b/platform/mv3/extension/_locales/pt_PT/messages.json @@ -111,6 +111,10 @@ "message": "Relate problemas de filtros em websites específicos no controlador de problemas uBlockOrigin/uAssets. Requer uma conta GitHub.", "description": "First paragraph of 'Filter issues' section in Support pane" }, + "supportS5H": { + "message": "Troubleshooting information", + "description": "Label of 'Troubleshooting information' section in 'Report a filter issue' page" + }, "supportS6P1S1": { "message": "Para evitar sobrecarregar os voluntários com relatórios duplicados, verifique se o problema ainda não foi relatado. Nota: clicar no botão fará com que a origem da página seja enviada para o GitHub.", "description": "A paragraph in the filter issue reporter section" diff --git a/platform/mv3/extension/_locales/ro/messages.json b/platform/mv3/extension/_locales/ro/messages.json index 4084828126d56..6890dee1d8699 100644 --- a/platform/mv3/extension/_locales/ro/messages.json +++ b/platform/mv3/extension/_locales/ro/messages.json @@ -111,6 +111,10 @@ "message": "Raportează aici o eroare cu filtrele pentru un site specific uBlockOrigin/uAssets issue tracker. Este necesar un cont GitHub.", "description": "First paragraph of 'Filter issues' section in Support pane" }, + "supportS5H": { + "message": "Troubleshooting information", + "description": "Label of 'Troubleshooting information' section in 'Report a filter issue' page" + }, "supportS6P1S1": { "message": "Pentru a evita încărcarea voluntarilor cu rapoarte duplicate, vă rugăm să verificați dacă problema nu a fost deja raportată.", "description": "A paragraph in the filter issue reporter section" diff --git a/platform/mv3/extension/_locales/ru/messages.json b/platform/mv3/extension/_locales/ru/messages.json index 07336f7ec8dab..0673524765941 100644 --- a/platform/mv3/extension/_locales/ru/messages.json +++ b/platform/mv3/extension/_locales/ru/messages.json @@ -111,6 +111,10 @@ "message": "Сообщайте о проблемах с фильтрами на определённых сайтах в трекер ошибок uBlockOrigin/uAssets. Требуется учётная запись в GitHub.", "description": "First paragraph of 'Filter issues' section in Support pane" }, + "supportS5H": { + "message": "Troubleshooting information", + "description": "Label of 'Troubleshooting information' section in 'Report a filter issue' page" + }, "supportS6P1S1": { "message": "Чтобы не обременять добровольцев повторяющимися отчётами, пожалуйста, убедитесь, что об этой проблеме ещё не сообщали. Примечание: щелчок по кнопке приведёт к отправке адреса посещенной страницы GitHub'у.", "description": "A paragraph in the filter issue reporter section" diff --git a/platform/mv3/extension/_locales/si/messages.json b/platform/mv3/extension/_locales/si/messages.json index d91403abae1cd..60c395b9a5e82 100644 --- a/platform/mv3/extension/_locales/si/messages.json +++ b/platform/mv3/extension/_locales/si/messages.json @@ -111,6 +111,10 @@ "message": "Report filter issues with specific websites to the uBlockOrigin/uAssets issue tracker. Requires a GitHub account.", "description": "First paragraph of 'Filter issues' section in Support pane" }, + "supportS5H": { + "message": "Troubleshooting information", + "description": "Label of 'Troubleshooting information' section in 'Report a filter issue' page" + }, "supportS6P1S1": { "message": "To avoid burdening volunteers with duplicate reports, please verify that the issue has not already been reported. Note: clicking the button will cause the page's origin to be sent to GitHub.", "description": "A paragraph in the filter issue reporter section" diff --git a/platform/mv3/extension/_locales/sk/messages.json b/platform/mv3/extension/_locales/sk/messages.json index ad4fc321ba56b..f0e32d280dc97 100644 --- a/platform/mv3/extension/_locales/sk/messages.json +++ b/platform/mv3/extension/_locales/sk/messages.json @@ -111,6 +111,10 @@ "message": "Nahlásenie problémov s filtrom s konkrétnymi webovými stránkami na uBlockOrigin/uAssets issue tracker. Vyžaduje sa GitHub účet.", "description": "First paragraph of 'Filter issues' section in Support pane" }, + "supportS5H": { + "message": "Troubleshooting information", + "description": "Label of 'Troubleshooting information' section in 'Report a filter issue' page" + }, "supportS6P1S1": { "message": "Aby ste dobrovoľníkov nezaťažovali duplicitnými hláseniami, overte si, či už problém nebol nahlásený. Poznámka: kliknutím na tlačidlo sa odošle pôvodná stránka na GitHub.", "description": "A paragraph in the filter issue reporter section" diff --git a/platform/mv3/extension/_locales/sl/messages.json b/platform/mv3/extension/_locales/sl/messages.json index 4021b8b2f4cdf..ff00c75cff2cc 100644 --- a/platform/mv3/extension/_locales/sl/messages.json +++ b/platform/mv3/extension/_locales/sl/messages.json @@ -111,6 +111,10 @@ "message": "Report filter issues with specific websites to the uBlockOrigin/uAssets issue tracker. Requires a GitHub account.", "description": "First paragraph of 'Filter issues' section in Support pane" }, + "supportS5H": { + "message": "Troubleshooting information", + "description": "Label of 'Troubleshooting information' section in 'Report a filter issue' page" + }, "supportS6P1S1": { "message": "To avoid burdening volunteers with duplicate reports, please verify that the issue has not already been reported. Note: clicking the button will cause the page's origin to be sent to GitHub.", "description": "A paragraph in the filter issue reporter section" diff --git a/platform/mv3/extension/_locales/so/messages.json b/platform/mv3/extension/_locales/so/messages.json index 4021b8b2f4cdf..ff00c75cff2cc 100644 --- a/platform/mv3/extension/_locales/so/messages.json +++ b/platform/mv3/extension/_locales/so/messages.json @@ -111,6 +111,10 @@ "message": "Report filter issues with specific websites to the uBlockOrigin/uAssets issue tracker. Requires a GitHub account.", "description": "First paragraph of 'Filter issues' section in Support pane" }, + "supportS5H": { + "message": "Troubleshooting information", + "description": "Label of 'Troubleshooting information' section in 'Report a filter issue' page" + }, "supportS6P1S1": { "message": "To avoid burdening volunteers with duplicate reports, please verify that the issue has not already been reported. Note: clicking the button will cause the page's origin to be sent to GitHub.", "description": "A paragraph in the filter issue reporter section" diff --git a/platform/mv3/extension/_locales/sq/messages.json b/platform/mv3/extension/_locales/sq/messages.json index 88cd9fd76ffd7..62eaa259e7672 100644 --- a/platform/mv3/extension/_locales/sq/messages.json +++ b/platform/mv3/extension/_locales/sq/messages.json @@ -111,6 +111,10 @@ "message": "Problemet e disa faqeve me filtrat duhen raportuar në ditarin e problemeve uBlockOrigin/uAssets. Duhet një konto GitHub.", "description": "First paragraph of 'Filter issues' section in Support pane" }, + "supportS5H": { + "message": "Troubleshooting information", + "description": "Label of 'Troubleshooting information' section in 'Report a filter issue' page" + }, "supportS6P1S1": { "message": "Verifikoni a është raportuar edhe më parë që të mos i lodhni vullnetarët e tjerë me të njëjtat probleme. Shënim: kur klikoni butonin, origjina e faqes do të dërgohet në GitHub.", "description": "A paragraph in the filter issue reporter section" diff --git a/platform/mv3/extension/_locales/sr/messages.json b/platform/mv3/extension/_locales/sr/messages.json index 06c4c847b9741..898750d291872 100644 --- a/platform/mv3/extension/_locales/sr/messages.json +++ b/platform/mv3/extension/_locales/sr/messages.json @@ -111,6 +111,10 @@ "message": "Пријавите проблеме са филтерима на одређеним веб сајтовима на uBlockOrigin/uAssets issue tracker. Захтева GitHub налог.", "description": "First paragraph of 'Filter issues' section in Support pane" }, + "supportS5H": { + "message": "Troubleshooting information", + "description": "Label of 'Troubleshooting information' section in 'Report a filter issue' page" + }, "supportS6P1S1": { "message": "Да не бисте оптерећивали волонтере дуплим извештајима, проверите да ли је проблем већ пријављен.", "description": "A paragraph in the filter issue reporter section" diff --git a/platform/mv3/extension/_locales/sv/messages.json b/platform/mv3/extension/_locales/sv/messages.json index 65f43d4de0fbc..0eed6cab597b0 100644 --- a/platform/mv3/extension/_locales/sv/messages.json +++ b/platform/mv3/extension/_locales/sv/messages.json @@ -111,6 +111,10 @@ "message": "Rapportera filterproblem med specifika webbplatser till uBlockOrigin/uAssets problemhanteringssystemet. Kräver ett GitHub-konto.", "description": "First paragraph of 'Filter issues' section in Support pane" }, + "supportS5H": { + "message": "Troubleshooting information", + "description": "Label of 'Troubleshooting information' section in 'Report a filter issue' page" + }, "supportS6P1S1": { "message": "För att undvika att belasta volontärer med dubbletter av rapporter, kontrollera att problemet inte redan har rapporterats. Observera: om du klickar på knappen kommer sidans ursprung att skickas till GitHub.", "description": "A paragraph in the filter issue reporter section" diff --git a/platform/mv3/extension/_locales/sw/messages.json b/platform/mv3/extension/_locales/sw/messages.json index 4021b8b2f4cdf..ff00c75cff2cc 100644 --- a/platform/mv3/extension/_locales/sw/messages.json +++ b/platform/mv3/extension/_locales/sw/messages.json @@ -111,6 +111,10 @@ "message": "Report filter issues with specific websites to the uBlockOrigin/uAssets issue tracker. Requires a GitHub account.", "description": "First paragraph of 'Filter issues' section in Support pane" }, + "supportS5H": { + "message": "Troubleshooting information", + "description": "Label of 'Troubleshooting information' section in 'Report a filter issue' page" + }, "supportS6P1S1": { "message": "To avoid burdening volunteers with duplicate reports, please verify that the issue has not already been reported. Note: clicking the button will cause the page's origin to be sent to GitHub.", "description": "A paragraph in the filter issue reporter section" diff --git a/platform/mv3/extension/_locales/ta/messages.json b/platform/mv3/extension/_locales/ta/messages.json index 4021b8b2f4cdf..ff00c75cff2cc 100644 --- a/platform/mv3/extension/_locales/ta/messages.json +++ b/platform/mv3/extension/_locales/ta/messages.json @@ -111,6 +111,10 @@ "message": "Report filter issues with specific websites to the uBlockOrigin/uAssets issue tracker. Requires a GitHub account.", "description": "First paragraph of 'Filter issues' section in Support pane" }, + "supportS5H": { + "message": "Troubleshooting information", + "description": "Label of 'Troubleshooting information' section in 'Report a filter issue' page" + }, "supportS6P1S1": { "message": "To avoid burdening volunteers with duplicate reports, please verify that the issue has not already been reported. Note: clicking the button will cause the page's origin to be sent to GitHub.", "description": "A paragraph in the filter issue reporter section" diff --git a/platform/mv3/extension/_locales/te/messages.json b/platform/mv3/extension/_locales/te/messages.json index 9a368701a2a12..4ffb1cdbbbb33 100644 --- a/platform/mv3/extension/_locales/te/messages.json +++ b/platform/mv3/extension/_locales/te/messages.json @@ -111,6 +111,10 @@ "message": "Report filter issues with specific websites to the uBlockOrigin/uAssets issue tracker. Requires a GitHub account.", "description": "First paragraph of 'Filter issues' section in Support pane" }, + "supportS5H": { + "message": "Troubleshooting information", + "description": "Label of 'Troubleshooting information' section in 'Report a filter issue' page" + }, "supportS6P1S1": { "message": "To avoid burdening volunteers with duplicate reports, please verify that the issue has not already been reported. Note: clicking the button will cause the page's origin to be sent to GitHub.", "description": "A paragraph in the filter issue reporter section" diff --git a/platform/mv3/extension/_locales/th/messages.json b/platform/mv3/extension/_locales/th/messages.json index 4c57881a1c7e8..cd69d31901037 100644 --- a/platform/mv3/extension/_locales/th/messages.json +++ b/platform/mv3/extension/_locales/th/messages.json @@ -111,6 +111,10 @@ "message": "รายงานปัญหาตัวกรองผ่านเว็บไซต์เฉพาะที่ uBlockOrigin/uAssets ปัญหาตัวติดตาม. ต้องใช้บัญชี GitHub", "description": "First paragraph of 'Filter issues' section in Support pane" }, + "supportS5H": { + "message": "Troubleshooting information", + "description": "Label of 'Troubleshooting information' section in 'Report a filter issue' page" + }, "supportS6P1S1": { "message": "เพื่อเป็นการลดภาระอาสาสมัครจากการรายงานซ้ำซ้อน โปรดตรวจสอบก่อนว่าปัญหาดังกล่าวได้รับการรายงานไปแล้วหรือยัง หมายเหตุ: คลิกที่ปุ่มจะเป็นการส่งต้นทางของหน้าเว็บไปยัง GitHub", "description": "A paragraph in the filter issue reporter section" diff --git a/platform/mv3/extension/_locales/tr/messages.json b/platform/mv3/extension/_locales/tr/messages.json index f9940ae9049e7..e56e66d38f5c0 100644 --- a/platform/mv3/extension/_locales/tr/messages.json +++ b/platform/mv3/extension/_locales/tr/messages.json @@ -111,6 +111,10 @@ "message": "Bir sitedeki filtre sorunlarını bildirmek için uBlockOrigin/uAssets issue tracker kullanın. Bir GitHub hesabı gerekir.", "description": "First paragraph of 'Filter issues' section in Support pane" }, + "supportS5H": { + "message": "Troubleshooting information", + "description": "Label of 'Troubleshooting information' section in 'Report a filter issue' page" + }, "supportS6P1S1": { "message": "Gönüllüleri benzer raporlar ile bezdirmemek için sorunun zaten bildirilip bildirilmediğine bakın. Not: Butona tıklamak sayfanın temel adresini GitHub'a gönderir.", "description": "A paragraph in the filter issue reporter section" diff --git a/platform/mv3/extension/_locales/uk/messages.json b/platform/mv3/extension/_locales/uk/messages.json index 21e463a6b0684..a90042cc5bd96 100644 --- a/platform/mv3/extension/_locales/uk/messages.json +++ b/platform/mv3/extension/_locales/uk/messages.json @@ -111,6 +111,10 @@ "message": "Повідомляйте про вади з фільтрами на конкретних вебсайтах у відстежувач помилок uBlockOrigin/uAssets. Потрібен обліковий запис GitHub.", "description": "First paragraph of 'Filter issues' section in Support pane" }, + "supportS5H": { + "message": "Troubleshooting information", + "description": "Label of 'Troubleshooting information' section in 'Report a filter issue' page" + }, "supportS6P1S1": { "message": "Щоб не обтяжувати волонтерів повторюваними звітами, переконайтеся, що про проблему ще не повідомлялося.Зауваження: натискання на кнопку призведе до надсилання походження сторінки на GitHub.", "description": "A paragraph in the filter issue reporter section" diff --git a/platform/mv3/extension/_locales/ur/messages.json b/platform/mv3/extension/_locales/ur/messages.json index fbb65b01ed8b1..b3f6616faa904 100644 --- a/platform/mv3/extension/_locales/ur/messages.json +++ b/platform/mv3/extension/_locales/ur/messages.json @@ -111,6 +111,10 @@ "message": "Report filter issues with specific websites to the uBlockOrigin/uAssets issue tracker. Requires a GitHub account.", "description": "First paragraph of 'Filter issues' section in Support pane" }, + "supportS5H": { + "message": "Troubleshooting information", + "description": "Label of 'Troubleshooting information' section in 'Report a filter issue' page" + }, "supportS6P1S1": { "message": "To avoid burdening volunteers with duplicate reports, please verify that the issue has not already been reported. Note: clicking the button will cause the page's origin to be sent to GitHub.", "description": "A paragraph in the filter issue reporter section" diff --git a/platform/mv3/extension/_locales/vi/messages.json b/platform/mv3/extension/_locales/vi/messages.json index fda7516f49d3b..8ede1ba44d299 100644 --- a/platform/mv3/extension/_locales/vi/messages.json +++ b/platform/mv3/extension/_locales/vi/messages.json @@ -111,6 +111,10 @@ "message": "Báo cáo các vấn đề về bộ lọc với các trang web cụ thể cho trình theo dõi vấn đề uBlockOrigin/uAssets xử lý. Cần có tài khoản GitHub.", "description": "First paragraph of 'Filter issues' section in Support pane" }, + "supportS5H": { + "message": "Troubleshooting information", + "description": "Label of 'Troubleshooting information' section in 'Report a filter issue' page" + }, "supportS6P1S1": { "message": "Để tránh tạo thêm gánh nặng cho các tình nguyện viên, hãy chắc chắn rằng chưa từng có vấn đề tương tự được báo cáo.", "description": "A paragraph in the filter issue reporter section" diff --git a/platform/mv3/extension/_locales/zh_CN/messages.json b/platform/mv3/extension/_locales/zh_CN/messages.json index 7eafea6f84045..8d899fbd18ca6 100644 --- a/platform/mv3/extension/_locales/zh_CN/messages.json +++ b/platform/mv3/extension/_locales/zh_CN/messages.json @@ -111,6 +111,10 @@ "message": "将特定网站的过滤问题报告给uBlockOrigin/uAssets issue tracker需要有 GitHub 账户。", "description": "First paragraph of 'Filter issues' section in Support pane" }, + "supportS5H": { + "message": "Troubleshooting information", + "description": "Label of 'Troubleshooting information' section in 'Report a filter issue' page" + }, "supportS6P1S1": { "message": "请确认该问题未曾上报,以避免加重志愿者负担。", "description": "A paragraph in the filter issue reporter section" diff --git a/platform/mv3/extension/_locales/zh_TW/messages.json b/platform/mv3/extension/_locales/zh_TW/messages.json index 3488118a765b1..1371636d42aa1 100644 --- a/platform/mv3/extension/_locales/zh_TW/messages.json +++ b/platform/mv3/extension/_locales/zh_TW/messages.json @@ -111,6 +111,10 @@ "message": "將特定網站的過濾器問題回報至 uBlockOrigin/uAssets 議題追蹤器需要 GitHub 帳號。.", "description": "First paragraph of 'Filter issues' section in Support pane" }, + "supportS5H": { + "message": "Troubleshooting information", + "description": "Label of 'Troubleshooting information' section in 'Report a filter issue' page" + }, "supportS6P1S1": { "message": "為避免增加志願者的負擔,請先確認此問題是否已被回報過。請注意:點選按鈕會將本頁的來源傳送到 GitHub。", "description": "A paragraph in the filter issue reporter section" From 5e9737d38eefd36a6985ead653bb6bf4f9b77c3f Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Tue, 25 Mar 2025 08:32:42 -0400 Subject: [PATCH 0734/1099] [mv3] Allow copy/paste troubleshooting info --- platform/mv3/extension/js/report.js | 46 ++++++++++++++++++----------- platform/mv3/extension/report.html | 8 ++++- 2 files changed, 36 insertions(+), 18 deletions(-) diff --git a/platform/mv3/extension/js/report.js b/platform/mv3/extension/js/report.js index 39fd60f671e09..1350edcd40e6d 100644 --- a/platform/mv3/extension/js/report.js +++ b/platform/mv3/extension/js/report.js @@ -91,6 +91,30 @@ function renderData(data, depth = 0) { /******************************************************************************/ +async function getConfigData() { + const manifest = runtime.getManifest(); + const [ + rulesets, + defaultMode, + ] = await Promise.all([ + dnr.getEnabledRulesets(), + sendMessage({ what: 'getDefaultFilteringMode' }), + ]); + const modes = [ 'no filtering', 'basic', 'optimal', 'complete' ]; + const config = { + name: manifest.name, + version: manifest.version, + filtering: { + 'site': `${modes[reportedPage.mode]}`, + 'default': `${modes[defaultMode]}`, + }, + rulesets, + }; + return renderData(config); +} + +/******************************************************************************/ + async function reportSpecificFilterIssue() { const githubURL = new URL( 'https://github.com/uBlockOrigin/uAssets/issues/new?template=specific_report_from_ubol.yml' @@ -107,22 +131,9 @@ async function reportSpecificFilterIssue() { ); githubURL.searchParams.set('category', issueType); - const manifest = runtime.getManifest(); - const rulesets = await dnr.getEnabledRulesets(); - const defaultMode = await sendMessage({ what: 'getDefaultFilteringMode' }); - const modes = [ 'no filtering', 'basic', 'optimal', 'complete' ]; - const config = { - name: manifest.name, - version: manifest.version, - filtering: { - 'site': `${modes[reportedPage.mode]}`, - 'default': `${modes[defaultMode]}`, - }, - rulesets, - }; const configBody = [ '```yaml', - renderData(config), + qs$('[data-i18n="supportS5H"] + pre').textContent, '```', '', ].join('\n'); @@ -132,7 +143,9 @@ async function reportSpecificFilterIssue() { /******************************************************************************/ -(async ( ) => { +getConfigData().then(config => { + qs$('[data-i18n="supportS5H"] + pre').textContent = config; + dom.on('[data-url]', 'click', ev => { const elem = ev.target.closest('[data-url]'); const url = dom.attr(elem, 'data-url'); @@ -154,5 +167,4 @@ async function reportSpecificFilterIssue() { ev.preventDefault(); }); } - -})(); +}); diff --git a/platform/mv3/extension/report.html b/platform/mv3/extension/report.html index 21997e936eefc..963549aae2343 100644 --- a/platform/mv3/extension/report.html +++ b/platform/mv3/extension/report.html @@ -16,7 +16,7 @@
-

+


@@ -49,6 +49,12 @@

+
+
+
+

+    
+
From 68a256bdde5d55ed952f9c3c6017dcca2daacc43 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Tue, 25 Mar 2025 12:12:00 -0400 Subject: [PATCH 0735/1099] Improve `trusted-prevent-dom-bypass` scriptlet --- src/js/resources/scriptlets.js | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/js/resources/scriptlets.js b/src/js/resources/scriptlets.js index ba196f85ab606..ab77be71179a3 100755 --- a/src/js/resources/scriptlets.js +++ b/src/js/resources/scriptlets.js @@ -2686,7 +2686,16 @@ function trustedPreventDomBypass( } } if ( targetProp !== '' ) { - elem.contentWindow[targetProp] = self[targetProp]; + let me = self, it = elem.contentWindow; + let chain = targetProp; + for (;;) { + const pos = chain.indexOf('.'); + if ( pos === -1 ) { break; } + const prop = chain.slice(0, pos) + me = me[prop]; it = it[prop]; + chain = chain.slice(pos+1); + } + it[chain] = me[chain]; } else { Object.defineProperty(elem, 'contentWindow', { value: self }); } From 419786feb732ab206e073acf6f9691b7f80596a8 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Tue, 25 Mar 2025 12:14:08 -0400 Subject: [PATCH 0736/1099] Update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index ec90fec1a550d..f9722d7a5cf31 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,4 @@ +- [Improve `trusted-prevent-dom-bypass` scriptlet](https://github.com/gorhill/uBlock/commit/68a256bdde) - [Add `jsonl-prune-xhr-response`/`jsonl-prune-fetch-response` scriptlets](https://github.com/gorhill/uBlock/commit/95a3be9d56) - [Improve `[json-prune|trusted-replace]-fetch-response` scriptlets](https://github.com/gorhill/uBlock/commit/88fa550a96) From 231269808d22875a8ad8e96277cc78ad7b104515 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Tue, 25 Mar 2025 12:14:27 -0400 Subject: [PATCH 0737/1099] New revision for dev build --- dist/version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dist/version b/dist/version index 6b7717df3af83..511af2e32f898 100644 --- a/dist/version +++ b/dist/version @@ -1 +1 @@ -1.63.3.2 +1.63.3.3 From 69ff3ca6fba562d1867a3b6d51fe66be1b017c45 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Tue, 25 Mar 2025 12:21:04 -0400 Subject: [PATCH 0738/1099] Make Firefox dev build auto-update --- dist/firefox/updates.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dist/firefox/updates.json b/dist/firefox/updates.json index 117490dc224d1..b091baf9d152a 100644 --- a/dist/firefox/updates.json +++ b/dist/firefox/updates.json @@ -3,9 +3,9 @@ "uBlock0@raymondhill.net": { "updates": [ { - "version": "1.63.3.2", + "version": "1.63.3.3", "browser_specific_settings": { "gecko": { "strict_min_version": "92.0" } }, - "update_link": "https://github.com/gorhill/uBlock/releases/download/1.63.3b2/uBlock0_1.63.3b2.firefox.signed.xpi" + "update_link": "https://github.com/gorhill/uBlock/releases/download/1.63.3b3/uBlock0_1.63.3b3.firefox.signed.xpi" } ] } From 760bd23c5e13f9c4c3590ad8d7185d1eb178aa20 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Tue, 25 Mar 2025 12:29:46 -0400 Subject: [PATCH 0739/1099] Minor --- src/js/resources/scriptlets.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/js/resources/scriptlets.js b/src/js/resources/scriptlets.js index ab77be71179a3..e9ec6717199ed 100755 --- a/src/js/resources/scriptlets.js +++ b/src/js/resources/scriptlets.js @@ -2691,7 +2691,7 @@ function trustedPreventDomBypass( for (;;) { const pos = chain.indexOf('.'); if ( pos === -1 ) { break; } - const prop = chain.slice(0, pos) + const prop = chain.slice(0, pos); me = me[prop]; it = it[prop]; chain = chain.slice(pos+1); } From d7ae3a185eddeae0f12d07149c1f0ddd11fd0c47 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Tue, 25 Mar 2025 12:58:26 -0400 Subject: [PATCH 0740/1099] [mv3] Test filters are trusted --- platform/mv3/make-rulesets.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform/mv3/make-rulesets.js b/platform/mv3/make-rulesets.js index 0a8e34c82626d..c13793a653088 100644 --- a/platform/mv3/make-rulesets.js +++ b/platform/mv3/make-rulesets.js @@ -235,7 +235,7 @@ async function fetchList(assetDetails) { } fetchedURLs.add(part.url); if ( - assetDetails.trustedSource || + assetDetails.trusted || part.url.startsWith('https://ublockorigin.github.io/uAssets/filters/') ) { newParts.push(`!#trusted on ${secret}`); From 59364510828e7f003657b605bf884368aab2c537 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Wed, 26 Mar 2025 07:28:00 -0400 Subject: [PATCH 0741/1099] [mv3] Fix toggling of "no filtering" as default mode A bad test prevented the "no filtering" by default mode to not take effect immediately when activated while no site had yet been set to "no filtering". It would however take effect as soon as a specific site would be excluded from "no filtering". --- platform/mv3/extension/js/ruleset-manager.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/platform/mv3/extension/js/ruleset-manager.js b/platform/mv3/extension/js/ruleset-manager.js index 6667b6deaef61..eee885f1349d7 100644 --- a/platform/mv3/extension/js/ruleset-manager.js +++ b/platform/mv3/extension/js/ruleset-manager.js @@ -559,7 +559,8 @@ async function filteringModesToDNR(modes) { return Promise.all(promises); } -const isDifferentAllowRules = (a, b) => { +const isDifferentAllowRules = (a = [], b = []) => { + if ( a.length !== b.length ) { return true; } const pp = [ 'requestDomains', 'excludedRequestDomains', @@ -567,8 +568,8 @@ const isDifferentAllowRules = (a, b) => { 'excludedInitiatorDomains', ]; for ( const p of pp ) { - const ac = a?.length && a[0].condition[p] || []; - const bc = b?.length && b[0].condition[p] || []; + const ac = a.length && a[0].condition[p] || []; + const bc = b.length && b[0].condition[p] || []; if ( ac.join() !== bc.join() ) { return true; } } return false; From d5fd1de15047dad838eeda9991ac0375f1346776 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Wed, 26 Mar 2025 11:47:56 -0400 Subject: [PATCH 0742/1099] Use JSONPath-like syntax for new `jsonl-` scriptlets --- src/js/jsonpath.js | 332 +++++++++++++++++++++++++++++ src/js/resources/json-prune.js | 36 +--- src/js/resources/jsonl-prune.js | 95 +++------ src/js/resources/parse-replace.js | 6 +- src/js/resources/shared.js | 18 +- src/js/scriptlet-filtering-core.js | 29 ++- tools/make-nodejs.sh | 63 +++--- 7 files changed, 435 insertions(+), 144 deletions(-) create mode 100644 src/js/jsonpath.js diff --git a/src/js/jsonpath.js b/src/js/jsonpath.js new file mode 100644 index 0000000000000..04cb95cddf83a --- /dev/null +++ b/src/js/jsonpath.js @@ -0,0 +1,332 @@ +/******************************************************************************* + + uBlock Origin - a comprehensive, efficient content blocker + Copyright (C) 2025-present Raymond Hill + + 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/}. + + Home: https://github.com/gorhill/uBlock + +*/ + +/******************************************************************************/ + +export class JSONPath { + static create(query) { + const jsonp = new JSONPath(); + jsonp.compile(query); + return jsonp; + } + compile(query) { + this.#compiled = this.#compile(query, 0); + return this.#compiled ? this.#compiled.i : 0; + } + evaluate(root) { + if ( this.#compiled === undefined ) { return []; } + this.root = root; + return this.#evaluate(this.#compiled.steps, []); + } + resolvePath(path) { + if ( path.length === 0 ) { return { value: this.root }; } + const key = path.at(-1); + let obj = this.root + for ( let i = 0, n = path.length-1; i < n; i++ ) { + obj = obj[path[i]]; + } + return { obj, key, value: obj[key] }; + } + toString() { + return JSON.stringify(this.#compiled); + } + #UNDEFINED = 0; + #ROOT = 1; + #CURRENT = 2; + #CHILDREN = 3; + #DESCENDANTS = 4; + #reUnquotedIdentifier = /^[A-Za-z_][\w]*|^\*/; + #reExpr = /^([!=^$*]=|[<>]=?)(.+?)\)\]/; + #reIndice = /^\[-?\d+\]/; + #compiled; + #compile(query, i) { + if ( query.length === 0 ) { return; } + const steps = []; + let c = query.charCodeAt(i); + steps.push({ mv: c === 0x24 /* $ */ ? this.#ROOT : this.#CURRENT }); + if ( c === 0x24 /* $ */ || c === 0x40 /* @ */ ) { i += 1; } + let mv = this.#UNDEFINED; + for (;;) { + if ( i === query.length ) { break; } + c = query.charCodeAt(i); + if ( c === 0x20 /* whitespace */ ) { + i += 1; + continue; + } + // Dot accessor syntax + if ( c === 0x2E /* . */ ) { + if ( mv !== this.#UNDEFINED ) { return; } + if ( query.startsWith('..', i) ) { + mv = this.#DESCENDANTS; + i += 2; + } else { + mv = this.#CHILDREN; + i += 1; + } + continue; + } + if ( c !== 0x5B /* [ */ ) { + if ( mv === this.#UNDEFINED ) { + const step = steps.at(-1); + if ( step === undefined ) { return; } + i = this.#compileExpr(step, query, i); + break; + } + const s = this.#consumeUnquotedIdentifier(query, i); + if ( s === undefined ) { return; } + steps.push({ mv, k: s }); + i += s.length; + mv = this.#UNDEFINED; + continue; + } + // Bracket accessor syntax + if ( query.startsWith('[*]', i) ) { + mv ||= this.#CHILDREN; + steps.push({ mv, k: '*' }); + i += 3; + mv = this.#UNDEFINED; + continue; + } + if ( query.startsWith("['", i) ) { + const r = this.#consumeQuotedIdentifier(query, i+2); + if ( r === undefined ) { return; } + mv ||= this.#CHILDREN; + steps.push({ mv, k: r.s }); + i = r.i; + mv = this.#UNDEFINED; + continue; + } + if ( query.startsWith('[?(', i) ) { + const not = query.charCodeAt(i+3) === 0x21 /* ! */; + const j = i + 3 + (not ? 1 : 0); + const r = this.#compile(query, j); + if ( r === undefined ) { return; } + if ( query.startsWith(')]', r.i) === false ) { return; } + if ( not ) { r.steps.at(-1).not = true; } + steps.push({ mv: mv || this.#CHILDREN, steps: r.steps }); + i = r.i + 2; + mv = this.#UNDEFINED; + continue; + } + if ( this.#reIndice.test(query.slice(i)) ) { + const match = this.#reIndice.exec(query.slice(i)); + const indice = parseInt(query.slice(i+1), 10); + mv ||= this.CHILDREN; + steps.push({ mv, k: indice }); + i += match[0].length; + mv = this.#UNDEFINED; + continue; + } + return; + } + if ( steps.length <= 1 ) { return; } + return { steps, i }; + } + #evaluate(steps, pathin) { + let resultset = []; + if ( Array.isArray(steps) === false ) { return resultset; } + for ( const step of steps ) { + switch ( step.mv ) { + case this.#ROOT: + resultset = [ [] ]; + break; + case this.#CURRENT: + resultset = [ pathin ]; + break; + case this.#CHILDREN: + case this.#DESCENDANTS: + resultset = this.#getMatches(resultset, step); + break; + default: + break; + } + } + return resultset; + } + #getMatches(listin, step) { + const listout = []; + const recursive = step.mv === this.#DESCENDANTS; + for ( const pathin of listin ) { + const { value: v } = this.resolvePath(pathin); + if ( v === null ) { continue; } + if ( v === undefined ) { continue; } + const { steps, k } = step; + if ( k ) { + if ( k === '*' ) { + const entries = Array.from(this.#getDescendants(v, recursive)); + for ( const { path } of entries ) { + this.#evaluateExpr(step, [ ...pathin, ...path ], listout); + } + continue; + } + if ( typeof k === 'number' ) { + if ( Array.isArray(v) === false ) { continue; } + const n = v.length; + const i = k >= 0 ? k : n + k; + if ( i < 0 ) { continue; } + if ( i >= n ) { continue; } + this.#evaluateExpr(step, [ ...pathin, i ], listout); + } else if ( Array.isArray(k) ) { + for ( const l of k ) { + this.#evaluateExpr(step, [ ...pathin, l ], listout); + } + } else { + this.#evaluateExpr(step, [ ...pathin, k ], listout); + } + if ( recursive !== true ) { continue; } + for ( const { obj, key, path } of this.#getDescendants(v, recursive) ) { + const w = obj[key]; + if ( w instanceof Object === false ) { continue; } + if ( Object.hasOwn(w, k) === false ) { continue; } + this.#evaluateExpr(step, [ ...pathin, ...path, k ], listout); + } + continue; + } + if ( steps ) { + const isArray = Array.isArray(v); + if ( isArray === false ) { + const r = this.#evaluate(steps, pathin); + if ( r.length !== 0 ) { + listout.push(pathin); + } + if ( recursive !== true ) { continue; } + } + for ( const { obj, key, path } of this.#getDescendants(v, recursive) ) { + const w = obj[key]; + if ( Array.isArray(w) ) { continue; } + const x = [ ...pathin, ...path ]; + const r = this.#evaluate(steps, x); + if ( r.length !== 0 ) { + listout.push(x); + } + } + } + } + return listout; + } + #getDescendants(v, recursive) { + const iterator = { + next() { + const n = this.stack.length; + if ( n === 0 ) { + this.value = undefined; + this.done = true; + return this; + } + const details = this.stack[n-1]; + const entry = details.keys.next(); + if ( entry.done ) { + this.stack.pop(); + this.path.pop(); + return this.next(); + } + this.path[n-1] = entry.value; + this.value = { + obj: details.obj, + key: entry.value, + path: this.path.slice(), + }; + const v = this.value.obj[this.value.key]; + if ( recursive ) { + if ( Array.isArray(v) ) { + this.stack.push({ obj: v, keys: v.keys() }); + } else if ( typeof v === 'object' && v !== null ) { + this.stack.push({ obj: v, keys: Object.keys(v).values() }); + } + } + return this; + }, + path: [], + value: undefined, + done: false, + stack: [], + [Symbol.iterator]() { return this; }, + }; + if ( Array.isArray(v) ) { + iterator.stack.push({ obj: v, keys: v.keys() }); + } else if ( typeof v === 'object' && v !== null ) { + iterator.stack.push({ obj: v, keys: Object.keys(v).values() }); + } + return iterator; + } + #consumeQuotedIdentifier(query, i) { + const len = query.length; + const parts = []; + let beg = i, end = i; + for (;;) { + if ( end === len ) { return; } + const c = query.charCodeAt(end); + if ( c === 0x27 /* ' */ ) { + if ( query.startsWith("']", end) === false ) { return; } + parts.push(query.slice(beg, end)); + end += 2; + break; + } + if ( c === 0x5C /* \ */ && (end+1) < len ) { + parts.push(query.slice(beg, end)); + const d = query.chatCodeAt(end+1); + if ( d === 0x27 || d === 0x5C ) { + end += 1; + beg = end; + } + } + end += 1; + } + return { s: parts.join(''), i: end }; + } + #consumeUnquotedIdentifier(query, i) { + const match = this.#reUnquotedIdentifier.exec(query.slice(i)); + if ( match === null ) { return; } + return match[0]; + } + #compileExpr(step, query, i) { + const match = this.#reExpr.exec(query.slice(i)); + if ( match === null ) { return i; } + try { + step.rval = JSON.parse(match[2]); + step.op = match[1]; + } catch { + } + return i + match[1].length + match[2].length; + } + #evaluateExpr(step, path, out) { + const { obj: o, key: k } = this.resolvePath(path); + const hasOwn = o instanceof Object && Object.hasOwn(o, k); + const v = o[k]; + let outcome = true; + if ( step.op !== undefined && hasOwn === false ) { return; } + switch ( step.op ) { + case '==': outcome = v === step.rval; break; + case '!=': outcome = v !== step.rval; break; + case '<': outcome = v < step.rval; break; + case '<=': outcome = v <= step.rval; break; + case '>': outcome = v > step.rval; break; + case '>=': outcome = v >= step.rval; break; + case '^=': outcome = `${v}`.startsWith(step.rval); break; + case '$=': outcome = `${v}`.endsWith(step.rval); break; + case '*=': outcome = `${v}`.includes(step.rval); break; + default: outcome = hasOwn; break; + } + if ( outcome === (step.not === true) ) { return; } + out.push(path); + } +} diff --git a/src/js/resources/json-prune.js b/src/js/resources/json-prune.js index 62f134473827d..08284e3132499 100644 --- a/src/js/resources/json-prune.js +++ b/src/js/resources/json-prune.js @@ -73,7 +73,7 @@ registerScriptlet(jsonPrune, { /******************************************************************************/ -function jsonPruneFetchResponseFn( +function jsonPruneFetchResponse( rawPrunePaths = '', rawNeedlePaths = '' ) { @@ -145,8 +145,8 @@ function jsonPruneFetchResponseFn( apply: applyHandler }); } -registerScriptlet(jsonPruneFetchResponseFn, { - name: 'json-prune-fetch-response.fn', +registerScriptlet(jsonPruneFetchResponse, { + name: 'json-prune-fetch-response.js', dependencies: [ matchObjectPropertiesFn, objectPruneFn, @@ -157,19 +157,7 @@ registerScriptlet(jsonPruneFetchResponseFn, { /******************************************************************************/ -function jsonPruneFetchResponse(...args) { - jsonPruneFetchResponseFn(...args); -} -registerScriptlet(jsonPruneFetchResponse, { - name: 'json-prune-fetch-response.js', - dependencies: [ - jsonPruneFetchResponseFn, - ], -}); - -/******************************************************************************/ - -function jsonPruneXhrResponseFn( +function jsonPruneXhrResponse( rawPrunePaths = '', rawNeedlePaths = '' ) { @@ -250,8 +238,8 @@ function jsonPruneXhrResponseFn( } }; } -registerScriptlet(jsonPruneXhrResponseFn, { - name: 'json-prune-xhr-response.fn', +registerScriptlet(jsonPruneXhrResponse, { + name: 'json-prune-xhr-response.js', dependencies: [ matchObjectPropertiesFn, objectPruneFn, @@ -262,18 +250,6 @@ registerScriptlet(jsonPruneXhrResponseFn, { /******************************************************************************/ -function jsonPruneXhrResponse(...args) { - jsonPruneXhrResponseFn(...args); -} -registerScriptlet(jsonPruneXhrResponse, { - name: 'json-prune-xhr-response.js', - dependencies: [ - jsonPruneXhrResponseFn, - ], -}); - -/******************************************************************************/ - // There is still code out there which uses `eval` in lieu of `JSON.parse`. function evaldataPrune( diff --git a/src/js/resources/jsonl-prune.js b/src/js/resources/jsonl-prune.js index d6268233246a7..be1f824375462 100644 --- a/src/js/resources/jsonl-prune.js +++ b/src/js/resources/jsonl-prune.js @@ -25,6 +25,7 @@ import { parsePropertiesToMatchFn, } from './utils.js'; +import { JSONPath } from './shared.js'; import { objectPruneFn } from './object-prune.js'; import { registerScriptlet } from './base.js'; import { safeSelf } from './safe-self.js'; @@ -32,36 +33,38 @@ import { safeSelf } from './safe-self.js'; /******************************************************************************/ function jsonlPruneFn( - text = '', - rawPrunePaths = '', - rawNeedlePaths = '' + jsonp, + text = '' ) { const safe = safeSelf(); const linesBefore = text.split(/\n+/); const linesAfter = []; for ( const lineBefore of linesBefore ) { - let objBefore; + let obj; try { - objBefore = safe.JSON_parse(lineBefore); + obj = safe.JSON_parse(lineBefore); } catch { } - if ( typeof objBefore !== 'object' ) { + if ( typeof obj !== 'object' || obj === null ) { linesAfter.push(lineBefore); continue; } - const objAfter = objectPruneFn(objBefore, rawPrunePaths, rawNeedlePaths); - if ( typeof objAfter !== 'object' ) { + const paths = jsonp.evaluate(obj); + if ( paths.length === 0 ) { linesAfter.push(lineBefore); continue; } - linesAfter.push(safe.JSON_stringify(objAfter).replace(/\//g, '\\/')); + for ( const path of paths ) { + const { obj, key } = jsonp.resolvePath(path); + delete obj[key]; + } + linesAfter.push(safe.JSON_stringify(obj).replace(/\//g, '\\/')); } return linesAfter.join('\n'); } registerScriptlet(jsonlPruneFn, { name: 'jsonl-prune.fn', dependencies: [ - objectPruneFn, safeSelf, ], }); @@ -74,11 +77,8 @@ registerScriptlet(jsonlPruneFn, { * @description * Prune the objects found in a JSONL resource fetched through a XHR instance. * - * @param rawPrunePaths - * The property to remove from the objects. - * - * @param rawNeedlePaths - * A property which must be present for the pruning to take effect. + * @param jsonq + * A uBO-flavored JSONPath query. * * @param [propsToMatch, value] * An optional vararg detailing the arguments to match when xhr.open() is @@ -86,14 +86,14 @@ registerScriptlet(jsonlPruneFn, { * * */ -function jsonlPruneXhrResponseFn( - rawPrunePaths = '', - rawNeedlePaths = '' +function jsonlPruneXhrResponse( + jsonq = '', ) { const safe = safeSelf(); - const logPrefix = safe.makeLogPrefix('jsonl-prune-xhr-response', rawPrunePaths, rawNeedlePaths); + const logPrefix = safe.makeLogPrefix('jsonl-prune-xhr-response', jsonq); const xhrInstances = new WeakMap(); - const extraArgs = safe.getExtraArgs(Array.from(arguments), 2); + const jsonp = JSONPath.create(jsonq); + const extraArgs = safe.getExtraArgs(Array.from(arguments), 1); const propNeedles = parsePropertiesToMatchFn(extraArgs.propsToMatch, 'url'); self.XMLHttpRequest = class extends self.XMLHttpRequest { open(method, url, ...args) { @@ -127,7 +127,7 @@ function jsonlPruneXhrResponseFn( if ( typeof innerResponse !== 'string' ) { return (xhrDetails.response = innerResponse); } - const outerResponse = jsonlPruneFn(innerResponse, rawPrunePaths, rawNeedlePaths); + const outerResponse = jsonlPruneFn(jsonp, innerResponse); if ( outerResponse !== innerResponse ) { safe.uboLog(logPrefix, 'Pruned'); } @@ -141,9 +141,10 @@ function jsonlPruneXhrResponseFn( } }; } -registerScriptlet(jsonlPruneXhrResponseFn, { - name: 'jsonl-prune-xhr-response.fn', +registerScriptlet(jsonlPruneXhrResponse, { + name: 'jsonl-prune-xhr-response.js', dependencies: [ + JSONPath, jsonlPruneFn, matchObjectPropertiesFn, parsePropertiesToMatchFn, @@ -153,18 +154,6 @@ registerScriptlet(jsonlPruneXhrResponseFn, { /******************************************************************************/ -function jsonlPruneXhrResponse(...args) { - jsonlPruneXhrResponseFn(...args); -} -registerScriptlet(jsonlPruneXhrResponse, { - name: 'jsonl-prune-xhr-response.js', - dependencies: [ - jsonlPruneXhrResponseFn, - ], -}); - -/******************************************************************************/ - /** * @scriptlet jsonl-prune-fetch-response.js * @@ -172,11 +161,8 @@ registerScriptlet(jsonlPruneXhrResponse, { * Prune the objects found in a JSONL resource fetched through the fetch API. * Once the pruning is performed. * - * @param rawPrunePaths - * The property to remove from the objects. - * - * @param rawNeedlePaths - * A property which must be present for the pruning to take effect. + * @param jsonq + * A uBO-flavored JSONPath query. * * @param [propsToMatch, value] * An optional vararg detailing the arguments to match when xhr.open() is @@ -184,15 +170,15 @@ registerScriptlet(jsonlPruneXhrResponse, { * * */ -function jsonlPruneFetchResponseFn( - rawPrunePaths = '', - rawNeedlePaths = '' +function jsonlPruneFetchResponse( + jsonq = '' ) { const safe = safeSelf(); - const logPrefix = safe.makeLogPrefix('jsonl-prune-fetch-response', rawPrunePaths, rawNeedlePaths); + const logPrefix = safe.makeLogPrefix('jsonl-prune-fetch-response', jsonq); + const jsonp = JSONPath.create(jsonq); const extraArgs = safe.getExtraArgs(Array.from(arguments), 2); const propNeedles = parsePropertiesToMatchFn(extraArgs.propsToMatch, 'url'); - const logall = rawPrunePaths === ''; + const logall = jsonq === ''; const applyHandler = function(target, thisArg, args) { const fetchPromise = Reflect.apply(target, thisArg, args); if ( propNeedles.size !== 0 ) { @@ -221,7 +207,7 @@ function jsonlPruneFetchResponseFn( safe.uboLog(logPrefix, textBefore); return responseBefore; } - const textAfter = jsonlPruneFn(textBefore, rawPrunePaths, rawNeedlePaths); + const textAfter = jsonlPruneFn(jsonp, textBefore); if ( textAfter === textBefore ) { return responseBefore; } safe.uboLog(logPrefix, 'Pruned'); const responseAfter = new Response(textAfter, { @@ -249,9 +235,10 @@ function jsonlPruneFetchResponseFn( apply: applyHandler }); } -registerScriptlet(jsonlPruneFetchResponseFn, { - name: 'jsonl-prune-fetch-response.fn', +registerScriptlet(jsonlPruneFetchResponse, { + name: 'jsonl-prune-fetch-response.js', dependencies: [ + JSONPath, jsonlPruneFn, matchObjectPropertiesFn, parsePropertiesToMatchFn, @@ -260,15 +247,3 @@ registerScriptlet(jsonlPruneFetchResponseFn, { }); /******************************************************************************/ - -function jsonlPruneFetchResponse(...args) { - jsonlPruneFetchResponseFn(...args); -} -registerScriptlet(jsonlPruneFetchResponse, { - name: 'jsonl-prune-fetch-response.js', - dependencies: [ - jsonlPruneFetchResponseFn, - ], -}); - -/******************************************************************************/ diff --git a/src/js/resources/parse-replace.js b/src/js/resources/parse-replace.js index e7fd488e9d7a2..604644f476a37 100644 --- a/src/js/resources/parse-replace.js +++ b/src/js/resources/parse-replace.js @@ -20,14 +20,14 @@ */ -import { createArglistParser } from './shared.js'; +import { ArglistParser } from './shared.js'; import { registerScriptlet } from './base.js'; /******************************************************************************/ export function parseReplaceFn(s) { if ( s.charCodeAt(0) !== 0x2F /* / */ ) { return; } - const parser = createArglistParser('/'); + const parser = new ArglistParser('/'); parser.nextArg(s, 1); let pattern = s.slice(parser.argBeg, parser.argEnd); if ( parser.transform ) { @@ -49,6 +49,6 @@ export function parseReplaceFn(s) { registerScriptlet(parseReplaceFn, { name: 'parse-replace.fn', dependencies: [ - createArglistParser, + ArglistParser, ], }); diff --git a/src/js/resources/shared.js b/src/js/resources/shared.js index 9a38fca48105b..01a9de7180033 100644 --- a/src/js/resources/shared.js +++ b/src/js/resources/shared.js @@ -21,24 +21,20 @@ */ // Code imported from main code base and exposed as injectable scriptlets -import { ArglistParser } from '../arglist-parser.js'; - +import { ArglistParser as __ArglistParser__ } from '../arglist-parser.js'; +import { JSONPath as __JSONPath__ } from '../jsonpath.js'; import { registerScriptlet } from './base.js'; /******************************************************************************/ +export const ArglistParser = __ArglistParser__; + registerScriptlet(ArglistParser, { name: 'arglist-parser.fn', }); -/******************************************************************************/ +export const JSONPath = __JSONPath__; -export function createArglistParser(...args) { - return new ArglistParser(...args); -} -registerScriptlet(createArglistParser, { - name: 'create-arglist-parser.fn', - dependencies: [ - ArglistParser, - ], +registerScriptlet(JSONPath, { + name: 'jsonpath.fn', }); diff --git a/src/js/scriptlet-filtering-core.js b/src/js/scriptlet-filtering-core.js index 89b06ca05f0d8..b6d7c0be2b662 100644 --- a/src/js/scriptlet-filtering-core.js +++ b/src/js/scriptlet-filtering-core.js @@ -35,6 +35,14 @@ const $isolatedWorldMap = new Map(); /******************************************************************************/ +// For debugging convenience: all the top function calls will appear +// at the bottom of a generated content script +const codeSorter = (a, b) => { + if ( a.startsWith('try') ) { return 1; } + if ( b.startsWith('try') ) { return -1; } + return 0; +}; + const normalizeRawFilter = (parser, sourceIsTrusted = false) => { const args = parser.getScriptletArgs(); if ( args.length !== 0 ) { @@ -59,6 +67,7 @@ const lookupScriptlet = (rawToken, mainMap, isolatedMap, debug = false) => { const details = reng.contentFromName(token, 'text/javascript'); if ( details === undefined ) { return; } const targetWorldMap = details.world !== 'ISOLATED' ? mainMap : isolatedMap; + targetWorldMap.set(token, details.js); const content = patchScriptlet(details.js, args.slice(1)); const dependencies = details.dependencies || []; while ( dependencies.length !== 0 ) { @@ -73,22 +82,22 @@ const lookupScriptlet = (rawToken, mainMap, isolatedMap, debug = false) => { } targetWorldMap.set(rawToken, [ 'try {', - '// >>>> scriptlet start', - content, - '// <<<< scriptlet end', + `\t${content}`, '} catch (e) {', - debug ? 'console.error(e);' : '', + debug ? '\tconsole.error(e);' : '', '}', ].join('\n')); }; // Fill-in scriptlet argument placeholders. const patchScriptlet = (content, arglist) => { - if ( content.startsWith('function') && content.endsWith('}') ) { - content = `(${content})({{args}});`; - } - for ( let i = 0; i < arglist.length; i++ ) { - content = content.replace(`{{${i+1}}}`, arglist[i]); + const match = /^function\s+([^(\s]+)\s*\(/.exec(content); + if ( match ) { + content = `${match[1]}({{args}});`; + } else { + for ( let i = 0; i < arglist.length; i++ ) { + content = content.replace(`{{${i+1}}}`, arglist[i]); + } } return content.replace('{{args}}', JSON.stringify(arglist).slice(1,-1).replace(/\$/g, '$$$') @@ -249,11 +258,13 @@ export class ScriptletFilteringEngine { for ( const js of $mainWorldMap.values() ) { mainWorldCode.push(js); } + mainWorldCode.sort(codeSorter); const isolatedWorldCode = []; for ( const js of $isolatedWorldMap.values() ) { isolatedWorldCode.push(js); } + isolatedWorldCode.sort(codeSorter); const scriptletDetails = { mainWorld: mainWorldCode.join('\n\n'), diff --git a/tools/make-nodejs.sh b/tools/make-nodejs.sh index ed8db6cbbf77a..fb0a7a65f3f64 100755 --- a/tools/make-nodejs.sh +++ b/tools/make-nodejs.sh @@ -6,41 +6,42 @@ set -e DES=$1 -mkdir -p $DES/js -cp src/js/arglist-parser.js $DES/js -cp src/js/base64-custom.js $DES/js -cp src/js/biditrie.js $DES/js -cp src/js/dynamic-net-filtering.js $DES/js -cp src/js/filtering-context.js $DES/js -cp src/js/hnswitches.js $DES/js -cp src/js/hntrie.js $DES/js -cp src/js/redirect-resources.js $DES/js -cp src/js/s14e-serializer.js $DES/js -cp src/js/static-dnr-filtering.js $DES/js -cp src/js/static-filtering-parser.js $DES/js -cp src/js/static-net-filtering.js $DES/js -cp src/js/static-filtering-io.js $DES/js -cp src/js/tasks.js $DES/js -cp src/js/text-utils.js $DES/js -cp src/js/urlskip.js $DES/js -cp src/js/uri-utils.js $DES/js -cp src/js/url-net-filtering.js $DES/js +mkdir -p "$DES/js" +cp src/js/arglist-parser.js "$DES/js" +cp src/js/base64-custom.js "$DES/js" +cp src/js/biditrie.js "$DES/js" +cp src/js/dynamic-net-filtering.js "$DES/js" +cp src/js/filtering-context.js "$DES/js" +cp src/js/hnswitches.js "$DES/js" +cp src/js/hntrie.js "$DES/js" +cp src/js/jsonpath.js "$DES/js" +cp src/js/redirect-resources.js "$DES/js" +cp src/js/s14e-serializer.js "$DES/js" +cp src/js/static-dnr-filtering.js "$DES/js" +cp src/js/static-filtering-parser.js "$DES/js" +cp src/js/static-net-filtering.js "$DES/js" +cp src/js/static-filtering-io.js "$DES/js" +cp src/js/tasks.js "$DES/js" +cp src/js/text-utils.js "$DES/js" +cp src/js/urlskip.js "$DES/js" +cp src/js/uri-utils.js "$DES/js" +cp src/js/url-net-filtering.js "$DES/js" -mkdir -p $DES/lib -cp -R src/lib/csstree $DES/lib/ -cp -R src/lib/punycode.js $DES/lib/ -cp -R src/lib/regexanalyzer $DES/lib/ -cp -R src/lib/publicsuffixlist $DES/lib/ +mkdir -p "$DES/lib/" +cp -R src/lib/csstree "$DES/lib/" +cp -R src/lib/punycode.js "$DES/lib/" +cp -R src/lib/regexanalyzer "$DES/lib/" +cp -R src/lib/publicsuffixlist "$DES/lib/" # Convert wasm modules into json arrays -mkdir -p $DES/js/wasm -cp src/js/wasm/* $DES/js/wasm/ +mkdir -p "$DES/js/wasm" +cp src/js/wasm/* "$DES/js/wasm" node -pe "JSON.stringify(Array.from(fs.readFileSync('src/js/wasm/hntrie.wasm')))" \ - > $DES/js/wasm/hntrie.wasm.json + > "$DES/js/wasm/hntrie.wasm.json" node -pe "JSON.stringify(Array.from(fs.readFileSync('src/js/wasm/biditrie.wasm')))" \ - > $DES/js/wasm/biditrie.wasm.json + > "$DES/js/wasm/biditrie.wasm.json" node -pe "JSON.stringify(Array.from(fs.readFileSync('src/lib/publicsuffixlist/wasm/publicsuffixlist.wasm')))" \ - > $DES/lib/publicsuffixlist/wasm/publicsuffixlist.wasm.json + > "$DES/lib/publicsuffixlist/wasm/publicsuffixlist.wasm.json" -cp platform/nodejs/*.js $DES/ -cp LICENSE.txt $DES/ +cp platform/nodejs/*.js "$DES/" +cp LICENSE.txt "$DES/" From 0e303060dd873e947a8b9b00dbd4711b0745620a Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Thu, 27 Mar 2025 09:11:47 -0400 Subject: [PATCH 0743/1099] [mv3] Fix CSS for dark theme Related feedback: https://github.com/uBlockOrigin/uBOL-home/discussions/291#discussioncomment-12403559 --- platform/mv3/extension/css/matched-rules.css | 3 +++ 1 file changed, 3 insertions(+) diff --git a/platform/mv3/extension/css/matched-rules.css b/platform/mv3/extension/css/matched-rules.css index 9db4d9c73f0fa..177f312730db3 100644 --- a/platform/mv3/extension/css/matched-rules.css +++ b/platform/mv3/extension/css/matched-rules.css @@ -16,6 +16,9 @@ .matchInfo:nth-of-type(2n) { background-color: lightgray; } +html.dark .matchInfo:nth-of-type(2n) { + background-color: #444; +} .requestInfo { border-inline-end: 1px dotted black; From 0a13683167e258dd57ba7751f6eb48115cf37f07 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Thu, 27 Mar 2025 22:47:25 -0400 Subject: [PATCH 0744/1099] [mv3] Remove pointless promise --- platform/mv3/extension/js/background.js | 39 ++++++++++++------------- 1 file changed, 18 insertions(+), 21 deletions(-) diff --git a/platform/mv3/extension/js/background.js b/platform/mv3/extension/js/background.js index 374f771d253c5..b2ae6a33fa3ab 100644 --- a/platform/mv3/extension/js/background.js +++ b/platform/mv3/extension/js/background.js @@ -526,28 +526,25 @@ async function start() { /******************************************************************************/ -const isFullyInitialized = new Promise(resolve => { - // https://github.com/uBlockOrigin/uBOL-home/issues/199 - // Force a restart of the extension once when an "internal error" occurs - start().then(( ) => { - localRemove('goodStart'); - return false; - }).catch(reason => { - console.trace(reason); - if ( process.wakeupRun ) { return; } - return localRead('goodStart').then(goodStart => { - if ( goodStart === false ) { - localRemove('goodStart'); - return false; - } - return localWrite('goodStart', false).then(( ) => true); - }); - }).then(restart => { - if ( restart !== true ) { return; } - runtime.reload(); - }).finally(( ) => { - resolve(true); +// https://github.com/uBlockOrigin/uBOL-home/issues/199 +// Force a restart of the extension once when an "internal error" occurs + +const isFullyInitialized = start().then(( ) => { + localRemove('goodStart'); + return false; +}).catch(reason => { + console.trace(reason); + if ( process.wakeupRun ) { return; } + return localRead('goodStart').then(goodStart => { + if ( goodStart === false ) { + localRemove('goodStart'); + return false; + } + return localWrite('goodStart', false).then(( ) => true); }); +}).then(restart => { + if ( restart !== true ) { return; } + runtime.reload(); }); runtime.onMessage.addListener((request, sender, callback) => { From d7f01065a5fa117c120ca88de22e9c94523a82a1 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Thu, 27 Mar 2025 22:47:58 -0400 Subject: [PATCH 0745/1099] New revision for dev build --- dist/version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dist/version b/dist/version index 511af2e32f898..07bd8b1dd2154 100644 --- a/dist/version +++ b/dist/version @@ -1 +1 @@ -1.63.3.3 +1.63.3.4 From 1ce00e4fda42a4fb9aa54c5538fd4985351bc17f Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Thu, 27 Mar 2025 22:51:48 -0400 Subject: [PATCH 0746/1099] Import translation work from https://crowdin.com/project/ublock --- platform/mv3/extension/_locales/ar/messages.json | 2 +- platform/mv3/extension/_locales/be/messages.json | 6 +++--- platform/mv3/extension/_locales/bg/messages.json | 2 +- platform/mv3/extension/_locales/br_FR/messages.json | 6 +++--- platform/mv3/extension/_locales/ca/messages.json | 2 +- platform/mv3/extension/_locales/da/messages.json | 2 +- platform/mv3/extension/_locales/de/messages.json | 2 +- platform/mv3/extension/_locales/el/messages.json | 2 +- platform/mv3/extension/_locales/es/messages.json | 2 +- platform/mv3/extension/_locales/et/messages.json | 6 +++--- platform/mv3/extension/_locales/fr/messages.json | 2 +- platform/mv3/extension/_locales/fy/messages.json | 2 +- platform/mv3/extension/_locales/gl/messages.json | 2 +- platform/mv3/extension/_locales/he/messages.json | 2 +- platform/mv3/extension/_locales/hr/messages.json | 2 +- platform/mv3/extension/_locales/hu/messages.json | 2 +- platform/mv3/extension/_locales/it/messages.json | 2 +- platform/mv3/extension/_locales/ja/messages.json | 2 +- platform/mv3/extension/_locales/ka/messages.json | 2 +- platform/mv3/extension/_locales/lv/messages.json | 2 +- platform/mv3/extension/_locales/nl/messages.json | 2 +- platform/mv3/extension/_locales/pl/messages.json | 2 +- platform/mv3/extension/_locales/pt_BR/messages.json | 2 +- platform/mv3/extension/_locales/ru/messages.json | 2 +- platform/mv3/extension/_locales/sk/messages.json | 2 +- platform/mv3/extension/_locales/sq/messages.json | 2 +- platform/mv3/extension/_locales/sv/messages.json | 2 +- platform/mv3/extension/_locales/tr/messages.json | 2 +- platform/mv3/extension/_locales/uk/messages.json | 2 +- platform/mv3/extension/_locales/zh_TW/messages.json | 2 +- src/_locales/el/messages.json | 2 +- src/_locales/et/messages.json | 6 +++--- 32 files changed, 40 insertions(+), 40 deletions(-) diff --git a/platform/mv3/extension/_locales/ar/messages.json b/platform/mv3/extension/_locales/ar/messages.json index de7c2b2041a17..78866f5f5f8eb 100644 --- a/platform/mv3/extension/_locales/ar/messages.json +++ b/platform/mv3/extension/_locales/ar/messages.json @@ -112,7 +112,7 @@ "description": "First paragraph of 'Filter issues' section in Support pane" }, "supportS5H": { - "message": "Troubleshooting information", + "message": "معلومات استكشاف وإصلاح الأخطاء", "description": "Label of 'Troubleshooting information' section in 'Report a filter issue' page" }, "supportS6P1S1": { diff --git a/platform/mv3/extension/_locales/be/messages.json b/platform/mv3/extension/_locales/be/messages.json index a0c4b891f7376..8356b89729760 100644 --- a/platform/mv3/extension/_locales/be/messages.json +++ b/platform/mv3/extension/_locales/be/messages.json @@ -112,7 +112,7 @@ "description": "First paragraph of 'Filter issues' section in Support pane" }, "supportS5H": { - "message": "Troubleshooting information", + "message": "Дыягнастычныя звесткі", "description": "Label of 'Troubleshooting information' section in 'Report a filter issue' page" }, "supportS6P1S1": { @@ -284,11 +284,11 @@ "description": "A button to navigate to the blocked page" }, "zapperTipEnter": { - "message": "Enter element zapper mode", + "message": "Перайсці ў рэжым імгненнага хавання элементаў", "description": "Tooltip for the button used to enter zapper mode" }, "zapperTipQuit": { - "message": "Exit element zapper mode", + "message": "Выйсці з рэжыму імгненнага хавання элементаў", "description": "Tooltip for the button used to exit zapper mode" } } diff --git a/platform/mv3/extension/_locales/bg/messages.json b/platform/mv3/extension/_locales/bg/messages.json index 8301ba3084fc9..cd21e2933e617 100644 --- a/platform/mv3/extension/_locales/bg/messages.json +++ b/platform/mv3/extension/_locales/bg/messages.json @@ -112,7 +112,7 @@ "description": "First paragraph of 'Filter issues' section in Support pane" }, "supportS5H": { - "message": "Troubleshooting information", + "message": "Информация за отстраняване на неизправности", "description": "Label of 'Troubleshooting information' section in 'Report a filter issue' page" }, "supportS6P1S1": { diff --git a/platform/mv3/extension/_locales/br_FR/messages.json b/platform/mv3/extension/_locales/br_FR/messages.json index bcfebb11d9717..cf8bafec648a2 100644 --- a/platform/mv3/extension/_locales/br_FR/messages.json +++ b/platform/mv3/extension/_locales/br_FR/messages.json @@ -112,7 +112,7 @@ "description": "First paragraph of 'Filter issues' section in Support pane" }, "supportS5H": { - "message": "Troubleshooting information", + "message": "Titouroù diagnostikañ kudennoù", "description": "Label of 'Troubleshooting information' section in 'Report a filter issue' page" }, "supportS6P1S1": { @@ -284,11 +284,11 @@ "description": "A button to navigate to the blocked page" }, "zapperTipEnter": { - "message": "Enter element zapper mode", + "message": "Mont er mod \"dilemel elfennoù\"", "description": "Tooltip for the button used to enter zapper mode" }, "zapperTipQuit": { - "message": "Exit element zapper mode", + "message": "Kuitaat ar mod \"dilemel elfennoù\"", "description": "Tooltip for the button used to exit zapper mode" } } diff --git a/platform/mv3/extension/_locales/ca/messages.json b/platform/mv3/extension/_locales/ca/messages.json index 1f3ef5f418d0b..26875ec37fc5c 100644 --- a/platform/mv3/extension/_locales/ca/messages.json +++ b/platform/mv3/extension/_locales/ca/messages.json @@ -112,7 +112,7 @@ "description": "First paragraph of 'Filter issues' section in Support pane" }, "supportS5H": { - "message": "Troubleshooting information", + "message": "Informació de resolució de problemes", "description": "Label of 'Troubleshooting information' section in 'Report a filter issue' page" }, "supportS6P1S1": { diff --git a/platform/mv3/extension/_locales/da/messages.json b/platform/mv3/extension/_locales/da/messages.json index 21382d36e318c..b6c284ff49452 100644 --- a/platform/mv3/extension/_locales/da/messages.json +++ b/platform/mv3/extension/_locales/da/messages.json @@ -112,7 +112,7 @@ "description": "First paragraph of 'Filter issues' section in Support pane" }, "supportS5H": { - "message": "Troubleshooting information", + "message": "Fejlfindingsinformation", "description": "Label of 'Troubleshooting information' section in 'Report a filter issue' page" }, "supportS6P1S1": { diff --git a/platform/mv3/extension/_locales/de/messages.json b/platform/mv3/extension/_locales/de/messages.json index 64d64f36fff65..baa6ad539582c 100644 --- a/platform/mv3/extension/_locales/de/messages.json +++ b/platform/mv3/extension/_locales/de/messages.json @@ -112,7 +112,7 @@ "description": "First paragraph of 'Filter issues' section in Support pane" }, "supportS5H": { - "message": "Troubleshooting information", + "message": "Informationen zur Fehlerbehebung", "description": "Label of 'Troubleshooting information' section in 'Report a filter issue' page" }, "supportS6P1S1": { diff --git a/platform/mv3/extension/_locales/el/messages.json b/platform/mv3/extension/_locales/el/messages.json index a662270516eae..05a2e9bf8fe60 100644 --- a/platform/mv3/extension/_locales/el/messages.json +++ b/platform/mv3/extension/_locales/el/messages.json @@ -112,7 +112,7 @@ "description": "First paragraph of 'Filter issues' section in Support pane" }, "supportS5H": { - "message": "Troubleshooting information", + "message": "Πληροφορίες για αντιμετώπιση προβλημάτων", "description": "Label of 'Troubleshooting information' section in 'Report a filter issue' page" }, "supportS6P1S1": { diff --git a/platform/mv3/extension/_locales/es/messages.json b/platform/mv3/extension/_locales/es/messages.json index 8eac5dbe0fcbd..c9d12d5c8e616 100644 --- a/platform/mv3/extension/_locales/es/messages.json +++ b/platform/mv3/extension/_locales/es/messages.json @@ -112,7 +112,7 @@ "description": "First paragraph of 'Filter issues' section in Support pane" }, "supportS5H": { - "message": "Troubleshooting information", + "message": "Información para solucionar problemas", "description": "Label of 'Troubleshooting information' section in 'Report a filter issue' page" }, "supportS6P1S1": { diff --git a/platform/mv3/extension/_locales/et/messages.json b/platform/mv3/extension/_locales/et/messages.json index 9db0bc5141bb5..79527ffe0aff7 100644 --- a/platform/mv3/extension/_locales/et/messages.json +++ b/platform/mv3/extension/_locales/et/messages.json @@ -112,7 +112,7 @@ "description": "First paragraph of 'Filter issues' section in Support pane" }, "supportS5H": { - "message": "Troubleshooting information", + "message": "Tõrkeotsingu teave", "description": "Label of 'Troubleshooting information' section in 'Report a filter issue' page" }, "supportS6P1S1": { @@ -120,7 +120,7 @@ "description": "A paragraph in the filter issue reporter section" }, "supportFindSpecificButton": { - "message": "Leidke sarnased aruanded", + "message": "Leia GitHubist sarnaseid aruandeid", "description": "A clickable link in the filter issue reporter section" }, "supportS6URL": { @@ -168,7 +168,7 @@ "description": "A checkbox to use for NSFW sites" }, "supportReportSpecificButton": { - "message": "Loo uus aruanne", + "message": "Loo GitHubis uus aruanne", "description": "Text for button which open an external webpage in Support pane" }, "firstRunSectionLabel": { diff --git a/platform/mv3/extension/_locales/fr/messages.json b/platform/mv3/extension/_locales/fr/messages.json index 58676ba7eafa7..10ff6d5f1a0ab 100644 --- a/platform/mv3/extension/_locales/fr/messages.json +++ b/platform/mv3/extension/_locales/fr/messages.json @@ -112,7 +112,7 @@ "description": "First paragraph of 'Filter issues' section in Support pane" }, "supportS5H": { - "message": "Troubleshooting information", + "message": "Informations de dépannage", "description": "Label of 'Troubleshooting information' section in 'Report a filter issue' page" }, "supportS6P1S1": { diff --git a/platform/mv3/extension/_locales/fy/messages.json b/platform/mv3/extension/_locales/fy/messages.json index 7b17476fce91d..428511d8e70e9 100644 --- a/platform/mv3/extension/_locales/fy/messages.json +++ b/platform/mv3/extension/_locales/fy/messages.json @@ -112,7 +112,7 @@ "description": "First paragraph of 'Filter issues' section in Support pane" }, "supportS5H": { - "message": "Troubleshooting information", + "message": "Probleemoplossingsynformaasje", "description": "Label of 'Troubleshooting information' section in 'Report a filter issue' page" }, "supportS6P1S1": { diff --git a/platform/mv3/extension/_locales/gl/messages.json b/platform/mv3/extension/_locales/gl/messages.json index 0496a611ba6c9..61bbb87dfc423 100644 --- a/platform/mv3/extension/_locales/gl/messages.json +++ b/platform/mv3/extension/_locales/gl/messages.json @@ -112,7 +112,7 @@ "description": "First paragraph of 'Filter issues' section in Support pane" }, "supportS5H": { - "message": "Troubleshooting information", + "message": "Información para arranxar problemas", "description": "Label of 'Troubleshooting information' section in 'Report a filter issue' page" }, "supportS6P1S1": { diff --git a/platform/mv3/extension/_locales/he/messages.json b/platform/mv3/extension/_locales/he/messages.json index 067d6127ae1d7..77899093e3161 100644 --- a/platform/mv3/extension/_locales/he/messages.json +++ b/platform/mv3/extension/_locales/he/messages.json @@ -112,7 +112,7 @@ "description": "First paragraph of 'Filter issues' section in Support pane" }, "supportS5H": { - "message": "Troubleshooting information", + "message": "מידע לפתרון בעיות", "description": "Label of 'Troubleshooting information' section in 'Report a filter issue' page" }, "supportS6P1S1": { diff --git a/platform/mv3/extension/_locales/hr/messages.json b/platform/mv3/extension/_locales/hr/messages.json index 704ab9e7ddba0..e1e35f04fee10 100644 --- a/platform/mv3/extension/_locales/hr/messages.json +++ b/platform/mv3/extension/_locales/hr/messages.json @@ -112,7 +112,7 @@ "description": "First paragraph of 'Filter issues' section in Support pane" }, "supportS5H": { - "message": "Troubleshooting information", + "message": "Informacije o rješavanju problema", "description": "Label of 'Troubleshooting information' section in 'Report a filter issue' page" }, "supportS6P1S1": { diff --git a/platform/mv3/extension/_locales/hu/messages.json b/platform/mv3/extension/_locales/hu/messages.json index 1a50d68f51af2..1600c174f992e 100644 --- a/platform/mv3/extension/_locales/hu/messages.json +++ b/platform/mv3/extension/_locales/hu/messages.json @@ -112,7 +112,7 @@ "description": "First paragraph of 'Filter issues' section in Support pane" }, "supportS5H": { - "message": "Troubleshooting information", + "message": "Hibakeresési információk", "description": "Label of 'Troubleshooting information' section in 'Report a filter issue' page" }, "supportS6P1S1": { diff --git a/platform/mv3/extension/_locales/it/messages.json b/platform/mv3/extension/_locales/it/messages.json index 1fa37dd4dff85..b8a1fbd64e2ed 100644 --- a/platform/mv3/extension/_locales/it/messages.json +++ b/platform/mv3/extension/_locales/it/messages.json @@ -112,7 +112,7 @@ "description": "First paragraph of 'Filter issues' section in Support pane" }, "supportS5H": { - "message": "Troubleshooting information", + "message": "Informazioni sulla risoluzione dei problemi", "description": "Label of 'Troubleshooting information' section in 'Report a filter issue' page" }, "supportS6P1S1": { diff --git a/platform/mv3/extension/_locales/ja/messages.json b/platform/mv3/extension/_locales/ja/messages.json index fca925a9e7bda..5fd12f9b29be1 100644 --- a/platform/mv3/extension/_locales/ja/messages.json +++ b/platform/mv3/extension/_locales/ja/messages.json @@ -112,7 +112,7 @@ "description": "First paragraph of 'Filter issues' section in Support pane" }, "supportS5H": { - "message": "Troubleshooting information", + "message": "トラブルシューティング情報", "description": "Label of 'Troubleshooting information' section in 'Report a filter issue' page" }, "supportS6P1S1": { diff --git a/platform/mv3/extension/_locales/ka/messages.json b/platform/mv3/extension/_locales/ka/messages.json index 12762611e04a6..7dddbc748d9ca 100644 --- a/platform/mv3/extension/_locales/ka/messages.json +++ b/platform/mv3/extension/_locales/ka/messages.json @@ -112,7 +112,7 @@ "description": "First paragraph of 'Filter issues' section in Support pane" }, "supportS5H": { - "message": "Troubleshooting information", + "message": "მონაცემები ხარვეზის მოსაგვარებლად", "description": "Label of 'Troubleshooting information' section in 'Report a filter issue' page" }, "supportS6P1S1": { diff --git a/platform/mv3/extension/_locales/lv/messages.json b/platform/mv3/extension/_locales/lv/messages.json index c18dffd242d3f..ac139385e3849 100644 --- a/platform/mv3/extension/_locales/lv/messages.json +++ b/platform/mv3/extension/_locales/lv/messages.json @@ -112,7 +112,7 @@ "description": "First paragraph of 'Filter issues' section in Support pane" }, "supportS5H": { - "message": "Troubleshooting information", + "message": "Traucējummeklēšanas informācija", "description": "Label of 'Troubleshooting information' section in 'Report a filter issue' page" }, "supportS6P1S1": { diff --git a/platform/mv3/extension/_locales/nl/messages.json b/platform/mv3/extension/_locales/nl/messages.json index 65e7d6c78fbfd..0b5d14e7ea66b 100644 --- a/platform/mv3/extension/_locales/nl/messages.json +++ b/platform/mv3/extension/_locales/nl/messages.json @@ -112,7 +112,7 @@ "description": "First paragraph of 'Filter issues' section in Support pane" }, "supportS5H": { - "message": "Troubleshooting information", + "message": "Probleemoplossingsinformatie", "description": "Label of 'Troubleshooting information' section in 'Report a filter issue' page" }, "supportS6P1S1": { diff --git a/platform/mv3/extension/_locales/pl/messages.json b/platform/mv3/extension/_locales/pl/messages.json index 39d5455687d7f..23e8f0893392d 100644 --- a/platform/mv3/extension/_locales/pl/messages.json +++ b/platform/mv3/extension/_locales/pl/messages.json @@ -112,7 +112,7 @@ "description": "First paragraph of 'Filter issues' section in Support pane" }, "supportS5H": { - "message": "Troubleshooting information", + "message": "Informacje pomocne w rozwiązywaniu problemów", "description": "Label of 'Troubleshooting information' section in 'Report a filter issue' page" }, "supportS6P1S1": { diff --git a/platform/mv3/extension/_locales/pt_BR/messages.json b/platform/mv3/extension/_locales/pt_BR/messages.json index 1f73def3ce140..378ca22fca1b5 100644 --- a/platform/mv3/extension/_locales/pt_BR/messages.json +++ b/platform/mv3/extension/_locales/pt_BR/messages.json @@ -112,7 +112,7 @@ "description": "First paragraph of 'Filter issues' section in Support pane" }, "supportS5H": { - "message": "Troubleshooting information", + "message": "Informações para solução de problemas", "description": "Label of 'Troubleshooting information' section in 'Report a filter issue' page" }, "supportS6P1S1": { diff --git a/platform/mv3/extension/_locales/ru/messages.json b/platform/mv3/extension/_locales/ru/messages.json index 0673524765941..7b58ba8d014f6 100644 --- a/platform/mv3/extension/_locales/ru/messages.json +++ b/platform/mv3/extension/_locales/ru/messages.json @@ -112,7 +112,7 @@ "description": "First paragraph of 'Filter issues' section in Support pane" }, "supportS5H": { - "message": "Troubleshooting information", + "message": "Диагностическая информация", "description": "Label of 'Troubleshooting information' section in 'Report a filter issue' page" }, "supportS6P1S1": { diff --git a/platform/mv3/extension/_locales/sk/messages.json b/platform/mv3/extension/_locales/sk/messages.json index f0e32d280dc97..238047561ea8f 100644 --- a/platform/mv3/extension/_locales/sk/messages.json +++ b/platform/mv3/extension/_locales/sk/messages.json @@ -112,7 +112,7 @@ "description": "First paragraph of 'Filter issues' section in Support pane" }, "supportS5H": { - "message": "Troubleshooting information", + "message": "Informácie o riešení problémov", "description": "Label of 'Troubleshooting information' section in 'Report a filter issue' page" }, "supportS6P1S1": { diff --git a/platform/mv3/extension/_locales/sq/messages.json b/platform/mv3/extension/_locales/sq/messages.json index 62eaa259e7672..4073398c0b5fa 100644 --- a/platform/mv3/extension/_locales/sq/messages.json +++ b/platform/mv3/extension/_locales/sq/messages.json @@ -112,7 +112,7 @@ "description": "First paragraph of 'Filter issues' section in Support pane" }, "supportS5H": { - "message": "Troubleshooting information", + "message": "Diagnostikimi i problemeve", "description": "Label of 'Troubleshooting information' section in 'Report a filter issue' page" }, "supportS6P1S1": { diff --git a/platform/mv3/extension/_locales/sv/messages.json b/platform/mv3/extension/_locales/sv/messages.json index 0eed6cab597b0..2d0c3c0364b61 100644 --- a/platform/mv3/extension/_locales/sv/messages.json +++ b/platform/mv3/extension/_locales/sv/messages.json @@ -112,7 +112,7 @@ "description": "First paragraph of 'Filter issues' section in Support pane" }, "supportS5H": { - "message": "Troubleshooting information", + "message": "Felsökningsinformation", "description": "Label of 'Troubleshooting information' section in 'Report a filter issue' page" }, "supportS6P1S1": { diff --git a/platform/mv3/extension/_locales/tr/messages.json b/platform/mv3/extension/_locales/tr/messages.json index e56e66d38f5c0..4a2e10955184d 100644 --- a/platform/mv3/extension/_locales/tr/messages.json +++ b/platform/mv3/extension/_locales/tr/messages.json @@ -112,7 +112,7 @@ "description": "First paragraph of 'Filter issues' section in Support pane" }, "supportS5H": { - "message": "Troubleshooting information", + "message": "Sorun giderme bilgisi", "description": "Label of 'Troubleshooting information' section in 'Report a filter issue' page" }, "supportS6P1S1": { diff --git a/platform/mv3/extension/_locales/uk/messages.json b/platform/mv3/extension/_locales/uk/messages.json index a90042cc5bd96..c651e7a186faf 100644 --- a/platform/mv3/extension/_locales/uk/messages.json +++ b/platform/mv3/extension/_locales/uk/messages.json @@ -112,7 +112,7 @@ "description": "First paragraph of 'Filter issues' section in Support pane" }, "supportS5H": { - "message": "Troubleshooting information", + "message": "Усунення проблем", "description": "Label of 'Troubleshooting information' section in 'Report a filter issue' page" }, "supportS6P1S1": { diff --git a/platform/mv3/extension/_locales/zh_TW/messages.json b/platform/mv3/extension/_locales/zh_TW/messages.json index 1371636d42aa1..3cf2ea88327d8 100644 --- a/platform/mv3/extension/_locales/zh_TW/messages.json +++ b/platform/mv3/extension/_locales/zh_TW/messages.json @@ -112,7 +112,7 @@ "description": "First paragraph of 'Filter issues' section in Support pane" }, "supportS5H": { - "message": "Troubleshooting information", + "message": "疑難排解資訊", "description": "Label of 'Troubleshooting information' section in 'Report a filter issue' page" }, "supportS6P1S1": { diff --git a/src/_locales/el/messages.json b/src/_locales/el/messages.json index b6c745952d8d3..e3b4f50801c01 100644 --- a/src/_locales/el/messages.json +++ b/src/_locales/el/messages.json @@ -948,7 +948,7 @@ "description": "First paragraph of 'Bug report' section in Support pane" }, "supportS5H": { - "message": "Πληροφορίες αντιμετώπισης προβλημάτων", + "message": "Πληροφορίες για αντιμετώπιση προβλημάτων", "description": "Header of 'Troubleshooting Information' section in Support pane" }, "supportS5P1": { diff --git a/src/_locales/et/messages.json b/src/_locales/et/messages.json index 0829551848ef9..e7eef4870c81a 100644 --- a/src/_locales/et/messages.json +++ b/src/_locales/et/messages.json @@ -900,11 +900,11 @@ "description": "Text for button which open an external webpage in Support pane" }, "supportReportSpecificButton": { - "message": "Loo uus aruanne", + "message": "Loo GitHubis uus aruanne", "description": "Text for button which open an external webpage in Support pane" }, "supportFindSpecificButton": { - "message": "Leia sarnaseid aruandeid", + "message": "Leia GitHubist sarnaseid aruandeid", "description": "A clickable link in the filter issue reporter section" }, "supportS1H": { @@ -964,7 +964,7 @@ "description": "Header of 'Report a filter issue' section in Support pane" }, "supportS6P1S1": { - "message": "Vabatahtlike koormuse vähendamiseks veendu, et probleemi pole juba teatatud.", + "message": "Vabatahtlike koormuse vähendamiseks palun veendu, et probleemi ei ole juba teatatud. Märkus: nupu klõpsamine saadab lehe aadressi GitHubi.", "description": "A paragraph in the filter issue reporter section" }, "supportS6P2S1": { From b18daa53aaa7c1cbe4f6d9acb09e8a165a3562c9 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Fri, 28 Mar 2025 09:40:22 -0400 Subject: [PATCH 0747/1099] Add `json-edit` suite of scriptlets; extend `replace=` option Scriptlets added: - json-edit - trusted-json-edit - json-edit-xhr-response - trusted-json-edit-xhr-response - json-edit-fetch-response - trusted-json-edit-fetch-response - jsonl-edit-xhr-response - trusted-jsonl-edit-xhr-response - jsonl-edit-fetch-response - trusted-jsonl-edit-fetch-response These scriptlets are functionally similar to their `json-prune` counterpart, except that they all use the new uBO-flavored JSONPath syntax, and the `trusted-` versions allow to modify values instead of just removing them. The `replace=` filter option has been extended to support applying uBO-flavored JSONPath syntax to the response body. If the `replace=` value starts with `json:` or `jsonl:`, the remaining of the value will be interpreted as a JSONPath directive, which can be used to either remove or modify property in a JSON document. --- src/js/jsonpath.js | 82 +++- src/js/resources/json-edit.js | 666 ++++++++++++++++++++++++++++++ src/js/resources/jsonl-prune.js | 249 ----------- src/js/resources/scriptlets.js | 2 +- src/js/static-filtering-parser.js | 22 +- src/js/static-net-filtering.js | 2 +- src/js/traffic.js | 57 ++- 7 files changed, 801 insertions(+), 279 deletions(-) create mode 100644 src/js/resources/json-edit.js delete mode 100644 src/js/resources/jsonl-prune.js diff --git a/src/js/jsonpath.js b/src/js/jsonpath.js index 04cb95cddf83a..7f022d6e7a1dd 100644 --- a/src/js/jsonpath.js +++ b/src/js/jsonpath.js @@ -28,26 +28,62 @@ export class JSONPath { jsonp.compile(query); return jsonp; } + static toJSON(obj, stringifier, ...args) { + return (stringifier || JSON.stringify)(obj, ...args) + .replace(/\//g, '\\/'); + } + get value() { + return this.#compiled && this.#compiled.rval; + } + set value(v) { + if ( this.#compiled === undefined ) { return; } + this.#compiled.rval = v; + } + get valid() { + return this.#compiled !== undefined; + } compile(query) { - this.#compiled = this.#compile(query, 0); - return this.#compiled ? this.#compiled.i : 0; + const r = this.#compile(query, 0); + if ( r === undefined ) { return; } + if ( r.i !== query.length ) { + if ( query.startsWith('=', r.i) === false ) { return; } + try { r.rval = JSON.parse(query.slice(r.i+1)); } + catch { return; } + } + this.#compiled = r; } evaluate(root) { - if ( this.#compiled === undefined ) { return []; } - this.root = root; - return this.#evaluate(this.#compiled.steps, []); + if ( this.valid === false ) { return []; } + this.#root = root; + const paths = this.#evaluate(this.#compiled.steps, []); + this.#root = null; + return paths; } - resolvePath(path) { - if ( path.length === 0 ) { return { value: this.root }; } - const key = path.at(-1); - let obj = this.root - for ( let i = 0, n = path.length-1; i < n; i++ ) { - obj = obj[path[i]]; + apply(root) { + if ( this.valid === false ) { return 0; } + const { rval } = this.#compiled; + this.#root = root; + const paths = this.#evaluate(this.#compiled.steps, []); + const n = paths.length; + let i = n; + while ( i-- ) { + const { obj, key } = this.#resolvePath(paths[i]); + if ( rval !== undefined ) { + obj[key] = rval; + } else if ( Array.isArray(obj) && typeof key === 'number' ) { + obj.splice(key, 1); + } else { + delete obj[key]; + } } - return { obj, key, value: obj[key] }; + this.#root = null; + return n; + } + toJSON(obj, ...args) { + return JSONPath.toJSON(obj, null, ...args) } - toString() { - return JSON.stringify(this.#compiled); + get [Symbol.toStringTag]() { + return 'JSONPath'; } #UNDEFINED = 0; #ROOT = 1; @@ -57,6 +93,7 @@ export class JSONPath { #reUnquotedIdentifier = /^[A-Za-z_][\w]*|^\*/; #reExpr = /^([!=^$*]=|[<>]=?)(.+?)\)\]/; #reIndice = /^\[-?\d+\]/; + #root; #compiled; #compile(query, i) { if ( query.length === 0 ) { return; } @@ -88,7 +125,7 @@ export class JSONPath { if ( mv === this.#UNDEFINED ) { const step = steps.at(-1); if ( step === undefined ) { return; } - i = this.#compileExpr(step, query, i); + i = this.#compileExpr(query, step, i); break; } const s = this.#consumeUnquotedIdentifier(query, i); @@ -166,7 +203,7 @@ export class JSONPath { const listout = []; const recursive = step.mv === this.#DESCENDANTS; for ( const pathin of listin ) { - const { value: v } = this.resolvePath(pathin); + const { value: v } = this.#resolvePath(pathin); if ( v === null ) { continue; } if ( v === undefined ) { continue; } const { steps, k } = step; @@ -298,7 +335,7 @@ export class JSONPath { if ( match === null ) { return; } return match[0]; } - #compileExpr(step, query, i) { + #compileExpr(query, step, i) { const match = this.#reExpr.exec(query.slice(i)); if ( match === null ) { return i; } try { @@ -308,8 +345,17 @@ export class JSONPath { } return i + match[1].length + match[2].length; } + #resolvePath(path) { + if ( path.length === 0 ) { return { value: this.#root }; } + const key = path.at(-1); + let obj = this.#root + for ( let i = 0, n = path.length-1; i < n; i++ ) { + obj = obj[path[i]]; + } + return { obj, key, value: obj[key] }; + } #evaluateExpr(step, path, out) { - const { obj: o, key: k } = this.resolvePath(path); + const { obj: o, key: k } = this.#resolvePath(path); const hasOwn = o instanceof Object && Object.hasOwn(o, k); const v = o[k]; let outcome = true; diff --git a/src/js/resources/json-edit.js b/src/js/resources/json-edit.js new file mode 100644 index 0000000000000..8a2af5928f215 --- /dev/null +++ b/src/js/resources/json-edit.js @@ -0,0 +1,666 @@ +/******************************************************************************* + + uBlock Origin - a comprehensive, efficient content blocker + Copyright (C) 2019-present Raymond Hill + + 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/}. + + Home: https://github.com/gorhill/uBlock + +*/ + +import { + matchObjectPropertiesFn, + parsePropertiesToMatchFn, +} from './utils.js'; + +import { JSONPath } from './shared.js'; +import { proxyApplyFn } from './proxy-apply.js'; +import { registerScriptlet } from './base.js'; +import { safeSelf } from './safe-self.js'; + +/******************************************************************************/ + +function jsonEditFn(trusted, jsonq = '') { + const safe = safeSelf(); + const logPrefix = safe.makeLogPrefix( + `${trusted ? 'trusted-' : ''}json-edit`, + jsonq + ); + const jsonp = JSONPath.create(jsonq); + if ( jsonp.valid === false || jsonp.value !== undefined && trusted !== true ) { + return safe.uboLog(logPrefix, 'Bad JSONPath query'); + } + proxyApplyFn('JSON.parse', function(context) { + const obj = context.reflect(); + if ( jsonp.apply(obj) !== 0 ) { return obj; } + safe.uboLog(logPrefix, 'Edited'); + if ( safe.logLevel > 1 ) { + safe.uboLog(logPrefix, `After edit:\n${safe.JSON_stringify(obj, null, 2)}`); + } + return obj; + }); +} +registerScriptlet(jsonEditFn, { + name: 'json-edit.fn', + dependencies: [ + JSONPath, + proxyApplyFn, + safeSelf, + ], +}); + +/******************************************************************************/ +/** + * @scriptlet json-edit.js + * + * @description + * Edit object generated through JSON.parse(). + * Properties can only be removed. + * + * @param jsonq + * A uBO-flavored JSONPath query. + * + * */ + +function jsonEdit(jsonq = '') { + jsonEditFn(false, jsonq); +} +registerScriptlet(jsonEdit, { + name: 'json-edit.js', + dependencies: [ + jsonEditFn, + ], +}); + +/******************************************************************************/ +/** + * @scriptlet trusted-json-edit.js + * + * @description + * Edit object generated through JSON.parse(). + * Properties can be assigned new values. + * + * @param jsonq + * A uBO-flavored JSONPath query. + * + * */ + +function trustedJsonEdit(jsonq = '') { + jsonEditFn(true, jsonq); +} +registerScriptlet(trustedJsonEdit, { + name: 'trusted-json-edit.js', + requiresTrust: true, + dependencies: [ + jsonEditFn, + ], +}); + +/******************************************************************************/ +/******************************************************************************/ + +function jsonEditXhrResponseFn(trusted, jsonq = '') { + const safe = safeSelf(); + const logPrefix = safe.makeLogPrefix( + `${trusted ? 'trusted-' : ''}json-edit-xhr-response`, + jsonq + ); + const xhrInstances = new WeakMap(); + const jsonp = JSONPath.create(jsonq); + if ( jsonp.valid === false || jsonp.value !== undefined && trusted !== true ) { + return safe.uboLog(logPrefix, 'Bad JSONPath query'); + } + const extraArgs = safe.getExtraArgs(Array.from(arguments), 2); + const propNeedles = parsePropertiesToMatchFn(extraArgs.propsToMatch, 'url'); + self.XMLHttpRequest = class extends self.XMLHttpRequest { + open(method, url, ...args) { + const xhrDetails = { method, url }; + const matched = propNeedles.size === 0 || + matchObjectPropertiesFn(propNeedles, xhrDetails); + if ( matched ) { + if ( safe.logLevel > 1 && Array.isArray(matched) ) { + safe.uboLog(logPrefix, `Matched "propsToMatch":\n\t${matched.join('\n\t')}`); + } + xhrInstances.set(this, xhrDetails); + } + return super.open(method, url, ...args); + } + get response() { + const innerResponse = super.response; + const xhrDetails = xhrInstances.get(this); + if ( xhrDetails === undefined ) { return innerResponse; } + const responseLength = typeof innerResponse === 'string' + ? innerResponse.length + : undefined; + if ( xhrDetails.lastResponseLength !== responseLength ) { + xhrDetails.response = undefined; + xhrDetails.lastResponseLength = responseLength; + } + if ( xhrDetails.response !== undefined ) { + return xhrDetails.response; + } + let obj; + if ( typeof innerResponse === 'object' ) { + obj = innerResponse; + } else if ( typeof innerResponse === 'string' ) { + try { obj = safe.JSON_parse(innerResponse); } catch { } + } + if ( typeof obj !== 'object' || obj === null || jsonp.apply(obj) === 0 ) { + return (xhrDetails.response = innerResponse); + } + safe.uboLog(logPrefix, 'Edited'); + const outerResponse = typeof innerResponse === 'string' + ? JSONPath.toJSON(obj, safe.JSON_stringify) + : obj; + return (xhrDetails.response = outerResponse); + } + get responseText() { + const response = this.response; + return typeof response !== 'string' + ? super.responseText + : response; + } + }; +} +registerScriptlet(jsonEditXhrResponseFn, { + name: 'json-edit-xhr-response.fn', + dependencies: [ + JSONPath, + matchObjectPropertiesFn, + parsePropertiesToMatchFn, + safeSelf, + ], +}); + +/******************************************************************************/ +/** + * @scriptlet json-edit-xhr-response.js + * + * @description + * Edit the object fetched through a XHR instance. + * Properties can only be removed. + * + * @param jsonq + * A uBO-flavored JSONPath query. + * + * @param [propsToMatch, value] + * An optional vararg detailing the arguments to match when xhr.open() is + * called. + * + * */ + +function jsonEditXhrResponse(jsonq = '', ...args) { + jsonEditXhrResponseFn(false, jsonq, ...args); +} +registerScriptlet(jsonEditXhrResponse, { + name: 'json-edit-xhr-response.js', + dependencies: [ + jsonEditXhrResponseFn, + ], +}); + +/******************************************************************************/ +/** + * @scriptlet trusted-json-edit-xhr-response.js + * + * @description + * Edit the object fetched through a XHR instance. + * Properties can be assigned new values. + * + * @param jsonq + * A uBO-flavored JSONPath query. + * + * @param [propsToMatch, value] + * An optional vararg detailing the arguments to match when xhr.open() is + * called. + * + * */ + +function trustedJsonEditXhrResponse(jsonq = '', ...args) { + jsonEditXhrResponseFn(true, jsonq, ...args); +} +registerScriptlet(trustedJsonEditXhrResponse, { + name: 'trusted-json-edit-xhr-response.js', + requiresTrust: true, + dependencies: [ + jsonEditXhrResponseFn, + ], +}); + +/******************************************************************************/ +/******************************************************************************/ + +function jsonEditFetchResponseFn(trusted, jsonq = '') { + const safe = safeSelf(); + const logPrefix = safe.makeLogPrefix( + `${trusted ? 'trusted-' : ''}json-edit-fetch-response`, + jsonq + ); + const jsonp = JSONPath.create(jsonq); + if ( jsonp.valid === false || jsonp.value !== undefined && trusted !== true ) { + return safe.uboLog(logPrefix, 'Bad JSONPath query'); + } + const extraArgs = safe.getExtraArgs(Array.from(arguments), 2); + const propNeedles = parsePropertiesToMatchFn(extraArgs.propsToMatch, 'url'); + proxyApplyFn('fetch', function(context) { + const args = context.callArgs; + const fetchPromise = context.reflect(); + if ( propNeedles.size !== 0 ) { + const objs = [ args[0] instanceof Object ? args[0] : { url: args[0] } ]; + if ( objs[0] instanceof Request ) { + try { + objs[0] = safe.Request_clone.call(objs[0]); + } catch(ex) { + safe.uboErr(logPrefix, 'Error:', ex); + } + } + if ( args[1] instanceof Object ) { + objs.push(args[1]); + } + const matched = matchObjectPropertiesFn(propNeedles, ...objs); + if ( matched === undefined ) { return fetchPromise; } + if ( safe.logLevel > 1 ) { + safe.uboLog(logPrefix, `Matched "propsToMatch":\n\t${matched.join('\n\t')}`); + } + } + return fetchPromise.then(responseBefore => { + const response = responseBefore.clone(); + return response.json().then(obj => { + if ( typeof obj !== 'object' ) { return responseBefore; } + if ( jsonp.apply(obj) === 0 ) { return responseBefore; } + safe.uboLog(logPrefix, 'Edited'); + const responseAfter = Response.json(obj, { + status: responseBefore.status, + statusText: responseBefore.statusText, + headers: responseBefore.headers, + }); + Object.defineProperties(responseAfter, { + ok: { value: responseBefore.ok }, + redirected: { value: responseBefore.redirected }, + type: { value: responseBefore.type }, + url: { value: responseBefore.url }, + }); + return responseAfter; + }).catch(reason => { + safe.uboErr(logPrefix, 'Error:', reason); + return responseBefore; + }); + }).catch(reason => { + safe.uboErr(logPrefix, 'Error:', reason); + return fetchPromise; + }); + }); +} +registerScriptlet(jsonEditFetchResponseFn, { + name: 'json-edit-fetch-response.fn', + dependencies: [ + JSONPath, + matchObjectPropertiesFn, + parsePropertiesToMatchFn, + proxyApplyFn, + safeSelf, + ], +}); + +/******************************************************************************/ +/** + * @scriptlet json-edit-fetch-response.js + * + * @description + * Edit the object fetched through the fetch API. + * Properties can only be removed. + * + * @param jsonq + * A uBO-flavored JSONPath query. + * + * @param [propsToMatch, value] + * An optional vararg detailing the arguments to match when xhr.open() is + * called. + * + * */ + +function jsonEditFetchResponse(jsonq = '', ...args) { + jsonEditFetchResponseFn(false, jsonq, ...args); +} +registerScriptlet(jsonEditFetchResponse, { + name: 'json-edit-fetch-response.js', + dependencies: [ + jsonEditFetchResponseFn, + ], +}); + +/******************************************************************************/ +/** + * @scriptlet trusted-json-edit-fetch-response.js + * + * @description + * Edit the object fetched through the fetch API. The trusted version allows + * Properties can be assigned new values. + * + * @param jsonq + * A uBO-flavored JSONPath query. + * + * @param [propsToMatch, value] + * An optional vararg detailing the arguments to match when xhr.open() is + * called. + * + * */ + +function trustedJsonEditFetchResponse(jsonq = '', ...args) { + jsonEditFetchResponseFn(true, jsonq, ...args); +} +registerScriptlet(trustedJsonEditFetchResponse, { + name: 'trusted-json-edit-fetch-response.js', + requiresTrust: true, + dependencies: [ + jsonEditFetchResponseFn, + ], +}); + +/******************************************************************************/ +/******************************************************************************/ + +function jsonlEditFn(jsonp, text = '') { + const safe = safeSelf(); + const linesBefore = text.split(/\n+/); + const linesAfter = []; + for ( const lineBefore of linesBefore ) { + let obj; + try { obj = safe.JSON_parse(lineBefore); } catch { } + if ( typeof obj !== 'object' || obj === null ) { + linesAfter.push(lineBefore); + continue; + } + if ( jsonp.apply(obj) === 0 ) { + linesAfter.push(lineBefore); + continue; + } + linesAfter.push(JSONPath.toJSON(obj, safe.JSON_stringify)); + } + return linesAfter.join('\n'); +} +registerScriptlet(jsonlEditFn, { + name: 'jsonl-edit.fn', + dependencies: [ + JSONPath, + safeSelf, + ], +}); + +/******************************************************************************/ + +function jsonlEditXhrResponseFn(trusted, jsonq = '') { + const safe = safeSelf(); + const logPrefix = safe.makeLogPrefix( + `${trusted ? 'trusted-' : ''}jsonl-edit-xhr-response`, + jsonq + ); + const xhrInstances = new WeakMap(); + const jsonp = JSONPath.create(jsonq); + if ( jsonp.valid === false || jsonp.value !== undefined && trusted !== true ) { + return safe.uboLog(logPrefix, 'Bad JSONPath query'); + } + const extraArgs = safe.getExtraArgs(Array.from(arguments), 2); + const propNeedles = parsePropertiesToMatchFn(extraArgs.propsToMatch, 'url'); + self.XMLHttpRequest = class extends self.XMLHttpRequest { + open(method, url, ...args) { + const xhrDetails = { method, url }; + const matched = propNeedles.size === 0 || + matchObjectPropertiesFn(propNeedles, xhrDetails); + if ( matched ) { + if ( safe.logLevel > 1 && Array.isArray(matched) ) { + safe.uboLog(logPrefix, `Matched "propsToMatch":\n\t${matched.join('\n\t')}`); + } + xhrInstances.set(this, xhrDetails); + } + return super.open(method, url, ...args); + } + get response() { + const innerResponse = super.response; + const xhrDetails = xhrInstances.get(this); + if ( xhrDetails === undefined ) { + return innerResponse; + } + const responseLength = typeof innerResponse === 'string' + ? innerResponse.length + : undefined; + if ( xhrDetails.lastResponseLength !== responseLength ) { + xhrDetails.response = undefined; + xhrDetails.lastResponseLength = responseLength; + } + if ( xhrDetails.response !== undefined ) { + return xhrDetails.response; + } + if ( typeof innerResponse !== 'string' ) { + return (xhrDetails.response = innerResponse); + } + const outerResponse = jsonlEditFn(jsonp, innerResponse); + if ( outerResponse !== innerResponse ) { + safe.uboLog(logPrefix, 'Pruned'); + } + return (xhrDetails.response = outerResponse); + } + get responseText() { + const response = this.response; + return typeof response !== 'string' + ? super.responseText + : response; + } + }; +} +registerScriptlet(jsonlEditXhrResponseFn, { + name: 'jsonl-edit-xhr-response.fn', + dependencies: [ + JSONPath, + jsonlEditFn, + matchObjectPropertiesFn, + parsePropertiesToMatchFn, + safeSelf, + ], +}); + +/******************************************************************************/ +/** + * @scriptlet jsonl-edit-xhr-response.js + * + * @description + * Edit the objects found in a JSONL resource fetched through a XHR instance. + * Properties can only be removed. + * + * @param jsonq + * A uBO-flavored JSONPath query. + * + * @param [propsToMatch, value] + * An optional vararg detailing the arguments to match when xhr.open() is + * called. + * + * */ + +function jsonlEditXhrResponse(jsonq = '', ...args) { + jsonlEditXhrResponseFn(false, jsonq, ...args); +} +registerScriptlet(jsonlEditXhrResponse, { + name: 'jsonl-edit-xhr-response.js', + dependencies: [ + jsonlEditXhrResponseFn, + ], +}); + +/******************************************************************************/ +/** + * @scriptlet trusted-jsonl-edit-xhr-response.js + * + * @description + * Edit the objects found in a JSONL resource fetched through a XHR instance. + * Properties can be assigned new values. + * + * @param jsonq + * A uBO-flavored JSONPath query. + * + * @param [propsToMatch, value] + * An optional vararg detailing the arguments to match when xhr.open() is + * called. + * + * */ + +function trustedJsonlEditXhrResponse(jsonq = '', ...args) { + jsonlEditXhrResponseFn(true, jsonq, ...args); +} +registerScriptlet(trustedJsonlEditXhrResponse, { + name: 'trusted-jsonl-edit-xhr-response.js', + requiresTrust: true, + dependencies: [ + jsonlEditXhrResponseFn, + ], +}); + +/******************************************************************************/ +/******************************************************************************/ + +function jsonlEditFetchResponseFn(trusted, jsonq = '') { + const safe = safeSelf(); + const logPrefix = safe.makeLogPrefix( + `${trusted ? 'trusted-' : ''}jsonl-edit-fetch-response`, + jsonq + ); + const jsonp = JSONPath.create(jsonq); + if ( jsonp.valid === false || jsonp.value !== undefined && trusted !== true ) { + return safe.uboLog(logPrefix, 'Bad JSONPath query'); + } + const extraArgs = safe.getExtraArgs(Array.from(arguments), 2); + const propNeedles = parsePropertiesToMatchFn(extraArgs.propsToMatch, 'url'); + const logall = jsonq === ''; + proxyApplyFn('fetch', function(context) { + const args = context.callArgs; + const fetchPromise = context.reflect(); + if ( propNeedles.size !== 0 ) { + const objs = [ args[0] instanceof Object ? args[0] : { url: args[0] } ]; + if ( objs[0] instanceof Request ) { + try { + objs[0] = safe.Request_clone.call(objs[0]); + } catch(ex) { + safe.uboErr(logPrefix, 'Error:', ex); + } + } + if ( args[1] instanceof Object ) { + objs.push(args[1]); + } + const matched = matchObjectPropertiesFn(propNeedles, ...objs); + if ( matched === undefined ) { return fetchPromise; } + if ( safe.logLevel > 1 ) { + safe.uboLog(logPrefix, `Matched "propsToMatch":\n\t${matched.join('\n\t')}`); + } + } + return fetchPromise.then(responseBefore => { + const response = responseBefore.clone(); + return response.text().then(textBefore => { + if ( typeof textBefore !== 'string' ) { return textBefore; } + if ( logall ) { + safe.uboLog(logPrefix, textBefore); + return responseBefore; + } + const textAfter = jsonlEditFn(jsonp, textBefore); + if ( textAfter === textBefore ) { return responseBefore; } + safe.uboLog(logPrefix, 'Pruned'); + const responseAfter = new Response(textAfter, { + status: responseBefore.status, + statusText: responseBefore.statusText, + headers: responseBefore.headers, + }); + Object.defineProperties(responseAfter, { + ok: { value: responseBefore.ok }, + redirected: { value: responseBefore.redirected }, + type: { value: responseBefore.type }, + url: { value: responseBefore.url }, + }); + return responseAfter; + }).catch(reason => { + safe.uboErr(logPrefix, 'Error:', reason); + return responseBefore; + }); + }).catch(reason => { + safe.uboErr(logPrefix, 'Error:', reason); + return fetchPromise; + }); + }); +} +registerScriptlet(jsonlEditFetchResponseFn, { + name: 'jsonl-edit-fetch-response.fn', + dependencies: [ + JSONPath, + jsonlEditFn, + matchObjectPropertiesFn, + parsePropertiesToMatchFn, + proxyApplyFn, + safeSelf, + ], +}); + +/******************************************************************************/ +/** + * @scriptlet jsonl-edit-fetch-response.js + * + * @description + * Edit the objects found in a JSONL resource fetched through the fetch API. + * Properties can only be removed. + * + * @param jsonq + * A uBO-flavored JSONPath query. + * + * @param [propsToMatch, value] + * An optional vararg detailing the arguments to match when xhr.open() is + * called. + * + * */ + +function jsonlEditFetchResponse(jsonq = '', ...args) { + jsonlEditFetchResponseFn(false, jsonq, ...args); +} +registerScriptlet(jsonlEditFetchResponse, { + name: 'jsonl-edit-fetch-response.js', + dependencies: [ + jsonlEditFetchResponseFn, + ], +}); + +/******************************************************************************/ +/** + * @scriptlet trusted-jsonl-edit-fetch-response.js + * + * @description + * Edit the objects found in a JSONL resource fetched through the fetch API. + * Properties can be assigned new values. + * + * @param jsonq + * A uBO-flavored JSONPath query. + * + * @param [propsToMatch, value] + * An optional vararg detailing the arguments to match when xhr.open() is + * called. + * + * */ + +function trustedJsonlEditFetchResponse(jsonq = '', ...args) { + jsonlEditFetchResponseFn(true, jsonq, ...args); +} +registerScriptlet(trustedJsonlEditFetchResponse, { + name: 'trusted-jsonl-edit-fetch-response.js', + requiresTrust: true, + dependencies: [ + jsonlEditFetchResponseFn, + ], +}); + +/******************************************************************************/ diff --git a/src/js/resources/jsonl-prune.js b/src/js/resources/jsonl-prune.js deleted file mode 100644 index be1f824375462..0000000000000 --- a/src/js/resources/jsonl-prune.js +++ /dev/null @@ -1,249 +0,0 @@ -/******************************************************************************* - - uBlock Origin - a comprehensive, efficient content blocker - Copyright (C) 2019-present Raymond Hill - - 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/}. - - Home: https://github.com/gorhill/uBlock - -*/ - -import { - matchObjectPropertiesFn, - parsePropertiesToMatchFn, -} from './utils.js'; - -import { JSONPath } from './shared.js'; -import { objectPruneFn } from './object-prune.js'; -import { registerScriptlet } from './base.js'; -import { safeSelf } from './safe-self.js'; - -/******************************************************************************/ - -function jsonlPruneFn( - jsonp, - text = '' -) { - const safe = safeSelf(); - const linesBefore = text.split(/\n+/); - const linesAfter = []; - for ( const lineBefore of linesBefore ) { - let obj; - try { - obj = safe.JSON_parse(lineBefore); - } catch { - } - if ( typeof obj !== 'object' || obj === null ) { - linesAfter.push(lineBefore); - continue; - } - const paths = jsonp.evaluate(obj); - if ( paths.length === 0 ) { - linesAfter.push(lineBefore); - continue; - } - for ( const path of paths ) { - const { obj, key } = jsonp.resolvePath(path); - delete obj[key]; - } - linesAfter.push(safe.JSON_stringify(obj).replace(/\//g, '\\/')); - } - return linesAfter.join('\n'); -} -registerScriptlet(jsonlPruneFn, { - name: 'jsonl-prune.fn', - dependencies: [ - safeSelf, - ], -}); - -/******************************************************************************/ - -/** - * @scriptlet jsonl-prune-xhr-response.js - * - * @description - * Prune the objects found in a JSONL resource fetched through a XHR instance. - * - * @param jsonq - * A uBO-flavored JSONPath query. - * - * @param [propsToMatch, value] - * An optional vararg detailing the arguments to match when xhr.open() is - * called. - * - * */ - -function jsonlPruneXhrResponse( - jsonq = '', -) { - const safe = safeSelf(); - const logPrefix = safe.makeLogPrefix('jsonl-prune-xhr-response', jsonq); - const xhrInstances = new WeakMap(); - const jsonp = JSONPath.create(jsonq); - const extraArgs = safe.getExtraArgs(Array.from(arguments), 1); - const propNeedles = parsePropertiesToMatchFn(extraArgs.propsToMatch, 'url'); - self.XMLHttpRequest = class extends self.XMLHttpRequest { - open(method, url, ...args) { - const xhrDetails = { method, url }; - const matched = propNeedles.size === 0 || - matchObjectPropertiesFn(propNeedles, xhrDetails); - if ( matched ) { - if ( safe.logLevel > 1 && Array.isArray(matched) ) { - safe.uboLog(logPrefix, `Matched "propsToMatch":\n\t${matched.join('\n\t')}`); - } - xhrInstances.set(this, xhrDetails); - } - return super.open(method, url, ...args); - } - get response() { - const innerResponse = super.response; - const xhrDetails = xhrInstances.get(this); - if ( xhrDetails === undefined ) { - return innerResponse; - } - const responseLength = typeof innerResponse === 'string' - ? innerResponse.length - : undefined; - if ( xhrDetails.lastResponseLength !== responseLength ) { - xhrDetails.response = undefined; - xhrDetails.lastResponseLength = responseLength; - } - if ( xhrDetails.response !== undefined ) { - return xhrDetails.response; - } - if ( typeof innerResponse !== 'string' ) { - return (xhrDetails.response = innerResponse); - } - const outerResponse = jsonlPruneFn(jsonp, innerResponse); - if ( outerResponse !== innerResponse ) { - safe.uboLog(logPrefix, 'Pruned'); - } - return (xhrDetails.response = outerResponse); - } - get responseText() { - const response = this.response; - return typeof response !== 'string' - ? super.responseText - : response; - } - }; -} -registerScriptlet(jsonlPruneXhrResponse, { - name: 'jsonl-prune-xhr-response.js', - dependencies: [ - JSONPath, - jsonlPruneFn, - matchObjectPropertiesFn, - parsePropertiesToMatchFn, - safeSelf, - ], -}); - -/******************************************************************************/ - -/** - * @scriptlet jsonl-prune-fetch-response.js - * - * @description - * Prune the objects found in a JSONL resource fetched through the fetch API. - * Once the pruning is performed. - * - * @param jsonq - * A uBO-flavored JSONPath query. - * - * @param [propsToMatch, value] - * An optional vararg detailing the arguments to match when xhr.open() is - * called. - * - * */ - -function jsonlPruneFetchResponse( - jsonq = '' -) { - const safe = safeSelf(); - const logPrefix = safe.makeLogPrefix('jsonl-prune-fetch-response', jsonq); - const jsonp = JSONPath.create(jsonq); - const extraArgs = safe.getExtraArgs(Array.from(arguments), 2); - const propNeedles = parsePropertiesToMatchFn(extraArgs.propsToMatch, 'url'); - const logall = jsonq === ''; - const applyHandler = function(target, thisArg, args) { - const fetchPromise = Reflect.apply(target, thisArg, args); - if ( propNeedles.size !== 0 ) { - const objs = [ args[0] instanceof Object ? args[0] : { url: args[0] } ]; - if ( objs[0] instanceof Request ) { - try { - objs[0] = safe.Request_clone.call(objs[0]); - } catch(ex) { - safe.uboErr(logPrefix, 'Error:', ex); - } - } - if ( args[1] instanceof Object ) { - objs.push(args[1]); - } - const matched = matchObjectPropertiesFn(propNeedles, ...objs); - if ( matched === undefined ) { return fetchPromise; } - if ( safe.logLevel > 1 ) { - safe.uboLog(logPrefix, `Matched "propsToMatch":\n\t${matched.join('\n\t')}`); - } - } - return fetchPromise.then(responseBefore => { - const response = responseBefore.clone(); - return response.text().then(textBefore => { - if ( typeof textBefore !== 'string' ) { return textBefore; } - if ( logall ) { - safe.uboLog(logPrefix, textBefore); - return responseBefore; - } - const textAfter = jsonlPruneFn(jsonp, textBefore); - if ( textAfter === textBefore ) { return responseBefore; } - safe.uboLog(logPrefix, 'Pruned'); - const responseAfter = new Response(textAfter, { - status: responseBefore.status, - statusText: responseBefore.statusText, - headers: responseBefore.headers, - }); - Object.defineProperties(responseAfter, { - ok: { value: responseBefore.ok }, - redirected: { value: responseBefore.redirected }, - type: { value: responseBefore.type }, - url: { value: responseBefore.url }, - }); - return responseAfter; - }).catch(reason => { - safe.uboErr(logPrefix, 'Error:', reason); - return responseBefore; - }); - }).catch(reason => { - safe.uboErr(logPrefix, 'Error:', reason); - return fetchPromise; - }); - }; - self.fetch = new Proxy(self.fetch, { - apply: applyHandler - }); -} -registerScriptlet(jsonlPruneFetchResponse, { - name: 'jsonl-prune-fetch-response.js', - dependencies: [ - JSONPath, - jsonlPruneFn, - matchObjectPropertiesFn, - parsePropertiesToMatchFn, - safeSelf, - ], -}); - -/******************************************************************************/ diff --git a/src/js/resources/scriptlets.js b/src/js/resources/scriptlets.js index e9ec6717199ed..0526d022a6863 100755 --- a/src/js/resources/scriptlets.js +++ b/src/js/resources/scriptlets.js @@ -22,8 +22,8 @@ import './attribute.js'; import './href-sanitizer.js'; +import './json-edit.js'; import './json-prune.js'; -import './jsonl-prune.js'; import './noeval.js'; import './object-prune.js'; import './prevent-innerHTML.js'; diff --git a/src/js/static-filtering-parser.js b/src/js/static-filtering-parser.js index 4b649d7d93e5d..716e4bc416361 100644 --- a/src/js/static-filtering-parser.js +++ b/src/js/static-filtering-parser.js @@ -21,6 +21,7 @@ import * as cssTree from '../lib/csstree/css-tree.js'; import { ArglistParser } from './arglist-parser.js'; +import { JSONPath } from './jsonpath.js'; import Regex from '../lib/regexanalyzer/regex.js'; /******************************************************************************* @@ -1472,7 +1473,7 @@ export class AstFilterParser { break; } const value = this.getNetOptionValue(NODE_TYPE_NET_OPTION_NAME_URLTRANSFORM); - if ( value !== '' && parseReplaceValue(value) === undefined ) { + if ( value !== '' && parseReplacebyRegexValue(value) === undefined ) { this.astError = AST_ERROR_OPTION_BADVALUE; realBad = true; } @@ -3028,7 +3029,7 @@ export function parseHeaderValue(arg) { // https://adguard.com/kb/general/ad-filtering/create-own-filters/#replace-modifier -export function parseReplaceValue(s) { +export function parseReplacebyRegexValue(s) { if ( s.charCodeAt(0) !== 0x2F /* / */ ) { return; } const parser = new ArglistParser('/'); parser.nextArg(s, 1); @@ -3054,6 +3055,23 @@ export function parseReplaceValue(s) { } } +export function parseReplaceValue(s) { + if ( s.startsWith('/') ) { + const r = parseReplacebyRegexValue(s); + if ( r ) { r.type = 'text'; } + return r; + } + const pos = s.indexOf(':'); + if ( pos === -1 ) { return; } + const type = s.slice(0, pos); + if ( type === 'json' || type === 'jsonl' ) { + const query = s.slice(pos+1); + const jsonp = JSONPath.create(query); + if ( jsonp.valid === false ) { return; } + return { type, jsonp }; + } +} + /******************************************************************************/ export const netOptionTokenDescriptors = new Map([ diff --git a/src/js/static-net-filtering.js b/src/js/static-net-filtering.js index c6b4f00c625c7..24961d5f73a5e 100644 --- a/src/js/static-net-filtering.js +++ b/src/js/static-net-filtering.js @@ -5412,7 +5412,7 @@ StaticNetFilteringEngine.prototype.transformRequest = function(fctxt, out = []) continue; } if ( directive.cache === null ) { - directive.cache = sfp.parseReplaceValue(directive.value); + directive.cache = sfp.parseReplaceByRegexValue(directive.value); } const cache = directive.cache; if ( cache === undefined ) { continue; } diff --git a/src/js/traffic.js b/src/js/traffic.js index 6e4b064b23123..827c94d51ef44 100644 --- a/src/js/traffic.js +++ b/src/js/traffic.js @@ -625,19 +625,60 @@ function textResponseFilterer(session, directives) { continue; } const { refs } = directive; + if ( refs.$cache !== null ) { + const { jsonp } = refs.$cache; + if ( jsonp && jsonp.apply === undefined ) { + refs.$cache = null; + } + } if ( refs.$cache === null ) { refs.$cache = sfp.parseReplaceValue(refs.value); } const cache = refs.$cache; if ( cache === undefined ) { continue; } - cache.re.lastIndex = 0; - if ( cache.re.test(session.getString()) !== true ) { continue; } - cache.re.lastIndex = 0; - session.setString(session.getString().replace( - cache.re, - cache.replacement - )); - applied.push(directive); + switch ( cache.type ) { + case 'json': { + const json = session.getString(); + let obj; + try { obj = JSON.parse(json); } catch { break; } + if ( cache.jsonp.apply(obj) === 0 ) { break; } + session.setString(cache.jsonp.toJSON(obj)); + applied.push(directive); + break; + } + case 'jsonl': { + const linesBefore = session.getString().split(/\n+/); + const linesAfter = []; + for ( const lineBefore of linesBefore ) { + let obj; + try { obj = JSON.parse(lineBefore); } catch { } + if ( typeof obj !== 'object' || obj === null ) { + linesAfter.push(lineBefore); + continue; + } + if ( cache.jsonp.apply(obj) === 0 ) { + linesAfter.push(lineBefore); + continue; + } + linesAfter.push(cache.jsonp.toJSON(obj)); + } + session.setString(linesAfter.join('\n')); + break; + } + case 'text': { + cache.re.lastIndex = 0; + if ( cache.re.test(session.getString()) !== true ) { break; } + cache.re.lastIndex = 0; + session.setString(session.getString().replace( + cache.re, + cache.replacement + )); + applied.push(directive); + break; + } + default: + break; + } } if ( applied.length === 0 ) { return; } if ( logger.enabled !== true ) { return; } From caa25cdaf3f255518abb45127465d14f2fb98d4b Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Fri, 28 Mar 2025 10:20:21 -0400 Subject: [PATCH 0748/1099] Add minimal in-code documentation for JSONPath --- src/js/jsonpath.js | 55 +++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 54 insertions(+), 1 deletion(-) diff --git a/src/js/jsonpath.js b/src/js/jsonpath.js index 7f022d6e7a1dd..09cda9b6dc971 100644 --- a/src/js/jsonpath.js +++ b/src/js/jsonpath.js @@ -20,7 +20,60 @@ */ -/******************************************************************************/ +/** + * Implement the parsing of uBO-flavored JSON path syntax + * + * Reference to original JSON path syntax: + * https://goessner.net/articles/JsonPath/index.html + * + * uBO-flavored JSON path implementation differs as follow: + * + * - Both $ and @ are implicit. Though you can use them, you do not have to. + * Use $ only when the implicit context is not that of root. Example: + * - Official: $..book[?(@.isbn)] + * - uBO-flavored: ..book[?(.isbn)] + * + * - uBO-flavor syntax do not (yet) support: + * - Union (,) operator. + * - Array slice operator + * + * - Regarding filter expressions, uBO-flavored JSON path supports a limited + * set of expressions since unlike the official implementation, uBO can't use + * JS eval() to evaluate arbitrary JS expressions. The operand MUST be valid + * JSON. The currently supported expressions are: + * - ==: strict equality + * - !=: strict inequality + * - <: less than + * - <=: less than or equal to + * - >: greater than + * - >=: greater than or equal to + * - ^=: stringified value starts with + * - $=: stringified value ends with + * - *=: stringified value includes + * + * - Examples (from "JSONPath examples" at reference link) + * - .store.book[*].author + * - ..author + * - .store.* + * - .store..price + * - ..book[2] + * - ..book[?(.isbn)] + * - ..book[?(.price<10)] + * - ..* + * + * uBO-flavored syntax supports assigning a value to a resolved JSON path by + * appending `=[value]` to the JSON path. The assigned value MUST be valid JSON. + * Examples: + * - .store..price=0 + * - .store.book[*].author="redacted" + * + * A JSONPath instance can be use to compile a JSON path query, and the result + * of the compilation can be applied to different objects. When a JSON path + * query does not assign a value, the resolved property will be removed. + * + * More capabilities can be added in the future as needed. + * + * */ export class JSONPath { static create(query) { From 564f3f3442bc2ffdbe8bd3b6fecb2a2c71872693 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Fri, 28 Mar 2025 10:24:21 -0400 Subject: [PATCH 0749/1099] English --- src/js/jsonpath.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/js/jsonpath.js b/src/js/jsonpath.js index 09cda9b6dc971..2f001fb0cd042 100644 --- a/src/js/jsonpath.js +++ b/src/js/jsonpath.js @@ -21,7 +21,7 @@ */ /** - * Implement the parsing of uBO-flavored JSON path syntax + * Implement the parsing of uBO-flavored JSON path queries. * * Reference to original JSON path syntax: * https://goessner.net/articles/JsonPath/index.html @@ -33,7 +33,7 @@ * - Official: $..book[?(@.isbn)] * - uBO-flavored: ..book[?(.isbn)] * - * - uBO-flavor syntax do not (yet) support: + * - uBO-flavor syntax does not (yet) support: * - Union (,) operator. * - Array slice operator * @@ -62,8 +62,8 @@ * - ..* * * uBO-flavored syntax supports assigning a value to a resolved JSON path by - * appending `=[value]` to the JSON path. The assigned value MUST be valid JSON. - * Examples: + * appending `=[value]` to the JSON path query. The assigned value MUST be + * valid JSON. Examples: * - .store..price=0 * - .store.book[*].author="redacted" * From 5f9f7d050da0cef40dd18affb3a890244b739109 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Fri, 28 Mar 2025 16:21:20 -0400 Subject: [PATCH 0750/1099] Make Firefox dev build auto-update --- dist/firefox/updates.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dist/firefox/updates.json b/dist/firefox/updates.json index b091baf9d152a..d76d344fd1c44 100644 --- a/dist/firefox/updates.json +++ b/dist/firefox/updates.json @@ -3,9 +3,9 @@ "uBlock0@raymondhill.net": { "updates": [ { - "version": "1.63.3.3", + "version": "1.63.3.4", "browser_specific_settings": { "gecko": { "strict_min_version": "92.0" } }, - "update_link": "https://github.com/gorhill/uBlock/releases/download/1.63.3b3/uBlock0_1.63.3b3.firefox.signed.xpi" + "update_link": "https://github.com/gorhill/uBlock/releases/download/1.63.3b4/uBlock0_1.63.3b4.firefox.signed.xpi" } ] } From 862557fd632b935773549e827cbde45a87c88e7d Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sat, 29 Mar 2025 09:05:26 -0400 Subject: [PATCH 0751/1099] Update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index f9722d7a5cf31..e0a2e412e1470 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,4 @@ +- [Add `json-edit` suite of scriptlets; extend `replace=` option](https://github.com/gorhill/uBlock/commit/b18daa53aa) - [Improve `trusted-prevent-dom-bypass` scriptlet](https://github.com/gorhill/uBlock/commit/68a256bdde) - [Add `jsonl-prune-xhr-response`/`jsonl-prune-fetch-response` scriptlets](https://github.com/gorhill/uBlock/commit/95a3be9d56) - [Improve `[json-prune|trusted-replace]-fetch-response` scriptlets](https://github.com/gorhill/uBlock/commit/88fa550a96) From 44c038b9a1be5aa59b151a580394d110b6bea256 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sat, 29 Mar 2025 09:06:07 -0400 Subject: [PATCH 0752/1099] Fix for old scriptlet syntax Related feedback: https://old.reddit.com/r/uBlockOrigin/comments/1jmcqhn/ --- src/js/scriptlet-filtering-core.js | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/js/scriptlet-filtering-core.js b/src/js/scriptlet-filtering-core.js index b6d7c0be2b662..eca58e31079cd 100644 --- a/src/js/scriptlet-filtering-core.js +++ b/src/js/scriptlet-filtering-core.js @@ -67,8 +67,10 @@ const lookupScriptlet = (rawToken, mainMap, isolatedMap, debug = false) => { const details = reng.contentFromName(token, 'text/javascript'); if ( details === undefined ) { return; } const targetWorldMap = details.world !== 'ISOLATED' ? mainMap : isolatedMap; - targetWorldMap.set(token, details.js); const content = patchScriptlet(details.js, args.slice(1)); + if ( content.startsWith('(function(') === false ) { + targetWorldMap.set(token, details.js); + } const dependencies = details.dependencies || []; while ( dependencies.length !== 0 ) { const token = dependencies.shift(); @@ -291,7 +293,7 @@ export class ScriptletFilteringEngine { options.debugScriptlets ? 'debugger;' : ';', '', // For use by scriptlets to share local data among themselves - `const scriptletGlobals = ${JSON.stringify(scriptletGlobals, null, 4)}`, + `const scriptletGlobals = ${JSON.stringify(scriptletGlobals, null, 4)};`, '', scriptletDetails.mainWorld, '', @@ -305,7 +307,7 @@ export class ScriptletFilteringEngine { options.debugScriptlets ? 'debugger;' : ';', '', // For use by scriptlets to share local data among themselves - `const scriptletGlobals = ${JSON.stringify(scriptletGlobals, null, 4)}`, + `const scriptletGlobals = ${JSON.stringify(scriptletGlobals, null, 4)};`, '', scriptletDetails.isolatedWorld, '', From b381ceda72bec4e718bf92ddba1b24a761957add Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sat, 29 Mar 2025 09:54:41 -0400 Subject: [PATCH 0753/1099] Mind other old constructs for scriptlets Related feedback: https://old.reddit.com/r/uBlockOrigin/comments/1jmcqhn/ Related commit: https://github.com/gorhill/uBlock/commit/44c038b9a1 --- src/js/scriptlet-filtering-core.js | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/js/scriptlet-filtering-core.js b/src/js/scriptlet-filtering-core.js index eca58e31079cd..00888da1ca8ee 100644 --- a/src/js/scriptlet-filtering-core.js +++ b/src/js/scriptlet-filtering-core.js @@ -67,8 +67,10 @@ const lookupScriptlet = (rawToken, mainMap, isolatedMap, debug = false) => { const details = reng.contentFromName(token, 'text/javascript'); if ( details === undefined ) { return; } const targetWorldMap = details.world !== 'ISOLATED' ? mainMap : isolatedMap; - const content = patchScriptlet(details.js, args.slice(1)); - if ( content.startsWith('(function(') === false ) { + const match = /^function\s+([^(\s]+)\s*\(/.exec(details.js); + const fname = match && match[1]; + const content = patchScriptlet(fname, details.js, args.slice(1)); + if ( fname ) { targetWorldMap.set(token, details.js); } const dependencies = details.dependencies || []; @@ -92,10 +94,9 @@ const lookupScriptlet = (rawToken, mainMap, isolatedMap, debug = false) => { }; // Fill-in scriptlet argument placeholders. -const patchScriptlet = (content, arglist) => { - const match = /^function\s+([^(\s]+)\s*\(/.exec(content); - if ( match ) { - content = `${match[1]}({{args}});`; +const patchScriptlet = (fname, content, arglist) => { + if ( fname ) { + content = `${fname}({{args}});`; } else { for ( let i = 0; i < arglist.length; i++ ) { content = content.replace(`{{${i+1}}}`, arglist[i]); From fbdc09a7aaaf3e5c56eb19df55e33883d49836ce Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sat, 29 Mar 2025 09:57:06 -0400 Subject: [PATCH 0754/1099] New revision for dev build --- dist/version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dist/version b/dist/version index 07bd8b1dd2154..82662df4bfe6c 100644 --- a/dist/version +++ b/dist/version @@ -1 +1 @@ -1.63.3.4 +1.63.3.5 From 935ce74d3a7b300be12a952f2e7dfa9f0f595db9 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sat, 29 Mar 2025 10:05:48 -0400 Subject: [PATCH 0755/1099] Make Firefox dev build auto-update --- dist/firefox/updates.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dist/firefox/updates.json b/dist/firefox/updates.json index d76d344fd1c44..09f9fea107aa3 100644 --- a/dist/firefox/updates.json +++ b/dist/firefox/updates.json @@ -3,9 +3,9 @@ "uBlock0@raymondhill.net": { "updates": [ { - "version": "1.63.3.4", + "version": "1.63.3.5", "browser_specific_settings": { "gecko": { "strict_min_version": "92.0" } }, - "update_link": "https://github.com/gorhill/uBlock/releases/download/1.63.3b4/uBlock0_1.63.3b4.firefox.signed.xpi" + "update_link": "https://github.com/gorhill/uBlock/releases/download/1.63.3b5/uBlock0_1.63.3b5.firefox.signed.xpi" } ] } From 97e740bd2c200b054358593c45186a7b754ca97f Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sun, 30 Mar 2025 09:34:22 -0400 Subject: [PATCH 0756/1099] Code viewer shouldn't be maximizable Related feedback: https://github.com/uBlockOrigin/uBlock-issues/issues/3161#issuecomment-2764549781 --- src/js/code-viewer.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/js/code-viewer.js b/src/js/code-viewer.js index 83d5de98a7270..e6cd1c01f6f11 100644 --- a/src/js/code-viewer.js +++ b/src/js/code-viewer.js @@ -35,6 +35,7 @@ const cmEditor = new CodeMirror(qs$('#content'), { gutters: [ 'CodeMirror-linenumbers' ], lineNumbers: true, lineWrapping: true, + maximizable: false, matchBrackets: true, styleActiveLine: { nonEmpty: true, From 5587111d212b97279378f4d3a7f151cd7f68c6b4 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sun, 30 Mar 2025 12:36:37 -0400 Subject: [PATCH 0757/1099] Add support for union syntax in JSONPath This immediately useful to list maintainers. Related commits: - https://github.com/gorhill/uBlock/commit/d5fd1de150 - https://github.com/gorhill/uBlock/commit/b18daa53aa - https://github.com/gorhill/uBlock/commit/caa25cdaf3 --- src/js/jsonpath.js | 218 +++++++++++++++++++++++++-------------------- 1 file changed, 120 insertions(+), 98 deletions(-) diff --git a/src/js/jsonpath.js b/src/js/jsonpath.js index 2f001fb0cd042..0e936ca79601f 100644 --- a/src/js/jsonpath.js +++ b/src/js/jsonpath.js @@ -34,7 +34,6 @@ * - uBO-flavored: ..book[?(.isbn)] * * - uBO-flavor syntax does not (yet) support: - * - Union (,) operator. * - Array slice operator * * - Regarding filter expressions, uBO-flavored JSON path supports a limited @@ -96,6 +95,7 @@ export class JSONPath { return this.#compiled !== undefined; } compile(query) { + this.#compiled = undefined; const r = this.#compile(query, 0); if ( r === undefined ) { return; } if ( r.i !== query.length ) { @@ -132,6 +132,9 @@ export class JSONPath { this.#root = null; return n; } + dump() { + return JSON.stringify(this.#compiled); + } toJSON(obj, ...args) { return JSONPath.toJSON(obj, null, ...args) } @@ -145,7 +148,7 @@ export class JSONPath { #DESCENDANTS = 4; #reUnquotedIdentifier = /^[A-Za-z_][\w]*|^\*/; #reExpr = /^([!=^$*]=|[<>]=?)(.+?)\)\]/; - #reIndice = /^\[-?\d+\]/; + #reIndice = /^-?\d+/; #root; #compiled; #compile(query, i) { @@ -189,22 +192,6 @@ export class JSONPath { continue; } // Bracket accessor syntax - if ( query.startsWith('[*]', i) ) { - mv ||= this.#CHILDREN; - steps.push({ mv, k: '*' }); - i += 3; - mv = this.#UNDEFINED; - continue; - } - if ( query.startsWith("['", i) ) { - const r = this.#consumeQuotedIdentifier(query, i+2); - if ( r === undefined ) { return; } - mv ||= this.#CHILDREN; - steps.push({ mv, k: r.s }); - i = r.i; - mv = this.#UNDEFINED; - continue; - } if ( query.startsWith('[?(', i) ) { const not = query.charCodeAt(i+3) === 0x21 /* ! */; const j = i + 3 + (not ? 1 : 0); @@ -217,16 +204,19 @@ export class JSONPath { mv = this.#UNDEFINED; continue; } - if ( this.#reIndice.test(query.slice(i)) ) { - const match = this.#reIndice.exec(query.slice(i)); - const indice = parseInt(query.slice(i+1), 10); - mv ||= this.CHILDREN; - steps.push({ mv, k: indice }); - i += match[0].length; + if ( query.startsWith('[*]', i) ) { + mv ||= this.#CHILDREN; + steps.push({ mv, k: '*' }); + i += 3; mv = this.#UNDEFINED; continue; } - return; + const r = this.#consumeIdentifier(query, i+1); + if ( r === undefined ) { return; } + mv ||= this.#CHILDREN; + steps.push({ mv, k: r.s }); + i = r.i + 1; + mv = this.#UNDEFINED; } if ( steps.length <= 1 ) { return; } return { steps, i }; @@ -254,65 +244,63 @@ export class JSONPath { } #getMatches(listin, step) { const listout = []; - const recursive = step.mv === this.#DESCENDANTS; for ( const pathin of listin ) { - const { value: v } = this.#resolvePath(pathin); - if ( v === null ) { continue; } - if ( v === undefined ) { continue; } - const { steps, k } = step; - if ( k ) { - if ( k === '*' ) { - const entries = Array.from(this.#getDescendants(v, recursive)); - for ( const { path } of entries ) { - this.#evaluateExpr(step, [ ...pathin, ...path ], listout); - } - continue; - } - if ( typeof k === 'number' ) { - if ( Array.isArray(v) === false ) { continue; } - const n = v.length; - const i = k >= 0 ? k : n + k; - if ( i < 0 ) { continue; } - if ( i >= n ) { continue; } - this.#evaluateExpr(step, [ ...pathin, i ], listout); - } else if ( Array.isArray(k) ) { - for ( const l of k ) { - this.#evaluateExpr(step, [ ...pathin, l ], listout); - } - } else { - this.#evaluateExpr(step, [ ...pathin, k ], listout); - } - if ( recursive !== true ) { continue; } - for ( const { obj, key, path } of this.#getDescendants(v, recursive) ) { - const w = obj[key]; - if ( w instanceof Object === false ) { continue; } - if ( Object.hasOwn(w, k) === false ) { continue; } - this.#evaluateExpr(step, [ ...pathin, ...path, k ], listout); - } - continue; - } - if ( steps ) { - const isArray = Array.isArray(v); - if ( isArray === false ) { - const r = this.#evaluate(steps, pathin); - if ( r.length !== 0 ) { - listout.push(pathin); - } - if ( recursive !== true ) { continue; } - } - for ( const { obj, key, path } of this.#getDescendants(v, recursive) ) { - const w = obj[key]; - if ( Array.isArray(w) ) { continue; } - const x = [ ...pathin, ...path ]; - const r = this.#evaluate(steps, x); - if ( r.length !== 0 ) { - listout.push(x); - } - } + const { value: owner } = this.#resolvePath(pathin); + if ( step.k === '*' ) { + this.#getMatchesFromAll(pathin, step, owner, listout); + } else if ( step.k !== undefined ) { + this.#getMatchesFromKeys(pathin, step, owner, listout); + } else if ( step.steps ) { + this.#getMatchesFromExpr(pathin, step, owner, listout); } } return listout; } + #getMatchesFromAll(pathin, step, owner, out) { + const recursive = step.mv === this.#DESCENDANTS; + for ( const { path } of this.#getDescendants(owner, recursive) ) { + out.push([ ...pathin, ...path ]); + } + } + #getMatchesFromKeys(pathin, step, owner, out) { + const kk = Array.isArray(step.k) ? step.k : [ step.k ]; + for ( const k of kk ) { + const normalized = this.#evaluateExpr(step, owner, k); + if ( normalized === undefined ) { continue; } + out.push([ ...pathin, normalized ]); + } + if ( step.mv !== this.#DESCENDANTS ) { return; } + for ( const { obj, key, path } of this.#getDescendants(owner, true) ) { + for ( const k of kk ) { + const normalized = this.#evaluateExpr(step, obj[key], k); + if ( normalized === undefined ) { continue; } + out.push([ ...pathin, ...path, normalized ]); + } + } + } + #getMatchesFromExpr(pathin, step, owner, out) { + const recursive = step.mv === this.#DESCENDANTS; + if ( Array.isArray(owner) === false ) { + const r = this.#evaluate(step.steps, pathin); + if ( r.length !== 0 ) { out.push(pathin); } + if ( recursive !== true ) { return; } + } + for ( const { obj, key, path } of this.#getDescendants(owner, recursive) ) { + if ( Array.isArray(obj[key]) ) { continue; } + const q = [ ...pathin, ...path ]; + const r = this.#evaluate(step.steps, q); + if ( r.length === 0 ) { continue; } + out.push(q); + } + } + #normalizeKey(owner, key) { + if ( typeof key === 'number' ) { + if ( Array.isArray(owner) ) { + return key >= 0 ? key : owner.length + key; + } + } + return key; + } #getDescendants(v, recursive) { const iterator = { next() { @@ -358,6 +346,37 @@ export class JSONPath { } return iterator; } + #consumeIdentifier(query, i) { + const keys = []; + for (;;) { + const c0 = query.charCodeAt(i); + if ( c0 === 0x5D /* ] */ ) { break; } + if ( c0 === 0x2C /* , */ ) { + i += 1; + continue; + } + if ( c0 === 0x27 /* ' */ ) { + const r = this.#consumeQuotedIdentifier(query, i+1); + if ( r === undefined ) { return; } + keys.push(r.s); + i = r.i; + continue; + } + if ( c0 === 0x2D /* - */ || c0 >= 0x30 && c0 <= 0x39 ) { + const match = this.#reIndice.exec(query.slice(i)); + if ( match === null ) { return; } + const indice = parseInt(query.slice(i), 10); + keys.push(indice); + i += match[0].length; + continue; + } + const s = this.#consumeUnquotedIdentifier(query, i); + if ( s === undefined ) { return; } + keys.push(s); + i += s.length; + } + return { s: keys.length === 1 ? keys[0] : keys, i }; + } #consumeQuotedIdentifier(query, i) { const len = query.length; const parts = []; @@ -366,9 +385,8 @@ export class JSONPath { if ( end === len ) { return; } const c = query.charCodeAt(end); if ( c === 0x27 /* ' */ ) { - if ( query.startsWith("']", end) === false ) { return; } parts.push(query.slice(beg, end)); - end += 2; + end += 1; break; } if ( c === 0x5C /* \ */ && (end+1) < len ) { @@ -407,25 +425,29 @@ export class JSONPath { } return { obj, key, value: obj[key] }; } - #evaluateExpr(step, path, out) { - const { obj: o, key: k } = this.#resolvePath(path); - const hasOwn = o instanceof Object && Object.hasOwn(o, k); - const v = o[k]; - let outcome = true; + #evaluateExpr(step, owner, key) { + if ( owner === undefined || owner === null ) { return; } + if ( typeof key === 'number' ) { + if ( Array.isArray(owner) === false ) { return; } + } + const k = this.#normalizeKey(owner, key); + const hasOwn = Object.hasOwn(owner, k); if ( step.op !== undefined && hasOwn === false ) { return; } + const target = step.not !== true; + const v = owner[k]; + let outcome = false; switch ( step.op ) { - case '==': outcome = v === step.rval; break; - case '!=': outcome = v !== step.rval; break; - case '<': outcome = v < step.rval; break; - case '<=': outcome = v <= step.rval; break; - case '>': outcome = v > step.rval; break; - case '>=': outcome = v >= step.rval; break; - case '^=': outcome = `${v}`.startsWith(step.rval); break; - case '$=': outcome = `${v}`.endsWith(step.rval); break; - case '*=': outcome = `${v}`.includes(step.rval); break; - default: outcome = hasOwn; break; + case '==': outcome = (v === step.rval) === target; break; + case '!=': outcome = (v !== step.rval) === target; break; + case '<': outcome = (v < step.rval) === target; break; + case '<=': outcome = (v <= step.rval) === target; break; + case '>': outcome = (v > step.rval) === target; break; + case '>=': outcome = (v >= step.rval) === target; break; + case '^=': outcome = `${v}`.startsWith(step.rval) === target; break; + case '$=': outcome = `${v}`.endsWith(step.rval) === target; break; + case '*=': outcome = `${v}`.includes(step.rval) === target; break; + default: outcome = hasOwn === target; break; } - if ( outcome === (step.not === true) ) { return; } - out.push(path); + if ( outcome ) { return k; } } } From 86210368f59e464df8d39c99747622d2982f66d7 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sun, 30 Mar 2025 12:37:56 -0400 Subject: [PATCH 0758/1099] New revision for dev build --- dist/version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dist/version b/dist/version index 82662df4bfe6c..2b821f94b6bac 100644 --- a/dist/version +++ b/dist/version @@ -1 +1 @@ -1.63.3.5 +1.63.3.6 From f4b2d48873b07513b46f84325e2dfe662a5d098a Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sun, 30 Mar 2025 12:39:34 -0400 Subject: [PATCH 0759/1099] Update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index e0a2e412e1470..a4be29825ef03 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,4 @@ +- [Code viewer shouldn't be maximizable](https://github.com/gorhill/uBlock/commit/97e740bd2c) - [Add `json-edit` suite of scriptlets; extend `replace=` option](https://github.com/gorhill/uBlock/commit/b18daa53aa) - [Improve `trusted-prevent-dom-bypass` scriptlet](https://github.com/gorhill/uBlock/commit/68a256bdde) - [Add `jsonl-prune-xhr-response`/`jsonl-prune-fetch-response` scriptlets](https://github.com/gorhill/uBlock/commit/95a3be9d56) From cdb1afc3b29219caff4170e937cc8734443f9445 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sun, 30 Mar 2025 12:46:26 -0400 Subject: [PATCH 0760/1099] Make Firefox dev build auto-update --- dist/firefox/updates.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dist/firefox/updates.json b/dist/firefox/updates.json index 09f9fea107aa3..dd6fbf1c5a110 100644 --- a/dist/firefox/updates.json +++ b/dist/firefox/updates.json @@ -3,9 +3,9 @@ "uBlock0@raymondhill.net": { "updates": [ { - "version": "1.63.3.5", + "version": "1.63.3.6", "browser_specific_settings": { "gecko": { "strict_min_version": "92.0" } }, - "update_link": "https://github.com/gorhill/uBlock/releases/download/1.63.3b5/uBlock0_1.63.3b5.firefox.signed.xpi" + "update_link": "https://github.com/gorhill/uBlock/releases/download/1.63.3b6/uBlock0_1.63.3b6.firefox.signed.xpi" } ] } From 9adedbc30e4cc5edbb467ac12cae20b107511f13 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sun, 30 Mar 2025 13:57:04 -0400 Subject: [PATCH 0761/1099] Remove the need for parethesis for JSONPath filter selectors As per official proposed standard: https://www.rfc-editor.org/rfc/rfc9535.html#name-filter-selector --- src/js/jsonpath.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/js/jsonpath.js b/src/js/jsonpath.js index 0e936ca79601f..e0edb1e4c339a 100644 --- a/src/js/jsonpath.js +++ b/src/js/jsonpath.js @@ -147,7 +147,7 @@ export class JSONPath { #CHILDREN = 3; #DESCENDANTS = 4; #reUnquotedIdentifier = /^[A-Za-z_][\w]*|^\*/; - #reExpr = /^([!=^$*]=|[<>]=?)(.+?)\)\]/; + #reExpr = /^([!=^$*]=|[<>]=?)(.+?)\]/; #reIndice = /^-?\d+/; #root; #compiled; @@ -192,15 +192,15 @@ export class JSONPath { continue; } // Bracket accessor syntax - if ( query.startsWith('[?(', i) ) { - const not = query.charCodeAt(i+3) === 0x21 /* ! */; - const j = i + 3 + (not ? 1 : 0); + if ( query.startsWith('[?', i) ) { + const not = query.charCodeAt(i+2) === 0x21 /* ! */; + const j = i + 2 + (not ? 1 : 0); const r = this.#compile(query, j); if ( r === undefined ) { return; } - if ( query.startsWith(')]', r.i) === false ) { return; } + if ( query.startsWith(']', r.i) === false ) { return; } if ( not ) { r.steps.at(-1).not = true; } steps.push({ mv: mv || this.#CHILDREN, steps: r.steps }); - i = r.i + 2; + i = r.i + 1; mv = this.#UNDEFINED; continue; } From 8632cd6072bf871c7038409a4bf947fc3180fb42 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sun, 30 Mar 2025 16:56:04 -0400 Subject: [PATCH 0762/1099] Fix typo causing regression in `uritransform=` Related feedback: https://github.com/uBlockOrigin/uAssets/discussions/27802 --- src/js/static-filtering-parser.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/js/static-filtering-parser.js b/src/js/static-filtering-parser.js index 716e4bc416361..3ba2c46539786 100644 --- a/src/js/static-filtering-parser.js +++ b/src/js/static-filtering-parser.js @@ -1473,7 +1473,7 @@ export class AstFilterParser { break; } const value = this.getNetOptionValue(NODE_TYPE_NET_OPTION_NAME_URLTRANSFORM); - if ( value !== '' && parseReplacebyRegexValue(value) === undefined ) { + if ( value !== '' && parseReplaceByRegexValue(value) === undefined ) { this.astError = AST_ERROR_OPTION_BADVALUE; realBad = true; } @@ -3029,7 +3029,7 @@ export function parseHeaderValue(arg) { // https://adguard.com/kb/general/ad-filtering/create-own-filters/#replace-modifier -export function parseReplacebyRegexValue(s) { +export function parseReplaceByRegexValue(s) { if ( s.charCodeAt(0) !== 0x2F /* / */ ) { return; } const parser = new ArglistParser('/'); parser.nextArg(s, 1); @@ -3057,7 +3057,7 @@ export function parseReplacebyRegexValue(s) { export function parseReplaceValue(s) { if ( s.startsWith('/') ) { - const r = parseReplacebyRegexValue(s); + const r = parseReplaceByRegexValue(s); if ( r ) { r.type = 'text'; } return r; } From c15278522e2edfa1ef0bd3dbb7e317d2bd465e50 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sun, 30 Mar 2025 16:57:38 -0400 Subject: [PATCH 0763/1099] New revision for dev build --- dist/version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dist/version b/dist/version index 2b821f94b6bac..d33eb39ecf778 100644 --- a/dist/version +++ b/dist/version @@ -1 +1 @@ -1.63.3.6 +1.63.3.7 From 9351497f16220894e342a537442171d9d603776d Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sun, 30 Mar 2025 17:06:18 -0400 Subject: [PATCH 0764/1099] Make Firefox dev build auto-update --- dist/firefox/updates.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dist/firefox/updates.json b/dist/firefox/updates.json index dd6fbf1c5a110..e718849e403a7 100644 --- a/dist/firefox/updates.json +++ b/dist/firefox/updates.json @@ -3,9 +3,9 @@ "uBlock0@raymondhill.net": { "updates": [ { - "version": "1.63.3.6", + "version": "1.63.3.7", "browser_specific_settings": { "gecko": { "strict_min_version": "92.0" } }, - "update_link": "https://github.com/gorhill/uBlock/releases/download/1.63.3b6/uBlock0_1.63.3b6.firefox.signed.xpi" + "update_link": "https://github.com/gorhill/uBlock/releases/download/1.63.3b7/uBlock0_1.63.3b7.firefox.signed.xpi" } ] } From 58a92b91c10d0ccbdff9de373a1802881320fb7f Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sun, 30 Mar 2025 17:21:18 -0400 Subject: [PATCH 0765/1099] Import translation work from https://crowdin.com/project/ublock --- platform/mv3/extension/_locales/cs/messages.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform/mv3/extension/_locales/cs/messages.json b/platform/mv3/extension/_locales/cs/messages.json index e2697983ebd73..8f2bc75691e34 100644 --- a/platform/mv3/extension/_locales/cs/messages.json +++ b/platform/mv3/extension/_locales/cs/messages.json @@ -112,7 +112,7 @@ "description": "First paragraph of 'Filter issues' section in Support pane" }, "supportS5H": { - "message": "Troubleshooting information", + "message": "Informace o řešení problémů", "description": "Label of 'Troubleshooting information' section in 'Report a filter issue' page" }, "supportS6P1S1": { From 0c6cbb7fcb4b6df480c43b1df463bc8c40cd8d3f Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Mon, 31 Mar 2025 11:26:41 -0400 Subject: [PATCH 0766/1099] Merge in long overdue change re. UKR list in assets.json Related discussion: https://github.com/uBlockOrigin/uBlock-issues/issues/2692 --- assets/assets.json | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/assets/assets.json b/assets/assets.json index 6cd042ee2b783..d1554d6a12e98 100644 --- a/assets/assets.json +++ b/assets/assets.json @@ -819,7 +819,7 @@ "off": true, "title": "🇷🇺ru 🇺🇦ua 🇺🇿uz 🇰🇿kz: RU AdList", "tags": "ads belarusian беларуская kazakh tatar russian русский ukrainian українська uzbek", - "lang": "be kk tt ru uk uz", + "lang": "be kk tt ru uz", "contentURL": "https://raw.githubusercontent.com/easylist/ruadlist/master/RuAdList-uBO.txt", "cdnURLs": [ "https://cdn.jsdelivr.net/gh/dimisa-RUAdList/RUAdListCDN@main/lists/ruadlist.ubo.min.txt", @@ -909,6 +909,16 @@ "contentURL": "https://filters.adtidy.org/extension/ublock/filters/13.txt", "supportURL": "https://github.com/AdguardTeam/AdguardFilters#adguard-filters" }, + "UKR-0": { + "content": "filters", + "group": "regions", + "off": true, + "title": "🇺🇦ua: Ukrainian Filters", + "tags": "ads ukraine україна", + "lang": "uk", + "contentURL": "https://raw.githubusercontent.com/ukrainianfilters/lists/main/combined/uBO/uBO.txt", + "supportURL": "https://github.com/ukrainianfilters/lists" + }, "VIE-1": { "content": "filters", "group": "regions", From 6ffffb7b2bb1386bb3fe97caa06baec729dc07de Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Mon, 31 Mar 2025 16:30:17 -0400 Subject: [PATCH 0767/1099] [mv3] Force real origin in `postMessage` so that `use_dynamic_url` can be used Call to postMessage was failing in the zapper when using `use_dynamic_url` in manifest. Manually overwriting the per-session dynamic hostname with the real extension id fixes this. --- platform/mv3/chromium/manifest.json | 3 ++- platform/mv3/extension/js/scripting/zapper.js | 8 +++++--- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/platform/mv3/chromium/manifest.json b/platform/mv3/chromium/manifest.json index a5ff0306f487a..d913bf1f60088 100644 --- a/platform/mv3/chromium/manifest.json +++ b/platform/mv3/chromium/manifest.json @@ -63,7 +63,8 @@ ], "matches": [ "" - ] + ], + "use_dynamic_url": true } ] } diff --git a/platform/mv3/extension/js/scripting/zapper.js b/platform/mv3/extension/js/scripting/zapper.js index 038ea1601d761..fb37ee1cf942f 100644 --- a/platform/mv3/extension/js/scripting/zapper.js +++ b/platform/mv3/extension/js/scripting/zapper.js @@ -365,7 +365,7 @@ const onFrameMessage = function(msg) { // can remove the iframe. const bootstrap = async ( ) => { - const url = new URL(chrome.runtime.getURL('/zapper-ui.html')); + const dynamicURL = new URL(chrome.runtime.getURL('/zapper-ui.html')); return new Promise(resolve => { const frame = document.createElement('iframe'); frame.setAttribute(zapperSecret, ''); @@ -381,9 +381,11 @@ const bootstrap = async ( ) => { port.onmessageerror = ( ) => { quitZapper(); }; + const realURL = new URL(dynamicURL); + realURL.hostname = chrome.i18n.getMessage('@@extension_id'); frame.contentWindow.postMessage( { what: 'zapperStart' }, - url.origin, + realURL.origin, [ channel.port2 ] ); frame.contentWindow.focus(); @@ -392,7 +394,7 @@ const bootstrap = async ( ) => { zapperFramePort: port, }); }; - frame.contentWindow.location = url.href; + frame.contentWindow.location = dynamicURL.href; }); }; From 2501eae25f1786ed9a8d6038e332d502087623e7 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Mon, 31 Mar 2025 17:43:51 -0400 Subject: [PATCH 0768/1099] [mv3] Fine tune zapper's quit button Related issue: https://github.com/uBlockOrigin/uBlock-issues/issues/3587 --- platform/mv3/extension/css/zapper-ui.css | 16 ++++++++++++++-- platform/mv3/extension/zapper-ui.html | 2 +- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/platform/mv3/extension/css/zapper-ui.css b/platform/mv3/extension/css/zapper-ui.css index cf729ac4791e9..4385d626d2a9d 100644 --- a/platform/mv3/extension/css/zapper-ui.css +++ b/platform/mv3/extension/css/zapper-ui.css @@ -1,3 +1,7 @@ +:root { + --quit-button-size: max(4em, min(6em, calc(100vw / 8), calc(100vh / 8))); +} + html#ubol-zapper, #ubol-zapper body { background: transparent; @@ -18,14 +22,22 @@ html#ubol-zapper, display: flex; flex-direction: column; position: fixed; + right: 2px; + top: 50%; + transform: translateY(-50%); z-index: 100; } +#ubol-zapper body[dir="rtl"] aside { + left: 2px; + right: unset; +} #ubol-zapper aside > #quit { + border: 1px solid rgba(0,0,0,0.5); fill: none; - height: 4em; stroke: var(--ink-1); stroke-width: 3px; - width: 4em; + width: var(--quit-button-size); + height: var(--quit-button-size); } #ubol-zapper svg#sea { cursor: crosshair; diff --git a/platform/mv3/extension/zapper-ui.html b/platform/mv3/extension/zapper-ui.html index 3e0ef51f80d1d..a739d97a42436 100644 --- a/platform/mv3/extension/zapper-ui.html +++ b/platform/mv3/extension/zapper-ui.html @@ -10,7 +10,7 @@ -
-
-

-

-
-

diff --git a/platform/mv3/extension/js/background.js b/platform/mv3/extension/js/background.js index b2ae6a33fa3ab..65c6da2bba3aa 100644 --- a/platform/mv3/extension/js/background.js +++ b/platform/mv3/extension/js/background.js @@ -31,15 +31,6 @@ import { syncWithBrowserPermissions, } from './mode-manager.js'; -import { - adminRead, - browser, - dnr, - localRead, localRemove, localWrite, - runtime, - windows, -} from './ext.js'; - import { adminReadEx, getAdminRulesets, @@ -47,9 +38,18 @@ import { import { broadcastMessage, + hasBroadHostPermissions, hostnamesFromMatches, } from './utils.js'; +import { + browser, + dnr, + localRead, localRemove, localWrite, + runtime, + windows, +} from './ext.js'; + import { enableRulesets, excludeFromStrictBlock, @@ -93,31 +93,9 @@ function getCurrentVersion() { /******************************************************************************/ -async function hasGreatPowers(origin) { - if ( /^https?:\/\//.test(origin) === false ) { return false; } - return browser.permissions.contains({ - origins: [ `${origin}/*` ], - }); -} - -async function hasOmnipotence() { - const manifest = runtime.getManifest(); - const hasOmnipotence = Array.isArray(manifest.host_permissions) && - manifest.host_permissions.includes(''); - if ( hasOmnipotence ) { return true; } - return browser.permissions.contains({ - origins: [ '' ], - }); -} - async function onPermissionsRemoved() { - const beforeMode = await getDefaultFilteringMode(); const modified = await syncWithBrowserPermissions(); if ( modified === false ) { return false; } - const afterMode = await getDefaultFilteringMode(); - if ( beforeMode > MODE_BASIC && afterMode <= MODE_BASIC ) { - updateDynamicRules(); - } registerInjectables(); return true; } @@ -126,7 +104,11 @@ async function onPermissionsRemoved() { async function onPermissionsAdded(permissions) { const details = pendingPermissionRequest; pendingPermissionRequest = undefined; - if ( details === undefined ) { return; } + if ( details === undefined ) { + const modified = await syncWithBrowserPermissions(); + if ( modified === false ) { return; } + return registerInjectables(); + } const defaultMode = await getDefaultFilteringMode(); if ( defaultMode >= MODE_OPTIMAL ) { return; } if ( Array.isArray(permissions.origins) === false ) { return; } @@ -240,6 +222,7 @@ function onMessage(request, sender, callback) { case 'getOptionsPageData': { Promise.all([ + hasBroadHostPermissions(), getDefaultFilteringMode(), getTrustedSites(), getRulesetDetails(), @@ -248,6 +231,7 @@ function onMessage(request, sender, callback) { adminReadEx('disabledFeatures'), ]).then(results => { const [ + hasOmnipotence, defaultFilteringMode, trustedSites, rulesetDetails, @@ -256,6 +240,7 @@ function onMessage(request, sender, callback) { disabledFeatures, ] = results; callback({ + hasOmnipotence, defaultFilteringMode, trustedSites: Array.from(trustedSites), enabledRulesets, @@ -314,21 +299,25 @@ function onMessage(request, sender, callback) { case 'popupPanelData': { Promise.all([ + hasBroadHostPermissions(), getFilteringMode(request.hostname), - hasOmnipotence(), - hasGreatPowers(request.origin), getEnabledRulesetsDetails(), adminReadEx('disabledFeatures'), ]).then(results => { + const [ + hasOmnipotence, + level, + rulesetDetails, + disabledFeatures, + ] = results; callback({ - level: results[0], + hasOmnipotence, + level, autoReload: rulesetConfig.autoReload, - hasOmnipotence: results[1], - hasGreatPowers: results[2], - rulesetDetails: results[3], + rulesetDetails, isSideloaded, developerMode: rulesetConfig.developerMode, - disabledFeatures: results[4], + disabledFeatures, }); }); return true; @@ -373,9 +362,6 @@ function onMessage(request, sender, callback) { ({ beforeLevel, afterLevel }) ) ).then(({ beforeLevel, afterLevel }) => { - if ( beforeLevel === 1 || afterLevel === 1 ) { - updateDynamicRules(); - } if ( afterLevel !== beforeLevel ) { registerInjectables(); } @@ -497,22 +483,16 @@ async function start() { }); } + // Switch to basic filtering if uBOL doesn't have broad permissions at + // install time. if ( process.firstRun ) { - const enableOptimal = await hasOmnipotence(); - if ( enableOptimal ) { - const afterLevel = await setDefaultFilteringMode(MODE_OPTIMAL); - if ( afterLevel === MODE_OPTIMAL ) { - updateDynamicRules(); + const enableOptimal = await hasBroadHostPermissions(); + if ( enableOptimal === false ) { + const afterLevel = await setDefaultFilteringMode(MODE_BASIC); + if ( afterLevel === MODE_BASIC ) { registerInjectables(); process.firstRun = false; } - } else { - const disableFirstRunPage = await adminRead('disableFirstRunPage'); - if ( disableFirstRunPage !== true ) { - runtime.openOptionsPage(); - } else { - process.firstRun = false; - } } } diff --git a/platform/mv3/extension/js/filter-lists.js b/platform/mv3/extension/js/filter-lists.js index b0e85e148d24f..a349ca832037a 100644 --- a/platform/mv3/extension/js/filter-lists.js +++ b/platform/mv3/extension/js/filter-lists.js @@ -90,12 +90,11 @@ function updateNodes(listEntries) { /******************************************************************************/ function rulesetStats(rulesetId) { - const hasOmnipotence = cachedRulesetData.defaultFilteringMode > 1; const rulesetDetails = rulesetMap.get(rulesetId); if ( rulesetDetails === undefined ) { return; } const { rules, filters } = rulesetDetails; let ruleCount = rules.plain + rules.regex; - if ( hasOmnipotence ) { + if ( cachedRulesetData.hasOmnipotence ) { ruleCount += rules.removeparam + rules.redirect + rules.modifyHeaders; } const filterCount = filters.accepted; diff --git a/platform/mv3/extension/js/mode-manager.js b/platform/mv3/extension/js/mode-manager.js index a8af4bbb930fe..9014ffd8f21d3 100644 --- a/platform/mv3/extension/js/mode-manager.js +++ b/platform/mv3/extension/js/mode-manager.js @@ -223,7 +223,7 @@ export async function readFilteringModeDetails(bypassCache = false) { adminReadEx('noFiltering'), ]); if ( userModes === undefined ) { - userModes = { basic: [ 'all-urls' ] }; + userModes = { optimal: [ 'all-urls' ] }; } userModes = unserializeModeDetails(userModes); if ( Array.isArray(adminNoFiltering) ) { @@ -347,9 +347,12 @@ export async function syncWithBrowserPermissions() { if ( beforeMode > MODE_BASIC && allowedHostnames.has('all-urls') === false ) { await setDefaultFilteringMode(MODE_BASIC); modified = true; + } else if ( beforeMode === MODE_BASIC && allowedHostnames.has('all-urls') ) { + await setDefaultFilteringMode(MODE_OPTIMAL); + modified = true; } const afterMode = await getDefaultFilteringMode(); - if ( afterMode > MODE_BASIC ) { return false; } + if ( afterMode > MODE_BASIC ) { return afterMode !== beforeMode; } const filteringModes = await getFilteringModeDetails(); const { optimal, complete } = filteringModes; for ( const hn of optimal ) { diff --git a/platform/mv3/extension/js/ruleset-manager.js b/platform/mv3/extension/js/ruleset-manager.js index eee885f1349d7..4dbc7f1e1b994 100644 --- a/platform/mv3/extension/js/ruleset-manager.js +++ b/platform/mv3/extension/js/ruleset-manager.js @@ -21,7 +21,6 @@ import { TAB_ID_NONE, - browser, dnr, i18n, localRead, localRemove, localWrite, @@ -34,9 +33,9 @@ import { saveRulesetConfig, } from './config.js'; - import { fetchJSON } from './fetch.js'; import { getAdminRulesets } from './admin.js'; +import { hasBroadHostPermissions } from './utils.js'; import { ubolLog } from './debug.js'; /******************************************************************************/ @@ -160,13 +159,7 @@ async function updateRemoveparamRules(currentRules, addRules, removeRuleIds) { removeRuleIds.push(rule.id); } - const [ - hasOmnipotence, - rulesetDetails, - ] = await Promise.all([ - browser.permissions.contains({ origins: [ '' ] }), - getEnabledRulesetsDetails(), - ]); + const rulesetDetails = await getEnabledRulesetsDetails(); // Fetch removeparam rules for all enabled rulesets const toFetch = []; @@ -178,12 +171,10 @@ async function updateRemoveparamRules(currentRules, addRules, removeRuleIds) { // Removeparam rules can only be enforced with omnipotence const allRules = []; - if ( hasOmnipotence ) { - for ( const rules of removeparamRulesets ) { - if ( Array.isArray(rules) === false ) { continue; } - for ( const rule of rules ) { - allRules.push(rule); - } + for ( const rules of removeparamRulesets ) { + if ( Array.isArray(rules) === false ) { continue; } + for ( const rule of rules ) { + allRules.push(rule); } } if ( allRules.length === 0 ) { return; } @@ -205,13 +196,7 @@ async function updateRedirectRules(currentRules, addRules, removeRuleIds) { removeRuleIds.push(rule.id); } - const [ - hasOmnipotence, - rulesetDetails, - ] = await Promise.all([ - browser.permissions.contains({ origins: [ '' ] }), - getEnabledRulesetsDetails(), - ]); + const rulesetDetails = await getEnabledRulesetsDetails(); // Fetch redirect rules for all enabled rulesets const toFetch = []; @@ -223,12 +208,10 @@ async function updateRedirectRules(currentRules, addRules, removeRuleIds) { // Redirect rules can only be enforced with omnipotence const allRules = []; - if ( hasOmnipotence ) { - for ( const rules of redirectRulesets ) { - if ( Array.isArray(rules) === false ) { continue; } - for ( const rule of rules ) { - allRules.push(rule); - } + for ( const rules of redirectRulesets ) { + if ( Array.isArray(rules) === false ) { continue; } + for ( const rule of rules ) { + allRules.push(rule); } } if ( allRules.length === 0 ) { return; } @@ -249,13 +232,7 @@ async function updateModifyHeadersRules(currentRules, addRules, removeRuleIds) { removeRuleIds.push(rule.id); } - const [ - hasOmnipotence, - rulesetDetails, - ] = await Promise.all([ - browser.permissions.contains({ origins: [ '' ] }), - getEnabledRulesetsDetails(), - ]); + const rulesetDetails = await getEnabledRulesetsDetails(); // Fetch modifyHeaders rules for all enabled rulesets const toFetch = []; @@ -267,12 +244,10 @@ async function updateModifyHeadersRules(currentRules, addRules, removeRuleIds) { // Redirect rules can only be enforced with omnipotence const allRules = []; - if ( hasOmnipotence ) { - for ( const rules of rulesets ) { - if ( Array.isArray(rules) === false ) { continue; } - for ( const rule of rules ) { - allRules.push(rule); - } + for ( const rules of rulesets ) { + if ( Array.isArray(rules) === false ) { continue; } + for ( const rule of rules ) { + allRules.push(rule); } } if ( allRules.length === 0 ) { return; } @@ -348,7 +323,7 @@ async function updateStrictBlockRules(currentRules, addRules, removeRuleIds) { permanentlyExcluded = [], temporarilyExcluded = [], ] = await Promise.all([ - browser.permissions.contains({ origins: [ '' ] }), + hasBroadHostPermissions(), getEnabledRulesetsDetails(), localRead('excludedStrictBlockHostnames'), sessionRead('excludedStrictBlockHostnames'), diff --git a/platform/mv3/extension/js/settings.js b/platform/mv3/extension/js/settings.js index 51ba0414af34d..6e76108752bc7 100644 --- a/platform/mv3/extension/js/settings.js +++ b/platform/mv3/extension/js/settings.js @@ -102,17 +102,18 @@ async function onFilteringModeChange(ev) { const newLevel = parseInt(input.value, 10); switch ( newLevel ) { - case 1: { // Revoke broad permissions - await browser.permissions.remove({ - origins: [ '' ] + case 1: { + const actualLevel = await sendMessage({ + what: 'setDefaultFilteringMode', + level: newLevel, }); - cachedRulesetData.defaultFilteringMode = 1; + cachedRulesetData.defaultFilteringMode = actualLevel; break; } case 2: - case 3: { // Request broad permissions + case 3: { const granted = await browser.permissions.request({ - origins: [ '' ] + origins: [ '' ], }); if ( granted ) { const actualLevel = await sendMessage({ @@ -120,6 +121,7 @@ async function onFilteringModeChange(ev) { level: newLevel, }); cachedRulesetData.defaultFilteringMode = actualLevel; + cachedRulesetData.hasOmnipotence = true; } break; } diff --git a/platform/mv3/extension/js/utils.js b/platform/mv3/extension/js/utils.js index f6f58fb90dce9..ff96e9ee63cb9 100644 --- a/platform/mv3/extension/js/utils.js +++ b/platform/mv3/extension/js/utils.js @@ -19,6 +19,8 @@ Home: https://github.com/gorhill/uBlock */ +import { browser } from './ext.js'; + /******************************************************************************/ function parsedURLromOrigin(origin) { @@ -134,6 +136,16 @@ const broadcastMessage = message => { /******************************************************************************/ +// https://developer.mozilla.org/docs/Mozilla/Add-ons/WebExtensions/manifest.json/host_permissions#requested_permissions_and_user_prompts +// "Users can grant or revoke host permissions on an ad hoc basis. Therefore, +// most browsers treat host_permissions as optional." + +async function hasBroadHostPermissions() { + return browser.permissions.contains({ origins: [ '' ] }); +} + +/******************************************************************************/ + export { broadcastMessage, parsedURLromOrigin, @@ -145,4 +157,5 @@ export { matchFromHostname, matchesFromHostnames, hostnamesFromMatches, + hasBroadHostPermissions, }; diff --git a/platform/mv3/firefox/manifest.json b/platform/mv3/firefox/manifest.json index 0bd10629ae648..c2dd5a3aac3a9 100644 --- a/platform/mv3/firefox/manifest.json +++ b/platform/mv3/firefox/manifest.json @@ -45,7 +45,7 @@ "open_in_tab": true, "page": "dashboard.html" }, - "optional_permissions": [ + "host_permissions": [ "" ], "permissions": [ From 0fdcd447943bc64f8333fc8dc0ba8fcfa2502c6d Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Wed, 9 Apr 2025 15:29:26 -0400 Subject: [PATCH 0787/1099] Import translation work from https://crowdin.com/project/ublock --- platform/mv3/description/webstore.br_FR.txt | 4 ++-- platform/mv3/description/webstore.ca.txt | 2 +- platform/mv3/description/webstore.el.txt | 2 +- platform/mv3/description/webstore.es.txt | 2 +- platform/mv3/description/webstore.ru.txt | 2 +- platform/mv3/description/webstore.sv.txt | 2 +- src/_locales/it/messages.json | 4 ++-- 7 files changed, 9 insertions(+), 9 deletions(-) diff --git a/platform/mv3/description/webstore.br_FR.txt b/platform/mv3/description/webstore.br_FR.txt index 279f62cd92bbb..d006200b050e8 100644 --- a/platform/mv3/description/webstore.br_FR.txt +++ b/platform/mv3/description/webstore.br_FR.txt @@ -1,4 +1,4 @@ -uBO Lite (uBOL) is an MV3-based content blocker. +uBO Lite (uBOL) zo ur stanker noazadurioù diazezet war ar manifesto MV3. Ar reolennoù dre ziouer a glot gant silañ dre ziouer uBlock Origin: @@ -7,6 +7,6 @@ Ar reolennoù dre ziouer a glot gant silañ dre ziouer uBlock Origin: - EasyPrivacy - Roll bruderezh ha servijerioù heuliañ Peter Lowe -You can enable more rulesets by visiting the options page -- click the _Cogs_ icon in the popup panel. +Tu zo deoc'h ouzhpennañ reolennoù all en arventennoù -- klikit war an ikon _kendentadur_ er banell popup. Disklêriañ a ra uBOL penn-da-benn, da lavaret eo n'eus ket ezhomm eus un argerzh uBOL padus evit ma c'hoarvezfe ar silañ, ha silañ endalc'hadoù diazezet war enlakaat CSS/JS a vez graet en un doare fizius gant ar merdeer e-unan kentoc'h eget gant an astenn. Kement-se a dalvez ne vez ket gounezet gant uBOL e-unan arc'hwelioù CPU/memor e-pad ma vez stanket an endalc'hadoù -- ezhomm zo eus argerzh al labourer servij uBOL _nemet_ pa vez etregweredet gant ar banell digeriñ pe ar pajennoù dibarzhioù. diff --git a/platform/mv3/description/webstore.ca.txt b/platform/mv3/description/webstore.ca.txt index da514135d92c1..e65272d84eb45 100644 --- a/platform/mv3/description/webstore.ca.txt +++ b/platform/mv3/description/webstore.ca.txt @@ -1,4 +1,4 @@ -uBO Lite (uBOL) is an MV3-based content blocker. +L'uBO Lite (uBOL) és un blocador de contingut basat en MV3. El conjunt de regles per defecte correspon al conjunt de filtres per defecte d'uBlock Origin: diff --git a/platform/mv3/description/webstore.el.txt b/platform/mv3/description/webstore.el.txt index 9244f7a03efbe..50b2449776486 100644 --- a/platform/mv3/description/webstore.el.txt +++ b/platform/mv3/description/webstore.el.txt @@ -1,4 +1,4 @@ -Το uBO Lite (uBOL) είναι εργαλείο φραγής περιεχομένου βασισμένο στο MV3. +Το uBO Lite (uBOL) είναι εργαλείο φραγής περιεχομένου, που βασίζεται στο MV3. Το προεπιλεγμένο σύνολο κανόνων αντιστοιχεί στο προεπιλεγμένο σύνολο φίλτρων του uBlock Origin: diff --git a/platform/mv3/description/webstore.es.txt b/platform/mv3/description/webstore.es.txt index 34d509a85c8e6..58a215590ed4b 100644 --- a/platform/mv3/description/webstore.es.txt +++ b/platform/mv3/description/webstore.es.txt @@ -1,4 +1,4 @@ -uBO Lite (uBOL) is an MV3-based content blocker. +uBo Lite (uBOL) es un bloqueador de contenido basado en MV3. Por defecto ya trae configuradas las siguientes listas de filtros: diff --git a/platform/mv3/description/webstore.ru.txt b/platform/mv3/description/webstore.ru.txt index c251713136577..e38d774f3a5e2 100644 --- a/platform/mv3/description/webstore.ru.txt +++ b/platform/mv3/description/webstore.ru.txt @@ -1,4 +1,4 @@ -uBO Lite (uBOL) — это блокировщик содержимого на базе MV3. +uBO Lite (uBOL) — это блокировщик веб-элементов на базе MV3. Стандартный набор правил соответствует типовому набору фильтров uBlock Origin: diff --git a/platform/mv3/description/webstore.sv.txt b/platform/mv3/description/webstore.sv.txt index 017e35828de6c..47216fb547a4f 100644 --- a/platform/mv3/description/webstore.sv.txt +++ b/platform/mv3/description/webstore.sv.txt @@ -1,4 +1,4 @@ -uBO Lite (uBOL) is an MV3-based content blocker. +uBO Lite (uBOL) är en MV3-baserad innehållsblockerare. Standardregeluppsättningen motsvarar uBlock Origins standardfilteruppsättning: diff --git a/src/_locales/it/messages.json b/src/_locales/it/messages.json index fc7b8499500bc..9e4041415ff26 100644 --- a/src/_locales/it/messages.json +++ b/src/_locales/it/messages.json @@ -28,7 +28,7 @@ "description": "appears as tab name in dashboard" }, "3pPageName": { - "message": "Filtri di terze parti", + "message": "Liste dei filtri", "description": "appears as tab name in dashboard" }, "1pPageName": { @@ -76,7 +76,7 @@ "description": "Message to be read by screen readers" }, "popupPowerSwitchInfo2": { - "message": "Clicca per attivare uBlock₀ in questo sito.", + "message": "Clicca per abilitare uBlock₀ per questo sito.", "description": "Message to be read by screen readers" }, "popupBlockedRequestPrompt": { From 9029d1d715f01bf04d3210554e0da238c2489481 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Wed, 9 Apr 2025 18:23:01 -0400 Subject: [PATCH 0788/1099] Fix regression in categorizing highly generic filters at load time Related feedback: https://github.com/uBlockOrigin/uBlock-issues/issues/3235#issuecomment-2790807915 Regression from: https://github.com/gorhill/uBlock/commit/8b696a691a0871da8200d1806300842d988a4326 --- src/js/cosmetic-filtering.js | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/src/js/cosmetic-filtering.js b/src/js/cosmetic-filtering.js index 24d2ab92506fa..7cb520a988063 100644 --- a/src/js/cosmetic-filtering.js +++ b/src/js/cosmetic-filtering.js @@ -474,18 +474,21 @@ CosmeticFilteringEngine.prototype.fromCompiledContent = function(reader, options // Handle specific filters meant to apply everywhere, i.e. selectors // not to be injected conditionally through the DOM surveyor. // hash, *, .promoted-tweet - case 8: - if ( args[1] === '*' ) { + case 8: { + if ( args[1] === '*' && args[2].charCodeAt(0) === 0x2D /* + */ ) { const selector = args[2].slice(1); - if ( this.reSimpleHighGeneric.test(selector) ) - this.highlyGeneric.simple.dict.add(selector); - else { - this.highlyGeneric.complex.dict.add(selector); + if ( selector.charCodeAt(0) !== 0x7B /* { */ ) { + if ( this.reSimpleHighGeneric.test(selector) ) { + this.highlyGeneric.simple.dict.add(selector); + } else { + this.highlyGeneric.complex.dict.add(selector); + } + break; } - break; } this.specificFilters.store(args[1], args[2]); break; + } default: this.discardedCount += 1; break; From 2679aafc17cdc1e56860fad6fe93977cf16ff547 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Wed, 9 Apr 2025 18:25:20 -0400 Subject: [PATCH 0789/1099] New revision for dev build --- dist/version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dist/version b/dist/version index b95d32346c890..93ade2664a8b0 100644 --- a/dist/version +++ b/dist/version @@ -1 +1 @@ -1.63.3.10 \ No newline at end of file +1.63.3.11 \ No newline at end of file From 981be2d5b2ca6344cdab70ea1b33f8e819f22808 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Wed, 9 Apr 2025 18:31:38 -0400 Subject: [PATCH 0790/1099] Make Firefox dev build auto-update --- dist/firefox/updates.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dist/firefox/updates.json b/dist/firefox/updates.json index 535a8721df331..805311aaebe0f 100644 --- a/dist/firefox/updates.json +++ b/dist/firefox/updates.json @@ -3,9 +3,9 @@ "uBlock0@raymondhill.net": { "updates": [ { - "version": "1.63.3.10", + "version": "1.63.3.11", "browser_specific_settings": { "gecko": { "strict_min_version": "92.0" } }, - "update_link": "https://github.com/gorhill/uBlock/releases/download/1.63.3b10/uBlock0_1.63.3b10.firefox.signed.xpi" + "update_link": "https://github.com/gorhill/uBlock/releases/download/1.63.3b11/uBlock0_1.63.3b11.firefox.signed.xpi" } ] } From d0e303ca19bf35b876ddcdff48e658ea5eeb0bc7 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Thu, 10 Apr 2025 07:32:09 -0400 Subject: [PATCH 0791/1099] Fix regression in response header filtering Related commit: https://github.com/gorhill/uBlock/commit/8b696a691a0871da8200d1806300842d988a4326 --- src/js/httpheader-filtering.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/js/httpheader-filtering.js b/src/js/httpheader-filtering.js index 9d09543ab6c37..c5aa918ecef1c 100644 --- a/src/js/httpheader-filtering.js +++ b/src/js/httpheader-filtering.js @@ -145,8 +145,8 @@ httpheaderFilteringEngine.apply = function(fctxt, headers) { } // Split filters in different groups - const filters = new Map(); - const exceptions = new Map(); + const filters = new Set(); + const exceptions = new Set(); for ( const s of all ) { const selector = s.slice(1); if ( s.charCodeAt(0) === 0x2D /* - */ ) { From ae336c3688f87607fcd100438890cc88af28bf83 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Thu, 10 Apr 2025 07:37:01 -0400 Subject: [PATCH 0792/1099] New revision for dev build --- dist/version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dist/version b/dist/version index 93ade2664a8b0..5fba9a30cbacd 100644 --- a/dist/version +++ b/dist/version @@ -1 +1 @@ -1.63.3.11 \ No newline at end of file +1.63.3.12 \ No newline at end of file From 70bdcfbae60356d8979def17433f7fdbc6b8a073 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Thu, 10 Apr 2025 07:56:12 -0400 Subject: [PATCH 0793/1099] Make Firefox dev build auto-update Mistakenly ran the update script from the safari-ubol branch. --- dist/firefox/updates.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dist/firefox/updates.json b/dist/firefox/updates.json index 805311aaebe0f..097caf063eab2 100644 --- a/dist/firefox/updates.json +++ b/dist/firefox/updates.json @@ -3,9 +3,9 @@ "uBlock0@raymondhill.net": { "updates": [ { - "version": "1.63.3.11", + "version": "1.63.3.12", "browser_specific_settings": { "gecko": { "strict_min_version": "92.0" } }, - "update_link": "https://github.com/gorhill/uBlock/releases/download/1.63.3b11/uBlock0_1.63.3b11.firefox.signed.xpi" + "update_link": "https://github.com/gorhill/uBlock/releases/download/1.63.3b12/uBlock0_1.63.3b12.firefox.signed.xpi" } ] } From d1c57d3fa6de0ae9f2a530ffaccfe0dedec42c60 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Thu, 10 Apr 2025 09:35:19 -0400 Subject: [PATCH 0794/1099] Remove stale extraneous POL-2 list Related issue: https://github.com/uBlockOrigin/uAssets/issues/27935 --- assets/assets.dev.json | 15 +-------------- assets/assets.json | 15 +-------------- 2 files changed, 2 insertions(+), 28 deletions(-) diff --git a/assets/assets.dev.json b/assets/assets.dev.json index 861a99ebe3857..5a2b18c6351e5 100644 --- a/assets/assets.dev.json +++ b/assets/assets.dev.json @@ -780,25 +780,12 @@ "POL-0": { "content": "filters", "group": "regions", - "parent": "🇵🇱pl: Oficjalne Polskie Filtry", "off": true, "title": "🇵🇱pl: Oficjalne Polskie Filtry do uBlocka Origin", "tags": "ads polish polski", "lang": "szl pl", "contentURL": "https://raw.githubusercontent.com/MajkiIT/polish-ads-filter/master/polish-adblock-filters/adblock.txt", - "supportURL": "https://github.com/MajkiIT/polish-ads-filter/issues", - "instructionURL": "https://github.com/MajkiIT/polish-ads-filter#polish-filters-for-adblock-ublock-origin--adguard" - }, - "POL-2": { - "content": "filters", - "group": "regions", - "parent": "🇵🇱pl: Oficjalne Polskie Filtry", - "off": true, - "title": "🇵🇱pl: Oficjalne polskie filtry przeciwko alertom o Adblocku", - "tags": "ads polish polski", - "lang": "szl pl", - "contentURL": "https://raw.githubusercontent.com/olegwukr/polish-privacy-filters/master/anti-adblock.txt", - "supportURL": "https://github.com/olegwukr/polish-privacy-filters/issues" + "supportURL": "https://github.com/MajkiIT/polish-ads-filter" }, "ROU-1": { "content": "filters", diff --git a/assets/assets.json b/assets/assets.json index d1554d6a12e98..3bb526b44b043 100644 --- a/assets/assets.json +++ b/assets/assets.json @@ -780,25 +780,12 @@ "POL-0": { "content": "filters", "group": "regions", - "parent": "🇵🇱pl: Oficjalne Polskie Filtry", "off": true, "title": "🇵🇱pl: Oficjalne Polskie Filtry do uBlocka Origin", "tags": "ads polish polski", "lang": "szl pl", "contentURL": "https://raw.githubusercontent.com/MajkiIT/polish-ads-filter/master/polish-adblock-filters/adblock.txt", - "supportURL": "https://github.com/MajkiIT/polish-ads-filter/issues", - "instructionURL": "https://github.com/MajkiIT/polish-ads-filter#polish-filters-for-adblock-ublock-origin--adguard" - }, - "POL-2": { - "content": "filters", - "group": "regions", - "parent": "🇵🇱pl: Oficjalne Polskie Filtry", - "off": true, - "title": "🇵🇱pl: Oficjalne polskie filtry przeciwko alertom o Adblocku", - "tags": "ads polish polski", - "lang": "szl pl", - "contentURL": "https://raw.githubusercontent.com/olegwukr/polish-privacy-filters/master/anti-adblock.txt", - "supportURL": "https://github.com/olegwukr/polish-privacy-filters/issues" + "supportURL": "https://github.com/MajkiIT/polish-ads-filter" }, "ROU-1": { "content": "filters", From 3d2f70ac567f93f40e08fa03bc6df54d1f31bb8a Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Fri, 11 Apr 2025 09:06:28 -0400 Subject: [PATCH 0795/1099] Add POL list for regional malware Related discussion: https://github.com/uBlockOrigin/uBlock-discussions/discussions/890#discussioncomment-9632360 --- assets/assets.dev.json | 10 ++++++++++ assets/assets.json | 10 ++++++++++ 2 files changed, 20 insertions(+) diff --git a/assets/assets.dev.json b/assets/assets.dev.json index 5a2b18c6351e5..55307977ae1d4 100644 --- a/assets/assets.dev.json +++ b/assets/assets.dev.json @@ -787,6 +787,16 @@ "contentURL": "https://raw.githubusercontent.com/MajkiIT/polish-ads-filter/master/polish-adblock-filters/adblock.txt", "supportURL": "https://github.com/MajkiIT/polish-ads-filter" }, + "POL-3": { + "content": "filters", + "group": "regions", + "off": true, + "title": "🇵🇱pl: CERT.PL's Warning List", + "tags": "malware polish polski", + "lang": "szl pl", + "contentURL": "https://hole.cert.pl/domains/v2/domains_adblock.txt", + "supportURL": "https://cert.pl/lista-ostrzezen/" + }, "ROU-1": { "content": "filters", "group": "regions", diff --git a/assets/assets.json b/assets/assets.json index 3bb526b44b043..68a9e9707aced 100644 --- a/assets/assets.json +++ b/assets/assets.json @@ -787,6 +787,16 @@ "contentURL": "https://raw.githubusercontent.com/MajkiIT/polish-ads-filter/master/polish-adblock-filters/adblock.txt", "supportURL": "https://github.com/MajkiIT/polish-ads-filter" }, + "POL-3": { + "content": "filters", + "group": "regions", + "off": true, + "title": "🇵🇱pl: CERT.PL's Warning List", + "tags": "malware polish polski", + "lang": "szl pl", + "contentURL": "https://hole.cert.pl/domains/v2/domains_adblock.txt", + "supportURL": "https://cert.pl/lista-ostrzezen/" + }, "ROU-1": { "content": "filters", "group": "regions", From 15e832da8a1dc074a21bf567691ebd01056b06d9 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Fri, 11 Apr 2025 09:35:38 -0400 Subject: [PATCH 0796/1099] Mind potential race condition when dynamically registering scriptlets In Firefox, scriptlets are dynamically registered as content scripts to ensure they execute in a timely manner. The race condition could lead to scriptlet injection failing at browser launch time in Firefox when the setting "Suspend network activity until all filter lists are loaded" had been disabled[1], even after forcing a page reload. Causing the filter lists to reload would make the issue go away. [1] Default is enabled in Firefox and it is strongly advised to NOT change this. --- src/js/scriptlet-filtering.js | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/js/scriptlet-filtering.js b/src/js/scriptlet-filtering.js index 89a5ec92c3b2d..f71b07c2ffddd 100644 --- a/src/js/scriptlet-filtering.js +++ b/src/js/scriptlet-filtering.js @@ -38,6 +38,7 @@ import µb from './background.js'; /******************************************************************************/ const contentScriptRegisterer = { + id: 1, hostnameToDetails: new Map(), register(hostname, code) { if ( browser.contentScripts === undefined ) { return false; } @@ -47,9 +48,10 @@ const contentScriptRegisterer = { if ( code === details.code ) { return details.handle instanceof Promise === false; } - details.handle.unregister(); + this.unregisterHandle(details.handle); this.hostnameToDetails.delete(hostname); } + const id = this.id++; const promise = browser.contentScripts.register({ js: [ { code } ], allFrames: true, @@ -57,12 +59,14 @@ const contentScriptRegisterer = { matchAboutBlank: true, runAt: 'document_start', }).then(handle => { - this.hostnameToDetails.set(hostname, { handle, code }); - return handle; + const details = this.hostnameToDetails.get(hostname); + if ( details === undefined ) { return; } + if ( details.id !== id ) { return; } + details.handle = handle; }).catch(( ) => { this.hostnameToDetails.delete(hostname); }); - this.hostnameToDetails.set(hostname, { handle: promise, code }); + this.hostnameToDetails.set(hostname, { id, handle: promise, code }); return false; }, unregister(hostname) { From 11e159fd317267c3b8eba9309d1d80239791b5e0 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Fri, 11 Apr 2025 09:49:03 -0400 Subject: [PATCH 0797/1099] [mv3] Discard regex- or path-based targets in static extended filters There is no support yet for such filters in uBO Lite. --- src/js/static-dnr-filtering.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/js/static-dnr-filtering.js b/src/js/static-dnr-filtering.js index 3f8081c7e28e5..b097e51d3b64b 100644 --- a/src/js/static-dnr-filtering.js +++ b/src/js/static-dnr-filtering.js @@ -31,7 +31,7 @@ import staticNetFilteringEngine from './static-net-filtering.js'; /******************************************************************************/ -const isRegex = hn => hn.startsWith('/') && hn.endsWith('/'); +const isRegexOrPath = hn => hn.includes('/'); /******************************************************************************/ @@ -112,7 +112,7 @@ function addExtendedToDNR(context, parser) { for ( const { hn, not, bad } of parser.getExtFilterDomainIterator() ) { if ( bad ) { continue; } if ( exception ) { continue; } - if ( isRegex(hn) ) { continue; } + if ( isRegexOrPath(hn) ) { continue; } let details = context.scriptletFilters.get(argsToken); if ( details === undefined ) { context.scriptletFilters.set(argsToken, details = { args }); @@ -170,7 +170,7 @@ function addExtendedToDNR(context, parser) { }; for ( const { hn, not, bad } of parser.getExtFilterDomainIterator() ) { if ( bad ) { continue; } - if ( isRegex(hn) ) { continue; } + if ( isRegexOrPath(hn) ) { continue; } if ( not ) { if ( rule.condition.excludedInitiatorDomains === undefined ) { rule.condition.excludedInitiatorDomains = []; @@ -225,7 +225,7 @@ function addExtendedToDNR(context, parser) { for ( const { hn, not, bad } of parser.getExtFilterDomainIterator() ) { if ( bad ) { continue; } if ( not && exception ) { continue; } - if ( isRegex(hn) ) { continue; } + if ( isRegexOrPath(hn) ) { continue; } if ( details === undefined ) { context.specificCosmeticFilters.set(compiled, details = {}); } From 809d4f93c42b4bd13f587433e4c23333cf61970c Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Fri, 11 Apr 2025 09:58:51 -0400 Subject: [PATCH 0798/1099] New revision for dev build --- dist/version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dist/version b/dist/version index 5fba9a30cbacd..e585d5999e2cd 100644 --- a/dist/version +++ b/dist/version @@ -1 +1 @@ -1.63.3.12 \ No newline at end of file +1.63.3.13 \ No newline at end of file From ad68834479ab7f8e4affbc0dcebdb10f480d6d8b Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Fri, 11 Apr 2025 10:01:31 -0400 Subject: [PATCH 0799/1099] Update changelog --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 005f841fe0839..128b2c4005013 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,5 @@ +- [Mind potential race condition when dynamically registering scriptlets](https://github.com/gorhill/uBlock/commit/15e832da8a) +- [Fix undue unchecking of setting in "My filters"](https://github.com/gorhill/uBlock/commit/2bb6999e3f) - [Add path support as target option in static extended filtering](https://github.com/gorhill/uBlock/commit/8b696a691a) - [Add `trusted-prevent-fetch` scriptlet](https://github.com/gorhill/uBlock/commit/4ce26b63ff) - [Code viewer shouldn't be maximizable](https://github.com/gorhill/uBlock/commit/97e740bd2c) From 90e3c352ec8f08ace2ceda737c06c6b3a05aeb0a Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Fri, 11 Apr 2025 10:11:13 -0400 Subject: [PATCH 0800/1099] Make Firefox dev build auto-update --- dist/firefox/updates.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dist/firefox/updates.json b/dist/firefox/updates.json index 097caf063eab2..7fa979b9a45a1 100644 --- a/dist/firefox/updates.json +++ b/dist/firefox/updates.json @@ -3,9 +3,9 @@ "uBlock0@raymondhill.net": { "updates": [ { - "version": "1.63.3.12", + "version": "1.63.3.13", "browser_specific_settings": { "gecko": { "strict_min_version": "92.0" } }, - "update_link": "https://github.com/gorhill/uBlock/releases/download/1.63.3b12/uBlock0_1.63.3b12.firefox.signed.xpi" + "update_link": "https://github.com/gorhill/uBlock/releases/download/1.63.3b13/uBlock0_1.63.3b13.firefox.signed.xpi" } ] } From f25a437fd1050e554ed9ab24c5e36b093a29a406 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Fri, 11 Apr 2025 18:20:51 -0400 Subject: [PATCH 0801/1099] Code review re. scriptlets lookup Possibly fixes a race condition at browser launch causing empty scriptlets to be injected (and cached). --- src/js/redirect-engine.js | 1 + src/js/scriptlet-filtering-core.js | 22 ++++++++++++---------- src/js/scriptlet-filtering.js | 1 + src/js/static-ext-filtering-db.js | 10 +++++----- 4 files changed, 19 insertions(+), 15 deletions(-) diff --git a/src/js/redirect-engine.js b/src/js/redirect-engine.js index 5e7a55884d283..14df52200a08a 100644 --- a/src/js/redirect-engine.js +++ b/src/js/redirect-engine.js @@ -460,6 +460,7 @@ class RedirectEngine { for ( const [ token, entry ] of this.resources ) { this.resources.set(token, RedirectEntry.fromDetails(entry)); } + this.modifyTime = Date.now(); return true; } diff --git a/src/js/scriptlet-filtering-core.js b/src/js/scriptlet-filtering-core.js index eda8876348e0a..5c56423c2b1ff 100644 --- a/src/js/scriptlet-filtering-core.js +++ b/src/js/scriptlet-filtering-core.js @@ -25,11 +25,6 @@ import { redirectEngine as reng } from './redirect-engine.js'; /******************************************************************************/ -const $mainWorldMap = new Map(); -const $isolatedWorldMap = new Map(); - -/******************************************************************************/ - // For debugging convenience: all the top function calls will appear // at the bottom of a generated content script const codeSorter = (a, b) => { @@ -243,18 +238,27 @@ export class ScriptletFilteringEngine { } } + const mainWorldMap = new Map(); + const isolatedWorldMap = new Map(); + for ( const token of scriptlets ) { - lookupScriptlet(token, $mainWorldMap, $isolatedWorldMap, options.debug); + lookupScriptlet(token, mainWorldMap, isolatedWorldMap, options.debug); + } + + if ( scriptlets.size !== 0 ) { + if ( mainWorldMap.size === 0 ) { + if ( isolatedWorldMap.size === 0 ) { return; } + } } const mainWorldCode = []; - for ( const js of $mainWorldMap.values() ) { + for ( const js of mainWorldMap.values() ) { mainWorldCode.push(js); } mainWorldCode.sort(codeSorter); const isolatedWorldCode = []; - for ( const js of $isolatedWorldMap.values() ) { + for ( const js of isolatedWorldMap.values() ) { isolatedWorldCode.push(js); } isolatedWorldCode.sort(codeSorter); @@ -267,8 +271,6 @@ export class ScriptletFilteringEngine { ...Array.from(exceptions).map(a => decompile(a, true)), ], }; - $mainWorldMap.clear(); - $isolatedWorldMap.clear(); const scriptletGlobals = options.scriptletGlobals || {}; diff --git a/src/js/scriptlet-filtering.js b/src/js/scriptlet-filtering.js index f71b07c2ffddd..c61444bb6faa6 100644 --- a/src/js/scriptlet-filtering.js +++ b/src/js/scriptlet-filtering.js @@ -328,6 +328,7 @@ export class ScriptletFilteringEngineEx extends ScriptletFilteringEngine { if ( typeof details.frameId !== 'number' ) { return; } const hostname = hostnameFromURI(details.url); + if ( hostname === '' ) { return; } const domain = domainFromHostname(hostname); const scriptletDetails = this.retrieve({ diff --git a/src/js/static-ext-filtering-db.js b/src/js/static-ext-filtering-db.js index 2e2347491877c..6d69fd63448c7 100644 --- a/src/js/static-ext-filtering-db.js +++ b/src/js/static-ext-filtering-db.js @@ -92,9 +92,9 @@ export class StaticExtFilteringHostnameDB { if ( target.includes('/') ) { return this.#storeMatcher(target, iStr); } - const iList = this.#hostnameToStringListMap.get(target); + const iList = this.#hostnameToStringListMap.get(target) ?? 0; this.#hostnameToStringListMap.set(target, this.#linkedLists.length); - this.#linkedLists.push(iStr, iList !== undefined ? iList : 0); + this.#linkedLists.push(iStr, iList); } #storeMatcher(target, iStr) { @@ -105,11 +105,11 @@ export class StaticExtFilteringHostnameDB { this.#matcherSlots.push({ isRegex, hn, pn, iList: 0 }); this.#matcherMap.set(target, iMatcher); if ( isRegex === false ) { - const iMatcherList = this.#hostnameToMatcherListMap.get(hn) || 0; + const iMatcherList = this.#hostnameToMatcherListMap.get(hn) ?? 0; this.#hostnameToMatcherListMap.set(hn, this.#linkedLists.length); this.#linkedLists.push(iMatcher, iMatcherList); } else { - const iMatcherList = this.#hostnameToMatcherListMap.get('') || 0; + const iMatcherList = this.#hostnameToMatcherListMap.get('') ?? 0; this.#hostnameToMatcherListMap.set('', this.#linkedLists.length); this.#linkedLists.push(iMatcher, iMatcherList); } @@ -176,7 +176,7 @@ export class StaticExtFilteringHostnameDB { } #retrieveSpecificsByRegex(hn, out, hostname, pathname) { - let iMatchList = this.#hostnameToMatcherListMap.get(hn) || 0; + let iMatchList = this.#hostnameToMatcherListMap.get(hn) ?? 0; while ( iMatchList !== 0 ) { const iMatchSlot = this.#linkedLists[iMatchList+0]; const matcher = this.#matcherSlots[iMatchSlot]; From 1e783d62e0f6a3fa9be3aaf32f97f77d29aecb80 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Fri, 11 Apr 2025 18:44:55 -0400 Subject: [PATCH 0802/1099] New revision for dev build --- dist/version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dist/version b/dist/version index e585d5999e2cd..e3d7806f2a63e 100644 --- a/dist/version +++ b/dist/version @@ -1 +1 @@ -1.63.3.13 \ No newline at end of file +1.63.3.14 \ No newline at end of file From af054f7980a30c7dd22dd2984ed3d65862b306f8 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Fri, 11 Apr 2025 18:51:21 -0400 Subject: [PATCH 0803/1099] Make Firefox dev build auto-update --- dist/firefox/updates.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dist/firefox/updates.json b/dist/firefox/updates.json index 7fa979b9a45a1..2ee1819c9c33a 100644 --- a/dist/firefox/updates.json +++ b/dist/firefox/updates.json @@ -3,9 +3,9 @@ "uBlock0@raymondhill.net": { "updates": [ { - "version": "1.63.3.13", + "version": "1.63.3.14", "browser_specific_settings": { "gecko": { "strict_min_version": "92.0" } }, - "update_link": "https://github.com/gorhill/uBlock/releases/download/1.63.3b13/uBlock0_1.63.3b13.firefox.signed.xpi" + "update_link": "https://github.com/gorhill/uBlock/releases/download/1.63.3b14/uBlock0_1.63.3b14.firefox.signed.xpi" } ] } From ad2add46764a9281f03e01a54be41cd8e3fe48f2 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sat, 12 Apr 2025 08:08:12 -0400 Subject: [PATCH 0804/1099] Code review --- src/js/scriptlet-filtering.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/js/scriptlet-filtering.js b/src/js/scriptlet-filtering.js index c61444bb6faa6..7980a40646728 100644 --- a/src/js/scriptlet-filtering.js +++ b/src/js/scriptlet-filtering.js @@ -317,7 +317,7 @@ export class ScriptletFilteringEngineEx extends ScriptletFilteringEngine { filters: scriptletDetails.filters, }; - if ( request.nocache !== true ) { + if ( hostname !== '' && request.nocache !== true ) { this.scriptletCache.add(hostname, cachedScriptletDetails); } @@ -328,7 +328,6 @@ export class ScriptletFilteringEngineEx extends ScriptletFilteringEngine { if ( typeof details.frameId !== 'number' ) { return; } const hostname = hostnameFromURI(details.url); - if ( hostname === '' ) { return; } const domain = domainFromHostname(hostname); const scriptletDetails = this.retrieve({ From a06f09acda7b61b2eb3be13f14b6cb3298fc861c Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sat, 12 Apr 2025 09:40:46 -0400 Subject: [PATCH 0805/1099] Group POL lists together --- assets/assets.dev.json | 2 ++ assets/assets.json | 2 ++ 2 files changed, 4 insertions(+) diff --git a/assets/assets.dev.json b/assets/assets.dev.json index 55307977ae1d4..01a59eb532561 100644 --- a/assets/assets.dev.json +++ b/assets/assets.dev.json @@ -780,6 +780,7 @@ "POL-0": { "content": "filters", "group": "regions", + "parent": "🇵🇱pl: Oficjalne Polskie Filtry", "off": true, "title": "🇵🇱pl: Oficjalne Polskie Filtry do uBlocka Origin", "tags": "ads polish polski", @@ -790,6 +791,7 @@ "POL-3": { "content": "filters", "group": "regions", + "parent": "🇵🇱pl: Oficjalne Polskie Filtry", "off": true, "title": "🇵🇱pl: CERT.PL's Warning List", "tags": "malware polish polski", diff --git a/assets/assets.json b/assets/assets.json index 68a9e9707aced..30c5d3146fe51 100644 --- a/assets/assets.json +++ b/assets/assets.json @@ -780,6 +780,7 @@ "POL-0": { "content": "filters", "group": "regions", + "parent": "🇵🇱pl: Oficjalne Polskie Filtry", "off": true, "title": "🇵🇱pl: Oficjalne Polskie Filtry do uBlocka Origin", "tags": "ads polish polski", @@ -790,6 +791,7 @@ "POL-3": { "content": "filters", "group": "regions", + "parent": "🇵🇱pl: Oficjalne Polskie Filtry", "off": true, "title": "🇵🇱pl: CERT.PL's Warning List", "tags": "malware polish polski", From 20dd6065049d0a80b23db83011ae0c10ce34cbd2 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sat, 12 Apr 2025 10:51:50 -0400 Subject: [PATCH 0806/1099] Add `trusted-create-element` scriptlet As discussed with filter list maintainers. * @scriptlet trusted-create-element * * @description * Element(s) from a parsed HTML string are added as child element(s) to a * specific parent element in the DOM. * * @param parent * A CSS selector identifying the element to which created element(s) will be * added. * * @param html * An HTML string to be parsed using DOMParser, and which resulting elements * are to be added as child element(s). * * @param duration * Optional. If specified, the time in ms after which the added elements will * be removed. No removal will occur if not specified. --- src/js/resources/create-element.js | 112 +++++++++++++++++++++++++++++ src/js/resources/scriptlets.js | 1 + 2 files changed, 113 insertions(+) create mode 100644 src/js/resources/create-element.js diff --git a/src/js/resources/create-element.js b/src/js/resources/create-element.js new file mode 100644 index 0000000000000..7b4ad5b02a057 --- /dev/null +++ b/src/js/resources/create-element.js @@ -0,0 +1,112 @@ +/******************************************************************************* + + uBlock Origin - a comprehensive, efficient content blocker + Copyright (C) 2025-present Raymond Hill + + 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/}. + + Home: https://github.com/gorhill/uBlock + +*/ + +import { registerScriptlet } from './base.js'; +import { safeSelf } from './safe-self.js'; + +/******************************************************************************/ + +/** + * @scriptlet trusted-create-element + * + * @description + * Element(s) from a parsed HTML string are added as child element(s) to a + * specific parent element in the DOM. + * + * @param parent + * A CSS selector identifying the element to which created element(s) will be + * added. + * + * @param html + * An HTML string to be parsed using DOMParser, and which resulting elements + * are to be added as child element(s). + * + * @param duration + * Optional. If specified, the time in ms after which the added elements will + * be removed. No removal will occur if not specified. + * + * */ + +function trustedCreateElement( + parentSelector, + htmlStr = '', + durationStr = '' +) { + if ( parentSelector === '' ) { return; } + if ( htmlStr === '' ) { return; } + const safe = safeSelf(); + const logPrefix = safe.makeLogPrefix('trusted-create-element', parentSelector, htmlStr, durationStr); + // We do not want to recursively create elements + self.trustedCreateElement = true; + let ancestor = self.frameElement; + while ( ancestor !== null ) { + const doc = ancestor.ownerDocument; + if ( doc === null ) { break; } + const win = doc.defaultView; + if ( win === null ) { break; } + if ( win.trustedCreateElement ) { return; } + ancestor = ancestor.frameElement; + } + const duration = parseInt(durationStr, 10); + const domParser = new DOMParser(); + const externalDoc = domParser.parseFromString(htmlStr, 'text/html'); + const docFragment = new DocumentFragment(); + const toRemove = []; + for ( const external of externalDoc.querySelectorAll('body > *') ) { + const imported = document.adoptNode(external); + docFragment.append(imported); + if ( isNaN(duration) ) { continue; } + toRemove.push(imported); + } + if ( docFragment.childElementCount === 0 ) { return; } + const remove = ( ) => { + for ( const elem of toRemove ) { + elem.remove(); + } + safe.uboLog(logPrefix, 'Element(s) removed'); + }; + const append = ( ) => { + const parent = document.querySelector(parentSelector); + if ( parent === null ) { return false; } + parent.append(docFragment); + safe.uboLog(logPrefix, 'Element(s) appended'); + if ( toRemove.length === 0 ) { return true; } + setTimeout(remove, duration); + return true; + }; + if ( append() ) { return; } + const observer = new MutationObserver(( ) => { + if ( append() === false ) { return; } + observer.disconnect(); + }); + observer.observe(document, { childList: true, subtree: true }); +} +registerScriptlet(trustedCreateElement, { + name: 'trusted-create-element.js', + requiresTrust: true, + dependencies: [ + safeSelf, + ], + world: 'ISOLATED', +}); + +/******************************************************************************/ diff --git a/src/js/resources/scriptlets.js b/src/js/resources/scriptlets.js index c6ef19ec9d3fc..e1ec8be16c246 100755 --- a/src/js/resources/scriptlets.js +++ b/src/js/resources/scriptlets.js @@ -21,6 +21,7 @@ */ import './attribute.js'; +import './create-element.js'; import './href-sanitizer.js'; import './json-edit.js'; import './json-prune.js'; From b4a8dd0754f2016943b7267c630014b8d75a911b Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sat, 12 Apr 2025 10:54:00 -0400 Subject: [PATCH 0807/1099] New revision for dev build --- dist/version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dist/version b/dist/version index e3d7806f2a63e..02ae54dc86003 100644 --- a/dist/version +++ b/dist/version @@ -1 +1 @@ -1.63.3.14 \ No newline at end of file +1.63.3.15 \ No newline at end of file From cc987cb07274203b05d9dba48d5a282ce41244b4 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sat, 12 Apr 2025 10:55:14 -0400 Subject: [PATCH 0808/1099] Update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 128b2c4005013..c93ef2ead5b71 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,4 @@ +- [Add `trusted-create-element` scriptlet](https://github.com/gorhill/uBlock/commit/20dd606504) - [Mind potential race condition when dynamically registering scriptlets](https://github.com/gorhill/uBlock/commit/15e832da8a) - [Fix undue unchecking of setting in "My filters"](https://github.com/gorhill/uBlock/commit/2bb6999e3f) - [Add path support as target option in static extended filtering](https://github.com/gorhill/uBlock/commit/8b696a691a) From d1fc38c4731c88f8ef3de09a1c2ae3aab4651e77 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sat, 12 Apr 2025 11:01:06 -0400 Subject: [PATCH 0809/1099] Make Firefox dev build auto-update --- dist/firefox/updates.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dist/firefox/updates.json b/dist/firefox/updates.json index 2ee1819c9c33a..45c38d68f4186 100644 --- a/dist/firefox/updates.json +++ b/dist/firefox/updates.json @@ -3,9 +3,9 @@ "uBlock0@raymondhill.net": { "updates": [ { - "version": "1.63.3.14", + "version": "1.63.3.15", "browser_specific_settings": { "gecko": { "strict_min_version": "92.0" } }, - "update_link": "https://github.com/gorhill/uBlock/releases/download/1.63.3b14/uBlock0_1.63.3b14.firefox.signed.xpi" + "update_link": "https://github.com/gorhill/uBlock/releases/download/1.63.3b15/uBlock0_1.63.3b15.firefox.signed.xpi" } ] } From dfd42ebf5ffa1e70c8ecd6f7cc0e8fb680e0d934 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sat, 12 Apr 2025 15:20:18 -0400 Subject: [PATCH 0810/1099] Improve `noscript` spoofing Related issue: https://github.com/uBlockOrigin/uBlock-issues/issues/2642 --- src/js/scriptlets/noscript-spoof.js | 40 +++++++++++++++++++++-------- 1 file changed, 29 insertions(+), 11 deletions(-) diff --git a/src/js/scriptlets/noscript-spoof.js b/src/js/scriptlets/noscript-spoof.js index a897df3c5885b..1abf8e06916b5 100644 --- a/src/js/scriptlets/noscript-spoof.js +++ b/src/js/scriptlets/noscript-spoof.js @@ -33,6 +33,8 @@ const reMetaContent = /^\s*(\d+)\s*;\s*url=(?:"([^"]+)"|'([^']+)'|(.+))/i; const reSafeURL = /^https?:\/\//; + const reNoscript = /(^|[ >+~])noscript([ >+~]|$)/g; + const className = vAPI.sessionId; let redirectTimer; const autoRefresh = function(root) { @@ -55,30 +57,46 @@ }; const morphNoscript = function(from) { + const fragment = new DocumentFragment(); + const inHead = from.closest('head'); if ( /^application\/(?:xhtml\+)?xml/.test(document.contentType) ) { - const to = document.createElement('span'); while ( from.firstChild !== null ) { - to.appendChild(from.firstChild); + fragment.append(from.firstChild); } - return to; + return fragment; } const parser = new DOMParser(); const doc = parser.parseFromString( - '' + from.textContent + '', + ``, 'text/html' ); - return document.adoptNode(doc.querySelector('span')); + for ( const elem of doc.querySelectorAll(inHead ? 'span > *' : 'span') ) { + fragment.append(document.adoptNode(elem)); + } + return fragment; }; for ( const noscript of noscripts ) { - const parent = noscript.parentNode; - if ( parent === null ) { continue; } - const span = morphNoscript(noscript); - span.style.setProperty('display', 'inline', 'important'); + const fragment = morphNoscript(noscript); if ( redirectTimer === undefined ) { - autoRefresh(span); + autoRefresh(fragment); } - parent.replaceChild(span, noscript); + noscript.replaceWith(fragment); + } + + for ( const sheet of document.styleSheets ) { + try { + for ( const rule of sheet.cssRules ) { + if ( reNoscript.test(rule.selectorText) === false ) { continue; } + rule.selectorText = + rule.selectorText.replace(reNoscript, `$1span.${className}$2`); + } + } catch { + } + } + + for ( const span of document.querySelectorAll(`.${className}`) ) { + span.style.setProperty('display', 'inline', 'important'); } })(); From c15dc9d8ff59f368061450e0b89275a13c26ec54 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sat, 12 Apr 2025 15:51:36 -0400 Subject: [PATCH 0811/1099] Fix scrollbars not following dark theme Related issue: https://github.com/uBlockOrigin/uBlock-issues/issues/3604 --- src/css/themes/default.css | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/css/themes/default.css b/src/css/themes/default.css index c37bd28f02a2b..bc132b0174159 100644 --- a/src/css/themes/default.css +++ b/src/css/themes/default.css @@ -381,9 +381,7 @@ --logger-scriptlet-surface: rgb(var(--yellow-30) / 40%); } -:root.dark input, -:root.dark select, -:root.dark textarea { +:root.dark { color-scheme: dark; } From cac420a22d161ad93657b32df08304cf50fd746b Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sun, 13 Apr 2025 07:34:36 -0400 Subject: [PATCH 0812/1099] Rename `trusted-create-element` to `trusted-create-html` To avoid confusion with AG's own `trusted-create-element`, which has a different syntax and a different purpose. --- .../{create-element.js => create-html.js} | 31 ++++++++++--------- src/js/resources/scriptlets.js | 2 +- 2 files changed, 17 insertions(+), 16 deletions(-) rename src/js/resources/{create-element.js => create-html.js} (80%) diff --git a/src/js/resources/create-element.js b/src/js/resources/create-html.js similarity index 80% rename from src/js/resources/create-element.js rename to src/js/resources/create-html.js index 7b4ad5b02a057..c5732d3359dcc 100644 --- a/src/js/resources/create-element.js +++ b/src/js/resources/create-html.js @@ -26,7 +26,7 @@ import { safeSelf } from './safe-self.js'; /******************************************************************************/ /** - * @scriptlet trusted-create-element + * @scriptlet trusted-create-html * * @description * Element(s) from a parsed HTML string are added as child element(s) to a @@ -46,7 +46,7 @@ import { safeSelf } from './safe-self.js'; * * */ -function trustedCreateElement( +function trustedCreateHTML( parentSelector, htmlStr = '', durationStr = '' @@ -54,16 +54,16 @@ function trustedCreateElement( if ( parentSelector === '' ) { return; } if ( htmlStr === '' ) { return; } const safe = safeSelf(); - const logPrefix = safe.makeLogPrefix('trusted-create-element', parentSelector, htmlStr, durationStr); + const logPrefix = safe.makeLogPrefix('trusted-create-html', parentSelector, htmlStr, durationStr); // We do not want to recursively create elements - self.trustedCreateElement = true; + self.trustedCreateHTML = true; let ancestor = self.frameElement; while ( ancestor !== null ) { const doc = ancestor.ownerDocument; if ( doc === null ) { break; } const win = doc.defaultView; if ( win === null ) { break; } - if ( win.trustedCreateElement ) { return; } + if ( win.trustedCreateHTML ) { return; } ancestor = ancestor.frameElement; } const duration = parseInt(durationStr, 10); @@ -71,24 +71,25 @@ function trustedCreateElement( const externalDoc = domParser.parseFromString(htmlStr, 'text/html'); const docFragment = new DocumentFragment(); const toRemove = []; - for ( const external of externalDoc.querySelectorAll('body > *') ) { - const imported = document.adoptNode(external); - docFragment.append(imported); + while ( externalDoc.body.firstChild !== null ) { + const imported = document.adoptNode(externalDoc.body.firstChild); + docFragment.appendChild(imported); if ( isNaN(duration) ) { continue; } toRemove.push(imported); } - if ( docFragment.childElementCount === 0 ) { return; } + if ( docFragment.firstChild === null ) { return; } const remove = ( ) => { - for ( const elem of toRemove ) { - elem.remove(); + for ( const node of toRemove ) { + if ( node.parentNode === null ) { continue; } + node.parentNode.removeChild(node); } - safe.uboLog(logPrefix, 'Element(s) removed'); + safe.uboLog(logPrefix, 'Node(s) removed'); }; const append = ( ) => { const parent = document.querySelector(parentSelector); if ( parent === null ) { return false; } parent.append(docFragment); - safe.uboLog(logPrefix, 'Element(s) appended'); + safe.uboLog(logPrefix, 'Node(s) appended'); if ( toRemove.length === 0 ) { return true; } setTimeout(remove, duration); return true; @@ -100,8 +101,8 @@ function trustedCreateElement( }); observer.observe(document, { childList: true, subtree: true }); } -registerScriptlet(trustedCreateElement, { - name: 'trusted-create-element.js', +registerScriptlet(trustedCreateHTML, { + name: 'trusted-create-html.js', requiresTrust: true, dependencies: [ safeSelf, diff --git a/src/js/resources/scriptlets.js b/src/js/resources/scriptlets.js index e1ec8be16c246..071b6052bd912 100755 --- a/src/js/resources/scriptlets.js +++ b/src/js/resources/scriptlets.js @@ -21,7 +21,7 @@ */ import './attribute.js'; -import './create-element.js'; +import './create-html.js'; import './href-sanitizer.js'; import './json-edit.js'; import './json-prune.js'; From 5ed1b0703434315cfdec779573cb50d89971750c Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sun, 13 Apr 2025 07:38:19 -0400 Subject: [PATCH 0813/1099] Update changelog --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c93ef2ead5b71..4d62065d0fc9c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,4 @@ -- [Add `trusted-create-element` scriptlet](https://github.com/gorhill/uBlock/commit/20dd606504) +- [Add `trusted-create-html` scriptlet](https://github.com/gorhill/uBlock/commit/20dd606504) - [Mind potential race condition when dynamically registering scriptlets](https://github.com/gorhill/uBlock/commit/15e832da8a) - [Fix undue unchecking of setting in "My filters"](https://github.com/gorhill/uBlock/commit/2bb6999e3f) - [Add path support as target option in static extended filtering](https://github.com/gorhill/uBlock/commit/8b696a691a) From a33e2e411502165aa0f4cdf3a4d538e619971383 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sun, 13 Apr 2025 07:39:03 -0400 Subject: [PATCH 0814/1099] New revision for dev build --- dist/version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dist/version b/dist/version index 02ae54dc86003..555e0a1557a99 100644 --- a/dist/version +++ b/dist/version @@ -1 +1 @@ -1.63.3.15 \ No newline at end of file +1.63.3.16 \ No newline at end of file From 12c8bc6c5b935706d552018c3856c7b4adab6fb8 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sun, 13 Apr 2025 07:45:55 -0400 Subject: [PATCH 0815/1099] Make Firefox dev build auto-update --- dist/firefox/updates.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dist/firefox/updates.json b/dist/firefox/updates.json index 45c38d68f4186..ba81adadd3ee9 100644 --- a/dist/firefox/updates.json +++ b/dist/firefox/updates.json @@ -3,9 +3,9 @@ "uBlock0@raymondhill.net": { "updates": [ { - "version": "1.63.3.15", + "version": "1.63.3.16", "browser_specific_settings": { "gecko": { "strict_min_version": "92.0" } }, - "update_link": "https://github.com/gorhill/uBlock/releases/download/1.63.3b15/uBlock0_1.63.3b15.firefox.signed.xpi" + "update_link": "https://github.com/gorhill/uBlock/releases/download/1.63.3b16/uBlock0_1.63.3b16.firefox.signed.xpi" } ] } From f51a4c79db09913b8705391b7da481c31959ab2f Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sun, 13 Apr 2025 09:02:12 -0400 Subject: [PATCH 0816/1099] [mv3] Determine "genericity" on a per-cosmetic filter basis Related issue: https://github.com/uBlockOrigin/uBOL-home/issues/328 --- src/js/static-dnr-filtering.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/js/static-dnr-filtering.js b/src/js/static-dnr-filtering.js index b097e51d3b64b..df23d863c08a8 100644 --- a/src/js/static-dnr-filtering.js +++ b/src/js/static-dnr-filtering.js @@ -222,6 +222,7 @@ function addExtendedToDNR(context, parser) { return; } let details = context.specificCosmeticFilters.get(compiled); + let isGeneric = true; for ( const { hn, not, bad } of parser.getExtFilterDomainIterator() ) { if ( bad ) { continue; } if ( not && exception ) { continue; } @@ -247,14 +248,14 @@ function addExtendedToDNR(context, parser) { details.matches = [ '*' ]; continue; } + isGeneric = false; details.matches.push(hn); } if ( details === undefined ) { return; } if ( exception ) { return; } if ( compiled.startsWith('{') ) { return; } - if ( details.matches === undefined || details.matches.includes('*') ) { + if ( isGeneric ) { addGenericCosmeticFilter(context, compiled, false); - details.matches = undefined; } } From 50f87c21eadfd12116a14af0d0020b42461b195c Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sun, 13 Apr 2025 09:04:13 -0400 Subject: [PATCH 0817/1099] [mv3] Provide console feedback that work is ongoing when building --- platform/mv3/make-rulesets.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/platform/mv3/make-rulesets.js b/platform/mv3/make-rulesets.js index 37739c58224be..93b1f0ede7852 100644 --- a/platform/mv3/make-rulesets.js +++ b/platform/mv3/make-rulesets.js @@ -114,6 +114,9 @@ const urlToFileName = url => { const fetchText = (url, cacheDir) => { return new Promise((resolve, reject) => { + process.stdout.clearLine(); + process.stdout.cursorTo(0); + process.stdout.write(`Fetching ${url}`); const fname = urlToFileName(url); fs.readFile(`${cacheDir}/${fname}`, { encoding: 'utf8' }).then(content => { log(`\tFetched local ${url}`); @@ -1570,6 +1573,9 @@ async function main() { homeURL: 'https://github.com/sander85/uBOL-et', }); + process.stdout.clearLine(); + process.stdout.cursorTo(0); + writeFile( `${rulesetDir}/ruleset-details.json`, `${JSON.stringify(rulesetDetails, null, 1)}\n` From 9bf05023c179072e45760674e7c5a48d723e07ff Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sun, 13 Apr 2025 09:16:54 -0400 Subject: [PATCH 0818/1099] Import translation work from https://crowdin.com/project/ublock --- platform/mv3/description/webstore.gl.txt | 2 +- platform/mv3/description/webstore.ka.txt | 6 +++--- platform/mv3/description/webstore.lv.txt | 2 +- platform/mv3/description/webstore.sr.txt | 2 +- src/_locales/lv/messages.json | 8 ++++---- 5 files changed, 10 insertions(+), 10 deletions(-) diff --git a/platform/mv3/description/webstore.gl.txt b/platform/mv3/description/webstore.gl.txt index ff487008df3b5..5acb8b794f005 100644 --- a/platform/mv3/description/webstore.gl.txt +++ b/platform/mv3/description/webstore.gl.txt @@ -1,4 +1,4 @@ -uBO Lite (uBOL) is an MV3-based content blocker. +uBO Lite (uBOL) é un bloqueador de contido baseado en MV3. O conxunto de regras predeterminado corresponde ao conxunto de filtros predeterminado de uBlock Origin: diff --git a/platform/mv3/description/webstore.ka.txt b/platform/mv3/description/webstore.ka.txt index 2515a4cf5ce68..0cb4db880e111 100644 --- a/platform/mv3/description/webstore.ka.txt +++ b/platform/mv3/description/webstore.ka.txt @@ -1,12 +1,12 @@ -uBO Lite (uBOL) is an MV3-based content blocker. +uBO Lite (uBOL) შიგთავსის შემზღუდავია MV3-ის მიხედვით. წესების ნაგულისხმევი კრებული იგივეა, რასაც uBlock Origin იყენებს: -- uBlock Origin – ფილტრების ჩაშენებული სიები +- ჩაშენებული ფილტრებით, uBlock Origin რომ იყენებს - EasyList - EasyPrivacy - Peter Lowe – სარეკლამო სერვერების სია შეგიძლიათ სხვა კრებულებიც აამოქმედოთ პარამეტრების გვერდიდან -- დაწკაპეთ _Cogs_ ხატულაზე ამომხტომ არეში. -uBOL სრულად დეკლარაციულია, ანუ არაა საჭირო მუდმივად იყოს გაშვებული uBOL-პროცესი გასაფილტრად, CSS/JS ჩანაცვლებით შიგთავსის გაფილტვრას თავად ბრაუზერი უზრუნველყოფს ნაცვლად გაფართოებისა, რაც მეტად საიმედოა. შესაბამისად, uBOL თავად არ დატვირთავს პროცესორს/ოპერატიულს შიგთავსის შეზღუდვის დროს -- uBOL-ის შუამავალი მომსახურე პროცესი საჭიროა _მხოლოდ_ მაშინ, როცა ამომხტომ არესთან ურთიერთქმედებთ ან ცვლით პარამეტრებს. +uBOL მოქმედებს სრულად დადგენილი წესებით, შესაბამისად, არ ესაჭიროება მუდმივად გაშვებული uBOL-პროცესი გასაფილტრად, ხოლო როცა შიგთავსის შეზღუდვა ითხოვს ჩანაცვლდეს CSS/JS, ამას თავად ბრაუზერი უზრუნველყოფს საიმედო გზით, ნაცვლად გაფართოებისა. აქედან გამომდინარე, uBOL თავად არ მოიხმარს პროცესორს/მეხსიერებას შიგთავსის შეზღუდვისას -- uBOL საჭიროებს შუამავალ მომსახურე პროცესს _მხოლოდ_ მაშინ, როცა ამომხტომ არესთან ურთიერთქმედებთ ან ცვლით პარამეტრებს. diff --git a/platform/mv3/description/webstore.lv.txt b/platform/mv3/description/webstore.lv.txt index eb144d7f4ecaa..aa4932f7be810 100644 --- a/platform/mv3/description/webstore.lv.txt +++ b/platform/mv3/description/webstore.lv.txt @@ -1,4 +1,4 @@ -uBO Lite (uBOL) is an MV3-based content blocker. +uBO Lite (uBOL) ir uz MV3 balstīts satura aizturētājs. Noklusējuma nosacījumu kopa atbilst uBokc Origin noklusējuma aizturēšanas kopai: diff --git a/platform/mv3/description/webstore.sr.txt b/platform/mv3/description/webstore.sr.txt index 068ddeb29a3ef..92b0eb100a8d8 100644 --- a/platform/mv3/description/webstore.sr.txt +++ b/platform/mv3/description/webstore.sr.txt @@ -1,4 +1,4 @@ -uBO Lite (uBOL) is an MV3-based content blocker. +uBO Lite (uBOL) је блокатор садржаја заснован на MV3. Подразумевани скуп правила одговара подразумеваном скупу филтера uBlock Origin-а: diff --git a/src/_locales/lv/messages.json b/src/_locales/lv/messages.json index 5135003bf11a5..4159b070549da 100644 --- a/src/_locales/lv/messages.json +++ b/src/_locales/lv/messages.json @@ -20,7 +20,7 @@ "description": "Label for button to prevent navigating away from unsaved changes" }, "dashboardUnsavedWarningIgnore": { - "message": "Ignorēt", + "message": "Neņemt vērā", "description": "Label for button to ignore unsaved changes" }, "settingsPageName": { @@ -448,11 +448,11 @@ "description": "Describes the purpose of the 'Parse and enforce cosmetic filters' feature." }, "3pIgnoreGenericCosmeticFilters": { - "message": "Ignorēt vispārīgos kosmētiskos filtrus", + "message": "Neņemt vērā vispārīgos kosmētiskos aizturētājus", "description": "This will cause uBO to ignore all generic cosmetic filters." }, "3pIgnoreGenericCosmeticFiltersInfo": { - "message": "

Vispārīgie kosmētikas filtri ir kosmētikas filtri, kurus paredzēts piemērot visās tīmekļvietnēs.

Lai gan uBlock ₀ darbojas efektīvi, vispārīgie kosmētikas filtri dažās tīmekļa lapās var ievērojami noslogot atmiņu un centrālo procesoru.

Šīs opcijas izmantošana samazinās vispārīgo kosmētikas filtru izmantotās atmiņas un procesora noslodzi kā arī samazinās paša uBlock₀ izmantoto atmiņu.

Ieteicams izmantot zemas veiktspējas ierīcēm.", + "message": "Vispārīgie kosmētiskie aizturētāji ir tādi kosmētiskie aizturētāji, kurus paredzēts pielietot visās tīmekļvietnēs. Šīs iespējas iespējošana novērsīs tīmekļa lapām pievienoto atmiņas un CPU virstēriņu kā vispārīgo kosmētisko aizturētāju apstrādāšanas iznākumu.\n\nIr ieteicams iespējot šo iespēju mazāk jaudīgās ierīcēs.", "description": "Describes the purpose of the 'Ignore generic cosmetic filters' feature." }, "3pSuspendUntilListsAreLoaded": { @@ -512,7 +512,7 @@ "description": "The label for the checkbox used to import external filter lists" }, "3pExternalListsHint": { - "message": "Vienu URL katrā rindiņā. Rindiņas, kuras sākas ar ‘!’ tiks ignorētas. Nederīgs URL tiks ignorēts bez brīdinājuma.", + "message": "Viens URL katrā rinda. Nederīgi URL netiks ņemti vērā.", "description": "Short information about how to use the textarea to import external filter lists by URL" }, "3pExternalListObsolete": { From dc3602985f3de85b4ccce9fcaa830821a920013e Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sun, 13 Apr 2025 09:19:25 -0400 Subject: [PATCH 0819/1099] [mv3] Update summary description --- platform/mv3/extension/_locales/en/messages.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform/mv3/extension/_locales/en/messages.json b/platform/mv3/extension/_locales/en/messages.json index 5d947df51830a..f4dc76f3cc4cb 100644 --- a/platform/mv3/extension/_locales/en/messages.json +++ b/platform/mv3/extension/_locales/en/messages.json @@ -4,7 +4,7 @@ "description": "extension name." }, "extShortDesc": { - "message": "A permission-less content blocker. Blocks ads, trackers, miners, and more immediately upon installation.", + "message": "An efficient content blocker. Blocks ads, trackers, miners, and more immediately upon installation.", "description": "this will be in the Chrome web store: must be 132 characters or less" }, "perRulesetStats": { From 7c5c93f0737e645953318521b0a524ba32679995 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sun, 13 Apr 2025 09:34:22 -0400 Subject: [PATCH 0820/1099] Import translation work from https://crowdin.com/project/ublock --- platform/mv3/extension/_locales/cv/messages.json | 2 +- platform/mv3/extension/_locales/eo/messages.json | 2 +- platform/mv3/extension/_locales/gu/messages.json | 2 +- platform/mv3/extension/_locales/it/messages.json | 2 +- platform/mv3/extension/_locales/kk/messages.json | 2 +- platform/mv3/extension/_locales/lt/messages.json | 2 +- platform/mv3/extension/_locales/mr/messages.json | 2 +- platform/mv3/extension/_locales/oc/messages.json | 2 +- platform/mv3/extension/_locales/pt_BR/messages.json | 2 +- platform/mv3/extension/_locales/sl/messages.json | 2 +- platform/mv3/extension/_locales/so/messages.json | 2 +- platform/mv3/extension/_locales/sw/messages.json | 2 +- platform/mv3/extension/_locales/ta/messages.json | 2 +- platform/mv3/extension/_locales/te/messages.json | 2 +- platform/mv3/extension/_locales/tr/messages.json | 2 +- 15 files changed, 15 insertions(+), 15 deletions(-) diff --git a/platform/mv3/extension/_locales/cv/messages.json b/platform/mv3/extension/_locales/cv/messages.json index a609d9d3b0ace..ace2c1d9f1881 100644 --- a/platform/mv3/extension/_locales/cv/messages.json +++ b/platform/mv3/extension/_locales/cv/messages.json @@ -4,7 +4,7 @@ "description": "extension name." }, "extShortDesc": { - "message": "A permission-less content blocker. Blocks ads, trackers, miners, and more immediately upon installation.", + "message": "An efficient content blocker. Blocks ads, trackers, miners, and more immediately upon installation.", "description": "this will be in the Chrome web store: must be 132 characters or less" }, "perRulesetStats": { diff --git a/platform/mv3/extension/_locales/eo/messages.json b/platform/mv3/extension/_locales/eo/messages.json index b1a7bf6ac8249..726dcd0bd70ad 100644 --- a/platform/mv3/extension/_locales/eo/messages.json +++ b/platform/mv3/extension/_locales/eo/messages.json @@ -4,7 +4,7 @@ "description": "extension name." }, "extShortDesc": { - "message": "A permission-less content blocker. Blocks ads, trackers, miners, and more immediately upon installation.", + "message": "An efficient content blocker. Blocks ads, trackers, miners, and more immediately upon installation.", "description": "this will be in the Chrome web store: must be 132 characters or less" }, "perRulesetStats": { diff --git a/platform/mv3/extension/_locales/gu/messages.json b/platform/mv3/extension/_locales/gu/messages.json index a609d9d3b0ace..ace2c1d9f1881 100644 --- a/platform/mv3/extension/_locales/gu/messages.json +++ b/platform/mv3/extension/_locales/gu/messages.json @@ -4,7 +4,7 @@ "description": "extension name." }, "extShortDesc": { - "message": "A permission-less content blocker. Blocks ads, trackers, miners, and more immediately upon installation.", + "message": "An efficient content blocker. Blocks ads, trackers, miners, and more immediately upon installation.", "description": "this will be in the Chrome web store: must be 132 characters or less" }, "perRulesetStats": { diff --git a/platform/mv3/extension/_locales/it/messages.json b/platform/mv3/extension/_locales/it/messages.json index 8ba2adfc3b23e..fba89f473f28d 100644 --- a/platform/mv3/extension/_locales/it/messages.json +++ b/platform/mv3/extension/_locales/it/messages.json @@ -4,7 +4,7 @@ "description": "extension name." }, "extShortDesc": { - "message": "Un blocco sperimentale che non richiede permessi. Blocca pubblicità, tracker, cryptominer e altro ancora.", + "message": "An efficient content blocker. Blocks ads, trackers, miners, and more immediately upon installation.", "description": "this will be in the Chrome web store: must be 132 characters or less" }, "perRulesetStats": { diff --git a/platform/mv3/extension/_locales/kk/messages.json b/platform/mv3/extension/_locales/kk/messages.json index a609d9d3b0ace..ace2c1d9f1881 100644 --- a/platform/mv3/extension/_locales/kk/messages.json +++ b/platform/mv3/extension/_locales/kk/messages.json @@ -4,7 +4,7 @@ "description": "extension name." }, "extShortDesc": { - "message": "A permission-less content blocker. Blocks ads, trackers, miners, and more immediately upon installation.", + "message": "An efficient content blocker. Blocks ads, trackers, miners, and more immediately upon installation.", "description": "this will be in the Chrome web store: must be 132 characters or less" }, "perRulesetStats": { diff --git a/platform/mv3/extension/_locales/lt/messages.json b/platform/mv3/extension/_locales/lt/messages.json index 95b9e4d123c76..810d9a3a687f8 100644 --- a/platform/mv3/extension/_locales/lt/messages.json +++ b/platform/mv3/extension/_locales/lt/messages.json @@ -4,7 +4,7 @@ "description": "extension name." }, "extShortDesc": { - "message": "A permission-less content blocker. Blocks ads, trackers, miners, and more immediately upon installation.", + "message": "An efficient content blocker. Blocks ads, trackers, miners, and more immediately upon installation.", "description": "this will be in the Chrome web store: must be 132 characters or less" }, "perRulesetStats": { diff --git a/platform/mv3/extension/_locales/mr/messages.json b/platform/mv3/extension/_locales/mr/messages.json index a609d9d3b0ace..ace2c1d9f1881 100644 --- a/platform/mv3/extension/_locales/mr/messages.json +++ b/platform/mv3/extension/_locales/mr/messages.json @@ -4,7 +4,7 @@ "description": "extension name." }, "extShortDesc": { - "message": "A permission-less content blocker. Blocks ads, trackers, miners, and more immediately upon installation.", + "message": "An efficient content blocker. Blocks ads, trackers, miners, and more immediately upon installation.", "description": "this will be in the Chrome web store: must be 132 characters or less" }, "perRulesetStats": { diff --git a/platform/mv3/extension/_locales/oc/messages.json b/platform/mv3/extension/_locales/oc/messages.json index a609d9d3b0ace..ace2c1d9f1881 100644 --- a/platform/mv3/extension/_locales/oc/messages.json +++ b/platform/mv3/extension/_locales/oc/messages.json @@ -4,7 +4,7 @@ "description": "extension name." }, "extShortDesc": { - "message": "A permission-less content blocker. Blocks ads, trackers, miners, and more immediately upon installation.", + "message": "An efficient content blocker. Blocks ads, trackers, miners, and more immediately upon installation.", "description": "this will be in the Chrome web store: must be 132 characters or less" }, "perRulesetStats": { diff --git a/platform/mv3/extension/_locales/pt_BR/messages.json b/platform/mv3/extension/_locales/pt_BR/messages.json index a1288438909db..56f2e6b65942d 100644 --- a/platform/mv3/extension/_locales/pt_BR/messages.json +++ b/platform/mv3/extension/_locales/pt_BR/messages.json @@ -4,7 +4,7 @@ "description": "extension name." }, "extShortDesc": { - "message": "Um bloqueador de conteúdo com menos permissões — Bloqueie anúncios, rastreadores, mineradores e mais imediatamente após a instalação", + "message": "Um bloqueador de conteúdo sem permissões. Bloqueia anúncios, rastreadores e muito mais, imediatamente após a instalação.", "description": "this will be in the Chrome web store: must be 132 characters or less" }, "perRulesetStats": { diff --git a/platform/mv3/extension/_locales/sl/messages.json b/platform/mv3/extension/_locales/sl/messages.json index a609d9d3b0ace..ace2c1d9f1881 100644 --- a/platform/mv3/extension/_locales/sl/messages.json +++ b/platform/mv3/extension/_locales/sl/messages.json @@ -4,7 +4,7 @@ "description": "extension name." }, "extShortDesc": { - "message": "A permission-less content blocker. Blocks ads, trackers, miners, and more immediately upon installation.", + "message": "An efficient content blocker. Blocks ads, trackers, miners, and more immediately upon installation.", "description": "this will be in the Chrome web store: must be 132 characters or less" }, "perRulesetStats": { diff --git a/platform/mv3/extension/_locales/so/messages.json b/platform/mv3/extension/_locales/so/messages.json index a609d9d3b0ace..ace2c1d9f1881 100644 --- a/platform/mv3/extension/_locales/so/messages.json +++ b/platform/mv3/extension/_locales/so/messages.json @@ -4,7 +4,7 @@ "description": "extension name." }, "extShortDesc": { - "message": "A permission-less content blocker. Blocks ads, trackers, miners, and more immediately upon installation.", + "message": "An efficient content blocker. Blocks ads, trackers, miners, and more immediately upon installation.", "description": "this will be in the Chrome web store: must be 132 characters or less" }, "perRulesetStats": { diff --git a/platform/mv3/extension/_locales/sw/messages.json b/platform/mv3/extension/_locales/sw/messages.json index a609d9d3b0ace..ace2c1d9f1881 100644 --- a/platform/mv3/extension/_locales/sw/messages.json +++ b/platform/mv3/extension/_locales/sw/messages.json @@ -4,7 +4,7 @@ "description": "extension name." }, "extShortDesc": { - "message": "A permission-less content blocker. Blocks ads, trackers, miners, and more immediately upon installation.", + "message": "An efficient content blocker. Blocks ads, trackers, miners, and more immediately upon installation.", "description": "this will be in the Chrome web store: must be 132 characters or less" }, "perRulesetStats": { diff --git a/platform/mv3/extension/_locales/ta/messages.json b/platform/mv3/extension/_locales/ta/messages.json index a609d9d3b0ace..ace2c1d9f1881 100644 --- a/platform/mv3/extension/_locales/ta/messages.json +++ b/platform/mv3/extension/_locales/ta/messages.json @@ -4,7 +4,7 @@ "description": "extension name." }, "extShortDesc": { - "message": "A permission-less content blocker. Blocks ads, trackers, miners, and more immediately upon installation.", + "message": "An efficient content blocker. Blocks ads, trackers, miners, and more immediately upon installation.", "description": "this will be in the Chrome web store: must be 132 characters or less" }, "perRulesetStats": { diff --git a/platform/mv3/extension/_locales/te/messages.json b/platform/mv3/extension/_locales/te/messages.json index 63bdb1f3f2de5..7f3f7d1150556 100644 --- a/platform/mv3/extension/_locales/te/messages.json +++ b/platform/mv3/extension/_locales/te/messages.json @@ -4,7 +4,7 @@ "description": "extension name." }, "extShortDesc": { - "message": "A permission-less content blocker. Blocks ads, trackers, miners, and more immediately upon installation.", + "message": "An efficient content blocker. Blocks ads, trackers, miners, and more immediately upon installation.", "description": "this will be in the Chrome web store: must be 132 characters or less" }, "perRulesetStats": { diff --git a/platform/mv3/extension/_locales/tr/messages.json b/platform/mv3/extension/_locales/tr/messages.json index d62c42abdc75e..077a95202692b 100644 --- a/platform/mv3/extension/_locales/tr/messages.json +++ b/platform/mv3/extension/_locales/tr/messages.json @@ -4,7 +4,7 @@ "description": "extension name." }, "extShortDesc": { - "message": "İzin gerektirmeyen içerik engelleyicisi. Kurulumdan hemen sonra reklamları, izleyicileri, ve daha fazlasını engeller.", + "message": "A permission-less content blocker. Blocks ads, trackers, miners, and more immediately upon installation.", "description": "this will be in the Chrome web store: must be 132 characters or less" }, "perRulesetStats": { From 51db128dc268abeecaf19255c22f1261ed386f69 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sun, 13 Apr 2025 10:13:18 -0400 Subject: [PATCH 0821/1099] [mv3] "Enable strict blocking" setting depends on broad host permissions --- platform/mv3/extension/js/mode-manager.js | 3 +++ platform/mv3/extension/js/settings.js | 9 ++++++++- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/platform/mv3/extension/js/mode-manager.js b/platform/mv3/extension/js/mode-manager.js index 9014ffd8f21d3..01f9739628abb 100644 --- a/platform/mv3/extension/js/mode-manager.js +++ b/platform/mv3/extension/js/mode-manager.js @@ -21,6 +21,7 @@ import { broadcastMessage, + hasBroadHostPermissions, hostnamesFromMatches, isDescendantHostnameOfIter, toBroaderHostname, @@ -255,12 +256,14 @@ async function writeFilteringModeDetails(afterDetails) { return Promise.all([ getDefaultFilteringMode(), getTrustedSites(), + hasBroadHostPermissions(), localWrite('filteringModeDetails', data), sessionWrite('filteringModeDetails', data), ]).then(results => { broadcastMessage({ defaultFilteringMode: results[0], trustedSites: Array.from(results[1]), + hasOmnipotence: results[2], }); }); } diff --git a/platform/mv3/extension/js/settings.js b/platform/mv3/extension/js/settings.js index 6e76108752bc7..4c9352f6eca74 100644 --- a/platform/mv3/extension/js/settings.js +++ b/platform/mv3/extension/js/settings.js @@ -69,7 +69,7 @@ function renderWidgets() { { const input = qs$('#strictBlockMode input[type="checkbox"]'); - const canStrictBlock = cachedRulesetData.defaultFilteringMode > 1; + const canStrictBlock = cachedRulesetData.hasOmnipotence; input.checked = canStrictBlock && cachedRulesetData.strictBlockMode; dom.attr(input, 'disabled', canStrictBlock ? null : ''); } @@ -235,6 +235,13 @@ listen.onmessage = ev => { } } + if ( message.hasOmnipotence !== undefined ) { + if ( message.hasOmnipotence !== local.hasOmnipotence ) { + local.hasOmnipotence = message.hasOmnipotence; + render = true; + } + } + if ( message.defaultFilteringMode !== undefined ) { if ( message.defaultFilteringMode !== local.defaultFilteringMode ) { local.defaultFilteringMode = message.defaultFilteringMode; From f15adcf2d5e4ab8584bc1bbdfe9010a96de9fa59 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sun, 13 Apr 2025 10:24:23 -0400 Subject: [PATCH 0822/1099] [mv3] Ensure strict-blocking is enabled when gaining broad permissions --- platform/mv3/extension/js/background.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/platform/mv3/extension/js/background.js b/platform/mv3/extension/js/background.js index 65c6da2bba3aa..6499c9d5ad3d4 100644 --- a/platform/mv3/extension/js/background.js +++ b/platform/mv3/extension/js/background.js @@ -107,7 +107,10 @@ async function onPermissionsAdded(permissions) { if ( details === undefined ) { const modified = await syncWithBrowserPermissions(); if ( modified === false ) { return; } - return registerInjectables(); + return Promise.all([ + updateSessionRules(), + registerInjectables(), + ]); } const defaultMode = await getDefaultFilteringMode(); if ( defaultMode >= MODE_OPTIMAL ) { return; } From 20f52daf9bf7be99dda8a0bdab46d0f79a4eafd4 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sun, 13 Apr 2025 15:23:08 -0400 Subject: [PATCH 0823/1099] Import translation work from https://crowdin.com/project/ublock --- platform/mv3/description/webstore.be.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform/mv3/description/webstore.be.txt b/platform/mv3/description/webstore.be.txt index 4ba7fcbb1e517..47612d4f103b2 100644 --- a/platform/mv3/description/webstore.be.txt +++ b/platform/mv3/description/webstore.be.txt @@ -1,4 +1,4 @@ -uBO Lite (uBOL) is an MV3-based content blocker. +uBO Lite (uBOL) гэта базаваны на MV3 блакавальнік змесціва. Прадвызначаны набор правіл адпавядае тыпавому набору фільтраў uBlock Origin: From e11335f5adaa52cdabdc7fb239edb4bac9edcb65 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sun, 13 Apr 2025 15:37:07 -0400 Subject: [PATCH 0824/1099] [mv3] Fix Github Actions --- platform/mv3/make-rulesets.js | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/platform/mv3/make-rulesets.js b/platform/mv3/make-rulesets.js index 93b1f0ede7852..f36976e0d15ca 100644 --- a/platform/mv3/make-rulesets.js +++ b/platform/mv3/make-rulesets.js @@ -104,6 +104,12 @@ const log = (text, silent = true) => { console.log = log; +const logProgress = text => { + process?.stdout?.clearLine(); + process?.stdout?.cursorTo(0); + process?.stdout?.write(text); +}; + /******************************************************************************/ const urlToFileName = url => { @@ -114,9 +120,7 @@ const urlToFileName = url => { const fetchText = (url, cacheDir) => { return new Promise((resolve, reject) => { - process.stdout.clearLine(); - process.stdout.cursorTo(0); - process.stdout.write(`Fetching ${url}`); + logProgress(`Fetching ${url}`); const fname = urlToFileName(url); fs.readFile(`${cacheDir}/${fname}`, { encoding: 'utf8' }).then(content => { log(`\tFetched local ${url}`); @@ -1573,8 +1577,7 @@ async function main() { homeURL: 'https://github.com/sander85/uBOL-et', }); - process.stdout.clearLine(); - process.stdout.cursorTo(0); + logProgress(''); writeFile( `${rulesetDir}/ruleset-details.json`, From b5eea3ce3a0f47dc8e1c789d602021bd9c3b0cce Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sun, 13 Apr 2025 15:45:39 -0400 Subject: [PATCH 0825/1099] [mv3] Fix Github Actions --- platform/mv3/make-rulesets.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/platform/mv3/make-rulesets.js b/platform/mv3/make-rulesets.js index f36976e0d15ca..17302460e5056 100644 --- a/platform/mv3/make-rulesets.js +++ b/platform/mv3/make-rulesets.js @@ -105,9 +105,9 @@ const log = (text, silent = true) => { console.log = log; const logProgress = text => { - process?.stdout?.clearLine(); - process?.stdout?.cursorTo(0); - process?.stdout?.write(text); + process?.stdout?.clearLine?.(); + process?.stdout?.cursorTo?.(0); + process?.stdout?.write?.(text); }; /******************************************************************************/ From 87007e62c00bc77d6a77ace8d02b6cf209d7ee23 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Mon, 14 Apr 2025 09:07:46 -0400 Subject: [PATCH 0826/1099] [mv3] Ensure POL-3 list is separate from POL-0 list in uBOL --- assets/assets.dev.json | 4 ++-- assets/assets.json | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/assets/assets.dev.json b/assets/assets.dev.json index 01a59eb532561..e215525fc7cc3 100644 --- a/assets/assets.dev.json +++ b/assets/assets.dev.json @@ -784,7 +784,7 @@ "off": true, "title": "🇵🇱pl: Oficjalne Polskie Filtry do uBlocka Origin", "tags": "ads polish polski", - "lang": "szl pl", + "lang": "szl pl _", "contentURL": "https://raw.githubusercontent.com/MajkiIT/polish-ads-filter/master/polish-adblock-filters/adblock.txt", "supportURL": "https://github.com/MajkiIT/polish-ads-filter" }, @@ -817,7 +817,7 @@ "parent": "🇷🇺ru 🇺🇦ua 🇺🇿uz 🇰🇿kz: RU AdList", "off": true, "title": "🇷🇺ru 🇺🇦ua 🇺🇿uz 🇰🇿kz: RU AdList", - "tags": "ads belarusian беларуская kazakh tatar russian русский ukrainian українська uzbek", + "tags": "ads belarusian беларуская kazakh tatar russian русский ukrainian українська uzbek uk", "lang": "be kk tt ru uz", "contentURL": "https://raw.githubusercontent.com/easylist/ruadlist/master/RuAdList-uBO.txt", "cdnURLs": [ diff --git a/assets/assets.json b/assets/assets.json index 30c5d3146fe51..d615c2722de18 100644 --- a/assets/assets.json +++ b/assets/assets.json @@ -784,7 +784,7 @@ "off": true, "title": "🇵🇱pl: Oficjalne Polskie Filtry do uBlocka Origin", "tags": "ads polish polski", - "lang": "szl pl", + "lang": "szl pl _", "contentURL": "https://raw.githubusercontent.com/MajkiIT/polish-ads-filter/master/polish-adblock-filters/adblock.txt", "supportURL": "https://github.com/MajkiIT/polish-ads-filter" }, @@ -817,7 +817,7 @@ "parent": "🇷🇺ru 🇺🇦ua 🇺🇿uz 🇰🇿kz: RU AdList", "off": true, "title": "🇷🇺ru 🇺🇦ua 🇺🇿uz 🇰🇿kz: RU AdList", - "tags": "ads belarusian беларуская kazakh tatar russian русский ukrainian українська uzbek", + "tags": "ads belarusian беларуская kazakh tatar russian русский ukrainian українська uzbek uk", "lang": "be kk tt ru uz", "contentURL": "https://raw.githubusercontent.com/easylist/ruadlist/master/RuAdList-uBO.txt", "cdnURLs": [ From eaedaf5b10d2f7857c6b77fbf7d4a80681d4d46c Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Tue, 15 Apr 2025 12:47:02 -0400 Subject: [PATCH 0827/1099] Fix regexes with potential catastrophic backtracking MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The quoted email below was sent to ubo-security at raymondhill dot net: ===== Dear Raymond, I am writing to report a potential Regular Expression Denial of Service (ReDoS) vulnerability in the 1p-filters.js script of uBlock Origin. The vulnerability occurs due to the use of the regular expression /\s+$/, which is used to remove trailing whitespace. This issue can lead to a denial of service when processing strings with a large number of trailing spaces, potentially causing a browser to freeze. Affected file(s) js/1p-filters.js Vulnerable pattern(s) Lines 131 and 167: /\s+$/ Description of the issue The regular expression /\s+$/ is applied to remove trailing whitespace in user‑ provided content. However, when the content has a large number of spaces (e.g., ~100,000), this pattern causes excessive backtracking in the regular expression engine, resulting in performance degradation and UI freezing. This is a classic ReDoS attack vector. Steps to reproduce 1. Open the uBlock Origin dashboard and navigate to the My filters tab. 2. Run the following code in the browser's DevTools Console or as a bookmarklet. 3. Observe the UI freezing for several seconds or even longer, depending on the number of spaces used. PoC (Proof of Concept) /** * poc.js — triggers ReDoS in 1p-filters.js * Expected: <1 ms; Actual: several seconds – UI freeze */ (() => { const payload = " ".repeat(100000) + "!"; // 100,000 spaces + sentinel const run = () => { if (!window.cmEditor) { console.error("cmEditor not ready"); return; } // Inject payload into the editor cmEditor.setValue(payload); console.time("ReDoS"); // Call the vulnerable function (mirroring getEditorText) cmEditor.getValue().replace(/\s+$/, ''); // Alternatively, simulate a realistic user flow: // document.querySelector('#userFiltersApply').click(); console.timeEnd("ReDoS"); }; if (document.readyState === "complete") { run(); } else { window.addEventListener("load", run, { once: true }); } })(); Impact This issue can significantly degrade the user experience, causing the page to become unresponsive. If an attacker can inject this malicious string into the page (for example, through XSS or other attacks), it could lead to a denial of service (DoS). This vulnerability can be triggered repeatedly, causing the browser to hang indefinitely. Suggested fix The issue can be mitigated by replacing /\s+$/ with a more efficient solution, such as a look‑behind assertion /(?<=\S)\s+$/ (available in modern browsers) which ensures no backtracking occurs, or using trimEnd() for legacy support: // Example of using look-behind: cmEditor.setValue(text.replace(/(?<=\S)\s+$/, '') + '\n\n'); // Alternatively, using trimEnd(): cmEditor.setValue(text.trimEnd() + '\n\n'); Additional information If required, I am happy to assist in testing or provide more information. Please feel free to contact me for further clarification. Best regards, [redacted] ===== --- src/js/1p-filters.js | 4 ++-- src/js/arglist-parser.js | 4 ++-- src/js/static-filtering-parser.js | 4 ++-- src/js/whitelist.js | 6 +++--- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/js/1p-filters.js b/src/js/1p-filters.js index c4f7590dff327..7e20a30c29fd3 100644 --- a/src/js/1p-filters.js +++ b/src/js/1p-filters.js @@ -111,12 +111,12 @@ function currentStateChanged() { } function getEditorText() { - const text = cmEditor.getValue().replace(/\s+$/, ''); + const text = cmEditor.getValue().trimEnd(); return text === '' ? text : `${text}\n`; } function setEditorText(text) { - cmEditor.setValue(text.replace(/\s+$/, '') + '\n\n'); + cmEditor.setValue(`${text.trimEnd()}\n\n`); } /******************************************************************************/ diff --git a/src/js/arglist-parser.js b/src/js/arglist-parser.js index d8200df5822a1..708029e2d9a51 100644 --- a/src/js/arglist-parser.js +++ b/src/js/arglist-parser.js @@ -32,7 +32,7 @@ export class ArglistParser { this.transform = false; this.failed = false; this.reWhitespaceStart = /^\s+/; - this.reWhitespaceEnd = /\s+$/; + this.reWhitespaceEnd = /(?:^|\S)(\s+)$/; this.reOddTrailingEscape = /(?:^|[^\\])(?:\\\\)*\\$/; this.reTrailingEscapeChars = /\\+$/; } @@ -90,7 +90,7 @@ export class ArglistParser { } rightWhitespaceCount(s) { const match = this.reWhitespaceEnd.exec(s); - return match === null ? 0 : match[0].length; + return match === null ? 0 : match[1].length; } indexOfNextArgSeparator(pattern, separatorCode) { this.argBeg = this.argEnd = separatorCode !== this.separatorCode diff --git a/src/js/static-filtering-parser.js b/src/js/static-filtering-parser.js index 66d2853ae6a72..a3f6e1cc54929 100644 --- a/src/js/static-filtering-parser.js +++ b/src/js/static-filtering-parser.js @@ -785,7 +785,7 @@ export class AstFilterParser { this.selectorCompiler = new ExtSelectorCompiler(options); // Regexes this.reWhitespaceStart = /^\s+/; - this.reWhitespaceEnd = /\s+$/; + this.reWhitespaceEnd = /(?:^|\S)(\s+)$/; this.reCommentLine = /^(?:!|#\s|####|\[adblock)/i; this.reExtAnchor = /(#@?(?:\$\?|\$|%|\?)?#).{1,2}/; this.reInlineComment = /(?:\s+#).*?$/; @@ -2786,7 +2786,7 @@ export class AstFilterParser { rightWhitespaceCount(s) { const match = this.reWhitespaceEnd.exec(s); - return match === null ? 0 : match[0].length; + return match === null ? 0 : match[1].length; } nextCommaInCommaSeparatedListString(s, start) { diff --git a/src/js/whitelist.js b/src/js/whitelist.js index 6d9e951c1be7f..371d25f997cf8 100644 --- a/src/js/whitelist.js +++ b/src/js/whitelist.js @@ -99,12 +99,12 @@ uBlockDashboard.patchCodeMirrorEditor(cmEditor); /******************************************************************************/ function getEditorText() { - let text = cmEditor.getValue().replace(/\s+$/, ''); - return text === '' ? text : text + '\n'; + const text = cmEditor.getValue().trimEnd(); + return text === '' ? text : `${text}\n`; } function setEditorText(text) { - cmEditor.setValue(text.replace(/\s+$/, '') + '\n'); + cmEditor.setValue(`${text.trimEnd()}\n`); } /******************************************************************************/ From e20e6addf0c873a49428c3f21e53716e991cf383 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Wed, 16 Apr 2025 08:18:26 -0400 Subject: [PATCH 0828/1099] [mv3] Mind excluded `to=` hostnames in strict-block rules --- platform/mv3/make-rulesets.js | 55 ++++++++++++++--------------------- 1 file changed, 22 insertions(+), 33 deletions(-) diff --git a/platform/mv3/make-rulesets.js b/platform/mv3/make-rulesets.js index 17302460e5056..3c20052697d34 100644 --- a/platform/mv3/make-rulesets.js +++ b/platform/mv3/make-rulesets.js @@ -408,7 +408,6 @@ function toStrictBlockRule(rule, out) { if ( condition.excludedResponseHeaders ) { return; } if ( condition.initiatorDomains ) { return; } if ( condition.excludedInitiatorDomains ) { return; } - if ( condition.excludedRequestDomains ) { return; } const { resourceTypes } = condition; if ( resourceTypes === undefined ) { if ( condition.requestDomains === undefined ) { return; } @@ -423,9 +422,7 @@ function toStrictBlockRule(rule, out) { } else { regexFilter = '^https?://.*'; } - if ( - regexFilter.startsWith('^') === false - ) { + if ( regexFilter.startsWith('^') === false ) { regexFilter = `^.*${regexFilter}`; } if ( @@ -435,7 +432,7 @@ function toStrictBlockRule(rule, out) { ) { regexFilter = `${regexFilter}.*`; } - const strictBlockRule = { + const strictBlockRule = out.get(regexFilter) || { action: { type: 'redirect', redirect: { @@ -449,9 +446,14 @@ function toStrictBlockRule(rule, out) { priority: 29, }; if ( condition.requestDomains ) { - strictBlockRule.condition.requestDomains = condition.requestDomains.slice(); + strictBlockRule.condition.requestDomains ??= []; + strictBlockRule.condition.requestDomains.push(...condition.requestDomains); } - out.set(toStrictBlockRule.ruleId++, strictBlockRule); + if ( condition.excludedRequestDomains ) { + strictBlockRule.condition.excludedRequestDomains ??= []; + strictBlockRule.condition.excludedRequestDomains.push(...condition.excludedRequestDomains); + } + out.set(regexFilter, strictBlockRule); } toStrictBlockRule.ruleId = 1; @@ -570,35 +572,30 @@ async function processNetworkFilters(assetDetails, network) { log(`\tUnsupported: ${bad.length}`); log(bad.map(rule => rule._error.map(v => `\t\t${v}`)).join('\n'), true); - writeFile( - `${rulesetDir}/main/${assetDetails.id}.json`, + writeFile(`${rulesetDir}/main/${assetDetails.id}.json`, toJSONRuleset(plainGood) ); if ( regexes.length !== 0 ) { - writeFile( - `${rulesetDir}/regex/${assetDetails.id}.json`, + writeFile(`${rulesetDir}/regex/${assetDetails.id}.json`, toJSONRuleset(regexes) ); } if ( removeparamsGood.length !== 0 ) { - writeFile( - `${rulesetDir}/removeparam/${assetDetails.id}.json`, + writeFile(`${rulesetDir}/removeparam/${assetDetails.id}.json`, toJSONRuleset(removeparamsGood) ); } if ( redirects.length !== 0 ) { - writeFile( - `${rulesetDir}/redirect/${assetDetails.id}.json`, + writeFile(`${rulesetDir}/redirect/${assetDetails.id}.json`, toJSONRuleset(redirects) ); } if ( modifyHeaders.length !== 0 ) { - writeFile( - `${rulesetDir}/modify-headers/${assetDetails.id}.json`, + writeFile(`${rulesetDir}/modify-headers/${assetDetails.id}.json`, toJSONRuleset(modifyHeaders) ); } @@ -613,15 +610,13 @@ async function processNetworkFilters(assetDetails, network) { for ( const rule of strictBlocked.values() ) { rule.id = id++; } - writeFile( - `${rulesetDir}/strictblock/${assetDetails.id}.json`, + writeFile(`${rulesetDir}/strictblock/${assetDetails.id}.json`, toJSONRuleset(Array.from(strictBlocked.values())) ); } if ( urlskips.size !== 0 ) { - writeFile( - `${rulesetDir}/urlskip/${assetDetails.id}.json`, + writeFile(`${rulesetDir}/urlskip/${assetDetails.id}.json`, JSON.stringify(Array.from(urlskips.values()), null, 1) ); } @@ -756,8 +751,7 @@ async function processGenericCosmeticFilters( `${JSON.stringify(genericExceptionMap, scriptletJsonReplacer)}` ); - writeFile( - `${scriptletDir}/generic/${assetDetails.id}.js`, + writeFile(`${scriptletDir}/generic/${assetDetails.id}.js`, patchedScriptlet ); @@ -815,8 +809,7 @@ async function processGenericHighCosmeticFilters( selectorLists ); - writeFile( - `${scriptletDir}/generichigh/${assetDetails.id}.css`, + writeFile(`${scriptletDir}/generichigh/${assetDetails.id}.css`, patchedScriptlet ); @@ -1579,18 +1572,15 @@ async function main() { logProgress(''); - writeFile( - `${rulesetDir}/ruleset-details.json`, + writeFile(`${rulesetDir}/ruleset-details.json`, `${JSON.stringify(rulesetDetails, null, 1)}\n` ); - writeFile( - `${rulesetDir}/scriptlet-details.json`, + writeFile(`${rulesetDir}/scriptlet-details.json`, `${JSON.stringify(scriptletStats, jsonSetMapReplacer, 1)}\n` ); - writeFile( - `${rulesetDir}/generic-details.json`, + writeFile(`${rulesetDir}/generic-details.json`, `${JSON.stringify(genericDetails, jsonSetMapReplacer, 1)}\n` ); @@ -1625,8 +1615,7 @@ async function main() { // Patch manifest version property manifest.version = version; // Commit changes - await fs.writeFile( - `${outputDir}/manifest.json`, + await fs.writeFile(`${outputDir}/manifest.json`, JSON.stringify(manifest, null, 2) + '\n' ); From ec19e352b18ce8fc0ff5e9aaadd1d651a9ef9cd1 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Wed, 16 Apr 2025 08:19:52 -0400 Subject: [PATCH 0829/1099] [mv3] Mitigate issues when hitting regex-based rules limit When there are too many regex-based rules, uBOL will start to drop some strict-block rules to comply with the framework limit regarding the maximum number of regex-based rules. Related issues: - https://github.com/uBlockOrigin/uBOL-home/issues/317 - https://github.com/w3c/webextensions/issues/744 --- platform/mv3/extension/js/ruleset-manager.js | 40 ++++++++++++-------- 1 file changed, 24 insertions(+), 16 deletions(-) diff --git a/platform/mv3/extension/js/ruleset-manager.js b/platform/mv3/extension/js/ruleset-manager.js index 4dbc7f1e1b994..add3c5e25bce6 100644 --- a/platform/mv3/extension/js/ruleset-manager.js +++ b/platform/mv3/extension/js/ruleset-manager.js @@ -43,6 +43,9 @@ import { ubolLog } from './debug.js'; const TRUSTED_DIRECTIVE_BASE_RULE_ID = 8000000; const STRICTBLOCK_PRIORITY = 29; +let dynamicRegexCount = 0; +let sessionRegexCount = 0; + /******************************************************************************/ const isStrictBlockRule = rule => { @@ -280,16 +283,15 @@ async function updateDynamicRules() { ]); if ( addRules.length === 0 && removeRuleIds.length === 0 ) { return; } - const maxRegexRuleCount = dnr.MAX_NUMBER_OF_REGEX_RULES; - let regexRuleCount = 0; + dynamicRegexCount = 0; let ruleId = 1; for ( const rule of addRules ) { - if ( rule?.condition.regexFilter ) { regexRuleCount += 1; } + if ( rule?.condition.regexFilter ) { dynamicRegexCount += 1; } if ( (rule.id || 0) >= TRUSTED_DIRECTIVE_BASE_RULE_ID ) { continue; } rule.id = ruleId++; } - if ( regexRuleCount !== 0 ) { - ubolLog(`Using ${regexRuleCount}/${maxRegexRuleCount} dynamic regex-based DNR rules`); + if ( dynamicRegexCount !== 0 ) { + ubolLog(`Using ${dynamicRegexCount}/${dnr.MAX_NUMBER_OF_REGEX_RULES} dynamic regex-based DNR rules`); } return Promise.all([ dnr.updateDynamicRules({ addRules, removeRuleIds }).then(( ) => { @@ -402,20 +404,26 @@ async function setStrictBlockMode(state) { /******************************************************************************/ async function updateSessionRules() { - const addRules = []; + const addRulesUnfiltered = []; const removeRuleIds = []; const currentRules = await dnr.getSessionRules(); - await updateStrictBlockRules(currentRules, addRules, removeRuleIds); - if ( addRules.length === 0 && removeRuleIds.length === 0 ) { return; } - const maxRegexRuleCount = dnr.MAX_NUMBER_OF_REGEX_RULES; - let regexRuleCount = 0; + await updateStrictBlockRules(currentRules, addRulesUnfiltered, removeRuleIds); + if ( addRulesUnfiltered.length === 0 && removeRuleIds.length === 0 ) { return; } + const maxRegexCount = dnr.MAX_NUMBER_OF_REGEX_RULES * 0.95; + let regexCount = dynamicRegexCount; let ruleId = 1; - for ( const rule of addRules ) { - if ( rule?.condition.regexFilter ) { regexRuleCount += 1; } - rule.id = ruleId++; - } - if ( regexRuleCount !== 0 ) { - ubolLog(`Using ${regexRuleCount}/${maxRegexRuleCount} session regex-based DNR rules`); + for ( const rule of addRulesUnfiltered ) { + if ( rule?.condition.regexFilter ) { regexCount += 1; } + rule.id = regexCount < maxRegexCount ? ruleId++ : 0; + } + sessionRegexCount = regexCount - dynamicRegexCount; + const addRules = addRulesUnfiltered.filter(a => a.id !== 0); + const rejectedRuleCount = addRulesUnfiltered.length - addRules.length; + if ( rejectedRuleCount !== 0 ) { + ubolLog(`Too many regex-based filters, ${rejectedRuleCount} session rules dropped`); + } + if ( sessionRegexCount !== 0 ) { + ubolLog(`Using ${sessionRegexCount}/${dnr.MAX_NUMBER_OF_REGEX_RULES} session regex-based DNR rules`); } return dnr.updateSessionRules({ addRules, removeRuleIds }).then(( ) => { if ( removeRuleIds.length !== 0 ) { From 3b34f9439a10ee230ca3f3c6cbc62e463fae8b2d Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Wed, 16 Apr 2025 08:39:43 -0400 Subject: [PATCH 0830/1099] Import translation work from https://crowdin.com/project/ublock --- platform/mv3/extension/_locales/de/messages.json | 2 +- platform/mv3/extension/_locales/en_GB/messages.json | 2 +- platform/mv3/extension/_locales/ru/messages.json | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/platform/mv3/extension/_locales/de/messages.json b/platform/mv3/extension/_locales/de/messages.json index 723715502a99e..0e73a10dbd5c5 100644 --- a/platform/mv3/extension/_locales/de/messages.json +++ b/platform/mv3/extension/_locales/de/messages.json @@ -4,7 +4,7 @@ "description": "extension name." }, "extShortDesc": { - "message": "Ein Inhaltsblocker, der ohne Berechtigungen auskommt. Blockiert Werbung, Tracker und mehr sofort nach der Installation.", + "message": "Ein effizienter Inhaltsblocker. Blockiert Werbung, Tracker und mehr sofort nach der Installation.", "description": "this will be in the Chrome web store: must be 132 characters or less" }, "perRulesetStats": { diff --git a/platform/mv3/extension/_locales/en_GB/messages.json b/platform/mv3/extension/_locales/en_GB/messages.json index dd571fec0780f..36e8955640a6b 100644 --- a/platform/mv3/extension/_locales/en_GB/messages.json +++ b/platform/mv3/extension/_locales/en_GB/messages.json @@ -4,7 +4,7 @@ "description": "extension name." }, "extShortDesc": { - "message": "An experimental, permission-less content blocker. Blocks ads, trackers, miners, and more immediately upon installation.", + "message": "An efficient content blocker. Blocks ads, trackers, miners, and more immediately upon installation.", "description": "this will be in the Chrome web store: must be 132 characters or less" }, "perRulesetStats": { diff --git a/platform/mv3/extension/_locales/ru/messages.json b/platform/mv3/extension/_locales/ru/messages.json index 5badbdc661cf6..993219a6be411 100644 --- a/platform/mv3/extension/_locales/ru/messages.json +++ b/platform/mv3/extension/_locales/ru/messages.json @@ -4,7 +4,7 @@ "description": "extension name." }, "extShortDesc": { - "message": "Блокировщик контента, не требующий разрешений. Сразу после инсталляции блокирует рекламу, трекеры, майнеры и многое другое.", + "message": "Блокировщик веб-элементов, не требующий разрешений. Сразу после установки блокирует рекламу, трекеры, майнеры и многое другое.", "description": "this will be in the Chrome web store: must be 132 characters or less" }, "perRulesetStats": { From b8adf5b027bcc6397657824e7a2c70499cc344aa Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Wed, 16 Apr 2025 15:04:47 -0400 Subject: [PATCH 0831/1099] [mv3] Add admin setting `defaultFiltering` Setting name: "defaultFiltering" Type: string Valid values: "none", "basic", "optimal", "complete" Additionally, fix default "basic" mode toggling back to "optimal" upon extension restart when uBOL has broad host permissions. The default mode will toggle automatically only when there is a change in broad host permissions status. --- platform/mv3/extension/js/admin.js | 8 +++++ platform/mv3/extension/js/config.js | 1 + platform/mv3/extension/js/mode-manager.js | 40 ++++++++++++++++++--- platform/mv3/extension/managed_storage.json | 4 +++ 4 files changed, 49 insertions(+), 4 deletions(-) diff --git a/platform/mv3/extension/js/admin.js b/platform/mv3/extension/js/admin.js index 8a6b91fb77765..843285d89b76a 100644 --- a/platform/mv3/extension/js/admin.js +++ b/platform/mv3/extension/js/admin.js @@ -31,6 +31,7 @@ import { } from './ruleset-manager.js'; import { + getDefaultFilteringMode, getTrustedSites, readFilteringModeDetails, } from './mode-manager.js'; @@ -66,6 +67,13 @@ const adminSettings = { const [ adminRulesets, enabledRulesets ] = results; broadcastMessage({ adminRulesets, enabledRulesets }); } + if ( this.keys.has('defaultFiltering') ) { + ubolLog('admin setting "defaultFiltering" changed'); + await readFilteringModeDetails(true); + await registerInjectables(); + const defaultFilteringMode = await getDefaultFilteringMode(); + broadcastMessage({ defaultFilteringMode }); + } if ( this.keys.has('noFiltering') ) { ubolLog('admin setting "noFiltering" changed'); await readFilteringModeDetails(true); diff --git a/platform/mv3/extension/js/config.js b/platform/mv3/extension/js/config.js index 0c79dd62b18f0..64e2f3e9b7927 100644 --- a/platform/mv3/extension/js/config.js +++ b/platform/mv3/extension/js/config.js @@ -33,6 +33,7 @@ export const rulesetConfig = { showBlockedCount: true, strictBlockMode: true, developerMode: false, + hasBroadHostPermissions: true, }; export const process = { diff --git a/platform/mv3/extension/js/mode-manager.js b/platform/mv3/extension/js/mode-manager.js index 01f9739628abb..805a45a99fe1d 100644 --- a/platform/mv3/extension/js/mode-manager.js +++ b/platform/mv3/extension/js/mode-manager.js @@ -33,6 +33,11 @@ import { sessionRead, sessionWrite, } from './ext.js'; +import { + rulesetConfig, + saveRulesetConfig, +} from './config.js'; + import { adminReadEx } from './admin.js'; import { filteringModesToDNR } from './ruleset-manager.js'; @@ -219,14 +224,31 @@ export async function readFilteringModeDetails(bypassCache = false) { return readFilteringModeDetails.cache; } } - let [ userModes, adminNoFiltering ] = await Promise.all([ + let [ + userModes, + adminDefaultFiltering, + adminNoFiltering, + ] = await Promise.all([ localRead('filteringModeDetails'), + adminReadEx('defaultFiltering'), adminReadEx('noFiltering'), ]); if ( userModes === undefined ) { userModes = { optimal: [ 'all-urls' ] }; } userModes = unserializeModeDetails(userModes); + if ( adminDefaultFiltering !== undefined ) { + const modefromName = { + none: MODE_NONE, + basic: MODE_BASIC, + optimal: MODE_OPTIMAL, + complete: MODE_COMPLETE, + }; + const adminDefaultFilteringMode = modefromName[adminDefaultFiltering]; + if ( adminDefaultFilteringMode !== undefined ) { + applyFilteringMode(userModes, 'all-urls', adminDefaultFilteringMode); + } + } if ( Array.isArray(adminNoFiltering) ) { if ( adminNoFiltering.includes('-*') ) { userModes.none.clear(); @@ -341,19 +363,29 @@ export async function setTrustedSites(hostnames) { /******************************************************************************/ export async function syncWithBrowserPermissions() { - const [ permissions, beforeMode ] = await Promise.all([ + const [ + permissions, + beforeMode, + ] = await Promise.all([ browser.permissions.getAll(), getDefaultFilteringMode(), ]); const allowedHostnames = new Set(hostnamesFromMatches(permissions.origins || [])); + const hasBroadHostPermissions = allowedHostnames.has('all-urls'); + const broadHostPermissionsToggled = + hasBroadHostPermissions !== rulesetConfig.hasBroadHostPermissions; let modified = false; - if ( beforeMode > MODE_BASIC && allowedHostnames.has('all-urls') === false ) { + if ( beforeMode > MODE_BASIC && hasBroadHostPermissions === false ) { await setDefaultFilteringMode(MODE_BASIC); modified = true; - } else if ( beforeMode === MODE_BASIC && allowedHostnames.has('all-urls') ) { + } else if ( beforeMode === MODE_BASIC && hasBroadHostPermissions && broadHostPermissionsToggled ) { await setDefaultFilteringMode(MODE_OPTIMAL); modified = true; } + if ( broadHostPermissionsToggled ) { + rulesetConfig.hasBroadHostPermissions = hasBroadHostPermissions; + saveRulesetConfig(); + } const afterMode = await getDefaultFilteringMode(); if ( afterMode > MODE_BASIC ) { return afterMode !== beforeMode; } const filteringModes = await getFilteringModeDetails(); diff --git a/platform/mv3/extension/managed_storage.json b/platform/mv3/extension/managed_storage.json index 9fb3be661a1b1..5cf817aeb73f2 100644 --- a/platform/mv3/extension/managed_storage.json +++ b/platform/mv3/extension/managed_storage.json @@ -2,6 +2,10 @@ "$schema": "http://json-schema.org/draft-03/schema#", "type": "object", "properties": { + "defaultFiltering": { + "title": "The default filtering mode", + "type": "string", + }, "noFiltering": { "title": "List of domains for which no filtering should occur", "type": "array", From a1689fb9d01d42624845f2425f1df4e14ce0ac13 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Wed, 16 Apr 2025 15:13:52 -0400 Subject: [PATCH 0832/1099] [mv3] Fix JSON, add description --- platform/mv3/extension/managed_storage.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/platform/mv3/extension/managed_storage.json b/platform/mv3/extension/managed_storage.json index 5cf817aeb73f2..a5ffd877e4c72 100644 --- a/platform/mv3/extension/managed_storage.json +++ b/platform/mv3/extension/managed_storage.json @@ -4,7 +4,8 @@ "properties": { "defaultFiltering": { "title": "The default filtering mode", - "type": "string", + "description": "Can be one of \"none\", \"basic\", \"optimal\", \"complete\".", + "type": "string" }, "noFiltering": { "title": "List of domains for which no filtering should occur", From c299d3911744b614d6d6372ea366004f409bc92f Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Wed, 16 Apr 2025 15:21:14 -0400 Subject: [PATCH 0833/1099] Import translation work from https://crowdin.com/project/ublock --- platform/mv3/description/webstore.ro.txt | 2 +- platform/mv3/extension/_locales/it/messages.json | 2 +- platform/mv3/extension/_locales/ru/messages.json | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/platform/mv3/description/webstore.ro.txt b/platform/mv3/description/webstore.ro.txt index cda3413f0eeb0..86ef894d53855 100644 --- a/platform/mv3/description/webstore.ro.txt +++ b/platform/mv3/description/webstore.ro.txt @@ -1,4 +1,4 @@ -uBO Lite (uBOL) is an MV3-based content blocker. +uBO Lite (uBOL) este un blocker de conținut bazat pe MV3. Setul de reguli implicit corespunde setului de filtre implicit al uBlock Origin: diff --git a/platform/mv3/extension/_locales/it/messages.json b/platform/mv3/extension/_locales/it/messages.json index fba89f473f28d..b0a15c153c5c7 100644 --- a/platform/mv3/extension/_locales/it/messages.json +++ b/platform/mv3/extension/_locales/it/messages.json @@ -4,7 +4,7 @@ "description": "extension name." }, "extShortDesc": { - "message": "An efficient content blocker. Blocks ads, trackers, miners, and more immediately upon installation.", + "message": "Un efficiente bloccatore dei contenuti. Blocca annunci, tracker, minatori e altro ancora subito dopo l'installazione.", "description": "this will be in the Chrome web store: must be 132 characters or less" }, "perRulesetStats": { diff --git a/platform/mv3/extension/_locales/ru/messages.json b/platform/mv3/extension/_locales/ru/messages.json index 993219a6be411..adb7c14706b10 100644 --- a/platform/mv3/extension/_locales/ru/messages.json +++ b/platform/mv3/extension/_locales/ru/messages.json @@ -4,7 +4,7 @@ "description": "extension name." }, "extShortDesc": { - "message": "Блокировщик веб-элементов, не требующий разрешений. Сразу после установки блокирует рекламу, трекеры, майнеры и многое другое.", + "message": "Эффективный блокировщик веб-элементов. Сразу после установки блокирует рекламу, трекеры, майнеры и многое другое.", "description": "this will be in the Chrome web store: must be 132 characters or less" }, "perRulesetStats": { From fd3a8720e9b29cb4a1e5e9e07193b14d6942242e Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Wed, 16 Apr 2025 16:14:58 -0400 Subject: [PATCH 0834/1099] Minor test to avoid code path --- platform/mv3/extension/js/mode-manager.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform/mv3/extension/js/mode-manager.js b/platform/mv3/extension/js/mode-manager.js index 805a45a99fe1d..41abc057d9983 100644 --- a/platform/mv3/extension/js/mode-manager.js +++ b/platform/mv3/extension/js/mode-manager.js @@ -249,7 +249,7 @@ export async function readFilteringModeDetails(bypassCache = false) { applyFilteringMode(userModes, 'all-urls', adminDefaultFilteringMode); } } - if ( Array.isArray(adminNoFiltering) ) { + if ( Array.isArray(adminNoFiltering) && adminNoFiltering.length !== 0 ) { if ( adminNoFiltering.includes('-*') ) { userModes.none.clear(); } From 63ea23f334d1d128fc0fd3c15e91c266612f2524 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Fri, 18 Apr 2025 07:57:21 -0400 Subject: [PATCH 0835/1099] [mv3] Minor change to build script --- platform/mv3/make-rulesets.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/platform/mv3/make-rulesets.js b/platform/mv3/make-rulesets.js index 3c20052697d34..6c48ed5f7113c 100644 --- a/platform/mv3/make-rulesets.js +++ b/platform/mv3/make-rulesets.js @@ -107,7 +107,7 @@ console.log = log; const logProgress = text => { process?.stdout?.clearLine?.(); process?.stdout?.cursorTo?.(0); - process?.stdout?.write?.(text); + process?.stdout?.write?.(text.length > 120 ? `${text.slice(0, 119)}…` : text); }; /******************************************************************************/ @@ -120,12 +120,13 @@ const urlToFileName = url => { const fetchText = (url, cacheDir) => { return new Promise((resolve, reject) => { - logProgress(`Fetching ${url}`); + logProgress(`Reading locally cached ${url}`); const fname = urlToFileName(url); fs.readFile(`${cacheDir}/${fname}`, { encoding: 'utf8' }).then(content => { log(`\tFetched local ${url}`); resolve({ url, content }); }).catch(( ) => { + logProgress(`Fetching remote ${url}`); log(`\tFetching remote ${url}`); https.get(url, response => { const data = []; From 8016e7733aba9f03a8d97afa5a488d097b67a8cf Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sat, 19 Apr 2025 08:37:29 -0400 Subject: [PATCH 0836/1099] [mv3] Move ruleset details into external config file --- platform/mv3/make-rulesets.js | 222 +------------ platform/mv3/rulesets.json | 593 ++++++++++++++++++++++++++++++++++ tools/make-mv3.sh | 3 +- 3 files changed, 603 insertions(+), 215 deletions(-) create mode 100644 platform/mv3/rulesets.json diff --git a/platform/mv3/make-rulesets.js b/platform/mv3/make-rulesets.js index 6c48ed5f7113c..930aa99b7162e 100644 --- a/platform/mv3/make-rulesets.js +++ b/platform/mv3/make-rulesets.js @@ -1216,6 +1216,9 @@ async function rulesetFromURLs(assetDetails) { { env, extensionPaths, secret } ); + // Release memory used by filter list content + assetDetails.text = undefined; + const netStats = await processNetworkFilters( assetDetails, results.network @@ -1353,223 +1356,16 @@ async function main() { } log(`Version: ${version}`, false); - // Get assets.json content - const assets = await fs.readFile( - `./assets.dev.json`, - { encoding: 'utf8' } - ).then(text => + // Get list of rulesets + const rulesets = await fs.readFile('rulesets.json', { + encoding: 'utf8' + }).then(text => JSON.parse(text) ); - // Assemble all default lists as the default ruleset - await rulesetFromURLs({ - id: 'default', - name: 'Ads, trackers, miners, and more' , - enabled: true, - urls: [ - 'https://ublockorigin.github.io/uAssets/filters/filters.min.txt', - 'https://ublockorigin.github.io/uAssets/filters/privacy.min.txt', - 'https://ublockorigin.github.io/uAssets/filters/unbreak.min.txt', - 'https://ublockorigin.github.io/uAssets/filters/quick-fixes.min.txt', - 'https://ublockorigin.github.io/uAssets/filters/ubol-filters.txt', - 'https://ublockorigin.github.io/uAssets/thirdparties/easylist.txt', - 'https://ublockorigin.github.io/uAssets/thirdparties/easyprivacy.txt', - 'https://pgl.yoyo.org/adservers/serverlist.php?hostformat=hosts&showintro=1&mimetype=plaintext', - ], - dnrURL: 'https://ublockorigin.github.io/uAssets/dnr/default.json', - homeURL: 'https://github.com/uBlockOrigin/uAssets', - filters: [` - `], - }); - - await rulesetFromURLs({ - id: 'badware', - name: 'Badware risks' , - group: 'malware', - enabled: true, - urls: [ - 'https://ublockorigin.github.io/uAssets/filters/badware.min.txt', - ], - homeURL: 'https://github.com/uBlockOrigin/uAssets', - filters: [ - ], - }); - - // Handpicked rulesets from assets.json - const handpicked = [ - 'block-lan', - 'dpollock-0', - 'adguard-spyware-url', - ]; - for ( const id of handpicked ) { - const asset = assets[id]; - if ( asset.content !== 'filters' ) { continue; } - const contentURL = Array.isArray(asset.contentURL) - ? asset.contentURL[0] - : asset.contentURL; - await rulesetFromURLs({ - id: id.toLowerCase(), - name: asset.title, - enabled: false, - urls: [ contentURL ], - homeURL: asset.supportURL, - }); - } - - // Handpicked annoyance rulesets from assets.json - await rulesetFromURLs({ - id: 'annoyances-cookies', - name: 'EasyList/uBO – Cookie Notices', - group: 'annoyances', - enabled: false, - urls: [ - 'https://ublockorigin.github.io/uAssets/thirdparties/easylist-cookies.txt', - 'https://ublockorigin.github.io/uAssets/filters/annoyances-cookies.txt', - ], - homeURL: 'https://github.com/easylist/easylist#fanboy-lists', - }); - await rulesetFromURLs({ - id: 'annoyances-overlays', - name: 'EasyList/uBO – Overlay Notices', - group: 'annoyances', - enabled: false, - urls: [ - 'https://ublockorigin.github.io/uAssets/thirdparties/easylist-newsletters.txt', - 'https://ublockorigin.github.io/uAssets/filters/annoyances-others.txt', - ], - homeURL: 'https://github.com/easylist/easylist#fanboy-lists', - }); - await rulesetFromURLs({ - id: 'annoyances-social', - name: 'EasyList – Social Widgets', - group: 'annoyances', - enabled: false, - urls: [ - 'https://ublockorigin.github.io/uAssets/thirdparties/easylist-social.txt', - ], - homeURL: 'https://github.com/easylist/easylist#fanboy-lists', - }); - await rulesetFromURLs({ - id: 'annoyances-widgets', - name: 'EasyList – Chat Widgets', - group: 'annoyances', - enabled: false, - urls: [ - 'https://ublockorigin.github.io/uAssets/thirdparties/easylist-chat.txt', - ], - homeURL: 'https://github.com/easylist/easylist#fanboy-lists', - }); - await rulesetFromURLs({ - id: 'annoyances-others', - name: 'EasyList – Other Annoyances', - group: 'annoyances', - enabled: false, - urls: [ - 'https://ublockorigin.github.io/uAssets/thirdparties/easylist-annoyances.txt' - ], - homeURL: 'https://github.com/easylist/easylist#fanboy-lists', - }); - - // Handpicked rulesets from abroad - await rulesetFromURLs({ - id: 'urlhaus-full', - name: 'Malicious URL Blocklist', - group: 'malware', - enabled: true, - urls: [ - 'https://malware-filter.gitlab.io/malware-filter/urlhaus-filter-hosts.txt', - ], - filters: [ - ], - homeURL: 'https://gitlab.com/malware-filter/urlhaus-filter', - }); - await rulesetFromURLs({ - id: 'openphish-domains', - name: 'OpenPhish Domain Blocklist', - group: 'malware', - enabled: true, - urls: [ - 'https://raw.githubusercontent.com/stephenhawk8054/openphish-adblock/refs/heads/main/filters_init_domains.txt', - ], - filters: [ - ], - homeURL: 'https://github.com/stephenhawk8054/openphish-adblock', - }); - - await rulesetFromURLs({ - id: 'stevenblack-hosts', - name: 'Steven Black’s Unified Hosts (adware + malware)', - enabled: false, - urls: [ 'https://raw.githubusercontent.com/StevenBlack/hosts/master/hosts' ], - homeURL: 'https://github.com/StevenBlack/hosts#readme', - }); - - await rulesetFromURLs({ - id: 'ubol-tests', - name: 'uBO Lite Test Filters', - enabled: false, - trusted: true, - urls: [ 'https://ublockorigin.github.io/uBOL-home/tests/test-filters.txt' ], - homeURL: 'https://ublockorigin.github.io/uBOL-home/tests/test-filters.html', - filters: [` - `], - }); - - // Regional rulesets - const excludedLists = [ - 'ara-0', - 'EST-0', - ]; - // Merge lists which have same target languages - const langToListsMap = new Map(); - for ( const [ id, asset ] of Object.entries(assets) ) { - if ( asset.content !== 'filters' ) { continue; } - if ( asset.off !== true ) { continue; } - if ( asset.group !== 'regions' ) { continue; } - if ( excludedLists.includes(id) ) { continue; } - // Not all "regions" lists have a set language - const bundleId = asset.lang || - createHash('sha256').update(randomBytes(16)).digest('hex').slice(0,16); - let ids = langToListsMap.get(bundleId); - if ( ids === undefined ) { - langToListsMap.set(bundleId, ids = []); - } - ids.push(id); + for ( const ruleset of rulesets ) { + await rulesetFromURLs(ruleset); } - for ( const ids of langToListsMap.values() ) { - const urls = []; - for ( const id of ids ) { - const asset = assets[id]; - const contentURL = Array.isArray(asset.contentURL) - ? asset.contentURL[0] - : asset.contentURL; - urls.push(contentURL); - } - const id = ids[0]; - const asset = assets[id]; - const rulesetDetails = { - id: id.toLowerCase(), - group: 'regions', - parent: asset.parent, - lang: asset.lang, - name: asset.title, - tags: asset.tags, - enabled: false, - urls, - homeURL: asset.supportURL, - }; - await rulesetFromURLs(rulesetDetails); - } - - await rulesetFromURLs({ - id: 'est-0', - group: 'regions', - lang: 'et', - name: '🇪🇪ee: Eesti saitidele kohandatud filter', - enabled: false, - urls: [ 'https://ubol-et.adblock.ee/list.txt' ], - homeURL: 'https://github.com/sander85/uBOL-et', - }); logProgress(''); diff --git a/platform/mv3/rulesets.json b/platform/mv3/rulesets.json new file mode 100644 index 0000000000000..88b5241fa574e --- /dev/null +++ b/platform/mv3/rulesets.json @@ -0,0 +1,593 @@ +[ + { + "id": "default", + "name": "Ads, trackers, miners, and more", + "enabled": true, + "urls": [ + "https://ublockorigin.github.io/uAssets/filters/filters.min.txt", + "https://ublockorigin.github.io/uAssets/filters/privacy.min.txt", + "https://ublockorigin.github.io/uAssets/filters/unbreak.min.txt", + "https://ublockorigin.github.io/uAssets/filters/quick-fixes.min.txt", + "https://ublockorigin.github.io/uAssets/filters/ubol-filters.txt", + "https://ublockorigin.github.io/uAssets/thirdparties/easylist.txt", + "https://ublockorigin.github.io/uAssets/thirdparties/easyprivacy.txt", + "https://pgl.yoyo.org/adservers/serverlist.php?hostformat=hosts&showintro=1&mimetype=plaintext" + ], + "dnrURL": "https://ublockorigin.github.io/uAssets/dnr/default.json", + "homeURL": "https://github.com/uBlockOrigin/uAssets" + }, + { + "id": "badware", + "name": "Badware risks", + "group": "malware", + "enabled": true, + "urls": [ + "https://ublockorigin.github.io/uAssets/filters/badware.min.txt" + ], + "homeURL": "https://github.com/uBlockOrigin/uAssets" + }, + { + "id": "urlhaus-full", + "name": "Malicious URL Blocklist", + "group": "malware", + "enabled": true, + "urls": [ + "https://malware-filter.gitlab.io/malware-filter/urlhaus-filter-hosts.txt" + ], + "homeURL": "https://gitlab.com/malware-filter/urlhaus-filter" + }, + { + "id": "openphish-domains", + "name": "OpenPhish Domain Blocklist", + "group": "malware", + "enabled": true, + "urls": [ + "https://raw.githubusercontent.com/stephenhawk8054/openphish-adblock/refs/heads/main/filters_init_domains.txt" + ], + "homeURL": "https://github.com/stephenhawk8054/openphish-adblock" + }, + { + "id": "block-lan", + "name": "Block Outsider Intrusion into LAN", + "enabled": false, + "urls": [ + "https://ublockorigin.github.io/uAssets/filters/lan-block.txt" + ], + "homeURL": "https://github.com/uBlockOrigin/uAssets" + }, + { + "id": "dpollock-0", + "name": "Dan Pollock’s hosts file", + "enabled": false, + "urls": [ + "https://someonewhocares.org/hosts/hosts" + ], + "homeURL": "https://someonewhocares.org/hosts/" + }, + { + "id": "adguard-spyware-url", + "name": "AdGuard URL Tracking Protection", + "enabled": false, + "urls": [ + "https://filters.adtidy.org/extension/ublock/filters/17.txt" + ], + "homeURL": "https://github.com/AdguardTeam/AdguardFilters#adguard-filters" + }, + { + "id": "annoyances-cookies", + "name": "EasyList/uBO – Cookie Notices", + "group": "annoyances", + "enabled": false, + "urls": [ + "https://ublockorigin.github.io/uAssets/thirdparties/easylist-cookies.txt", + "https://ublockorigin.github.io/uAssets/filters/annoyances-cookies.txt" + ], + "homeURL": "https://github.com/easylist/easylist#fanboy-lists" + }, + { + "id": "annoyances-overlays", + "name": "EasyList/uBO – Overlay Notices", + "group": "annoyances", + "enabled": false, + "urls": [ + "https://ublockorigin.github.io/uAssets/thirdparties/easylist-newsletters.txt", + "https://ublockorigin.github.io/uAssets/filters/annoyances-others.txt" + ], + "homeURL": "https://github.com/easylist/easylist#fanboy-lists" + }, + { + "id": "annoyances-social", + "name": "EasyList – Social Widgets", + "group": "annoyances", + "enabled": false, + "urls": [ + "https://ublockorigin.github.io/uAssets/thirdparties/easylist-social.txt" + ], + "homeURL": "https://github.com/easylist/easylist#fanboy-lists" + }, + { + "id": "annoyances-widgets", + "name": "EasyList – Chat Widgets", + "group": "annoyances", + "enabled": false, + "urls": [ + "https://ublockorigin.github.io/uAssets/thirdparties/easylist-chat.txt" + ], + "homeURL": "https://github.com/easylist/easylist#fanboy-lists" + }, + { + "id": "annoyances-others", + "name": "EasyList – Other Annoyances", + "group": "annoyances", + "enabled": false, + "urls": [ + "https://ublockorigin.github.io/uAssets/thirdparties/easylist-annoyances.txt" + ], + "homeURL": "https://github.com/easylist/easylist#fanboy-lists" + }, + { + "id": "stevenblack-hosts", + "name": "Steven Black’s Unified Hosts (adware + malware)", + "enabled": false, + "urls": [ + "https://raw.githubusercontent.com/StevenBlack/hosts/master/hosts" + ], + "homeURL": "https://github.com/StevenBlack/hosts#readme" + }, + { + "id": "ubol-tests", + "name": "uBO Lite Test Filters", + "enabled": false, + "trusted": true, + "urls": [ + "https://ublockorigin.github.io/uBOL-home/tests/test-filters.txt" + ], + "homeURL": "https://ublockorigin.github.io/uBOL-home/tests/test-filters.html" + }, + { + "id": "alb-0", + "group": "regions", + "lang": "sq", + "name": "🇦🇱al 🇽🇰xk: Adblock List for Albania", + "tags": "ads albania shqipja", + "enabled": false, + "urls": [ + "https://raw.githubusercontent.com/AnXh3L0/blocklist/master/albanian-easylist-addition/Albania.txt" + ], + "homeURL": "https://github.com/AnXh3L0/blocklist" + }, + { + "id": "bgr-0", + "group": "regions", + "lang": "bg mk", + "name": "🇧🇬bg: Bulgarian Adblock list", + "tags": "ads bulgarian България macedonian Македонија", + "enabled": false, + "urls": [ + "https://stanev.org/abp/adblock_bg.txt" + ], + "homeURL": "https://stanev.org/abp/" + }, + { + "id": "chn-0", + "group": "regions", + "lang": "ug zh", + "name": "🇨🇳cn 🇹🇼tw: AdGuard Chinese (中文)", + "tags": "ads chinese 中文", + "enabled": false, + "urls": [ + "https://filters.adtidy.org/extension/ublock/filters/224.txt" + ], + "homeURL": "https://github.com/AdguardTeam/AdguardFilters#adguard-filters" + }, + { + "id": "cze-0", + "group": "regions", + "lang": "cs sk", + "name": "🇨🇿cz 🇸🇰sk: EasyList Czech and Slovak", + "tags": "ads czech česká slovak slovenská", + "enabled": false, + "urls": [ + "https://raw.githubusercontent.com/tomasko126/easylistczechandslovak/master/filters.txt" + ], + "homeURL": "https://github.com/tomasko126/easylistczechandslovak" + }, + { + "id": "deu-0", + "group": "regions", + "lang": "de dsb hsb lb rm", + "name": "🇩🇪de 🇨🇭ch 🇦🇹at: EasyList Germany", + "tags": "ads german deutschland luxembourgish lëtzebuerg romansh", + "enabled": false, + "urls": [ + "https://easylist.to/easylistgermany/easylistgermany.txt" + ], + "homeURL": "https://forums.lanik.us/viewforum.php?f=90" + }, + { + "id": "est-0", + "group": "regions", + "lang": "et", + "name": "🇪🇪ee: Eesti saitidele kohandatud filter", + "enabled": false, + "urls": [ + "https://ubol-et.adblock.ee/list.txt" + ], + "homeURL": "https://github.com/sander85/uBOL-et" + }, + { + "id": "fin-0", + "group": "regions", + "lang": "fi", + "name": "🇫🇮fi: Adblock List for Finland", + "tags": "ads finnish", + "enabled": false, + "urls": [ + "https://raw.githubusercontent.com/finnish-easylist-addition/finnish-easylist-addition/gh-pages/Finland_adb.txt" + ], + "homeURL": "https://github.com/finnish-easylist-addition/finnish-easylist-addition" + }, + { + "id": "fra-0", + "group": "regions", + "lang": "ar br ff fr lb oc son", + "name": "🇫🇷fr 🇨🇦ca: AdGuard Français", + "tags": "ads french", + "enabled": false, + "urls": [ + "https://filters.adtidy.org/extension/ublock/filters/16.txt" + ], + "homeURL": "https://github.com/AdguardTeam/AdguardFilters#adguard-filters" + }, + { + "id": "grc-0", + "group": "regions", + "lang": "el", + "name": "🇬🇷gr 🇨🇾cy: Greek AdBlock Filter", + "tags": "ads greek", + "enabled": false, + "urls": [ + "https://www.void.gr/kargig/void-gr-filters.txt" + ], + "homeURL": "https://github.com/kargig/greek-adblockplus-filter" + }, + { + "id": "hrv-0", + "group": "regions", + "lang": "bs hr sr", + "name": "🇭🇷hr 🇷🇸rs: Dandelion Sprout's Serbo-Croatian filters", + "tags": "ads croatian serbian bosnian", + "enabled": false, + "urls": [ + "https://raw.githubusercontent.com/DandelionSprout/adfilt/master/SerboCroatianList.txt" + ], + "homeURL": "https://github.com/DandelionSprout/adfilt#readme" + }, + { + "id": "hun-0", + "group": "regions", + "lang": "hu", + "name": "🇭🇺hu: hufilter", + "tags": "ads hungarian", + "enabled": false, + "urls": [ + "https://cdn.jsdelivr.net/gh/hufilter/hufilter@gh-pages/hufilter-ublock.txt" + ], + "homeURL": "https://github.com/hufilter/hufilter" + }, + { + "id": "idn-0", + "group": "regions", + "lang": "id ms", + "name": "🇮🇩id 🇲🇾my: ABPindo", + "tags": "ads indonesian malay", + "enabled": false, + "urls": [ + "https://raw.githubusercontent.com/ABPindo/indonesianadblockrules/master/subscriptions/abpindo.txt" + ], + "homeURL": "https://github.com/ABPindo/indonesianadblockrules" + }, + { + "id": "ind-0", + "group": "regions", + "lang": "as bn gu hi kn ml mr ne pa si ta te", + "name": "🇮🇳in 🇱🇰lk 🇳🇵np: IndianList", + "tags": "ads assamese bengali gujarati hindi kannada malayalam marathi nepali punjabi sinhala tamil telugu", + "enabled": false, + "urls": [ + "https://easylist-downloads.adblockplus.org/indianlist.txt" + ], + "homeURL": "https://github.com/mediumkreation/IndianList" + }, + { + "id": "irn-0", + "group": "regions", + "lang": "fa ps tg", + "name": "🇮🇷ir: PersianBlocker", + "tags": "ads af ir persian pashto tajik tj", + "enabled": false, + "urls": [ + "https://raw.githubusercontent.com/MasterKia/PersianBlocker/main/PersianBlocker.txt" + ], + "homeURL": "https://github.com/MasterKia/PersianBlocker" + }, + { + "id": "isl-0", + "group": "regions", + "lang": "is", + "name": "🇮🇸is: Icelandic ABP List", + "tags": "ads icelandic", + "enabled": false, + "urls": [ + "https://raw.githubusercontent.com/brave/adblock-lists/master/custom/is.txt" + ], + "homeURL": "https://github.com/brave/adblock-lists/issues" + }, + { + "id": "isr-0", + "group": "regions", + "lang": "he", + "name": "🇮🇱il: EasyList Hebrew", + "tags": "ads hebrew", + "enabled": false, + "urls": [ + "https://raw.githubusercontent.com/easylist/EasyListHebrew/master/EasyListHebrew.txt" + ], + "homeURL": "https://github.com/easylist/EasyListHebrew" + }, + { + "id": "ita-0", + "group": "regions", + "lang": "it lij", + "name": "🇮🇹it: EasyList Italy", + "tags": "ads italian", + "enabled": false, + "urls": [ + "https://easylist-downloads.adblockplus.org/easylistitaly.txt" + ], + "homeURL": "https://forums.lanik.us/viewforum.php?f=96" + }, + { + "id": "jpn-1", + "group": "regions", + "lang": "ja", + "name": "🇯🇵jp: AdGuard Japanese", + "tags": "ads japanese 日本語", + "enabled": false, + "urls": [ + "https://filters.adtidy.org/extension/ublock/filters/7.txt" + ], + "homeURL": "https://github.com/AdguardTeam/AdguardFilters#adguard-filters" + }, + { + "id": "kor-1", + "group": "regions", + "lang": "ko", + "name": "🇰🇷kr: List-KR", + "tags": "ads korean 한국어", + "enabled": false, + "urls": [ + "https://cdn.jsdelivr.net/gh/List-KR/List-KR@latest/filter-uBlockOrigin.txt" + ], + "homeURL": "https://github.com/List-KR/List-KR#readme" + }, + { + "id": "ltu-0", + "group": "regions", + "lang": "lt", + "name": "🇱🇹lt: EasyList Lithuania", + "tags": "ads lithuanian", + "enabled": false, + "urls": [ + "https://raw.githubusercontent.com/EasyList-Lithuania/easylist_lithuania/master/easylistlithuania.txt" + ], + "homeURL": "https://github.com/EasyList-Lithuania/easylist_lithuania" + }, + { + "id": "lva-0", + "group": "regions", + "lang": "lv", + "name": "🇱🇻lv: Latvian List", + "tags": "ads latvian", + "enabled": false, + "urls": [ + "https://raw.githubusercontent.com/Latvian-List/adblock-latvian/master/lists/latvian-list.txt" + ], + "homeURL": "https://github.com/Latvian-List/adblock-latvian" + }, + { + "id": "mkd-0", + "group": "regions", + "lang": "mk", + "name": "🇲🇰mk: Macedonian adBlock Filters", + "tags": "ads macedonian", + "enabled": false, + "urls": [ + "https://raw.githubusercontent.com/DeepSpaceHarbor/Macedonian-adBlock-Filters/master/Filters" + ], + "homeURL": "https://github.com/DeepSpaceHarbor/Macedonian-adBlock-Filters" + }, + { + "id": "nld-0", + "group": "regions", + "lang": "af fy nl", + "name": "🇳🇱nl 🇧🇪be: AdGuard Dutch", + "tags": "ads afrikaans be belgië frisian dutch flemish nederlands netherlands nl sr suriname za", + "enabled": false, + "urls": [ + "https://filters.adtidy.org/extension/ublock/filters/8.txt" + ], + "homeURL": "https://github.com/AdguardTeam/AdguardFilters#adguard-filters" + }, + { + "id": "nor-0", + "group": "regions", + "lang": "nb nn no da is", + "name": "🇳🇴no 🇩🇰dk 🇮🇸is: Dandelion Sprouts nordiske filtre", + "tags": "ads norwegian danish icelandic", + "enabled": false, + "urls": [ + "https://raw.githubusercontent.com/DandelionSprout/adfilt/master/NorwegianList.txt" + ], + "homeURL": "https://github.com/DandelionSprout/adfilt" + }, + { + "id": "pol-0", + "group": "regions", + "parent": "🇵🇱pl: Oficjalne Polskie Filtry", + "lang": "szl pl _", + "name": "🇵🇱pl: Oficjalne Polskie Filtry do uBlocka Origin", + "tags": "ads polish polski", + "enabled": false, + "urls": [ + "https://raw.githubusercontent.com/MajkiIT/polish-ads-filter/master/polish-adblock-filters/adblock.txt" + ], + "homeURL": "https://github.com/MajkiIT/polish-ads-filter" + }, + { + "id": "pol-3", + "group": "regions", + "parent": "🇵🇱pl: Oficjalne Polskie Filtry", + "lang": "szl pl", + "name": "🇵🇱pl: CERT.PL's Warning List", + "tags": "malware polish polski", + "enabled": false, + "urls": [ + "https://hole.cert.pl/domains/v2/domains_adblock.txt" + ], + "homeURL": "https://cert.pl/lista-ostrzezen/" + }, + { + "id": "rou-1", + "group": "regions", + "lang": "ro", + "name": "🇷🇴ro 🇲🇩md: Romanian Ad (ROad) Block List Light", + "tags": "ads romanian română moldavian moldovenească молдовеняскэ", + "enabled": false, + "urls": [ + "https://raw.githubusercontent.com/tcptomato/ROad-Block/master/road-block-filters-light.txt" + ], + "homeURL": "https://github.com/tcptomato/ROad-Block" + }, + { + "id": "rus-0", + "group": "regions", + "parent": "🇷🇺ru 🇺🇦ua 🇺🇿uz 🇰🇿kz: RU AdList", + "lang": "be kk tt ru uz", + "name": "🇷🇺ru 🇺🇦ua 🇺🇿uz 🇰🇿kz: RU AdList", + "tags": "ads belarusian беларуская kazakh tatar russian русский ukrainian українська uzbek uk", + "enabled": false, + "urls": [ + "https://raw.githubusercontent.com/easylist/ruadlist/master/RuAdList-uBO.txt" + ], + "homeURL": "https://forums.lanik.us/viewforum.php?f=102" + }, + { + "id": "rus-1", + "group": "regions", + "parent": "🇷🇺ru 🇺🇦ua 🇺🇿uz 🇰🇿kz: RU AdList", + "name": "🇷🇺ru 🇺🇦ua 🇺🇿uz 🇰🇿kz: RU AdList: Counters", + "tags": "ads belarusian беларуская kazakh tatar russian русский ukrainian українська uzbek be kk tt ru uk uz", + "enabled": false, + "urls": [ + "https://raw.githubusercontent.com/easylist/ruadlist/master/cntblock.txt" + ], + "homeURL": "https://forums.lanik.us/viewforum.php?f=102" + }, + { + "id": "spa-0", + "group": "regions", + "lang": "an ast ca cak es eu gl gn trs quz", + "name": "🇪🇸es 🇦🇷ar 🇲🇽mx 🇨🇴co: EasyList Spanish", + "tags": "ads aragonese basque catalan spanish español galician guarani", + "enabled": false, + "urls": [ + "https://easylist-downloads.adblockplus.org/easylistspanish.txt" + ], + "homeURL": "https://forums.lanik.us/viewforum.php?f=103" + }, + { + "id": "spa-1", + "group": "regions", + "lang": "an ast ca cak es eu gl gn trs pt quz", + "name": "🇪🇸es 🇦🇷ar 🇧🇷br 🇵🇹pt: AdGuard Spanish/Portuguese", + "tags": "ads aragonese basque catalan spanish español galician guarani portuguese português", + "enabled": false, + "urls": [ + "https://filters.adtidy.org/extension/ublock/filters/9.txt" + ], + "homeURL": "https://github.com/AdguardTeam/AdguardFilters#adguard-filters" + }, + { + "id": "svn-0", + "group": "regions", + "lang": "sl", + "name": "🇸🇮si: Slovenian List", + "tags": "ads slovenian slovenski", + "enabled": false, + "urls": [ + "https://raw.githubusercontent.com/betterwebleon/slovenian-list/master/filters.txt" + ], + "homeURL": "https://github.com/betterwebleon/slovenian-list" + }, + { + "id": "swe-1", + "group": "regions", + "lang": "sv", + "name": "🇸🇪se: Frellwit's Swedish Filter", + "tags": "ads swedish svenska", + "enabled": false, + "urls": [ + "https://raw.githubusercontent.com/lassekongo83/Frellwits-filter-lists/master/Frellwits-Swedish-Filter.txt" + ], + "homeURL": "https://github.com/lassekongo83/Frellwits-filter-lists" + }, + { + "id": "tha-0", + "group": "regions", + "lang": "th", + "name": "🇹🇭th: EasyList Thailand", + "tags": "ads thai ไทย", + "enabled": false, + "urls": [ + "https://raw.githubusercontent.com/easylist-thailand/easylist-thailand/master/subscription/easylist-thailand.txt" + ], + "homeURL": "https://github.com/easylist-thailand/easylist-thailand" + }, + { + "id": "tur-0", + "group": "regions", + "lang": "tr", + "name": "🇹🇷tr: AdGuard Turkish", + "tags": "ads turkish türkçe", + "enabled": false, + "urls": [ + "https://filters.adtidy.org/extension/ublock/filters/13.txt" + ], + "homeURL": "https://github.com/AdguardTeam/AdguardFilters#adguard-filters" + }, + { + "id": "ukr-0", + "group": "regions", + "lang": "uk", + "name": "🇺🇦ua: Ukrainian Filters", + "tags": "ads ukraine україна", + "enabled": false, + "urls": [ + "https://raw.githubusercontent.com/ukrainianfilters/lists/main/combined/uBO/uBO.txt" + ], + "homeURL": "https://github.com/ukrainianfilters/lists" + }, + { + "id": "vie-1", + "group": "regions", + "lang": "vi", + "name": "🇻🇳vn: ABPVN List", + "tags": "ads vietnamese việt", + "enabled": false, + "urls": [ + "https://raw.githubusercontent.com/abpvn/abpvn/master/filter/abpvn_ublock.txt" + ], + "homeURL": "https://abpvn.com/" + } +] diff --git a/tools/make-mv3.sh b/tools/make-mv3.sh index c985ae154df88..6d2364930df27 100755 --- a/tools/make-mv3.sh +++ b/tools/make-mv3.sh @@ -102,12 +102,11 @@ elif [ "$PLATFORM" = "firefox" ]; then cp platform/mv3/firefox/manifest.json "$DES"/ fi ./tools/make-nodejs.sh "$TMPDIR" -cp platform/mv3/package.json "$TMPDIR"/ +cp platform/mv3/*.json "$TMPDIR"/ cp platform/mv3/*.js "$TMPDIR"/ cp platform/mv3/*.mjs "$TMPDIR"/ cp platform/mv3/extension/js/utils.js "$TMPDIR"/js/ cp -R "$UBO_DIR"/src/js/resources "$TMPDIR"/js/ -cp "$UBO_DIR"/assets/assets.dev.json "$TMPDIR"/ cp -R platform/mv3/scriptlets "$TMPDIR"/ mkdir -p "$TMPDIR"/web_accessible_resources cp "$UBO_DIR"/src/web_accessible_resources/* "$TMPDIR"/web_accessible_resources/ From b5651417aacd6024cb0a89a2b357563c0761a5e6 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sat, 19 Apr 2025 13:08:59 -0400 Subject: [PATCH 0837/1099] [mv3] Merge Safari branch Safari version of uBO Lite can now be built from master branch. Related issue: https://github.com/uBlockOrigin/uBOL-home/issues/327 --- Makefile | 7 +- platform/mv3/extension/js/admin.js | 2 +- platform/mv3/extension/js/background.js | 8 +- platform/mv3/extension/js/debug.js | 53 ++++--- platform/mv3/extension/js/ext-compat.js | 96 +++++++++++++ platform/mv3/extension/js/ext.js | 11 +- platform/mv3/extension/js/popup.js | 1 + platform/mv3/extension/js/report.js | 3 +- platform/mv3/extension/js/ruleset-manager.js | 131 +++--------------- .../mv3/extension/js/scripting-manager.js | 7 +- platform/mv3/extension/js/scripting/zapper.js | 17 ++- platform/mv3/extension/js/utils.js | 7 +- platform/mv3/make-rulesets.js | 74 ++++++++-- platform/mv3/safari/ext-compat.js | 131 ++++++++++++++++++ platform/mv3/safari/manifest.json | 64 +++++++++ src/js/static-dnr-filtering.js | 14 ++ tools/make-mv3.sh | 13 +- 17 files changed, 466 insertions(+), 173 deletions(-) create mode 100644 platform/mv3/extension/js/ext-compat.js create mode 100644 platform/mv3/safari/ext-compat.js create mode 100644 platform/mv3/safari/manifest.json diff --git a/Makefile b/Makefile index c9d92a35c730e..477a3b3f73687 100644 --- a/Makefile +++ b/Makefile @@ -2,7 +2,7 @@ run_options := $(filter-out $@,$(MAKECMDGOALS)) .PHONY: all clean cleanassets test lint chromium opera firefox npm dig \ - mv3-chromium mv3-firefox mv3-edge \ + mv3-chromium mv3-firefox mv3-edge mv3-safari \ compare maxcost medcost mincost modifiers record wasm sources := $(wildcard assets/* assets/*/* dist/version src/* src/*/* src/*/*/* src/*/*/*/*) @@ -80,6 +80,11 @@ dist/build/uBOLite.edge: tools/make-mv3.sh tools/make-edge.mjs $(sources) $(plat mv3-edge: dist/build/uBOLite.edge +dist/build/uBOLite.safari: tools/make-mv3.sh $(sources) $(platform) $(mv3-data) dist/build/mv3-data + tools/make-mv3.sh safari + +mv3-safari: dist/build/uBOLite.safari + dist/build/uAssets: tools/pull-assets.sh diff --git a/platform/mv3/extension/js/admin.js b/platform/mv3/extension/js/admin.js index 843285d89b76a..c9fa5f5b98021 100644 --- a/platform/mv3/extension/js/admin.js +++ b/platform/mv3/extension/js/admin.js @@ -37,7 +37,7 @@ import { } from './mode-manager.js'; import { broadcastMessage } from './utils.js'; -import { dnr } from './ext.js'; +import { dnr } from './ext-compat.js'; import { registerInjectables } from './scripting-manager.js'; import { rulesetConfig } from './config.js'; import { ubolLog } from './debug.js'; diff --git a/platform/mv3/extension/js/background.js b/platform/mv3/extension/js/background.js index 6499c9d5ad3d4..bc2ba932b53a6 100644 --- a/platform/mv3/extension/js/background.js +++ b/platform/mv3/extension/js/background.js @@ -44,7 +44,6 @@ import { import { browser, - dnr, localRead, localRemove, localWrite, runtime, windows, @@ -75,11 +74,12 @@ import { saveRulesetConfig, } from './config.js'; +import { dnr } from './ext-compat.js'; import { registerInjectables } from './scripting-manager.js'; /******************************************************************************/ -const UBOL_ORIGIN = runtime.getURL('').replace(/\/$/, ''); +const UBOL_ORIGIN = runtime.getURL('').replace(/\/$/, '').toLowerCase(); const canShowBlockedCount = typeof dnr.setExtensionActionOptions === 'function'; @@ -205,7 +205,9 @@ function onMessage(request, sender, callback) { // https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/runtime/MessageSender // Firefox API does not set `sender.origin` - if ( sender.origin !== undefined && sender.origin !== UBOL_ORIGIN ) { return; } + if ( sender.origin !== undefined ) { + if ( sender.origin.toLowerCase() !== UBOL_ORIGIN ) { return; } + } switch ( request.what ) { diff --git a/platform/mv3/extension/js/debug.js b/platform/mv3/extension/js/debug.js index 0a6ecaffe70ee..ea5dafbed27fb 100644 --- a/platform/mv3/extension/js/debug.js +++ b/platform/mv3/extension/js/debug.js @@ -19,11 +19,19 @@ Home: https://github.com/gorhill/uBlock */ -import { dnr } from './ext.js'; +import { INITIATOR_DOMAINS, dnr } from './ext-compat.js'; +import { browser } from './ext.js'; /******************************************************************************/ -export const isSideloaded = dnr.onRuleMatchedDebug instanceof Object; +const isModern = dnr.onRuleMatchedDebug instanceof Object; + +export const isSideloaded = (( ) => { + if ( isModern ) { return true; } + if ( typeof dnr.getMatchedRules === 'function' ) { return true; } + const manifest = browser.runtime.getManifest(); + return manifest.permissions?.includes('declarativeNetRequestFeedback') ?? false; +})(); /******************************************************************************/ @@ -67,8 +75,8 @@ const getRuleset = async rulesetId => { if ( condition.requestDomains ) { condition.requestDomains = pruneLongLists(condition.requestDomains); } - if ( condition.initiatorDomains ) { - condition.initiatorDomains = pruneLongLists(condition.initiatorDomains); + if ( condition[INITIATOR_DOMAINS] ) { + condition[INITIATOR_DOMAINS] = pruneLongLists(condition[INITIATOR_DOMAINS]); } } const ruleId = rule.id; @@ -92,20 +100,32 @@ export const getMatchedRules = (( ) => { const noopFn = ( ) => Promise.resolve([]); if ( isSideloaded !== true ) { return noopFn; } - return async tabId => { - const promises = []; - for ( let i = 0; i < bufferSize; i++ ) { - const j = (writePtr + i) % bufferSize; - const ruleInfo = matchedRules[j]; - if ( ruleInfo === null ) { continue; } - if ( ruleInfo.request.tabId !== -1 ) { - if ( ruleInfo.request.tabId !== tabId ) { continue; } + if ( isModern ) { + return async tabId => { + const promises = []; + for ( let i = 0; i < bufferSize; i++ ) { + const j = (writePtr + i) % bufferSize; + const ruleInfo = matchedRules[j]; + if ( ruleInfo === null ) { continue; } + if ( ruleInfo.request.tabId !== -1 ) { + if ( ruleInfo.request.tabId !== tabId ) { continue; } + } + const promise = getRuleDetails(ruleInfo); + if ( promise === undefined ) { continue; } + promises.unshift(promise); } - const promise = getRuleDetails(ruleInfo); - if ( promise === undefined ) { continue; } - promises.unshift(promise); + return Promise.all(promises); + }; + } + + return async tabId => { + const matchedRules = await dnr.getMatchedRules({ tabId }); + if ( matchedRules instanceof Object === false ) { return []; } + const out = []; + for ( const ruleInfo of matchedRules.rulesMatchedInfo ) { + out.push({ request: ruleInfo.request }); } - return Promise.all(promises); + return out; }; })(); @@ -118,6 +138,7 @@ const matchedRuleListener = ruleInfo => { export const toggleDeveloperMode = state => { if ( isSideloaded !== true ) { return; } + if ( isModern === false ) { return; } if ( state ) { dnr.onRuleMatchedDebug.addListener(matchedRuleListener); } else { diff --git a/platform/mv3/extension/js/ext-compat.js b/platform/mv3/extension/js/ext-compat.js new file mode 100644 index 0000000000000..9e8469ce76321 --- /dev/null +++ b/platform/mv3/extension/js/ext-compat.js @@ -0,0 +1,96 @@ +/******************************************************************************* + + uBlock Origin Lite - a comprehensive, MV3-compliant content blocker + Copyright (C) 2022-present Raymond Hill + + 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/}. + + Home: https://github.com/gorhill/uBlock +*/ + +export const webext = self.browser || self.chrome; +export const dnr = webext.declarativeNetRequest; +export const INITIATOR_DOMAINS = 'initiatorDomains'; +export const EXCLUDED_INITIATOR_DOMAINS = 'excludedInitiatorDomains'; + +/******************************************************************************/ + +const ruleCompare = (a, b) => a.id - b.id; + +const isSameRules = (a, b) => { + a.sort(ruleCompare); + b.sort(ruleCompare); + return JSON.stringify(a) === JSON.stringify(b); +}; + +/******************************************************************************/ + +dnr.setAllowAllRules = async function(id, allowed, notAllowed, reverse) { + const [ + beforeDynamicRules, + beforeSessionRules, + ] = await Promise.all([ + dnr.getDynamicRules({ ruleIds: [ id+0 ] }), + dnr.getSessionRules({ ruleIds: [ id+1 ] }), + ]); + const addDynamicRules = []; + const addSessionRules = []; + if ( reverse || allowed.length || notAllowed.length ) { + const rule0 = { + id: id+0, + action: { type: 'allowAllRequests' }, + condition: { + resourceTypes: [ 'main_frame' ], + }, + priority: 1000000, + }; + if ( allowed.length ) { + rule0.condition.requestDomains = allowed.slice(); + } else if ( notAllowed.length ) { + rule0.condition.excludedRequestDomains = notAllowed.slice(); + } + addDynamicRules.push(rule0); + // https://github.com/uBlockOrigin/uBOL-home/issues/114 + // https://github.com/uBlockOrigin/uBOL-home/issues/247 + const rule1 = { + id: id+1, + action: { type: 'allow' }, + condition: { + tabIds: [ webext.tabs.TAB_ID_NONE ], + }, + priority: 1000000, + }; + if ( allowed.length ) { + rule1.condition.initiatorDomains = allowed.slice(); + } else if ( notAllowed.length ) { + rule1.condition.excludedInitiatorDomains = notAllowed.slice(); + } + addSessionRules.push(rule1); + } + if ( isSameRules(addDynamicRules, beforeDynamicRules) ) { return false; } + return Promise.all([ + dnr.updateDynamicRules({ + addRules: addDynamicRules, + removeRuleIds: beforeDynamicRules.map(r => r.id), + }), + dnr.updateSessionRules({ + addRules: addSessionRules, + removeRuleIds: beforeSessionRules.map(r => r.id), + }), + ]).then(( ) => + true + ).catch(( ) => + false + ); +}; diff --git a/platform/mv3/extension/js/ext.js b/platform/mv3/extension/js/ext.js index 2f3285388d62f..ee735b21be30b 100644 --- a/platform/mv3/extension/js/ext.js +++ b/platform/mv3/extension/js/ext.js @@ -19,16 +19,13 @@ Home: https://github.com/gorhill/uBlock */ -export const browser = - self.browser instanceof Object && - self.browser instanceof Element === false - ? self.browser - : self.chrome; +import { webext } from './ext-compat.js'; -export const dnr = browser.declarativeNetRequest; +/******************************************************************************/ + +export const browser = webext; export const i18n = browser.i18n; export const runtime = browser.runtime; -export const TAB_ID_NONE = browser.tabs.TAB_ID_NONE; export const windows = browser.windows; /******************************************************************************/ diff --git a/platform/mv3/extension/js/popup.js b/platform/mv3/extension/js/popup.js index a50c889b6ac52..2cb89045b9467 100644 --- a/platform/mv3/extension/js/popup.js +++ b/platform/mv3/extension/js/popup.js @@ -347,6 +347,7 @@ async function init() { } tabURL.href = url.href || ''; } catch { + return false; } if ( url !== undefined ) { diff --git a/platform/mv3/extension/js/report.js b/platform/mv3/extension/js/report.js index 1350edcd40e6d..eda30f97fa8b8 100644 --- a/platform/mv3/extension/js/report.js +++ b/platform/mv3/extension/js/report.js @@ -19,8 +19,9 @@ Home: https://github.com/gorhill/uBlock */ -import { dnr, runtime } from './ext.js'; import { dom, qs$ } from './dom.js'; +import { dnr } from './ext-compat.js'; +import { runtime } from './ext.js'; import { sendMessage } from './ext.js'; /******************************************************************************/ diff --git a/platform/mv3/extension/js/ruleset-manager.js b/platform/mv3/extension/js/ruleset-manager.js index add3c5e25bce6..ae5ef7f32abdb 100644 --- a/platform/mv3/extension/js/ruleset-manager.js +++ b/platform/mv3/extension/js/ruleset-manager.js @@ -20,8 +20,6 @@ */ import { - TAB_ID_NONE, - dnr, i18n, localRead, localRemove, localWrite, runtime, @@ -33,6 +31,7 @@ import { saveRulesetConfig, } from './config.js'; +import { dnr } from './ext-compat.js'; import { fetchJSON } from './fetch.js'; import { getAdminRulesets } from './admin.js'; import { hasBroadHostPermissions } from './utils.js'; @@ -440,124 +439,30 @@ async function updateSessionRules() { /******************************************************************************/ async function filteringModesToDNR(modes) { - const [ - dynamicRules, - sessionRules, - ] = await Promise.all([ - dnr.getDynamicRules({ ruleIds: [ TRUSTED_DIRECTIVE_BASE_RULE_ID+0 ] }), - dnr.getSessionRules({ ruleIds: [ TRUSTED_DIRECTIVE_BASE_RULE_ID+1 ] }), - ]); - const dynamicRule = dynamicRules?.length && dynamicRules[0] || undefined; - const beforeRequestDomainSet = new Set(dynamicRule?.condition.requestDomains); - const beforeExcludedRrequestDomainSet = new Set(dynamicRule?.condition.excludedRequestDomains); - if ( dynamicRule !== undefined && beforeRequestDomainSet.size === 0 ) { - beforeRequestDomainSet.add('all-urls'); - } else { - beforeExcludedRrequestDomainSet.add('all-urls'); - } - const noneHostnames = new Set([ ...modes.none ]); const notNoneHostnames = new Set([ ...modes.basic, ...modes.optimal, ...modes.complete ]); - let afterRequestDomainSet = new Set(); - let afterExcludedRequestDomainSet = new Set(); - if ( noneHostnames.has('all-urls') ) { - afterRequestDomainSet = new Set([ 'all-urls' ]); - afterExcludedRequestDomainSet = notNoneHostnames; + const requestDomains = []; + const excludedRequestDomains = []; + const allowEverywhere = noneHostnames.has('all-urls'); + if ( allowEverywhere ) { + excludedRequestDomains.push(...notNoneHostnames); } else { - afterRequestDomainSet = noneHostnames; - afterExcludedRequestDomainSet = new Set(); - } - - const removeDynamicRuleIds = []; - const removeSessionRuleIds = []; - if ( dynamicRule ) { - removeDynamicRuleIds.push(TRUSTED_DIRECTIVE_BASE_RULE_ID+0); - removeSessionRuleIds.push(TRUSTED_DIRECTIVE_BASE_RULE_ID+1); - } - - const allowEverywhere = afterRequestDomainSet.delete('all-urls'); - const addDynamicRules = []; - const addSessionRules = []; - if ( - allowEverywhere || - afterRequestDomainSet.size !== 0 || - afterExcludedRequestDomainSet.size !== 0 - ) { - const rule0 = { - id: TRUSTED_DIRECTIVE_BASE_RULE_ID+0, - action: { type: 'allowAllRequests' }, - condition: { - resourceTypes: [ 'main_frame' ], - }, - priority: 100, - }; - if ( afterRequestDomainSet.size !== 0 ) { - rule0.condition.requestDomains = - Array.from(afterRequestDomainSet).sort(); - } else if ( afterExcludedRequestDomainSet.size !== 0 ) { - rule0.condition.excludedRequestDomains = - Array.from(afterExcludedRequestDomainSet).sort(); - } - addDynamicRules.push(rule0); - // https://github.com/uBlockOrigin/uBOL-home/issues/114 - // https://github.com/uBlockOrigin/uBOL-home/issues/247 - const rule1 = { - id: TRUSTED_DIRECTIVE_BASE_RULE_ID+1, - action: { type: 'allow' }, - condition: { - tabIds: [ TAB_ID_NONE ], - }, - priority: 100, - }; - if ( rule0.condition.requestDomains ) { - rule1.condition.initiatorDomains = - rule0.condition.requestDomains.slice(); - } else if ( rule0.condition.excludedRequestDomains ) { - rule1.condition.excludedInitiatorDomains = - rule0.condition.excludedRequestDomains.slice(); - } - addSessionRules.push(rule1); + requestDomains.push(...noneHostnames); } - - const noneCount = noneHostnames.has('all-urls') - ? -notNoneHostnames.size + const noneCount = allowEverywhere + ? notNoneHostnames.size : noneHostnames.size; - - const promises = []; - if ( isDifferentAllowRules(addDynamicRules, dynamicRules) ) { - promises.push(dnr.updateDynamicRules({ - addRules: addDynamicRules, - removeRuleIds: removeDynamicRuleIds, - })); - ubolLog(`Add "allowAllRequests" dynamic rule for ${noneCount} sites`); - } - if ( isDifferentAllowRules(addSessionRules, sessionRules) ) { - promises.push(dnr.updateSessionRules({ - addRules: addSessionRules, - removeRuleIds: removeSessionRuleIds, - })); - ubolLog(`Add "allow" session rule for ${noneCount} sites`); - } - if ( promises.length === 0 ) { return; } - return Promise.all(promises); + return dnr.setAllowAllRules( + TRUSTED_DIRECTIVE_BASE_RULE_ID, + requestDomains.sort(), + excludedRequestDomains.sort(), + allowEverywhere + ).then(modified => { + if ( modified === false ) { return; } + ubolLog(`${allowEverywhere ? 'Enabled' : 'Disabled'} DNR filtering for ${noneCount} sites`); + }); } -const isDifferentAllowRules = (a = [], b = []) => { - if ( a.length !== b.length ) { return true; } - const pp = [ - 'requestDomains', - 'excludedRequestDomains', - 'initiatorDomains', - 'excludedInitiatorDomains', - ]; - for ( const p of pp ) { - const ac = a.length && a[0].condition[p] || []; - const bc = b.length && b[0].condition[p] || []; - if ( ac.join() !== bc.join() ) { return true; } - } - return false; -}; - /******************************************************************************/ async function defaultRulesetsFromLanguage() { diff --git a/platform/mv3/extension/js/scripting-manager.js b/platform/mv3/extension/js/scripting-manager.js index e22a7c53092d8..8ff9a5c368044 100644 --- a/platform/mv3/extension/js/scripting-manager.js +++ b/platform/mv3/extension/js/scripting-manager.js @@ -68,7 +68,9 @@ const arrayEq = (a = [], b = [], sort = true) => { const normalizeMatches = matches => { if ( matches.length <= 1 ) { return; } - if ( matches.includes('') === false ) { return; } + if ( matches.includes('') === false ) { + if ( matches.includes('*://*/*') === false ) { return; } + } matches.length = 0; matches.push(''); }; @@ -555,6 +557,9 @@ function registerScriptlet(context, scriptletDetails) { /******************************************************************************/ +// Issue: Safari appears to completely ignore excludeMatches +// https://github.com/radiolondra/ExcludeMatches-Test + async function registerInjectables() { if ( browser.scripting === undefined ) { return false; } diff --git a/platform/mv3/extension/js/scripting/zapper.js b/platform/mv3/extension/js/scripting/zapper.js index fb37ee1cf942f..5a0e81c58c403 100644 --- a/platform/mv3/extension/js/scripting/zapper.js +++ b/platform/mv3/extension/js/scripting/zapper.js @@ -28,11 +28,13 @@ const zapper = self.uBOLZapper = self.uBOLZapper || {}; if ( zapper.injected ) { return; } zapper.injected = true; +const webext = typeof browser === 'object' ? browser : chrome; + /******************************************************************************/ const sendMessage = msg => { try { - chrome.runtime.sendMessage(msg).catch(( ) => { }); + webext.runtime.sendMessage(msg).catch(( ) => { }); } catch { } }; @@ -365,11 +367,10 @@ const onFrameMessage = function(msg) { // can remove the iframe. const bootstrap = async ( ) => { - const dynamicURL = new URL(chrome.runtime.getURL('/zapper-ui.html')); + const dynamicURL = new URL(webext.runtime.getURL('/zapper-ui.html')); return new Promise(resolve => { const frame = document.createElement('iframe'); frame.setAttribute(zapperSecret, ''); - document.documentElement.append(frame); frame.onload = ( ) => { frame.onload = null; frame.setAttribute(`${zapperSecret}-loaded`, ''); @@ -382,7 +383,7 @@ const bootstrap = async ( ) => { quitZapper(); }; const realURL = new URL(dynamicURL); - realURL.hostname = chrome.i18n.getMessage('@@extension_id'); + realURL.hostname = webext.i18n.getMessage('@@extension_id'); frame.contentWindow.postMessage( { what: 'zapperStart' }, realURL.origin, @@ -394,7 +395,13 @@ const bootstrap = async ( ) => { zapperFramePort: port, }); }; - frame.contentWindow.location = dynamicURL.href; + if ( dynamicURL.protocol !== 'safari-web-extension:' ) { + document.documentElement.append(frame); + frame.contentWindow.location = dynamicURL.href; + } else { + frame.setAttribute('src', dynamicURL.href); + document.documentElement.append(frame); + } }); }; diff --git a/platform/mv3/extension/js/utils.js b/platform/mv3/extension/js/utils.js index ff96e9ee63cb9..00451f15f9b29 100644 --- a/platform/mv3/extension/js/utils.js +++ b/platform/mv3/extension/js/utils.js @@ -116,7 +116,7 @@ const matchesFromHostnames = hostnames => { const hostnamesFromMatches = origins => { const out = []; for ( const origin of origins ) { - if ( origin === '' ) { + if ( origin === '' || origin === '*://*/*' ) { out.push('all-urls'); continue; } @@ -141,7 +141,10 @@ const broadcastMessage = message => { // most browsers treat host_permissions as optional." async function hasBroadHostPermissions() { - return browser.permissions.contains({ origins: [ '' ] }); + return browser.permissions.getAll().then(permissions => + permissions.origins.includes('') || + permissions.origins.includes('*://*/*') + ).catch(( ) => false); } /******************************************************************************/ diff --git a/platform/mv3/make-rulesets.js b/platform/mv3/make-rulesets.js index 930aa99b7162e..b69e27a598c84 100644 --- a/platform/mv3/make-rulesets.js +++ b/platform/mv3/make-rulesets.js @@ -71,7 +71,7 @@ const env = [ 'user_stylesheet', ]; -if ( platform === 'edge' ) { +if ( platform === 'edge' || platform === 'safari' ) { env.push('chromium'); } @@ -318,6 +318,38 @@ const isURLSkip = rule => /******************************************************************************/ +function patchRuleset(ruleset) { + if ( platform !== 'safari' ) { return ruleset; } + const out = []; + for ( const rule of ruleset ) { + const condition = rule.condition; + if ( rule.action.type === 'modifyHeaders' ) { + log(`Safari's incomplete API: ${JSON.stringify(rule)}`, true); + continue; + } + if ( Array.isArray(condition.requestMethods) ) { + log(`Safari's incomplete API: ${JSON.stringify(rule)}`, true); + continue; + } + if ( Array.isArray(condition.excludedRequestMethods) ) { + log(`Safari's incomplete API: ${JSON.stringify(rule)}`, true); + continue; + } + if ( Array.isArray(condition.initiatorDomains) ) { + condition.domains = condition.initiatorDomains; + delete condition.initiatorDomains; + } + if ( Array.isArray(condition.excludedInitiatorDomains) ) { + condition.excludedDomains = condition.excludedInitiatorDomains; + delete condition.excludedInitiatorDomains; + } + out.push(rule); + } + return out; +} + +/******************************************************************************/ + // Two distinct hostnames: // www.example.com // example.com @@ -489,7 +521,9 @@ async function processNetworkFilters(assetDetails, network) { } } - const plainGood = rules.filter(rule => isSafe(rule) && isRegex(rule) === false); + const plainGood = patchRuleset( + rules.filter(rule => isSafe(rule) && isRegex(rule) === false) + ); log(`\tPlain good: ${plainGood.length}`); log(plainGood .filter(rule => Array.isArray(rule._warning)) @@ -497,12 +531,16 @@ async function processNetworkFilters(assetDetails, network) { .join('\n'), true ); - const regexes = rules.filter(rule => isSafe(rule) && isRegex(rule)); + const regexes = patchRuleset( + rules.filter(rule => isSafe(rule) && isRegex(rule)) + ); log(`\tMaybe good (regexes): ${regexes.length}`); - const redirects = rules.filter(rule => - isUnsupported(rule) === false && - isRedirect(rule) + const redirects = patchRuleset( + rules.filter(rule => + isUnsupported(rule) === false && + isRedirect(rule) + ) ); redirects.forEach(rule => { if ( rule.action.redirect.extensionPath === undefined ) { return; } @@ -512,17 +550,23 @@ async function processNetworkFilters(assetDetails, network) { }); log(`\tredirect=: ${redirects.length}`); - const removeparamsGood = rules.filter(rule => - isUnsupported(rule) === false && isRemoveparam(rule) + const removeparamsGood = patchRuleset( + rules.filter(rule => + isUnsupported(rule) === false && isRemoveparam(rule) + ) ); - const removeparamsBad = rules.filter(rule => - isUnsupported(rule) && isRemoveparam(rule) + const removeparamsBad = patchRuleset( + rules.filter(rule => + isUnsupported(rule) && isRemoveparam(rule) + ) ); log(`\tremoveparams= (accepted/discarded): ${removeparamsGood.length}/${removeparamsBad.length}`); - const modifyHeaders = rules.filter(rule => - isUnsupported(rule) === false && - isModifyHeaders(rule) + const modifyHeaders = patchRuleset( + rules.filter(rule => + isUnsupported(rule) === false && + isModifyHeaders(rule) + ) ); log(`\tmodifyHeaders=: ${modifyHeaders.length}`); @@ -1401,10 +1445,10 @@ async function main() { // Patch web_accessible_resources key manifest.web_accessible_resources = manifest.web_accessible_resources || []; const web_accessible_resources = { - resources: Array.from(requiredRedirectResources).map(path => `/${path}`), + resources: Array.from(requiredRedirectResources).map(path => `${path}`), matches: [ '' ], }; - if ( env.includes('chromium') ) { + if ( env.includes('chromium') && env.includes('safari') === false ) { web_accessible_resources.use_dynamic_url = true; } manifest.web_accessible_resources.push(web_accessible_resources); diff --git a/platform/mv3/safari/ext-compat.js b/platform/mv3/safari/ext-compat.js new file mode 100644 index 0000000000000..c7f02984d0bbb --- /dev/null +++ b/platform/mv3/safari/ext-compat.js @@ -0,0 +1,131 @@ +/******************************************************************************* + + uBlock Origin Lite - a comprehensive, MV3-compliant content blocker + Copyright (C) 2022-present Raymond Hill + + 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/}. + + Home: https://github.com/gorhill/uBlock +*/ + + +export const webext = self.browser; +export const INITIATOR_DOMAINS = 'domains'; +export const EXCLUDED_INITIATOR_DOMAINS = 'excludedDomains'; + +/******************************************************************************/ + +// https://developer.mozilla.org/docs/Mozilla/Add-ons/WebExtensions/API/declarativeNetRequest/ + +const nativeDNR = webext.declarativeNetRequest; + +const isSupportedRule = r => { + if ( r.action?.responseHeaders ) { return false; } + if ( r.condition?.tabIds !== undefined ) { return false; } + return true; +}; + +const ruleCompare = (a, b) => a.id - b.id; + +const isSameRules = (a, b) => { + a.sort(ruleCompare); + b.sort(ruleCompare); + return JSON.stringify(a) === JSON.stringify(b); +}; + +/******************************************************************************/ + +export const dnr = { + DYNAMIC_RULESET_ID: '_dynamic', + MAX_NUMBER_OF_ENABLED_STATIC_RULESETS: nativeDNR.MAX_NUMBER_OF_ENABLED_STATIC_RULESETS, + MAX_NUMBER_OF_REGEX_RULES: nativeDNR.MAX_NUMBER_OF_DYNAMIC_AND_SESSION_RULES, + async getAvailableStaticRuleCount() { + return 150000; + }, + getDynamicRules({ ruleIds } = {}) { + return new Promise(resolve => { + nativeDNR.getDynamicRules(rules => { + if ( Array.isArray(rules) === false ) { return resolve([]); } + if ( Array.isArray(ruleIds) === false ) { return resolve(rules); } + return resolve(rules.filter(rule => ruleIds.includes(rule.id))); + }); + }); + }, + getEnabledRulesets(...args) { + return nativeDNR.getEnabledRulesets(...args); + }, + getMatchedRules(...args) { + return nativeDNR.getMatchedRules(...args); + }, + getSessionRules({ ruleIds } = {}) { + return new Promise(resolve => { + nativeDNR.getSessionRules(rules => { + if ( Array.isArray(rules) === false ) { return resolve([]); } + if ( Array.isArray(ruleIds) === false ) { return resolve(rules); } + return resolve(rules.filter(rule => ruleIds.includes(rule.id))); + }); + }); + }, + isRegexSupported(...args) { + return nativeDNR.isRegexSupported(...args); + }, + async updateDynamicRules(optionsBefore) { + const { addRules, removeRuleIds } = optionsBefore; + const addRulesAfter = addRules?.filter(isSupportedRule); + if ( Boolean(addRulesAfter?.length || removeRuleIds?.length) === false ) { return; } + const optionsAfter = {}; + if ( addRulesAfter?.length ) { optionsAfter.addRules = addRulesAfter; } + if ( removeRuleIds?.length ) { optionsAfter.removeRuleIds = removeRuleIds; } + return nativeDNR.updateDynamicRules(optionsAfter); + }, + updateEnabledRulesets(...args) { + return nativeDNR.updateEnabledRulesets(...args); + }, + async updateSessionRules(optionsBefore) { + const { addRules, removeRuleIds } = optionsBefore; + const addRulesAfter = addRules?.filter(isSupportedRule); + if ( Boolean(addRulesAfter?.length || removeRuleIds?.length) === false ) { return; } + const optionsAfter = {}; + if ( optionsAfter?.length ) { optionsAfter.addRules = addRulesAfter; } + if ( removeRuleIds?.length ) { optionsAfter.removeRuleIds = removeRuleIds; } + return nativeDNR.updateSessionRules(optionsAfter); + }, + async setAllowAllRules(id, allowed, notAllowed, reverse) { + const beforeRules = await this.getDynamicRules({ ruleIds: [ id+0 ] }); + const addRules = []; + if ( reverse || allowed.length || notAllowed.length ) { + const rule0 = { + id: id+0, + action: { type: 'allow' }, + condition: { urlFilter: '*' }, + priority: 1000000, + }; + if ( allowed.length ) { + rule0.condition.domains = allowed; + } else if ( notAllowed.length ) { + rule0.condition.excludedDomains = notAllowed; + } + addRules.push(rule0); + } + if ( isSameRules(addRules, beforeRules) ) { return false; } + return this.updateDynamicRules({ + addRules, + removeRuleIds: beforeRules.map(r => r.id), + }).then(( ) => + true + ).catch(( ) => + false + ); + }, +}; diff --git a/platform/mv3/safari/manifest.json b/platform/mv3/safari/manifest.json new file mode 100644 index 0000000000000..ba7cc7715f37d --- /dev/null +++ b/platform/mv3/safari/manifest.json @@ -0,0 +1,64 @@ +{ + "action": { + "default_icon": "/img/icon_64.png", + "default_popup": "popup.html" + }, + "author": "Raymond Hill", + "background": { + "scripts": [ "/js/background.js" ], + "type": "module" + }, + "commands": { + "enter-zapper-mode": { + "description": "__MSG_zapperTipEnter__" + } + }, + "declarative_net_request": { + "rule_resources": [ + ] + }, + "default_locale": "en", + "description": "__MSG_extShortDesc__", + "icons": { + "16": "/img/icon_16.png", + "32": "/img/icon_32.png", + "64": "/img/icon_64.png", + "128": "/img/icon_128.png", + "512": "/img/icon_512.png" + }, + "manifest_version": 3, + "name": "__MSG_extName__", + "options_ui": { + "page": "dashboard.html" + }, + "host_permissions": [ + "" + ], + "permissions": [ + "activeTab", + "declarativeNetRequest", + "declarativeNetRequestWithHostAccess", + "scripting", + "storage" + ], + "short_name": "uBO Lite", + "version": "1.0", + "web_accessible_resources": [ + { + "resources": [ + "strictblock.html" + ], + "matches": [ + "" + ] + }, + { + "resources": [ + "zapper-ui.html" + ], + "matches": [ + "" + ] + } + ] +} diff --git a/src/js/static-dnr-filtering.js b/src/js/static-dnr-filtering.js index df23d863c08a8..6da12ae45f228 100644 --- a/src/js/static-dnr-filtering.js +++ b/src/js/static-dnr-filtering.js @@ -437,6 +437,20 @@ function finalizeRuleset(context, network) { mergeRules(rulesetMap, 'requestDomains'); mergeRules(rulesetMap, 'responseHeaders'); + // Convert back single-entry requestDomains into pattern-based filters + // https://github.com/uBlockOrigin/uBOL-home/issues/327 + // TODO: Remove when (if) Safari is changed to interpret requestDomains as + // in other browsers. + for ( const rule of rulesetMap.values() ) { + const { condition } = rule; + if ( condition?.requestDomains === undefined ) { continue; } + if ( condition.requestDomains.length !== 1 ) { continue; } + if ( condition.urlFilter !== undefined ) { continue; } + if ( condition.regexFilter !== undefined ) { continue; } + condition.urlFilter = `||${condition.requestDomains[0]}^`; + condition.requestDomains = undefined; + } + // Patch id const rulesetFinal = []; { diff --git a/tools/make-mv3.sh b/tools/make-mv3.sh index 6d2364930df27..7a9988433eff3 100755 --- a/tools/make-mv3.sh +++ b/tools/make-mv3.sh @@ -23,6 +23,9 @@ for i in "$@"; do edge) PLATFORM="edge" ;; + safari) + PLATFORM="safari" + ;; uBOLite_+([0-9]).+([0-9]).+([0-9]).+([0-9])) TAGNAME="$i" FULL="yes" @@ -82,13 +85,12 @@ cp -R "$UBO_DIR/src/img/flags-of-the-world" "$DES"/img cp LICENSE.txt "$DES"/ echo "*** uBOLite.mv3: Copying mv3-specific files" -if [ "$PLATFORM" = "firefox" ]; then - cp platform/mv3/firefox/background.html "$DES"/ -fi +cp platform/mv3/"$PLATFORM"/manifest.json "$DES"/ cp platform/mv3/extension/*.html "$DES"/ cp platform/mv3/extension/*.json "$DES"/ cp platform/mv3/extension/css/* "$DES"/css/ cp -R platform/mv3/extension/js/* "$DES"/js/ +cp platform/mv3/"$PLATFORM"/ext-compat.js "$DES"/js/ 2>/dev/null || : cp platform/mv3/extension/img/* "$DES"/img/ cp -R platform/mv3/extension/_locales "$DES"/ cp platform/mv3/README.md "$DES/" @@ -96,11 +98,6 @@ cp platform/mv3/README.md "$DES/" echo "*** uBOLite.mv3: Generating rulesets" TMPDIR=$(mktemp -d) mkdir -p "$TMPDIR" -if [ "$PLATFORM" = "chromium" ] || [ "$PLATFORM" = "edge" ]; then - cp platform/mv3/chromium/manifest.json "$DES"/ -elif [ "$PLATFORM" = "firefox" ]; then - cp platform/mv3/firefox/manifest.json "$DES"/ -fi ./tools/make-nodejs.sh "$TMPDIR" cp platform/mv3/*.json "$TMPDIR"/ cp platform/mv3/*.js "$TMPDIR"/ From fbe1ba018ef0f9310d7df743fbec12096425d04d Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sat, 19 Apr 2025 13:34:34 -0400 Subject: [PATCH 0838/1099] [mv3] Remove unused file --- platform/mv3/firefox/background.html | 10 ---------- 1 file changed, 10 deletions(-) delete mode 100644 platform/mv3/firefox/background.html diff --git a/platform/mv3/firefox/background.html b/platform/mv3/firefox/background.html deleted file mode 100644 index 58e9c5e17757e..0000000000000 --- a/platform/mv3/firefox/background.html +++ /dev/null @@ -1,10 +0,0 @@ - - - - -uBlock Origin Background Page - - - - - From 8a14a8dc6bb8c2334627b9d3bd6cb187b6988fc0 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sat, 19 Apr 2025 14:02:32 -0400 Subject: [PATCH 0839/1099] Import translation work from https://crowdin.com/project/ublock --- platform/mv3/description/webstore.cs.txt | 2 +- platform/mv3/description/webstore.zh_CN.txt | 6 ++--- .../extension/_locales/zh_CN/messages.json | 22 +++++++++---------- src/_locales/zh_CN/messages.json | 4 ++-- 4 files changed, 17 insertions(+), 17 deletions(-) diff --git a/platform/mv3/description/webstore.cs.txt b/platform/mv3/description/webstore.cs.txt index 5046a3ac1f7de..97d87722c20dc 100644 --- a/platform/mv3/description/webstore.cs.txt +++ b/platform/mv3/description/webstore.cs.txt @@ -1,4 +1,4 @@ -uBO Lite (uBOL) is an MV3-based content blocker. +uBO Lite (uBOL) je blokovač obsahu založený na MV3. Výchozí sada pravidel koresponduje k výchozím sadám filtrů uBlock Origin: diff --git a/platform/mv3/description/webstore.zh_CN.txt b/platform/mv3/description/webstore.zh_CN.txt index 6264ca5d708fb..aeacd9841af3c 100644 --- a/platform/mv3/description/webstore.zh_CN.txt +++ b/platform/mv3/description/webstore.zh_CN.txt @@ -1,4 +1,4 @@ -uBO Lite (uBOL) is an MV3-based content blocker. +uBO Lite (uBOL) 是一个基于最新浏览器扩展接口(Manifest Version 3)打造的的内容屏蔽工具。 该扩展预设的规则列表对应 uBlock Origin 的预设过滤规则列表: @@ -7,6 +7,6 @@ uBO Lite (uBOL) is an MV3-based content blocker. - EasyPrivacy - Peter Lowe 的广告和跟踪服务器列表 -访问选项页面,点击弹出面板中的齿轮图标,即可启用更多规则集。 +访问选项页面,点击弹出面板中的 _齿轮_ 图标,即可启用更多规则集。 -uBOL 的过滤规则是完全声明式的,并不需要固定保留一个 uBOL 扩展进程,基于 CSS/JS 注入的内容过滤更是交由浏览器进行调度,比起扩展本身更为可靠。 这也即是说当内容被过滤时 uBOL 自身并不占用额外 CPU 和内存资源,只有在您打开弹出面板或是设置页面时才会生成 uBOL 扩展进程。 +uBOL 的过滤规则是完全声明式的,并不需要固定保留一个 uBOL 扩展进程,基于 CSS/JS 注入的内容过滤更是交由浏览器进行调度,比起扩展本身更为可靠。 这也即是说当内容被过滤时 uBOL 自身并不占用额外 CPU 和内存资源,_只有_在您打开弹出面板或是设置页面时才会生成 uBOL 扩展进程。 diff --git a/platform/mv3/extension/_locales/zh_CN/messages.json b/platform/mv3/extension/_locales/zh_CN/messages.json index 49b531797856a..f133d63364053 100644 --- a/platform/mv3/extension/_locales/zh_CN/messages.json +++ b/platform/mv3/extension/_locales/zh_CN/messages.json @@ -32,11 +32,11 @@ "description": "Label in the popup panel for the current filtering mode" }, "popupTipReport": { - "message": "反馈该网站上的问题", + "message": "报告此网站上的问题", "description": "Tooltip used for the 'chat' icon in the panel" }, "popupTipDashboard": { - "message": "在仪表板中", + "message": "打开控制面板", "description": "English: Click to open the dashboard" }, "popupMoreButton": { @@ -48,7 +48,7 @@ "description": "Label to be used to hide popup panel sections" }, "3pGroupDefault": { - "message": "预设", + "message": "默认", "description": "Header for a ruleset section in 'Filter lists pane'" }, "3pGroupAds": { @@ -144,7 +144,7 @@ "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option3": { - "message": "检测到 uBO Lite", + "message": "检测 uBO Lite", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option4": { @@ -208,15 +208,15 @@ "description": "This describes the 'complete' filtering mode" }, "noFilteringModeDescription": { - "message": "不进行过滤的主机名列表", + "message": "不进行过滤的网站列表。", "description": "A short description for the editable field which lists trusted sites" }, "noFilteringModePlaceholder": { - "message": "[域名]\nexample.com\ngames.example\n...", + "message": "[仅限主机名]\nexample.com\ngames.example\n...", "description": "Default text for in edit field" }, "behaviorSectionLabel": { - "message": "操作设置", + "message": "行为", "description": "The header text for the 'Behavior' section" }, "autoReloadLabel": { @@ -244,7 +244,7 @@ "description": "Webpage title for the strict-blocked page" }, "strictblockSentence1": { - "message": "uBO Lite 已阻止了以下页面的加载:", + "message": "uBO Lite 已阻止加载以下页面:", "description": "Sentence used in the strict-blocked page" }, "strictblockReasonSentence1": { @@ -252,7 +252,7 @@ "description": "Text informing about what is causing the page to be blocked" }, "strictblockRedirectSentence1": { - "message": "被屏蔽的页面希望重定向到另一个网站。如果您选择继续,将直接导航到:{{url}}", + "message": "被拦截的页面将重定向至其他网站。如果您选择继续,将直接导航至:{{url}}", "description": "Text warning about an incoming redirect" }, "strictblockNoParamsPrompt": { @@ -268,7 +268,7 @@ "description": "A button to close the current tab" }, "strictblockDontWarn": { - "message": "不要再警告我关于这个网站了", + "message": "不要再警告我有关此网站", "description": "Label for checkbox in document-blocked page" }, "strictblockProceed": { @@ -280,7 +280,7 @@ "description": "Tooltip for the button used to enter zapper mode" }, "zapperTipQuit": { - "message": "进入临时元素移除模式", + "message": "退出临时元素移除模式", "description": "Tooltip for the button used to exit zapper mode" } } diff --git a/src/_locales/zh_CN/messages.json b/src/_locales/zh_CN/messages.json index 651c4224d6862..25665ede6140c 100644 --- a/src/_locales/zh_CN/messages.json +++ b/src/_locales/zh_CN/messages.json @@ -908,7 +908,7 @@ "description": "A clickable link in the filter issue reporter section" }, "supportS1H": { - "message": "使用说明", + "message": "文档", "description": "Header of 'Documentation' section in Support pane" }, "supportS1P1": { @@ -1032,7 +1032,7 @@ "description": "Link to privacy policy on GitHub (English)" }, "aboutChangelog": { - "message": "变更日志", + "message": "更新日志", "description": "" }, "aboutCode": { From 39913a8212d483311d2734db93aac434bcafc066 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sat, 19 Apr 2025 14:17:46 -0400 Subject: [PATCH 0840/1099] Fix Edge build script --- tools/make-mv3.sh | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/tools/make-mv3.sh b/tools/make-mv3.sh index 7a9988433eff3..5cc0d4df3df14 100755 --- a/tools/make-mv3.sh +++ b/tools/make-mv3.sh @@ -42,6 +42,12 @@ echo "BEFORE=$BEFORE" DES="dist/build/uBOLite.$PLATFORM" +if [ "$PLATFORM" = "edge" ]; then + MANIFEST_DIR="chromium" +else + MANIFEST_DIR="$PLATFORM" +fi + rm -rf $DES mkdir -p $DES @@ -85,7 +91,7 @@ cp -R "$UBO_DIR/src/img/flags-of-the-world" "$DES"/img cp LICENSE.txt "$DES"/ echo "*** uBOLite.mv3: Copying mv3-specific files" -cp platform/mv3/"$PLATFORM"/manifest.json "$DES"/ +cp platform/mv3/"$MANIFEST_DIR"/manifest.json "$DES"/ cp platform/mv3/extension/*.html "$DES"/ cp platform/mv3/extension/*.json "$DES"/ cp platform/mv3/extension/css/* "$DES"/css/ From 39b505a52e5d5cb26b307350ff2c9194f1d543e7 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sat, 19 Apr 2025 14:39:00 -0400 Subject: [PATCH 0841/1099] [mv3] Fix safari build script --- platform/mv3/salvage-ruleids.mjs | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/platform/mv3/salvage-ruleids.mjs b/platform/mv3/salvage-ruleids.mjs index f11d986e76855..c272597b9b420 100644 --- a/platform/mv3/salvage-ruleids.mjs +++ b/platform/mv3/salvage-ruleids.mjs @@ -19,8 +19,6 @@ Home: https://github.com/gorhill/uBlock */ -'use strict'; - /******************************************************************************/ import fs from 'fs/promises'; @@ -64,15 +62,16 @@ async function main() { ]; const writePromises = []; for ( const folder of folders ) { - const afterFiles = await fs.readdir(`${afterDir}/rulesets/${folder}`); + const afterFiles = await fs.readdir(`${afterDir}/rulesets/${folder}`).catch(( ) => { }); + if ( afterFiles === undefined ) { continue; } for ( const file of afterFiles ) { let raw = await fs.readFile(`${beforeDir}/rulesets/${folder}/${file}`, 'utf-8').catch(( ) => ''); let beforeRules; - try { beforeRules = JSON.parse(raw); } catch(_) { } + try { beforeRules = JSON.parse(raw); } catch { } if ( Array.isArray(beforeRules) === false ) { continue; } raw = await fs.readFile(`${afterDir}/rulesets/${folder}/${file}`, 'utf-8').catch(( ) => ''); let afterRules; - try { afterRules = JSON.parse(raw); } catch(_) { } + try { afterRules = JSON.parse(raw); } catch { } if ( Array.isArray(afterRules) === false ) { continue; } const beforeMap = new Map(beforeRules.map(a => { const id = a.id; From de7a40692093721f31de11e793788c3612b0ba67 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sat, 19 Apr 2025 17:52:28 -0400 Subject: [PATCH 0842/1099] [mv3] Minor code review --- platform/mv3/extension/js/background.js | 3 ++- platform/mv3/extension/js/config.js | 14 ++------------ 2 files changed, 4 insertions(+), 13 deletions(-) diff --git a/platform/mv3/extension/js/background.js b/platform/mv3/extension/js/background.js index bc2ba932b53a6..b01e7286178ea 100644 --- a/platform/mv3/extension/js/background.js +++ b/platform/mv3/extension/js/background.js @@ -464,7 +464,8 @@ async function start() { } // Permissions may have been removed while the extension was disabled - const permissionsChanged = await onPermissionsRemoved(); + const permissionsChanged = process.wakeupRun === false && + await onPermissionsRemoved(); // Unsure whether the browser remembers correctly registered css/scripts // after we quit the browser. For now uBOL will check unconditionally at diff --git a/platform/mv3/extension/js/config.js b/platform/mv3/extension/js/config.js index 64e2f3e9b7927..0986f73397e38 100644 --- a/platform/mv3/extension/js/config.js +++ b/platform/mv3/extension/js/config.js @@ -46,23 +46,13 @@ export const process = { export async function loadRulesetConfig() { const sessionData = await sessionRead('rulesetConfig'); if ( sessionData ) { - rulesetConfig.version = sessionData.version; - rulesetConfig.enabledRulesets = sessionData.enabledRulesets; - rulesetConfig.autoReload = sessionData.autoReload ?? true; - rulesetConfig.showBlockedCount = sessionData.showBlockedCount ?? true; - rulesetConfig.strictBlockMode = sessionData.strictBlockMode ?? true; - rulesetConfig.developerMode = sessionData.developerMode ?? false; + Object.assign(rulesetConfig, sessionData) process.wakeupRun = true; return; } const localData = await localRead('rulesetConfig'); if ( localData ) { - rulesetConfig.version = localData.version; - rulesetConfig.enabledRulesets = localData.enabledRulesets; - rulesetConfig.autoReload = localData.autoReload ?? true; - rulesetConfig.showBlockedCount = localData.showBlockedCount ?? true; - rulesetConfig.strictBlockMode = localData.strictBlockMode ?? true; - rulesetConfig.developerMode = localData.developerMode ?? false; + Object.assign(rulesetConfig, localData) sessionWrite('rulesetConfig', rulesetConfig); return; } From 2bd09e6f84a1b7e3acb16640604674b873c43001 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sun, 20 Apr 2025 12:14:29 -0400 Subject: [PATCH 0843/1099] [mv3] Code review of conversion to DNR ruleset - Ensure no duplicates in domain collection fields - Sort rules according to the number of entries in their domain collections --- platform/mv3/make-rulesets.js | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/platform/mv3/make-rulesets.js b/platform/mv3/make-rulesets.js index b69e27a598c84..6ebadfcdc6ee4 100644 --- a/platform/mv3/make-rulesets.js +++ b/platform/mv3/make-rulesets.js @@ -405,6 +405,23 @@ function pruneHostnameArray(hostnames) { * */ function toJSONRuleset(ruleset) { + const nodupProps = [ 'domains', 'excludedDomains', 'requestDomains', 'excludedRequestDomains', 'initiatorDomains', 'excludedInitiatorDomains' ]; + for ( const { condition } of ruleset ) { + if ( condition === undefined ) { continue; } + for ( const prop of nodupProps ) { + if ( condition[prop] === undefined ) { continue; } + condition[prop] = Array.from(new Set(condition[prop])); + } + } + const sortProps = [ 'requestDomains', 'initiatorDomains', 'domains' ]; + ruleset.sort((a, b) => { + let aLen = 0, bLen = 0; + for ( const prop of sortProps ) { + aLen += a.condition[prop]?.length ?? 0; + bLen += b.condition[prop]?.length ?? 0; + } + return bLen - aLen; + }); const replacer = (k, v) => { if ( k.startsWith('_') ) { return; } if ( Array.isArray(v) ) { @@ -421,7 +438,9 @@ function toJSONRuleset(ruleset) { }; const indent = ruleset.length > 10 ? undefined : 1; const out = []; + let id = 1; for ( const rule of ruleset ) { + rule.id = id++; out.push(JSON.stringify(rule, replacer, indent)); } return `[\n${out.join(',\n')}\n]\n`; @@ -651,10 +670,6 @@ async function processNetworkFilters(assetDetails, network) { } if ( strictBlocked.size !== 0 ) { mergeRules(strictBlocked, 'requestDomains'); - let id = 1; - for ( const rule of strictBlocked.values() ) { - rule.id = id++; - } writeFile(`${rulesetDir}/strictblock/${assetDetails.id}.json`, toJSONRuleset(Array.from(strictBlocked.values())) ); From 782fff35ea68687b041e995b825dcd120cc334cb Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sun, 20 Apr 2025 12:17:57 -0400 Subject: [PATCH 0844/1099] [mv3] Split code path for wakeup versus launch status Additionally, leave more regex rooms for non-strictblock rules. --- platform/mv3/extension/js/background.js | 39 +++++++++----------- platform/mv3/extension/js/ruleset-manager.js | 13 ++++++- 2 files changed, 28 insertions(+), 24 deletions(-) diff --git a/platform/mv3/extension/js/background.js b/platform/mv3/extension/js/background.js index b01e7286178ea..6af564822ac0a 100644 --- a/platform/mv3/extension/js/background.js +++ b/platform/mv3/extension/js/background.js @@ -436,9 +436,7 @@ function onCommand(command, tab) { /******************************************************************************/ -async function start() { - await loadRulesetConfig(); - +async function launch() { const currentVersion = getCurrentVersion(); const isNewVersion = currentVersion !== rulesetConfig.version; @@ -451,39 +449,28 @@ async function start() { saveRulesetConfig(); } - const rulesetsUpdated = process.wakeupRun === false && - await enableRulesets(rulesetConfig.enabledRulesets); + const rulesetsUpdated = await enableRulesets(rulesetConfig.enabledRulesets); // We need to update the regex rules only when ruleset version changes. if ( rulesetsUpdated === false ) { if ( isNewVersion ) { updateDynamicRules(); - } else if ( process.wakeupRun === false ) { + } else { updateSessionRules(); } } // Permissions may have been removed while the extension was disabled - const permissionsChanged = process.wakeupRun === false && - await onPermissionsRemoved(); + await syncWithBrowserPermissions(); // Unsure whether the browser remembers correctly registered css/scripts // after we quit the browser. For now uBOL will check unconditionally at // launch time whether content css/scripts are properly registered. - if ( process.wakeupRun === false || permissionsChanged ) { - registerInjectables(); - - const enabledRulesets = await dnr.getEnabledRulesets(); - ubolLog(`Enabled rulesets: ${enabledRulesets}`); - - dnr.getAvailableStaticRuleCount().then(count => { - ubolLog(`Available static rule count: ${count}`); - }); - } + registerInjectables(); // https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/declarativeNetRequest // Firefox API does not support `dnr.setExtensionActionOptions` - if ( process.wakeupRun === false && canShowBlockedCount ) { + if ( canShowBlockedCount ) { dnr.setExtensionActionOptions({ displayActionCountAsBadgeText: rulesetConfig.showBlockedCount, }); @@ -502,12 +489,20 @@ async function start() { } } - toggleDeveloperMode(rulesetConfig.developerMode); - // Required to ensure the up to date property is available when needed + adminReadEx('disabledFeatures'); +} + +/******************************************************************************/ + +async function start() { + await loadRulesetConfig(); + if ( process.wakeupRun === false ) { - adminReadEx('disabledFeatures'); + await launch(); } + + toggleDeveloperMode(rulesetConfig.developerMode); } /******************************************************************************/ diff --git a/platform/mv3/extension/js/ruleset-manager.js b/platform/mv3/extension/js/ruleset-manager.js index ae5ef7f32abdb..7d717c4c47675 100644 --- a/platform/mv3/extension/js/ruleset-manager.js +++ b/platform/mv3/extension/js/ruleset-manager.js @@ -408,7 +408,7 @@ async function updateSessionRules() { const currentRules = await dnr.getSessionRules(); await updateStrictBlockRules(currentRules, addRulesUnfiltered, removeRuleIds); if ( addRulesUnfiltered.length === 0 && removeRuleIds.length === 0 ) { return; } - const maxRegexCount = dnr.MAX_NUMBER_OF_REGEX_RULES * 0.95; + const maxRegexCount = dnr.MAX_NUMBER_OF_REGEX_RULES * 0.80; let regexCount = dynamicRegexCount; let ruleId = 1; for ( const rule of addRulesUnfiltered ) { @@ -595,10 +595,19 @@ async function enableRulesets(ids) { if ( disableRulesetIds.length !== 0 ) { ubolLog(`Disable ruleset: ${disableRulesetIds}`); } - await dnr.updateEnabledRulesets({ enableRulesetIds, disableRulesetIds }); + await dnr.updateEnabledRulesets({ enableRulesetIds, disableRulesetIds }).catch(reason => { + ubolLog(reason); + }); await updateDynamicRules(); + dnr.getEnabledRulesets().then(enabledRulesets => { + ubolLog(`Enabled rulesets: ${enabledRulesets}`); + return dnr.getAvailableStaticRuleCount(); + }).then(count => { + ubolLog(`Available static rule count: ${count}`); + }); + return true; } From d0e32a5f47809e417e8cddab57dbff5c2ae844f5 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sun, 20 Apr 2025 16:44:08 -0400 Subject: [PATCH 0845/1099] [mv3] Programmatically inject content scripts on version change Related issue: https://github.com/w3c/webextensions/issues/617 --- platform/mv3/extension/js/background.js | 7 +- platform/mv3/extension/js/ext.js | 6 ++ .../mv3/extension/js/scripting-manager.js | 92 ++++++++++++++++++- 3 files changed, 96 insertions(+), 9 deletions(-) diff --git a/platform/mv3/extension/js/background.js b/platform/mv3/extension/js/background.js index 6af564822ac0a..5afa975f2bbc0 100644 --- a/platform/mv3/extension/js/background.js +++ b/platform/mv3/extension/js/background.js @@ -463,10 +463,9 @@ async function launch() { // Permissions may have been removed while the extension was disabled await syncWithBrowserPermissions(); - // Unsure whether the browser remembers correctly registered css/scripts - // after we quit the browser. For now uBOL will check unconditionally at - // launch time whether content css/scripts are properly registered. - registerInjectables(); + // Ensure that scriplets are registered. Force-execute them when a new + // version is detected. + registerInjectables(isNewVersion); // https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/declarativeNetRequest // Firefox API does not support `dnr.setExtensionActionOptions` diff --git a/platform/mv3/extension/js/ext.js b/platform/mv3/extension/js/ext.js index ee735b21be30b..1b95b11ecb094 100644 --- a/platform/mv3/extension/js/ext.js +++ b/platform/mv3/extension/js/ext.js @@ -27,6 +27,12 @@ export const browser = webext; export const i18n = browser.i18n; export const runtime = browser.runtime; export const windows = browser.windows; +export const vendor = (( ) => { + const url = browser.runtime.getURL(''); + const pos = url.indexOf(':'); + if ( pos === -1 ) { return ''; } + return url.slice(0, pos); +})(); /******************************************************************************/ diff --git a/platform/mv3/extension/js/scripting-manager.js b/platform/mv3/extension/js/scripting-manager.js index 8ff9a5c368044..37c754e346d63 100644 --- a/platform/mv3/extension/js/scripting-manager.js +++ b/platform/mv3/extension/js/scripting-manager.js @@ -21,7 +21,7 @@ import * as ut from './utils.js'; -import { browser } from './ext.js'; +import { browser, vendor } from './ext.js'; import { fetchJSON } from './fetch.js'; import { getEnabledRulesetsDetails } from './ruleset-manager.js'; import { getFilteringModeDetails } from './mode-manager.js'; @@ -168,6 +168,7 @@ function registerHighGeneric(context, genericDetails) { // update if ( + context.isNewVersion || arrayEq(registered.css, css, false) === false || arrayEq(registered.matches, matches) === false || arrayEq(registered.excludeMatches, excludeMatches) === false @@ -234,6 +235,7 @@ function registerGeneric(context, genericDetails) { arrayEq(registered.js, js, false) === false || arrayEq(registered.matches, directive.matches) === false ) { + context.isNewVersion || context.toRemove.push('css-generic-some'); context.toAdd.push(directive); } @@ -260,6 +262,7 @@ function registerGeneric(context, genericDetails) { arrayEq(registeredAll.js, js, false) === false || arrayEq(registeredAll.excludeMatches, directiveAll.excludeMatches) === false ) { + context.isNewVersion || context.toRemove.push('css-generic-all'); context.toAdd.push(directiveAll); } @@ -284,6 +287,7 @@ function registerGeneric(context, genericDetails) { arrayEq(registeredSome.js, js, false) === false || arrayEq(registeredSome.matches, directiveSome.matches) === false ) { + context.isNewVersion || context.toRemove.push('css-generic-some'); context.toAdd.push(directiveSome); } @@ -342,6 +346,7 @@ function registerProcedural(context) { // update if ( + context.isNewVersion || arrayEq(registered.js, js, false) === false || arrayEq(registered.matches, matches) === false || arrayEq(registered.excludeMatches, excludeMatches) === false @@ -404,6 +409,7 @@ function registerDeclarative(context) { // update if ( + context.isNewVersion || arrayEq(registered.js, js, false) === false || arrayEq(registered.matches, matches) === false || arrayEq(registered.excludeMatches, excludeMatches) === false @@ -466,6 +472,7 @@ function registerSpecific(context) { // update if ( + context.isNewVersion || arrayEq(registered.js, js, false) === false || arrayEq(registered.matches, matches) === false || arrayEq(registered.excludeMatches, excludeMatches) === false @@ -545,6 +552,7 @@ function registerScriptlet(context, scriptletDetails) { // update if ( + context.isNewVersion || arrayEq(registered.matches, matches) === false || arrayEq(registered.excludeMatches, excludeMatches) === false ) { @@ -557,10 +565,77 @@ function registerScriptlet(context, scriptletDetails) { /******************************************************************************/ -// Issue: Safari appears to completely ignore excludeMatches -// https://github.com/radiolondra/ExcludeMatches-Test +async function injectImmediately(tabId, info) { + try { + const results = await browser.scripting.executeScript({ + args: [ info.matches || [], info.excludeMatches || [] ], + func: injectImmediately.targetMatches, + target: { tabId }, + }); + if ( Array.isArray(results) === false ) { return; } + if ( results.length === 0 ) { return; } + const { frameId, result } = results[0] + if ( result !== true ) { return; } + if ( Array.isArray(info.js) && info.js.length !== 0 ) { + browser.scripting.executeScript({ + files: info.js, + injectImmediately: info.runAt === 'document_start', + world: info.world || 'ISOLATED', + target: { tabId, frameIds: [ frameId ] }, + }).catch(( ) => { }); + } else if ( Array.isArray(info.css) && info.css.length !== 0 ) { + browser.scripting.insertCSS({ + files: info.css, + origin: info.origin, + target: { tabId, frameIds: [ frameId ] }, + }).catch(( ) => { }); + } + } catch { + return; + } + return true; +} -async function registerInjectables() { +injectImmediately.targetMatches = function(matches, excludeMatches) { + let matched = matches.includes(''); + if ( matched === false ) { + let hn = document.location.hostname; + for (;;) { + matched = matches.includes(`*://*.${hn}/*`); + if ( matched ) { break; } + const pos = hn.indexOf('.'); + if ( pos === -1 ) { break; } + hn = hn.slice(pos + 1); + } + if ( matched === false ) { return false; } + } + let hn = document.location.hostname; + for (;;) { + if ( excludeMatches.includes(`*://*.${hn}/*`) ) { return false; } + const pos = hn.indexOf('.'); + if ( pos === -1 ) { break; } + hn = hn.slice(pos + 1); + } + return true; +}; + +async function installContentScripts(toInject) { + const tabs = await browser.tabs.query({ discarded: false }); + const promises = []; + for ( const tab of tabs ) { + if ( tab.status === 'unloaded' ) { continue; } + for ( const info of toInject ) { + promises.push(injectImmediately(tab.id, info)); + } + } + const results = await Promise.all(promises); + const count = results.reduce((a, b) => b ? a+1 : a, 0); + ubolLog(`Injected ${count} scriptlets into already opened tabs`); +} + +/******************************************************************************/ + +async function registerInjectables(isNewVersion = false) { if ( browser.scripting === undefined ) { return false; } if ( registerInjectables.barrier ) { return true; } @@ -586,6 +661,7 @@ async function registerInjectables() { ); const toAdd = [], toRemove = []; const context = { + isNewVersion, filteringModeDetails, rulesetsDetails, before, @@ -611,7 +687,13 @@ async function registerInjectables() { if ( toAdd.length !== 0 ) { ubolLog(`Registered ${toAdd.map(v => v.id)} content (css/js)`); await browser.scripting.registerContentScripts(toAdd) - .catch(reason => { console.info(reason); }); + .catch(reason => { ubolLog(reason); }); + // Chromium-based browsers do not inject newly registered scripts into + // already opened tabs, so we do this manually. + // https://github.com/w3c/webextensions/issues/617 + if ( isNewVersion && vendor === 'chrome-extension' ) { + installContentScripts(toAdd); + } } registerInjectables.barrier = false; From 34d6abca6ddabdb2961336a92ddcb37e60e71932 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sun, 20 Apr 2025 16:58:16 -0400 Subject: [PATCH 0846/1099] Revert "[mv3] Programmatically inject content scripts on version change" This reverts commit d0e32a5f47809e417e8cddab57dbff5c2ae844f5. --- platform/mv3/extension/js/background.js | 7 +- platform/mv3/extension/js/ext.js | 6 -- .../mv3/extension/js/scripting-manager.js | 92 +------------------ 3 files changed, 9 insertions(+), 96 deletions(-) diff --git a/platform/mv3/extension/js/background.js b/platform/mv3/extension/js/background.js index 5afa975f2bbc0..6af564822ac0a 100644 --- a/platform/mv3/extension/js/background.js +++ b/platform/mv3/extension/js/background.js @@ -463,9 +463,10 @@ async function launch() { // Permissions may have been removed while the extension was disabled await syncWithBrowserPermissions(); - // Ensure that scriplets are registered. Force-execute them when a new - // version is detected. - registerInjectables(isNewVersion); + // Unsure whether the browser remembers correctly registered css/scripts + // after we quit the browser. For now uBOL will check unconditionally at + // launch time whether content css/scripts are properly registered. + registerInjectables(); // https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/declarativeNetRequest // Firefox API does not support `dnr.setExtensionActionOptions` diff --git a/platform/mv3/extension/js/ext.js b/platform/mv3/extension/js/ext.js index 1b95b11ecb094..ee735b21be30b 100644 --- a/platform/mv3/extension/js/ext.js +++ b/platform/mv3/extension/js/ext.js @@ -27,12 +27,6 @@ export const browser = webext; export const i18n = browser.i18n; export const runtime = browser.runtime; export const windows = browser.windows; -export const vendor = (( ) => { - const url = browser.runtime.getURL(''); - const pos = url.indexOf(':'); - if ( pos === -1 ) { return ''; } - return url.slice(0, pos); -})(); /******************************************************************************/ diff --git a/platform/mv3/extension/js/scripting-manager.js b/platform/mv3/extension/js/scripting-manager.js index 37c754e346d63..8ff9a5c368044 100644 --- a/platform/mv3/extension/js/scripting-manager.js +++ b/platform/mv3/extension/js/scripting-manager.js @@ -21,7 +21,7 @@ import * as ut from './utils.js'; -import { browser, vendor } from './ext.js'; +import { browser } from './ext.js'; import { fetchJSON } from './fetch.js'; import { getEnabledRulesetsDetails } from './ruleset-manager.js'; import { getFilteringModeDetails } from './mode-manager.js'; @@ -168,7 +168,6 @@ function registerHighGeneric(context, genericDetails) { // update if ( - context.isNewVersion || arrayEq(registered.css, css, false) === false || arrayEq(registered.matches, matches) === false || arrayEq(registered.excludeMatches, excludeMatches) === false @@ -235,7 +234,6 @@ function registerGeneric(context, genericDetails) { arrayEq(registered.js, js, false) === false || arrayEq(registered.matches, directive.matches) === false ) { - context.isNewVersion || context.toRemove.push('css-generic-some'); context.toAdd.push(directive); } @@ -262,7 +260,6 @@ function registerGeneric(context, genericDetails) { arrayEq(registeredAll.js, js, false) === false || arrayEq(registeredAll.excludeMatches, directiveAll.excludeMatches) === false ) { - context.isNewVersion || context.toRemove.push('css-generic-all'); context.toAdd.push(directiveAll); } @@ -287,7 +284,6 @@ function registerGeneric(context, genericDetails) { arrayEq(registeredSome.js, js, false) === false || arrayEq(registeredSome.matches, directiveSome.matches) === false ) { - context.isNewVersion || context.toRemove.push('css-generic-some'); context.toAdd.push(directiveSome); } @@ -346,7 +342,6 @@ function registerProcedural(context) { // update if ( - context.isNewVersion || arrayEq(registered.js, js, false) === false || arrayEq(registered.matches, matches) === false || arrayEq(registered.excludeMatches, excludeMatches) === false @@ -409,7 +404,6 @@ function registerDeclarative(context) { // update if ( - context.isNewVersion || arrayEq(registered.js, js, false) === false || arrayEq(registered.matches, matches) === false || arrayEq(registered.excludeMatches, excludeMatches) === false @@ -472,7 +466,6 @@ function registerSpecific(context) { // update if ( - context.isNewVersion || arrayEq(registered.js, js, false) === false || arrayEq(registered.matches, matches) === false || arrayEq(registered.excludeMatches, excludeMatches) === false @@ -552,7 +545,6 @@ function registerScriptlet(context, scriptletDetails) { // update if ( - context.isNewVersion || arrayEq(registered.matches, matches) === false || arrayEq(registered.excludeMatches, excludeMatches) === false ) { @@ -565,77 +557,10 @@ function registerScriptlet(context, scriptletDetails) { /******************************************************************************/ -async function injectImmediately(tabId, info) { - try { - const results = await browser.scripting.executeScript({ - args: [ info.matches || [], info.excludeMatches || [] ], - func: injectImmediately.targetMatches, - target: { tabId }, - }); - if ( Array.isArray(results) === false ) { return; } - if ( results.length === 0 ) { return; } - const { frameId, result } = results[0] - if ( result !== true ) { return; } - if ( Array.isArray(info.js) && info.js.length !== 0 ) { - browser.scripting.executeScript({ - files: info.js, - injectImmediately: info.runAt === 'document_start', - world: info.world || 'ISOLATED', - target: { tabId, frameIds: [ frameId ] }, - }).catch(( ) => { }); - } else if ( Array.isArray(info.css) && info.css.length !== 0 ) { - browser.scripting.insertCSS({ - files: info.css, - origin: info.origin, - target: { tabId, frameIds: [ frameId ] }, - }).catch(( ) => { }); - } - } catch { - return; - } - return true; -} +// Issue: Safari appears to completely ignore excludeMatches +// https://github.com/radiolondra/ExcludeMatches-Test -injectImmediately.targetMatches = function(matches, excludeMatches) { - let matched = matches.includes(''); - if ( matched === false ) { - let hn = document.location.hostname; - for (;;) { - matched = matches.includes(`*://*.${hn}/*`); - if ( matched ) { break; } - const pos = hn.indexOf('.'); - if ( pos === -1 ) { break; } - hn = hn.slice(pos + 1); - } - if ( matched === false ) { return false; } - } - let hn = document.location.hostname; - for (;;) { - if ( excludeMatches.includes(`*://*.${hn}/*`) ) { return false; } - const pos = hn.indexOf('.'); - if ( pos === -1 ) { break; } - hn = hn.slice(pos + 1); - } - return true; -}; - -async function installContentScripts(toInject) { - const tabs = await browser.tabs.query({ discarded: false }); - const promises = []; - for ( const tab of tabs ) { - if ( tab.status === 'unloaded' ) { continue; } - for ( const info of toInject ) { - promises.push(injectImmediately(tab.id, info)); - } - } - const results = await Promise.all(promises); - const count = results.reduce((a, b) => b ? a+1 : a, 0); - ubolLog(`Injected ${count} scriptlets into already opened tabs`); -} - -/******************************************************************************/ - -async function registerInjectables(isNewVersion = false) { +async function registerInjectables() { if ( browser.scripting === undefined ) { return false; } if ( registerInjectables.barrier ) { return true; } @@ -661,7 +586,6 @@ async function registerInjectables(isNewVersion = false) { ); const toAdd = [], toRemove = []; const context = { - isNewVersion, filteringModeDetails, rulesetsDetails, before, @@ -687,13 +611,7 @@ async function registerInjectables(isNewVersion = false) { if ( toAdd.length !== 0 ) { ubolLog(`Registered ${toAdd.map(v => v.id)} content (css/js)`); await browser.scripting.registerContentScripts(toAdd) - .catch(reason => { ubolLog(reason); }); - // Chromium-based browsers do not inject newly registered scripts into - // already opened tabs, so we do this manually. - // https://github.com/w3c/webextensions/issues/617 - if ( isNewVersion && vendor === 'chrome-extension' ) { - installContentScripts(toAdd); - } + .catch(reason => { console.info(reason); }); } registerInjectables.barrier = false; From 22e848c9c22e48cd5375e23b625fa029d44b27b5 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Mon, 21 Apr 2025 08:11:22 -0400 Subject: [PATCH 0847/1099] [mv3] Fix zapper Regression from: https://github.com/gorhill/uBlock/commit/b5651417aacd6024cb0a89a2b357563c0761a5e6 --- platform/mv3/extension/js/scripting/zapper.js | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/platform/mv3/extension/js/scripting/zapper.js b/platform/mv3/extension/js/scripting/zapper.js index 5a0e81c58c403..a373498a276d1 100644 --- a/platform/mv3/extension/js/scripting/zapper.js +++ b/platform/mv3/extension/js/scripting/zapper.js @@ -371,7 +371,7 @@ const bootstrap = async ( ) => { return new Promise(resolve => { const frame = document.createElement('iframe'); frame.setAttribute(zapperSecret, ''); - frame.onload = ( ) => { + const onZapperLoad = ( ) => { frame.onload = null; frame.setAttribute(`${zapperSecret}-loaded`, ''); const channel = new MessageChannel(); @@ -395,13 +395,11 @@ const bootstrap = async ( ) => { zapperFramePort: port, }); }; - if ( dynamicURL.protocol !== 'safari-web-extension:' ) { - document.documentElement.append(frame); + frame.onload = ( ) => { + frame.onload = onZapperLoad; frame.contentWindow.location = dynamicURL.href; - } else { - frame.setAttribute('src', dynamicURL.href); - document.documentElement.append(frame); - } + }; + document.documentElement.append(frame); }); }; From 1d5344145642bee7a7107d49606fcd39a525b5e1 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Mon, 21 Apr 2025 08:13:16 -0400 Subject: [PATCH 0848/1099] [mv3] Simplify sendMessage onMessage() is now a listener installed synchrnously when uBOL executes, so no longer need to deal with failure to send messages. Related commit: https://github.com/gorhill/uBlock/commit/ab458b492a187bf5e702a04f6db78bbc43d5cd8d --- platform/mv3/extension/js/ext.js | 17 ++--------------- 1 file changed, 2 insertions(+), 15 deletions(-) diff --git a/platform/mv3/extension/js/ext.js b/platform/mv3/extension/js/ext.js index ee735b21be30b..ea1ab61ced874 100644 --- a/platform/mv3/extension/js/ext.js +++ b/platform/mv3/extension/js/ext.js @@ -34,21 +34,8 @@ export const windows = browser.windows; // send a message, we try a few more times when the message fails to be sent. export function sendMessage(msg) { - return new Promise((resolve, reject) => { - let i = 5; - const send = ( ) => { - runtime.sendMessage(msg).then(response => { - resolve(response); - }).catch(reason => { - i -= 1; - if ( i <= 0 ) { - reject(reason); - } else { - setTimeout(send, 200); - } - }); - }; - send(); + return runtime.sendMessage(msg).catch(reason => { + console.log(reason); }); } From d17da2b081c2aa2ed03448619dbacb29e7ff1233 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Mon, 21 Apr 2025 09:42:58 -0400 Subject: [PATCH 0849/1099] Import translation work from https://crowdin.com/project/ublock --- platform/mv3/extension/_locales/fr/messages.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform/mv3/extension/_locales/fr/messages.json b/platform/mv3/extension/_locales/fr/messages.json index a6ad7b299ecfb..1b103c3fedeb9 100644 --- a/platform/mv3/extension/_locales/fr/messages.json +++ b/platform/mv3/extension/_locales/fr/messages.json @@ -4,7 +4,7 @@ "description": "extension name." }, "extShortDesc": { - "message": "Un bloqueur de contenu sans permission requise. Bloque les publicités, pisteurs, mineurs et plus dès l'installation.", + "message": "Un bloqueur de contenu efficace. Bloque les publicités, pisteurs, mineurs et plus dès l'installation.", "description": "this will be in the Chrome web store: must be 132 characters or less" }, "perRulesetStats": { From d3f39c97d1709b856a25bc5e33be64f725c6ca09 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Mon, 21 Apr 2025 12:58:44 -0400 Subject: [PATCH 0850/1099] [mv3-safari] Fix broken zapper due to latest changes Related commit: https://github.com/gorhill/uBlock/commit/22e848c9c22e48cd5375e23b625fa029d44b27b5 --- platform/mv3/extension/js/scripting/zapper.js | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/platform/mv3/extension/js/scripting/zapper.js b/platform/mv3/extension/js/scripting/zapper.js index a373498a276d1..d3b77b72386d7 100644 --- a/platform/mv3/extension/js/scripting/zapper.js +++ b/platform/mv3/extension/js/scripting/zapper.js @@ -395,10 +395,15 @@ const bootstrap = async ( ) => { zapperFramePort: port, }); }; - frame.onload = ( ) => { + if ( dynamicURL.protocol !== 'safari-web-extension:' ) { + frame.onload = ( ) => { + frame.onload = onZapperLoad; + frame.contentWindow.location = dynamicURL.href; + }; + } else { frame.onload = onZapperLoad; - frame.contentWindow.location = dynamicURL.href; - }; + frame.setAttribute('src', dynamicURL.href); + } document.documentElement.append(frame); }); }; From b5642ba602e64e0c565ee2207dfbbf552bef989a Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Mon, 21 Apr 2025 13:21:08 -0400 Subject: [PATCH 0851/1099] [mv3] Minor --- platform/mv3/extension/js/ruleset-manager.js | 1 + 1 file changed, 1 insertion(+) diff --git a/platform/mv3/extension/js/ruleset-manager.js b/platform/mv3/extension/js/ruleset-manager.js index 7d717c4c47675..90e2aebf88abc 100644 --- a/platform/mv3/extension/js/ruleset-manager.js +++ b/platform/mv3/extension/js/ruleset-manager.js @@ -359,6 +359,7 @@ async function updateStrictBlockRules(currentRules, addRules, removeRuleIds) { if ( validRules.length === 0 ) { return; } ubolLog(`Add ${validRules.length} DNR strictblock rules`); for ( const rule of validRules ) { + rule.priority = STRICTBLOCK_PRIORITY; addRules.push(rule); } From 8b964a8c5416966b3374d1fdf43ac7cb7a9cc541 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Mon, 21 Apr 2025 15:42:04 -0400 Subject: [PATCH 0852/1099] Fetch diff patches from "reliable" servers only Some CDN servers take too long to mirror updated resources, potentially preventing diff-updating to work reliably as intended. --- assets/assets.dev.json | 32 ++++++++++++++++++++++++++++++++ assets/assets.json | 32 ++++++++++++++++++++++++++++++++ src/js/assets.js | 3 +++ src/js/diff-updater.js | 4 ++-- 4 files changed, 69 insertions(+), 2 deletions(-) diff --git a/assets/assets.dev.json b/assets/assets.dev.json index e215525fc7cc3..eeda088634d16 100644 --- a/assets/assets.dev.json +++ b/assets/assets.dev.json @@ -52,6 +52,10 @@ "https://cdn.jsdelivr.net/gh/uBlockOrigin/uAssetsCDN@main/filters/filters.min.txt", "https://cdn.statically.io/gh/uBlockOrigin/uAssetsCDN/main/filters/filters.min.txt" ], + "patchURLs": [ + "https://ublockorigin.github.io/uAssetsCDN/filters/", + "https://ublockorigin.pages.dev/filters/" + ], "supportURL": "https://github.com/uBlockOrigin/uAssets" }, "ublock-badware": { @@ -71,6 +75,10 @@ "https://cdn.jsdelivr.net/gh/uBlockOrigin/uAssetsCDN@main/filters/badware.min.txt", "https://cdn.statically.io/gh/uBlockOrigin/uAssetsCDN/main/filters/badware.min.txt" ], + "patchURLs": [ + "https://ublockorigin.github.io/uAssetsCDN/filters/", + "https://ublockorigin.pages.dev/filters/" + ], "supportURL": "https://github.com/uBlockOrigin/uAssets", "instructionURL": "https://github.com/gorhill/uBlock/wiki/Badware-risks" }, @@ -91,6 +99,10 @@ "https://cdn.jsdelivr.net/gh/uBlockOrigin/uAssetsCDN@main/filters/privacy.min.txt", "https://cdn.statically.io/gh/uBlockOrigin/uAssetsCDN/main/filters/privacy.min.txt" ], + "patchURLs": [ + "https://ublockorigin.github.io/uAssetsCDN/filters/", + "https://ublockorigin.pages.dev/filters/" + ], "supportURL": "https://github.com/uBlockOrigin/uAssets" }, "ublock-unbreak": { @@ -109,6 +121,10 @@ "https://cdn.jsdelivr.net/gh/uBlockOrigin/uAssetsCDN@main/filters/unbreak.min.txt", "https://cdn.statically.io/gh/uBlockOrigin/uAssetsCDN/main/filters/unbreak.min.txt" ], + "patchURLs": [ + "https://ublockorigin.github.io/uAssetsCDN/filters/", + "https://ublockorigin.pages.dev/filters/" + ], "supportURL": "https://github.com/uBlockOrigin/uAssets" }, "ublock-quick-fixes": { @@ -127,6 +143,10 @@ "https://cdn.jsdelivr.net/gh/uBlockOrigin/uAssetsCDN@main/filters/quick-fixes.min.txt", "https://cdn.statically.io/gh/uBlockOrigin/uAssetsCDN/main/filters/quick-fixes.min.txt" ], + "patchURLs": [ + "https://ublockorigin.github.io/uAssetsCDN/filters/", + "https://ublockorigin.pages.dev/filters/" + ], "supportURL": "https://github.com/uBlockOrigin/uAssets" }, "adguard-generic": { @@ -163,6 +183,10 @@ "https://cdn.statically.io/gh/uBlockOrigin/uAssetsCDN/main/thirdparties/easylist.txt", "https://ublockorigin.pages.dev/thirdparties/easylist.txt" ], + "patchURLs": [ + "https://ublockorigin.github.io/uAssetsCDN/filters/", + "https://ublockorigin.pages.dev/filters/" + ], "supportURL": "https://easylist.to/" }, "adguard-spyware-url": { @@ -212,6 +236,10 @@ "https://cdn.statically.io/gh/uBlockOrigin/uAssetsCDN/main/thirdparties/easyprivacy.txt", "https://ublockorigin.pages.dev/thirdparties/easyprivacy.txt" ], + "patchURLs": [ + "https://ublockorigin.github.io/uAssetsCDN/filters/", + "https://ublockorigin.pages.dev/filters/" + ], "supportURL": "https://easylist.to/" }, "urlhaus-1": { @@ -478,6 +506,10 @@ "https://cdn.jsdelivr.net/gh/uBlockOrigin/uAssetsCDN@main/filters/annoyances.min.txt", "https://cdn.statically.io/gh/uBlockOrigin/uAssetsCDN/main/filters/annoyances.min.txt" ], + "patchURLs": [ + "https://ublockorigin.github.io/uAssetsCDN/filters/", + "https://ublockorigin.pages.dev/filters/" + ], "supportURL": "https://github.com/uBlockOrigin/uAssets" }, "dpollock-0": { diff --git a/assets/assets.json b/assets/assets.json index d615c2722de18..05f670333ccbe 100644 --- a/assets/assets.json +++ b/assets/assets.json @@ -52,6 +52,10 @@ "https://cdn.jsdelivr.net/gh/uBlockOrigin/uAssetsCDN@main/filters/filters.min.txt", "https://cdn.statically.io/gh/uBlockOrigin/uAssetsCDN/main/filters/filters.min.txt" ], + "patchURLs": [ + "https://ublockorigin.github.io/uAssetsCDN/filters/", + "https://ublockorigin.pages.dev/filters/" + ], "supportURL": "https://github.com/uBlockOrigin/uAssets" }, "ublock-badware": { @@ -71,6 +75,10 @@ "https://cdn.jsdelivr.net/gh/uBlockOrigin/uAssetsCDN@main/filters/badware.min.txt", "https://cdn.statically.io/gh/uBlockOrigin/uAssetsCDN/main/filters/badware.min.txt" ], + "patchURLs": [ + "https://ublockorigin.github.io/uAssetsCDN/filters/", + "https://ublockorigin.pages.dev/filters/" + ], "supportURL": "https://github.com/uBlockOrigin/uAssets", "instructionURL": "https://github.com/gorhill/uBlock/wiki/Badware-risks" }, @@ -91,6 +99,10 @@ "https://cdn.jsdelivr.net/gh/uBlockOrigin/uAssetsCDN@main/filters/privacy.min.txt", "https://cdn.statically.io/gh/uBlockOrigin/uAssetsCDN/main/filters/privacy.min.txt" ], + "patchURLs": [ + "https://ublockorigin.github.io/uAssetsCDN/filters/", + "https://ublockorigin.pages.dev/filters/" + ], "supportURL": "https://github.com/uBlockOrigin/uAssets" }, "ublock-unbreak": { @@ -109,6 +121,10 @@ "https://cdn.jsdelivr.net/gh/uBlockOrigin/uAssetsCDN@main/filters/unbreak.min.txt", "https://cdn.statically.io/gh/uBlockOrigin/uAssetsCDN/main/filters/unbreak.min.txt" ], + "patchURLs": [ + "https://ublockorigin.github.io/uAssetsCDN/filters/", + "https://ublockorigin.pages.dev/filters/" + ], "supportURL": "https://github.com/uBlockOrigin/uAssets" }, "ublock-quick-fixes": { @@ -127,6 +143,10 @@ "https://cdn.jsdelivr.net/gh/uBlockOrigin/uAssetsCDN@main/filters/quick-fixes.min.txt", "https://cdn.statically.io/gh/uBlockOrigin/uAssetsCDN/main/filters/quick-fixes.min.txt" ], + "patchURLs": [ + "https://ublockorigin.github.io/uAssetsCDN/filters/", + "https://ublockorigin.pages.dev/filters/" + ], "supportURL": "https://github.com/uBlockOrigin/uAssets" }, "adguard-generic": { @@ -163,6 +183,10 @@ "https://cdn.statically.io/gh/uBlockOrigin/uAssetsCDN/main/thirdparties/easylist.txt", "https://ublockorigin.pages.dev/thirdparties/easylist.txt" ], + "patchURLs": [ + "https://ublockorigin.github.io/uAssetsCDN/filters/", + "https://ublockorigin.pages.dev/filters/" + ], "supportURL": "https://easylist.to/" }, "adguard-spyware-url": { @@ -212,6 +236,10 @@ "https://cdn.statically.io/gh/uBlockOrigin/uAssetsCDN/main/thirdparties/easyprivacy.txt", "https://ublockorigin.pages.dev/thirdparties/easyprivacy.txt" ], + "patchURLs": [ + "https://ublockorigin.github.io/uAssetsCDN/filters/", + "https://ublockorigin.pages.dev/filters/" + ], "supportURL": "https://easylist.to/" }, "urlhaus-1": { @@ -478,6 +506,10 @@ "https://cdn.jsdelivr.net/gh/uBlockOrigin/uAssetsCDN@main/filters/annoyances.min.txt", "https://cdn.statically.io/gh/uBlockOrigin/uAssetsCDN/main/filters/annoyances.min.txt" ], + "patchURLs": [ + "https://ublockorigin.github.io/uAssetsCDN/filters/", + "https://ublockorigin.pages.dev/filters/" + ], "supportURL": "https://github.com/uBlockOrigin/uAssets" }, "dpollock-0": { diff --git a/src/js/assets.js b/src/js/assets.js index 809f5c4704fca..b7b18fb286210 100644 --- a/src/js/assets.js +++ b/src/js/assets.js @@ -1214,6 +1214,9 @@ const getAssetDiffDetails = assetKey => { } if ( Array.isArray(out.cdnURLs) === false ) { return; } if ( out.cdnURLs.length === 0 ) { return; } + if ( Array.isArray(assetEntry.patchURLs) ) { + out.patchURLs = assetEntry.patchURLs.slice(); + } return out; }; diff --git a/src/js/diff-updater.js b/src/js/diff-updater.js index b9e906bd688ef..93d1dd2fe6390 100644 --- a/src/js/diff-updater.js +++ b/src/js/diff-updater.js @@ -181,10 +181,10 @@ async function applyPatchAndValidate(assetDetails, diffDetails) { } async function fetchPatchDetailsFromCDNs(assetDetails) { - const { patchPath, cdnURLs } = assetDetails; + const { patchPath, cdnURLs, patchURLs } = assetDetails; if ( Array.isArray(cdnURLs) === false ) { return null; } if ( cdnURLs.length === 0 ) { return null; } - for ( const cdnURL of suffleArray(cdnURLs) ) { + for ( const cdnURL of suffleArray(patchURLs || cdnURLs) ) { const patchURL = resolveURL(patchPath, cdnURL); if ( patchURL === undefined ) { continue; } const response = await fetch(patchURL).catch(reason => { From 2ee90bf22fcfa3c4293e54534a2455c971053e33 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Mon, 21 Apr 2025 15:45:55 -0400 Subject: [PATCH 0853/1099] Update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4d62065d0fc9c..6823ee30b6795 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,4 @@ +- [Fetch diff patches from "reliable" servers only](https://github.com/gorhill/uBlock/commit/8b964a8c54) - [Add `trusted-create-html` scriptlet](https://github.com/gorhill/uBlock/commit/20dd606504) - [Mind potential race condition when dynamically registering scriptlets](https://github.com/gorhill/uBlock/commit/15e832da8a) - [Fix undue unchecking of setting in "My filters"](https://github.com/gorhill/uBlock/commit/2bb6999e3f) From 6cc63d825e030b88fb9e90b2efe4eedef1c69b69 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Mon, 21 Apr 2025 15:46:06 -0400 Subject: [PATCH 0854/1099] New revision for dev build --- dist/version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dist/version b/dist/version index 555e0a1557a99..0c0c55ab6046e 100644 --- a/dist/version +++ b/dist/version @@ -1 +1 @@ -1.63.3.16 \ No newline at end of file +1.63.3.17 \ No newline at end of file From 9c523f032565f05357c5e53b50c85340918df2c2 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Mon, 21 Apr 2025 15:51:02 -0400 Subject: [PATCH 0855/1099] Make Firefox dev build auto-update --- dist/firefox/updates.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dist/firefox/updates.json b/dist/firefox/updates.json index ba81adadd3ee9..951f6a7c1eb64 100644 --- a/dist/firefox/updates.json +++ b/dist/firefox/updates.json @@ -3,9 +3,9 @@ "uBlock0@raymondhill.net": { "updates": [ { - "version": "1.63.3.16", + "version": "1.63.3.17", "browser_specific_settings": { "gecko": { "strict_min_version": "92.0" } }, - "update_link": "https://github.com/gorhill/uBlock/releases/download/1.63.3b16/uBlock0_1.63.3b16.firefox.signed.xpi" + "update_link": "https://github.com/gorhill/uBlock/releases/download/1.63.3b17/uBlock0_1.63.3b17.firefox.signed.xpi" } ] } From 59f4aca0106fec350842c862d1958ff602070394 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Tue, 22 Apr 2025 09:34:26 -0400 Subject: [PATCH 0856/1099] Exclude `chrome:` as valid openers for popup candidates Related issue: https://github.com/uBlockOrigin/uBlock-issues/issues/2227 --- src/js/tab.js | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/src/js/tab.js b/src/js/tab.js index 1bf65912db647..743ea6b6206dd 100644 --- a/src/js/tab.js +++ b/src/js/tab.js @@ -573,14 +573,12 @@ housekeep itself. } catch { return; } - if ( - Array.isArray(openerDetails) === false || - openerDetails.length !== 2 || - openerDetails[1] === null || - openerDetails[1].url === 'about:newtab' - ) { - return; - } + if ( Array.isArray(openerDetails) === false ) { return; } + if ( openerDetails.length !== 2 ) { return; } + if ( openerDetails[1] === null ) { return; } + if ( openerDetails[1].url === 'about:newtab' ) { return; } + // https://github.com/uBlockOrigin/uBlock-issues/issues/2227 + if ( openerDetails[1].url.startsWith('chrome:') ) { return; } popupCandidates.set( tabId, new PopupCandidate(createDetails, openerDetails) From 89ba371fbe1c321dae4b8a462bc8d1c5b1233f5b Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Tue, 22 Apr 2025 09:38:52 -0400 Subject: [PATCH 0857/1099] New revision for dev build --- dist/version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dist/version b/dist/version index 0c0c55ab6046e..b598b6f486149 100644 --- a/dist/version +++ b/dist/version @@ -1 +1 @@ -1.63.3.17 \ No newline at end of file +1.63.3.18 \ No newline at end of file From c86beb3a374d939b689c3512d4ca60584d9ce026 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Tue, 22 Apr 2025 09:40:21 -0400 Subject: [PATCH 0858/1099] Update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6823ee30b6795..58425c16b6c01 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,4 @@ +- [Exclude `chrome:` as valid openers for popup candidates](https://github.com/gorhill/uBlock/commit/59f4aca010) - [Fetch diff patches from "reliable" servers only](https://github.com/gorhill/uBlock/commit/8b964a8c54) - [Add `trusted-create-html` scriptlet](https://github.com/gorhill/uBlock/commit/20dd606504) - [Mind potential race condition when dynamically registering scriptlets](https://github.com/gorhill/uBlock/commit/15e832da8a) From b4eae1ef0bbb4373b6e9a757c856f3bf97208a62 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Tue, 22 Apr 2025 09:56:04 -0400 Subject: [PATCH 0859/1099] Make Firefox dev build auto-update --- dist/firefox/updates.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dist/firefox/updates.json b/dist/firefox/updates.json index 951f6a7c1eb64..86e6782f693ea 100644 --- a/dist/firefox/updates.json +++ b/dist/firefox/updates.json @@ -3,9 +3,9 @@ "uBlock0@raymondhill.net": { "updates": [ { - "version": "1.63.3.17", + "version": "1.63.3.18", "browser_specific_settings": { "gecko": { "strict_min_version": "92.0" } }, - "update_link": "https://github.com/gorhill/uBlock/releases/download/1.63.3b17/uBlock0_1.63.3b17.firefox.signed.xpi" + "update_link": "https://github.com/gorhill/uBlock/releases/download/1.63.3b18/uBlock0_1.63.3b18.firefox.signed.xpi" } ] } From 0243a141a762df5ea325ce71b53f0cce3e6a3ee8 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Wed, 23 Apr 2025 09:34:00 -0400 Subject: [PATCH 0860/1099] Ignore `start_page` transition for popup-blocking purpose Related issue: https://github.com/uBlockOrigin/uBlock-issues/issues/2227 --- platform/chromium/vapi-background-ext.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/platform/chromium/vapi-background-ext.js b/platform/chromium/vapi-background-ext.js index 17526ba9cac18..193b7fe6fa0fc 100644 --- a/platform/chromium/vapi-background-ext.js +++ b/platform/chromium/vapi-background-ext.js @@ -26,6 +26,8 @@ // so we synthetize these missing events when this happens. // https://github.com/uBlockOrigin/uAssets/issues/10323 // Also mind whether the new tab is launched from an external application. +// https://github.com/uBlockOrigin/uBlock-issues/issues/2227 +// Revert commit related to issue above. vAPI.Tabs = class extends vAPI.Tabs { constructor() { @@ -66,8 +68,7 @@ vAPI.Tabs = class extends vAPI.Tabs { const isClientRedirect = Array.isArray(details.transitionQualifiers) && details.transitionQualifiers.includes('client_redirect'); - const isStartPage = details.transitionType === 'start_page'; - if ( isClientRedirect === false && isStartPage === false ) { return; } + if ( isClientRedirect === false ) { return; } this.onCreatedNavigationTargetHandler({ tabId: details.tabId, sourceTabId: details.tabId, From 79a77f915a60ee6fe224b527e8266a0620e1440b Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Wed, 23 Apr 2025 09:40:00 -0400 Subject: [PATCH 0861/1099] Update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 58425c16b6c01..6399a92b3d831 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,4 @@ +- [Ignore `start_page` transition for popup-blocking purpose](https://github.com/gorhill/uBlock/commit/0243a141a7) - [Exclude `chrome:` as valid openers for popup candidates](https://github.com/gorhill/uBlock/commit/59f4aca010) - [Fetch diff patches from "reliable" servers only](https://github.com/gorhill/uBlock/commit/8b964a8c54) - [Add `trusted-create-html` scriptlet](https://github.com/gorhill/uBlock/commit/20dd606504) From bd6263078fe21bbf2b09bf9eb570d8017212c2ca Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Wed, 23 Apr 2025 09:40:26 -0400 Subject: [PATCH 0862/1099] New revision for dev build --- dist/version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dist/version b/dist/version index b598b6f486149..841afe85e44e5 100644 --- a/dist/version +++ b/dist/version @@ -1 +1 @@ -1.63.3.18 \ No newline at end of file +1.63.3.19 \ No newline at end of file From 0e5dec7fbb41c9fe77ac030808a79d3cb13cce7d Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Thu, 24 Apr 2025 09:58:31 -0400 Subject: [PATCH 0863/1099] [mv3] Separate EasyList, EasyPrivacy, PGL lists from uBlock filters Additionally, remember `badfilter` filters across conversion of filter lists to DNR rulesets. --- platform/mv3/extension/js/admin.js | 25 +++++++++- platform/mv3/extension/js/debug.js | 7 ++- platform/mv3/extension/js/filter-lists.js | 9 ++-- platform/mv3/extension/js/ruleset-manager.js | 33 +++++++------ platform/mv3/make-rulesets.js | 4 +- platform/mv3/rulesets.json | 51 +++++++++++++++----- src/js/static-dnr-filtering.js | 2 + src/js/static-net-filtering.js | 2 +- 8 files changed, 97 insertions(+), 36 deletions(-) diff --git a/platform/mv3/extension/js/admin.js b/platform/mv3/extension/js/admin.js index c9fa5f5b98021..7596516177d5e 100644 --- a/platform/mv3/extension/js/admin.js +++ b/platform/mv3/extension/js/admin.js @@ -87,11 +87,32 @@ const adminSettings = { /******************************************************************************/ export async function getAdminRulesets() { - const adminList = await adminReadEx('rulesets'); + const [ + adminList, + rulesetDetails, + ] = await Promise.all([ + adminReadEx('rulesets'), + getRulesetDetails(), + ]); const adminRulesets = new Set(Array.isArray(adminList) && adminList || []); + if ( adminRulesets.has('-default') ) { + adminRulesets.delete('-default'); + for ( const ruleset of rulesetDetails.values() ) { + if ( ruleset.enabled !== true ) { continue; } + if ( adminRulesets.has(`+${ruleset.id}`) ) { continue; } + adminRulesets.add(`-${ruleset.id}`); + } + } + if ( adminRulesets.has('+default') ) { + adminRulesets.delete('+default'); + for ( const ruleset of rulesetDetails.values() ) { + if ( ruleset.enabled !== true ) { continue; } + if ( adminRulesets.has(`-${ruleset.id}`) ) { continue; } + adminRulesets.add(`+${ruleset.id}`); + } + } if ( adminRulesets.has('-*') ) { adminRulesets.delete('-*'); - const rulesetDetails = await getRulesetDetails(); for ( const ruleset of rulesetDetails.values() ) { if ( ruleset.enabled ) { continue; } if ( adminRulesets.has(`+${ruleset.id}`) ) { continue; } diff --git a/platform/mv3/extension/js/debug.js b/platform/mv3/extension/js/debug.js index ea5dafbed27fb..77231b0177758 100644 --- a/platform/mv3/extension/js/debug.js +++ b/platform/mv3/extension/js/debug.js @@ -27,10 +27,8 @@ import { browser } from './ext.js'; const isModern = dnr.onRuleMatchedDebug instanceof Object; export const isSideloaded = (( ) => { - if ( isModern ) { return true; } - if ( typeof dnr.getMatchedRules === 'function' ) { return true; } - const manifest = browser.runtime.getManifest(); - return manifest.permissions?.includes('declarativeNetRequestFeedback') ?? false; + const { permissions } = browser.runtime.getManifest(); + return permissions?.includes('declarativeNetRequestFeedback') ?? false; })(); /******************************************************************************/ @@ -119,6 +117,7 @@ export const getMatchedRules = (( ) => { } return async tabId => { + if ( typeof dnr.getMatchedRules !== 'function' ) { return []; } const matchedRules = await dnr.getMatchedRules({ tabId }); if ( matchedRules instanceof Object === false ) { return []; } const out = []; diff --git a/platform/mv3/extension/js/filter-lists.js b/platform/mv3/extension/js/filter-lists.js index a349ca832037a..80b0af79a421e 100644 --- a/platform/mv3/extension/js/filter-lists.js +++ b/platform/mv3/extension/js/filter-lists.js @@ -135,7 +135,7 @@ export function renderFilterLists(rulesetData) { if ( ruleset.homeURL ) { dom.attr(qs$(listEntry, 'a.support'), 'href', ruleset.homeURL); } - dom.cl.toggle(listEntry, 'isDefault', ruleset.id === 'default'); + dom.cl.toggle(listEntry, 'isDefault', ruleset.enabled === true); const stats = rulesetStats(ruleset.id); if ( stats === undefined ) { return; } listEntry.title = listStatsTemplate @@ -215,9 +215,13 @@ export function renderFilterLists(rulesetData) { [ 'default', rulesetDetails.filter(ruleset => - ruleset.id === 'default' || ruleset.group === 'default' ), + ], [ + 'privacy', + rulesetDetails.filter(ruleset => + ruleset.group === 'privacy' + ), ], [ 'malware', rulesetDetails.filter(ruleset => @@ -231,7 +235,6 @@ export function renderFilterLists(rulesetData) { ], [ 'misc', rulesetDetails.filter(ruleset => - ruleset.id !== 'default' && ruleset.group === undefined && typeof ruleset.lang !== 'string' ), diff --git a/platform/mv3/extension/js/ruleset-manager.js b/platform/mv3/extension/js/ruleset-manager.js index 90e2aebf88abc..79c299d1a1a98 100644 --- a/platform/mv3/extension/js/ruleset-manager.js +++ b/platform/mv3/extension/js/ruleset-manager.js @@ -484,19 +484,16 @@ async function defaultRulesetsFromLanguage() { `\\b(${Array.from(langSet).join('|')})\\b` ); - const manifest = runtime.getManifest(); - const rulesets = manifest.declarative_net_request.rule_resources; const rulesetDetails = await getRulesetDetails(); const out = []; - for ( const ruleset of rulesets ) { + for ( const ruleset of rulesetDetails.values() ) { const { id, enabled } = ruleset; if ( enabled ) { out.push(id); continue; } - const details = rulesetDetails.get(id); - if ( typeof details.lang !== 'string' ) { continue; } - if ( reTargetLang.test(details.lang) === false ) { continue; } + if ( typeof ruleset.lang !== 'string' ) { continue; } + if ( reTargetLang.test(ruleset.lang) === false ) { continue; } out.push(id); } return out; @@ -508,15 +505,12 @@ async function patchDefaultRulesets() { const [ oldDefaultIds = [], newDefaultIds, + staticRulesetIds, ] = await Promise.all([ localRead('defaultRulesetIds'), defaultRulesetsFromLanguage(), + getStaticRulesets().then(r => r.map(a => a.id)), ]); - - const manifest = runtime.getManifest(); - const validIds = new Set( - manifest.declarative_net_request.rule_resources.map(r => r.id) - ); const toAdd = []; const toRemove = []; for ( const id of newDefaultIds ) { @@ -528,7 +522,7 @@ async function patchDefaultRulesets() { toRemove.push(id); } for ( const id of rulesetConfig.enabledRulesets ) { - if ( validIds.has(id) ) { continue; } + if ( staticRulesetIds.includes(id) ) { continue; } toRemove.push(id); } localWrite('defaultRulesetIds', newDefaultIds); @@ -545,7 +539,11 @@ async function patchDefaultRulesets() { async function enableRulesets(ids) { const afterIds = new Set(ids); - const [ beforeIds, adminIds, rulesetDetails ] = await Promise.all([ + const [ + beforeIds, + adminIds, + rulesetDetails, + ] = await Promise.all([ dnr.getEnabledRulesets().then(ids => new Set(ids)), getAdminRulesets(), getRulesetDetails(), @@ -614,6 +612,13 @@ async function enableRulesets(ids) { /******************************************************************************/ +async function getStaticRulesets() { + const manifest = runtime.getManifest(); + return manifest.declarative_net_request.rule_resources; +} + +/******************************************************************************/ + async function getEnabledRulesetsDetails() { const [ ids, @@ -638,8 +643,8 @@ export { enableRulesets, excludeFromStrictBlock, filteringModesToDNR, - getRulesetDetails, getEnabledRulesetsDetails, + getRulesetDetails, patchDefaultRulesets, setStrictBlockMode, updateDynamicRules, diff --git a/platform/mv3/make-rulesets.js b/platform/mv3/make-rulesets.js index 6ebadfcdc6ee4..fc93c2ec37aa0 100644 --- a/platform/mv3/make-rulesets.js +++ b/platform/mv3/make-rulesets.js @@ -175,6 +175,7 @@ const rulesetDetails = []; const scriptletStats = new Map(); const genericDetails = new Map(); const requiredRedirectResources = new Set(); +let networkBad = new Set(); // This will be used to sign our inserted `!#trusted on` directives const secret = createHash('sha256').update(randomBytes(16)).digest('hex').slice(0,16); @@ -1272,8 +1273,9 @@ async function rulesetFromURLs(assetDetails) { const results = await dnrRulesetFromRawLists( [ { name: assetDetails.id, text: assetDetails.text } ], - { env, extensionPaths, secret } + { env, extensionPaths, secret, networkBad } ); + networkBad = results.networkBad; // Release memory used by filter list content assetDetails.text = undefined; diff --git a/platform/mv3/rulesets.json b/platform/mv3/rulesets.json index 88b5241fa574e..3042950886324 100644 --- a/platform/mv3/rulesets.json +++ b/platform/mv3/rulesets.json @@ -1,24 +1,51 @@ [ { - "id": "default", - "name": "Ads, trackers, miners, and more", + "id": "ublock-filters", + "name": "uBlock filters – Ads, trackers, and more", + "group": "default", "enabled": true, "urls": [ + "https://ublockorigin.github.io/uAssets/filters/quick-fixes.min.txt", + "https://ublockorigin.github.io/uAssets/filters/unbreak.min.txt", "https://ublockorigin.github.io/uAssets/filters/filters.min.txt", "https://ublockorigin.github.io/uAssets/filters/privacy.min.txt", - "https://ublockorigin.github.io/uAssets/filters/unbreak.min.txt", - "https://ublockorigin.github.io/uAssets/filters/quick-fixes.min.txt", - "https://ublockorigin.github.io/uAssets/filters/ubol-filters.txt", - "https://ublockorigin.github.io/uAssets/thirdparties/easylist.txt", - "https://ublockorigin.github.io/uAssets/thirdparties/easyprivacy.txt", - "https://pgl.yoyo.org/adservers/serverlist.php?hostformat=hosts&showintro=1&mimetype=plaintext" + "https://ublockorigin.github.io/uAssets/filters/ubol-filters.txt" ], - "dnrURL": "https://ublockorigin.github.io/uAssets/dnr/default.json", "homeURL": "https://github.com/uBlockOrigin/uAssets" }, { - "id": "badware", - "name": "Badware risks", + "id": "easylist", + "name": "EasyList", + "group": "default", + "enabled": true, + "urls": [ + "https://ublockorigin.github.io/uAssets/thirdparties/easylist.txt" + ], + "homeURL": "https://easylist.to/" + }, + { + "id": "easyprivacy", + "name": "EasyPrivacy", + "group": "default", + "enabled": true, + "urls": [ + "https://ublockorigin.github.io/uAssets/thirdparties/easyprivacy.txt" + ], + "homeURL": "https://easylist.to/" + }, + { + "id": "pgl", + "name": "Peter Lowe – Ads, trackers, and more", + "group": "default", + "enabled": true, + "urls": [ + "https://pgl.yoyo.org/adservers/serverlist.php?hostformat=hosts&showintro=1&mimetype=plaintext" + ], + "homeURL": "https://pgl.yoyo.org/adservers/" + }, + { + "id": "ublock-badware", + "name": "uBlock filters – Badware risks", "group": "malware", "enabled": true, "urls": [ @@ -49,6 +76,7 @@ { "id": "block-lan", "name": "Block Outsider Intrusion into LAN", + "group": "privacy", "enabled": false, "urls": [ "https://ublockorigin.github.io/uAssets/filters/lan-block.txt" @@ -67,6 +95,7 @@ { "id": "adguard-spyware-url", "name": "AdGuard URL Tracking Protection", + "group": "privacy", "enabled": false, "urls": [ "https://filters.adtidy.org/extension/ublock/filters/17.txt" diff --git a/src/js/static-dnr-filtering.js b/src/js/static-dnr-filtering.js index 6da12ae45f228..0bc82a7f75b3a 100644 --- a/src/js/static-dnr-filtering.js +++ b/src/js/static-dnr-filtering.js @@ -475,6 +475,7 @@ function finalizeRuleset(context, network) { async function dnrRulesetFromRawLists(lists, options = {}) { const context = Object.assign({}, options); + context.bad = options.networkBad; staticNetFilteringEngine.dnrFromCompiled('begin', context); context.extensionPaths = new Map(context.extensionPaths || []); const toLoad = []; @@ -489,6 +490,7 @@ async function dnrRulesetFromRawLists(lists, options = {}) { await Promise.all(toLoad); const result = { network: staticNetFilteringEngine.dnrFromCompiled('end', context), + networkBad: context.bad, genericCosmeticFilters: context.genericCosmeticFilters, genericCosmeticExceptions: context.genericCosmeticExceptions, specificCosmetic: context.specificCosmeticFilters, diff --git a/src/js/static-net-filtering.js b/src/js/static-net-filtering.js index 3aad7fb10193f..d08db1e51c8e5 100644 --- a/src/js/static-net-filtering.js +++ b/src/js/static-net-filtering.js @@ -4324,7 +4324,7 @@ StaticNetFilteringEngine.prototype.dnrFromCompiled = function(op, context, ...ar if ( op === 'begin' ) { Object.assign(context, { good: new Set(), - bad: new Set(), + bad: new Set(context.bad), invalid: new Set(), filterCount: 0, acceptedFilterCount: 0, From 83d62b9aca07c727fd729fa42ecfbd1437a4a20a Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Thu, 24 Apr 2025 13:44:06 -0400 Subject: [PATCH 0864/1099] Make Firefox dev build auto-update --- dist/firefox/updates.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dist/firefox/updates.json b/dist/firefox/updates.json index 86e6782f693ea..ab4add78150a7 100644 --- a/dist/firefox/updates.json +++ b/dist/firefox/updates.json @@ -3,9 +3,9 @@ "uBlock0@raymondhill.net": { "updates": [ { - "version": "1.63.3.18", + "version": "1.63.3.19", "browser_specific_settings": { "gecko": { "strict_min_version": "92.0" } }, - "update_link": "https://github.com/gorhill/uBlock/releases/download/1.63.3b18/uBlock0_1.63.3b18.firefox.signed.xpi" + "update_link": "https://github.com/gorhill/uBlock/releases/download/1.63.3b19/uBlock0_1.63.3b19.firefox.signed.xpi" } ] } From 203b2235aaee886bd74de48ba6b0e36bb63bc096 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Thu, 24 Apr 2025 19:32:40 -0400 Subject: [PATCH 0865/1099] [mv3] Reflect no-filtering mode on the toolbar icon Related issue: https://github.com/uBlockOrigin/uBOL-home/issues/198 --- platform/mv3/chromium/manifest.json | 4 +- platform/mv3/extension/img/icon_128_off.png | Bin 0 -> 3004 bytes platform/mv3/extension/img/icon_16_off.png | Bin 0 -> 446 bytes platform/mv3/extension/img/icon_32_off.png | Bin 0 -> 802 bytes platform/mv3/extension/img/icon_64_off.png | Bin 0 -> 1483 bytes platform/mv3/extension/js/action.js | 120 ++++++++++++++++++++ platform/mv3/extension/js/background.js | 56 +++------ platform/mv3/extension/js/ext.js | 1 - platform/mv3/extension/js/mode-manager.js | 33 +++--- platform/mv3/extension/js/utils.js | 36 +++++- platform/mv3/firefox/manifest.json | 4 +- platform/mv3/safari/manifest.json | 4 +- 12 files changed, 202 insertions(+), 56 deletions(-) create mode 100644 platform/mv3/extension/img/icon_128_off.png create mode 100644 platform/mv3/extension/img/icon_16_off.png create mode 100644 platform/mv3/extension/img/icon_32_off.png create mode 100644 platform/mv3/extension/img/icon_64_off.png create mode 100644 platform/mv3/extension/js/action.js diff --git a/platform/mv3/chromium/manifest.json b/platform/mv3/chromium/manifest.json index f42001a9d06fa..c633619b6193d 100644 --- a/platform/mv3/chromium/manifest.json +++ b/platform/mv3/chromium/manifest.json @@ -40,7 +40,9 @@ "activeTab", "declarativeNetRequest", "scripting", - "storage" + "storage", + "tabs", + "webNavigation" ], "short_name": "uBO Lite", "storage": { diff --git a/platform/mv3/extension/img/icon_128_off.png b/platform/mv3/extension/img/icon_128_off.png new file mode 100644 index 0000000000000000000000000000000000000000..f878785974fcf94da46916f4582048b703cb4e7a GIT binary patch literal 3004 zcmai0hf~wb8r>xHUZhuppmYS1NJkCQ%LNi8AcQAPX^|^MQ3?`=~i*B-7sFd-1th(3(sW1lP>yL645EPyrhO=y>m8}B*R zMNfA8-e1YMeNtM~?k8=#GkVD6a$d{iTE@`N$fBj|=0lsegj`*y#*VndQYwuq>pC8Q zD@^>uE;p`&N^u|Wbj(M8OB9s5MkFdzsew3Fr+do%^33&nP!TzwKp_0Oc=4joe(dSf zu;%7w8suAi8k;Ty9(Kh@F>le^hshAB6s zN)ZK_rkMxdtLyfMC}zRSMiGDh=|Q^@$-@KBfDIr^xyb)u;*9EravK7NC@X`5gRPXh zr%#je^72GTVlgh*-&a6x0p*|&Pc3STJN&((Vx8{rFRK5E!Ts(ODem|MNE`}A>W6@& zx^)lBI}*&AiHV7o^z?LFTIIusV$|yDJC=2vj1=TZFsPs01x%58KH|=gIgh!yIo`;$ zd-v|uTxdYT1rT+xWh_Gh0ey5V;-2N4zI^C=@v6C_O8pRu+I^al9b#A ze-QbZ$w{w*LF;5(uVIJO3WHYin!M@@xrF*3{JWJNO*QdmYqor&}GVj~)A37tt*5wH3D**D>h@*5(l zzb7ap#68%3gH@ac2dJo11Y>WIS`gZqCqPuzlY#s|Hq3#Gy#Pd@=SaQ`|Gyi3c2TEB zQ)`sD+$|$d)exaobf}90|dbePy?DKXqAW@Lx*&K&}G>{jzJd6SVVt_OoRXZCd z0MroW5#k~SpuF~Nx`47;I=fC9xSm416#$jIdf?Rg+Vv=SS?UVG|F2T86|t9Ao+@@; z@m$MGB?E98T3nBDIH6Ff?p9f_hS*BSAPOvUvo-4;TO>uV}j00jo%6dLgn4jtdy+fyqQ8r}Lf3Ts~h#eK^xq1;U4TA|@^DR9`O zC2+NLKYnNh&<*xZ!uNApm@ z6Hl*z^Kj^AcXzj1qtJ(ysV3N`mlB|8ND5UFnfS^(yl4){`!mYy(+WzC(p5}gF;cyH zXHWMCzznp8LJ)Lm5$zKv^hBLV9)+wE!gSE{xRDIj$;{JF#&)q$SdoojE)Xf z9S&UOw!|98Bl>c5G5l8ISWSG53!S{^ILpS?-Q3tH4TuQZSd3`v?dw}cP!vZGGuB^) z!{<_@bkRZ%jatIyh;1inFoEsLuBfOG&E{0wne!nKWMLHfRYt}|89+1{_HDK}mt8y) z*5Md`NbuGx0Tgr-@9WrY!?7)Q6I+RX+LL~Ld6qit`)S(bn@i=xbyW(&XoU%0^ z4W?v(=oDv2O?7ojQttD63#y_ewY55M!w0=`E%KwaDQ-Anl1OXkvuFRTXvuJ+(CBT1 z5=f~rlebM0Abv0cv+#nN=g$?;QB&1yV)vzM)%WXchH^p)^dt2zii?%yc{m(yL28_E zn6`!nAHp8XxLerMLQ6f4w2oJew^TNa`+akPB53y8QdxxSadNM9l#+!`q?f1>$Cx`F zpSU!TALu1trlFznluG48yiDqS(;6c&Bs(|WD*xKj$jGS1 zqe+e9g`VqTer9BjxOa=&cr(=A($Z41y&bh+>OvsAf2eBzH{^!g_!~=!b^YE#y`jQ9 zcjCzVsrJZVyEx?!bJ6o=xsM)=BgT?@;~$tGmP`D=MXJ^Fw|2I*O(|t4^vXZnu;2)j z%38?2s*r8`XBid#3&hk`SNBbhj64)u?qh%Fi&w8q;id>v6=XKv*g5H$39e9Zg`;+k z4H0nSh@IWSEpw%vPqE>5v*t|j9=K6k+~56k)GM+~1A5hcA%%_C2ir5Ys5(=&(qDWL z^v6e8UbC>JyH2iv1moea3CTR%=+g~M?DDmiD|+(8mhDTVPhF-h3#*Q>R2d!p1W)EE z=I;7Jk3rPx^ddg;6XVMOX5RGLT;wDR_L}?lQDg(Qv$!E=es7q z8Nl@CiHM(vR|_ec=3d`re!knj%@$Qj9WUvIeO=*{UXSB%$FG`&;%L0P26~s+lIKz7 zRG2rm{Lj!(;5ZdWOI;c8(M2ATTul{>#$0hyBqb)CKG*1Pe@+YCaG;sdbCw-!L}6gT zJ&_g=cN;;bDLXVL9FkE4lH=Q;lJ<6jy`9}YSph1WAlO)Is3&Vv32;N$=FaL&&cS+* zQqQ8Pm6g@l*ceB=3WS)T*rwxMaWn3b&rpQknU+w`uWf%Wg&5V&uPsqxfAAt`kP5O< z+v7ivuVt)XuqwAES8;K3)7bvviT;FyjZRLA$&c2uxc#kf2xyI5Wp(VC;$6q-rB-*8 zQjBD4by->0s-)Ho?`0+3q@-T8+vnbMM-fr{^Ru+r=&0!E9aSHT zOYa2mwO%rQelw5Ol5ITodycD87%wIiYPf!bU7s7@Dzz(ZJ}(P)%!wOUs~2tAj}ou*Q$#z++qhU;v;f%3IL&ilSHmpnCtj0N7jHuto;2{sqx! zv=Q`zcsySB4NTL#H4J08E^HXakaK?L8xT>Sb6(aoZTcUq>-y9*%`y@7{Vsf~*XtcL zo6YOVWRhM6hr{7+KA%5Hr_+x=%CsmcWvo`KU3RmXOqGvr8e=hHm;;l;9!@>7s4|3W6YXEx1rY!G+r{bmKzE zN)$xWO*ef&v5mBJj20DR8|qkFn;;>bq%FyDlZ4nrO!JtH2L^`A;r`$J_uM=805t|P z`y-LaoAveeRc5|i>pWD0uv1ml`}6bjlLrR}X8=Hwq@&^C;TfmXd7FqnH3fi~UGaGQ zW;h(4+}_?kUn$3Kw}1C~z0a*y>tiDNTr~hQivS$?F$(}Q04soVs;XYt+uIx1+}!l; z@9&$dnY5#$Bh}N>6SmoGtKHq*Ujgg@hyzFgNCIF0F(S$p0OW;*g)gyKtT;&~lR+cH zXfzhz9S#SNj*j*bQM9Gta3Yc5OeRBUQj~sBB9UMKIRFp}0QpCkAflpP5gw7GY7V=s;Ur z+fJoqP170(0*3bhE{6Hd_Y3 zOc?+XeRR9stIY;*yWMYz=tCt3(A(SlLKMZOiHo8LS(ab^aVDI|nI0b>Uu+hD&*xhN zF#T5mBFZ@&jwzSRwbf{Gm&>(fx7()*Ct%q&EP;qH^R;X?`!pJj%8d}0WjQuBHg=te zB31VV%sikd%9E9qm5cR=4-O709*^e+5rt3Pm@xC%WHR|E6bc1Wsg!ie(=P4z`-2vX zgW1{HtGm0q)=D{D zU0rdX&-YA{q&q}Z)la0B=Cn^$)jM-@b5}G?Gvxnn$0H*nA*a)Mmx$i$bE#*ZP!wfZ gQIut7o~U)6-xkjoYYdk`2mk;807*qoM6N<$f)fL13jhEB literal 0 HcmV?d00001 diff --git a/platform/mv3/extension/img/icon_64_off.png b/platform/mv3/extension/img/icon_64_off.png new file mode 100644 index 0000000000000000000000000000000000000000..d9a22955a509c6a9e15c587139ed6e452d06b187 GIT binary patch literal 1483 zcmV;+1vL7JP)$7*-}9W) z_y6njKJR(IV3ABIwOJ|kn^Nk$QtFMxsz=Hq2zLWb-QC^gXV0Ep0Z>p-@VQdzWni<2 z3?{NZl0e~BV5hF@uQoR~=jpnB*L6+P*tBWWpr&c9z)lgFGz|cy)N8=@g9i_;85tST z!ue)pWlh)D*Y^V70DDA4B_#l*lm&PNc+2bcmJSRIq{ni9et!Oq%F4=op)*Xu2gRd@&xla-Z~=Ya2lFGOT~NdZtw3Gle_Z45|U8ub~_~{C8I?}MSlZ-0apM&FboU3=#+gn3=(sifNjL-EJo}HT71R)oQ)YfdBLSF5OG`6t6G%@_*CPV3*=)Lrn?S?}2w1IF z)8>Gbl$0spdN=@nZcZqg)uy;PS$Z4F%ohcvR7^`!TefUjR7FiqO|iI7&K&JpLOGc@ z0)qFv9*1z6Ffo9MXaEFE3?L8<0KbU=_@e>vkEUs690&o7+|xCN@cqN-bTT|V9FHu# zXy$Y}0WL%n0bO}{d8SK*`T6-1KxZ@nPM4OJ4x0@?NlD2ta3&%E5g7)?Oqc*i4Q&x4 z0N}C-0bB`HI2(ZDE|<$R%5!pZ0Dg)EK+F2|>#v#xKt)BxRiGv02r*Qlh+I%gT?9-K z2AP?e7e%B$mIweiG;=paGTO?OE2n`&vt17dV7JHP8Ax^jH8nK@!0wmgvt`JHq*d1t22-0iTwYm5nBw`111dQQ(t1_XN%* z0uYgXg@uLfZnryWW{Prha;8?VUj3bj?2F`UiSn6SJswYYMn=Yx7oU-lL2Yeq7w~2@ z{!sxC5gqU}HZ~r2I2=oM_1JE=tH#F0F5m^D&XKjn@&igK8*sR-t?ikyu`$b15}%co zrRwYJPXNywxr+q{U_eL%zU}PntnTaUTd@?x7Zw%<%F4=q1R6yorn!tcZwFCIy?5=} zwQcR~?e0bH>MmzzXMZUoyXJELy!OK=rJe@%?BBnCO&}1EM7Q^i8#kT--Vu?X=lYz5 zI51%Y_V)Mp*B(1|?7>8cU$<`EXi-s-7kE8VKOc7hcYvq`K5A)cS>yNn;}Q)HheI_q zG@JoG5Ro4i;+YFtQ6m~Y^ZWfTwzjseQfgs(Oc9~Fy83c%Zmt*DI^TaiZUF88@dU89 zqod=g!NI|K9h%C^%ezrkRrM?Iwuqcw=(@%=aT(_Y-)(DaD;gUclNiIDhK7dUfv<_< z`N6nnZlhUztn2zq&CSh^MLKU}u~?|7sqwj7uA{(q5sB+kT_TUpJ_79S?d`4T>gvi0 z ({ + originAndPathMatches: `^https?://(.+\\.)?${a}/` + })), + }); + + if ( wakeup === undefined ) { return; } + + // If waking up, update icon for existing tabs + const tabs = await browser.tabs.query({ + url: Array.from(toToggle).map(a => `*://*.${a}/*`) + }).catch(( ) => ([])); + + for ( const tab of tabs ) { + toggleToolbarIcon(tab.id); + } +} diff --git a/platform/mv3/extension/js/background.js b/platform/mv3/extension/js/background.js index 6af564822ac0a..4668b467f98e6 100644 --- a/platform/mv3/extension/js/background.js +++ b/platform/mv3/extension/js/background.js @@ -21,6 +21,7 @@ import { MODE_BASIC, + MODE_NONE, MODE_OPTIMAL, getDefaultFilteringMode, getFilteringMode, @@ -38,6 +39,7 @@ import { import { broadcastMessage, + gotoURL, hasBroadHostPermissions, hostnamesFromMatches, } from './utils.js'; @@ -46,7 +48,6 @@ import { browser, localRead, localRemove, localWrite, runtime, - windows, } from './ext.js'; import { @@ -76,6 +77,7 @@ import { import { dnr } from './ext-compat.js'; import { registerInjectables } from './scripting-manager.js'; +import { syncToolbarIcon } from './action.js'; /******************************************************************************/ @@ -133,36 +135,6 @@ async function onPermissionsAdded(permissions) { /******************************************************************************/ -async function gotoURL(url, type) { - const pageURL = new URL(url, runtime.getURL('/')); - const tabs = await browser.tabs.query({ - url: pageURL.href, - windowType: type !== 'popup' ? 'normal' : 'popup' - }); - - if ( Array.isArray(tabs) && tabs.length !== 0 ) { - const { windowId, id } = tabs[0]; - return Promise.all([ - browser.windows.update(windowId, { focused: true }), - browser.tabs.update(id, { active: true }), - ]); - } - - if ( type === 'popup' ) { - return windows.create({ - type: 'popup', - url: pageURL.href, - }); - } - - return browser.tabs.create({ - active: true, - url: pageURL.href, - }); -} - -/******************************************************************************/ - function onMessage(request, sender, callback) { // Does not require trusted origin. @@ -340,12 +312,18 @@ function onMessage(request, sender, callback) { break; case 'setFilteringMode': { - getFilteringMode(request.hostname).then(actualLevel => { - if ( request.level === actualLevel ) { return actualLevel; } + let trustedSitesChanged = false; + getFilteringMode(request.hostname).then(beforeLevel => { + if ( request.level === beforeLevel ) { return beforeLevel; } + trustedSitesChanged = beforeLevel === MODE_NONE; return setFilteringMode(request.hostname, request.level); - }).then(actualLevel => { + }).then(afterLevel => { registerInjectables(); - callback(actualLevel); + trustedSitesChanged ||= afterLevel === MODE_NONE; + if ( trustedSitesChanged ) { + syncToolbarIcon(); + } + callback(afterLevel); }); return true; } @@ -378,6 +356,7 @@ function onMessage(request, sender, callback) { case 'setTrustedSites': setTrustedSites(request.hostnames).then(( ) => { registerInjectables(); + syncToolbarIcon(true); return Promise.all([ getDefaultFilteringMode(), getTrustedSites(), @@ -404,7 +383,7 @@ function onMessage(request, sender, callback) { return true; case 'showMatchedRules': - windows.create({ + browser.windows.create({ type: 'popup', url: `/matched-rules.html?tab=${request.tabId}`, }); @@ -436,7 +415,7 @@ function onCommand(command, tab) { /******************************************************************************/ -async function launch() { +async function startSession() { const currentVersion = getCurrentVersion(); const isNewVersion = currentVersion !== rulesetConfig.version; @@ -499,9 +478,10 @@ async function start() { await loadRulesetConfig(); if ( process.wakeupRun === false ) { - await launch(); + await startSession(); } + syncToolbarIcon(process.wakeupRun); toggleDeveloperMode(rulesetConfig.developerMode); } diff --git a/platform/mv3/extension/js/ext.js b/platform/mv3/extension/js/ext.js index ea1ab61ced874..a2e6f18f500a4 100644 --- a/platform/mv3/extension/js/ext.js +++ b/platform/mv3/extension/js/ext.js @@ -26,7 +26,6 @@ import { webext } from './ext-compat.js'; export const browser = webext; export const i18n = browser.i18n; export const runtime = browser.runtime; -export const windows = browser.windows; /******************************************************************************/ diff --git a/platform/mv3/extension/js/mode-manager.js b/platform/mv3/extension/js/mode-manager.js index 41abc057d9983..6affead5799af 100644 --- a/platform/mv3/extension/js/mode-manager.js +++ b/platform/mv3/extension/js/mode-manager.js @@ -334,13 +334,18 @@ export async function getTrustedSites() { } export async function setTrustedSites(hostnames) { - const filteringModes = await getFilteringModeDetails(); + const [ + filteringModes, + hasOmnipotence, + ] = await Promise.all([ + getFilteringModeDetails(), + hasBroadHostPermissions(), + ]); const { none } = filteringModes; const hnSet = new Set(hostnames); let modified = false; - // Set default mode to Basic when removing No-filtering as default mode if ( none.has('all-urls') && hnSet.has('all-urls') === false ) { - applyFilteringMode(filteringModes, 'all-urls', MODE_BASIC); + applyFilteringMode(filteringModes, 'all-urls', hasOmnipotence ? MODE_OPTIMAL : MODE_BASIC); modified = true; } for ( const hn of none ) { @@ -389,16 +394,18 @@ export async function syncWithBrowserPermissions() { const afterMode = await getDefaultFilteringMode(); if ( afterMode > MODE_BASIC ) { return afterMode !== beforeMode; } const filteringModes = await getFilteringModeDetails(); - const { optimal, complete } = filteringModes; - for ( const hn of optimal ) { - if ( allowedHostnames.has(hn) ) { continue; } - optimal.delete(hn); - modified = true; - } - for ( const hn of complete ) { - if ( allowedHostnames.has(hn) ) { continue; } - complete.delete(hn); - modified = true; + if ( allowedHostnames.has('all-urls') === false ) { + const { optimal, complete } = filteringModes; + for ( const hn of optimal ) { + if ( allowedHostnames.has(hn) ) { continue; } + optimal.delete(hn); + modified = true; + } + for ( const hn of complete ) { + if ( allowedHostnames.has(hn) ) { continue; } + complete.delete(hn); + modified = true; + } } await writeFilteringModeDetails(filteringModes); return modified; diff --git a/platform/mv3/extension/js/utils.js b/platform/mv3/extension/js/utils.js index 00451f15f9b29..94ed121b2d960 100644 --- a/platform/mv3/extension/js/utils.js +++ b/platform/mv3/extension/js/utils.js @@ -19,7 +19,10 @@ Home: https://github.com/gorhill/uBlock */ -import { browser } from './ext.js'; +import { + browser, + runtime, +} from './ext.js'; /******************************************************************************/ @@ -149,6 +152,36 @@ async function hasBroadHostPermissions() { /******************************************************************************/ +async function gotoURL(url, type) { + const pageURL = new URL(url, runtime.getURL('/')); + const tabs = await browser.tabs.query({ + url: pageURL.href, + windowType: type !== 'popup' ? 'normal' : 'popup' + }); + + if ( Array.isArray(tabs) && tabs.length !== 0 ) { + const { windowId, id } = tabs[0]; + return Promise.all([ + browser.windows.update(windowId, { focused: true }), + browser.tabs.update(id, { active: true }), + ]); + } + + if ( type === 'popup' ) { + return browser.windows.create({ + type: 'popup', + url: pageURL.href, + }); + } + + return browser.tabs.create({ + active: true, + url: pageURL.href, + }); +} + +/******************************************************************************/ + export { broadcastMessage, parsedURLromOrigin, @@ -161,4 +194,5 @@ export { matchesFromHostnames, hostnamesFromMatches, hasBroadHostPermissions, + gotoURL, }; diff --git a/platform/mv3/firefox/manifest.json b/platform/mv3/firefox/manifest.json index c2dd5a3aac3a9..73d2fa1dd98f6 100644 --- a/platform/mv3/firefox/manifest.json +++ b/platform/mv3/firefox/manifest.json @@ -52,7 +52,9 @@ "activeTab", "declarativeNetRequest", "scripting", - "storage" + "storage", + "tabs", + "webNavigation" ], "short_name": "uBO Lite", "version": "1.0", diff --git a/platform/mv3/safari/manifest.json b/platform/mv3/safari/manifest.json index ba7cc7715f37d..a24dfb8850476 100644 --- a/platform/mv3/safari/manifest.json +++ b/platform/mv3/safari/manifest.json @@ -39,7 +39,9 @@ "declarativeNetRequest", "declarativeNetRequestWithHostAccess", "scripting", - "storage" + "storage", + "tabs", + "webNavigation" ], "short_name": "uBO Lite", "version": "1.0", From ed66a8d1573b8a40e89d20e4b15e4756e28699e2 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Thu, 24 Apr 2025 19:47:29 -0400 Subject: [PATCH 0866/1099] [mv3] Code review --- platform/mv3/extension/js/action.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform/mv3/extension/js/action.js b/platform/mv3/extension/js/action.js index f196dee5ed60f..3772e5cbf01b8 100644 --- a/platform/mv3/extension/js/action.js +++ b/platform/mv3/extension/js/action.js @@ -103,7 +103,7 @@ export async function syncToolbarIcon(wakeup) { webNavigation.onCommitted.addListener(toolbarIconListener, { url: Array.from(toToggle).map(a => ({ - originAndPathMatches: `^https?://(.+\\.)?${a}/` + originAndPathMatches: `^https?://([^.]+\\.)*${a}/` })), }); From 84ad64daea237d45a5c1e4d59250e42accbc910d Mon Sep 17 00:00:00 2001 From: anewuser Date: Fri, 25 Apr 2025 08:30:17 -0300 Subject: [PATCH 0867/1099] Update README.md (#3937) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 18e6aacad9e6b..e069c7cdccc1b 100644 --- a/README.md +++ b/README.md @@ -148,7 +148,7 @@ If you ever want to contribute something, think about the people working hard to [Peter Lowe's Blocklist]: https://pgl.yoyo.org/adservers/ [Malicious Blocklist]: https://gitlab.com/malware-filter/urlhaus-filter#malicious-url-blocklist -[Performance]: https://www.debugbear.com/blog/chrome-extension-performance-2021#how-do-ad-blockers-and-privacy-tools-affect-browser-performance +[Performance]: https://www.debugbear.com/blog/chrome-extensions-website-performance#the-impact-of-ad-blocking-on-website-performance [EasyPrivacy]: https://easylist.to/#easyprivacy [Thunderbird]: https://addons.thunderbird.net/thunderbird/addon/ublock-origin/ [Chrome Dev]: https://chromewebstore.google.com/detail/ublock-origin-development/cgbcahbpdhpcegmbfconppldiemgcoii From e0290608896e9492bed868440ea4d3cad33d5f0f Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Fri, 25 Apr 2025 07:46:46 -0400 Subject: [PATCH 0868/1099] [mv3] Use safer env variable names Related feedback: https://github.com/uBlockOrigin/uBOL-home/issues/327#issuecomment-2823012715 --- tools/make-mv3.sh | 128 +++++++++++++++++++++++----------------------- 1 file changed, 64 insertions(+), 64 deletions(-) diff --git a/tools/make-mv3.sh b/tools/make-mv3.sh index 5cc0d4df3df14..66b7c169782f4 100755 --- a/tools/make-mv3.sh +++ b/tools/make-mv3.sh @@ -40,7 +40,7 @@ echo "PLATFORM=$PLATFORM" echo "TAGNAME=$TAGNAME" echo "BEFORE=$BEFORE" -DES="dist/build/uBOLite.$PLATFORM" +UBOL_DIR="dist/build/uBOLite.$PLATFORM" if [ "$PLATFORM" = "edge" ]; then MANIFEST_DIR="chromium" @@ -48,16 +48,16 @@ else MANIFEST_DIR="$PLATFORM" fi -rm -rf $DES +rm -rf $UBOL_DIR -mkdir -p $DES -cd $DES -DES=$(pwd) +mkdir -p $UBOL_DIR +cd $UBOL_DIR +UBOL_DIR=$(pwd) cd - > /dev/null -mkdir -p "$DES"/css/fonts -mkdir -p "$DES"/js -mkdir -p "$DES"/img +mkdir -p "$UBOL_DIR"/css/fonts +mkdir -p "$UBOL_DIR"/js +mkdir -p "$UBOL_DIR"/img if [ -n "$UBO_VERSION" ]; then UBO_REPO="https://github.com/gorhill/uBlock.git" @@ -74,79 +74,79 @@ else fi echo "*** uBOLite.mv3: Copying common files" -cp -R "$UBO_DIR"/src/css/fonts/* "$DES"/css/fonts/ -cp "$UBO_DIR"/src/css/themes/default.css "$DES"/css/ -cp "$UBO_DIR"/src/css/common.css "$DES"/css/ -cp "$UBO_DIR"/src/css/dashboard-common.css "$DES"/css/ -cp "$UBO_DIR"/src/css/fa-icons.css "$DES"/css/ +cp -R "$UBO_DIR"/src/css/fonts/* "$UBOL_DIR"/css/fonts/ +cp "$UBO_DIR"/src/css/themes/default.css "$UBOL_DIR"/css/ +cp "$UBO_DIR"/src/css/common.css "$UBOL_DIR"/css/ +cp "$UBO_DIR"/src/css/dashboard-common.css "$UBOL_DIR"/css/ +cp "$UBO_DIR"/src/css/fa-icons.css "$UBOL_DIR"/css/ -cp "$UBO_DIR"/src/js/dom.js "$DES"/js/ -cp "$UBO_DIR"/src/js/fa-icons.js "$DES"/js/ -cp "$UBO_DIR"/src/js/i18n.js "$DES"/js/ -cp "$UBO_DIR"/src/js/urlskip.js "$DES"/js/ -cp "$UBO_DIR"/src/lib/punycode.js "$DES"/js/ +cp "$UBO_DIR"/src/js/dom.js "$UBOL_DIR"/js/ +cp "$UBO_DIR"/src/js/fa-icons.js "$UBOL_DIR"/js/ +cp "$UBO_DIR"/src/js/i18n.js "$UBOL_DIR"/js/ +cp "$UBO_DIR"/src/js/urlskip.js "$UBOL_DIR"/js/ +cp "$UBO_DIR"/src/lib/punycode.js "$UBOL_DIR"/js/ -cp -R "$UBO_DIR/src/img/flags-of-the-world" "$DES"/img +cp -R "$UBO_DIR/src/img/flags-of-the-world" "$UBOL_DIR"/img -cp LICENSE.txt "$DES"/ +cp LICENSE.txt "$UBOL_DIR"/ echo "*** uBOLite.mv3: Copying mv3-specific files" -cp platform/mv3/"$MANIFEST_DIR"/manifest.json "$DES"/ -cp platform/mv3/extension/*.html "$DES"/ -cp platform/mv3/extension/*.json "$DES"/ -cp platform/mv3/extension/css/* "$DES"/css/ -cp -R platform/mv3/extension/js/* "$DES"/js/ -cp platform/mv3/"$PLATFORM"/ext-compat.js "$DES"/js/ 2>/dev/null || : -cp platform/mv3/extension/img/* "$DES"/img/ -cp -R platform/mv3/extension/_locales "$DES"/ -cp platform/mv3/README.md "$DES/" +cp platform/mv3/"$MANIFEST_DIR"/manifest.json "$UBOL_DIR"/ +cp platform/mv3/extension/*.html "$UBOL_DIR"/ +cp platform/mv3/extension/*.json "$UBOL_DIR"/ +cp platform/mv3/extension/css/* "$UBOL_DIR"/css/ +cp -R platform/mv3/extension/js/* "$UBOL_DIR"/js/ +cp platform/mv3/"$PLATFORM"/ext-compat.js "$UBOL_DIR"/js/ 2>/dev/null || : +cp platform/mv3/extension/img/* "$UBOL_DIR"/img/ +cp -R platform/mv3/extension/_locales "$UBOL_DIR"/ +cp platform/mv3/README.md "$UBOL_DIR/" echo "*** uBOLite.mv3: Generating rulesets" -TMPDIR=$(mktemp -d) -mkdir -p "$TMPDIR" -./tools/make-nodejs.sh "$TMPDIR" -cp platform/mv3/*.json "$TMPDIR"/ -cp platform/mv3/*.js "$TMPDIR"/ -cp platform/mv3/*.mjs "$TMPDIR"/ -cp platform/mv3/extension/js/utils.js "$TMPDIR"/js/ -cp -R "$UBO_DIR"/src/js/resources "$TMPDIR"/js/ -cp -R platform/mv3/scriptlets "$TMPDIR"/ -mkdir -p "$TMPDIR"/web_accessible_resources -cp "$UBO_DIR"/src/web_accessible_resources/* "$TMPDIR"/web_accessible_resources/ -cd "$TMPDIR" -node --no-warnings make-rulesets.js output="$DES" platform="$PLATFORM" +UBOL_BUILD_DIR=$(mktemp -d) +mkdir -p "$UBOL_BUILD_DIR" +./tools/make-nodejs.sh "$UBOL_BUILD_DIR" +cp platform/mv3/*.json "$UBOL_BUILD_DIR"/ +cp platform/mv3/*.js "$UBOL_BUILD_DIR"/ +cp platform/mv3/*.mjs "$UBOL_BUILD_DIR"/ +cp platform/mv3/extension/js/utils.js "$UBOL_BUILD_DIR"/js/ +cp -R "$UBO_DIR"/src/js/resources "$UBOL_BUILD_DIR"/js/ +cp -R platform/mv3/scriptlets "$UBOL_BUILD_DIR"/ +mkdir -p "$UBOL_BUILD_DIR"/web_accessible_resources +cp "$UBO_DIR"/src/web_accessible_resources/* "$UBOL_BUILD_DIR"/web_accessible_resources/ +cd "$UBOL_BUILD_DIR" +node --no-warnings make-rulesets.js output="$UBOL_DIR" platform="$PLATFORM" if [ -n "$BEFORE" ]; then echo "*** uBOLite.mv3: salvaging rule ids to minimize diff size" echo " before=$BEFORE/$PLATFORM" - echo " after=$DES" - node salvage-ruleids.mjs before="$BEFORE"/"$PLATFORM" after="$DES" + echo " after=$UBOL_DIR" + node salvage-ruleids.mjs before="$BEFORE"/"$PLATFORM" after="$UBOL_DIR" fi cd - > /dev/null -rm -rf "$TMPDIR" +rm -rf "$UBOL_BUILD_DIR" # For Edge, declared rulesets must be at package root if [ "$PLATFORM" = "edge" ]; then echo "*** uBOLite.edge: Modify reference implementation for Edge compatibility" - mv "$DES"/rulesets/main/* "$DES/" - rmdir "$DES/rulesets/main" + mv "$UBOL_DIR"/rulesets/main/* "$UBOL_DIR/" + rmdir "$UBOL_DIR/rulesets/main" node tools/make-edge.mjs fi echo "*** uBOLite.$PLATFORM: extension ready" -echo "Extension location: $DES/" +echo "Extension location: $UBOL_DIR/" # Local build if [ -z "$TAGNAME" ]; then # Enable DNR rule debugging tmp=$(mktemp) jq '.permissions += ["declarativeNetRequestFeedback"]' \ - "$DES/manifest.json" > "$tmp" \ - && mv "$tmp" "$DES/manifest.json" + "$UBOL_DIR/manifest.json" > "$tmp" \ + && mv "$tmp" "$UBOL_DIR/manifest.json" # Use a different extension id than the official one if [ "$PLATFORM" = "firefox" ]; then tmp=$(mktemp) - jq '.browser_specific_settings.gecko.id = "uBOLite.dev@raymondhill.net"' "$DES/manifest.json" > "$tmp" \ - && mv "$tmp" "$DES/manifest.json" + jq '.browser_specific_settings.gecko.id = "uBOLite.dev@raymondhill.net"' "$UBOL_DIR/manifest.json" > "$tmp" \ + && mv "$tmp" "$UBOL_DIR/manifest.json" fi fi @@ -157,21 +157,21 @@ if [ "$FULL" = "yes" ]; then fi echo "*** uBOLite.mv3: Creating publishable package..." if [ -z "$TAGNAME" ]; then - TAGNAME="uBOLite_$(jq -r .version "$DES"/manifest.json)" + TAGNAME="uBOLite_$(jq -r .version "$UBOL_DIR"/manifest.json)" else tmp=$(mktemp) - jq --arg version "${TAGNAME:8}" '.version = $version' "$DES/manifest.json" > "$tmp" \ - && mv "$tmp" "$DES/manifest.json" + jq --arg version "${TAGNAME:8}" '.version = $version' "$UBOL_DIR/manifest.json" > "$tmp" \ + && mv "$tmp" "$UBOL_DIR/manifest.json" fi - PACKAGENAME="$TAGNAME.$PLATFORM.mv3.$EXTENSION" - TMPDIR=$(mktemp -d) - mkdir -p "$TMPDIR" - cp -R "$DES"/* "$TMPDIR"/ - cd "$TMPDIR" > /dev/null + UBOL_PACKAGE_NAME="$TAGNAME.$PLATFORM.mv3.$EXTENSION" + UBOL_PACKAGE_DIR=$(mktemp -d) + mkdir -p "$UBOL_PACKAGE_DIR" + cp -R "$UBOL_DIR"/* "$UBOL_PACKAGE_DIR"/ + cd "$UBOL_PACKAGE_DIR" > /dev/null rm -f ./log.txt - zip "$PACKAGENAME" -qr ./* + zip "$UBOL_PACKAGE_NAME" -qr ./* cd - > /dev/null - cp "$TMPDIR"/"$PACKAGENAME" dist/build/ - rm -rf "$TMPDIR" - echo "Package location: $(pwd)/dist/build/$PACKAGENAME" + cp "$UBOL_PACKAGE_DIR"/"$UBOL_PACKAGE_NAME" dist/build/ + rm -rf "$UBOL_PACKAGE_DIR" + echo "Package location: $(pwd)/dist/build/$UBOL_PACKAGE_NAME" fi From e5efe64d20070f14bbf756d4deaa8f6c9eb16668 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Fri, 25 Apr 2025 08:06:19 -0400 Subject: [PATCH 0869/1099] [mv3] Code review --- platform/mv3/extension/js/action.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform/mv3/extension/js/action.js b/platform/mv3/extension/js/action.js index 3772e5cbf01b8..a76210cef4fff 100644 --- a/platform/mv3/extension/js/action.js +++ b/platform/mv3/extension/js/action.js @@ -103,7 +103,7 @@ export async function syncToolbarIcon(wakeup) { webNavigation.onCommitted.addListener(toolbarIconListener, { url: Array.from(toToggle).map(a => ({ - originAndPathMatches: `^https?://([^.]+\\.)*${a}/` + originAndPathMatches: `^https?://([^.].*\\.)?${a.replaceAll('.', '\\.')}/` })), }); From a56e13156f1fd34a5abfdc937e7c6d25e871d7d9 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Fri, 25 Apr 2025 11:25:50 -0400 Subject: [PATCH 0870/1099] [mv3] Add more managed policies "showBlockedCount": Boolean, to enable/disable the badge count on the toolbar icon. "strictBlockMode": Boolean. to enable/disable strict-blocking. Related issues/discussion: - https://github.com/uBlockOrigin/uBOL-home/discussions/35#discussioncomment-12567804 - https://github.com/uBlockOrigin/uBOL-home/issues/334 --- platform/mv3/extension/js/admin.js | 76 ++++++++++++++++++-- platform/mv3/extension/js/background.js | 4 ++ platform/mv3/extension/js/config.js | 2 +- platform/mv3/extension/js/ruleset-manager.js | 6 +- platform/mv3/extension/managed_storage.json | 22 ++++-- 5 files changed, 95 insertions(+), 15 deletions(-) diff --git a/platform/mv3/extension/js/admin.js b/platform/mv3/extension/js/admin.js index 7596516177d5e..865f37ddacb97 100644 --- a/platform/mv3/extension/js/admin.js +++ b/platform/mv3/extension/js/admin.js @@ -28,6 +28,7 @@ import { import { enableRulesets, getRulesetDetails, + setStrictBlockMode, } from './ruleset-manager.js'; import { @@ -36,19 +37,74 @@ import { readFilteringModeDetails, } from './mode-manager.js'; +import { + rulesetConfig, + saveRulesetConfig, +} from './config.js'; + import { broadcastMessage } from './utils.js'; import { dnr } from './ext-compat.js'; import { registerInjectables } from './scripting-manager.js'; -import { rulesetConfig } from './config.js'; import { ubolLog } from './debug.js'; /******************************************************************************/ +export async function loadAdminConfig() { + const [ + showBlockedCount, + strictBlockMode, + ] = await Promise.all([ + adminReadEx('showBlockedCount'), + adminReadEx('strictBlockMode'), + ]); + applyAdminConfig({ showBlockedCount, strictBlockMode }); +} + +/******************************************************************************/ + +function applyAdminConfig(config, apply = false) { + const toApply = []; + for ( const [ key, val ] of Object.entries(config) ) { + if ( typeof val !== typeof rulesetConfig[key] ) { continue; } + if ( val === rulesetConfig[key] ) { continue; } + rulesetConfig[key] = val; + toApply.push(key); + } + if ( toApply.length === 0 ) { return; } + saveRulesetConfig(); + if ( apply !== true ) { return; } + while ( toApply.length !== 0 ) { + const key = toApply.pop(); + switch ( key ) { + case 'showBlockedCount': { + if ( typeof dnr.setExtensionActionOptions !== 'function' ) { break; } + const { showBlockedCount } = config; + dnr.setExtensionActionOptions({ + displayActionCountAsBadgeText: showBlockedCount, + }); + broadcastMessage({ showBlockedCount }); + break; + } + case 'strictBlockMode': { + const { strictBlockMode } = config; + setStrictBlockMode(strictBlockMode, true).then(( ) => { + broadcastMessage({ strictBlockMode }); + }); + break; + } + default: + break; + } + } +} + +/******************************************************************************/ + const adminSettings = { - keys: new Set(), + keys: new Map(), timer: undefined, - change(key) { - this.keys.add(key); + change(key, value) { + this.keys.set(key, value); if ( this.timer !== undefined ) { return; } this.timer = self.setTimeout(( ) => { this.timer = undefined; @@ -80,6 +136,16 @@ const adminSettings = { const trustedSites = await getTrustedSites(); broadcastMessage({ trustedSites: Array.from(trustedSites) }); } + if ( this.keys.has('showBlockedCount') ) { + ubolLog('admin setting "showBlockedCount" changed'); + const showBlockedCount = this.keys.get('showBlockedCount'); + applyAdminConfig({ showBlockedCount }, true); + } + if ( this.keys.has('strictBlockMode') ) { + ubolLog('admin setting "strictBlockMode" changed'); + const strictBlockMode = this.keys.get('strictBlockMode'); + applyAdminConfig({ strictBlockMode }, true); + } this.keys.clear(); } }; @@ -142,7 +208,7 @@ export async function adminReadEx(key) { localWrite(adminKey, { data: value }), ]); if ( JSON.stringify(value) === JSON.stringify(cacheValue) ) { return; } - adminSettings.change(key); + adminSettings.change(key, value); }); return cacheValue; } diff --git a/platform/mv3/extension/js/background.js b/platform/mv3/extension/js/background.js index 4668b467f98e6..f97771dcf9297 100644 --- a/platform/mv3/extension/js/background.js +++ b/platform/mv3/extension/js/background.js @@ -35,6 +35,7 @@ import { import { adminReadEx, getAdminRulesets, + loadAdminConfig, } from './admin.js'; import { @@ -419,6 +420,9 @@ async function startSession() { const currentVersion = getCurrentVersion(); const isNewVersion = currentVersion !== rulesetConfig.version; + // Admin settings override user settings + await loadAdminConfig(); + // The default rulesets may have changed, find out new ruleset to enable, // obsolete ruleset to remove. if ( isNewVersion ) { diff --git a/platform/mv3/extension/js/config.js b/platform/mv3/extension/js/config.js index 0986f73397e38..f48533473f189 100644 --- a/platform/mv3/extension/js/config.js +++ b/platform/mv3/extension/js/config.js @@ -46,7 +46,7 @@ export const process = { export async function loadRulesetConfig() { const sessionData = await sessionRead('rulesetConfig'); if ( sessionData ) { - Object.assign(rulesetConfig, sessionData) + Object.assign(rulesetConfig, sessionData); process.wakeupRun = true; return; } diff --git a/platform/mv3/extension/js/ruleset-manager.js b/platform/mv3/extension/js/ruleset-manager.js index 79c299d1a1a98..1b52ca4a5a6cb 100644 --- a/platform/mv3/extension/js/ruleset-manager.js +++ b/platform/mv3/extension/js/ruleset-manager.js @@ -386,9 +386,11 @@ async function excludeFromStrictBlock(hostname, permanent) { return updateSessionRules(); } -async function setStrictBlockMode(state) { +async function setStrictBlockMode(state, force = false) { const newState = Boolean(state); - if ( newState === rulesetConfig.strictBlockMode ) { return; } + if ( force === false ) { + if ( newState === rulesetConfig.strictBlockMode ) { return; } + } rulesetConfig.strictBlockMode = newState; const promises = [ saveRulesetConfig() ]; if ( newState === false ) { diff --git a/platform/mv3/extension/managed_storage.json b/platform/mv3/extension/managed_storage.json index a5ffd877e4c72..85446f2e63cfe 100644 --- a/platform/mv3/extension/managed_storage.json +++ b/platform/mv3/extension/managed_storage.json @@ -7,8 +7,9 @@ "description": "Can be one of \"none\", \"basic\", \"optimal\", \"complete\".", "type": "string" }, - "noFiltering": { - "title": "List of domains for which no filtering should occur", + "disabledFeatures": { + "title": "User interface features to disable", + "description": "A list of tokens, each of which correspond to a user interface feature to disable.", "type": "array", "items": { "type": "string" } }, @@ -16,17 +17,24 @@ "title": "Disable first run page", "type": "boolean" }, + "noFiltering": { + "title": "List of domains for which no filtering should occur", + "type": "array", + "items": { "type": "string" } + }, "rulesets": { "title": "Rulesets to add/remove", "description": "Prefix a ruleset id with '+' to add, or '-' to remove. Use '-*' to disable all non-default lists.", "type": "array", "items": { "type": "string" } }, - "disabledFeatures": { - "title": "User interface features to disable", - "description": "A list of tokens, each of which correspond to a user interface feature to disable.", - "type": "array", - "items": { "type": "string" } + "showBlockedCount": { + "title": "Enable/disable toolbar icon count badge", + "type": "boolean" + }, + "strictBlockMode": { + "title": "Enable/disable strict blocking", + "type": "boolean" } } } From ef9709fb072588cd92101154503da82148429021 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Fri, 25 Apr 2025 19:26:16 -0400 Subject: [PATCH 0871/1099] [mv3] Safari does not support resource type `object` Need to filter out DNR rules with `object`, otherwise this causes a failure when adding the rules dynamically. --- platform/mv3/safari/ext-compat.js | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/platform/mv3/safari/ext-compat.js b/platform/mv3/safari/ext-compat.js index c7f02984d0bbb..c2940e0c9ea6f 100644 --- a/platform/mv3/safari/ext-compat.js +++ b/platform/mv3/safari/ext-compat.js @@ -33,6 +33,16 @@ const nativeDNR = webext.declarativeNetRequest; const isSupportedRule = r => { if ( r.action?.responseHeaders ) { return false; } if ( r.condition?.tabIds !== undefined ) { return false; } + if ( r.condition?.resourceTypes?.includes('object') ) { + if ( r.condition.resourceTypes.length === 1 ) { return false; } + const i = r.condition.resourceTypes.indexOf('object'); + r.condition.resourceTypes.splice(i, 1); + } + if ( r.condition?.excludedResourceTypes?.includes('object') ) { + if ( r.condition.excludedResourceTypes.length === 1 ) { return false; } + const i = r.condition.excludedResourceTypes.indexOf('object'); + r.condition.excludedResourceTypes.splice(i, 1); + } return true; }; From 5e64ace64d4af33879946857c8e9fe3a682687bf Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sat, 26 Apr 2025 10:11:33 -0400 Subject: [PATCH 0872/1099] [mv3] Code review of safari-specific code --- platform/mv3/safari/ext-compat.js | 49 +++++++++++++++++-------------- 1 file changed, 27 insertions(+), 22 deletions(-) diff --git a/platform/mv3/safari/ext-compat.js b/platform/mv3/safari/ext-compat.js index c2940e0c9ea6f..d5c954c506c85 100644 --- a/platform/mv3/safari/ext-compat.js +++ b/platform/mv3/safari/ext-compat.js @@ -31,21 +31,34 @@ export const EXCLUDED_INITIATOR_DOMAINS = 'excludedDomains'; const nativeDNR = webext.declarativeNetRequest; const isSupportedRule = r => { - if ( r.action?.responseHeaders ) { return false; } - if ( r.condition?.tabIds !== undefined ) { return false; } - if ( r.condition?.resourceTypes?.includes('object') ) { - if ( r.condition.resourceTypes.length === 1 ) { return false; } - const i = r.condition.resourceTypes.indexOf('object'); - r.condition.resourceTypes.splice(i, 1); + if ( r.action.responseHeaders ) { return false; } + const { condition } = r; + if ( condition.tabIds !== undefined ) { return false; } + if ( condition.resourceTypes?.includes('object') ) { + if ( condition.resourceTypes.length === 1 ) { return false; } + const i = condition.resourceTypes.indexOf('object'); + condition.resourceTypes.splice(i, 1); } - if ( r.condition?.excludedResourceTypes?.includes('object') ) { - if ( r.condition.excludedResourceTypes.length === 1 ) { return false; } - const i = r.condition.excludedResourceTypes.indexOf('object'); - r.condition.excludedResourceTypes.splice(i, 1); + if ( condition.excludedResourceTypes?.includes('object') ) { + const i = condition.excludedResourceTypes.indexOf('object'); + condition.excludedResourceTypes.splice(i, 1); + if ( condition.excludedResourceTypes.length === 0 ) { + delete condition.excludedResourceTypes; + } } return true; }; +const prepareUpdateRules = optionsBefore => { + const { addRules, removeRuleIds } = optionsBefore; + const addRulesAfter = addRules?.filter(isSupportedRule); + if ( Boolean(addRulesAfter?.length || removeRuleIds?.length) === false ) { return; } + const optionsAfter = {}; + if ( addRulesAfter?.length ) { optionsAfter.addRules = addRulesAfter; } + if ( removeRuleIds?.length ) { optionsAfter.removeRuleIds = removeRuleIds; } + return optionsAfter; +}; + const ruleCompare = (a, b) => a.id - b.id; const isSameRules = (a, b) => { @@ -91,24 +104,16 @@ export const dnr = { return nativeDNR.isRegexSupported(...args); }, async updateDynamicRules(optionsBefore) { - const { addRules, removeRuleIds } = optionsBefore; - const addRulesAfter = addRules?.filter(isSupportedRule); - if ( Boolean(addRulesAfter?.length || removeRuleIds?.length) === false ) { return; } - const optionsAfter = {}; - if ( addRulesAfter?.length ) { optionsAfter.addRules = addRulesAfter; } - if ( removeRuleIds?.length ) { optionsAfter.removeRuleIds = removeRuleIds; } + const optionsAfter = prepareUpdateRules(optionsBefore); + if ( optionsAfter === undefined ) { return; } return nativeDNR.updateDynamicRules(optionsAfter); }, updateEnabledRulesets(...args) { return nativeDNR.updateEnabledRulesets(...args); }, async updateSessionRules(optionsBefore) { - const { addRules, removeRuleIds } = optionsBefore; - const addRulesAfter = addRules?.filter(isSupportedRule); - if ( Boolean(addRulesAfter?.length || removeRuleIds?.length) === false ) { return; } - const optionsAfter = {}; - if ( optionsAfter?.length ) { optionsAfter.addRules = addRulesAfter; } - if ( removeRuleIds?.length ) { optionsAfter.removeRuleIds = removeRuleIds; } + const optionsAfter = prepareUpdateRules(optionsBefore); + if ( optionsAfter === undefined ) { return; } return nativeDNR.updateSessionRules(optionsAfter); }, async setAllowAllRules(id, allowed, notAllowed, reverse) { From 6c84ae7abae2e738bfd1d696f43d5aec27fe569c Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sun, 27 Apr 2025 08:22:04 -0400 Subject: [PATCH 0873/1099] [mv3] Support disabling all lists Related discussion: https://github.com/uBlockOrigin/uBlock-discussions/discussions/943 --- platform/mv3/extension/js/filter-lists.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/platform/mv3/extension/js/filter-lists.js b/platform/mv3/extension/js/filter-lists.js index 80b0af79a421e..ce62dc3104b91 100644 --- a/platform/mv3/extension/js/filter-lists.js +++ b/platform/mv3/extension/js/filter-lists.js @@ -403,8 +403,6 @@ const applyEnabledRulesets = (( ) => { dom.cl.remove('#lists .listEntry.toggled', 'toggled'); - if ( enabledRulesets.length === 0 ) { return; } - await sendMessage({ what: 'applyRulesets', enabledRulesets, From b4aea4ab273f4023464509e069fb8a188c07759a Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sun, 27 Apr 2025 09:02:29 -0400 Subject: [PATCH 0874/1099] [mv3] Avoid pointless messaging when no changes at `beforeunload` --- platform/mv3/extension/js/dashboard.js | 6 ++++++ platform/mv3/extension/js/filter-lists.js | 5 +++++ platform/mv3/extension/js/settings.js | 7 +------ 3 files changed, 12 insertions(+), 6 deletions(-) diff --git a/platform/mv3/extension/js/dashboard.js b/platform/mv3/extension/js/dashboard.js index b8c240d5c3c43..d43f45c5b7a8d 100644 --- a/platform/mv3/extension/js/dashboard.js +++ b/platform/mv3/extension/js/dashboard.js @@ -36,3 +36,9 @@ dom.on('#dashboard-nav', 'click', '.tabButton', ev => { }); /******************************************************************************/ + +export function hashFromIterable(iter) { + return Array.from(iter).sort().join('\n'); +} + +/******************************************************************************/ diff --git a/platform/mv3/extension/js/filter-lists.js b/platform/mv3/extension/js/filter-lists.js index ce62dc3104b91..a609a7d534aaa 100644 --- a/platform/mv3/extension/js/filter-lists.js +++ b/platform/mv3/extension/js/filter-lists.js @@ -22,6 +22,7 @@ import { dom, qs$, qsa$ } from './dom.js'; import { i18n, i18n$ } from './i18n.js'; import { localRead, localWrite, sendMessage } from './ext.js'; +import { hashFromIterable } from './dashboard.js'; /******************************************************************************/ @@ -403,6 +404,10 @@ const applyEnabledRulesets = (( ) => { dom.cl.remove('#lists .listEntry.toggled', 'toggled'); + const unmodified = hashFromIterable(enabledRulesets) === + hashFromIterable(cachedRulesetData.enabledRulesets); + if ( unmodified ) { return; } + await sendMessage({ what: 'applyRulesets', enabledRulesets, diff --git a/platform/mv3/extension/js/settings.js b/platform/mv3/extension/js/settings.js index 4c9352f6eca74..1a7812cbc2ceb 100644 --- a/platform/mv3/extension/js/settings.js +++ b/platform/mv3/extension/js/settings.js @@ -21,6 +21,7 @@ import { browser, sendMessage } from './ext.js'; import { dom, qs$ } from './dom.js'; +import { hashFromIterable } from './dashboard.js'; import punycode from './punycode.js'; import { renderFilterLists } from './filter-lists.js'; @@ -30,12 +31,6 @@ let cachedRulesetData = {}; /******************************************************************************/ -function hashFromIterable(iter) { - return Array.from(iter).sort().join('\n'); -} - -/******************************************************************************/ - function renderAdminRules() { const { disabledFeatures: forbid = [] } = cachedRulesetData; if ( forbid.length === 0 ) { return; } From cf7777e9fd56b842473eef374fd72fd2a474a33f Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sun, 27 Apr 2025 10:50:21 -0400 Subject: [PATCH 0875/1099] Allow user to change CodeMirror input style in "My filters" Related issue: https://github.com/uBlockOrigin/uBlock-issues/issues/3613 --- src/js/1p-filters.js | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/src/js/1p-filters.js b/src/js/1p-filters.js index 7e20a30c29fd3..d27b6710feea1 100644 --- a/src/js/1p-filters.js +++ b/src/js/1p-filters.js @@ -28,7 +28,7 @@ import { onBroadcast } from './broadcast.js'; /******************************************************************************/ -const cmEditor = new CodeMirror(qs$('#userFilters'), { +const cmOptions = { autoCloseBrackets: true, autofocus: true, extraKeys: { @@ -47,7 +47,21 @@ const cmEditor = new CodeMirror(qs$('#userFilters'), { styleActiveLine: { nonEmpty: true, }, -}); +}; +(( ) => { + try { + const url = new URL(self.top.location.href); + const input = url.searchParams.get('input'); + if ( input === 't' ) { + cmOptions.inputStyle = 'textarea'; + } else if ( input === 'c' ) { + cmOptions.inputStyle = 'contenteditable'; + } + } catch { + } +})(); + +const cmEditor = new CodeMirror(qs$('#userFilters'), cmOptions); uBlockDashboard.patchCodeMirrorEditor(cmEditor); From daa00251578df31627a7debe37edf9e66131675f Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sun, 27 Apr 2025 10:52:18 -0400 Subject: [PATCH 0876/1099] New revision for dev build --- dist/version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dist/version b/dist/version index 841afe85e44e5..5537921fd9c67 100644 --- a/dist/version +++ b/dist/version @@ -1 +1 @@ -1.63.3.19 \ No newline at end of file +1.63.3.100 \ No newline at end of file From c2eee87029508e73a8e2960544de20bf9d1cf09c Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sun, 27 Apr 2025 10:55:12 -0400 Subject: [PATCH 0877/1099] Import translation work from https://crowdin.com/project/ublock --- platform/mv3/description/webstore.be.txt | 4 ++-- platform/mv3/description/webstore.br_FR.txt | 2 +- platform/mv3/extension/_locales/br_FR/messages.json | 6 +++--- platform/mv3/extension/_locales/da/messages.json | 2 +- platform/mv3/extension/_locales/fy/messages.json | 2 +- src/_locales/be/messages.json | 2 +- src/_locales/br_FR/messages.json | 8 ++++---- 7 files changed, 13 insertions(+), 13 deletions(-) diff --git a/platform/mv3/description/webstore.be.txt b/platform/mv3/description/webstore.be.txt index 47612d4f103b2..0c143201a3709 100644 --- a/platform/mv3/description/webstore.be.txt +++ b/platform/mv3/description/webstore.be.txt @@ -7,6 +7,6 @@ uBO Lite (uBOL) гэта базаваны на MV3 блакавальнік зм - EasyPrivacy - Спіс сервераў з рэкламай і адсочвання ад Peter Lowe -You can enable more rulesets by visiting the options page -- click the _Cogs_ icon in the popup panel. +Вы можаце ўключыць больш набораў правіл праз старонку налад -- націсніце на значок _Шасцярэнькі_ на ўсплывальнай панэлі. -uBOL is entirely declarative, meaning there is no need for a permanent uBOL process for the filtering to occur, and CSS/JS injection-based content filtering is performed reliably by the browser itself rather than by the extension. This means that uBOL itself does not consume CPU/memory resources while content blocking is ongoing -- uBOL's service worker process is required _only_ when you interact with the popup panel or the option pages. +uBOL цалкам дэкларатыўны, то-бок не мае неабходнасці ў сталым uBOL працэсе дзеля фільтрацыі, а фільтрацыя змесціва на аснове інʼекцыі CSS/JS надзейна выконваецца пераважна самім браўзерам замест пашырэння. Гэта значыць, што uBOL не спажывае рэсурсаў працэсара/памяці пры блакаванні зместу -- службовы працэс uBOL патрэбны _толькі_ падчас узаемадзеяння з усплывальнай панэллю або наладамі. diff --git a/platform/mv3/description/webstore.br_FR.txt b/platform/mv3/description/webstore.br_FR.txt index d006200b050e8..1a0465f7a7f8e 100644 --- a/platform/mv3/description/webstore.br_FR.txt +++ b/platform/mv3/description/webstore.br_FR.txt @@ -5,7 +5,7 @@ Ar reolennoù dre ziouer a glot gant silañ dre ziouer uBlock Origin: - Rolloù siloù genidik a uBlock Origin - EasyList - EasyPrivacy -- Roll bruderezh ha servijerioù heuliañ Peter Lowe +- Roll ar servijerioù brudañ ha heuliañ eus Peter Lowe Tu zo deoc'h ouzhpennañ reolennoù all en arventennoù -- klikit war an ikon _kendentadur_ er banell popup. diff --git a/platform/mv3/extension/_locales/br_FR/messages.json b/platform/mv3/extension/_locales/br_FR/messages.json index 18457cce5c8b9..e8fbb70c55217 100644 --- a/platform/mv3/extension/_locales/br_FR/messages.json +++ b/platform/mv3/extension/_locales/br_FR/messages.json @@ -108,7 +108,7 @@ "description": "Header of 'Report a filter issue' section in Support pane" }, "supportS3P1": { - "message": "Report filter issues with specific websites to the uBlockOrigin/uAssets issue tracker. Requires a GitHub account.", + "message": "Danevellit kudennoù ar siloù e lec'hiennoù resis e-barzh uBlockOrigin/uAssets roll evezhiañ kudennoù. Ur c'hont GitHub zo rekis.", "description": "First paragraph of 'Filter issues' section in Support pane" }, "supportS5H": { @@ -116,7 +116,7 @@ "description": "Label of 'Troubleshooting information' section in 'Report a filter issue' page" }, "supportS6P1S1": { - "message": "Evit nompas sammañ ar genlabourerien a-youl vat gant meur a zanevell heñvel, gwiriit ma n'eo ket bet danevellet ho kudenn en ar-raok mar plij. Notenn: ma klikit ar bouton e vo kaset anv herberc'hier ar bajenn da c'h-GitHub.", + "message": "Evit nompas sammañ ar genlabourerien a-youl vat gant meur a zanevell heñvel, gwiriit ma n'eo ket bet danevellet ho kudenn en a-raok mar plij. Notenn: ma klikit ar bouton e vo kaset anv herberc'hier ar bajenn da c'h-GitHub.", "description": "A paragraph in the filter issue reporter section" }, "supportFindSpecificButton": { @@ -160,7 +160,7 @@ "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option7": { - "message": "Leads to badware, phishing", + "message": "Kas a ra da veziantoù droukyoulet pe d'an higennañ niverel", "description": "An entry in the widget used to select the type of issue" }, "supportS6Checkbox1": { diff --git a/platform/mv3/extension/_locales/da/messages.json b/platform/mv3/extension/_locales/da/messages.json index f3659787ba7af..0422b4f556db9 100644 --- a/platform/mv3/extension/_locales/da/messages.json +++ b/platform/mv3/extension/_locales/da/messages.json @@ -4,7 +4,7 @@ "description": "extension name." }, "extShortDesc": { - "message": "En tilladelsesløs indholdsblocker. Blokerer som standard annoncer, trackere, minere mv. straks efter installationen.", + "message": "En effektiv indholdsblokering. Blokerer annoncer, trackere, minere og mere lige efter installation.", "description": "this will be in the Chrome web store: must be 132 characters or less" }, "perRulesetStats": { diff --git a/platform/mv3/extension/_locales/fy/messages.json b/platform/mv3/extension/_locales/fy/messages.json index 5bc626577ed29..37f87477d313d 100644 --- a/platform/mv3/extension/_locales/fy/messages.json +++ b/platform/mv3/extension/_locales/fy/messages.json @@ -4,7 +4,7 @@ "description": "extension name." }, "extShortDesc": { - "message": "In eksperimintele, tastimmingsleaze ynhâldsblokkearder. Blokkearret daliks nei ynstallaasje advertinsjes, trackers, miners en mear.", + "message": "In, tastimmingsleaze ynhâldsblokkearder. Blokkearret daliks nei ynstallaasje advertinsjes, trackers, miners en mear.", "description": "this will be in the Chrome web store: must be 132 characters or less" }, "perRulesetStats": { diff --git a/src/_locales/be/messages.json b/src/_locales/be/messages.json index 95be96c0ba038..6af84474fdae6 100644 --- a/src/_locales/be/messages.json +++ b/src/_locales/be/messages.json @@ -308,7 +308,7 @@ "description": "English: Cosmetic filters" }, "pickerCosmeticFiltersHint": { - "message": "Клік, Ctrl-клік", + "message": "Націсканне, Ctrl + націсканне", "description": "English: Click, Ctrl-click" }, "pickerContextMenuEntry": { diff --git a/src/_locales/br_FR/messages.json b/src/_locales/br_FR/messages.json index 53d3ce41e38c9..62c931cd81ee2 100644 --- a/src/_locales/br_FR/messages.json +++ b/src/_locales/br_FR/messages.json @@ -356,7 +356,7 @@ "description": "Checkbox to let user access advanced, technical features" }, "settingsPrefetchingDisabledPrompt": { - "message": "Diweredekaat ar \"rak-lenn\", pe diougan an oberoù war ar rouedad (evit ma ne gennaskfe ket ar rekedoù rouedad stanket)", + "message": "Diweredekaat ar \"rak-lenn\", pe diougan an oberoù war ar rouedad (evit ma ne gennaskfe ket rekedoù ar rouedadoù stanket)", "description": "English: " }, "settingsHyperlinkAuditingDisabledPrompt": { @@ -488,7 +488,7 @@ "description": "Filter lists section name" }, "3pGroupCookies": { - "message": "Kemennadennoù diwar-benn an toupinoù", + "message": "Notennoù diwar-benn an toupinoù", "description": "Filter lists section name" }, "3pGroupAnnoyances": { @@ -928,7 +928,7 @@ "description": "Header of 'Filter issues' section in Support pane" }, "supportS3P1": { - "message": "Danevellit kudennoù silañ e lec'hiennoù resis zo e-barzh roll evezhiañ kudennoù uBlockOrigin/uAssets. Ur c'hont GitHub zo rekis.", + "message": "Danevellit kudennoù ar siloù e lec'hiennoù resis e-barzh uBlockOrigin/uAssets roll evezhiañ kudennoù. Ur c'hont GitHub zo rekis.", "description": "First paragraph of 'Filter issues' section in Support pane" }, "supportS3P2": { @@ -1012,7 +1012,7 @@ "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option7": { - "message": "Leads to badware, phishing", + "message": "Kas a ra da veziantoù droukyoulet pe d'an higennañ niverel", "description": "An entry in the widget used to select the type of issue" }, "supportS6Checkbox1": { From e04879003669e92d9794dbec373b43bcc7621d48 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sun, 27 Apr 2025 11:06:27 -0400 Subject: [PATCH 0878/1099] Make Firefox dev build auto-update --- dist/firefox/updates.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dist/firefox/updates.json b/dist/firefox/updates.json index ab4add78150a7..c180613ee864c 100644 --- a/dist/firefox/updates.json +++ b/dist/firefox/updates.json @@ -3,9 +3,9 @@ "uBlock0@raymondhill.net": { "updates": [ { - "version": "1.63.3.19", + "version": "1.63.3.100", "browser_specific_settings": { "gecko": { "strict_min_version": "92.0" } }, - "update_link": "https://github.com/gorhill/uBlock/releases/download/1.63.3b19/uBlock0_1.63.3b19.firefox.signed.xpi" + "update_link": "https://github.com/gorhill/uBlock/releases/download/1.63.3rc0/uBlock0_1.63.3rc0.firefox.signed.xpi" } ] } From 526af628640a510af60a4f2fe1c472797187a790 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Mon, 28 Apr 2025 10:56:43 -0400 Subject: [PATCH 0879/1099] [mv3] Fix overzealous strict-blocking in Safari --- platform/mv3/safari/ext-compat.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/platform/mv3/safari/ext-compat.js b/platform/mv3/safari/ext-compat.js index d5c954c506c85..70f1d6b5b1a6b 100644 --- a/platform/mv3/safari/ext-compat.js +++ b/platform/mv3/safari/ext-compat.js @@ -53,6 +53,12 @@ const prepareUpdateRules = optionsBefore => { const { addRules, removeRuleIds } = optionsBefore; const addRulesAfter = addRules?.filter(isSupportedRule); if ( Boolean(addRulesAfter?.length || removeRuleIds?.length) === false ) { return; } + addRulesAfter.forEach(r => { + if ( r.action.redirect?.regexSubstitution === undefined ) { return; } + if ( r.condition.requestDomains === undefined ) { return; } + r.condition.domains = r.condition.requestDomains; + delete r.condition.requestDomains; + }); const optionsAfter = {}; if ( addRulesAfter?.length ) { optionsAfter.addRules = addRulesAfter; } if ( removeRuleIds?.length ) { optionsAfter.removeRuleIds = removeRuleIds; } From 8df96e47180b5558e9b93f18710a558708e50bd4 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Mon, 28 Apr 2025 17:42:01 -0400 Subject: [PATCH 0880/1099] Fix regression in parsing of AG's `[domain=...]` syntax Related feedback: https://github.com/uBlockOrigin/uBlock-issues/issues/3235#issuecomment-2836674712 Related commit: https://github.com/gorhill/uBlock/commit/8b696a691a0871da8200d1806300842d988a4326#diff-848f3a5c8459fe07d1c65764e30b7c9471be77f9e9574674442319b831138024 --- src/js/static-filtering-parser.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/js/static-filtering-parser.js b/src/js/static-filtering-parser.js index a3f6e1cc54929..53f642c22ec43 100644 --- a/src/js/static-filtering-parser.js +++ b/src/js/static-filtering-parser.js @@ -2225,7 +2225,7 @@ export class AstFilterParser { normalizeDomainRegexValue(before) { const regex = before.startsWith('[$domain=/') - ? `/${before.slice(9, -1)}/` + ? `${before.slice(9, -1)}` : before; const source = this.normalizeRegexPattern(regex); if ( source === '' ) { return ''; } From 2c4819ac7470d80038e338fe2f3dc203c3bd1042 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Mon, 28 Apr 2025 17:46:11 -0400 Subject: [PATCH 0881/1099] New revision for dev build --- dist/version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dist/version b/dist/version index 5537921fd9c67..06eef286a23f0 100644 --- a/dist/version +++ b/dist/version @@ -1 +1 @@ -1.63.3.100 \ No newline at end of file +1.63.3.101 \ No newline at end of file From 9ac12647d15125605448340193a9a792dd293304 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Mon, 28 Apr 2025 17:57:06 -0400 Subject: [PATCH 0882/1099] Make Firefox dev build auto-update --- dist/firefox/updates.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dist/firefox/updates.json b/dist/firefox/updates.json index c180613ee864c..00131bc106699 100644 --- a/dist/firefox/updates.json +++ b/dist/firefox/updates.json @@ -3,9 +3,9 @@ "uBlock0@raymondhill.net": { "updates": [ { - "version": "1.63.3.100", + "version": "1.63.3.101", "browser_specific_settings": { "gecko": { "strict_min_version": "92.0" } }, - "update_link": "https://github.com/gorhill/uBlock/releases/download/1.63.3rc0/uBlock0_1.63.3rc0.firefox.signed.xpi" + "update_link": "https://github.com/gorhill/uBlock/releases/download/1.63.3rc1/uBlock0_1.63.3rc1.firefox.signed.xpi" } ] } From 1d1490523d2e9d9621be67bbe1911de76bbc7c2a Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Wed, 30 Apr 2025 08:50:45 -0400 Subject: [PATCH 0883/1099] [mv3] Fix incorrect DNR priority for redirect/important filters --- src/js/static-net-filtering.js | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/js/static-net-filtering.js b/src/js/static-net-filtering.js index d08db1e51c8e5..68364c80c524b 100644 --- a/src/js/static-net-filtering.js +++ b/src/js/static-net-filtering.js @@ -762,6 +762,10 @@ class FilterImportant { return filterDataAlloc(args[0]); } + static dnrFromCompiled(args, rule) { + rule.__important = true; + } + static keyFromArgs() { } @@ -4446,10 +4450,8 @@ StaticNetFilteringEngine.prototype.dnrFromCompiled = function(op, context, ...ar const realms = new Map([ [ BLOCK_REALM, { type: 'block', priority: 10 } ], - [ BLOCK_REALM | IMPORTANT_REALM, { type: 'block', priority: 40 } ], [ ALLOW_REALM, { type: 'allow', priority: 30 } ], [ REDIRECT_REALM, { type: 'redirect', priority: 11 } ], - [ REDIRECT_REALM | IMPORTANT_REALM, { type: 'redirect', priority: 41 } ], [ REMOVEPARAM_REALM, { type: 'removeparam', priority: 0 } ], [ CSP_REALM, { type: 'csp', priority: 0 } ], [ PERMISSIONS_REALM, { type: 'permissions', priority: 0 } ], @@ -4510,6 +4512,13 @@ StaticNetFilteringEngine.prototype.dnrFromCompiled = function(op, context, ...ar } } + // Adjust `important` priority + for ( const rule of ruleset ) { + if ( rule.__important !== true ) { continue; } + if ( rule.priority === undefined ) { continue; } + rule.priority += 30; + } + // Collect generichide filters const generichideExclusions = []; const generichideInclusions = []; From 457dd7acdbb0cb40dbf5dd5a70316ba10c9b53a7 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Wed, 30 Apr 2025 10:00:35 -0400 Subject: [PATCH 0884/1099] [mv3] Remove "OpenPhish Domain Blocklist" as per "Terms of Use" Excerpt from : > Except as expressly permitted by OpenPhish in writing, you agree not > to license, sell, rent, lease, transfer, assign, distribute, display, > disclose, create derivative works or otherwise make all or any portion > of the information obtained through the Services available to any > third party. --- platform/mv3/rulesets.json | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/platform/mv3/rulesets.json b/platform/mv3/rulesets.json index 3042950886324..77f3095c7efe7 100644 --- a/platform/mv3/rulesets.json +++ b/platform/mv3/rulesets.json @@ -63,16 +63,6 @@ ], "homeURL": "https://gitlab.com/malware-filter/urlhaus-filter" }, - { - "id": "openphish-domains", - "name": "OpenPhish Domain Blocklist", - "group": "malware", - "enabled": true, - "urls": [ - "https://raw.githubusercontent.com/stephenhawk8054/openphish-adblock/refs/heads/main/filters_init_domains.txt" - ], - "homeURL": "https://github.com/stephenhawk8054/openphish-adblock" - }, { "id": "block-lan", "name": "Block Outsider Intrusion into LAN", From ea0c400c5153c4246ec7295aa980178a56a1de14 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Fri, 2 May 2025 08:43:47 -0400 Subject: [PATCH 0885/1099] Revert "Allow user to change CodeMirror input style in "My filters"" This reverts commit cf7777e9fd56b842473eef374fd72fd2a474a33f. --- src/js/1p-filters.js | 18 ++---------------- 1 file changed, 2 insertions(+), 16 deletions(-) diff --git a/src/js/1p-filters.js b/src/js/1p-filters.js index d27b6710feea1..7e20a30c29fd3 100644 --- a/src/js/1p-filters.js +++ b/src/js/1p-filters.js @@ -28,7 +28,7 @@ import { onBroadcast } from './broadcast.js'; /******************************************************************************/ -const cmOptions = { +const cmEditor = new CodeMirror(qs$('#userFilters'), { autoCloseBrackets: true, autofocus: true, extraKeys: { @@ -47,21 +47,7 @@ const cmOptions = { styleActiveLine: { nonEmpty: true, }, -}; -(( ) => { - try { - const url = new URL(self.top.location.href); - const input = url.searchParams.get('input'); - if ( input === 't' ) { - cmOptions.inputStyle = 'textarea'; - } else if ( input === 'c' ) { - cmOptions.inputStyle = 'contenteditable'; - } - } catch { - } -})(); - -const cmEditor = new CodeMirror(qs$('#userFilters'), cmOptions); +}); uBlockDashboard.patchCodeMirrorEditor(cmEditor); From 3f59f94b60c32be09adddf7c27ffc26991abab83 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Fri, 2 May 2025 08:56:30 -0400 Subject: [PATCH 0886/1099] Bring zapper look in line with uBO Lite's zapper --- src/css/epicker-ui.css | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/css/epicker-ui.css b/src/css/epicker-ui.css index 7edbfe84524cf..a8aa13908526d 100644 --- a/src/css/epicker-ui.css +++ b/src/css/epicker-ui.css @@ -1,3 +1,7 @@ +:root { + --quit-button-size: max(4em, min(6em, calc(100vw / 8), calc(100vh / 8))); +} + html#ublock0-epicker, #ublock0-epicker body { background: transparent; @@ -26,7 +30,10 @@ html#ublock0-epicker, z-index: 100; } #ublock0-epicker.zap aside { + bottom: unset !important; min-width: unset !important; + top: 50%; + transform: translateY(-50%); width: unset !important; } #ublock0-epicker.zap aside > section, @@ -35,8 +42,8 @@ html#ublock0-epicker, display: none; } #ublock0-epicker.zap aside > #windowbar > #quit { - height: 2.5em; - width: 2.5em; + width: var(--quit-button-size); + height: var(--quit-button-size); } #ublock0-epicker:not(.paused) aside, #ublock0-epicker.minimized aside { From 03bea3aafc8b388bd3c6df256d1295f4be72dbe3 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Fri, 2 May 2025 09:02:55 -0400 Subject: [PATCH 0887/1099] New revision for release candidate --- dist/version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dist/version b/dist/version index 06eef286a23f0..522122413daf8 100644 --- a/dist/version +++ b/dist/version @@ -1 +1 @@ -1.63.3.101 \ No newline at end of file +1.63.3.102 \ No newline at end of file From e94ba8afadc84983f006e695ec52fabe198d930c Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Fri, 2 May 2025 09:04:30 -0400 Subject: [PATCH 0888/1099] Update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6399a92b3d831..025036ffe681e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,4 @@ +- [Bring zapper look in line with uBO Lite's zapper](https://github.com/gorhill/uBlock/commit/3f59f94b60) - [Ignore `start_page` transition for popup-blocking purpose](https://github.com/gorhill/uBlock/commit/0243a141a7) - [Exclude `chrome:` as valid openers for popup candidates](https://github.com/gorhill/uBlock/commit/59f4aca010) - [Fetch diff patches from "reliable" servers only](https://github.com/gorhill/uBlock/commit/8b964a8c54) From a4ba51a790d0b56962ae5da659e82d907af8e126 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Fri, 2 May 2025 09:05:53 -0400 Subject: [PATCH 0889/1099] Import translation work from https://crowdin.com/project/ublock --- platform/mv3/description/webstore.es.txt | 2 +- platform/mv3/extension/_locales/es/messages.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/platform/mv3/description/webstore.es.txt b/platform/mv3/description/webstore.es.txt index 58a215590ed4b..5e693285357bd 100644 --- a/platform/mv3/description/webstore.es.txt +++ b/platform/mv3/description/webstore.es.txt @@ -1,4 +1,4 @@ -uBo Lite (uBOL) es un bloqueador de contenido basado en MV3. +uBO Lite (uBOL) es un bloqueador de contenidos basado en MV3. Por defecto ya trae configuradas las siguientes listas de filtros: diff --git a/platform/mv3/extension/_locales/es/messages.json b/platform/mv3/extension/_locales/es/messages.json index 69ab185fa0efd..67af6f64091b3 100644 --- a/platform/mv3/extension/_locales/es/messages.json +++ b/platform/mv3/extension/_locales/es/messages.json @@ -4,7 +4,7 @@ "description": "extension name." }, "extShortDesc": { - "message": "Un bloqueador de contenido con menos permisos. Bloquea anuncios, rastreadores, criptomineros y aún más.", + "message": "Un bloqueador de contenido eficiente. Bloquea anuncios, rastreadores, criptomineros y aún más.", "description": "this will be in the Chrome web store: must be 132 characters or less" }, "perRulesetStats": { From 6a088a7477ea16bfadee35cb28646f0530359b55 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Fri, 2 May 2025 09:22:57 -0400 Subject: [PATCH 0890/1099] Make Firefox dev build auto-update --- dist/firefox/updates.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dist/firefox/updates.json b/dist/firefox/updates.json index 00131bc106699..6f5e770b02871 100644 --- a/dist/firefox/updates.json +++ b/dist/firefox/updates.json @@ -3,9 +3,9 @@ "uBlock0@raymondhill.net": { "updates": [ { - "version": "1.63.3.101", + "version": "1.63.3.102", "browser_specific_settings": { "gecko": { "strict_min_version": "92.0" } }, - "update_link": "https://github.com/gorhill/uBlock/releases/download/1.63.3rc1/uBlock0_1.63.3rc1.firefox.signed.xpi" + "update_link": "https://github.com/gorhill/uBlock/releases/download/1.63.3rc2/uBlock0_1.63.3rc2.firefox.signed.xpi" } ] } From 48146f8351d47065908164be992bbc436b9dad74 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Fri, 2 May 2025 14:33:33 -0400 Subject: [PATCH 0891/1099] [mv3] Address various issues for Safari build --- Makefile | 11 +- platform/mv3/extension/css/filtering-mode.css | 5 +- platform/mv3/extension/css/popup.css | 10 +- platform/mv3/extension/popup.html | 19 ++-- platform/mv3/safari/manifest.json | 3 +- platform/mv3/safari/patch-extension.js | 107 ++++++++++++++++++ 6 files changed, 139 insertions(+), 16 deletions(-) create mode 100644 platform/mv3/safari/patch-extension.js diff --git a/Makefile b/Makefile index 477a3b3f73687..5aac4a6e80763 100644 --- a/Makefile +++ b/Makefile @@ -5,10 +5,11 @@ run_options := $(filter-out $@,$(MAKECMDGOALS)) mv3-chromium mv3-firefox mv3-edge mv3-safari \ compare maxcost medcost mincost modifiers record wasm -sources := $(wildcard assets/* assets/*/* dist/version src/* src/*/* src/*/*/* src/*/*/*/*) -platform := $(wildcard platform/* platform/*/* platform/*/*/* platform/*/*/*/* platform/*/*/*/*/*) +sources := ./dist/version $(shell find ./assets -type f) $(shell find ./src -type f) +platform := $(wildcard platform/**/*) assets := dist/build/uAssets -mv3-data := $(wildcard dist/build/mv3-data/*) +mv3-data := $(shell find ./dist/build/mv3-data -type f) +mv3-safari-deps := $(shell find ./platform/mv3/extension -type f) $(wildcard platform/mv3/safari/*) all: chromium firefox npm @@ -80,8 +81,10 @@ dist/build/uBOLite.edge: tools/make-mv3.sh tools/make-edge.mjs $(sources) $(plat mv3-edge: dist/build/uBOLite.edge -dist/build/uBOLite.safari: tools/make-mv3.sh $(sources) $(platform) $(mv3-data) dist/build/mv3-data +dist/build/uBOLite.safari: tools/make-mv3.sh $(sources) $(mv3-safari-deps) $(mv3-data) dist/build/mv3-data tools/make-mv3.sh safari + node platform/mv3/safari/patch-extension.js \ + packageDir=./dist/build/uBOLite.safari mv3-safari: dist/build/uBOLite.safari diff --git a/platform/mv3/extension/css/filtering-mode.css b/platform/mv3/extension/css/filtering-mode.css index f6ed7783197bc..c7d37c03b7469 100644 --- a/platform/mv3/extension/css/filtering-mode.css +++ b/platform/mv3/extension/css/filtering-mode.css @@ -1,5 +1,6 @@ .filteringModeSlider { align-items: center; + container-type: size; display: flex; height: 60px; justify-content: center; @@ -11,9 +12,9 @@ background-color: var(--surface-1); box-sizing: border-box; border-radius: 30% 15% / 15% 30%; - height: 100%; + height: calc(25cqw + 2px); position: absolute; - width: calc(25% + 2px); + width: calc(25cqw + 2px); z-index: 10; } diff --git a/platform/mv3/extension/css/popup.css b/platform/mv3/extension/css/popup.css index 6ccc8f8147c04..19bef6ac1100f 100644 --- a/platform/mv3/extension/css/popup.css +++ b/platform/mv3/extension/css/popup.css @@ -70,9 +70,14 @@ body[data-forbid~="dashboard"] #gotoDashboard { display: none; } +.lrspacer { + padding-left: var(--default-gap-small); + padding-right: var(--default-gap-small); + } + #filteringModeText { color: var(--ink-3); - margin: var(--default-gap-small); + margin-bottom: var(--default-gap-small); margin-top: 0; text-align: center; text-transform: lowercase; @@ -95,7 +100,8 @@ body[data-forbid~="dashboard"] #gotoDashboard { .filteringModeSlider { align-self: center; - margin: var(--popup-gap); + margin-bottom: var(--popup-gap); + margin-top: var(--popup-gap); width: calc(var(--popup-main-min-width) - 1em); } diff --git a/platform/mv3/extension/popup.html b/platform/mv3/extension/popup.html index bd1c00d9182ed..1c4ed595a8cb2 100644 --- a/platform/mv3/extension/popup.html +++ b/platform/mv3/extension/popup.html @@ -17,15 +17,20 @@

­
-
-
- - - - +
+
+
+ + + + +
+
+ +
+

_
-

_
bolt list-altShow matched rules diff --git a/platform/mv3/safari/manifest.json b/platform/mv3/safari/manifest.json index a24dfb8850476..d4c8725f86316 100644 --- a/platform/mv3/safari/manifest.json +++ b/platform/mv3/safari/manifest.json @@ -6,7 +6,8 @@ "author": "Raymond Hill", "background": { "scripts": [ "/js/background.js" ], - "type": "module" + "type": "module", + "persistent": false }, "commands": { "enter-zapper-mode": { diff --git a/platform/mv3/safari/patch-extension.js b/platform/mv3/safari/patch-extension.js new file mode 100644 index 0000000000000..6a285535c7d81 --- /dev/null +++ b/platform/mv3/safari/patch-extension.js @@ -0,0 +1,107 @@ +/******************************************************************************* + + uBlock Origin - a comprehensive, efficient content blocker + Copyright (C) 2025-present Raymond Hill + + 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/}. + + Home: https://github.com/gorhill/uBlock +*/ + +import fs from 'fs/promises'; +import process from 'process'; + +/******************************************************************************/ + +const commandLineArgs = (( ) => { + const args = new Map(); + let name, value; + for ( const arg of process.argv.slice(2) ) { + const pos = arg.indexOf('='); + if ( pos === -1 ) { + name = arg; + value = ''; + } else { + name = arg.slice(0, pos); + value = arg.slice(pos+1); + } + args.set(name, value); + } + return args; +})(); + +/******************************************************************************/ + +// Apple store rejects when description (extShortDesc) is longer than 112 +// characters. + +async function fixLongDescription(path) { + let text = await fs.readFile(path, { encoding: 'utf8' }); + const messages = JSON.parse(text); + let message = messages.extShortDesc.message; + if ( message.length <= 112 ) { return; } + const pos = message.indexOf('.'); + if ( pos !== -1 ) { + message = message.slice(0, pos+1); + } + if ( message.length >= 112 ) { + message = `${message.slice(0, 111)}…`; + } + messages.extShortDesc.message = message; + text = JSON.stringify(messages, null, 2); + await fs.writeFile(path, text); +} + +async function fixLongDescriptions() { + const promises = []; + const packageDir = commandLineArgs.get('packageDir'); + const entries = await fs.readdir(`${packageDir}/_locales/`, { withFileTypes: true }); + for ( const entry of entries ) { + if ( entry.isDirectory() === false ) { continue; } + promises.push(fixLongDescription(`${packageDir}/_locales/${entry.name}/messages.json`)); + } + return Promise.all(promises); +} + +/******************************************************************************/ + +// Apple store rejects when version has four components. + +async function fixManifest() { + const packageDir = commandLineArgs.get('packageDir'); + const path = `${packageDir}/manifest.json`; + let text = await fs.readFile(path, { encoding: 'utf8' }); + const manifest = JSON.parse(text); + const match = /^(\d+)\.(\d+)\.(\d+)\.(\d+)$/.exec(manifest.version); + const month = parseInt(match[2], 10); + const dayofmonth = parseInt(match[3], 10); + const monthday /* sort of */ = month * 100 + dayofmonth; + manifest.version = `${match[1]}.${monthday}.${match[4]}`; + text = JSON.stringify(manifest, null, 2); + await fs.writeFile(path, text); +} + +/******************************************************************************/ + +async function main() { + await Promise.all([ + fixLongDescriptions(), + fixManifest(), + ]); + +} + +main(); + +/******************************************************************************/ From e384f2b6122600de344b804a6ed25adfc58ead2d Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Fri, 2 May 2025 14:45:06 -0400 Subject: [PATCH 0892/1099] [mv3] Build script changes --- Makefile | 2 -- tools/make-mv3.sh | 5 +++++ 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index 5aac4a6e80763..38a93e4d44c90 100644 --- a/Makefile +++ b/Makefile @@ -83,8 +83,6 @@ mv3-edge: dist/build/uBOLite.edge dist/build/uBOLite.safari: tools/make-mv3.sh $(sources) $(mv3-safari-deps) $(mv3-data) dist/build/mv3-data tools/make-mv3.sh safari - node platform/mv3/safari/patch-extension.js \ - packageDir=./dist/build/uBOLite.safari mv3-safari: dist/build/uBOLite.safari diff --git a/tools/make-mv3.sh b/tools/make-mv3.sh index 66b7c169782f4..7e1a89fb53d74 100755 --- a/tools/make-mv3.sh +++ b/tools/make-mv3.sh @@ -132,6 +132,11 @@ if [ "$PLATFORM" = "edge" ]; then node tools/make-edge.mjs fi +# For Safari, we must fix the package for compliance +if [ "$PLATFORM" = "safari" ]; then + node platform/mv3/safari/patch-extension.js packageDir="$UBOL_DIR" +fi + echo "*** uBOLite.$PLATFORM: extension ready" echo "Extension location: $UBOL_DIR/" From 47f1a19e9e1bbf1aaf38de7ee9ee1100c2a2e59b Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sun, 4 May 2025 06:37:31 -0400 Subject: [PATCH 0893/1099] Change stock UKR list to AdGuard-maintained list --- assets/assets.dev.json | 4 ++-- assets/assets.json | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/assets/assets.dev.json b/assets/assets.dev.json index eeda088634d16..73d73f37848ee 100644 --- a/assets/assets.dev.json +++ b/assets/assets.dev.json @@ -947,8 +947,8 @@ "title": "🇺🇦ua: Ukrainian Filters", "tags": "ads ukraine україна", "lang": "uk", - "contentURL": "https://raw.githubusercontent.com/ukrainianfilters/lists/main/combined/uBO/uBO.txt", - "supportURL": "https://github.com/ukrainianfilters/lists" + "contentURL": "https://filters.adtidy.org/extension/ublock/filters/23.txt", + "supportURL": "https://github.com/AdguardTeam/AdguardFilters#adguard-filters" }, "VIE-1": { "content": "filters", diff --git a/assets/assets.json b/assets/assets.json index 05f670333ccbe..5ed41ffa1edbc 100644 --- a/assets/assets.json +++ b/assets/assets.json @@ -947,8 +947,8 @@ "title": "🇺🇦ua: Ukrainian Filters", "tags": "ads ukraine україна", "lang": "uk", - "contentURL": "https://raw.githubusercontent.com/ukrainianfilters/lists/main/combined/uBO/uBO.txt", - "supportURL": "https://github.com/ukrainianfilters/lists" + "contentURL": "https://filters.adtidy.org/extension/ublock/filters/23.txt", + "supportURL": "https://github.com/AdguardTeam/AdguardFilters#adguard-filters" }, "VIE-1": { "content": "filters", From 3cd04c3806faed1b4dc840b05a2b75a352ccefef Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sun, 4 May 2025 06:42:42 -0400 Subject: [PATCH 0894/1099] Fix list name; also change for uBOL --- assets/assets.dev.json | 2 +- assets/assets.json | 2 +- platform/mv3/rulesets.json | 6 +++--- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/assets/assets.dev.json b/assets/assets.dev.json index 73d73f37848ee..8fd1fe2c08b9e 100644 --- a/assets/assets.dev.json +++ b/assets/assets.dev.json @@ -944,7 +944,7 @@ "content": "filters", "group": "regions", "off": true, - "title": "🇺🇦ua: Ukrainian Filters", + "title": "🇺🇦ua: AdGuard Ukrainian", "tags": "ads ukraine україна", "lang": "uk", "contentURL": "https://filters.adtidy.org/extension/ublock/filters/23.txt", diff --git a/assets/assets.json b/assets/assets.json index 5ed41ffa1edbc..6a5d3393d8b9c 100644 --- a/assets/assets.json +++ b/assets/assets.json @@ -944,7 +944,7 @@ "content": "filters", "group": "regions", "off": true, - "title": "🇺🇦ua: Ukrainian Filters", + "title": "🇺🇦ua: AdGuard Ukrainian", "tags": "ads ukraine україна", "lang": "uk", "contentURL": "https://filters.adtidy.org/extension/ublock/filters/23.txt", diff --git a/platform/mv3/rulesets.json b/platform/mv3/rulesets.json index 77f3095c7efe7..7bcd6d9da9d50 100644 --- a/platform/mv3/rulesets.json +++ b/platform/mv3/rulesets.json @@ -589,13 +589,13 @@ "id": "ukr-0", "group": "regions", "lang": "uk", - "name": "🇺🇦ua: Ukrainian Filters", + "name": "🇺🇦ua: AdGuard Ukrainian", "tags": "ads ukraine україна", "enabled": false, "urls": [ - "https://raw.githubusercontent.com/ukrainianfilters/lists/main/combined/uBO/uBO.txt" + "https://filters.adtidy.org/extension/ublock/filters/23.txt" ], - "homeURL": "https://github.com/ukrainianfilters/lists" + "homeURL": "https://github.com/AdguardTeam/AdguardFilters#adguard-filters" }, { "id": "vie-1", From 58f5c53fbbf3e8173a84bd49ff38846f739f9346 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Mon, 5 May 2025 08:04:21 -0400 Subject: [PATCH 0895/1099] [mv3] Fix strict-block exceptions caused duplicate rule ids Related issue: https://github.com/uBlockOrigin/uBOL-home/issues/304 --- platform/mv3/extension/js/ruleset-manager.js | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/platform/mv3/extension/js/ruleset-manager.js b/platform/mv3/extension/js/ruleset-manager.js index 1b52ca4a5a6cb..0bcf23fb49fb6 100644 --- a/platform/mv3/extension/js/ruleset-manager.js +++ b/platform/mv3/extension/js/ruleset-manager.js @@ -49,10 +49,18 @@ let sessionRegexCount = 0; const isStrictBlockRule = rule => { if ( rule.priority !== STRICTBLOCK_PRIORITY ) { return false; } - if ( rule.action.type !== 'redirect' ) { return false; } - const substitution = rule.action.redirect.regexSubstitution; - return substitution !== undefined && - substitution.includes('/strictblock.'); + if ( rule.condition?.resourceTypes === undefined ) { return false; } + if ( rule.condition.resourceTypes.length !== 1 ) { return false; } + if ( rule.condition.resourceTypes[0] !== 'main_frame' ) { return false; } + if ( rule.action.type === 'redirect' ) { + const substitution = rule.action.redirect.regexSubstitution; + return substitution !== undefined && + substitution.includes('/strictblock.'); + } + if ( rule.action.type === 'allow' ) { + return Array.isArray(rule.condition?.requestDomains); + } + return false; }; /******************************************************************************/ @@ -365,7 +373,7 @@ async function updateStrictBlockRules(currentRules, addRules, removeRuleIds) { const allExcluded = permanentlyExcluded.concat(temporarilyExcluded); if ( allExcluded.length === 0 ) { return; } - addRules.push({ + addRules.unshift({ action: { type: 'allow' }, condition: { requestDomains: allExcluded, From f40b989dc8dab4f2ff248522973e37af1ee3b2e4 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Mon, 5 May 2025 08:37:26 -0400 Subject: [PATCH 0896/1099] Fix makefile --- Makefile | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/Makefile b/Makefile index 38a93e4d44c90..57be09a964802 100644 --- a/Makefile +++ b/Makefile @@ -8,6 +8,7 @@ run_options := $(filter-out $@,$(MAKECMDGOALS)) sources := ./dist/version $(shell find ./assets -type f) $(shell find ./src -type f) platform := $(wildcard platform/**/*) assets := dist/build/uAssets +mv3-sources := $(shell find ./src -type f) $(shell find ./platform/mv3 -type f) mv3-data := $(shell find ./dist/build/mv3-data -type f) mv3-safari-deps := $(shell find ./platform/mv3/extension -type f) $(wildcard platform/mv3/safari/*) @@ -66,22 +67,22 @@ dig-snfe: dig dist/build/mv3-data: mkdir -p dist/build/mv3-data -dist/build/uBOLite.chromium: tools/make-mv3.sh $(sources) $(platform) $(mv3-data) dist/build/mv3-data +dist/build/uBOLite.chromium: tools/make-mv3.sh $(mv3-sources) $(platform) $(mv3-data) dist/build/mv3-data tools/make-mv3.sh chromium mv3-chromium: dist/build/uBOLite.chromium -dist/build/uBOLite.firefox: tools/make-mv3.sh $(sources) $(platform) $(mv3-data) dist/build/mv3-data +dist/build/uBOLite.firefox: tools/make-mv3.sh $(mv3-sources) $(platform) $(mv3-data) dist/build/mv3-data tools/make-mv3.sh firefox mv3-firefox: dist/build/uBOLite.firefox -dist/build/uBOLite.edge: tools/make-mv3.sh tools/make-edge.mjs $(sources) $(platform) $(mv3-data) dist/build/mv3-data +dist/build/uBOLite.edge: tools/make-mv3.sh tools/make-edge.mjs $(mv3-sources) $(platform) $(mv3-data) dist/build/mv3-data tools/make-mv3.sh edge mv3-edge: dist/build/uBOLite.edge -dist/build/uBOLite.safari: tools/make-mv3.sh $(sources) $(mv3-safari-deps) $(mv3-data) dist/build/mv3-data +dist/build/uBOLite.safari: tools/make-mv3.sh $(mv3-sources) $(mv3-safari-deps) $(mv3-data) dist/build/mv3-data tools/make-mv3.sh safari mv3-safari: dist/build/uBOLite.safari From 0ee6e3044d2745c7cdb7d8eef5d09ed9e9c6e75b Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Wed, 7 May 2025 07:47:28 -0400 Subject: [PATCH 0897/1099] [mv3] No need to retry sending messages with top-level listener --- .../extension/js/scripting/css-declarative.js | 14 +++++--------- .../mv3/extension/js/scripting/css-generic.js | 10 +++++----- .../mv3/extension/js/scripting/css-procedural.js | 10 +++++----- .../mv3/extension/js/scripting/css-specific.js | 16 +++++----------- 4 files changed, 20 insertions(+), 30 deletions(-) diff --git a/platform/mv3/extension/js/scripting/css-declarative.js b/platform/mv3/extension/js/scripting/css-declarative.js index 9f63fe5585483..a0364ce2027fd 100644 --- a/platform/mv3/extension/js/scripting/css-declarative.js +++ b/platform/mv3/extension/js/scripting/css-declarative.js @@ -103,18 +103,14 @@ for ( const selector of exceptedSelectors ) { if ( sheetText.length === 0 ) { return; } -(function uBOL_injectCSS(css, count = 10) { - chrome.runtime.sendMessage({ what: 'insertCSS', css }).catch(( ) => { - count -= 1; - if ( count === 0 ) { return; } - uBOL_injectCSS(css, count); - }); -})(sheetText.join('\n')); +chrome.runtime.sendMessage({ + what: 'insertCSS', + css: sheetText.join('\n'), +}).catch(( ) => { +}); /******************************************************************************/ })(); -/******************************************************************************/ - void 0; diff --git a/platform/mv3/extension/js/scripting/css-generic.js b/platform/mv3/extension/js/scripting/css-generic.js index f242b417b650f..f9979a9fbe150 100644 --- a/platform/mv3/extension/js/scripting/css-generic.js +++ b/platform/mv3/extension/js/scripting/css-generic.js @@ -214,11 +214,11 @@ const uBOL_processChanges = mutations => { /******************************************************************************/ -const uBOL_injectCSS = (css, count = 10) => { - chrome.runtime.sendMessage({ what: 'insertCSS', css }).catch(( ) => { - count -= 1; - if ( count === 0 ) { return; } - uBOL_injectCSS(css, count); +const uBOL_injectCSS = css => { + chrome.runtime.sendMessage({ + what: 'insertCSS', + css, + }).catch(( ) => { }); }; diff --git a/platform/mv3/extension/js/scripting/css-procedural.js b/platform/mv3/extension/js/scripting/css-procedural.js index 101aa7ee77185..8080e69562268 100644 --- a/platform/mv3/extension/js/scripting/css-procedural.js +++ b/platform/mv3/extension/js/scripting/css-procedural.js @@ -65,11 +65,11 @@ if ( exceptedSelectors.length === 0 ) { return; } /******************************************************************************/ -const uBOL_injectCSS = (css, count = 10) => { - chrome.runtime.sendMessage({ what: 'insertCSS', css }).catch(( ) => { - count -= 1; - if ( count === 0 ) { return; } - uBOL_injectCSS(css, count); +const uBOL_injectCSS = css => { + chrome.runtime.sendMessage({ + what: 'insertCSS', + css, + }).catch(( ) => { }); }; diff --git a/platform/mv3/extension/js/scripting/css-specific.js b/platform/mv3/extension/js/scripting/css-specific.js index 661cca61605ca..2d2c0db37a456 100644 --- a/platform/mv3/extension/js/scripting/css-specific.js +++ b/platform/mv3/extension/js/scripting/css-specific.js @@ -63,20 +63,14 @@ const exceptedSelectors = exceptions.length !== 0 : selectors; if ( exceptedSelectors.length === 0 ) { return; } -/******************************************************************************/ - -(function uBOL_injectCSS(css, count = 10) { - chrome.runtime.sendMessage({ what: 'insertCSS', css }).catch(( ) => { - count -= 1; - if ( count === 0 ) { return; } - uBOL_injectCSS(css, count); - }); -})(`${exceptedSelectors.join(',')}{display:none!important;}`); +chrome.runtime.sendMessage({ + what: 'insertCSS', + css: `${exceptedSelectors.join(',')}{display:none!important;}`, +}).catch(( ) => { +}); /******************************************************************************/ })(); -/******************************************************************************/ - void 0; From cc2760f4d6bbf4333e631c337a82e4f77fe842d9 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Wed, 7 May 2025 08:53:59 -0400 Subject: [PATCH 0898/1099] [mv3] New approach to toggle toolbar icon not requiring extra permissions Related issue: https://github.com/uBlockOrigin/uBOL-home/issues/198#issuecomment-2855135571 As a result, the permissions `tabs` and `webNavigation` have been removed. --- platform/mv3/chromium/manifest.json | 4 +- platform/mv3/extension/js/action.js | 54 ++++++++---------- platform/mv3/extension/js/background.js | 19 +++---- .../mv3/extension/js/scripting-manager.js | 55 ++++++++----------- .../extension/js/scripting/toolbar-icon.js | 27 +++++++++ platform/mv3/extension/js/utils.js | 14 +++++ platform/mv3/firefox/manifest.json | 4 +- platform/mv3/safari/manifest.json | 4 +- 8 files changed, 97 insertions(+), 84 deletions(-) create mode 100644 platform/mv3/extension/js/scripting/toolbar-icon.js diff --git a/platform/mv3/chromium/manifest.json b/platform/mv3/chromium/manifest.json index c633619b6193d..f42001a9d06fa 100644 --- a/platform/mv3/chromium/manifest.json +++ b/platform/mv3/chromium/manifest.json @@ -40,9 +40,7 @@ "activeTab", "declarativeNetRequest", "scripting", - "storage", - "tabs", - "webNavigation" + "storage" ], "short_name": "uBO Lite", "storage": { diff --git a/platform/mv3/extension/js/action.js b/platform/mv3/extension/js/action.js index a76210cef4fff..811fe101c0b1b 100644 --- a/platform/mv3/extension/js/action.js +++ b/platform/mv3/extension/js/action.js @@ -19,8 +19,8 @@ Home: https://github.com/gorhill/uBlock */ +import { matchesFromHostnames, strArrayEq } from './utils.js'; import { browser } from './ext.js'; -import { getFilteringModeDetails } from './mode-manager.js'; /******************************************************************************/ @@ -28,14 +28,6 @@ let reverseMode = false; /******************************************************************************/ -function toggleToolbarIcon(tabId) { - if ( reverseMode ) { - enableToolbarIcon(tabId); - } else { - disableToolbarIcon(tabId); - } -} - function disableToolbarIcon(tabId) { const details = { path: { @@ -68,9 +60,12 @@ function enableToolbarIcon(tabId) { /******************************************************************************/ -function toolbarIconListener(details) { - if ( details.frameId !== 0 ) { return; } - toggleToolbarIcon(details.tabId); +export function toggleToolbarIcon(tabId) { + if ( reverseMode ) { + enableToolbarIcon(tabId); + } else { + disableToolbarIcon(tabId); + } } /******************************************************************************/ @@ -78,13 +73,8 @@ function toolbarIconListener(details) { // https://github.com/uBlockOrigin/uBOL-home/issues/198 // Ensure the toolbar icon reflects the no-filtering mode of "trusted sites" -export async function syncToolbarIcon(wakeup) { - const { webNavigation } = browser; - if ( typeof webNavigation !== 'object' ) { return; } - - webNavigation.onCommitted.removeListener(toolbarIconListener); - - const { none, basic, optimal, complete } = await getFilteringModeDetails(); +export async function registerToolbarIconToggler(context) { + const { none, basic, optimal, complete } = context.filteringModeDetails; const reverseModeAfter = none.delete('all-urls'); const toToggle = reverseModeAfter ? new Set([ ...basic, ...optimal, ...complete ]) @@ -101,20 +91,20 @@ export async function syncToolbarIcon(wakeup) { if ( toToggle.size === 0 ) { return; } - webNavigation.onCommitted.addListener(toolbarIconListener, { - url: Array.from(toToggle).map(a => ({ - originAndPathMatches: `^https?://([^.].*\\.)?${a.replaceAll('.', '\\.')}/` - })), - }); + const registered = context.before.get('toolbar-icon'); + context.before.delete('toolbar-icon'); // Important! - if ( wakeup === undefined ) { return; } - - // If waking up, update icon for existing tabs - const tabs = await browser.tabs.query({ - url: Array.from(toToggle).map(a => `*://*.${a}/*`) - }).catch(( ) => ([])); + const directive = { + id: 'toolbar-icon', + js: [ '/js/scripting/toolbar-icon.js' ], + matches: matchesFromHostnames(toToggle), + runAt: 'document_start', + }; - for ( const tab of tabs ) { - toggleToolbarIcon(tab.id); + if ( registered === undefined ) { + context.toAdd.push(directive); + } else if ( strArrayEq(registered.matches, directive.matches) === false ) { + context.toRemove.push('toolbar-icon'); + context.toAdd.push(directive); } } diff --git a/platform/mv3/extension/js/background.js b/platform/mv3/extension/js/background.js index f97771dcf9297..7d7661a37e700 100644 --- a/platform/mv3/extension/js/background.js +++ b/platform/mv3/extension/js/background.js @@ -21,7 +21,6 @@ import { MODE_BASIC, - MODE_NONE, MODE_OPTIMAL, getDefaultFilteringMode, getFilteringMode, @@ -78,7 +77,7 @@ import { import { dnr } from './ext-compat.js'; import { registerInjectables } from './scripting-manager.js'; -import { syncToolbarIcon } from './action.js'; +import { toggleToolbarIcon } from './action.js'; /******************************************************************************/ @@ -170,6 +169,14 @@ function onMessage(request, sender, callback) { return false; } + case 'toggleToolbarIcon': { + const tabId = sender?.tab?.id ?? false; + if ( tabId ) { + toggleToolbarIcon(tabId); + } + return false; + } + default: break; } @@ -313,17 +320,11 @@ function onMessage(request, sender, callback) { break; case 'setFilteringMode': { - let trustedSitesChanged = false; getFilteringMode(request.hostname).then(beforeLevel => { if ( request.level === beforeLevel ) { return beforeLevel; } - trustedSitesChanged = beforeLevel === MODE_NONE; return setFilteringMode(request.hostname, request.level); }).then(afterLevel => { registerInjectables(); - trustedSitesChanged ||= afterLevel === MODE_NONE; - if ( trustedSitesChanged ) { - syncToolbarIcon(); - } callback(afterLevel); }); return true; @@ -357,7 +358,6 @@ function onMessage(request, sender, callback) { case 'setTrustedSites': setTrustedSites(request.hostnames).then(( ) => { registerInjectables(); - syncToolbarIcon(true); return Promise.all([ getDefaultFilteringMode(), getTrustedSites(), @@ -485,7 +485,6 @@ async function start() { await startSession(); } - syncToolbarIcon(process.wakeupRun); toggleDeveloperMode(rulesetConfig.developerMode); } diff --git a/platform/mv3/extension/js/scripting-manager.js b/platform/mv3/extension/js/scripting-manager.js index 8ff9a5c368044..6730e031db031 100644 --- a/platform/mv3/extension/js/scripting-manager.js +++ b/platform/mv3/extension/js/scripting-manager.js @@ -25,6 +25,7 @@ import { browser } from './ext.js'; import { fetchJSON } from './fetch.js'; import { getEnabledRulesetsDetails } from './ruleset-manager.js'; import { getFilteringModeDetails } from './mode-manager.js'; +import { registerToolbarIconToggler } from './action.js'; import { ubolLog } from './debug.js'; /******************************************************************************/ @@ -53,19 +54,6 @@ function getGenericDetails() { /******************************************************************************/ -// Important: We need to sort the arrays for fast comparison -const arrayEq = (a = [], b = [], sort = true) => { - const alen = a.length; - if ( alen !== b.length ) { return false; } - if ( sort ) { a.sort(); b.sort(); } - for ( let i = 0; i < alen; i++ ) { - if ( a[i] !== b[i] ) { return false; } - } - return true; -}; - -/******************************************************************************/ - const normalizeMatches = matches => { if ( matches.length <= 1 ) { return; } if ( matches.includes('') === false ) { @@ -168,9 +156,9 @@ function registerHighGeneric(context, genericDetails) { // update if ( - arrayEq(registered.css, css, false) === false || - arrayEq(registered.matches, matches) === false || - arrayEq(registered.excludeMatches, excludeMatches) === false + ut.strArrayEq(registered.css, css, false) === false || + ut.strArrayEq(registered.matches, matches) === false || + ut.strArrayEq(registered.excludeMatches, excludeMatches) === false ) { context.toRemove.push('css-generichigh'); context.toAdd.push(directive); @@ -231,8 +219,8 @@ function registerGeneric(context, genericDetails) { if ( registered === undefined ) { // register context.toAdd.push(directive); } else if ( // update - arrayEq(registered.js, js, false) === false || - arrayEq(registered.matches, directive.matches) === false + ut.strArrayEq(registered.js, js, false) === false || + ut.strArrayEq(registered.matches, directive.matches) === false ) { context.toRemove.push('css-generic-some'); context.toAdd.push(directive); @@ -257,8 +245,8 @@ function registerGeneric(context, genericDetails) { if ( registeredAll === undefined ) { // register context.toAdd.push(directiveAll); } else if ( // update - arrayEq(registeredAll.js, js, false) === false || - arrayEq(registeredAll.excludeMatches, directiveAll.excludeMatches) === false + ut.strArrayEq(registeredAll.js, js, false) === false || + ut.strArrayEq(registeredAll.excludeMatches, directiveAll.excludeMatches) === false ) { context.toRemove.push('css-generic-all'); context.toAdd.push(directiveAll); @@ -281,8 +269,8 @@ function registerGeneric(context, genericDetails) { if ( registeredSome === undefined ) { // register context.toAdd.push(directiveSome); } else if ( // update - arrayEq(registeredSome.js, js, false) === false || - arrayEq(registeredSome.matches, directiveSome.matches) === false + ut.strArrayEq(registeredSome.js, js, false) === false || + ut.strArrayEq(registeredSome.matches, directiveSome.matches) === false ) { context.toRemove.push('css-generic-some'); context.toAdd.push(directiveSome); @@ -342,9 +330,9 @@ function registerProcedural(context) { // update if ( - arrayEq(registered.js, js, false) === false || - arrayEq(registered.matches, matches) === false || - arrayEq(registered.excludeMatches, excludeMatches) === false + ut.strArrayEq(registered.js, js, false) === false || + ut.strArrayEq(registered.matches, matches) === false || + ut.strArrayEq(registered.excludeMatches, excludeMatches) === false ) { context.toRemove.push('css-procedural'); context.toAdd.push(directive); @@ -404,9 +392,9 @@ function registerDeclarative(context) { // update if ( - arrayEq(registered.js, js, false) === false || - arrayEq(registered.matches, matches) === false || - arrayEq(registered.excludeMatches, excludeMatches) === false + ut.strArrayEq(registered.js, js, false) === false || + ut.strArrayEq(registered.matches, matches) === false || + ut.strArrayEq(registered.excludeMatches, excludeMatches) === false ) { context.toRemove.push('css-declarative'); context.toAdd.push(directive); @@ -466,9 +454,9 @@ function registerSpecific(context) { // update if ( - arrayEq(registered.js, js, false) === false || - arrayEq(registered.matches, matches) === false || - arrayEq(registered.excludeMatches, excludeMatches) === false + ut.strArrayEq(registered.js, js, false) === false || + ut.strArrayEq(registered.matches, matches) === false || + ut.strArrayEq(registered.excludeMatches, excludeMatches) === false ) { context.toRemove.push('css-specific'); context.toAdd.push(directive); @@ -545,8 +533,8 @@ function registerScriptlet(context, scriptletDetails) { // update if ( - arrayEq(registered.matches, matches) === false || - arrayEq(registered.excludeMatches, excludeMatches) === false + ut.strArrayEq(registered.matches, matches) === false || + ut.strArrayEq(registered.excludeMatches, excludeMatches) === false ) { context.toRemove.push(id); context.toAdd.push(directive); @@ -599,6 +587,7 @@ async function registerInjectables() { registerSpecific(context); registerGeneric(context, genericDetails); registerHighGeneric(context, genericDetails); + registerToolbarIconToggler(context); toRemove.push(...Array.from(before.keys())); diff --git a/platform/mv3/extension/js/scripting/toolbar-icon.js b/platform/mv3/extension/js/scripting/toolbar-icon.js new file mode 100644 index 0000000000000..c5843a05c77c2 --- /dev/null +++ b/platform/mv3/extension/js/scripting/toolbar-icon.js @@ -0,0 +1,27 @@ +/******************************************************************************* + + uBlock Origin Lite - a comprehensive, MV3-compliant content blocker + Copyright (C) 2025-present Raymond Hill + + 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/}. + + Home: https://github.com/gorhill/uBlock +*/ + +(function uBOL_toggleToolbarIcon() { + chrome.runtime.sendMessage({ + what: 'toggleToolbarIcon', + }).catch(( ) => { + }); +})(); diff --git a/platform/mv3/extension/js/utils.js b/platform/mv3/extension/js/utils.js index 94ed121b2d960..935d61cfcf2d9 100644 --- a/platform/mv3/extension/js/utils.js +++ b/platform/mv3/extension/js/utils.js @@ -182,6 +182,19 @@ async function gotoURL(url, type) { /******************************************************************************/ +// Important: We need to sort the arrays for fast comparison +const strArrayEq = (a = [], b = [], sort = true) => { + const alen = a.length; + if ( alen !== b.length ) { return false; } + if ( sort ) { a.sort(); b.sort(); } + for ( let i = 0; i < alen; i++ ) { + if ( a[i] !== b[i] ) { return false; } + } + return true; +}; + +/******************************************************************************/ + export { broadcastMessage, parsedURLromOrigin, @@ -195,4 +208,5 @@ export { hostnamesFromMatches, hasBroadHostPermissions, gotoURL, + strArrayEq, }; diff --git a/platform/mv3/firefox/manifest.json b/platform/mv3/firefox/manifest.json index 73d2fa1dd98f6..c2dd5a3aac3a9 100644 --- a/platform/mv3/firefox/manifest.json +++ b/platform/mv3/firefox/manifest.json @@ -52,9 +52,7 @@ "activeTab", "declarativeNetRequest", "scripting", - "storage", - "tabs", - "webNavigation" + "storage" ], "short_name": "uBO Lite", "version": "1.0", diff --git a/platform/mv3/safari/manifest.json b/platform/mv3/safari/manifest.json index d4c8725f86316..c724367cb7dfb 100644 --- a/platform/mv3/safari/manifest.json +++ b/platform/mv3/safari/manifest.json @@ -40,9 +40,7 @@ "declarativeNetRequest", "declarativeNetRequestWithHostAccess", "scripting", - "storage", - "tabs", - "webNavigation" + "storage" ], "short_name": "uBO Lite", "version": "1.0", From df9cd6c9a2e987873745b7be6da9fbf74f3f2a7a Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Thu, 8 May 2025 10:09:03 -0400 Subject: [PATCH 0899/1099] Import translation work from https://crowdin.com/project/ublock --- platform/mv3/description/webstore.es.txt | 2 +- platform/mv3/extension/_locales/sv/messages.json | 2 +- src/_locales/ro/messages.json | 2 +- src/_locales/sv/messages.json | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/platform/mv3/description/webstore.es.txt b/platform/mv3/description/webstore.es.txt index 5e693285357bd..8d7e3cca0e37c 100644 --- a/platform/mv3/description/webstore.es.txt +++ b/platform/mv3/description/webstore.es.txt @@ -1,4 +1,4 @@ -uBO Lite (uBOL) es un bloqueador de contenidos basado en MV3. +uBO Lite (uBOL) es un bloqueador de contenido basado en MV3. Por defecto ya trae configuradas las siguientes listas de filtros: diff --git a/platform/mv3/extension/_locales/sv/messages.json b/platform/mv3/extension/_locales/sv/messages.json index 47f4e734fd5eb..7816476c4f560 100644 --- a/platform/mv3/extension/_locales/sv/messages.json +++ b/platform/mv3/extension/_locales/sv/messages.json @@ -4,7 +4,7 @@ "description": "extension name." }, "extShortDesc": { - "message": "En behörighetslös innehållsblockerare. Blockerar annonser, spårare, miners och mer omedelbart efter installationen.", + "message": "En behörighetslös innehållsblockerare. Blockerar annonser, spårare, miners och mer omedelbart efter installationen.", "description": "this will be in the Chrome web store: must be 132 characters or less" }, "perRulesetStats": { diff --git a/src/_locales/ro/messages.json b/src/_locales/ro/messages.json index 75b712aea7956..a3092f0fd11dc 100644 --- a/src/_locales/ro/messages.json +++ b/src/_locales/ro/messages.json @@ -840,7 +840,7 @@ "description": "Message to show when a filter cannot be found in any filter lists" }, "loggerSettingDiscardPrompt": { - "message": "Intrările din jurnal care nu îndeplinesc cele trei condiții de mai jos vor fi respinse automat:", + "message": "Intrările în registrul de logare care nu îndeplinesc toate cele trei condiții de mai jos vor fi automat eliminate.", "description": "Logger setting: A sentence to describe the purpose of the settings below" }, "loggerSettingPerEntryMaxAge": { diff --git a/src/_locales/sv/messages.json b/src/_locales/sv/messages.json index e6320fb28bd56..355c701ad24c9 100644 --- a/src/_locales/sv/messages.json +++ b/src/_locales/sv/messages.json @@ -4,7 +4,7 @@ "description": "extension name." }, "extShortDesc": { - "message": "Äntligen en effektiv blockerare. Skonsam mot både processor och minne.", + "message": "Äntligen en effektiv blockerare. Lätt för både processor och minne.", "description": "this will be in the Chrome web store: must be 132 characters or less" }, "dashboardName": { From 1c98faf46c09bda135edee771a8a3649605bfd33 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Thu, 8 May 2025 16:53:07 -0400 Subject: [PATCH 0900/1099] [mv3] Safari works; use 3-component version for uBOL uBOL doesn't pass validation when using 4-component versioning, so falling back to 3-component versioning for all platforms to avoid special case. --- Makefile | 10 +++-- .../mv3/edge/patch-extension.js | 25 ++++++++++-- platform/mv3/make-rulesets.js | 2 +- platform/mv3/safari/manifest.json | 5 +++ platform/mv3/safari/patch-extension.js | 10 ++--- tools/make-mv3.sh | 39 +++++++++---------- 6 files changed, 56 insertions(+), 35 deletions(-) rename tools/make-edge.mjs => platform/mv3/edge/patch-extension.js (75%) diff --git a/Makefile b/Makefile index 57be09a964802..2ce9beb54a436 100644 --- a/Makefile +++ b/Makefile @@ -6,11 +6,13 @@ run_options := $(filter-out $@,$(MAKECMDGOALS)) compare maxcost medcost mincost modifiers record wasm sources := ./dist/version $(shell find ./assets -type f) $(shell find ./src -type f) -platform := $(wildcard platform/**/*) +platform := $(wildcard platform/*/*) assets := dist/build/uAssets -mv3-sources := $(shell find ./src -type f) $(shell find ./platform/mv3 -type f) +mv3-sources := $(shell find ./src -type f) $(wildcard platform/mv3/*) $(shell find ./platform/mv3/extension -type f) mv3-data := $(shell find ./dist/build/mv3-data -type f) -mv3-safari-deps := $(shell find ./platform/mv3/extension -type f) $(wildcard platform/mv3/safari/*) + +mv3-edge-deps := $(wildcard platform/mv3/edge/*) +mv3-safari-deps := $(wildcard platform/mv3/safari/*) all: chromium firefox npm @@ -77,7 +79,7 @@ dist/build/uBOLite.firefox: tools/make-mv3.sh $(mv3-sources) $(platform) $(mv3-d mv3-firefox: dist/build/uBOLite.firefox -dist/build/uBOLite.edge: tools/make-mv3.sh tools/make-edge.mjs $(mv3-sources) $(platform) $(mv3-data) dist/build/mv3-data +dist/build/uBOLite.edge: tools/make-mv3.sh $(mv3-sources) $(mv3-edge-deps) $(mv3-data) dist/build/mv3-data tools/make-mv3.sh edge mv3-edge: dist/build/uBOLite.edge diff --git a/tools/make-edge.mjs b/platform/mv3/edge/patch-extension.js similarity index 75% rename from tools/make-edge.mjs rename to platform/mv3/edge/patch-extension.js index 45bf7d7d7f827..78e482f5cb4fe 100644 --- a/tools/make-edge.mjs +++ b/platform/mv3/edge/patch-extension.js @@ -19,16 +19,33 @@ Home: https://github.com/gorhill/uBlock */ -'use strict'; +import fs from 'fs/promises'; +import process from 'process'; /******************************************************************************/ -import fs from 'fs/promises'; +const commandLineArgs = (( ) => { + const args = Object.create(null); + let name, value; + for ( const arg of process.argv.slice(2) ) { + const pos = arg.indexOf('='); + if ( pos === -1 ) { + name = arg; + value = ''; + } else { + name = arg.slice(0, pos); + value = arg.slice(pos+1); + } + args[name] = value; + } + return args; +})(); /******************************************************************************/ async function main() { - const manifestPath = 'dist/build/uBOLite.edge/manifest.json'; + const packageDir = commandLineArgs.packageDir; + const manifestPath = `${packageDir}/manifest.json`; // Get manifest content const manifest = await fs.readFile(manifestPath, { encoding: 'utf8' @@ -45,7 +62,7 @@ async function main() { } // Commit changes await fs.writeFile(manifestPath, - JSON.stringify(manifest, null, 2) + '\n' + JSON.stringify(manifest, null, 2) ); } diff --git a/platform/mv3/make-rulesets.js b/platform/mv3/make-rulesets.js index fc93c2ec37aa0..8f6bc71e0d550 100644 --- a/platform/mv3/make-rulesets.js +++ b/platform/mv3/make-rulesets.js @@ -1413,7 +1413,7 @@ async function main() { const dayPart = now.getUTCDate(); const hourPart = Math.floor(now.getUTCHours()); const minutePart = Math.floor(now.getUTCMinutes()); - version = `${yearPart}.${monthPart}.${dayPart}.${hourPart * 60 + minutePart}`; + version = `${yearPart}.${monthPart*100+dayPart}.${hourPart*100+minutePart}`; } log(`Version: ${version}`, false); diff --git a/platform/mv3/safari/manifest.json b/platform/mv3/safari/manifest.json index c724367cb7dfb..d18525c1aa875 100644 --- a/platform/mv3/safari/manifest.json +++ b/platform/mv3/safari/manifest.json @@ -9,6 +9,11 @@ "type": "module", "persistent": false }, + "browser_specific_settings": { + "safari": { + "strict_min_version": "18.4" + } + }, "commands": { "enter-zapper-mode": { "description": "__MSG_zapperTipEnter__" diff --git a/platform/mv3/safari/patch-extension.js b/platform/mv3/safari/patch-extension.js index 6a285535c7d81..142d48033936b 100644 --- a/platform/mv3/safari/patch-extension.js +++ b/platform/mv3/safari/patch-extension.js @@ -25,7 +25,7 @@ import process from 'process'; /******************************************************************************/ const commandLineArgs = (( ) => { - const args = new Map(); + const args = Object.create(null); let name, value; for ( const arg of process.argv.slice(2) ) { const pos = arg.indexOf('='); @@ -36,7 +36,7 @@ const commandLineArgs = (( ) => { name = arg.slice(0, pos); value = arg.slice(pos+1); } - args.set(name, value); + args[name] = value; } return args; })(); @@ -65,7 +65,7 @@ async function fixLongDescription(path) { async function fixLongDescriptions() { const promises = []; - const packageDir = commandLineArgs.get('packageDir'); + const packageDir = commandLineArgs.packageDir; const entries = await fs.readdir(`${packageDir}/_locales/`, { withFileTypes: true }); for ( const entry of entries ) { if ( entry.isDirectory() === false ) { continue; } @@ -79,11 +79,12 @@ async function fixLongDescriptions() { // Apple store rejects when version has four components. async function fixManifest() { - const packageDir = commandLineArgs.get('packageDir'); + const packageDir = commandLineArgs.packageDir; const path = `${packageDir}/manifest.json`; let text = await fs.readFile(path, { encoding: 'utf8' }); const manifest = JSON.parse(text); const match = /^(\d+)\.(\d+)\.(\d+)\.(\d+)$/.exec(manifest.version); + if ( match === null ) { return; } const month = parseInt(match[2], 10); const dayofmonth = parseInt(match[3], 10); const monthday /* sort of */ = month * 100 + dayofmonth; @@ -99,7 +100,6 @@ async function main() { fixLongDescriptions(), fixManifest(), ]); - } main(); diff --git a/tools/make-mv3.sh b/tools/make-mv3.sh index 7e1a89fb53d74..78413ed525fb5 100755 --- a/tools/make-mv3.sh +++ b/tools/make-mv3.sh @@ -26,7 +26,7 @@ for i in "$@"; do safari) PLATFORM="safari" ;; - uBOLite_+([0-9]).+([0-9]).+([0-9]).+([0-9])) + uBOLite_+([0-9]).+([0-9]).+([0-9])) TAGNAME="$i" FULL="yes" ;; @@ -124,24 +124,12 @@ fi cd - > /dev/null rm -rf "$UBOL_BUILD_DIR" -# For Edge, declared rulesets must be at package root -if [ "$PLATFORM" = "edge" ]; then - echo "*** uBOLite.edge: Modify reference implementation for Edge compatibility" - mv "$UBOL_DIR"/rulesets/main/* "$UBOL_DIR/" - rmdir "$UBOL_DIR/rulesets/main" - node tools/make-edge.mjs -fi - -# For Safari, we must fix the package for compliance -if [ "$PLATFORM" = "safari" ]; then - node platform/mv3/safari/patch-extension.js packageDir="$UBOL_DIR" -fi - echo "*** uBOLite.$PLATFORM: extension ready" echo "Extension location: $UBOL_DIR/" # Local build if [ -z "$TAGNAME" ]; then + TAGNAME="uBOLite_$(jq -r .version "$UBOL_DIR"/manifest.json)" # Enable DNR rule debugging tmp=$(mktemp) jq '.permissions += ["declarativeNetRequestFeedback"]' \ @@ -153,6 +141,22 @@ if [ -z "$TAGNAME" ]; then jq '.browser_specific_settings.gecko.id = "uBOLite.dev@raymondhill.net"' "$UBOL_DIR/manifest.json" > "$tmp" \ && mv "$tmp" "$UBOL_DIR/manifest.json" fi +else + tmp=$(mktemp) + jq --arg version "${TAGNAME:8}" '.version = $version' "$UBOL_DIR/manifest.json" > "$tmp" \ + && mv "$tmp" "$UBOL_DIR/manifest.json" +fi + +# Platform-specific steps +if [ "$PLATFORM" = "edge" ]; then + # For Edge, declared rulesets must be at package root + echo "*** uBOLite.edge: Modify reference implementation for Edge compatibility" + mv "$UBOL_DIR"/rulesets/main/* "$UBOL_DIR/" + rmdir "$UBOL_DIR/rulesets/main" + node platform/mv3/edge/patch-extension.js packageDir="$UBOL_DIR" +elif [ "$PLATFORM" = "safari" ]; then + # For Safari, we must fix the package for compliance + node platform/mv3/safari/patch-extension.js packageDir="$UBOL_DIR" fi if [ "$FULL" = "yes" ]; then @@ -161,13 +165,6 @@ if [ "$FULL" = "yes" ]; then EXTENSION="xpi" fi echo "*** uBOLite.mv3: Creating publishable package..." - if [ -z "$TAGNAME" ]; then - TAGNAME="uBOLite_$(jq -r .version "$UBOL_DIR"/manifest.json)" - else - tmp=$(mktemp) - jq --arg version "${TAGNAME:8}" '.version = $version' "$UBOL_DIR/manifest.json" > "$tmp" \ - && mv "$tmp" "$UBOL_DIR/manifest.json" - fi UBOL_PACKAGE_NAME="$TAGNAME.$PLATFORM.mv3.$EXTENSION" UBOL_PACKAGE_DIR=$(mktemp -d) mkdir -p "$UBOL_PACKAGE_DIR" From 8a7f1589ec5bd1816441b1a1b51033efb179ecb8 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sat, 10 May 2025 12:18:57 -0400 Subject: [PATCH 0901/1099] [mv3] Expose setExtensionActionOptions in Safari compatibility layer Related issue: https://github.com/uBlockOrigin/uBOL-home/issues/327#issuecomment-2864897864 --- platform/mv3/safari/ext-compat.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/platform/mv3/safari/ext-compat.js b/platform/mv3/safari/ext-compat.js index 70f1d6b5b1a6b..5e89492f7e82b 100644 --- a/platform/mv3/safari/ext-compat.js +++ b/platform/mv3/safari/ext-compat.js @@ -149,4 +149,7 @@ export const dnr = { false ); }, + setExtensionActionOptions(...args) { + return nativeDNR.setExtensionActionOptions(...args); + }, }; From a83be1998799a1f72f040afa5ae35580e41ef372 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sat, 10 May 2025 12:32:09 -0400 Subject: [PATCH 0902/1099] [mv3] Remove equivalence between chromium and safari --- platform/mv3/make-rulesets.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform/mv3/make-rulesets.js b/platform/mv3/make-rulesets.js index 8f6bc71e0d550..c339502841835 100644 --- a/platform/mv3/make-rulesets.js +++ b/platform/mv3/make-rulesets.js @@ -71,7 +71,7 @@ const env = [ 'user_stylesheet', ]; -if ( platform === 'edge' || platform === 'safari' ) { +if ( platform === 'edge' ) { env.push('chromium'); } From 3292f128d2fb5a2fa41246a4daa799bc82ee733f Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sun, 11 May 2025 07:32:32 -0400 Subject: [PATCH 0903/1099] [mv3] Mind discarded regex- or path-based entries when determining genericity Related issue: https://github.com/uBlockOrigin/uAssets/issues/28260 --- src/js/static-dnr-filtering.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/js/static-dnr-filtering.js b/src/js/static-dnr-filtering.js index 0bc82a7f75b3a..231788604dbe0 100644 --- a/src/js/static-dnr-filtering.js +++ b/src/js/static-dnr-filtering.js @@ -226,6 +226,7 @@ function addExtendedToDNR(context, parser) { for ( const { hn, not, bad } of parser.getExtFilterDomainIterator() ) { if ( bad ) { continue; } if ( not && exception ) { continue; } + isGeneric = false; if ( isRegexOrPath(hn) ) { continue; } if ( details === undefined ) { context.specificCosmeticFilters.set(compiled, details = {}); @@ -248,7 +249,6 @@ function addExtendedToDNR(context, parser) { details.matches = [ '*' ]; continue; } - isGeneric = false; details.matches.push(hn); } if ( details === undefined ) { return; } From b604524c2f6354674a56963208a0efc1fdb5e4d7 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sun, 11 May 2025 09:30:34 -0400 Subject: [PATCH 0904/1099] Explicitly set background color according to state of `prefers-color-scheme` Related issue: https://github.com/uBlockOrigin/uBlock-issues/issues/3631 --- src/css/dashboard.css | 1 + 1 file changed, 1 insertion(+) diff --git a/src/css/dashboard.css b/src/css/dashboard.css index 20b35e57cc416..cc38559c93c71 100644 --- a/src/css/dashboard.css +++ b/src/css/dashboard.css @@ -1,4 +1,5 @@ html, body { + background-color: var(--surface-0); display: flex; flex-direction: column; height: 100vh; From 3650117b4f84a48c4e56039ef78bf1c155c369a8 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sun, 11 May 2025 09:41:34 -0400 Subject: [PATCH 0905/1099] Fix background color of navigation bar Related commit: https://github.com/gorhill/uBlock/commit/b604524c2f6354674a56963208a0efc1fdb5e4d7 --- src/css/dashboard.css | 1 + 1 file changed, 1 insertion(+) diff --git a/src/css/dashboard.css b/src/css/dashboard.css index cc38559c93c71..bff9fa15922f8 100644 --- a/src/css/dashboard.css +++ b/src/css/dashboard.css @@ -14,6 +14,7 @@ body.notReady { } #dashboard-nav { align-items: center; + background-color: var(--surface-1); border: 0; border-bottom: 1px solid var(--border-1); display: flex; From 5c029b353204a47efd8d7be22a3f741f1802780e Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sun, 11 May 2025 10:17:17 -0400 Subject: [PATCH 0906/1099] Use `color-scheme` `meta` tag as suggested Related issue: https://github.com/uBlockOrigin/uBlock-issues/issues/3631 --- platform/mv3/extension/dashboard.html | 1 + platform/mv3/extension/matched-rules.html | 1 + platform/mv3/extension/popup.html | 1 + platform/mv3/extension/report.html | 1 + platform/mv3/extension/strictblock.html | 1 + src/1p-filters.html | 1 + src/3p-filters.html | 1 + src/about.html | 1 + src/advanced-settings.html | 1 + src/asset-viewer.html | 1 + src/code-viewer.html | 1 + src/dashboard.html | 1 + src/devtools.html | 1 + src/document-blocked.html | 1 + src/dyna-rules.html | 1 + src/logger-ui.html | 1 + src/no-dashboard.html | 1 + src/popup-fenix.html | 1 + src/settings.html | 1 + src/support.html | 1 + src/whitelist.html | 1 + 21 files changed, 21 insertions(+) diff --git a/platform/mv3/extension/dashboard.html b/platform/mv3/extension/dashboard.html index b6ca492e275a4..708e34d93c0a3 100644 --- a/platform/mv3/extension/dashboard.html +++ b/platform/mv3/extension/dashboard.html @@ -3,6 +3,7 @@ + diff --git a/platform/mv3/extension/matched-rules.html b/platform/mv3/extension/matched-rules.html index 488e150a3079e..493c681fada78 100644 --- a/platform/mv3/extension/matched-rules.html +++ b/platform/mv3/extension/matched-rules.html @@ -3,6 +3,7 @@ + Matched rules diff --git a/platform/mv3/extension/popup.html b/platform/mv3/extension/popup.html index 1c4ed595a8cb2..c54035b46e06b 100644 --- a/platform/mv3/extension/popup.html +++ b/platform/mv3/extension/popup.html @@ -4,6 +4,7 @@ + diff --git a/platform/mv3/extension/report.html b/platform/mv3/extension/report.html index 963549aae2343..378701f7d3291 100644 --- a/platform/mv3/extension/report.html +++ b/platform/mv3/extension/report.html @@ -3,6 +3,7 @@ + uBO Lite — Report diff --git a/platform/mv3/extension/strictblock.html b/platform/mv3/extension/strictblock.html index 8b6ef2d9be94b..9cd2570302a27 100644 --- a/platform/mv3/extension/strictblock.html +++ b/platform/mv3/extension/strictblock.html @@ -3,6 +3,7 @@ + diff --git a/src/1p-filters.html b/src/1p-filters.html index bc08479c5c2e4..d35124f21dcb9 100644 --- a/src/1p-filters.html +++ b/src/1p-filters.html @@ -3,6 +3,7 @@ + uBlock — Your filters diff --git a/src/3p-filters.html b/src/3p-filters.html index c9123cc9dfdf6..c7db3b7bfb39f 100644 --- a/src/3p-filters.html +++ b/src/3p-filters.html @@ -3,6 +3,7 @@ + uBlock — Filter lists diff --git a/src/about.html b/src/about.html index fc98984178b23..e747a93051d2a 100644 --- a/src/about.html +++ b/src/about.html @@ -3,6 +3,7 @@ + uBlock — About diff --git a/src/advanced-settings.html b/src/advanced-settings.html index 22bee72db1207..fffd92b4ccd7a 100644 --- a/src/advanced-settings.html +++ b/src/advanced-settings.html @@ -3,6 +3,7 @@ + diff --git a/src/asset-viewer.html b/src/asset-viewer.html index b30320f9e523e..fe12a8c1b3960 100644 --- a/src/asset-viewer.html +++ b/src/asset-viewer.html @@ -3,6 +3,7 @@ + diff --git a/src/code-viewer.html b/src/code-viewer.html index 4b699feda925d..caddbdaab87f4 100644 --- a/src/code-viewer.html +++ b/src/code-viewer.html @@ -3,6 +3,7 @@ + Code viewer diff --git a/src/dashboard.html b/src/dashboard.html index b62813be838ca..7924678a1cf37 100644 --- a/src/dashboard.html +++ b/src/dashboard.html @@ -3,6 +3,7 @@ + diff --git a/src/devtools.html b/src/devtools.html index 561037661c2c5..8893759d92e60 100644 --- a/src/devtools.html +++ b/src/devtools.html @@ -3,6 +3,7 @@ + uBlock — Dev tools diff --git a/src/document-blocked.html b/src/document-blocked.html index 4c69359f1664e..da7fdb847e3a1 100644 --- a/src/document-blocked.html +++ b/src/document-blocked.html @@ -3,6 +3,7 @@ + diff --git a/src/dyna-rules.html b/src/dyna-rules.html index 451f85867a43c..2e8fd9a85ac4f 100644 --- a/src/dyna-rules.html +++ b/src/dyna-rules.html @@ -3,6 +3,7 @@ + uBlock — Dynamic filtering rules diff --git a/src/logger-ui.html b/src/logger-ui.html index 8836bd373de45..06b9c49dc8901 100644 --- a/src/logger-ui.html +++ b/src/logger-ui.html @@ -3,6 +3,7 @@ + diff --git a/src/no-dashboard.html b/src/no-dashboard.html index 35b27c9d503ad..b7345cb8cb79f 100644 --- a/src/no-dashboard.html +++ b/src/no-dashboard.html @@ -3,6 +3,7 @@ + uBlock — About diff --git a/src/popup-fenix.html b/src/popup-fenix.html index 7c43e980ace09..ee7ab415a73b3 100644 --- a/src/popup-fenix.html +++ b/src/popup-fenix.html @@ -4,6 +4,7 @@ + diff --git a/src/settings.html b/src/settings.html index 92dfca3c7d85e..91f9aedb03257 100644 --- a/src/settings.html +++ b/src/settings.html @@ -3,6 +3,7 @@ + uBlock — Settings diff --git a/src/support.html b/src/support.html index fedb42ef2f470..63ca054210b6b 100644 --- a/src/support.html +++ b/src/support.html @@ -3,6 +3,7 @@ + uBlock Origin — Support diff --git a/src/whitelist.html b/src/whitelist.html index 517c0f5c3efe4..ca207ba19aa56 100644 --- a/src/whitelist.html +++ b/src/whitelist.html @@ -3,6 +3,7 @@ + uBlock — Whitelist From 60735f88f910d708bfea69439b76e902abce01f9 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sun, 11 May 2025 10:22:56 -0400 Subject: [PATCH 0907/1099] New revision for release candidate --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 025036ffe681e..6fd834bbe2cb1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,4 @@ +- [Use `color-scheme` `meta` tag, as suggested](https://github.com/gorhill/uBlock/commit/5c029b3532) - [Bring zapper look in line with uBO Lite's zapper](https://github.com/gorhill/uBlock/commit/3f59f94b60) - [Ignore `start_page` transition for popup-blocking purpose](https://github.com/gorhill/uBlock/commit/0243a141a7) - [Exclude `chrome:` as valid openers for popup candidates](https://github.com/gorhill/uBlock/commit/59f4aca010) From 3508176fcc73a1fa6b7ef0d6d9f7979e7011d60e Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sun, 11 May 2025 10:23:31 -0400 Subject: [PATCH 0908/1099] New revision for release candidate --- dist/version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dist/version b/dist/version index 522122413daf8..d1cf74730dd88 100644 --- a/dist/version +++ b/dist/version @@ -1 +1 @@ -1.63.3.102 \ No newline at end of file +1.63.3.103 \ No newline at end of file From 63eeaae5e957fd971fcaface8a0a5e3b4d68bee9 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sun, 11 May 2025 10:31:14 -0400 Subject: [PATCH 0909/1099] Make Firefox dev build auto-update --- dist/firefox/updates.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dist/firefox/updates.json b/dist/firefox/updates.json index 6f5e770b02871..15bdb2889201d 100644 --- a/dist/firefox/updates.json +++ b/dist/firefox/updates.json @@ -3,9 +3,9 @@ "uBlock0@raymondhill.net": { "updates": [ { - "version": "1.63.3.102", + "version": "1.63.3.103", "browser_specific_settings": { "gecko": { "strict_min_version": "92.0" } }, - "update_link": "https://github.com/gorhill/uBlock/releases/download/1.63.3rc2/uBlock0_1.63.3rc2.firefox.signed.xpi" + "update_link": "https://github.com/gorhill/uBlock/releases/download/1.63.3rc3/uBlock0_1.63.3rc3.firefox.signed.xpi" } ] } From 3ff54b7f2a7cf7abd56adf2c0a2fdaad06319208 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sun, 11 May 2025 10:34:49 -0400 Subject: [PATCH 0910/1099] [mv3] Just force a reload when URL doesn't change Most of the time the URL doesn't change and just forcing a reload of the page is sufficient. When a document is strict-blocked, the URL must be updated however. --- platform/mv3/extension/js/popup.js | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/platform/mv3/extension/js/popup.js b/platform/mv3/extension/js/popup.js index 2cb89045b9467..0556d695350c4 100644 --- a/platform/mv3/extension/js/popup.js +++ b/platform/mv3/extension/js/popup.js @@ -109,10 +109,13 @@ async function commitFilteringMode() { setFilteringMode(actualLevel); } if ( actualLevel !== beforeLevel && popupPanelData.autoReload ) { + const justReload = tabURL.href === currentTab.url; self.setTimeout(( ) => { - browser.tabs.update(currentTab.id, { - url: tabURL.href, - }); + if ( justReload ) { + browser.tabs.reload(currentTab.id); + } else { + browser.tabs.update(currentTab.id, { url: tabURL.href }); + } }, 437); } } From 2e7d0a07e77fcab57256ca48352c3156ade8c9c3 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sun, 11 May 2025 10:36:21 -0400 Subject: [PATCH 0911/1099] [mv3] Add TODO comment --- src/js/static-dnr-filtering.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/js/static-dnr-filtering.js b/src/js/static-dnr-filtering.js index 231788604dbe0..cca012b7a112b 100644 --- a/src/js/static-dnr-filtering.js +++ b/src/js/static-dnr-filtering.js @@ -227,6 +227,7 @@ function addExtendedToDNR(context, parser) { if ( bad ) { continue; } if ( not && exception ) { continue; } isGeneric = false; + // TODO: Support regex- and path-based entries if ( isRegexOrPath(hn) ) { continue; } if ( details === undefined ) { context.specificCosmeticFilters.set(compiled, details = {}); From a075442e1c474b96e0c50484d64fcd3538474270 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sun, 11 May 2025 10:37:24 -0400 Subject: [PATCH 0912/1099] Import translation work from https://crowdin.com/project/ublock --- platform/mv3/description/webstore.vi.txt | 2 +- platform/mv3/extension/_locales/sv/messages.json | 2 +- platform/mv3/extension/_locales/vi/messages.json | 6 +++--- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/platform/mv3/description/webstore.vi.txt b/platform/mv3/description/webstore.vi.txt index 5fb52bcc9c4b3..e35032cd04f4d 100644 --- a/platform/mv3/description/webstore.vi.txt +++ b/platform/mv3/description/webstore.vi.txt @@ -1,4 +1,4 @@ -uBO Lite (uBOL) is an MV3-based content blocker. +uBO Lite (uBOL) là trình chặn nội dung dựa trên MV3. Bộ quy tắc mặc định tương tự bộ lọc của uBlock Origin: diff --git a/platform/mv3/extension/_locales/sv/messages.json b/platform/mv3/extension/_locales/sv/messages.json index 7816476c4f560..93c337c914ff6 100644 --- a/platform/mv3/extension/_locales/sv/messages.json +++ b/platform/mv3/extension/_locales/sv/messages.json @@ -4,7 +4,7 @@ "description": "extension name." }, "extShortDesc": { - "message": "En behörighetslös innehållsblockerare. Blockerar annonser, spårare, miners och mer omedelbart efter installationen.", + "message": "En effektiv innehållsblockerare. Blockerar annonser, spårare, miners och mer direkt efter den har installerats.", "description": "this will be in the Chrome web store: must be 132 characters or less" }, "perRulesetStats": { diff --git a/platform/mv3/extension/_locales/vi/messages.json b/platform/mv3/extension/_locales/vi/messages.json index 195dd5905f95b..548be6fe60d6a 100644 --- a/platform/mv3/extension/_locales/vi/messages.json +++ b/platform/mv3/extension/_locales/vi/messages.json @@ -112,7 +112,7 @@ "description": "First paragraph of 'Filter issues' section in Support pane" }, "supportS5H": { - "message": "Troubleshooting information", + "message": "Thông tin chẩn đoán lỗi", "description": "Label of 'Troubleshooting information' section in 'Report a filter issue' page" }, "supportS6P1S1": { @@ -276,11 +276,11 @@ "description": "A button to navigate to the blocked page" }, "zapperTipEnter": { - "message": "Enter element zapper mode", + "message": "Chuyển sang chế độ chặn phần tử tạm thời", "description": "Tooltip for the button used to enter zapper mode" }, "zapperTipQuit": { - "message": "Exit element zapper mode", + "message": "Thoát khỏi chế độ chặn phần tử tạm thời", "description": "Tooltip for the button used to exit zapper mode" } } From e3a3026cac5993b3781c5075d8e9f666ec7b7979 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Mon, 12 May 2025 07:46:33 -0400 Subject: [PATCH 0913/1099] Use custom blank page for embedded iframe in dashboard Related issue: https://github.com/uBlockOrigin/uBlock-issues/issues/3631#issuecomment-2870424577 --- src/blank.html | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 src/blank.html diff --git a/src/blank.html b/src/blank.html new file mode 100644 index 0000000000000..be329a5cc9426 --- /dev/null +++ b/src/blank.html @@ -0,0 +1,11 @@ + + + + + + +uBO blank + + + + From 8cd6212867051dd51d405a0d22b5d711f297a777 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Mon, 12 May 2025 07:48:22 -0400 Subject: [PATCH 0914/1099] Use custom blank page for embedded iframe in dashboard Related issue: https://github.com/uBlockOrigin/uBlock-issues/issues/3631#issuecomment-2870424577 --- src/dashboard.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/dashboard.html b/src/dashboard.html index 7924678a1cf37..7614d9299c300 100644 --- a/src/dashboard.html +++ b/src/dashboard.html @@ -37,7 +37,7 @@
- + From c02c571b3e4627ed842c3e3ed88a9ff517e43d58 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Mon, 12 May 2025 07:50:22 -0400 Subject: [PATCH 0915/1099] New revision for release candidate --- dist/version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dist/version b/dist/version index d1cf74730dd88..2fdcc30755001 100644 --- a/dist/version +++ b/dist/version @@ -1 +1 @@ -1.63.3.103 \ No newline at end of file +1.63.3.104 \ No newline at end of file From 7c5c9beda6db29c5bb006830c5f53d7bcd8c53aa Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Mon, 12 May 2025 07:51:41 -0400 Subject: [PATCH 0916/1099] Update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6fd834bbe2cb1..69e4d53079fa6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,4 @@ +- [Use custom blank page for embedded iframe in dashboard](https://github.com/gorhill/uBlock/commit/8cd6212867) - [Use `color-scheme` `meta` tag, as suggested](https://github.com/gorhill/uBlock/commit/5c029b3532) - [Bring zapper look in line with uBO Lite's zapper](https://github.com/gorhill/uBlock/commit/3f59f94b60) - [Ignore `start_page` transition for popup-blocking purpose](https://github.com/gorhill/uBlock/commit/0243a141a7) From 82c6d5e8ff0fe489387d74971c2f080a64c7ab7d Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Mon, 12 May 2025 08:40:38 -0400 Subject: [PATCH 0917/1099] Make Firefox dev build auto-update --- dist/firefox/updates.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dist/firefox/updates.json b/dist/firefox/updates.json index 15bdb2889201d..70f8da3f27068 100644 --- a/dist/firefox/updates.json +++ b/dist/firefox/updates.json @@ -3,9 +3,9 @@ "uBlock0@raymondhill.net": { "updates": [ { - "version": "1.63.3.103", + "version": "1.63.3.104", "browser_specific_settings": { "gecko": { "strict_min_version": "92.0" } }, - "update_link": "https://github.com/gorhill/uBlock/releases/download/1.63.3rc3/uBlock0_1.63.3rc3.firefox.signed.xpi" + "update_link": "https://github.com/gorhill/uBlock/releases/download/1.63.3rc4/uBlock0_1.63.3rc4.firefox.signed.xpi" } ] } From 569dfea3cb65773dbee2a765a7fc99d6a9426514 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Mon, 12 May 2025 16:55:23 -0400 Subject: [PATCH 0918/1099] [mv3] Have the slider button size dictates all other dimensions --- platform/mv3/extension/css/filtering-mode.css | 49 +++++++++++-------- platform/mv3/extension/css/popup.css | 16 +++--- platform/mv3/extension/css/settings.css | 6 ++- platform/mv3/extension/popup.html | 18 +++---- 4 files changed, 48 insertions(+), 41 deletions(-) diff --git a/platform/mv3/extension/css/filtering-mode.css b/platform/mv3/extension/css/filtering-mode.css index c7d37c03b7469..2554bdc3ac385 100644 --- a/platform/mv3/extension/css/filtering-mode.css +++ b/platform/mv3/extension/css/filtering-mode.css @@ -1,20 +1,24 @@ +:root { + --filtering-mode-button-size: 60px; /* should be multiple of 4 */ + --filtering-mode-slider-width: calc(4 * var(--filtering-mode-button-size) + 3px); + } + .filteringModeSlider { align-items: center; container-type: size; display: flex; - height: 60px; + height: var(--filtering-mode-button-size); justify-content: center; position: relative; - width: 240px; + width: var(--filtering-mode-slider-width); } .filteringModeButton { background-color: var(--surface-1); - box-sizing: border-box; - border-radius: 30% 15% / 15% 30%; - height: calc(25cqw + 2px); + border-radius: 20%; + height: var(--filtering-mode-button-size); position: absolute; - width: calc(25cqw + 2px); + width: var(--filtering-mode-button-size); z-index: 10; } @@ -23,9 +27,8 @@ border: 4px solid var(--accent-surface-1); border-radius: inherit; box-sizing: border-box; - height: calc(100% - 2px); - margin: 1px; - width: calc(100% - 2px); + height: 100%; + width: 100%; } .filteringModeSlider.moving .filteringModeButton > div, @@ -40,14 +43,12 @@ .filteringModeSlider span[data-level] { background-color: var(--accent-surface-1); - border: 1px solid var(--accent-surface-1); - box-sizing: border-box; display: inline-flex; + flex-shrink: 0; height: 30%; margin-left: 1px; - width: 25%; + width: var(--filtering-mode-button-size); } - .filteringModeSlider span[data-level]:first-of-type { margin-left: 0; } @@ -60,25 +61,33 @@ left: 0; } .filteringModeSlider[data-level="1"] .filteringModeButton { - left: calc(25% - 1px); + left: calc(var(--filtering-mode-button-size) + 1px); } .filteringModeSlider[data-level="2"] .filteringModeButton { - left: calc(50% - 1px); + left: calc(var(--filtering-mode-button-size) * 2 + 2px); } .filteringModeSlider[data-level="3"] .filteringModeButton { - left: calc(75% - 1px); + left: calc(var(--filtering-mode-button-size) * 3 + 3px); + } + +[dir="rtl"] .filteringModeSlider span[data-level] { + margin-left: 0; + margin-right: 1px; + } +[dir="rtl"] .filteringModeSlider span[data-level]:first-of-type { + margin-right: 0; } [dir="rtl"] .filteringModeSlider[data-level="0"] .filteringModeButton { - left: 75%; + right: 0; } [dir="rtl"] .filteringModeSlider[data-level="1"] .filteringModeButton { - left: 50%; + right: calc(var(--filtering-mode-button-size) + 1px); } [dir="rtl"] .filteringModeSlider[data-level="2"] .filteringModeButton { - left: 25%; + right: calc(var(--filtering-mode-button-size) * 2 + 2px); } [dir="rtl"] .filteringModeSlider[data-level="3"] .filteringModeButton { - left: 0; + right: calc(var(--filtering-mode-button-size) * 3 + 3px); } diff --git a/platform/mv3/extension/css/popup.css b/platform/mv3/extension/css/popup.css index 19bef6ac1100f..497c42d59703a 100644 --- a/platform/mv3/extension/css/popup.css +++ b/platform/mv3/extension/css/popup.css @@ -10,7 +10,10 @@ --popup-gap: var(--font-size); --popup-gap-thin: calc(0.5 * var(--popup-gap)); --popup-gap-extra-thin: calc(0.25 * var(--popup-gap)); - --popup-main-min-width: 18em; + --popup-main-min-width: calc( + var(--filtering-mode-slider-width) + + var(--filtering-mode-button-size) / 2 + ); --popup-firewall-min-width: 30em; --popup-rule-cell-width: 5em; font-size: var(--font-size); @@ -34,11 +37,14 @@ a { display: flex; flex-direction: column; max-width: 340px; - min-width: 100%; + min-width: var(--popup-main-min-width); } :root.portrait #main { align-self: inherit; } +:root.mobile #main { + min-width: 100%; + } hr { border: 0; border-top: 1px solid var(--hr-ink); @@ -70,11 +76,6 @@ body[data-forbid~="dashboard"] #gotoDashboard { display: none; } -.lrspacer { - padding-left: var(--default-gap-small); - padding-right: var(--default-gap-small); - } - #filteringModeText { color: var(--ink-3); margin-bottom: var(--default-gap-small); @@ -102,7 +103,6 @@ body[data-forbid~="dashboard"] #gotoDashboard { align-self: center; margin-bottom: var(--popup-gap); margin-top: var(--popup-gap); - width: calc(var(--popup-main-min-width) - 1em); } .rulesetTools { diff --git a/platform/mv3/extension/css/settings.css b/platform/mv3/extension/css/settings.css index 994c7a8d2a151..f5db7ecd2d2d5 100644 --- a/platform/mv3/extension/css/settings.css +++ b/platform/mv3/extension/css/settings.css @@ -1,3 +1,7 @@ +:root { + --filtering-mode-button-size: 32px; + } + body.loading { visibility: hidden; } @@ -67,9 +71,7 @@ label:has(input[type="checkbox"][disabled]) + legend { white-space: pre-line; } .filteringModeSlider { - height: calc(60px / 2); pointer-events: none; - width: calc(240px / 2); } h3[data-i18n="filteringMode0Name"]::first-letter { diff --git a/platform/mv3/extension/popup.html b/platform/mv3/extension/popup.html index c54035b46e06b..44387bead8b84 100644 --- a/platform/mv3/extension/popup.html +++ b/platform/mv3/extension/popup.html @@ -18,19 +18,15 @@
­
-
-
-
- - - - -
+
+
+ + + +
-
-

_
-
+

_
bolt From 9763a79c01986037e4e7cc9abd8b6e7fe046a052 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Mon, 12 May 2025 18:00:14 -0400 Subject: [PATCH 0919/1099] [mv3] Minor CSS adjustments --- platform/mv3/extension/css/popup.css | 4 ++-- platform/mv3/extension/js/theme.js | 4 +++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/platform/mv3/extension/css/popup.css b/platform/mv3/extension/css/popup.css index 497c42d59703a..66fffe5d2dcf2 100644 --- a/platform/mv3/extension/css/popup.css +++ b/platform/mv3/extension/css/popup.css @@ -12,7 +12,7 @@ --popup-gap-extra-thin: calc(0.25 * var(--popup-gap)); --popup-main-min-width: calc( var(--filtering-mode-slider-width) + - var(--filtering-mode-button-size) / 2 + var(--filtering-mode-button-size) ); --popup-firewall-min-width: 30em; --popup-rule-cell-width: 5em; @@ -43,7 +43,7 @@ a { align-self: inherit; } :root.mobile #main { - min-width: 100%; + /*min-width: 100%;*/ } hr { border: 0; diff --git a/platform/mv3/extension/js/theme.js b/platform/mv3/extension/js/theme.js index a20d459f01600..d816250a3d512 100644 --- a/platform/mv3/extension/js/theme.js +++ b/platform/mv3/extension/js/theme.js @@ -34,5 +34,7 @@ import { dom } from './dom.js'; { const mql = self.matchMedia('(hover: hover)'); - dom.cl.toggle(dom.html, 'mobile', mql.matches !== true); + const isTouchScreen = mql.matches !== true; + dom.cl.toggle(dom.html, 'mobile', isTouchScreen); + dom.cl.toggle(dom.html, 'desktop', isTouchScreen === false); } From 84db4585f5c346ade18bcc13d3dbc4f9450ffa7e Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Mon, 12 May 2025 19:31:40 -0400 Subject: [PATCH 0920/1099] [mv3] Fix popup width for Firefox --- platform/mv3/extension/css/popup.css | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/platform/mv3/extension/css/popup.css b/platform/mv3/extension/css/popup.css index 66fffe5d2dcf2..ec886dd616686 100644 --- a/platform/mv3/extension/css/popup.css +++ b/platform/mv3/extension/css/popup.css @@ -4,21 +4,24 @@ top: -20%; } +:root { + --popup-min-width: calc( + var(--filtering-mode-slider-width) + + var(--filtering-mode-button-size) + ); +} + :root body, :root.mobile body { --font-size: 14px; --popup-gap: var(--font-size); --popup-gap-thin: calc(0.5 * var(--popup-gap)); --popup-gap-extra-thin: calc(0.25 * var(--popup-gap)); - --popup-main-min-width: calc( - var(--filtering-mode-slider-width) + - var(--filtering-mode-button-size) - ); --popup-firewall-min-width: 30em; --popup-rule-cell-width: 5em; font-size: var(--font-size); line-height: 20px; - min-width: 100%; + min-width: var(--popup-min-width); } :root body.loading { opacity: 0; @@ -37,14 +40,10 @@ a { display: flex; flex-direction: column; max-width: 340px; - min-width: var(--popup-main-min-width); } :root.portrait #main { align-self: inherit; } -:root.mobile #main { - /*min-width: 100%;*/ - } hr { border: 0; border-top: 1px solid var(--hr-ink); From 3e39e5beab9f2f00da4b645a1f0d08f01888a70a Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Tue, 13 May 2025 08:00:39 -0400 Subject: [PATCH 0921/1099] [mv3] CSS fine tuning --- platform/mv3/extension/css/popup.css | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/platform/mv3/extension/css/popup.css b/platform/mv3/extension/css/popup.css index ec886dd616686..0e2533dc8c29b 100644 --- a/platform/mv3/extension/css/popup.css +++ b/platform/mv3/extension/css/popup.css @@ -7,18 +7,16 @@ :root { --popup-min-width: calc( var(--filtering-mode-slider-width) + - var(--filtering-mode-button-size) + var(--filtering-mode-button-size) / 2 ); } :root body, :root.mobile body { - --font-size: 14px; + --font-size: 16px; --popup-gap: var(--font-size); --popup-gap-thin: calc(0.5 * var(--popup-gap)); --popup-gap-extra-thin: calc(0.25 * var(--popup-gap)); - --popup-firewall-min-width: 30em; - --popup-rule-cell-width: 5em; font-size: var(--font-size); line-height: 20px; min-width: var(--popup-min-width); @@ -176,7 +174,7 @@ body.needReload #refresh { font-size: 1.4em; min-width: 32px; padding: var(--popup-gap) - var(--popup-gap-thin); + var(--popup-gap-extra-thin); unicode-bidi: embed; visibility: hidden; } @@ -188,6 +186,7 @@ body.needReload #refresh { visibility: visible; } .toolRibbon .tool .caption { + color: var(--ink-2); font: 10px/12px sans-serif; margin-top: 6px; text-align: center; From 71244bc37b8a57b9bf88e4b1190a4d2c29a65977 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Tue, 13 May 2025 09:18:38 -0400 Subject: [PATCH 0922/1099] New revision for stable release --- dist/version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dist/version b/dist/version index 2fdcc30755001..75733293117e6 100644 --- a/dist/version +++ b/dist/version @@ -1 +1 @@ -1.63.3.104 \ No newline at end of file +1.64.0 \ No newline at end of file From ed3a0b8ab9a9ee645331e0ebabb4b5619cab15c2 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Tue, 13 May 2025 10:52:42 -0400 Subject: [PATCH 0923/1099] [mv3] Remove hard-coded line height --- platform/mv3/extension/css/popup.css | 1 - 1 file changed, 1 deletion(-) diff --git a/platform/mv3/extension/css/popup.css b/platform/mv3/extension/css/popup.css index 0e2533dc8c29b..4fb540577b1ee 100644 --- a/platform/mv3/extension/css/popup.css +++ b/platform/mv3/extension/css/popup.css @@ -18,7 +18,6 @@ --popup-gap-thin: calc(0.5 * var(--popup-gap)); --popup-gap-extra-thin: calc(0.25 * var(--popup-gap)); font-size: var(--font-size); - line-height: 20px; min-width: var(--popup-min-width); } :root body.loading { From 7e239db9a80acffb95a3c6a9f7e1d4732e658b44 Mon Sep 17 00:00:00 2001 From: Imre Eilertsen Date: Tue, 13 May 2025 18:38:33 +0200 Subject: [PATCH 0924/1099] Added AdGuard's 2 newest !#if tokens. (#3938) --- src/js/static-filtering-parser.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/js/static-filtering-parser.js b/src/js/static-filtering-parser.js index 53f642c22ec43..b9b54ccb73d95 100644 --- a/src/js/static-filtering-parser.js +++ b/src/js/static-filtering-parser.js @@ -610,11 +610,13 @@ export const preparserIfTokens = new Set([ 'ext_abp', 'adguard', 'adguard_app_android', + 'adguard_app_cli', 'adguard_app_ios', 'adguard_app_mac', 'adguard_app_windows', 'adguard_ext_android_cb', 'adguard_ext_chromium', + 'adguard_ext_chromium_mv3', 'adguard_ext_edge', 'adguard_ext_firefox', 'adguard_ext_opera', @@ -4182,11 +4184,13 @@ export const utils = (( ) => { // https://adguard.com/kb/general/ad-filtering/create-own-filters/#conditions-directive [ 'adguard', 'adguard' ], [ 'adguard_app_android', 'false' ], + [ 'adguard_app_cli', 'false' ], [ 'adguard_app_ios', 'false' ], [ 'adguard_app_mac', 'false' ], [ 'adguard_app_windows', 'false' ], [ 'adguard_ext_android_cb', 'false' ], [ 'adguard_ext_chromium', 'chromium' ], + [ 'adguard_ext_chromium_mv3', 'mv3' ], [ 'adguard_ext_edge', 'edge' ], [ 'adguard_ext_firefox', 'firefox' ], [ 'adguard_ext_opera', 'chromium' ], From d12e7817d290f7cdbc4393cd409405fd43757029 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Wed, 14 May 2025 10:02:37 -0400 Subject: [PATCH 0925/1099] [mv3] Add ability to reset zapper selection on mobile devices --- platform/mv3/extension/css/zapper-ui.css | 25 ++++++++++++++---------- platform/mv3/extension/js/zapper-ui.js | 11 +++++++++++ platform/mv3/extension/zapper-ui.html | 8 +++++++- 3 files changed, 33 insertions(+), 11 deletions(-) diff --git a/platform/mv3/extension/css/zapper-ui.css b/platform/mv3/extension/css/zapper-ui.css index 4385d626d2a9d..5f66d49a5ee04 100644 --- a/platform/mv3/extension/css/zapper-ui.css +++ b/platform/mv3/extension/css/zapper-ui.css @@ -16,11 +16,11 @@ html#ubol-zapper, outline: none; } #ubol-zapper aside { - background-color: var(--surface-1); box-sizing: border-box; cursor: default; display: flex; flex-direction: column; + gap: 2px; position: fixed; right: 2px; top: 50%; @@ -31,14 +31,6 @@ html#ubol-zapper, left: 2px; right: unset; } -#ubol-zapper aside > #quit { - border: 1px solid rgba(0,0,0,0.5); - fill: none; - stroke: var(--ink-1); - stroke-width: 3px; - width: var(--quit-button-size); - height: var(--quit-button-size); -} #ubol-zapper svg#sea { cursor: crosshair; box-sizing: border-box; @@ -57,6 +49,19 @@ html#ubol-zapper, stroke-width: 0.5px; fill: rgba(255,255,63,0.20); } -#ubol-zapper #quit:hover { +#ubol-zapper aside > div { + background-color: var(--surface-1); + border: 1px solid rgba(0,0,0,0.5); + box-sizing: border-box; + fill: none; + stroke: var(--ink-1); + stroke-width: 2px; + width: var(--quit-button-size); + height: var(--quit-button-size); +} +#ubol-zapper aside > div:hover { background-color: var(--surface-2) } +:root:not(.mobile) #pick { + display: none; +} \ No newline at end of file diff --git a/platform/mv3/extension/js/zapper-ui.js b/platform/mv3/extension/js/zapper-ui.js index 46bf3217abe6f..ec9e08c19b339 100644 --- a/platform/mv3/extension/js/zapper-ui.js +++ b/platform/mv3/extension/js/zapper-ui.js @@ -126,6 +126,7 @@ const svgListening = (( ) => { }; return state => { + if ( $stor(':root.mobile') !== null ) { return; } if ( state === on ) { return; } on = state; if ( on ) { @@ -190,6 +191,7 @@ const startZapper = function(port) { $stor('svg#sea').addEventListener('touchstart', onSvgTouch, { passive: true }); $stor('svg#sea').addEventListener('touchend', onSvgTouch); $id('quit').addEventListener('click', quitZapper ); + $id('pick').addEventListener('click', resetZapper ); svgListening(true); }; @@ -201,6 +203,7 @@ const quitZapper = function() { $stor('svg#sea').removeEventListener('touchstart', onSvgTouch, { passive: true }); $stor('svg#sea').removeEventListener('touchend', onSvgTouch); $id('quit').removeEventListener('click', quitZapper ); + $id('pick').removeEventListener('click', resetZapper ); svgListening(false); if ( zapperScriptPort ) { zapperScriptPort.postMessage({ what: 'quitZapper' }); @@ -213,6 +216,14 @@ const quitZapper = function() { /******************************************************************************/ +const resetZapper = function() { + zapperScriptPort.postMessage({ + what: 'unhighlight' + }); +}; + +/******************************************************************************/ + // Wait for the content script to establish communication globalThis.addEventListener('message', ev => { diff --git a/platform/mv3/extension/zapper-ui.html b/platform/mv3/extension/zapper-ui.html index a739d97a42436..c80208fb6395e 100644 --- a/platform/mv3/extension/zapper-ui.html +++ b/platform/mv3/extension/zapper-ui.html @@ -14,12 +14,18 @@
+
+ + + + +
- + From fd5da3fcd22dfb86fb6dc4fbf98cbb2ce7882260 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Wed, 14 May 2025 17:20:55 -0400 Subject: [PATCH 0926/1099] [mv3] Use CodeMirror editor for list of no-filtering websites This is a first step to integrate CodeMirror6 into the project. As a side effect, this should take care of: https://github.com/uBlockOrigin/uBOL-home/issues/297 Though most likely the list of no-filtering websites will probably move to its own pane as in uBO in some future. --- platform/mv3/extension/css/settings.css | 7 ++-- platform/mv3/extension/dashboard.html | 4 +-- platform/mv3/extension/js/settings.js | 35 ++++++++++++++----- .../mv3/extension/lib/codemirror/README.md | 9 +++++ .../lib/codemirror/cm6.bundle.ubol.min.js | 1 + tools/make-mv3.sh | 1 + 6 files changed, 42 insertions(+), 15 deletions(-) create mode 100644 platform/mv3/extension/lib/codemirror/README.md create mode 100644 platform/mv3/extension/lib/codemirror/cm6.bundle.ubol.min.js diff --git a/platform/mv3/extension/css/settings.css b/platform/mv3/extension/css/settings.css index f5db7ecd2d2d5..315dea79d0117 100644 --- a/platform/mv3/extension/css/settings.css +++ b/platform/mv3/extension/css/settings.css @@ -78,10 +78,9 @@ h3[data-i18n="filteringMode0Name"]::first-letter { text-transform: capitalize; } #trustedSites { - box-sizing: border-box; - height: 6rem; - resize: vertical; - width: 100%; + background-color: var(--surface-0); + border: 1px solid var(--surface-3); + min-height: 8rem; } section[data-pane="rulesets"] > div:first-of-type { diff --git a/platform/mv3/extension/dashboard.html b/platform/mv3/extension/dashboard.html index 708e34d93c0a3..3009215578011 100644 --- a/platform/mv3/extension/dashboard.html +++ b/platform/mv3/extension/dashboard.html @@ -91,8 +91,7 @@

_

-

-

+
@@ -156,6 +155,7 @@

+ diff --git a/platform/mv3/extension/js/settings.js b/platform/mv3/extension/js/settings.js index 1a7812cbc2ceb..19303e4068f5f 100644 --- a/platform/mv3/extension/js/settings.js +++ b/platform/mv3/extension/js/settings.js @@ -22,11 +22,24 @@ import { browser, sendMessage } from './ext.js'; import { dom, qs$ } from './dom.js'; import { hashFromIterable } from './dashboard.js'; +import { i18n$ } from './i18n.js'; import punycode from './punycode.js'; import { renderFilterLists } from './filter-lists.js'; /******************************************************************************/ +const cm6 = self.cm6; +const cmView = (( ) => { + const options = {}; + if ( dom.cl.has(':root', 'dark') ) { + options.oneDark = true; + } + options.placeholder = i18n$('noFilteringModePlaceholder'); + return cm6.createEditorView( + cm6.createEditorState('', options), + qs$('#trustedSites') + ); +})(); let cachedRulesetData = {}; /******************************************************************************/ @@ -167,12 +180,15 @@ dom.on('#developerMode input[type="checkbox"]', 'change', ev => { /******************************************************************************/ function renderTrustedSites() { - const textarea = qs$('#trustedSites'); const hostnames = cachedRulesetData.trustedSites || []; - textarea.value = hostnames.map(hn => punycode.toUnicode(hn)).join('\n'); - if ( textarea.value !== '' ) { - textarea.value += '\n'; - } + let text = hostnames.map(hn => punycode.toUnicode(hn)).join('\n'); + if ( text !== '' ) { text += '\n'; } + cmView.dispatch({ + changes: { + from: 0, to: cmView.state.doc.length, + insert: text + }, + }); } function changeTrustedSites() { @@ -186,8 +202,8 @@ function changeTrustedSites() { } function getStagedTrustedSites() { - const textarea = qs$('#trustedSites'); - return textarea.value.split(/\s/).map(hn => { + const text = cmView.state.doc.toString(); + return text.split(/\s/).map(hn => { try { return punycode.toASCII( (new URL(`https://${hn}/`)).hostname @@ -198,7 +214,7 @@ function getStagedTrustedSites() { }).filter(hn => hn !== ''); } -dom.on('#trustedSites', 'blur', changeTrustedSites); +dom.on(cmView.contentDOM, 'blur', changeTrustedSites); self.addEventListener('beforeunload', changeTrustedSites); @@ -296,7 +312,8 @@ sendMessage({ renderFilterLists(cachedRulesetData); renderWidgets(); dom.cl.remove(dom.body, 'loading'); - } catch { + } catch(reason) { + console.error(reason); } listen(); }).catch(reason => { diff --git a/platform/mv3/extension/lib/codemirror/README.md b/platform/mv3/extension/lib/codemirror/README.md new file mode 100644 index 0000000000000..67f9e3d6e4a15 --- /dev/null +++ b/platform/mv3/extension/lib/codemirror/README.md @@ -0,0 +1,9 @@ +Steps to build `cm6.bundle.ubol.min.js` -- command line: + +- `git clone https://github.com/gorhill/codemirror-quickstart.git` + - This is a customized repo forked from +- `cd codemirror-quickstart` +- `npm install` +- `npm build` +- `cm6.bundle.ubol.min.js` should be in `dist` directory +- This is the origin of the `cm6.bundle.ubol.min.js` in the current directory diff --git a/platform/mv3/extension/lib/codemirror/cm6.bundle.ubol.min.js b/platform/mv3/extension/lib/codemirror/cm6.bundle.ubol.min.js new file mode 100644 index 0000000000000..f22991c190ec0 --- /dev/null +++ b/platform/mv3/extension/lib/codemirror/cm6.bundle.ubol.min.js @@ -0,0 +1 @@ +var cm6=function(t){"use strict";class e{constructor(){}lineAt(t){if(t<0||t>this.length)throw new RangeError(`Invalid position ${t} in document of length ${this.length}`);return this.lineInner(t,!1,1,0)}line(t){if(t<1||t>this.lines)throw new RangeError(`Invalid line number ${t} in ${this.lines}-line document`);return this.lineInner(t,!0,1,0)}replace(t,e,i){let r=[];return this.decompose(0,t,r,2),i.length&&i.decompose(0,i.length,r,3),this.decompose(e,this.length,r,1),n.from(r,this.length-(e-t)+i.length)}append(t){return this.replace(this.length,this.length,t)}slice(t,e=this.length){let i=[];return this.decompose(t,e,i,0),n.from(i,e-t)}eq(t){if(t==this)return!0;if(t.length!=this.length||t.lines!=this.lines)return!1;let e=this.scanIdentical(t,1),i=this.length-this.scanIdentical(t,-1),n=new o(this),r=new o(t);for(let t=e,s=e;;){if(n.next(t),r.next(t),t=0,n.lineBreak!=r.lineBreak||n.done!=r.done||n.value!=r.value)return!1;if(s+=n.value.length,n.done||s>=i)return!0}}iter(t=1){return new o(this,t)}iterRange(t,e=this.length){return new l(this,t,e)}iterLines(t,e){let i;if(null==t)i=this.iter();else{null==e&&(e=this.lines+1);let n=this.line(t).from;i=this.iterRange(n,Math.max(n,e==this.lines+1?this.length:e<=1?0:this.line(e-1).to))}return new h(i)}toString(){return this.sliceString(0)}toJSON(){let t=[];return this.flatten(t),t}static of(t){if(0==t.length)throw new RangeError("A document must have at least one line");return 1!=t.length||t[0]?t.length<=32?new i(t):n.from(i.split(t,[])):e.empty}}class i extends e{constructor(t,e=function(t){let e=-1;for(let i of t)e+=i.length+1;return e}(t)){super(),this.text=t,this.length=e}get lines(){return this.text.length}get children(){return null}lineInner(t,e,i,n){for(let r=0;;r++){let s=this.text[r],o=n+s.length;if((e?i:o)>=t)return new a(n,o,i,s);n=o+1,i++}}decompose(t,e,n,o){let l=t<=0&&e>=this.length?this:new i(s(this.text,t,e),Math.min(e,this.length)-Math.max(0,t));if(1&o){let t=n.pop(),e=r(l.text,t.text.slice(),0,l.length);if(e.length<=32)n.push(new i(e,t.length+l.length));else{let t=e.length>>1;n.push(new i(e.slice(0,t)),new i(e.slice(t)))}}else n.push(l)}replace(t,e,o){if(!(o instanceof i))return super.replace(t,e,o);let l=r(this.text,r(o.text,s(this.text,0,t)),e),h=this.length+o.length-(e-t);return l.length<=32?new i(l,h):n.from(i.split(l,[]),h)}sliceString(t,e=this.length,i="\n"){let n="";for(let r=0,s=0;r<=e&&st&&s&&(n+=i),tr&&(n+=o.slice(Math.max(0,t-r),e-r)),r=l+1}return n}flatten(t){for(let e of this.text)t.push(e)}scanIdentical(){return 0}static split(t,e){let n=[],r=-1;for(let s of t)n.push(s),r+=s.length+1,32==n.length&&(e.push(new i(n,r)),n=[],r=-1);return r>-1&&e.push(new i(n,r)),e}}class n extends e{constructor(t,e){super(),this.children=t,this.length=e,this.lines=0;for(let e of t)this.lines+=e.lines}lineInner(t,e,i,n){for(let r=0;;r++){let s=this.children[r],o=n+s.length,l=i+s.lines-1;if((e?l:o)>=t)return s.lineInner(t,e,i,n);n=o+1,i=l+1}}decompose(t,e,i,n){for(let r=0,s=0;s<=e&&r=s){let r=n&((s<=t?1:0)|(l>=e?2:0));s>=t&&l<=e&&!r?i.push(o):o.decompose(t-s,e-s,i,r)}s=l+1}}replace(t,e,i){if(i.lines=s&&e<=l){let h=o.replace(t-s,e-s,i),a=this.lines-o.lines+h.lines;if(h.lines>4&&h.lines>a>>6){let s=this.children.slice();return s[r]=h,new n(s,this.length-(e-t)+i.length)}return super.replace(s,l,h)}s=l+1}return super.replace(t,e,i)}sliceString(t,e=this.length,i="\n"){let n="";for(let r=0,s=0;rt&&r&&(n+=i),ts&&(n+=o.sliceString(t-s,e-s,i)),s=l+1}return n}flatten(t){for(let e of this.children)e.flatten(t)}scanIdentical(t,e){if(!(t instanceof n))return 0;let i=0,[r,s,o,l]=e>0?[0,0,this.children.length,t.children.length]:[this.children.length-1,t.children.length-1,-1,-1];for(;;r+=e,s+=e){if(r==o||s==l)return i;let n=this.children[r],h=t.children[s];if(n!=h)return i+n.scanIdentical(h,e);i+=n.length+1}}static from(t,e=t.reduce(((t,e)=>t+e.length+1),-1)){let r=0;for(let e of t)r+=e.lines;if(r<32){let n=[];for(let e of t)e.flatten(n);return new i(n,e)}let s=Math.max(32,r>>5),o=s<<1,l=s>>1,h=[],a=0,c=-1,u=[];function f(t){let e;if(t.lines>o&&t instanceof n)for(let e of t.children)f(e);else t.lines>l&&(a>l||!a)?(d(),h.push(t)):t instanceof i&&a&&(e=u[u.length-1])instanceof i&&t.lines+e.lines<=32?(a+=t.lines,c+=t.length+1,u[u.length-1]=new i(e.text.concat(t.text),e.length+1+t.length)):(a+t.lines>s&&d(),a+=t.lines,c+=t.length+1,u.push(t))}function d(){0!=a&&(h.push(1==u.length?u[0]:n.from(u,c)),c=-1,a=u.length=0)}for(let e of t)f(e);return d(),1==h.length?h[0]:new n(h,e)}}function r(t,e,i=0,n=1e9){for(let r=0,s=0,o=!0;s=i&&(h>n&&(l=l.slice(0,n-r)),r0?1:(t instanceof i?t.text.length:t.children.length)<<1]}nextInner(t,e){for(this.done=this.lineBreak=!1;;){let n=this.nodes.length-1,r=this.nodes[n],s=this.offsets[n],o=s>>1,l=r instanceof i?r.text.length:r.children.length;if(o==(e>0?l:0)){if(0==n)return this.done=!0,this.value="",this;e>0&&this.offsets[n-1]++,this.nodes.pop(),this.offsets.pop()}else if((1&s)==(e>0?0:1)){if(this.offsets[n]+=e,0==t)return this.lineBreak=!0,this.value="\n",this;t--}else if(r instanceof i){let i=r.text[o+(e<0?-1:0)];if(this.offsets[n]+=e,i.length>Math.max(0,t))return this.value=0==t?i:e>0?i.slice(t):i.slice(0,i.length-t),this;t-=i.length}else{let s=r.children[o+(e<0?-1:0)];t>s.length?(t-=s.length,this.offsets[n]+=e):(e<0&&this.offsets[n]--,this.nodes.push(s),this.offsets.push(e>0?1:(s instanceof i?s.text.length:s.children.length)<<1))}}}next(t=0){return t<0&&(this.nextInner(-t,-this.dir),t=this.value.length),this.nextInner(t,this.dir)}}class l{constructor(t,e,i){this.value="",this.done=!1,this.cursor=new o(t,e>i?-1:1),this.pos=e>i?t.length:0,this.from=Math.min(e,i),this.to=Math.max(e,i)}nextInner(t,e){if(e<0?this.pos<=this.from:this.pos>=this.to)return this.value="",this.done=!0,this;t+=Math.max(0,e<0?this.pos-this.to:this.from-this.pos);let i=e<0?this.pos-this.from:this.to-this.pos;t>i&&(t=i),i-=t;let{value:n}=this.cursor.next(t);return this.pos+=(n.length+t)*e,this.value=n.length<=i?n:e<0?n.slice(n.length-i):n.slice(0,i),this.done=!this.value,this}next(t=0){return t<0?t=Math.max(t,this.from-this.pos):t>0&&(t=Math.min(t,this.to-this.pos)),this.nextInner(t,this.cursor.dir)}get lineBreak(){return this.cursor.lineBreak&&""!=this.value}}class h{constructor(t){this.inner=t,this.afterBreak=!0,this.value="",this.done=!1}next(t=0){let{done:e,lineBreak:i,value:n}=this.inner.next(t);return e?(this.done=!0,this.value=""):i?this.afterBreak?this.value="":(this.afterBreak=!0,this.next()):(this.value=n,this.afterBreak=!1),this}get lineBreak(){return!1}}"undefined"!=typeof Symbol&&(e.prototype[Symbol.iterator]=function(){return this.iter()},o.prototype[Symbol.iterator]=l.prototype[Symbol.iterator]=h.prototype[Symbol.iterator]=function(){return this});class a{constructor(t,e,i,n){this.from=t,this.to=e,this.number=i,this.text=n}get length(){return this.to-this.from}}let c="lc,34,7n,7,7b,19,,,,2,,2,,,20,b,1c,l,g,,2t,7,2,6,2,2,,4,z,,u,r,2j,b,1m,9,9,,o,4,,9,,3,,5,17,3,3b,f,,w,1j,,,,4,8,4,,3,7,a,2,t,,1m,,,,2,4,8,,9,,a,2,q,,2,2,1l,,4,2,4,2,2,3,3,,u,2,3,,b,2,1l,,4,5,,2,4,,k,2,m,6,,,1m,,,2,,4,8,,7,3,a,2,u,,1n,,,,c,,9,,14,,3,,1l,3,5,3,,4,7,2,b,2,t,,1m,,2,,2,,3,,5,2,7,2,b,2,s,2,1l,2,,,2,4,8,,9,,a,2,t,,20,,4,,2,3,,,8,,29,,2,7,c,8,2q,,2,9,b,6,22,2,r,,,,,,1j,e,,5,,2,5,b,,10,9,,2u,4,,6,,2,2,2,p,2,4,3,g,4,d,,2,2,6,,f,,jj,3,qa,3,t,3,t,2,u,2,1s,2,,7,8,,2,b,9,,19,3,3b,2,y,,3a,3,4,2,9,,6,3,63,2,2,,1m,,,7,,,,,2,8,6,a,2,,1c,h,1r,4,1c,7,,,5,,14,9,c,2,w,4,2,2,,3,1k,,,2,3,,,3,1m,8,2,2,48,3,,d,,7,4,,6,,3,2,5i,1m,,5,ek,,5f,x,2da,3,3x,,2o,w,fe,6,2x,2,n9w,4,,a,w,2,28,2,7k,,3,,4,,p,2,5,,47,2,q,i,d,,12,8,p,b,1a,3,1c,,2,4,2,2,13,,1v,6,2,2,2,2,c,,8,,1b,,1f,,,3,2,2,5,2,,,16,2,8,,6m,,2,,4,,fn4,,kh,g,g,g,a6,2,gt,,6a,,45,5,1ae,3,,2,5,4,14,3,4,,4l,2,fx,4,ar,2,49,b,4w,,1i,f,1k,3,1d,4,2,2,1x,3,10,5,,8,1q,,c,2,1g,9,a,4,2,,2n,3,2,,,2,6,,4g,,3,8,l,2,1l,2,,,,,m,,e,7,3,5,5f,8,2,3,,,n,,29,,2,6,,,2,,,2,,2,6j,,2,4,6,2,,2,r,2,2d,8,2,,,2,2y,,,,2,6,,,2t,3,2,4,,5,77,9,,2,6t,,a,2,,,4,,40,4,2,2,4,,w,a,14,6,2,4,8,,9,6,2,3,1a,d,,2,ba,7,,6,,,2a,m,2,7,,2,,2,3e,6,3,,,2,,7,,,20,2,3,,,,9n,2,f0b,5,1n,7,t4,,1r,4,29,,f5k,2,43q,,,3,4,5,8,8,2,7,u,4,44,3,1iz,1j,4,1e,8,,e,,m,5,,f,11s,7,,h,2,7,,2,,5,79,7,c5,4,15s,7,31,7,240,5,gx7k,2o,3k,6o".split(",").map((t=>t?parseInt(t,36):1));for(let t=1;tt)return c[e-1]<=t;return!1}function f(t){return t>=127462&&t<=127487}function d(t,e,i=!0,n=!0){return(i?p:g)(t,e,n)}function p(t,e,i){if(e==t.length)return e;e&&m(t.charCodeAt(e))&&v(t.charCodeAt(e-1))&&e--;let n=w(t,e);for(e+=b(n);e=0&&f(w(t,n));)i++,n-=2;if(i%2==0)break;e+=2}}}return e}function g(t,e,i){for(;e>0;){let n=p(t,e-2,i);if(n=56320&&t<57344}function v(t){return t>=55296&&t<56320}function w(t,e){let i=t.charCodeAt(e);if(!v(i)||e+1==t.length)return i;let n=t.charCodeAt(e+1);return m(n)?n-56320+(i-55296<<10)+65536:i}function y(t){return t<=65535?String.fromCharCode(t):(t-=65536,String.fromCharCode(55296+(t>>10),56320+(1023&t)))}function b(t){return t<65536?1:2}const x=/\r\n?|\n/;var k=function(t){return t[t.Simple=0]="Simple",t[t.TrackDel=1]="TrackDel",t[t.TrackBefore=2]="TrackBefore",t[t.TrackAfter=3]="TrackAfter",t}(k||(k={}));class S{constructor(t){this.sections=t}get length(){let t=0;for(let e=0;et)return r+(t-n);r+=o}else{if(i!=k.Simple&&h>=t&&(i==k.TrackDel&&nt||i==k.TrackBefore&&nt))return null;if(h>t||h==t&&e<0&&!o)return t==n||e<0?r:r+l;r+=l}n=h}if(t>n)throw new RangeError(`Position ${t} is out of range for changeset of length ${n}`);return r}touchesRange(t,e=t){for(let i=0,n=0;i=0&&n<=e&&r>=t)return!(ne)||"cover";n=r}return!1}toString(){let t="";for(let e=0;e=0?":"+n:"")}return t}toJSON(){return this.sections}static fromJSON(t){if(!Array.isArray(t)||t.length%2||t.some((t=>"number"!=typeof t)))throw new RangeError("Invalid JSON representation of ChangeDesc");return new S(t)}static create(t){return new S(t)}}class A extends S{constructor(t,e){super(t),this.inserted=e}apply(t){if(this.length!=t.length)throw new RangeError("Applying change set to a document with the wrong length");return D(this,((e,i,n,r,s)=>t=t.replace(n,n+(i-e),s)),!1),t}mapDesc(t,e=!1){return O(this,t,e,!0)}invert(t){let i=this.sections.slice(),n=[];for(let r=0,s=0;r=0){i[r]=l,i[r+1]=o;let h=r>>1;for(;n.length0&&C(i,e,r.text),r.forward(t),o+=t}let h=t[s++];for(;o>1].toJSON()))}return t}static of(t,i,n){let r=[],s=[],o=0,l=null;function h(t=!1){if(!t&&!r.length)return;ol||t<0||l>i)throw new RangeError(`Invalid change range ${t} to ${l} (in doc of length ${i})`);let u=c?"string"==typeof c?e.of(c.split(n||x)):c:e.empty,f=u.length;if(t==l&&0==f)return;to&&M(r,t-o,-1),M(r,l-t,f),C(s,r,u),o=l}}(t),h(!l),l}static empty(t){return new A(t?[t,-1]:[],[])}static fromJSON(t){if(!Array.isArray(t))throw new RangeError("Invalid JSON representation of ChangeSet");let i=[],n=[];for(let r=0;re&&"string"!=typeof t)))throw new RangeError("Invalid JSON representation of ChangeSet");if(1==s.length)i.push(s[0],0);else{for(;n.length=0&&i<=0&&i==t[r+1]?t[r]+=e:0==e&&0==t[r]?t[r+1]+=i:n?(t[r]+=e,t[r+1]+=i):t.push(e,i)}function C(t,i,n){if(0==n.length)return;let r=i.length-2>>1;if(r>1])),!(n||l==t.sections.length||t.sections[l+1]<0);)h=t.sections[l++],a=t.sections[l++];i(s,c,o,u,f),s=c,o=u}}}function O(t,e,i,n=!1){let r=[],s=n?[]:null,o=new E(t),l=new E(e);for(let t=-1;;)if(-1==o.ins&&-1==l.ins){let t=Math.min(o.len,l.len);M(r,t,-1),o.forward(t),l.forward(t)}else if(l.ins>=0&&(o.ins<0||t==o.i||0==o.off&&(l.len=0&&t=0)){if(o.done&&l.done)return s?A.createSet(r,s):S.create(r);throw new Error("Mismatched change set lengths")}{let e=0,i=o.len;for(;i;)if(-1==l.ins){let t=Math.min(i,l.len);e+=t,i-=t,l.forward(t)}else{if(!(0==l.ins&&l.lene||o.ins>=0&&o.len>e)&&(t||n.length>i),s.forward2(e),o.forward(e)}}else M(n,0,o.ins,t),r&&C(r,n,o.text),o.next()}}class E{constructor(t){this.set=t,this.i=0,this.next()}next(){let{sections:t}=this.set;this.i>1;return i>=t.length?e.empty:t[i]}textBit(t){let{inserted:i}=this.set,n=this.i-2>>1;return n>=i.length&&!t?e.empty:i[n].slice(this.off,null==t?void 0:this.off+t)}forward(t){t==this.len?this.next():(this.len-=t,this.off+=t)}forward2(t){-1==this.ins?this.forward(t):t==this.ins?this.next():(this.ins-=t,this.off+=t)}}class B{constructor(t,e,i){this.from=t,this.to=e,this.flags=i}get anchor(){return 16&this.flags?this.to:this.from}get head(){return 16&this.flags?this.from:this.to}get empty(){return this.from==this.to}get assoc(){return 4&this.flags?-1:8&this.flags?1:0}get bidiLevel(){let t=3&this.flags;return 3==t?null:t}get goalColumn(){let t=this.flags>>5;return 33554431==t?void 0:t}map(t,e=-1){let i,n;return this.empty?i=n=t.mapPos(this.from,e):(i=t.mapPos(this.from,1),n=t.mapPos(this.to,-1)),i==this.from&&n==this.to?this:new B(i,n,this.flags)}extend(t,e=t){if(t<=this.anchor&&e>=this.anchor)return R.range(t,e);let i=Math.abs(t-this.anchor)>Math.abs(e-this.anchor)?t:e;return R.range(this.anchor,i)}eq(t){return this.anchor==t.anchor&&this.head==t.head}toJSON(){return{anchor:this.anchor,head:this.head}}static fromJSON(t){if(!t||"number"!=typeof t.anchor||"number"!=typeof t.head)throw new RangeError("Invalid JSON representation for SelectionRange");return R.range(t.anchor,t.head)}static create(t,e,i){return new B(t,e,i)}}class R{constructor(t,e){this.ranges=t,this.mainIndex=e}map(t,e=-1){return t.empty?this:R.create(this.ranges.map((i=>i.map(t,e))),this.mainIndex)}eq(t){if(this.ranges.length!=t.ranges.length||this.mainIndex!=t.mainIndex)return!1;for(let e=0;et.toJSON())),main:this.mainIndex}}static fromJSON(t){if(!t||!Array.isArray(t.ranges)||"number"!=typeof t.main||t.main>=t.ranges.length)throw new RangeError("Invalid JSON representation for EditorSelection");return new R(t.ranges.map((t=>B.fromJSON(t))),t.main)}static single(t,e=t){return new R([R.range(t,e)],0)}static create(t,e=0){if(0==t.length)throw new RangeError("A selection needs at least one range");for(let i=0,n=0;nt?4:0))}static normalized(t,e=0){let i=t[e];t.sort(((t,e)=>t.from-e.from)),e=t.indexOf(i);for(let i=1;in.head?R.range(o,s):R.range(s,o))}}return new R(t,e)}}function L(t,e){for(let i of t.ranges)if(i.to>e)throw new RangeError("Selection points outside of document")}let N=0;class P{constructor(t,e,i,n,r){this.combine=t,this.compareInput=e,this.compare=i,this.isStatic=n,this.id=N++,this.default=t([]),this.extensions="function"==typeof r?r(this):r}static define(t={}){return new P(t.combine||(t=>t),t.compareInput||((t,e)=>t===e),t.compare||(t.combine?(t,e)=>t===e:I),!!t.static,t.enables)}of(t){return new V([],this,0,t)}compute(t,e){if(this.isStatic)throw new Error("Can't compute a static facet");return new V(t,this,1,e)}computeN(t,e){if(this.isStatic)throw new Error("Can't compute a static facet");return new V(t,this,2,e)}from(t,e){return e||(e=t=>t),this.compute([t],(i=>e(i.field(t))))}}function I(t,e){return t==e||t.length==e.length&&t.every(((t,i)=>t===e[i]))}class V{constructor(t,e,i,n){this.dependencies=t,this.facet=e,this.type=i,this.value=n,this.id=N++}dynamicSlot(t){var e;let i=this.value,n=this.facet.compareInput,r=this.id,s=t[r]>>1,o=2==this.type,l=!1,h=!1,a=[];for(let i of this.dependencies)"doc"==i?l=!0:"selection"==i?h=!0:0==(1&(null!==(e=t[i.id])&&void 0!==e?e:1))&&a.push(t[i.id]);return{create:t=>(t.values[s]=i(t),1),update(t,e){if(l&&e.docChanged||h&&(e.docChanged||e.selection)||H(t,a)){let e=i(t);if(o?!W(e,t.values[s],n):!n(e,t.values[s]))return t.values[s]=e,1}return 0},reconfigure:(t,e)=>{let l,h=e.config.address[r];if(null!=h){let r=tt(e,h);if(this.dependencies.every((i=>i instanceof P?e.facet(i)===t.facet(i):!(i instanceof q)||e.field(i,!1)==t.field(i,!1)))||(o?W(l=i(t),r,n):n(l=i(t),r)))return t.values[s]=r,0}else l=i(t);return t.values[s]=l,1}}}}function W(t,e,i){if(t.length!=e.length)return!1;for(let n=0;nt[e.id])),r=i.map((t=>t.type)),s=n.filter((t=>!(1&t))),o=t[e.id]>>1;function l(t){let i=[];for(let e=0;et===e),t);return t.provide&&(e.provides=t.provide(e)),e}create(t){let e=t.facet(z).find((t=>t.field==this));return((null==e?void 0:e.create)||this.createF)(t)}slot(t){let e=t[this.id]>>1;return{create:t=>(t.values[e]=this.create(t),1),update:(t,i)=>{let n=t.values[e],r=this.updateF(n,i);return this.compareF(n,r)?0:(t.values[e]=r,1)},reconfigure:(t,i)=>null!=i.config.address[this.id]?(t.values[e]=i.field(this),0):(t.values[e]=this.create(t),1)}}init(t){return[this,z.of({field:this,create:t})]}get extension(){return this}}const _=4,K=3,j=2,$=1;function G(t){return e=>new J(e,t)}const U={highest:G(0),high:G($),default:G(j),low:G(K),lowest:G(_)};class J{constructor(t,e){this.inner=t,this.prec=e}}class X{of(t){return new Y(this,t)}reconfigure(t){return X.reconfigure.of({compartment:this,extension:t})}get(t){return t.config.compartments.get(this)}}class Y{constructor(t,e){this.compartment=t,this.inner=e}}class Q{constructor(t,e,i,n,r,s){for(this.base=t,this.compartments=e,this.dynamicSlots=i,this.address=n,this.staticValues=r,this.facets=s,this.statusTemplate=[];this.statusTemplate.length>1]}static resolve(t,e,i){let n=[],r=Object.create(null),s=new Map;for(let i of function(t,e,i){let n=[[],[],[],[],[]],r=new Map;function s(t,o){let l=r.get(t);if(null!=l){if(l<=o)return;let e=n[l].indexOf(t);e>-1&&n[l].splice(e,1),t instanceof Y&&i.delete(t.compartment)}if(r.set(t,o),Array.isArray(t))for(let e of t)s(e,o);else if(t instanceof Y){if(i.has(t.compartment))throw new RangeError("Duplicate use of compartment in extensions");let n=e.get(t.compartment)||t.inner;i.set(t.compartment,n),s(n,o)}else if(t instanceof J)s(t.inner,t.prec);else if(t instanceof q)n[o].push(t),t.provides&&s(t.provides,o);else if(t instanceof V)n[o].push(t),t.facet.extensions&&s(t.facet.extensions,j);else{let e=t.extension;if(!e)throw new Error(`Unrecognized extension value in extension set (${t}). This sometimes happens because multiple instances of @codemirror/state are loaded, breaking instanceof checks.`);s(e,o)}}return s(t,j),n.reduce(((t,e)=>t.concat(e)))}(t,e,s))i instanceof q?n.push(i):(r[i.facet.id]||(r[i.facet.id]=[])).push(i);let o=Object.create(null),l=[],h=[];for(let t of n)o[t.id]=h.length<<1,h.push((e=>t.slot(e)));let a=null==i?void 0:i.config.facets;for(let t in r){let e=r[t],n=e[0].facet,s=a&&a[t]||[];if(e.every((t=>0==t.type)))if(o[n.id]=l.length<<1|1,I(s,e))l.push(i.facet(n));else{let t=n.combine(e.map((t=>t.value)));l.push(i&&n.compare(t,i.facet(n))?i.facet(n):t)}else{for(let t of e)0==t.type?(o[t.id]=l.length<<1|1,l.push(t.value)):(o[t.id]=h.length<<1,h.push((e=>t.dynamicSlot(e))));o[n.id]=h.length<<1,h.push((t=>F(t,n,e)))}}let c=h.map((t=>t(o)));return new Q(t,s,c,o,l,r)}}function Z(t,e){if(1&e)return 2;let i=e>>1,n=t.status[i];if(4==n)throw new Error("Cyclic dependency between fields and/or facets");if(2&n)return n;t.status[i]=4;let r=t.computeSlot(t,t.config.dynamicSlots[i]);return t.status[i]=2|r}function tt(t,e){return 1&e?t.config.staticValues[e>>1]:t.values[e>>1]}const et=P.define(),it=P.define({combine:t=>t.some((t=>t)),static:!0}),nt=P.define({combine:t=>t.length?t[0]:void 0,static:!0}),rt=P.define(),st=P.define(),ot=P.define(),lt=P.define({combine:t=>!!t.length&&t[0]});class ht{constructor(t,e){this.type=t,this.value=e}static define(){return new at}}class at{of(t){return new ht(this,t)}}class ct{constructor(t){this.map=t}of(t){return new ut(this,t)}}class ut{constructor(t,e){this.type=t,this.value=e}map(t){let e=this.type.map(this.value,t);return void 0===e?void 0:e==this.value?this:new ut(this.type,e)}is(t){return this.type==t}static define(t={}){return new ct(t.map||(t=>t))}static mapEffects(t,e){if(!t.length)return t;let i=[];for(let n of t){let t=n.map(e);t&&i.push(t)}return i}}ut.reconfigure=ut.define(),ut.appendConfig=ut.define();class ft{constructor(t,e,i,n,r,s){this.startState=t,this.changes=e,this.selection=i,this.effects=n,this.annotations=r,this.scrollIntoView=s,this._doc=null,this._state=null,i&&L(i,e.newLength),r.some((t=>t.type==ft.time))||(this.annotations=r.concat(ft.time.of(Date.now())))}static create(t,e,i,n,r,s){return new ft(t,e,i,n,r,s)}get newDoc(){return this._doc||(this._doc=this.changes.apply(this.startState.doc))}get newSelection(){return this.selection||this.startState.selection.map(this.changes)}get state(){return this._state||this.startState.applyTransaction(this),this._state}annotation(t){for(let e of this.annotations)if(e.type==t)return e.value}get docChanged(){return!this.changes.empty}get reconfigured(){return this.startState.config!=this.state.config}isUserEvent(t){let e=this.annotation(ft.userEvent);return!(!e||!(e==t||e.length>t.length&&e.slice(0,t.length)==t&&"."==e[t.length]))}}function dt(t,e){let i=[];for(let n=0,r=0;;){let s,o;if(n=t[n]))s=t[n++],o=t[n++];else{if(!(r=0;r--){let s=i[r](t);s&&Object.keys(s).length&&(n=pt(n,gt(e,s,t.changes.newLength),!0))}return n==t?t:ft.create(e,t.changes,t.selection,n.effects,n.annotations,n.scrollIntoView)}(i?function(t){let e=t.startState,i=!0;for(let n of e.facet(rt)){let e=n(t);if(!1===e){i=!1;break}Array.isArray(e)&&(i=!0===i?e:dt(i,e))}if(!0!==i){let n,r;if(!1===i)r=t.changes.invertedDesc,n=A.empty(e.doc.length);else{let e=t.changes.filter(i);n=e.changes,r=e.filtered.mapDesc(e.changes).invertedDesc}t=ft.create(e,n,t.selection&&t.selection.map(r),ut.mapEffects(t.effects,r),t.annotations,t.scrollIntoView)}let n=e.facet(st);for(let i=n.length-1;i>=0;i--){let r=n[i](t);t=r instanceof ft?r:Array.isArray(r)&&1==r.length&&r[0]instanceof ft?r[0]:mt(e,wt(r),!1)}return t}(r):r)}ft.time=ht.define(),ft.userEvent=ht.define(),ft.addToHistory=ht.define(),ft.remote=ht.define();const vt=[];function wt(t){return null==t?vt:Array.isArray(t)?t:[t]}var yt=function(t){return t[t.Word=0]="Word",t[t.Space=1]="Space",t[t.Other=2]="Other",t}(yt||(yt={}));const bt=/[\u00df\u0587\u0590-\u05f4\u0600-\u06ff\u3040-\u309f\u30a0-\u30ff\u3400-\u4db5\u4e00-\u9fcc\uac00-\ud7af]/;let xt;try{xt=new RegExp("[\\p{Alphabetic}\\p{Number}_]","u")}catch(t){}function kt(t){return e=>{if(!/\S/.test(e))return yt.Space;if(function(t){if(xt)return xt.test(t);for(let e=0;e"€"&&(i.toUpperCase()!=i.toLowerCase()||bt.test(i)))return!0}return!1}(e))return yt.Word;for(let i=0;i-1)return yt.Word;return yt.Other}}class St{constructor(t,e,i,n,r,s){this.config=t,this.doc=e,this.selection=i,this.values=n,this.status=t.statusTemplate.slice(),this.computeSlot=r,s&&(s._state=this);for(let t=0;tr.set(e,t))),i=null),r.set(e.value.compartment,e.value.extension)):e.is(ut.reconfigure)?(i=null,n=e.value):e.is(ut.appendConfig)&&(i=null,n=wt(n).concat(e.value));if(i)e=t.startState.values.slice();else{i=Q.resolve(n,r,this),e=new St(i,this.doc,this.selection,i.dynamicSlots.map((()=>null)),((t,e)=>e.reconfigure(t,this)),null).values}new St(i,t.newDoc,t.newSelection,e,((e,i)=>i.update(e,t)),t)}replaceSelection(t){return"string"==typeof t&&(t=this.toText(t)),this.changeByRange((e=>({changes:{from:e.from,to:e.to,insert:t},range:R.cursor(e.from+t.length)})))}changeByRange(t){let e=this.selection,i=t(e.ranges[0]),n=this.changes(i.changes),r=[i.range],s=wt(i.effects);for(let i=1;ir.spec.fromJSON(s,t))))}return St.create({doc:t.doc,selection:R.fromJSON(t.selection),extensions:e.extensions?n.concat([e.extensions]):n})}static create(t={}){let i=Q.resolve(t.extensions||[],new Map),n=t.doc instanceof e?t.doc:e.of((t.doc||"").split(i.staticFacet(St.lineSeparator)||x)),r=t.selection?t.selection instanceof R?t.selection:R.single(t.selection.anchor,t.selection.head):R.single(0);return L(r,n.length),i.staticFacet(it)||(r=r.asSingle()),new St(i,n,r,i.dynamicSlots.map((()=>null)),((t,e)=>e.create(t)),null)}get tabSize(){return this.facet(St.tabSize)}get lineBreak(){return this.facet(St.lineSeparator)||"\n"}get readOnly(){return this.facet(lt)}phrase(t,...e){for(let e of this.facet(St.phrases))if(Object.prototype.hasOwnProperty.call(e,t)){t=e[t];break}return e.length&&(t=t.replace(/\$(\$|\d*)/g,((t,i)=>{if("$"==i)return"$";let n=+(i||1);return!n||n>e.length?t:e[n-1]}))),t}languageDataAt(t,e,i=-1){let n=[];for(let r of this.facet(et))for(let s of r(this,e,i))Object.prototype.hasOwnProperty.call(s,t)&&n.push(s[t]);return n}charCategorizer(t){return kt(this.languageDataAt("wordChars",t).join(""))}wordAt(t){let{text:e,from:i,length:n}=this.doc.lineAt(t),r=this.charCategorizer(t),s=t-i,o=t-i;for(;s>0;){let t=d(e,s,!1);if(r(e.slice(t,s))!=yt.Word)break;s=t}for(;ot.length?t[0]:4}),St.lineSeparator=nt,St.readOnly=lt,St.phrases=P.define({compare(t,e){let i=Object.keys(t),n=Object.keys(e);return i.length==n.length&&i.every((i=>t[i]==e[i]))}}),St.languageData=et,St.changeFilter=rt,St.transactionFilter=st,St.transactionExtender=ot,X.reconfigure=ut.define();class Mt{eq(t){return this==t}range(t,e=t){return Ct.create(t,e,this)}}Mt.prototype.startSide=Mt.prototype.endSide=0,Mt.prototype.point=!1,Mt.prototype.mapMode=k.TrackDel;let Ct=class t{constructor(t,e,i){this.from=t,this.to=e,this.value=i}static create(e,i,n){return new t(e,i,n)}};function Dt(t,e){return t.from-e.from||t.value.startSide-e.value.startSide}class Ot{constructor(t,e,i,n){this.from=t,this.to=e,this.value=i,this.maxPoint=n}get length(){return this.to[this.to.length-1]}findIndex(t,e,i,n=0){let r=i?this.to:this.from;for(let s=n,o=r.length;;){if(s==o)return s;let n=s+o>>1,l=r[n]-t||(i?this.value[n].endSide:this.value[n].startSide)-e;if(n==s)return l>=0?s:o;l>=0?o=n:s=n+1}}between(t,e,i,n){for(let r=this.findIndex(e,-1e9,!0),s=this.findIndex(i,1e9,!1,r);ra||h==a&&c.startSide>0&&c.endSide<=0)continue;(a-h||c.endSide-c.startSide)<0||(s<0&&(s=h),c.point&&(o=Math.max(o,a-h)),i.push(c),n.push(h-s),r.push(a-s))}return{mapped:i.length?new Ot(n,r,i,o):null,pos:s}}}class Tt{constructor(t,e,i,n){this.chunkPos=t,this.chunk=e,this.nextLayer=i,this.maxPoint=n}static create(t,e,i,n){return new Tt(t,e,i,n)}get length(){let t=this.chunk.length-1;return t<0?0:Math.max(this.chunkEnd(t),this.nextLayer.length)}get size(){if(this.isEmpty)return 0;let t=this.nextLayer.size;for(let e of this.chunk)t+=e.value.length;return t}chunkEnd(t){return this.chunkPos[t]+this.chunk[t].length}update(t){let{add:e=[],sort:i=!1,filterFrom:n=0,filterTo:r=this.length}=t,s=t.filter;if(0==e.length&&!s)return this;if(i&&(e=e.slice().sort(Dt)),this.isEmpty)return e.length?Tt.of(e):this;let o=new Rt(this,null,-1).goto(0),l=0,h=[],a=new Et;for(;o.value||l=0){let t=e[l++];a.addInner(t.from,t.to,t.value)||h.push(t)}else 1==o.rangeIndex&&o.chunkIndexthis.chunkEnd(o.chunkIndex)||ro.to||r=r&&t<=r+s.length&&!1===s.between(r,t-r,e-r,i))return}this.nextLayer.between(t,e,i)}}iter(t=0){return Lt.from([this]).goto(t)}get isEmpty(){return this.nextLayer==this}static iter(t,e=0){return Lt.from(t).goto(e)}static compare(t,e,i,n,r=-1){let s=t.filter((t=>t.maxPoint>0||!t.isEmpty&&t.maxPoint>=r)),o=e.filter((t=>t.maxPoint>0||!t.isEmpty&&t.maxPoint>=r)),l=Bt(s,o,i),h=new Pt(s,l,r),a=new Pt(o,l,r);i.iterGaps(((t,e,i)=>It(h,t,a,e,i,n))),i.empty&&0==i.length&&It(h,0,a,0,0,n)}static eq(t,e,i=0,n){null==n&&(n=999999999);let r=t.filter((t=>!t.isEmpty&&e.indexOf(t)<0)),s=e.filter((e=>!e.isEmpty&&t.indexOf(e)<0));if(r.length!=s.length)return!1;if(!r.length)return!0;let o=Bt(r,s),l=new Pt(r,o,0).goto(i),h=new Pt(s,o,0).goto(i);for(;;){if(l.to!=h.to||!Vt(l.active,h.active)||l.point&&(!h.point||!l.point.eq(h.point)))return!1;if(l.to>n)return!0;l.next(),h.next()}}static spans(t,e,i,n,r=-1){let s=new Pt(t,null,r).goto(e),o=e,l=s.openStart;for(;;){let t=Math.min(s.to,i);if(s.point){let i=s.activeForPoint(s.to),r=s.pointFromo&&(n.span(o,t,s.active,l),l=s.openEnd(t));if(s.to>i)return l+(s.point&&s.to>i?1:0);o=s.to,s.next()}}static of(t,e=!1){let i=new Et;for(let n of t instanceof Ct?[t]:e?function(t){if(t.length>1)for(let e=t[0],i=1;i0)return t.slice().sort(Dt);e=n}return t}(t):t)i.add(n.from,n.to,n.value);return i.finish()}}Tt.empty=new Tt([],[],null,-1),Tt.empty.nextLayer=Tt.empty;class Et{constructor(){this.chunks=[],this.chunkPos=[],this.chunkStart=-1,this.last=null,this.lastFrom=-1e9,this.lastTo=-1e9,this.from=[],this.to=[],this.value=[],this.maxPoint=-1,this.setMaxPoint=-1,this.nextLayer=null}finishChunk(t){this.chunks.push(new Ot(this.from,this.to,this.value,this.maxPoint)),this.chunkPos.push(this.chunkStart),this.chunkStart=-1,this.setMaxPoint=Math.max(this.setMaxPoint,this.maxPoint),this.maxPoint=-1,t&&(this.from=[],this.to=[],this.value=[])}add(t,e,i){this.addInner(t,e,i)||(this.nextLayer||(this.nextLayer=new Et)).add(t,e,i)}addInner(t,e,i){let n=t-this.lastTo||i.startSide-this.last.endSide;if(n<=0&&(t-this.lastFrom||i.startSide-this.last.startSide)<0)throw new Error("Ranges must be added sorted by `from` position and `startSide`");return!(n<0)&&(250==this.from.length&&this.finishChunk(!0),this.chunkStart<0&&(this.chunkStart=t),this.from.push(t-this.chunkStart),this.to.push(e-this.chunkStart),this.last=i,this.lastFrom=t,this.lastTo=e,this.value.push(i),i.point&&(this.maxPoint=Math.max(this.maxPoint,e-t)),!0)}addChunk(t,e){if((t-this.lastTo||e.value[0].startSide-this.last.endSide)<0)return!1;this.from.length&&this.finishChunk(!0),this.setMaxPoint=Math.max(this.setMaxPoint,e.maxPoint),this.chunks.push(e),this.chunkPos.push(t);let i=e.value.length-1;return this.last=e.value[i],this.lastFrom=e.from[i]+t,this.lastTo=e.to[i]+t,!0}finish(){return this.finishInner(Tt.empty)}finishInner(t){if(this.from.length&&this.finishChunk(!1),0==this.chunks.length)return t;let e=Tt.create(this.chunkPos,this.chunks,this.nextLayer?this.nextLayer.finishInner(t):t,this.setMaxPoint);return this.from=null,e}}function Bt(t,e,i){let n=new Map;for(let e of t)for(let t=0;t=this.minPoint)break}}}setRangeIndex(t){if(t==this.layer.chunk[this.chunkIndex].value.length){if(this.chunkIndex++,this.skip)for(;this.chunkIndex=i&&n.push(new Rt(s,e,i,r));return 1==n.length?n[0]:new Lt(n)}get startSide(){return this.value?this.value.startSide:0}goto(t,e=-1e9){for(let i of this.heap)i.goto(t,e);for(let t=this.heap.length>>1;t>=0;t--)Nt(this.heap,t);return this.next(),this}forward(t,e){for(let i of this.heap)i.forward(t,e);for(let t=this.heap.length>>1;t>=0;t--)Nt(this.heap,t);(this.to-t||this.value.endSide-e)<0&&this.next()}next(){if(0==this.heap.length)this.from=this.to=1e9,this.value=null,this.rank=-1;else{let t=this.heap[0];this.from=t.from,this.to=t.to,this.value=t.value,this.rank=t.rank,t.value&&t.next(),Nt(this.heap,0)}}}function Nt(t,e){for(let i=t[e];;){let n=1+(e<<1);if(n>=t.length)break;let r=t[n];if(n+1=0&&(r=t[n+1],n++),i.compare(r)<0)break;t[n]=i,t[e]=r,e=n}}class Pt{constructor(t,e,i){this.minPoint=i,this.active=[],this.activeTo=[],this.activeRank=[],this.minActive=-1,this.point=null,this.pointFrom=0,this.pointRank=0,this.to=-1e9,this.endSide=0,this.openStart=-1,this.cursor=Lt.from(t,e,i)}goto(t,e=-1e9){return this.cursor.goto(t,e),this.active.length=this.activeTo.length=this.activeRank.length=0,this.minActive=-1,this.to=t,this.endSide=e,this.openStart=-1,this.next(),this}forward(t,e){for(;this.minActive>-1&&(this.activeTo[this.minActive]-t||this.active[this.minActive].endSide-e)<0;)this.removeActive(this.minActive);this.cursor.forward(t,e)}removeActive(t){Wt(this.active,t),Wt(this.activeTo,t),Wt(this.activeRank,t),this.minActive=Ft(this.active,this.activeTo)}addActive(t){let e=0,{value:i,to:n,rank:r}=this.cursor;for(;e-1&&(this.activeTo[n]-this.cursor.from||this.active[n].endSide-this.cursor.startSide)<0){if(this.activeTo[n]>t){this.to=this.activeTo[n],this.endSide=this.active[n].endSide;break}this.removeActive(n),i&&Wt(i,n)}else{if(!this.cursor.value){this.to=this.endSide=1e9;break}if(this.cursor.from>t){this.to=this.cursor.from,this.endSide=this.cursor.startSide;break}{let t=this.cursor.value;if(t.point){if(!(e&&this.cursor.to==this.to&&this.cursor.from=0&&i[e]=0&&!(this.activeRank[i]t||this.activeTo[i]==t&&this.active[i].endSide>=this.point.endSide)&&e.push(this.active[i]);return e.reverse()}openEnd(t){let e=0;for(let i=this.activeTo.length-1;i>=0&&this.activeTo[i]>t;i--)e++;return e}}function It(t,e,i,n,r,s){t.goto(e),i.goto(n);let o=n+r,l=n,h=n-e;for(;;){let e=t.to+h-i.to||t.endSide-i.endSide,n=e<0?t.to+h:i.to,r=Math.min(n,o);if(t.point||i.point?t.point&&i.point&&(t.point==i.point||t.point.eq(i.point))&&Vt(t.activeForPoint(t.to+h),i.activeForPoint(i.to))||s.comparePoint(l,r,t.point,i.point):r>l&&!Vt(t.active,i.active)&&s.compareRange(l,r,t.active,i.active),n>o)break;l=n,e<=0&&t.next(),e>=0&&i.next()}}function Vt(t,e){if(t.length!=e.length)return!1;for(let i=0;i=e;i--)t[i+1]=t[i];t[e]=i}function Ft(t,e){let i=-1,n=1e9;for(let r=0;r=e)return n;if(n==t.length)break;r+=9==t.charCodeAt(n)?i-r%i:1,n=d(t,n)}return!0===n?-1:t.length}const _t="undefined"==typeof Symbol?"__ͼ":Symbol.for("ͼ"),Kt="undefined"==typeof Symbol?"__styleSet"+Math.floor(1e8*Math.random()):Symbol("styleSet"),jt="undefined"!=typeof globalThis?globalThis:"undefined"!=typeof window?window:{};class $t{constructor(t,e){this.rules=[];let{finish:i}=e||{};function n(t){return/^@/.test(t)?[t]:t.split(/,\s*/)}function r(t,e,s,o){let l=[],h=/^@(\w+)\b/.exec(t[0]),a=h&&"keyframes"==h[1];if(h&&null==e)return s.push(t[0]+";");for(let i in e){let o=e[i];if(/&/.test(i))r(i.split(/,\s*/).map((e=>t.map((t=>e.replace(/&/,t))))).reduce(((t,e)=>t.concat(e))),o,s);else if(o&&"object"==typeof o){if(!h)throw new RangeError("The value of a property ("+i+") should be a primitive value.");r(n(i),o,l,a)}else null!=o&&l.push(i.replace(/_.*/,"").replace(/[A-Z]/g,(t=>"-"+t.toLowerCase()))+": "+o+";")}(l.length||a)&&s.push((!i||h||o?t:t.map(i)).join(", ")+" {"+l.join(" ")+"}")}for(let e in t)r(n(e),t[e],this.rules)}getRules(){return this.rules.join("\n")}static newName(){let t=jt[_t]||1;return jt[_t]=t+1,"ͼ"+t.toString(36)}static mount(t,e){(t[Kt]||new Ut(t)).mount(Array.isArray(e)?e:[e])}}let Gt=null;class Ut{constructor(t){if(!t.head&&t.adoptedStyleSheets&&"undefined"!=typeof CSSStyleSheet){if(Gt)return t.adoptedStyleSheets=[Gt.sheet].concat(t.adoptedStyleSheets),t[Kt]=Gt;this.sheet=new CSSStyleSheet,t.adoptedStyleSheets=[this.sheet].concat(t.adoptedStyleSheets),Gt=this}else{this.styleTag=(t.ownerDocument||t).createElement("style");let e=t.head||t;e.insertBefore(this.styleTag,e.firstChild)}this.modules=[],t[Kt]=this}mount(t){let e=this.sheet,i=0,n=0;for(let r=0;r-1&&(this.modules.splice(o,1),n--,o=-1),-1==o){if(this.modules.splice(n++,0,s),e)for(let t=0;t",191:"?",192:"~",219:"{",220:"|",221:"}",222:'"'},Yt="undefined"!=typeof navigator&&/Chrome\/(\d+)/.exec(navigator.userAgent),Qt="undefined"!=typeof navigator&&/Mac/.test(navigator.platform),Zt="undefined"!=typeof navigator&&/MSIE \d|Trident\/(?:[7-9]|\d{2,})\..*rv:(\d+)/.exec(navigator.userAgent),te=Qt||Yt&&+Yt[1]<57,ee=0;ee<10;ee++)Jt[48+ee]=Jt[96+ee]=String(ee);for(ee=1;ee<=24;ee++)Jt[ee+111]="F"+ee;for(ee=65;ee<=90;ee++)Jt[ee]=String.fromCharCode(ee+32),Xt[ee]=String.fromCharCode(ee);for(var ie in Jt)Xt.hasOwnProperty(ie)||(Xt[ie]=Jt[ie]);function ne(t){let e;return e=11==t.nodeType?t.getSelection?t:t.ownerDocument:t,e.getSelection()}function re(t,e){return!!e&&(t==e||t.contains(1!=e.nodeType?e.parentNode:e))}function se(t,e){if(!e.anchorNode)return!1;try{return re(t,e.anchorNode)}catch(t){return!1}}function oe(t){return 3==t.nodeType?we(t,0,t.nodeValue.length).getClientRects():1==t.nodeType?t.getClientRects():[]}function le(t,e,i,n){return!!i&&(ae(t,e,i,n,-1)||ae(t,e,i,n,1))}function he(t){for(var e=0;;e++)if(!(t=t.previousSibling))return e}function ae(t,e,i,n,r){for(;;){if(t==i&&e==n)return!0;if(e==(r<0?0:ce(t))){if("DIV"==t.nodeName)return!1;let i=t.parentNode;if(!i||1!=i.nodeType)return!1;e=he(t)+(r<0?0:1),t=i}else{if(1!=t.nodeType)return!1;if(1==(t=t.childNodes[e+(r<0?-1:0)]).nodeType&&"false"==t.contentEditable)return!1;e=r<0?ce(t):0}}}function ce(t){return 3==t.nodeType?t.nodeValue.length:t.childNodes.length}const ue={left:0,right:0,top:0,bottom:0};function fe(t,e){let i=e?t.left:t.right;return{left:i,right:i,top:t.top,bottom:t.bottom}}function de(t){return{left:0,right:t.innerWidth,top:0,bottom:t.innerHeight}}class pe{constructor(){this.anchorNode=null,this.anchorOffset=0,this.focusNode=null,this.focusOffset=0}eq(t){return this.anchorNode==t.anchorNode&&this.anchorOffset==t.anchorOffset&&this.focusNode==t.focusNode&&this.focusOffset==t.focusOffset}setRange(t){this.set(t.anchorNode,t.anchorOffset,t.focusNode,t.focusOffset)}set(t,e,i,n){this.anchorNode=t,this.anchorOffset=e,this.focusNode=i,this.focusOffset=n}}let ge,me=null;function ve(t){if(t.setActive)return t.setActive();if(me)return t.focus(me);let e=[];for(let i=t;i&&(e.push(i,i.scrollTop,i.scrollLeft),i!=i.ownerDocument);i=i.parentNode);if(t.focus(null==me?{get preventScroll(){return me={preventScroll:!0},!0}}:void 0),!me){me=!1;for(let t=0;te)return i.domBoundsAround(t,e,h);if(c>=t&&-1==n&&(n=l,r=h),h>e&&i.dom.parentNode==this.dom){s=l,o=a;break}a=c,h=c+i.breakAfter}return{from:r,to:o<0?i+this.length:o,startDOM:(n?this.children[n-1].dom.nextSibling:null)||this.dom.firstChild,endDOM:s=0?this.children[s].dom:null}}markDirty(t=!1){this.dirty|=2,this.markParentsDirty(t)}markParentsDirty(t){for(let e=this.parent;e;e=e.parent){if(t&&(e.dirty|=2),1&e.dirty)return;e.dirty|=1,t=!1}}setParent(t){this.parent!=t&&(this.parent=t,this.dirty&&this.markParentsDirty(!0))}setDOM(t){this.dom&&(this.dom.cmView=null),this.dom=t,t.cmView=this}get rootView(){for(let t=this;;){let e=t.parent;if(!e)return t;t=e}}replaceChildren(t,e,i=ke){this.markDirty();for(let i=t;ithis.pos||t==this.pos&&(e>0||0==this.i||this.children[this.i-1].breakAfter))return this.off=t-this.pos,this;let i=this.children[--this.i];this.pos-=i.length+i.breakAfter}}}function Ce(t,e,i,n,r,s,o,l,h){let{children:a}=t,c=a.length?a[e]:null,u=s.length?s[s.length-1]:null,f=u?u.breakAfter:o;if(!(e==n&&c&&!o&&!f&&s.length<2&&c.merge(i,r,s.length?u:null,0==i,l,h))){if(n0&&(!o&&s.length&&c.merge(i,c.length,s[0],!1,l,0)?c.breakAfter=s.shift().breakAfter:(i2);var He={mac:We||/Mac/.test(Oe.platform),windows:/Win/.test(Oe.platform),linux:/Linux|X11/.test(Oe.platform),ie:Le,ie_version:Be?Te.documentMode||6:Re?+Re[1]:Ee?+Ee[1]:0,gecko:Ne,gecko_version:Ne?+(/Firefox\/(\d+)/.exec(Oe.userAgent)||[0,0])[1]:0,chrome:!!Pe,chrome_version:Pe?+Pe[1]:0,ios:We,android:/Android\b/.test(Oe.userAgent),webkit:Ie,safari:Ve,webkit_version:Ie?+(/\bAppleWebKit\/(\d+)/.exec(navigator.userAgent)||[0,0])[1]:0,tabSize:null!=Te.documentElement.style.tabSize?"tab-size":"-moz-tab-size"};class Fe extends Se{constructor(t){super(),this.text=t}get length(){return this.text.length}createDOM(t){this.setDOM(t||document.createTextNode(this.text))}sync(t){this.dom||this.createDOM(),this.dom.nodeValue!=this.text&&(t&&t.node==this.dom&&(t.written=!0),this.dom.nodeValue=this.text)}reuseDOM(t){3==t.nodeType&&this.createDOM(t)}merge(t,e,i){return(!i||i instanceof Fe&&!(this.length-(e-t)+i.length>256))&&(this.text=this.text.slice(0,t)+(i?i.text:"")+this.text.slice(e),this.markDirty(),!0)}split(t){let e=new Fe(this.text.slice(t));return this.text=this.text.slice(0,t),this.markDirty(),e}localPosFromDOM(t,e){return t==this.dom?e:e?this.text.length:0}domAtPos(t){return new xe(this.dom,t)}domBoundsAround(t,e,i){return{from:i,to:i+this.length,startDOM:this.dom,endDOM:this.dom.nextSibling}}coordsAt(t,e){return qe(this.dom,t,e)}}class ze extends Se{constructor(t,e=[],i=0){super(),this.mark=t,this.children=e,this.length=i;for(let t of e)t.setParent(this)}setAttrs(t){if(be(t),this.mark.class&&(t.className=this.mark.class),this.mark.attrs)for(let e in this.mark.attrs)t.setAttribute(e,this.mark.attrs[e]);return t}reuseDOM(t){t.nodeName==this.mark.tagName.toUpperCase()&&(this.setDOM(t),this.dirty|=6)}sync(t){this.dom?4&this.dirty&&this.setAttrs(this.dom):this.setDOM(this.setAttrs(document.createElement(this.mark.tagName))),super.sync(t)}merge(t,e,i,n,r,s){return(!i||!(!(i instanceof ze&&i.mark.eq(this.mark))||t&&r<=0||et&&e.push(i=t&&(n=r),i=o,r++}let s=this.length-t;return this.length=t,n>-1&&(this.children.length=n,this.markDirty()),new ze(this.mark,e,s)}domAtPos(t){return Ue(this,t)}coordsAt(t,e){return Xe(this,t,e)}}function qe(t,e,i){let n=t.nodeValue.length;e>n&&(e=n);let r=e,s=e,o=0;0==e&&i<0||e==n&&i>=0?He.chrome||He.gecko||(e?(r--,o=1):s=0)?0:l.length-1];return He.safari&&!o&&0==h.width&&(h=Array.prototype.find.call(l,(t=>t.width))||h),o?fe(h,o<0):h||null}class _e extends Se{constructor(t,e,i){super(),this.widget=t,this.length=e,this.side=i,this.prevWidget=null}static create(t,e,i){return new(t.customView||_e)(t,e,i)}split(t){let e=_e.create(this.widget,this.length-t,this.side);return this.length-=t,e}sync(){this.dom&&this.widget.updateDOM(this.dom)||(this.dom&&this.prevWidget&&this.prevWidget.destroy(this.dom),this.prevWidget=null,this.setDOM(this.widget.toDOM(this.editorView)),this.dom.contentEditable="false")}getSide(){return this.side}merge(t,e,i,n,r,s){return!(i&&(!(i instanceof _e&&this.widget.compare(i.widget))||t>0&&r<=0||e0?i.length-1:0;n=i[e],!(t>0?0==e:e==i.length-1||n.top0?-1:1);return this.length?n:fe(n,this.side>0)}get isEditable(){return!1}destroy(){super.destroy(),this.dom&&this.widget.destroy(this.dom)}}class Ke extends _e{domAtPos(t){let{topView:e,text:i}=this.widget;return e?je(t,0,e,i,((t,e)=>t.domAtPos(e)),(t=>new xe(i,Math.min(t,i.nodeValue.length)))):new xe(i,Math.min(t,i.nodeValue.length))}sync(){this.setDOM(this.widget.toDOM())}localPosFromDOM(t,e){let{topView:i,text:n}=this.widget;return i?$e(t,e,i,n):Math.min(e,this.length)}ignoreMutation(){return!1}get overrideDOMText(){return null}coordsAt(t,e){let{topView:i,text:n}=this.widget;return i?je(t,e,i,n,((t,e,i)=>t.coordsAt(e,i)),((t,e)=>qe(n,t,e))):qe(n,t,e)}destroy(){var t;super.destroy(),null===(t=this.widget.topView)||void 0===t||t.destroy()}get isEditable(){return!0}canReuseDOM(){return!0}}function je(t,e,i,n,r,s){if(i instanceof ze){for(let o=i.dom.firstChild;o;o=o.nextSibling){let i=Se.get(o);if(!i)return s(t,e);let l=re(o,n),h=i.length+(l?n.nodeValue.length:0);if(t=0;)if(e<0?n>0:n0?-1:1);return i&&i.tope.top?{left:e.left,right:e.right,top:i.top,bottom:i.bottom}:e}get overrideDOMText(){return e.empty}}function Ue(t,e){let i=t.dom,{children:n}=t,r=0;for(let t=0;rt&&e0;t--){let e=n[t-1];if(e.dom.parentNode==i)return e.domAtPos(e.length)}for(let t=r;t0&&e instanceof ze&&r.length&&(n=r[r.length-1])instanceof ze&&n.mark.eq(e.mark)?Je(n,e.children[0],i-1):(r.push(e),e.setParent(t)),t.length+=e.length}function Xe(t,e,i){let n=null,r=-1,s=null,o=-1;!function t(e,i){for(let l=0,h=0;l=i&&(a.children.length?t(a,i-h):!s&&(c>i||h==c&&a.getSide()>0)?(s=a,o=i-h):(h0?3e8:-4e8:e>0?1e8:-1e8,new si(t,e,e,i,t.widget||null,!1)}static replace(t){let e,i,n=!!t.block;if(t.isBlockGap)e=-5e8,i=4e8;else{let{start:r,end:s}=oi(t,n);e=(r?n?-3e8:-1:5e8)-1,i=1+(s?n?2e8:1:-6e8)}return new si(t,e,i,n,t.widget||null,!0)}static line(t){return new ri(t)}static set(t,e=!1){return Tt.of(t,e)}hasHeight(){return!!this.widget&&this.widget.estimatedHeight>-1}}ii.none=Tt.empty;class ni extends ii{constructor(t){let{start:e,end:i}=oi(t);super(e?-1:5e8,i?1:-6e8,null,t),this.tagName=t.tagName||"span",this.class=t.class||"",this.attrs=t.attributes||null}eq(t){return this==t||t instanceof ni&&this.tagName==t.tagName&&this.class==t.class&&Qe(this.attrs,t.attrs)}range(t,e=t){if(t>=e)throw new RangeError("Mark decorations may not be empty");return super.range(t,e)}}ni.prototype.point=!1;class ri extends ii{constructor(t){super(-2e8,-2e8,null,t)}eq(t){return t instanceof ri&&Qe(this.spec.attributes,t.spec.attributes)}range(t,e=t){if(e!=t)throw new RangeError("Line decoration ranges must be zero-length");return super.range(t,e)}}ri.prototype.mapMode=k.TrackBefore,ri.prototype.point=!0;class si extends ii{constructor(t,e,i,n,r,s){super(e,i,r,t),this.block=n,this.isReplace=s,this.mapMode=n?e<=0?k.TrackBefore:k.TrackAfter:k.TrackDel}get type(){return this.startSide=5}eq(t){return t instanceof si&&(e=this.widget,i=t.widget,e==i||!!(e&&i&&e.compare(i)))&&this.block==t.block&&this.startSide==t.startSide&&this.endSide==t.endSide;var e,i}range(t,e=t){if(this.isReplace&&(t>e||t==e&&this.startSide>0&&this.endSide<=0))throw new RangeError("Invalid range for replacement decoration");if(!this.isReplace&&e!=t)throw new RangeError("Widget decorations can only have zero-length ranges");return super.range(t,e)}}function oi(t,e=!1){let{inclusiveStart:i,inclusiveEnd:n}=t;return null==i&&(i=t.inclusive),null==n&&(n=t.inclusive),{start:null!=i?i:e,end:null!=n?n:e}}function li(t,e,i,n=0){let r=i.length-1;r>=0&&i[r]+n>=t?i[r]=Math.max(i[r],e):i.push(t,e)}si.prototype.point=!0;class hi extends Se{constructor(){super(...arguments),this.children=[],this.length=0,this.prevAttrs=void 0,this.attrs=null,this.breakAfter=0}merge(t,e,i,n,r,s){if(i){if(!(i instanceof hi))return!1;this.dom||i.transferDOM(this)}return n&&this.setDeco(i?i.attrs:null),De(this,t,e,i?i.children:[],r,s),!0}split(t){let e=new hi;if(e.breakAfter=this.breakAfter,0==this.length)return e;let{i:i,off:n}=this.childPos(t);n&&(e.append(this.children[i].split(n),0),this.children[i].merge(n,this.children[i].length,null,!1,0,0),i++);for(let t=i;t0&&0==this.children[i-1].length;)this.children[--i].destroy();return this.children.length=i,this.markDirty(),this.length=t,e}transferDOM(t){this.dom&&(this.markDirty(),t.setDOM(this.dom),t.prevAttrs=void 0===this.prevAttrs?this.attrs:this.prevAttrs,this.prevAttrs=void 0,this.dom=null)}setDeco(t){Qe(this.attrs,t)||(this.dom&&(this.prevAttrs=this.attrs,this.markDirty()),this.attrs=t)}append(t,e){Je(this,t,e)}addLineDeco(t){let e=t.spec.attributes,i=t.spec.class;e&&(this.attrs=Ye(e,this.attrs||{})),i&&(this.attrs=Ye({class:i},this.attrs||{}))}domAtPos(t){return Ue(this,t)}reuseDOM(t){"DIV"==t.nodeName&&(this.setDOM(t),this.dirty|=6)}sync(t){var e;this.dom?4&this.dirty&&(be(this.dom),this.dom.className="cm-line",this.prevAttrs=this.attrs?null:void 0):(this.setDOM(document.createElement("div")),this.dom.className="cm-line",this.prevAttrs=this.attrs?null:void 0),void 0!==this.prevAttrs&&(Ze(this.dom,this.prevAttrs,this.attrs),this.dom.classList.add("cm-line"),this.prevAttrs=void 0),super.sync(t);let i=this.dom.lastChild;for(;i&&Se.get(i)instanceof ze;)i=i.lastChild;if(!(i&&this.length&&("BR"==i.nodeName||0!=(null===(e=Se.get(i))||void 0===e?void 0:e.isEditable)||He.ios&&this.children.some((t=>t instanceof Fe))))){let t=document.createElement("BR");t.cmIgnore=!0,this.dom.appendChild(t)}}measureTextSize(){if(0==this.children.length||this.length>20)return null;let t=0;for(let e of this.children){if(!(e instanceof Fe)||/[^ -~]/.test(e.text))return null;let i=oe(e.dom);if(1!=i.length)return null;t+=i[0].width}return t?{lineHeight:this.dom.getBoundingClientRect().height,charWidth:t/this.length}:null}coordsAt(t,e){return Xe(this,t,e)}become(t){return!1}get type(){return ei.Text}static find(t,e){for(let i=0,n=0;i=e){if(r instanceof hi)return r;if(s>e)break}n=s+r.breakAfter}return null}}class ai extends Se{constructor(t,e,i){super(),this.widget=t,this.length=e,this.type=i,this.breakAfter=0,this.prevWidget=null}merge(t,e,i,n,r,s){return!(i&&(!(i instanceof ai&&this.widget.compare(i.widget))||t>0&&r<=0||e0;){if(this.textOff==this.text.length){let{value:e,lineBreak:i,done:n}=this.cursor.next(this.skip);if(this.skip=0,n)throw new Error("Ran out of text content when drawing inline views");if(i){this.posCovered()||this.getLine(),this.content.length?this.content[this.content.length-1].breakAfter=1:this.breakAtStart=1,this.flushBuffer([]),this.curLine=null,t--;continue}this.text=e,this.textOff=0}let n=Math.min(this.text.length-this.textOff,t,512);this.flushBuffer(e.slice(e.length-i)),this.getLine().append(ui(new Fe(this.text.slice(this.textOff,this.textOff+n)),e),i),this.atCursorPos=!0,this.textOff+=n,t-=n,i=0}}span(t,e,i,n){this.buildText(e-t,i,n),this.pos=e,this.openStart<0&&(this.openStart=n)}point(t,e,i,n,r,s){if(this.disallowBlockEffectsFor[s]&&i instanceof si){if(i.block)throw new RangeError("Block decorations may not be specified via plugins");if(e>this.doc.lineAt(this.pos).to)throw new RangeError("Decorations that replace line breaks may not be specified via plugins")}let o=e-t;if(i instanceof si)if(i.block){let{type:t}=i;t!=ei.WidgetAfter||this.posCovered()||this.getLine(),this.addBlockWidget(new ai(i.widget||new fi("div"),o,t))}else{let s=_e.create(i.widget||new fi("span"),o,o?0:i.startSide),l=this.atCursorPos&&!s.isEditable&&r<=n.length&&(t0),h=!s.isEditable&&(tt.some((t=>t))}),bi=P.define({combine:t=>t.some((t=>t))});class xi{constructor(t,e="nearest",i="nearest",n=5,r=5){this.range=t,this.y=e,this.x=i,this.yMargin=n,this.xMargin=r}map(t){return t.empty?this:new xi(this.range.map(t),this.y,this.x,this.yMargin,this.xMargin)}}const ki=ut.define({map:(t,e)=>t.map(e)});function Si(t,e,i){let n=t.facet(mi);n.length?n[0](e):window.onerror?window.onerror(String(e),i,void 0,void 0,e):i?console.error(i+":",e):console.error(e)}const Ai=P.define({combine:t=>!t.length||t[0]});let Mi=0;const Ci=P.define();class Di{constructor(t,e,i,n){this.id=t,this.create=e,this.domEventHandlers=i,this.extension=n(this)}static define(t,e){const{eventHandlers:i,provide:n,decorations:r}=e||{};return new Di(Mi++,t,i,(t=>{let e=[Ci.of(t)];return r&&e.push(Bi.of((e=>{let i=e.plugin(t);return i?r(i):ii.none}))),n&&e.push(n(t)),e}))}static fromClass(t,e){return Di.define((e=>new t(e)),e)}}class Oi{constructor(t){this.spec=t,this.mustUpdate=null,this.value=null}update(t){if(this.value){if(this.mustUpdate){let t=this.mustUpdate;if(this.mustUpdate=null,this.value.update)try{this.value.update(t)}catch(e){if(Si(t.state,e,"CodeMirror plugin crashed"),this.value.destroy)try{this.value.destroy()}catch(t){}this.deactivate()}}}else if(this.spec)try{this.value=this.spec.create(t)}catch(e){Si(t.state,e,"CodeMirror plugin crashed"),this.deactivate()}return this}destroy(t){var e;if(null===(e=this.value)||void 0===e?void 0:e.destroy)try{this.value.destroy()}catch(e){Si(t.state,e,"CodeMirror plugin crashed")}}deactivate(){this.spec=this.value=null}}const Ti=P.define(),Ei=P.define(),Bi=P.define(),Ri=P.define(),Li=P.define(),Ni=P.define();class Pi{constructor(t,e,i,n){this.fromA=t,this.toA=e,this.fromB=i,this.toB=n}join(t){return new Pi(Math.min(this.fromA,t.fromA),Math.max(this.toA,t.toA),Math.min(this.fromB,t.fromB),Math.max(this.toB,t.toB))}addToSet(t){let e=t.length,i=this;for(;e>0;e--){let n=t[e-1];if(!(n.fromA>i.toA)){if(n.toAa)break;r+=2}if(!l)return i;new Pi(l.fromA,l.toA,l.fromB,l.toB).addToSet(i),s=l.toA,o=l.toB}}}class Ii{constructor(t,e,i){this.view=t,this.state=e,this.transactions=i,this.flags=0,this.startState=t.state,this.changes=A.empty(this.startState.doc.length);for(let t of i)this.changes=this.changes.compose(t.changes);let n=[];this.changes.iterChangedRanges(((t,e,i,r)=>n.push(new Pi(t,e,i,r)))),this.changedRanges=n;let r=t.hasFocus;r!=t.inputState.notifiedFocused&&(t.inputState.notifiedFocused=r,this.flags|=1)}static create(t,e,i){return new Ii(t,e,i)}get viewportChanged(){return(4&this.flags)>0}get heightChanged(){return(2&this.flags)>0}get geometryChanged(){return this.docChanged||(10&this.flags)>0}get focusChanged(){return(1&this.flags)>0}get docChanged(){return!this.changes.empty}get selectionSet(){return this.transactions.some((t=>t.selection))}get empty(){return 0==this.flags&&0==this.transactions.length}}var Vi=function(t){return t[t.LTR=0]="LTR",t[t.RTL=1]="RTL",t}(Vi||(Vi={}));const Wi=Vi.LTR,Hi=Vi.RTL;function Fi(t){let e=[];for(let i=0;i=e){if(o.level==i)return s;(r<0||(0!=n?n<0?o.frome:t[r].level>o.level))&&(r=s)}}if(r<0)throw new RangeError("Index out of range");return r}}const Gi=[];function Ui(t){return[new $i(0,t,0)]}let Ji="";function Xi(t,e,i,n,r){var s;let o=n.head-t.from,l=-1;if(0==o){if(!r||!t.length)return null;e[0].level!=i&&(o=e[0].side(!1,i),l=0)}else if(o==t.length){if(r)return null;let t=e[e.length-1];t.level!=i&&(o=t.side(!0,i),l=e.length-1)}l<0&&(l=$i.find(e,o,null!==(s=n.bidiLevel)&&void 0!==s?s:-1,n.assoc));let h=e[l];o==h.side(r,i)&&(h=e[l+=r?1:-1],o=h.side(!r,i));let a=r==(h.dir==i),c=d(t.text,o,a);if(Ji=t.text.slice(Math.min(o,c),Math.max(o,c)),c!=h.side(r,i))return R.cursor(c+t.from,a?-1:1,h.level);let u=l==(r?e.length-1:0)?null:e[l+(r?1:-1)];return u||h.level==i?u&&u.level1)for(let e of this.points)e.node==t&&e.pos>this.text.length&&(e.pos-=o-1);i=s+o}}readNode(t){if(t.cmIgnore)return;let e=Se.get(t),i=e&&e.overrideDOMText;if(null!=i){this.findPointInside(t,i.length);for(let t=i.iter();!t.next().done;)t.lineBreak?this.lineBreak():this.append(t.value)}else 3==t.nodeType?this.readTextNode(t):"BR"==t.nodeName?t.nextSibling&&this.lineBreak():1==t.nodeType&&this.readRange(t.firstChild,null)}findPointBefore(t,e){for(let i of this.points)i.node==t&&t.childNodes[i.offset]==e&&(i.pos=this.text.length)}findPointInside(t,e){for(let i of this.points)(3==t.nodeType?i.node==t:t.contains(i.node))&&(i.pos=this.text.length+Math.min(e,i.offset))}}function Zi(t){return 1==t.nodeType&&/^(DIV|P|LI|UL|OL|BLOCKQUOTE|DD|DT|H\d|SECTION|PRE)$/.test(t.nodeName)}class tn{constructor(t,e){this.node=t,this.offset=e,this.pos=-1}}class en extends Se{constructor(t){super(),this.view=t,this.compositionDeco=ii.none,this.decorations=[],this.dynamicDecorationMap=[],this.minWidth=0,this.minWidthFrom=0,this.minWidthTo=0,this.impreciseAnchor=null,this.impreciseHead=null,this.forceSelection=!1,this.lastUpdate=Date.now(),this.setDOM(t.contentDOM),this.children=[new hi],this.children[0].setParent(this),this.updateDeco(),this.updateInner([new Pi(0,0,0,t.state.doc.length)],0)}get editorView(){return this.view}get length(){return this.view.state.doc.length}update(t){let e=t.changedRanges;this.minWidth>0&&e.length&&(e.every((({fromA:t,toA:e})=>ethis.minWidthTo))?(this.minWidthFrom=t.changes.mapPos(this.minWidthFrom,1),this.minWidthTo=t.changes.mapPos(this.minWidthTo,1)):this.minWidth=this.minWidthFrom=this.minWidthTo=0),this.view.inputState.composing<0?this.compositionDeco=ii.none:(t.transactions.length||this.dirty)&&(this.compositionDeco=function(t,e){let i=rn(t);if(!i)return ii.none;let{from:n,to:r,node:s,text:o}=i,l=e.mapPos(n,1),h=Math.max(l,e.mapPos(r,-1)),{state:a}=t,c=3==s.nodeType?s.nodeValue:new Qi([],a).readRange(s.firstChild,null).text;if(h-l{this.dom.style.height=this.view.viewState.contentHeight+"px",this.dom.style.flexBasis=this.minWidth?this.minWidth+"px":"";let t=He.chrome||He.ios?{node:i.selectionRange.focusNode,written:!1}:void 0;this.sync(t),this.dirty=0,t&&(t.written||i.selectionRange.focusNode!=t.node)&&(this.forceSelection=!0),this.dom.style.height=""}));let n=[];if(this.view.viewport.from||this.view.viewport.to=0?t[e]:null;if(!n)break;let{fromA:r,toA:s,fromB:o,toB:l}=n,{content:h,breakAtStart:a,openStart:c,openEnd:u}=ci.build(this.view.state.doc,o,l,this.decorations,this.dynamicDecorationMap),{i:f,off:d}=i.findPos(s,1),{i:p,off:g}=i.findPos(r,-1);Ce(this,p,g,f,d,h,a,c,u)}}updateSelection(t=!1,e=!1){if(!t&&this.view.observer.selectionRange.focusNode||this.view.observer.readSelectionRange(),!e&&!this.mayControlSelection())return;let i=this.forceSelection;this.forceSelection=!1;let n=this.view.state.selection.main,r=this.domAtPos(n.anchor),s=n.empty?r:this.domAtPos(n.head);if(He.gecko&&n.empty&&(1==(o=r).node.nodeType&&o.node.firstChild&&(0==o.offset||"false"==o.node.childNodes[o.offset-1].contentEditable)&&(o.offset==o.node.childNodes.length||"false"==o.node.childNodes[o.offset].contentEditable))){let t=document.createTextNode("");this.view.observer.ignore((()=>r.node.insertBefore(t,r.node.childNodes[r.offset]||null))),r=s=new xe(t,0),i=!0}var o;let l=this.view.observer.selectionRange;!i&&l.focusNode&&le(r.node,r.offset,l.anchorNode,l.anchorOffset)&&le(s.node,s.offset,l.focusNode,l.focusOffset)||(this.view.observer.ignore((()=>{He.android&&He.chrome&&this.dom.contains(l.focusNode)&&function(t,e){for(let i=t;i&&i!=e;i=i.assignedSlot||i.parentNode)if(1==i.nodeType&&"false"==i.contentEditable)return!0;return!1}(l.focusNode,this.dom)&&(this.dom.blur(),this.dom.focus({preventScroll:!0}));let t=ne(this.view.root);if(t)if(n.empty){if(He.gecko){let t=(e=r.node,i=r.offset,1!=e.nodeType?0:(i&&"false"==e.childNodes[i-1].contentEditable?1:0)|(in.head&&([r,s]=[s,r]),e.setEnd(s.node,s.offset),e.setStart(r.node,r.offset),t.removeAllRanges(),t.addRange(e)}else;var e,i})),this.view.observer.setSelectionRange(r,s)),this.impreciseAnchor=r.precise?null:new xe(l.anchorNode,l.anchorOffset),this.impreciseHead=s.precise?null:new xe(l.focusNode,l.focusOffset)}enforceCursorAssoc(){if(this.compositionDeco.size)return;let{view:t}=this,e=t.state.selection.main,i=ne(t.root),{anchorNode:n,anchorOffset:r}=t.observer.selectionRange;if(!(i&&e.empty&&e.assoc&&i.modify))return;let s=hi.find(this,e.head);if(!s)return;let o=s.posAtStart;if(e.head==o||e.head==o+s.length)return;let l=this.coordsAt(e.head,-1),h=this.coordsAt(e.head,1);if(!l||!h||l.bottom>h.top)return;let a=this.domAtPos(e.head+e.assoc);i.collapse(a.node,a.offset),i.modify("move",e.assoc<0?"forward":"backward","lineboundary"),t.observer.readSelectionRange();let c=t.observer.selectionRange;t.docView.posFromDOM(c.anchorNode,c.anchorOffset)!=e.from&&i.collapse(n,r)}mayControlSelection(){let t=this.view.root.activeElement;return t==this.dom||se(this.dom,this.view.observer.selectionRange)&&!(t&&this.dom.contains(t))}nearest(t){for(let e=t;e;){let t=Se.get(e);if(t&&t.rootView==this)return t;e=e.parentNode}return null}posFromDOM(t,e){let i=this.nearest(t);if(!i)throw new RangeError("Trying to find position for a DOM position outside of the document");return i.localPosFromDOM(t,e)+i.posAtStart}domAtPos(t){let{i:e,off:i}=this.childCursor().findPos(t,-1);for(;es||t==s&&r.type!=ei.WidgetBefore&&r.type!=ei.WidgetAfter&&(!n||2==e||this.children[n-1].breakAfter||this.children[n-1].type==ei.WidgetBefore&&e>-2))return r.coordsAt(t-s,e);i=s}}measureVisibleLineHeights(t){let e=[],{from:i,to:n}=t,r=this.view.contentDOM.clientWidth,s=r>Math.max(this.view.scrollDOM.clientWidth,this.minWidth)+1,o=-1,l=this.view.textDirection==Vi.LTR;for(let t=0,h=0;hn)break;if(t>=i){let i=a.dom.getBoundingClientRect();if(e.push(i.height),s){let e=a.dom.lastChild,n=e?oe(e):[];if(n.length){let e=n[n.length-1],s=l?e.right-i.left:i.right-e.left;s>o&&(o=s,this.minWidth=r,this.minWidthFrom=t,this.minWidthTo=c)}}}t=c+a.breakAfter}return e}textDirectionAt(t){let{i:e}=this.childPos(t,1);return"rtl"==getComputedStyle(this.children[e].dom).direction?Vi.RTL:Vi.LTR}measureTextSize(){for(let t of this.children)if(t instanceof hi){let e=t.measureTextSize();if(e)return e}let t,e,i=document.createElement("div");return i.className="cm-line",i.style.width="99999px",i.textContent="abc def ghi jkl mno pqr stu",this.view.observer.ignore((()=>{this.dom.appendChild(i);let n=oe(i.firstChild)[0];t=i.getBoundingClientRect().height,e=n?n.width/27:7,i.remove()})),{lineHeight:t,charWidth:e}}childCursor(t=this.length){let e=this.children.length;return e&&(t-=this.children[--e].length),new Me(this.children,t,e)}computeBlockGapDeco(){let t=[],e=this.view.viewState;for(let i=0,n=0;;n++){let r=n==e.viewports.length?null:e.viewports[n],s=r?r.from-1:this.length;if(s>i){let n=e.lineBlockAt(s).bottom-e.lineBlockAt(i).top;t.push(ii.replace({widget:new nn(n),block:!0,inclusive:!0,isBlockGap:!0}).range(i,s))}if(!r)break;i=r.to+1}return ii.set(t)}updateDeco(){let t=this.view.state.facet(Bi).map(((t,e)=>(this.dynamicDecorationMap[e]="function"==typeof t)?t(this.view):t));for(let e=t.length;ei.anchor?-1:1);if(!n)return;!i.empty&&(e=this.coordsAt(i.anchor,i.anchor>i.head?-1:1))&&(n={left:Math.min(n.left,e.left),top:Math.min(n.top,e.top),right:Math.max(n.right,e.right),bottom:Math.max(n.bottom,e.bottom)});let r=0,s=0,o=0,l=0;for(let t of this.view.state.facet(Li).map((t=>t(this.view))))if(t){let{left:e,right:i,top:n,bottom:h}=t;null!=e&&(r=Math.max(r,e)),null!=i&&(s=Math.max(s,i)),null!=n&&(o=Math.max(o,n)),null!=h&&(l=Math.max(l,h))}let h={left:n.left-r,top:n.top-o,right:n.right+s,bottom:n.bottom+l};!function(t,e,i,n,r,s,o,l){let h=t.ownerDocument,a=h.defaultView||window;for(let c=t;c;)if(1==c.nodeType){let t,u=c==h.body;if(u)t=de(a);else{if(c.scrollHeight<=c.clientHeight&&c.scrollWidth<=c.clientWidth){c=c.assignedSlot||c.parentNode;continue}let e=c.getBoundingClientRect();t={left:e.left,right:e.left+c.clientWidth,top:e.top,bottom:e.top+c.clientHeight}}let f=0,d=0;if("nearest"==r)e.top0&&e.bottom>t.bottom+d&&(d=e.bottom-t.bottom+d+o)):e.bottom>t.bottom&&(d=e.bottom-t.bottom+o,i<0&&e.top-d0&&e.right>t.right+f&&(f=e.right-t.right+f+s)):e.right>t.right&&(f=e.right-t.right+s,i<0&&e.left0&&i<=0)e=ce(t=t.childNodes[e-1]);else{if(!(1==t.nodeType&&e=0))return null;t=t.childNodes[e],e=0}}}class ln{constructor(){this.changes=[]}compareRange(t,e){li(t,e,this.changes)}comparePoint(t,e){li(t,e,this.changes)}}function hn(t,e){return e.left>t?e.left-t:Math.max(0,t-e.right)}function an(t,e){return e.top>t?e.top-t:Math.max(0,t-e.bottom)}function cn(t,e){return t.tope.top+1}function un(t,e){return et.bottom?{top:t.top,left:t.left,right:t.right,bottom:e}:t}function dn(t,e,i){let n,r,s,o,l,h,a,c,u=!1;for(let f=t.firstChild;f;f=f.nextSibling){let t=oe(f);for(let d=0;dm||o==m&&s>g)&&(n=f,r=p,s=g,o=m,u=!g||(g>0?d0)),0==g?i>p.bottom&&(!a||a.bottomp.top)&&(h=f,c=p):a&&cn(a,p)?a=fn(a,p.bottom):c&&cn(c,p)&&(c=un(c,p.top))}}if(a&&a.bottom>=i?(n=l,r=a):c&&c.top<=i&&(n=h,r=c),!n)return{node:t,offset:0};let f=Math.max(r.left,Math.min(r.right,e));return 3==n.nodeType?pn(n,f,i):u&&"false"!=n.contentEditable?dn(n,f,i):{node:t,offset:Array.prototype.indexOf.call(t.childNodes,n)+(e>=(r.left+r.right)/2?1:0)}}function pn(t,e,i){let n=t.nodeValue.length,r=-1,s=1e9,o=0;for(let l=0;li?a.top-i:i-a.bottom)-1;if(a.left-1<=e&&a.right+1>=e&&c=(a.left+a.right)/2,n=i;if(He.chrome||He.gecko){we(t,l).getBoundingClientRect().left==a.right&&(n=!i)}if(c<=0)return{node:t,offset:l+(n?1:0)};r=l+(n?1:0),s=c}}}return{node:t,offset:r>-1?r:o>0?t.nodeValue.length:0}}function gn(t,{x:e,y:i},n,r=-1){var s;let o,l=t.contentDOM.getBoundingClientRect(),h=l.top+t.viewState.paddingTop,{docHeight:a}=t.viewState,c=i-h;if(c<0)return 0;if(c>a)return t.state.doc.length;for(let e=t.defaultLineHeight/2,i=!1;o=t.elementAtHeight(c),o.type!=ei.Text;)for(;c=r>0?o.bottom+e:o.top-e,!(c>=0&&c<=a);){if(i)return n?null:0;i=!0,r=-r}i=h+c;let u=o.from;if(ut.viewport.to)return t.viewport.to==t.state.doc.length?t.state.doc.length:n?null:mn(t,l,o,e,i);let f=t.dom.ownerDocument,d=t.root.elementFromPoint?t.root:f,p=d.elementFromPoint(e,i);p&&!t.contentDOM.contains(p)&&(p=null),p||(e=Math.max(l.left+1,Math.min(l.right-1,e)),p=d.elementFromPoint(e,i),p&&!t.contentDOM.contains(p)&&(p=null));let g,m=-1;if(p&&0!=(null===(s=t.docView.nearest(p))||void 0===s?void 0:s.isEditable))if(f.caretPositionFromPoint){let t=f.caretPositionFromPoint(e,i);t&&({offsetNode:g,offset:m}=t)}else if(f.caretRangeFromPoint){let n=f.caretRangeFromPoint(e,i);n&&(({startContainer:g,startOffset:m}=n),(!t.contentDOM.contains(g)||He.safari&&function(t,e,i){let n;if(3!=t.nodeType||e!=(n=t.nodeValue.length))return!1;for(let e=t.nextSibling;e;e=e.nextSibling)if(1!=e.nodeType||"BR"!=e.nodeName)return!1;return we(t,n-1,n).getBoundingClientRect().left>i}(g,m,e)||He.chrome&&function(t,e,i){if(0!=e)return!1;for(let e=t;;){let t=e.parentNode;if(!t||1!=t.nodeType||t.firstChild!=e)return!1;if(t.classList.contains("cm-line"))break;e=t}let n=1==t.nodeType?t.getBoundingClientRect():we(t,0,Math.max(t.nodeValue.length,1)).getBoundingClientRect();return i-n.left>5}(g,m,e))&&(g=void 0))}if(!g||!t.docView.dom.contains(g)){let n=hi.find(t.docView,u);if(!n)return c>o.top+o.height/2?o.to:o.from;({node:g,offset:m}=dn(n.dom,e,i))}return t.docView.posFromDOM(g,m)}function mn(t,e,i,n,r){let s=Math.round((n-e.left)*t.defaultCharacterWidth);if(t.lineWrapping&&i.height>1.5*t.defaultLineHeight){s+=Math.floor((r-i.top)/t.defaultLineHeight)*t.viewState.heightOracle.lineLength}let o=t.state.sliceDoc(i.from,i.to);return i.from+qt(o,s,t.state.tabSize)}function vn(t,e,i,n){let r=t.state.doc.lineAt(e.head),s=t.bidiSpans(r),o=t.textDirectionAt(r.from);for(let l=e,h=null;;){let e=Xi(r,s,o,l,i),a=Ji;if(!e){if(r.number==(i?t.state.doc.lines:1))return l;a="\n",r=t.state.doc.line(r.number+(i?1:-1)),s=t.bidiSpans(r),e=R.cursor(i?r.from:r.to)}if(h){if(!h(a))return l}else{if(!n)return e;h=n(a)}l=e}}function wn(t,e,i){let n=t.state.facet(Ri).map((e=>e(t)));for(;;){let t=!1;for(let r of n)r.between(i.from-1,i.from+1,((n,r,s)=>{i.from>n&&i.fromi.from?R.cursor(n,1):R.cursor(r,-1),t=!0)}));if(!t)return i}}class yn{constructor(t){this.lastKeyCode=0,this.lastKeyTime=0,this.lastTouchTime=0,this.lastFocusTime=0,this.lastScrollTop=0,this.lastScrollLeft=0,this.chromeScrollHack=-1,this.pendingIOSKey=void 0,this.lastSelectionOrigin=null,this.lastSelectionTime=0,this.lastEscPress=0,this.lastContextMenu=0,this.scrollHandlers=[],this.registeredEvents=[],this.customHandlers=[],this.composing=-1,this.compositionFirstChange=null,this.compositionEndedAt=0,this.mouseSelection=null;for(let e in Mn){let i=Mn[e];t.contentDOM.addEventListener(e,(n=>{An(t,n)&&!this.ignoreDuringComposition(n)&&("keydown"==e&&this.keydown(t,n)||(this.mustFlushObserver(n)&&t.observer.forceFlush(),this.runCustomHandlers(e,t,n)?n.preventDefault():i(t,n)))}),Cn[e]),this.registeredEvents.push(e)}He.chrome&&102==He.chrome_version&&t.scrollDOM.addEventListener("wheel",(()=>{this.chromeScrollHack<0?t.contentDOM.style.pointerEvents="none":window.clearTimeout(this.chromeScrollHack),this.chromeScrollHack=setTimeout((()=>{this.chromeScrollHack=-1,t.contentDOM.style.pointerEvents=""}),100)}),{passive:!0}),this.notifiedFocused=t.hasFocus,He.safari&&t.contentDOM.addEventListener("input",(()=>null))}setSelectionOrigin(t){this.lastSelectionOrigin=t,this.lastSelectionTime=Date.now()}ensureHandlers(t,e){var i;let n;this.customHandlers=[];for(let r of e)if(n=null===(i=r.update(t).spec)||void 0===i?void 0:i.domEventHandlers){this.customHandlers.push({plugin:r.value,handlers:n});for(let e in n)this.registeredEvents.indexOf(e)<0&&"scroll"!=e&&(this.registeredEvents.push(e),t.contentDOM.addEventListener(e,(i=>{An(t,i)&&this.runCustomHandlers(e,t,i)&&i.preventDefault()})))}}runCustomHandlers(t,e,i){for(let n of this.customHandlers){let r=n.handlers[t];if(r)try{if(r.call(n.plugin,i,e)||i.defaultPrevented)return!0}catch(t){Si(e.state,t)}}return!1}runScrollHandlers(t,e){this.lastScrollTop=t.scrollDOM.scrollTop,this.lastScrollLeft=t.scrollDOM.scrollLeft;for(let i of this.customHandlers){let n=i.handlers.scroll;if(n)try{n.call(i.plugin,e,t)}catch(e){Si(t.state,e)}}}keydown(t,e){if(this.lastKeyCode=e.keyCode,this.lastKeyTime=Date.now(),9==e.keyCode&&Date.now()t.keyCode==e.keyCode)))&&!e.ctrlKey||xn.indexOf(e.key)>-1&&e.ctrlKey&&!e.shiftKey))&&(this.pendingIOSKey=i||e,setTimeout((()=>this.flushIOSKey(t)),250),!0)}flushIOSKey(t){let e=this.pendingIOSKey;return!!e&&(this.pendingIOSKey=void 0,ye(t.contentDOM,e.key,e.keyCode))}ignoreDuringComposition(t){return!!/^key/.test(t.type)&&(this.composing>0||!!(He.safari&&!He.ios&&Date.now()-this.compositionEndedAt<100)&&(this.compositionEndedAt=0,!0))}mustFlushObserver(t){return"keydown"==t.type&&229!=t.keyCode}startMouseSelection(t){this.mouseSelection&&this.mouseSelection.destroy(),this.mouseSelection=t}update(t){this.mouseSelection&&this.mouseSelection.update(t),t.transactions.length&&(this.lastKeyCode=this.lastSelectionTime=0)}destroy(){this.mouseSelection&&this.mouseSelection.destroy()}}const bn=[{key:"Backspace",keyCode:8,inputType:"deleteContentBackward"},{key:"Enter",keyCode:13,inputType:"insertParagraph"},{key:"Delete",keyCode:46,inputType:"deleteContentForward"}],xn="dthko",kn=[16,17,18,20,91,92,224,225];class Sn{constructor(t,e,i,n){this.view=t,this.style=i,this.mustSelect=n,this.lastEvent=e;let r=t.contentDOM.ownerDocument;r.addEventListener("mousemove",this.move=this.move.bind(this)),r.addEventListener("mouseup",this.up=this.up.bind(this)),this.extend=e.shiftKey,this.multiple=t.state.facet(St.allowMultipleSelections)&&function(t,e){let i=t.state.facet(di);return i.length?i[0](e):He.mac?e.metaKey:e.ctrlKey}(t,e),this.dragMove=function(t,e){let i=t.state.facet(pi);return i.length?i[0](e):He.mac?!e.altKey:!e.ctrlKey}(t,e),this.dragging=!(!function(t,e){let{main:i}=t.state.selection;if(i.empty)return!1;let n=ne(t.root);if(!n||0==n.rangeCount)return!0;let r=n.getRangeAt(0).getClientRects();for(let t=0;t=e.clientX&&i.top<=e.clientY&&i.bottom>=e.clientY)return!0}return!1}(t,e)||1!=Wn(e))&&null,!1===this.dragging&&(e.preventDefault(),this.select(e))}move(t){if(0==t.buttons)return this.destroy();!1===this.dragging&&this.select(this.lastEvent=t)}up(t){null==this.dragging&&this.select(this.lastEvent),this.dragging||t.preventDefault(),this.destroy()}destroy(){let t=this.view.contentDOM.ownerDocument;t.removeEventListener("mousemove",this.move),t.removeEventListener("mouseup",this.up),this.view.inputState.mouseSelection=null}select(t){let e=this.style.get(t,this.extend,this.multiple);!this.mustSelect&&e.eq(this.view.state.selection)&&e.main.assoc==this.view.state.selection.main.assoc||this.view.dispatch({selection:e,userEvent:"select.pointer",scrollIntoView:!0}),this.mustSelect=!1}update(t){t.docChanged&&this.dragging&&(this.dragging=this.dragging.map(t.changes)),this.style.update(t)&&setTimeout((()=>this.select(this.lastEvent)),20)}}function An(t,e){if(!e.bubbles)return!0;if(e.defaultPrevented)return!1;for(let i,n=e.target;n!=t.contentDOM;n=n.parentNode)if(!n||11==n.nodeType||(i=Se.get(n))&&i.ignoreEvent(e))return!1;return!0}const Mn=Object.create(null),Cn=Object.create(null),Dn=He.ie&&He.ie_version<15||He.ios&&He.webkit_version<604;function On(t,e){let i,{state:n}=t,r=1,s=n.toText(e),o=s.lines==n.selection.ranges.length;if(null!=Fn&&n.selection.ranges.every((t=>t.empty))&&Fn==s.toString()){let t=-1;i=n.changeByRange((i=>{let l=n.doc.lineAt(i.from);if(l.from==t)return{range:i};t=l.from;let h=n.toText((o?s.line(r++).text:e)+n.lineBreak);return{changes:{from:l.from,insert:h},range:R.cursor(i.from+h.length)}}))}else i=o?n.changeByRange((t=>{let e=s.line(r++);return{changes:{from:t.from,to:t.to,insert:e.text},range:R.cursor(t.from+e.length)}})):n.replaceSelection(s);t.dispatch(i,{userEvent:"input.paste",scrollIntoView:!0})}function Tn(t,e,i,n){if(1==n)return R.cursor(e,i);if(2==n)return function(t,e,i=1){let n=t.charCategorizer(e),r=t.doc.lineAt(e),s=e-r.from;if(0==r.length)return R.cursor(e);0==s?i=1:s==r.length&&(i=-1);let o=s,l=s;i<0?o=d(r.text,s,!1):l=d(r.text,s);let h=n(r.text.slice(o,l));for(;o>0;){let t=d(r.text,o,!1);if(n(r.text.slice(t,o))!=h)break;o=t}for(;l{t.inputState.setSelectionOrigin("select"),27==e.keyCode?t.inputState.lastEscPress=Date.now():kn.indexOf(e.keyCode)<0&&(t.inputState.lastEscPress=0)},Mn.touchstart=(t,e)=>{t.inputState.lastTouchTime=Date.now(),t.inputState.setSelectionOrigin("select.pointer")},Mn.touchmove=t=>{t.inputState.setSelectionOrigin("select.pointer")},Cn.touchstart=Cn.touchmove={passive:!0},Mn.mousedown=(t,e)=>{if(t.observer.flush(),t.inputState.lastTouchTime>Date.now()-2e3)return;let i=null;for(let n of t.state.facet(gi))if(i=n(t,e),i)break;if(i||0!=e.button||(i=function(t,e){let i=Ln(t,e),n=Wn(e),r=t.state.selection,s=i,o=e;return{update(t){t.docChanged&&(i.pos=t.changes.mapPos(i.pos),r=r.map(t.changes),o=null)},get(e,l,h){let a;o&&e.clientX==o.clientX&&e.clientY==o.clientY?a=s:(a=s=Ln(t,e),o=e);let c=Tn(t,a.pos,a.bias,n);if(i.pos!=a.pos&&!l){let e=Tn(t,i.pos,i.bias,n),r=Math.min(e.from,c.from),s=Math.max(e.to,c.to);c=r1&&r.ranges.some((t=>t.eq(c)))?function(t,e){for(let i=0;;i++)if(t.ranges[i].eq(e))return R.create(t.ranges.slice(0,i).concat(t.ranges.slice(i+1)),t.mainIndex==i?0:t.mainIndex-(t.mainIndex>i?1:0))}(r,c):h?r.addRange(c):R.create([c])}}}(t,e)),i){let n=t.root.activeElement!=t.contentDOM;n&&t.observer.ignore((()=>ve(t.contentDOM))),t.inputState.startMouseSelection(new Sn(t,e,i,n))}};let En=(t,e)=>t>=e.top&&t<=e.bottom,Bn=(t,e,i)=>En(e,i)&&t>=i.left&&t<=i.right;function Rn(t,e,i,n){let r=hi.find(t.docView,e);if(!r)return 1;let s=e-r.posAtStart;if(0==s)return 1;if(s==r.length)return-1;let o=r.coordsAt(s,-1);if(o&&Bn(i,n,o))return-1;let l=r.coordsAt(s,1);return l&&Bn(i,n,l)?1:o&&En(n,o)?-1:1}function Ln(t,e){let i=t.posAtCoords({x:e.clientX,y:e.clientY},!1);return{pos:i,bias:Rn(t,i,e.clientX,e.clientY)}}const Nn=He.ie&&He.ie_version<=11;let Pn=null,In=0,Vn=0;function Wn(t){if(!Nn)return t.detail;let e=Pn,i=Vn;return Pn=t,Vn=Date.now(),In=!e||i>Date.now()-400&&Math.abs(e.clientX-t.clientX)<2&&Math.abs(e.clientY-t.clientY)<2?(In+1)%3:1}function Hn(t,e,i,n){if(!i)return;let r=t.posAtCoords({x:e.clientX,y:e.clientY},!1);e.preventDefault();let{mouseSelection:s}=t.inputState,o=n&&s&&s.dragging&&s.dragMove?{from:s.dragging.from,to:s.dragging.to}:null,l={from:r,insert:i},h=t.state.changes(o?[o,l]:l);t.focus(),t.dispatch({changes:h,selection:{anchor:h.mapPos(r,-1),head:h.mapPos(r,1)},userEvent:o?"move.drop":"input.drop"})}Mn.dragstart=(t,e)=>{let{selection:{main:i}}=t.state,{mouseSelection:n}=t.inputState;n&&(n.dragging=i),e.dataTransfer&&(e.dataTransfer.setData("Text",t.state.sliceDoc(i.from,i.to)),e.dataTransfer.effectAllowed="copyMove")},Mn.drop=(t,e)=>{if(!e.dataTransfer)return;if(t.state.readOnly)return e.preventDefault();let i=e.dataTransfer.files;if(i&&i.length){e.preventDefault();let n=Array(i.length),r=0,s=()=>{++r==i.length&&Hn(t,e,n.filter((t=>null!=t)).join(t.state.lineBreak),!1)};for(let t=0;t{/[\x00-\x08\x0e-\x1f]{2}/.test(e.result)||(n[t]=e.result),s()},e.readAsText(i[t])}}else Hn(t,e,e.dataTransfer.getData("Text"),!0)},Mn.paste=(t,e)=>{if(t.state.readOnly)return e.preventDefault();t.observer.flush();let i=Dn?null:e.clipboardData;i?(On(t,i.getData("text/plain")),e.preventDefault()):function(t){let e=t.dom.parentNode;if(!e)return;let i=e.appendChild(document.createElement("textarea"));i.style.cssText="position: fixed; left: -10000px; top: 10px",i.focus(),setTimeout((()=>{t.focus(),i.remove(),On(t,i.value)}),50)}(t)};let Fn=null;function zn(t){setTimeout((()=>{t.hasFocus!=t.inputState.notifiedFocused&&t.update([])}),10)}Mn.copy=Mn.cut=(t,e)=>{let{text:i,ranges:n,linewise:r}=function(t){let e=[],i=[],n=!1;for(let n of t.selection.ranges)n.empty||(e.push(t.sliceDoc(n.from,n.to)),i.push(n));if(!e.length){let r=-1;for(let{from:n}of t.selection.ranges){let s=t.doc.lineAt(n);s.number>r&&(e.push(s.text),i.push({from:s.from,to:Math.min(t.doc.length,s.to+1)})),r=s.number}n=!0}return{text:e.join(t.lineBreak),ranges:i,linewise:n}}(t.state);if(!i&&!r)return;Fn=r?i:null;let s=Dn?null:e.clipboardData;s?(e.preventDefault(),s.clearData(),s.setData("text/plain",i)):function(t,e){let i=t.dom.parentNode;if(!i)return;let n=i.appendChild(document.createElement("textarea"));n.style.cssText="position: fixed; left: -10000px; top: 10px",n.value=e,n.focus(),n.selectionEnd=e.length,n.selectionStart=0,setTimeout((()=>{n.remove(),t.focus()}),50)}(t,i),"cut"!=e.type||t.state.readOnly||t.dispatch({changes:n,scrollIntoView:!0,userEvent:"delete.cut"})},Mn.focus=t=>{t.inputState.lastFocusTime=Date.now(),t.scrollDOM.scrollTop||!t.inputState.lastScrollTop&&!t.inputState.lastScrollLeft||(t.scrollDOM.scrollTop=t.inputState.lastScrollTop,t.scrollDOM.scrollLeft=t.inputState.lastScrollLeft),zn(t)},Mn.blur=t=>{t.observer.clearSelectionRange(),zn(t)},Mn.compositionstart=Mn.compositionupdate=t=>{null==t.inputState.compositionFirstChange&&(t.inputState.compositionFirstChange=!0),t.inputState.composing<0&&(t.inputState.composing=0)},Mn.compositionend=t=>{t.inputState.composing=-1,t.inputState.compositionEndedAt=Date.now(),t.inputState.compositionFirstChange=null,He.chrome&&He.android&&t.observer.flushSoon(),setTimeout((()=>{t.inputState.composing<0&&t.docView.compositionDeco.size&&t.update([])}),50)},Mn.contextmenu=t=>{t.inputState.lastContextMenu=Date.now()},Mn.beforeinput=(t,e)=>{var i;let n;if(He.chrome&&He.android&&(n=bn.find((t=>t.inputType==e.inputType)))&&(t.observer.delayAndroidKey(n.key,n.keyCode),"Backspace"==n.key||"Delete"==n.key)){let e=(null===(i=window.visualViewport)||void 0===i?void 0:i.height)||0;setTimeout((()=>{var i;((null===(i=window.visualViewport)||void 0===i?void 0:i.height)||0)>e+10&&t.hasFocus&&(t.contentDOM.blur(),t.focus())}),100)}};const qn=["pre-wrap","normal","pre-line","break-spaces"];class _n{constructor(t){this.lineWrapping=t,this.doc=e.empty,this.heightSamples={},this.lineHeight=14,this.charWidth=7,this.lineLength=30,this.heightChanged=!1}heightForGap(t,e){let i=this.doc.lineAt(e).number-this.doc.lineAt(t).number+1;return this.lineWrapping&&(i+=Math.ceil((e-t-i*this.lineLength*.5)/this.lineLength)),this.lineHeight*i}heightForLine(t){if(!this.lineWrapping)return this.lineHeight;return(1+Math.max(0,Math.ceil((t-this.lineLength)/(this.lineLength-5))))*this.lineHeight}setDoc(t){return this.doc=t,this}mustRefreshForWrapping(t){return qn.indexOf(t)>-1!=this.lineWrapping}mustRefreshForHeights(t){let e=!1;for(let i=0;i-1,o=Math.round(e)!=Math.round(this.lineHeight)||this.lineWrapping!=s;if(this.lineWrapping=s,this.lineHeight=e,this.charWidth=i,this.lineLength=n,o){this.heightSamples={};for(let t=0;t0}set outdated(t){this.flags=(t?2:0)|-3&this.flags}setHeight(t,e){this.height!=e&&(Math.abs(this.height-e)>Gn&&(t.heightChanged=!0),this.height=e)}replace(t,e,i){return Un.of(i)}decomposeLeft(t,e){e.push(this)}decomposeRight(t,e){e.push(this)}applyChanges(t,e,i,n){let r=this;for(let s=n.length-1;s>=0;s--){let{fromA:o,toA:l,fromB:h,toB:a}=n[s],c=r.lineAt(o,$n.ByPosNoHeight,e,0,0),u=c.to>=l?c:r.lineAt(l,$n.ByPosNoHeight,e,0,0);for(a+=u.to-l,l=u.to;s>0&&c.from<=n[s-1].toA;)o=n[s-1].fromA,h=n[s-1].fromB,s--,o2*r){let r=t[e-1];r.break?t.splice(--e,1,r.left,null,r.right):t.splice(--e,1,r.left,r.right),i+=1+r.break,n-=r.size}else{if(!(r>2*n))break;{let e=t[i];e.break?t.splice(i,1,e.left,null,e.right):t.splice(i,1,e.left,e.right),i+=2+e.break,r-=e.size}}else if(n=r&&s(this.blockAt(0,i,n,r))}updateHeight(t,e=0,i=!1,n){return n&&n.from<=e&&n.more&&this.setHeight(t,n.heights[n.index++]),this.outdated=!1,this}toString(){return`block(${this.length})`}}class Xn extends Jn{constructor(t,e){super(t,e,ei.Text),this.collapsed=0,this.widgetHeight=0}replace(t,e,i){let n=i[0];return 1==i.length&&(n instanceof Xn||n instanceof Yn&&4&n.flags)&&Math.abs(this.length-n.length)<10?(n instanceof Yn?n=new Xn(n.length,this.height):n.height=this.height,this.outdated||(n.outdated=!1),n):Un.of(i)}updateHeight(t,e=0,i=!1,n){return n&&n.from<=e&&n.more?this.setHeight(t,n.heights[n.index++]):(i||this.outdated)&&this.setHeight(t,Math.max(this.widgetHeight,t.heightForLine(this.length-this.collapsed))),this.outdated=!1,this}toString(){return`line(${this.length}${this.collapsed?-this.collapsed:""}${this.widgetHeight?":"+this.widgetHeight:""})`}}class Yn extends Un{constructor(t){super(t,0)}lines(t,e){let i=t.lineAt(e).number,n=t.lineAt(e+this.length).number;return{firstLine:i,lastLine:n,lineHeight:this.height/(n-i+1)}}blockAt(t,e,i,n){let{firstLine:r,lastLine:s,lineHeight:o}=this.lines(e,n),l=Math.max(0,Math.min(s-r,Math.floor((t-i)/o))),{from:h,length:a}=e.line(r+l);return new jn(h,a,i+o*l,o,ei.Text)}lineAt(t,e,i,n,r){if(e==$n.ByHeight)return this.blockAt(t,i,n,r);if(e==$n.ByPosNoHeight){let{from:e,to:n}=i.lineAt(t);return new jn(e,n-e,0,0,ei.Text)}let{firstLine:s,lineHeight:o}=this.lines(i,r),{from:l,length:h,number:a}=i.lineAt(t);return new jn(l,h,n+o*(a-s),o,ei.Text)}forEachLine(t,e,i,n,r,s){let{firstLine:o,lineHeight:l}=this.lines(i,r);for(let h=Math.max(t,r),a=Math.min(r+this.length,e);h<=a;){let e=i.lineAt(h);h==t&&(n+=l*(e.number-o)),s(new jn(e.from,e.length,n,l,ei.Text)),n+=l,h=e.to+1}}replace(t,e,i){let n=this.length-e;if(n>0){let t=i[i.length-1];t instanceof Yn?i[i.length-1]=new Yn(t.length+n):i.push(null,new Yn(n-1))}if(t>0){let e=i[0];e instanceof Yn?i[0]=new Yn(t+e.length):i.unshift(new Yn(t-1),null)}return Un.of(i)}decomposeLeft(t,e){e.push(new Yn(t-1),null)}decomposeRight(t,e){e.push(null,new Yn(this.length-t-1))}updateHeight(t,e=0,i=!1,n){let r=e+this.length;if(n&&n.from<=e+this.length&&n.more){let i=[],s=Math.max(e,n.from),o=-1,l=t.heightChanged;for(n.from>e&&i.push(new Yn(n.from-e-1).updateHeight(t,e));s<=r&&n.more;){let e=t.doc.lineAt(s).length;i.length&&i.push(null);let r=n.heights[n.index++];-1==o?o=r:Math.abs(r-o)>=Gn&&(o=-2);let l=new Xn(e,r);l.outdated=!1,i.push(l),s+=e+1}s<=r&&i.push(null,new Yn(r-s).updateHeight(t,s));let h=Un.of(i);return t.heightChanged=l||o<0||Math.abs(h.height-this.height)>=Gn||Math.abs(o-this.lines(t.doc,e).lineHeight)>=Gn,h}return(i||this.outdated)&&(this.setHeight(t,t.heightForGap(e,e+this.length)),this.outdated=!1),this}toString(){return`gap(${this.length})`}}class Qn extends Un{constructor(t,e,i){super(t.length+e+i.length,t.height+i.height,e|(t.outdated||i.outdated?2:0)),this.left=t,this.right=i,this.size=t.size+i.size}get break(){return 1&this.flags}blockAt(t,e,i,n){let r=i+this.left.height;return to))return h;let a=e==$n.ByPosNoHeight?$n.ByPosNoHeight:$n.ByPos;return l?h.join(this.right.lineAt(o,a,i,s,o)):this.left.lineAt(o,a,i,n,r).join(h)}forEachLine(t,e,i,n,r,s){let o=n+this.left.height,l=r+this.left.length+this.break;if(this.break)t=l&&this.right.forEachLine(t,e,i,o,l,s);else{let h=this.lineAt(l,$n.ByPos,i,n,r);t=t&&h.from<=e&&s(h),e>h.to&&this.right.forEachLine(h.to+1,e,i,o,l,s)}}replace(t,e,i){let n=this.left.length+this.break;if(ethis.left.length)return this.balanced(this.left,this.right.replace(t-n,e-n,i));let r=[];t>0&&this.decomposeLeft(t,r);let s=r.length;for(let t of i)r.push(t);if(t>0&&Zn(r,s-1),e=i&&e.push(null)),t>i&&this.right.decomposeLeft(t-i,e)}decomposeRight(t,e){let i=this.left.length,n=i+this.break;if(t>=n)return this.right.decomposeRight(t-n,e);t2*e.size||e.size>2*t.size?Un.of(this.break?[t,null,e]:[t,e]):(this.left=t,this.right=e,this.height=t.height+e.height,this.outdated=t.outdated||e.outdated,this.size=t.size+e.size,this.length=t.length+this.break+e.length,this)}updateHeight(t,e=0,i=!1,n){let{left:r,right:s}=this,o=e+r.length+this.break,l=null;return n&&n.from<=e+r.length&&n.more?l=r=r.updateHeight(t,e,i,n):r.updateHeight(t,e,i),n&&n.from<=o+s.length&&n.more?l=s=s.updateHeight(t,o,i,n):s.updateHeight(t,o,i),l?this.balanced(r,s):(this.height=this.left.height+this.right.height,this.outdated=!1,this)}toString(){return this.left+(this.break?" ":"-")+this.right}}function Zn(t,e){let i,n;null==t[e]&&(i=t[e-1])instanceof Yn&&(n=t[e+1])instanceof Yn&&t.splice(e-1,3,new Yn(i.length+1+n.length))}class tr{constructor(t,e){this.pos=t,this.oracle=e,this.nodes=[],this.lineStart=-1,this.lineEnd=-1,this.covering=null,this.writtenTo=t}get isCovered(){return this.covering&&this.nodes[this.nodes.length-1]==this.covering}span(t,e){if(this.lineStart>-1){let t=Math.min(e,this.lineEnd),i=this.nodes[this.nodes.length-1];i instanceof Xn?i.length+=t-this.pos:(t>this.pos||!this.isCovered)&&this.nodes.push(new Xn(t-this.pos,-1)),this.writtenTo=t,e>t&&(this.nodes.push(null),this.writtenTo++,this.lineStart=-1)}this.pos=e}point(t,e,i){if(t=5)&&this.addLineDeco(n,r)}else e>t&&this.span(t,e);this.lineEnd>-1&&this.lineEnd-1)return;let{from:t,to:e}=this.oracle.doc.lineAt(this.pos);this.lineStart=t,this.lineEnd=e,this.writtenTot&&this.nodes.push(new Xn(this.pos-t,-1)),this.writtenTo=this.pos}blankContent(t,e){let i=new Yn(e-t);return this.oracle.doc.lineAt(t).to==e&&(i.flags|=4),i}ensureLine(){this.enterLine();let t=this.nodes.length?this.nodes[this.nodes.length-1]:null;if(t instanceof Xn)return t;let e=new Xn(0,-1);return this.nodes.push(e),e}addBlock(t){this.enterLine(),t.type!=ei.WidgetAfter||this.isCovered||this.ensureLine(),this.nodes.push(t),this.writtenTo=this.pos=this.pos+t.length,t.type!=ei.WidgetBefore&&(this.covering=t)}addLineDeco(t,e){let i=this.ensureLine();i.length+=e,i.collapsed+=e,i.widgetHeight=Math.max(i.widgetHeight,t),this.writtenTo=this.pos=this.pos+e}finish(t){let e=0==this.nodes.length?null:this.nodes[this.nodes.length-1];!(this.lineStart>-1)||e instanceof Xn||this.isCovered?(this.writtenToi.clientHeight||i.scrollWidth>i.clientWidth)&&"visible"!=n.overflow){let n=i.getBoundingClientRect();s=Math.max(s,n.left),o=Math.min(o,n.right),l=Math.max(l,n.top),h=e==t.parentNode?n.bottom:Math.min(h,n.bottom)}e="absolute"==n.position||"fixed"==n.position?i.offsetParent:i.parentNode}else{if(11!=e.nodeType)break;e=e.host}return{left:s-i.left,right:Math.max(s,o)-i.left,top:l-(i.top+e),bottom:Math.max(l,h)-(i.top+e)}}function nr(t,e){let i=t.getBoundingClientRect();return{left:0,right:i.right-i.left,top:e,bottom:i.bottom-(i.top+e)}}class rr{constructor(t,e,i){this.from=t,this.to=e,this.size=i}static same(t,e){if(t.length!=e.length)return!1;for(let i=0;i"function"!=typeof t&&"cm-lineWrapping"==t.class));this.heightOracle=new _n(i),this.stateDeco=t.facet(Bi).filter((t=>"function"!=typeof t)),this.heightMap=Un.empty().applyChanges(this.stateDeco,e.empty,this.heightOracle.setDoc(t.doc),[new Pi(0,0,0,t.doc.length)]),this.viewport=this.getViewport(0,null),this.updateViewportLines(),this.updateForViewport(),this.lineGaps=this.ensureLineGaps([]),this.lineGapDeco=ii.set(this.lineGaps.map((t=>t.draw(!1)))),this.computeVisibleRanges()}updateForViewport(){let t=[this.viewport],{main:e}=this.state.selection;for(let i=0;i<=1;i++){let n=i?e.head:e.anchor;if(!t.some((({from:t,to:e})=>n>=t&&n<=e))){let{from:e,to:i}=this.lineBlockAt(n);t.push(new lr(e,i))}}this.viewports=t.sort(((t,e)=>t.from-e.from)),this.scaler=this.heightMap.height<=7e6?ur:new fr(this.heightOracle.doc,this.heightMap,this.viewports)}updateViewportLines(){this.viewportLines=[],this.heightMap.forEachLine(this.viewport.from,this.viewport.to,this.state.doc,0,0,(t=>{this.viewportLines.push(1==this.scaler.scale?t:dr(t,this.scaler))}))}update(t,e=null){this.state=t.state;let i=this.stateDeco;this.stateDeco=this.state.facet(Bi).filter((t=>"function"!=typeof t));let n=t.changedRanges,r=Pi.extendWithRanges(n,function(t,e,i){let n=new er;return Tt.compare(t,e,i,n,0),n.changes}(i,this.stateDeco,t?t.changes:A.empty(this.state.doc.length))),s=this.heightMap.height;this.heightMap=this.heightMap.applyChanges(this.stateDeco,t.startState.doc,this.heightOracle.setDoc(this.state.doc),r),this.heightMap.height!=s&&(t.flags|=2);let o=r.length?this.mapViewport(this.viewport,t.changes):this.viewport;(e&&(e.range.heado.to)||!this.viewportIsAppropriate(o))&&(o=this.getViewport(0,e));let l=!t.changes.empty||2&t.flags||o.from!=this.viewport.from||o.to!=this.viewport.to;this.viewport=o,this.updateForViewport(),l&&this.updateViewportLines(),(this.lineGaps.length||this.viewport.to-this.viewport.from>4e3)&&this.updateLineGaps(this.ensureLineGaps(this.mapLineGaps(this.lineGaps,t.changes))),t.flags|=this.computeVisibleRanges(),e&&(this.scrollTarget=e),!this.mustEnforceCursorAssoc&&t.selectionSet&&t.view.lineWrapping&&t.state.selection.main.empty&&t.state.selection.main.assoc&&!t.state.facet(bi)&&(this.mustEnforceCursorAssoc=!0)}measure(t){let i=t.contentDOM,n=window.getComputedStyle(i),r=this.heightOracle,s=n.whiteSpace;this.defaultTextDirection="rtl"==n.direction?Vi.RTL:Vi.LTR;let o=this.heightOracle.mustRefreshForWrapping(s),l=o||this.mustMeasureContent||this.contentDOMHeight!=i.clientHeight;this.contentDOMHeight=i.clientHeight,this.mustMeasureContent=!1;let h=0,a=0,c=parseInt(n.paddingTop)||0,u=parseInt(n.paddingBottom)||0;this.paddingTop==c&&this.paddingBottom==u||(this.paddingTop=c,this.paddingBottom=u,h|=10),this.editorWidth!=t.scrollDOM.clientWidth&&(r.lineWrapping&&(l=!0),this.editorWidth=t.scrollDOM.clientWidth,h|=8);let f=(this.printing?nr:ir)(i,this.paddingTop),d=f.top-this.pixelViewport.top,p=f.bottom-this.pixelViewport.bottom;this.pixelViewport=f;let g=this.pixelViewport.bottom>this.pixelViewport.top&&this.pixelViewport.right>this.pixelViewport.left;if(g!=this.inView&&(this.inView=g,g&&(l=!0)),!this.inView&&!this.scrollTarget)return 0;let m=i.clientWidth;if(this.contentDOMWidth==m&&this.editorHeight==t.scrollDOM.clientHeight||(this.contentDOMWidth=m,this.editorHeight=t.scrollDOM.clientHeight,h|=8),l){let i=t.docView.measureVisibleLineHeights(this.viewport);if(r.mustRefreshForHeights(i)&&(o=!0),o||r.lineWrapping&&Math.abs(m-this.contentDOMWidth)>r.charWidth){let{lineHeight:e,charWidth:n}=t.docView.measureTextSize();o=e>0&&r.refresh(s,e,n,m/n,i),o&&(t.docView.minWidth=0,h|=8)}d>0&&p>0?a=Math.max(d,p):d<0&&p<0&&(a=Math.min(d,p)),r.heightChanged=!1;for(let n of this.viewports){let s=n.from==this.viewport.from?i:t.docView.measureVisibleLineHeights(n);this.heightMap=(o?Un.empty().applyChanges(this.stateDeco,e.empty,this.heightOracle,[new Pi(0,0,0,t.state.doc.length)]):this.heightMap).updateHeight(r,0,o,new Kn(n.from,s))}r.heightChanged&&(h|=2)}let v=!this.viewportIsAppropriate(this.viewport,a)||this.scrollTarget&&(this.scrollTarget.range.headthis.viewport.to);return v&&(this.viewport=this.getViewport(a,this.scrollTarget)),this.updateForViewport(),(2&h||v)&&this.updateViewportLines(),(this.lineGaps.length||this.viewport.to-this.viewport.from>4e3)&&this.updateLineGaps(this.ensureLineGaps(o?[]:this.lineGaps,t)),h|=this.computeVisibleRanges(),this.mustEnforceCursorAssoc&&(this.mustEnforceCursorAssoc=!1,t.docView.enforceCursorAssoc()),h}get visibleTop(){return this.scaler.fromDOM(this.pixelViewport.top)}get visibleBottom(){return this.scaler.fromDOM(this.pixelViewport.bottom)}getViewport(t,e){let i=.5-Math.max(-.5,Math.min(.5,t/1e3/2)),n=this.heightMap,r=this.state.doc,{visibleTop:s,visibleBottom:o}=this,l=new lr(n.lineAt(s-1e3*i,$n.ByHeight,r,0,0).from,n.lineAt(o+1e3*(1-i),$n.ByHeight,r,0,0).to);if(e){let{head:t}=e.range;if(tl.to){let i,s=Math.min(this.editorHeight,this.pixelViewport.bottom-this.pixelViewport.top),o=n.lineAt(t,$n.ByPos,r,0,0);i="center"==e.y?(o.top+o.bottom)/2-s/2:"start"==e.y||"nearest"==e.y&&t=o+Math.max(10,Math.min(i,250)))&&n>s-2e3&&r>1,s=n<<1;if(this.defaultTextDirection!=Vi.LTR&&!i)return[];let o=[],l=(n,s,h,a)=>{if(s-nn&&tt.from>=h.from&&t.to<=h.to&&Math.abs(t.from-n)t.frome))));if(!f){if(st.from<=s&&t.to>=s))){let t=e.moveToLineBoundary(R.cursor(s),!1,!0).head;t>n&&(s=t)}f=new rr(n,s,this.gapSize(h,n,s,a))}o.push(f)};for(let t of this.viewportLines){if(t.lengtht.from&&l(t.from,r,t,e),ot.draw(this.heightOracle.lineWrapping)))))}computeVisibleRanges(){let t=this.stateDeco;this.lineGaps.length&&(t=t.concat(this.lineGapDeco));let e=[];Tt.spans(t,this.viewport.from,this.viewport.to,{span(t,i){e.push({from:t,to:i})},point(){}},20);let i=e.length!=this.visibleRanges.length||this.visibleRanges.some(((t,i)=>t.from!=e[i].from||t.to!=e[i].to));return this.visibleRanges=e,i?4:0}lineBlockAt(t){return t>=this.viewport.from&&t<=this.viewport.to&&this.viewportLines.find((e=>e.from<=t&&e.to>=t))||dr(this.heightMap.lineAt(t,$n.ByPos,this.state.doc,0,0),this.scaler)}lineBlockAtHeight(t){return dr(this.heightMap.lineAt(this.scaler.fromDOM(t),$n.ByHeight,this.state.doc,0,0),this.scaler)}elementAtHeight(t){return dr(this.heightMap.blockAt(this.scaler.fromDOM(t),this.state.doc,0,0),this.scaler)}get docHeight(){return this.scaler.toDOM(this.heightMap.height)}get contentHeight(){return this.docHeight+this.paddingTop+this.paddingBottom}}class lr{constructor(t,e){this.from=t,this.to=e}}function hr(t,e,i){let n=[],r=t,s=0;return Tt.spans(i,t,e,{span(){},point(t,e){t>r&&(n.push({from:r,to:t}),s+=t-r),r=e}},20),r=1)return e[e.length-1].to;let n=Math.floor(t*i);for(let t=0;;t++){let{from:i,to:r}=e[t],s=r-i;if(n<=s)return i+n;n-=s}}function cr(t,e){let i=0;for(let{from:n,to:r}of t.ranges){if(e<=r){i+=e-n;break}i+=r-n}return i/t.total}const ur={toDOM:t=>t,fromDOM:t=>t,scale:1};class fr{constructor(t,e,i){let n=0,r=0,s=0;this.viewports=i.map((({from:i,to:r})=>{let s=e.lineAt(i,$n.ByPos,t,0,0).top,o=e.lineAt(r,$n.ByPos,t,0,0).bottom;return n+=o-s,{from:i,to:r,top:s,bottom:o,domTop:0,domBottom:0}})),this.scale=(7e6-n)/(e.height-n);for(let t of this.viewports)t.domTop=s+(t.top-r)*this.scale,s=t.domBottom=t.domTop+(t.bottom-t.top),r=t.bottom}toDOM(t){for(let e=0,i=0,n=0;;e++){let r=edr(t,e))):t.type)}const pr=P.define({combine:t=>t.join(" ")}),gr=P.define({combine:t=>t.indexOf(!0)>-1}),mr=$t.newName(),vr=$t.newName(),wr=$t.newName(),yr={"&light":"."+vr,"&dark":"."+wr};function br(t,e,i){return new $t(e,{finish:e=>/&/.test(e)?e.replace(/&\w*/,(e=>{if("&"==e)return t;if(!i||!i[e])throw new RangeError(`Unsupported selector: ${e}`);return i[e]})):t+" "+e})}const xr=br("."+mr,{"&.cm-editor":{position:"relative !important",boxSizing:"border-box","&.cm-focused":{outline:"1px dotted #212121"},display:"flex !important",flexDirection:"column"},".cm-scroller":{display:"flex !important",alignItems:"flex-start !important",fontFamily:"monospace",lineHeight:1.4,height:"100%",overflowX:"auto",position:"relative",zIndex:0},".cm-content":{margin:0,flexGrow:2,flexShrink:0,minHeight:"100%",display:"block",whiteSpace:"pre",wordWrap:"normal",boxSizing:"border-box",padding:"4px 0",outline:"none","&[contenteditable=true]":{WebkitUserModify:"read-write-plaintext-only"}},".cm-lineWrapping":{whiteSpace_fallback:"pre-wrap",whiteSpace:"break-spaces",wordBreak:"break-word",overflowWrap:"anywhere",flexShrink:1},"&light .cm-content":{caretColor:"black"},"&dark .cm-content":{caretColor:"white"},".cm-line":{display:"block",padding:"0 2px 0 6px"},".cm-layer":{contain:"size style","& > *":{position:"absolute"}},"&light .cm-selectionBackground":{background:"#d9d9d9"},"&dark .cm-selectionBackground":{background:"#222"},"&light.cm-focused .cm-selectionBackground":{background:"#d7d4f0"},"&dark.cm-focused .cm-selectionBackground":{background:"#233"},".cm-cursorLayer":{pointerEvents:"none"},"&.cm-focused .cm-cursorLayer":{animation:"steps(1) cm-blink 1.2s infinite"},"@keyframes cm-blink":{"0%":{},"50%":{opacity:0},"100%":{}},"@keyframes cm-blink2":{"0%":{},"50%":{opacity:0},"100%":{}},".cm-cursor, .cm-dropCursor":{borderLeft:"1.2px solid black",marginLeft:"-0.6px",pointerEvents:"none"},".cm-cursor":{display:"none"},"&dark .cm-cursor":{borderLeftColor:"#444"},"&.cm-focused .cm-cursor":{display:"block"},"&light .cm-activeLine":{backgroundColor:"#cceeff44"},"&dark .cm-activeLine":{backgroundColor:"#99eeff33"},"&light .cm-specialChar":{color:"red"},"&dark .cm-specialChar":{color:"#f78"},".cm-gutters":{flexShrink:0,display:"flex",height:"100%",boxSizing:"border-box",left:0,zIndex:200},"&light .cm-gutters":{backgroundColor:"#f5f5f5",color:"#6c6c6c",borderRight:"1px solid #ddd"},"&dark .cm-gutters":{backgroundColor:"#333338",color:"#ccc"},".cm-gutter":{display:"flex !important",flexDirection:"column",flexShrink:0,boxSizing:"border-box",minHeight:"100%",overflow:"hidden"},".cm-gutterElement":{boxSizing:"border-box"},".cm-lineNumbers .cm-gutterElement":{padding:"0 3px 0 5px",minWidth:"20px",textAlign:"right",whiteSpace:"nowrap"},"&light .cm-activeLineGutter":{backgroundColor:"#e2f2ff"},"&dark .cm-activeLineGutter":{backgroundColor:"#222227"},".cm-panels":{boxSizing:"border-box",position:"sticky",left:0,right:0},"&light .cm-panels":{backgroundColor:"#f5f5f5",color:"black"},"&light .cm-panels-top":{borderBottom:"1px solid #ddd"},"&light .cm-panels-bottom":{borderTop:"1px solid #ddd"},"&dark .cm-panels":{backgroundColor:"#333338",color:"white"},".cm-tab":{display:"inline-block",overflow:"hidden",verticalAlign:"bottom"},".cm-widgetBuffer":{verticalAlign:"text-top",height:"1em",width:0,display:"inline"},".cm-placeholder":{color:"#888",display:"inline-block",verticalAlign:"top"},".cm-button":{verticalAlign:"middle",color:"inherit",fontSize:"70%",padding:".2em 1em",borderRadius:"1px"},"&light .cm-button":{backgroundImage:"linear-gradient(#eff1f5, #d9d9df)",border:"1px solid #888","&:active":{backgroundImage:"linear-gradient(#b4b4b4, #d0d3d6)"}},"&dark .cm-button":{backgroundImage:"linear-gradient(#393939, #111)",border:"1px solid #888","&:active":{backgroundImage:"linear-gradient(#111, #333)"}},".cm-textfield":{verticalAlign:"middle",color:"inherit",fontSize:"70%",border:"1px solid silver",padding:".2em .5em"},"&light .cm-textfield":{backgroundColor:"white"},"&dark .cm-textfield":{border:"1px solid #555",backgroundColor:"inherit"}},yr);class kr{constructor(t,e,i,n){this.typeOver=n,this.bounds=null,this.text="";let{impreciseHead:r,impreciseAnchor:s}=t.docView;if(t.state.readOnly&&e>-1)this.newSel=null;else if(e>-1&&(this.bounds=t.docView.domBoundsAround(e,i,0))){let e=r||s?[]:function(t){let e=[];if(t.root.activeElement!=t.contentDOM)return e;let{anchorNode:i,anchorOffset:n,focusNode:r,focusOffset:s}=t.observer.selectionRange;i&&(e.push(new tn(i,n)),r==i&&s==n||e.push(new tn(r,s)));return e}(t),i=new Qi(e,t.state);i.readRange(this.bounds.startDOM,this.bounds.endDOM),this.text=i.text,this.newSel=function(t,e){if(0==t.length)return null;let i=t[0].pos,n=2==t.length?t[1].pos:i;return i>-1&&n>-1?R.single(i+e,n+e):null}(e,this.bounds.from)}else{let e=t.observer.selectionRange,i=r&&r.node==e.focusNode&&r.offset==e.focusOffset||!re(t.contentDOM,e.focusNode)?t.state.selection.main.head:t.docView.posFromDOM(e.focusNode,e.focusOffset),n=s&&s.node==e.anchorNode&&s.offset==e.anchorOffset||!re(t.contentDOM,e.anchorNode)?t.state.selection.main.anchor:t.docView.posFromDOM(e.anchorNode,e.anchorOffset);this.newSel=R.single(n,i)}}}function Sr(t,i){let n,{newSel:r}=i,s=t.state.selection.main;if(i.bounds){let{from:r,to:o}=i.bounds,l=s.from,h=null;(8===t.inputState.lastKeyCode&&t.inputState.lastKeyTime>Date.now()-100||He.android&&i.text.length0&&l>0&&t.charCodeAt(o-1)==e.charCodeAt(l-1);)o--,l--;if("end"==n){i-=o+Math.max(0,s-Math.min(o,l))-s}if(o=o?s-i:0,l=s+(l-o),o=s}else if(l=l?s-i:0,o=s+(o-l),l=s}return{from:s,toA:o,toB:l}}(t.state.doc.sliceString(r,o,Yi),i.text,l-r,h);a&&(He.chrome&&13==t.inputState.lastKeyCode&&a.toB==a.from+2&&i.text.slice(a.from,a.toB)==Yi+Yi&&a.toB--,n={from:r+a.from,to:r+a.toA,insert:e.of(i.text.slice(a.from,a.toB).split(Yi))})}else!r||t.hasFocus&&t.state.facet(Ai)&&!r.main.eq(s)||(r=null);if(!n&&!r)return!1;if(!n&&i.typeOver&&!s.empty&&r&&r.main.empty?n={from:s.from,to:s.to,insert:t.state.doc.slice(s.from,s.to)}:n&&n.from>=s.from&&n.to<=s.to&&(n.from!=s.from||n.to!=s.to)&&s.to-s.from-(n.to-n.from)<=4?n={from:s.from,to:s.to,insert:t.state.doc.slice(s.from,n.from).append(n.insert).append(t.state.doc.slice(n.to,s.to))}:(He.mac||He.android)&&n&&n.from==n.to&&n.from==s.head-1&&/^\. ?$/.test(n.insert.toString())?(r&&2==n.insert.length&&(r=R.single(r.main.anchor-1,r.main.head-1)),n={from:s.from,to:s.to,insert:e.of([" "])}):He.chrome&&n&&n.from==n.to&&n.from==s.head&&"\n "==n.insert.toString()&&t.lineWrapping&&(r&&(r=R.single(r.main.anchor-1,r.main.head-1)),n={from:s.from,to:s.to,insert:e.of([" "])}),n){let e=t.state;if(He.ios&&t.inputState.flushIOSKey(t))return!0;if(He.android&&(n.from==s.from&&n.to==s.to&&1==n.insert.length&&2==n.insert.lines&&ye(t.contentDOM,"Enter",13)||n.from==s.from-1&&n.to==s.to&&0==n.insert.length&&ye(t.contentDOM,"Backspace",8)||n.from==s.from&&n.to==s.to+1&&0==n.insert.length&&ye(t.contentDOM,"Delete",46)))return!0;let i,o=n.insert.toString();if(t.state.facet(wi).some((e=>e(t,n.from,n.to,o))))return!0;if(t.inputState.composing>=0&&t.inputState.composing++,n.from>=s.from&&n.to<=s.to&&n.to-n.from>=(s.to-s.from)/3&&(!r||r.main.empty&&r.main.from==n.from+n.insert.length)&&t.inputState.composing<0){let r=s.fromn.to?e.sliceDoc(n.to,s.to):"";i=e.replaceSelection(t.state.toText(r+n.insert.sliceString(0,void 0,t.state.lineBreak)+o))}else{let o=e.changes(n),l=r&&!e.selection.main.eq(r.main)&&r.main.to<=o.newLength?r.main:void 0;if(e.selection.ranges.length>1&&t.inputState.composing>=0&&n.to<=s.to&&n.to>=s.to-10){let r=t.state.sliceDoc(n.from,n.to),h=rn(t)||t.state.doc.lineAt(s.head),a=s.to-n.to,c=s.to-s.from;i=e.changeByRange((i=>{if(i.from==s.from&&i.to==s.to)return{changes:o,range:l||i.map(o)};let u=i.to-a,f=u-r.length;if(i.to-i.from!=c||t.state.sliceDoc(f,u)!=r||h&&i.to>=h.from&&i.from<=h.to)return{range:i};let d=e.changes({from:f,to:u,insert:n.insert}),p=i.to-s.to;return{changes:d,range:l?R.range(Math.max(0,l.anchor+p),Math.max(0,l.head+p)):i.map(d)}}))}else i={changes:o,selection:l&&e.selection.replaceRange(l)}}let l="input.type";return t.composing&&(l+=".compose",t.inputState.compositionFirstChange&&(l+=".start",t.inputState.compositionFirstChange=!1)),t.dispatch(i,{scrollIntoView:!0,userEvent:l}),!0}if(r&&!r.main.eq(s)){let e=!1,i="select";return t.inputState.lastSelectionTime>Date.now()-50&&("select"==t.inputState.lastSelectionOrigin&&(e=!0),i=t.inputState.lastSelectionOrigin),t.dispatch({selection:r,scrollIntoView:e,userEvent:i}),!0}return!1}const Ar={childList:!0,characterData:!0,subtree:!0,attributes:!0,characterDataOldValue:!0},Mr=He.ie&&He.ie_version<=11;class Cr{constructor(t){this.view=t,this.active=!1,this.selectionRange=new pe,this.selectionChanged=!1,this.delayedFlush=-1,this.resizeTimeout=-1,this.queue=[],this.delayedAndroidKey=null,this.flushingAndroidKey=-1,this.lastChange=0,this.scrollTargets=[],this.intersection=null,this.resize=null,this.intersecting=!1,this.gapIntersection=null,this.gaps=[],this.parentCheck=-1,this.dom=t.contentDOM,this.observer=new MutationObserver((e=>{for(let t of e)this.queue.push(t);(He.ie&&He.ie_version<=11||He.ios&&t.composing)&&e.some((t=>"childList"==t.type&&t.removedNodes.length||"characterData"==t.type&&t.oldValue.length>t.target.nodeValue.length))?this.flushSoon():this.flush()})),Mr&&(this.onCharData=t=>{this.queue.push({target:t.target,type:"characterData",oldValue:t.prevValue}),this.flushSoon()}),this.onSelectionChange=this.onSelectionChange.bind(this),this.onResize=this.onResize.bind(this),this.onPrint=this.onPrint.bind(this),this.onScroll=this.onScroll.bind(this),"function"==typeof ResizeObserver&&(this.resize=new ResizeObserver((()=>{var t;(null===(t=this.view.docView)||void 0===t?void 0:t.lastUpdate){this.parentCheck<0&&(this.parentCheck=setTimeout(this.listenForScroll.bind(this),1e3)),t.length>0&&t[t.length-1].intersectionRatio>0!=this.intersecting&&(this.intersecting=!this.intersecting,this.intersecting!=this.view.inView&&this.onScrollChanged(document.createEvent("Event")))}),{}),this.intersection.observe(this.dom),this.gapIntersection=new IntersectionObserver((t=>{t.length>0&&t[t.length-1].intersectionRatio>0&&this.onScrollChanged(document.createEvent("Event"))}),{})),this.listenForScroll(),this.readSelectionRange()}onScrollChanged(t){this.view.inputState.runScrollHandlers(this.view,t),this.intersecting&&this.view.measure()}onScroll(t){this.intersecting&&this.flush(!1),this.onScrollChanged(t)}onResize(){this.resizeTimeout<0&&(this.resizeTimeout=setTimeout((()=>{this.resizeTimeout=-1,this.view.requestMeasure()}),50))}onPrint(){this.view.viewState.printing=!0,this.view.measure(),setTimeout((()=>{this.view.viewState.printing=!1,this.view.requestMeasure()}),500)}updateGaps(t){if(this.gapIntersection&&(t.length!=this.gaps.length||this.gaps.some(((e,i)=>e!=t[i])))){this.gapIntersection.disconnect();for(let e of t)this.gapIntersection.observe(e);this.gaps=t}}onSelectionChange(t){let e=this.selectionChanged;if(!this.readSelectionRange()||this.delayedAndroidKey)return;let{view:i}=this,n=this.selectionRange;if(i.state.facet(Ai)?i.root.activeElement!=this.dom:!se(i.dom,n))return;let r=n.anchorNode&&i.docView.nearest(n.anchorNode);r&&r.ignoreEvent(t)?e||(this.selectionChanged=!1):(He.ie&&He.ie_version<=11||He.android&&He.chrome)&&!i.state.selection.main.empty&&n.focusNode&&le(n.focusNode,n.focusOffset,n.anchorNode,n.anchorOffset)?this.flushSoon():this.flush(!1)}readSelectionRange(){let{view:t}=this,e=He.safari&&11==t.root.nodeType&&function(t){let e=t.activeElement;for(;e&&e.shadowRoot;)e=e.shadowRoot.activeElement;return e}(this.dom.ownerDocument)==this.dom&&function(t){let e=null;function i(t){t.preventDefault(),t.stopImmediatePropagation(),e=t.getTargetRanges()[0]}if(t.contentDOM.addEventListener("beforeinput",i,!0),t.dom.ownerDocument.execCommand("indent"),t.contentDOM.removeEventListener("beforeinput",i,!0),!e)return null;let n=e.startContainer,r=e.startOffset,s=e.endContainer,o=e.endOffset,l=t.docView.domAtPos(t.state.selection.main.anchor);le(l.node,l.offset,s,o)&&([n,r,s,o]=[s,o,n,r]);return{anchorNode:n,anchorOffset:r,focusNode:s,focusOffset:o}}(this.view)||ne(t.root);if(!e||this.selectionRange.eq(e))return!1;let i=se(this.dom,e);return i&&!this.selectionChanged&&t.inputState.lastFocusTime>Date.now()-200&&t.inputState.lastTouchTime{let t=this.delayedAndroidKey;t&&(this.clearDelayedAndroidKey(),!this.flush()&&t.force&&ye(this.dom,t.key,t.keyCode))};this.flushingAndroidKey=this.view.win.requestAnimationFrame(t)}this.delayedAndroidKey&&"Enter"!=t||(this.delayedAndroidKey={key:t,keyCode:e,force:this.lastChange{this.delayedFlush=-1,this.flush()})))}forceFlush(){this.delayedFlush>=0&&(this.view.win.cancelAnimationFrame(this.delayedFlush),this.delayedFlush=-1),this.flush()}processRecords(){let t=this.queue;for(let e of this.observer.takeRecords())t.push(e);t.length&&(this.queue=[]);let e=-1,i=-1,n=!1;for(let r of t){let t=this.readMutation(r);t&&(t.typeOver&&(n=!0),-1==e?({from:e,to:i}=t):(e=Math.min(t.from,e),i=Math.max(t.to,i)))}return{from:e,to:i,typeOver:n}}readChange(){let{from:t,to:e,typeOver:i}=this.processRecords(),n=this.selectionChanged&&se(this.dom,this.selectionRange);return t<0&&!n?null:(t>-1&&(this.lastChange=Date.now()),this.view.inputState.lastFocusTime=0,this.selectionChanged=!1,new kr(this.view,t,e,i))}flush(t=!0){if(this.delayedFlush>=0||this.delayedAndroidKey)return!1;t&&this.readSelectionRange();let e=this.readChange();if(!e)return!1;let i=this.view.state,n=Sr(this.view,e);return this.view.state==i&&this.view.update([]),n}readMutation(t){let e=this.view.docView.nearest(t.target);if(!e||e.ignoreMutation(t))return null;if(e.markDirty("attributes"==t.type),"attributes"==t.type&&(e.dirty|=4),"childList"==t.type){let i=Dr(e,t.previousSibling||t.target.previousSibling,-1),n=Dr(e,t.nextSibling||t.target.nextSibling,1);return{from:i?e.posAfter(i):e.posAtStart,to:n?e.posBefore(n):e.posAtEnd,typeOver:!1}}return"characterData"==t.type?{from:e.posAtStart,to:e.posAtEnd,typeOver:t.target.nodeValue==t.oldValue}:null}setWindow(t){t!=this.win&&(this.removeWindowListeners(this.win),this.win=t,this.addWindowListeners(this.win))}addWindowListeners(t){t.addEventListener("resize",this.onResize),t.addEventListener("beforeprint",this.onPrint),t.addEventListener("scroll",this.onScroll),t.document.addEventListener("selectionchange",this.onSelectionChange)}removeWindowListeners(t){t.removeEventListener("scroll",this.onScroll),t.removeEventListener("resize",this.onResize),t.removeEventListener("beforeprint",this.onPrint),t.document.removeEventListener("selectionchange",this.onSelectionChange)}destroy(){var t,e,i;this.stop(),null===(t=this.intersection)||void 0===t||t.disconnect(),null===(e=this.gapIntersection)||void 0===e||e.disconnect(),null===(i=this.resize)||void 0===i||i.disconnect();for(let t of this.scrollTargets)t.removeEventListener("scroll",this.onScroll);this.removeWindowListeners(this.win),clearTimeout(this.parentCheck),clearTimeout(this.resizeTimeout),this.win.cancelAnimationFrame(this.delayedFlush),this.win.cancelAnimationFrame(this.flushingAndroidKey)}}function Dr(t,e,i){for(;e;){let n=Se.get(e);if(n&&n.parent==t)return n;let r=e.parentNode;e=r!=t.dom?r:i>0?e.nextSibling:e.previousSibling}return null}class Or{constructor(t={}){this.plugins=[],this.pluginMap=new Map,this.editorAttrs={},this.contentAttrs={},this.bidiCache=[],this.destroyed=!1,this.updateState=2,this.measureScheduled=-1,this.measureRequests=[],this.contentDOM=document.createElement("div"),this.scrollDOM=document.createElement("div"),this.scrollDOM.tabIndex=-1,this.scrollDOM.className="cm-scroller",this.scrollDOM.appendChild(this.contentDOM),this.announceDOM=document.createElement("div"),this.announceDOM.style.cssText="position: absolute; top: -10000px",this.announceDOM.setAttribute("aria-live","polite"),this.dom=document.createElement("div"),this.dom.appendChild(this.announceDOM),this.dom.appendChild(this.scrollDOM),this._dispatch=t.dispatch||(t=>this.update([t])),this.dispatch=this.dispatch.bind(this),this._root=t.root||function(t){for(;t;){if(t&&(9==t.nodeType||11==t.nodeType&&t.host))return t;t=t.assignedSlot||t.parentNode}return null}(t.parent)||document,this.viewState=new or(t.state||St.create(t)),this.plugins=this.state.facet(Ci).map((t=>new Oi(t)));for(let t of this.plugins)t.update(this);this.observer=new Cr(this),this.inputState=new yn(this),this.inputState.ensureHandlers(this,this.plugins),this.docView=new en(this),this.mountStyles(),this.updateAttrs(),this.updateState=0,this.requestMeasure(),t.parent&&t.parent.appendChild(this.dom)}get state(){return this.viewState.state}get viewport(){return this.viewState.viewport}get visibleRanges(){return this.viewState.visibleRanges}get inView(){return this.viewState.inView}get composing(){return this.inputState.composing>0}get compositionStarted(){return this.inputState.composing>=0}get root(){return this._root}get win(){return this.dom.ownerDocument.defaultView||window}dispatch(...t){this._dispatch(1==t.length&&t[0]instanceof ft?t[0]:this.state.update(...t))}update(t){if(0!=this.updateState)throw new Error("Calls to EditorView.update are not allowed while an update is in progress");let e,i=!1,n=!1,r=this.state;for(let e of t){if(e.startState!=r)throw new RangeError("Trying to update state with a transaction that doesn't start from the previous state.");r=e.state}if(this.destroyed)return void(this.viewState.state=r);let s=this.observer.delayedAndroidKey,o=null;if(s?(this.observer.clearDelayedAndroidKey(),o=this.observer.readChange(),(o&&!this.state.doc.eq(r.doc)||!this.state.selection.eq(r.selection))&&(o=null)):this.observer.clear(),r.facet(St.phrases)!=this.state.facet(St.phrases))return this.setState(r);e=Ii.create(this,r,t);let l=this.viewState.scrollTarget;try{this.updateState=2;for(let e of t){if(l&&(l=l.map(e.changes)),e.scrollIntoView){let{main:t}=e.state.selection;l=new xi(t.empty?t:R.cursor(t.head,t.head>t.anchor?-1:1))}for(let t of e.effects)t.is(ki)&&(l=t.value)}this.viewState.update(e,l),this.bidiCache=Br.update(this.bidiCache,e.changes),e.empty||(this.updatePlugins(e),this.inputState.update(e)),i=this.docView.update(e),this.state.facet(Ni)!=this.styleModules&&this.mountStyles(),n=this.updateAttrs(),this.showAnnouncements(t),this.docView.updateSelection(i,t.some((t=>t.isUserEvent("select.pointer"))))}finally{this.updateState=0}if(e.startState.facet(pr)!=e.state.facet(pr)&&(this.viewState.mustMeasureContent=!0),(i||n||l||this.viewState.mustEnforceCursorAssoc||this.viewState.mustMeasureContent)&&this.requestMeasure(),!e.empty)for(let t of this.state.facet(vi))t(e);o&&!Sr(this,o)&&s.force&&ye(this.contentDOM,s.key,s.keyCode)}setState(t){if(0!=this.updateState)throw new Error("Calls to EditorView.setState are not allowed while an update is in progress");if(this.destroyed)return void(this.viewState.state=t);this.updateState=2;let e=this.hasFocus;try{for(let t of this.plugins)t.destroy(this);this.viewState=new or(t),this.plugins=t.facet(Ci).map((t=>new Oi(t))),this.pluginMap.clear();for(let t of this.plugins)t.update(this);this.docView=new en(this),this.inputState.ensureHandlers(this,this.plugins),this.mountStyles(),this.updateAttrs(),this.bidiCache=[]}finally{this.updateState=0}e&&this.focus(),this.requestMeasure()}updatePlugins(t){let e=t.startState.facet(Ci),i=t.state.facet(Ci);if(e!=i){let n=[];for(let r of i){let i=e.indexOf(r);if(i<0)n.push(new Oi(r));else{let e=this.plugins[i];e.mustUpdate=t,n.push(e)}}for(let e of this.plugins)e.mustUpdate!=t&&e.destroy(this);this.plugins=n,this.pluginMap.clear(),this.inputState.ensureHandlers(this,this.plugins)}else for(let e of this.plugins)e.mustUpdate=t;for(let t=0;t-1&&cancelAnimationFrame(this.measureScheduled),this.measureScheduled=0,t&&this.observer.forceFlush();let e=null,{scrollHeight:i,scrollTop:n,clientHeight:r}=this.scrollDOM,s=n>i-r-4?i:n;try{for(let t=0;;t++){this.updateState=1;let i=this.viewport,n=this.viewState.lineBlockAtHeight(s),r=this.viewState.measure(this);if(!r&&!this.measureRequests.length&&null==this.viewState.scrollTarget)break;if(t>5){console.warn(this.measureRequests.length?"Measure loop restarted more than 5 times":"Viewport failed to stabilize");break}let o=[];4&r||([this.measureRequests,o]=[o,this.measureRequests]);let l=o.map((t=>{try{return t.read(this)}catch(t){return Si(this.state,t),Er}})),h=Ii.create(this,this.state,[]),a=!1,c=!1;h.flags|=r,e?e.flags|=r:e=h,this.updateState=2,h.empty||(this.updatePlugins(h),this.inputState.update(h),this.updateAttrs(),a=this.docView.update(h));for(let t=0;t1||t<-1)&&(this.scrollDOM.scrollTop+=t,c=!0)}if(a&&this.docView.updateSelection(!0),this.viewport.from==i.from&&this.viewport.to==i.to&&!c&&0==this.measureRequests.length)break}}finally{this.updateState=0,this.measureScheduled=-1}if(e&&!e.empty)for(let t of this.state.facet(vi))t(e)}get themeClasses(){return mr+" "+(this.state.facet(gr)?wr:vr)+" "+this.state.facet(pr)}updateAttrs(){let t=Rr(this,Ti,{class:"cm-editor"+(this.hasFocus?" cm-focused ":" ")+this.themeClasses}),e={spellcheck:"false",autocorrect:"off",autocapitalize:"off",translate:"no",contenteditable:this.state.facet(Ai)?"true":"false",class:"cm-content",style:`${He.tabSize}: ${this.state.tabSize}`,role:"textbox","aria-multiline":"true"};this.state.readOnly&&(e["aria-readonly"]="true"),Rr(this,Ei,e);let i=this.observer.ignore((()=>{let i=Ze(this.contentDOM,this.contentAttrs,e),n=Ze(this.dom,this.editorAttrs,t);return i||n}));return this.editorAttrs=t,this.contentAttrs=e,i}showAnnouncements(t){let e=!0;for(let i of t)for(let t of i.effects)if(t.is(Or.announce)){e&&(this.announceDOM.textContent=""),e=!1,this.announceDOM.appendChild(document.createElement("div")).textContent=t.value}}mountStyles(){this.styleModules=this.state.facet(Ni),$t.mount(this.root,this.styleModules.concat(xr).reverse())}readMeasured(){if(2==this.updateState)throw new Error("Reading the editor layout isn't allowed during an update");0==this.updateState&&this.measureScheduled>-1&&this.measure(!1)}requestMeasure(t){if(this.measureScheduled<0&&(this.measureScheduled=this.win.requestAnimationFrame((()=>this.measure()))),t){if(null!=t.key)for(let e=0;ee.spec==t))||null),e&&e.update(this).value}get documentTop(){return this.contentDOM.getBoundingClientRect().top+this.viewState.paddingTop}get documentPadding(){return{top:this.viewState.paddingTop,bottom:this.viewState.paddingBottom}}elementAtHeight(t){return this.readMeasured(),this.viewState.elementAtHeight(t)}lineBlockAtHeight(t){return this.readMeasured(),this.viewState.lineBlockAtHeight(t)}get viewportLineBlocks(){return this.viewState.viewportLines}lineBlockAt(t){return this.viewState.lineBlockAt(t)}get contentHeight(){return this.viewState.contentHeight}moveByChar(t,e,i){return wn(this,t,vn(this,t,e,i))}moveByGroup(t,e){return wn(this,t,vn(this,t,e,(e=>function(t,e,i){let n=t.state.charCategorizer(e),r=n(i);return t=>{let e=n(t);return r==yt.Space&&(r=e),r==e}}(this,t.head,e))))}moveToLineBoundary(t,e,i=!0){return function(t,e,i,n){let r=t.state.doc.lineAt(e.head),s=n&&t.lineWrapping?t.coordsAtPos(e.assoc<0&&e.head>r.from?e.head-1:e.head):null;if(s){let e=t.dom.getBoundingClientRect(),n=t.textDirectionAt(r.from),o=t.posAtCoords({x:i==(n==Vi.LTR)?e.right-1:e.left+1,y:(s.top+s.bottom)/2});if(null!=o)return R.cursor(o,i?-1:1)}let o=hi.find(t.docView,e.head),l=o?i?o.posAtEnd:o.posAtStart:i?r.to:r.from;return R.cursor(l,i?-1:1)}(this,t,e,i)}moveVertically(t,e,i){return wn(this,t,function(t,e,i,n){let r=e.head,s=i?1:-1;if(r==(i?t.state.doc.length:0))return R.cursor(r,e.assoc);let o,l=e.goalColumn,h=t.contentDOM.getBoundingClientRect(),a=t.coordsAtPos(r),c=t.documentTop;if(a)null==l&&(l=a.left-h.left),o=s<0?a.top:a.bottom;else{let e=t.viewState.lineBlockAt(r);null==l&&(l=Math.min(h.right-h.left,t.defaultCharacterWidth*(r-e.from))),o=(s<0?e.top:e.bottom)+c}let u=h.left+l,f=null!=n?n:t.defaultLineHeight>>1;for(let i=0;;i+=10){let n=o+(f+i)*s,a=gn(t,{x:u,y:n},!1,s);if(nh.bottom||(s<0?ar))return R.cursor(a,e.assoc,void 0,l)}}(this,t,e,i))}domAtPos(t){return this.docView.domAtPos(t)}posAtDOM(t,e=0){return this.docView.posFromDOM(t,e)}posAtCoords(t,e=!0){return this.readMeasured(),gn(this,t,e)}coordsAtPos(t,e=1){this.readMeasured();let i=this.docView.coordsAt(t,e);if(!i||i.left==i.right)return i;let n=this.state.doc.lineAt(t),r=this.bidiSpans(n);return fe(i,r[$i.find(r,t-n.from,-1,e)].dir==Vi.LTR==e>0)}get defaultCharacterWidth(){return this.viewState.heightOracle.charWidth}get defaultLineHeight(){return this.viewState.heightOracle.lineHeight}get textDirection(){return this.viewState.defaultTextDirection}textDirectionAt(t){return!this.state.facet(yi)||tthis.viewport.to?this.textDirection:(this.readMeasured(),this.docView.textDirectionAt(t))}get lineWrapping(){return this.viewState.heightOracle.lineWrapping}bidiSpans(t){if(t.length>Tr)return Ui(t.length);let e=this.textDirectionAt(t.from);for(let i of this.bidiCache)if(i.from==t.from&&i.dir==e)return i.order;let i=function(t,e){let i=t.length,n=e==Wi?1:2,r=e==Wi?2:1;if(!t||1==n&&!ji.test(t))return Ui(i);for(let e=0,r=n,o=n;e=0;t-=3)if(Ki[t+1]==-s){let e=Ki[t+2],i=2&e?n:4&e?1&e?r:n:0;i&&(Gi[l]=Gi[Ki[t]]=i),h=t;break}}else{if(189==Ki.length)break;Ki[h++]=l,Ki[h++]=e,Ki[h++]=a}else if(2==(o=Gi[l])||1==o){let t=o==n;a=t?0:1;for(let e=h-3;e>=0;e-=3){let i=Ki[e+2];if(2&i)break;if(t)Ki[e+2]|=2;else{if(4&i)break;Ki[e+2]|=4}}}for(let t=0;te;){let t=i,n=2!=Gi[--i];for(;i>e&&n==(2!=Gi[i-1]);)i--;o.push(new $i(i,t,n?2:1))}else o.push(new $i(e,t,0))}else for(let t=0;tDate.now()-3e4)&&this.root.activeElement==this.contentDOM}focus(){this.observer.ignore((()=>{ve(this.contentDOM),this.docView.updateSelection()}))}setRoot(t){this._root!=t&&(this._root=t,this.observer.setWindow((9==t.nodeType?t:t.ownerDocument).defaultView||window),this.mountStyles())}destroy(){for(let t of this.plugins)t.destroy(this);this.plugins=[],this.inputState.destroy(),this.dom.remove(),this.observer.destroy(),this.measureScheduled>-1&&cancelAnimationFrame(this.measureScheduled),this.destroyed=!0}static scrollIntoView(t,e={}){return ki.of(new xi("number"==typeof t?R.cursor(t):t,e.y,e.x,e.yMargin,e.xMargin))}static domEventHandlers(t){return Di.define((()=>({})),{eventHandlers:t})}static theme(t,e){let i=$t.newName(),n=[pr.of(i),Ni.of(br(`.${i}`,t))];return e&&e.dark&&n.push(gr.of(!0)),n}static baseTheme(t){return U.lowest(Ni.of(br("."+mr,t,yr)))}static findFromDOM(t){var e;let i=t.querySelector(".cm-content"),n=i&&Se.get(i)||Se.get(t);return(null===(e=null==n?void 0:n.rootView)||void 0===e?void 0:e.view)||null}}Or.styleModule=Ni,Or.inputHandler=wi,Or.perLineTextDirection=yi,Or.exceptionSink=mi,Or.updateListener=vi,Or.editable=Ai,Or.mouseSelectionStyle=gi,Or.dragMovesSelection=pi,Or.clickAddsSelectionRange=di,Or.decorations=Bi,Or.atomicRanges=Ri,Or.scrollMargins=Li,Or.darkTheme=gr,Or.contentAttributes=Ei,Or.editorAttributes=Ti,Or.lineWrapping=Or.contentAttributes.of({class:"cm-lineWrapping"}),Or.announce=ut.define();const Tr=4096,Er={};class Br{constructor(t,e,i,n){this.from=t,this.to=e,this.dir=i,this.order=n}static update(t,e){if(e.empty)return t;let i=[],n=t.length?t[t.length-1].dir:Vi.LTR;for(let r=Math.max(0,t.length-10);r=0;r--){let e=n[r],s="function"==typeof e?e(t):e;s&&Ye(s,i)}return i}const Lr=He.mac?"mac":He.windows?"win":He.linux?"linux":"key";function Nr(t,e,i){return e.altKey&&(t="Alt-"+t),e.ctrlKey&&(t="Ctrl-"+t),e.metaKey&&(t="Meta-"+t),!1!==i&&e.shiftKey&&(t="Shift-"+t),t}const Pr=P.define({enables:U.default(Or.domEventHandlers({keydown:(t,e)=>Hr(Vr(e.state),t,e,"editor")}))}),Ir=new WeakMap;function Vr(t){let e=t.facet(Pr),i=Ir.get(e);return i||Ir.set(e,i=function(t,e=Lr){let i=Object.create(null),n=Object.create(null),r=(t,e)=>{let i=n[t];if(null==i)n[t]=e;else if(i!=e)throw new Error("Key binding "+t+" is used both as a regular binding and as a multi-stroke prefix")},s=(t,n,s,o)=>{var l,h;let a=i[t]||(i[t]=Object.create(null)),c=n.split(/ (?!$)/).map((t=>function(t,e){const i=t.split(/-(?!$)/);let n,r,s,o,l=i[i.length-1];"Space"==l&&(l=" ");for(let t=0;t{let n=Wr={view:e,prefix:i,scope:t};return setTimeout((()=>{Wr==n&&(Wr=null)}),4e3),!0}]})}let u=c.join(" ");r(u,!1);let f=a[u]||(a[u]={preventDefault:!1,run:(null===(h=null===(l=a._any)||void 0===l?void 0:l.run)||void 0===h?void 0:h.slice())||[]});s&&f.run.push(s),o&&(f.preventDefault=!0)};for(let n of t){let t=n.scope?n.scope.split(" "):["editor"];if(n.any)for(let e of t){let t=i[e]||(i[e]=Object.create(null));t._any||(t._any={preventDefault:!1,run:[]});for(let e in t)t[e].run.push(n.any)}let r=n[e]||n.key;if(r)for(let e of t)s(e,r,n.run,n.preventDefault),n.shift&&s(e,"Shift-"+r,n.shift,n.preventDefault)}return i}(e.reduce(((t,e)=>t.concat(e)),[]))),i}let Wr=null;function Hr(t,e,i,n){let r=function(t){var e=!(te&&(t.ctrlKey||t.altKey||t.metaKey)||Zt&&t.shiftKey&&t.key&&1==t.key.length||"Unidentified"==t.key)&&t.key||(t.shiftKey?Xt:Jt)[t.keyCode]||t.key||"Unidentified";return"Esc"==e&&(e="Escape"),"Del"==e&&(e="Delete"),"Left"==e&&(e="ArrowLeft"),"Up"==e&&(e="ArrowUp"),"Right"==e&&(e="ArrowRight"),"Down"==e&&(e="ArrowDown"),e}(e),s=b(w(r,0))==r.length&&" "!=r,o="",l=!1;Wr&&Wr.view==i&&Wr.scope==n&&(o=Wr.prefix+" ",(l=kn.indexOf(e.keyCode)<0)&&(Wr=null));let h,a,c=new Set,u=t=>{if(t){for(let n of t.run)if(!c.has(n)&&(c.add(n),n(i,e)))return!0;t.preventDefault&&(l=!0)}return!1},f=t[n];if(f){if(u(f[o+Nr(r,e,!s)]))return!0;if(s&&(e.altKey||e.metaKey||e.ctrlKey)&&(h=Jt[e.keyCode])&&h!=r){if(u(f[o+Nr(h,e,!0)]))return!0;if(e.shiftKey&&(a=Xt[e.keyCode])!=r&&a!=h&&u(f[o+Nr(a,e,!1)]))return!0}else if(s&&e.shiftKey&&u(f[o+Nr(r,e,!0)]))return!0;if(u(f._any))return!0}return l}class Fr{constructor(t,e,i,n,r){this.className=t,this.left=e,this.top=i,this.width=n,this.height=r}draw(){let t=document.createElement("div");return t.className=this.className,this.adjust(t),t}update(t,e){return e.className==this.className&&(this.adjust(t),!0)}adjust(t){t.style.left=this.left+"px",t.style.top=this.top+"px",this.width>=0&&(t.style.width=this.width+"px"),t.style.height=this.height+"px"}eq(t){return this.left==t.left&&this.top==t.top&&this.width==t.width&&this.height==t.height&&this.className==t.className}}class zr{constructor(t,e){this.view=t,this.layer=e,this.drawn=[],this.measureReq={read:this.measure.bind(this),write:this.draw.bind(this)},this.dom=t.scrollDOM.appendChild(document.createElement("div")),this.dom.classList.add("cm-layer"),e.above&&this.dom.classList.add("cm-layer-above"),e.class&&this.dom.classList.add(e.class),this.dom.setAttribute("aria-hidden","true"),this.setOrder(t.state),t.requestMeasure(this.measureReq),e.mount&&e.mount(this.dom,t)}update(t){t.startState.facet(qr)!=t.state.facet(qr)&&this.setOrder(t.state),(this.layer.update(t,this.dom)||t.geometryChanged)&&t.view.requestMeasure(this.measureReq)}setOrder(t){let e=0,i=t.facet(qr);for(;e{return i=t,n=this.drawn[e],!(i.constructor==n.constructor&&i.eq(n));var i,n}))){let e=this.dom.firstChild,i=0;for(let n of t)n.update&&e&&n.constructor&&this.drawn[i].constructor&&n.update(e,this.drawn[i])?(e=e.nextSibling,i++):this.dom.insertBefore(n.draw(),e);for(;e;){let t=e.nextSibling;e.remove(),e=t}this.drawn=t}}destroy(){this.dom.remove()}}const qr=P.define();function _r(t){return[Di.define((e=>new zr(e,t))),qr.of(t)]}const Kr=!He.ios,jr=P.define({combine:t=>At(t,{cursorBlinkRate:1200,drawRangeCursor:!0},{cursorBlinkRate:(t,e)=>Math.min(t,e),drawRangeCursor:(t,e)=>t||e})});function $r(t={}){return[jr.of(t),Ur,Xr,Qr,bi.of(!0)]}function Gr(t){return t.startState.facet(jr)!=t.startState.facet(jr)}const Ur=_r({above:!0,markers(t){let{state:e}=t,i=e.facet(jr),n=[];for(let r of e.selection.ranges){let s=r==e.selection.main;if(r.empty?!s||Kr:i.drawRangeCursor){let e=is(t,r,s);e&&n.push(e)}}return n},update(t,e){t.transactions.some((t=>t.scrollIntoView))&&(e.style.animationName="cm-blink"==e.style.animationName?"cm-blink2":"cm-blink");let i=Gr(t);return i&&Jr(t.state,e),t.docChanged||t.selectionSet||i},mount(t,e){Jr(e.state,t)},class:"cm-cursorLayer"});function Jr(t,e){e.style.animationDuration=t.facet(jr).cursorBlinkRate+"ms"}const Xr=_r({above:!1,markers:t=>t.state.selection.ranges.map((e=>e.empty?[]:function(t,e){if(e.to<=t.viewport.from||e.from>=t.viewport.to)return[];let i=Math.max(e.from,t.viewport.from),n=Math.min(e.to,t.viewport.to),r=t.textDirection==Vi.LTR,s=t.contentDOM,o=s.getBoundingClientRect(),l=Zr(t),h=window.getComputedStyle(s.firstChild),a=o.left+parseInt(h.paddingLeft)+Math.min(0,parseInt(h.textIndent)),c=o.right-parseInt(h.paddingRight),u=es(t,i),f=es(t,n),d=u.type==ei.Text?u:null,p=f.type==ei.Text?f:null;t.lineWrapping&&(d&&(d=ts(t,i,d)),p&&(p=ts(t,n,p)));if(d&&p&&d.from==p.from)return m(v(e.from,e.to,d));{let i=d?v(e.from,null,d):w(u,!1),n=p?v(null,e.to,p):w(f,!0),r=[];return(d||u).to<(p||f).from-1?r.push(g(a,i.bottom,c,n.top)):i.bottomu&&n.from=s)break;l>r&&h(Math.max(t,r),null==e&&t<=u,Math.min(l,s),null==i&&l>=f,o.dir)}if(r=n.to+1,r>=s)break}return 0==l.length&&h(u,null==e,f,null==i,t.textDirection),{top:s,bottom:o,horizontal:l}}function w(t,e){let i=o.top+(e?t.top:t.bottom);return{top:i,bottom:i,horizontal:[]}}}(t,e))).reduce(((t,e)=>t.concat(e))),update:(t,e)=>t.docChanged||t.selectionSet||t.viewportChanged||Gr(t),class:"cm-selectionLayer"}),Yr={".cm-line":{"& ::selection":{backgroundColor:"transparent !important"},"&::selection":{backgroundColor:"transparent !important"}}};Kr&&(Yr[".cm-line"].caretColor="transparent !important");const Qr=U.highest(Or.theme(Yr));function Zr(t){let e=t.scrollDOM.getBoundingClientRect();return{left:(t.textDirection==Vi.LTR?e.left:e.right-t.scrollDOM.clientWidth)-t.scrollDOM.scrollLeft,top:e.top-t.scrollDOM.scrollTop}}function ts(t,e,i){let n=R.cursor(e);return{from:Math.max(i.from,t.moveToLineBoundary(n,!1,!0).from),to:Math.min(i.to,t.moveToLineBoundary(n,!0,!0).from),type:ei.Text}}function es(t,e){let i=t.lineBlockAt(e);if(Array.isArray(i.type))for(let t of i.type)if(t.to>e||t.to==e&&(t.to==i.to||t.type==ei.Text))return t;return i}function is(t,e,i){let n=t.coordsAtPos(e.head,e.assoc||1);if(!n)return null;let r=Zr(t);return new Fr(i?"cm-cursor cm-cursor-primary":"cm-cursor cm-cursor-secondary",n.left-r.left,n.top-r.top,-1,n.bottom-n.top)}function ns(t,e,i,n,r){e.lastIndex=0;for(let s,o=t.iterRange(i,n),l=i;!o.next().done;l+=o.value.length)if(!o.lineBreak)for(;s=e.exec(o.value);)r(l+s.index,s)}class rs{constructor(t){const{regexp:e,decoration:i,decorate:n,boundary:r,maxLength:s=1e3}=t;if(!e.global)throw new RangeError("The regular expression given to MatchDecorator should have its 'g' flag set");if(this.regexp=e,n)this.addMatch=(t,e,i,r)=>n(r,i,i+t[0].length,t,e);else if("function"==typeof i)this.addMatch=(t,e,n,r)=>{let s=i(t,e,n);s&&r(n,n+t[0].length,s)};else{if(!i)throw new RangeError("Either 'decorate' or 'decoration' should be provided to MatchDecorator");this.addMatch=(t,e,n,r)=>r(n,n+t[0].length,i)}this.boundary=r,this.maxLength=s}createDeco(t){let e=new Et,i=e.add.bind(e);for(let{from:e,to:n}of function(t,e){let i=t.visibleRanges;if(1==i.length&&i[0].from==t.viewport.from&&i[0].to==t.viewport.to)return i;let n=[];for(let{from:r,to:s}of i)r=Math.max(t.state.doc.lineAt(r).from,r-e),s=Math.min(t.state.doc.lineAt(s).to,s+e),n.length&&n[n.length-1].to>=r?n[n.length-1].to=s:n.push({from:r,to:s});return n}(t,this.maxLength))ns(t.state.doc,this.regexp,e,n,((e,n)=>this.addMatch(n,t,e,i)));return e.finish()}updateDeco(t,e){let i=1e9,n=-1;return t.docChanged&&t.changes.iterChanges(((e,r,s,o)=>{o>t.view.viewport.from&&s1e3?this.createDeco(t.view):n>-1?this.updateRange(t.view,e.map(t.changes),i,n):e}updateRange(t,e,i,n){for(let r of t.visibleRanges){let s=Math.max(r.from,i),o=Math.min(r.to,n);if(o>s){let i=t.state.doc.lineAt(s),n=i.toi.from;s--)if(this.boundary.test(i.text[s-1-i.from])){l=s;break}for(;oc.push(i.range(t,e));if(i==n)for(this.regexp.lastIndex=l-i.from;(a=this.regexp.exec(i.text))&&a.indexthis.addMatch(i,t,e,u)));e=e.update({filterFrom:l,filterTo:h,filter:(t,e)=>th,add:c})}}return e}}const ss=null!=/x/.unicode?"gu":"g",os=new RegExp("[\0-\b\n--Ÿ­؜​‎‏\u2028\u2029‭‮⁦⁧⁩\ufeff-]",ss),ls={0:"null",7:"bell",8:"backspace",10:"newline",11:"vertical tab",13:"carriage return",27:"escape",8203:"zero width space",8204:"zero width non-joiner",8205:"zero width joiner",8206:"left-to-right mark",8207:"right-to-left mark",8232:"line separator",8237:"left-to-right override",8238:"right-to-left override",8294:"left-to-right isolate",8295:"right-to-left isolate",8297:"pop directional isolate",8233:"paragraph separator",65279:"zero width no-break space",65532:"object replacement"};let hs=null;const as=P.define({combine(t){let e=At(t,{render:null,specialChars:os,addSpecialChars:null});return(e.replaceTabs=!function(){var t;if(null==hs&&"undefined"!=typeof document&&document.body){let e=document.body.style;hs=null!=(null!==(t=e.tabSize)&&void 0!==t?t:e.MozTabSize)}return hs||!1}())&&(e.specialChars=new RegExp("\t|"+e.specialChars.source,ss)),e.addSpecialChars&&(e.specialChars=new RegExp(e.specialChars.source+"|"+e.addSpecialChars.source,ss)),e}});function cs(t={}){return[as.of(t),us||(us=Di.fromClass(class{constructor(t){this.view=t,this.decorations=ii.none,this.decorationCache=Object.create(null),this.decorator=this.makeDecorator(t.state.facet(as)),this.decorations=this.decorator.createDeco(t)}makeDecorator(t){return new rs({regexp:t.specialChars,decoration:(e,i,n)=>{let{doc:r}=i.state,s=w(e[0],0);if(9==s){let t=r.lineAt(n),e=i.state.tabSize,s=zt(t.text,e,n-t.from);return ii.replace({widget:new ds((e-s%e)*this.view.defaultCharacterWidth)})}return this.decorationCache[s]||(this.decorationCache[s]=ii.replace({widget:new fs(t,s)}))},boundary:t.replaceTabs?void 0:/[^]/})}update(t){let e=t.state.facet(as);t.startState.facet(as)!=e?(this.decorator=this.makeDecorator(e),this.decorations=this.decorator.createDeco(t.view)):this.decorations=this.decorator.updateDeco(t,this.decorations)}},{decorations:t=>t.decorations}))]}let us=null;class fs extends ti{constructor(t,e){super(),this.options=t,this.code=e}eq(t){return t.code==this.code}toDOM(t){let e=function(t){return t>=32?"•":10==t?"␤":String.fromCharCode(9216+t)}(this.code),i=t.state.phrase("Control character")+" "+(ls[this.code]||"0x"+this.code.toString(16)),n=this.options.render&&this.options.render(this.code,i,e);if(n)return n;let r=document.createElement("span");return r.textContent=e,r.title=i,r.setAttribute("aria-label",i),r.className="cm-specialChar",r}ignoreEvent(){return!1}}class ds extends ti{constructor(t){super(),this.width=t}eq(t){return t.width==this.width}toDOM(){let t=document.createElement("span");return t.textContent="\t",t.className="cm-tab",t.style.width=this.width+"px",t}ignoreEvent(){return!1}}const ps=ii.line({class:"cm-activeLine"}),gs=Di.fromClass(class{constructor(t){this.decorations=this.getDeco(t)}update(t){(t.docChanged||t.selectionSet)&&(this.decorations=this.getDeco(t.view))}getDeco(t){let e=-1,i=[];for(let n of t.state.selection.ranges){let r=t.lineBlockAt(n.head);r.from>e&&(i.push(ps.range(r.from)),e=r.from)}return ii.set(i)}},{decorations:t=>t.decorations});class ms extends ti{constructor(t){super(),this.content=t}toDOM(){let t=document.createElement("span");return t.className="cm-placeholder",t.style.pointerEvents="none",t.appendChild("string"==typeof this.content?document.createTextNode(this.content):this.content),"string"==typeof this.content?t.setAttribute("aria-label","placeholder "+this.content):t.setAttribute("aria-hidden","true"),t}ignoreEvent(){return!1}}const vs=2e3;function ws(t,e){let i=t.posAtCoords({x:e.clientX,y:e.clientY},!1),n=t.state.doc.lineAt(i),r=i-n.from,s=r>vs?-1:r==n.length?function(t,e){let i=t.coordsAtPos(t.viewport.from);return i?Math.round(Math.abs((i.left-e)/t.defaultCharacterWidth)):-1}(t,e.clientX):zt(n.text,t.state.tabSize,i-n.from);return{line:n.number,col:s,off:r}}function ys(t,e){let i=ws(t,e),n=t.state.selection;return i?{update(t){if(t.docChanged){let e=t.changes.mapPos(t.startState.doc.line(i.line).from),r=t.state.doc.lineAt(e);i={line:r.number,col:i.col,off:Math.min(i.off,r.length)},n=n.map(t.changes)}},get(e,r,s){let o=ws(t,e);if(!o)return n;let l=function(t,e,i){let n=Math.min(e.line,i.line),r=Math.max(e.line,i.line),s=[];if(e.off>vs||i.off>vs||e.col<0||i.col<0){let o=Math.min(e.off,i.off),l=Math.max(e.off,i.off);for(let e=n;e<=r;e++){let i=t.doc.line(e);i.length<=l&&s.push(R.range(i.from+o,i.to+l))}}else{let o=Math.min(e.col,i.col),l=Math.max(e.col,i.col);for(let e=n;e<=r;e++){let i=t.doc.line(e),n=qt(i.text,o,t.tabSize,!0);if(n<0)s.push(R.cursor(i.to));else{let e=qt(i.text,l,t.tabSize);s.push(R.range(i.from+n,i.from+e))}}}return s}(t.state,i,o);return l.length?s?R.create(l.concat(n.ranges)):R.create(l):n}}:null}function bs(t){let e=(null==t?void 0:t.eventFilter)||(t=>t.altKey&&0==t.button);return Or.mouseSelectionStyle.of(((t,i)=>e(i)?ys(t,i):null))}const xs={Alt:[18,t=>t.altKey],Control:[17,t=>t.ctrlKey],Shift:[16,t=>t.shiftKey],Meta:[91,t=>t.metaKey]},ks={style:"cursor: crosshair"};function Ss(t={}){let[e,i]=xs[t.key||"Alt"],n=Di.fromClass(class{constructor(t){this.view=t,this.isDown=!1}set(t){this.isDown!=t&&(this.isDown=t,this.view.update([]))}},{eventHandlers:{keydown(t){this.set(t.keyCode==e||i(t))},keyup(t){t.keyCode!=e&&i(t)||this.set(!1)},mousemove(t){this.set(i(t))}}});return[n,Or.contentAttributes.of((t=>{var e;return(null===(e=t.plugin(n))||void 0===e?void 0:e.isDown)?ks:null}))]}const As=P.define({combine(t){let e,i;for(let n of t)e=e||n.topContainer,i=i||n.bottomContainer;return{topContainer:e,bottomContainer:i}}});function Ms(t,e){let i=t.plugin(Cs),n=i?i.specs.indexOf(e):-1;return n>-1?i.panels[n]:null}const Cs=Di.fromClass(class{constructor(t){this.input=t.state.facet(Ts),this.specs=this.input.filter((t=>t)),this.panels=this.specs.map((e=>e(t)));let e=t.state.facet(As);this.top=new Ds(t,!0,e.topContainer),this.bottom=new Ds(t,!1,e.bottomContainer),this.top.sync(this.panels.filter((t=>t.top))),this.bottom.sync(this.panels.filter((t=>!t.top)));for(let t of this.panels)t.dom.classList.add("cm-panel"),t.mount&&t.mount()}update(t){let e=t.state.facet(As);this.top.container!=e.topContainer&&(this.top.sync([]),this.top=new Ds(t.view,!0,e.topContainer)),this.bottom.container!=e.bottomContainer&&(this.bottom.sync([]),this.bottom=new Ds(t.view,!1,e.bottomContainer)),this.top.syncClasses(),this.bottom.syncClasses();let i=t.state.facet(Ts);if(i!=this.input){let e=i.filter((t=>t)),n=[],r=[],s=[],o=[];for(let i of e){let e,l=this.specs.indexOf(i);l<0?(e=i(t.view),o.push(e)):(e=this.panels[l],e.update&&e.update(t)),n.push(e),(e.top?r:s).push(e)}this.specs=e,this.panels=n,this.top.sync(r),this.bottom.sync(s);for(let t of o)t.dom.classList.add("cm-panel"),t.mount&&t.mount()}else for(let e of this.panels)e.update&&e.update(t)}destroy(){this.top.sync([]),this.bottom.sync([])}},{provide:t=>Or.scrollMargins.of((e=>{let i=e.plugin(t);return i&&{top:i.top.scrollMargin(),bottom:i.bottom.scrollMargin()}}))});class Ds{constructor(t,e,i){this.view=t,this.top=e,this.container=i,this.dom=void 0,this.classes="",this.panels=[],this.syncClasses()}sync(t){for(let e of this.panels)e.destroy&&t.indexOf(e)<0&&e.destroy();this.panels=t,this.syncDOM()}syncDOM(){if(0==this.panels.length)return void(this.dom&&(this.dom.remove(),this.dom=void 0));if(!this.dom){this.dom=document.createElement("div"),this.dom.className=this.top?"cm-panels cm-panels-top":"cm-panels cm-panels-bottom",this.dom.style[this.top?"top":"bottom"]="0";let t=this.container||this.view.dom;t.insertBefore(this.dom,this.top?t.firstChild:null)}let t=this.dom.firstChild;for(let e of this.panels)if(e.dom.parentNode==this.dom){for(;t!=e.dom;)t=Os(t);t=t.nextSibling}else this.dom.insertBefore(e.dom,t);for(;t;)t=Os(t)}scrollMargin(){return!this.dom||this.container?0:Math.max(0,this.top?this.dom.getBoundingClientRect().bottom-Math.max(0,this.view.scrollDOM.getBoundingClientRect().top):Math.min(innerHeight,this.view.scrollDOM.getBoundingClientRect().bottom)-this.dom.getBoundingClientRect().top)}syncClasses(){if(this.container&&this.classes!=this.view.themeClasses){for(let t of this.classes.split(" "))t&&this.container.classList.remove(t);for(let t of(this.classes=this.view.themeClasses).split(" "))t&&this.container.classList.add(t)}}}function Os(t){let e=t.nextSibling;return t.remove(),e}const Ts=P.define({enables:Cs});class Es extends Mt{compare(t){return this==t||this.constructor==t.constructor&&this.eq(t)}eq(t){return!1}destroy(t){}}Es.prototype.elementClass="",Es.prototype.toDOM=void 0,Es.prototype.mapMode=k.TrackBefore,Es.prototype.startSide=Es.prototype.endSide=-1,Es.prototype.point=!0;const Bs=P.define(),Rs=P.define(),Ls=P.define({combine:t=>t.some((t=>t))});function Ns(t){let e=[Ps];return t&&!1===t.fixed&&e.push(Ls.of(!0)),e}const Ps=Di.fromClass(class{constructor(t){this.view=t,this.prevViewport=t.viewport,this.dom=document.createElement("div"),this.dom.className="cm-gutters",this.dom.setAttribute("aria-hidden","true"),this.dom.style.minHeight=this.view.contentHeight+"px",this.gutters=t.state.facet(Rs).map((e=>new Hs(t,e)));for(let t of this.gutters)this.dom.appendChild(t.dom);this.fixed=!t.state.facet(Ls),this.fixed&&(this.dom.style.position="sticky"),this.syncGutters(!1),t.scrollDOM.insertBefore(this.dom,t.contentDOM)}update(t){if(this.updateGutters(t)){let e=this.prevViewport,i=t.view.viewport,n=Math.min(e.to,i.to)-Math.max(e.from,i.from);this.syncGutters(n<.8*(i.to-i.from))}t.geometryChanged&&(this.dom.style.minHeight=this.view.contentHeight+"px"),this.view.state.facet(Ls)!=!this.fixed&&(this.fixed=!this.fixed,this.dom.style.position=this.fixed?"sticky":""),this.prevViewport=t.view.viewport}syncGutters(t){let e=this.dom.nextSibling;t&&this.dom.remove();let i=Tt.iter(this.view.state.facet(Bs),this.view.viewport.from),n=[],r=this.gutters.map((t=>new Ws(t,this.view.viewport,-this.view.documentPadding.top)));for(let t of this.view.viewportLineBlocks){let e;if(Array.isArray(t.type)){for(let i of t.type)if(i.type==ei.Text){e=i;break}}else e=t.type==ei.Text?t:void 0;if(e){n.length&&(n=[]),Vs(i,n,t.from);for(let t of r)t.line(this.view,e,n)}}for(let t of r)t.finish();t&&this.view.scrollDOM.insertBefore(this.dom,e)}updateGutters(t){let e=t.startState.facet(Rs),i=t.state.facet(Rs),n=t.docChanged||t.heightChanged||t.viewportChanged||!Tt.eq(t.startState.facet(Bs),t.state.facet(Bs),t.view.viewport.from,t.view.viewport.to);if(e==i)for(let e of this.gutters)e.update(t)&&(n=!0);else{n=!0;let r=[];for(let n of i){let i=e.indexOf(n);i<0?r.push(new Hs(this.view,n)):(this.gutters[i].update(t),r.push(this.gutters[i]))}for(let t of this.gutters)t.dom.remove(),r.indexOf(t)<0&&t.destroy();for(let t of r)this.dom.appendChild(t.dom);this.gutters=r}return n}destroy(){for(let t of this.gutters)t.destroy();this.dom.remove()}},{provide:t=>Or.scrollMargins.of((e=>{let i=e.plugin(t);return i&&0!=i.gutters.length&&i.fixed?e.textDirection==Vi.LTR?{left:i.dom.offsetWidth}:{right:i.dom.offsetWidth}:null}))});function Is(t){return Array.isArray(t)?t:[t]}function Vs(t,e,i){for(;t.value&&t.from<=i;)t.from==i&&e.push(t.value),t.next()}class Ws{constructor(t,e,i){this.gutter=t,this.height=i,this.localMarkers=[],this.i=0,this.cursor=Tt.iter(t.markers,e.from)}line(t,e,i){this.localMarkers.length&&(this.localMarkers=[]),Vs(this.cursor,this.localMarkers,e.from);let n=i.length?this.localMarkers.concat(i):this.localMarkers,r=this.gutter.config.lineMarker(t,e,n);r&&n.unshift(r);let s=this.gutter;if(0==n.length&&!s.config.renderEmptyElements)return;let o=e.top-this.height;if(this.i==s.elements.length){let i=new Fs(t,e.height,o,n);s.elements.push(i),s.dom.appendChild(i.dom)}else s.elements[this.i].update(t,e.height,o,n);this.height=e.bottom,this.i++}finish(){let t=this.gutter;for(;t.elements.length>this.i;){let e=t.elements.pop();t.dom.removeChild(e.dom),e.destroy()}}}class Hs{constructor(t,e){this.view=t,this.config=e,this.elements=[],this.spacer=null,this.dom=document.createElement("div"),this.dom.className="cm-gutter"+(this.config.class?" "+this.config.class:"");for(let i in e.domEventHandlers)this.dom.addEventListener(i,(n=>{let r=t.lineBlockAtHeight(n.clientY-t.documentTop);e.domEventHandlers[i](t,r,n)&&n.preventDefault()}));this.markers=Is(e.markers(t)),e.initialSpacer&&(this.spacer=new Fs(t,0,0,[e.initialSpacer(t)]),this.dom.appendChild(this.spacer.dom),this.spacer.dom.style.cssText+="visibility: hidden; pointer-events: none")}update(t){let e=this.markers;if(this.markers=Is(this.config.markers(t.view)),this.spacer&&this.config.updateSpacer){let e=this.config.updateSpacer(this.spacer.markers[0],t);e!=this.spacer.markers[0]&&this.spacer.update(t.view,0,0,[e])}let i=t.view.viewport;return!Tt.eq(this.markers,e,i.from,i.to)||!!this.config.lineMarkerChange&&this.config.lineMarkerChange(t)}destroy(){for(let t of this.elements)t.destroy()}}class Fs{constructor(t,e,i,n){this.height=-1,this.above=0,this.markers=[],this.dom=document.createElement("div"),this.dom.className="cm-gutterElement",this.update(t,e,i,n)}update(t,e,i,n){this.height!=e&&(this.dom.style.height=(this.height=e)+"px"),this.above!=i&&(this.dom.style.marginTop=(this.above=i)?i+"px":""),function(t,e){if(t.length!=e.length)return!1;for(let i=0;iAt(t,{formatNumber:String,domEventHandlers:{}},{domEventHandlers(t,e){let i=Object.assign({},t);for(let t in e){let n=i[t],r=e[t];i[t]=n?(t,e,i)=>n(t,e,i)||r(t,e,i):r}return i}})});class _s extends Es{constructor(t){super(),this.number=t}eq(t){return this.number==t.number}toDOM(){return document.createTextNode(this.number)}}function Ks(t,e){return t.state.facet(qs).formatNumber(e,t.state)}const js=Rs.compute([qs],(t=>({class:"cm-lineNumbers",renderEmptyElements:!1,markers:t=>t.state.facet(zs),lineMarker:(t,e,i)=>i.some((t=>t.toDOM))?null:new _s(Ks(t,t.state.doc.lineAt(e.from).number)),lineMarkerChange:t=>t.startState.facet(qs)!=t.state.facet(qs),initialSpacer:t=>new _s(Ks(t,Gs(t.state.doc.lines))),updateSpacer(t,e){let i=Ks(e.view,Gs(e.view.state.doc.lines));return i==t.number?t:new _s(i)},domEventHandlers:t.facet(qs).domEventHandlers})));function $s(t={}){return[qs.of(t),Ns(),js]}function Gs(t){let e=9;for(;e{let e=[],i=-1;for(let n of t.selection.ranges){let r=t.doc.lineAt(n.head).from;r>i&&(i=r,e.push(Us.range(r)))}return Tt.of(e)}));const Xs=1024;let Ys=0;class Qs{constructor(t,e){this.from=t,this.to=e}}class Zs{constructor(t={}){this.id=Ys++,this.perNode=!!t.perNode,this.deserialize=t.deserialize||(()=>{throw new Error("This node type doesn't define a deserialize function")})}add(t){if(this.perNode)throw new RangeError("Can't add per-node props to node types");return"function"!=typeof t&&(t=eo.match(t)),e=>{let i=t(e);return void 0===i?null:[this,i]}}}Zs.closedBy=new Zs({deserialize:t=>t.split(" ")}),Zs.openedBy=new Zs({deserialize:t=>t.split(" ")}),Zs.group=new Zs({deserialize:t=>t.split(" ")}),Zs.contextHash=new Zs({perNode:!0}),Zs.lookAhead=new Zs({perNode:!0}),Zs.mounted=new Zs({perNode:!0});const to=Object.create(null);class eo{constructor(t,e,i,n=0){this.name=t,this.props=e,this.id=i,this.flags=n}static define(t){let e=t.props&&t.props.length?Object.create(null):to,i=(t.top?1:0)|(t.skipped?2:0)|(t.error?4:0)|(null==t.name?8:0),n=new eo(t.name||"",e,t.id,i);if(t.props)for(let i of t.props)if(Array.isArray(i)||(i=i(n)),i){if(i[0].perNode)throw new RangeError("Can't store a per-node prop on a node type");e[i[0].id]=i[1]}return n}prop(t){return this.props[t.id]}get isTop(){return(1&this.flags)>0}get isSkipped(){return(2&this.flags)>0}get isError(){return(4&this.flags)>0}get isAnonymous(){return(8&this.flags)>0}is(t){if("string"==typeof t){if(this.name==t)return!0;let e=this.prop(Zs.group);return!!e&&e.indexOf(t)>-1}return this.id==t}static match(t){let e=Object.create(null);for(let i in t)for(let n of i.split(" "))e[n]=t[i];return t=>{for(let i=t.prop(Zs.group),n=-1;n<(i?i.length:0);n++){let r=e[n<0?t.name:i[n]];if(r)return r}}}}eo.none=new eo("",Object.create(null),0,8);const io=new WeakMap,no=new WeakMap;var ro;!function(t){t[t.ExcludeBuffers=1]="ExcludeBuffers",t[t.IncludeAnonymous=2]="IncludeAnonymous",t[t.IgnoreMounts=4]="IgnoreMounts",t[t.IgnoreOverlays=8]="IgnoreOverlays"}(ro||(ro={}));class so{constructor(t,e,i,n,r){if(this.type=t,this.children=e,this.positions=i,this.length=n,this.props=null,r&&r.length){this.props=Object.create(null);for(let[t,e]of r)this.props["number"==typeof t?t:t.id]=e}}toString(){let t=this.prop(Zs.mounted);if(t&&!t.overlay)return t.tree.toString();let e="";for(let t of this.children){let i=t.toString();i&&(e&&(e+=","),e+=i)}return this.type.name?(/\W/.test(this.type.name)&&!this.type.isError?JSON.stringify(this.type.name):this.type.name)+(e.length?"("+e+")":""):e}cursor(t=0){return new vo(this.topNode,t)}cursorAt(t,e=0,i=0){let n=io.get(this)||this.topNode,r=new vo(n);return r.moveTo(t,e),io.set(this,r._tree),r}get topNode(){return new uo(this,0,0,null)}resolve(t,e=0){let i=co(io.get(this)||this.topNode,t,e,!1);return io.set(this,i),i}resolveInner(t,e=0){let i=co(no.get(this)||this.topNode,t,e,!0);return no.set(this,i),i}iterate(t){let{enter:e,leave:i,from:n=0,to:r=this.length}=t;for(let s=this.cursor((t.mode||0)|ro.IncludeAnonymous);;){let t=!1;if(s.from<=r&&s.to>=n&&(s.type.isAnonymous||!1!==e(s))){if(s.firstChild())continue;t=!0}for(;t&&i&&!s.type.isAnonymous&&i(s),!s.nextSibling();){if(!s.parent())return;t=!0}}}prop(t){return t.perNode?this.props?this.props[t.id]:void 0:this.type.prop(t)}get propValues(){let t=[];if(this.props)for(let e in this.props)t.push([+e,this.props[e]]);return t}balance(t={}){return this.children.length<=8?this:xo(eo.none,this.children,this.positions,0,this.children.length,0,this.length,((t,e,i)=>new so(this.type,t,e,i,this.propValues)),t.makeTree||((t,e,i)=>new so(eo.none,t,e,i)))}static build(t){return function(t){var e;let{buffer:i,nodeSet:n,maxBufferLength:r=Xs,reused:s=[],minRepeatType:o=n.types.length}=t,l=Array.isArray(i)?new oo(i,i.length):i,h=n.types,a=0,c=0;function u(t,e,i,v,w){let{id:y,start:b,end:x,size:k}=l,S=c;for(;k<0;){if(l.next(),-1==k){let e=s[y];return i.push(e),void v.push(b-t)}if(-3==k)return void(a=y);if(-4==k)return void(c=y);throw new RangeError(`Unrecognized record size: ${k}`)}let A,M,C=h[y],D=b-t;if(x-b<=r&&(M=g(l.pos-e,w))){let e=new Uint16Array(M.size-M.skip),i=l.pos-M.size,r=e.length;for(;l.pos>i;)r=m(M.start,e,r);A=new lo(e,x-M.start,n),D=M.start-t}else{let t=l.pos-k;l.next();let e=[],i=[],n=y>=o?y:-1,s=0,h=x;for(;l.pos>t;)n>=0&&l.id==n&&l.size>=0?(l.end<=h-r&&(d(e,i,b,s,l.end,h,n,S),s=e.length,h=l.end),l.next()):u(b,t,e,i,n);if(n>=0&&s>0&&s-1&&s>0){let t=f(C);A=xo(C,e,i,0,e.length,0,x-b,t,t)}else A=p(C,e,i,x-b,S-x)}i.push(A),v.push(D)}function f(t){return(e,i,n)=>{let r,s,o=0,l=e.length-1;if(l>=0&&(r=e[l])instanceof so){if(!l&&r.type==t&&r.length==n)return r;(s=r.prop(Zs.lookAhead))&&(o=i[l]+r.length+s)}return p(t,e,i,n,o)}}function d(t,e,i,r,s,o,l,h){let a=[],c=[];for(;t.length>r;)a.push(t.pop()),c.push(e.pop()+i-s);t.push(p(n.types[l],a,c,o-s,h-o)),e.push(s-i)}function p(t,e,i,n,r=0,s){if(a){let t=[Zs.contextHash,a];s=s?[t].concat(s):[t]}if(r>25){let t=[Zs.lookAhead,r];s=s?[t].concat(s):[t]}return new so(t,e,i,n,s)}function g(t,e){let i=l.fork(),n=0,s=0,h=0,a=i.end-r,c={size:0,start:0,skip:0};t:for(let r=i.pos-t;i.pos>r;){let t=i.size;if(i.id==e&&t>=0){c.size=n,c.start=s,c.skip=h,h+=4,n+=4,i.next();continue}let l=i.pos-t;if(t<0||l=o?4:0,f=i.start;for(i.next();i.pos>l;){if(i.size<0){if(-3!=i.size)break t;u+=4}else i.id>=o&&(u+=4);i.next()}s=f,n+=t,h+=u}return(e<0||n==t)&&(c.size=n,c.start=s,c.skip=h),c.size>4?c:void 0}function m(t,e,i){let{id:n,start:r,end:s,size:h}=l;if(l.next(),h>=0&&n4){let n=l.pos-(h-4);for(;l.pos>n;)i=m(t,e,i)}e[--i]=o,e[--i]=s-t,e[--i]=r-t,e[--i]=n}else-3==h?a=n:-4==h&&(c=n);return i}let v=[],w=[];for(;l.pos>0;)u(t.start||0,t.bufferStart||0,v,w,-1);let y=null!==(e=t.length)&&void 0!==e?e:v.length?w[0]+v[0].length:0;return new so(h[t.topID],v.reverse(),w.reverse(),y)}(t)}}so.empty=new so(eo.none,[],[],0);class oo{constructor(t,e){this.buffer=t,this.index=e}get id(){return this.buffer[this.index-4]}get start(){return this.buffer[this.index-3]}get end(){return this.buffer[this.index-2]}get size(){return this.buffer[this.index-1]}get pos(){return this.index}next(){this.index-=4}fork(){return new oo(this.buffer,this.index)}}class lo{constructor(t,e,i){this.buffer=t,this.length=e,this.set=i}get type(){return eo.none}toString(){let t=[];for(let e=0;e0));l=s[l+3]);return o}slice(t,e,i){let n=this.buffer,r=new Uint16Array(e-t),s=0;for(let o=t,l=0;o=e&&ie;case 1:return i<=e&&n>e;case 2:return n>e;case 4:return!0}}function ao(t,e){let i=t.childBefore(e);for(;i;){let e=i.lastChild;if(!e||e.to!=i.to)break;e.type.isError&&e.from==e.to?(t=i,i=e.prevSibling):i=e}return t}function co(t,e,i,n){for(var r;t.from==t.to||(i<1?t.from>=e:t.from>e)||(i>-1?t.to<=e:t.to0?o.length:-1;t!=h;t+=e){let h=o[t],a=l[t]+s.from;if(ho(n,i,a,a+h.length))if(h instanceof lo){if(r&ro.ExcludeBuffers)continue;let o=h.findChild(0,h.buffer.length,e,i-a,n);if(o>-1)return new mo(new go(s,h,t,a),null,o)}else if(r&ro.IncludeAnonymous||!h.type.isAnonymous||wo(h)){let o;if(!(r&ro.IgnoreMounts)&&h.props&&(o=h.prop(Zs.mounted))&&!o.overlay)return new uo(o.tree,a,t,s);let l=new uo(h,a,t,s);return r&ro.IncludeAnonymous||!l.type.isAnonymous?l:l.nextChild(e<0?h.children.length-1:0,e,i,n)}}if(r&ro.IncludeAnonymous||!s.type.isAnonymous)return null;if(t=s.index>=0?s.index+e:e<0?-1:s._parent._tree.children.length,s=s._parent,!s)return null}}get firstChild(){return this.nextChild(0,1,0,4)}get lastChild(){return this.nextChild(this._tree.children.length-1,-1,0,4)}childAfter(t){return this.nextChild(0,1,t,2)}childBefore(t){return this.nextChild(this._tree.children.length-1,-1,t,-2)}enter(t,e,i=0){let n;if(!(i&ro.IgnoreOverlays)&&(n=this._tree.prop(Zs.mounted))&&n.overlay){let i=t-this.from;for(let{from:t,to:r}of n.overlay)if((e>0?t<=i:t=i:r>i))return new uo(n.tree,n.overlay[0].from+this.from,-1,this)}return this.nextChild(0,1,t,e,i)}nextSignificantParent(){let t=this;for(;t.type.isAnonymous&&t._parent;)t=t._parent;return t}get parent(){return this._parent?this._parent.nextSignificantParent():null}get nextSibling(){return this._parent&&this.index>=0?this._parent.nextChild(this.index+1,1,0,4):null}get prevSibling(){return this._parent&&this.index>=0?this._parent.nextChild(this.index-1,-1,0,4):null}cursor(t=0){return new vo(this,t)}get tree(){return this._tree}toTree(){return this._tree}resolve(t,e=0){return co(this,t,e,!1)}resolveInner(t,e=0){return co(this,t,e,!0)}enterUnfinishedNodesBefore(t){return ao(this,t)}getChild(t,e=null,i=null){let n=fo(this,t,e,i);return n.length?n[0]:null}getChildren(t,e=null,i=null){return fo(this,t,e,i)}toString(){return this._tree.toString()}get node(){return this}matchContext(t){return po(this,t)}}function fo(t,e,i,n){let r=t.cursor(),s=[];if(!r.firstChild())return s;if(null!=i)for(;!r.type.is(i);)if(!r.nextSibling())return s;for(;;){if(null!=n&&r.type.is(n))return s;if(r.type.is(e)&&s.push(r.node),!r.nextSibling())return null==n?s:[]}}function po(t,e,i=e.length-1){for(let n=t.parent;i>=0;n=n.parent){if(!n)return!1;if(!n.type.isAnonymous){if(e[i]&&e[i]!=n.name)return!1;i--}}return!0}class go{constructor(t,e,i,n){this.parent=t,this.buffer=e,this.index=i,this.start=n}}class mo{get name(){return this.type.name}get from(){return this.context.start+this.context.buffer.buffer[this.index+1]}get to(){return this.context.start+this.context.buffer.buffer[this.index+2]}constructor(t,e,i){this.context=t,this._parent=e,this.index=i,this.type=t.buffer.set.types[t.buffer.buffer[i]]}child(t,e,i){let{buffer:n}=this.context,r=n.findChild(this.index+4,n.buffer[this.index+3],t,e-this.context.start,i);return r<0?null:new mo(this.context,this,r)}get firstChild(){return this.child(1,0,4)}get lastChild(){return this.child(-1,0,4)}childAfter(t){return this.child(1,t,2)}childBefore(t){return this.child(-1,t,-2)}enter(t,e,i=0){if(i&ro.ExcludeBuffers)return null;let{buffer:n}=this.context,r=n.findChild(this.index+4,n.buffer[this.index+3],e>0?1:-1,t-this.context.start,e);return r<0?null:new mo(this.context,this,r)}get parent(){return this._parent||this.context.parent.nextSignificantParent()}externalSibling(t){return this._parent?null:this.context.parent.nextChild(this.context.index+t,t,0,4)}get nextSibling(){let{buffer:t}=this.context,e=t.buffer[this.index+3];return e<(this._parent?t.buffer[this._parent.index+3]:t.buffer.length)?new mo(this.context,this._parent,e):this.externalSibling(1)}get prevSibling(){let{buffer:t}=this.context,e=this._parent?this._parent.index+4:0;return this.index==e?this.externalSibling(-1):new mo(this.context,this._parent,t.findChild(e,this.index,-1,0,4))}cursor(t=0){return new vo(this,t)}get tree(){return null}toTree(){let t=[],e=[],{buffer:i}=this.context,n=this.index+4,r=i.buffer[this.index+3];if(r>n){let s=i.buffer[this.index+1];t.push(i.slice(n,r,s)),e.push(0)}return new so(this.type,t,e,this.to-this.from)}resolve(t,e=0){return co(this,t,e,!1)}resolveInner(t,e=0){return co(this,t,e,!0)}enterUnfinishedNodesBefore(t){return ao(this,t)}toString(){return this.context.buffer.childString(this.index)}getChild(t,e=null,i=null){let n=fo(this,t,e,i);return n.length?n[0]:null}getChildren(t,e=null,i=null){return fo(this,t,e,i)}get node(){return this}matchContext(t){return po(this,t)}}class vo{get name(){return this.type.name}constructor(t,e=0){if(this.mode=e,this.buffer=null,this.stack=[],this.index=0,this.bufferNode=null,t instanceof uo)this.yieldNode(t);else{this._tree=t.context.parent,this.buffer=t.context;for(let e=t._parent;e;e=e._parent)this.stack.unshift(e.index);this.bufferNode=t,this.yieldBuf(t.index)}}yieldNode(t){return!!t&&(this._tree=t,this.type=t.type,this.from=t.from,this.to=t.to,!0)}yieldBuf(t,e){this.index=t;let{start:i,buffer:n}=this.buffer;return this.type=e||n.set.types[n.buffer[t]],this.from=i+n.buffer[t+1],this.to=i+n.buffer[t+2],!0}yield(t){return!!t&&(t instanceof uo?(this.buffer=null,this.yieldNode(t)):(this.buffer=t.context,this.yieldBuf(t.index,t.type)))}toString(){return this.buffer?this.buffer.buffer.childString(this.index):this._tree.toString()}enterChild(t,e,i){if(!this.buffer)return this.yield(this._tree.nextChild(t<0?this._tree._tree.children.length-1:0,t,e,i,this.mode));let{buffer:n}=this.buffer,r=n.findChild(this.index+4,n.buffer[this.index+3],t,e-this.buffer.start,i);return!(r<0)&&(this.stack.push(this.index),this.yieldBuf(r))}firstChild(){return this.enterChild(1,0,4)}lastChild(){return this.enterChild(-1,0,4)}childAfter(t){return this.enterChild(1,t,2)}childBefore(t){return this.enterChild(-1,t,-2)}enter(t,e,i=this.mode){return this.buffer?!(i&ro.ExcludeBuffers)&&this.enterChild(1,t,e):this.yield(this._tree.enter(t,e,i))}parent(){if(!this.buffer)return this.yieldNode(this.mode&ro.IncludeAnonymous?this._tree._parent:this._tree.parent);if(this.stack.length)return this.yieldBuf(this.stack.pop());let t=this.mode&ro.IncludeAnonymous?this.buffer.parent:this.buffer.parent.nextSignificantParent();return this.buffer=null,this.yieldNode(t)}sibling(t){if(!this.buffer)return!!this._tree._parent&&this.yield(this._tree.index<0?null:this._tree._parent.nextChild(this._tree.index+t,t,0,4,this.mode));let{buffer:e}=this.buffer,i=this.stack.length-1;if(t<0){let t=i<0?0:this.stack[i]+4;if(this.index!=t)return this.yieldBuf(e.findChild(t,this.index,-1,0,4))}else{let t=e.buffer[this.index+3];if(t<(i<0?e.buffer.length:e.buffer[this.stack[i]+3]))return this.yieldBuf(t)}return i<0&&this.yield(this.buffer.parent.nextChild(this.buffer.index+t,t,0,4,this.mode))}nextSibling(){return this.sibling(1)}prevSibling(){return this.sibling(-1)}atLastNode(t){let e,i,{buffer:n}=this;if(n){if(t>0){if(this.index-1)for(let n=e+t,r=t<0?-1:i._tree.children.length;n!=r;n+=t){let t=i._tree.children[n];if(this.mode&ro.IncludeAnonymous||t instanceof lo||!t.type.isAnonymous||wo(t))return!1}return!0}move(t,e){if(e&&this.enterChild(t,0,4))return!0;for(;;){if(this.sibling(t))return!0;if(this.atLastNode(t)||!this.parent())return!1}}next(t=!0){return this.move(1,t)}prev(t=!0){return this.move(-1,t)}moveTo(t,e=0){for(;(this.from==this.to||(e<1?this.from>=t:this.from>t)||(e>-1?this.to<=t:this.to=0;){for(let s=t;s;s=s._parent)if(s.index==n){if(n==this.index)return s;e=s,i=r+1;break t}n=this.stack[--r]}for(let t=i;t=0;r--){if(r<0)return po(this.node,t,n);let s=i[e.buffer[this.stack[r]]];if(!s.isAnonymous){if(t[n]&&t[n]!=s.name)return!1;n--}}return!0}}function wo(t){return t.children.some((t=>t instanceof lo||!t.type.isAnonymous||wo(t)))}const yo=new WeakMap;function bo(t,e){if(!t.isAnonymous||e instanceof lo||e.type!=t)return 1;let i=yo.get(e);if(null==i){i=1;for(let n of e.children){if(n.type!=t||!(n instanceof so)){i=1;break}i+=bo(t,n)}yo.set(e,i)}return i}function xo(t,e,i,n,r,s,o,l,h){let a=0;for(let i=n;i=c)break;p+=e}if(a==r+1){if(p>c){let t=i[r];e(t.children,t.positions,0,t.children.length,n[r]+l);continue}u.push(i[r])}else{let e=n[a-1]+i[a-1].length-d;u.push(xo(t,i,n,r,a,d,e,null,h))}f.push(d+l-s)}}(e,i,n,r,0),(l||h)(u,f,o)}class ko{constructor(t,e,i,n,r=!1,s=!1){this.from=t,this.to=e,this.tree=i,this.offset=n,this.open=(r?1:0)|(s?2:0)}get openStart(){return(1&this.open)>0}get openEnd(){return(2&this.open)>0}static addTree(t,e=[],i=!1){let n=[new ko(0,t.length,t,0,!1,i)];for(let i of e)i.to>t.length&&n.push(i);return n}static applyChanges(t,e,i=128){if(!e.length)return t;let n=[],r=1,s=t.length?t[0]:null;for(let o=0,l=0,h=0;;o++){let a=o=i)for(;s&&s.from=e.from||c<=e.to||h){let t=Math.max(e.from,l)-h,i=Math.min(e.to,c)-h;e=t>=i?null:new ko(t,i,e.tree,e.offset+h,o>0,!!a)}if(e&&n.push(e),s.to>c)break;s=rnew Qs(t.from,t.to))):[new Qs(0,0)]:[new Qs(0,t.length)],this.createParse(t,e||[],i)}parse(t,e,i){let n=this.startParse(t,e,i);for(;;){let t=n.advance();if(t)return t}}}class Ao{constructor(t){this.string=t}get length(){return this.string.length}chunk(t){return this.string.slice(t)}get lineChunks(){return!1}read(t,e){return this.string.slice(t,e)}}new Zs({perNode:!0});let Mo=0;class Co{constructor(t,e,i){this.set=t,this.base=e,this.modified=i,this.id=Mo++}static define(t){if(null==t?void 0:t.base)throw new Error("Can not derive from a modified tag");let e=new Co([],null,[]);if(e.set.push(e),t)for(let i of t.set)e.set.push(i);return e}static defineModifier(){let t=new Oo;return e=>e.modified.indexOf(t)>-1?e:Oo.get(e.base||e,e.modified.concat(t).sort(((t,e)=>t.id-e.id)))}}let Do=0;class Oo{constructor(){this.instances=[],this.id=Do++}static get(t,e){if(!e.length)return t;let i=e[0].instances.find((i=>{return i.base==t&&(n=e,r=i.modified,n.length==r.length&&n.every(((t,e)=>t==r[e])));var n,r}));if(i)return i;let n=[],r=new Co(n,t,e);for(let t of e)t.instances.push(r);let s=function(t){let e=[[]];for(let i=0;ie.length-t.length))}(e);for(let e of t.set)if(!e.modified.length)for(let t of s)n.push(Oo.get(e,t));return r}}function To(t){let e=Object.create(null);for(let i in t){let n=t[i];Array.isArray(n)||(n=[n]);for(let t of i.split(" "))if(t){let i=[],r=2,s=t;for(let e=0;;){if("..."==s&&e>0&&e+3==t.length){r=1;break}let n=/^"(?:[^"\\]|\\.)*?"|[^\/!]+/.exec(s);if(!n)throw new RangeError("Invalid path: "+t);if(i.push("*"==n[0]?"":'"'==n[0][0]?JSON.parse(n[0]):n[0]),e+=n[0].length,e==t.length)break;let o=t[e++];if(e==t.length&&"!"==o){r=0;break}if("/"!=o)throw new RangeError("Invalid path: "+t);s=t.slice(e)}let o=i.length-1,l=i[o];if(!l)throw new RangeError("Invalid path: "+t);let h=new Bo(n,r,o>0?i.slice(0,o):null);e[l]=h.sort(e[l])}}return Eo.add(e)}const Eo=new Zs;class Bo{constructor(t,e,i,n){this.tags=t,this.mode=e,this.context=i,this.next=n}get opaque(){return 0==this.mode}get inherit(){return 1==this.mode}sort(t){return!t||t.depth{let e=r;for(let n of t)for(let t of n.set){let n=i[t.id];if(n){e=e?e+" "+n:n;break}}return e},scope:n}}function Lo(t,e,i,n=0,r=t.length){let s=new No(n,Array.isArray(e)?e:[e],i);s.highlightRange(t.cursor(),n,r,"",s.highlighters),s.flush(r)}Bo.empty=new Bo([],2,null);class No{constructor(t,e,i){this.at=t,this.highlighters=e,this.span=i,this.class=""}startSpan(t,e){e!=this.class&&(this.flush(t),t>this.at&&(this.at=t),this.class=e)}flush(t){t>this.at&&this.class&&this.span(this.at,t,this.class)}highlightRange(t,e,i,n,r){let{type:s,from:o,to:l}=t;if(o>=i||l<=e)return;s.isTop&&(r=this.highlighters.filter((t=>!t.scope||t.scope(s))));let h=n,a=function(t){let e=t.type.prop(Eo);for(;e&&e.context&&!t.matchContext(e.context);)e=e.next;return e||null}(t)||Bo.empty,c=function(t,e){let i=null;for(let n of t){let t=n.style(e);t&&(i=i?i+" "+t:t)}return i}(r,a.tags);if(c&&(h&&(h+=" "),h+=c,1==a.mode&&(n+=(n?" ":"")+c)),this.startSpan(t.from,h),a.opaque)return;let u=t.tree&&t.tree.prop(Zs.mounted);if(u&&u.overlay){let s=t.node.enter(u.overlay[0].from+o,1),a=this.highlighters.filter((t=>!t.scope||t.scope(u.tree.type))),c=t.firstChild();for(let f=0,d=o;;f++){let p=f=g)&&t.nextSibling()););if(!p||g>i)break;d=p.to+o,d>e&&(this.highlightRange(s.cursor(),Math.max(e,p.from+o),Math.min(i,d),n,a),this.startSpan(d,h))}c&&t.parent()}else if(t.firstChild()){do{if(!(t.to<=e)){if(t.from>=i)break;this.highlightRange(t,e,i,n,r),this.startSpan(Math.min(i,t.to),h)}}while(t.nextSibling());t.parent()}}}const Po=Co.define,Io=Po(),Vo=Po(),Wo=Po(Vo),Ho=Po(Vo),Fo=Po(),zo=Po(Fo),qo=Po(Fo),_o=Po(),Ko=Po(_o),jo=Po(),$o=Po(),Go=Po(),Uo=Po(Go),Jo=Po(),Xo={comment:Io,lineComment:Po(Io),blockComment:Po(Io),docComment:Po(Io),name:Vo,variableName:Po(Vo),typeName:Wo,tagName:Po(Wo),propertyName:Ho,attributeName:Po(Ho),className:Po(Vo),labelName:Po(Vo),namespace:Po(Vo),macroName:Po(Vo),literal:Fo,string:zo,docString:Po(zo),character:Po(zo),attributeValue:Po(zo),number:qo,integer:Po(qo),float:Po(qo),bool:Po(Fo),regexp:Po(Fo),escape:Po(Fo),color:Po(Fo),url:Po(Fo),keyword:jo,self:Po(jo),null:Po(jo),atom:Po(jo),unit:Po(jo),modifier:Po(jo),operatorKeyword:Po(jo),controlKeyword:Po(jo),definitionKeyword:Po(jo),moduleKeyword:Po(jo),operator:$o,derefOperator:Po($o),arithmeticOperator:Po($o),logicOperator:Po($o),bitwiseOperator:Po($o),compareOperator:Po($o),updateOperator:Po($o),definitionOperator:Po($o),typeOperator:Po($o),controlOperator:Po($o),punctuation:Go,separator:Po(Go),bracket:Uo,angleBracket:Po(Uo),squareBracket:Po(Uo),paren:Po(Uo),brace:Po(Uo),content:_o,heading:Ko,heading1:Po(Ko),heading2:Po(Ko),heading3:Po(Ko),heading4:Po(Ko),heading5:Po(Ko),heading6:Po(Ko),contentSeparator:Po(_o),list:Po(_o),quote:Po(_o),emphasis:Po(_o),strong:Po(_o),link:Po(_o),monospace:Po(_o),strikethrough:Po(_o),inserted:Po(),deleted:Po(),changed:Po(),invalid:Po(),meta:Jo,documentMeta:Po(Jo),annotation:Po(Jo),processingInstruction:Po(Jo),definition:Co.defineModifier(),constant:Co.defineModifier(),function:Co.defineModifier(),standard:Co.defineModifier(),local:Co.defineModifier(),special:Co.defineModifier()};var Yo;Ro([{tag:Xo.link,class:"tok-link"},{tag:Xo.heading,class:"tok-heading"},{tag:Xo.emphasis,class:"tok-emphasis"},{tag:Xo.strong,class:"tok-strong"},{tag:Xo.keyword,class:"tok-keyword"},{tag:Xo.atom,class:"tok-atom"},{tag:Xo.bool,class:"tok-bool"},{tag:Xo.url,class:"tok-url"},{tag:Xo.labelName,class:"tok-labelName"},{tag:Xo.inserted,class:"tok-inserted"},{tag:Xo.deleted,class:"tok-deleted"},{tag:Xo.literal,class:"tok-literal"},{tag:Xo.string,class:"tok-string"},{tag:Xo.number,class:"tok-number"},{tag:[Xo.regexp,Xo.escape,Xo.special(Xo.string)],class:"tok-string2"},{tag:Xo.variableName,class:"tok-variableName"},{tag:Xo.local(Xo.variableName),class:"tok-variableName tok-local"},{tag:Xo.definition(Xo.variableName),class:"tok-variableName tok-definition"},{tag:Xo.special(Xo.variableName),class:"tok-variableName2"},{tag:Xo.definition(Xo.propertyName),class:"tok-propertyName tok-definition"},{tag:Xo.typeName,class:"tok-typeName"},{tag:Xo.namespace,class:"tok-namespace"},{tag:Xo.className,class:"tok-className"},{tag:Xo.macroName,class:"tok-macroName"},{tag:Xo.propertyName,class:"tok-propertyName"},{tag:Xo.operator,class:"tok-operator"},{tag:Xo.comment,class:"tok-comment"},{tag:Xo.meta,class:"tok-meta"},{tag:Xo.invalid,class:"tok-invalid"},{tag:Xo.punctuation,class:"tok-punctuation"}]);const Qo=new Zs;class Zo{constructor(t,e,i=[],n=""){this.data=t,this.name=n,St.prototype.hasOwnProperty("tree")||Object.defineProperty(St.prototype,"tree",{get(){return el(this)}}),this.parser=e,this.extension=[cl.of(this),St.languageData.of(((t,e,i)=>t.facet(tl(t,e,i))))].concat(i)}isActiveAt(t,e,i=-1){return tl(t,e,i)==this.data}findRegions(t){let e=t.facet(cl);if((null==e?void 0:e.data)==this.data)return[{from:0,to:t.doc.length}];if(!e||!e.allowsNesting)return[];let i=[],n=(t,e)=>{if(t.prop(Qo)==this.data)return void i.push({from:e,to:e+t.length});let r=t.prop(Zs.mounted);if(r){if(r.tree.prop(Qo)==this.data){if(r.overlay)for(let t of r.overlay)i.push({from:t.from+e,to:t.to+e});else i.push({from:e,to:e+t.length});return}if(r.overlay){let t=i.length;if(n(r.tree,r.overlay[0].from+e),i.length>t)return}}for(let i=0;i=this.cursorPos?this.doc.sliceString(t,e):this.string.slice(t-i,e-i)}}let nl=null;class rl{constructor(t,e,i=[],n,r,s,o,l){this.parser=t,this.state=e,this.fragments=i,this.tree=n,this.treeLen=r,this.viewport=s,this.skipped=o,this.scheduleOn=l,this.parse=null,this.tempSkipped=[]}static create(t,e,i){return new rl(t,e,[],so.empty,0,i,[],null)}startParse(){return this.parser.startParse(new il(this.state.doc),this.fragments)}work(t,e){return null!=e&&e>=this.state.doc.length&&(e=void 0),this.tree!=so.empty&&this.isDone(null!=e?e:this.state.doc.length)?(this.takeTree(),!0):this.withContext((()=>{var i;if("number"==typeof t){let e=Date.now()+t;t=()=>Date.now()>e}for(this.parse||(this.parse=this.startParse()),null!=e&&(null==this.parse.stoppedAt||this.parse.stoppedAt>e)&&e=this.treeLen&&((null==this.parse.stoppedAt||this.parse.stoppedAt>t)&&this.parse.stopAt(t),this.withContext((()=>{for(;!(e=this.parse.advance()););})),this.treeLen=t,this.tree=e,this.fragments=this.withoutTempSkipped(ko.addTree(this.tree,this.fragments,!0)),this.parse=null)}withContext(t){let e=nl;nl=this;try{return t()}finally{nl=e}}withoutTempSkipped(t){for(let e;e=this.tempSkipped.pop();)t=sl(t,e.from,e.to);return t}changes(t,e){let{fragments:i,tree:n,treeLen:r,viewport:s,skipped:o}=this;if(this.takeTree(),!t.empty){let e=[];if(t.iterChangedRanges(((t,i,n,r)=>e.push({fromA:t,toA:i,fromB:n,toB:r}))),i=ko.applyChanges(i,e),n=so.empty,r=0,s={from:t.mapPos(s.from,-1),to:t.mapPos(s.to,1)},this.skipped.length){o=[];for(let e of this.skipped){let i=t.mapPos(e.from,1),n=t.mapPos(e.to,-1);it.from&&(this.fragments=sl(this.fragments,i,n),this.skipped.splice(e--,1))}return!(this.skipped.length>=e)&&(this.reset(),!0)}reset(){this.parse&&(this.takeTree(),this.parse=null)}skipUntilInView(t,e){this.skipped.push({from:t,to:e})}static getSkippingParser(t){return new class extends So{createParse(e,i,n){let r=n[0].from,s=n[n.length-1].to;return{parsedPos:r,advance(){let e=nl;if(e){for(let t of n)e.tempSkipped.push(t);t&&(e.scheduleOn=e.scheduleOn?Promise.all([e.scheduleOn,t]):t)}return this.parsedPos=s,new so(eo.none,[],[],s-r)},stoppedAt:null,stopAt(){}}}}}isDone(t){t=Math.min(t,this.state.doc.length);let e=this.fragments;return this.treeLen>=t&&e.length&&0==e[0].from&&e[0].to>=t}static get(){return nl}}function sl(t,e,i){return ko.applyChanges(t,[{fromA:e,toA:i,fromB:e,toB:i}])}class ol{constructor(t){this.context=t,this.tree=t.tree}apply(t){if(!t.docChanged&&this.tree==this.context.tree)return this;let e=this.context.changes(t.changes,t.state),i=this.context.treeLen==t.startState.doc.length?void 0:Math.max(t.changes.mapPos(this.context.treeLen),e.viewport.to);return e.work(20,i)||e.takeTree(),new ol(e)}static init(t){let e=Math.min(3e3,t.doc.length),i=rl.create(t.facet(cl).parser,t,{from:0,to:e});return i.work(20,e)||i.takeTree(),new ol(i)}}Zo.state=q.define({create:ol.init,update(t,e){for(let t of e.effects)if(t.is(Zo.setState))return t.value;return e.startState.facet(cl)!=e.state.facet(cl)?ol.init(e.state):t.apply(e)}});let ll=t=>{let e=setTimeout((()=>t()),500);return()=>clearTimeout(e)};"undefined"!=typeof requestIdleCallback&&(ll=t=>{let e=-1,i=setTimeout((()=>{e=requestIdleCallback(t,{timeout:400})}),100);return()=>e<0?clearTimeout(i):cancelIdleCallback(e)});const hl="undefined"!=typeof navigator&&(null===(Yo=navigator.scheduling)||void 0===Yo?void 0:Yo.isInputPending)?()=>navigator.scheduling.isInputPending():null,al=Di.fromClass(class{constructor(t){this.view=t,this.working=null,this.workScheduled=0,this.chunkEnd=-1,this.chunkBudget=-1,this.work=this.work.bind(this),this.scheduleWork()}update(t){let e=this.view.state.field(Zo.state).context;(e.updateViewport(t.view.viewport)||this.view.viewport.to>e.treeLen)&&this.scheduleWork(),t.docChanged&&(this.view.hasFocus&&(this.chunkBudget+=50),this.scheduleWork()),this.checkAsyncSchedule(e)}scheduleWork(){if(this.working)return;let{state:t}=this.view,e=t.field(Zo.state);e.tree==e.context.tree&&e.context.isDone(t.doc.length)||(this.working=ll(this.work))}work(t){this.working=null;let e=Date.now();if(this.chunkEndn+1e3,l=r.context.work((()=>hl&&hl()||Date.now()>s),n+(o?0:1e5));this.chunkBudget-=Date.now()-e,(l||this.chunkBudget<=0)&&(r.context.takeTree(),this.view.dispatch({effects:Zo.setState.of(new ol(r.context))})),this.chunkBudget>0&&(!l||o)&&this.scheduleWork(),this.checkAsyncSchedule(r.context)}checkAsyncSchedule(t){t.scheduleOn&&(this.workScheduled++,t.scheduleOn.then((()=>this.scheduleWork())).catch((t=>Si(this.view.state,t))).then((()=>this.workScheduled--)),t.scheduleOn=null)}destroy(){this.working&&this.working()}isWorking(){return!!(this.working||this.workScheduled>0)}},{eventHandlers:{focus(){this.scheduleWork()}}}),cl=P.define({combine:t=>t.length?t[0]:null,enables:t=>[Zo.state,al,Or.contentAttributes.compute([t],(e=>{let i=e.facet(t);return i&&i.name?{"data-language":i.name}:{}}))]}),ul=P.define(),fl=P.define({combine:t=>{if(!t.length)return" ";if(!/^(?: +|\t+)$/.test(t[0]))throw new Error("Invalid indent unit: "+JSON.stringify(t[0]));return t[0]}});function dl(t){let e=t.facet(fl);return 9==e.charCodeAt(0)?t.tabSize*e.length:e.length}function pl(t,e){let i="",n=t.tabSize;if(9==t.facet(fl).charCodeAt(0))for(;e>=n;)i+="\t",e-=n;for(let t=0;t=i.from&&n<=i.to?r&&n==t?{text:"",from:t}:(e<0?n-1&&(r+=s-this.countColumn(i,i.search(/\S|$/))),r}countColumn(t,e=t.length){return zt(t,this.state.tabSize,e)}lineIndent(t,e=1){let{text:i,from:n}=this.lineAt(t,e),r=this.options.overrideIndentation;if(r){let t=r(n);if(t>-1)return t}return this.countColumn(i,i.search(/\S|$/))}get simulatedBreak(){return this.options.simulateBreak||null}}const vl=new Zs;function wl(t){let e=t.type.prop(vl);if(e)return e;let i,n=t.firstChild;if(n&&(i=n.type.prop(Zs.closedBy))){let e=t.lastChild,n=e&&i.indexOf(e.name)>-1;return t=>function(t,e,i,n,r){let s=t.textAfter,o=s.match(/^\s*/)[0].length,l=n&&s.slice(o,o+n.length)==n||r==t.pos+o,h=e?function(t){let e=t.node,i=e.childAfter(e.from),n=e.lastChild;if(!i)return null;let r=t.options.simulateBreak,s=t.state.doc.lineAt(i.from),o=null==r||r<=s.from?s.to:Math.min(s.to,r);for(let t=i.to;;){let r=e.childAfter(t);if(!r||r==n)return null;if(!r.type.isSkipped)return r.fromt.prop(Qo)==s.data:s?t=>t==s:void 0,this.style=Ro(t.map((t=>({tag:t.tag,class:t.class||n(Object.assign({},t,{tag:null}))}))),{all:r}).style,this.module=i?new $t(i):null,this.themeType=e.themeType}static define(t,e){return new Sl(t,e||{})}}const Al=P.define(),Ml=P.define({combine:t=>t.length?[t[0]]:null});function Cl(t){let e=t.facet(Al);return e.length?e:t.facet(Ml)}function Dl(t,e){let i,n=[Tl];return t instanceof Sl&&(t.module&&n.push(Or.styleModule.of(t.module)),i=t.themeType),(null==e?void 0:e.fallback)?n.push(Ml.of(t)):i?n.push(Al.computeN([Or.darkTheme],(e=>e.facet(Or.darkTheme)==("dark"==i)?[t]:[]))):n.push(Al.of(t)),n}class Ol{constructor(t){this.markCache=Object.create(null),this.tree=el(t.state),this.decorations=this.buildDeco(t,Cl(t.state))}update(t){let e=el(t.state),i=Cl(t.state),n=i!=Cl(t.startState);e.length{i.add(t,e,this.markCache[n]||(this.markCache[n]=ii.mark({class:n})))}),n,r);return i.finish()}}const Tl=U.high(Di.fromClass(Ol,{decorations:t=>t.decorations})),El=Or.baseTheme({"&.cm-focused .cm-matchingBracket":{backgroundColor:"#328c8252"},"&.cm-focused .cm-nonmatchingBracket":{backgroundColor:"#bb555544"}}),Bl="()[]{}",Rl=P.define({combine:t=>At(t,{afterCursor:!0,brackets:Bl,maxScanDistance:1e4,renderMatch:Pl})}),Ll=ii.mark({class:"cm-matchingBracket"}),Nl=ii.mark({class:"cm-nonmatchingBracket"});function Pl(t){let e=[],i=t.matched?Ll:Nl;return e.push(i.range(t.start.from,t.start.to)),t.end&&e.push(i.range(t.end.from,t.end.to)),e}const Il=q.define({create:()=>ii.none,update(t,e){if(!e.docChanged&&!e.selection)return t;let i=[],n=e.state.facet(Rl);for(let t of e.state.selection.ranges){if(!t.empty)continue;let r=Fl(e.state,t.head,-1,n)||t.head>0&&Fl(e.state,t.head-1,1,n)||n.afterCursor&&(Fl(e.state,t.head,1,n)||t.headOr.decorations.from(t)}),Vl=[Il,El];function Wl(t={}){return[Rl.of(t),Vl]}function Hl(t,e,i){let n=t.prop(e<0?Zs.openedBy:Zs.closedBy);if(n)return n;if(1==t.name.length){let n=i.indexOf(t.name);if(n>-1&&n%2==(e<0?1:0))return[i[n+e]]}return null}function Fl(t,e,i,n={}){let r=n.maxScanDistance||1e4,s=n.brackets||Bl,o=el(t),l=o.resolveInner(e,i);for(let n=l;n;n=n.parent){let r=Hl(n.type,i,s);if(r&&n.from0)return null;let a={from:i<0?e-1:e,to:i>0?e+1:e},c=t.doc.iterRange(e,i>0?t.doc.length:0),u=0;for(let t=0;!c.next().done&&t<=s;){let s=c.value;i<0&&(t+=s.length);let l=e+t*i;for(let t=i>0?0:s.length-1,e=i>0?s.length:-1;t!=e;t+=i){let e=o.indexOf(s[t]);if(!(e<0||n.resolveInner(l+t,1).type!=r))if(e%2==0==i>0)u++;else{if(1==u)return{start:a,end:{from:l+t,to:l+t+1},matched:e>>1==h>>1};u--}}i>0&&(t+=s.length)}return c.done?{start:a,matched:!1}:null}(t,e,i,o,l.type,r,s)}function zl(t,e,i,n,r,s){let o=n.parent,l={from:n.from,to:n.to},h=0,a=null==o?void 0:o.cursor();if(a&&(i<0?a.childBefore(n.from):a.childAfter(n.to)))do{if(i<0?a.to<=n.from:a.from>=n.to){if(0==h&&r.indexOf(a.type.name)>-1&&a.from-1||(Kl.push(t),console.warn(e))}function Gl(t,e){let i=null;for(let n of e.split(".")){let e=t[n]||Xo[n];e?"function"==typeof e?i?i=e(i):$l(n,`Modifier ${n} used at start of tag`):i?$l(n,`Tag ${n} used as modifier`):i=e:$l(n,`Unknown highlighting tag ${n}`)}if(!i)return 0;let n=e.replace(/ /g,"_"),r=eo.define({id:_l.length,name:n,props:[To({[n]:i})]});return _l.push(r),r.id}const Ul={brackets:["(","[","{","'",'"'],before:")]}:;>",stringPrefixes:[]},Jl=ut.define({map(t,e){let i=e.mapPos(t,-1,k.TrackAfter);return null==i?void 0:i}}),Xl=ut.define({map:(t,e)=>e.mapPos(t)}),Yl=new class extends Mt{};Yl.startSide=1,Yl.endSide=-1;const Ql=q.define({create:()=>Tt.empty,update(t,e){if(e.selection){let i=e.state.doc.lineAt(e.selection.main.head).from,n=e.startState.doc.lineAt(e.startState.selection.main.head).from;i!=e.changes.mapPos(n,-1)&&(t=Tt.empty)}t=t.map(e.changes);for(let i of e.effects)i.is(Jl)?t=t.update({add:[Yl.range(i.value,i.value+1)]}):i.is(Xl)&&(t=t.update({filter:t=>t!=i.value}));return t}});const Zl="()[]{}<>";function th(t){for(let e=0;e{if((ih?t.composing:t.compositionStarted)||t.state.readOnly)return!1;let r=t.state.selection.main;if(n.length>2||2==n.length&&1==b(w(n,0))||e!=r.from||i!=r.to)return!1;let s=function(t,e){let i=eh(t,t.selection.main.head),n=i.brackets||Ul.brackets;for(let r of n){let s=th(w(r,0));if(e==r)return s==r?ah(t,r,n.indexOf(r+r+r)>-1,i):lh(t,r,s,i.before||Ul.before);if(e==s&&sh(t,t.selection.main.from))return hh(t,r,s)}return null}(t.state,n);return!!s&&(t.dispatch(s),!0)})),rh=[{key:"Backspace",run:({state:t,dispatch:e})=>{if(t.readOnly)return!1;let i=eh(t,t.selection.main.head).brackets||Ul.brackets,n=null,r=t.changeByRange((e=>{if(e.empty){let n=function(t,e){let i=t.sliceString(e-2,e);return b(w(i,0))==i.length?i:i.slice(1)}(t.doc,e.head);for(let r of i)if(r==n&&oh(t.doc,e.head)==th(w(r,0)))return{changes:{from:e.head-r.length,to:e.head+r.length},range:R.cursor(e.head-r.length)}}return{range:n=e}}));return n||e(t.update(r,{scrollIntoView:!0,userEvent:"delete.backward"})),!n}}];function sh(t,e){let i=!1;return t.field(Ql).between(0,t.doc.length,(t=>{t==e&&(i=!0)})),i}function oh(t,e){let i=t.sliceString(e,e+2);return i.slice(0,b(w(i,0)))}function lh(t,e,i,n){let r=null,s=t.changeByRange((s=>{if(!s.empty)return{changes:[{insert:e,from:s.from},{insert:i,from:s.to}],effects:Jl.of(s.to+e.length),range:R.range(s.anchor+e.length,s.head+e.length)};let o=oh(t.doc,s.head);return!o||/\s/.test(o)||n.indexOf(o)>-1?{changes:{insert:e+i,from:s.head},effects:Jl.of(s.head+e.length),range:R.cursor(s.head+e.length)}:{range:r=s}}));return r?null:t.update(s,{scrollIntoView:!0,userEvent:"input.type"})}function hh(t,e,i){let n=null,r=t.selection.ranges.map((e=>e.empty&&oh(t.doc,e.head)==i?R.cursor(e.head+i.length):n=e));return n?null:t.update({selection:R.create(r,t.selection.mainIndex),scrollIntoView:!0,effects:t.selection.ranges.map((({from:t})=>Xl.of(t)))})}function ah(t,e,i,n){let r=n.stringPrefixes||Ul.stringPrefixes,s=null,o=t.changeByRange((n=>{if(!n.empty)return{changes:[{insert:e,from:n.from},{insert:e,from:n.to}],effects:Jl.of(n.to+e.length),range:R.range(n.anchor+e.length,n.head+e.length)};let o,l=n.head,h=oh(t.doc,l);if(h==e){if(ch(t,l))return{changes:{insert:e+e,from:l},effects:Jl.of(l+e.length),range:R.cursor(l+e.length)};if(sh(t,l)){let n=i&&t.sliceDoc(l,l+3*e.length)==e+e+e;return{range:R.cursor(l+e.length*(n?3:1)),effects:Xl.of(l)}}}else{if(i&&t.sliceDoc(l-2*e.length,l)==e+e&&(o=uh(t,l-2*e.length,r))>-1&&ch(t,o))return{changes:{insert:e+e+e+e,from:l},effects:Jl.of(l+e.length),range:R.cursor(l+e.length)};if(t.charCategorizer(l)(h)!=yt.Word&&uh(t,l,r)>-1&&!function(t,e,i,n){let r=el(t).resolveInner(e,-1),s=n.reduce(((t,e)=>Math.max(t,e.length)),0);for(let o=0;o<5;o++){let o=t.sliceDoc(r.from,Math.min(r.to,r.from+i.length+s)),l=o.indexOf(i);if(!l||l>-1&&n.indexOf(o.slice(0,l))>-1){let e=r.firstChild;for(;e&&e.from==r.from&&e.to-e.from>i.length+l;){if(t.sliceDoc(e.to-i.length,e.to)==i)return!1;e=e.firstChild}return!0}let h=r.to==e&&r.parent;if(!h)break;r=h}return!1}(t,l,e,r))return{changes:{insert:e+e,from:l},effects:Jl.of(l+e.length),range:R.cursor(l+e.length)}}return{range:s=n}}));return s?null:t.update(o,{scrollIntoView:!0,userEvent:"input.type"})}function ch(t,e){let i=el(t).resolveInner(e+1);return i.parent&&i.from==e}function uh(t,e,i){let n=t.charCategorizer(e);if(n(t.sliceDoc(e-1,e))!=yt.Word)return e;for(let r of i){let i=e-r.length;if(t.sliceDoc(i,e)==r&&n(t.sliceDoc(i-1,i))!=yt.Word)return i}return-1}function fh(t,e){return({state:i,dispatch:n})=>{if(i.readOnly)return!1;let r=t(e,i);return!!r&&(n(i.update(r)),!0)}}const dh=fh(wh,0),ph=fh(vh,0),gh=fh(((t,e)=>vh(t,e,function(t){let e=[];for(let i of t.selection.ranges){let n=t.doc.lineAt(i.from),r=i.to<=n.to?n:t.doc.lineAt(i.to),s=e.length-1;s>=0&&e[s].to>n.from?e[s].to=r.to:e.push({from:n.from,to:r.to})}return e}(e))),0);function mh(t,e=t.selection.main.head){let i=t.languageDataAt("commentTokens",e);return i.length?i[0]:{}}function vh(t,e,i=e.selection.ranges){let n=i.map((t=>mh(e,t.from).block));if(!n.every((t=>t)))return null;let r=i.map(((t,i)=>function(t,{open:e,close:i},n,r){let s,o,l=t.sliceDoc(n-50,n),h=t.sliceDoc(r,r+50),a=/\s*$/.exec(l)[0].length,c=/^\s*/.exec(h)[0].length,u=l.length-a;if(l.slice(u-e.length,u)==e&&h.slice(c,c+i.length)==i)return{open:{pos:n-a,margin:a&&1},close:{pos:r+c,margin:c&&1}};r-n<=100?s=o=t.sliceDoc(n,r):(s=t.sliceDoc(n,n+50),o=t.sliceDoc(r-50,r));let f=/^\s*/.exec(s)[0].length,d=/\s*$/.exec(o)[0].length,p=o.length-d-i.length;return s.slice(f,f+e.length)==e&&o.slice(p,p+i.length)==i?{open:{pos:n+f+e.length,margin:/\s/.test(s.charAt(f+e.length))?1:0},close:{pos:r-d-i.length,margin:/\s/.test(o.charAt(p-1))?1:0}}:null}(e,n[i],t.from,t.to)));if(2!=t&&!r.every((t=>t)))return{changes:e.changes(i.map(((t,e)=>r[e]?[]:[{from:t.from,insert:n[e].open+" "},{from:t.to,insert:" "+n[e].close}])))};if(1!=t&&r.some((t=>t))){let t=[];for(let e,i=0;ir&&(t==s||s>l.from)){r=l.from;let t=mh(e,i).line;if(!t)continue;let s=/^\s*/.exec(l.text)[0].length,h=s==l.length,a=l.text.slice(s,s+t.length)==t?s:-1;st.comment<0&&(!t.empty||t.single)))){let t=[];for(let{line:e,token:i,indent:r,empty:s,single:o}of n)!o&&s||t.push({from:e.from+r,insert:i+" "});let i=e.changes(t);return{changes:i,selection:e.selection.map(i,1)}}if(1!=t&&n.some((t=>t.comment>=0))){let t=[];for(let{line:e,comment:i,token:r}of n)if(i>=0){let n=e.from+i,s=n+r.length;" "==e.text[s-e.from]&&s++,t.push({from:n,to:s})}return{changes:t}}return null}const yh=ht.define(),bh=ht.define(),xh=P.define(),kh=P.define({combine:t=>At(t,{minDepth:100,newGroupDelay:500},{minDepth:Math.max,newGroupDelay:Math.min})});const Sh=q.define({create:()=>Hh.empty,update(t,e){let i=e.state.facet(kh),n=e.annotation(yh);if(n){let r=e.docChanged?R.single(function(t){let e=0;return t.iterChangedRanges(((t,i)=>e=i)),e}(e.changes)):void 0,s=Eh.fromTransaction(e,r),o=n.side,l=0==o?t.undone:t.done;return l=s?Bh(l,l.length,i.minDepth,s):Nh(l,e.startState.selection),new Hh(0==o?n.rest:l,0==o?l:n.rest)}let r=e.annotation(bh);if("full"!=r&&"before"!=r||(t=t.isolate()),!1===e.annotation(ft.addToHistory))return e.changes.empty?t:t.addMapping(e.changes.desc);let s=Eh.fromTransaction(e),o=e.annotation(ft.time),l=e.annotation(ft.userEvent);return s?t=t.addChanges(s,o,l,i.newGroupDelay,i.minDepth):e.selection&&(t=t.addSelection(e.startState.selection,o,l,i.newGroupDelay)),"full"!=r&&"after"!=r||(t=t.isolate()),t},toJSON:t=>({done:t.done.map((t=>t.toJSON())),undone:t.undone.map((t=>t.toJSON()))}),fromJSON:t=>new Hh(t.done.map(Eh.fromJSON),t.undone.map(Eh.fromJSON))});function Ah(t={}){return[Sh,kh.of(t),Or.domEventHandlers({beforeinput(t,e){let i="historyUndo"==t.inputType?Ch:"historyRedo"==t.inputType?Dh:null;return!!i&&(t.preventDefault(),i(e))}})]}function Mh(t,e){return function({state:i,dispatch:n}){if(!e&&i.readOnly)return!1;let r=i.field(Sh,!1);if(!r)return!1;let s=r.pop(t,i,e);return!!s&&(n(s),!0)}}const Ch=Mh(0,!1),Dh=Mh(1,!1),Oh=Mh(0,!0),Th=Mh(1,!0);class Eh{constructor(t,e,i,n,r){this.changes=t,this.effects=e,this.mapped=i,this.startSelection=n,this.selectionsAfter=r}setSelAfter(t){return new Eh(this.changes,this.effects,this.mapped,this.startSelection,t)}toJSON(){var t,e,i;return{changes:null===(t=this.changes)||void 0===t?void 0:t.toJSON(),mapped:null===(e=this.mapped)||void 0===e?void 0:e.toJSON(),startSelection:null===(i=this.startSelection)||void 0===i?void 0:i.toJSON(),selectionsAfter:this.selectionsAfter.map((t=>t.toJSON()))}}static fromJSON(t){return new Eh(t.changes&&A.fromJSON(t.changes),[],t.mapped&&S.fromJSON(t.mapped),t.startSelection&&R.fromJSON(t.startSelection),t.selectionsAfter.map(R.fromJSON))}static fromTransaction(t,e){let i=Lh;for(let e of t.startState.facet(xh)){let n=e(t);n.length&&(i=i.concat(n))}return!i.length&&t.changes.empty?null:new Eh(t.changes.invert(t.startState.doc),i,void 0,e||t.startState.selection,Lh)}static selection(t){return new Eh(void 0,Lh,void 0,void 0,t)}}function Bh(t,e,i,n){let r=e+1>i+20?e-i-1:0,s=t.slice(r,e);return s.push(n),s}function Rh(t,e){return t.length?e.length?t.concat(e):t:e}const Lh=[];function Nh(t,e){if(t.length){let i=t[t.length-1],n=i.selectionsAfter.slice(Math.max(0,i.selectionsAfter.length-200));return n.length&&n[n.length-1].eq(e)?t:(n.push(e),Bh(t,t.length-1,1e9,i.setSelAfter(n)))}return[Eh.selection([e])]}function Ph(t){let e=t[t.length-1],i=t.slice();return i[t.length-1]=e.setSelAfter(e.selectionsAfter.slice(0,e.selectionsAfter.length-1)),i}function Ih(t,e){if(!t.length)return t;let i=t.length,n=Lh;for(;i;){let r=Vh(t[i-1],e,n);if(r.changes&&!r.changes.empty||r.effects.length){let e=t.slice(0,i);return e[i-1]=r,e}e=r.mapped,i--,n=r.selectionsAfter}return n.length?[Eh.selection(n)]:Lh}function Vh(t,e,i){let n=Rh(t.selectionsAfter.length?t.selectionsAfter.map((t=>t.map(e))):Lh,i);if(!t.changes)return Eh.selection(n);let r=t.changes.map(e),s=e.mapDesc(t.changes,!0),o=t.mapped?t.mapped.composeDesc(s):s;return new Eh(r,ut.mapEffects(t.effects,e),o,t.startSelection.map(s),n)}const Wh=/^(input\.type|delete)($|\.)/;class Hh{constructor(t,e,i=0,n){this.done=t,this.undone=e,this.prevTime=i,this.prevUserEvent=n}isolate(){return this.prevTime?new Hh(this.done,this.undone):this}addChanges(t,e,i,n,r){let s=this.done,o=s[s.length-1];return s=o&&o.changes&&!o.changes.empty&&t.changes&&(!i||Wh.test(i))&&(!o.selectionsAfter.length&&e-this.prevTimei.push(t,e))),e.iterChangedRanges(((t,e,r,s)=>{for(let t=0;t=e&&r<=o&&(n=!0)}})),n}(o.changes,t.changes)||"input.type.compose"==i)?Bh(s,s.length-1,r,new Eh(t.changes.compose(o.changes),Rh(t.effects,o.effects),o.mapped,o.startSelection,Lh)):Bh(s,s.length,r,t),new Hh(s,Lh,e,i)}addSelection(t,e,i,n){let r=this.done.length?this.done[this.done.length-1].selectionsAfter:Lh;return r.length>0&&e-this.prevTimet.empty!=o.ranges[e].empty)).length)?this:new Hh(Nh(this.done,t),this.undone,e,i);var s,o}addMapping(t){return new Hh(Ih(this.done,t),Ih(this.undone,t),this.prevTime,this.prevUserEvent)}pop(t,e,i){let n=0==t?this.done:this.undone;if(0==n.length)return null;let r=n[n.length-1];if(i&&r.selectionsAfter.length)return e.update({selection:r.selectionsAfter[r.selectionsAfter.length-1],annotations:yh.of({side:t,rest:Ph(n)}),userEvent:0==t?"select.undo":"select.redo",scrollIntoView:!0});if(r.changes){let i=1==n.length?Lh:n.slice(0,n.length-1);return r.mapped&&(i=Ih(i,r.mapped)),e.update({changes:r.changes,selection:r.startSelection,effects:r.effects,annotations:yh.of({side:t,rest:i}),filter:!1,userEvent:0==t?"undo":"redo",scrollIntoView:!0})}return null}}Hh.empty=new Hh(Lh,Lh);const Fh=[{key:"Mod-z",run:Ch,preventDefault:!0},{key:"Mod-y",mac:"Mod-Shift-z",run:Dh,preventDefault:!0},{linux:"Ctrl-Shift-z",run:Dh,preventDefault:!0},{key:"Mod-u",run:Oh,preventDefault:!0},{key:"Alt-u",mac:"Mod-Shift-u",run:Th,preventDefault:!0}];function zh(t,e){return R.create(t.ranges.map(e),t.mainIndex)}function qh(t,e){return t.update({selection:e,scrollIntoView:!0,userEvent:"select"})}function _h({state:t,dispatch:e},i){let n=zh(t.selection,i);return!n.eq(t.selection)&&(e(qh(t,n)),!0)}function Kh(t,e){return R.cursor(e?t.to:t.from)}function jh(t,e){return _h(t,(i=>i.empty?t.moveByChar(i,e):Kh(i,e)))}function $h(t){return t.textDirectionAt(t.state.selection.main.head)==Vi.LTR}const Gh=t=>jh(t,!$h(t)),Uh=t=>jh(t,$h(t));function Jh(t,e){return _h(t,(i=>i.empty?t.moveByGroup(i,e):Kh(i,e)))}function Xh(t,e,i){if(e.type.prop(i))return!0;let n=e.to-e.from;return n&&(n>2||/[^\s,.;:]/.test(t.sliceDoc(e.from,e.to)))||e.firstChild}function Yh(t,e,i){let n,r,s=el(t).resolveInner(e.head),o=i?Zs.closedBy:Zs.openedBy;for(let n=e.head;;){let e=i?s.childAfter(n):s.childBefore(n);if(!e)break;Xh(t,e,o)?s=e:n=i?e.to:e.from}return r=s.type.prop(o)&&(n=i?Fl(t,s.from,1):Fl(t,s.to,-1))&&n.matched?i?n.end.to:n.end.from:i?s.to:s.from,R.cursor(r,i?-1:1)}function Qh(t,e){return _h(t,(i=>{if(!i.empty)return Kh(i,e);let n=t.moveVertically(i,e);return n.head!=i.head?n:t.moveToLineBoundary(i,e)}))}const Zh=t=>Qh(t,!1),ta=t=>Qh(t,!0);function ea(t){return Math.max(t.defaultLineHeight,Math.min(t.dom.clientHeight,innerHeight)-5)}function ia(t,e){let{state:i}=t,n=zh(i.selection,(i=>i.empty?t.moveVertically(i,e,ea(t)):Kh(i,e)));if(n.eq(i.selection))return!1;let r,s=t.coordsAtPos(i.selection.main.head),o=t.scrollDOM.getBoundingClientRect();return s&&s.top>o.top&&s.bottomia(t,!1),ra=t=>ia(t,!0);function sa(t,e,i){let n=t.lineBlockAt(e.head),r=t.moveToLineBoundary(e,i);if(r.head==e.head&&r.head!=(i?n.to:n.from)&&(r=t.moveToLineBoundary(e,i,!1)),!i&&r.head==n.from&&n.length){let i=/^\s*/.exec(t.state.sliceDoc(n.from,Math.min(n.from+100,n.to)))[0].length;i&&e.head!=n.from+i&&(r=R.cursor(n.from+i))}return r}function oa(t,e){let i=zh(t.state.selection,(t=>{let i=e(t);return R.range(t.anchor,i.head,i.goalColumn)}));return!i.eq(t.state.selection)&&(t.dispatch(qh(t.state,i)),!0)}function la(t,e){return oa(t,(i=>t.moveByChar(i,e)))}const ha=t=>la(t,!$h(t)),aa=t=>la(t,$h(t));function ca(t,e){return oa(t,(i=>t.moveByGroup(i,e)))}function ua(t,e){return oa(t,(i=>t.moveVertically(i,e)))}const fa=t=>ua(t,!1),da=t=>ua(t,!0);function pa(t,e){return oa(t,(i=>t.moveVertically(i,e,ea(t))))}const ga=t=>pa(t,!1),ma=t=>pa(t,!0),va=({state:t,dispatch:e})=>(e(qh(t,{anchor:0})),!0),wa=({state:t,dispatch:e})=>(e(qh(t,{anchor:t.doc.length})),!0),ya=({state:t,dispatch:e})=>(e(qh(t,{anchor:t.selection.main.anchor,head:0})),!0),ba=({state:t,dispatch:e})=>(e(qh(t,{anchor:t.selection.main.anchor,head:t.doc.length})),!0);function xa(t,e){if(t.state.readOnly)return!1;let i="delete.selection",{state:n}=t,r=n.changeByRange((n=>{let{from:r,to:s}=n;if(r==s){let n=e(r);nr&&(i="delete.forward",n=ka(t,n,!0)),r=Math.min(r,n),s=Math.max(s,n)}else r=ka(t,r,!1),s=ka(t,s,!0);return r==s?{range:n}:{changes:{from:r,to:s},range:R.cursor(r)}}));return!r.changes.empty&&(t.dispatch(n.update(r,{scrollIntoView:!0,userEvent:i,effects:"delete.selection"==i?Or.announce.of(n.phrase("Selection deleted")):void 0})),!0)}function ka(t,e,i){if(t instanceof Or)for(let n of t.state.facet(Or.atomicRanges).map((e=>e(t))))n.between(e,e,((t,n)=>{te&&(e=i?n:t)}));return e}const Sa=(t,e)=>xa(t,(i=>{let n,r,{state:s}=t,o=s.doc.lineAt(i);if(!e&&i>o.from&&iSa(t,!1),Ma=t=>Sa(t,!0),Ca=(t,e)=>xa(t,(i=>{let n=i,{state:r}=t,s=r.doc.lineAt(n),o=r.charCategorizer(n);for(let t=null;;){if(n==(e?s.to:s.from)){n==i&&s.number!=(e?r.doc.lines:1)&&(n+=e?1:-1);break}let l=d(s.text,n-s.from,e)+s.from,h=s.text.slice(Math.min(n,l)-s.from,Math.max(n,l)-s.from),a=o(h);if(null!=t&&a!=t)break;" "==h&&n==i||(t=a),n=l}return n})),Da=t=>Ca(t,!1),Oa=t=>xa(t,(e=>{let i=t.lineBlockAt(e).to;return e=r.number){let t=e[e.length-1];t.to=s.to,t.ranges.push(n)}else e.push({from:r.from,to:s.to,ranges:[n]});i=s.number+1}return e}function Ea(t,e,i){if(t.readOnly)return!1;let n=[],r=[];for(let e of Ta(t)){if(i?e.to==t.doc.length:0==e.from)continue;let s=t.doc.lineAt(i?e.to+1:e.from-1),o=s.length+1;if(i){n.push({from:e.to,to:s.to},{from:e.from,insert:s.text+t.lineBreak});for(let i of e.ranges)r.push(R.range(Math.min(t.doc.length,i.anchor+o),Math.min(t.doc.length,i.head+o)))}else{n.push({from:s.from,to:e.from},{from:e.to,insert:t.lineBreak+s.text});for(let t of e.ranges)r.push(R.range(t.anchor-o,t.head-o))}}return!!n.length&&(e(t.update({changes:n,scrollIntoView:!0,selection:R.create(r,t.selection.mainIndex),userEvent:"move.line"})),!0)}function Ba(t,e,i){if(t.readOnly)return!1;let n=[];for(let e of Ta(t))i?n.push({from:e.from,insert:t.doc.slice(e.from,e.to)+t.lineBreak}):n.push({from:e.to,insert:t.lineBreak+t.doc.slice(e.from,e.to)});return e(t.update({changes:n,scrollIntoView:!0,userEvent:"input.copyline"})),!0}const Ra=La(!1);function La(t){return({state:i,dispatch:n})=>{if(i.readOnly)return!1;let r=i.changeByRange((n=>{let{from:r,to:s}=n,o=i.doc.lineAt(r),l=!t&&r==s&&function(t,e){if(/\(\)|\[\]|\{\}/.test(t.sliceDoc(e-1,e+1)))return{from:e,to:e};let i,n=el(t).resolveInner(e),r=n.childBefore(e),s=n.childAfter(e);return r&&s&&r.to<=e&&s.from>=e&&(i=r.type.prop(Zs.closedBy))&&i.indexOf(s.name)>-1&&t.doc.lineAt(r.to).from==t.doc.lineAt(s.from).from?{from:r.to,to:s.from}:null}(i,r);t&&(r=s=(s<=o.to?o:i.doc.lineAt(s)).to);let h=new ml(i,{simulateBreak:r,simulateDoubleBreak:!!l}),a=gl(h,r);for(null==a&&(a=/^\s*/.exec(i.doc.lineAt(r).text)[0].length);so.from&&r{let r=[];for(let s=n.from;s<=n.to;){let o=t.doc.lineAt(s);o.number>i&&(n.empty||n.to>o.from)&&(e(o,r,n),i=o.number),s=o.to+1}let s=t.changes(r);return{changes:r,range:R.range(s.mapPos(n.anchor,1),s.mapPos(n.head,1))}}))}const Pa=[{key:"Alt-ArrowLeft",mac:"Ctrl-ArrowLeft",run:t=>_h(t,(e=>Yh(t.state,e,!$h(t)))),shift:t=>oa(t,(e=>Yh(t.state,e,!$h(t))))},{key:"Alt-ArrowRight",mac:"Ctrl-ArrowRight",run:t=>_h(t,(e=>Yh(t.state,e,$h(t)))),shift:t=>oa(t,(e=>Yh(t.state,e,$h(t))))},{key:"Alt-ArrowUp",run:({state:t,dispatch:e})=>Ea(t,e,!1)},{key:"Shift-Alt-ArrowUp",run:({state:t,dispatch:e})=>Ba(t,e,!1)},{key:"Alt-ArrowDown",run:({state:t,dispatch:e})=>Ea(t,e,!0)},{key:"Shift-Alt-ArrowDown",run:({state:t,dispatch:e})=>Ba(t,e,!0)},{key:"Escape",run:({state:t,dispatch:e})=>{let i=t.selection,n=null;return i.ranges.length>1?n=R.create([i.main]):i.main.empty||(n=R.create([R.cursor(i.main.head)])),!!n&&(e(qh(t,n)),!0)}},{key:"Mod-Enter",run:La(!0)},{key:"Alt-l",mac:"Ctrl-l",run:({state:t,dispatch:e})=>{let i=Ta(t).map((({from:e,to:i})=>R.range(e,Math.min(i+1,t.doc.length))));return e(t.update({selection:R.create(i),userEvent:"select"})),!0}},{key:"Mod-i",run:({state:t,dispatch:e})=>{let i=zh(t.selection,(e=>{var i;let n=el(t).resolveInner(e.head,1);for(;!(n.from=e.to||n.to>e.to&&n.from<=e.from)&&(null===(i=n.parent)||void 0===i?void 0:i.parent);)n=n.parent;return R.range(n.to,n.from)}));return e(qh(t,i)),!0},preventDefault:!0},{key:"Mod-[",run:({state:t,dispatch:e})=>!t.readOnly&&(e(t.update(Na(t,((e,i)=>{let n=/^\s*/.exec(e.text)[0];if(!n)return;let r=zt(n,t.tabSize),s=0,o=pl(t,Math.max(0,r-dl(t)));for(;s!t.readOnly&&(e(t.update(Na(t,((e,i)=>{i.push({from:e.from,insert:t.facet(fl)})})),{userEvent:"input.indent"})),!0)},{key:"Mod-Alt-\\",run:({state:t,dispatch:e})=>{if(t.readOnly)return!1;let i=Object.create(null),n=new ml(t,{overrideIndentation:t=>{let e=i[t];return null==e?-1:e}}),r=Na(t,((e,r,s)=>{let o=gl(n,e.from);if(null==o)return;/\S/.test(e.text)||(o=0);let l=/^\s*/.exec(e.text)[0],h=pl(t,o);(l!=h||s.from{if(t.state.readOnly)return!1;let{state:e}=t,i=e.changes(Ta(e).map((({from:t,to:i})=>(t>0?t--:it.moveVertically(e,!0))).map(i);return t.dispatch({changes:i,selection:n,scrollIntoView:!0,userEvent:"delete.line"}),!0}},{key:"Shift-Mod-\\",run:({state:t,dispatch:e})=>function(t,e,i){let n=!1,r=zh(t.selection,(e=>{let r=Fl(t,e.head,-1)||Fl(t,e.head,1)||e.head>0&&Fl(t,e.head-1,1)||e.head{let e=mh(t.state);return e.line?dh(t):!!e.block&&gh(t)}},{key:"Alt-A",run:ph}].concat([{key:"ArrowLeft",run:Gh,shift:ha,preventDefault:!0},{key:"Mod-ArrowLeft",mac:"Alt-ArrowLeft",run:t=>Jh(t,!$h(t)),shift:t=>ca(t,!$h(t)),preventDefault:!0},{mac:"Cmd-ArrowLeft",run:t=>_h(t,(e=>sa(t,e,!$h(t)))),shift:t=>oa(t,(e=>sa(t,e,!$h(t)))),preventDefault:!0},{key:"ArrowRight",run:Uh,shift:aa,preventDefault:!0},{key:"Mod-ArrowRight",mac:"Alt-ArrowRight",run:t=>Jh(t,$h(t)),shift:t=>ca(t,$h(t)),preventDefault:!0},{mac:"Cmd-ArrowRight",run:t=>_h(t,(e=>sa(t,e,$h(t)))),shift:t=>oa(t,(e=>sa(t,e,$h(t)))),preventDefault:!0},{key:"ArrowUp",run:Zh,shift:fa,preventDefault:!0},{mac:"Cmd-ArrowUp",run:va,shift:ya},{mac:"Ctrl-ArrowUp",run:na,shift:ga},{key:"ArrowDown",run:ta,shift:da,preventDefault:!0},{mac:"Cmd-ArrowDown",run:wa,shift:ba},{mac:"Ctrl-ArrowDown",run:ra,shift:ma},{key:"PageUp",run:na,shift:ga},{key:"PageDown",run:ra,shift:ma},{key:"Home",run:t=>_h(t,(e=>sa(t,e,!1))),shift:t=>oa(t,(e=>sa(t,e,!1))),preventDefault:!0},{key:"Mod-Home",run:va,shift:ya},{key:"End",run:t=>_h(t,(e=>sa(t,e,!0))),shift:t=>oa(t,(e=>sa(t,e,!0))),preventDefault:!0},{key:"Mod-End",run:wa,shift:ba},{key:"Enter",run:Ra},{key:"Mod-a",run:({state:t,dispatch:e})=>(e(t.update({selection:{anchor:0,head:t.doc.length},userEvent:"select"})),!0)},{key:"Backspace",run:Aa,shift:Aa},{key:"Delete",run:Ma},{key:"Mod-Backspace",mac:"Alt-Backspace",run:Da},{key:"Mod-Delete",mac:"Alt-Delete",run:t=>Ca(t,!0)},{mac:"Mod-Backspace",run:t=>xa(t,(e=>{let i=t.lineBlockAt(e).from;return e>i?i:Math.max(0,e-1)}))},{mac:"Mod-Delete",run:Oa}].concat([{key:"Ctrl-b",run:Gh,shift:ha,preventDefault:!0},{key:"Ctrl-f",run:Uh,shift:aa},{key:"Ctrl-p",run:Zh,shift:fa},{key:"Ctrl-n",run:ta,shift:da},{key:"Ctrl-a",run:t=>_h(t,(e=>R.cursor(t.lineBlockAt(e.head).from,1))),shift:t=>oa(t,(e=>R.cursor(t.lineBlockAt(e.head).from)))},{key:"Ctrl-e",run:t=>_h(t,(e=>R.cursor(t.lineBlockAt(e.head).to,-1))),shift:t=>oa(t,(e=>R.cursor(t.lineBlockAt(e.head).to)))},{key:"Ctrl-d",run:Ma},{key:"Ctrl-h",run:Aa},{key:"Ctrl-k",run:Oa},{key:"Ctrl-Alt-h",run:Da},{key:"Ctrl-o",run:({state:t,dispatch:i})=>{if(t.readOnly)return!1;let n=t.changeByRange((t=>({changes:{from:t.from,to:t.to,insert:e.of(["",""])},range:R.cursor(t.from)})));return i(t.update(n,{scrollIntoView:!0,userEvent:"input"})),!0}},{key:"Ctrl-t",run:({state:t,dispatch:e})=>{if(t.readOnly)return!1;let i=t.changeByRange((e=>{if(!e.empty||0==e.from||e.from==t.doc.length)return{range:e};let i=e.from,n=t.doc.lineAt(i),r=i==n.from?i-1:d(n.text,i-n.from,!1)+n.from,s=i==n.to?i+1:d(n.text,i-n.from,!0)+n.from;return{changes:{from:r,to:s,insert:t.doc.slice(i,s).append(t.doc.slice(r,i))},range:R.cursor(s)}}));return!i.changes.empty&&(e(t.update(i,{scrollIntoView:!0,userEvent:"move.character"})),!0)}},{key:"Ctrl-v",run:ra}].map((t=>({mac:t.key,run:t.run,shift:t.shift})))));function Ia(){var t=arguments[0];"string"==typeof t&&(t=document.createElement(t));var e=1,i=arguments[1];if(i&&"object"==typeof i&&null==i.nodeType&&!Array.isArray(i)){for(var n in i)if(Object.prototype.hasOwnProperty.call(i,n)){var r=i[n];"string"==typeof r?t.setAttribute(n,r):null!=r&&(t[n]=r)}e++}for(;et.normalize("NFKD"):t=>t;class Ha{constructor(t,e,i=0,n=t.length,r,s){this.test=s,this.value={from:0,to:0},this.done=!1,this.matches=[],this.buffer="",this.bufferPos=0,this.iter=t.iterRange(i,n),this.bufferStart=i,this.normalize=r?t=>r(Wa(t)):Wa,this.query=this.normalize(e)}peek(){if(this.bufferPos==this.buffer.length){if(this.bufferStart+=this.buffer.length,this.iter.next(),this.iter.done)return-1;this.bufferPos=0,this.buffer=this.iter.value}return w(this.buffer,this.bufferPos)}next(){for(;this.matches.length;)this.matches.pop();return this.nextOverlapping()}nextOverlapping(){for(;;){let t=this.peek();if(t<0)return this.done=!0,this;let e=y(t),i=this.bufferStart+this.bufferPos;this.bufferPos+=b(t);let n=this.normalize(e);for(let t=0,r=i;;t++){let s=n.charCodeAt(t),o=this.match(s,r);if(o)return this.value=o,this;if(t==n.length-1)break;r==i&&tthis.to&&(this.curLine=this.curLine.slice(0,this.to-this.curLineStart)),this.iter.next())}nextLine(){this.curLineStart=this.curLineStart+this.curLine.length+1,this.curLineStart>this.to?this.curLine="":this.getLine(0)}next(){for(let t=this.matchPos-this.curLineStart;;){this.re.lastIndex=t;let e=this.matchPos<=this.to&&this.re.exec(this.curLine);if(e){let i=this.curLineStart+e.index,n=i+e[0].length;if(this.matchPos=$a(this.text,n+(i==n?1:0)),i==this.curLineStart+this.curLine.length&&this.nextLine(),(ithis.value.to)&&(!this.test||this.test(i,n,e)))return this.value={from:i,to:n,match:e},this;t=this.matchPos-this.curLineStart}else{if(!(this.curLineStart+this.curLine.length=i||n.to<=e){let n=new Ka(e,t.sliceString(e,i));return _a.set(t,n),n}if(n.from==e&&n.to==i)return n;let{text:r,from:s}=n;return s>e&&(r=t.sliceString(e,s)+r,s=e),n.to=this.to?this.to:this.text.lineAt(t).to}next(){for(;;){let t=this.re.lastIndex=this.matchPos-this.flat.from,e=this.re.exec(this.flat.text);if(e&&!e[0]&&e.index==t&&(this.re.lastIndex=t+1,e=this.re.exec(this.flat.text)),e){let t=this.flat.from+e.index,i=t+e[0].length;if((this.flat.to>=this.to||e.index+e[0].length<=this.flat.text.length-10)&&(!this.test||this.test(t,i,e)))return this.value={from:t,to:i,match:e},this.matchPos=$a(this.text,i+(t==i?1:0)),this}if(this.flat.to==this.to)return this.done=!0,this;this.flat=Ka.get(this.text,this.flat.from,this.chunkEnd(this.flat.from+2*this.flat.text.length))}}}function $a(t,e){if(e>=t.length)return e;let i,n=t.lineAt(e);for(;e=56320&&i<57344;)e++;return e}"undefined"!=typeof Symbol&&(qa.prototype[Symbol.iterator]=ja.prototype[Symbol.iterator]=function(){return this});const Ga={highlightWordAroundCursor:!1,minSelectionLength:1,maxMatches:100,wholeWords:!1},Ua=P.define({combine:t=>At(t,Ga,{highlightWordAroundCursor:(t,e)=>t||e,minSelectionLength:Math.min,maxMatches:Math.min})});function Ja(t){let e=[tc,Za];return t&&e.push(Ua.of(t)),e}const Xa=ii.mark({class:"cm-selectionMatch"}),Ya=ii.mark({class:"cm-selectionMatch cm-selectionMatch-main"});function Qa(t,e,i,n){return!(0!=i&&t(e.sliceDoc(i-1,i))==yt.Word||n!=e.doc.length&&t(e.sliceDoc(n,n+1))==yt.Word)}const Za=Di.fromClass(class{constructor(t){this.decorations=this.getDeco(t)}update(t){(t.selectionSet||t.docChanged||t.viewportChanged)&&(this.decorations=this.getDeco(t.view))}getDeco(t){let e=t.state.facet(Ua),{state:i}=t,n=i.selection;if(n.ranges.length>1)return ii.none;let r,s=n.main,o=null;if(s.empty){if(!e.highlightWordAroundCursor)return ii.none;let t=i.wordAt(s.head);if(!t)return ii.none;o=i.charCategorizer(s.head),r=i.sliceDoc(t.from,t.to)}else{let t=s.to-s.from;if(t200)return ii.none;if(e.wholeWords){if(r=i.sliceDoc(s.from,s.to),o=i.charCategorizer(s.head),!Qa(o,i,s.from,s.to)||!function(t,e,i,n){return t(e.sliceDoc(i,i+1))==yt.Word&&t(e.sliceDoc(n-1,n))==yt.Word}(o,i,s.from,s.to))return ii.none}else if(r=i.sliceDoc(s.from,s.to).trim(),!r)return ii.none}let l=[];for(let n of t.visibleRanges){let t=new Ha(i.doc,r,n.from,n.to);for(;!t.next().done;){let{from:n,to:r}=t.value;if((!o||Qa(o,i,n,r))&&(s.empty&&n<=s.from&&r>=s.to?l.push(Ya.range(n,r)):(n>=s.to||r<=s.from)&&l.push(Xa.range(n,r)),l.length>e.maxMatches))return ii.none}}return ii.set(l)}},{decorations:t=>t.decorations}),tc=Or.baseTheme({".cm-selectionMatch":{backgroundColor:"#99ff7780"},".cm-searchMatch .cm-selectionMatch":{backgroundColor:"transparent"}}),ec=P.define({combine:t=>At(t,{top:!1,caseSensitive:!1,literal:!1,wholeWord:!1,createPanel:t=>new Cc(t)})});class ic{constructor(t){this.search=t.search,this.caseSensitive=!!t.caseSensitive,this.literal=!!t.literal,this.regexp=!!t.regexp,this.replace=t.replace||"",this.valid=!!this.search&&(!this.regexp||function(t){try{return new RegExp(t,za),!0}catch(t){return!1}}(this.search)),this.unquoted=this.unquote(this.search),this.wholeWord=!!t.wholeWord}unquote(t){return this.literal?t:t.replace(/\\([nrt\\])/g,((t,e)=>"n"==e?"\n":"r"==e?"\r":"t"==e?"\t":"\\"))}eq(t){return this.search==t.search&&this.replace==t.replace&&this.caseSensitive==t.caseSensitive&&this.regexp==t.regexp&&this.wholeWord==t.wholeWord}create(){return this.regexp?new ac(this):new sc(this)}getCursor(t,e=0,i){let n=t.doc?t:St.create({doc:t});return null==i&&(i=n.doc.length),this.regexp?oc(this,n,e,i):rc(this,n,e,i)}}class nc{constructor(t){this.spec=t}}function rc(t,e,i,n){return new Ha(e.doc,t.unquoted,i,n,t.caseSensitive?void 0:t=>t.toLowerCase(),t.wholeWord?function(t,e){return(i,n,r,s)=>((s>i||s+r.length=e)return null;n.push(i.value)}return n}highlight(t,e,i,n){let r=rc(this.spec,t,Math.max(0,e-this.spec.unquoted.length),Math.min(i+this.spec.unquoted.length,t.doc.length));for(;!r.next().done;)n(r.value.from,r.value.to)}}function oc(t,e,i,n){return new qa(e.doc,t.search,{ignoreCase:!t.caseSensitive,test:t.wholeWord?(r=e.charCategorizer(e.selection.main.head),(t,e,i)=>!i[0].length||(r(lc(i.input,i.index))!=yt.Word||r(hc(i.input,i.index))!=yt.Word)&&(r(hc(i.input,i.index+i[0].length))!=yt.Word||r(lc(i.input,i.index+i[0].length))!=yt.Word)):void 0},i,n);var r}function lc(t,e){return t.slice(d(t,e,!1),e)}function hc(t,e){return t.slice(e,d(t,e))}class ac extends nc{nextMatch(t,e,i){let n=oc(this.spec,t,i,t.doc.length).next();return n.done&&(n=oc(this.spec,t,0,e).next()),n.done?null:n.value}prevMatchInRange(t,e,i){for(let n=1;;n++){let r=Math.max(e,i-1e4*n),s=oc(this.spec,t,r,i),o=null;for(;!s.next().done;)o=s.value;if(o&&(r==e||o.from>r+10))return o;if(r==e)return null}}prevMatch(t,e,i){return this.prevMatchInRange(t,0,e)||this.prevMatchInRange(t,i,t.doc.length)}getReplacement(t){return this.spec.unquote(this.spec.replace.replace(/\$([$&\d+])/g,((e,i)=>"$"==i?"$":"&"==i?t.match[0]:"0"!=i&&+i=e)return null;n.push(i.value)}return n}highlight(t,e,i,n){let r=oc(this.spec,t,Math.max(0,e-250),Math.min(i+250,t.doc.length));for(;!r.next().done;)n(r.value.from,r.value.to)}}const cc=ut.define(),uc=ut.define(),fc=q.define({create:t=>new dc(Ac(t).create(),null),update(t,e){for(let i of e.effects)i.is(cc)?t=new dc(i.value.create(),t.panel):i.is(uc)&&(t=new dc(t.query,i.value?Sc:null));return t},provide:t=>Ts.from(t,(t=>t.panel))});class dc{constructor(t,e){this.query=t,this.panel=e}}const pc=ii.mark({class:"cm-searchMatch"}),gc=ii.mark({class:"cm-searchMatch cm-searchMatch-selected"}),mc=Di.fromClass(class{constructor(t){this.view=t,this.decorations=this.highlight(t.state.field(fc))}update(t){let e=t.state.field(fc);(e!=t.startState.field(fc)||t.docChanged||t.selectionSet||t.viewportChanged)&&(this.decorations=this.highlight(e))}highlight({query:t,panel:e}){if(!e||!t.spec.valid)return ii.none;let{view:i}=this,n=new Et;for(let e=0,r=i.visibleRanges,s=r.length;er[e+1].from-500;)l=r[++e].to;t.highlight(i.state,o,l,((t,e)=>{let r=i.state.selection.ranges.some((i=>i.from==t&&i.to==e));n.add(t,e,r?gc:pc)}))}return n.finish()}},{decorations:t=>t.decorations});function vc(t){return e=>{let i=e.state.field(fc,!1);return i&&i.query.spec.valid?t(e,i):Mc(e)}}const wc=vc(((t,{query:e})=>{let{to:i}=t.state.selection.main,n=e.nextMatch(t.state,i,i);return!!n&&(t.dispatch({selection:{anchor:n.from,head:n.to},scrollIntoView:!0,effects:Tc(t,n),userEvent:"select.search"}),!0)})),yc=vc(((t,{query:e})=>{let{state:i}=t,{from:n}=i.selection.main,r=e.prevMatch(i,n,n);return!!r&&(t.dispatch({selection:{anchor:r.from,head:r.to},scrollIntoView:!0,effects:Tc(t,r),userEvent:"select.search"}),!0)})),bc=vc(((t,{query:e})=>{let i=e.matchAll(t.state,1e3);return!(!i||!i.length)&&(t.dispatch({selection:R.create(i.map((t=>R.range(t.from,t.to)))),userEvent:"select.search.matches"}),!0)})),xc=vc(((t,{query:e})=>{let{state:i}=t,{from:n,to:r}=i.selection.main;if(i.readOnly)return!1;let s=e.nextMatch(i,n,n);if(!s)return!1;let o,l,h=[],a=[];if(s.from==n&&s.to==r&&(l=i.toText(e.getReplacement(s)),h.push({from:s.from,to:s.to,insert:l}),s=e.nextMatch(i,s.from,s.to),a.push(Or.announce.of(i.phrase("replaced match on line $",i.doc.lineAt(n).number)+"."))),s){let e=0==h.length||h[0].from>=s.to?0:s.to-s.from-l.length;o={anchor:s.from-e,head:s.to-e},a.push(Tc(t,s))}return t.dispatch({changes:h,selection:o,scrollIntoView:!!o,effects:a,userEvent:"input.replace"}),!0})),kc=vc(((t,{query:e})=>{if(t.state.readOnly)return!1;let i=e.matchAll(t.state,1e9).map((t=>{let{from:i,to:n}=t;return{from:i,to:n,insert:e.getReplacement(t)}}));if(!i.length)return!1;let n=t.state.phrase("replaced $ matches",i.length)+".";return t.dispatch({changes:i,effects:Or.announce.of(n),userEvent:"input.replace.all"}),!0}));function Sc(t){return t.state.facet(ec).createPanel(t)}function Ac(t,e){var i,n,r,s;let o=t.selection.main,l=o.empty||o.to>o.from+100?"":t.sliceDoc(o.from,o.to);if(e&&!l)return e;let h=t.facet(ec);return new ic({search:(null!==(i=null==e?void 0:e.literal)&&void 0!==i?i:h.literal)?l:l.replace(/\n/g,"\\n"),caseSensitive:null!==(n=null==e?void 0:e.caseSensitive)&&void 0!==n?n:h.caseSensitive,literal:null!==(r=null==e?void 0:e.literal)&&void 0!==r?r:h.literal,wholeWord:null!==(s=null==e?void 0:e.wholeWord)&&void 0!==s?s:h.wholeWord})}const Mc=t=>{let e=t.state.field(fc,!1);if(e&&e.panel){let i=Ms(t,Sc);if(!i)return!1;let n=i.dom.querySelector("[main-field]");if(n&&n!=t.root.activeElement){let i=Ac(t.state,e.query.spec);i.valid&&t.dispatch({effects:cc.of(i)}),n.focus(),n.select()}}else t.dispatch({effects:[uc.of(!0),e?cc.of(Ac(t.state,e.query.spec)):ut.appendConfig.of(Bc)]});return!0};class Cc{constructor(t){this.view=t;let e=this.query=t.state.field(fc).query.spec;function i(t,e,i){return Ia("button",{class:"cm-button",name:t,onclick:e,type:"button"},i)}this.commit=this.commit.bind(this),this.searchField=Ia("input",{value:e.search,placeholder:Dc(t,"Find"),"aria-label":Dc(t,"Find"),class:"cm-textfield",name:"search",form:"","main-field":"true",onchange:this.commit,onkeyup:this.commit}),this.replaceField=Ia("input",{value:e.replace,placeholder:Dc(t,"Replace"),"aria-label":Dc(t,"Replace"),class:"cm-textfield",name:"replace",form:"",onchange:this.commit,onkeyup:this.commit}),this.caseField=Ia("input",{type:"checkbox",name:"case",form:"",checked:e.caseSensitive,onchange:this.commit}),this.reField=Ia("input",{type:"checkbox",name:"re",form:"",checked:e.regexp,onchange:this.commit}),this.wordField=Ia("input",{type:"checkbox",name:"word",form:"",checked:e.wholeWord,onchange:this.commit}),this.dom=Ia("div",{onkeydown:t=>this.keydown(t),class:"cm-search"},[this.searchField,i("next",(()=>wc(t)),[Dc(t,"next")]),i("prev",(()=>yc(t)),[Dc(t,"previous")]),i("select",(()=>bc(t)),[Dc(t,"all")]),Ia("label",null,[this.caseField,Dc(t,"match case")]),Ia("label",null,[this.reField,Dc(t,"regexp")]),Ia("label",null,[this.wordField,Dc(t,"by word")]),...t.state.readOnly?[]:[Ia("br"),this.replaceField,i("replace",(()=>xc(t)),[Dc(t,"replace")]),i("replaceAll",(()=>kc(t)),[Dc(t,"replace all")])],Ia("button",{name:"close",onclick:()=>(t=>{let e=t.state.field(fc,!1);if(!e||!e.panel)return!1;let i=Ms(t,Sc);return i&&i.dom.contains(t.root.activeElement)&&t.focus(),t.dispatch({effects:uc.of(!1)}),!0})(t),"aria-label":Dc(t,"close"),type:"button"},["×"])])}commit(){let t=new ic({search:this.searchField.value,caseSensitive:this.caseField.checked,regexp:this.reField.checked,wholeWord:this.wordField.checked,replace:this.replaceField.value});t.eq(this.query)||(this.query=t,this.view.dispatch({effects:cc.of(t)}))}keydown(t){var e,i,n;e=this.view,i=t,n="search-panel",Hr(Vr(e.state),i,e,n)?t.preventDefault():13==t.keyCode&&t.target==this.searchField?(t.preventDefault(),(t.shiftKey?yc:wc)(this.view)):13==t.keyCode&&t.target==this.replaceField&&(t.preventDefault(),xc(this.view))}update(t){for(let e of t.transactions)for(let t of e.effects)t.is(cc)&&!t.value.eq(this.query)&&this.setQuery(t.value)}setQuery(t){this.query=t,this.searchField.value=t.search,this.replaceField.value=t.replace,this.caseField.checked=t.caseSensitive,this.reField.checked=t.regexp,this.wordField.checked=t.wholeWord}mount(){this.searchField.select()}get pos(){return 80}get top(){return this.view.state.facet(ec).top}}function Dc(t,e){return t.state.phrase(e)}const Oc=/[\s\.,:;?!]/;function Tc(t,{from:e,to:i}){let n=t.state.doc.lineAt(e),r=t.state.doc.lineAt(i).to,s=Math.max(n.from,e-30),o=Math.min(r,i+30),l=t.state.sliceDoc(s,o);if(s!=n.from)for(let t=0;t<30;t++)if(!Oc.test(l[t+1])&&Oc.test(l[t])){l=l.slice(t);break}if(o!=r)for(let t=l.length-1;t>l.length-30;t--)if(!Oc.test(l[t-1])&&Oc.test(l[t])){l=l.slice(0,t);break}return Or.announce.of(`${t.state.phrase("current match")}. ${l} ${t.state.phrase("on line")} ${n.number}.`)}const Ec=Or.baseTheme({".cm-panel.cm-search":{padding:"2px 6px 4px",position:"relative","& [name=close]":{position:"absolute",top:"0",right:"4px",backgroundColor:"inherit",border:"none",font:"inherit",padding:0,margin:0},"& input, & button, & label":{margin:".2em .6em .2em 0"},"& input[type=checkbox]":{marginRight:".2em"},"& label":{fontSize:"80%",whiteSpace:"pre"}},"&light .cm-searchMatch":{backgroundColor:"#ffff0054"},"&dark .cm-searchMatch":{backgroundColor:"#00ffff8a"},"&light .cm-searchMatch-selected":{backgroundColor:"#ff6a0054"},"&dark .cm-searchMatch-selected":{backgroundColor:"#ff00ff8a"}}),Bc=[fc,U.lowest(mc),Ec],Rc="#e06c75",Lc="#abb2bf",Nc="#7d8799",Pc="#d19a66",Ic="#2c313a",Vc="#282c34",Wc="#353a42",Hc="#528bff",Fc=[Or.theme({"&":{color:Lc,backgroundColor:Vc},".cm-content":{caretColor:Hc},".cm-cursor, .cm-dropCursor":{borderLeftColor:Hc},"&.cm-focused .cm-selectionBackground, .cm-selectionBackground, .cm-content ::selection":{backgroundColor:"#3E4451"},".cm-panels":{backgroundColor:"#21252b",color:Lc},".cm-panels.cm-panels-top":{borderBottom:"2px solid black"},".cm-panels.cm-panels-bottom":{borderTop:"2px solid black"},".cm-searchMatch":{backgroundColor:"#72a1ff59",outline:"1px solid #457dff"},".cm-searchMatch.cm-searchMatch-selected":{backgroundColor:"#6199ff2f"},".cm-activeLine":{backgroundColor:"#6699ff0b"},".cm-selectionMatch":{backgroundColor:"#aafe661a"},"&.cm-focused .cm-matchingBracket, &.cm-focused .cm-nonmatchingBracket":{backgroundColor:"#bad0f847",outline:"1px solid #515a6b"},".cm-gutters":{backgroundColor:Vc,color:Nc,border:"none"},".cm-activeLineGutter":{backgroundColor:Ic},".cm-foldPlaceholder":{backgroundColor:"transparent",border:"none",color:"#ddd"},".cm-tooltip":{border:"none",backgroundColor:Wc},".cm-tooltip .cm-tooltip-arrow:before":{borderTopColor:"transparent",borderBottomColor:"transparent"},".cm-tooltip .cm-tooltip-arrow:after":{borderTopColor:Wc,borderBottomColor:Wc},".cm-tooltip-autocomplete":{"& > ul > li[aria-selected]":{backgroundColor:Ic,color:Lc}}},{dark:!0}),Dl(Sl.define([{tag:Xo.keyword,color:"#c678dd"},{tag:[Xo.name,Xo.deleted,Xo.character,Xo.propertyName,Xo.macroName],color:Rc},{tag:[Xo.function(Xo.variableName),Xo.labelName],color:"#61afef"},{tag:[Xo.color,Xo.constant(Xo.name),Xo.standard(Xo.name)],color:Pc},{tag:[Xo.definition(Xo.name),Xo.separator],color:Lc},{tag:[Xo.typeName,Xo.className,Xo.number,Xo.changed,Xo.annotation,Xo.modifier,Xo.self,Xo.namespace],color:"#e5c07b"},{tag:[Xo.operator,Xo.operatorKeyword,Xo.url,Xo.escape,Xo.regexp,Xo.link,Xo.special(Xo.string)],color:"#56b6c2"},{tag:[Xo.meta,Xo.comment],color:Nc},{tag:Xo.strong,fontWeight:"bold"},{tag:Xo.emphasis,fontStyle:"italic"},{tag:Xo.strikethrough,textDecoration:"line-through"},{tag:Xo.link,color:Nc,textDecoration:"underline"},{tag:Xo.heading,fontWeight:"bold",color:Rc},{tag:[Xo.atom,Xo.bool,Xo.special(Xo.variableName)],color:Pc},{tag:[Xo.processingInstruction,Xo.string,Xo.inserted],color:"#98c379"},{tag:Xo.invalid,color:"#ffffff"}]))];return t.createEditorState=function(t,e={}){const i=[$s(),Js,cs(),Ah(),$r(),Wl(),[nh,Ql],bs(),Ss(),gs,Ja(),Pr.of([...rh,...Pa,...Fh])];return e.placeholder&&i.push(function(t){return Di.fromClass(class{constructor(e){this.view=e,this.placeholder=ii.set([ii.widget({widget:new ms(t),side:1}).range(0)])}get decorations(){return this.view.state.doc.length?ii.none:this.placeholder}},{decorations:t=>t.decorations})}(e.placeholder)),e.oneDark&&i.push(Fc),St.create({doc:t,extensions:i})},t.createEditorView=function(t,e){return new Or({state:t,parent:e})},t.openSearchPanel=Mc,t}({}); diff --git a/tools/make-mv3.sh b/tools/make-mv3.sh index 78413ed525fb5..e50f85a464d1e 100755 --- a/tools/make-mv3.sh +++ b/tools/make-mv3.sh @@ -96,6 +96,7 @@ cp platform/mv3/extension/*.html "$UBOL_DIR"/ cp platform/mv3/extension/*.json "$UBOL_DIR"/ cp platform/mv3/extension/css/* "$UBOL_DIR"/css/ cp -R platform/mv3/extension/js/* "$UBOL_DIR"/js/ +cp -R platform/mv3/extension/lib "$UBOL_DIR"/ cp platform/mv3/"$PLATFORM"/ext-compat.js "$UBOL_DIR"/js/ 2>/dev/null || : cp platform/mv3/extension/img/* "$UBOL_DIR"/img/ cp -R platform/mv3/extension/_locales "$UBOL_DIR"/ From 9491fcdb2e9e0aecce7036cdb4ede55db610dc3e Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Wed, 14 May 2025 17:31:26 -0400 Subject: [PATCH 0927/1099] Update README.md --- platform/mv3/README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/platform/mv3/README.md b/platform/mv3/README.md index 28f0bdab2a395..4c4209f4d2155 100644 --- a/platform/mv3/README.md +++ b/platform/mv3/README.md @@ -7,13 +7,15 @@ The following assumes a linux environment. 1. Open Bash console 2. `git clone https://github.com/gorhill/uBlock.git` 3. `cd uBlock` -4. `make mv3-[platform]`, where `[platform]` is either `chromium` or `firefox` +4. `make mv3-[platform]`, where `[platform]` is either `chromium`, `edge`, `firefox`, or `safari` 5. This will fully build uBO Lite, and during the process filter lists will be downloaded from their respective remote servers Upon completion of the script, the resulting extension package will become present in: - Chromium: `dist/build/uBOLite.chromium` +- Edge: `dist/build/uBOLite.edge` - Firefox: `dist/build/uBOLite.firefox` +- Safari: `dist/build/uBOLite.safari` The folder `dist/build/mv3-data` will cache data fetched from remote servers, so as to avoid fetching repeatedly from remote servers with repeated build commands. Use `make cleanassets` to remove all locally cached filter lists if you want to build with latest versions of filter lists. From ae461f47d3b2953c062e9eed86e07ea26db87e1a Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Wed, 14 May 2025 17:31:49 -0400 Subject: [PATCH 0928/1099] Update README.md --- platform/mv3/README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/platform/mv3/README.md b/platform/mv3/README.md index 4c4209f4d2155..fd1efa2208bbe 100644 --- a/platform/mv3/README.md +++ b/platform/mv3/README.md @@ -13,9 +13,9 @@ The following assumes a linux environment. Upon completion of the script, the resulting extension package will become present in: - Chromium: `dist/build/uBOLite.chromium` -- Edge: `dist/build/uBOLite.edge` -- Firefox: `dist/build/uBOLite.firefox` -- Safari: `dist/build/uBOLite.safari` +- Edge: `dist/build/uBOLite.edge` +- Firefox: `dist/build/uBOLite.firefox` +- Safari: `dist/build/uBOLite.safari` The folder `dist/build/mv3-data` will cache data fetched from remote servers, so as to avoid fetching repeatedly from remote servers with repeated build commands. Use `make cleanassets` to remove all locally cached filter lists if you want to build with latest versions of filter lists. From 2322038e8f0895c485580ec9071632eb90fb0acd Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Wed, 14 May 2025 17:32:20 -0400 Subject: [PATCH 0929/1099] Update README.md --- platform/mv3/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform/mv3/README.md b/platform/mv3/README.md index fd1efa2208bbe..66163593968f0 100644 --- a/platform/mv3/README.md +++ b/platform/mv3/README.md @@ -29,5 +29,5 @@ All the final rulesets are present in the `dist/build/uBOLite.[platform]/ruleset --- -[1] https://github.com/gorhill/uBlock/blob/c4d324362fdb95ff8ef20f0b18f42f0eec955433/tools/make-mv3.sh +[1] https://github.com/gorhill/uBlock/blob/c4d324362fdb95ff8ef20f0b18f42f0eec955433/tools/make-mv3.sh
[2] https://github.com/gorhill/uBlock/blob/c4d324362fdb95ff8ef20f0b18f42f0eec955433/tools/make-mv3.sh#L103 From 907e33ef59d184ff13936fa2dcd9db1b19a6e4bb Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Wed, 14 May 2025 17:33:10 -0400 Subject: [PATCH 0930/1099] Update README.md --- platform/mv3/README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/platform/mv3/README.md b/platform/mv3/README.md index 66163593968f0..d80f9ceaa5096 100644 --- a/platform/mv3/README.md +++ b/platform/mv3/README.md @@ -30,4 +30,3 @@ All the final rulesets are present in the `dist/build/uBOLite.[platform]/ruleset --- [1] https://github.com/gorhill/uBlock/blob/c4d324362fdb95ff8ef20f0b18f42f0eec955433/tools/make-mv3.sh
-[2] https://github.com/gorhill/uBlock/blob/c4d324362fdb95ff8ef20f0b18f42f0eec955433/tools/make-mv3.sh#L103 From a551d1cc01097661dcb10db79b026c06b67fe0df Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Thu, 15 May 2025 07:26:40 -0400 Subject: [PATCH 0931/1099] [mv3] Editor font size --- platform/mv3/extension/css/settings.css | 1 + 1 file changed, 1 insertion(+) diff --git a/platform/mv3/extension/css/settings.css b/platform/mv3/extension/css/settings.css index 315dea79d0117..a9b4ad6d5433c 100644 --- a/platform/mv3/extension/css/settings.css +++ b/platform/mv3/extension/css/settings.css @@ -80,6 +80,7 @@ h3[data-i18n="filteringMode0Name"]::first-letter { #trustedSites { background-color: var(--surface-0); border: 1px solid var(--surface-3); + font-size: var(--monospace-size); min-height: 8rem; } From 4c99471a259f9c41090d058016438c290f5f8fe4 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Thu, 15 May 2025 11:20:52 -0400 Subject: [PATCH 0932/1099] [mv3] Avoid passing empty arrays to scripting.registerContentScripts Maybe related issue: https://github.com/uBlockOrigin/uBOL-home/issues/344 --- .../mv3/extension/js/scripting-manager.js | 45 ++++++++++++++----- 1 file changed, 34 insertions(+), 11 deletions(-) diff --git a/platform/mv3/extension/js/scripting-manager.js b/platform/mv3/extension/js/scripting-manager.js index 6730e031db031..ab674e0bb6a1c 100644 --- a/platform/mv3/extension/js/scripting-manager.js +++ b/platform/mv3/extension/js/scripting-manager.js @@ -143,10 +143,14 @@ function registerHighGeneric(context, genericDetails) { id: 'css-generichigh', css, allFrames: true, - matches, - excludeMatches, runAt: 'document_end', }; + if ( matches.length !== 0 ) { + directive.matches = matches; + } + if ( excludeMatches.length !== 0 ) { + directive.excludeMatches = excludeMatches; + } // register if ( registered === undefined ) { @@ -239,9 +243,12 @@ function registerGeneric(context, genericDetails) { js, allFrames: true, matches: [ '' ], - excludeMatches, runAt: 'document_idle', }; + if ( excludeMatches.length !== 0 ) { + directiveAll.excludeMatches = excludeMatches; + } + if ( registeredAll === undefined ) { // register context.toAdd.push(directiveAll); } else if ( // update @@ -317,10 +324,14 @@ function registerProcedural(context) { id: 'css-procedural', js, allFrames: true, - matches, - excludeMatches, runAt: 'document_start', }; + if ( matches.length !== 0 ) { + directive.matches = matches; + } + if ( excludeMatches.length !== 0 ) { + directive.excludeMatches = excludeMatches; + } // register if ( registered === undefined ) { @@ -379,10 +390,14 @@ function registerDeclarative(context) { id: 'css-declarative', js, allFrames: true, - matches, - excludeMatches, runAt: 'document_start', }; + if ( matches.length !== 0 ) { + directive.matches = matches; + } + if ( excludeMatches.length !== 0 ) { + directive.excludeMatches = excludeMatches; + } // register if ( registered === undefined ) { @@ -441,10 +456,14 @@ function registerSpecific(context) { id: 'css-specific', js, allFrames: true, - matches, - excludeMatches, runAt: 'document_start', }; + if ( matches.length !== 0 ) { + directive.matches = matches; + } + if ( excludeMatches.length !== 0 ) { + directive.excludeMatches = excludeMatches; + } // register if ( registered === undefined ) { @@ -518,12 +537,16 @@ function registerScriptlet(context, scriptletDetails) { id, js: [ `/rulesets/scripting/scriptlet/${id}.js` ], allFrames: true, - matches, - excludeMatches, matchOriginAsFallback: true, runAt: 'document_start', world: details.world, }; + if ( matches.length !== 0 ) { + directive.matches = matches; + } + if ( excludeMatches.length !== 0 ) { + directive.excludeMatches = excludeMatches; + } // register if ( registered === undefined ) { From aa696d0669be78a1b20543d88debb97c145a3df7 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Fri, 16 May 2025 08:52:36 -0400 Subject: [PATCH 0933/1099] Ignore lib directory when linting --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index fd65ac4cb4861..dc01c127dae4d 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,7 @@ "description": "npm dev tools", "main": "index.js", "scripts": { - "lint": "eslint --no-warn-ignored -- \"./src/js/*.js\" \"./src/js/**/*.js\" \"./**/*.json\" \"./platform/**/*.js\"", + "lint": "eslint --no-warn-ignored --ignore-pattern \"**/lib/\" --ignore-pattern \"**/npm/\" -- \"./src/js/*.js\" \"./src/js/**/*.js\" \"./**/*.json\" \"./platform/**/*.js\"", "test": "echo \"Error: no test specified\" && exit 1" }, "repository": { @@ -22,10 +22,10 @@ "npm": ">=11" }, "devDependencies": { + "eslint": "^9.17.0", "@eslint/compat": "^1.2.4", "@eslint/js": "^9.17.0", "@eslint/json": "^0.9.0", - "eslint": "^9.17.0", "eslint-formatter-compact": "^8.40.0", "globals": "^15.14.0" } From b2c9be14cfc491ead32a16bcd260d4f6cc8d8df0 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Fri, 16 May 2025 10:30:20 -0400 Subject: [PATCH 0934/1099] [mv3] Code review: matches is never empty --- .../mv3/extension/js/scripting-manager.js | 21 ++++++------------- 1 file changed, 6 insertions(+), 15 deletions(-) diff --git a/platform/mv3/extension/js/scripting-manager.js b/platform/mv3/extension/js/scripting-manager.js index ab674e0bb6a1c..a400ce856325b 100644 --- a/platform/mv3/extension/js/scripting-manager.js +++ b/platform/mv3/extension/js/scripting-manager.js @@ -142,12 +142,10 @@ function registerHighGeneric(context, genericDetails) { const directive = { id: 'css-generichigh', css, + matches, allFrames: true, runAt: 'document_end', }; - if ( matches.length !== 0 ) { - directive.matches = matches; - } if ( excludeMatches.length !== 0 ) { directive.excludeMatches = excludeMatches; } @@ -323,12 +321,10 @@ function registerProcedural(context) { const directive = { id: 'css-procedural', js, + matches, allFrames: true, runAt: 'document_start', }; - if ( matches.length !== 0 ) { - directive.matches = matches; - } if ( excludeMatches.length !== 0 ) { directive.excludeMatches = excludeMatches; } @@ -389,12 +385,10 @@ function registerDeclarative(context) { const directive = { id: 'css-declarative', js, + matches, allFrames: true, runAt: 'document_start', }; - if ( matches.length !== 0 ) { - directive.matches = matches; - } if ( excludeMatches.length !== 0 ) { directive.excludeMatches = excludeMatches; } @@ -455,12 +449,10 @@ function registerSpecific(context) { const directive = { id: 'css-specific', js, + matches, allFrames: true, runAt: 'document_start', }; - if ( matches.length !== 0 ) { - directive.matches = matches; - } if ( excludeMatches.length !== 0 ) { directive.excludeMatches = excludeMatches; } @@ -530,20 +522,19 @@ function registerScriptlet(context, scriptletDetails) { } if ( targetHostnames.length === 0 ) { continue; } matches.push(...ut.matchesFromHostnames(targetHostnames)); + normalizeMatches(matches); before.delete(id); // Important! const directive = { id, js: [ `/rulesets/scripting/scriptlet/${id}.js` ], + matches, allFrames: true, matchOriginAsFallback: true, runAt: 'document_start', world: details.world, }; - if ( matches.length !== 0 ) { - directive.matches = matches; - } if ( excludeMatches.length !== 0 ) { directive.excludeMatches = excludeMatches; } From 6d2123b196fcbb4acfcc8b1b33413737f6d09ae1 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sat, 17 May 2025 08:10:49 -0400 Subject: [PATCH 0935/1099] Update README.md --- README.md | 25 +++++++------------------ 1 file changed, 7 insertions(+), 18 deletions(-) diff --git a/README.md b/README.md index e069c7cdccc1b..d55ce59a1dd67 100644 --- a/README.md +++ b/README.md @@ -15,25 +15,14 @@ uBlock Origin (uBO) -

-BEWARE! uBO is (and has always been) COMPLETELY UNRELATED to the website ublock.org. -

-*** - -

-Get uBlock Origin for Firefox -Get uBlock Origin for Microsoft Edge -Get uBlock Origin for Opera -Get uBlock Origin for Thunderbird -

- -*** - -

-Get uBlock Origin for Chromium
-IMPORTANT: About Google Chrome's "This extension may soon no longer be supported" -

+| Browser | Install from ... | Status | +| :-------: | ---------------- | ------ | +| Get uBlock Origin for Firefox | Firefox Add-ons | [uBO works best on Firefox](https://github.com/gorhill/uBlock/wiki/uBlock-Origin-works-best-on-Firefox) | +| Get uBlock Origin for Opera | Opera Add-ons | +| Get uBlock Origin for Chromium | Chrome Web Store | About Google Chrome's "This extension may soon no longer be supported" | +| Get uBlock Origin for Microsoft Edge | Edge Add-ons | [No longer updated and stuck at 1.62.0](https://github.com/uBlockOrigin/uBlock-issues/discussions/3641) | +| Get uBlock Origin for Thunderbird | Thunderbird Add-ons | [No longer updated and stuck at 1.49.2](https://github.com/uBlockOrigin/uBlock-issues/issues/2928) | *** From ce4a4fcd75fd98f1370d3171cd9f8fb058a1ee31 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sun, 18 May 2025 08:50:54 -0400 Subject: [PATCH 0936/1099] [mv3] Minor CSS change --- platform/mv3/extension/css/popup.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform/mv3/extension/css/popup.css b/platform/mv3/extension/css/popup.css index 4fb540577b1ee..1afea78d31c63 100644 --- a/platform/mv3/extension/css/popup.css +++ b/platform/mv3/extension/css/popup.css @@ -13,7 +13,7 @@ :root body, :root.mobile body { - --font-size: 16px; + --font-size: 15px; --popup-gap: var(--font-size); --popup-gap-thin: calc(0.5 * var(--popup-gap)); --popup-gap-extra-thin: calc(0.25 * var(--popup-gap)); From 4aa16e940925a74bc6d53718a7b61e3ca0b6b5fb Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sun, 18 May 2025 08:57:48 -0400 Subject: [PATCH 0937/1099] [mv3] Add missing image --- platform/mv3/extension/img/icon_512.png | Bin 0 -> 16524 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 platform/mv3/extension/img/icon_512.png diff --git a/platform/mv3/extension/img/icon_512.png b/platform/mv3/extension/img/icon_512.png new file mode 100644 index 0000000000000000000000000000000000000000..b5f29734802ee03ca8a0da0a70abb4ae4db5ec3d GIT binary patch literal 16524 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 KeYjy0{(k`#umq(5 literal 0 HcmV?d00001 From 4c6f8222af9760bca9154e68570b80142fa1d5dd Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sun, 18 May 2025 09:00:28 -0400 Subject: [PATCH 0938/1099] Import translation work from https://crowdin.com/project/ublock --- platform/mv3/description/webstore.pt_PT.txt | 2 +- src/_locales/ka/messages.json | 14 +++++++------- src/_locales/tr/messages.json | 4 ++-- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/platform/mv3/description/webstore.pt_PT.txt b/platform/mv3/description/webstore.pt_PT.txt index cbfe9e6f9dfbb..96547aeb1c5e4 100644 --- a/platform/mv3/description/webstore.pt_PT.txt +++ b/platform/mv3/description/webstore.pt_PT.txt @@ -1,4 +1,4 @@ -uBO Lite (uBOL) is an MV3-based content blocker. +O uBO Lite (uBOL) é um bloqueador de conteúdo baseado em MV3. O conjunto de regras padrão corresponde ao conjunto de filtros padrão do uBlock Origin: diff --git a/src/_locales/ka/messages.json b/src/_locales/ka/messages.json index b95c3d428376d..3693dfe6eac21 100644 --- a/src/_locales/ka/messages.json +++ b/src/_locales/ka/messages.json @@ -68,7 +68,7 @@ "description": "Title for the advanced settings page" }, "popupPowerSwitchInfo": { - "message": "დაწკაპუნება: ამ საიტისთვის uBlock-ის ჩართვა/გამორთვა.\n\nCtrl+დაწკაპუნება: uBlock-ის მხოლოდ ამ გვერდზე გამორთვა.", + "message": "დაწკაპებით: ჩაირთვება/გამოირთვება uBlock ამ საიტზე.\n\nCtrl+დაწკაპებით: გამოირთვება uBlock მხოლოდ ამ გვერდზე.", "description": "English: Click: disable/enable uBlock₀ for this site.\n\nCtrl+click: disable uBlock₀ only on this page." }, "popupPowerSwitchInfo1": { @@ -364,7 +364,7 @@ "description": "English: " }, "settingsWebRTCIPAddressHiddenPrompt": { - "message": "WebRTC-სთვის ხელის შეშლა შიდა IP მისამართის გაჟონვაში", + "message": "WebRTC-ით შიდა IP-მისამართის გამჟღავნების აღკვეთა", "description": "English: " }, "settingPerSiteSwitchGroup": { @@ -388,7 +388,7 @@ "description": "" }, "settingsNoScriptingPrompt": { - "message": "JavaScript-ის გათიშვა", + "message": "გაითიშოს JavaScript", "description": "The default state for the per-site no-scripting switch" }, "settingsNoCSPReportsPrompt": { @@ -944,7 +944,7 @@ "description": "Header of 'Bug report' section in Support pane" }, "supportS4P1": { - "message": "თავად uBlock Origin-ის ხარვეზების მოსახსენებლად, გამოიყენეთ uBlockOrigin/uBlock-issue ხარვეზების აღსარიცხავი. დაგჭირდებათ GitHub-ანგარიში.", + "message": "ხარვეზების მოხსენებისთვის, რომლის წყაროცაა თავად uBlock Origin, გამოიყენეთ uBlockOrigin/uBlock-issue ხარვეზების აღსარიცხავი. დაგჭირდებათ GitHub-ანგარიში.", "description": "First paragraph of 'Bug report' section in Support pane" }, "supportS5H": { @@ -1060,7 +1060,7 @@ "description": "Shown in the About pane" }, "aboutCDNs": { - "message": "uBO-ს კუთვნილი ფილტრები ღიადაა განთავსებული შემდეგ CDN-ებზე:", + "message": "uBO საკუთარ ფილტრებს ღიად ათავსებს მოცემულ CDN-ებზე:", "description": "Shown in the About pane" }, "aboutCDNsInfo": { @@ -1092,7 +1092,7 @@ "description": "Message to display when an error occurred during restore" }, "aboutResetDataConfirm": { - "message": "თქვენ მიერ მითითებული ყველა პარამეტრი წაიშლება და uBlock₀ გაეშვება ხელახლა.\n\nნამდვილად გსურთ uBlock₀-ის ნაგულისხმევ პარამეტრებზე დაბრუნება?", + "message": "თქვენ მიერ მითითებული ყველა პარამეტრი წაიშლება და uBlock₀ გაეშვება ხელახლა.\n\nნამდვილად გსურთ დაბრუნდეს uBlock₀ ნაგულისხმევ პარამეტრებზე?", "description": "Message asking user to confirm reset" }, "errorCantConnectTo": { @@ -1272,7 +1272,7 @@ "description": "Label for keyboard shortcut used to toggle cosmetic filtering" }, "toggleJavascript": { - "message": "JavaScript-ის ჩამრთველი", + "message": "JavaScript – გადამრთველი", "description": "Label for keyboard shortcut used to toggle no-scripting switch" }, "relaxBlockingMode": { diff --git a/src/_locales/tr/messages.json b/src/_locales/tr/messages.json index c6bad17d536c5..944c23d511d13 100644 --- a/src/_locales/tr/messages.json +++ b/src/_locales/tr/messages.json @@ -904,7 +904,7 @@ "description": "Text for button which open an external webpage in Support pane" }, "supportFindSpecificButton": { - "message": "Benzer raporlar bul", + "message": "GitHub'da benzer raporları bul.", "description": "A clickable link in the filter issue reporter section" }, "supportS1H": { @@ -964,7 +964,7 @@ "description": "Header of 'Report a filter issue' section in Support pane" }, "supportS6P1S1": { - "message": "Gönüllülere aynı raporlarla sıkıntı vermemek için, lütfen sorunun daha önce bildirilmediğinden emin olun.", + "message": "Gönüllüleri yinelenen bildirimlerle meşgul etmemek için lütfen sorunun daha önce bildirilip bildirilmediğini kontrol edin.\nNot: Butona tıklamak, sayfanın kaynağının GitHub'a gönderilmesine neden olacaktır.", "description": "A paragraph in the filter issue reporter section" }, "supportS6P2S1": { From ed109737170abcc1b40c35e294a6823fbd3d4e45 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sun, 18 May 2025 09:55:20 -0400 Subject: [PATCH 0939/1099] [mv3] Add "AdGuard -- Mobile Ads" Maybe related issue: https://github.com/uBlockOrigin/uBOL-home/issues/351 The list is enabled by default on mobile devices. --- platform/mv3/extension/js/filter-lists.js | 7 +++++- platform/mv3/extension/js/ruleset-manager.js | 25 +++++++++++++++----- platform/mv3/rulesets.json | 11 +++++++++ 3 files changed, 36 insertions(+), 7 deletions(-) diff --git a/platform/mv3/extension/js/filter-lists.js b/platform/mv3/extension/js/filter-lists.js index a609a7d534aaa..1cb54e78b1501 100644 --- a/platform/mv3/extension/js/filter-lists.js +++ b/platform/mv3/extension/js/filter-lists.js @@ -29,7 +29,7 @@ import { hashFromIterable } from './dashboard.js'; export const rulesetMap = new Map(); let cachedRulesetData = {}; -let hideUnusedSet = new Set([ 'regions' ]); +let hideUnusedSet = new Set([ 'ads', 'regions' ]); /******************************************************************************/ @@ -218,6 +218,11 @@ export function renderFilterLists(rulesetData) { rulesetDetails.filter(ruleset => ruleset.group === 'default' ), + ], [ + 'ads', + rulesetDetails.filter(ruleset => + ruleset.group === 'ads' + ), ], [ 'privacy', rulesetDetails.filter(ruleset => diff --git a/platform/mv3/extension/js/ruleset-manager.js b/platform/mv3/extension/js/ruleset-manager.js index 0bcf23fb49fb6..d87133fc9a48c 100644 --- a/platform/mv3/extension/js/ruleset-manager.js +++ b/platform/mv3/extension/js/ruleset-manager.js @@ -476,7 +476,7 @@ async function filteringModesToDNR(modes) { /******************************************************************************/ -async function defaultRulesetsFromLanguage() { +async function defaultRulesetsFromEnv() { const dropCountry = lang => { const pos = lang.indexOf('-'); if ( pos === -1 ) { return lang; } @@ -494,6 +494,10 @@ async function defaultRulesetsFromLanguage() { `\\b(${Array.from(langSet).join('|')})\\b` ); + const reMobile = /\bMobile\b/.test(navigator.userAgent) + ? /\bmobile\b/ + : null + const rulesetDetails = await getRulesetDetails(); const out = []; for ( const ruleset of rulesetDetails.values() ) { @@ -502,10 +506,20 @@ async function defaultRulesetsFromLanguage() { out.push(id); continue; } - if ( typeof ruleset.lang !== 'string' ) { continue; } - if ( reTargetLang.test(ruleset.lang) === false ) { continue; } - out.push(id); + if ( typeof ruleset.lang === 'string' ) { + if ( reTargetLang.test(ruleset.lang) ) { + out.push(id); + continue; + } + } + if ( typeof ruleset.tags === 'string' ) { + if ( reMobile?.test(ruleset.tags) ) { + out.push(id); + continue; + } + } } + return out; } @@ -518,7 +532,7 @@ async function patchDefaultRulesets() { staticRulesetIds, ] = await Promise.all([ localRead('defaultRulesetIds'), - defaultRulesetsFromLanguage(), + defaultRulesetsFromEnv(), getStaticRulesets().then(r => r.map(a => a.id)), ]); const toAdd = []; @@ -649,7 +663,6 @@ async function getEnabledRulesetsDetails() { /******************************************************************************/ export { - defaultRulesetsFromLanguage, enableRulesets, excludeFromStrictBlock, filteringModesToDNR, diff --git a/platform/mv3/rulesets.json b/platform/mv3/rulesets.json index 7bcd6d9da9d50..07efaf5f5f2d7 100644 --- a/platform/mv3/rulesets.json +++ b/platform/mv3/rulesets.json @@ -63,6 +63,17 @@ ], "homeURL": "https://gitlab.com/malware-filter/urlhaus-filter" }, + { + "id": "adguard-mobile", + "name": "AdGuard – Mobile Ads", + "group": "ads", + "enabled": false, + "tags": "mobile", + "urls": [ + "https://filters.adtidy.org/extension/ublock/filters/11.txt" + ], + "homeURL": "https://github.com/AdguardTeam/AdguardFilters#adguard-filters" + }, { "id": "block-lan", "name": "Block Outsider Intrusion into LAN", From be8b6238d3fd249fddfb29d3bf0ae9be61d73f00 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Mon, 19 May 2025 10:39:31 -0400 Subject: [PATCH 0940/1099] [mv3] Add browser info in troubleshooting data --- platform/mv3/extension/js/report.js | 26 ++++++++++++++++++++++++++ platform/mv3/extension/report.html | 2 +- 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/platform/mv3/extension/js/report.js b/platform/mv3/extension/js/report.js index eda30f97fa8b8..6a96374f64c6f 100644 --- a/platform/mv3/extension/js/report.js +++ b/platform/mv3/extension/js/report.js @@ -95,16 +95,42 @@ function renderData(data, depth = 0) { async function getConfigData() { const manifest = runtime.getManifest(); const [ + platformInfo, rulesets, defaultMode, ] = await Promise.all([ + runtime.getPlatformInfo(), dnr.getEnabledRulesets(), sendMessage({ what: 'getDefaultFilteringMode' }), ]); + let browser = (( ) => { + const extURL = runtime.getURL(''); + let agent = ''; + if ( extURL.startsWith('moz-extension:') ) { + agent = 'Firefox'; + } else if ( extURL.startsWith('safari-web-extension:') ) { + agent = 'Safari'; + } else if ( /\bEdg\/\b/.test(navigator.userAgent) ) { + agent = 'Edge'; + } else { + agent = 'Chrome'; + } + if ( /\bMobile\b/.test(navigator.userAgent) ) { + agent += ' Mobile'; + } + const reVersion = new RegExp(`\\b${agent.slice(0,3)}[^/]*/(\\d+)`); + const match = reVersion.exec(navigator.userAgent); + if ( match ) { + agent += ` ${match[1]}`; + } + agent += ` (${platformInfo.os})` + return agent; + })(); const modes = [ 'no filtering', 'basic', 'optimal', 'complete' ]; const config = { name: manifest.name, version: manifest.version, + browser, filtering: { 'site': `${modes[reportedPage.mode]}`, 'default': `${modes[defaultMode]}`, diff --git a/platform/mv3/extension/report.html b/platform/mv3/extension/report.html index 378701f7d3291..0cb258ad9752e 100644 --- a/platform/mv3/extension/report.html +++ b/platform/mv3/extension/report.html @@ -50,7 +50,7 @@

-
+



From 05f7a7b8c321bcede4bbbfa9767bd48688aec048 Mon Sep 17 00:00:00 2001
From: Raymond Hill 
Date: Mon, 19 May 2025 10:41:15 -0400
Subject: [PATCH 0941/1099] [mv3] Minor code review

---
 platform/mv3/extension/js/report.js | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/platform/mv3/extension/js/report.js b/platform/mv3/extension/js/report.js
index 6a96374f64c6f..c39c933adb468 100644
--- a/platform/mv3/extension/js/report.js
+++ b/platform/mv3/extension/js/report.js
@@ -103,7 +103,7 @@ async function getConfigData() {
         dnr.getEnabledRulesets(),
         sendMessage({ what: 'getDefaultFilteringMode' }),
     ]);
-    let browser = (( ) => {
+    const browser = (( ) => {
         const extURL = runtime.getURL('');
         let agent = '';
         if ( extURL.startsWith('moz-extension:') ) {

From 13b7f381b23d447c8dd2cf2b073f0469efc3a434 Mon Sep 17 00:00:00 2001
From: Raymond Hill 
Date: Mon, 19 May 2025 16:55:27 -0400
Subject: [PATCH 0942/1099] [mv3] Safari: warn to not open filter issues for
 stable Safari

Related issue:
https://github.com/uBlockOrigin/uAssets/issues/28408

Stable Safari is known to not block certain network requests due to
the current state of the DNR engine in Safari. The warning is to
ensure volunteers are not burdened by issues originating from the
browser.

Eventually Safari's DNR implementation will catch up with the
specification, at which time the warning will be removed.
---
 platform/mv3/extension/js/report.js | 1 +
 platform/mv3/extension/report.html  | 4 +++-
 2 files changed, 4 insertions(+), 1 deletion(-)

diff --git a/platform/mv3/extension/js/report.js b/platform/mv3/extension/js/report.js
index c39c933adb468..f3072c1a4b316 100644
--- a/platform/mv3/extension/js/report.js
+++ b/platform/mv3/extension/js/report.js
@@ -115,6 +115,7 @@ async function getConfigData() {
         } else {
             agent = 'Chrome';
         }
+        dom.cl.add('html', agent.toLowerCase());
         if ( /\bMobile\b/.test(navigator.userAgent) ) {
             agent += ' Mobile';
         }
diff --git a/platform/mv3/extension/report.html b/platform/mv3/extension/report.html
index 0cb258ad9752e..5ebabe3baf66c 100644
--- a/platform/mv3/extension/report.html
+++ b/platform/mv3/extension/report.html
@@ -10,7 +10,8 @@
 
 
 
-
+
+
 
 
 
@@ -18,6 +19,7 @@
 

+

Safari users: Please do not report filter issues at the moment. The declarativeNetRequest engine on stable version of Safari is not yet fully implemented as per specs. As a consequence this leads to many cases of network requests not being blocked.


From a59daf597862e34aba36638effa9e11b12b2affe Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Mon, 19 May 2025 19:02:41 -0400 Subject: [PATCH 0943/1099] [mv3] Forgot to commit new CSS file --- platform/mv3/extension/css/report.css | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 platform/mv3/extension/css/report.css diff --git a/platform/mv3/extension/css/report.css b/platform/mv3/extension/css/report.css new file mode 100644 index 0000000000000..0f8ad31107c89 --- /dev/null +++ b/platform/mv3/extension/css/report.css @@ -0,0 +1,11 @@ +:root .safari { + display: none; +} + +:root.safari .safari { + display: initial; +} + +.warning { + color: var(--info2-ink); +} \ No newline at end of file From 7ee99e6875e35ec3fef9b4e3f422779a7b2fd2bf Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Tue, 20 May 2025 07:34:05 -0400 Subject: [PATCH 0944/1099] [mv3] Add fallback fetch in case main one fails --- platform/mv3/make-rulesets.js | 98 +++++++++++++++++++---------------- platform/mv3/rulesets.json | 2 + 2 files changed, 55 insertions(+), 45 deletions(-) diff --git a/platform/mv3/make-rulesets.js b/platform/mv3/make-rulesets.js index c339502841835..9883db6a2dd23 100644 --- a/platform/mv3/make-rulesets.js +++ b/platform/mv3/make-rulesets.js @@ -31,8 +31,8 @@ import { mergeRules, } from './js/static-dnr-filtering.js'; +import { execSync } from 'node:child_process'; import fs from 'fs/promises'; -import https from 'https'; import path from 'path'; import process from 'process'; import redirectResourcesMap from './js/redirect-resources.js'; @@ -107,46 +107,55 @@ console.log = log; const logProgress = text => { process?.stdout?.clearLine?.(); process?.stdout?.cursorTo?.(0); - process?.stdout?.write?.(text.length > 120 ? `${text.slice(0, 119)}…` : text); + process?.stdout?.write?.(text.length > 120 ? `${text.slice(0, 119)}… ` : `${text} `); }; /******************************************************************************/ -const urlToFileName = url => { - return url +async function fetchText(url, cacheDir) { + logProgress(`Reading locally cached ${url}`); + const fname = url .replace(/^https?:\/\//, '') - .replace(/\//g, '_'); -}; + .replace(/\//g, '_');(url); + const content = await fs.readFile( + `${cacheDir}/${fname}`, + { encoding: 'utf8' } + ).catch(( ) => { }); + if ( content !== undefined ) { + log(`\tFetched local ${url}`); + return { url, content }; + } + logProgress(`Fetching remote ${url}`); + log(`\tFetching remote ${url}`); + const response = await fetch(url).catch(( ) => { }); + if ( response === undefined ) { + return { url, error: `Fetching failed: ${url}` }; + } + let text; + if ( response.ok ) { + text = await response.text().catch(( ) => { }); + } else { + text = await fallbackFetchText(url).catch(( ) => { }); + } + if ( text === undefined ) { + return { url, error: `Fetching text content failed: ${url}` }; + } + writeFile(`${cacheDir}/${fname}`, text); + return { url, content: text }; +} -const fetchText = (url, cacheDir) => { - return new Promise((resolve, reject) => { - logProgress(`Reading locally cached ${url}`); - const fname = urlToFileName(url); - fs.readFile(`${cacheDir}/${fname}`, { encoding: 'utf8' }).then(content => { - log(`\tFetched local ${url}`); - resolve({ url, content }); - }).catch(( ) => { - logProgress(`Fetching remote ${url}`); - log(`\tFetching remote ${url}`); - https.get(url, response => { - const data = []; - response.on('data', chunk => { - data.push(chunk.toString()); - }); - response.on('end', ( ) => { - const content = data.join(''); - try { - writeFile(`${cacheDir}/${fname}`, content); - } catch { - } - resolve({ url, content }); - }); - }).on('error', error => { - reject(error); - }); - }); - }); -}; +async function fallbackFetchText(url) { + const match = /^https:\/\/raw\.githubusercontent\.com\/([^/]+)\/([^/]+)\/master\/([^?]+)/.exec(url); + if ( match === null ) { return; } + logProgress(`\tGitHub CLI-fetching remote ${url}`); + // https://docs.github.com/en/rest/repos/contents + const content = execSync(`gh api \ + -H "Accept: application/vnd.github.raw+json" \ + -H "X-GitHub-Api-Version: 2022-11-28" \ + /repos/${match[1]}/${match[2]}/contents/${match[3]} \ + `, { encoding: 'utf8' }); + return content; +} /******************************************************************************/ @@ -251,22 +260,18 @@ async function fetchList(assetDetails) { } newParts.push( fetchText(part.url, cacheDir).then(details => { - const { url } = details; + const { url, error } = details; + if ( error !== undefined ) { return details; } const content = details.content.trim(); - if ( typeof content === 'string' && content !== '' ) { - if ( - content.startsWith('<') === false || - content.endsWith('>') === false - ) { - return { url, content }; - } + if ( content === '' || /^<.*>$/.test(content) ) { + return { url, error: `Bad content: ${url}` }; } - log(`No valid content for ${url}`, false); - return { url, content: '' }; + return { url, content }; }) ); newParts.push(`!#trusted off ${secret}`); } + if ( parts.some(v => typeof v === 'object' && v.error) ) { return; } parts = await Promise.all(newParts); parts = sfp.utils.preparser.expandIncludes(parts, env); } @@ -1239,6 +1244,9 @@ async function rulesetFromURLs(assetDetails) { if ( assetDetails.text === undefined && assetDetails.urls.length !== 0 ) { const text = await fetchList(assetDetails); + if ( text === undefined ) { + process.exit(1); + } assetDetails.text = text; } else { assetDetails.text = ''; diff --git a/platform/mv3/rulesets.json b/platform/mv3/rulesets.json index 07efaf5f5f2d7..ac581ac7b2bcc 100644 --- a/platform/mv3/rulesets.json +++ b/platform/mv3/rulesets.json @@ -4,6 +4,7 @@ "name": "uBlock filters – Ads, trackers, and more", "group": "default", "enabled": true, + "trusted": true, "urls": [ "https://ublockorigin.github.io/uAssets/filters/quick-fixes.min.txt", "https://ublockorigin.github.io/uAssets/filters/unbreak.min.txt", @@ -48,6 +49,7 @@ "name": "uBlock filters – Badware risks", "group": "malware", "enabled": true, + "trusted": true, "urls": [ "https://ublockorigin.github.io/uAssets/filters/badware.min.txt" ], From 4888ec6d35739f43933f810398c1666c9d249a66 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Tue, 20 May 2025 10:14:35 -0400 Subject: [PATCH 0945/1099] Add tool to evaluate uBO-flavored JSONpath expressions Requires a local server in root of repo: python3 -m http.server Then load the following URL in the browser: http://localhost:8000/tools/jsonpath-tool.html --- tools/jsonpath-tool.html | 108 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 108 insertions(+) create mode 100644 tools/jsonpath-tool.html diff --git a/tools/jsonpath-tool.html b/tools/jsonpath-tool.html new file mode 100644 index 0000000000000..abe50410ffba4 --- /dev/null +++ b/tools/jsonpath-tool.html @@ -0,0 +1,108 @@ + + + + + +JSONPath tool + + + +

uBO-flavored JSONPath tool

+
+ + +
 
+
+ + + + + From cdd46c5c7bd95b8ae07110626352ed1e975b9b1c Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Tue, 20 May 2025 11:59:32 -0400 Subject: [PATCH 0946/1099] Minor change --- tools/jsonpath-tool.html | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tools/jsonpath-tool.html b/tools/jsonpath-tool.html index abe50410ffba4..17a5877dd0566 100644 --- a/tools/jsonpath-tool.html +++ b/tools/jsonpath-tool.html @@ -43,8 +43,8 @@

uBO-flavored JSONPath tool

- - + +
 
@@ -65,7 +65,7 @@

uBO-flavored JSONPath tool

} const out = []; for ( const i of a ) { - out.push(`[ ${i.map(j => JSON.stringify(j)).join(' ,')} ]`); + out.push(`[ ${i.map(j => JSON.stringify(j)).join(', ')} ]`); } return out.join('\n'); } From 0130fdf4a1a38b402a6a7ef71430ed0ddbf11de6 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Tue, 20 May 2025 12:14:33 -0400 Subject: [PATCH 0947/1099] Fix element picker issue with explicit dark theme Related issue: https://github.com/uBlockOrigin/uBlock-issues/issues/3624 --- src/css/themes/default.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/css/themes/default.css b/src/css/themes/default.css index bc132b0174159..7136b6c471870 100644 --- a/src/css/themes/default.css +++ b/src/css/themes/default.css @@ -382,7 +382,7 @@ } :root.dark { - color-scheme: dark; + color-scheme: dark light; } /* From 1b0f2ac14c3f8441616bda3a0efa2712a9d9c482 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Tue, 20 May 2025 12:15:48 -0400 Subject: [PATCH 0948/1099] New revision for dev build --- dist/version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dist/version b/dist/version index 75733293117e6..fdfd21d01237e 100644 --- a/dist/version +++ b/dist/version @@ -1 +1 @@ -1.64.0 \ No newline at end of file +1.64.1.0 \ No newline at end of file From 943a63d1e6d2109995158baeea492d15ef7a4d26 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Tue, 20 May 2025 12:17:08 -0400 Subject: [PATCH 0949/1099] Update changelog --- CHANGELOG.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 69e4d53079fa6..64d22d85587f1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,11 @@ +- [Fix element picker issue with explicit dark theme](https://github.com/gorhill/uBlock/commit/0130fdf4a1) + +---------- + +# 1.64.0 + +## Fixes / changes + - [Use custom blank page for embedded iframe in dashboard](https://github.com/gorhill/uBlock/commit/8cd6212867) - [Use `color-scheme` `meta` tag, as suggested](https://github.com/gorhill/uBlock/commit/5c029b3532) - [Bring zapper look in line with uBO Lite's zapper](https://github.com/gorhill/uBlock/commit/3f59f94b60) From ee7af6f0050a8b8ee1818d1cfa81567a5515a6bc Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Tue, 20 May 2025 12:26:37 -0400 Subject: [PATCH 0950/1099] Make Firefox dev build auto-update --- dist/firefox/updates.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dist/firefox/updates.json b/dist/firefox/updates.json index 70f8da3f27068..2842869bfa81e 100644 --- a/dist/firefox/updates.json +++ b/dist/firefox/updates.json @@ -3,9 +3,9 @@ "uBlock0@raymondhill.net": { "updates": [ { - "version": "1.63.3.104", + "version": "1.64.1.0", "browser_specific_settings": { "gecko": { "strict_min_version": "92.0" } }, - "update_link": "https://github.com/gorhill/uBlock/releases/download/1.63.3rc4/uBlock0_1.63.3rc4.firefox.signed.xpi" + "update_link": "https://github.com/gorhill/uBlock/releases/download/1.64.1b0/uBlock0_1.64.1b0.firefox.signed.xpi" } ] } From eee279be01326d5ec6b61a81db3090ce9db33ab4 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Wed, 21 May 2025 08:01:21 -0400 Subject: [PATCH 0951/1099] [mv3] Add codemirror-ubol as submodule --- .gitmodules | 3 +++ platform/mv3/extension/lib/codemirror/README.md | 10 +++++----- .../extension/lib/codemirror/cm6.bundle.ubol.min.js | 1 - platform/mv3/extension/lib/codemirror/codemirror-ubol | 1 + tools/make-mv3.sh | 8 +++++++- 5 files changed, 16 insertions(+), 7 deletions(-) create mode 100644 .gitmodules delete mode 100644 platform/mv3/extension/lib/codemirror/cm6.bundle.ubol.min.js create mode 160000 platform/mv3/extension/lib/codemirror/codemirror-ubol diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000000000..b81211f330b7a --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "platform/mv3/extension/lib/codemirror/codemirror-ubol"] + path = platform/mv3/extension/lib/codemirror/codemirror-ubol + url = https://github.com/gorhill/codemirror-ubol.git diff --git a/platform/mv3/extension/lib/codemirror/README.md b/platform/mv3/extension/lib/codemirror/README.md index 67f9e3d6e4a15..ac2833e77e7a9 100644 --- a/platform/mv3/extension/lib/codemirror/README.md +++ b/platform/mv3/extension/lib/codemirror/README.md @@ -1,9 +1,9 @@ -Steps to build `cm6.bundle.ubol.min.js` -- command line: +Steps to build `cm6.bundle.ubol.min.js` -- command line from repo root: -- `git clone https://github.com/gorhill/codemirror-quickstart.git` - - This is a customized repo forked from -- `cd codemirror-quickstart` +- `git submodule init platform/mv3/extension/lib/codemirror/codemirror-ubol` +- `cd platform/mv3/extension/lib/codemirror/codemirror-ubol/` + - We are now in a customized repo forked from - `npm install` -- `npm build` +- `npm run build` - `cm6.bundle.ubol.min.js` should be in `dist` directory - This is the origin of the `cm6.bundle.ubol.min.js` in the current directory diff --git a/platform/mv3/extension/lib/codemirror/cm6.bundle.ubol.min.js b/platform/mv3/extension/lib/codemirror/cm6.bundle.ubol.min.js deleted file mode 100644 index f22991c190ec0..0000000000000 --- a/platform/mv3/extension/lib/codemirror/cm6.bundle.ubol.min.js +++ /dev/null @@ -1 +0,0 @@ -var cm6=function(t){"use strict";class e{constructor(){}lineAt(t){if(t<0||t>this.length)throw new RangeError(`Invalid position ${t} in document of length ${this.length}`);return this.lineInner(t,!1,1,0)}line(t){if(t<1||t>this.lines)throw new RangeError(`Invalid line number ${t} in ${this.lines}-line document`);return this.lineInner(t,!0,1,0)}replace(t,e,i){let r=[];return this.decompose(0,t,r,2),i.length&&i.decompose(0,i.length,r,3),this.decompose(e,this.length,r,1),n.from(r,this.length-(e-t)+i.length)}append(t){return this.replace(this.length,this.length,t)}slice(t,e=this.length){let i=[];return this.decompose(t,e,i,0),n.from(i,e-t)}eq(t){if(t==this)return!0;if(t.length!=this.length||t.lines!=this.lines)return!1;let e=this.scanIdentical(t,1),i=this.length-this.scanIdentical(t,-1),n=new o(this),r=new o(t);for(let t=e,s=e;;){if(n.next(t),r.next(t),t=0,n.lineBreak!=r.lineBreak||n.done!=r.done||n.value!=r.value)return!1;if(s+=n.value.length,n.done||s>=i)return!0}}iter(t=1){return new o(this,t)}iterRange(t,e=this.length){return new l(this,t,e)}iterLines(t,e){let i;if(null==t)i=this.iter();else{null==e&&(e=this.lines+1);let n=this.line(t).from;i=this.iterRange(n,Math.max(n,e==this.lines+1?this.length:e<=1?0:this.line(e-1).to))}return new h(i)}toString(){return this.sliceString(0)}toJSON(){let t=[];return this.flatten(t),t}static of(t){if(0==t.length)throw new RangeError("A document must have at least one line");return 1!=t.length||t[0]?t.length<=32?new i(t):n.from(i.split(t,[])):e.empty}}class i extends e{constructor(t,e=function(t){let e=-1;for(let i of t)e+=i.length+1;return e}(t)){super(),this.text=t,this.length=e}get lines(){return this.text.length}get children(){return null}lineInner(t,e,i,n){for(let r=0;;r++){let s=this.text[r],o=n+s.length;if((e?i:o)>=t)return new a(n,o,i,s);n=o+1,i++}}decompose(t,e,n,o){let l=t<=0&&e>=this.length?this:new i(s(this.text,t,e),Math.min(e,this.length)-Math.max(0,t));if(1&o){let t=n.pop(),e=r(l.text,t.text.slice(),0,l.length);if(e.length<=32)n.push(new i(e,t.length+l.length));else{let t=e.length>>1;n.push(new i(e.slice(0,t)),new i(e.slice(t)))}}else n.push(l)}replace(t,e,o){if(!(o instanceof i))return super.replace(t,e,o);let l=r(this.text,r(o.text,s(this.text,0,t)),e),h=this.length+o.length-(e-t);return l.length<=32?new i(l,h):n.from(i.split(l,[]),h)}sliceString(t,e=this.length,i="\n"){let n="";for(let r=0,s=0;r<=e&&st&&s&&(n+=i),tr&&(n+=o.slice(Math.max(0,t-r),e-r)),r=l+1}return n}flatten(t){for(let e of this.text)t.push(e)}scanIdentical(){return 0}static split(t,e){let n=[],r=-1;for(let s of t)n.push(s),r+=s.length+1,32==n.length&&(e.push(new i(n,r)),n=[],r=-1);return r>-1&&e.push(new i(n,r)),e}}class n extends e{constructor(t,e){super(),this.children=t,this.length=e,this.lines=0;for(let e of t)this.lines+=e.lines}lineInner(t,e,i,n){for(let r=0;;r++){let s=this.children[r],o=n+s.length,l=i+s.lines-1;if((e?l:o)>=t)return s.lineInner(t,e,i,n);n=o+1,i=l+1}}decompose(t,e,i,n){for(let r=0,s=0;s<=e&&r=s){let r=n&((s<=t?1:0)|(l>=e?2:0));s>=t&&l<=e&&!r?i.push(o):o.decompose(t-s,e-s,i,r)}s=l+1}}replace(t,e,i){if(i.lines=s&&e<=l){let h=o.replace(t-s,e-s,i),a=this.lines-o.lines+h.lines;if(h.lines>4&&h.lines>a>>6){let s=this.children.slice();return s[r]=h,new n(s,this.length-(e-t)+i.length)}return super.replace(s,l,h)}s=l+1}return super.replace(t,e,i)}sliceString(t,e=this.length,i="\n"){let n="";for(let r=0,s=0;rt&&r&&(n+=i),ts&&(n+=o.sliceString(t-s,e-s,i)),s=l+1}return n}flatten(t){for(let e of this.children)e.flatten(t)}scanIdentical(t,e){if(!(t instanceof n))return 0;let i=0,[r,s,o,l]=e>0?[0,0,this.children.length,t.children.length]:[this.children.length-1,t.children.length-1,-1,-1];for(;;r+=e,s+=e){if(r==o||s==l)return i;let n=this.children[r],h=t.children[s];if(n!=h)return i+n.scanIdentical(h,e);i+=n.length+1}}static from(t,e=t.reduce(((t,e)=>t+e.length+1),-1)){let r=0;for(let e of t)r+=e.lines;if(r<32){let n=[];for(let e of t)e.flatten(n);return new i(n,e)}let s=Math.max(32,r>>5),o=s<<1,l=s>>1,h=[],a=0,c=-1,u=[];function f(t){let e;if(t.lines>o&&t instanceof n)for(let e of t.children)f(e);else t.lines>l&&(a>l||!a)?(d(),h.push(t)):t instanceof i&&a&&(e=u[u.length-1])instanceof i&&t.lines+e.lines<=32?(a+=t.lines,c+=t.length+1,u[u.length-1]=new i(e.text.concat(t.text),e.length+1+t.length)):(a+t.lines>s&&d(),a+=t.lines,c+=t.length+1,u.push(t))}function d(){0!=a&&(h.push(1==u.length?u[0]:n.from(u,c)),c=-1,a=u.length=0)}for(let e of t)f(e);return d(),1==h.length?h[0]:new n(h,e)}}function r(t,e,i=0,n=1e9){for(let r=0,s=0,o=!0;s=i&&(h>n&&(l=l.slice(0,n-r)),r0?1:(t instanceof i?t.text.length:t.children.length)<<1]}nextInner(t,e){for(this.done=this.lineBreak=!1;;){let n=this.nodes.length-1,r=this.nodes[n],s=this.offsets[n],o=s>>1,l=r instanceof i?r.text.length:r.children.length;if(o==(e>0?l:0)){if(0==n)return this.done=!0,this.value="",this;e>0&&this.offsets[n-1]++,this.nodes.pop(),this.offsets.pop()}else if((1&s)==(e>0?0:1)){if(this.offsets[n]+=e,0==t)return this.lineBreak=!0,this.value="\n",this;t--}else if(r instanceof i){let i=r.text[o+(e<0?-1:0)];if(this.offsets[n]+=e,i.length>Math.max(0,t))return this.value=0==t?i:e>0?i.slice(t):i.slice(0,i.length-t),this;t-=i.length}else{let s=r.children[o+(e<0?-1:0)];t>s.length?(t-=s.length,this.offsets[n]+=e):(e<0&&this.offsets[n]--,this.nodes.push(s),this.offsets.push(e>0?1:(s instanceof i?s.text.length:s.children.length)<<1))}}}next(t=0){return t<0&&(this.nextInner(-t,-this.dir),t=this.value.length),this.nextInner(t,this.dir)}}class l{constructor(t,e,i){this.value="",this.done=!1,this.cursor=new o(t,e>i?-1:1),this.pos=e>i?t.length:0,this.from=Math.min(e,i),this.to=Math.max(e,i)}nextInner(t,e){if(e<0?this.pos<=this.from:this.pos>=this.to)return this.value="",this.done=!0,this;t+=Math.max(0,e<0?this.pos-this.to:this.from-this.pos);let i=e<0?this.pos-this.from:this.to-this.pos;t>i&&(t=i),i-=t;let{value:n}=this.cursor.next(t);return this.pos+=(n.length+t)*e,this.value=n.length<=i?n:e<0?n.slice(n.length-i):n.slice(0,i),this.done=!this.value,this}next(t=0){return t<0?t=Math.max(t,this.from-this.pos):t>0&&(t=Math.min(t,this.to-this.pos)),this.nextInner(t,this.cursor.dir)}get lineBreak(){return this.cursor.lineBreak&&""!=this.value}}class h{constructor(t){this.inner=t,this.afterBreak=!0,this.value="",this.done=!1}next(t=0){let{done:e,lineBreak:i,value:n}=this.inner.next(t);return e?(this.done=!0,this.value=""):i?this.afterBreak?this.value="":(this.afterBreak=!0,this.next()):(this.value=n,this.afterBreak=!1),this}get lineBreak(){return!1}}"undefined"!=typeof Symbol&&(e.prototype[Symbol.iterator]=function(){return this.iter()},o.prototype[Symbol.iterator]=l.prototype[Symbol.iterator]=h.prototype[Symbol.iterator]=function(){return this});class a{constructor(t,e,i,n){this.from=t,this.to=e,this.number=i,this.text=n}get length(){return this.to-this.from}}let c="lc,34,7n,7,7b,19,,,,2,,2,,,20,b,1c,l,g,,2t,7,2,6,2,2,,4,z,,u,r,2j,b,1m,9,9,,o,4,,9,,3,,5,17,3,3b,f,,w,1j,,,,4,8,4,,3,7,a,2,t,,1m,,,,2,4,8,,9,,a,2,q,,2,2,1l,,4,2,4,2,2,3,3,,u,2,3,,b,2,1l,,4,5,,2,4,,k,2,m,6,,,1m,,,2,,4,8,,7,3,a,2,u,,1n,,,,c,,9,,14,,3,,1l,3,5,3,,4,7,2,b,2,t,,1m,,2,,2,,3,,5,2,7,2,b,2,s,2,1l,2,,,2,4,8,,9,,a,2,t,,20,,4,,2,3,,,8,,29,,2,7,c,8,2q,,2,9,b,6,22,2,r,,,,,,1j,e,,5,,2,5,b,,10,9,,2u,4,,6,,2,2,2,p,2,4,3,g,4,d,,2,2,6,,f,,jj,3,qa,3,t,3,t,2,u,2,1s,2,,7,8,,2,b,9,,19,3,3b,2,y,,3a,3,4,2,9,,6,3,63,2,2,,1m,,,7,,,,,2,8,6,a,2,,1c,h,1r,4,1c,7,,,5,,14,9,c,2,w,4,2,2,,3,1k,,,2,3,,,3,1m,8,2,2,48,3,,d,,7,4,,6,,3,2,5i,1m,,5,ek,,5f,x,2da,3,3x,,2o,w,fe,6,2x,2,n9w,4,,a,w,2,28,2,7k,,3,,4,,p,2,5,,47,2,q,i,d,,12,8,p,b,1a,3,1c,,2,4,2,2,13,,1v,6,2,2,2,2,c,,8,,1b,,1f,,,3,2,2,5,2,,,16,2,8,,6m,,2,,4,,fn4,,kh,g,g,g,a6,2,gt,,6a,,45,5,1ae,3,,2,5,4,14,3,4,,4l,2,fx,4,ar,2,49,b,4w,,1i,f,1k,3,1d,4,2,2,1x,3,10,5,,8,1q,,c,2,1g,9,a,4,2,,2n,3,2,,,2,6,,4g,,3,8,l,2,1l,2,,,,,m,,e,7,3,5,5f,8,2,3,,,n,,29,,2,6,,,2,,,2,,2,6j,,2,4,6,2,,2,r,2,2d,8,2,,,2,2y,,,,2,6,,,2t,3,2,4,,5,77,9,,2,6t,,a,2,,,4,,40,4,2,2,4,,w,a,14,6,2,4,8,,9,6,2,3,1a,d,,2,ba,7,,6,,,2a,m,2,7,,2,,2,3e,6,3,,,2,,7,,,20,2,3,,,,9n,2,f0b,5,1n,7,t4,,1r,4,29,,f5k,2,43q,,,3,4,5,8,8,2,7,u,4,44,3,1iz,1j,4,1e,8,,e,,m,5,,f,11s,7,,h,2,7,,2,,5,79,7,c5,4,15s,7,31,7,240,5,gx7k,2o,3k,6o".split(",").map((t=>t?parseInt(t,36):1));for(let t=1;tt)return c[e-1]<=t;return!1}function f(t){return t>=127462&&t<=127487}function d(t,e,i=!0,n=!0){return(i?p:g)(t,e,n)}function p(t,e,i){if(e==t.length)return e;e&&m(t.charCodeAt(e))&&v(t.charCodeAt(e-1))&&e--;let n=w(t,e);for(e+=b(n);e=0&&f(w(t,n));)i++,n-=2;if(i%2==0)break;e+=2}}}return e}function g(t,e,i){for(;e>0;){let n=p(t,e-2,i);if(n=56320&&t<57344}function v(t){return t>=55296&&t<56320}function w(t,e){let i=t.charCodeAt(e);if(!v(i)||e+1==t.length)return i;let n=t.charCodeAt(e+1);return m(n)?n-56320+(i-55296<<10)+65536:i}function y(t){return t<=65535?String.fromCharCode(t):(t-=65536,String.fromCharCode(55296+(t>>10),56320+(1023&t)))}function b(t){return t<65536?1:2}const x=/\r\n?|\n/;var k=function(t){return t[t.Simple=0]="Simple",t[t.TrackDel=1]="TrackDel",t[t.TrackBefore=2]="TrackBefore",t[t.TrackAfter=3]="TrackAfter",t}(k||(k={}));class S{constructor(t){this.sections=t}get length(){let t=0;for(let e=0;et)return r+(t-n);r+=o}else{if(i!=k.Simple&&h>=t&&(i==k.TrackDel&&nt||i==k.TrackBefore&&nt))return null;if(h>t||h==t&&e<0&&!o)return t==n||e<0?r:r+l;r+=l}n=h}if(t>n)throw new RangeError(`Position ${t} is out of range for changeset of length ${n}`);return r}touchesRange(t,e=t){for(let i=0,n=0;i=0&&n<=e&&r>=t)return!(ne)||"cover";n=r}return!1}toString(){let t="";for(let e=0;e=0?":"+n:"")}return t}toJSON(){return this.sections}static fromJSON(t){if(!Array.isArray(t)||t.length%2||t.some((t=>"number"!=typeof t)))throw new RangeError("Invalid JSON representation of ChangeDesc");return new S(t)}static create(t){return new S(t)}}class A extends S{constructor(t,e){super(t),this.inserted=e}apply(t){if(this.length!=t.length)throw new RangeError("Applying change set to a document with the wrong length");return D(this,((e,i,n,r,s)=>t=t.replace(n,n+(i-e),s)),!1),t}mapDesc(t,e=!1){return O(this,t,e,!0)}invert(t){let i=this.sections.slice(),n=[];for(let r=0,s=0;r=0){i[r]=l,i[r+1]=o;let h=r>>1;for(;n.length0&&C(i,e,r.text),r.forward(t),o+=t}let h=t[s++];for(;o>1].toJSON()))}return t}static of(t,i,n){let r=[],s=[],o=0,l=null;function h(t=!1){if(!t&&!r.length)return;ol||t<0||l>i)throw new RangeError(`Invalid change range ${t} to ${l} (in doc of length ${i})`);let u=c?"string"==typeof c?e.of(c.split(n||x)):c:e.empty,f=u.length;if(t==l&&0==f)return;to&&M(r,t-o,-1),M(r,l-t,f),C(s,r,u),o=l}}(t),h(!l),l}static empty(t){return new A(t?[t,-1]:[],[])}static fromJSON(t){if(!Array.isArray(t))throw new RangeError("Invalid JSON representation of ChangeSet");let i=[],n=[];for(let r=0;re&&"string"!=typeof t)))throw new RangeError("Invalid JSON representation of ChangeSet");if(1==s.length)i.push(s[0],0);else{for(;n.length=0&&i<=0&&i==t[r+1]?t[r]+=e:0==e&&0==t[r]?t[r+1]+=i:n?(t[r]+=e,t[r+1]+=i):t.push(e,i)}function C(t,i,n){if(0==n.length)return;let r=i.length-2>>1;if(r>1])),!(n||l==t.sections.length||t.sections[l+1]<0);)h=t.sections[l++],a=t.sections[l++];i(s,c,o,u,f),s=c,o=u}}}function O(t,e,i,n=!1){let r=[],s=n?[]:null,o=new E(t),l=new E(e);for(let t=-1;;)if(-1==o.ins&&-1==l.ins){let t=Math.min(o.len,l.len);M(r,t,-1),o.forward(t),l.forward(t)}else if(l.ins>=0&&(o.ins<0||t==o.i||0==o.off&&(l.len=0&&t=0)){if(o.done&&l.done)return s?A.createSet(r,s):S.create(r);throw new Error("Mismatched change set lengths")}{let e=0,i=o.len;for(;i;)if(-1==l.ins){let t=Math.min(i,l.len);e+=t,i-=t,l.forward(t)}else{if(!(0==l.ins&&l.lene||o.ins>=0&&o.len>e)&&(t||n.length>i),s.forward2(e),o.forward(e)}}else M(n,0,o.ins,t),r&&C(r,n,o.text),o.next()}}class E{constructor(t){this.set=t,this.i=0,this.next()}next(){let{sections:t}=this.set;this.i>1;return i>=t.length?e.empty:t[i]}textBit(t){let{inserted:i}=this.set,n=this.i-2>>1;return n>=i.length&&!t?e.empty:i[n].slice(this.off,null==t?void 0:this.off+t)}forward(t){t==this.len?this.next():(this.len-=t,this.off+=t)}forward2(t){-1==this.ins?this.forward(t):t==this.ins?this.next():(this.ins-=t,this.off+=t)}}class B{constructor(t,e,i){this.from=t,this.to=e,this.flags=i}get anchor(){return 16&this.flags?this.to:this.from}get head(){return 16&this.flags?this.from:this.to}get empty(){return this.from==this.to}get assoc(){return 4&this.flags?-1:8&this.flags?1:0}get bidiLevel(){let t=3&this.flags;return 3==t?null:t}get goalColumn(){let t=this.flags>>5;return 33554431==t?void 0:t}map(t,e=-1){let i,n;return this.empty?i=n=t.mapPos(this.from,e):(i=t.mapPos(this.from,1),n=t.mapPos(this.to,-1)),i==this.from&&n==this.to?this:new B(i,n,this.flags)}extend(t,e=t){if(t<=this.anchor&&e>=this.anchor)return R.range(t,e);let i=Math.abs(t-this.anchor)>Math.abs(e-this.anchor)?t:e;return R.range(this.anchor,i)}eq(t){return this.anchor==t.anchor&&this.head==t.head}toJSON(){return{anchor:this.anchor,head:this.head}}static fromJSON(t){if(!t||"number"!=typeof t.anchor||"number"!=typeof t.head)throw new RangeError("Invalid JSON representation for SelectionRange");return R.range(t.anchor,t.head)}static create(t,e,i){return new B(t,e,i)}}class R{constructor(t,e){this.ranges=t,this.mainIndex=e}map(t,e=-1){return t.empty?this:R.create(this.ranges.map((i=>i.map(t,e))),this.mainIndex)}eq(t){if(this.ranges.length!=t.ranges.length||this.mainIndex!=t.mainIndex)return!1;for(let e=0;et.toJSON())),main:this.mainIndex}}static fromJSON(t){if(!t||!Array.isArray(t.ranges)||"number"!=typeof t.main||t.main>=t.ranges.length)throw new RangeError("Invalid JSON representation for EditorSelection");return new R(t.ranges.map((t=>B.fromJSON(t))),t.main)}static single(t,e=t){return new R([R.range(t,e)],0)}static create(t,e=0){if(0==t.length)throw new RangeError("A selection needs at least one range");for(let i=0,n=0;nt?4:0))}static normalized(t,e=0){let i=t[e];t.sort(((t,e)=>t.from-e.from)),e=t.indexOf(i);for(let i=1;in.head?R.range(o,s):R.range(s,o))}}return new R(t,e)}}function L(t,e){for(let i of t.ranges)if(i.to>e)throw new RangeError("Selection points outside of document")}let N=0;class P{constructor(t,e,i,n,r){this.combine=t,this.compareInput=e,this.compare=i,this.isStatic=n,this.id=N++,this.default=t([]),this.extensions="function"==typeof r?r(this):r}static define(t={}){return new P(t.combine||(t=>t),t.compareInput||((t,e)=>t===e),t.compare||(t.combine?(t,e)=>t===e:I),!!t.static,t.enables)}of(t){return new V([],this,0,t)}compute(t,e){if(this.isStatic)throw new Error("Can't compute a static facet");return new V(t,this,1,e)}computeN(t,e){if(this.isStatic)throw new Error("Can't compute a static facet");return new V(t,this,2,e)}from(t,e){return e||(e=t=>t),this.compute([t],(i=>e(i.field(t))))}}function I(t,e){return t==e||t.length==e.length&&t.every(((t,i)=>t===e[i]))}class V{constructor(t,e,i,n){this.dependencies=t,this.facet=e,this.type=i,this.value=n,this.id=N++}dynamicSlot(t){var e;let i=this.value,n=this.facet.compareInput,r=this.id,s=t[r]>>1,o=2==this.type,l=!1,h=!1,a=[];for(let i of this.dependencies)"doc"==i?l=!0:"selection"==i?h=!0:0==(1&(null!==(e=t[i.id])&&void 0!==e?e:1))&&a.push(t[i.id]);return{create:t=>(t.values[s]=i(t),1),update(t,e){if(l&&e.docChanged||h&&(e.docChanged||e.selection)||H(t,a)){let e=i(t);if(o?!W(e,t.values[s],n):!n(e,t.values[s]))return t.values[s]=e,1}return 0},reconfigure:(t,e)=>{let l,h=e.config.address[r];if(null!=h){let r=tt(e,h);if(this.dependencies.every((i=>i instanceof P?e.facet(i)===t.facet(i):!(i instanceof q)||e.field(i,!1)==t.field(i,!1)))||(o?W(l=i(t),r,n):n(l=i(t),r)))return t.values[s]=r,0}else l=i(t);return t.values[s]=l,1}}}}function W(t,e,i){if(t.length!=e.length)return!1;for(let n=0;nt[e.id])),r=i.map((t=>t.type)),s=n.filter((t=>!(1&t))),o=t[e.id]>>1;function l(t){let i=[];for(let e=0;et===e),t);return t.provide&&(e.provides=t.provide(e)),e}create(t){let e=t.facet(z).find((t=>t.field==this));return((null==e?void 0:e.create)||this.createF)(t)}slot(t){let e=t[this.id]>>1;return{create:t=>(t.values[e]=this.create(t),1),update:(t,i)=>{let n=t.values[e],r=this.updateF(n,i);return this.compareF(n,r)?0:(t.values[e]=r,1)},reconfigure:(t,i)=>null!=i.config.address[this.id]?(t.values[e]=i.field(this),0):(t.values[e]=this.create(t),1)}}init(t){return[this,z.of({field:this,create:t})]}get extension(){return this}}const _=4,K=3,j=2,$=1;function G(t){return e=>new J(e,t)}const U={highest:G(0),high:G($),default:G(j),low:G(K),lowest:G(_)};class J{constructor(t,e){this.inner=t,this.prec=e}}class X{of(t){return new Y(this,t)}reconfigure(t){return X.reconfigure.of({compartment:this,extension:t})}get(t){return t.config.compartments.get(this)}}class Y{constructor(t,e){this.compartment=t,this.inner=e}}class Q{constructor(t,e,i,n,r,s){for(this.base=t,this.compartments=e,this.dynamicSlots=i,this.address=n,this.staticValues=r,this.facets=s,this.statusTemplate=[];this.statusTemplate.length>1]}static resolve(t,e,i){let n=[],r=Object.create(null),s=new Map;for(let i of function(t,e,i){let n=[[],[],[],[],[]],r=new Map;function s(t,o){let l=r.get(t);if(null!=l){if(l<=o)return;let e=n[l].indexOf(t);e>-1&&n[l].splice(e,1),t instanceof Y&&i.delete(t.compartment)}if(r.set(t,o),Array.isArray(t))for(let e of t)s(e,o);else if(t instanceof Y){if(i.has(t.compartment))throw new RangeError("Duplicate use of compartment in extensions");let n=e.get(t.compartment)||t.inner;i.set(t.compartment,n),s(n,o)}else if(t instanceof J)s(t.inner,t.prec);else if(t instanceof q)n[o].push(t),t.provides&&s(t.provides,o);else if(t instanceof V)n[o].push(t),t.facet.extensions&&s(t.facet.extensions,j);else{let e=t.extension;if(!e)throw new Error(`Unrecognized extension value in extension set (${t}). This sometimes happens because multiple instances of @codemirror/state are loaded, breaking instanceof checks.`);s(e,o)}}return s(t,j),n.reduce(((t,e)=>t.concat(e)))}(t,e,s))i instanceof q?n.push(i):(r[i.facet.id]||(r[i.facet.id]=[])).push(i);let o=Object.create(null),l=[],h=[];for(let t of n)o[t.id]=h.length<<1,h.push((e=>t.slot(e)));let a=null==i?void 0:i.config.facets;for(let t in r){let e=r[t],n=e[0].facet,s=a&&a[t]||[];if(e.every((t=>0==t.type)))if(o[n.id]=l.length<<1|1,I(s,e))l.push(i.facet(n));else{let t=n.combine(e.map((t=>t.value)));l.push(i&&n.compare(t,i.facet(n))?i.facet(n):t)}else{for(let t of e)0==t.type?(o[t.id]=l.length<<1|1,l.push(t.value)):(o[t.id]=h.length<<1,h.push((e=>t.dynamicSlot(e))));o[n.id]=h.length<<1,h.push((t=>F(t,n,e)))}}let c=h.map((t=>t(o)));return new Q(t,s,c,o,l,r)}}function Z(t,e){if(1&e)return 2;let i=e>>1,n=t.status[i];if(4==n)throw new Error("Cyclic dependency between fields and/or facets");if(2&n)return n;t.status[i]=4;let r=t.computeSlot(t,t.config.dynamicSlots[i]);return t.status[i]=2|r}function tt(t,e){return 1&e?t.config.staticValues[e>>1]:t.values[e>>1]}const et=P.define(),it=P.define({combine:t=>t.some((t=>t)),static:!0}),nt=P.define({combine:t=>t.length?t[0]:void 0,static:!0}),rt=P.define(),st=P.define(),ot=P.define(),lt=P.define({combine:t=>!!t.length&&t[0]});class ht{constructor(t,e){this.type=t,this.value=e}static define(){return new at}}class at{of(t){return new ht(this,t)}}class ct{constructor(t){this.map=t}of(t){return new ut(this,t)}}class ut{constructor(t,e){this.type=t,this.value=e}map(t){let e=this.type.map(this.value,t);return void 0===e?void 0:e==this.value?this:new ut(this.type,e)}is(t){return this.type==t}static define(t={}){return new ct(t.map||(t=>t))}static mapEffects(t,e){if(!t.length)return t;let i=[];for(let n of t){let t=n.map(e);t&&i.push(t)}return i}}ut.reconfigure=ut.define(),ut.appendConfig=ut.define();class ft{constructor(t,e,i,n,r,s){this.startState=t,this.changes=e,this.selection=i,this.effects=n,this.annotations=r,this.scrollIntoView=s,this._doc=null,this._state=null,i&&L(i,e.newLength),r.some((t=>t.type==ft.time))||(this.annotations=r.concat(ft.time.of(Date.now())))}static create(t,e,i,n,r,s){return new ft(t,e,i,n,r,s)}get newDoc(){return this._doc||(this._doc=this.changes.apply(this.startState.doc))}get newSelection(){return this.selection||this.startState.selection.map(this.changes)}get state(){return this._state||this.startState.applyTransaction(this),this._state}annotation(t){for(let e of this.annotations)if(e.type==t)return e.value}get docChanged(){return!this.changes.empty}get reconfigured(){return this.startState.config!=this.state.config}isUserEvent(t){let e=this.annotation(ft.userEvent);return!(!e||!(e==t||e.length>t.length&&e.slice(0,t.length)==t&&"."==e[t.length]))}}function dt(t,e){let i=[];for(let n=0,r=0;;){let s,o;if(n=t[n]))s=t[n++],o=t[n++];else{if(!(r=0;r--){let s=i[r](t);s&&Object.keys(s).length&&(n=pt(n,gt(e,s,t.changes.newLength),!0))}return n==t?t:ft.create(e,t.changes,t.selection,n.effects,n.annotations,n.scrollIntoView)}(i?function(t){let e=t.startState,i=!0;for(let n of e.facet(rt)){let e=n(t);if(!1===e){i=!1;break}Array.isArray(e)&&(i=!0===i?e:dt(i,e))}if(!0!==i){let n,r;if(!1===i)r=t.changes.invertedDesc,n=A.empty(e.doc.length);else{let e=t.changes.filter(i);n=e.changes,r=e.filtered.mapDesc(e.changes).invertedDesc}t=ft.create(e,n,t.selection&&t.selection.map(r),ut.mapEffects(t.effects,r),t.annotations,t.scrollIntoView)}let n=e.facet(st);for(let i=n.length-1;i>=0;i--){let r=n[i](t);t=r instanceof ft?r:Array.isArray(r)&&1==r.length&&r[0]instanceof ft?r[0]:mt(e,wt(r),!1)}return t}(r):r)}ft.time=ht.define(),ft.userEvent=ht.define(),ft.addToHistory=ht.define(),ft.remote=ht.define();const vt=[];function wt(t){return null==t?vt:Array.isArray(t)?t:[t]}var yt=function(t){return t[t.Word=0]="Word",t[t.Space=1]="Space",t[t.Other=2]="Other",t}(yt||(yt={}));const bt=/[\u00df\u0587\u0590-\u05f4\u0600-\u06ff\u3040-\u309f\u30a0-\u30ff\u3400-\u4db5\u4e00-\u9fcc\uac00-\ud7af]/;let xt;try{xt=new RegExp("[\\p{Alphabetic}\\p{Number}_]","u")}catch(t){}function kt(t){return e=>{if(!/\S/.test(e))return yt.Space;if(function(t){if(xt)return xt.test(t);for(let e=0;e"€"&&(i.toUpperCase()!=i.toLowerCase()||bt.test(i)))return!0}return!1}(e))return yt.Word;for(let i=0;i-1)return yt.Word;return yt.Other}}class St{constructor(t,e,i,n,r,s){this.config=t,this.doc=e,this.selection=i,this.values=n,this.status=t.statusTemplate.slice(),this.computeSlot=r,s&&(s._state=this);for(let t=0;tr.set(e,t))),i=null),r.set(e.value.compartment,e.value.extension)):e.is(ut.reconfigure)?(i=null,n=e.value):e.is(ut.appendConfig)&&(i=null,n=wt(n).concat(e.value));if(i)e=t.startState.values.slice();else{i=Q.resolve(n,r,this),e=new St(i,this.doc,this.selection,i.dynamicSlots.map((()=>null)),((t,e)=>e.reconfigure(t,this)),null).values}new St(i,t.newDoc,t.newSelection,e,((e,i)=>i.update(e,t)),t)}replaceSelection(t){return"string"==typeof t&&(t=this.toText(t)),this.changeByRange((e=>({changes:{from:e.from,to:e.to,insert:t},range:R.cursor(e.from+t.length)})))}changeByRange(t){let e=this.selection,i=t(e.ranges[0]),n=this.changes(i.changes),r=[i.range],s=wt(i.effects);for(let i=1;ir.spec.fromJSON(s,t))))}return St.create({doc:t.doc,selection:R.fromJSON(t.selection),extensions:e.extensions?n.concat([e.extensions]):n})}static create(t={}){let i=Q.resolve(t.extensions||[],new Map),n=t.doc instanceof e?t.doc:e.of((t.doc||"").split(i.staticFacet(St.lineSeparator)||x)),r=t.selection?t.selection instanceof R?t.selection:R.single(t.selection.anchor,t.selection.head):R.single(0);return L(r,n.length),i.staticFacet(it)||(r=r.asSingle()),new St(i,n,r,i.dynamicSlots.map((()=>null)),((t,e)=>e.create(t)),null)}get tabSize(){return this.facet(St.tabSize)}get lineBreak(){return this.facet(St.lineSeparator)||"\n"}get readOnly(){return this.facet(lt)}phrase(t,...e){for(let e of this.facet(St.phrases))if(Object.prototype.hasOwnProperty.call(e,t)){t=e[t];break}return e.length&&(t=t.replace(/\$(\$|\d*)/g,((t,i)=>{if("$"==i)return"$";let n=+(i||1);return!n||n>e.length?t:e[n-1]}))),t}languageDataAt(t,e,i=-1){let n=[];for(let r of this.facet(et))for(let s of r(this,e,i))Object.prototype.hasOwnProperty.call(s,t)&&n.push(s[t]);return n}charCategorizer(t){return kt(this.languageDataAt("wordChars",t).join(""))}wordAt(t){let{text:e,from:i,length:n}=this.doc.lineAt(t),r=this.charCategorizer(t),s=t-i,o=t-i;for(;s>0;){let t=d(e,s,!1);if(r(e.slice(t,s))!=yt.Word)break;s=t}for(;ot.length?t[0]:4}),St.lineSeparator=nt,St.readOnly=lt,St.phrases=P.define({compare(t,e){let i=Object.keys(t),n=Object.keys(e);return i.length==n.length&&i.every((i=>t[i]==e[i]))}}),St.languageData=et,St.changeFilter=rt,St.transactionFilter=st,St.transactionExtender=ot,X.reconfigure=ut.define();class Mt{eq(t){return this==t}range(t,e=t){return Ct.create(t,e,this)}}Mt.prototype.startSide=Mt.prototype.endSide=0,Mt.prototype.point=!1,Mt.prototype.mapMode=k.TrackDel;let Ct=class t{constructor(t,e,i){this.from=t,this.to=e,this.value=i}static create(e,i,n){return new t(e,i,n)}};function Dt(t,e){return t.from-e.from||t.value.startSide-e.value.startSide}class Ot{constructor(t,e,i,n){this.from=t,this.to=e,this.value=i,this.maxPoint=n}get length(){return this.to[this.to.length-1]}findIndex(t,e,i,n=0){let r=i?this.to:this.from;for(let s=n,o=r.length;;){if(s==o)return s;let n=s+o>>1,l=r[n]-t||(i?this.value[n].endSide:this.value[n].startSide)-e;if(n==s)return l>=0?s:o;l>=0?o=n:s=n+1}}between(t,e,i,n){for(let r=this.findIndex(e,-1e9,!0),s=this.findIndex(i,1e9,!1,r);ra||h==a&&c.startSide>0&&c.endSide<=0)continue;(a-h||c.endSide-c.startSide)<0||(s<0&&(s=h),c.point&&(o=Math.max(o,a-h)),i.push(c),n.push(h-s),r.push(a-s))}return{mapped:i.length?new Ot(n,r,i,o):null,pos:s}}}class Tt{constructor(t,e,i,n){this.chunkPos=t,this.chunk=e,this.nextLayer=i,this.maxPoint=n}static create(t,e,i,n){return new Tt(t,e,i,n)}get length(){let t=this.chunk.length-1;return t<0?0:Math.max(this.chunkEnd(t),this.nextLayer.length)}get size(){if(this.isEmpty)return 0;let t=this.nextLayer.size;for(let e of this.chunk)t+=e.value.length;return t}chunkEnd(t){return this.chunkPos[t]+this.chunk[t].length}update(t){let{add:e=[],sort:i=!1,filterFrom:n=0,filterTo:r=this.length}=t,s=t.filter;if(0==e.length&&!s)return this;if(i&&(e=e.slice().sort(Dt)),this.isEmpty)return e.length?Tt.of(e):this;let o=new Rt(this,null,-1).goto(0),l=0,h=[],a=new Et;for(;o.value||l=0){let t=e[l++];a.addInner(t.from,t.to,t.value)||h.push(t)}else 1==o.rangeIndex&&o.chunkIndexthis.chunkEnd(o.chunkIndex)||ro.to||r=r&&t<=r+s.length&&!1===s.between(r,t-r,e-r,i))return}this.nextLayer.between(t,e,i)}}iter(t=0){return Lt.from([this]).goto(t)}get isEmpty(){return this.nextLayer==this}static iter(t,e=0){return Lt.from(t).goto(e)}static compare(t,e,i,n,r=-1){let s=t.filter((t=>t.maxPoint>0||!t.isEmpty&&t.maxPoint>=r)),o=e.filter((t=>t.maxPoint>0||!t.isEmpty&&t.maxPoint>=r)),l=Bt(s,o,i),h=new Pt(s,l,r),a=new Pt(o,l,r);i.iterGaps(((t,e,i)=>It(h,t,a,e,i,n))),i.empty&&0==i.length&&It(h,0,a,0,0,n)}static eq(t,e,i=0,n){null==n&&(n=999999999);let r=t.filter((t=>!t.isEmpty&&e.indexOf(t)<0)),s=e.filter((e=>!e.isEmpty&&t.indexOf(e)<0));if(r.length!=s.length)return!1;if(!r.length)return!0;let o=Bt(r,s),l=new Pt(r,o,0).goto(i),h=new Pt(s,o,0).goto(i);for(;;){if(l.to!=h.to||!Vt(l.active,h.active)||l.point&&(!h.point||!l.point.eq(h.point)))return!1;if(l.to>n)return!0;l.next(),h.next()}}static spans(t,e,i,n,r=-1){let s=new Pt(t,null,r).goto(e),o=e,l=s.openStart;for(;;){let t=Math.min(s.to,i);if(s.point){let i=s.activeForPoint(s.to),r=s.pointFromo&&(n.span(o,t,s.active,l),l=s.openEnd(t));if(s.to>i)return l+(s.point&&s.to>i?1:0);o=s.to,s.next()}}static of(t,e=!1){let i=new Et;for(let n of t instanceof Ct?[t]:e?function(t){if(t.length>1)for(let e=t[0],i=1;i0)return t.slice().sort(Dt);e=n}return t}(t):t)i.add(n.from,n.to,n.value);return i.finish()}}Tt.empty=new Tt([],[],null,-1),Tt.empty.nextLayer=Tt.empty;class Et{constructor(){this.chunks=[],this.chunkPos=[],this.chunkStart=-1,this.last=null,this.lastFrom=-1e9,this.lastTo=-1e9,this.from=[],this.to=[],this.value=[],this.maxPoint=-1,this.setMaxPoint=-1,this.nextLayer=null}finishChunk(t){this.chunks.push(new Ot(this.from,this.to,this.value,this.maxPoint)),this.chunkPos.push(this.chunkStart),this.chunkStart=-1,this.setMaxPoint=Math.max(this.setMaxPoint,this.maxPoint),this.maxPoint=-1,t&&(this.from=[],this.to=[],this.value=[])}add(t,e,i){this.addInner(t,e,i)||(this.nextLayer||(this.nextLayer=new Et)).add(t,e,i)}addInner(t,e,i){let n=t-this.lastTo||i.startSide-this.last.endSide;if(n<=0&&(t-this.lastFrom||i.startSide-this.last.startSide)<0)throw new Error("Ranges must be added sorted by `from` position and `startSide`");return!(n<0)&&(250==this.from.length&&this.finishChunk(!0),this.chunkStart<0&&(this.chunkStart=t),this.from.push(t-this.chunkStart),this.to.push(e-this.chunkStart),this.last=i,this.lastFrom=t,this.lastTo=e,this.value.push(i),i.point&&(this.maxPoint=Math.max(this.maxPoint,e-t)),!0)}addChunk(t,e){if((t-this.lastTo||e.value[0].startSide-this.last.endSide)<0)return!1;this.from.length&&this.finishChunk(!0),this.setMaxPoint=Math.max(this.setMaxPoint,e.maxPoint),this.chunks.push(e),this.chunkPos.push(t);let i=e.value.length-1;return this.last=e.value[i],this.lastFrom=e.from[i]+t,this.lastTo=e.to[i]+t,!0}finish(){return this.finishInner(Tt.empty)}finishInner(t){if(this.from.length&&this.finishChunk(!1),0==this.chunks.length)return t;let e=Tt.create(this.chunkPos,this.chunks,this.nextLayer?this.nextLayer.finishInner(t):t,this.setMaxPoint);return this.from=null,e}}function Bt(t,e,i){let n=new Map;for(let e of t)for(let t=0;t=this.minPoint)break}}}setRangeIndex(t){if(t==this.layer.chunk[this.chunkIndex].value.length){if(this.chunkIndex++,this.skip)for(;this.chunkIndex=i&&n.push(new Rt(s,e,i,r));return 1==n.length?n[0]:new Lt(n)}get startSide(){return this.value?this.value.startSide:0}goto(t,e=-1e9){for(let i of this.heap)i.goto(t,e);for(let t=this.heap.length>>1;t>=0;t--)Nt(this.heap,t);return this.next(),this}forward(t,e){for(let i of this.heap)i.forward(t,e);for(let t=this.heap.length>>1;t>=0;t--)Nt(this.heap,t);(this.to-t||this.value.endSide-e)<0&&this.next()}next(){if(0==this.heap.length)this.from=this.to=1e9,this.value=null,this.rank=-1;else{let t=this.heap[0];this.from=t.from,this.to=t.to,this.value=t.value,this.rank=t.rank,t.value&&t.next(),Nt(this.heap,0)}}}function Nt(t,e){for(let i=t[e];;){let n=1+(e<<1);if(n>=t.length)break;let r=t[n];if(n+1=0&&(r=t[n+1],n++),i.compare(r)<0)break;t[n]=i,t[e]=r,e=n}}class Pt{constructor(t,e,i){this.minPoint=i,this.active=[],this.activeTo=[],this.activeRank=[],this.minActive=-1,this.point=null,this.pointFrom=0,this.pointRank=0,this.to=-1e9,this.endSide=0,this.openStart=-1,this.cursor=Lt.from(t,e,i)}goto(t,e=-1e9){return this.cursor.goto(t,e),this.active.length=this.activeTo.length=this.activeRank.length=0,this.minActive=-1,this.to=t,this.endSide=e,this.openStart=-1,this.next(),this}forward(t,e){for(;this.minActive>-1&&(this.activeTo[this.minActive]-t||this.active[this.minActive].endSide-e)<0;)this.removeActive(this.minActive);this.cursor.forward(t,e)}removeActive(t){Wt(this.active,t),Wt(this.activeTo,t),Wt(this.activeRank,t),this.minActive=Ft(this.active,this.activeTo)}addActive(t){let e=0,{value:i,to:n,rank:r}=this.cursor;for(;e-1&&(this.activeTo[n]-this.cursor.from||this.active[n].endSide-this.cursor.startSide)<0){if(this.activeTo[n]>t){this.to=this.activeTo[n],this.endSide=this.active[n].endSide;break}this.removeActive(n),i&&Wt(i,n)}else{if(!this.cursor.value){this.to=this.endSide=1e9;break}if(this.cursor.from>t){this.to=this.cursor.from,this.endSide=this.cursor.startSide;break}{let t=this.cursor.value;if(t.point){if(!(e&&this.cursor.to==this.to&&this.cursor.from=0&&i[e]=0&&!(this.activeRank[i]t||this.activeTo[i]==t&&this.active[i].endSide>=this.point.endSide)&&e.push(this.active[i]);return e.reverse()}openEnd(t){let e=0;for(let i=this.activeTo.length-1;i>=0&&this.activeTo[i]>t;i--)e++;return e}}function It(t,e,i,n,r,s){t.goto(e),i.goto(n);let o=n+r,l=n,h=n-e;for(;;){let e=t.to+h-i.to||t.endSide-i.endSide,n=e<0?t.to+h:i.to,r=Math.min(n,o);if(t.point||i.point?t.point&&i.point&&(t.point==i.point||t.point.eq(i.point))&&Vt(t.activeForPoint(t.to+h),i.activeForPoint(i.to))||s.comparePoint(l,r,t.point,i.point):r>l&&!Vt(t.active,i.active)&&s.compareRange(l,r,t.active,i.active),n>o)break;l=n,e<=0&&t.next(),e>=0&&i.next()}}function Vt(t,e){if(t.length!=e.length)return!1;for(let i=0;i=e;i--)t[i+1]=t[i];t[e]=i}function Ft(t,e){let i=-1,n=1e9;for(let r=0;r=e)return n;if(n==t.length)break;r+=9==t.charCodeAt(n)?i-r%i:1,n=d(t,n)}return!0===n?-1:t.length}const _t="undefined"==typeof Symbol?"__ͼ":Symbol.for("ͼ"),Kt="undefined"==typeof Symbol?"__styleSet"+Math.floor(1e8*Math.random()):Symbol("styleSet"),jt="undefined"!=typeof globalThis?globalThis:"undefined"!=typeof window?window:{};class $t{constructor(t,e){this.rules=[];let{finish:i}=e||{};function n(t){return/^@/.test(t)?[t]:t.split(/,\s*/)}function r(t,e,s,o){let l=[],h=/^@(\w+)\b/.exec(t[0]),a=h&&"keyframes"==h[1];if(h&&null==e)return s.push(t[0]+";");for(let i in e){let o=e[i];if(/&/.test(i))r(i.split(/,\s*/).map((e=>t.map((t=>e.replace(/&/,t))))).reduce(((t,e)=>t.concat(e))),o,s);else if(o&&"object"==typeof o){if(!h)throw new RangeError("The value of a property ("+i+") should be a primitive value.");r(n(i),o,l,a)}else null!=o&&l.push(i.replace(/_.*/,"").replace(/[A-Z]/g,(t=>"-"+t.toLowerCase()))+": "+o+";")}(l.length||a)&&s.push((!i||h||o?t:t.map(i)).join(", ")+" {"+l.join(" ")+"}")}for(let e in t)r(n(e),t[e],this.rules)}getRules(){return this.rules.join("\n")}static newName(){let t=jt[_t]||1;return jt[_t]=t+1,"ͼ"+t.toString(36)}static mount(t,e){(t[Kt]||new Ut(t)).mount(Array.isArray(e)?e:[e])}}let Gt=null;class Ut{constructor(t){if(!t.head&&t.adoptedStyleSheets&&"undefined"!=typeof CSSStyleSheet){if(Gt)return t.adoptedStyleSheets=[Gt.sheet].concat(t.adoptedStyleSheets),t[Kt]=Gt;this.sheet=new CSSStyleSheet,t.adoptedStyleSheets=[this.sheet].concat(t.adoptedStyleSheets),Gt=this}else{this.styleTag=(t.ownerDocument||t).createElement("style");let e=t.head||t;e.insertBefore(this.styleTag,e.firstChild)}this.modules=[],t[Kt]=this}mount(t){let e=this.sheet,i=0,n=0;for(let r=0;r-1&&(this.modules.splice(o,1),n--,o=-1),-1==o){if(this.modules.splice(n++,0,s),e)for(let t=0;t",191:"?",192:"~",219:"{",220:"|",221:"}",222:'"'},Yt="undefined"!=typeof navigator&&/Chrome\/(\d+)/.exec(navigator.userAgent),Qt="undefined"!=typeof navigator&&/Mac/.test(navigator.platform),Zt="undefined"!=typeof navigator&&/MSIE \d|Trident\/(?:[7-9]|\d{2,})\..*rv:(\d+)/.exec(navigator.userAgent),te=Qt||Yt&&+Yt[1]<57,ee=0;ee<10;ee++)Jt[48+ee]=Jt[96+ee]=String(ee);for(ee=1;ee<=24;ee++)Jt[ee+111]="F"+ee;for(ee=65;ee<=90;ee++)Jt[ee]=String.fromCharCode(ee+32),Xt[ee]=String.fromCharCode(ee);for(var ie in Jt)Xt.hasOwnProperty(ie)||(Xt[ie]=Jt[ie]);function ne(t){let e;return e=11==t.nodeType?t.getSelection?t:t.ownerDocument:t,e.getSelection()}function re(t,e){return!!e&&(t==e||t.contains(1!=e.nodeType?e.parentNode:e))}function se(t,e){if(!e.anchorNode)return!1;try{return re(t,e.anchorNode)}catch(t){return!1}}function oe(t){return 3==t.nodeType?we(t,0,t.nodeValue.length).getClientRects():1==t.nodeType?t.getClientRects():[]}function le(t,e,i,n){return!!i&&(ae(t,e,i,n,-1)||ae(t,e,i,n,1))}function he(t){for(var e=0;;e++)if(!(t=t.previousSibling))return e}function ae(t,e,i,n,r){for(;;){if(t==i&&e==n)return!0;if(e==(r<0?0:ce(t))){if("DIV"==t.nodeName)return!1;let i=t.parentNode;if(!i||1!=i.nodeType)return!1;e=he(t)+(r<0?0:1),t=i}else{if(1!=t.nodeType)return!1;if(1==(t=t.childNodes[e+(r<0?-1:0)]).nodeType&&"false"==t.contentEditable)return!1;e=r<0?ce(t):0}}}function ce(t){return 3==t.nodeType?t.nodeValue.length:t.childNodes.length}const ue={left:0,right:0,top:0,bottom:0};function fe(t,e){let i=e?t.left:t.right;return{left:i,right:i,top:t.top,bottom:t.bottom}}function de(t){return{left:0,right:t.innerWidth,top:0,bottom:t.innerHeight}}class pe{constructor(){this.anchorNode=null,this.anchorOffset=0,this.focusNode=null,this.focusOffset=0}eq(t){return this.anchorNode==t.anchorNode&&this.anchorOffset==t.anchorOffset&&this.focusNode==t.focusNode&&this.focusOffset==t.focusOffset}setRange(t){this.set(t.anchorNode,t.anchorOffset,t.focusNode,t.focusOffset)}set(t,e,i,n){this.anchorNode=t,this.anchorOffset=e,this.focusNode=i,this.focusOffset=n}}let ge,me=null;function ve(t){if(t.setActive)return t.setActive();if(me)return t.focus(me);let e=[];for(let i=t;i&&(e.push(i,i.scrollTop,i.scrollLeft),i!=i.ownerDocument);i=i.parentNode);if(t.focus(null==me?{get preventScroll(){return me={preventScroll:!0},!0}}:void 0),!me){me=!1;for(let t=0;te)return i.domBoundsAround(t,e,h);if(c>=t&&-1==n&&(n=l,r=h),h>e&&i.dom.parentNode==this.dom){s=l,o=a;break}a=c,h=c+i.breakAfter}return{from:r,to:o<0?i+this.length:o,startDOM:(n?this.children[n-1].dom.nextSibling:null)||this.dom.firstChild,endDOM:s=0?this.children[s].dom:null}}markDirty(t=!1){this.dirty|=2,this.markParentsDirty(t)}markParentsDirty(t){for(let e=this.parent;e;e=e.parent){if(t&&(e.dirty|=2),1&e.dirty)return;e.dirty|=1,t=!1}}setParent(t){this.parent!=t&&(this.parent=t,this.dirty&&this.markParentsDirty(!0))}setDOM(t){this.dom&&(this.dom.cmView=null),this.dom=t,t.cmView=this}get rootView(){for(let t=this;;){let e=t.parent;if(!e)return t;t=e}}replaceChildren(t,e,i=ke){this.markDirty();for(let i=t;ithis.pos||t==this.pos&&(e>0||0==this.i||this.children[this.i-1].breakAfter))return this.off=t-this.pos,this;let i=this.children[--this.i];this.pos-=i.length+i.breakAfter}}}function Ce(t,e,i,n,r,s,o,l,h){let{children:a}=t,c=a.length?a[e]:null,u=s.length?s[s.length-1]:null,f=u?u.breakAfter:o;if(!(e==n&&c&&!o&&!f&&s.length<2&&c.merge(i,r,s.length?u:null,0==i,l,h))){if(n0&&(!o&&s.length&&c.merge(i,c.length,s[0],!1,l,0)?c.breakAfter=s.shift().breakAfter:(i2);var He={mac:We||/Mac/.test(Oe.platform),windows:/Win/.test(Oe.platform),linux:/Linux|X11/.test(Oe.platform),ie:Le,ie_version:Be?Te.documentMode||6:Re?+Re[1]:Ee?+Ee[1]:0,gecko:Ne,gecko_version:Ne?+(/Firefox\/(\d+)/.exec(Oe.userAgent)||[0,0])[1]:0,chrome:!!Pe,chrome_version:Pe?+Pe[1]:0,ios:We,android:/Android\b/.test(Oe.userAgent),webkit:Ie,safari:Ve,webkit_version:Ie?+(/\bAppleWebKit\/(\d+)/.exec(navigator.userAgent)||[0,0])[1]:0,tabSize:null!=Te.documentElement.style.tabSize?"tab-size":"-moz-tab-size"};class Fe extends Se{constructor(t){super(),this.text=t}get length(){return this.text.length}createDOM(t){this.setDOM(t||document.createTextNode(this.text))}sync(t){this.dom||this.createDOM(),this.dom.nodeValue!=this.text&&(t&&t.node==this.dom&&(t.written=!0),this.dom.nodeValue=this.text)}reuseDOM(t){3==t.nodeType&&this.createDOM(t)}merge(t,e,i){return(!i||i instanceof Fe&&!(this.length-(e-t)+i.length>256))&&(this.text=this.text.slice(0,t)+(i?i.text:"")+this.text.slice(e),this.markDirty(),!0)}split(t){let e=new Fe(this.text.slice(t));return this.text=this.text.slice(0,t),this.markDirty(),e}localPosFromDOM(t,e){return t==this.dom?e:e?this.text.length:0}domAtPos(t){return new xe(this.dom,t)}domBoundsAround(t,e,i){return{from:i,to:i+this.length,startDOM:this.dom,endDOM:this.dom.nextSibling}}coordsAt(t,e){return qe(this.dom,t,e)}}class ze extends Se{constructor(t,e=[],i=0){super(),this.mark=t,this.children=e,this.length=i;for(let t of e)t.setParent(this)}setAttrs(t){if(be(t),this.mark.class&&(t.className=this.mark.class),this.mark.attrs)for(let e in this.mark.attrs)t.setAttribute(e,this.mark.attrs[e]);return t}reuseDOM(t){t.nodeName==this.mark.tagName.toUpperCase()&&(this.setDOM(t),this.dirty|=6)}sync(t){this.dom?4&this.dirty&&this.setAttrs(this.dom):this.setDOM(this.setAttrs(document.createElement(this.mark.tagName))),super.sync(t)}merge(t,e,i,n,r,s){return(!i||!(!(i instanceof ze&&i.mark.eq(this.mark))||t&&r<=0||et&&e.push(i=t&&(n=r),i=o,r++}let s=this.length-t;return this.length=t,n>-1&&(this.children.length=n,this.markDirty()),new ze(this.mark,e,s)}domAtPos(t){return Ue(this,t)}coordsAt(t,e){return Xe(this,t,e)}}function qe(t,e,i){let n=t.nodeValue.length;e>n&&(e=n);let r=e,s=e,o=0;0==e&&i<0||e==n&&i>=0?He.chrome||He.gecko||(e?(r--,o=1):s=0)?0:l.length-1];return He.safari&&!o&&0==h.width&&(h=Array.prototype.find.call(l,(t=>t.width))||h),o?fe(h,o<0):h||null}class _e extends Se{constructor(t,e,i){super(),this.widget=t,this.length=e,this.side=i,this.prevWidget=null}static create(t,e,i){return new(t.customView||_e)(t,e,i)}split(t){let e=_e.create(this.widget,this.length-t,this.side);return this.length-=t,e}sync(){this.dom&&this.widget.updateDOM(this.dom)||(this.dom&&this.prevWidget&&this.prevWidget.destroy(this.dom),this.prevWidget=null,this.setDOM(this.widget.toDOM(this.editorView)),this.dom.contentEditable="false")}getSide(){return this.side}merge(t,e,i,n,r,s){return!(i&&(!(i instanceof _e&&this.widget.compare(i.widget))||t>0&&r<=0||e0?i.length-1:0;n=i[e],!(t>0?0==e:e==i.length-1||n.top0?-1:1);return this.length?n:fe(n,this.side>0)}get isEditable(){return!1}destroy(){super.destroy(),this.dom&&this.widget.destroy(this.dom)}}class Ke extends _e{domAtPos(t){let{topView:e,text:i}=this.widget;return e?je(t,0,e,i,((t,e)=>t.domAtPos(e)),(t=>new xe(i,Math.min(t,i.nodeValue.length)))):new xe(i,Math.min(t,i.nodeValue.length))}sync(){this.setDOM(this.widget.toDOM())}localPosFromDOM(t,e){let{topView:i,text:n}=this.widget;return i?$e(t,e,i,n):Math.min(e,this.length)}ignoreMutation(){return!1}get overrideDOMText(){return null}coordsAt(t,e){let{topView:i,text:n}=this.widget;return i?je(t,e,i,n,((t,e,i)=>t.coordsAt(e,i)),((t,e)=>qe(n,t,e))):qe(n,t,e)}destroy(){var t;super.destroy(),null===(t=this.widget.topView)||void 0===t||t.destroy()}get isEditable(){return!0}canReuseDOM(){return!0}}function je(t,e,i,n,r,s){if(i instanceof ze){for(let o=i.dom.firstChild;o;o=o.nextSibling){let i=Se.get(o);if(!i)return s(t,e);let l=re(o,n),h=i.length+(l?n.nodeValue.length:0);if(t=0;)if(e<0?n>0:n0?-1:1);return i&&i.tope.top?{left:e.left,right:e.right,top:i.top,bottom:i.bottom}:e}get overrideDOMText(){return e.empty}}function Ue(t,e){let i=t.dom,{children:n}=t,r=0;for(let t=0;rt&&e0;t--){let e=n[t-1];if(e.dom.parentNode==i)return e.domAtPos(e.length)}for(let t=r;t0&&e instanceof ze&&r.length&&(n=r[r.length-1])instanceof ze&&n.mark.eq(e.mark)?Je(n,e.children[0],i-1):(r.push(e),e.setParent(t)),t.length+=e.length}function Xe(t,e,i){let n=null,r=-1,s=null,o=-1;!function t(e,i){for(let l=0,h=0;l=i&&(a.children.length?t(a,i-h):!s&&(c>i||h==c&&a.getSide()>0)?(s=a,o=i-h):(h0?3e8:-4e8:e>0?1e8:-1e8,new si(t,e,e,i,t.widget||null,!1)}static replace(t){let e,i,n=!!t.block;if(t.isBlockGap)e=-5e8,i=4e8;else{let{start:r,end:s}=oi(t,n);e=(r?n?-3e8:-1:5e8)-1,i=1+(s?n?2e8:1:-6e8)}return new si(t,e,i,n,t.widget||null,!0)}static line(t){return new ri(t)}static set(t,e=!1){return Tt.of(t,e)}hasHeight(){return!!this.widget&&this.widget.estimatedHeight>-1}}ii.none=Tt.empty;class ni extends ii{constructor(t){let{start:e,end:i}=oi(t);super(e?-1:5e8,i?1:-6e8,null,t),this.tagName=t.tagName||"span",this.class=t.class||"",this.attrs=t.attributes||null}eq(t){return this==t||t instanceof ni&&this.tagName==t.tagName&&this.class==t.class&&Qe(this.attrs,t.attrs)}range(t,e=t){if(t>=e)throw new RangeError("Mark decorations may not be empty");return super.range(t,e)}}ni.prototype.point=!1;class ri extends ii{constructor(t){super(-2e8,-2e8,null,t)}eq(t){return t instanceof ri&&Qe(this.spec.attributes,t.spec.attributes)}range(t,e=t){if(e!=t)throw new RangeError("Line decoration ranges must be zero-length");return super.range(t,e)}}ri.prototype.mapMode=k.TrackBefore,ri.prototype.point=!0;class si extends ii{constructor(t,e,i,n,r,s){super(e,i,r,t),this.block=n,this.isReplace=s,this.mapMode=n?e<=0?k.TrackBefore:k.TrackAfter:k.TrackDel}get type(){return this.startSide=5}eq(t){return t instanceof si&&(e=this.widget,i=t.widget,e==i||!!(e&&i&&e.compare(i)))&&this.block==t.block&&this.startSide==t.startSide&&this.endSide==t.endSide;var e,i}range(t,e=t){if(this.isReplace&&(t>e||t==e&&this.startSide>0&&this.endSide<=0))throw new RangeError("Invalid range for replacement decoration");if(!this.isReplace&&e!=t)throw new RangeError("Widget decorations can only have zero-length ranges");return super.range(t,e)}}function oi(t,e=!1){let{inclusiveStart:i,inclusiveEnd:n}=t;return null==i&&(i=t.inclusive),null==n&&(n=t.inclusive),{start:null!=i?i:e,end:null!=n?n:e}}function li(t,e,i,n=0){let r=i.length-1;r>=0&&i[r]+n>=t?i[r]=Math.max(i[r],e):i.push(t,e)}si.prototype.point=!0;class hi extends Se{constructor(){super(...arguments),this.children=[],this.length=0,this.prevAttrs=void 0,this.attrs=null,this.breakAfter=0}merge(t,e,i,n,r,s){if(i){if(!(i instanceof hi))return!1;this.dom||i.transferDOM(this)}return n&&this.setDeco(i?i.attrs:null),De(this,t,e,i?i.children:[],r,s),!0}split(t){let e=new hi;if(e.breakAfter=this.breakAfter,0==this.length)return e;let{i:i,off:n}=this.childPos(t);n&&(e.append(this.children[i].split(n),0),this.children[i].merge(n,this.children[i].length,null,!1,0,0),i++);for(let t=i;t0&&0==this.children[i-1].length;)this.children[--i].destroy();return this.children.length=i,this.markDirty(),this.length=t,e}transferDOM(t){this.dom&&(this.markDirty(),t.setDOM(this.dom),t.prevAttrs=void 0===this.prevAttrs?this.attrs:this.prevAttrs,this.prevAttrs=void 0,this.dom=null)}setDeco(t){Qe(this.attrs,t)||(this.dom&&(this.prevAttrs=this.attrs,this.markDirty()),this.attrs=t)}append(t,e){Je(this,t,e)}addLineDeco(t){let e=t.spec.attributes,i=t.spec.class;e&&(this.attrs=Ye(e,this.attrs||{})),i&&(this.attrs=Ye({class:i},this.attrs||{}))}domAtPos(t){return Ue(this,t)}reuseDOM(t){"DIV"==t.nodeName&&(this.setDOM(t),this.dirty|=6)}sync(t){var e;this.dom?4&this.dirty&&(be(this.dom),this.dom.className="cm-line",this.prevAttrs=this.attrs?null:void 0):(this.setDOM(document.createElement("div")),this.dom.className="cm-line",this.prevAttrs=this.attrs?null:void 0),void 0!==this.prevAttrs&&(Ze(this.dom,this.prevAttrs,this.attrs),this.dom.classList.add("cm-line"),this.prevAttrs=void 0),super.sync(t);let i=this.dom.lastChild;for(;i&&Se.get(i)instanceof ze;)i=i.lastChild;if(!(i&&this.length&&("BR"==i.nodeName||0!=(null===(e=Se.get(i))||void 0===e?void 0:e.isEditable)||He.ios&&this.children.some((t=>t instanceof Fe))))){let t=document.createElement("BR");t.cmIgnore=!0,this.dom.appendChild(t)}}measureTextSize(){if(0==this.children.length||this.length>20)return null;let t=0;for(let e of this.children){if(!(e instanceof Fe)||/[^ -~]/.test(e.text))return null;let i=oe(e.dom);if(1!=i.length)return null;t+=i[0].width}return t?{lineHeight:this.dom.getBoundingClientRect().height,charWidth:t/this.length}:null}coordsAt(t,e){return Xe(this,t,e)}become(t){return!1}get type(){return ei.Text}static find(t,e){for(let i=0,n=0;i=e){if(r instanceof hi)return r;if(s>e)break}n=s+r.breakAfter}return null}}class ai extends Se{constructor(t,e,i){super(),this.widget=t,this.length=e,this.type=i,this.breakAfter=0,this.prevWidget=null}merge(t,e,i,n,r,s){return!(i&&(!(i instanceof ai&&this.widget.compare(i.widget))||t>0&&r<=0||e0;){if(this.textOff==this.text.length){let{value:e,lineBreak:i,done:n}=this.cursor.next(this.skip);if(this.skip=0,n)throw new Error("Ran out of text content when drawing inline views");if(i){this.posCovered()||this.getLine(),this.content.length?this.content[this.content.length-1].breakAfter=1:this.breakAtStart=1,this.flushBuffer([]),this.curLine=null,t--;continue}this.text=e,this.textOff=0}let n=Math.min(this.text.length-this.textOff,t,512);this.flushBuffer(e.slice(e.length-i)),this.getLine().append(ui(new Fe(this.text.slice(this.textOff,this.textOff+n)),e),i),this.atCursorPos=!0,this.textOff+=n,t-=n,i=0}}span(t,e,i,n){this.buildText(e-t,i,n),this.pos=e,this.openStart<0&&(this.openStart=n)}point(t,e,i,n,r,s){if(this.disallowBlockEffectsFor[s]&&i instanceof si){if(i.block)throw new RangeError("Block decorations may not be specified via plugins");if(e>this.doc.lineAt(this.pos).to)throw new RangeError("Decorations that replace line breaks may not be specified via plugins")}let o=e-t;if(i instanceof si)if(i.block){let{type:t}=i;t!=ei.WidgetAfter||this.posCovered()||this.getLine(),this.addBlockWidget(new ai(i.widget||new fi("div"),o,t))}else{let s=_e.create(i.widget||new fi("span"),o,o?0:i.startSide),l=this.atCursorPos&&!s.isEditable&&r<=n.length&&(t0),h=!s.isEditable&&(tt.some((t=>t))}),bi=P.define({combine:t=>t.some((t=>t))});class xi{constructor(t,e="nearest",i="nearest",n=5,r=5){this.range=t,this.y=e,this.x=i,this.yMargin=n,this.xMargin=r}map(t){return t.empty?this:new xi(this.range.map(t),this.y,this.x,this.yMargin,this.xMargin)}}const ki=ut.define({map:(t,e)=>t.map(e)});function Si(t,e,i){let n=t.facet(mi);n.length?n[0](e):window.onerror?window.onerror(String(e),i,void 0,void 0,e):i?console.error(i+":",e):console.error(e)}const Ai=P.define({combine:t=>!t.length||t[0]});let Mi=0;const Ci=P.define();class Di{constructor(t,e,i,n){this.id=t,this.create=e,this.domEventHandlers=i,this.extension=n(this)}static define(t,e){const{eventHandlers:i,provide:n,decorations:r}=e||{};return new Di(Mi++,t,i,(t=>{let e=[Ci.of(t)];return r&&e.push(Bi.of((e=>{let i=e.plugin(t);return i?r(i):ii.none}))),n&&e.push(n(t)),e}))}static fromClass(t,e){return Di.define((e=>new t(e)),e)}}class Oi{constructor(t){this.spec=t,this.mustUpdate=null,this.value=null}update(t){if(this.value){if(this.mustUpdate){let t=this.mustUpdate;if(this.mustUpdate=null,this.value.update)try{this.value.update(t)}catch(e){if(Si(t.state,e,"CodeMirror plugin crashed"),this.value.destroy)try{this.value.destroy()}catch(t){}this.deactivate()}}}else if(this.spec)try{this.value=this.spec.create(t)}catch(e){Si(t.state,e,"CodeMirror plugin crashed"),this.deactivate()}return this}destroy(t){var e;if(null===(e=this.value)||void 0===e?void 0:e.destroy)try{this.value.destroy()}catch(e){Si(t.state,e,"CodeMirror plugin crashed")}}deactivate(){this.spec=this.value=null}}const Ti=P.define(),Ei=P.define(),Bi=P.define(),Ri=P.define(),Li=P.define(),Ni=P.define();class Pi{constructor(t,e,i,n){this.fromA=t,this.toA=e,this.fromB=i,this.toB=n}join(t){return new Pi(Math.min(this.fromA,t.fromA),Math.max(this.toA,t.toA),Math.min(this.fromB,t.fromB),Math.max(this.toB,t.toB))}addToSet(t){let e=t.length,i=this;for(;e>0;e--){let n=t[e-1];if(!(n.fromA>i.toA)){if(n.toAa)break;r+=2}if(!l)return i;new Pi(l.fromA,l.toA,l.fromB,l.toB).addToSet(i),s=l.toA,o=l.toB}}}class Ii{constructor(t,e,i){this.view=t,this.state=e,this.transactions=i,this.flags=0,this.startState=t.state,this.changes=A.empty(this.startState.doc.length);for(let t of i)this.changes=this.changes.compose(t.changes);let n=[];this.changes.iterChangedRanges(((t,e,i,r)=>n.push(new Pi(t,e,i,r)))),this.changedRanges=n;let r=t.hasFocus;r!=t.inputState.notifiedFocused&&(t.inputState.notifiedFocused=r,this.flags|=1)}static create(t,e,i){return new Ii(t,e,i)}get viewportChanged(){return(4&this.flags)>0}get heightChanged(){return(2&this.flags)>0}get geometryChanged(){return this.docChanged||(10&this.flags)>0}get focusChanged(){return(1&this.flags)>0}get docChanged(){return!this.changes.empty}get selectionSet(){return this.transactions.some((t=>t.selection))}get empty(){return 0==this.flags&&0==this.transactions.length}}var Vi=function(t){return t[t.LTR=0]="LTR",t[t.RTL=1]="RTL",t}(Vi||(Vi={}));const Wi=Vi.LTR,Hi=Vi.RTL;function Fi(t){let e=[];for(let i=0;i=e){if(o.level==i)return s;(r<0||(0!=n?n<0?o.frome:t[r].level>o.level))&&(r=s)}}if(r<0)throw new RangeError("Index out of range");return r}}const Gi=[];function Ui(t){return[new $i(0,t,0)]}let Ji="";function Xi(t,e,i,n,r){var s;let o=n.head-t.from,l=-1;if(0==o){if(!r||!t.length)return null;e[0].level!=i&&(o=e[0].side(!1,i),l=0)}else if(o==t.length){if(r)return null;let t=e[e.length-1];t.level!=i&&(o=t.side(!0,i),l=e.length-1)}l<0&&(l=$i.find(e,o,null!==(s=n.bidiLevel)&&void 0!==s?s:-1,n.assoc));let h=e[l];o==h.side(r,i)&&(h=e[l+=r?1:-1],o=h.side(!r,i));let a=r==(h.dir==i),c=d(t.text,o,a);if(Ji=t.text.slice(Math.min(o,c),Math.max(o,c)),c!=h.side(r,i))return R.cursor(c+t.from,a?-1:1,h.level);let u=l==(r?e.length-1:0)?null:e[l+(r?1:-1)];return u||h.level==i?u&&u.level1)for(let e of this.points)e.node==t&&e.pos>this.text.length&&(e.pos-=o-1);i=s+o}}readNode(t){if(t.cmIgnore)return;let e=Se.get(t),i=e&&e.overrideDOMText;if(null!=i){this.findPointInside(t,i.length);for(let t=i.iter();!t.next().done;)t.lineBreak?this.lineBreak():this.append(t.value)}else 3==t.nodeType?this.readTextNode(t):"BR"==t.nodeName?t.nextSibling&&this.lineBreak():1==t.nodeType&&this.readRange(t.firstChild,null)}findPointBefore(t,e){for(let i of this.points)i.node==t&&t.childNodes[i.offset]==e&&(i.pos=this.text.length)}findPointInside(t,e){for(let i of this.points)(3==t.nodeType?i.node==t:t.contains(i.node))&&(i.pos=this.text.length+Math.min(e,i.offset))}}function Zi(t){return 1==t.nodeType&&/^(DIV|P|LI|UL|OL|BLOCKQUOTE|DD|DT|H\d|SECTION|PRE)$/.test(t.nodeName)}class tn{constructor(t,e){this.node=t,this.offset=e,this.pos=-1}}class en extends Se{constructor(t){super(),this.view=t,this.compositionDeco=ii.none,this.decorations=[],this.dynamicDecorationMap=[],this.minWidth=0,this.minWidthFrom=0,this.minWidthTo=0,this.impreciseAnchor=null,this.impreciseHead=null,this.forceSelection=!1,this.lastUpdate=Date.now(),this.setDOM(t.contentDOM),this.children=[new hi],this.children[0].setParent(this),this.updateDeco(),this.updateInner([new Pi(0,0,0,t.state.doc.length)],0)}get editorView(){return this.view}get length(){return this.view.state.doc.length}update(t){let e=t.changedRanges;this.minWidth>0&&e.length&&(e.every((({fromA:t,toA:e})=>ethis.minWidthTo))?(this.minWidthFrom=t.changes.mapPos(this.minWidthFrom,1),this.minWidthTo=t.changes.mapPos(this.minWidthTo,1)):this.minWidth=this.minWidthFrom=this.minWidthTo=0),this.view.inputState.composing<0?this.compositionDeco=ii.none:(t.transactions.length||this.dirty)&&(this.compositionDeco=function(t,e){let i=rn(t);if(!i)return ii.none;let{from:n,to:r,node:s,text:o}=i,l=e.mapPos(n,1),h=Math.max(l,e.mapPos(r,-1)),{state:a}=t,c=3==s.nodeType?s.nodeValue:new Qi([],a).readRange(s.firstChild,null).text;if(h-l{this.dom.style.height=this.view.viewState.contentHeight+"px",this.dom.style.flexBasis=this.minWidth?this.minWidth+"px":"";let t=He.chrome||He.ios?{node:i.selectionRange.focusNode,written:!1}:void 0;this.sync(t),this.dirty=0,t&&(t.written||i.selectionRange.focusNode!=t.node)&&(this.forceSelection=!0),this.dom.style.height=""}));let n=[];if(this.view.viewport.from||this.view.viewport.to=0?t[e]:null;if(!n)break;let{fromA:r,toA:s,fromB:o,toB:l}=n,{content:h,breakAtStart:a,openStart:c,openEnd:u}=ci.build(this.view.state.doc,o,l,this.decorations,this.dynamicDecorationMap),{i:f,off:d}=i.findPos(s,1),{i:p,off:g}=i.findPos(r,-1);Ce(this,p,g,f,d,h,a,c,u)}}updateSelection(t=!1,e=!1){if(!t&&this.view.observer.selectionRange.focusNode||this.view.observer.readSelectionRange(),!e&&!this.mayControlSelection())return;let i=this.forceSelection;this.forceSelection=!1;let n=this.view.state.selection.main,r=this.domAtPos(n.anchor),s=n.empty?r:this.domAtPos(n.head);if(He.gecko&&n.empty&&(1==(o=r).node.nodeType&&o.node.firstChild&&(0==o.offset||"false"==o.node.childNodes[o.offset-1].contentEditable)&&(o.offset==o.node.childNodes.length||"false"==o.node.childNodes[o.offset].contentEditable))){let t=document.createTextNode("");this.view.observer.ignore((()=>r.node.insertBefore(t,r.node.childNodes[r.offset]||null))),r=s=new xe(t,0),i=!0}var o;let l=this.view.observer.selectionRange;!i&&l.focusNode&&le(r.node,r.offset,l.anchorNode,l.anchorOffset)&&le(s.node,s.offset,l.focusNode,l.focusOffset)||(this.view.observer.ignore((()=>{He.android&&He.chrome&&this.dom.contains(l.focusNode)&&function(t,e){for(let i=t;i&&i!=e;i=i.assignedSlot||i.parentNode)if(1==i.nodeType&&"false"==i.contentEditable)return!0;return!1}(l.focusNode,this.dom)&&(this.dom.blur(),this.dom.focus({preventScroll:!0}));let t=ne(this.view.root);if(t)if(n.empty){if(He.gecko){let t=(e=r.node,i=r.offset,1!=e.nodeType?0:(i&&"false"==e.childNodes[i-1].contentEditable?1:0)|(in.head&&([r,s]=[s,r]),e.setEnd(s.node,s.offset),e.setStart(r.node,r.offset),t.removeAllRanges(),t.addRange(e)}else;var e,i})),this.view.observer.setSelectionRange(r,s)),this.impreciseAnchor=r.precise?null:new xe(l.anchorNode,l.anchorOffset),this.impreciseHead=s.precise?null:new xe(l.focusNode,l.focusOffset)}enforceCursorAssoc(){if(this.compositionDeco.size)return;let{view:t}=this,e=t.state.selection.main,i=ne(t.root),{anchorNode:n,anchorOffset:r}=t.observer.selectionRange;if(!(i&&e.empty&&e.assoc&&i.modify))return;let s=hi.find(this,e.head);if(!s)return;let o=s.posAtStart;if(e.head==o||e.head==o+s.length)return;let l=this.coordsAt(e.head,-1),h=this.coordsAt(e.head,1);if(!l||!h||l.bottom>h.top)return;let a=this.domAtPos(e.head+e.assoc);i.collapse(a.node,a.offset),i.modify("move",e.assoc<0?"forward":"backward","lineboundary"),t.observer.readSelectionRange();let c=t.observer.selectionRange;t.docView.posFromDOM(c.anchorNode,c.anchorOffset)!=e.from&&i.collapse(n,r)}mayControlSelection(){let t=this.view.root.activeElement;return t==this.dom||se(this.dom,this.view.observer.selectionRange)&&!(t&&this.dom.contains(t))}nearest(t){for(let e=t;e;){let t=Se.get(e);if(t&&t.rootView==this)return t;e=e.parentNode}return null}posFromDOM(t,e){let i=this.nearest(t);if(!i)throw new RangeError("Trying to find position for a DOM position outside of the document");return i.localPosFromDOM(t,e)+i.posAtStart}domAtPos(t){let{i:e,off:i}=this.childCursor().findPos(t,-1);for(;es||t==s&&r.type!=ei.WidgetBefore&&r.type!=ei.WidgetAfter&&(!n||2==e||this.children[n-1].breakAfter||this.children[n-1].type==ei.WidgetBefore&&e>-2))return r.coordsAt(t-s,e);i=s}}measureVisibleLineHeights(t){let e=[],{from:i,to:n}=t,r=this.view.contentDOM.clientWidth,s=r>Math.max(this.view.scrollDOM.clientWidth,this.minWidth)+1,o=-1,l=this.view.textDirection==Vi.LTR;for(let t=0,h=0;hn)break;if(t>=i){let i=a.dom.getBoundingClientRect();if(e.push(i.height),s){let e=a.dom.lastChild,n=e?oe(e):[];if(n.length){let e=n[n.length-1],s=l?e.right-i.left:i.right-e.left;s>o&&(o=s,this.minWidth=r,this.minWidthFrom=t,this.minWidthTo=c)}}}t=c+a.breakAfter}return e}textDirectionAt(t){let{i:e}=this.childPos(t,1);return"rtl"==getComputedStyle(this.children[e].dom).direction?Vi.RTL:Vi.LTR}measureTextSize(){for(let t of this.children)if(t instanceof hi){let e=t.measureTextSize();if(e)return e}let t,e,i=document.createElement("div");return i.className="cm-line",i.style.width="99999px",i.textContent="abc def ghi jkl mno pqr stu",this.view.observer.ignore((()=>{this.dom.appendChild(i);let n=oe(i.firstChild)[0];t=i.getBoundingClientRect().height,e=n?n.width/27:7,i.remove()})),{lineHeight:t,charWidth:e}}childCursor(t=this.length){let e=this.children.length;return e&&(t-=this.children[--e].length),new Me(this.children,t,e)}computeBlockGapDeco(){let t=[],e=this.view.viewState;for(let i=0,n=0;;n++){let r=n==e.viewports.length?null:e.viewports[n],s=r?r.from-1:this.length;if(s>i){let n=e.lineBlockAt(s).bottom-e.lineBlockAt(i).top;t.push(ii.replace({widget:new nn(n),block:!0,inclusive:!0,isBlockGap:!0}).range(i,s))}if(!r)break;i=r.to+1}return ii.set(t)}updateDeco(){let t=this.view.state.facet(Bi).map(((t,e)=>(this.dynamicDecorationMap[e]="function"==typeof t)?t(this.view):t));for(let e=t.length;ei.anchor?-1:1);if(!n)return;!i.empty&&(e=this.coordsAt(i.anchor,i.anchor>i.head?-1:1))&&(n={left:Math.min(n.left,e.left),top:Math.min(n.top,e.top),right:Math.max(n.right,e.right),bottom:Math.max(n.bottom,e.bottom)});let r=0,s=0,o=0,l=0;for(let t of this.view.state.facet(Li).map((t=>t(this.view))))if(t){let{left:e,right:i,top:n,bottom:h}=t;null!=e&&(r=Math.max(r,e)),null!=i&&(s=Math.max(s,i)),null!=n&&(o=Math.max(o,n)),null!=h&&(l=Math.max(l,h))}let h={left:n.left-r,top:n.top-o,right:n.right+s,bottom:n.bottom+l};!function(t,e,i,n,r,s,o,l){let h=t.ownerDocument,a=h.defaultView||window;for(let c=t;c;)if(1==c.nodeType){let t,u=c==h.body;if(u)t=de(a);else{if(c.scrollHeight<=c.clientHeight&&c.scrollWidth<=c.clientWidth){c=c.assignedSlot||c.parentNode;continue}let e=c.getBoundingClientRect();t={left:e.left,right:e.left+c.clientWidth,top:e.top,bottom:e.top+c.clientHeight}}let f=0,d=0;if("nearest"==r)e.top0&&e.bottom>t.bottom+d&&(d=e.bottom-t.bottom+d+o)):e.bottom>t.bottom&&(d=e.bottom-t.bottom+o,i<0&&e.top-d0&&e.right>t.right+f&&(f=e.right-t.right+f+s)):e.right>t.right&&(f=e.right-t.right+s,i<0&&e.left0&&i<=0)e=ce(t=t.childNodes[e-1]);else{if(!(1==t.nodeType&&e=0))return null;t=t.childNodes[e],e=0}}}class ln{constructor(){this.changes=[]}compareRange(t,e){li(t,e,this.changes)}comparePoint(t,e){li(t,e,this.changes)}}function hn(t,e){return e.left>t?e.left-t:Math.max(0,t-e.right)}function an(t,e){return e.top>t?e.top-t:Math.max(0,t-e.bottom)}function cn(t,e){return t.tope.top+1}function un(t,e){return et.bottom?{top:t.top,left:t.left,right:t.right,bottom:e}:t}function dn(t,e,i){let n,r,s,o,l,h,a,c,u=!1;for(let f=t.firstChild;f;f=f.nextSibling){let t=oe(f);for(let d=0;dm||o==m&&s>g)&&(n=f,r=p,s=g,o=m,u=!g||(g>0?d0)),0==g?i>p.bottom&&(!a||a.bottomp.top)&&(h=f,c=p):a&&cn(a,p)?a=fn(a,p.bottom):c&&cn(c,p)&&(c=un(c,p.top))}}if(a&&a.bottom>=i?(n=l,r=a):c&&c.top<=i&&(n=h,r=c),!n)return{node:t,offset:0};let f=Math.max(r.left,Math.min(r.right,e));return 3==n.nodeType?pn(n,f,i):u&&"false"!=n.contentEditable?dn(n,f,i):{node:t,offset:Array.prototype.indexOf.call(t.childNodes,n)+(e>=(r.left+r.right)/2?1:0)}}function pn(t,e,i){let n=t.nodeValue.length,r=-1,s=1e9,o=0;for(let l=0;li?a.top-i:i-a.bottom)-1;if(a.left-1<=e&&a.right+1>=e&&c=(a.left+a.right)/2,n=i;if(He.chrome||He.gecko){we(t,l).getBoundingClientRect().left==a.right&&(n=!i)}if(c<=0)return{node:t,offset:l+(n?1:0)};r=l+(n?1:0),s=c}}}return{node:t,offset:r>-1?r:o>0?t.nodeValue.length:0}}function gn(t,{x:e,y:i},n,r=-1){var s;let o,l=t.contentDOM.getBoundingClientRect(),h=l.top+t.viewState.paddingTop,{docHeight:a}=t.viewState,c=i-h;if(c<0)return 0;if(c>a)return t.state.doc.length;for(let e=t.defaultLineHeight/2,i=!1;o=t.elementAtHeight(c),o.type!=ei.Text;)for(;c=r>0?o.bottom+e:o.top-e,!(c>=0&&c<=a);){if(i)return n?null:0;i=!0,r=-r}i=h+c;let u=o.from;if(ut.viewport.to)return t.viewport.to==t.state.doc.length?t.state.doc.length:n?null:mn(t,l,o,e,i);let f=t.dom.ownerDocument,d=t.root.elementFromPoint?t.root:f,p=d.elementFromPoint(e,i);p&&!t.contentDOM.contains(p)&&(p=null),p||(e=Math.max(l.left+1,Math.min(l.right-1,e)),p=d.elementFromPoint(e,i),p&&!t.contentDOM.contains(p)&&(p=null));let g,m=-1;if(p&&0!=(null===(s=t.docView.nearest(p))||void 0===s?void 0:s.isEditable))if(f.caretPositionFromPoint){let t=f.caretPositionFromPoint(e,i);t&&({offsetNode:g,offset:m}=t)}else if(f.caretRangeFromPoint){let n=f.caretRangeFromPoint(e,i);n&&(({startContainer:g,startOffset:m}=n),(!t.contentDOM.contains(g)||He.safari&&function(t,e,i){let n;if(3!=t.nodeType||e!=(n=t.nodeValue.length))return!1;for(let e=t.nextSibling;e;e=e.nextSibling)if(1!=e.nodeType||"BR"!=e.nodeName)return!1;return we(t,n-1,n).getBoundingClientRect().left>i}(g,m,e)||He.chrome&&function(t,e,i){if(0!=e)return!1;for(let e=t;;){let t=e.parentNode;if(!t||1!=t.nodeType||t.firstChild!=e)return!1;if(t.classList.contains("cm-line"))break;e=t}let n=1==t.nodeType?t.getBoundingClientRect():we(t,0,Math.max(t.nodeValue.length,1)).getBoundingClientRect();return i-n.left>5}(g,m,e))&&(g=void 0))}if(!g||!t.docView.dom.contains(g)){let n=hi.find(t.docView,u);if(!n)return c>o.top+o.height/2?o.to:o.from;({node:g,offset:m}=dn(n.dom,e,i))}return t.docView.posFromDOM(g,m)}function mn(t,e,i,n,r){let s=Math.round((n-e.left)*t.defaultCharacterWidth);if(t.lineWrapping&&i.height>1.5*t.defaultLineHeight){s+=Math.floor((r-i.top)/t.defaultLineHeight)*t.viewState.heightOracle.lineLength}let o=t.state.sliceDoc(i.from,i.to);return i.from+qt(o,s,t.state.tabSize)}function vn(t,e,i,n){let r=t.state.doc.lineAt(e.head),s=t.bidiSpans(r),o=t.textDirectionAt(r.from);for(let l=e,h=null;;){let e=Xi(r,s,o,l,i),a=Ji;if(!e){if(r.number==(i?t.state.doc.lines:1))return l;a="\n",r=t.state.doc.line(r.number+(i?1:-1)),s=t.bidiSpans(r),e=R.cursor(i?r.from:r.to)}if(h){if(!h(a))return l}else{if(!n)return e;h=n(a)}l=e}}function wn(t,e,i){let n=t.state.facet(Ri).map((e=>e(t)));for(;;){let t=!1;for(let r of n)r.between(i.from-1,i.from+1,((n,r,s)=>{i.from>n&&i.fromi.from?R.cursor(n,1):R.cursor(r,-1),t=!0)}));if(!t)return i}}class yn{constructor(t){this.lastKeyCode=0,this.lastKeyTime=0,this.lastTouchTime=0,this.lastFocusTime=0,this.lastScrollTop=0,this.lastScrollLeft=0,this.chromeScrollHack=-1,this.pendingIOSKey=void 0,this.lastSelectionOrigin=null,this.lastSelectionTime=0,this.lastEscPress=0,this.lastContextMenu=0,this.scrollHandlers=[],this.registeredEvents=[],this.customHandlers=[],this.composing=-1,this.compositionFirstChange=null,this.compositionEndedAt=0,this.mouseSelection=null;for(let e in Mn){let i=Mn[e];t.contentDOM.addEventListener(e,(n=>{An(t,n)&&!this.ignoreDuringComposition(n)&&("keydown"==e&&this.keydown(t,n)||(this.mustFlushObserver(n)&&t.observer.forceFlush(),this.runCustomHandlers(e,t,n)?n.preventDefault():i(t,n)))}),Cn[e]),this.registeredEvents.push(e)}He.chrome&&102==He.chrome_version&&t.scrollDOM.addEventListener("wheel",(()=>{this.chromeScrollHack<0?t.contentDOM.style.pointerEvents="none":window.clearTimeout(this.chromeScrollHack),this.chromeScrollHack=setTimeout((()=>{this.chromeScrollHack=-1,t.contentDOM.style.pointerEvents=""}),100)}),{passive:!0}),this.notifiedFocused=t.hasFocus,He.safari&&t.contentDOM.addEventListener("input",(()=>null))}setSelectionOrigin(t){this.lastSelectionOrigin=t,this.lastSelectionTime=Date.now()}ensureHandlers(t,e){var i;let n;this.customHandlers=[];for(let r of e)if(n=null===(i=r.update(t).spec)||void 0===i?void 0:i.domEventHandlers){this.customHandlers.push({plugin:r.value,handlers:n});for(let e in n)this.registeredEvents.indexOf(e)<0&&"scroll"!=e&&(this.registeredEvents.push(e),t.contentDOM.addEventListener(e,(i=>{An(t,i)&&this.runCustomHandlers(e,t,i)&&i.preventDefault()})))}}runCustomHandlers(t,e,i){for(let n of this.customHandlers){let r=n.handlers[t];if(r)try{if(r.call(n.plugin,i,e)||i.defaultPrevented)return!0}catch(t){Si(e.state,t)}}return!1}runScrollHandlers(t,e){this.lastScrollTop=t.scrollDOM.scrollTop,this.lastScrollLeft=t.scrollDOM.scrollLeft;for(let i of this.customHandlers){let n=i.handlers.scroll;if(n)try{n.call(i.plugin,e,t)}catch(e){Si(t.state,e)}}}keydown(t,e){if(this.lastKeyCode=e.keyCode,this.lastKeyTime=Date.now(),9==e.keyCode&&Date.now()t.keyCode==e.keyCode)))&&!e.ctrlKey||xn.indexOf(e.key)>-1&&e.ctrlKey&&!e.shiftKey))&&(this.pendingIOSKey=i||e,setTimeout((()=>this.flushIOSKey(t)),250),!0)}flushIOSKey(t){let e=this.pendingIOSKey;return!!e&&(this.pendingIOSKey=void 0,ye(t.contentDOM,e.key,e.keyCode))}ignoreDuringComposition(t){return!!/^key/.test(t.type)&&(this.composing>0||!!(He.safari&&!He.ios&&Date.now()-this.compositionEndedAt<100)&&(this.compositionEndedAt=0,!0))}mustFlushObserver(t){return"keydown"==t.type&&229!=t.keyCode}startMouseSelection(t){this.mouseSelection&&this.mouseSelection.destroy(),this.mouseSelection=t}update(t){this.mouseSelection&&this.mouseSelection.update(t),t.transactions.length&&(this.lastKeyCode=this.lastSelectionTime=0)}destroy(){this.mouseSelection&&this.mouseSelection.destroy()}}const bn=[{key:"Backspace",keyCode:8,inputType:"deleteContentBackward"},{key:"Enter",keyCode:13,inputType:"insertParagraph"},{key:"Delete",keyCode:46,inputType:"deleteContentForward"}],xn="dthko",kn=[16,17,18,20,91,92,224,225];class Sn{constructor(t,e,i,n){this.view=t,this.style=i,this.mustSelect=n,this.lastEvent=e;let r=t.contentDOM.ownerDocument;r.addEventListener("mousemove",this.move=this.move.bind(this)),r.addEventListener("mouseup",this.up=this.up.bind(this)),this.extend=e.shiftKey,this.multiple=t.state.facet(St.allowMultipleSelections)&&function(t,e){let i=t.state.facet(di);return i.length?i[0](e):He.mac?e.metaKey:e.ctrlKey}(t,e),this.dragMove=function(t,e){let i=t.state.facet(pi);return i.length?i[0](e):He.mac?!e.altKey:!e.ctrlKey}(t,e),this.dragging=!(!function(t,e){let{main:i}=t.state.selection;if(i.empty)return!1;let n=ne(t.root);if(!n||0==n.rangeCount)return!0;let r=n.getRangeAt(0).getClientRects();for(let t=0;t=e.clientX&&i.top<=e.clientY&&i.bottom>=e.clientY)return!0}return!1}(t,e)||1!=Wn(e))&&null,!1===this.dragging&&(e.preventDefault(),this.select(e))}move(t){if(0==t.buttons)return this.destroy();!1===this.dragging&&this.select(this.lastEvent=t)}up(t){null==this.dragging&&this.select(this.lastEvent),this.dragging||t.preventDefault(),this.destroy()}destroy(){let t=this.view.contentDOM.ownerDocument;t.removeEventListener("mousemove",this.move),t.removeEventListener("mouseup",this.up),this.view.inputState.mouseSelection=null}select(t){let e=this.style.get(t,this.extend,this.multiple);!this.mustSelect&&e.eq(this.view.state.selection)&&e.main.assoc==this.view.state.selection.main.assoc||this.view.dispatch({selection:e,userEvent:"select.pointer",scrollIntoView:!0}),this.mustSelect=!1}update(t){t.docChanged&&this.dragging&&(this.dragging=this.dragging.map(t.changes)),this.style.update(t)&&setTimeout((()=>this.select(this.lastEvent)),20)}}function An(t,e){if(!e.bubbles)return!0;if(e.defaultPrevented)return!1;for(let i,n=e.target;n!=t.contentDOM;n=n.parentNode)if(!n||11==n.nodeType||(i=Se.get(n))&&i.ignoreEvent(e))return!1;return!0}const Mn=Object.create(null),Cn=Object.create(null),Dn=He.ie&&He.ie_version<15||He.ios&&He.webkit_version<604;function On(t,e){let i,{state:n}=t,r=1,s=n.toText(e),o=s.lines==n.selection.ranges.length;if(null!=Fn&&n.selection.ranges.every((t=>t.empty))&&Fn==s.toString()){let t=-1;i=n.changeByRange((i=>{let l=n.doc.lineAt(i.from);if(l.from==t)return{range:i};t=l.from;let h=n.toText((o?s.line(r++).text:e)+n.lineBreak);return{changes:{from:l.from,insert:h},range:R.cursor(i.from+h.length)}}))}else i=o?n.changeByRange((t=>{let e=s.line(r++);return{changes:{from:t.from,to:t.to,insert:e.text},range:R.cursor(t.from+e.length)}})):n.replaceSelection(s);t.dispatch(i,{userEvent:"input.paste",scrollIntoView:!0})}function Tn(t,e,i,n){if(1==n)return R.cursor(e,i);if(2==n)return function(t,e,i=1){let n=t.charCategorizer(e),r=t.doc.lineAt(e),s=e-r.from;if(0==r.length)return R.cursor(e);0==s?i=1:s==r.length&&(i=-1);let o=s,l=s;i<0?o=d(r.text,s,!1):l=d(r.text,s);let h=n(r.text.slice(o,l));for(;o>0;){let t=d(r.text,o,!1);if(n(r.text.slice(t,o))!=h)break;o=t}for(;l{t.inputState.setSelectionOrigin("select"),27==e.keyCode?t.inputState.lastEscPress=Date.now():kn.indexOf(e.keyCode)<0&&(t.inputState.lastEscPress=0)},Mn.touchstart=(t,e)=>{t.inputState.lastTouchTime=Date.now(),t.inputState.setSelectionOrigin("select.pointer")},Mn.touchmove=t=>{t.inputState.setSelectionOrigin("select.pointer")},Cn.touchstart=Cn.touchmove={passive:!0},Mn.mousedown=(t,e)=>{if(t.observer.flush(),t.inputState.lastTouchTime>Date.now()-2e3)return;let i=null;for(let n of t.state.facet(gi))if(i=n(t,e),i)break;if(i||0!=e.button||(i=function(t,e){let i=Ln(t,e),n=Wn(e),r=t.state.selection,s=i,o=e;return{update(t){t.docChanged&&(i.pos=t.changes.mapPos(i.pos),r=r.map(t.changes),o=null)},get(e,l,h){let a;o&&e.clientX==o.clientX&&e.clientY==o.clientY?a=s:(a=s=Ln(t,e),o=e);let c=Tn(t,a.pos,a.bias,n);if(i.pos!=a.pos&&!l){let e=Tn(t,i.pos,i.bias,n),r=Math.min(e.from,c.from),s=Math.max(e.to,c.to);c=r1&&r.ranges.some((t=>t.eq(c)))?function(t,e){for(let i=0;;i++)if(t.ranges[i].eq(e))return R.create(t.ranges.slice(0,i).concat(t.ranges.slice(i+1)),t.mainIndex==i?0:t.mainIndex-(t.mainIndex>i?1:0))}(r,c):h?r.addRange(c):R.create([c])}}}(t,e)),i){let n=t.root.activeElement!=t.contentDOM;n&&t.observer.ignore((()=>ve(t.contentDOM))),t.inputState.startMouseSelection(new Sn(t,e,i,n))}};let En=(t,e)=>t>=e.top&&t<=e.bottom,Bn=(t,e,i)=>En(e,i)&&t>=i.left&&t<=i.right;function Rn(t,e,i,n){let r=hi.find(t.docView,e);if(!r)return 1;let s=e-r.posAtStart;if(0==s)return 1;if(s==r.length)return-1;let o=r.coordsAt(s,-1);if(o&&Bn(i,n,o))return-1;let l=r.coordsAt(s,1);return l&&Bn(i,n,l)?1:o&&En(n,o)?-1:1}function Ln(t,e){let i=t.posAtCoords({x:e.clientX,y:e.clientY},!1);return{pos:i,bias:Rn(t,i,e.clientX,e.clientY)}}const Nn=He.ie&&He.ie_version<=11;let Pn=null,In=0,Vn=0;function Wn(t){if(!Nn)return t.detail;let e=Pn,i=Vn;return Pn=t,Vn=Date.now(),In=!e||i>Date.now()-400&&Math.abs(e.clientX-t.clientX)<2&&Math.abs(e.clientY-t.clientY)<2?(In+1)%3:1}function Hn(t,e,i,n){if(!i)return;let r=t.posAtCoords({x:e.clientX,y:e.clientY},!1);e.preventDefault();let{mouseSelection:s}=t.inputState,o=n&&s&&s.dragging&&s.dragMove?{from:s.dragging.from,to:s.dragging.to}:null,l={from:r,insert:i},h=t.state.changes(o?[o,l]:l);t.focus(),t.dispatch({changes:h,selection:{anchor:h.mapPos(r,-1),head:h.mapPos(r,1)},userEvent:o?"move.drop":"input.drop"})}Mn.dragstart=(t,e)=>{let{selection:{main:i}}=t.state,{mouseSelection:n}=t.inputState;n&&(n.dragging=i),e.dataTransfer&&(e.dataTransfer.setData("Text",t.state.sliceDoc(i.from,i.to)),e.dataTransfer.effectAllowed="copyMove")},Mn.drop=(t,e)=>{if(!e.dataTransfer)return;if(t.state.readOnly)return e.preventDefault();let i=e.dataTransfer.files;if(i&&i.length){e.preventDefault();let n=Array(i.length),r=0,s=()=>{++r==i.length&&Hn(t,e,n.filter((t=>null!=t)).join(t.state.lineBreak),!1)};for(let t=0;t{/[\x00-\x08\x0e-\x1f]{2}/.test(e.result)||(n[t]=e.result),s()},e.readAsText(i[t])}}else Hn(t,e,e.dataTransfer.getData("Text"),!0)},Mn.paste=(t,e)=>{if(t.state.readOnly)return e.preventDefault();t.observer.flush();let i=Dn?null:e.clipboardData;i?(On(t,i.getData("text/plain")),e.preventDefault()):function(t){let e=t.dom.parentNode;if(!e)return;let i=e.appendChild(document.createElement("textarea"));i.style.cssText="position: fixed; left: -10000px; top: 10px",i.focus(),setTimeout((()=>{t.focus(),i.remove(),On(t,i.value)}),50)}(t)};let Fn=null;function zn(t){setTimeout((()=>{t.hasFocus!=t.inputState.notifiedFocused&&t.update([])}),10)}Mn.copy=Mn.cut=(t,e)=>{let{text:i,ranges:n,linewise:r}=function(t){let e=[],i=[],n=!1;for(let n of t.selection.ranges)n.empty||(e.push(t.sliceDoc(n.from,n.to)),i.push(n));if(!e.length){let r=-1;for(let{from:n}of t.selection.ranges){let s=t.doc.lineAt(n);s.number>r&&(e.push(s.text),i.push({from:s.from,to:Math.min(t.doc.length,s.to+1)})),r=s.number}n=!0}return{text:e.join(t.lineBreak),ranges:i,linewise:n}}(t.state);if(!i&&!r)return;Fn=r?i:null;let s=Dn?null:e.clipboardData;s?(e.preventDefault(),s.clearData(),s.setData("text/plain",i)):function(t,e){let i=t.dom.parentNode;if(!i)return;let n=i.appendChild(document.createElement("textarea"));n.style.cssText="position: fixed; left: -10000px; top: 10px",n.value=e,n.focus(),n.selectionEnd=e.length,n.selectionStart=0,setTimeout((()=>{n.remove(),t.focus()}),50)}(t,i),"cut"!=e.type||t.state.readOnly||t.dispatch({changes:n,scrollIntoView:!0,userEvent:"delete.cut"})},Mn.focus=t=>{t.inputState.lastFocusTime=Date.now(),t.scrollDOM.scrollTop||!t.inputState.lastScrollTop&&!t.inputState.lastScrollLeft||(t.scrollDOM.scrollTop=t.inputState.lastScrollTop,t.scrollDOM.scrollLeft=t.inputState.lastScrollLeft),zn(t)},Mn.blur=t=>{t.observer.clearSelectionRange(),zn(t)},Mn.compositionstart=Mn.compositionupdate=t=>{null==t.inputState.compositionFirstChange&&(t.inputState.compositionFirstChange=!0),t.inputState.composing<0&&(t.inputState.composing=0)},Mn.compositionend=t=>{t.inputState.composing=-1,t.inputState.compositionEndedAt=Date.now(),t.inputState.compositionFirstChange=null,He.chrome&&He.android&&t.observer.flushSoon(),setTimeout((()=>{t.inputState.composing<0&&t.docView.compositionDeco.size&&t.update([])}),50)},Mn.contextmenu=t=>{t.inputState.lastContextMenu=Date.now()},Mn.beforeinput=(t,e)=>{var i;let n;if(He.chrome&&He.android&&(n=bn.find((t=>t.inputType==e.inputType)))&&(t.observer.delayAndroidKey(n.key,n.keyCode),"Backspace"==n.key||"Delete"==n.key)){let e=(null===(i=window.visualViewport)||void 0===i?void 0:i.height)||0;setTimeout((()=>{var i;((null===(i=window.visualViewport)||void 0===i?void 0:i.height)||0)>e+10&&t.hasFocus&&(t.contentDOM.blur(),t.focus())}),100)}};const qn=["pre-wrap","normal","pre-line","break-spaces"];class _n{constructor(t){this.lineWrapping=t,this.doc=e.empty,this.heightSamples={},this.lineHeight=14,this.charWidth=7,this.lineLength=30,this.heightChanged=!1}heightForGap(t,e){let i=this.doc.lineAt(e).number-this.doc.lineAt(t).number+1;return this.lineWrapping&&(i+=Math.ceil((e-t-i*this.lineLength*.5)/this.lineLength)),this.lineHeight*i}heightForLine(t){if(!this.lineWrapping)return this.lineHeight;return(1+Math.max(0,Math.ceil((t-this.lineLength)/(this.lineLength-5))))*this.lineHeight}setDoc(t){return this.doc=t,this}mustRefreshForWrapping(t){return qn.indexOf(t)>-1!=this.lineWrapping}mustRefreshForHeights(t){let e=!1;for(let i=0;i-1,o=Math.round(e)!=Math.round(this.lineHeight)||this.lineWrapping!=s;if(this.lineWrapping=s,this.lineHeight=e,this.charWidth=i,this.lineLength=n,o){this.heightSamples={};for(let t=0;t0}set outdated(t){this.flags=(t?2:0)|-3&this.flags}setHeight(t,e){this.height!=e&&(Math.abs(this.height-e)>Gn&&(t.heightChanged=!0),this.height=e)}replace(t,e,i){return Un.of(i)}decomposeLeft(t,e){e.push(this)}decomposeRight(t,e){e.push(this)}applyChanges(t,e,i,n){let r=this;for(let s=n.length-1;s>=0;s--){let{fromA:o,toA:l,fromB:h,toB:a}=n[s],c=r.lineAt(o,$n.ByPosNoHeight,e,0,0),u=c.to>=l?c:r.lineAt(l,$n.ByPosNoHeight,e,0,0);for(a+=u.to-l,l=u.to;s>0&&c.from<=n[s-1].toA;)o=n[s-1].fromA,h=n[s-1].fromB,s--,o2*r){let r=t[e-1];r.break?t.splice(--e,1,r.left,null,r.right):t.splice(--e,1,r.left,r.right),i+=1+r.break,n-=r.size}else{if(!(r>2*n))break;{let e=t[i];e.break?t.splice(i,1,e.left,null,e.right):t.splice(i,1,e.left,e.right),i+=2+e.break,r-=e.size}}else if(n=r&&s(this.blockAt(0,i,n,r))}updateHeight(t,e=0,i=!1,n){return n&&n.from<=e&&n.more&&this.setHeight(t,n.heights[n.index++]),this.outdated=!1,this}toString(){return`block(${this.length})`}}class Xn extends Jn{constructor(t,e){super(t,e,ei.Text),this.collapsed=0,this.widgetHeight=0}replace(t,e,i){let n=i[0];return 1==i.length&&(n instanceof Xn||n instanceof Yn&&4&n.flags)&&Math.abs(this.length-n.length)<10?(n instanceof Yn?n=new Xn(n.length,this.height):n.height=this.height,this.outdated||(n.outdated=!1),n):Un.of(i)}updateHeight(t,e=0,i=!1,n){return n&&n.from<=e&&n.more?this.setHeight(t,n.heights[n.index++]):(i||this.outdated)&&this.setHeight(t,Math.max(this.widgetHeight,t.heightForLine(this.length-this.collapsed))),this.outdated=!1,this}toString(){return`line(${this.length}${this.collapsed?-this.collapsed:""}${this.widgetHeight?":"+this.widgetHeight:""})`}}class Yn extends Un{constructor(t){super(t,0)}lines(t,e){let i=t.lineAt(e).number,n=t.lineAt(e+this.length).number;return{firstLine:i,lastLine:n,lineHeight:this.height/(n-i+1)}}blockAt(t,e,i,n){let{firstLine:r,lastLine:s,lineHeight:o}=this.lines(e,n),l=Math.max(0,Math.min(s-r,Math.floor((t-i)/o))),{from:h,length:a}=e.line(r+l);return new jn(h,a,i+o*l,o,ei.Text)}lineAt(t,e,i,n,r){if(e==$n.ByHeight)return this.blockAt(t,i,n,r);if(e==$n.ByPosNoHeight){let{from:e,to:n}=i.lineAt(t);return new jn(e,n-e,0,0,ei.Text)}let{firstLine:s,lineHeight:o}=this.lines(i,r),{from:l,length:h,number:a}=i.lineAt(t);return new jn(l,h,n+o*(a-s),o,ei.Text)}forEachLine(t,e,i,n,r,s){let{firstLine:o,lineHeight:l}=this.lines(i,r);for(let h=Math.max(t,r),a=Math.min(r+this.length,e);h<=a;){let e=i.lineAt(h);h==t&&(n+=l*(e.number-o)),s(new jn(e.from,e.length,n,l,ei.Text)),n+=l,h=e.to+1}}replace(t,e,i){let n=this.length-e;if(n>0){let t=i[i.length-1];t instanceof Yn?i[i.length-1]=new Yn(t.length+n):i.push(null,new Yn(n-1))}if(t>0){let e=i[0];e instanceof Yn?i[0]=new Yn(t+e.length):i.unshift(new Yn(t-1),null)}return Un.of(i)}decomposeLeft(t,e){e.push(new Yn(t-1),null)}decomposeRight(t,e){e.push(null,new Yn(this.length-t-1))}updateHeight(t,e=0,i=!1,n){let r=e+this.length;if(n&&n.from<=e+this.length&&n.more){let i=[],s=Math.max(e,n.from),o=-1,l=t.heightChanged;for(n.from>e&&i.push(new Yn(n.from-e-1).updateHeight(t,e));s<=r&&n.more;){let e=t.doc.lineAt(s).length;i.length&&i.push(null);let r=n.heights[n.index++];-1==o?o=r:Math.abs(r-o)>=Gn&&(o=-2);let l=new Xn(e,r);l.outdated=!1,i.push(l),s+=e+1}s<=r&&i.push(null,new Yn(r-s).updateHeight(t,s));let h=Un.of(i);return t.heightChanged=l||o<0||Math.abs(h.height-this.height)>=Gn||Math.abs(o-this.lines(t.doc,e).lineHeight)>=Gn,h}return(i||this.outdated)&&(this.setHeight(t,t.heightForGap(e,e+this.length)),this.outdated=!1),this}toString(){return`gap(${this.length})`}}class Qn extends Un{constructor(t,e,i){super(t.length+e+i.length,t.height+i.height,e|(t.outdated||i.outdated?2:0)),this.left=t,this.right=i,this.size=t.size+i.size}get break(){return 1&this.flags}blockAt(t,e,i,n){let r=i+this.left.height;return to))return h;let a=e==$n.ByPosNoHeight?$n.ByPosNoHeight:$n.ByPos;return l?h.join(this.right.lineAt(o,a,i,s,o)):this.left.lineAt(o,a,i,n,r).join(h)}forEachLine(t,e,i,n,r,s){let o=n+this.left.height,l=r+this.left.length+this.break;if(this.break)t=l&&this.right.forEachLine(t,e,i,o,l,s);else{let h=this.lineAt(l,$n.ByPos,i,n,r);t=t&&h.from<=e&&s(h),e>h.to&&this.right.forEachLine(h.to+1,e,i,o,l,s)}}replace(t,e,i){let n=this.left.length+this.break;if(ethis.left.length)return this.balanced(this.left,this.right.replace(t-n,e-n,i));let r=[];t>0&&this.decomposeLeft(t,r);let s=r.length;for(let t of i)r.push(t);if(t>0&&Zn(r,s-1),e=i&&e.push(null)),t>i&&this.right.decomposeLeft(t-i,e)}decomposeRight(t,e){let i=this.left.length,n=i+this.break;if(t>=n)return this.right.decomposeRight(t-n,e);t2*e.size||e.size>2*t.size?Un.of(this.break?[t,null,e]:[t,e]):(this.left=t,this.right=e,this.height=t.height+e.height,this.outdated=t.outdated||e.outdated,this.size=t.size+e.size,this.length=t.length+this.break+e.length,this)}updateHeight(t,e=0,i=!1,n){let{left:r,right:s}=this,o=e+r.length+this.break,l=null;return n&&n.from<=e+r.length&&n.more?l=r=r.updateHeight(t,e,i,n):r.updateHeight(t,e,i),n&&n.from<=o+s.length&&n.more?l=s=s.updateHeight(t,o,i,n):s.updateHeight(t,o,i),l?this.balanced(r,s):(this.height=this.left.height+this.right.height,this.outdated=!1,this)}toString(){return this.left+(this.break?" ":"-")+this.right}}function Zn(t,e){let i,n;null==t[e]&&(i=t[e-1])instanceof Yn&&(n=t[e+1])instanceof Yn&&t.splice(e-1,3,new Yn(i.length+1+n.length))}class tr{constructor(t,e){this.pos=t,this.oracle=e,this.nodes=[],this.lineStart=-1,this.lineEnd=-1,this.covering=null,this.writtenTo=t}get isCovered(){return this.covering&&this.nodes[this.nodes.length-1]==this.covering}span(t,e){if(this.lineStart>-1){let t=Math.min(e,this.lineEnd),i=this.nodes[this.nodes.length-1];i instanceof Xn?i.length+=t-this.pos:(t>this.pos||!this.isCovered)&&this.nodes.push(new Xn(t-this.pos,-1)),this.writtenTo=t,e>t&&(this.nodes.push(null),this.writtenTo++,this.lineStart=-1)}this.pos=e}point(t,e,i){if(t=5)&&this.addLineDeco(n,r)}else e>t&&this.span(t,e);this.lineEnd>-1&&this.lineEnd-1)return;let{from:t,to:e}=this.oracle.doc.lineAt(this.pos);this.lineStart=t,this.lineEnd=e,this.writtenTot&&this.nodes.push(new Xn(this.pos-t,-1)),this.writtenTo=this.pos}blankContent(t,e){let i=new Yn(e-t);return this.oracle.doc.lineAt(t).to==e&&(i.flags|=4),i}ensureLine(){this.enterLine();let t=this.nodes.length?this.nodes[this.nodes.length-1]:null;if(t instanceof Xn)return t;let e=new Xn(0,-1);return this.nodes.push(e),e}addBlock(t){this.enterLine(),t.type!=ei.WidgetAfter||this.isCovered||this.ensureLine(),this.nodes.push(t),this.writtenTo=this.pos=this.pos+t.length,t.type!=ei.WidgetBefore&&(this.covering=t)}addLineDeco(t,e){let i=this.ensureLine();i.length+=e,i.collapsed+=e,i.widgetHeight=Math.max(i.widgetHeight,t),this.writtenTo=this.pos=this.pos+e}finish(t){let e=0==this.nodes.length?null:this.nodes[this.nodes.length-1];!(this.lineStart>-1)||e instanceof Xn||this.isCovered?(this.writtenToi.clientHeight||i.scrollWidth>i.clientWidth)&&"visible"!=n.overflow){let n=i.getBoundingClientRect();s=Math.max(s,n.left),o=Math.min(o,n.right),l=Math.max(l,n.top),h=e==t.parentNode?n.bottom:Math.min(h,n.bottom)}e="absolute"==n.position||"fixed"==n.position?i.offsetParent:i.parentNode}else{if(11!=e.nodeType)break;e=e.host}return{left:s-i.left,right:Math.max(s,o)-i.left,top:l-(i.top+e),bottom:Math.max(l,h)-(i.top+e)}}function nr(t,e){let i=t.getBoundingClientRect();return{left:0,right:i.right-i.left,top:e,bottom:i.bottom-(i.top+e)}}class rr{constructor(t,e,i){this.from=t,this.to=e,this.size=i}static same(t,e){if(t.length!=e.length)return!1;for(let i=0;i"function"!=typeof t&&"cm-lineWrapping"==t.class));this.heightOracle=new _n(i),this.stateDeco=t.facet(Bi).filter((t=>"function"!=typeof t)),this.heightMap=Un.empty().applyChanges(this.stateDeco,e.empty,this.heightOracle.setDoc(t.doc),[new Pi(0,0,0,t.doc.length)]),this.viewport=this.getViewport(0,null),this.updateViewportLines(),this.updateForViewport(),this.lineGaps=this.ensureLineGaps([]),this.lineGapDeco=ii.set(this.lineGaps.map((t=>t.draw(!1)))),this.computeVisibleRanges()}updateForViewport(){let t=[this.viewport],{main:e}=this.state.selection;for(let i=0;i<=1;i++){let n=i?e.head:e.anchor;if(!t.some((({from:t,to:e})=>n>=t&&n<=e))){let{from:e,to:i}=this.lineBlockAt(n);t.push(new lr(e,i))}}this.viewports=t.sort(((t,e)=>t.from-e.from)),this.scaler=this.heightMap.height<=7e6?ur:new fr(this.heightOracle.doc,this.heightMap,this.viewports)}updateViewportLines(){this.viewportLines=[],this.heightMap.forEachLine(this.viewport.from,this.viewport.to,this.state.doc,0,0,(t=>{this.viewportLines.push(1==this.scaler.scale?t:dr(t,this.scaler))}))}update(t,e=null){this.state=t.state;let i=this.stateDeco;this.stateDeco=this.state.facet(Bi).filter((t=>"function"!=typeof t));let n=t.changedRanges,r=Pi.extendWithRanges(n,function(t,e,i){let n=new er;return Tt.compare(t,e,i,n,0),n.changes}(i,this.stateDeco,t?t.changes:A.empty(this.state.doc.length))),s=this.heightMap.height;this.heightMap=this.heightMap.applyChanges(this.stateDeco,t.startState.doc,this.heightOracle.setDoc(this.state.doc),r),this.heightMap.height!=s&&(t.flags|=2);let o=r.length?this.mapViewport(this.viewport,t.changes):this.viewport;(e&&(e.range.heado.to)||!this.viewportIsAppropriate(o))&&(o=this.getViewport(0,e));let l=!t.changes.empty||2&t.flags||o.from!=this.viewport.from||o.to!=this.viewport.to;this.viewport=o,this.updateForViewport(),l&&this.updateViewportLines(),(this.lineGaps.length||this.viewport.to-this.viewport.from>4e3)&&this.updateLineGaps(this.ensureLineGaps(this.mapLineGaps(this.lineGaps,t.changes))),t.flags|=this.computeVisibleRanges(),e&&(this.scrollTarget=e),!this.mustEnforceCursorAssoc&&t.selectionSet&&t.view.lineWrapping&&t.state.selection.main.empty&&t.state.selection.main.assoc&&!t.state.facet(bi)&&(this.mustEnforceCursorAssoc=!0)}measure(t){let i=t.contentDOM,n=window.getComputedStyle(i),r=this.heightOracle,s=n.whiteSpace;this.defaultTextDirection="rtl"==n.direction?Vi.RTL:Vi.LTR;let o=this.heightOracle.mustRefreshForWrapping(s),l=o||this.mustMeasureContent||this.contentDOMHeight!=i.clientHeight;this.contentDOMHeight=i.clientHeight,this.mustMeasureContent=!1;let h=0,a=0,c=parseInt(n.paddingTop)||0,u=parseInt(n.paddingBottom)||0;this.paddingTop==c&&this.paddingBottom==u||(this.paddingTop=c,this.paddingBottom=u,h|=10),this.editorWidth!=t.scrollDOM.clientWidth&&(r.lineWrapping&&(l=!0),this.editorWidth=t.scrollDOM.clientWidth,h|=8);let f=(this.printing?nr:ir)(i,this.paddingTop),d=f.top-this.pixelViewport.top,p=f.bottom-this.pixelViewport.bottom;this.pixelViewport=f;let g=this.pixelViewport.bottom>this.pixelViewport.top&&this.pixelViewport.right>this.pixelViewport.left;if(g!=this.inView&&(this.inView=g,g&&(l=!0)),!this.inView&&!this.scrollTarget)return 0;let m=i.clientWidth;if(this.contentDOMWidth==m&&this.editorHeight==t.scrollDOM.clientHeight||(this.contentDOMWidth=m,this.editorHeight=t.scrollDOM.clientHeight,h|=8),l){let i=t.docView.measureVisibleLineHeights(this.viewport);if(r.mustRefreshForHeights(i)&&(o=!0),o||r.lineWrapping&&Math.abs(m-this.contentDOMWidth)>r.charWidth){let{lineHeight:e,charWidth:n}=t.docView.measureTextSize();o=e>0&&r.refresh(s,e,n,m/n,i),o&&(t.docView.minWidth=0,h|=8)}d>0&&p>0?a=Math.max(d,p):d<0&&p<0&&(a=Math.min(d,p)),r.heightChanged=!1;for(let n of this.viewports){let s=n.from==this.viewport.from?i:t.docView.measureVisibleLineHeights(n);this.heightMap=(o?Un.empty().applyChanges(this.stateDeco,e.empty,this.heightOracle,[new Pi(0,0,0,t.state.doc.length)]):this.heightMap).updateHeight(r,0,o,new Kn(n.from,s))}r.heightChanged&&(h|=2)}let v=!this.viewportIsAppropriate(this.viewport,a)||this.scrollTarget&&(this.scrollTarget.range.headthis.viewport.to);return v&&(this.viewport=this.getViewport(a,this.scrollTarget)),this.updateForViewport(),(2&h||v)&&this.updateViewportLines(),(this.lineGaps.length||this.viewport.to-this.viewport.from>4e3)&&this.updateLineGaps(this.ensureLineGaps(o?[]:this.lineGaps,t)),h|=this.computeVisibleRanges(),this.mustEnforceCursorAssoc&&(this.mustEnforceCursorAssoc=!1,t.docView.enforceCursorAssoc()),h}get visibleTop(){return this.scaler.fromDOM(this.pixelViewport.top)}get visibleBottom(){return this.scaler.fromDOM(this.pixelViewport.bottom)}getViewport(t,e){let i=.5-Math.max(-.5,Math.min(.5,t/1e3/2)),n=this.heightMap,r=this.state.doc,{visibleTop:s,visibleBottom:o}=this,l=new lr(n.lineAt(s-1e3*i,$n.ByHeight,r,0,0).from,n.lineAt(o+1e3*(1-i),$n.ByHeight,r,0,0).to);if(e){let{head:t}=e.range;if(tl.to){let i,s=Math.min(this.editorHeight,this.pixelViewport.bottom-this.pixelViewport.top),o=n.lineAt(t,$n.ByPos,r,0,0);i="center"==e.y?(o.top+o.bottom)/2-s/2:"start"==e.y||"nearest"==e.y&&t=o+Math.max(10,Math.min(i,250)))&&n>s-2e3&&r>1,s=n<<1;if(this.defaultTextDirection!=Vi.LTR&&!i)return[];let o=[],l=(n,s,h,a)=>{if(s-nn&&tt.from>=h.from&&t.to<=h.to&&Math.abs(t.from-n)t.frome))));if(!f){if(st.from<=s&&t.to>=s))){let t=e.moveToLineBoundary(R.cursor(s),!1,!0).head;t>n&&(s=t)}f=new rr(n,s,this.gapSize(h,n,s,a))}o.push(f)};for(let t of this.viewportLines){if(t.lengtht.from&&l(t.from,r,t,e),ot.draw(this.heightOracle.lineWrapping)))))}computeVisibleRanges(){let t=this.stateDeco;this.lineGaps.length&&(t=t.concat(this.lineGapDeco));let e=[];Tt.spans(t,this.viewport.from,this.viewport.to,{span(t,i){e.push({from:t,to:i})},point(){}},20);let i=e.length!=this.visibleRanges.length||this.visibleRanges.some(((t,i)=>t.from!=e[i].from||t.to!=e[i].to));return this.visibleRanges=e,i?4:0}lineBlockAt(t){return t>=this.viewport.from&&t<=this.viewport.to&&this.viewportLines.find((e=>e.from<=t&&e.to>=t))||dr(this.heightMap.lineAt(t,$n.ByPos,this.state.doc,0,0),this.scaler)}lineBlockAtHeight(t){return dr(this.heightMap.lineAt(this.scaler.fromDOM(t),$n.ByHeight,this.state.doc,0,0),this.scaler)}elementAtHeight(t){return dr(this.heightMap.blockAt(this.scaler.fromDOM(t),this.state.doc,0,0),this.scaler)}get docHeight(){return this.scaler.toDOM(this.heightMap.height)}get contentHeight(){return this.docHeight+this.paddingTop+this.paddingBottom}}class lr{constructor(t,e){this.from=t,this.to=e}}function hr(t,e,i){let n=[],r=t,s=0;return Tt.spans(i,t,e,{span(){},point(t,e){t>r&&(n.push({from:r,to:t}),s+=t-r),r=e}},20),r=1)return e[e.length-1].to;let n=Math.floor(t*i);for(let t=0;;t++){let{from:i,to:r}=e[t],s=r-i;if(n<=s)return i+n;n-=s}}function cr(t,e){let i=0;for(let{from:n,to:r}of t.ranges){if(e<=r){i+=e-n;break}i+=r-n}return i/t.total}const ur={toDOM:t=>t,fromDOM:t=>t,scale:1};class fr{constructor(t,e,i){let n=0,r=0,s=0;this.viewports=i.map((({from:i,to:r})=>{let s=e.lineAt(i,$n.ByPos,t,0,0).top,o=e.lineAt(r,$n.ByPos,t,0,0).bottom;return n+=o-s,{from:i,to:r,top:s,bottom:o,domTop:0,domBottom:0}})),this.scale=(7e6-n)/(e.height-n);for(let t of this.viewports)t.domTop=s+(t.top-r)*this.scale,s=t.domBottom=t.domTop+(t.bottom-t.top),r=t.bottom}toDOM(t){for(let e=0,i=0,n=0;;e++){let r=edr(t,e))):t.type)}const pr=P.define({combine:t=>t.join(" ")}),gr=P.define({combine:t=>t.indexOf(!0)>-1}),mr=$t.newName(),vr=$t.newName(),wr=$t.newName(),yr={"&light":"."+vr,"&dark":"."+wr};function br(t,e,i){return new $t(e,{finish:e=>/&/.test(e)?e.replace(/&\w*/,(e=>{if("&"==e)return t;if(!i||!i[e])throw new RangeError(`Unsupported selector: ${e}`);return i[e]})):t+" "+e})}const xr=br("."+mr,{"&.cm-editor":{position:"relative !important",boxSizing:"border-box","&.cm-focused":{outline:"1px dotted #212121"},display:"flex !important",flexDirection:"column"},".cm-scroller":{display:"flex !important",alignItems:"flex-start !important",fontFamily:"monospace",lineHeight:1.4,height:"100%",overflowX:"auto",position:"relative",zIndex:0},".cm-content":{margin:0,flexGrow:2,flexShrink:0,minHeight:"100%",display:"block",whiteSpace:"pre",wordWrap:"normal",boxSizing:"border-box",padding:"4px 0",outline:"none","&[contenteditable=true]":{WebkitUserModify:"read-write-plaintext-only"}},".cm-lineWrapping":{whiteSpace_fallback:"pre-wrap",whiteSpace:"break-spaces",wordBreak:"break-word",overflowWrap:"anywhere",flexShrink:1},"&light .cm-content":{caretColor:"black"},"&dark .cm-content":{caretColor:"white"},".cm-line":{display:"block",padding:"0 2px 0 6px"},".cm-layer":{contain:"size style","& > *":{position:"absolute"}},"&light .cm-selectionBackground":{background:"#d9d9d9"},"&dark .cm-selectionBackground":{background:"#222"},"&light.cm-focused .cm-selectionBackground":{background:"#d7d4f0"},"&dark.cm-focused .cm-selectionBackground":{background:"#233"},".cm-cursorLayer":{pointerEvents:"none"},"&.cm-focused .cm-cursorLayer":{animation:"steps(1) cm-blink 1.2s infinite"},"@keyframes cm-blink":{"0%":{},"50%":{opacity:0},"100%":{}},"@keyframes cm-blink2":{"0%":{},"50%":{opacity:0},"100%":{}},".cm-cursor, .cm-dropCursor":{borderLeft:"1.2px solid black",marginLeft:"-0.6px",pointerEvents:"none"},".cm-cursor":{display:"none"},"&dark .cm-cursor":{borderLeftColor:"#444"},"&.cm-focused .cm-cursor":{display:"block"},"&light .cm-activeLine":{backgroundColor:"#cceeff44"},"&dark .cm-activeLine":{backgroundColor:"#99eeff33"},"&light .cm-specialChar":{color:"red"},"&dark .cm-specialChar":{color:"#f78"},".cm-gutters":{flexShrink:0,display:"flex",height:"100%",boxSizing:"border-box",left:0,zIndex:200},"&light .cm-gutters":{backgroundColor:"#f5f5f5",color:"#6c6c6c",borderRight:"1px solid #ddd"},"&dark .cm-gutters":{backgroundColor:"#333338",color:"#ccc"},".cm-gutter":{display:"flex !important",flexDirection:"column",flexShrink:0,boxSizing:"border-box",minHeight:"100%",overflow:"hidden"},".cm-gutterElement":{boxSizing:"border-box"},".cm-lineNumbers .cm-gutterElement":{padding:"0 3px 0 5px",minWidth:"20px",textAlign:"right",whiteSpace:"nowrap"},"&light .cm-activeLineGutter":{backgroundColor:"#e2f2ff"},"&dark .cm-activeLineGutter":{backgroundColor:"#222227"},".cm-panels":{boxSizing:"border-box",position:"sticky",left:0,right:0},"&light .cm-panels":{backgroundColor:"#f5f5f5",color:"black"},"&light .cm-panels-top":{borderBottom:"1px solid #ddd"},"&light .cm-panels-bottom":{borderTop:"1px solid #ddd"},"&dark .cm-panels":{backgroundColor:"#333338",color:"white"},".cm-tab":{display:"inline-block",overflow:"hidden",verticalAlign:"bottom"},".cm-widgetBuffer":{verticalAlign:"text-top",height:"1em",width:0,display:"inline"},".cm-placeholder":{color:"#888",display:"inline-block",verticalAlign:"top"},".cm-button":{verticalAlign:"middle",color:"inherit",fontSize:"70%",padding:".2em 1em",borderRadius:"1px"},"&light .cm-button":{backgroundImage:"linear-gradient(#eff1f5, #d9d9df)",border:"1px solid #888","&:active":{backgroundImage:"linear-gradient(#b4b4b4, #d0d3d6)"}},"&dark .cm-button":{backgroundImage:"linear-gradient(#393939, #111)",border:"1px solid #888","&:active":{backgroundImage:"linear-gradient(#111, #333)"}},".cm-textfield":{verticalAlign:"middle",color:"inherit",fontSize:"70%",border:"1px solid silver",padding:".2em .5em"},"&light .cm-textfield":{backgroundColor:"white"},"&dark .cm-textfield":{border:"1px solid #555",backgroundColor:"inherit"}},yr);class kr{constructor(t,e,i,n){this.typeOver=n,this.bounds=null,this.text="";let{impreciseHead:r,impreciseAnchor:s}=t.docView;if(t.state.readOnly&&e>-1)this.newSel=null;else if(e>-1&&(this.bounds=t.docView.domBoundsAround(e,i,0))){let e=r||s?[]:function(t){let e=[];if(t.root.activeElement!=t.contentDOM)return e;let{anchorNode:i,anchorOffset:n,focusNode:r,focusOffset:s}=t.observer.selectionRange;i&&(e.push(new tn(i,n)),r==i&&s==n||e.push(new tn(r,s)));return e}(t),i=new Qi(e,t.state);i.readRange(this.bounds.startDOM,this.bounds.endDOM),this.text=i.text,this.newSel=function(t,e){if(0==t.length)return null;let i=t[0].pos,n=2==t.length?t[1].pos:i;return i>-1&&n>-1?R.single(i+e,n+e):null}(e,this.bounds.from)}else{let e=t.observer.selectionRange,i=r&&r.node==e.focusNode&&r.offset==e.focusOffset||!re(t.contentDOM,e.focusNode)?t.state.selection.main.head:t.docView.posFromDOM(e.focusNode,e.focusOffset),n=s&&s.node==e.anchorNode&&s.offset==e.anchorOffset||!re(t.contentDOM,e.anchorNode)?t.state.selection.main.anchor:t.docView.posFromDOM(e.anchorNode,e.anchorOffset);this.newSel=R.single(n,i)}}}function Sr(t,i){let n,{newSel:r}=i,s=t.state.selection.main;if(i.bounds){let{from:r,to:o}=i.bounds,l=s.from,h=null;(8===t.inputState.lastKeyCode&&t.inputState.lastKeyTime>Date.now()-100||He.android&&i.text.length0&&l>0&&t.charCodeAt(o-1)==e.charCodeAt(l-1);)o--,l--;if("end"==n){i-=o+Math.max(0,s-Math.min(o,l))-s}if(o=o?s-i:0,l=s+(l-o),o=s}else if(l=l?s-i:0,o=s+(o-l),l=s}return{from:s,toA:o,toB:l}}(t.state.doc.sliceString(r,o,Yi),i.text,l-r,h);a&&(He.chrome&&13==t.inputState.lastKeyCode&&a.toB==a.from+2&&i.text.slice(a.from,a.toB)==Yi+Yi&&a.toB--,n={from:r+a.from,to:r+a.toA,insert:e.of(i.text.slice(a.from,a.toB).split(Yi))})}else!r||t.hasFocus&&t.state.facet(Ai)&&!r.main.eq(s)||(r=null);if(!n&&!r)return!1;if(!n&&i.typeOver&&!s.empty&&r&&r.main.empty?n={from:s.from,to:s.to,insert:t.state.doc.slice(s.from,s.to)}:n&&n.from>=s.from&&n.to<=s.to&&(n.from!=s.from||n.to!=s.to)&&s.to-s.from-(n.to-n.from)<=4?n={from:s.from,to:s.to,insert:t.state.doc.slice(s.from,n.from).append(n.insert).append(t.state.doc.slice(n.to,s.to))}:(He.mac||He.android)&&n&&n.from==n.to&&n.from==s.head-1&&/^\. ?$/.test(n.insert.toString())?(r&&2==n.insert.length&&(r=R.single(r.main.anchor-1,r.main.head-1)),n={from:s.from,to:s.to,insert:e.of([" "])}):He.chrome&&n&&n.from==n.to&&n.from==s.head&&"\n "==n.insert.toString()&&t.lineWrapping&&(r&&(r=R.single(r.main.anchor-1,r.main.head-1)),n={from:s.from,to:s.to,insert:e.of([" "])}),n){let e=t.state;if(He.ios&&t.inputState.flushIOSKey(t))return!0;if(He.android&&(n.from==s.from&&n.to==s.to&&1==n.insert.length&&2==n.insert.lines&&ye(t.contentDOM,"Enter",13)||n.from==s.from-1&&n.to==s.to&&0==n.insert.length&&ye(t.contentDOM,"Backspace",8)||n.from==s.from&&n.to==s.to+1&&0==n.insert.length&&ye(t.contentDOM,"Delete",46)))return!0;let i,o=n.insert.toString();if(t.state.facet(wi).some((e=>e(t,n.from,n.to,o))))return!0;if(t.inputState.composing>=0&&t.inputState.composing++,n.from>=s.from&&n.to<=s.to&&n.to-n.from>=(s.to-s.from)/3&&(!r||r.main.empty&&r.main.from==n.from+n.insert.length)&&t.inputState.composing<0){let r=s.fromn.to?e.sliceDoc(n.to,s.to):"";i=e.replaceSelection(t.state.toText(r+n.insert.sliceString(0,void 0,t.state.lineBreak)+o))}else{let o=e.changes(n),l=r&&!e.selection.main.eq(r.main)&&r.main.to<=o.newLength?r.main:void 0;if(e.selection.ranges.length>1&&t.inputState.composing>=0&&n.to<=s.to&&n.to>=s.to-10){let r=t.state.sliceDoc(n.from,n.to),h=rn(t)||t.state.doc.lineAt(s.head),a=s.to-n.to,c=s.to-s.from;i=e.changeByRange((i=>{if(i.from==s.from&&i.to==s.to)return{changes:o,range:l||i.map(o)};let u=i.to-a,f=u-r.length;if(i.to-i.from!=c||t.state.sliceDoc(f,u)!=r||h&&i.to>=h.from&&i.from<=h.to)return{range:i};let d=e.changes({from:f,to:u,insert:n.insert}),p=i.to-s.to;return{changes:d,range:l?R.range(Math.max(0,l.anchor+p),Math.max(0,l.head+p)):i.map(d)}}))}else i={changes:o,selection:l&&e.selection.replaceRange(l)}}let l="input.type";return t.composing&&(l+=".compose",t.inputState.compositionFirstChange&&(l+=".start",t.inputState.compositionFirstChange=!1)),t.dispatch(i,{scrollIntoView:!0,userEvent:l}),!0}if(r&&!r.main.eq(s)){let e=!1,i="select";return t.inputState.lastSelectionTime>Date.now()-50&&("select"==t.inputState.lastSelectionOrigin&&(e=!0),i=t.inputState.lastSelectionOrigin),t.dispatch({selection:r,scrollIntoView:e,userEvent:i}),!0}return!1}const Ar={childList:!0,characterData:!0,subtree:!0,attributes:!0,characterDataOldValue:!0},Mr=He.ie&&He.ie_version<=11;class Cr{constructor(t){this.view=t,this.active=!1,this.selectionRange=new pe,this.selectionChanged=!1,this.delayedFlush=-1,this.resizeTimeout=-1,this.queue=[],this.delayedAndroidKey=null,this.flushingAndroidKey=-1,this.lastChange=0,this.scrollTargets=[],this.intersection=null,this.resize=null,this.intersecting=!1,this.gapIntersection=null,this.gaps=[],this.parentCheck=-1,this.dom=t.contentDOM,this.observer=new MutationObserver((e=>{for(let t of e)this.queue.push(t);(He.ie&&He.ie_version<=11||He.ios&&t.composing)&&e.some((t=>"childList"==t.type&&t.removedNodes.length||"characterData"==t.type&&t.oldValue.length>t.target.nodeValue.length))?this.flushSoon():this.flush()})),Mr&&(this.onCharData=t=>{this.queue.push({target:t.target,type:"characterData",oldValue:t.prevValue}),this.flushSoon()}),this.onSelectionChange=this.onSelectionChange.bind(this),this.onResize=this.onResize.bind(this),this.onPrint=this.onPrint.bind(this),this.onScroll=this.onScroll.bind(this),"function"==typeof ResizeObserver&&(this.resize=new ResizeObserver((()=>{var t;(null===(t=this.view.docView)||void 0===t?void 0:t.lastUpdate){this.parentCheck<0&&(this.parentCheck=setTimeout(this.listenForScroll.bind(this),1e3)),t.length>0&&t[t.length-1].intersectionRatio>0!=this.intersecting&&(this.intersecting=!this.intersecting,this.intersecting!=this.view.inView&&this.onScrollChanged(document.createEvent("Event")))}),{}),this.intersection.observe(this.dom),this.gapIntersection=new IntersectionObserver((t=>{t.length>0&&t[t.length-1].intersectionRatio>0&&this.onScrollChanged(document.createEvent("Event"))}),{})),this.listenForScroll(),this.readSelectionRange()}onScrollChanged(t){this.view.inputState.runScrollHandlers(this.view,t),this.intersecting&&this.view.measure()}onScroll(t){this.intersecting&&this.flush(!1),this.onScrollChanged(t)}onResize(){this.resizeTimeout<0&&(this.resizeTimeout=setTimeout((()=>{this.resizeTimeout=-1,this.view.requestMeasure()}),50))}onPrint(){this.view.viewState.printing=!0,this.view.measure(),setTimeout((()=>{this.view.viewState.printing=!1,this.view.requestMeasure()}),500)}updateGaps(t){if(this.gapIntersection&&(t.length!=this.gaps.length||this.gaps.some(((e,i)=>e!=t[i])))){this.gapIntersection.disconnect();for(let e of t)this.gapIntersection.observe(e);this.gaps=t}}onSelectionChange(t){let e=this.selectionChanged;if(!this.readSelectionRange()||this.delayedAndroidKey)return;let{view:i}=this,n=this.selectionRange;if(i.state.facet(Ai)?i.root.activeElement!=this.dom:!se(i.dom,n))return;let r=n.anchorNode&&i.docView.nearest(n.anchorNode);r&&r.ignoreEvent(t)?e||(this.selectionChanged=!1):(He.ie&&He.ie_version<=11||He.android&&He.chrome)&&!i.state.selection.main.empty&&n.focusNode&&le(n.focusNode,n.focusOffset,n.anchorNode,n.anchorOffset)?this.flushSoon():this.flush(!1)}readSelectionRange(){let{view:t}=this,e=He.safari&&11==t.root.nodeType&&function(t){let e=t.activeElement;for(;e&&e.shadowRoot;)e=e.shadowRoot.activeElement;return e}(this.dom.ownerDocument)==this.dom&&function(t){let e=null;function i(t){t.preventDefault(),t.stopImmediatePropagation(),e=t.getTargetRanges()[0]}if(t.contentDOM.addEventListener("beforeinput",i,!0),t.dom.ownerDocument.execCommand("indent"),t.contentDOM.removeEventListener("beforeinput",i,!0),!e)return null;let n=e.startContainer,r=e.startOffset,s=e.endContainer,o=e.endOffset,l=t.docView.domAtPos(t.state.selection.main.anchor);le(l.node,l.offset,s,o)&&([n,r,s,o]=[s,o,n,r]);return{anchorNode:n,anchorOffset:r,focusNode:s,focusOffset:o}}(this.view)||ne(t.root);if(!e||this.selectionRange.eq(e))return!1;let i=se(this.dom,e);return i&&!this.selectionChanged&&t.inputState.lastFocusTime>Date.now()-200&&t.inputState.lastTouchTime{let t=this.delayedAndroidKey;t&&(this.clearDelayedAndroidKey(),!this.flush()&&t.force&&ye(this.dom,t.key,t.keyCode))};this.flushingAndroidKey=this.view.win.requestAnimationFrame(t)}this.delayedAndroidKey&&"Enter"!=t||(this.delayedAndroidKey={key:t,keyCode:e,force:this.lastChange{this.delayedFlush=-1,this.flush()})))}forceFlush(){this.delayedFlush>=0&&(this.view.win.cancelAnimationFrame(this.delayedFlush),this.delayedFlush=-1),this.flush()}processRecords(){let t=this.queue;for(let e of this.observer.takeRecords())t.push(e);t.length&&(this.queue=[]);let e=-1,i=-1,n=!1;for(let r of t){let t=this.readMutation(r);t&&(t.typeOver&&(n=!0),-1==e?({from:e,to:i}=t):(e=Math.min(t.from,e),i=Math.max(t.to,i)))}return{from:e,to:i,typeOver:n}}readChange(){let{from:t,to:e,typeOver:i}=this.processRecords(),n=this.selectionChanged&&se(this.dom,this.selectionRange);return t<0&&!n?null:(t>-1&&(this.lastChange=Date.now()),this.view.inputState.lastFocusTime=0,this.selectionChanged=!1,new kr(this.view,t,e,i))}flush(t=!0){if(this.delayedFlush>=0||this.delayedAndroidKey)return!1;t&&this.readSelectionRange();let e=this.readChange();if(!e)return!1;let i=this.view.state,n=Sr(this.view,e);return this.view.state==i&&this.view.update([]),n}readMutation(t){let e=this.view.docView.nearest(t.target);if(!e||e.ignoreMutation(t))return null;if(e.markDirty("attributes"==t.type),"attributes"==t.type&&(e.dirty|=4),"childList"==t.type){let i=Dr(e,t.previousSibling||t.target.previousSibling,-1),n=Dr(e,t.nextSibling||t.target.nextSibling,1);return{from:i?e.posAfter(i):e.posAtStart,to:n?e.posBefore(n):e.posAtEnd,typeOver:!1}}return"characterData"==t.type?{from:e.posAtStart,to:e.posAtEnd,typeOver:t.target.nodeValue==t.oldValue}:null}setWindow(t){t!=this.win&&(this.removeWindowListeners(this.win),this.win=t,this.addWindowListeners(this.win))}addWindowListeners(t){t.addEventListener("resize",this.onResize),t.addEventListener("beforeprint",this.onPrint),t.addEventListener("scroll",this.onScroll),t.document.addEventListener("selectionchange",this.onSelectionChange)}removeWindowListeners(t){t.removeEventListener("scroll",this.onScroll),t.removeEventListener("resize",this.onResize),t.removeEventListener("beforeprint",this.onPrint),t.document.removeEventListener("selectionchange",this.onSelectionChange)}destroy(){var t,e,i;this.stop(),null===(t=this.intersection)||void 0===t||t.disconnect(),null===(e=this.gapIntersection)||void 0===e||e.disconnect(),null===(i=this.resize)||void 0===i||i.disconnect();for(let t of this.scrollTargets)t.removeEventListener("scroll",this.onScroll);this.removeWindowListeners(this.win),clearTimeout(this.parentCheck),clearTimeout(this.resizeTimeout),this.win.cancelAnimationFrame(this.delayedFlush),this.win.cancelAnimationFrame(this.flushingAndroidKey)}}function Dr(t,e,i){for(;e;){let n=Se.get(e);if(n&&n.parent==t)return n;let r=e.parentNode;e=r!=t.dom?r:i>0?e.nextSibling:e.previousSibling}return null}class Or{constructor(t={}){this.plugins=[],this.pluginMap=new Map,this.editorAttrs={},this.contentAttrs={},this.bidiCache=[],this.destroyed=!1,this.updateState=2,this.measureScheduled=-1,this.measureRequests=[],this.contentDOM=document.createElement("div"),this.scrollDOM=document.createElement("div"),this.scrollDOM.tabIndex=-1,this.scrollDOM.className="cm-scroller",this.scrollDOM.appendChild(this.contentDOM),this.announceDOM=document.createElement("div"),this.announceDOM.style.cssText="position: absolute; top: -10000px",this.announceDOM.setAttribute("aria-live","polite"),this.dom=document.createElement("div"),this.dom.appendChild(this.announceDOM),this.dom.appendChild(this.scrollDOM),this._dispatch=t.dispatch||(t=>this.update([t])),this.dispatch=this.dispatch.bind(this),this._root=t.root||function(t){for(;t;){if(t&&(9==t.nodeType||11==t.nodeType&&t.host))return t;t=t.assignedSlot||t.parentNode}return null}(t.parent)||document,this.viewState=new or(t.state||St.create(t)),this.plugins=this.state.facet(Ci).map((t=>new Oi(t)));for(let t of this.plugins)t.update(this);this.observer=new Cr(this),this.inputState=new yn(this),this.inputState.ensureHandlers(this,this.plugins),this.docView=new en(this),this.mountStyles(),this.updateAttrs(),this.updateState=0,this.requestMeasure(),t.parent&&t.parent.appendChild(this.dom)}get state(){return this.viewState.state}get viewport(){return this.viewState.viewport}get visibleRanges(){return this.viewState.visibleRanges}get inView(){return this.viewState.inView}get composing(){return this.inputState.composing>0}get compositionStarted(){return this.inputState.composing>=0}get root(){return this._root}get win(){return this.dom.ownerDocument.defaultView||window}dispatch(...t){this._dispatch(1==t.length&&t[0]instanceof ft?t[0]:this.state.update(...t))}update(t){if(0!=this.updateState)throw new Error("Calls to EditorView.update are not allowed while an update is in progress");let e,i=!1,n=!1,r=this.state;for(let e of t){if(e.startState!=r)throw new RangeError("Trying to update state with a transaction that doesn't start from the previous state.");r=e.state}if(this.destroyed)return void(this.viewState.state=r);let s=this.observer.delayedAndroidKey,o=null;if(s?(this.observer.clearDelayedAndroidKey(),o=this.observer.readChange(),(o&&!this.state.doc.eq(r.doc)||!this.state.selection.eq(r.selection))&&(o=null)):this.observer.clear(),r.facet(St.phrases)!=this.state.facet(St.phrases))return this.setState(r);e=Ii.create(this,r,t);let l=this.viewState.scrollTarget;try{this.updateState=2;for(let e of t){if(l&&(l=l.map(e.changes)),e.scrollIntoView){let{main:t}=e.state.selection;l=new xi(t.empty?t:R.cursor(t.head,t.head>t.anchor?-1:1))}for(let t of e.effects)t.is(ki)&&(l=t.value)}this.viewState.update(e,l),this.bidiCache=Br.update(this.bidiCache,e.changes),e.empty||(this.updatePlugins(e),this.inputState.update(e)),i=this.docView.update(e),this.state.facet(Ni)!=this.styleModules&&this.mountStyles(),n=this.updateAttrs(),this.showAnnouncements(t),this.docView.updateSelection(i,t.some((t=>t.isUserEvent("select.pointer"))))}finally{this.updateState=0}if(e.startState.facet(pr)!=e.state.facet(pr)&&(this.viewState.mustMeasureContent=!0),(i||n||l||this.viewState.mustEnforceCursorAssoc||this.viewState.mustMeasureContent)&&this.requestMeasure(),!e.empty)for(let t of this.state.facet(vi))t(e);o&&!Sr(this,o)&&s.force&&ye(this.contentDOM,s.key,s.keyCode)}setState(t){if(0!=this.updateState)throw new Error("Calls to EditorView.setState are not allowed while an update is in progress");if(this.destroyed)return void(this.viewState.state=t);this.updateState=2;let e=this.hasFocus;try{for(let t of this.plugins)t.destroy(this);this.viewState=new or(t),this.plugins=t.facet(Ci).map((t=>new Oi(t))),this.pluginMap.clear();for(let t of this.plugins)t.update(this);this.docView=new en(this),this.inputState.ensureHandlers(this,this.plugins),this.mountStyles(),this.updateAttrs(),this.bidiCache=[]}finally{this.updateState=0}e&&this.focus(),this.requestMeasure()}updatePlugins(t){let e=t.startState.facet(Ci),i=t.state.facet(Ci);if(e!=i){let n=[];for(let r of i){let i=e.indexOf(r);if(i<0)n.push(new Oi(r));else{let e=this.plugins[i];e.mustUpdate=t,n.push(e)}}for(let e of this.plugins)e.mustUpdate!=t&&e.destroy(this);this.plugins=n,this.pluginMap.clear(),this.inputState.ensureHandlers(this,this.plugins)}else for(let e of this.plugins)e.mustUpdate=t;for(let t=0;t-1&&cancelAnimationFrame(this.measureScheduled),this.measureScheduled=0,t&&this.observer.forceFlush();let e=null,{scrollHeight:i,scrollTop:n,clientHeight:r}=this.scrollDOM,s=n>i-r-4?i:n;try{for(let t=0;;t++){this.updateState=1;let i=this.viewport,n=this.viewState.lineBlockAtHeight(s),r=this.viewState.measure(this);if(!r&&!this.measureRequests.length&&null==this.viewState.scrollTarget)break;if(t>5){console.warn(this.measureRequests.length?"Measure loop restarted more than 5 times":"Viewport failed to stabilize");break}let o=[];4&r||([this.measureRequests,o]=[o,this.measureRequests]);let l=o.map((t=>{try{return t.read(this)}catch(t){return Si(this.state,t),Er}})),h=Ii.create(this,this.state,[]),a=!1,c=!1;h.flags|=r,e?e.flags|=r:e=h,this.updateState=2,h.empty||(this.updatePlugins(h),this.inputState.update(h),this.updateAttrs(),a=this.docView.update(h));for(let t=0;t1||t<-1)&&(this.scrollDOM.scrollTop+=t,c=!0)}if(a&&this.docView.updateSelection(!0),this.viewport.from==i.from&&this.viewport.to==i.to&&!c&&0==this.measureRequests.length)break}}finally{this.updateState=0,this.measureScheduled=-1}if(e&&!e.empty)for(let t of this.state.facet(vi))t(e)}get themeClasses(){return mr+" "+(this.state.facet(gr)?wr:vr)+" "+this.state.facet(pr)}updateAttrs(){let t=Rr(this,Ti,{class:"cm-editor"+(this.hasFocus?" cm-focused ":" ")+this.themeClasses}),e={spellcheck:"false",autocorrect:"off",autocapitalize:"off",translate:"no",contenteditable:this.state.facet(Ai)?"true":"false",class:"cm-content",style:`${He.tabSize}: ${this.state.tabSize}`,role:"textbox","aria-multiline":"true"};this.state.readOnly&&(e["aria-readonly"]="true"),Rr(this,Ei,e);let i=this.observer.ignore((()=>{let i=Ze(this.contentDOM,this.contentAttrs,e),n=Ze(this.dom,this.editorAttrs,t);return i||n}));return this.editorAttrs=t,this.contentAttrs=e,i}showAnnouncements(t){let e=!0;for(let i of t)for(let t of i.effects)if(t.is(Or.announce)){e&&(this.announceDOM.textContent=""),e=!1,this.announceDOM.appendChild(document.createElement("div")).textContent=t.value}}mountStyles(){this.styleModules=this.state.facet(Ni),$t.mount(this.root,this.styleModules.concat(xr).reverse())}readMeasured(){if(2==this.updateState)throw new Error("Reading the editor layout isn't allowed during an update");0==this.updateState&&this.measureScheduled>-1&&this.measure(!1)}requestMeasure(t){if(this.measureScheduled<0&&(this.measureScheduled=this.win.requestAnimationFrame((()=>this.measure()))),t){if(null!=t.key)for(let e=0;ee.spec==t))||null),e&&e.update(this).value}get documentTop(){return this.contentDOM.getBoundingClientRect().top+this.viewState.paddingTop}get documentPadding(){return{top:this.viewState.paddingTop,bottom:this.viewState.paddingBottom}}elementAtHeight(t){return this.readMeasured(),this.viewState.elementAtHeight(t)}lineBlockAtHeight(t){return this.readMeasured(),this.viewState.lineBlockAtHeight(t)}get viewportLineBlocks(){return this.viewState.viewportLines}lineBlockAt(t){return this.viewState.lineBlockAt(t)}get contentHeight(){return this.viewState.contentHeight}moveByChar(t,e,i){return wn(this,t,vn(this,t,e,i))}moveByGroup(t,e){return wn(this,t,vn(this,t,e,(e=>function(t,e,i){let n=t.state.charCategorizer(e),r=n(i);return t=>{let e=n(t);return r==yt.Space&&(r=e),r==e}}(this,t.head,e))))}moveToLineBoundary(t,e,i=!0){return function(t,e,i,n){let r=t.state.doc.lineAt(e.head),s=n&&t.lineWrapping?t.coordsAtPos(e.assoc<0&&e.head>r.from?e.head-1:e.head):null;if(s){let e=t.dom.getBoundingClientRect(),n=t.textDirectionAt(r.from),o=t.posAtCoords({x:i==(n==Vi.LTR)?e.right-1:e.left+1,y:(s.top+s.bottom)/2});if(null!=o)return R.cursor(o,i?-1:1)}let o=hi.find(t.docView,e.head),l=o?i?o.posAtEnd:o.posAtStart:i?r.to:r.from;return R.cursor(l,i?-1:1)}(this,t,e,i)}moveVertically(t,e,i){return wn(this,t,function(t,e,i,n){let r=e.head,s=i?1:-1;if(r==(i?t.state.doc.length:0))return R.cursor(r,e.assoc);let o,l=e.goalColumn,h=t.contentDOM.getBoundingClientRect(),a=t.coordsAtPos(r),c=t.documentTop;if(a)null==l&&(l=a.left-h.left),o=s<0?a.top:a.bottom;else{let e=t.viewState.lineBlockAt(r);null==l&&(l=Math.min(h.right-h.left,t.defaultCharacterWidth*(r-e.from))),o=(s<0?e.top:e.bottom)+c}let u=h.left+l,f=null!=n?n:t.defaultLineHeight>>1;for(let i=0;;i+=10){let n=o+(f+i)*s,a=gn(t,{x:u,y:n},!1,s);if(nh.bottom||(s<0?ar))return R.cursor(a,e.assoc,void 0,l)}}(this,t,e,i))}domAtPos(t){return this.docView.domAtPos(t)}posAtDOM(t,e=0){return this.docView.posFromDOM(t,e)}posAtCoords(t,e=!0){return this.readMeasured(),gn(this,t,e)}coordsAtPos(t,e=1){this.readMeasured();let i=this.docView.coordsAt(t,e);if(!i||i.left==i.right)return i;let n=this.state.doc.lineAt(t),r=this.bidiSpans(n);return fe(i,r[$i.find(r,t-n.from,-1,e)].dir==Vi.LTR==e>0)}get defaultCharacterWidth(){return this.viewState.heightOracle.charWidth}get defaultLineHeight(){return this.viewState.heightOracle.lineHeight}get textDirection(){return this.viewState.defaultTextDirection}textDirectionAt(t){return!this.state.facet(yi)||tthis.viewport.to?this.textDirection:(this.readMeasured(),this.docView.textDirectionAt(t))}get lineWrapping(){return this.viewState.heightOracle.lineWrapping}bidiSpans(t){if(t.length>Tr)return Ui(t.length);let e=this.textDirectionAt(t.from);for(let i of this.bidiCache)if(i.from==t.from&&i.dir==e)return i.order;let i=function(t,e){let i=t.length,n=e==Wi?1:2,r=e==Wi?2:1;if(!t||1==n&&!ji.test(t))return Ui(i);for(let e=0,r=n,o=n;e=0;t-=3)if(Ki[t+1]==-s){let e=Ki[t+2],i=2&e?n:4&e?1&e?r:n:0;i&&(Gi[l]=Gi[Ki[t]]=i),h=t;break}}else{if(189==Ki.length)break;Ki[h++]=l,Ki[h++]=e,Ki[h++]=a}else if(2==(o=Gi[l])||1==o){let t=o==n;a=t?0:1;for(let e=h-3;e>=0;e-=3){let i=Ki[e+2];if(2&i)break;if(t)Ki[e+2]|=2;else{if(4&i)break;Ki[e+2]|=4}}}for(let t=0;te;){let t=i,n=2!=Gi[--i];for(;i>e&&n==(2!=Gi[i-1]);)i--;o.push(new $i(i,t,n?2:1))}else o.push(new $i(e,t,0))}else for(let t=0;tDate.now()-3e4)&&this.root.activeElement==this.contentDOM}focus(){this.observer.ignore((()=>{ve(this.contentDOM),this.docView.updateSelection()}))}setRoot(t){this._root!=t&&(this._root=t,this.observer.setWindow((9==t.nodeType?t:t.ownerDocument).defaultView||window),this.mountStyles())}destroy(){for(let t of this.plugins)t.destroy(this);this.plugins=[],this.inputState.destroy(),this.dom.remove(),this.observer.destroy(),this.measureScheduled>-1&&cancelAnimationFrame(this.measureScheduled),this.destroyed=!0}static scrollIntoView(t,e={}){return ki.of(new xi("number"==typeof t?R.cursor(t):t,e.y,e.x,e.yMargin,e.xMargin))}static domEventHandlers(t){return Di.define((()=>({})),{eventHandlers:t})}static theme(t,e){let i=$t.newName(),n=[pr.of(i),Ni.of(br(`.${i}`,t))];return e&&e.dark&&n.push(gr.of(!0)),n}static baseTheme(t){return U.lowest(Ni.of(br("."+mr,t,yr)))}static findFromDOM(t){var e;let i=t.querySelector(".cm-content"),n=i&&Se.get(i)||Se.get(t);return(null===(e=null==n?void 0:n.rootView)||void 0===e?void 0:e.view)||null}}Or.styleModule=Ni,Or.inputHandler=wi,Or.perLineTextDirection=yi,Or.exceptionSink=mi,Or.updateListener=vi,Or.editable=Ai,Or.mouseSelectionStyle=gi,Or.dragMovesSelection=pi,Or.clickAddsSelectionRange=di,Or.decorations=Bi,Or.atomicRanges=Ri,Or.scrollMargins=Li,Or.darkTheme=gr,Or.contentAttributes=Ei,Or.editorAttributes=Ti,Or.lineWrapping=Or.contentAttributes.of({class:"cm-lineWrapping"}),Or.announce=ut.define();const Tr=4096,Er={};class Br{constructor(t,e,i,n){this.from=t,this.to=e,this.dir=i,this.order=n}static update(t,e){if(e.empty)return t;let i=[],n=t.length?t[t.length-1].dir:Vi.LTR;for(let r=Math.max(0,t.length-10);r=0;r--){let e=n[r],s="function"==typeof e?e(t):e;s&&Ye(s,i)}return i}const Lr=He.mac?"mac":He.windows?"win":He.linux?"linux":"key";function Nr(t,e,i){return e.altKey&&(t="Alt-"+t),e.ctrlKey&&(t="Ctrl-"+t),e.metaKey&&(t="Meta-"+t),!1!==i&&e.shiftKey&&(t="Shift-"+t),t}const Pr=P.define({enables:U.default(Or.domEventHandlers({keydown:(t,e)=>Hr(Vr(e.state),t,e,"editor")}))}),Ir=new WeakMap;function Vr(t){let e=t.facet(Pr),i=Ir.get(e);return i||Ir.set(e,i=function(t,e=Lr){let i=Object.create(null),n=Object.create(null),r=(t,e)=>{let i=n[t];if(null==i)n[t]=e;else if(i!=e)throw new Error("Key binding "+t+" is used both as a regular binding and as a multi-stroke prefix")},s=(t,n,s,o)=>{var l,h;let a=i[t]||(i[t]=Object.create(null)),c=n.split(/ (?!$)/).map((t=>function(t,e){const i=t.split(/-(?!$)/);let n,r,s,o,l=i[i.length-1];"Space"==l&&(l=" ");for(let t=0;t{let n=Wr={view:e,prefix:i,scope:t};return setTimeout((()=>{Wr==n&&(Wr=null)}),4e3),!0}]})}let u=c.join(" ");r(u,!1);let f=a[u]||(a[u]={preventDefault:!1,run:(null===(h=null===(l=a._any)||void 0===l?void 0:l.run)||void 0===h?void 0:h.slice())||[]});s&&f.run.push(s),o&&(f.preventDefault=!0)};for(let n of t){let t=n.scope?n.scope.split(" "):["editor"];if(n.any)for(let e of t){let t=i[e]||(i[e]=Object.create(null));t._any||(t._any={preventDefault:!1,run:[]});for(let e in t)t[e].run.push(n.any)}let r=n[e]||n.key;if(r)for(let e of t)s(e,r,n.run,n.preventDefault),n.shift&&s(e,"Shift-"+r,n.shift,n.preventDefault)}return i}(e.reduce(((t,e)=>t.concat(e)),[]))),i}let Wr=null;function Hr(t,e,i,n){let r=function(t){var e=!(te&&(t.ctrlKey||t.altKey||t.metaKey)||Zt&&t.shiftKey&&t.key&&1==t.key.length||"Unidentified"==t.key)&&t.key||(t.shiftKey?Xt:Jt)[t.keyCode]||t.key||"Unidentified";return"Esc"==e&&(e="Escape"),"Del"==e&&(e="Delete"),"Left"==e&&(e="ArrowLeft"),"Up"==e&&(e="ArrowUp"),"Right"==e&&(e="ArrowRight"),"Down"==e&&(e="ArrowDown"),e}(e),s=b(w(r,0))==r.length&&" "!=r,o="",l=!1;Wr&&Wr.view==i&&Wr.scope==n&&(o=Wr.prefix+" ",(l=kn.indexOf(e.keyCode)<0)&&(Wr=null));let h,a,c=new Set,u=t=>{if(t){for(let n of t.run)if(!c.has(n)&&(c.add(n),n(i,e)))return!0;t.preventDefault&&(l=!0)}return!1},f=t[n];if(f){if(u(f[o+Nr(r,e,!s)]))return!0;if(s&&(e.altKey||e.metaKey||e.ctrlKey)&&(h=Jt[e.keyCode])&&h!=r){if(u(f[o+Nr(h,e,!0)]))return!0;if(e.shiftKey&&(a=Xt[e.keyCode])!=r&&a!=h&&u(f[o+Nr(a,e,!1)]))return!0}else if(s&&e.shiftKey&&u(f[o+Nr(r,e,!0)]))return!0;if(u(f._any))return!0}return l}class Fr{constructor(t,e,i,n,r){this.className=t,this.left=e,this.top=i,this.width=n,this.height=r}draw(){let t=document.createElement("div");return t.className=this.className,this.adjust(t),t}update(t,e){return e.className==this.className&&(this.adjust(t),!0)}adjust(t){t.style.left=this.left+"px",t.style.top=this.top+"px",this.width>=0&&(t.style.width=this.width+"px"),t.style.height=this.height+"px"}eq(t){return this.left==t.left&&this.top==t.top&&this.width==t.width&&this.height==t.height&&this.className==t.className}}class zr{constructor(t,e){this.view=t,this.layer=e,this.drawn=[],this.measureReq={read:this.measure.bind(this),write:this.draw.bind(this)},this.dom=t.scrollDOM.appendChild(document.createElement("div")),this.dom.classList.add("cm-layer"),e.above&&this.dom.classList.add("cm-layer-above"),e.class&&this.dom.classList.add(e.class),this.dom.setAttribute("aria-hidden","true"),this.setOrder(t.state),t.requestMeasure(this.measureReq),e.mount&&e.mount(this.dom,t)}update(t){t.startState.facet(qr)!=t.state.facet(qr)&&this.setOrder(t.state),(this.layer.update(t,this.dom)||t.geometryChanged)&&t.view.requestMeasure(this.measureReq)}setOrder(t){let e=0,i=t.facet(qr);for(;e{return i=t,n=this.drawn[e],!(i.constructor==n.constructor&&i.eq(n));var i,n}))){let e=this.dom.firstChild,i=0;for(let n of t)n.update&&e&&n.constructor&&this.drawn[i].constructor&&n.update(e,this.drawn[i])?(e=e.nextSibling,i++):this.dom.insertBefore(n.draw(),e);for(;e;){let t=e.nextSibling;e.remove(),e=t}this.drawn=t}}destroy(){this.dom.remove()}}const qr=P.define();function _r(t){return[Di.define((e=>new zr(e,t))),qr.of(t)]}const Kr=!He.ios,jr=P.define({combine:t=>At(t,{cursorBlinkRate:1200,drawRangeCursor:!0},{cursorBlinkRate:(t,e)=>Math.min(t,e),drawRangeCursor:(t,e)=>t||e})});function $r(t={}){return[jr.of(t),Ur,Xr,Qr,bi.of(!0)]}function Gr(t){return t.startState.facet(jr)!=t.startState.facet(jr)}const Ur=_r({above:!0,markers(t){let{state:e}=t,i=e.facet(jr),n=[];for(let r of e.selection.ranges){let s=r==e.selection.main;if(r.empty?!s||Kr:i.drawRangeCursor){let e=is(t,r,s);e&&n.push(e)}}return n},update(t,e){t.transactions.some((t=>t.scrollIntoView))&&(e.style.animationName="cm-blink"==e.style.animationName?"cm-blink2":"cm-blink");let i=Gr(t);return i&&Jr(t.state,e),t.docChanged||t.selectionSet||i},mount(t,e){Jr(e.state,t)},class:"cm-cursorLayer"});function Jr(t,e){e.style.animationDuration=t.facet(jr).cursorBlinkRate+"ms"}const Xr=_r({above:!1,markers:t=>t.state.selection.ranges.map((e=>e.empty?[]:function(t,e){if(e.to<=t.viewport.from||e.from>=t.viewport.to)return[];let i=Math.max(e.from,t.viewport.from),n=Math.min(e.to,t.viewport.to),r=t.textDirection==Vi.LTR,s=t.contentDOM,o=s.getBoundingClientRect(),l=Zr(t),h=window.getComputedStyle(s.firstChild),a=o.left+parseInt(h.paddingLeft)+Math.min(0,parseInt(h.textIndent)),c=o.right-parseInt(h.paddingRight),u=es(t,i),f=es(t,n),d=u.type==ei.Text?u:null,p=f.type==ei.Text?f:null;t.lineWrapping&&(d&&(d=ts(t,i,d)),p&&(p=ts(t,n,p)));if(d&&p&&d.from==p.from)return m(v(e.from,e.to,d));{let i=d?v(e.from,null,d):w(u,!1),n=p?v(null,e.to,p):w(f,!0),r=[];return(d||u).to<(p||f).from-1?r.push(g(a,i.bottom,c,n.top)):i.bottomu&&n.from=s)break;l>r&&h(Math.max(t,r),null==e&&t<=u,Math.min(l,s),null==i&&l>=f,o.dir)}if(r=n.to+1,r>=s)break}return 0==l.length&&h(u,null==e,f,null==i,t.textDirection),{top:s,bottom:o,horizontal:l}}function w(t,e){let i=o.top+(e?t.top:t.bottom);return{top:i,bottom:i,horizontal:[]}}}(t,e))).reduce(((t,e)=>t.concat(e))),update:(t,e)=>t.docChanged||t.selectionSet||t.viewportChanged||Gr(t),class:"cm-selectionLayer"}),Yr={".cm-line":{"& ::selection":{backgroundColor:"transparent !important"},"&::selection":{backgroundColor:"transparent !important"}}};Kr&&(Yr[".cm-line"].caretColor="transparent !important");const Qr=U.highest(Or.theme(Yr));function Zr(t){let e=t.scrollDOM.getBoundingClientRect();return{left:(t.textDirection==Vi.LTR?e.left:e.right-t.scrollDOM.clientWidth)-t.scrollDOM.scrollLeft,top:e.top-t.scrollDOM.scrollTop}}function ts(t,e,i){let n=R.cursor(e);return{from:Math.max(i.from,t.moveToLineBoundary(n,!1,!0).from),to:Math.min(i.to,t.moveToLineBoundary(n,!0,!0).from),type:ei.Text}}function es(t,e){let i=t.lineBlockAt(e);if(Array.isArray(i.type))for(let t of i.type)if(t.to>e||t.to==e&&(t.to==i.to||t.type==ei.Text))return t;return i}function is(t,e,i){let n=t.coordsAtPos(e.head,e.assoc||1);if(!n)return null;let r=Zr(t);return new Fr(i?"cm-cursor cm-cursor-primary":"cm-cursor cm-cursor-secondary",n.left-r.left,n.top-r.top,-1,n.bottom-n.top)}function ns(t,e,i,n,r){e.lastIndex=0;for(let s,o=t.iterRange(i,n),l=i;!o.next().done;l+=o.value.length)if(!o.lineBreak)for(;s=e.exec(o.value);)r(l+s.index,s)}class rs{constructor(t){const{regexp:e,decoration:i,decorate:n,boundary:r,maxLength:s=1e3}=t;if(!e.global)throw new RangeError("The regular expression given to MatchDecorator should have its 'g' flag set");if(this.regexp=e,n)this.addMatch=(t,e,i,r)=>n(r,i,i+t[0].length,t,e);else if("function"==typeof i)this.addMatch=(t,e,n,r)=>{let s=i(t,e,n);s&&r(n,n+t[0].length,s)};else{if(!i)throw new RangeError("Either 'decorate' or 'decoration' should be provided to MatchDecorator");this.addMatch=(t,e,n,r)=>r(n,n+t[0].length,i)}this.boundary=r,this.maxLength=s}createDeco(t){let e=new Et,i=e.add.bind(e);for(let{from:e,to:n}of function(t,e){let i=t.visibleRanges;if(1==i.length&&i[0].from==t.viewport.from&&i[0].to==t.viewport.to)return i;let n=[];for(let{from:r,to:s}of i)r=Math.max(t.state.doc.lineAt(r).from,r-e),s=Math.min(t.state.doc.lineAt(s).to,s+e),n.length&&n[n.length-1].to>=r?n[n.length-1].to=s:n.push({from:r,to:s});return n}(t,this.maxLength))ns(t.state.doc,this.regexp,e,n,((e,n)=>this.addMatch(n,t,e,i)));return e.finish()}updateDeco(t,e){let i=1e9,n=-1;return t.docChanged&&t.changes.iterChanges(((e,r,s,o)=>{o>t.view.viewport.from&&s1e3?this.createDeco(t.view):n>-1?this.updateRange(t.view,e.map(t.changes),i,n):e}updateRange(t,e,i,n){for(let r of t.visibleRanges){let s=Math.max(r.from,i),o=Math.min(r.to,n);if(o>s){let i=t.state.doc.lineAt(s),n=i.toi.from;s--)if(this.boundary.test(i.text[s-1-i.from])){l=s;break}for(;oc.push(i.range(t,e));if(i==n)for(this.regexp.lastIndex=l-i.from;(a=this.regexp.exec(i.text))&&a.indexthis.addMatch(i,t,e,u)));e=e.update({filterFrom:l,filterTo:h,filter:(t,e)=>th,add:c})}}return e}}const ss=null!=/x/.unicode?"gu":"g",os=new RegExp("[\0-\b\n--Ÿ­؜​‎‏\u2028\u2029‭‮⁦⁧⁩\ufeff-]",ss),ls={0:"null",7:"bell",8:"backspace",10:"newline",11:"vertical tab",13:"carriage return",27:"escape",8203:"zero width space",8204:"zero width non-joiner",8205:"zero width joiner",8206:"left-to-right mark",8207:"right-to-left mark",8232:"line separator",8237:"left-to-right override",8238:"right-to-left override",8294:"left-to-right isolate",8295:"right-to-left isolate",8297:"pop directional isolate",8233:"paragraph separator",65279:"zero width no-break space",65532:"object replacement"};let hs=null;const as=P.define({combine(t){let e=At(t,{render:null,specialChars:os,addSpecialChars:null});return(e.replaceTabs=!function(){var t;if(null==hs&&"undefined"!=typeof document&&document.body){let e=document.body.style;hs=null!=(null!==(t=e.tabSize)&&void 0!==t?t:e.MozTabSize)}return hs||!1}())&&(e.specialChars=new RegExp("\t|"+e.specialChars.source,ss)),e.addSpecialChars&&(e.specialChars=new RegExp(e.specialChars.source+"|"+e.addSpecialChars.source,ss)),e}});function cs(t={}){return[as.of(t),us||(us=Di.fromClass(class{constructor(t){this.view=t,this.decorations=ii.none,this.decorationCache=Object.create(null),this.decorator=this.makeDecorator(t.state.facet(as)),this.decorations=this.decorator.createDeco(t)}makeDecorator(t){return new rs({regexp:t.specialChars,decoration:(e,i,n)=>{let{doc:r}=i.state,s=w(e[0],0);if(9==s){let t=r.lineAt(n),e=i.state.tabSize,s=zt(t.text,e,n-t.from);return ii.replace({widget:new ds((e-s%e)*this.view.defaultCharacterWidth)})}return this.decorationCache[s]||(this.decorationCache[s]=ii.replace({widget:new fs(t,s)}))},boundary:t.replaceTabs?void 0:/[^]/})}update(t){let e=t.state.facet(as);t.startState.facet(as)!=e?(this.decorator=this.makeDecorator(e),this.decorations=this.decorator.createDeco(t.view)):this.decorations=this.decorator.updateDeco(t,this.decorations)}},{decorations:t=>t.decorations}))]}let us=null;class fs extends ti{constructor(t,e){super(),this.options=t,this.code=e}eq(t){return t.code==this.code}toDOM(t){let e=function(t){return t>=32?"•":10==t?"␤":String.fromCharCode(9216+t)}(this.code),i=t.state.phrase("Control character")+" "+(ls[this.code]||"0x"+this.code.toString(16)),n=this.options.render&&this.options.render(this.code,i,e);if(n)return n;let r=document.createElement("span");return r.textContent=e,r.title=i,r.setAttribute("aria-label",i),r.className="cm-specialChar",r}ignoreEvent(){return!1}}class ds extends ti{constructor(t){super(),this.width=t}eq(t){return t.width==this.width}toDOM(){let t=document.createElement("span");return t.textContent="\t",t.className="cm-tab",t.style.width=this.width+"px",t}ignoreEvent(){return!1}}const ps=ii.line({class:"cm-activeLine"}),gs=Di.fromClass(class{constructor(t){this.decorations=this.getDeco(t)}update(t){(t.docChanged||t.selectionSet)&&(this.decorations=this.getDeco(t.view))}getDeco(t){let e=-1,i=[];for(let n of t.state.selection.ranges){let r=t.lineBlockAt(n.head);r.from>e&&(i.push(ps.range(r.from)),e=r.from)}return ii.set(i)}},{decorations:t=>t.decorations});class ms extends ti{constructor(t){super(),this.content=t}toDOM(){let t=document.createElement("span");return t.className="cm-placeholder",t.style.pointerEvents="none",t.appendChild("string"==typeof this.content?document.createTextNode(this.content):this.content),"string"==typeof this.content?t.setAttribute("aria-label","placeholder "+this.content):t.setAttribute("aria-hidden","true"),t}ignoreEvent(){return!1}}const vs=2e3;function ws(t,e){let i=t.posAtCoords({x:e.clientX,y:e.clientY},!1),n=t.state.doc.lineAt(i),r=i-n.from,s=r>vs?-1:r==n.length?function(t,e){let i=t.coordsAtPos(t.viewport.from);return i?Math.round(Math.abs((i.left-e)/t.defaultCharacterWidth)):-1}(t,e.clientX):zt(n.text,t.state.tabSize,i-n.from);return{line:n.number,col:s,off:r}}function ys(t,e){let i=ws(t,e),n=t.state.selection;return i?{update(t){if(t.docChanged){let e=t.changes.mapPos(t.startState.doc.line(i.line).from),r=t.state.doc.lineAt(e);i={line:r.number,col:i.col,off:Math.min(i.off,r.length)},n=n.map(t.changes)}},get(e,r,s){let o=ws(t,e);if(!o)return n;let l=function(t,e,i){let n=Math.min(e.line,i.line),r=Math.max(e.line,i.line),s=[];if(e.off>vs||i.off>vs||e.col<0||i.col<0){let o=Math.min(e.off,i.off),l=Math.max(e.off,i.off);for(let e=n;e<=r;e++){let i=t.doc.line(e);i.length<=l&&s.push(R.range(i.from+o,i.to+l))}}else{let o=Math.min(e.col,i.col),l=Math.max(e.col,i.col);for(let e=n;e<=r;e++){let i=t.doc.line(e),n=qt(i.text,o,t.tabSize,!0);if(n<0)s.push(R.cursor(i.to));else{let e=qt(i.text,l,t.tabSize);s.push(R.range(i.from+n,i.from+e))}}}return s}(t.state,i,o);return l.length?s?R.create(l.concat(n.ranges)):R.create(l):n}}:null}function bs(t){let e=(null==t?void 0:t.eventFilter)||(t=>t.altKey&&0==t.button);return Or.mouseSelectionStyle.of(((t,i)=>e(i)?ys(t,i):null))}const xs={Alt:[18,t=>t.altKey],Control:[17,t=>t.ctrlKey],Shift:[16,t=>t.shiftKey],Meta:[91,t=>t.metaKey]},ks={style:"cursor: crosshair"};function Ss(t={}){let[e,i]=xs[t.key||"Alt"],n=Di.fromClass(class{constructor(t){this.view=t,this.isDown=!1}set(t){this.isDown!=t&&(this.isDown=t,this.view.update([]))}},{eventHandlers:{keydown(t){this.set(t.keyCode==e||i(t))},keyup(t){t.keyCode!=e&&i(t)||this.set(!1)},mousemove(t){this.set(i(t))}}});return[n,Or.contentAttributes.of((t=>{var e;return(null===(e=t.plugin(n))||void 0===e?void 0:e.isDown)?ks:null}))]}const As=P.define({combine(t){let e,i;for(let n of t)e=e||n.topContainer,i=i||n.bottomContainer;return{topContainer:e,bottomContainer:i}}});function Ms(t,e){let i=t.plugin(Cs),n=i?i.specs.indexOf(e):-1;return n>-1?i.panels[n]:null}const Cs=Di.fromClass(class{constructor(t){this.input=t.state.facet(Ts),this.specs=this.input.filter((t=>t)),this.panels=this.specs.map((e=>e(t)));let e=t.state.facet(As);this.top=new Ds(t,!0,e.topContainer),this.bottom=new Ds(t,!1,e.bottomContainer),this.top.sync(this.panels.filter((t=>t.top))),this.bottom.sync(this.panels.filter((t=>!t.top)));for(let t of this.panels)t.dom.classList.add("cm-panel"),t.mount&&t.mount()}update(t){let e=t.state.facet(As);this.top.container!=e.topContainer&&(this.top.sync([]),this.top=new Ds(t.view,!0,e.topContainer)),this.bottom.container!=e.bottomContainer&&(this.bottom.sync([]),this.bottom=new Ds(t.view,!1,e.bottomContainer)),this.top.syncClasses(),this.bottom.syncClasses();let i=t.state.facet(Ts);if(i!=this.input){let e=i.filter((t=>t)),n=[],r=[],s=[],o=[];for(let i of e){let e,l=this.specs.indexOf(i);l<0?(e=i(t.view),o.push(e)):(e=this.panels[l],e.update&&e.update(t)),n.push(e),(e.top?r:s).push(e)}this.specs=e,this.panels=n,this.top.sync(r),this.bottom.sync(s);for(let t of o)t.dom.classList.add("cm-panel"),t.mount&&t.mount()}else for(let e of this.panels)e.update&&e.update(t)}destroy(){this.top.sync([]),this.bottom.sync([])}},{provide:t=>Or.scrollMargins.of((e=>{let i=e.plugin(t);return i&&{top:i.top.scrollMargin(),bottom:i.bottom.scrollMargin()}}))});class Ds{constructor(t,e,i){this.view=t,this.top=e,this.container=i,this.dom=void 0,this.classes="",this.panels=[],this.syncClasses()}sync(t){for(let e of this.panels)e.destroy&&t.indexOf(e)<0&&e.destroy();this.panels=t,this.syncDOM()}syncDOM(){if(0==this.panels.length)return void(this.dom&&(this.dom.remove(),this.dom=void 0));if(!this.dom){this.dom=document.createElement("div"),this.dom.className=this.top?"cm-panels cm-panels-top":"cm-panels cm-panels-bottom",this.dom.style[this.top?"top":"bottom"]="0";let t=this.container||this.view.dom;t.insertBefore(this.dom,this.top?t.firstChild:null)}let t=this.dom.firstChild;for(let e of this.panels)if(e.dom.parentNode==this.dom){for(;t!=e.dom;)t=Os(t);t=t.nextSibling}else this.dom.insertBefore(e.dom,t);for(;t;)t=Os(t)}scrollMargin(){return!this.dom||this.container?0:Math.max(0,this.top?this.dom.getBoundingClientRect().bottom-Math.max(0,this.view.scrollDOM.getBoundingClientRect().top):Math.min(innerHeight,this.view.scrollDOM.getBoundingClientRect().bottom)-this.dom.getBoundingClientRect().top)}syncClasses(){if(this.container&&this.classes!=this.view.themeClasses){for(let t of this.classes.split(" "))t&&this.container.classList.remove(t);for(let t of(this.classes=this.view.themeClasses).split(" "))t&&this.container.classList.add(t)}}}function Os(t){let e=t.nextSibling;return t.remove(),e}const Ts=P.define({enables:Cs});class Es extends Mt{compare(t){return this==t||this.constructor==t.constructor&&this.eq(t)}eq(t){return!1}destroy(t){}}Es.prototype.elementClass="",Es.prototype.toDOM=void 0,Es.prototype.mapMode=k.TrackBefore,Es.prototype.startSide=Es.prototype.endSide=-1,Es.prototype.point=!0;const Bs=P.define(),Rs=P.define(),Ls=P.define({combine:t=>t.some((t=>t))});function Ns(t){let e=[Ps];return t&&!1===t.fixed&&e.push(Ls.of(!0)),e}const Ps=Di.fromClass(class{constructor(t){this.view=t,this.prevViewport=t.viewport,this.dom=document.createElement("div"),this.dom.className="cm-gutters",this.dom.setAttribute("aria-hidden","true"),this.dom.style.minHeight=this.view.contentHeight+"px",this.gutters=t.state.facet(Rs).map((e=>new Hs(t,e)));for(let t of this.gutters)this.dom.appendChild(t.dom);this.fixed=!t.state.facet(Ls),this.fixed&&(this.dom.style.position="sticky"),this.syncGutters(!1),t.scrollDOM.insertBefore(this.dom,t.contentDOM)}update(t){if(this.updateGutters(t)){let e=this.prevViewport,i=t.view.viewport,n=Math.min(e.to,i.to)-Math.max(e.from,i.from);this.syncGutters(n<.8*(i.to-i.from))}t.geometryChanged&&(this.dom.style.minHeight=this.view.contentHeight+"px"),this.view.state.facet(Ls)!=!this.fixed&&(this.fixed=!this.fixed,this.dom.style.position=this.fixed?"sticky":""),this.prevViewport=t.view.viewport}syncGutters(t){let e=this.dom.nextSibling;t&&this.dom.remove();let i=Tt.iter(this.view.state.facet(Bs),this.view.viewport.from),n=[],r=this.gutters.map((t=>new Ws(t,this.view.viewport,-this.view.documentPadding.top)));for(let t of this.view.viewportLineBlocks){let e;if(Array.isArray(t.type)){for(let i of t.type)if(i.type==ei.Text){e=i;break}}else e=t.type==ei.Text?t:void 0;if(e){n.length&&(n=[]),Vs(i,n,t.from);for(let t of r)t.line(this.view,e,n)}}for(let t of r)t.finish();t&&this.view.scrollDOM.insertBefore(this.dom,e)}updateGutters(t){let e=t.startState.facet(Rs),i=t.state.facet(Rs),n=t.docChanged||t.heightChanged||t.viewportChanged||!Tt.eq(t.startState.facet(Bs),t.state.facet(Bs),t.view.viewport.from,t.view.viewport.to);if(e==i)for(let e of this.gutters)e.update(t)&&(n=!0);else{n=!0;let r=[];for(let n of i){let i=e.indexOf(n);i<0?r.push(new Hs(this.view,n)):(this.gutters[i].update(t),r.push(this.gutters[i]))}for(let t of this.gutters)t.dom.remove(),r.indexOf(t)<0&&t.destroy();for(let t of r)this.dom.appendChild(t.dom);this.gutters=r}return n}destroy(){for(let t of this.gutters)t.destroy();this.dom.remove()}},{provide:t=>Or.scrollMargins.of((e=>{let i=e.plugin(t);return i&&0!=i.gutters.length&&i.fixed?e.textDirection==Vi.LTR?{left:i.dom.offsetWidth}:{right:i.dom.offsetWidth}:null}))});function Is(t){return Array.isArray(t)?t:[t]}function Vs(t,e,i){for(;t.value&&t.from<=i;)t.from==i&&e.push(t.value),t.next()}class Ws{constructor(t,e,i){this.gutter=t,this.height=i,this.localMarkers=[],this.i=0,this.cursor=Tt.iter(t.markers,e.from)}line(t,e,i){this.localMarkers.length&&(this.localMarkers=[]),Vs(this.cursor,this.localMarkers,e.from);let n=i.length?this.localMarkers.concat(i):this.localMarkers,r=this.gutter.config.lineMarker(t,e,n);r&&n.unshift(r);let s=this.gutter;if(0==n.length&&!s.config.renderEmptyElements)return;let o=e.top-this.height;if(this.i==s.elements.length){let i=new Fs(t,e.height,o,n);s.elements.push(i),s.dom.appendChild(i.dom)}else s.elements[this.i].update(t,e.height,o,n);this.height=e.bottom,this.i++}finish(){let t=this.gutter;for(;t.elements.length>this.i;){let e=t.elements.pop();t.dom.removeChild(e.dom),e.destroy()}}}class Hs{constructor(t,e){this.view=t,this.config=e,this.elements=[],this.spacer=null,this.dom=document.createElement("div"),this.dom.className="cm-gutter"+(this.config.class?" "+this.config.class:"");for(let i in e.domEventHandlers)this.dom.addEventListener(i,(n=>{let r=t.lineBlockAtHeight(n.clientY-t.documentTop);e.domEventHandlers[i](t,r,n)&&n.preventDefault()}));this.markers=Is(e.markers(t)),e.initialSpacer&&(this.spacer=new Fs(t,0,0,[e.initialSpacer(t)]),this.dom.appendChild(this.spacer.dom),this.spacer.dom.style.cssText+="visibility: hidden; pointer-events: none")}update(t){let e=this.markers;if(this.markers=Is(this.config.markers(t.view)),this.spacer&&this.config.updateSpacer){let e=this.config.updateSpacer(this.spacer.markers[0],t);e!=this.spacer.markers[0]&&this.spacer.update(t.view,0,0,[e])}let i=t.view.viewport;return!Tt.eq(this.markers,e,i.from,i.to)||!!this.config.lineMarkerChange&&this.config.lineMarkerChange(t)}destroy(){for(let t of this.elements)t.destroy()}}class Fs{constructor(t,e,i,n){this.height=-1,this.above=0,this.markers=[],this.dom=document.createElement("div"),this.dom.className="cm-gutterElement",this.update(t,e,i,n)}update(t,e,i,n){this.height!=e&&(this.dom.style.height=(this.height=e)+"px"),this.above!=i&&(this.dom.style.marginTop=(this.above=i)?i+"px":""),function(t,e){if(t.length!=e.length)return!1;for(let i=0;iAt(t,{formatNumber:String,domEventHandlers:{}},{domEventHandlers(t,e){let i=Object.assign({},t);for(let t in e){let n=i[t],r=e[t];i[t]=n?(t,e,i)=>n(t,e,i)||r(t,e,i):r}return i}})});class _s extends Es{constructor(t){super(),this.number=t}eq(t){return this.number==t.number}toDOM(){return document.createTextNode(this.number)}}function Ks(t,e){return t.state.facet(qs).formatNumber(e,t.state)}const js=Rs.compute([qs],(t=>({class:"cm-lineNumbers",renderEmptyElements:!1,markers:t=>t.state.facet(zs),lineMarker:(t,e,i)=>i.some((t=>t.toDOM))?null:new _s(Ks(t,t.state.doc.lineAt(e.from).number)),lineMarkerChange:t=>t.startState.facet(qs)!=t.state.facet(qs),initialSpacer:t=>new _s(Ks(t,Gs(t.state.doc.lines))),updateSpacer(t,e){let i=Ks(e.view,Gs(e.view.state.doc.lines));return i==t.number?t:new _s(i)},domEventHandlers:t.facet(qs).domEventHandlers})));function $s(t={}){return[qs.of(t),Ns(),js]}function Gs(t){let e=9;for(;e{let e=[],i=-1;for(let n of t.selection.ranges){let r=t.doc.lineAt(n.head).from;r>i&&(i=r,e.push(Us.range(r)))}return Tt.of(e)}));const Xs=1024;let Ys=0;class Qs{constructor(t,e){this.from=t,this.to=e}}class Zs{constructor(t={}){this.id=Ys++,this.perNode=!!t.perNode,this.deserialize=t.deserialize||(()=>{throw new Error("This node type doesn't define a deserialize function")})}add(t){if(this.perNode)throw new RangeError("Can't add per-node props to node types");return"function"!=typeof t&&(t=eo.match(t)),e=>{let i=t(e);return void 0===i?null:[this,i]}}}Zs.closedBy=new Zs({deserialize:t=>t.split(" ")}),Zs.openedBy=new Zs({deserialize:t=>t.split(" ")}),Zs.group=new Zs({deserialize:t=>t.split(" ")}),Zs.contextHash=new Zs({perNode:!0}),Zs.lookAhead=new Zs({perNode:!0}),Zs.mounted=new Zs({perNode:!0});const to=Object.create(null);class eo{constructor(t,e,i,n=0){this.name=t,this.props=e,this.id=i,this.flags=n}static define(t){let e=t.props&&t.props.length?Object.create(null):to,i=(t.top?1:0)|(t.skipped?2:0)|(t.error?4:0)|(null==t.name?8:0),n=new eo(t.name||"",e,t.id,i);if(t.props)for(let i of t.props)if(Array.isArray(i)||(i=i(n)),i){if(i[0].perNode)throw new RangeError("Can't store a per-node prop on a node type");e[i[0].id]=i[1]}return n}prop(t){return this.props[t.id]}get isTop(){return(1&this.flags)>0}get isSkipped(){return(2&this.flags)>0}get isError(){return(4&this.flags)>0}get isAnonymous(){return(8&this.flags)>0}is(t){if("string"==typeof t){if(this.name==t)return!0;let e=this.prop(Zs.group);return!!e&&e.indexOf(t)>-1}return this.id==t}static match(t){let e=Object.create(null);for(let i in t)for(let n of i.split(" "))e[n]=t[i];return t=>{for(let i=t.prop(Zs.group),n=-1;n<(i?i.length:0);n++){let r=e[n<0?t.name:i[n]];if(r)return r}}}}eo.none=new eo("",Object.create(null),0,8);const io=new WeakMap,no=new WeakMap;var ro;!function(t){t[t.ExcludeBuffers=1]="ExcludeBuffers",t[t.IncludeAnonymous=2]="IncludeAnonymous",t[t.IgnoreMounts=4]="IgnoreMounts",t[t.IgnoreOverlays=8]="IgnoreOverlays"}(ro||(ro={}));class so{constructor(t,e,i,n,r){if(this.type=t,this.children=e,this.positions=i,this.length=n,this.props=null,r&&r.length){this.props=Object.create(null);for(let[t,e]of r)this.props["number"==typeof t?t:t.id]=e}}toString(){let t=this.prop(Zs.mounted);if(t&&!t.overlay)return t.tree.toString();let e="";for(let t of this.children){let i=t.toString();i&&(e&&(e+=","),e+=i)}return this.type.name?(/\W/.test(this.type.name)&&!this.type.isError?JSON.stringify(this.type.name):this.type.name)+(e.length?"("+e+")":""):e}cursor(t=0){return new vo(this.topNode,t)}cursorAt(t,e=0,i=0){let n=io.get(this)||this.topNode,r=new vo(n);return r.moveTo(t,e),io.set(this,r._tree),r}get topNode(){return new uo(this,0,0,null)}resolve(t,e=0){let i=co(io.get(this)||this.topNode,t,e,!1);return io.set(this,i),i}resolveInner(t,e=0){let i=co(no.get(this)||this.topNode,t,e,!0);return no.set(this,i),i}iterate(t){let{enter:e,leave:i,from:n=0,to:r=this.length}=t;for(let s=this.cursor((t.mode||0)|ro.IncludeAnonymous);;){let t=!1;if(s.from<=r&&s.to>=n&&(s.type.isAnonymous||!1!==e(s))){if(s.firstChild())continue;t=!0}for(;t&&i&&!s.type.isAnonymous&&i(s),!s.nextSibling();){if(!s.parent())return;t=!0}}}prop(t){return t.perNode?this.props?this.props[t.id]:void 0:this.type.prop(t)}get propValues(){let t=[];if(this.props)for(let e in this.props)t.push([+e,this.props[e]]);return t}balance(t={}){return this.children.length<=8?this:xo(eo.none,this.children,this.positions,0,this.children.length,0,this.length,((t,e,i)=>new so(this.type,t,e,i,this.propValues)),t.makeTree||((t,e,i)=>new so(eo.none,t,e,i)))}static build(t){return function(t){var e;let{buffer:i,nodeSet:n,maxBufferLength:r=Xs,reused:s=[],minRepeatType:o=n.types.length}=t,l=Array.isArray(i)?new oo(i,i.length):i,h=n.types,a=0,c=0;function u(t,e,i,v,w){let{id:y,start:b,end:x,size:k}=l,S=c;for(;k<0;){if(l.next(),-1==k){let e=s[y];return i.push(e),void v.push(b-t)}if(-3==k)return void(a=y);if(-4==k)return void(c=y);throw new RangeError(`Unrecognized record size: ${k}`)}let A,M,C=h[y],D=b-t;if(x-b<=r&&(M=g(l.pos-e,w))){let e=new Uint16Array(M.size-M.skip),i=l.pos-M.size,r=e.length;for(;l.pos>i;)r=m(M.start,e,r);A=new lo(e,x-M.start,n),D=M.start-t}else{let t=l.pos-k;l.next();let e=[],i=[],n=y>=o?y:-1,s=0,h=x;for(;l.pos>t;)n>=0&&l.id==n&&l.size>=0?(l.end<=h-r&&(d(e,i,b,s,l.end,h,n,S),s=e.length,h=l.end),l.next()):u(b,t,e,i,n);if(n>=0&&s>0&&s-1&&s>0){let t=f(C);A=xo(C,e,i,0,e.length,0,x-b,t,t)}else A=p(C,e,i,x-b,S-x)}i.push(A),v.push(D)}function f(t){return(e,i,n)=>{let r,s,o=0,l=e.length-1;if(l>=0&&(r=e[l])instanceof so){if(!l&&r.type==t&&r.length==n)return r;(s=r.prop(Zs.lookAhead))&&(o=i[l]+r.length+s)}return p(t,e,i,n,o)}}function d(t,e,i,r,s,o,l,h){let a=[],c=[];for(;t.length>r;)a.push(t.pop()),c.push(e.pop()+i-s);t.push(p(n.types[l],a,c,o-s,h-o)),e.push(s-i)}function p(t,e,i,n,r=0,s){if(a){let t=[Zs.contextHash,a];s=s?[t].concat(s):[t]}if(r>25){let t=[Zs.lookAhead,r];s=s?[t].concat(s):[t]}return new so(t,e,i,n,s)}function g(t,e){let i=l.fork(),n=0,s=0,h=0,a=i.end-r,c={size:0,start:0,skip:0};t:for(let r=i.pos-t;i.pos>r;){let t=i.size;if(i.id==e&&t>=0){c.size=n,c.start=s,c.skip=h,h+=4,n+=4,i.next();continue}let l=i.pos-t;if(t<0||l=o?4:0,f=i.start;for(i.next();i.pos>l;){if(i.size<0){if(-3!=i.size)break t;u+=4}else i.id>=o&&(u+=4);i.next()}s=f,n+=t,h+=u}return(e<0||n==t)&&(c.size=n,c.start=s,c.skip=h),c.size>4?c:void 0}function m(t,e,i){let{id:n,start:r,end:s,size:h}=l;if(l.next(),h>=0&&n4){let n=l.pos-(h-4);for(;l.pos>n;)i=m(t,e,i)}e[--i]=o,e[--i]=s-t,e[--i]=r-t,e[--i]=n}else-3==h?a=n:-4==h&&(c=n);return i}let v=[],w=[];for(;l.pos>0;)u(t.start||0,t.bufferStart||0,v,w,-1);let y=null!==(e=t.length)&&void 0!==e?e:v.length?w[0]+v[0].length:0;return new so(h[t.topID],v.reverse(),w.reverse(),y)}(t)}}so.empty=new so(eo.none,[],[],0);class oo{constructor(t,e){this.buffer=t,this.index=e}get id(){return this.buffer[this.index-4]}get start(){return this.buffer[this.index-3]}get end(){return this.buffer[this.index-2]}get size(){return this.buffer[this.index-1]}get pos(){return this.index}next(){this.index-=4}fork(){return new oo(this.buffer,this.index)}}class lo{constructor(t,e,i){this.buffer=t,this.length=e,this.set=i}get type(){return eo.none}toString(){let t=[];for(let e=0;e0));l=s[l+3]);return o}slice(t,e,i){let n=this.buffer,r=new Uint16Array(e-t),s=0;for(let o=t,l=0;o=e&&ie;case 1:return i<=e&&n>e;case 2:return n>e;case 4:return!0}}function ao(t,e){let i=t.childBefore(e);for(;i;){let e=i.lastChild;if(!e||e.to!=i.to)break;e.type.isError&&e.from==e.to?(t=i,i=e.prevSibling):i=e}return t}function co(t,e,i,n){for(var r;t.from==t.to||(i<1?t.from>=e:t.from>e)||(i>-1?t.to<=e:t.to0?o.length:-1;t!=h;t+=e){let h=o[t],a=l[t]+s.from;if(ho(n,i,a,a+h.length))if(h instanceof lo){if(r&ro.ExcludeBuffers)continue;let o=h.findChild(0,h.buffer.length,e,i-a,n);if(o>-1)return new mo(new go(s,h,t,a),null,o)}else if(r&ro.IncludeAnonymous||!h.type.isAnonymous||wo(h)){let o;if(!(r&ro.IgnoreMounts)&&h.props&&(o=h.prop(Zs.mounted))&&!o.overlay)return new uo(o.tree,a,t,s);let l=new uo(h,a,t,s);return r&ro.IncludeAnonymous||!l.type.isAnonymous?l:l.nextChild(e<0?h.children.length-1:0,e,i,n)}}if(r&ro.IncludeAnonymous||!s.type.isAnonymous)return null;if(t=s.index>=0?s.index+e:e<0?-1:s._parent._tree.children.length,s=s._parent,!s)return null}}get firstChild(){return this.nextChild(0,1,0,4)}get lastChild(){return this.nextChild(this._tree.children.length-1,-1,0,4)}childAfter(t){return this.nextChild(0,1,t,2)}childBefore(t){return this.nextChild(this._tree.children.length-1,-1,t,-2)}enter(t,e,i=0){let n;if(!(i&ro.IgnoreOverlays)&&(n=this._tree.prop(Zs.mounted))&&n.overlay){let i=t-this.from;for(let{from:t,to:r}of n.overlay)if((e>0?t<=i:t=i:r>i))return new uo(n.tree,n.overlay[0].from+this.from,-1,this)}return this.nextChild(0,1,t,e,i)}nextSignificantParent(){let t=this;for(;t.type.isAnonymous&&t._parent;)t=t._parent;return t}get parent(){return this._parent?this._parent.nextSignificantParent():null}get nextSibling(){return this._parent&&this.index>=0?this._parent.nextChild(this.index+1,1,0,4):null}get prevSibling(){return this._parent&&this.index>=0?this._parent.nextChild(this.index-1,-1,0,4):null}cursor(t=0){return new vo(this,t)}get tree(){return this._tree}toTree(){return this._tree}resolve(t,e=0){return co(this,t,e,!1)}resolveInner(t,e=0){return co(this,t,e,!0)}enterUnfinishedNodesBefore(t){return ao(this,t)}getChild(t,e=null,i=null){let n=fo(this,t,e,i);return n.length?n[0]:null}getChildren(t,e=null,i=null){return fo(this,t,e,i)}toString(){return this._tree.toString()}get node(){return this}matchContext(t){return po(this,t)}}function fo(t,e,i,n){let r=t.cursor(),s=[];if(!r.firstChild())return s;if(null!=i)for(;!r.type.is(i);)if(!r.nextSibling())return s;for(;;){if(null!=n&&r.type.is(n))return s;if(r.type.is(e)&&s.push(r.node),!r.nextSibling())return null==n?s:[]}}function po(t,e,i=e.length-1){for(let n=t.parent;i>=0;n=n.parent){if(!n)return!1;if(!n.type.isAnonymous){if(e[i]&&e[i]!=n.name)return!1;i--}}return!0}class go{constructor(t,e,i,n){this.parent=t,this.buffer=e,this.index=i,this.start=n}}class mo{get name(){return this.type.name}get from(){return this.context.start+this.context.buffer.buffer[this.index+1]}get to(){return this.context.start+this.context.buffer.buffer[this.index+2]}constructor(t,e,i){this.context=t,this._parent=e,this.index=i,this.type=t.buffer.set.types[t.buffer.buffer[i]]}child(t,e,i){let{buffer:n}=this.context,r=n.findChild(this.index+4,n.buffer[this.index+3],t,e-this.context.start,i);return r<0?null:new mo(this.context,this,r)}get firstChild(){return this.child(1,0,4)}get lastChild(){return this.child(-1,0,4)}childAfter(t){return this.child(1,t,2)}childBefore(t){return this.child(-1,t,-2)}enter(t,e,i=0){if(i&ro.ExcludeBuffers)return null;let{buffer:n}=this.context,r=n.findChild(this.index+4,n.buffer[this.index+3],e>0?1:-1,t-this.context.start,e);return r<0?null:new mo(this.context,this,r)}get parent(){return this._parent||this.context.parent.nextSignificantParent()}externalSibling(t){return this._parent?null:this.context.parent.nextChild(this.context.index+t,t,0,4)}get nextSibling(){let{buffer:t}=this.context,e=t.buffer[this.index+3];return e<(this._parent?t.buffer[this._parent.index+3]:t.buffer.length)?new mo(this.context,this._parent,e):this.externalSibling(1)}get prevSibling(){let{buffer:t}=this.context,e=this._parent?this._parent.index+4:0;return this.index==e?this.externalSibling(-1):new mo(this.context,this._parent,t.findChild(e,this.index,-1,0,4))}cursor(t=0){return new vo(this,t)}get tree(){return null}toTree(){let t=[],e=[],{buffer:i}=this.context,n=this.index+4,r=i.buffer[this.index+3];if(r>n){let s=i.buffer[this.index+1];t.push(i.slice(n,r,s)),e.push(0)}return new so(this.type,t,e,this.to-this.from)}resolve(t,e=0){return co(this,t,e,!1)}resolveInner(t,e=0){return co(this,t,e,!0)}enterUnfinishedNodesBefore(t){return ao(this,t)}toString(){return this.context.buffer.childString(this.index)}getChild(t,e=null,i=null){let n=fo(this,t,e,i);return n.length?n[0]:null}getChildren(t,e=null,i=null){return fo(this,t,e,i)}get node(){return this}matchContext(t){return po(this,t)}}class vo{get name(){return this.type.name}constructor(t,e=0){if(this.mode=e,this.buffer=null,this.stack=[],this.index=0,this.bufferNode=null,t instanceof uo)this.yieldNode(t);else{this._tree=t.context.parent,this.buffer=t.context;for(let e=t._parent;e;e=e._parent)this.stack.unshift(e.index);this.bufferNode=t,this.yieldBuf(t.index)}}yieldNode(t){return!!t&&(this._tree=t,this.type=t.type,this.from=t.from,this.to=t.to,!0)}yieldBuf(t,e){this.index=t;let{start:i,buffer:n}=this.buffer;return this.type=e||n.set.types[n.buffer[t]],this.from=i+n.buffer[t+1],this.to=i+n.buffer[t+2],!0}yield(t){return!!t&&(t instanceof uo?(this.buffer=null,this.yieldNode(t)):(this.buffer=t.context,this.yieldBuf(t.index,t.type)))}toString(){return this.buffer?this.buffer.buffer.childString(this.index):this._tree.toString()}enterChild(t,e,i){if(!this.buffer)return this.yield(this._tree.nextChild(t<0?this._tree._tree.children.length-1:0,t,e,i,this.mode));let{buffer:n}=this.buffer,r=n.findChild(this.index+4,n.buffer[this.index+3],t,e-this.buffer.start,i);return!(r<0)&&(this.stack.push(this.index),this.yieldBuf(r))}firstChild(){return this.enterChild(1,0,4)}lastChild(){return this.enterChild(-1,0,4)}childAfter(t){return this.enterChild(1,t,2)}childBefore(t){return this.enterChild(-1,t,-2)}enter(t,e,i=this.mode){return this.buffer?!(i&ro.ExcludeBuffers)&&this.enterChild(1,t,e):this.yield(this._tree.enter(t,e,i))}parent(){if(!this.buffer)return this.yieldNode(this.mode&ro.IncludeAnonymous?this._tree._parent:this._tree.parent);if(this.stack.length)return this.yieldBuf(this.stack.pop());let t=this.mode&ro.IncludeAnonymous?this.buffer.parent:this.buffer.parent.nextSignificantParent();return this.buffer=null,this.yieldNode(t)}sibling(t){if(!this.buffer)return!!this._tree._parent&&this.yield(this._tree.index<0?null:this._tree._parent.nextChild(this._tree.index+t,t,0,4,this.mode));let{buffer:e}=this.buffer,i=this.stack.length-1;if(t<0){let t=i<0?0:this.stack[i]+4;if(this.index!=t)return this.yieldBuf(e.findChild(t,this.index,-1,0,4))}else{let t=e.buffer[this.index+3];if(t<(i<0?e.buffer.length:e.buffer[this.stack[i]+3]))return this.yieldBuf(t)}return i<0&&this.yield(this.buffer.parent.nextChild(this.buffer.index+t,t,0,4,this.mode))}nextSibling(){return this.sibling(1)}prevSibling(){return this.sibling(-1)}atLastNode(t){let e,i,{buffer:n}=this;if(n){if(t>0){if(this.index-1)for(let n=e+t,r=t<0?-1:i._tree.children.length;n!=r;n+=t){let t=i._tree.children[n];if(this.mode&ro.IncludeAnonymous||t instanceof lo||!t.type.isAnonymous||wo(t))return!1}return!0}move(t,e){if(e&&this.enterChild(t,0,4))return!0;for(;;){if(this.sibling(t))return!0;if(this.atLastNode(t)||!this.parent())return!1}}next(t=!0){return this.move(1,t)}prev(t=!0){return this.move(-1,t)}moveTo(t,e=0){for(;(this.from==this.to||(e<1?this.from>=t:this.from>t)||(e>-1?this.to<=t:this.to=0;){for(let s=t;s;s=s._parent)if(s.index==n){if(n==this.index)return s;e=s,i=r+1;break t}n=this.stack[--r]}for(let t=i;t=0;r--){if(r<0)return po(this.node,t,n);let s=i[e.buffer[this.stack[r]]];if(!s.isAnonymous){if(t[n]&&t[n]!=s.name)return!1;n--}}return!0}}function wo(t){return t.children.some((t=>t instanceof lo||!t.type.isAnonymous||wo(t)))}const yo=new WeakMap;function bo(t,e){if(!t.isAnonymous||e instanceof lo||e.type!=t)return 1;let i=yo.get(e);if(null==i){i=1;for(let n of e.children){if(n.type!=t||!(n instanceof so)){i=1;break}i+=bo(t,n)}yo.set(e,i)}return i}function xo(t,e,i,n,r,s,o,l,h){let a=0;for(let i=n;i=c)break;p+=e}if(a==r+1){if(p>c){let t=i[r];e(t.children,t.positions,0,t.children.length,n[r]+l);continue}u.push(i[r])}else{let e=n[a-1]+i[a-1].length-d;u.push(xo(t,i,n,r,a,d,e,null,h))}f.push(d+l-s)}}(e,i,n,r,0),(l||h)(u,f,o)}class ko{constructor(t,e,i,n,r=!1,s=!1){this.from=t,this.to=e,this.tree=i,this.offset=n,this.open=(r?1:0)|(s?2:0)}get openStart(){return(1&this.open)>0}get openEnd(){return(2&this.open)>0}static addTree(t,e=[],i=!1){let n=[new ko(0,t.length,t,0,!1,i)];for(let i of e)i.to>t.length&&n.push(i);return n}static applyChanges(t,e,i=128){if(!e.length)return t;let n=[],r=1,s=t.length?t[0]:null;for(let o=0,l=0,h=0;;o++){let a=o=i)for(;s&&s.from=e.from||c<=e.to||h){let t=Math.max(e.from,l)-h,i=Math.min(e.to,c)-h;e=t>=i?null:new ko(t,i,e.tree,e.offset+h,o>0,!!a)}if(e&&n.push(e),s.to>c)break;s=rnew Qs(t.from,t.to))):[new Qs(0,0)]:[new Qs(0,t.length)],this.createParse(t,e||[],i)}parse(t,e,i){let n=this.startParse(t,e,i);for(;;){let t=n.advance();if(t)return t}}}class Ao{constructor(t){this.string=t}get length(){return this.string.length}chunk(t){return this.string.slice(t)}get lineChunks(){return!1}read(t,e){return this.string.slice(t,e)}}new Zs({perNode:!0});let Mo=0;class Co{constructor(t,e,i){this.set=t,this.base=e,this.modified=i,this.id=Mo++}static define(t){if(null==t?void 0:t.base)throw new Error("Can not derive from a modified tag");let e=new Co([],null,[]);if(e.set.push(e),t)for(let i of t.set)e.set.push(i);return e}static defineModifier(){let t=new Oo;return e=>e.modified.indexOf(t)>-1?e:Oo.get(e.base||e,e.modified.concat(t).sort(((t,e)=>t.id-e.id)))}}let Do=0;class Oo{constructor(){this.instances=[],this.id=Do++}static get(t,e){if(!e.length)return t;let i=e[0].instances.find((i=>{return i.base==t&&(n=e,r=i.modified,n.length==r.length&&n.every(((t,e)=>t==r[e])));var n,r}));if(i)return i;let n=[],r=new Co(n,t,e);for(let t of e)t.instances.push(r);let s=function(t){let e=[[]];for(let i=0;ie.length-t.length))}(e);for(let e of t.set)if(!e.modified.length)for(let t of s)n.push(Oo.get(e,t));return r}}function To(t){let e=Object.create(null);for(let i in t){let n=t[i];Array.isArray(n)||(n=[n]);for(let t of i.split(" "))if(t){let i=[],r=2,s=t;for(let e=0;;){if("..."==s&&e>0&&e+3==t.length){r=1;break}let n=/^"(?:[^"\\]|\\.)*?"|[^\/!]+/.exec(s);if(!n)throw new RangeError("Invalid path: "+t);if(i.push("*"==n[0]?"":'"'==n[0][0]?JSON.parse(n[0]):n[0]),e+=n[0].length,e==t.length)break;let o=t[e++];if(e==t.length&&"!"==o){r=0;break}if("/"!=o)throw new RangeError("Invalid path: "+t);s=t.slice(e)}let o=i.length-1,l=i[o];if(!l)throw new RangeError("Invalid path: "+t);let h=new Bo(n,r,o>0?i.slice(0,o):null);e[l]=h.sort(e[l])}}return Eo.add(e)}const Eo=new Zs;class Bo{constructor(t,e,i,n){this.tags=t,this.mode=e,this.context=i,this.next=n}get opaque(){return 0==this.mode}get inherit(){return 1==this.mode}sort(t){return!t||t.depth{let e=r;for(let n of t)for(let t of n.set){let n=i[t.id];if(n){e=e?e+" "+n:n;break}}return e},scope:n}}function Lo(t,e,i,n=0,r=t.length){let s=new No(n,Array.isArray(e)?e:[e],i);s.highlightRange(t.cursor(),n,r,"",s.highlighters),s.flush(r)}Bo.empty=new Bo([],2,null);class No{constructor(t,e,i){this.at=t,this.highlighters=e,this.span=i,this.class=""}startSpan(t,e){e!=this.class&&(this.flush(t),t>this.at&&(this.at=t),this.class=e)}flush(t){t>this.at&&this.class&&this.span(this.at,t,this.class)}highlightRange(t,e,i,n,r){let{type:s,from:o,to:l}=t;if(o>=i||l<=e)return;s.isTop&&(r=this.highlighters.filter((t=>!t.scope||t.scope(s))));let h=n,a=function(t){let e=t.type.prop(Eo);for(;e&&e.context&&!t.matchContext(e.context);)e=e.next;return e||null}(t)||Bo.empty,c=function(t,e){let i=null;for(let n of t){let t=n.style(e);t&&(i=i?i+" "+t:t)}return i}(r,a.tags);if(c&&(h&&(h+=" "),h+=c,1==a.mode&&(n+=(n?" ":"")+c)),this.startSpan(t.from,h),a.opaque)return;let u=t.tree&&t.tree.prop(Zs.mounted);if(u&&u.overlay){let s=t.node.enter(u.overlay[0].from+o,1),a=this.highlighters.filter((t=>!t.scope||t.scope(u.tree.type))),c=t.firstChild();for(let f=0,d=o;;f++){let p=f=g)&&t.nextSibling()););if(!p||g>i)break;d=p.to+o,d>e&&(this.highlightRange(s.cursor(),Math.max(e,p.from+o),Math.min(i,d),n,a),this.startSpan(d,h))}c&&t.parent()}else if(t.firstChild()){do{if(!(t.to<=e)){if(t.from>=i)break;this.highlightRange(t,e,i,n,r),this.startSpan(Math.min(i,t.to),h)}}while(t.nextSibling());t.parent()}}}const Po=Co.define,Io=Po(),Vo=Po(),Wo=Po(Vo),Ho=Po(Vo),Fo=Po(),zo=Po(Fo),qo=Po(Fo),_o=Po(),Ko=Po(_o),jo=Po(),$o=Po(),Go=Po(),Uo=Po(Go),Jo=Po(),Xo={comment:Io,lineComment:Po(Io),blockComment:Po(Io),docComment:Po(Io),name:Vo,variableName:Po(Vo),typeName:Wo,tagName:Po(Wo),propertyName:Ho,attributeName:Po(Ho),className:Po(Vo),labelName:Po(Vo),namespace:Po(Vo),macroName:Po(Vo),literal:Fo,string:zo,docString:Po(zo),character:Po(zo),attributeValue:Po(zo),number:qo,integer:Po(qo),float:Po(qo),bool:Po(Fo),regexp:Po(Fo),escape:Po(Fo),color:Po(Fo),url:Po(Fo),keyword:jo,self:Po(jo),null:Po(jo),atom:Po(jo),unit:Po(jo),modifier:Po(jo),operatorKeyword:Po(jo),controlKeyword:Po(jo),definitionKeyword:Po(jo),moduleKeyword:Po(jo),operator:$o,derefOperator:Po($o),arithmeticOperator:Po($o),logicOperator:Po($o),bitwiseOperator:Po($o),compareOperator:Po($o),updateOperator:Po($o),definitionOperator:Po($o),typeOperator:Po($o),controlOperator:Po($o),punctuation:Go,separator:Po(Go),bracket:Uo,angleBracket:Po(Uo),squareBracket:Po(Uo),paren:Po(Uo),brace:Po(Uo),content:_o,heading:Ko,heading1:Po(Ko),heading2:Po(Ko),heading3:Po(Ko),heading4:Po(Ko),heading5:Po(Ko),heading6:Po(Ko),contentSeparator:Po(_o),list:Po(_o),quote:Po(_o),emphasis:Po(_o),strong:Po(_o),link:Po(_o),monospace:Po(_o),strikethrough:Po(_o),inserted:Po(),deleted:Po(),changed:Po(),invalid:Po(),meta:Jo,documentMeta:Po(Jo),annotation:Po(Jo),processingInstruction:Po(Jo),definition:Co.defineModifier(),constant:Co.defineModifier(),function:Co.defineModifier(),standard:Co.defineModifier(),local:Co.defineModifier(),special:Co.defineModifier()};var Yo;Ro([{tag:Xo.link,class:"tok-link"},{tag:Xo.heading,class:"tok-heading"},{tag:Xo.emphasis,class:"tok-emphasis"},{tag:Xo.strong,class:"tok-strong"},{tag:Xo.keyword,class:"tok-keyword"},{tag:Xo.atom,class:"tok-atom"},{tag:Xo.bool,class:"tok-bool"},{tag:Xo.url,class:"tok-url"},{tag:Xo.labelName,class:"tok-labelName"},{tag:Xo.inserted,class:"tok-inserted"},{tag:Xo.deleted,class:"tok-deleted"},{tag:Xo.literal,class:"tok-literal"},{tag:Xo.string,class:"tok-string"},{tag:Xo.number,class:"tok-number"},{tag:[Xo.regexp,Xo.escape,Xo.special(Xo.string)],class:"tok-string2"},{tag:Xo.variableName,class:"tok-variableName"},{tag:Xo.local(Xo.variableName),class:"tok-variableName tok-local"},{tag:Xo.definition(Xo.variableName),class:"tok-variableName tok-definition"},{tag:Xo.special(Xo.variableName),class:"tok-variableName2"},{tag:Xo.definition(Xo.propertyName),class:"tok-propertyName tok-definition"},{tag:Xo.typeName,class:"tok-typeName"},{tag:Xo.namespace,class:"tok-namespace"},{tag:Xo.className,class:"tok-className"},{tag:Xo.macroName,class:"tok-macroName"},{tag:Xo.propertyName,class:"tok-propertyName"},{tag:Xo.operator,class:"tok-operator"},{tag:Xo.comment,class:"tok-comment"},{tag:Xo.meta,class:"tok-meta"},{tag:Xo.invalid,class:"tok-invalid"},{tag:Xo.punctuation,class:"tok-punctuation"}]);const Qo=new Zs;class Zo{constructor(t,e,i=[],n=""){this.data=t,this.name=n,St.prototype.hasOwnProperty("tree")||Object.defineProperty(St.prototype,"tree",{get(){return el(this)}}),this.parser=e,this.extension=[cl.of(this),St.languageData.of(((t,e,i)=>t.facet(tl(t,e,i))))].concat(i)}isActiveAt(t,e,i=-1){return tl(t,e,i)==this.data}findRegions(t){let e=t.facet(cl);if((null==e?void 0:e.data)==this.data)return[{from:0,to:t.doc.length}];if(!e||!e.allowsNesting)return[];let i=[],n=(t,e)=>{if(t.prop(Qo)==this.data)return void i.push({from:e,to:e+t.length});let r=t.prop(Zs.mounted);if(r){if(r.tree.prop(Qo)==this.data){if(r.overlay)for(let t of r.overlay)i.push({from:t.from+e,to:t.to+e});else i.push({from:e,to:e+t.length});return}if(r.overlay){let t=i.length;if(n(r.tree,r.overlay[0].from+e),i.length>t)return}}for(let i=0;i=this.cursorPos?this.doc.sliceString(t,e):this.string.slice(t-i,e-i)}}let nl=null;class rl{constructor(t,e,i=[],n,r,s,o,l){this.parser=t,this.state=e,this.fragments=i,this.tree=n,this.treeLen=r,this.viewport=s,this.skipped=o,this.scheduleOn=l,this.parse=null,this.tempSkipped=[]}static create(t,e,i){return new rl(t,e,[],so.empty,0,i,[],null)}startParse(){return this.parser.startParse(new il(this.state.doc),this.fragments)}work(t,e){return null!=e&&e>=this.state.doc.length&&(e=void 0),this.tree!=so.empty&&this.isDone(null!=e?e:this.state.doc.length)?(this.takeTree(),!0):this.withContext((()=>{var i;if("number"==typeof t){let e=Date.now()+t;t=()=>Date.now()>e}for(this.parse||(this.parse=this.startParse()),null!=e&&(null==this.parse.stoppedAt||this.parse.stoppedAt>e)&&e=this.treeLen&&((null==this.parse.stoppedAt||this.parse.stoppedAt>t)&&this.parse.stopAt(t),this.withContext((()=>{for(;!(e=this.parse.advance()););})),this.treeLen=t,this.tree=e,this.fragments=this.withoutTempSkipped(ko.addTree(this.tree,this.fragments,!0)),this.parse=null)}withContext(t){let e=nl;nl=this;try{return t()}finally{nl=e}}withoutTempSkipped(t){for(let e;e=this.tempSkipped.pop();)t=sl(t,e.from,e.to);return t}changes(t,e){let{fragments:i,tree:n,treeLen:r,viewport:s,skipped:o}=this;if(this.takeTree(),!t.empty){let e=[];if(t.iterChangedRanges(((t,i,n,r)=>e.push({fromA:t,toA:i,fromB:n,toB:r}))),i=ko.applyChanges(i,e),n=so.empty,r=0,s={from:t.mapPos(s.from,-1),to:t.mapPos(s.to,1)},this.skipped.length){o=[];for(let e of this.skipped){let i=t.mapPos(e.from,1),n=t.mapPos(e.to,-1);it.from&&(this.fragments=sl(this.fragments,i,n),this.skipped.splice(e--,1))}return!(this.skipped.length>=e)&&(this.reset(),!0)}reset(){this.parse&&(this.takeTree(),this.parse=null)}skipUntilInView(t,e){this.skipped.push({from:t,to:e})}static getSkippingParser(t){return new class extends So{createParse(e,i,n){let r=n[0].from,s=n[n.length-1].to;return{parsedPos:r,advance(){let e=nl;if(e){for(let t of n)e.tempSkipped.push(t);t&&(e.scheduleOn=e.scheduleOn?Promise.all([e.scheduleOn,t]):t)}return this.parsedPos=s,new so(eo.none,[],[],s-r)},stoppedAt:null,stopAt(){}}}}}isDone(t){t=Math.min(t,this.state.doc.length);let e=this.fragments;return this.treeLen>=t&&e.length&&0==e[0].from&&e[0].to>=t}static get(){return nl}}function sl(t,e,i){return ko.applyChanges(t,[{fromA:e,toA:i,fromB:e,toB:i}])}class ol{constructor(t){this.context=t,this.tree=t.tree}apply(t){if(!t.docChanged&&this.tree==this.context.tree)return this;let e=this.context.changes(t.changes,t.state),i=this.context.treeLen==t.startState.doc.length?void 0:Math.max(t.changes.mapPos(this.context.treeLen),e.viewport.to);return e.work(20,i)||e.takeTree(),new ol(e)}static init(t){let e=Math.min(3e3,t.doc.length),i=rl.create(t.facet(cl).parser,t,{from:0,to:e});return i.work(20,e)||i.takeTree(),new ol(i)}}Zo.state=q.define({create:ol.init,update(t,e){for(let t of e.effects)if(t.is(Zo.setState))return t.value;return e.startState.facet(cl)!=e.state.facet(cl)?ol.init(e.state):t.apply(e)}});let ll=t=>{let e=setTimeout((()=>t()),500);return()=>clearTimeout(e)};"undefined"!=typeof requestIdleCallback&&(ll=t=>{let e=-1,i=setTimeout((()=>{e=requestIdleCallback(t,{timeout:400})}),100);return()=>e<0?clearTimeout(i):cancelIdleCallback(e)});const hl="undefined"!=typeof navigator&&(null===(Yo=navigator.scheduling)||void 0===Yo?void 0:Yo.isInputPending)?()=>navigator.scheduling.isInputPending():null,al=Di.fromClass(class{constructor(t){this.view=t,this.working=null,this.workScheduled=0,this.chunkEnd=-1,this.chunkBudget=-1,this.work=this.work.bind(this),this.scheduleWork()}update(t){let e=this.view.state.field(Zo.state).context;(e.updateViewport(t.view.viewport)||this.view.viewport.to>e.treeLen)&&this.scheduleWork(),t.docChanged&&(this.view.hasFocus&&(this.chunkBudget+=50),this.scheduleWork()),this.checkAsyncSchedule(e)}scheduleWork(){if(this.working)return;let{state:t}=this.view,e=t.field(Zo.state);e.tree==e.context.tree&&e.context.isDone(t.doc.length)||(this.working=ll(this.work))}work(t){this.working=null;let e=Date.now();if(this.chunkEndn+1e3,l=r.context.work((()=>hl&&hl()||Date.now()>s),n+(o?0:1e5));this.chunkBudget-=Date.now()-e,(l||this.chunkBudget<=0)&&(r.context.takeTree(),this.view.dispatch({effects:Zo.setState.of(new ol(r.context))})),this.chunkBudget>0&&(!l||o)&&this.scheduleWork(),this.checkAsyncSchedule(r.context)}checkAsyncSchedule(t){t.scheduleOn&&(this.workScheduled++,t.scheduleOn.then((()=>this.scheduleWork())).catch((t=>Si(this.view.state,t))).then((()=>this.workScheduled--)),t.scheduleOn=null)}destroy(){this.working&&this.working()}isWorking(){return!!(this.working||this.workScheduled>0)}},{eventHandlers:{focus(){this.scheduleWork()}}}),cl=P.define({combine:t=>t.length?t[0]:null,enables:t=>[Zo.state,al,Or.contentAttributes.compute([t],(e=>{let i=e.facet(t);return i&&i.name?{"data-language":i.name}:{}}))]}),ul=P.define(),fl=P.define({combine:t=>{if(!t.length)return" ";if(!/^(?: +|\t+)$/.test(t[0]))throw new Error("Invalid indent unit: "+JSON.stringify(t[0]));return t[0]}});function dl(t){let e=t.facet(fl);return 9==e.charCodeAt(0)?t.tabSize*e.length:e.length}function pl(t,e){let i="",n=t.tabSize;if(9==t.facet(fl).charCodeAt(0))for(;e>=n;)i+="\t",e-=n;for(let t=0;t=i.from&&n<=i.to?r&&n==t?{text:"",from:t}:(e<0?n-1&&(r+=s-this.countColumn(i,i.search(/\S|$/))),r}countColumn(t,e=t.length){return zt(t,this.state.tabSize,e)}lineIndent(t,e=1){let{text:i,from:n}=this.lineAt(t,e),r=this.options.overrideIndentation;if(r){let t=r(n);if(t>-1)return t}return this.countColumn(i,i.search(/\S|$/))}get simulatedBreak(){return this.options.simulateBreak||null}}const vl=new Zs;function wl(t){let e=t.type.prop(vl);if(e)return e;let i,n=t.firstChild;if(n&&(i=n.type.prop(Zs.closedBy))){let e=t.lastChild,n=e&&i.indexOf(e.name)>-1;return t=>function(t,e,i,n,r){let s=t.textAfter,o=s.match(/^\s*/)[0].length,l=n&&s.slice(o,o+n.length)==n||r==t.pos+o,h=e?function(t){let e=t.node,i=e.childAfter(e.from),n=e.lastChild;if(!i)return null;let r=t.options.simulateBreak,s=t.state.doc.lineAt(i.from),o=null==r||r<=s.from?s.to:Math.min(s.to,r);for(let t=i.to;;){let r=e.childAfter(t);if(!r||r==n)return null;if(!r.type.isSkipped)return r.fromt.prop(Qo)==s.data:s?t=>t==s:void 0,this.style=Ro(t.map((t=>({tag:t.tag,class:t.class||n(Object.assign({},t,{tag:null}))}))),{all:r}).style,this.module=i?new $t(i):null,this.themeType=e.themeType}static define(t,e){return new Sl(t,e||{})}}const Al=P.define(),Ml=P.define({combine:t=>t.length?[t[0]]:null});function Cl(t){let e=t.facet(Al);return e.length?e:t.facet(Ml)}function Dl(t,e){let i,n=[Tl];return t instanceof Sl&&(t.module&&n.push(Or.styleModule.of(t.module)),i=t.themeType),(null==e?void 0:e.fallback)?n.push(Ml.of(t)):i?n.push(Al.computeN([Or.darkTheme],(e=>e.facet(Or.darkTheme)==("dark"==i)?[t]:[]))):n.push(Al.of(t)),n}class Ol{constructor(t){this.markCache=Object.create(null),this.tree=el(t.state),this.decorations=this.buildDeco(t,Cl(t.state))}update(t){let e=el(t.state),i=Cl(t.state),n=i!=Cl(t.startState);e.length{i.add(t,e,this.markCache[n]||(this.markCache[n]=ii.mark({class:n})))}),n,r);return i.finish()}}const Tl=U.high(Di.fromClass(Ol,{decorations:t=>t.decorations})),El=Or.baseTheme({"&.cm-focused .cm-matchingBracket":{backgroundColor:"#328c8252"},"&.cm-focused .cm-nonmatchingBracket":{backgroundColor:"#bb555544"}}),Bl="()[]{}",Rl=P.define({combine:t=>At(t,{afterCursor:!0,brackets:Bl,maxScanDistance:1e4,renderMatch:Pl})}),Ll=ii.mark({class:"cm-matchingBracket"}),Nl=ii.mark({class:"cm-nonmatchingBracket"});function Pl(t){let e=[],i=t.matched?Ll:Nl;return e.push(i.range(t.start.from,t.start.to)),t.end&&e.push(i.range(t.end.from,t.end.to)),e}const Il=q.define({create:()=>ii.none,update(t,e){if(!e.docChanged&&!e.selection)return t;let i=[],n=e.state.facet(Rl);for(let t of e.state.selection.ranges){if(!t.empty)continue;let r=Fl(e.state,t.head,-1,n)||t.head>0&&Fl(e.state,t.head-1,1,n)||n.afterCursor&&(Fl(e.state,t.head,1,n)||t.headOr.decorations.from(t)}),Vl=[Il,El];function Wl(t={}){return[Rl.of(t),Vl]}function Hl(t,e,i){let n=t.prop(e<0?Zs.openedBy:Zs.closedBy);if(n)return n;if(1==t.name.length){let n=i.indexOf(t.name);if(n>-1&&n%2==(e<0?1:0))return[i[n+e]]}return null}function Fl(t,e,i,n={}){let r=n.maxScanDistance||1e4,s=n.brackets||Bl,o=el(t),l=o.resolveInner(e,i);for(let n=l;n;n=n.parent){let r=Hl(n.type,i,s);if(r&&n.from0)return null;let a={from:i<0?e-1:e,to:i>0?e+1:e},c=t.doc.iterRange(e,i>0?t.doc.length:0),u=0;for(let t=0;!c.next().done&&t<=s;){let s=c.value;i<0&&(t+=s.length);let l=e+t*i;for(let t=i>0?0:s.length-1,e=i>0?s.length:-1;t!=e;t+=i){let e=o.indexOf(s[t]);if(!(e<0||n.resolveInner(l+t,1).type!=r))if(e%2==0==i>0)u++;else{if(1==u)return{start:a,end:{from:l+t,to:l+t+1},matched:e>>1==h>>1};u--}}i>0&&(t+=s.length)}return c.done?{start:a,matched:!1}:null}(t,e,i,o,l.type,r,s)}function zl(t,e,i,n,r,s){let o=n.parent,l={from:n.from,to:n.to},h=0,a=null==o?void 0:o.cursor();if(a&&(i<0?a.childBefore(n.from):a.childAfter(n.to)))do{if(i<0?a.to<=n.from:a.from>=n.to){if(0==h&&r.indexOf(a.type.name)>-1&&a.from-1||(Kl.push(t),console.warn(e))}function Gl(t,e){let i=null;for(let n of e.split(".")){let e=t[n]||Xo[n];e?"function"==typeof e?i?i=e(i):$l(n,`Modifier ${n} used at start of tag`):i?$l(n,`Tag ${n} used as modifier`):i=e:$l(n,`Unknown highlighting tag ${n}`)}if(!i)return 0;let n=e.replace(/ /g,"_"),r=eo.define({id:_l.length,name:n,props:[To({[n]:i})]});return _l.push(r),r.id}const Ul={brackets:["(","[","{","'",'"'],before:")]}:;>",stringPrefixes:[]},Jl=ut.define({map(t,e){let i=e.mapPos(t,-1,k.TrackAfter);return null==i?void 0:i}}),Xl=ut.define({map:(t,e)=>e.mapPos(t)}),Yl=new class extends Mt{};Yl.startSide=1,Yl.endSide=-1;const Ql=q.define({create:()=>Tt.empty,update(t,e){if(e.selection){let i=e.state.doc.lineAt(e.selection.main.head).from,n=e.startState.doc.lineAt(e.startState.selection.main.head).from;i!=e.changes.mapPos(n,-1)&&(t=Tt.empty)}t=t.map(e.changes);for(let i of e.effects)i.is(Jl)?t=t.update({add:[Yl.range(i.value,i.value+1)]}):i.is(Xl)&&(t=t.update({filter:t=>t!=i.value}));return t}});const Zl="()[]{}<>";function th(t){for(let e=0;e{if((ih?t.composing:t.compositionStarted)||t.state.readOnly)return!1;let r=t.state.selection.main;if(n.length>2||2==n.length&&1==b(w(n,0))||e!=r.from||i!=r.to)return!1;let s=function(t,e){let i=eh(t,t.selection.main.head),n=i.brackets||Ul.brackets;for(let r of n){let s=th(w(r,0));if(e==r)return s==r?ah(t,r,n.indexOf(r+r+r)>-1,i):lh(t,r,s,i.before||Ul.before);if(e==s&&sh(t,t.selection.main.from))return hh(t,r,s)}return null}(t.state,n);return!!s&&(t.dispatch(s),!0)})),rh=[{key:"Backspace",run:({state:t,dispatch:e})=>{if(t.readOnly)return!1;let i=eh(t,t.selection.main.head).brackets||Ul.brackets,n=null,r=t.changeByRange((e=>{if(e.empty){let n=function(t,e){let i=t.sliceString(e-2,e);return b(w(i,0))==i.length?i:i.slice(1)}(t.doc,e.head);for(let r of i)if(r==n&&oh(t.doc,e.head)==th(w(r,0)))return{changes:{from:e.head-r.length,to:e.head+r.length},range:R.cursor(e.head-r.length)}}return{range:n=e}}));return n||e(t.update(r,{scrollIntoView:!0,userEvent:"delete.backward"})),!n}}];function sh(t,e){let i=!1;return t.field(Ql).between(0,t.doc.length,(t=>{t==e&&(i=!0)})),i}function oh(t,e){let i=t.sliceString(e,e+2);return i.slice(0,b(w(i,0)))}function lh(t,e,i,n){let r=null,s=t.changeByRange((s=>{if(!s.empty)return{changes:[{insert:e,from:s.from},{insert:i,from:s.to}],effects:Jl.of(s.to+e.length),range:R.range(s.anchor+e.length,s.head+e.length)};let o=oh(t.doc,s.head);return!o||/\s/.test(o)||n.indexOf(o)>-1?{changes:{insert:e+i,from:s.head},effects:Jl.of(s.head+e.length),range:R.cursor(s.head+e.length)}:{range:r=s}}));return r?null:t.update(s,{scrollIntoView:!0,userEvent:"input.type"})}function hh(t,e,i){let n=null,r=t.selection.ranges.map((e=>e.empty&&oh(t.doc,e.head)==i?R.cursor(e.head+i.length):n=e));return n?null:t.update({selection:R.create(r,t.selection.mainIndex),scrollIntoView:!0,effects:t.selection.ranges.map((({from:t})=>Xl.of(t)))})}function ah(t,e,i,n){let r=n.stringPrefixes||Ul.stringPrefixes,s=null,o=t.changeByRange((n=>{if(!n.empty)return{changes:[{insert:e,from:n.from},{insert:e,from:n.to}],effects:Jl.of(n.to+e.length),range:R.range(n.anchor+e.length,n.head+e.length)};let o,l=n.head,h=oh(t.doc,l);if(h==e){if(ch(t,l))return{changes:{insert:e+e,from:l},effects:Jl.of(l+e.length),range:R.cursor(l+e.length)};if(sh(t,l)){let n=i&&t.sliceDoc(l,l+3*e.length)==e+e+e;return{range:R.cursor(l+e.length*(n?3:1)),effects:Xl.of(l)}}}else{if(i&&t.sliceDoc(l-2*e.length,l)==e+e&&(o=uh(t,l-2*e.length,r))>-1&&ch(t,o))return{changes:{insert:e+e+e+e,from:l},effects:Jl.of(l+e.length),range:R.cursor(l+e.length)};if(t.charCategorizer(l)(h)!=yt.Word&&uh(t,l,r)>-1&&!function(t,e,i,n){let r=el(t).resolveInner(e,-1),s=n.reduce(((t,e)=>Math.max(t,e.length)),0);for(let o=0;o<5;o++){let o=t.sliceDoc(r.from,Math.min(r.to,r.from+i.length+s)),l=o.indexOf(i);if(!l||l>-1&&n.indexOf(o.slice(0,l))>-1){let e=r.firstChild;for(;e&&e.from==r.from&&e.to-e.from>i.length+l;){if(t.sliceDoc(e.to-i.length,e.to)==i)return!1;e=e.firstChild}return!0}let h=r.to==e&&r.parent;if(!h)break;r=h}return!1}(t,l,e,r))return{changes:{insert:e+e,from:l},effects:Jl.of(l+e.length),range:R.cursor(l+e.length)}}return{range:s=n}}));return s?null:t.update(o,{scrollIntoView:!0,userEvent:"input.type"})}function ch(t,e){let i=el(t).resolveInner(e+1);return i.parent&&i.from==e}function uh(t,e,i){let n=t.charCategorizer(e);if(n(t.sliceDoc(e-1,e))!=yt.Word)return e;for(let r of i){let i=e-r.length;if(t.sliceDoc(i,e)==r&&n(t.sliceDoc(i-1,i))!=yt.Word)return i}return-1}function fh(t,e){return({state:i,dispatch:n})=>{if(i.readOnly)return!1;let r=t(e,i);return!!r&&(n(i.update(r)),!0)}}const dh=fh(wh,0),ph=fh(vh,0),gh=fh(((t,e)=>vh(t,e,function(t){let e=[];for(let i of t.selection.ranges){let n=t.doc.lineAt(i.from),r=i.to<=n.to?n:t.doc.lineAt(i.to),s=e.length-1;s>=0&&e[s].to>n.from?e[s].to=r.to:e.push({from:n.from,to:r.to})}return e}(e))),0);function mh(t,e=t.selection.main.head){let i=t.languageDataAt("commentTokens",e);return i.length?i[0]:{}}function vh(t,e,i=e.selection.ranges){let n=i.map((t=>mh(e,t.from).block));if(!n.every((t=>t)))return null;let r=i.map(((t,i)=>function(t,{open:e,close:i},n,r){let s,o,l=t.sliceDoc(n-50,n),h=t.sliceDoc(r,r+50),a=/\s*$/.exec(l)[0].length,c=/^\s*/.exec(h)[0].length,u=l.length-a;if(l.slice(u-e.length,u)==e&&h.slice(c,c+i.length)==i)return{open:{pos:n-a,margin:a&&1},close:{pos:r+c,margin:c&&1}};r-n<=100?s=o=t.sliceDoc(n,r):(s=t.sliceDoc(n,n+50),o=t.sliceDoc(r-50,r));let f=/^\s*/.exec(s)[0].length,d=/\s*$/.exec(o)[0].length,p=o.length-d-i.length;return s.slice(f,f+e.length)==e&&o.slice(p,p+i.length)==i?{open:{pos:n+f+e.length,margin:/\s/.test(s.charAt(f+e.length))?1:0},close:{pos:r-d-i.length,margin:/\s/.test(o.charAt(p-1))?1:0}}:null}(e,n[i],t.from,t.to)));if(2!=t&&!r.every((t=>t)))return{changes:e.changes(i.map(((t,e)=>r[e]?[]:[{from:t.from,insert:n[e].open+" "},{from:t.to,insert:" "+n[e].close}])))};if(1!=t&&r.some((t=>t))){let t=[];for(let e,i=0;ir&&(t==s||s>l.from)){r=l.from;let t=mh(e,i).line;if(!t)continue;let s=/^\s*/.exec(l.text)[0].length,h=s==l.length,a=l.text.slice(s,s+t.length)==t?s:-1;st.comment<0&&(!t.empty||t.single)))){let t=[];for(let{line:e,token:i,indent:r,empty:s,single:o}of n)!o&&s||t.push({from:e.from+r,insert:i+" "});let i=e.changes(t);return{changes:i,selection:e.selection.map(i,1)}}if(1!=t&&n.some((t=>t.comment>=0))){let t=[];for(let{line:e,comment:i,token:r}of n)if(i>=0){let n=e.from+i,s=n+r.length;" "==e.text[s-e.from]&&s++,t.push({from:n,to:s})}return{changes:t}}return null}const yh=ht.define(),bh=ht.define(),xh=P.define(),kh=P.define({combine:t=>At(t,{minDepth:100,newGroupDelay:500},{minDepth:Math.max,newGroupDelay:Math.min})});const Sh=q.define({create:()=>Hh.empty,update(t,e){let i=e.state.facet(kh),n=e.annotation(yh);if(n){let r=e.docChanged?R.single(function(t){let e=0;return t.iterChangedRanges(((t,i)=>e=i)),e}(e.changes)):void 0,s=Eh.fromTransaction(e,r),o=n.side,l=0==o?t.undone:t.done;return l=s?Bh(l,l.length,i.minDepth,s):Nh(l,e.startState.selection),new Hh(0==o?n.rest:l,0==o?l:n.rest)}let r=e.annotation(bh);if("full"!=r&&"before"!=r||(t=t.isolate()),!1===e.annotation(ft.addToHistory))return e.changes.empty?t:t.addMapping(e.changes.desc);let s=Eh.fromTransaction(e),o=e.annotation(ft.time),l=e.annotation(ft.userEvent);return s?t=t.addChanges(s,o,l,i.newGroupDelay,i.minDepth):e.selection&&(t=t.addSelection(e.startState.selection,o,l,i.newGroupDelay)),"full"!=r&&"after"!=r||(t=t.isolate()),t},toJSON:t=>({done:t.done.map((t=>t.toJSON())),undone:t.undone.map((t=>t.toJSON()))}),fromJSON:t=>new Hh(t.done.map(Eh.fromJSON),t.undone.map(Eh.fromJSON))});function Ah(t={}){return[Sh,kh.of(t),Or.domEventHandlers({beforeinput(t,e){let i="historyUndo"==t.inputType?Ch:"historyRedo"==t.inputType?Dh:null;return!!i&&(t.preventDefault(),i(e))}})]}function Mh(t,e){return function({state:i,dispatch:n}){if(!e&&i.readOnly)return!1;let r=i.field(Sh,!1);if(!r)return!1;let s=r.pop(t,i,e);return!!s&&(n(s),!0)}}const Ch=Mh(0,!1),Dh=Mh(1,!1),Oh=Mh(0,!0),Th=Mh(1,!0);class Eh{constructor(t,e,i,n,r){this.changes=t,this.effects=e,this.mapped=i,this.startSelection=n,this.selectionsAfter=r}setSelAfter(t){return new Eh(this.changes,this.effects,this.mapped,this.startSelection,t)}toJSON(){var t,e,i;return{changes:null===(t=this.changes)||void 0===t?void 0:t.toJSON(),mapped:null===(e=this.mapped)||void 0===e?void 0:e.toJSON(),startSelection:null===(i=this.startSelection)||void 0===i?void 0:i.toJSON(),selectionsAfter:this.selectionsAfter.map((t=>t.toJSON()))}}static fromJSON(t){return new Eh(t.changes&&A.fromJSON(t.changes),[],t.mapped&&S.fromJSON(t.mapped),t.startSelection&&R.fromJSON(t.startSelection),t.selectionsAfter.map(R.fromJSON))}static fromTransaction(t,e){let i=Lh;for(let e of t.startState.facet(xh)){let n=e(t);n.length&&(i=i.concat(n))}return!i.length&&t.changes.empty?null:new Eh(t.changes.invert(t.startState.doc),i,void 0,e||t.startState.selection,Lh)}static selection(t){return new Eh(void 0,Lh,void 0,void 0,t)}}function Bh(t,e,i,n){let r=e+1>i+20?e-i-1:0,s=t.slice(r,e);return s.push(n),s}function Rh(t,e){return t.length?e.length?t.concat(e):t:e}const Lh=[];function Nh(t,e){if(t.length){let i=t[t.length-1],n=i.selectionsAfter.slice(Math.max(0,i.selectionsAfter.length-200));return n.length&&n[n.length-1].eq(e)?t:(n.push(e),Bh(t,t.length-1,1e9,i.setSelAfter(n)))}return[Eh.selection([e])]}function Ph(t){let e=t[t.length-1],i=t.slice();return i[t.length-1]=e.setSelAfter(e.selectionsAfter.slice(0,e.selectionsAfter.length-1)),i}function Ih(t,e){if(!t.length)return t;let i=t.length,n=Lh;for(;i;){let r=Vh(t[i-1],e,n);if(r.changes&&!r.changes.empty||r.effects.length){let e=t.slice(0,i);return e[i-1]=r,e}e=r.mapped,i--,n=r.selectionsAfter}return n.length?[Eh.selection(n)]:Lh}function Vh(t,e,i){let n=Rh(t.selectionsAfter.length?t.selectionsAfter.map((t=>t.map(e))):Lh,i);if(!t.changes)return Eh.selection(n);let r=t.changes.map(e),s=e.mapDesc(t.changes,!0),o=t.mapped?t.mapped.composeDesc(s):s;return new Eh(r,ut.mapEffects(t.effects,e),o,t.startSelection.map(s),n)}const Wh=/^(input\.type|delete)($|\.)/;class Hh{constructor(t,e,i=0,n){this.done=t,this.undone=e,this.prevTime=i,this.prevUserEvent=n}isolate(){return this.prevTime?new Hh(this.done,this.undone):this}addChanges(t,e,i,n,r){let s=this.done,o=s[s.length-1];return s=o&&o.changes&&!o.changes.empty&&t.changes&&(!i||Wh.test(i))&&(!o.selectionsAfter.length&&e-this.prevTimei.push(t,e))),e.iterChangedRanges(((t,e,r,s)=>{for(let t=0;t=e&&r<=o&&(n=!0)}})),n}(o.changes,t.changes)||"input.type.compose"==i)?Bh(s,s.length-1,r,new Eh(t.changes.compose(o.changes),Rh(t.effects,o.effects),o.mapped,o.startSelection,Lh)):Bh(s,s.length,r,t),new Hh(s,Lh,e,i)}addSelection(t,e,i,n){let r=this.done.length?this.done[this.done.length-1].selectionsAfter:Lh;return r.length>0&&e-this.prevTimet.empty!=o.ranges[e].empty)).length)?this:new Hh(Nh(this.done,t),this.undone,e,i);var s,o}addMapping(t){return new Hh(Ih(this.done,t),Ih(this.undone,t),this.prevTime,this.prevUserEvent)}pop(t,e,i){let n=0==t?this.done:this.undone;if(0==n.length)return null;let r=n[n.length-1];if(i&&r.selectionsAfter.length)return e.update({selection:r.selectionsAfter[r.selectionsAfter.length-1],annotations:yh.of({side:t,rest:Ph(n)}),userEvent:0==t?"select.undo":"select.redo",scrollIntoView:!0});if(r.changes){let i=1==n.length?Lh:n.slice(0,n.length-1);return r.mapped&&(i=Ih(i,r.mapped)),e.update({changes:r.changes,selection:r.startSelection,effects:r.effects,annotations:yh.of({side:t,rest:i}),filter:!1,userEvent:0==t?"undo":"redo",scrollIntoView:!0})}return null}}Hh.empty=new Hh(Lh,Lh);const Fh=[{key:"Mod-z",run:Ch,preventDefault:!0},{key:"Mod-y",mac:"Mod-Shift-z",run:Dh,preventDefault:!0},{linux:"Ctrl-Shift-z",run:Dh,preventDefault:!0},{key:"Mod-u",run:Oh,preventDefault:!0},{key:"Alt-u",mac:"Mod-Shift-u",run:Th,preventDefault:!0}];function zh(t,e){return R.create(t.ranges.map(e),t.mainIndex)}function qh(t,e){return t.update({selection:e,scrollIntoView:!0,userEvent:"select"})}function _h({state:t,dispatch:e},i){let n=zh(t.selection,i);return!n.eq(t.selection)&&(e(qh(t,n)),!0)}function Kh(t,e){return R.cursor(e?t.to:t.from)}function jh(t,e){return _h(t,(i=>i.empty?t.moveByChar(i,e):Kh(i,e)))}function $h(t){return t.textDirectionAt(t.state.selection.main.head)==Vi.LTR}const Gh=t=>jh(t,!$h(t)),Uh=t=>jh(t,$h(t));function Jh(t,e){return _h(t,(i=>i.empty?t.moveByGroup(i,e):Kh(i,e)))}function Xh(t,e,i){if(e.type.prop(i))return!0;let n=e.to-e.from;return n&&(n>2||/[^\s,.;:]/.test(t.sliceDoc(e.from,e.to)))||e.firstChild}function Yh(t,e,i){let n,r,s=el(t).resolveInner(e.head),o=i?Zs.closedBy:Zs.openedBy;for(let n=e.head;;){let e=i?s.childAfter(n):s.childBefore(n);if(!e)break;Xh(t,e,o)?s=e:n=i?e.to:e.from}return r=s.type.prop(o)&&(n=i?Fl(t,s.from,1):Fl(t,s.to,-1))&&n.matched?i?n.end.to:n.end.from:i?s.to:s.from,R.cursor(r,i?-1:1)}function Qh(t,e){return _h(t,(i=>{if(!i.empty)return Kh(i,e);let n=t.moveVertically(i,e);return n.head!=i.head?n:t.moveToLineBoundary(i,e)}))}const Zh=t=>Qh(t,!1),ta=t=>Qh(t,!0);function ea(t){return Math.max(t.defaultLineHeight,Math.min(t.dom.clientHeight,innerHeight)-5)}function ia(t,e){let{state:i}=t,n=zh(i.selection,(i=>i.empty?t.moveVertically(i,e,ea(t)):Kh(i,e)));if(n.eq(i.selection))return!1;let r,s=t.coordsAtPos(i.selection.main.head),o=t.scrollDOM.getBoundingClientRect();return s&&s.top>o.top&&s.bottomia(t,!1),ra=t=>ia(t,!0);function sa(t,e,i){let n=t.lineBlockAt(e.head),r=t.moveToLineBoundary(e,i);if(r.head==e.head&&r.head!=(i?n.to:n.from)&&(r=t.moveToLineBoundary(e,i,!1)),!i&&r.head==n.from&&n.length){let i=/^\s*/.exec(t.state.sliceDoc(n.from,Math.min(n.from+100,n.to)))[0].length;i&&e.head!=n.from+i&&(r=R.cursor(n.from+i))}return r}function oa(t,e){let i=zh(t.state.selection,(t=>{let i=e(t);return R.range(t.anchor,i.head,i.goalColumn)}));return!i.eq(t.state.selection)&&(t.dispatch(qh(t.state,i)),!0)}function la(t,e){return oa(t,(i=>t.moveByChar(i,e)))}const ha=t=>la(t,!$h(t)),aa=t=>la(t,$h(t));function ca(t,e){return oa(t,(i=>t.moveByGroup(i,e)))}function ua(t,e){return oa(t,(i=>t.moveVertically(i,e)))}const fa=t=>ua(t,!1),da=t=>ua(t,!0);function pa(t,e){return oa(t,(i=>t.moveVertically(i,e,ea(t))))}const ga=t=>pa(t,!1),ma=t=>pa(t,!0),va=({state:t,dispatch:e})=>(e(qh(t,{anchor:0})),!0),wa=({state:t,dispatch:e})=>(e(qh(t,{anchor:t.doc.length})),!0),ya=({state:t,dispatch:e})=>(e(qh(t,{anchor:t.selection.main.anchor,head:0})),!0),ba=({state:t,dispatch:e})=>(e(qh(t,{anchor:t.selection.main.anchor,head:t.doc.length})),!0);function xa(t,e){if(t.state.readOnly)return!1;let i="delete.selection",{state:n}=t,r=n.changeByRange((n=>{let{from:r,to:s}=n;if(r==s){let n=e(r);nr&&(i="delete.forward",n=ka(t,n,!0)),r=Math.min(r,n),s=Math.max(s,n)}else r=ka(t,r,!1),s=ka(t,s,!0);return r==s?{range:n}:{changes:{from:r,to:s},range:R.cursor(r)}}));return!r.changes.empty&&(t.dispatch(n.update(r,{scrollIntoView:!0,userEvent:i,effects:"delete.selection"==i?Or.announce.of(n.phrase("Selection deleted")):void 0})),!0)}function ka(t,e,i){if(t instanceof Or)for(let n of t.state.facet(Or.atomicRanges).map((e=>e(t))))n.between(e,e,((t,n)=>{te&&(e=i?n:t)}));return e}const Sa=(t,e)=>xa(t,(i=>{let n,r,{state:s}=t,o=s.doc.lineAt(i);if(!e&&i>o.from&&iSa(t,!1),Ma=t=>Sa(t,!0),Ca=(t,e)=>xa(t,(i=>{let n=i,{state:r}=t,s=r.doc.lineAt(n),o=r.charCategorizer(n);for(let t=null;;){if(n==(e?s.to:s.from)){n==i&&s.number!=(e?r.doc.lines:1)&&(n+=e?1:-1);break}let l=d(s.text,n-s.from,e)+s.from,h=s.text.slice(Math.min(n,l)-s.from,Math.max(n,l)-s.from),a=o(h);if(null!=t&&a!=t)break;" "==h&&n==i||(t=a),n=l}return n})),Da=t=>Ca(t,!1),Oa=t=>xa(t,(e=>{let i=t.lineBlockAt(e).to;return e=r.number){let t=e[e.length-1];t.to=s.to,t.ranges.push(n)}else e.push({from:r.from,to:s.to,ranges:[n]});i=s.number+1}return e}function Ea(t,e,i){if(t.readOnly)return!1;let n=[],r=[];for(let e of Ta(t)){if(i?e.to==t.doc.length:0==e.from)continue;let s=t.doc.lineAt(i?e.to+1:e.from-1),o=s.length+1;if(i){n.push({from:e.to,to:s.to},{from:e.from,insert:s.text+t.lineBreak});for(let i of e.ranges)r.push(R.range(Math.min(t.doc.length,i.anchor+o),Math.min(t.doc.length,i.head+o)))}else{n.push({from:s.from,to:e.from},{from:e.to,insert:t.lineBreak+s.text});for(let t of e.ranges)r.push(R.range(t.anchor-o,t.head-o))}}return!!n.length&&(e(t.update({changes:n,scrollIntoView:!0,selection:R.create(r,t.selection.mainIndex),userEvent:"move.line"})),!0)}function Ba(t,e,i){if(t.readOnly)return!1;let n=[];for(let e of Ta(t))i?n.push({from:e.from,insert:t.doc.slice(e.from,e.to)+t.lineBreak}):n.push({from:e.to,insert:t.lineBreak+t.doc.slice(e.from,e.to)});return e(t.update({changes:n,scrollIntoView:!0,userEvent:"input.copyline"})),!0}const Ra=La(!1);function La(t){return({state:i,dispatch:n})=>{if(i.readOnly)return!1;let r=i.changeByRange((n=>{let{from:r,to:s}=n,o=i.doc.lineAt(r),l=!t&&r==s&&function(t,e){if(/\(\)|\[\]|\{\}/.test(t.sliceDoc(e-1,e+1)))return{from:e,to:e};let i,n=el(t).resolveInner(e),r=n.childBefore(e),s=n.childAfter(e);return r&&s&&r.to<=e&&s.from>=e&&(i=r.type.prop(Zs.closedBy))&&i.indexOf(s.name)>-1&&t.doc.lineAt(r.to).from==t.doc.lineAt(s.from).from?{from:r.to,to:s.from}:null}(i,r);t&&(r=s=(s<=o.to?o:i.doc.lineAt(s)).to);let h=new ml(i,{simulateBreak:r,simulateDoubleBreak:!!l}),a=gl(h,r);for(null==a&&(a=/^\s*/.exec(i.doc.lineAt(r).text)[0].length);so.from&&r{let r=[];for(let s=n.from;s<=n.to;){let o=t.doc.lineAt(s);o.number>i&&(n.empty||n.to>o.from)&&(e(o,r,n),i=o.number),s=o.to+1}let s=t.changes(r);return{changes:r,range:R.range(s.mapPos(n.anchor,1),s.mapPos(n.head,1))}}))}const Pa=[{key:"Alt-ArrowLeft",mac:"Ctrl-ArrowLeft",run:t=>_h(t,(e=>Yh(t.state,e,!$h(t)))),shift:t=>oa(t,(e=>Yh(t.state,e,!$h(t))))},{key:"Alt-ArrowRight",mac:"Ctrl-ArrowRight",run:t=>_h(t,(e=>Yh(t.state,e,$h(t)))),shift:t=>oa(t,(e=>Yh(t.state,e,$h(t))))},{key:"Alt-ArrowUp",run:({state:t,dispatch:e})=>Ea(t,e,!1)},{key:"Shift-Alt-ArrowUp",run:({state:t,dispatch:e})=>Ba(t,e,!1)},{key:"Alt-ArrowDown",run:({state:t,dispatch:e})=>Ea(t,e,!0)},{key:"Shift-Alt-ArrowDown",run:({state:t,dispatch:e})=>Ba(t,e,!0)},{key:"Escape",run:({state:t,dispatch:e})=>{let i=t.selection,n=null;return i.ranges.length>1?n=R.create([i.main]):i.main.empty||(n=R.create([R.cursor(i.main.head)])),!!n&&(e(qh(t,n)),!0)}},{key:"Mod-Enter",run:La(!0)},{key:"Alt-l",mac:"Ctrl-l",run:({state:t,dispatch:e})=>{let i=Ta(t).map((({from:e,to:i})=>R.range(e,Math.min(i+1,t.doc.length))));return e(t.update({selection:R.create(i),userEvent:"select"})),!0}},{key:"Mod-i",run:({state:t,dispatch:e})=>{let i=zh(t.selection,(e=>{var i;let n=el(t).resolveInner(e.head,1);for(;!(n.from=e.to||n.to>e.to&&n.from<=e.from)&&(null===(i=n.parent)||void 0===i?void 0:i.parent);)n=n.parent;return R.range(n.to,n.from)}));return e(qh(t,i)),!0},preventDefault:!0},{key:"Mod-[",run:({state:t,dispatch:e})=>!t.readOnly&&(e(t.update(Na(t,((e,i)=>{let n=/^\s*/.exec(e.text)[0];if(!n)return;let r=zt(n,t.tabSize),s=0,o=pl(t,Math.max(0,r-dl(t)));for(;s!t.readOnly&&(e(t.update(Na(t,((e,i)=>{i.push({from:e.from,insert:t.facet(fl)})})),{userEvent:"input.indent"})),!0)},{key:"Mod-Alt-\\",run:({state:t,dispatch:e})=>{if(t.readOnly)return!1;let i=Object.create(null),n=new ml(t,{overrideIndentation:t=>{let e=i[t];return null==e?-1:e}}),r=Na(t,((e,r,s)=>{let o=gl(n,e.from);if(null==o)return;/\S/.test(e.text)||(o=0);let l=/^\s*/.exec(e.text)[0],h=pl(t,o);(l!=h||s.from{if(t.state.readOnly)return!1;let{state:e}=t,i=e.changes(Ta(e).map((({from:t,to:i})=>(t>0?t--:it.moveVertically(e,!0))).map(i);return t.dispatch({changes:i,selection:n,scrollIntoView:!0,userEvent:"delete.line"}),!0}},{key:"Shift-Mod-\\",run:({state:t,dispatch:e})=>function(t,e,i){let n=!1,r=zh(t.selection,(e=>{let r=Fl(t,e.head,-1)||Fl(t,e.head,1)||e.head>0&&Fl(t,e.head-1,1)||e.head{let e=mh(t.state);return e.line?dh(t):!!e.block&&gh(t)}},{key:"Alt-A",run:ph}].concat([{key:"ArrowLeft",run:Gh,shift:ha,preventDefault:!0},{key:"Mod-ArrowLeft",mac:"Alt-ArrowLeft",run:t=>Jh(t,!$h(t)),shift:t=>ca(t,!$h(t)),preventDefault:!0},{mac:"Cmd-ArrowLeft",run:t=>_h(t,(e=>sa(t,e,!$h(t)))),shift:t=>oa(t,(e=>sa(t,e,!$h(t)))),preventDefault:!0},{key:"ArrowRight",run:Uh,shift:aa,preventDefault:!0},{key:"Mod-ArrowRight",mac:"Alt-ArrowRight",run:t=>Jh(t,$h(t)),shift:t=>ca(t,$h(t)),preventDefault:!0},{mac:"Cmd-ArrowRight",run:t=>_h(t,(e=>sa(t,e,$h(t)))),shift:t=>oa(t,(e=>sa(t,e,$h(t)))),preventDefault:!0},{key:"ArrowUp",run:Zh,shift:fa,preventDefault:!0},{mac:"Cmd-ArrowUp",run:va,shift:ya},{mac:"Ctrl-ArrowUp",run:na,shift:ga},{key:"ArrowDown",run:ta,shift:da,preventDefault:!0},{mac:"Cmd-ArrowDown",run:wa,shift:ba},{mac:"Ctrl-ArrowDown",run:ra,shift:ma},{key:"PageUp",run:na,shift:ga},{key:"PageDown",run:ra,shift:ma},{key:"Home",run:t=>_h(t,(e=>sa(t,e,!1))),shift:t=>oa(t,(e=>sa(t,e,!1))),preventDefault:!0},{key:"Mod-Home",run:va,shift:ya},{key:"End",run:t=>_h(t,(e=>sa(t,e,!0))),shift:t=>oa(t,(e=>sa(t,e,!0))),preventDefault:!0},{key:"Mod-End",run:wa,shift:ba},{key:"Enter",run:Ra},{key:"Mod-a",run:({state:t,dispatch:e})=>(e(t.update({selection:{anchor:0,head:t.doc.length},userEvent:"select"})),!0)},{key:"Backspace",run:Aa,shift:Aa},{key:"Delete",run:Ma},{key:"Mod-Backspace",mac:"Alt-Backspace",run:Da},{key:"Mod-Delete",mac:"Alt-Delete",run:t=>Ca(t,!0)},{mac:"Mod-Backspace",run:t=>xa(t,(e=>{let i=t.lineBlockAt(e).from;return e>i?i:Math.max(0,e-1)}))},{mac:"Mod-Delete",run:Oa}].concat([{key:"Ctrl-b",run:Gh,shift:ha,preventDefault:!0},{key:"Ctrl-f",run:Uh,shift:aa},{key:"Ctrl-p",run:Zh,shift:fa},{key:"Ctrl-n",run:ta,shift:da},{key:"Ctrl-a",run:t=>_h(t,(e=>R.cursor(t.lineBlockAt(e.head).from,1))),shift:t=>oa(t,(e=>R.cursor(t.lineBlockAt(e.head).from)))},{key:"Ctrl-e",run:t=>_h(t,(e=>R.cursor(t.lineBlockAt(e.head).to,-1))),shift:t=>oa(t,(e=>R.cursor(t.lineBlockAt(e.head).to)))},{key:"Ctrl-d",run:Ma},{key:"Ctrl-h",run:Aa},{key:"Ctrl-k",run:Oa},{key:"Ctrl-Alt-h",run:Da},{key:"Ctrl-o",run:({state:t,dispatch:i})=>{if(t.readOnly)return!1;let n=t.changeByRange((t=>({changes:{from:t.from,to:t.to,insert:e.of(["",""])},range:R.cursor(t.from)})));return i(t.update(n,{scrollIntoView:!0,userEvent:"input"})),!0}},{key:"Ctrl-t",run:({state:t,dispatch:e})=>{if(t.readOnly)return!1;let i=t.changeByRange((e=>{if(!e.empty||0==e.from||e.from==t.doc.length)return{range:e};let i=e.from,n=t.doc.lineAt(i),r=i==n.from?i-1:d(n.text,i-n.from,!1)+n.from,s=i==n.to?i+1:d(n.text,i-n.from,!0)+n.from;return{changes:{from:r,to:s,insert:t.doc.slice(i,s).append(t.doc.slice(r,i))},range:R.cursor(s)}}));return!i.changes.empty&&(e(t.update(i,{scrollIntoView:!0,userEvent:"move.character"})),!0)}},{key:"Ctrl-v",run:ra}].map((t=>({mac:t.key,run:t.run,shift:t.shift})))));function Ia(){var t=arguments[0];"string"==typeof t&&(t=document.createElement(t));var e=1,i=arguments[1];if(i&&"object"==typeof i&&null==i.nodeType&&!Array.isArray(i)){for(var n in i)if(Object.prototype.hasOwnProperty.call(i,n)){var r=i[n];"string"==typeof r?t.setAttribute(n,r):null!=r&&(t[n]=r)}e++}for(;et.normalize("NFKD"):t=>t;class Ha{constructor(t,e,i=0,n=t.length,r,s){this.test=s,this.value={from:0,to:0},this.done=!1,this.matches=[],this.buffer="",this.bufferPos=0,this.iter=t.iterRange(i,n),this.bufferStart=i,this.normalize=r?t=>r(Wa(t)):Wa,this.query=this.normalize(e)}peek(){if(this.bufferPos==this.buffer.length){if(this.bufferStart+=this.buffer.length,this.iter.next(),this.iter.done)return-1;this.bufferPos=0,this.buffer=this.iter.value}return w(this.buffer,this.bufferPos)}next(){for(;this.matches.length;)this.matches.pop();return this.nextOverlapping()}nextOverlapping(){for(;;){let t=this.peek();if(t<0)return this.done=!0,this;let e=y(t),i=this.bufferStart+this.bufferPos;this.bufferPos+=b(t);let n=this.normalize(e);for(let t=0,r=i;;t++){let s=n.charCodeAt(t),o=this.match(s,r);if(o)return this.value=o,this;if(t==n.length-1)break;r==i&&tthis.to&&(this.curLine=this.curLine.slice(0,this.to-this.curLineStart)),this.iter.next())}nextLine(){this.curLineStart=this.curLineStart+this.curLine.length+1,this.curLineStart>this.to?this.curLine="":this.getLine(0)}next(){for(let t=this.matchPos-this.curLineStart;;){this.re.lastIndex=t;let e=this.matchPos<=this.to&&this.re.exec(this.curLine);if(e){let i=this.curLineStart+e.index,n=i+e[0].length;if(this.matchPos=$a(this.text,n+(i==n?1:0)),i==this.curLineStart+this.curLine.length&&this.nextLine(),(ithis.value.to)&&(!this.test||this.test(i,n,e)))return this.value={from:i,to:n,match:e},this;t=this.matchPos-this.curLineStart}else{if(!(this.curLineStart+this.curLine.length=i||n.to<=e){let n=new Ka(e,t.sliceString(e,i));return _a.set(t,n),n}if(n.from==e&&n.to==i)return n;let{text:r,from:s}=n;return s>e&&(r=t.sliceString(e,s)+r,s=e),n.to=this.to?this.to:this.text.lineAt(t).to}next(){for(;;){let t=this.re.lastIndex=this.matchPos-this.flat.from,e=this.re.exec(this.flat.text);if(e&&!e[0]&&e.index==t&&(this.re.lastIndex=t+1,e=this.re.exec(this.flat.text)),e){let t=this.flat.from+e.index,i=t+e[0].length;if((this.flat.to>=this.to||e.index+e[0].length<=this.flat.text.length-10)&&(!this.test||this.test(t,i,e)))return this.value={from:t,to:i,match:e},this.matchPos=$a(this.text,i+(t==i?1:0)),this}if(this.flat.to==this.to)return this.done=!0,this;this.flat=Ka.get(this.text,this.flat.from,this.chunkEnd(this.flat.from+2*this.flat.text.length))}}}function $a(t,e){if(e>=t.length)return e;let i,n=t.lineAt(e);for(;e=56320&&i<57344;)e++;return e}"undefined"!=typeof Symbol&&(qa.prototype[Symbol.iterator]=ja.prototype[Symbol.iterator]=function(){return this});const Ga={highlightWordAroundCursor:!1,minSelectionLength:1,maxMatches:100,wholeWords:!1},Ua=P.define({combine:t=>At(t,Ga,{highlightWordAroundCursor:(t,e)=>t||e,minSelectionLength:Math.min,maxMatches:Math.min})});function Ja(t){let e=[tc,Za];return t&&e.push(Ua.of(t)),e}const Xa=ii.mark({class:"cm-selectionMatch"}),Ya=ii.mark({class:"cm-selectionMatch cm-selectionMatch-main"});function Qa(t,e,i,n){return!(0!=i&&t(e.sliceDoc(i-1,i))==yt.Word||n!=e.doc.length&&t(e.sliceDoc(n,n+1))==yt.Word)}const Za=Di.fromClass(class{constructor(t){this.decorations=this.getDeco(t)}update(t){(t.selectionSet||t.docChanged||t.viewportChanged)&&(this.decorations=this.getDeco(t.view))}getDeco(t){let e=t.state.facet(Ua),{state:i}=t,n=i.selection;if(n.ranges.length>1)return ii.none;let r,s=n.main,o=null;if(s.empty){if(!e.highlightWordAroundCursor)return ii.none;let t=i.wordAt(s.head);if(!t)return ii.none;o=i.charCategorizer(s.head),r=i.sliceDoc(t.from,t.to)}else{let t=s.to-s.from;if(t200)return ii.none;if(e.wholeWords){if(r=i.sliceDoc(s.from,s.to),o=i.charCategorizer(s.head),!Qa(o,i,s.from,s.to)||!function(t,e,i,n){return t(e.sliceDoc(i,i+1))==yt.Word&&t(e.sliceDoc(n-1,n))==yt.Word}(o,i,s.from,s.to))return ii.none}else if(r=i.sliceDoc(s.from,s.to).trim(),!r)return ii.none}let l=[];for(let n of t.visibleRanges){let t=new Ha(i.doc,r,n.from,n.to);for(;!t.next().done;){let{from:n,to:r}=t.value;if((!o||Qa(o,i,n,r))&&(s.empty&&n<=s.from&&r>=s.to?l.push(Ya.range(n,r)):(n>=s.to||r<=s.from)&&l.push(Xa.range(n,r)),l.length>e.maxMatches))return ii.none}}return ii.set(l)}},{decorations:t=>t.decorations}),tc=Or.baseTheme({".cm-selectionMatch":{backgroundColor:"#99ff7780"},".cm-searchMatch .cm-selectionMatch":{backgroundColor:"transparent"}}),ec=P.define({combine:t=>At(t,{top:!1,caseSensitive:!1,literal:!1,wholeWord:!1,createPanel:t=>new Cc(t)})});class ic{constructor(t){this.search=t.search,this.caseSensitive=!!t.caseSensitive,this.literal=!!t.literal,this.regexp=!!t.regexp,this.replace=t.replace||"",this.valid=!!this.search&&(!this.regexp||function(t){try{return new RegExp(t,za),!0}catch(t){return!1}}(this.search)),this.unquoted=this.unquote(this.search),this.wholeWord=!!t.wholeWord}unquote(t){return this.literal?t:t.replace(/\\([nrt\\])/g,((t,e)=>"n"==e?"\n":"r"==e?"\r":"t"==e?"\t":"\\"))}eq(t){return this.search==t.search&&this.replace==t.replace&&this.caseSensitive==t.caseSensitive&&this.regexp==t.regexp&&this.wholeWord==t.wholeWord}create(){return this.regexp?new ac(this):new sc(this)}getCursor(t,e=0,i){let n=t.doc?t:St.create({doc:t});return null==i&&(i=n.doc.length),this.regexp?oc(this,n,e,i):rc(this,n,e,i)}}class nc{constructor(t){this.spec=t}}function rc(t,e,i,n){return new Ha(e.doc,t.unquoted,i,n,t.caseSensitive?void 0:t=>t.toLowerCase(),t.wholeWord?function(t,e){return(i,n,r,s)=>((s>i||s+r.length=e)return null;n.push(i.value)}return n}highlight(t,e,i,n){let r=rc(this.spec,t,Math.max(0,e-this.spec.unquoted.length),Math.min(i+this.spec.unquoted.length,t.doc.length));for(;!r.next().done;)n(r.value.from,r.value.to)}}function oc(t,e,i,n){return new qa(e.doc,t.search,{ignoreCase:!t.caseSensitive,test:t.wholeWord?(r=e.charCategorizer(e.selection.main.head),(t,e,i)=>!i[0].length||(r(lc(i.input,i.index))!=yt.Word||r(hc(i.input,i.index))!=yt.Word)&&(r(hc(i.input,i.index+i[0].length))!=yt.Word||r(lc(i.input,i.index+i[0].length))!=yt.Word)):void 0},i,n);var r}function lc(t,e){return t.slice(d(t,e,!1),e)}function hc(t,e){return t.slice(e,d(t,e))}class ac extends nc{nextMatch(t,e,i){let n=oc(this.spec,t,i,t.doc.length).next();return n.done&&(n=oc(this.spec,t,0,e).next()),n.done?null:n.value}prevMatchInRange(t,e,i){for(let n=1;;n++){let r=Math.max(e,i-1e4*n),s=oc(this.spec,t,r,i),o=null;for(;!s.next().done;)o=s.value;if(o&&(r==e||o.from>r+10))return o;if(r==e)return null}}prevMatch(t,e,i){return this.prevMatchInRange(t,0,e)||this.prevMatchInRange(t,i,t.doc.length)}getReplacement(t){return this.spec.unquote(this.spec.replace.replace(/\$([$&\d+])/g,((e,i)=>"$"==i?"$":"&"==i?t.match[0]:"0"!=i&&+i=e)return null;n.push(i.value)}return n}highlight(t,e,i,n){let r=oc(this.spec,t,Math.max(0,e-250),Math.min(i+250,t.doc.length));for(;!r.next().done;)n(r.value.from,r.value.to)}}const cc=ut.define(),uc=ut.define(),fc=q.define({create:t=>new dc(Ac(t).create(),null),update(t,e){for(let i of e.effects)i.is(cc)?t=new dc(i.value.create(),t.panel):i.is(uc)&&(t=new dc(t.query,i.value?Sc:null));return t},provide:t=>Ts.from(t,(t=>t.panel))});class dc{constructor(t,e){this.query=t,this.panel=e}}const pc=ii.mark({class:"cm-searchMatch"}),gc=ii.mark({class:"cm-searchMatch cm-searchMatch-selected"}),mc=Di.fromClass(class{constructor(t){this.view=t,this.decorations=this.highlight(t.state.field(fc))}update(t){let e=t.state.field(fc);(e!=t.startState.field(fc)||t.docChanged||t.selectionSet||t.viewportChanged)&&(this.decorations=this.highlight(e))}highlight({query:t,panel:e}){if(!e||!t.spec.valid)return ii.none;let{view:i}=this,n=new Et;for(let e=0,r=i.visibleRanges,s=r.length;er[e+1].from-500;)l=r[++e].to;t.highlight(i.state,o,l,((t,e)=>{let r=i.state.selection.ranges.some((i=>i.from==t&&i.to==e));n.add(t,e,r?gc:pc)}))}return n.finish()}},{decorations:t=>t.decorations});function vc(t){return e=>{let i=e.state.field(fc,!1);return i&&i.query.spec.valid?t(e,i):Mc(e)}}const wc=vc(((t,{query:e})=>{let{to:i}=t.state.selection.main,n=e.nextMatch(t.state,i,i);return!!n&&(t.dispatch({selection:{anchor:n.from,head:n.to},scrollIntoView:!0,effects:Tc(t,n),userEvent:"select.search"}),!0)})),yc=vc(((t,{query:e})=>{let{state:i}=t,{from:n}=i.selection.main,r=e.prevMatch(i,n,n);return!!r&&(t.dispatch({selection:{anchor:r.from,head:r.to},scrollIntoView:!0,effects:Tc(t,r),userEvent:"select.search"}),!0)})),bc=vc(((t,{query:e})=>{let i=e.matchAll(t.state,1e3);return!(!i||!i.length)&&(t.dispatch({selection:R.create(i.map((t=>R.range(t.from,t.to)))),userEvent:"select.search.matches"}),!0)})),xc=vc(((t,{query:e})=>{let{state:i}=t,{from:n,to:r}=i.selection.main;if(i.readOnly)return!1;let s=e.nextMatch(i,n,n);if(!s)return!1;let o,l,h=[],a=[];if(s.from==n&&s.to==r&&(l=i.toText(e.getReplacement(s)),h.push({from:s.from,to:s.to,insert:l}),s=e.nextMatch(i,s.from,s.to),a.push(Or.announce.of(i.phrase("replaced match on line $",i.doc.lineAt(n).number)+"."))),s){let e=0==h.length||h[0].from>=s.to?0:s.to-s.from-l.length;o={anchor:s.from-e,head:s.to-e},a.push(Tc(t,s))}return t.dispatch({changes:h,selection:o,scrollIntoView:!!o,effects:a,userEvent:"input.replace"}),!0})),kc=vc(((t,{query:e})=>{if(t.state.readOnly)return!1;let i=e.matchAll(t.state,1e9).map((t=>{let{from:i,to:n}=t;return{from:i,to:n,insert:e.getReplacement(t)}}));if(!i.length)return!1;let n=t.state.phrase("replaced $ matches",i.length)+".";return t.dispatch({changes:i,effects:Or.announce.of(n),userEvent:"input.replace.all"}),!0}));function Sc(t){return t.state.facet(ec).createPanel(t)}function Ac(t,e){var i,n,r,s;let o=t.selection.main,l=o.empty||o.to>o.from+100?"":t.sliceDoc(o.from,o.to);if(e&&!l)return e;let h=t.facet(ec);return new ic({search:(null!==(i=null==e?void 0:e.literal)&&void 0!==i?i:h.literal)?l:l.replace(/\n/g,"\\n"),caseSensitive:null!==(n=null==e?void 0:e.caseSensitive)&&void 0!==n?n:h.caseSensitive,literal:null!==(r=null==e?void 0:e.literal)&&void 0!==r?r:h.literal,wholeWord:null!==(s=null==e?void 0:e.wholeWord)&&void 0!==s?s:h.wholeWord})}const Mc=t=>{let e=t.state.field(fc,!1);if(e&&e.panel){let i=Ms(t,Sc);if(!i)return!1;let n=i.dom.querySelector("[main-field]");if(n&&n!=t.root.activeElement){let i=Ac(t.state,e.query.spec);i.valid&&t.dispatch({effects:cc.of(i)}),n.focus(),n.select()}}else t.dispatch({effects:[uc.of(!0),e?cc.of(Ac(t.state,e.query.spec)):ut.appendConfig.of(Bc)]});return!0};class Cc{constructor(t){this.view=t;let e=this.query=t.state.field(fc).query.spec;function i(t,e,i){return Ia("button",{class:"cm-button",name:t,onclick:e,type:"button"},i)}this.commit=this.commit.bind(this),this.searchField=Ia("input",{value:e.search,placeholder:Dc(t,"Find"),"aria-label":Dc(t,"Find"),class:"cm-textfield",name:"search",form:"","main-field":"true",onchange:this.commit,onkeyup:this.commit}),this.replaceField=Ia("input",{value:e.replace,placeholder:Dc(t,"Replace"),"aria-label":Dc(t,"Replace"),class:"cm-textfield",name:"replace",form:"",onchange:this.commit,onkeyup:this.commit}),this.caseField=Ia("input",{type:"checkbox",name:"case",form:"",checked:e.caseSensitive,onchange:this.commit}),this.reField=Ia("input",{type:"checkbox",name:"re",form:"",checked:e.regexp,onchange:this.commit}),this.wordField=Ia("input",{type:"checkbox",name:"word",form:"",checked:e.wholeWord,onchange:this.commit}),this.dom=Ia("div",{onkeydown:t=>this.keydown(t),class:"cm-search"},[this.searchField,i("next",(()=>wc(t)),[Dc(t,"next")]),i("prev",(()=>yc(t)),[Dc(t,"previous")]),i("select",(()=>bc(t)),[Dc(t,"all")]),Ia("label",null,[this.caseField,Dc(t,"match case")]),Ia("label",null,[this.reField,Dc(t,"regexp")]),Ia("label",null,[this.wordField,Dc(t,"by word")]),...t.state.readOnly?[]:[Ia("br"),this.replaceField,i("replace",(()=>xc(t)),[Dc(t,"replace")]),i("replaceAll",(()=>kc(t)),[Dc(t,"replace all")])],Ia("button",{name:"close",onclick:()=>(t=>{let e=t.state.field(fc,!1);if(!e||!e.panel)return!1;let i=Ms(t,Sc);return i&&i.dom.contains(t.root.activeElement)&&t.focus(),t.dispatch({effects:uc.of(!1)}),!0})(t),"aria-label":Dc(t,"close"),type:"button"},["×"])])}commit(){let t=new ic({search:this.searchField.value,caseSensitive:this.caseField.checked,regexp:this.reField.checked,wholeWord:this.wordField.checked,replace:this.replaceField.value});t.eq(this.query)||(this.query=t,this.view.dispatch({effects:cc.of(t)}))}keydown(t){var e,i,n;e=this.view,i=t,n="search-panel",Hr(Vr(e.state),i,e,n)?t.preventDefault():13==t.keyCode&&t.target==this.searchField?(t.preventDefault(),(t.shiftKey?yc:wc)(this.view)):13==t.keyCode&&t.target==this.replaceField&&(t.preventDefault(),xc(this.view))}update(t){for(let e of t.transactions)for(let t of e.effects)t.is(cc)&&!t.value.eq(this.query)&&this.setQuery(t.value)}setQuery(t){this.query=t,this.searchField.value=t.search,this.replaceField.value=t.replace,this.caseField.checked=t.caseSensitive,this.reField.checked=t.regexp,this.wordField.checked=t.wholeWord}mount(){this.searchField.select()}get pos(){return 80}get top(){return this.view.state.facet(ec).top}}function Dc(t,e){return t.state.phrase(e)}const Oc=/[\s\.,:;?!]/;function Tc(t,{from:e,to:i}){let n=t.state.doc.lineAt(e),r=t.state.doc.lineAt(i).to,s=Math.max(n.from,e-30),o=Math.min(r,i+30),l=t.state.sliceDoc(s,o);if(s!=n.from)for(let t=0;t<30;t++)if(!Oc.test(l[t+1])&&Oc.test(l[t])){l=l.slice(t);break}if(o!=r)for(let t=l.length-1;t>l.length-30;t--)if(!Oc.test(l[t-1])&&Oc.test(l[t])){l=l.slice(0,t);break}return Or.announce.of(`${t.state.phrase("current match")}. ${l} ${t.state.phrase("on line")} ${n.number}.`)}const Ec=Or.baseTheme({".cm-panel.cm-search":{padding:"2px 6px 4px",position:"relative","& [name=close]":{position:"absolute",top:"0",right:"4px",backgroundColor:"inherit",border:"none",font:"inherit",padding:0,margin:0},"& input, & button, & label":{margin:".2em .6em .2em 0"},"& input[type=checkbox]":{marginRight:".2em"},"& label":{fontSize:"80%",whiteSpace:"pre"}},"&light .cm-searchMatch":{backgroundColor:"#ffff0054"},"&dark .cm-searchMatch":{backgroundColor:"#00ffff8a"},"&light .cm-searchMatch-selected":{backgroundColor:"#ff6a0054"},"&dark .cm-searchMatch-selected":{backgroundColor:"#ff00ff8a"}}),Bc=[fc,U.lowest(mc),Ec],Rc="#e06c75",Lc="#abb2bf",Nc="#7d8799",Pc="#d19a66",Ic="#2c313a",Vc="#282c34",Wc="#353a42",Hc="#528bff",Fc=[Or.theme({"&":{color:Lc,backgroundColor:Vc},".cm-content":{caretColor:Hc},".cm-cursor, .cm-dropCursor":{borderLeftColor:Hc},"&.cm-focused .cm-selectionBackground, .cm-selectionBackground, .cm-content ::selection":{backgroundColor:"#3E4451"},".cm-panels":{backgroundColor:"#21252b",color:Lc},".cm-panels.cm-panels-top":{borderBottom:"2px solid black"},".cm-panels.cm-panels-bottom":{borderTop:"2px solid black"},".cm-searchMatch":{backgroundColor:"#72a1ff59",outline:"1px solid #457dff"},".cm-searchMatch.cm-searchMatch-selected":{backgroundColor:"#6199ff2f"},".cm-activeLine":{backgroundColor:"#6699ff0b"},".cm-selectionMatch":{backgroundColor:"#aafe661a"},"&.cm-focused .cm-matchingBracket, &.cm-focused .cm-nonmatchingBracket":{backgroundColor:"#bad0f847",outline:"1px solid #515a6b"},".cm-gutters":{backgroundColor:Vc,color:Nc,border:"none"},".cm-activeLineGutter":{backgroundColor:Ic},".cm-foldPlaceholder":{backgroundColor:"transparent",border:"none",color:"#ddd"},".cm-tooltip":{border:"none",backgroundColor:Wc},".cm-tooltip .cm-tooltip-arrow:before":{borderTopColor:"transparent",borderBottomColor:"transparent"},".cm-tooltip .cm-tooltip-arrow:after":{borderTopColor:Wc,borderBottomColor:Wc},".cm-tooltip-autocomplete":{"& > ul > li[aria-selected]":{backgroundColor:Ic,color:Lc}}},{dark:!0}),Dl(Sl.define([{tag:Xo.keyword,color:"#c678dd"},{tag:[Xo.name,Xo.deleted,Xo.character,Xo.propertyName,Xo.macroName],color:Rc},{tag:[Xo.function(Xo.variableName),Xo.labelName],color:"#61afef"},{tag:[Xo.color,Xo.constant(Xo.name),Xo.standard(Xo.name)],color:Pc},{tag:[Xo.definition(Xo.name),Xo.separator],color:Lc},{tag:[Xo.typeName,Xo.className,Xo.number,Xo.changed,Xo.annotation,Xo.modifier,Xo.self,Xo.namespace],color:"#e5c07b"},{tag:[Xo.operator,Xo.operatorKeyword,Xo.url,Xo.escape,Xo.regexp,Xo.link,Xo.special(Xo.string)],color:"#56b6c2"},{tag:[Xo.meta,Xo.comment],color:Nc},{tag:Xo.strong,fontWeight:"bold"},{tag:Xo.emphasis,fontStyle:"italic"},{tag:Xo.strikethrough,textDecoration:"line-through"},{tag:Xo.link,color:Nc,textDecoration:"underline"},{tag:Xo.heading,fontWeight:"bold",color:Rc},{tag:[Xo.atom,Xo.bool,Xo.special(Xo.variableName)],color:Pc},{tag:[Xo.processingInstruction,Xo.string,Xo.inserted],color:"#98c379"},{tag:Xo.invalid,color:"#ffffff"}]))];return t.createEditorState=function(t,e={}){const i=[$s(),Js,cs(),Ah(),$r(),Wl(),[nh,Ql],bs(),Ss(),gs,Ja(),Pr.of([...rh,...Pa,...Fh])];return e.placeholder&&i.push(function(t){return Di.fromClass(class{constructor(e){this.view=e,this.placeholder=ii.set([ii.widget({widget:new ms(t),side:1}).range(0)])}get decorations(){return this.view.state.doc.length?ii.none:this.placeholder}},{decorations:t=>t.decorations})}(e.placeholder)),e.oneDark&&i.push(Fc),St.create({doc:t,extensions:i})},t.createEditorView=function(t,e){return new Or({state:t,parent:e})},t.openSearchPanel=Mc,t}({}); diff --git a/platform/mv3/extension/lib/codemirror/codemirror-ubol b/platform/mv3/extension/lib/codemirror/codemirror-ubol new file mode 160000 index 0000000000000..7067d0c2a9745 --- /dev/null +++ b/platform/mv3/extension/lib/codemirror/codemirror-ubol @@ -0,0 +1 @@ +Subproject commit 7067d0c2a9745bfddceedb46e1428a0b545954bd diff --git a/tools/make-mv3.sh b/tools/make-mv3.sh index e50f85a464d1e..8791487b83dcc 100755 --- a/tools/make-mv3.sh +++ b/tools/make-mv3.sh @@ -96,12 +96,18 @@ cp platform/mv3/extension/*.html "$UBOL_DIR"/ cp platform/mv3/extension/*.json "$UBOL_DIR"/ cp platform/mv3/extension/css/* "$UBOL_DIR"/css/ cp -R platform/mv3/extension/js/* "$UBOL_DIR"/js/ -cp -R platform/mv3/extension/lib "$UBOL_DIR"/ cp platform/mv3/"$PLATFORM"/ext-compat.js "$UBOL_DIR"/js/ 2>/dev/null || : cp platform/mv3/extension/img/* "$UBOL_DIR"/img/ cp -R platform/mv3/extension/_locales "$UBOL_DIR"/ cp platform/mv3/README.md "$UBOL_DIR/" +# Libraries +mkdir -p "$UBOL_DIR"/lib/codemirror +cp platform/mv3/extension/lib/codemirror/* \ + "$UBOL_DIR"/lib/codemirror/ 2>/dev/null || : +cp platform/mv3/extension/lib/codemirror/codemirror-ubol/dist/cm6.bundle.ubol.min.js \ + "$UBOL_DIR"/lib/codemirror/ + echo "*** uBOLite.mv3: Generating rulesets" UBOL_BUILD_DIR=$(mktemp -d) mkdir -p "$UBOL_BUILD_DIR" From c44f043ed3986f237f475c95732cb7d129f6587d Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Thu, 22 May 2025 08:39:29 -0400 Subject: [PATCH 0952/1099] Counter CodeMirror's `pointer-events: none` on scrollbars Related issue: https://github.com/uBlockOrigin/uBlock-issues/issues/3645 --- src/css/codemirror.css | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/css/codemirror.css b/src/css/codemirror.css index d1ca9fa1008de..d56d8c0da1a13 100644 --- a/src/css/codemirror.css +++ b/src/css/codemirror.css @@ -356,3 +356,9 @@ html:not(.mobile) .cm-search-widget .fa-icon:not(.fa-icon-ro):hover { .CodeMirror-lintmarker[data-error="y"] > span:hover { display: initial; } + +/* https://github.com/uBlockOrigin/uBlock-issues/issues/3645 */ +.CodeMirror-vscrollbar { + pointer-events: initial !important; + } + From 408b538e75b7937204fb900ccc260067c2a95bb4 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Thu, 22 May 2025 11:46:08 -0400 Subject: [PATCH 0953/1099] [mv3] Add support to convert `header=` option to DNR rules Related issue: https://github.com/uBlockOrigin/uBOL-home/issues/157 The `header=` option will be converted into DNR's `responseHeaders` condition. There will be an attempt to convert regex-based values into DNR- compatible syntax. Not all regex-based patterns can be converted to use DNR's patterns with `*` and `?` special characters. The implementation of `header=` option in uBO has been revisited to improve compatibility with DNR syntax to minimize burden for list maintainers when creating `header=` filters compatible with both uBO and uBOL. The changes: - Header names are now case-insensitive by default - Occurrences of `*` in non-regex-based header values now mean "matches any number of characters" - Occurrences of `?` in non-regex-based header values now mean "matches zero or one character" At time of commit, and as per MDN, only Chromium-based browsers currently support filtering on repsonse headers: https://developer.mozilla.org/docs/Mozilla/Add-ons/WebExtensions/API/declarativeNetRequest/HeaderInfo Also as per MDN, Chromium 121-127 silently ignore the `responseHeaders` condition, potentially causing undue blocking of network requests. Currently uBOL support Chromium 122 and later, meaning we need to mind potential false positives in Chromium 122-127 for filters using `header=` option. --- platform/mv3/make-rulesets.js | 4 +++ src/js/regex-analyzer.js | 54 +++++++++++++++++++++++++++++++ src/js/static-filtering-parser.js | 43 +++++++++++++++++------- src/js/static-net-filtering.js | 41 +++++++++++++++++++---- 4 files changed, 125 insertions(+), 17 deletions(-) diff --git a/platform/mv3/make-rulesets.js b/platform/mv3/make-rulesets.js index 9883db6a2dd23..8a9b0c7124517 100644 --- a/platform/mv3/make-rulesets.js +++ b/platform/mv3/make-rulesets.js @@ -333,6 +333,10 @@ function patchRuleset(ruleset) { log(`Safari's incomplete API: ${JSON.stringify(rule)}`, true); continue; } + if ( Array.isArray(rule.condition.responseHeaders) ) { + log(`Safari's incomplete API: ${JSON.stringify(rule)}`, true); + continue; + } if ( Array.isArray(condition.requestMethods) ) { log(`Safari's incomplete API: ${JSON.stringify(rule)}`, true); continue; diff --git a/src/js/regex-analyzer.js b/src/js/regex-analyzer.js index d41915ba7d272..0a885047f934d 100644 --- a/src/js/regex-analyzer.js +++ b/src/js/regex-analyzer.js @@ -61,6 +61,8 @@ function _isRE2(node) { return true; } +/******************************************************************************/ + function _literalStrFromRegex(reStr) { if ( RegexAnalyzer === null ) { return ''; } let s = ''; @@ -200,3 +202,55 @@ function tokenizableStrFromNode(node) { } return '\x01'; } + +/******************************************************************************/ + +export function toHeaderPattern(reStr) { + if ( RegexAnalyzer === null ) { return; } + try { + return _toHeaderPattern(RegexAnalyzer(reStr, false).tree()); + } catch { + } +} + +function _toHeaderPattern(branch, depth = 0) { + switch ( branch.type ) { + case 1: /* T_SEQUENCE, 'Sequence' */ { + let s = ''; + for ( const node of branch.val ) { + const t = _toHeaderPattern(node, depth+1); + if ( t === undefined ) { return; } + s += t; + } + if ( depth === 0 && branch.val.length !== 0 ) { + const first = branch.val[0]; + if ( first.type !== 128 || first.val !== '^' ) { s = `*${s}`; } + const last = branch.val.at(-1); + if ( last.type !== 128 || last.val !== '$' ) { s = `${s}*`; } + } + return s; + } + case 4: /* T_GROUP, 'Group' */ { + if ( + branch.flags.NegativeLookAhead === 1 || + branch.flags.NegativeLookBehind === 1 + ) { + return; + } + return _toHeaderPattern(branch.val, depth+1); + } + case 64: /* T_HEXCHAR, 'HexChar' */ + return branch.flags.Char; + case 128: /* T_SPECIAL, 'Special' */ { + if ( branch.val === '^' ) { return ''; } + if ( branch.val === '$' ) { return ''; } + return; + } + case 1024: /* T_STRING, 'String' */ + return branch.val; + case 2048: /* T_COMMENT, 'Comment' */ + return ''; + default: + break; + } +} diff --git a/src/js/static-filtering-parser.js b/src/js/static-filtering-parser.js index b9b54ccb73d95..f9dca000054dc 100644 --- a/src/js/static-filtering-parser.js +++ b/src/js/static-filtering-parser.js @@ -630,6 +630,9 @@ const exCharCodeAt = (s, i) => { return pos >= 0 ? s.charCodeAt(pos) : -1; }; +const escapeForRegex = s => + s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); + /******************************************************************************/ class AstWalker { @@ -3024,25 +3027,44 @@ export function parseHeaderValue(arg) { const out = { }; let pos = s.indexOf(':'); if ( pos === -1 ) { pos = s.length; } - out.name = s.slice(0, pos); + out.name = s.slice(0, pos).toLowerCase(); out.bad = out.name === ''; s = s.slice(pos + 1); out.not = s.charCodeAt(0) === 0x7E /* '~' */; if ( out.not ) { s = s.slice(1); } out.value = s; + if ( s === '' ) { return out; } const match = /^\/(.+)\/(i)?$/.exec(s); - if ( match !== null ) { - try { - out.re = new RegExp(match[1], match[2] || ''); - } - catch { - out.bad = true; - } + out.isRegex = match !== null; + if ( out.isRegex ) { + out.reStr = match[1]; + out.reFlags = match[2] || ''; + try { new RegExp(out.reStr, out.reFlags); } + catch { out.bad = true; } + return out; + } + out.reFlags = 'i'; + if ( /[*?]/.test(s) === false ) { + out.reStr = escapeForRegex(s); + return out; } + const reConstruct = /(?+~]\s*)(?:[A-Za-z_][\w-]*(?:[.#][A-Za-z_][\w-]*)*(?:\[[A-Za-z_][\w-]*(?:[*^$]?="[^"\]\\]+")?\])*|[.#][A-Za-z_][\w-]*(?:[.#][A-Za-z_][\w-]*)*(?:\[[A-Za-z_][\w-]*(?:[*^$]?="[^"\]\\]+")?\])*|\[[A-Za-z_][\w-]*(?:[*^$]?="[^"\]\\]+")?\](?:\[[A-Za-z_][\w-]*(?:[*^$]?="[^"\]\\]+")?\])*))*$/ this.reEatBackslashes = /\\([()])/g; - this.reEscapeRegex = /[.*+?^${}()|[\]\\]/g; // https://developer.mozilla.org/en-US/docs/Web/CSS/Pseudo-classes this.knownPseudoClasses = new Set([ 'active', 'any-link', 'autofill', @@ -4043,7 +4064,7 @@ class ExtSelectorCompiler { regexDetails = [ regexDetails, match[2] ]; } } else { - regexDetails = '^' + value.replace(this.reEscapeRegex, '\\$&') + '$'; + regexDetails = `^${escapeForRegex(value)}$`; } return { name, pseudo, value: regexDetails }; } diff --git a/src/js/static-net-filtering.js b/src/js/static-net-filtering.js index 68364c80c524b..65eabf94c9ed1 100644 --- a/src/js/static-net-filtering.js +++ b/src/js/static-net-filtering.js @@ -23,7 +23,7 @@ import * as sfp from './static-filtering-parser.js'; import { domainFromHostname, hostnameFromNetworkURL } from './uri-utils.js'; import { dropTask, queueTask } from './tasks.js'; -import { isRE2, tokenizableStrFromRegex } from './regex-analyzer.js'; +import { isRE2, toHeaderPattern, tokenizableStrFromRegex } from './regex-analyzer.js'; import BidiTrieContainer from './biditrie.js'; import { CompiledListReader } from './static-filtering-io.js'; @@ -2929,18 +2929,26 @@ class FilterOnHeaders { if ( refs.$parsed === null ) { refs.$parsed = sfp.parseHeaderValue(refs.headerOpt); } - const { bad, name, not, re, value } = refs.$parsed; + const { bad, name, not, value } = refs.$parsed; if ( bad ) { return false; } const headerValue = $httpHeaders.lookup(name); if ( headerValue === undefined ) { return false; } if ( value === '' ) { return true; } - return re === undefined - ? (headerValue === value) !== not - : re.test(headerValue) !== not; + let { re } = refs.$parsed; + if ( re === undefined ) { + re = new RegExp(refs.$parsed.reStr, refs.$parsed.reFlags); + refs.$parsed.re = re; + } + return re.test(headerValue) !== not; } static compile(details) { - return [ FilterOnHeaders.fid, details.optionValues.get('header') ]; + const parsed = sfp.parseHeaderValue(details.optionValues.get('header')); + let normalized = parsed.name; + if ( parsed.value !== '' ) { + normalized += `:${parsed.value}`; + } + return [ FilterOnHeaders.fid, normalized ]; } static fromCompiled(args) { @@ -2954,6 +2962,27 @@ class FilterOnHeaders { } static dnrFromCompiled(args, rule) { + rule.condition ||= {}; + const parsed = sfp.parseHeaderValue(args[1]); + if ( parsed.bad !== true ) { + const value = parsed.isRegex + ? toHeaderPattern(parsed.reStr) + : parsed.value; + if ( value !== undefined ) { + const prop = parsed.not + ? 'excludedResponseHeaders' + : 'responseHeaders'; + rule.condition[prop] ||= []; + const details = { + header: parsed.name, + }; + if ( value !== '' ) { + details.values = [ value ]; + } + rule.condition[prop].push(details); + return; + } + } dnrAddRuleError(rule, `header="${args[1]}" not supported`); } From 4eae23065e71ddbbd7b2be301b301c1e713f965e Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Thu, 22 May 2025 13:19:02 -0400 Subject: [PATCH 0954/1099] [mv3] Allow `body` element to vertically scroll Related issue: https://github.com/uBlockOrigin/uBOL-home/issues/352 --- platform/mv3/extension/css/dashboard-common.css | 1 - platform/mv3/extension/css/dashboard.css | 4 +++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/platform/mv3/extension/css/dashboard-common.css b/platform/mv3/extension/css/dashboard-common.css index 7abaeb5664dd0..8621d08704053 100644 --- a/platform/mv3/extension/css/dashboard-common.css +++ b/platform/mv3/extension/css/dashboard-common.css @@ -3,7 +3,6 @@ body { box-sizing: border-box; display: flex; flex-direction: column; - max-height: 100vh; padding: 0 var(--default-gap-xxsmall); } body > * { diff --git a/platform/mv3/extension/css/dashboard.css b/platform/mv3/extension/css/dashboard.css index b7aa5adeee110..63aaeaa1559ae 100644 --- a/platform/mv3/extension/css/dashboard.css +++ b/platform/mv3/extension/css/dashboard.css @@ -7,6 +7,9 @@ flex-wrap: wrap; overflow-x: hidden; padding: 0; + position: sticky; + top: 0; + z-index: 100; } .tabButton { background-color: transparent; @@ -40,7 +43,6 @@ body[data-pane="about"] #dashboard-nav .tabButton[data-pane="about"] { body > section { display: none; - overflow: auto; padding-bottom: 8rem; } body[data-pane="settings"] > section[data-pane="settings"], From c5d1eb109e58ab80b6952e310369df183a04ffef Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Thu, 22 May 2025 14:51:19 -0400 Subject: [PATCH 0955/1099] Update changelog --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 64d22d85587f1..910a84f942565 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,5 @@ +- [Make `header=` syntax compatible with DNR rules](https://github.com/gorhill/uBlock/commit/408b538e75) +- [Counter CodeMirror's `pointer-events: none` on scrollbars](https://github.com/gorhill/uBlock/commit/c44f043ed3) - [Fix element picker issue with explicit dark theme](https://github.com/gorhill/uBlock/commit/0130fdf4a1) ---------- From ca4430c0ba94ba28bce131fa70853c46a562d5dd Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Thu, 22 May 2025 14:52:59 -0400 Subject: [PATCH 0956/1099] New revision for dev build --- dist/version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dist/version b/dist/version index fdfd21d01237e..ba5694df5f89a 100644 --- a/dist/version +++ b/dist/version @@ -1 +1 @@ -1.64.1.0 \ No newline at end of file +1.64.1.1 \ No newline at end of file From 69d584a28343c45899b911491fc7a1806d7881f6 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Thu, 22 May 2025 15:06:34 -0400 Subject: [PATCH 0957/1099] Make Firefox dev build auto-update --- dist/firefox/updates.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dist/firefox/updates.json b/dist/firefox/updates.json index 2842869bfa81e..b25fb5dce1d90 100644 --- a/dist/firefox/updates.json +++ b/dist/firefox/updates.json @@ -3,9 +3,9 @@ "uBlock0@raymondhill.net": { "updates": [ { - "version": "1.64.1.0", + "version": "1.64.1.1", "browser_specific_settings": { "gecko": { "strict_min_version": "92.0" } }, - "update_link": "https://github.com/gorhill/uBlock/releases/download/1.64.1b0/uBlock0_1.64.1b0.firefox.signed.xpi" + "update_link": "https://github.com/gorhill/uBlock/releases/download/1.64.1b1/uBlock0_1.64.1b1.firefox.signed.xpi" } ] } From ecc64ae125e77cdcbd48c0e09d180fd49b0b2d64 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Thu, 22 May 2025 16:53:11 -0400 Subject: [PATCH 0958/1099] [mv3] Give admins ability to prevent usage of zapper Related issue: https://github.com/uBlockOrigin/uBOL-home/pull/341 --- platform/mv3/extension/css/popup.css | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/platform/mv3/extension/css/popup.css b/platform/mv3/extension/css/popup.css index 1afea78d31c63..411ba3aa40889 100644 --- a/platform/mv3/extension/css/popup.css +++ b/platform/mv3/extension/css/popup.css @@ -68,8 +68,9 @@ hr { body[data-forbid~="filteringMode"] .filteringModeSlider { pointer-events: none; } -body[data-forbid~="dashboard"] #gotoDashboard { - display: none; +body[data-forbid~="dashboard"] #gotoDashboard, +body[data-forbid~="zapper"] #gotoZapper { + visibility: hidden; } #filteringModeText { From 13886f728d1c8d9f509a74c69f3f09b8609353ab Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Fri, 23 May 2025 08:28:04 -0400 Subject: [PATCH 0959/1099] [mv3] Fix tag --- platform/mv3/extension/report.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform/mv3/extension/report.html b/platform/mv3/extension/report.html index 5ebabe3baf66c..8632097f7619b 100644 --- a/platform/mv3/extension/report.html +++ b/platform/mv3/extension/report.html @@ -11,7 +11,7 @@ - + From 2076d4223994aa5f9b8be3bf65f13e91eb932d98 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Fri, 23 May 2025 09:36:21 -0400 Subject: [PATCH 0960/1099] [mv3] Add to troubleshooting info whether webext API calls failed Related discussion: https://github.com/uBlockOrigin/uBOL-home/issues/327#issuecomment-2904240310 --- platform/mv3/extension/js/report.js | 24 ++++++++++++++++--- .../mv3/extension/js/scripting-manager.js | 23 ++++++++++++++---- 2 files changed, 39 insertions(+), 8 deletions(-) diff --git a/platform/mv3/extension/js/report.js b/platform/mv3/extension/js/report.js index f3072c1a4b316..e31914bc4d697 100644 --- a/platform/mv3/extension/js/report.js +++ b/platform/mv3/extension/js/report.js @@ -19,10 +19,18 @@ Home: https://github.com/gorhill/uBlock */ -import { dom, qs$ } from './dom.js'; +import { + dom, + qs$, +} from './dom.js'; + +import { + localRead, + runtime, + sendMessage, +} from './ext.js'; + import { dnr } from './ext-compat.js'; -import { runtime } from './ext.js'; -import { sendMessage } from './ext.js'; /******************************************************************************/ @@ -98,10 +106,14 @@ async function getConfigData() { platformInfo, rulesets, defaultMode, + registerContentScriptsReason, + unregisterContentScriptsReason, ] = await Promise.all([ runtime.getPlatformInfo(), dnr.getEnabledRulesets(), sendMessage({ what: 'getDefaultFilteringMode' }), + localRead('$scripting.registerContentScripts'), + localRead('$scripting.unregisterContentScripts'), ]); const browser = (( ) => { const extURL = runtime.getURL(''); @@ -138,6 +150,12 @@ async function getConfigData() { }, rulesets, }; + if ( registerContentScriptsReason !== undefined ) { + config.registerContentScripts = registerContentScriptsReason; + } + if ( unregisterContentScriptsReason !== undefined ) { + config.unregisterContentScripts = unregisterContentScriptsReason; + } return renderData(config); } diff --git a/platform/mv3/extension/js/scripting-manager.js b/platform/mv3/extension/js/scripting-manager.js index a400ce856325b..810d0cae0b173 100644 --- a/platform/mv3/extension/js/scripting-manager.js +++ b/platform/mv3/extension/js/scripting-manager.js @@ -21,7 +21,12 @@ import * as ut from './utils.js'; -import { browser } from './ext.js'; +import { + browser, + localRemove, + localWrite, +} from './ext.js'; + import { fetchJSON } from './fetch.js'; import { getEnabledRulesetsDetails } from './ruleset-manager.js'; import { getFilteringModeDetails } from './mode-manager.js'; @@ -607,14 +612,22 @@ async function registerInjectables() { if ( toRemove.length !== 0 ) { ubolLog(`Unregistered ${toRemove} content (css/js)`); - await browser.scripting.unregisterContentScripts({ ids: toRemove }) - .catch(reason => { console.info(reason); }); + await browser.scripting.unregisterContentScripts({ ids: toRemove }).then(( ) => { + localRemove('$scripting.unregisterContentScripts'); + }).catch(reason => { + localWrite('$scripting.unregisterContentScripts', reason); + console.info(reason); + }); } if ( toAdd.length !== 0 ) { ubolLog(`Registered ${toAdd.map(v => v.id)} content (css/js)`); - await browser.scripting.registerContentScripts(toAdd) - .catch(reason => { console.info(reason); }); + await browser.scripting.registerContentScripts(toAdd).then(( ) => { + localRemove('$scripting.registerContentScripts'); + }).catch(reason => { + localWrite('$scripting.registerContentScripts', reason); + console.info(reason); + }); } registerInjectables.barrier = false; From 93d8e639ce91b633cd585b0e031ec52cd77413bc Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Fri, 23 May 2025 09:49:07 -0400 Subject: [PATCH 0961/1099] [mv3] Code review last commit Related commit: https://github.com/gorhill/uBlock/commit/2076d4223994aa5f9b8be3bf65f13e91eb932d98 --- platform/mv3/extension/js/scripting-manager.js | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/platform/mv3/extension/js/scripting-manager.js b/platform/mv3/extension/js/scripting-manager.js index 810d0cae0b173..4b0b0d59223c6 100644 --- a/platform/mv3/extension/js/scripting-manager.js +++ b/platform/mv3/extension/js/scripting-manager.js @@ -612,22 +612,24 @@ async function registerInjectables() { if ( toRemove.length !== 0 ) { ubolLog(`Unregistered ${toRemove} content (css/js)`); - await browser.scripting.unregisterContentScripts({ ids: toRemove }).then(( ) => { + try { + await browser.scripting.unregisterContentScripts({ ids: toRemove }); localRemove('$scripting.unregisterContentScripts'); - }).catch(reason => { + } catch(reason) { localWrite('$scripting.unregisterContentScripts', reason); console.info(reason); - }); + } } if ( toAdd.length !== 0 ) { ubolLog(`Registered ${toAdd.map(v => v.id)} content (css/js)`); - await browser.scripting.registerContentScripts(toAdd).then(( ) => { + try { + await browser.scripting.registerContentScripts(toAdd); localRemove('$scripting.registerContentScripts'); - }).catch(reason => { + } catch(reason) { localWrite('$scripting.registerContentScripts', reason); console.info(reason); - }); + } } registerInjectables.barrier = false; From e077d36c6e55ba1700ce43b79fe8e90ee56f95a9 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sun, 25 May 2025 18:17:20 -0400 Subject: [PATCH 0962/1099] Import translation work from https://crowdin.com/project/ublock --- platform/mv3/description/webstore.si.txt | 16 +- platform/mv3/description/webstore.sq.txt | 2 +- .../mv3/extension/_locales/si/messages.json | 54 ++-- .../mv3/extension/_locales/sq/messages.json | 2 +- src/_locales/si/messages.json | 288 +++++++++--------- 5 files changed, 181 insertions(+), 181 deletions(-) diff --git a/platform/mv3/description/webstore.si.txt b/platform/mv3/description/webstore.si.txt index ef089202b9565..939dc39c17357 100644 --- a/platform/mv3/description/webstore.si.txt +++ b/platform/mv3/description/webstore.si.txt @@ -1,12 +1,12 @@ -uBO Lite (uBOL) is an MV3-based content blocker. +uBO Lite (uBOL) යනු MV3-පාදක අන්තර්ගත අවහිර කරන්නා වේ. -The default ruleset corresponds to uBlock Origin's default filterset: +පෙරනිමි රීති කට්ටලය uBlock Origin හි පෙරනිමි පෙරහන් කට්ටලයට අනුරූප වේ: -- uBlock Origin's built-in filter lists -- EasyList -- EasyPrivacy -- Peter Lowe’s Ad and tracking server list +- uBlock Origin හි බිල්ට් පෙරහන් ලැයිස්තු +- පහසු ලැයිස්තුව +- පහසු පෞද්ගලිකත්වය +- පීටර් ලෝගේ දැන්වීම් සහ ලුහුබැඳීමේ සේවාදායක ලැයිස්තුව -You can enable more rulesets by visiting the options page -- click the _Cogs_ icon in the popup panel. +විකල්ප පිටුවට පිවිසීමෙන් ඔබට තවත් නීති කට්ටල සක්‍රීය කළ හැකිය -- උත්පතන පැනලයේ _Cogs_ නිරූපකය ක්ලික් කරන්න. -uBOL is entirely declarative, meaning there is no need for a permanent uBOL process for the filtering to occur, and CSS/JS injection-based content filtering is performed reliably by the browser itself rather than by the extension. This means that uBOL itself does not consume CPU/memory resources while content blocking is ongoing -- uBOL's service worker process is required _only_ when you interact with the popup panel or the option pages. +uBOL සම්පූර්ණයෙන්ම ප්‍රකාශනාත්මකයි, එනම් පෙරීම සිදුවීමට ස්ථිර uBOL ක්‍රියාවලියක් අවශ්‍ය නොවන අතර, CSS/JS එන්නත්-පාදක අන්තර්ගත පෙරීම දිගුව මගින් නොව බ්‍රවුසරය විසින්ම විශ්වාසදායක ලෙස සිදු කරයි. මෙයින් අදහස් කරන්නේ අන්තර්ගත අවහිර කිරීම සිදුවෙමින් පවතින අතරතුර uBOL විසින්ම CPU/මතක සම්පත් පරිභෝජනය නොකරන බවයි -- ඔබ උත්පතන පැනලය හෝ විකල්ප පිටු සමඟ අන්තර් ක්‍රියා කරන විට uBOL හි සේවා සේවක ක්‍රියාවලිය _only_ අවශ්‍ය වේ. diff --git a/platform/mv3/description/webstore.sq.txt b/platform/mv3/description/webstore.sq.txt index 7f706a0edf785..45ed3c8e9ff38 100644 --- a/platform/mv3/description/webstore.sq.txt +++ b/platform/mv3/description/webstore.sq.txt @@ -1,4 +1,4 @@ -uBO Lite (uBOL) është një bllokues *i pavarur* që funksionon sipas modelit MV3. +uBO Lite (uBOL) është një bllokues materialesh sipas modelit MV3. Rregullat e tij janë të barasvlershme me filtrat standardë që përdor uBlock Origin: diff --git a/platform/mv3/extension/_locales/si/messages.json b/platform/mv3/extension/_locales/si/messages.json index 1fc0e9cfd5645..841ea098c3498 100644 --- a/platform/mv3/extension/_locales/si/messages.json +++ b/platform/mv3/extension/_locales/si/messages.json @@ -108,15 +108,15 @@ "description": "Header of 'Report a filter issue' section in Support pane" }, "supportS3P1": { - "message": "Report filter issues with specific websites to the uBlockOrigin/uAssets issue tracker. Requires a GitHub account.", + "message": "නිශ්චිත වෙබ් අඩවි සමඟ පෙරහන් ගැටළු uBlockOrigin/uAssets ගැටළු ට්රැකර්වෙත වාර්තා කරන්න. GitHub ගිණුමක් අවශ්‍යයි.", "description": "First paragraph of 'Filter issues' section in Support pane" }, "supportS5H": { - "message": "Troubleshooting information", + "message": "දෝශ නිරාකරණ තොරතුරු", "description": "Label of 'Troubleshooting information' section in 'Report a filter issue' page" }, "supportS6P1S1": { - "message": "To avoid burdening volunteers with duplicate reports, please verify that the issue has not already been reported. Note: clicking the button will cause the page's origin to be sent to GitHub.", + "message": "අනුපිටපත් වාර්තා සමඟ ස්වේච්ඡා සේවකයින්ට බරක් වීම වළක්වා ගැනීම සඳහා, කරුණාකර ගැටළුව දැනටමත් වාර්තා කර නොමැති බව තහවුරු කරගන්න. සටහන: බොත්තම ක්ලික් කිරීමෙන් පිටුවේ මූලාරම්භය GitHub වෙත යවනු ලැබේ.", "description": "A paragraph in the filter issue reporter section" }, "supportFindSpecificButton": { @@ -136,11 +136,11 @@ "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option1": { - "message": "Shows ads or ad leftovers", + "message": "දැන්වීම් හෝ දැන්වීම් ඉතිරි කොටස් පෙන්වයි", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option2": { - "message": "Has overlays or other nuisances", + "message": "උඩැතිරි හෝ වෙනත් කරදර ඇති", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option3": { @@ -148,11 +148,11 @@ "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option4": { - "message": "Has privacy-related issues", + "message": "පෞද්ගලිකත්‍වය ආශ්‍රිත ගැටළු තිබේ", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option5": { - "message": "Malfunctions when uBO Lite is enabled", + "message": "uBO Lite සක්‍රීය කර ඇති විට සිදුවන අක්‍රමිකතා", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option6": { @@ -160,11 +160,11 @@ "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option7": { - "message": "Leads to badware, phishing", + "message": "නරක මෘදුකාංග, තතුබෑම් වලට මග පාදයි", "description": "An entry in the widget used to select the type of issue" }, "supportS6Checkbox1": { - "message": "Label the webpage as “NSFW” (“Not Safe For Work”)", + "message": "වෙබ් පිටුව “NSFW” ලෙස ලේබල් කරන්න (“වැඩ සඳහා ආරක්ෂිත නොවේ”)", "description": "A checkbox to use for NSFW sites" }, "supportReportSpecificButton": { @@ -176,7 +176,7 @@ "description": "The header text for the default filtering mode section" }, "defaultFilteringModeDescription": { - "message": "The default filtering mode will be overridden by per-website filtering modes. You can adjust the filtering mode on any given website according to whichever mode works best on that website. Each mode has its advantages and disadvantages.", + "message": "පෙරනිමි පෙරහන් මාදිලිය එක් එක් වෙබ් අඩවියට පෙරහන් මාදිලි මගින් අභිබවා යනු ඇත. ඔබට ඕනෑම වෙබ් අඩවියක පෙරහන් මාදිලිය එම වෙබ් අඩවියේ වඩාත් හොඳින් ක්‍රියාත්මක වන මාදිලිය අනුව සකස් කළ හැකිය. සෑම මාදිලියකටම එහි වාසි සහ අවාසි ඇත.", "description": "This describes the default filtering mode setting" }, "filteringMode0Name": { @@ -196,15 +196,15 @@ "description": "Name of blocking mode 3" }, "basicFilteringModeDescription": { - "message": "Basic network filtering from selected filter lists.\n\nDoes not require permission to read and modify data on websites.", + "message": "තෝරාගත් පෙරහන් ලැයිස්තු වලින් මූලික ජාල පෙරහන.\n\nවෙබ් අඩවි වල දත්ත කියවීමට සහ වෙනස් කිරීමට අවසර අවශ්‍ය නොවේ.", "description": "This describes the 'basic' filtering mode" }, "optimalFilteringModeDescription": { - "message": "Advanced network filtering plus specific extended filtering from selected filter lists.\n\nRequires broad permission to read and modify data on all websites.", + "message": "තෝරාගත් පෙරහන් ලැයිස්තු වලින් උසස් ජාල පෙරහන් සහ නිශ්චිත දිගු පෙරහන්.\n\nසියලුම වෙබ් අඩවි වල දත්ත කියවීමට සහ වෙනස් කිරීමට පුළුල් අවසරයක් අවශ්‍ය වේ.", "description": "This describes the 'optimal' filtering mode" }, "completeFilteringModeDescription": { - "message": "Advanced network filtering plus specific and generic extended filtering from selected filter lists.\n\nRequires broad permission to read and modify data on all websites.\n\nGeneric extended filtering may cause higher webpage resources usage.", + "message": "තෝරාගත් පෙරහන් ලැයිස්තු වලින් උසස් ජාල පෙරහන් සහ විශේෂිත සහ සාමාන්‍ය දිගු පෙරහන්.\n\nසියලුම වෙබ් අඩවි වල දත්ත කියවීමට සහ වෙනස් කිරීමට පුළුල් අවසරයක් අවශ්‍ය වේ.\n\nසාමාන්‍ය දිගු පෙරහන් මඟින් වෙබ් පිටු සම්පත් භාවිතය ඉහළ යාමට හේතු විය හැක.", "description": "This describes the 'complete' filtering mode" }, "noFilteringModeDescription": { @@ -228,59 +228,59 @@ "description": "Label for a checkbox in the options page" }, "enableStrictBlockLabel": { - "message": "Enable strict blocking", + "message": "දැඩි අවහිර කිරීම සක්‍රීය කරන්න", "description": "Label for a checkbox in the options page" }, "enableStrictBlockLegend": { - "message": "Navigation to potentially undesirable sites will be blocked, and you will be offered the option to proceed.", + "message": "අනවශ්‍ය විය හැකි අඩවි වෙත සංචාලනය අවහිර කරනු ලබන අතර, ඉදිරියට යාමට ඔබට විකල්පය ලබා දෙනු ඇත.", "description": "Label for a checkbox in the options page" }, "findListsPlaceholder": { - "message": "Find lists", + "message": "ලැයිස්තු සොයන්න", "description": "Placeholder for the input field used to find lists" }, "strictblockTitle": { - "message": "Page blocked", + "message": "පිටුව අවහිරයි", "description": "Webpage title for the strict-blocked page" }, "strictblockSentence1": { - "message": "uBO Lite has prevented the following page from loading:", + "message": "uBO Lite විසින් පහත පිටුව පූරණය වීම වළක්වා ඇත:", "description": "Sentence used in the strict-blocked page" }, "strictblockReasonSentence1": { - "message": "The page was blocked because of a matching filter in {{listname}}.", + "message": "{{listname}}හි ගැළපෙන පෙරහනක් නිසා පිටුව අවහිර කරන ලදී.", "description": "Text informing about what is causing the page to be blocked" }, "strictblockRedirectSentence1": { - "message": "The blocked page wants to redirect to another site. If you choose to proceed, you will navigate directly to: {{url}}", + "message": "අවහිර කළ පිටුව වෙනත් අඩවියකට හරවා යැවීමට අවශ්‍යයි. ඔබ ඉදිරියට යාමට තෝරා ගන්නේ නම්, ඔබ කෙලින්ම මෙහි සංචාලනය කරනු ඇත: {{url}}", "description": "Text warning about an incoming redirect" }, "strictblockNoParamsPrompt": { - "message": "without parameters", + "message": "පරාමිතීන් නොමැතිව", "description": "Label to be used for the parameter-less URL" }, "strictblockBack": { - "message": "Go back", + "message": "ආපසු යන්න", "description": "A button to go back to the previous webpage" }, "strictblockClose": { - "message": "Close this window", + "message": "මෙම කවුළුව වසන්න", "description": "A button to close the current tab" }, "strictblockDontWarn": { - "message": "Don't warn me again about this site", + "message": "මෙම අඩවිය ගැන මට නැවත අනතුරු අඟවන්න එපා.", "description": "Label for checkbox in document-blocked page" }, "strictblockProceed": { - "message": "Proceed", + "message": "ඉදිරියට", "description": "A button to navigate to the blocked page" }, "zapperTipEnter": { - "message": "Enter element zapper mode", + "message": "මූලද්‍රව්‍ය zapper ප්‍රකාරයට ඇතුළු වන්න", "description": "Tooltip for the button used to enter zapper mode" }, "zapperTipQuit": { - "message": "Exit element zapper mode", + "message": "මූලද්‍රව්‍ය zapper ප්‍රකාරයෙන් ඉවත් වන්න", "description": "Tooltip for the button used to exit zapper mode" } } diff --git a/platform/mv3/extension/_locales/sq/messages.json b/platform/mv3/extension/_locales/sq/messages.json index 466c754551473..85591ef8baa42 100644 --- a/platform/mv3/extension/_locales/sq/messages.json +++ b/platform/mv3/extension/_locales/sq/messages.json @@ -4,7 +4,7 @@ "description": "extension name." }, "extShortDesc": { - "message": "Një bllokues që bllokon në mënyrë të pavarur reklamat, gjurmuesit, kriptominatorët etj. menjëherë pas instalimit.", + "message": "Një bllokues efikas për reklamat, gjurmuesit, kriptominatorët e të tjera, që vepron menjëherë pas instalimit.", "description": "this will be in the Chrome web store: must be 132 characters or less" }, "perRulesetStats": { diff --git a/src/_locales/si/messages.json b/src/_locales/si/messages.json index 71ab9a720f98b..59d5e23ed452e 100644 --- a/src/_locales/si/messages.json +++ b/src/_locales/si/messages.json @@ -48,7 +48,7 @@ "description": "appears as tab name in dashboard" }, "statsPageName": { - "message": "uBlock₀ — Logger", + "message": "යූබ්ලොක්₀ — ලඝුර", "description": "Title for the logger window" }, "aboutPageName": { @@ -60,7 +60,7 @@ "description": "appears as tab name in dashboard" }, "assetViewerPageName": { - "message": "uBlock₀ — Asset viewer", + "message": "යූබ්ලොක්₀ — වත්කම් දක්වනය", "description": "Title for the asset viewer page" }, "advancedSettingsPageName": { @@ -68,11 +68,11 @@ "description": "Title for the advanced settings page" }, "popupPowerSwitchInfo": { - "message": "Click: disable/enable uBlock₀ for this site.\n\nCtrl+click: disable uBlock₀ only on this page.", + "message": "ඔබන්න: මෙම අඩවියට යූබ්ලොක්₀ අබල/සබල කරන්න.\n\nCtrl+ඔබන්න: මෙම පිටුවට පමණක් යූබ්ලොක්₀ අබල කරන්න.", "description": "English: Click: disable/enable uBlock₀ for this site.\n\nCtrl+click: disable uBlock₀ only on this page." }, "popupPowerSwitchInfo1": { - "message": "Click to disable uBlock₀ for this site.\n\nCtrl+click to disable uBlock₀ only on this page.", + "message": "මෙම අඩවියට යූබ්ලොක්₀ අබල/සබල කිරීමට ඔබන්න.\n\nමෙම පිටුවට පමණක් යූබ්ලොක්₀ අබල කිරීමට Ctrl+ඔබන්න.", "description": "Message to be read by screen readers" }, "popupPowerSwitchInfo2": { @@ -116,15 +116,15 @@ "description": "English: Click to open the dashboard" }, "popupTipZapper": { - "message": "Enter element zapper mode", + "message": "මූලද්‍රව්‍ය zapper ප්‍රකාරයට ඇතුළු වන්න", "description": "Tooltip for the element-zapper icon in the popup panel" }, "popupTipPicker": { - "message": "Enter element picker mode", + "message": "මූලද්‍රව්‍ය තෝරක ප්‍රකාරයට ඇතුළු වන්න", "description": "English: Enter element picker mode" }, "popupTipLog": { - "message": "Open the logger", + "message": "ලඝුර අරින්න", "description": "Tooltip used for the logger icon in the panel" }, "popupTipReport": { @@ -132,51 +132,51 @@ "description": "Tooltip used for the 'chat' icon in the panel" }, "popupTipNoPopups": { - "message": "Toggle the blocking of all popups for this site", + "message": "මෙම අඩවිය සඳහා සියලු උත්පතන අවහිර කිරීම ටොගල් කරන්න", "description": "Tooltip for the no-popups per-site switch" }, "popupTipNoPopups1": { - "message": "Click to block all popups on this site", + "message": "මෙම අඩවියේ ඇති සියලුම උත්පතන අවහිර කිරීමට ක්ලික් කරන්න.", "description": "Tooltip for the no-popups per-site switch" }, "popupTipNoPopups2": { - "message": "Click to no longer block all popups on this site", + "message": "මෙම අඩවියේ ඇති සියලුම උත්පතන තවදුරටත් අවහිර නොකිරීමට ක්ලික් කරන්න.", "description": "Tooltip for the no-popups per-site switch" }, "popupTipNoLargeMedia": { - "message": "Toggle the blocking of large media elements for this site", + "message": "මෙම අඩවිය සඳහා විශාල මාධ්‍ය අංග අවහිර කිරීම ටොගල් කරන්න.", "description": "Tooltip for the no-large-media per-site switch" }, "popupTipNoLargeMedia1": { - "message": "Click to block large media elements on this site", + "message": "මෙම අඩවියේ විශාල මාධ්‍ය අංග අවහිර කිරීමට ක්ලික් කරන්න.", "description": "Tooltip for the no-large-media per-site switch" }, "popupTipNoLargeMedia2": { - "message": "Click to no longer block large media elements on this site", + "message": "මෙම අඩවියේ විශාල මාධ්‍ය අංග තවදුරටත් අවහිර නොකිරීමට ක්ලික් කරන්න.", "description": "Tooltip for the no-large-media per-site switch" }, "popupTipNoCosmeticFiltering": { - "message": "Toggle cosmetic filtering for this site", + "message": "මෙම අඩවිය සඳහා රූපලාවන්‍ය පෙරහන ටොගල් කරන්න", "description": "Tooltip for the no-cosmetic-filtering per-site switch" }, "popupTipNoCosmeticFiltering1": { - "message": "Click to disable cosmetic filtering on this site", + "message": "මෙම අඩවියේ රූපලාවන්‍ය පෙරහන් අක්‍රිය කිරීමට ක්ලික් කරන්න.", "description": "Tooltip for the no-cosmetic-filtering per-site switch" }, "popupTipNoCosmeticFiltering2": { - "message": "Click to enable cosmetic filtering on this site", + "message": "මෙම අඩවියේ රූපලාවන්‍ය පෙරහන් සක්‍රීය කිරීමට ක්ලික් කරන්න.", "description": "Tooltip for the no-cosmetic-filtering per-site switch" }, "popupTipNoRemoteFonts": { - "message": "Toggle the blocking of remote fonts for this site", + "message": "මෙම අඩවිය සඳහා දුරස්ථ අකුරු අවහිර කිරීම ටොගල් කරන්න", "description": "Tooltip for the no-remote-fonts per-site switch" }, "popupTipNoRemoteFonts1": { - "message": "Click to block remote fonts on this site", + "message": "මෙම අඩවියේ දුරස්ථ අකුරු අවහිර කිරීමට ක්ලික් කරන්න.", "description": "Tooltip for the no-remote-fonts per-site switch" }, "popupTipNoRemoteFonts2": { - "message": "Click to no longer block remote fonts on this site", + "message": "මෙම අඩවියේ දුරස්ථ අකුරු තවදුරටත් අවහිර නොකිරීමට ක්ලික් කරන්න.", "description": "Tooltip for the no-remote-fonts per-site switch" }, "popupTipNoScripting1": { @@ -196,7 +196,7 @@ "description": "Caption for the no-large-media per-site switch" }, "popupNoCosmeticFiltering_v2": { - "message": "Cosmetic filtering", + "message": "රූපලාවන්‍ය පෙරහන", "description": "Caption for the no-cosmetic-filtering per-site switch" }, "popupNoRemoteFonts_v2": { @@ -220,15 +220,15 @@ "description": "Tooltip when hovering the top-most cell of the global-rules column." }, "popupTipLocalRules": { - "message": "Local rules: this column is for rules which apply to the current site only.\nLocal rules override global rules.", + "message": "දේශීය නීති: මෙම තීරුව වත්මන් අඩවියට පමණක් අදාළ වන නීති සඳහා වේ.\nදේශීය නීති ගෝලීය නීති අභිබවා යයි.", "description": "Tooltip when hovering the top-most cell of the local-rules column." }, "popupTipSaveRules": { - "message": "Click to make your changes permanent.", + "message": "ඔබගේ වෙනස්කම් ස්ථිර කිරීමට ක්ලික් කරන්න.", "description": "Tooltip when hovering over the padlock in the dynamic filtering pane." }, "popupTipRevertRules": { - "message": "Click to revert your changes.", + "message": "ඔබගේ වෙනස්කම් ප්‍රතිවර්තනය කිරීමට ක්ලික් කරන්න.", "description": "Tooltip when hovering over the eraser in the dynamic filtering pane." }, "popupAnyRulePrompt": { @@ -268,7 +268,7 @@ "description": "appears in popup" }, "popupHitDomainCount": { - "message": "{{count}} out of {{total}}", + "message": "{{total}}න් {{count}}", "description": "appears in popup" }, "popupVersion": { @@ -288,7 +288,7 @@ "description": "English: Create" }, "pickerPick": { - "message": "Pick", + "message": "තෝරන්න", "description": "English: Pick" }, "pickerQuit": { @@ -304,7 +304,7 @@ "description": "English: header for a type of filter in the element picker dialog" }, "pickerCosmeticFilters": { - "message": "Cosmetic filters", + "message": "රූපලාවන්‍ය පෙරහන්", "description": "English: Cosmetic filters" }, "pickerCosmeticFiltersHint": { @@ -316,7 +316,7 @@ "description": "An entry in the browser's contextual menu" }, "settingsCollapseBlockedPrompt": { - "message": "Hide placeholders of blocked elements", + "message": "අවහිර කළ මූලද්‍රව්‍යවල ස්ථාන දරන්නන් සඟවන්න", "description": "English: Hide placeholders of blocked elements" }, "settingsIconBadgePrompt": { @@ -324,7 +324,7 @@ "description": "English: Show the number of blocked requests on the icon" }, "settingsTooltipsPrompt": { - "message": "Disable tooltips", + "message": "මෙවලම් ඉඟි අක්‍රීය කරන්න", "description": "A checkbox in the Settings pane" }, "settingsContextMenuPrompt": { @@ -344,7 +344,7 @@ "description": "Label for checkbox to enable a custom dark theme" }, "settingsThemeAccent0Label": { - "message": "Custom accent color", + "message": "අභිරුචි උදාත්ත වර්ණය", "description": "Label for checkbox to pick an accent color" }, "settingsCloudStorageEnabledPrompt": { @@ -356,7 +356,7 @@ "description": "Checkbox to let user access advanced, technical features" }, "settingsPrefetchingDisabledPrompt": { - "message": "Disable pre-fetching (to prevent any connection for blocked network requests)", + "message": "පූර්ව-ලබා ගැනීම අක්‍රීය කරන්න (අවහිර කළ ජාල ඉල්ලීම් සඳහා කිසිදු සම්බන්ධතාවයක් වැළැක්වීමට)", "description": "English: " }, "settingsHyperlinkAuditingDisabledPrompt": { @@ -364,19 +364,19 @@ "description": "English: " }, "settingsWebRTCIPAddressHiddenPrompt": { - "message": "Prevent WebRTC from leaking local IP addresses", + "message": "දේශීය IP ලිපින කාන්දු වීම WebRTC වලක්වන්න", "description": "English: " }, "settingPerSiteSwitchGroup": { - "message": "Default behavior", + "message": "පෙරනිමි හැසිරීම", "description": "" }, "settingPerSiteSwitchGroupSynopsis": { - "message": "These default behaviors can be overridden on a per-site basis", + "message": "මෙම පෙරනිමි හැසිරීම් එක් එක් අඩවියට අනුව අභිබවා යා හැක.", "description": "" }, "settingsNoCosmeticFilteringPrompt": { - "message": "Disable cosmetic filtering", + "message": "රූපලාවන්‍ය පෙරහන අක්‍රීය කරන්න", "description": "" }, "settingsNoLargeMediaPrompt": { @@ -396,7 +396,7 @@ "description": "background information: https://github.com/gorhill/uBlock/issues/3150" }, "settingsUncloakCnamePrompt": { - "message": "Uncloak canonical names", + "message": "කැනොනිකල් නම් ඉවත් කරන්න", "description": "background information: https://github.com/uBlockOrigin/uBlock-issues/issues/1513" }, "settingsAdvanced": { @@ -420,15 +420,15 @@ "description": "English: Last backup:" }, "3pListsOfBlockedHostsPrompt": { - "message": "{{netFilterCount}} network filters + {{cosmeticFilterCount}} cosmetic filters from:", + "message": "{{netFilterCount}} ජාල පෙරහන් + {{cosmeticFilterCount}} රූපලාවන්‍ය පෙරහන්:", "description": "Appears at the top of the _3rd-party filters_ pane" }, "3pListsOfBlockedHostsPerListStats": { - "message": "{{used}} used out of {{total}}", + "message": "{{total}} න් {{used}} ක් භාවිතා කර ඇත", "description": "Appears aside each filter list in the _3rd-party filters_ pane" }, "3pAutoUpdatePrompt1": { - "message": "Auto-update filter lists", + "message": "පෙරහන් ලැයිස්තු ස්වයංක්‍රීයව යාවත්කාලීන කරන්න", "description": "A checkbox in the _3rd-party filters_ pane" }, "3pUpdateNow": { @@ -436,27 +436,27 @@ "description": "A button in the in the _3rd-party filters_ pane" }, "3pPurgeAll": { - "message": "Purge all caches", + "message": "සියලුම හැඹිලි ඉවත් කරන්න", "description": "A button in the in the _3rd-party filters_ pane" }, "3pParseAllABPHideFiltersPrompt1": { - "message": "Parse and enforce cosmetic filters", + "message": "රූපලාවන්‍ය පෙරහන් විග්‍රහ කර බලාත්මක කරන්න", "description": "English: Parse and enforce Adblock+ element hiding filters." }, "3pParseAllABPHideFiltersInfo": { - "message": "Cosmetic filters serve to hide elements in a web page which are deemed to be a visual nuisance, and which can't be blocked by the network request-based filtering engines.", + "message": "රූපලාවන්‍ය පෙරහන් යනු වෙබ් පිටුවක දෘශ්‍ය කරදරයක් ලෙස සලකනු ලබන සහ ජාල ඉල්ලීම් මත පදනම් වූ පෙරහන් එන්ජින් මගින් අවහිර කළ නොහැකි අංග සැඟවීමට සේවය කරයි.", "description": "Describes the purpose of the 'Parse and enforce cosmetic filters' feature." }, "3pIgnoreGenericCosmeticFilters": { - "message": "Ignore generic cosmetic filters", + "message": "සාමාන්‍ය රූපලාවන්‍ය පෙරහන් නොසලකා හරින්න.", "description": "This will cause uBO to ignore all generic cosmetic filters." }, "3pIgnoreGenericCosmeticFiltersInfo": { - "message": "Generic cosmetic filters are those cosmetic filters which are meant to apply on all web sites. Enabling this option will eliminate the memory and CPU overhead added to web pages as a result of handling generic cosmetic filters.\n\nIt is recommended to enable this option on less powerful devices.", + "message": "සාමාන්‍ය රූපලාවන්‍ය පෙරහන් යනු සියලුම වෙබ් අඩවිවල යෙදීමට අදහස් කරන රූපලාවන්‍ය පෙරහන් වේ. මෙම විකල්පය සක්‍රීය කිරීමෙන් සාමාන්‍ය රූපලාවන්‍ය පෙරහන් හැසිරවීමේ ප්‍රතිඵලයක් ලෙස වෙබ් පිටුවලට එකතු වන මතකය සහ CPU උඩිස් බර ඉවත් කරනු ඇත.\n\nඅඩු බලවත් උපාංගවල මෙම විකල්පය සක්‍රීය කිරීම නිර්දේශ කෙරේ.", "description": "Describes the purpose of the 'Ignore generic cosmetic filters' feature." }, "3pSuspendUntilListsAreLoaded": { - "message": "Suspend network activity until all filter lists are loaded", + "message": "සියලුම පෙරහන් ලැයිස්තු පූරණය වන තුරු ජාල ක්‍රියාකාරකම් අත්හිටුවන්න.", "description": "A checkbox in the 'Filter lists' pane" }, "3pListsOfBlockedHostsHeader": { @@ -480,11 +480,11 @@ "description": "Filter lists section name" }, "3pGroupMalware": { - "message": "Malware protection, security", + "message": "අනිෂ්ට මෘදුකාංග ආරක්ෂාව, ආරක්ෂාව", "description": "Filter lists section name" }, "3pGroupSocial": { - "message": "Social widgets", + "message": "සමාජ විජට්", "description": "Filter lists section name" }, "3pGroupCookies": { @@ -492,15 +492,15 @@ "description": "Filter lists section name" }, "3pGroupAnnoyances": { - "message": "Annoyances", + "message": "කරදර", "description": "Filter lists section name" }, "3pGroupMultipurpose": { - "message": "Multipurpose", + "message": "බහුකාර්ය", "description": "Filter lists section name" }, "3pGroupRegions": { - "message": "Regions, languages", + "message": "කලාප, භාෂා", "description": "Filter lists section name" }, "3pGroupCustom": { @@ -512,11 +512,11 @@ "description": "The label for the checkbox used to import external filter lists" }, "3pExternalListsHint": { - "message": "One URL per line. Invalid URLs will be silently ignored.", + "message": "පේළියකට එක් URL එකක් බැගින්. අවලංගු URL නිහඬව නොසලකා හරිනු ලැබේ.", "description": "Short information about how to use the textarea to import external filter lists by URL" }, "3pExternalListObsolete": { - "message": "Out of date.", + "message": "කල් ඉකුත් වී ඇත.", "description": "used as a tooltip for the out-of-date icon beside a list" }, "3pViewContent": { @@ -524,7 +524,7 @@ "description": "used as a tooltip for eye icon beside a list" }, "3pLastUpdate": { - "message": "Last update: {{ago}}.\nClick to force an update.", + "message": "අවසන් යාවත්කාලීන කිරීම: {{ago}}.\nයාවත්කාලීන කිරීමට බල කිරීමට ක්ලික් කරන්න.", "description": "used as a tooltip for the clock icon beside a list" }, "3pUpdating": { @@ -532,23 +532,23 @@ "description": "used as a tooltip for the spinner icon beside a list" }, "3pNetworkError": { - "message": "A network error prevented the resource from being updated.", + "message": "ජාල දෝෂයක් නිසා සම්පත යාවත්කාලීන වීම වැළැක්විණි.", "description": "used as a tooltip for error icon beside a list" }, "1pTrustWarning": { - "message": "Do not add filters from untrusted sources.", + "message": "විශ්වාස නොකළ මූලාශ්‍රවලින් පෙරහන් එක් නොකරන්න.", "description": "Warning against copy-pasting filters from random sources" }, "1pEnableMyFiltersLabel": { - "message": "Enable my custom filters", + "message": "මගේ අභිරුචි පෙරහන් සක්‍රීය කරන්න", "description": "Label for the checkbox use to enable/disable 'My filters' list" }, "1pTrustMyFiltersLabel": { - "message": "Allow custom filters requiring trust", + "message": "විශ්වාසය අවශ්‍ය අභිරුචි පෙරහන් වලට ඉඩ දෙන්න.", "description": "Label for the checkbox use to trust the content of 'My filters' list" }, "1pImport": { - "message": "Import and append…", + "message": "…ආයාත කර එකතු කරන්න", "description": "Button in the 'My filters' pane" }, "1pExport": { @@ -556,7 +556,7 @@ "description": "Button in the 'My filters' pane" }, "1pExportFilename": { - "message": "my-ublock-static-filters_{{datetime}}.txt", + "message": "මගේ-ublock-static-filters_{{datetime}}.txt", "description": "English: my-ublock-static-filters_{{datetime}}.txt" }, "1pApplyChanges": { @@ -564,7 +564,7 @@ "description": "English: Apply changes" }, "rulesPermanentHeader": { - "message": "Permanent rules", + "message": "ස්ථිර නීති", "description": "header" }, "rulesTemporaryHeader": { @@ -576,7 +576,7 @@ "description": "This will remove all temporary rules" }, "rulesCommit": { - "message": "Commit", + "message": "කැප කරන්න", "description": "This will persist temporary rules" }, "rulesEdit": { @@ -604,11 +604,11 @@ "description": "default file name to use" }, "rulesHint": { - "message": "List of your dynamic filtering rules.", + "message": "ඔබගේ ගතික පෙරහන් නීති ලැයිස්තුව.", "description": "English: List of your dynamic filtering rules." }, "rulesFormatHint": { - "message": "Rule syntax: source destination type action (full documentation).", + "message": "රීති වාක්‍ය ඛණ්ඩය: මූලාශ්‍ර ගමනාන්ත වර්ගය ක්‍රියාව (සම්පූර්ණ ලියකියවිලි).", "description": "English: dynamic rule syntax and full documentation." }, "rulesSort": { @@ -624,15 +624,15 @@ "description": "English: a sort option for list of rules." }, "rulesSortByDestination": { - "message": "Destination", + "message": "ගමනාන්තය", "description": "English: a sort option for list of rules." }, "whitelistPrompt": { - "message": "The trusted site directives dictate on which web pages uBlock Origin should be disabled. One entry per line.", + "message": "විශ්වාසදායක අඩවි නියෝග මඟින් uBlock Origin අක්‍රිය කළ යුත්තේ කුමන වෙබ් පිටු මතද යන්න නියම කරයි. පේළියකට එක් ඇතුළත් කිරීමක් පමණි.", "description": "A concise description of the 'Trusted sites' pane." }, "whitelistImport": { - "message": "Import and append…", + "message": "…ආයාත කර එකතු කරන්න", "description": "Button in the 'Trusted sites' pane" }, "whitelistExport": { @@ -668,55 +668,55 @@ "description": "Appears in the logger's tab selector" }, "logBehindTheScene": { - "message": "Tabless", + "message": "මේස", "description": "Pretty name for behind-the-scene network requests" }, "loggerCurrentTab": { - "message": "Current tab", + "message": "වත්මන් ටැබය", "description": "Appears in the logger's tab selector" }, "loggerReloadTip": { - "message": "Reload the tab content", + "message": "ටැබ් අන්තර්ගතය නැවත පූරණය කරන්න", "description": "Tooltip for the reload button in the logger page" }, "loggerDomInspectorTip": { - "message": "Toggle the DOM inspector", + "message": "DOM පරීක්ෂක ටොගල් කරන්න", "description": "Tooltip for the DOM inspector button in the logger page" }, "loggerPopupPanelTip": { - "message": "Toggle the popup panel", + "message": "උත්පතන පැනලය ටොගල් කරන්න", "description": "Tooltip for the popup panel button in the logger page" }, "loggerInfoTip": { - "message": "uBlock Origin wiki: The logger", + "message": "uBlock සම්භවය විකි: ලොගර්", "description": "Tooltip for the top-right info label in the logger page" }, "loggerClearTip": { - "message": "Clear logger", + "message": "ලොගර් හිස් කරන්න", "description": "Tooltip for the eraser in the logger page; used to blank the content of the logger" }, "loggerPauseTip": { - "message": "Pause logger (discard all incoming data)", + "message": "ලොගර් විරාම කරන්න (එන සියලුම දත්ත ඉවතලන්න)", "description": "Tooltip for the pause button in the logger page" }, "loggerUnpauseTip": { - "message": "Unpause logger", + "message": "ලොගර් විරාම නොකරන්න", "description": "Tooltip for the play button in the logger page" }, "loggerRowFiltererButtonTip": { - "message": "Toggle logger filtering", + "message": "ලොගර් පෙරහන ටොගල් කරන්න", "description": "Tooltip for the row filterer button in the logger page" }, "logFilterPrompt": { - "message": "filter logger content", + "message": "පෙරහන් ලොගර් අන්තර්ගතය", "description": "Placeholder string for logger output filtering input field" }, "loggerRowFiltererBuiltinTip": { - "message": "Logger filtering options", + "message": "ලොගර් පෙරහන් විකල්ප", "description": "Tooltip for the button to bring up logger output filtering options" }, "loggerRowFiltererBuiltinNot": { - "message": "Not", + "message": "නැහැ", "description": "A keyword in the built-in row filtering expression" }, "loggerRowFiltererBuiltinEventful": { @@ -768,7 +768,7 @@ "description": "Label to identify a root context field (typically a hostname)" }, "loggerEntryDetailsPartyness": { - "message": "Partyness", + "message": "සාද ගතිය", "description": "Label to identify a field providing partyness information" }, "loggerEntryDetailsType": { @@ -796,7 +796,7 @@ "description": "Small header to identify the static filtering section" }, "loggerStaticFilteringSentence": { - "message": "{{action}} network requests of {{type}} {{br}}which URL address matches {{url}} {{br}}and which originates {{origin}},{{br}}{{importance}} there is a matching exception filter.", + "message": "{{action}} ජාල ඉල්ලීම් {{type}} {{br}}වන URL ලිපිනය {{url}} {{br}}ට ගැලපෙන අතර {{origin}}ට ආරම්භ වේ,{{br}}{{importance}} ට ගැලපෙන ව්‍යතිරේක පෙරහනක් ඇත.", "description": "Used in the static filtering wizard" }, "loggerStaticFilteringSentencePartBlock": { @@ -820,43 +820,43 @@ "description": "Used in the static filtering wizard" }, "loggerStaticFilteringSentencePartAnyOrigin": { - "message": "from anywhere", + "message": "ඕනෑම තැනක සිට", "description": "Used in the static filtering wizard" }, "loggerStaticFilteringSentencePartNotImportant": { - "message": "except when", + "message": "විට හැර", "description": "Used in the static filtering wizard" }, "loggerStaticFilteringSentencePartImportant": { - "message": "even if", + "message": "විට දී වුවත්", "description": "Used in the static filtering wizard" }, "loggerStaticFilteringFinderSentence1": { - "message": "Static filter {{filter}} found in:", + "message": "ස්ථිතික පෙරහන {{filter}} හමු වූයේ:", "description": "Below this sentence, the filter list(s) in which the filter was found" }, "loggerStaticFilteringFinderSentence2": { - "message": "Static filter could not be found in any of the currently enabled filter lists", + "message": "දැනට සක්‍රිය කර ඇති කිසිදු පෙරහන් ලැයිස්තුවක ස්ථිතික පෙරහන සොයාගත නොහැකි විය.", "description": "Message to show when a filter cannot be found in any filter lists" }, "loggerSettingDiscardPrompt": { - "message": "Logger entries which do not fulfill all three conditions below will be automatically discarded:", + "message": "පහත කොන්දේසි තුනම සපුරා නොමැති ලොගර් ඇතුළත් කිරීම් ස්වයංක්‍රීයව ඉවත දමනු ලැබේ:", "description": "Logger setting: A sentence to describe the purpose of the settings below" }, "loggerSettingPerEntryMaxAge": { - "message": "Preserve entries from the last {{input}} minutes", + "message": "අවසාන මිනිත්තු {{input}} වලින් ඇතුළත් කිරීම් සුරකින්න", "description": "A logger setting" }, "loggerSettingPerTabMaxLoads": { - "message": "Preserve at most {{input}} page loads per tab", + "message": "ටැබ් එකකට උපරිම වශයෙන් {{input}} පිටු පූරණ සංරක්ෂණය කරන්න", "description": "A logger setting" }, "loggerSettingPerTabMaxEntries": { - "message": "Preserve at most {{input}} entries per tab", + "message": "ටැබ් එකකට උපරිම වශයෙන් {{input}} ඇතුළත් කිරීම් සංරක්ෂණය කරන්න", "description": "A logger setting" }, "loggerSettingPerEntryLineCount": { - "message": "Use {{input}} lines per entry in vertically expanded mode", + "message": "සිරස් අතට පුළුල් කළ ප්‍රකාරයේදී ඇතුළත් කිරීමකට {{input}} රේඛා භාවිතා කරන්න.", "description": "A logger setting" }, "loggerSettingHideColumnsPrompt": { @@ -872,15 +872,15 @@ "description": "A label for the filter or rule column" }, "loggerSettingHideColumnContext": { - "message": "{{input}} Context", + "message": "{{input}} සන්දර්භය", "description": "A label for the context column" }, "loggerSettingHideColumnPartyness": { - "message": "{{input}} Partyness", + "message": "{{input}} සාද ස්වභාවය", "description": "A label for the partyness column" }, "loggerExportFormatList": { - "message": "List", + "message": "ලැයිස්තුව", "description": "Label for radio-button to pick export format" }, "loggerExportFormatTable": { @@ -888,11 +888,11 @@ "description": "Label for radio-button to pick export format" }, "loggerExportEncodePlain": { - "message": "Plain", + "message": "සරල", "description": "Label for radio-button to pick export text format" }, "loggerExportEncodeMarkdown": { - "message": "Markdown", + "message": "මාර්ක්ඩවුන්", "description": "Label for radio-button to pick export text format" }, "supportOpenButton": { @@ -912,7 +912,7 @@ "description": "Header of 'Documentation' section in Support pane" }, "supportS1P1": { - "message": "Read the documentation at uBlock/wiki to learn about all of uBlock Origin's features.", + "message": "uBlock Origin හි සියලුම විශේෂාංග ගැන ඉගෙන ගැනීමට uBlock/wiki හි ඇති ලේඛන කියවන්න.", "description": "First paragraph of 'Documentation' section in Support pane" }, "supportS2H": { @@ -920,23 +920,23 @@ "description": "Header of 'Questions and support' section in Support pane" }, "supportS2P1": { - "message": "Answers to questions and other kinds of help support is provided on the subreddit /r/uBlockOrigin.", + "message": "ප්‍රශ්නවලට පිළිතුරු සහ අනෙකුත් ආකාරයේ උපකාර සහාය /r/uBlockOriginයන උප රෙඩිට් එකෙන් සපයනු ලැබේ.", "description": "First paragraph of 'Questions and support' section in Support pane" }, "supportS3H": { - "message": "Filter issues/website is broken", + "message": "පෙරහන් ගැටළු/වෙබ් අඩවිය බිඳ වැටී ඇත", "description": "Header of 'Filter issues' section in Support pane" }, "supportS3P1": { - "message": "Report filter issues with specific websites to the uBlockOrigin/uAssets issue tracker. Requires a GitHub account.", + "message": "නිශ්චිත වෙබ් අඩවි සමඟ පෙරහන් ගැටළු uBlockOrigin/uAssets ගැටළු ට්රැකර්වෙත වාර්තා කරන්න. GitHub ගිණුමක් අවශ්‍යයි.", "description": "First paragraph of 'Filter issues' section in Support pane" }, "supportS3P2": { - "message": "Important: Avoid using other similarly-purposed blockers along with uBlock Origin, as this may cause filter issues on specific websites.", + "message": "වැදගත්: uBlock Origin සමඟ සමාන අරමුණු සහිත වෙනත් අවහිර කරන්නන් භාවිතා කිරීමෙන් වළකින්න, මන්ද මෙය නිශ්චිත වෙබ් අඩවි වල පෙරහන් ගැටළු ඇති කළ හැකිය.", "description": "Second paragraph of 'Filter issues' section in Support pane" }, "supportS3P3": { - "message": "Tips: Be sure your filter lists are up to date. The logger is the primary tool to diagnose filter-related issues.", + "message": "ඉඟි: ඔබගේ පෙරහන් ලැයිස්තු යාවත්කාලීනව ඇති බවට වග බලා ගන්න. පෙරහන් ආශ්‍රිත ගැටළු හඳුනා ගැනීම සඳහා ලොගර් මූලික මෙවලම වේ.", "description": "Third paragraph of 'Filter issues' section in Support pane" }, "supportS4H": { @@ -944,39 +944,39 @@ "description": "Header of 'Bug report' section in Support pane" }, "supportS4P1": { - "message": "Report issues with uBlock Origin itself to the uBlockOrigin/uBlock-issue issue tracker. Requires a GitHub account.", + "message": "uBlock Origin සමඟ ඇති ගැටළු uBlockOrigin/uBlock-issue ගැටළු ට්‍රැකර්වෙත වාර්තා කරන්න. GitHub ගිණුමක් අවශ්‍යයි.", "description": "First paragraph of 'Bug report' section in Support pane" }, "supportS5H": { - "message": "Troubleshooting Information", + "message": "දෝශ නිරාකරණ තොරතුරු", "description": "Header of 'Troubleshooting Information' section in Support pane" }, "supportS5P1": { - "message": "Below is technical information that might be useful when volunteers are trying to help you solve a problem.", + "message": "ස්වේච්ඡා සේවකයන් ගැටලුවක් විසඳීමට ඔබට උදව් කිරීමට උත්සාහ කරන විට ප්‍රයෝජනවත් විය හැකි තාක්ෂණික තොරතුරු පහත දැක්වේ.", "description": "First paragraph of 'Troubleshooting Information' section in Support pane" }, "supportS5P2": { - "message": "Important: Potentially private or sensitive information is redacted by default. Redacted information may make it more difficult to solve a problem.", + "message": "වැදගත්: පුද්ගලික හෝ සංවේදී තොරතුරු පෙරනිමියෙන් සංස්කරණය කරනු ලැබේ. සංස්කරණය කරන ලද තොරතුරු ගැටළුවක් විසඳීම වඩාත් අපහසු කළ හැකිය.", "description": "Second paragraph of 'Troubleshooting Information' section in Support pane" }, "supportS6H": { - "message": "Report a filter issue", + "message": "පෙරහන් ගැටළුවක් වාර්තා කරන්න", "description": "Header of 'Report a filter issue' section in Support pane" }, "supportS6P1S1": { - "message": "To avoid burdening volunteers with duplicate reports, please verify that the issue has not already been reported. Note: clicking the button will cause the page's origin to be sent to GitHub.", + "message": "අනුපිටපත් වාර්තා සමඟ ස්වේච්ඡා සේවකයින්ට බරක් වීම වළක්වා ගැනීම සඳහා, කරුණාකර ගැටළුව දැනටමත් වාර්තා කර නොමැති බව තහවුරු කරගන්න. සටහන: බොත්තම ක්ලික් කිරීමෙන් පිටුවේ මූලාරම්භය GitHub වෙත යවනු ලැබේ.", "description": "A paragraph in the filter issue reporter section" }, "supportS6P2S1": { - "message": "Filter lists are updated daily. Be sure your issue has not already been addressed in the most recent filter lists.", + "message": "පෙරහන් ලැයිස්තු දිනපතා යාවත්කාලීන වේ. ඔබගේ ගැටලුව මෑත කාලීන පෙරහන් ලැයිස්තු වල දැනටමත් විසඳා නොමැති බවට වග බලා ගන්න.", "description": "A paragraph in the filter issue reporter section" }, "supportS6P2S2": { - "message": "Verify that the issue still exists after reloading the problematic webpage.", + "message": "ගැටළු සහගත වෙබ් පිටුව නැවත පූරණය කිරීමෙන් පසුවද ගැටලුව පවතින බව තහවුරු කරන්න.", "description": "A paragraph in the filter issue reporter section" }, "supportS6URL": { - "message": "Address of the web page:", + "message": "වියමන පිටුවේ ලිපිනය:", "description": "Label for the URL of the page" }, "supportS6Select1": { @@ -984,39 +984,39 @@ "description": "Label for widget to select type of issue" }, "supportS6Select1Option0": { - "message": "-- Pick an entry --", + "message": "-- නිවේශිතයක් තෝරන්න --", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option1": { - "message": "Shows ads or ad leftovers", + "message": "දැන්වීම් හෝ දැන්වීම් ඉතිරි කොටස් පෙන්වයි", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option2": { - "message": "Has overlays or other nuisances", + "message": "උඩැතිරි හෝ වෙනත් කරදර ඇති", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option3": { - "message": "Detects uBlock Origin", + "message": "යූබ්ලොක් ඔරිජින් හඳුනා ගනියි", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option4": { - "message": "Has privacy-related issues", + "message": "පෞද්ගලිකත්‍වය ආශ්‍රිත ගැටළු තිබේ", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option5": { - "message": "Malfunctions when uBlock Origin is enabled", + "message": "uBlock Origin සක්‍රීය කර ඇති විට සිදුවන අක්‍රමිකතා", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option6": { - "message": "Opens unwanted tabs or windows", + "message": "අනවශ්‍ය පටිති හෝ කවුළු අරියි", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option7": { - "message": "Leads to badware, phishing", + "message": "නරක මෘදුකාංග, තතුබෑම් වලට මග පාදයි", "description": "An entry in the widget used to select the type of issue" }, "supportS6Checkbox1": { - "message": "Label the web page as “NSFW” (“Not Safe For Work”)", + "message": "වෙබ් පිටුව “NSFW” ලෙස ලේබල් කරන්න (“වැඩ සඳහා ආරක්ෂිත නොවේ”)", "description": "A checkbox to use for NSFW sites" }, "supportRedact": { @@ -1056,15 +1056,15 @@ "description": "Link text to uBO's own filter lists repo" }, "aboutDependencies": { - "message": "External dependencies (GPLv3-compatible):", + "message": "බාහිර පරායත්ත (GPLv3-අනුකූල):", "description": "Shown in the About pane" }, "aboutCDNs": { - "message": "uBO's own filter lists are freely hosted on the following CDNs:", + "message": "uBO හි පෙරහන් ලැයිස්තු පහත CDNsමත නිදහසේ සත්කාරකත්වය දරයි:", "description": "Shown in the About pane" }, "aboutCDNsInfo": { - "message": "A randomly picked CDN is used when a filter list needs to be updated.", + "message": "පෙරහන් ලැයිස්තුවක් යාවත්කාලීන කිරීමට අවශ්‍ය වූ විට අහඹු ලෙස තෝරාගත් CDN එකක් භාවිතා වේ.", "description": "Shown in the About pane" }, "aboutBackupDataButton": { @@ -1072,7 +1072,7 @@ "description": "Text for button to create a backup of all settings" }, "aboutBackupFilename": { - "message": "my-ublock-backup_{{datetime}}.txt", + "message": "මාගේ-ublock-උපස්ථය_{{datetime}}.txt", "description": "English: my-ublock-backup_{{datetime}}.txt" }, "aboutRestoreDataButton": { @@ -1084,15 +1084,15 @@ "description": "English: Reset to default settings..." }, "aboutRestoreDataConfirm": { - "message": "All your settings will be overwritten using data backed up on {{time}}, and uBlock₀ will restart.\n\nOverwrite all existing settings using backed up data?", + "message": "{{time}}මත උපස්ථ කළ දත්ත භාවිතයෙන් ඔබගේ සියලු සැකසුම් උඩින් ලියනු ලබන අතර, uBlock₀ නැවත ආරම්භ වේ.\n\nඋපස්ථ කළ දත්ත භාවිතයෙන් පවතින සියලුම සැකසුම් උඩින් ලියන්නද?", "description": "Message asking user to confirm restore" }, "aboutRestoreDataError": { - "message": "The data could not be read or is invalid", + "message": "දත්ත කියවිය නොහැකි විය නැතහොත් අවලංගුය.", "description": "Message to display when an error occurred during restore" }, "aboutResetDataConfirm": { - "message": "All your settings will be removed, and uBlock₀ will restart.\n\nReset uBlock₀ to factory settings?", + "message": "ඔබගේ සියලු සැකසුම් ඉවත් කරනු ලබන අතර, uBlock₀ නැවත ආරම්භ වේ.\n\nuBlock₀ කර්මාන්තශාලා සැකසුම් වෙත නැවත සකසන්නද?", "description": "Message asking user to confirm reset" }, "errorCantConnectTo": { @@ -1100,11 +1100,11 @@ "description": "English: Network error: {{msg}}" }, "subscriberConfirm": { - "message": "Add the following URL to your custom filter lists?\n\nTitle: \"{{title}}\"\nURL: {{url}}", + "message": "ඔබගේ අභිරුචි පෙරහන් ලැයිස්තුවලට පහත URL එක එක් කරන්නද?\n\nමාතෘකාව: \"{{title}}\"\nURL: {{url}}", "description": "No longer used" }, "subscribeButton": { - "message": "Subscribe", + "message": "දායක වන්න", "description": "For the button used to subscribe to a filter list" }, "elapsedOneMinuteAgo": { @@ -1136,7 +1136,7 @@ "description": "Firefox/Fennec-specific: Show Dashboard" }, "showNetworkLogButton": { - "message": "Show Logger", + "message": "ලඝුව පෙන්වන්න", "description": "Firefox/Fennec-specific: Show Logger" }, "fennecMenuItemBlockingOff": { @@ -1148,15 +1148,15 @@ "description": "Used as a title for the document-blocked page" }, "docblockedPrompt1": { - "message": "uBlock Origin has prevented the following page from loading:", + "message": "uBlock Origin විසින් පහත පිටුව පූරණය වීම වළක්වා ඇත:", "description": "Used in the strict-blocking page" }, "docblockedPrompt2": { - "message": "Because of the following filter:", + "message": "පහත පෙරහන නිසා:", "description": "Used in the strict-blocking page" }, "docblockedNoParamsPrompt": { - "message": "without parameters", + "message": "පරාමිතීන් නොමැතිව", "description": "label to be used for the parameter-less URL: https://cloud.githubusercontent.com/assets/585534/9832014/bfb1b8f0-593b-11e5-8a27-fba472a5529a.png" }, "docblockedFoundIn": { @@ -1172,11 +1172,11 @@ "description": "English: Close this window" }, "docblockedDontWarn": { - "message": "Don't warn me again about this site", + "message": "මෙම අඩවිය ගැන මට නැවත අනතුරු අඟවන්න එපා.", "description": "Label for checkbox in document-blocked page" }, "docblockedProceed": { - "message": "Disable strict blocking for {{hostname}}", + "message": "{{hostname}}සඳහා දැඩි අවහිර කිරීම අක්‍රීය කරන්න", "description": "English: Disable strict blocking for {{hostname}} ..." }, "docblockedDisableTemporary": { @@ -1188,11 +1188,11 @@ "description": "English: Permanently" }, "docblockedDisable": { - "message": "Proceed", + "message": "ඉදිරියට", "description": "Button text to navigate to the blocked page" }, "docblockedRedirectPrompt": { - "message": "The blocked page wants to redirect to another site. If you choose to proceed, you will navigate directly to: {{url}}", + "message": "අවහිර කළ පිටුව වෙනත් අඩවියකට හරවා යැවීමට අවශ්‍යයි. ඔබ ඉදිරියට යාමට තෝරා ගන්නේ නම්, ඔබ කෙලින්ම මෙහි සංචාලනය කරනු ඇත: {{url}}", "description": "Text warning about an incoming redirect" }, "cloudPush": { @@ -1236,15 +1236,15 @@ "description": "" }, "contextMenuBlockElementInFrame": { - "message": "Block element in frame…", + "message": "රාමුවේ බ්ලොක් මූලද්‍රව්‍යය…", "description": "An entry in the browser's contextual menu" }, "contextMenuSubscribeToList": { - "message": "Subscribe to filter list…", + "message": "පෙරහන් ලැයිස්තුවට දායක වන්න…", "description": "An entry in the browser's contextual menu" }, "contextMenuTemporarilyAllowLargeMediaElements": { - "message": "Temporarily allow large media elements", + "message": "තාවකාලිකව විශාල මාධ්‍ය අංගවලට ඉඩ දෙන්න.", "description": "A context menu entry, present when large media elements have been blocked on the current site" }, "contextMenuViewSource": { @@ -1256,7 +1256,7 @@ "description": "Placeholder string for input field used to capture a keyboard shortcut" }, "genericMergeViewScrollLock": { - "message": "Toggle locked scrolling", + "message": "අගුළු දැමූ අනුචලනය ටොගල් කරන්න", "description": "Tooltip for the button used to lock scrolling between the views in the 'My rules' pane" }, "genericCopyToClipboard": { @@ -1268,15 +1268,15 @@ "description": "Label for buttons used to select all text in editor" }, "toggleCosmeticFiltering": { - "message": "Toggle cosmetic filtering", + "message": "රූපලාවන්‍ය පෙරහන ටොගල් කරන්න", "description": "Label for keyboard shortcut used to toggle cosmetic filtering" }, "toggleJavascript": { - "message": "Toggle JavaScript", + "message": "ජාවාස්ක්‍රිප්ට් ටොගල් කරන්න", "description": "Label for keyboard shortcut used to toggle no-scripting switch" }, "relaxBlockingMode": { - "message": "Relax blocking mode", + "message": "අවහිර කිරීමේ මාදිලිය ලිහිල් කරන්න", "description": "Label for keyboard shortcut used to relax blocking mode" }, "storageUsed": { @@ -1304,7 +1304,7 @@ "description": "Summary of number of errors as reported by the linter " }, "unprocessedRequestTooltip": { - "message": "Could not filter properly at browser launch. Reload the page to ensure proper filtering.", + "message": "බ්‍රව්සරය දියත් කිරීමේදී නිසි ලෙස පෙරීමට නොහැකි විය. නිසි පෙරහන සහතික කිරීම සඳහා පිටුව නැවත පූරණය කරන්න.", "description": "A warning which will appear in the popup panel if needed" }, "dummy": { From 686eefd6b080e611c6a19642b279ea85896a7465 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sun, 25 May 2025 18:55:39 -0400 Subject: [PATCH 0963/1099] [mv3] Remove CERT.PL's Warning List --- platform/mv3/rulesets.json | 15 +-------------- 1 file changed, 1 insertion(+), 14 deletions(-) diff --git a/platform/mv3/rulesets.json b/platform/mv3/rulesets.json index ac581ac7b2bcc..2a38196304d36 100644 --- a/platform/mv3/rulesets.json +++ b/platform/mv3/rulesets.json @@ -466,7 +466,7 @@ { "id": "pol-0", "group": "regions", - "parent": "🇵🇱pl: Oficjalne Polskie Filtry", + "parent": null, "lang": "szl pl _", "name": "🇵🇱pl: Oficjalne Polskie Filtry do uBlocka Origin", "tags": "ads polish polski", @@ -476,19 +476,6 @@ ], "homeURL": "https://github.com/MajkiIT/polish-ads-filter" }, - { - "id": "pol-3", - "group": "regions", - "parent": "🇵🇱pl: Oficjalne Polskie Filtry", - "lang": "szl pl", - "name": "🇵🇱pl: CERT.PL's Warning List", - "tags": "malware polish polski", - "enabled": false, - "urls": [ - "https://hole.cert.pl/domains/v2/domains_adblock.txt" - ], - "homeURL": "https://cert.pl/lista-ostrzezen/" - }, { "id": "rou-1", "group": "regions", From 4e2585545b930197f04137ed870cc68e2b740319 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sun, 25 May 2025 19:10:18 -0400 Subject: [PATCH 0964/1099] [mv3] This is not needed for uBOL --- platform/mv3/rulesets.json | 1 - 1 file changed, 1 deletion(-) diff --git a/platform/mv3/rulesets.json b/platform/mv3/rulesets.json index 2a38196304d36..8f8a6f8f208eb 100644 --- a/platform/mv3/rulesets.json +++ b/platform/mv3/rulesets.json @@ -466,7 +466,6 @@ { "id": "pol-0", "group": "regions", - "parent": null, "lang": "szl pl _", "name": "🇵🇱pl: Oficjalne Polskie Filtry do uBlocka Origin", "tags": "ads polish polski", From 9339a759528d0e80dd7a91c8b660756b101f964d Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Thu, 29 May 2025 09:06:02 -0400 Subject: [PATCH 0965/1099] [mv3] Add support for custom DNR rules This feature is hidden behind the "Developer mode" setting in the dashboard. When "Developer mode" is enabled, a tab named "Develop" will become available in the dashboard. This tab is meant to contain tools for technical users. At the moment, the "Develop" pane allows to create custom DNR rules through a (CodeMirror-based) editor. For the sake of convenience, the DNR rule must be entered in YAML-like format. The format is not really full compliant YAML, just YAML-like, and very strict in order to ensure the parser stays simple enough. Lines starting with `#` are comments and will be ignored by the parser. Any line which do not match the parser's expectation will be marked as invalid, and the whole DNR rule containing such invalid lines will be discarded. There must not be empty lines inside a rule definition. Each DNR rule must be separated with a `---` line, which is known as a YAML document separator. String values must not be quoted, otherwise the quotes will be considered part of the value. There is one exception: `''` will be parsed as "an empty string". The editor will attempt to auto-complete known DNR keywords. That feature will improve over time. Though the parser will identify some errors, not all invalid DNR rules are currently identified by the parser, and these will be reported when the rules are registered through the DNR API. Better identifying invalid DNR rules at edit time will improve over time. The editor will report `regexFilter` values which are not supported by the DNR engine on the current platform. The editor reacts to instances of `regexFilter: ...` to report whether a regex value is supported. This means you can test for a regex value by using `# regexFilter: ...` so that you do not have to create an actual DNR rules just for the sake of testing. Custom DNR rules can be exported into a JSON file (a format known by the DNR API as a "static ruleset"). JSON-based ruleset can be imported, the content will be converted to YAML-like syntax. The editor will attempt to convert to YAML pasted content which can be JSON-parsed. It's possible to paste partially or wholly JSON-based rulesets. When disabling "Developer mode", all custom DNR rules will be unregistered from the DNR API. The DNR rules content will be left intact in such case. Existing DNR rules will be registered into the DNR API when re-enabling "Developer mode". Administrators can prevent "Developer mode" from being enabled by adding `develop` token to `disabledFeatures` setting. Related discussion: https://github.com/uBlockOrigin/uBOL-home/discussions/323 The main motivation is to give list maintainers a tool to assist with resolving filter issues. Custom DNR rules can assist in crafting and validating filters meant to work with uBOL. A secondary motivation is to provide technical users the ability to further customize their content blocker. More conveniences will be added over time, this is a first version. --- Makefile | 19 +- .../mv3/extension/_locales/ar/messages.json | 28 + .../mv3/extension/_locales/az/messages.json | 28 + .../mv3/extension/_locales/be/messages.json | 28 + .../mv3/extension/_locales/bg/messages.json | 28 + .../mv3/extension/_locales/bn/messages.json | 28 + .../extension/_locales/br_FR/messages.json | 28 + .../mv3/extension/_locales/bs/messages.json | 28 + .../mv3/extension/_locales/ca/messages.json | 28 + .../mv3/extension/_locales/cs/messages.json | 28 + .../mv3/extension/_locales/cv/messages.json | 28 + .../mv3/extension/_locales/cy/messages.json | 28 + .../mv3/extension/_locales/da/messages.json | 28 + .../mv3/extension/_locales/de/messages.json | 28 + .../mv3/extension/_locales/el/messages.json | 28 + .../mv3/extension/_locales/en/messages.json | 32 + .../extension/_locales/en_GB/messages.json | 28 + .../mv3/extension/_locales/eo/messages.json | 28 + .../mv3/extension/_locales/es/messages.json | 28 + .../mv3/extension/_locales/et/messages.json | 28 + .../mv3/extension/_locales/eu/messages.json | 28 + .../mv3/extension/_locales/fa/messages.json | 28 + .../mv3/extension/_locales/fi/messages.json | 28 + .../mv3/extension/_locales/fil/messages.json | 28 + .../mv3/extension/_locales/fr/messages.json | 28 + .../mv3/extension/_locales/fy/messages.json | 28 + .../mv3/extension/_locales/gl/messages.json | 28 + .../mv3/extension/_locales/gu/messages.json | 28 + .../mv3/extension/_locales/he/messages.json | 28 + .../mv3/extension/_locales/hi/messages.json | 28 + .../mv3/extension/_locales/hr/messages.json | 28 + .../mv3/extension/_locales/hu/messages.json | 28 + .../mv3/extension/_locales/hy/messages.json | 28 + .../mv3/extension/_locales/id/messages.json | 28 + .../mv3/extension/_locales/it/messages.json | 28 + .../mv3/extension/_locales/ja/messages.json | 28 + .../mv3/extension/_locales/ka/messages.json | 28 + .../mv3/extension/_locales/kk/messages.json | 28 + .../mv3/extension/_locales/kn/messages.json | 28 + .../mv3/extension/_locales/ko/messages.json | 28 + .../mv3/extension/_locales/lt/messages.json | 28 + .../mv3/extension/_locales/lv/messages.json | 28 + .../mv3/extension/_locales/mk/messages.json | 28 + .../mv3/extension/_locales/ml/messages.json | 28 + .../mv3/extension/_locales/mr/messages.json | 28 + .../mv3/extension/_locales/ms/messages.json | 28 + .../mv3/extension/_locales/nb/messages.json | 28 + .../mv3/extension/_locales/nl/messages.json | 28 + .../mv3/extension/_locales/oc/messages.json | 28 + .../mv3/extension/_locales/pa/messages.json | 28 + .../mv3/extension/_locales/pl/messages.json | 28 + .../extension/_locales/pt_BR/messages.json | 28 + .../extension/_locales/pt_PT/messages.json | 28 + .../mv3/extension/_locales/ro/messages.json | 28 + .../mv3/extension/_locales/ru/messages.json | 28 + .../mv3/extension/_locales/si/messages.json | 28 + .../mv3/extension/_locales/sk/messages.json | 28 + .../mv3/extension/_locales/sl/messages.json | 28 + .../mv3/extension/_locales/so/messages.json | 28 + .../mv3/extension/_locales/sq/messages.json | 28 + .../mv3/extension/_locales/sr/messages.json | 28 + .../mv3/extension/_locales/sv/messages.json | 28 + .../mv3/extension/_locales/sw/messages.json | 28 + .../mv3/extension/_locales/ta/messages.json | 28 + .../mv3/extension/_locales/te/messages.json | 28 + .../mv3/extension/_locales/th/messages.json | 28 + .../mv3/extension/_locales/tr/messages.json | 28 + .../mv3/extension/_locales/uk/messages.json | 28 + .../mv3/extension/_locales/ur/messages.json | 28 + .../mv3/extension/_locales/vi/messages.json | 28 + .../extension/_locales/zh_CN/messages.json | 28 + .../extension/_locales/zh_TW/messages.json | 28 + .../mv3/extension/css/dashboard-common.css | 6 + platform/mv3/extension/css/dashboard.css | 19 +- platform/mv3/extension/css/develop.css | 112 +++ platform/mv3/extension/css/settings.css | 3 + platform/mv3/extension/dashboard.html | 41 +- platform/mv3/extension/js/background.js | 33 +- platform/mv3/extension/js/dashboard.js | 19 +- platform/mv3/extension/js/develop.js | 687 ++++++++++++++++++ platform/mv3/extension/js/dnr-parser.js | 607 ++++++++++++++++ platform/mv3/extension/js/ext-compat.js | 6 +- platform/mv3/extension/js/ruleset-manager.js | 118 ++- platform/mv3/extension/js/settings.js | 34 +- .../extension/lib/codemirror/codemirror-ubol | 2 +- platform/mv3/safari/ext-compat.js | 4 +- src/css/common.css | 3 +- tools/jsonpath-tool.html | 67 +- tools/make-mv3.sh | 2 + 89 files changed, 3700 insertions(+), 74 deletions(-) create mode 100644 platform/mv3/extension/css/develop.css create mode 100644 platform/mv3/extension/js/develop.js create mode 100644 platform/mv3/extension/js/dnr-parser.js diff --git a/Makefile b/Makefile index 2ce9beb54a436..ddec8e4dca6bc 100644 --- a/Makefile +++ b/Makefile @@ -2,13 +2,17 @@ run_options := $(filter-out $@,$(MAKECMDGOALS)) .PHONY: all clean cleanassets test lint chromium opera firefox npm dig \ - mv3-chromium mv3-firefox mv3-edge mv3-safari \ + mv3-chromium mv3-firefox mv3-edge mv3-safari ubol-codemirror \ compare maxcost medcost mincost modifiers record wasm sources := ./dist/version $(shell find ./assets -type f) $(shell find ./src -type f) platform := $(wildcard platform/*/*) assets := dist/build/uAssets -mv3-sources := $(shell find ./src -type f) $(wildcard platform/mv3/*) $(shell find ./platform/mv3/extension -type f) +mv3-sources := \ + $(shell find ./src -type f) \ + $(wildcard platform/mv3/*) \ + $(shell find ./platform/mv3/extension -name codemirror-ubol -prune -o -type f) \ + platform/mv3/extension/lib/codemirror/codemirror-ubol/dist/cm6.bundle.ubol.min.js mv3-data := $(shell find ./dist/build/mv3-data -type f) mv3-edge-deps := $(wildcard platform/mv3/edge/*) @@ -69,25 +73,28 @@ dig-snfe: dig dist/build/mv3-data: mkdir -p dist/build/mv3-data +ubol-codemirror: + $(MAKE) -sC platform/mv3/extension/lib/codemirror/codemirror-ubol/ ubol.bundle + dist/build/uBOLite.chromium: tools/make-mv3.sh $(mv3-sources) $(platform) $(mv3-data) dist/build/mv3-data tools/make-mv3.sh chromium -mv3-chromium: dist/build/uBOLite.chromium +mv3-chromium: ubol-codemirror dist/build/uBOLite.chromium dist/build/uBOLite.firefox: tools/make-mv3.sh $(mv3-sources) $(platform) $(mv3-data) dist/build/mv3-data tools/make-mv3.sh firefox -mv3-firefox: dist/build/uBOLite.firefox +mv3-firefox: ubol-codemirror dist/build/uBOLite.firefox dist/build/uBOLite.edge: tools/make-mv3.sh $(mv3-sources) $(mv3-edge-deps) $(mv3-data) dist/build/mv3-data tools/make-mv3.sh edge -mv3-edge: dist/build/uBOLite.edge +mv3-edge: ubol-codemirror dist/build/uBOLite.edge dist/build/uBOLite.safari: tools/make-mv3.sh $(mv3-sources) $(mv3-safari-deps) $(mv3-data) dist/build/mv3-data tools/make-mv3.sh safari -mv3-safari: dist/build/uBOLite.safari +mv3-safari: ubol-codemirror dist/build/uBOLite.safari dist/build/uAssets: tools/pull-assets.sh diff --git a/platform/mv3/extension/_locales/ar/messages.json b/platform/mv3/extension/_locales/ar/messages.json index 58bd71e0059c7..f7b0a4906e3ac 100644 --- a/platform/mv3/extension/_locales/ar/messages.json +++ b/platform/mv3/extension/_locales/ar/messages.json @@ -233,8 +233,16 @@ }, "enableStrictBlockLegend": { "message": "سيتم حظر التنقل إلى المواقع غير المرغوب فيها، وسيتم تقديم خيار لك للمتابعة.", + "description": "Short description for a checkbox in the options page" + }, + "developerModeLabel": { + "message": "Developer mode", "description": "Label for a checkbox in the options page" }, + "developerModeLegend": { + "message": "Enable access to features suitable for technical users.", + "description": "Short description for a checkbox in the options page" + }, "findListsPlaceholder": { "message": "البحث عن القوائم", "description": "Placeholder for the input field used to find lists" @@ -282,5 +290,25 @@ "zapperTipQuit": { "message": "Exit element zapper mode", "description": "Tooltip for the button used to exit zapper mode" + }, + "saveButton": { + "message": "Save", + "description": "Text for buttons used to save changes" + }, + "revertButton": { + "message": "Revert", + "description": "Text for buttons used to revert changes" + }, + "importAndAppendButton": { + "message": "Import and append…", + "description": "Text for buttons used to import and append content" + }, + "exportButton": { + "message": "Export…", + "description": "Text for buttons used to export content" + }, + "dnrRulesSummary": { + "message": "Enter your own DNR rules below. Do not add content from untrusted sources.", + "description": "Short description of the DNR rules editor pane" } } diff --git a/platform/mv3/extension/_locales/az/messages.json b/platform/mv3/extension/_locales/az/messages.json index 31968c5585134..27f7fc1b6cf44 100644 --- a/platform/mv3/extension/_locales/az/messages.json +++ b/platform/mv3/extension/_locales/az/messages.json @@ -233,8 +233,16 @@ }, "enableStrictBlockLegend": { "message": "Navigation to potentially undesirable sites will be blocked, and you will be offered the option to proceed.", + "description": "Short description for a checkbox in the options page" + }, + "developerModeLabel": { + "message": "Developer mode", "description": "Label for a checkbox in the options page" }, + "developerModeLegend": { + "message": "Enable access to features suitable for technical users.", + "description": "Short description for a checkbox in the options page" + }, "findListsPlaceholder": { "message": "Find lists", "description": "Placeholder for the input field used to find lists" @@ -282,5 +290,25 @@ "zapperTipQuit": { "message": "Exit element zapper mode", "description": "Tooltip for the button used to exit zapper mode" + }, + "saveButton": { + "message": "Save", + "description": "Text for buttons used to save changes" + }, + "revertButton": { + "message": "Revert", + "description": "Text for buttons used to revert changes" + }, + "importAndAppendButton": { + "message": "Import and append…", + "description": "Text for buttons used to import and append content" + }, + "exportButton": { + "message": "Export…", + "description": "Text for buttons used to export content" + }, + "dnrRulesSummary": { + "message": "Enter your own DNR rules below. Do not add content from untrusted sources.", + "description": "Short description of the DNR rules editor pane" } } diff --git a/platform/mv3/extension/_locales/be/messages.json b/platform/mv3/extension/_locales/be/messages.json index c41e4e05af16e..b35b66c6531a4 100644 --- a/platform/mv3/extension/_locales/be/messages.json +++ b/platform/mv3/extension/_locales/be/messages.json @@ -233,8 +233,16 @@ }, "enableStrictBlockLegend": { "message": "Пераход да патэнцыйна непажаданых сайтаў будзе заблакаваны, і вам будзе прапанавана магчымасць працягнуць.", + "description": "Short description for a checkbox in the options page" + }, + "developerModeLabel": { + "message": "Developer mode", "description": "Label for a checkbox in the options page" }, + "developerModeLegend": { + "message": "Enable access to features suitable for technical users.", + "description": "Short description for a checkbox in the options page" + }, "findListsPlaceholder": { "message": "Знайсці спісы", "description": "Placeholder for the input field used to find lists" @@ -282,5 +290,25 @@ "zapperTipQuit": { "message": "Выйсці з рэжыму імгненнага хавання элементаў", "description": "Tooltip for the button used to exit zapper mode" + }, + "saveButton": { + "message": "Save", + "description": "Text for buttons used to save changes" + }, + "revertButton": { + "message": "Revert", + "description": "Text for buttons used to revert changes" + }, + "importAndAppendButton": { + "message": "Import and append…", + "description": "Text for buttons used to import and append content" + }, + "exportButton": { + "message": "Export…", + "description": "Text for buttons used to export content" + }, + "dnrRulesSummary": { + "message": "Enter your own DNR rules below. Do not add content from untrusted sources.", + "description": "Short description of the DNR rules editor pane" } } diff --git a/platform/mv3/extension/_locales/bg/messages.json b/platform/mv3/extension/_locales/bg/messages.json index fde7add85fcaa..753cd2fe9fe31 100644 --- a/platform/mv3/extension/_locales/bg/messages.json +++ b/platform/mv3/extension/_locales/bg/messages.json @@ -233,8 +233,16 @@ }, "enableStrictBlockLegend": { "message": "Навигацията към потенциално нежелани сайтове ще бъде блокирана и ще ви бъде предложена възможността да продължите.", + "description": "Short description for a checkbox in the options page" + }, + "developerModeLabel": { + "message": "Developer mode", "description": "Label for a checkbox in the options page" }, + "developerModeLegend": { + "message": "Enable access to features suitable for technical users.", + "description": "Short description for a checkbox in the options page" + }, "findListsPlaceholder": { "message": "Намиране на списъци", "description": "Placeholder for the input field used to find lists" @@ -282,5 +290,25 @@ "zapperTipQuit": { "message": "Излизане от режима на временно скриване на елемента", "description": "Tooltip for the button used to exit zapper mode" + }, + "saveButton": { + "message": "Save", + "description": "Text for buttons used to save changes" + }, + "revertButton": { + "message": "Revert", + "description": "Text for buttons used to revert changes" + }, + "importAndAppendButton": { + "message": "Import and append…", + "description": "Text for buttons used to import and append content" + }, + "exportButton": { + "message": "Export…", + "description": "Text for buttons used to export content" + }, + "dnrRulesSummary": { + "message": "Enter your own DNR rules below. Do not add content from untrusted sources.", + "description": "Short description of the DNR rules editor pane" } } diff --git a/platform/mv3/extension/_locales/bn/messages.json b/platform/mv3/extension/_locales/bn/messages.json index 9bad885a7b883..bc87ca5830b7d 100644 --- a/platform/mv3/extension/_locales/bn/messages.json +++ b/platform/mv3/extension/_locales/bn/messages.json @@ -233,8 +233,16 @@ }, "enableStrictBlockLegend": { "message": "সম্ভাব্য অযাচিত ওয়েবসাইট অবরুদ্ধ করা হবে, আর সেখানে আগানোর উপায় দেওয়া থাকবে।", + "description": "Short description for a checkbox in the options page" + }, + "developerModeLabel": { + "message": "Developer mode", "description": "Label for a checkbox in the options page" }, + "developerModeLegend": { + "message": "Enable access to features suitable for technical users.", + "description": "Short description for a checkbox in the options page" + }, "findListsPlaceholder": { "message": "তালিকা খুঁজো", "description": "Placeholder for the input field used to find lists" @@ -282,5 +290,25 @@ "zapperTipQuit": { "message": "Exit element zapper mode", "description": "Tooltip for the button used to exit zapper mode" + }, + "saveButton": { + "message": "Save", + "description": "Text for buttons used to save changes" + }, + "revertButton": { + "message": "Revert", + "description": "Text for buttons used to revert changes" + }, + "importAndAppendButton": { + "message": "Import and append…", + "description": "Text for buttons used to import and append content" + }, + "exportButton": { + "message": "Export…", + "description": "Text for buttons used to export content" + }, + "dnrRulesSummary": { + "message": "Enter your own DNR rules below. Do not add content from untrusted sources.", + "description": "Short description of the DNR rules editor pane" } } diff --git a/platform/mv3/extension/_locales/br_FR/messages.json b/platform/mv3/extension/_locales/br_FR/messages.json index e8fbb70c55217..72e2d5e8f93d2 100644 --- a/platform/mv3/extension/_locales/br_FR/messages.json +++ b/platform/mv3/extension/_locales/br_FR/messages.json @@ -233,8 +233,16 @@ }, "enableStrictBlockLegend": { "message": "Stanket e vo ar merdeiñ etrezek lec'hiennoù a c'hallfe bezañ dañjerus, ha moaien a vo deoc'h dibab da genderc'hel pe get.", + "description": "Short description for a checkbox in the options page" + }, + "developerModeLabel": { + "message": "Developer mode", "description": "Label for a checkbox in the options page" }, + "developerModeLegend": { + "message": "Enable access to features suitable for technical users.", + "description": "Short description for a checkbox in the options page" + }, "findListsPlaceholder": { "message": "Kavout rolloù", "description": "Placeholder for the input field used to find lists" @@ -282,5 +290,25 @@ "zapperTipQuit": { "message": "Kuitaat ar mod \"dilemel elfennoù\"", "description": "Tooltip for the button used to exit zapper mode" + }, + "saveButton": { + "message": "Save", + "description": "Text for buttons used to save changes" + }, + "revertButton": { + "message": "Revert", + "description": "Text for buttons used to revert changes" + }, + "importAndAppendButton": { + "message": "Import and append…", + "description": "Text for buttons used to import and append content" + }, + "exportButton": { + "message": "Export…", + "description": "Text for buttons used to export content" + }, + "dnrRulesSummary": { + "message": "Enter your own DNR rules below. Do not add content from untrusted sources.", + "description": "Short description of the DNR rules editor pane" } } diff --git a/platform/mv3/extension/_locales/bs/messages.json b/platform/mv3/extension/_locales/bs/messages.json index 2694cb07412bd..bded4c96425c5 100644 --- a/platform/mv3/extension/_locales/bs/messages.json +++ b/platform/mv3/extension/_locales/bs/messages.json @@ -233,8 +233,16 @@ }, "enableStrictBlockLegend": { "message": "Navigation to potentially undesirable sites will be blocked, and you will be offered the option to proceed.", + "description": "Short description for a checkbox in the options page" + }, + "developerModeLabel": { + "message": "Developer mode", "description": "Label for a checkbox in the options page" }, + "developerModeLegend": { + "message": "Enable access to features suitable for technical users.", + "description": "Short description for a checkbox in the options page" + }, "findListsPlaceholder": { "message": "Find lists", "description": "Placeholder for the input field used to find lists" @@ -282,5 +290,25 @@ "zapperTipQuit": { "message": "Exit element zapper mode", "description": "Tooltip for the button used to exit zapper mode" + }, + "saveButton": { + "message": "Save", + "description": "Text for buttons used to save changes" + }, + "revertButton": { + "message": "Revert", + "description": "Text for buttons used to revert changes" + }, + "importAndAppendButton": { + "message": "Import and append…", + "description": "Text for buttons used to import and append content" + }, + "exportButton": { + "message": "Export…", + "description": "Text for buttons used to export content" + }, + "dnrRulesSummary": { + "message": "Enter your own DNR rules below. Do not add content from untrusted sources.", + "description": "Short description of the DNR rules editor pane" } } diff --git a/platform/mv3/extension/_locales/ca/messages.json b/platform/mv3/extension/_locales/ca/messages.json index 1c65daf6fd059..250e1308830f7 100644 --- a/platform/mv3/extension/_locales/ca/messages.json +++ b/platform/mv3/extension/_locales/ca/messages.json @@ -233,8 +233,16 @@ }, "enableStrictBlockLegend": { "message": "Es blocarà la navegació en webs potencialment no desitjables, oferint-vos la possibilitat de continuar.", + "description": "Short description for a checkbox in the options page" + }, + "developerModeLabel": { + "message": "Developer mode", "description": "Label for a checkbox in the options page" }, + "developerModeLegend": { + "message": "Enable access to features suitable for technical users.", + "description": "Short description for a checkbox in the options page" + }, "findListsPlaceholder": { "message": "Cerca llistes", "description": "Placeholder for the input field used to find lists" @@ -282,5 +290,25 @@ "zapperTipQuit": { "message": "Surt del mode d'eliminació d'elements", "description": "Tooltip for the button used to exit zapper mode" + }, + "saveButton": { + "message": "Save", + "description": "Text for buttons used to save changes" + }, + "revertButton": { + "message": "Revert", + "description": "Text for buttons used to revert changes" + }, + "importAndAppendButton": { + "message": "Import and append…", + "description": "Text for buttons used to import and append content" + }, + "exportButton": { + "message": "Export…", + "description": "Text for buttons used to export content" + }, + "dnrRulesSummary": { + "message": "Enter your own DNR rules below. Do not add content from untrusted sources.", + "description": "Short description of the DNR rules editor pane" } } diff --git a/platform/mv3/extension/_locales/cs/messages.json b/platform/mv3/extension/_locales/cs/messages.json index c60b63a9da1dc..43324e823d35e 100644 --- a/platform/mv3/extension/_locales/cs/messages.json +++ b/platform/mv3/extension/_locales/cs/messages.json @@ -233,8 +233,16 @@ }, "enableStrictBlockLegend": { "message": "Navigace na potenciálně nežádoucí stránky bude zablokována a bude vám nabídnuta možnost pokračovat.", + "description": "Short description for a checkbox in the options page" + }, + "developerModeLabel": { + "message": "Developer mode", "description": "Label for a checkbox in the options page" }, + "developerModeLegend": { + "message": "Enable access to features suitable for technical users.", + "description": "Short description for a checkbox in the options page" + }, "findListsPlaceholder": { "message": "Najít seznamy", "description": "Placeholder for the input field used to find lists" @@ -282,5 +290,25 @@ "zapperTipQuit": { "message": "Opustit režim dočasného skrytí prvků", "description": "Tooltip for the button used to exit zapper mode" + }, + "saveButton": { + "message": "Save", + "description": "Text for buttons used to save changes" + }, + "revertButton": { + "message": "Revert", + "description": "Text for buttons used to revert changes" + }, + "importAndAppendButton": { + "message": "Import and append…", + "description": "Text for buttons used to import and append content" + }, + "exportButton": { + "message": "Export…", + "description": "Text for buttons used to export content" + }, + "dnrRulesSummary": { + "message": "Enter your own DNR rules below. Do not add content from untrusted sources.", + "description": "Short description of the DNR rules editor pane" } } diff --git a/platform/mv3/extension/_locales/cv/messages.json b/platform/mv3/extension/_locales/cv/messages.json index ace2c1d9f1881..136a025b98095 100644 --- a/platform/mv3/extension/_locales/cv/messages.json +++ b/platform/mv3/extension/_locales/cv/messages.json @@ -233,8 +233,16 @@ }, "enableStrictBlockLegend": { "message": "Navigation to potentially undesirable sites will be blocked, and you will be offered the option to proceed.", + "description": "Short description for a checkbox in the options page" + }, + "developerModeLabel": { + "message": "Developer mode", "description": "Label for a checkbox in the options page" }, + "developerModeLegend": { + "message": "Enable access to features suitable for technical users.", + "description": "Short description for a checkbox in the options page" + }, "findListsPlaceholder": { "message": "Find lists", "description": "Placeholder for the input field used to find lists" @@ -282,5 +290,25 @@ "zapperTipQuit": { "message": "Exit element zapper mode", "description": "Tooltip for the button used to exit zapper mode" + }, + "saveButton": { + "message": "Save", + "description": "Text for buttons used to save changes" + }, + "revertButton": { + "message": "Revert", + "description": "Text for buttons used to revert changes" + }, + "importAndAppendButton": { + "message": "Import and append…", + "description": "Text for buttons used to import and append content" + }, + "exportButton": { + "message": "Export…", + "description": "Text for buttons used to export content" + }, + "dnrRulesSummary": { + "message": "Enter your own DNR rules below. Do not add content from untrusted sources.", + "description": "Short description of the DNR rules editor pane" } } diff --git a/platform/mv3/extension/_locales/cy/messages.json b/platform/mv3/extension/_locales/cy/messages.json index 65790d0d84bb4..e034be8972287 100644 --- a/platform/mv3/extension/_locales/cy/messages.json +++ b/platform/mv3/extension/_locales/cy/messages.json @@ -233,8 +233,16 @@ }, "enableStrictBlockLegend": { "message": "Navigation to potentially undesirable sites will be blocked, and you will be offered the option to proceed.", + "description": "Short description for a checkbox in the options page" + }, + "developerModeLabel": { + "message": "Developer mode", "description": "Label for a checkbox in the options page" }, + "developerModeLegend": { + "message": "Enable access to features suitable for technical users.", + "description": "Short description for a checkbox in the options page" + }, "findListsPlaceholder": { "message": "Find lists", "description": "Placeholder for the input field used to find lists" @@ -282,5 +290,25 @@ "zapperTipQuit": { "message": "Exit element zapper mode", "description": "Tooltip for the button used to exit zapper mode" + }, + "saveButton": { + "message": "Save", + "description": "Text for buttons used to save changes" + }, + "revertButton": { + "message": "Revert", + "description": "Text for buttons used to revert changes" + }, + "importAndAppendButton": { + "message": "Import and append…", + "description": "Text for buttons used to import and append content" + }, + "exportButton": { + "message": "Export…", + "description": "Text for buttons used to export content" + }, + "dnrRulesSummary": { + "message": "Enter your own DNR rules below. Do not add content from untrusted sources.", + "description": "Short description of the DNR rules editor pane" } } diff --git a/platform/mv3/extension/_locales/da/messages.json b/platform/mv3/extension/_locales/da/messages.json index 0422b4f556db9..8e0dc1661b0c2 100644 --- a/platform/mv3/extension/_locales/da/messages.json +++ b/platform/mv3/extension/_locales/da/messages.json @@ -233,8 +233,16 @@ }, "enableStrictBlockLegend": { "message": "Navigering til potentielt uønskede websteder blokeres, men man tilbydes muligheden for at fortsætte.", + "description": "Short description for a checkbox in the options page" + }, + "developerModeLabel": { + "message": "Developer mode", "description": "Label for a checkbox in the options page" }, + "developerModeLegend": { + "message": "Enable access to features suitable for technical users.", + "description": "Short description for a checkbox in the options page" + }, "findListsPlaceholder": { "message": "Find lister", "description": "Placeholder for the input field used to find lists" @@ -282,5 +290,25 @@ "zapperTipQuit": { "message": "Forlad elementdræber­tilstand", "description": "Tooltip for the button used to exit zapper mode" + }, + "saveButton": { + "message": "Save", + "description": "Text for buttons used to save changes" + }, + "revertButton": { + "message": "Revert", + "description": "Text for buttons used to revert changes" + }, + "importAndAppendButton": { + "message": "Import and append…", + "description": "Text for buttons used to import and append content" + }, + "exportButton": { + "message": "Export…", + "description": "Text for buttons used to export content" + }, + "dnrRulesSummary": { + "message": "Enter your own DNR rules below. Do not add content from untrusted sources.", + "description": "Short description of the DNR rules editor pane" } } diff --git a/platform/mv3/extension/_locales/de/messages.json b/platform/mv3/extension/_locales/de/messages.json index 0e73a10dbd5c5..22bb53da49665 100644 --- a/platform/mv3/extension/_locales/de/messages.json +++ b/platform/mv3/extension/_locales/de/messages.json @@ -233,8 +233,16 @@ }, "enableStrictBlockLegend": { "message": "Die Navigation zu potenziell unerwünschten Websites wird verhindert, jedoch wird eine Möglichkeit zum Fortfahren angeboten.", + "description": "Short description for a checkbox in the options page" + }, + "developerModeLabel": { + "message": "Developer mode", "description": "Label for a checkbox in the options page" }, + "developerModeLegend": { + "message": "Enable access to features suitable for technical users.", + "description": "Short description for a checkbox in the options page" + }, "findListsPlaceholder": { "message": "Listen suchen", "description": "Placeholder for the input field used to find lists" @@ -282,5 +290,25 @@ "zapperTipQuit": { "message": "Temporären Modus beenden", "description": "Tooltip for the button used to exit zapper mode" + }, + "saveButton": { + "message": "Save", + "description": "Text for buttons used to save changes" + }, + "revertButton": { + "message": "Revert", + "description": "Text for buttons used to revert changes" + }, + "importAndAppendButton": { + "message": "Import and append…", + "description": "Text for buttons used to import and append content" + }, + "exportButton": { + "message": "Export…", + "description": "Text for buttons used to export content" + }, + "dnrRulesSummary": { + "message": "Enter your own DNR rules below. Do not add content from untrusted sources.", + "description": "Short description of the DNR rules editor pane" } } diff --git a/platform/mv3/extension/_locales/el/messages.json b/platform/mv3/extension/_locales/el/messages.json index b451e0fde7a59..d9f343099f908 100644 --- a/platform/mv3/extension/_locales/el/messages.json +++ b/platform/mv3/extension/_locales/el/messages.json @@ -233,8 +233,16 @@ }, "enableStrictBlockLegend": { "message": "Θα απετραπεί η πρόσβαση σε πιθανά ανεπιθύμητους ιστοτόπους. Θα σας προσφερθεί η επιλογή να συνεχίσετε.", + "description": "Short description for a checkbox in the options page" + }, + "developerModeLabel": { + "message": "Developer mode", "description": "Label for a checkbox in the options page" }, + "developerModeLegend": { + "message": "Enable access to features suitable for technical users.", + "description": "Short description for a checkbox in the options page" + }, "findListsPlaceholder": { "message": "Εύρεση λιστών", "description": "Placeholder for the input field used to find lists" @@ -282,5 +290,25 @@ "zapperTipQuit": { "message": "Έξοδος από λειτουργία αφαίρεσης στοιχείων", "description": "Tooltip for the button used to exit zapper mode" + }, + "saveButton": { + "message": "Save", + "description": "Text for buttons used to save changes" + }, + "revertButton": { + "message": "Revert", + "description": "Text for buttons used to revert changes" + }, + "importAndAppendButton": { + "message": "Import and append…", + "description": "Text for buttons used to import and append content" + }, + "exportButton": { + "message": "Export…", + "description": "Text for buttons used to export content" + }, + "dnrRulesSummary": { + "message": "Enter your own DNR rules below. Do not add content from untrusted sources.", + "description": "Short description of the DNR rules editor pane" } } diff --git a/platform/mv3/extension/_locales/en/messages.json b/platform/mv3/extension/_locales/en/messages.json index f4dc76f3cc4cb..03e28740de124 100644 --- a/platform/mv3/extension/_locales/en/messages.json +++ b/platform/mv3/extension/_locales/en/messages.json @@ -233,8 +233,16 @@ }, "enableStrictBlockLegend": { "message": "Navigation to potentially undesirable sites will be blocked, and you will be offered the option to proceed.", + "description": "Short description for a checkbox in the options page" + }, + "developerModeLabel": { + "message": "Developer mode", "description": "Label for a checkbox in the options page" }, + "developerModeLegend": { + "message": "Enables access to features suitable for technical users.", + "description": "Short description for a checkbox in the options page" + }, "findListsPlaceholder": { "message": "Find lists", "description": "Placeholder for the input field used to find lists" @@ -282,5 +290,29 @@ "zapperTipQuit": { "message": "Exit element zapper mode", "description": "Tooltip for the button used to exit zapper mode" + }, + "saveButton": { + "message": "Save", + "description": "Text for buttons used to save changes" + }, + "revertButton": { + "message": "Revert", + "description": "Text for buttons used to revert changes" + }, + "importAndAppendButton": { + "message": "Import and append…", + "description": "Text for buttons used to import and append content" + }, + "exportButton": { + "message": "Export…", + "description": "Text for buttons used to export content" + }, + "dnrRulesSummary": { + "message": "Enter your own DNR rules below. Do not add content from untrusted sources.", + "description": "Short description of the DNR rules editor pane" + }, + "dnrRulesCountInfo": { + "message": "Number of registered rules: {count}", + "description": "Short sentence to report the number of currently registered DNR rules" } } diff --git a/platform/mv3/extension/_locales/en_GB/messages.json b/platform/mv3/extension/_locales/en_GB/messages.json index 36e8955640a6b..f61236cc6d9f4 100644 --- a/platform/mv3/extension/_locales/en_GB/messages.json +++ b/platform/mv3/extension/_locales/en_GB/messages.json @@ -233,8 +233,16 @@ }, "enableStrictBlockLegend": { "message": "Navigation to potentially undesirable sites will be blocked, and you will be offered the option to proceed.", + "description": "Short description for a checkbox in the options page" + }, + "developerModeLabel": { + "message": "Developer mode", "description": "Label for a checkbox in the options page" }, + "developerModeLegend": { + "message": "Enable access to features suitable for technical users.", + "description": "Short description for a checkbox in the options page" + }, "findListsPlaceholder": { "message": "Find lists", "description": "Placeholder for the input field used to find lists" @@ -282,5 +290,25 @@ "zapperTipQuit": { "message": "Exit element zapper mode", "description": "Tooltip for the button used to exit zapper mode" + }, + "saveButton": { + "message": "Save", + "description": "Text for buttons used to save changes" + }, + "revertButton": { + "message": "Revert", + "description": "Text for buttons used to revert changes" + }, + "importAndAppendButton": { + "message": "Import and append…", + "description": "Text for buttons used to import and append content" + }, + "exportButton": { + "message": "Export…", + "description": "Text for buttons used to export content" + }, + "dnrRulesSummary": { + "message": "Enter your own DNR rules below. Do not add content from untrusted sources.", + "description": "Short description of the DNR rules editor pane" } } diff --git a/platform/mv3/extension/_locales/eo/messages.json b/platform/mv3/extension/_locales/eo/messages.json index 726dcd0bd70ad..acc785f1d8fb2 100644 --- a/platform/mv3/extension/_locales/eo/messages.json +++ b/platform/mv3/extension/_locales/eo/messages.json @@ -233,8 +233,16 @@ }, "enableStrictBlockLegend": { "message": "Navigation to potentially undesirable sites will be blocked, and you will be offered the option to proceed.", + "description": "Short description for a checkbox in the options page" + }, + "developerModeLabel": { + "message": "Developer mode", "description": "Label for a checkbox in the options page" }, + "developerModeLegend": { + "message": "Enable access to features suitable for technical users.", + "description": "Short description for a checkbox in the options page" + }, "findListsPlaceholder": { "message": "Find lists", "description": "Placeholder for the input field used to find lists" @@ -282,5 +290,25 @@ "zapperTipQuit": { "message": "Exit element zapper mode", "description": "Tooltip for the button used to exit zapper mode" + }, + "saveButton": { + "message": "Save", + "description": "Text for buttons used to save changes" + }, + "revertButton": { + "message": "Revert", + "description": "Text for buttons used to revert changes" + }, + "importAndAppendButton": { + "message": "Import and append…", + "description": "Text for buttons used to import and append content" + }, + "exportButton": { + "message": "Export…", + "description": "Text for buttons used to export content" + }, + "dnrRulesSummary": { + "message": "Enter your own DNR rules below. Do not add content from untrusted sources.", + "description": "Short description of the DNR rules editor pane" } } diff --git a/platform/mv3/extension/_locales/es/messages.json b/platform/mv3/extension/_locales/es/messages.json index 67af6f64091b3..13f08971f7b7d 100644 --- a/platform/mv3/extension/_locales/es/messages.json +++ b/platform/mv3/extension/_locales/es/messages.json @@ -233,8 +233,16 @@ }, "enableStrictBlockLegend": { "message": "La navegación a sitios potencialmente no deseados será bloqueada, y se te ofrecerá la opción de continuar.", + "description": "Short description for a checkbox in the options page" + }, + "developerModeLabel": { + "message": "Developer mode", "description": "Label for a checkbox in the options page" }, + "developerModeLegend": { + "message": "Enable access to features suitable for technical users.", + "description": "Short description for a checkbox in the options page" + }, "findListsPlaceholder": { "message": "Encontrar listas", "description": "Placeholder for the input field used to find lists" @@ -282,5 +290,25 @@ "zapperTipQuit": { "message": "Salir del modo eliminación de elementos", "description": "Tooltip for the button used to exit zapper mode" + }, + "saveButton": { + "message": "Save", + "description": "Text for buttons used to save changes" + }, + "revertButton": { + "message": "Revert", + "description": "Text for buttons used to revert changes" + }, + "importAndAppendButton": { + "message": "Import and append…", + "description": "Text for buttons used to import and append content" + }, + "exportButton": { + "message": "Export…", + "description": "Text for buttons used to export content" + }, + "dnrRulesSummary": { + "message": "Enter your own DNR rules below. Do not add content from untrusted sources.", + "description": "Short description of the DNR rules editor pane" } } diff --git a/platform/mv3/extension/_locales/et/messages.json b/platform/mv3/extension/_locales/et/messages.json index 7718b5b061a1d..3fe467a1de793 100644 --- a/platform/mv3/extension/_locales/et/messages.json +++ b/platform/mv3/extension/_locales/et/messages.json @@ -233,8 +233,16 @@ }, "enableStrictBlockLegend": { "message": "Kahtlastele veebilehtedele suunamist takistatakse ja pakutakse võimalust jätkamiseks.", + "description": "Short description for a checkbox in the options page" + }, + "developerModeLabel": { + "message": "Developer mode", "description": "Label for a checkbox in the options page" }, + "developerModeLegend": { + "message": "Enable access to features suitable for technical users.", + "description": "Short description for a checkbox in the options page" + }, "findListsPlaceholder": { "message": "Otsi nimekirju", "description": "Placeholder for the input field used to find lists" @@ -282,5 +290,25 @@ "zapperTipQuit": { "message": "Välju elemendi hävitusrežiimist", "description": "Tooltip for the button used to exit zapper mode" + }, + "saveButton": { + "message": "Save", + "description": "Text for buttons used to save changes" + }, + "revertButton": { + "message": "Revert", + "description": "Text for buttons used to revert changes" + }, + "importAndAppendButton": { + "message": "Import and append…", + "description": "Text for buttons used to import and append content" + }, + "exportButton": { + "message": "Export…", + "description": "Text for buttons used to export content" + }, + "dnrRulesSummary": { + "message": "Enter your own DNR rules below. Do not add content from untrusted sources.", + "description": "Short description of the DNR rules editor pane" } } diff --git a/platform/mv3/extension/_locales/eu/messages.json b/platform/mv3/extension/_locales/eu/messages.json index 9b189acec3fc0..22594ceb43f3c 100644 --- a/platform/mv3/extension/_locales/eu/messages.json +++ b/platform/mv3/extension/_locales/eu/messages.json @@ -233,8 +233,16 @@ }, "enableStrictBlockLegend": { "message": "Navigation to potentially undesirable sites will be blocked, and you will be offered the option to proceed.", + "description": "Short description for a checkbox in the options page" + }, + "developerModeLabel": { + "message": "Developer mode", "description": "Label for a checkbox in the options page" }, + "developerModeLegend": { + "message": "Enable access to features suitable for technical users.", + "description": "Short description for a checkbox in the options page" + }, "findListsPlaceholder": { "message": "Find lists", "description": "Placeholder for the input field used to find lists" @@ -282,5 +290,25 @@ "zapperTipQuit": { "message": "Irten elementua zapper moduan", "description": "Tooltip for the button used to exit zapper mode" + }, + "saveButton": { + "message": "Save", + "description": "Text for buttons used to save changes" + }, + "revertButton": { + "message": "Revert", + "description": "Text for buttons used to revert changes" + }, + "importAndAppendButton": { + "message": "Import and append…", + "description": "Text for buttons used to import and append content" + }, + "exportButton": { + "message": "Export…", + "description": "Text for buttons used to export content" + }, + "dnrRulesSummary": { + "message": "Enter your own DNR rules below. Do not add content from untrusted sources.", + "description": "Short description of the DNR rules editor pane" } } diff --git a/platform/mv3/extension/_locales/fa/messages.json b/platform/mv3/extension/_locales/fa/messages.json index 28c60c1b4536d..743cc736ad029 100644 --- a/platform/mv3/extension/_locales/fa/messages.json +++ b/platform/mv3/extension/_locales/fa/messages.json @@ -233,8 +233,16 @@ }, "enableStrictBlockLegend": { "message": "Navigation to potentially undesirable sites will be blocked, and you will be offered the option to proceed.", + "description": "Short description for a checkbox in the options page" + }, + "developerModeLabel": { + "message": "Developer mode", "description": "Label for a checkbox in the options page" }, + "developerModeLegend": { + "message": "Enable access to features suitable for technical users.", + "description": "Short description for a checkbox in the options page" + }, "findListsPlaceholder": { "message": "Find lists", "description": "Placeholder for the input field used to find lists" @@ -282,5 +290,25 @@ "zapperTipQuit": { "message": "Exit element zapper mode", "description": "Tooltip for the button used to exit zapper mode" + }, + "saveButton": { + "message": "Save", + "description": "Text for buttons used to save changes" + }, + "revertButton": { + "message": "Revert", + "description": "Text for buttons used to revert changes" + }, + "importAndAppendButton": { + "message": "Import and append…", + "description": "Text for buttons used to import and append content" + }, + "exportButton": { + "message": "Export…", + "description": "Text for buttons used to export content" + }, + "dnrRulesSummary": { + "message": "Enter your own DNR rules below. Do not add content from untrusted sources.", + "description": "Short description of the DNR rules editor pane" } } diff --git a/platform/mv3/extension/_locales/fi/messages.json b/platform/mv3/extension/_locales/fi/messages.json index 92bee243e9f9c..e29c903ff4993 100644 --- a/platform/mv3/extension/_locales/fi/messages.json +++ b/platform/mv3/extension/_locales/fi/messages.json @@ -233,8 +233,16 @@ }, "enableStrictBlockLegend": { "message": "Potentiaalisesti ei-toivottujen sivustojen avaaminen estetään mahdollisuudella jatkaa.", + "description": "Short description for a checkbox in the options page" + }, + "developerModeLabel": { + "message": "Developer mode", "description": "Label for a checkbox in the options page" }, + "developerModeLegend": { + "message": "Enable access to features suitable for technical users.", + "description": "Short description for a checkbox in the options page" + }, "findListsPlaceholder": { "message": "Etsi listoja", "description": "Placeholder for the input field used to find lists" @@ -282,5 +290,25 @@ "zapperTipQuit": { "message": "Exit element zapper mode", "description": "Tooltip for the button used to exit zapper mode" + }, + "saveButton": { + "message": "Save", + "description": "Text for buttons used to save changes" + }, + "revertButton": { + "message": "Revert", + "description": "Text for buttons used to revert changes" + }, + "importAndAppendButton": { + "message": "Import and append…", + "description": "Text for buttons used to import and append content" + }, + "exportButton": { + "message": "Export…", + "description": "Text for buttons used to export content" + }, + "dnrRulesSummary": { + "message": "Enter your own DNR rules below. Do not add content from untrusted sources.", + "description": "Short description of the DNR rules editor pane" } } diff --git a/platform/mv3/extension/_locales/fil/messages.json b/platform/mv3/extension/_locales/fil/messages.json index abd45087f3c6d..a9ed6840d1b5b 100644 --- a/platform/mv3/extension/_locales/fil/messages.json +++ b/platform/mv3/extension/_locales/fil/messages.json @@ -233,8 +233,16 @@ }, "enableStrictBlockLegend": { "message": "Navigation to potentially undesirable sites will be blocked, and you will be offered the option to proceed.", + "description": "Short description for a checkbox in the options page" + }, + "developerModeLabel": { + "message": "Developer mode", "description": "Label for a checkbox in the options page" }, + "developerModeLegend": { + "message": "Enable access to features suitable for technical users.", + "description": "Short description for a checkbox in the options page" + }, "findListsPlaceholder": { "message": "Find lists", "description": "Placeholder for the input field used to find lists" @@ -282,5 +290,25 @@ "zapperTipQuit": { "message": "Exit element zapper mode", "description": "Tooltip for the button used to exit zapper mode" + }, + "saveButton": { + "message": "Save", + "description": "Text for buttons used to save changes" + }, + "revertButton": { + "message": "Revert", + "description": "Text for buttons used to revert changes" + }, + "importAndAppendButton": { + "message": "Import and append…", + "description": "Text for buttons used to import and append content" + }, + "exportButton": { + "message": "Export…", + "description": "Text for buttons used to export content" + }, + "dnrRulesSummary": { + "message": "Enter your own DNR rules below. Do not add content from untrusted sources.", + "description": "Short description of the DNR rules editor pane" } } diff --git a/platform/mv3/extension/_locales/fr/messages.json b/platform/mv3/extension/_locales/fr/messages.json index 1b103c3fedeb9..7bc28e98aa5bf 100644 --- a/platform/mv3/extension/_locales/fr/messages.json +++ b/platform/mv3/extension/_locales/fr/messages.json @@ -233,8 +233,16 @@ }, "enableStrictBlockLegend": { "message": "La navigation vers des sites potentiellement indésirables sera bloquée, et vous aurez la possibilité de continuer", + "description": "Short description for a checkbox in the options page" + }, + "developerModeLabel": { + "message": "Developer mode", "description": "Label for a checkbox in the options page" }, + "developerModeLegend": { + "message": "Enable access to features suitable for technical users.", + "description": "Short description for a checkbox in the options page" + }, "findListsPlaceholder": { "message": "Trouver des listes", "description": "Placeholder for the input field used to find lists" @@ -282,5 +290,25 @@ "zapperTipQuit": { "message": "Quitter le mode Zappeur d'élément", "description": "Tooltip for the button used to exit zapper mode" + }, + "saveButton": { + "message": "Save", + "description": "Text for buttons used to save changes" + }, + "revertButton": { + "message": "Revert", + "description": "Text for buttons used to revert changes" + }, + "importAndAppendButton": { + "message": "Import and append…", + "description": "Text for buttons used to import and append content" + }, + "exportButton": { + "message": "Export…", + "description": "Text for buttons used to export content" + }, + "dnrRulesSummary": { + "message": "Enter your own DNR rules below. Do not add content from untrusted sources.", + "description": "Short description of the DNR rules editor pane" } } diff --git a/platform/mv3/extension/_locales/fy/messages.json b/platform/mv3/extension/_locales/fy/messages.json index 37f87477d313d..b2d0f0b1b8910 100644 --- a/platform/mv3/extension/_locales/fy/messages.json +++ b/platform/mv3/extension/_locales/fy/messages.json @@ -233,8 +233,16 @@ }, "enableStrictBlockLegend": { "message": "Navigaasje nei potinsjeel net-winske websites wurdt blokkearre, en jo krije de opsje om troch te gean.", + "description": "Short description for a checkbox in the options page" + }, + "developerModeLabel": { + "message": "Developer mode", "description": "Label for a checkbox in the options page" }, + "developerModeLegend": { + "message": "Enable access to features suitable for technical users.", + "description": "Short description for a checkbox in the options page" + }, "findListsPlaceholder": { "message": "Listen sykje", "description": "Placeholder for the input field used to find lists" @@ -282,5 +290,25 @@ "zapperTipQuit": { "message": "Elemint­wisker­modus slute", "description": "Tooltip for the button used to exit zapper mode" + }, + "saveButton": { + "message": "Save", + "description": "Text for buttons used to save changes" + }, + "revertButton": { + "message": "Revert", + "description": "Text for buttons used to revert changes" + }, + "importAndAppendButton": { + "message": "Import and append…", + "description": "Text for buttons used to import and append content" + }, + "exportButton": { + "message": "Export…", + "description": "Text for buttons used to export content" + }, + "dnrRulesSummary": { + "message": "Enter your own DNR rules below. Do not add content from untrusted sources.", + "description": "Short description of the DNR rules editor pane" } } diff --git a/platform/mv3/extension/_locales/gl/messages.json b/platform/mv3/extension/_locales/gl/messages.json index b6d4e00843ae3..4640caa738291 100644 --- a/platform/mv3/extension/_locales/gl/messages.json +++ b/platform/mv3/extension/_locales/gl/messages.json @@ -233,8 +233,16 @@ }, "enableStrictBlockLegend": { "message": "Vaise bloquear a navegación en webs potencialmente non desexables, e ofrecerase a opción de bloquealas.", + "description": "Short description for a checkbox in the options page" + }, + "developerModeLabel": { + "message": "Developer mode", "description": "Label for a checkbox in the options page" }, + "developerModeLegend": { + "message": "Enable access to features suitable for technical users.", + "description": "Short description for a checkbox in the options page" + }, "findListsPlaceholder": { "message": "Atopa listas", "description": "Placeholder for the input field used to find lists" @@ -282,5 +290,25 @@ "zapperTipQuit": { "message": "Sair do modo eliminador de elementos", "description": "Tooltip for the button used to exit zapper mode" + }, + "saveButton": { + "message": "Save", + "description": "Text for buttons used to save changes" + }, + "revertButton": { + "message": "Revert", + "description": "Text for buttons used to revert changes" + }, + "importAndAppendButton": { + "message": "Import and append…", + "description": "Text for buttons used to import and append content" + }, + "exportButton": { + "message": "Export…", + "description": "Text for buttons used to export content" + }, + "dnrRulesSummary": { + "message": "Enter your own DNR rules below. Do not add content from untrusted sources.", + "description": "Short description of the DNR rules editor pane" } } diff --git a/platform/mv3/extension/_locales/gu/messages.json b/platform/mv3/extension/_locales/gu/messages.json index ace2c1d9f1881..136a025b98095 100644 --- a/platform/mv3/extension/_locales/gu/messages.json +++ b/platform/mv3/extension/_locales/gu/messages.json @@ -233,8 +233,16 @@ }, "enableStrictBlockLegend": { "message": "Navigation to potentially undesirable sites will be blocked, and you will be offered the option to proceed.", + "description": "Short description for a checkbox in the options page" + }, + "developerModeLabel": { + "message": "Developer mode", "description": "Label for a checkbox in the options page" }, + "developerModeLegend": { + "message": "Enable access to features suitable for technical users.", + "description": "Short description for a checkbox in the options page" + }, "findListsPlaceholder": { "message": "Find lists", "description": "Placeholder for the input field used to find lists" @@ -282,5 +290,25 @@ "zapperTipQuit": { "message": "Exit element zapper mode", "description": "Tooltip for the button used to exit zapper mode" + }, + "saveButton": { + "message": "Save", + "description": "Text for buttons used to save changes" + }, + "revertButton": { + "message": "Revert", + "description": "Text for buttons used to revert changes" + }, + "importAndAppendButton": { + "message": "Import and append…", + "description": "Text for buttons used to import and append content" + }, + "exportButton": { + "message": "Export…", + "description": "Text for buttons used to export content" + }, + "dnrRulesSummary": { + "message": "Enter your own DNR rules below. Do not add content from untrusted sources.", + "description": "Short description of the DNR rules editor pane" } } diff --git a/platform/mv3/extension/_locales/he/messages.json b/platform/mv3/extension/_locales/he/messages.json index 7118496ce80a2..c2739016386c5 100644 --- a/platform/mv3/extension/_locales/he/messages.json +++ b/platform/mv3/extension/_locales/he/messages.json @@ -233,8 +233,16 @@ }, "enableStrictBlockLegend": { "message": "ניווט אפשרי לאתרים לא רצויים יחסם ותהיה אפשרות להחליט להמשיך.", + "description": "Short description for a checkbox in the options page" + }, + "developerModeLabel": { + "message": "Developer mode", "description": "Label for a checkbox in the options page" }, + "developerModeLegend": { + "message": "Enable access to features suitable for technical users.", + "description": "Short description for a checkbox in the options page" + }, "findListsPlaceholder": { "message": "חיפוש רשימות", "description": "Placeholder for the input field used to find lists" @@ -282,5 +290,25 @@ "zapperTipQuit": { "message": "Exit element zapper mode", "description": "Tooltip for the button used to exit zapper mode" + }, + "saveButton": { + "message": "Save", + "description": "Text for buttons used to save changes" + }, + "revertButton": { + "message": "Revert", + "description": "Text for buttons used to revert changes" + }, + "importAndAppendButton": { + "message": "Import and append…", + "description": "Text for buttons used to import and append content" + }, + "exportButton": { + "message": "Export…", + "description": "Text for buttons used to export content" + }, + "dnrRulesSummary": { + "message": "Enter your own DNR rules below. Do not add content from untrusted sources.", + "description": "Short description of the DNR rules editor pane" } } diff --git a/platform/mv3/extension/_locales/hi/messages.json b/platform/mv3/extension/_locales/hi/messages.json index f28896b99a88c..42fae58002f1e 100644 --- a/platform/mv3/extension/_locales/hi/messages.json +++ b/platform/mv3/extension/_locales/hi/messages.json @@ -233,8 +233,16 @@ }, "enableStrictBlockLegend": { "message": "Navigation to potentially undesirable sites will be blocked, and you will be offered the option to proceed.", + "description": "Short description for a checkbox in the options page" + }, + "developerModeLabel": { + "message": "Developer mode", "description": "Label for a checkbox in the options page" }, + "developerModeLegend": { + "message": "Enable access to features suitable for technical users.", + "description": "Short description for a checkbox in the options page" + }, "findListsPlaceholder": { "message": "Find lists", "description": "Placeholder for the input field used to find lists" @@ -282,5 +290,25 @@ "zapperTipQuit": { "message": "Exit element zapper mode", "description": "Tooltip for the button used to exit zapper mode" + }, + "saveButton": { + "message": "Save", + "description": "Text for buttons used to save changes" + }, + "revertButton": { + "message": "Revert", + "description": "Text for buttons used to revert changes" + }, + "importAndAppendButton": { + "message": "Import and append…", + "description": "Text for buttons used to import and append content" + }, + "exportButton": { + "message": "Export…", + "description": "Text for buttons used to export content" + }, + "dnrRulesSummary": { + "message": "Enter your own DNR rules below. Do not add content from untrusted sources.", + "description": "Short description of the DNR rules editor pane" } } diff --git a/platform/mv3/extension/_locales/hr/messages.json b/platform/mv3/extension/_locales/hr/messages.json index f3086f2632cae..ed9ea6c1da36d 100644 --- a/platform/mv3/extension/_locales/hr/messages.json +++ b/platform/mv3/extension/_locales/hr/messages.json @@ -233,8 +233,16 @@ }, "enableStrictBlockLegend": { "message": "Navigacija do potencijalno nepoželjnih stranica bit će blokirana i bit će vam ponuđena opcija za nastavak.", + "description": "Short description for a checkbox in the options page" + }, + "developerModeLabel": { + "message": "Developer mode", "description": "Label for a checkbox in the options page" }, + "developerModeLegend": { + "message": "Enable access to features suitable for technical users.", + "description": "Short description for a checkbox in the options page" + }, "findListsPlaceholder": { "message": "Pronađi liste", "description": "Placeholder for the input field used to find lists" @@ -282,5 +290,25 @@ "zapperTipQuit": { "message": "Zatvori način rada uklanjanja elementa", "description": "Tooltip for the button used to exit zapper mode" + }, + "saveButton": { + "message": "Save", + "description": "Text for buttons used to save changes" + }, + "revertButton": { + "message": "Revert", + "description": "Text for buttons used to revert changes" + }, + "importAndAppendButton": { + "message": "Import and append…", + "description": "Text for buttons used to import and append content" + }, + "exportButton": { + "message": "Export…", + "description": "Text for buttons used to export content" + }, + "dnrRulesSummary": { + "message": "Enter your own DNR rules below. Do not add content from untrusted sources.", + "description": "Short description of the DNR rules editor pane" } } diff --git a/platform/mv3/extension/_locales/hu/messages.json b/platform/mv3/extension/_locales/hu/messages.json index fa53a455024cb..d7eee7347cdb5 100644 --- a/platform/mv3/extension/_locales/hu/messages.json +++ b/platform/mv3/extension/_locales/hu/messages.json @@ -233,8 +233,16 @@ }, "enableStrictBlockLegend": { "message": "Az esetleges nem kívánatos webhelyekre való navigáció blokkolva lesz, és rákérdez arra, hogy folytatja-e.", + "description": "Short description for a checkbox in the options page" + }, + "developerModeLabel": { + "message": "Developer mode", "description": "Label for a checkbox in the options page" }, + "developerModeLegend": { + "message": "Enable access to features suitable for technical users.", + "description": "Short description for a checkbox in the options page" + }, "findListsPlaceholder": { "message": "Listák keresése", "description": "Placeholder for the input field used to find lists" @@ -282,5 +290,25 @@ "zapperTipQuit": { "message": "Kilépés az elemeltávolító módból", "description": "Tooltip for the button used to exit zapper mode" + }, + "saveButton": { + "message": "Save", + "description": "Text for buttons used to save changes" + }, + "revertButton": { + "message": "Revert", + "description": "Text for buttons used to revert changes" + }, + "importAndAppendButton": { + "message": "Import and append…", + "description": "Text for buttons used to import and append content" + }, + "exportButton": { + "message": "Export…", + "description": "Text for buttons used to export content" + }, + "dnrRulesSummary": { + "message": "Enter your own DNR rules below. Do not add content from untrusted sources.", + "description": "Short description of the DNR rules editor pane" } } diff --git a/platform/mv3/extension/_locales/hy/messages.json b/platform/mv3/extension/_locales/hy/messages.json index 60c32c4069b73..853b020711f1b 100644 --- a/platform/mv3/extension/_locales/hy/messages.json +++ b/platform/mv3/extension/_locales/hy/messages.json @@ -233,8 +233,16 @@ }, "enableStrictBlockLegend": { "message": "Navigation to potentially undesirable sites will be blocked, and you will be offered the option to proceed.", + "description": "Short description for a checkbox in the options page" + }, + "developerModeLabel": { + "message": "Developer mode", "description": "Label for a checkbox in the options page" }, + "developerModeLegend": { + "message": "Enable access to features suitable for technical users.", + "description": "Short description for a checkbox in the options page" + }, "findListsPlaceholder": { "message": "Find lists", "description": "Placeholder for the input field used to find lists" @@ -282,5 +290,25 @@ "zapperTipQuit": { "message": "Exit element zapper mode", "description": "Tooltip for the button used to exit zapper mode" + }, + "saveButton": { + "message": "Save", + "description": "Text for buttons used to save changes" + }, + "revertButton": { + "message": "Revert", + "description": "Text for buttons used to revert changes" + }, + "importAndAppendButton": { + "message": "Import and append…", + "description": "Text for buttons used to import and append content" + }, + "exportButton": { + "message": "Export…", + "description": "Text for buttons used to export content" + }, + "dnrRulesSummary": { + "message": "Enter your own DNR rules below. Do not add content from untrusted sources.", + "description": "Short description of the DNR rules editor pane" } } diff --git a/platform/mv3/extension/_locales/id/messages.json b/platform/mv3/extension/_locales/id/messages.json index 1f0e33840445c..9760f0e834002 100644 --- a/platform/mv3/extension/_locales/id/messages.json +++ b/platform/mv3/extension/_locales/id/messages.json @@ -233,8 +233,16 @@ }, "enableStrictBlockLegend": { "message": "Navigasi ke situs yang mungkin tidak diinginkan akan diblokir, dan Anda akan ditawari opsi untuk melanjutkan.", + "description": "Short description for a checkbox in the options page" + }, + "developerModeLabel": { + "message": "Developer mode", "description": "Label for a checkbox in the options page" }, + "developerModeLegend": { + "message": "Enable access to features suitable for technical users.", + "description": "Short description for a checkbox in the options page" + }, "findListsPlaceholder": { "message": "Temukan daftar", "description": "Placeholder for the input field used to find lists" @@ -282,5 +290,25 @@ "zapperTipQuit": { "message": "Exit element zapper mode", "description": "Tooltip for the button used to exit zapper mode" + }, + "saveButton": { + "message": "Save", + "description": "Text for buttons used to save changes" + }, + "revertButton": { + "message": "Revert", + "description": "Text for buttons used to revert changes" + }, + "importAndAppendButton": { + "message": "Import and append…", + "description": "Text for buttons used to import and append content" + }, + "exportButton": { + "message": "Export…", + "description": "Text for buttons used to export content" + }, + "dnrRulesSummary": { + "message": "Enter your own DNR rules below. Do not add content from untrusted sources.", + "description": "Short description of the DNR rules editor pane" } } diff --git a/platform/mv3/extension/_locales/it/messages.json b/platform/mv3/extension/_locales/it/messages.json index b0a15c153c5c7..53b04b95cfb3a 100644 --- a/platform/mv3/extension/_locales/it/messages.json +++ b/platform/mv3/extension/_locales/it/messages.json @@ -233,8 +233,16 @@ }, "enableStrictBlockLegend": { "message": "La navigazione verso siti potenzialmente indesiderati verrà bloccata e ti verrà offerta la possibilità di procedere.", + "description": "Short description for a checkbox in the options page" + }, + "developerModeLabel": { + "message": "Developer mode", "description": "Label for a checkbox in the options page" }, + "developerModeLegend": { + "message": "Enable access to features suitable for technical users.", + "description": "Short description for a checkbox in the options page" + }, "findListsPlaceholder": { "message": "Trova elenchi", "description": "Placeholder for the input field used to find lists" @@ -282,5 +290,25 @@ "zapperTipQuit": { "message": "Esci dalla modalità blocco rapido", "description": "Tooltip for the button used to exit zapper mode" + }, + "saveButton": { + "message": "Save", + "description": "Text for buttons used to save changes" + }, + "revertButton": { + "message": "Revert", + "description": "Text for buttons used to revert changes" + }, + "importAndAppendButton": { + "message": "Import and append…", + "description": "Text for buttons used to import and append content" + }, + "exportButton": { + "message": "Export…", + "description": "Text for buttons used to export content" + }, + "dnrRulesSummary": { + "message": "Enter your own DNR rules below. Do not add content from untrusted sources.", + "description": "Short description of the DNR rules editor pane" } } diff --git a/platform/mv3/extension/_locales/ja/messages.json b/platform/mv3/extension/_locales/ja/messages.json index 454941642a42d..0e00e22a75e59 100644 --- a/platform/mv3/extension/_locales/ja/messages.json +++ b/platform/mv3/extension/_locales/ja/messages.json @@ -233,8 +233,16 @@ }, "enableStrictBlockLegend": { "message": "望ましくない可能性のあるサイトへのナビゲーションはブロックされ、次に進むためのオプションが表示されます。", + "description": "Short description for a checkbox in the options page" + }, + "developerModeLabel": { + "message": "Developer mode", "description": "Label for a checkbox in the options page" }, + "developerModeLegend": { + "message": "Enable access to features suitable for technical users.", + "description": "Short description for a checkbox in the options page" + }, "findListsPlaceholder": { "message": "リストを検索", "description": "Placeholder for the input field used to find lists" @@ -282,5 +290,25 @@ "zapperTipQuit": { "message": "要素抹消モードを終了", "description": "Tooltip for the button used to exit zapper mode" + }, + "saveButton": { + "message": "Save", + "description": "Text for buttons used to save changes" + }, + "revertButton": { + "message": "Revert", + "description": "Text for buttons used to revert changes" + }, + "importAndAppendButton": { + "message": "Import and append…", + "description": "Text for buttons used to import and append content" + }, + "exportButton": { + "message": "Export…", + "description": "Text for buttons used to export content" + }, + "dnrRulesSummary": { + "message": "Enter your own DNR rules below. Do not add content from untrusted sources.", + "description": "Short description of the DNR rules editor pane" } } diff --git a/platform/mv3/extension/_locales/ka/messages.json b/platform/mv3/extension/_locales/ka/messages.json index b47d3bae97806..59b33f61bd415 100644 --- a/platform/mv3/extension/_locales/ka/messages.json +++ b/platform/mv3/extension/_locales/ka/messages.json @@ -233,8 +233,16 @@ }, "enableStrictBlockLegend": { "message": "შეიზღუდა სავარაუდოდ არასასურველ გვერდებზე გადასვლა, საშუალება გეძლევათ, მაინც განაგრძოთ.", + "description": "Short description for a checkbox in the options page" + }, + "developerModeLabel": { + "message": "Developer mode", "description": "Label for a checkbox in the options page" }, + "developerModeLegend": { + "message": "Enable access to features suitable for technical users.", + "description": "Short description for a checkbox in the options page" + }, "findListsPlaceholder": { "message": "სიების მოძიება", "description": "Placeholder for the input field used to find lists" @@ -282,5 +290,25 @@ "zapperTipQuit": { "message": "ნაწილების ამოჭრის რეჟიმიდან გასვლა", "description": "Tooltip for the button used to exit zapper mode" + }, + "saveButton": { + "message": "Save", + "description": "Text for buttons used to save changes" + }, + "revertButton": { + "message": "Revert", + "description": "Text for buttons used to revert changes" + }, + "importAndAppendButton": { + "message": "Import and append…", + "description": "Text for buttons used to import and append content" + }, + "exportButton": { + "message": "Export…", + "description": "Text for buttons used to export content" + }, + "dnrRulesSummary": { + "message": "Enter your own DNR rules below. Do not add content from untrusted sources.", + "description": "Short description of the DNR rules editor pane" } } diff --git a/platform/mv3/extension/_locales/kk/messages.json b/platform/mv3/extension/_locales/kk/messages.json index ace2c1d9f1881..136a025b98095 100644 --- a/platform/mv3/extension/_locales/kk/messages.json +++ b/platform/mv3/extension/_locales/kk/messages.json @@ -233,8 +233,16 @@ }, "enableStrictBlockLegend": { "message": "Navigation to potentially undesirable sites will be blocked, and you will be offered the option to proceed.", + "description": "Short description for a checkbox in the options page" + }, + "developerModeLabel": { + "message": "Developer mode", "description": "Label for a checkbox in the options page" }, + "developerModeLegend": { + "message": "Enable access to features suitable for technical users.", + "description": "Short description for a checkbox in the options page" + }, "findListsPlaceholder": { "message": "Find lists", "description": "Placeholder for the input field used to find lists" @@ -282,5 +290,25 @@ "zapperTipQuit": { "message": "Exit element zapper mode", "description": "Tooltip for the button used to exit zapper mode" + }, + "saveButton": { + "message": "Save", + "description": "Text for buttons used to save changes" + }, + "revertButton": { + "message": "Revert", + "description": "Text for buttons used to revert changes" + }, + "importAndAppendButton": { + "message": "Import and append…", + "description": "Text for buttons used to import and append content" + }, + "exportButton": { + "message": "Export…", + "description": "Text for buttons used to export content" + }, + "dnrRulesSummary": { + "message": "Enter your own DNR rules below. Do not add content from untrusted sources.", + "description": "Short description of the DNR rules editor pane" } } diff --git a/platform/mv3/extension/_locales/kn/messages.json b/platform/mv3/extension/_locales/kn/messages.json index fca0ab51c877a..357c70f7edb7f 100644 --- a/platform/mv3/extension/_locales/kn/messages.json +++ b/platform/mv3/extension/_locales/kn/messages.json @@ -233,8 +233,16 @@ }, "enableStrictBlockLegend": { "message": "Navigation to potentially undesirable sites will be blocked, and you will be offered the option to proceed.", + "description": "Short description for a checkbox in the options page" + }, + "developerModeLabel": { + "message": "Developer mode", "description": "Label for a checkbox in the options page" }, + "developerModeLegend": { + "message": "Enable access to features suitable for technical users.", + "description": "Short description for a checkbox in the options page" + }, "findListsPlaceholder": { "message": "Find lists", "description": "Placeholder for the input field used to find lists" @@ -282,5 +290,25 @@ "zapperTipQuit": { "message": "Exit element zapper mode", "description": "Tooltip for the button used to exit zapper mode" + }, + "saveButton": { + "message": "Save", + "description": "Text for buttons used to save changes" + }, + "revertButton": { + "message": "Revert", + "description": "Text for buttons used to revert changes" + }, + "importAndAppendButton": { + "message": "Import and append…", + "description": "Text for buttons used to import and append content" + }, + "exportButton": { + "message": "Export…", + "description": "Text for buttons used to export content" + }, + "dnrRulesSummary": { + "message": "Enter your own DNR rules below. Do not add content from untrusted sources.", + "description": "Short description of the DNR rules editor pane" } } diff --git a/platform/mv3/extension/_locales/ko/messages.json b/platform/mv3/extension/_locales/ko/messages.json index 4776053f33116..f18e86be17b09 100644 --- a/platform/mv3/extension/_locales/ko/messages.json +++ b/platform/mv3/extension/_locales/ko/messages.json @@ -233,8 +233,16 @@ }, "enableStrictBlockLegend": { "message": "잠재적으로 좋지 않은 사이트로의 접속을 차단하고, 사용자가 진행할지 선택하도록 합니다.", + "description": "Short description for a checkbox in the options page" + }, + "developerModeLabel": { + "message": "Developer mode", "description": "Label for a checkbox in the options page" }, + "developerModeLegend": { + "message": "Enable access to features suitable for technical users.", + "description": "Short description for a checkbox in the options page" + }, "findListsPlaceholder": { "message": "목록 찾기", "description": "Placeholder for the input field used to find lists" @@ -282,5 +290,25 @@ "zapperTipQuit": { "message": "구성 요소 제거 모드 종료", "description": "Tooltip for the button used to exit zapper mode" + }, + "saveButton": { + "message": "Save", + "description": "Text for buttons used to save changes" + }, + "revertButton": { + "message": "Revert", + "description": "Text for buttons used to revert changes" + }, + "importAndAppendButton": { + "message": "Import and append…", + "description": "Text for buttons used to import and append content" + }, + "exportButton": { + "message": "Export…", + "description": "Text for buttons used to export content" + }, + "dnrRulesSummary": { + "message": "Enter your own DNR rules below. Do not add content from untrusted sources.", + "description": "Short description of the DNR rules editor pane" } } diff --git a/platform/mv3/extension/_locales/lt/messages.json b/platform/mv3/extension/_locales/lt/messages.json index 810d9a3a687f8..60955d7cdf7ad 100644 --- a/platform/mv3/extension/_locales/lt/messages.json +++ b/platform/mv3/extension/_locales/lt/messages.json @@ -233,8 +233,16 @@ }, "enableStrictBlockLegend": { "message": "Navigation to potentially undesirable sites will be blocked, and you will be offered the option to proceed.", + "description": "Short description for a checkbox in the options page" + }, + "developerModeLabel": { + "message": "Developer mode", "description": "Label for a checkbox in the options page" }, + "developerModeLegend": { + "message": "Enable access to features suitable for technical users.", + "description": "Short description for a checkbox in the options page" + }, "findListsPlaceholder": { "message": "Find lists", "description": "Placeholder for the input field used to find lists" @@ -282,5 +290,25 @@ "zapperTipQuit": { "message": "Exit element zapper mode", "description": "Tooltip for the button used to exit zapper mode" + }, + "saveButton": { + "message": "Save", + "description": "Text for buttons used to save changes" + }, + "revertButton": { + "message": "Revert", + "description": "Text for buttons used to revert changes" + }, + "importAndAppendButton": { + "message": "Import and append…", + "description": "Text for buttons used to import and append content" + }, + "exportButton": { + "message": "Export…", + "description": "Text for buttons used to export content" + }, + "dnrRulesSummary": { + "message": "Enter your own DNR rules below. Do not add content from untrusted sources.", + "description": "Short description of the DNR rules editor pane" } } diff --git a/platform/mv3/extension/_locales/lv/messages.json b/platform/mv3/extension/_locales/lv/messages.json index 204e683bcc7d8..c308f2c463dd1 100644 --- a/platform/mv3/extension/_locales/lv/messages.json +++ b/platform/mv3/extension/_locales/lv/messages.json @@ -233,8 +233,16 @@ }, "enableStrictBlockLegend": { "message": "Iespējami nevēlamu vietņu apmeklēšana tiks aizturēta, un tiks piedāvāta iespēja turpināt.", + "description": "Short description for a checkbox in the options page" + }, + "developerModeLabel": { + "message": "Developer mode", "description": "Label for a checkbox in the options page" }, + "developerModeLegend": { + "message": "Enable access to features suitable for technical users.", + "description": "Short description for a checkbox in the options page" + }, "findListsPlaceholder": { "message": "Atrast sarakstus", "description": "Placeholder for the input field used to find lists" @@ -282,5 +290,25 @@ "zapperTipQuit": { "message": "Iziet no elementu iznīcināšanas", "description": "Tooltip for the button used to exit zapper mode" + }, + "saveButton": { + "message": "Save", + "description": "Text for buttons used to save changes" + }, + "revertButton": { + "message": "Revert", + "description": "Text for buttons used to revert changes" + }, + "importAndAppendButton": { + "message": "Import and append…", + "description": "Text for buttons used to import and append content" + }, + "exportButton": { + "message": "Export…", + "description": "Text for buttons used to export content" + }, + "dnrRulesSummary": { + "message": "Enter your own DNR rules below. Do not add content from untrusted sources.", + "description": "Short description of the DNR rules editor pane" } } diff --git a/platform/mv3/extension/_locales/mk/messages.json b/platform/mv3/extension/_locales/mk/messages.json index 0d6146d37cab6..431dba89a64c9 100644 --- a/platform/mv3/extension/_locales/mk/messages.json +++ b/platform/mv3/extension/_locales/mk/messages.json @@ -233,8 +233,16 @@ }, "enableStrictBlockLegend": { "message": "Navigation to potentially undesirable sites will be blocked, and you will be offered the option to proceed.", + "description": "Short description for a checkbox in the options page" + }, + "developerModeLabel": { + "message": "Developer mode", "description": "Label for a checkbox in the options page" }, + "developerModeLegend": { + "message": "Enable access to features suitable for technical users.", + "description": "Short description for a checkbox in the options page" + }, "findListsPlaceholder": { "message": "Најди листи", "description": "Placeholder for the input field used to find lists" @@ -282,5 +290,25 @@ "zapperTipQuit": { "message": "Exit element zapper mode", "description": "Tooltip for the button used to exit zapper mode" + }, + "saveButton": { + "message": "Save", + "description": "Text for buttons used to save changes" + }, + "revertButton": { + "message": "Revert", + "description": "Text for buttons used to revert changes" + }, + "importAndAppendButton": { + "message": "Import and append…", + "description": "Text for buttons used to import and append content" + }, + "exportButton": { + "message": "Export…", + "description": "Text for buttons used to export content" + }, + "dnrRulesSummary": { + "message": "Enter your own DNR rules below. Do not add content from untrusted sources.", + "description": "Short description of the DNR rules editor pane" } } diff --git a/platform/mv3/extension/_locales/ml/messages.json b/platform/mv3/extension/_locales/ml/messages.json index 8bb8f39616c0a..4e6a284b44cc7 100644 --- a/platform/mv3/extension/_locales/ml/messages.json +++ b/platform/mv3/extension/_locales/ml/messages.json @@ -233,8 +233,16 @@ }, "enableStrictBlockLegend": { "message": "Navigation to potentially undesirable sites will be blocked, and you will be offered the option to proceed.", + "description": "Short description for a checkbox in the options page" + }, + "developerModeLabel": { + "message": "Developer mode", "description": "Label for a checkbox in the options page" }, + "developerModeLegend": { + "message": "Enable access to features suitable for technical users.", + "description": "Short description for a checkbox in the options page" + }, "findListsPlaceholder": { "message": "Find lists", "description": "Placeholder for the input field used to find lists" @@ -282,5 +290,25 @@ "zapperTipQuit": { "message": "Exit element zapper mode", "description": "Tooltip for the button used to exit zapper mode" + }, + "saveButton": { + "message": "Save", + "description": "Text for buttons used to save changes" + }, + "revertButton": { + "message": "Revert", + "description": "Text for buttons used to revert changes" + }, + "importAndAppendButton": { + "message": "Import and append…", + "description": "Text for buttons used to import and append content" + }, + "exportButton": { + "message": "Export…", + "description": "Text for buttons used to export content" + }, + "dnrRulesSummary": { + "message": "Enter your own DNR rules below. Do not add content from untrusted sources.", + "description": "Short description of the DNR rules editor pane" } } diff --git a/platform/mv3/extension/_locales/mr/messages.json b/platform/mv3/extension/_locales/mr/messages.json index ace2c1d9f1881..136a025b98095 100644 --- a/platform/mv3/extension/_locales/mr/messages.json +++ b/platform/mv3/extension/_locales/mr/messages.json @@ -233,8 +233,16 @@ }, "enableStrictBlockLegend": { "message": "Navigation to potentially undesirable sites will be blocked, and you will be offered the option to proceed.", + "description": "Short description for a checkbox in the options page" + }, + "developerModeLabel": { + "message": "Developer mode", "description": "Label for a checkbox in the options page" }, + "developerModeLegend": { + "message": "Enable access to features suitable for technical users.", + "description": "Short description for a checkbox in the options page" + }, "findListsPlaceholder": { "message": "Find lists", "description": "Placeholder for the input field used to find lists" @@ -282,5 +290,25 @@ "zapperTipQuit": { "message": "Exit element zapper mode", "description": "Tooltip for the button used to exit zapper mode" + }, + "saveButton": { + "message": "Save", + "description": "Text for buttons used to save changes" + }, + "revertButton": { + "message": "Revert", + "description": "Text for buttons used to revert changes" + }, + "importAndAppendButton": { + "message": "Import and append…", + "description": "Text for buttons used to import and append content" + }, + "exportButton": { + "message": "Export…", + "description": "Text for buttons used to export content" + }, + "dnrRulesSummary": { + "message": "Enter your own DNR rules below. Do not add content from untrusted sources.", + "description": "Short description of the DNR rules editor pane" } } diff --git a/platform/mv3/extension/_locales/ms/messages.json b/platform/mv3/extension/_locales/ms/messages.json index a1031f7611a05..a0c5b761b95f7 100644 --- a/platform/mv3/extension/_locales/ms/messages.json +++ b/platform/mv3/extension/_locales/ms/messages.json @@ -233,8 +233,16 @@ }, "enableStrictBlockLegend": { "message": "Navigation to potentially undesirable sites will be blocked, and you will be offered the option to proceed.", + "description": "Short description for a checkbox in the options page" + }, + "developerModeLabel": { + "message": "Developer mode", "description": "Label for a checkbox in the options page" }, + "developerModeLegend": { + "message": "Enable access to features suitable for technical users.", + "description": "Short description for a checkbox in the options page" + }, "findListsPlaceholder": { "message": "Find lists", "description": "Placeholder for the input field used to find lists" @@ -282,5 +290,25 @@ "zapperTipQuit": { "message": "Exit element zapper mode", "description": "Tooltip for the button used to exit zapper mode" + }, + "saveButton": { + "message": "Save", + "description": "Text for buttons used to save changes" + }, + "revertButton": { + "message": "Revert", + "description": "Text for buttons used to revert changes" + }, + "importAndAppendButton": { + "message": "Import and append…", + "description": "Text for buttons used to import and append content" + }, + "exportButton": { + "message": "Export…", + "description": "Text for buttons used to export content" + }, + "dnrRulesSummary": { + "message": "Enter your own DNR rules below. Do not add content from untrusted sources.", + "description": "Short description of the DNR rules editor pane" } } diff --git a/platform/mv3/extension/_locales/nb/messages.json b/platform/mv3/extension/_locales/nb/messages.json index 6d9ac55038605..7282364c22462 100644 --- a/platform/mv3/extension/_locales/nb/messages.json +++ b/platform/mv3/extension/_locales/nb/messages.json @@ -233,8 +233,16 @@ }, "enableStrictBlockLegend": { "message": "Navigering til potensielt uønskede nettsteder blir blokkert, og du får tilbud om å fortsette.", + "description": "Short description for a checkbox in the options page" + }, + "developerModeLabel": { + "message": "Developer mode", "description": "Label for a checkbox in the options page" }, + "developerModeLegend": { + "message": "Enable access to features suitable for technical users.", + "description": "Short description for a checkbox in the options page" + }, "findListsPlaceholder": { "message": "Finn lister", "description": "Placeholder for the input field used to find lists" @@ -282,5 +290,25 @@ "zapperTipQuit": { "message": "Exit element zapper mode", "description": "Tooltip for the button used to exit zapper mode" + }, + "saveButton": { + "message": "Save", + "description": "Text for buttons used to save changes" + }, + "revertButton": { + "message": "Revert", + "description": "Text for buttons used to revert changes" + }, + "importAndAppendButton": { + "message": "Import and append…", + "description": "Text for buttons used to import and append content" + }, + "exportButton": { + "message": "Export…", + "description": "Text for buttons used to export content" + }, + "dnrRulesSummary": { + "message": "Enter your own DNR rules below. Do not add content from untrusted sources.", + "description": "Short description of the DNR rules editor pane" } } diff --git a/platform/mv3/extension/_locales/nl/messages.json b/platform/mv3/extension/_locales/nl/messages.json index 9d2692b867b1c..1de06d4a04583 100644 --- a/platform/mv3/extension/_locales/nl/messages.json +++ b/platform/mv3/extension/_locales/nl/messages.json @@ -233,8 +233,16 @@ }, "enableStrictBlockLegend": { "message": "Navigatie naar mogelijk ongewenste websites wordt geblokkeerd, en u krijgt de mogelijkheid om door te gaan.", + "description": "Short description for a checkbox in the options page" + }, + "developerModeLabel": { + "message": "Developer mode", "description": "Label for a checkbox in the options page" }, + "developerModeLegend": { + "message": "Enable access to features suitable for technical users.", + "description": "Short description for a checkbox in the options page" + }, "findListsPlaceholder": { "message": "Lijsten zoeken", "description": "Placeholder for the input field used to find lists" @@ -282,5 +290,25 @@ "zapperTipQuit": { "message": "Element­wisser­modus sluiten", "description": "Tooltip for the button used to exit zapper mode" + }, + "saveButton": { + "message": "Save", + "description": "Text for buttons used to save changes" + }, + "revertButton": { + "message": "Revert", + "description": "Text for buttons used to revert changes" + }, + "importAndAppendButton": { + "message": "Import and append…", + "description": "Text for buttons used to import and append content" + }, + "exportButton": { + "message": "Export…", + "description": "Text for buttons used to export content" + }, + "dnrRulesSummary": { + "message": "Enter your own DNR rules below. Do not add content from untrusted sources.", + "description": "Short description of the DNR rules editor pane" } } diff --git a/platform/mv3/extension/_locales/oc/messages.json b/platform/mv3/extension/_locales/oc/messages.json index ace2c1d9f1881..136a025b98095 100644 --- a/platform/mv3/extension/_locales/oc/messages.json +++ b/platform/mv3/extension/_locales/oc/messages.json @@ -233,8 +233,16 @@ }, "enableStrictBlockLegend": { "message": "Navigation to potentially undesirable sites will be blocked, and you will be offered the option to proceed.", + "description": "Short description for a checkbox in the options page" + }, + "developerModeLabel": { + "message": "Developer mode", "description": "Label for a checkbox in the options page" }, + "developerModeLegend": { + "message": "Enable access to features suitable for technical users.", + "description": "Short description for a checkbox in the options page" + }, "findListsPlaceholder": { "message": "Find lists", "description": "Placeholder for the input field used to find lists" @@ -282,5 +290,25 @@ "zapperTipQuit": { "message": "Exit element zapper mode", "description": "Tooltip for the button used to exit zapper mode" + }, + "saveButton": { + "message": "Save", + "description": "Text for buttons used to save changes" + }, + "revertButton": { + "message": "Revert", + "description": "Text for buttons used to revert changes" + }, + "importAndAppendButton": { + "message": "Import and append…", + "description": "Text for buttons used to import and append content" + }, + "exportButton": { + "message": "Export…", + "description": "Text for buttons used to export content" + }, + "dnrRulesSummary": { + "message": "Enter your own DNR rules below. Do not add content from untrusted sources.", + "description": "Short description of the DNR rules editor pane" } } diff --git a/platform/mv3/extension/_locales/pa/messages.json b/platform/mv3/extension/_locales/pa/messages.json index 107f4b98e0721..035a82fc7f6fe 100644 --- a/platform/mv3/extension/_locales/pa/messages.json +++ b/platform/mv3/extension/_locales/pa/messages.json @@ -233,8 +233,16 @@ }, "enableStrictBlockLegend": { "message": "Navigation to potentially undesirable sites will be blocked, and you will be offered the option to proceed.", + "description": "Short description for a checkbox in the options page" + }, + "developerModeLabel": { + "message": "Developer mode", "description": "Label for a checkbox in the options page" }, + "developerModeLegend": { + "message": "Enable access to features suitable for technical users.", + "description": "Short description for a checkbox in the options page" + }, "findListsPlaceholder": { "message": "Find lists", "description": "Placeholder for the input field used to find lists" @@ -282,5 +290,25 @@ "zapperTipQuit": { "message": "Exit element zapper mode", "description": "Tooltip for the button used to exit zapper mode" + }, + "saveButton": { + "message": "Save", + "description": "Text for buttons used to save changes" + }, + "revertButton": { + "message": "Revert", + "description": "Text for buttons used to revert changes" + }, + "importAndAppendButton": { + "message": "Import and append…", + "description": "Text for buttons used to import and append content" + }, + "exportButton": { + "message": "Export…", + "description": "Text for buttons used to export content" + }, + "dnrRulesSummary": { + "message": "Enter your own DNR rules below. Do not add content from untrusted sources.", + "description": "Short description of the DNR rules editor pane" } } diff --git a/platform/mv3/extension/_locales/pl/messages.json b/platform/mv3/extension/_locales/pl/messages.json index e36a317bc8f53..46e37a83ca6dc 100644 --- a/platform/mv3/extension/_locales/pl/messages.json +++ b/platform/mv3/extension/_locales/pl/messages.json @@ -233,8 +233,16 @@ }, "enableStrictBlockLegend": { "message": "Nawigacja do potencjalnie niepożądanych witryn zostanie zablokowana i pojawi się opcja kontynuowania.", + "description": "Short description for a checkbox in the options page" + }, + "developerModeLabel": { + "message": "Developer mode", "description": "Label for a checkbox in the options page" }, + "developerModeLegend": { + "message": "Enable access to features suitable for technical users.", + "description": "Short description for a checkbox in the options page" + }, "findListsPlaceholder": { "message": "Znajdź listy", "description": "Placeholder for the input field used to find lists" @@ -282,5 +290,25 @@ "zapperTipQuit": { "message": "Wyjdź z trybu usuwania elementów", "description": "Tooltip for the button used to exit zapper mode" + }, + "saveButton": { + "message": "Save", + "description": "Text for buttons used to save changes" + }, + "revertButton": { + "message": "Revert", + "description": "Text for buttons used to revert changes" + }, + "importAndAppendButton": { + "message": "Import and append…", + "description": "Text for buttons used to import and append content" + }, + "exportButton": { + "message": "Export…", + "description": "Text for buttons used to export content" + }, + "dnrRulesSummary": { + "message": "Enter your own DNR rules below. Do not add content from untrusted sources.", + "description": "Short description of the DNR rules editor pane" } } diff --git a/platform/mv3/extension/_locales/pt_BR/messages.json b/platform/mv3/extension/_locales/pt_BR/messages.json index 56f2e6b65942d..c852e5b9e1645 100644 --- a/platform/mv3/extension/_locales/pt_BR/messages.json +++ b/platform/mv3/extension/_locales/pt_BR/messages.json @@ -233,8 +233,16 @@ }, "enableStrictBlockLegend": { "message": "A navegação até sites potencialmente indesejáveis ​​será bloqueada e será oferecido a você a opção de prosseguir.", + "description": "Short description for a checkbox in the options page" + }, + "developerModeLabel": { + "message": "Developer mode", "description": "Label for a checkbox in the options page" }, + "developerModeLegend": { + "message": "Enable access to features suitable for technical users.", + "description": "Short description for a checkbox in the options page" + }, "findListsPlaceholder": { "message": "Achar listas", "description": "Placeholder for the input field used to find lists" @@ -282,5 +290,25 @@ "zapperTipQuit": { "message": "Sair do modo do elemento zapper", "description": "Tooltip for the button used to exit zapper mode" + }, + "saveButton": { + "message": "Save", + "description": "Text for buttons used to save changes" + }, + "revertButton": { + "message": "Revert", + "description": "Text for buttons used to revert changes" + }, + "importAndAppendButton": { + "message": "Import and append…", + "description": "Text for buttons used to import and append content" + }, + "exportButton": { + "message": "Export…", + "description": "Text for buttons used to export content" + }, + "dnrRulesSummary": { + "message": "Enter your own DNR rules below. Do not add content from untrusted sources.", + "description": "Short description of the DNR rules editor pane" } } diff --git a/platform/mv3/extension/_locales/pt_PT/messages.json b/platform/mv3/extension/_locales/pt_PT/messages.json index dbd8d39498779..a3cadf1958c11 100644 --- a/platform/mv3/extension/_locales/pt_PT/messages.json +++ b/platform/mv3/extension/_locales/pt_PT/messages.json @@ -233,8 +233,16 @@ }, "enableStrictBlockLegend": { "message": "A navegação para sites potencialmente indesejáveis será bloqueada e ser-lhe-á dada a opção de proceder.", + "description": "Short description for a checkbox in the options page" + }, + "developerModeLabel": { + "message": "Developer mode", "description": "Label for a checkbox in the options page" }, + "developerModeLegend": { + "message": "Enable access to features suitable for technical users.", + "description": "Short description for a checkbox in the options page" + }, "findListsPlaceholder": { "message": "Encontrar listas", "description": "Placeholder for the input field used to find lists" @@ -282,5 +290,25 @@ "zapperTipQuit": { "message": "Sair do modo \"element zapper\"", "description": "Tooltip for the button used to exit zapper mode" + }, + "saveButton": { + "message": "Save", + "description": "Text for buttons used to save changes" + }, + "revertButton": { + "message": "Revert", + "description": "Text for buttons used to revert changes" + }, + "importAndAppendButton": { + "message": "Import and append…", + "description": "Text for buttons used to import and append content" + }, + "exportButton": { + "message": "Export…", + "description": "Text for buttons used to export content" + }, + "dnrRulesSummary": { + "message": "Enter your own DNR rules below. Do not add content from untrusted sources.", + "description": "Short description of the DNR rules editor pane" } } diff --git a/platform/mv3/extension/_locales/ro/messages.json b/platform/mv3/extension/_locales/ro/messages.json index 7213dc0aba8d8..ce522a2bd10e7 100644 --- a/platform/mv3/extension/_locales/ro/messages.json +++ b/platform/mv3/extension/_locales/ro/messages.json @@ -233,8 +233,16 @@ }, "enableStrictBlockLegend": { "message": "Navigarea către site-uri potențial nedorite va fi blocată și vi se va oferi opțiunea de a continua.", + "description": "Short description for a checkbox in the options page" + }, + "developerModeLabel": { + "message": "Developer mode", "description": "Label for a checkbox in the options page" }, + "developerModeLegend": { + "message": "Enable access to features suitable for technical users.", + "description": "Short description for a checkbox in the options page" + }, "findListsPlaceholder": { "message": "Căutați liste", "description": "Placeholder for the input field used to find lists" @@ -282,5 +290,25 @@ "zapperTipQuit": { "message": "Exit element zapper mode", "description": "Tooltip for the button used to exit zapper mode" + }, + "saveButton": { + "message": "Save", + "description": "Text for buttons used to save changes" + }, + "revertButton": { + "message": "Revert", + "description": "Text for buttons used to revert changes" + }, + "importAndAppendButton": { + "message": "Import and append…", + "description": "Text for buttons used to import and append content" + }, + "exportButton": { + "message": "Export…", + "description": "Text for buttons used to export content" + }, + "dnrRulesSummary": { + "message": "Enter your own DNR rules below. Do not add content from untrusted sources.", + "description": "Short description of the DNR rules editor pane" } } diff --git a/platform/mv3/extension/_locales/ru/messages.json b/platform/mv3/extension/_locales/ru/messages.json index adb7c14706b10..0eca35d9ec705 100644 --- a/platform/mv3/extension/_locales/ru/messages.json +++ b/platform/mv3/extension/_locales/ru/messages.json @@ -233,8 +233,16 @@ }, "enableStrictBlockLegend": { "message": "Переход к потенциально нежелательным сайтам будет заблокирован, и будет дана возможность продолжить переход на сайт", + "description": "Short description for a checkbox in the options page" + }, + "developerModeLabel": { + "message": "Developer mode", "description": "Label for a checkbox in the options page" }, + "developerModeLegend": { + "message": "Enable access to features suitable for technical users.", + "description": "Short description for a checkbox in the options page" + }, "findListsPlaceholder": { "message": "Найти списки", "description": "Placeholder for the input field used to find lists" @@ -282,5 +290,25 @@ "zapperTipQuit": { "message": "Покинуть режим временного скрытия элемента", "description": "Tooltip for the button used to exit zapper mode" + }, + "saveButton": { + "message": "Save", + "description": "Text for buttons used to save changes" + }, + "revertButton": { + "message": "Revert", + "description": "Text for buttons used to revert changes" + }, + "importAndAppendButton": { + "message": "Import and append…", + "description": "Text for buttons used to import and append content" + }, + "exportButton": { + "message": "Export…", + "description": "Text for buttons used to export content" + }, + "dnrRulesSummary": { + "message": "Enter your own DNR rules below. Do not add content from untrusted sources.", + "description": "Short description of the DNR rules editor pane" } } diff --git a/platform/mv3/extension/_locales/si/messages.json b/platform/mv3/extension/_locales/si/messages.json index 841ea098c3498..5d6467aa9cfe8 100644 --- a/platform/mv3/extension/_locales/si/messages.json +++ b/platform/mv3/extension/_locales/si/messages.json @@ -233,8 +233,16 @@ }, "enableStrictBlockLegend": { "message": "අනවශ්‍ය විය හැකි අඩවි වෙත සංචාලනය අවහිර කරනු ලබන අතර, ඉදිරියට යාමට ඔබට විකල්පය ලබා දෙනු ඇත.", + "description": "Short description for a checkbox in the options page" + }, + "developerModeLabel": { + "message": "Developer mode", "description": "Label for a checkbox in the options page" }, + "developerModeLegend": { + "message": "Enable access to features suitable for technical users.", + "description": "Short description for a checkbox in the options page" + }, "findListsPlaceholder": { "message": "ලැයිස්තු සොයන්න", "description": "Placeholder for the input field used to find lists" @@ -282,5 +290,25 @@ "zapperTipQuit": { "message": "මූලද්‍රව්‍ය zapper ප්‍රකාරයෙන් ඉවත් වන්න", "description": "Tooltip for the button used to exit zapper mode" + }, + "saveButton": { + "message": "Save", + "description": "Text for buttons used to save changes" + }, + "revertButton": { + "message": "Revert", + "description": "Text for buttons used to revert changes" + }, + "importAndAppendButton": { + "message": "Import and append…", + "description": "Text for buttons used to import and append content" + }, + "exportButton": { + "message": "Export…", + "description": "Text for buttons used to export content" + }, + "dnrRulesSummary": { + "message": "Enter your own DNR rules below. Do not add content from untrusted sources.", + "description": "Short description of the DNR rules editor pane" } } diff --git a/platform/mv3/extension/_locales/sk/messages.json b/platform/mv3/extension/_locales/sk/messages.json index 79d5336bd2e07..22aad57c39746 100644 --- a/platform/mv3/extension/_locales/sk/messages.json +++ b/platform/mv3/extension/_locales/sk/messages.json @@ -233,8 +233,16 @@ }, "enableStrictBlockLegend": { "message": "Navigácia na potenciálne nežiaduce stránky sa zablokuje a ponúkne sa vám možnosť pokračovať.", + "description": "Short description for a checkbox in the options page" + }, + "developerModeLabel": { + "message": "Developer mode", "description": "Label for a checkbox in the options page" }, + "developerModeLegend": { + "message": "Enable access to features suitable for technical users.", + "description": "Short description for a checkbox in the options page" + }, "findListsPlaceholder": { "message": "Nájsť zoznamy", "description": "Placeholder for the input field used to find lists" @@ -282,5 +290,25 @@ "zapperTipQuit": { "message": "Ukončiť režim dočasného skrytia prvkov", "description": "Tooltip for the button used to exit zapper mode" + }, + "saveButton": { + "message": "Save", + "description": "Text for buttons used to save changes" + }, + "revertButton": { + "message": "Revert", + "description": "Text for buttons used to revert changes" + }, + "importAndAppendButton": { + "message": "Import and append…", + "description": "Text for buttons used to import and append content" + }, + "exportButton": { + "message": "Export…", + "description": "Text for buttons used to export content" + }, + "dnrRulesSummary": { + "message": "Enter your own DNR rules below. Do not add content from untrusted sources.", + "description": "Short description of the DNR rules editor pane" } } diff --git a/platform/mv3/extension/_locales/sl/messages.json b/platform/mv3/extension/_locales/sl/messages.json index ace2c1d9f1881..136a025b98095 100644 --- a/platform/mv3/extension/_locales/sl/messages.json +++ b/platform/mv3/extension/_locales/sl/messages.json @@ -233,8 +233,16 @@ }, "enableStrictBlockLegend": { "message": "Navigation to potentially undesirable sites will be blocked, and you will be offered the option to proceed.", + "description": "Short description for a checkbox in the options page" + }, + "developerModeLabel": { + "message": "Developer mode", "description": "Label for a checkbox in the options page" }, + "developerModeLegend": { + "message": "Enable access to features suitable for technical users.", + "description": "Short description for a checkbox in the options page" + }, "findListsPlaceholder": { "message": "Find lists", "description": "Placeholder for the input field used to find lists" @@ -282,5 +290,25 @@ "zapperTipQuit": { "message": "Exit element zapper mode", "description": "Tooltip for the button used to exit zapper mode" + }, + "saveButton": { + "message": "Save", + "description": "Text for buttons used to save changes" + }, + "revertButton": { + "message": "Revert", + "description": "Text for buttons used to revert changes" + }, + "importAndAppendButton": { + "message": "Import and append…", + "description": "Text for buttons used to import and append content" + }, + "exportButton": { + "message": "Export…", + "description": "Text for buttons used to export content" + }, + "dnrRulesSummary": { + "message": "Enter your own DNR rules below. Do not add content from untrusted sources.", + "description": "Short description of the DNR rules editor pane" } } diff --git a/platform/mv3/extension/_locales/so/messages.json b/platform/mv3/extension/_locales/so/messages.json index ace2c1d9f1881..136a025b98095 100644 --- a/platform/mv3/extension/_locales/so/messages.json +++ b/platform/mv3/extension/_locales/so/messages.json @@ -233,8 +233,16 @@ }, "enableStrictBlockLegend": { "message": "Navigation to potentially undesirable sites will be blocked, and you will be offered the option to proceed.", + "description": "Short description for a checkbox in the options page" + }, + "developerModeLabel": { + "message": "Developer mode", "description": "Label for a checkbox in the options page" }, + "developerModeLegend": { + "message": "Enable access to features suitable for technical users.", + "description": "Short description for a checkbox in the options page" + }, "findListsPlaceholder": { "message": "Find lists", "description": "Placeholder for the input field used to find lists" @@ -282,5 +290,25 @@ "zapperTipQuit": { "message": "Exit element zapper mode", "description": "Tooltip for the button used to exit zapper mode" + }, + "saveButton": { + "message": "Save", + "description": "Text for buttons used to save changes" + }, + "revertButton": { + "message": "Revert", + "description": "Text for buttons used to revert changes" + }, + "importAndAppendButton": { + "message": "Import and append…", + "description": "Text for buttons used to import and append content" + }, + "exportButton": { + "message": "Export…", + "description": "Text for buttons used to export content" + }, + "dnrRulesSummary": { + "message": "Enter your own DNR rules below. Do not add content from untrusted sources.", + "description": "Short description of the DNR rules editor pane" } } diff --git a/platform/mv3/extension/_locales/sq/messages.json b/platform/mv3/extension/_locales/sq/messages.json index 85591ef8baa42..97675a6b69f27 100644 --- a/platform/mv3/extension/_locales/sq/messages.json +++ b/platform/mv3/extension/_locales/sq/messages.json @@ -233,8 +233,16 @@ }, "enableStrictBlockLegend": { "message": "Uebsajtet potencialisht të padëshirueshme do të bllokohen dhe do t'ju jepet mundësia për të vazhduar.", + "description": "Short description for a checkbox in the options page" + }, + "developerModeLabel": { + "message": "Developer mode", "description": "Label for a checkbox in the options page" }, + "developerModeLegend": { + "message": "Enable access to features suitable for technical users.", + "description": "Short description for a checkbox in the options page" + }, "findListsPlaceholder": { "message": "Gjej listat", "description": "Placeholder for the input field used to find lists" @@ -282,5 +290,25 @@ "zapperTipQuit": { "message": "Mbyll asgjësuesin e elementeve", "description": "Tooltip for the button used to exit zapper mode" + }, + "saveButton": { + "message": "Save", + "description": "Text for buttons used to save changes" + }, + "revertButton": { + "message": "Revert", + "description": "Text for buttons used to revert changes" + }, + "importAndAppendButton": { + "message": "Import and append…", + "description": "Text for buttons used to import and append content" + }, + "exportButton": { + "message": "Export…", + "description": "Text for buttons used to export content" + }, + "dnrRulesSummary": { + "message": "Enter your own DNR rules below. Do not add content from untrusted sources.", + "description": "Short description of the DNR rules editor pane" } } diff --git a/platform/mv3/extension/_locales/sr/messages.json b/platform/mv3/extension/_locales/sr/messages.json index f94879cd9bfa0..2ba9996532391 100644 --- a/platform/mv3/extension/_locales/sr/messages.json +++ b/platform/mv3/extension/_locales/sr/messages.json @@ -233,8 +233,16 @@ }, "enableStrictBlockLegend": { "message": "Навигација до потенцијално непожељних сајтова ће бити блокирана и биће вам понуђена опција да наставите.", + "description": "Short description for a checkbox in the options page" + }, + "developerModeLabel": { + "message": "Developer mode", "description": "Label for a checkbox in the options page" }, + "developerModeLegend": { + "message": "Enable access to features suitable for technical users.", + "description": "Short description for a checkbox in the options page" + }, "findListsPlaceholder": { "message": "Пронађи листе", "description": "Placeholder for the input field used to find lists" @@ -282,5 +290,25 @@ "zapperTipQuit": { "message": "Exit element zapper mode", "description": "Tooltip for the button used to exit zapper mode" + }, + "saveButton": { + "message": "Save", + "description": "Text for buttons used to save changes" + }, + "revertButton": { + "message": "Revert", + "description": "Text for buttons used to revert changes" + }, + "importAndAppendButton": { + "message": "Import and append…", + "description": "Text for buttons used to import and append content" + }, + "exportButton": { + "message": "Export…", + "description": "Text for buttons used to export content" + }, + "dnrRulesSummary": { + "message": "Enter your own DNR rules below. Do not add content from untrusted sources.", + "description": "Short description of the DNR rules editor pane" } } diff --git a/platform/mv3/extension/_locales/sv/messages.json b/platform/mv3/extension/_locales/sv/messages.json index 93c337c914ff6..79a345a5edda7 100644 --- a/platform/mv3/extension/_locales/sv/messages.json +++ b/platform/mv3/extension/_locales/sv/messages.json @@ -233,8 +233,16 @@ }, "enableStrictBlockLegend": { "message": "Navigering till potentiellt oönskade webbplatser kommer att blockeras och du kommer att erbjudas alternativet att fortsätta.", + "description": "Short description for a checkbox in the options page" + }, + "developerModeLabel": { + "message": "Developer mode", "description": "Label for a checkbox in the options page" }, + "developerModeLegend": { + "message": "Enable access to features suitable for technical users.", + "description": "Short description for a checkbox in the options page" + }, "findListsPlaceholder": { "message": "Hitta listor", "description": "Placeholder for the input field used to find lists" @@ -282,5 +290,25 @@ "zapperTipQuit": { "message": "Lämna elementzapperläge", "description": "Tooltip for the button used to exit zapper mode" + }, + "saveButton": { + "message": "Save", + "description": "Text for buttons used to save changes" + }, + "revertButton": { + "message": "Revert", + "description": "Text for buttons used to revert changes" + }, + "importAndAppendButton": { + "message": "Import and append…", + "description": "Text for buttons used to import and append content" + }, + "exportButton": { + "message": "Export…", + "description": "Text for buttons used to export content" + }, + "dnrRulesSummary": { + "message": "Enter your own DNR rules below. Do not add content from untrusted sources.", + "description": "Short description of the DNR rules editor pane" } } diff --git a/platform/mv3/extension/_locales/sw/messages.json b/platform/mv3/extension/_locales/sw/messages.json index ace2c1d9f1881..136a025b98095 100644 --- a/platform/mv3/extension/_locales/sw/messages.json +++ b/platform/mv3/extension/_locales/sw/messages.json @@ -233,8 +233,16 @@ }, "enableStrictBlockLegend": { "message": "Navigation to potentially undesirable sites will be blocked, and you will be offered the option to proceed.", + "description": "Short description for a checkbox in the options page" + }, + "developerModeLabel": { + "message": "Developer mode", "description": "Label for a checkbox in the options page" }, + "developerModeLegend": { + "message": "Enable access to features suitable for technical users.", + "description": "Short description for a checkbox in the options page" + }, "findListsPlaceholder": { "message": "Find lists", "description": "Placeholder for the input field used to find lists" @@ -282,5 +290,25 @@ "zapperTipQuit": { "message": "Exit element zapper mode", "description": "Tooltip for the button used to exit zapper mode" + }, + "saveButton": { + "message": "Save", + "description": "Text for buttons used to save changes" + }, + "revertButton": { + "message": "Revert", + "description": "Text for buttons used to revert changes" + }, + "importAndAppendButton": { + "message": "Import and append…", + "description": "Text for buttons used to import and append content" + }, + "exportButton": { + "message": "Export…", + "description": "Text for buttons used to export content" + }, + "dnrRulesSummary": { + "message": "Enter your own DNR rules below. Do not add content from untrusted sources.", + "description": "Short description of the DNR rules editor pane" } } diff --git a/platform/mv3/extension/_locales/ta/messages.json b/platform/mv3/extension/_locales/ta/messages.json index ace2c1d9f1881..136a025b98095 100644 --- a/platform/mv3/extension/_locales/ta/messages.json +++ b/platform/mv3/extension/_locales/ta/messages.json @@ -233,8 +233,16 @@ }, "enableStrictBlockLegend": { "message": "Navigation to potentially undesirable sites will be blocked, and you will be offered the option to proceed.", + "description": "Short description for a checkbox in the options page" + }, + "developerModeLabel": { + "message": "Developer mode", "description": "Label for a checkbox in the options page" }, + "developerModeLegend": { + "message": "Enable access to features suitable for technical users.", + "description": "Short description for a checkbox in the options page" + }, "findListsPlaceholder": { "message": "Find lists", "description": "Placeholder for the input field used to find lists" @@ -282,5 +290,25 @@ "zapperTipQuit": { "message": "Exit element zapper mode", "description": "Tooltip for the button used to exit zapper mode" + }, + "saveButton": { + "message": "Save", + "description": "Text for buttons used to save changes" + }, + "revertButton": { + "message": "Revert", + "description": "Text for buttons used to revert changes" + }, + "importAndAppendButton": { + "message": "Import and append…", + "description": "Text for buttons used to import and append content" + }, + "exportButton": { + "message": "Export…", + "description": "Text for buttons used to export content" + }, + "dnrRulesSummary": { + "message": "Enter your own DNR rules below. Do not add content from untrusted sources.", + "description": "Short description of the DNR rules editor pane" } } diff --git a/platform/mv3/extension/_locales/te/messages.json b/platform/mv3/extension/_locales/te/messages.json index 7f3f7d1150556..e96f320082090 100644 --- a/platform/mv3/extension/_locales/te/messages.json +++ b/platform/mv3/extension/_locales/te/messages.json @@ -233,8 +233,16 @@ }, "enableStrictBlockLegend": { "message": "Navigation to potentially undesirable sites will be blocked, and you will be offered the option to proceed.", + "description": "Short description for a checkbox in the options page" + }, + "developerModeLabel": { + "message": "Developer mode", "description": "Label for a checkbox in the options page" }, + "developerModeLegend": { + "message": "Enable access to features suitable for technical users.", + "description": "Short description for a checkbox in the options page" + }, "findListsPlaceholder": { "message": "Find lists", "description": "Placeholder for the input field used to find lists" @@ -282,5 +290,25 @@ "zapperTipQuit": { "message": "Exit element zapper mode", "description": "Tooltip for the button used to exit zapper mode" + }, + "saveButton": { + "message": "Save", + "description": "Text for buttons used to save changes" + }, + "revertButton": { + "message": "Revert", + "description": "Text for buttons used to revert changes" + }, + "importAndAppendButton": { + "message": "Import and append…", + "description": "Text for buttons used to import and append content" + }, + "exportButton": { + "message": "Export…", + "description": "Text for buttons used to export content" + }, + "dnrRulesSummary": { + "message": "Enter your own DNR rules below. Do not add content from untrusted sources.", + "description": "Short description of the DNR rules editor pane" } } diff --git a/platform/mv3/extension/_locales/th/messages.json b/platform/mv3/extension/_locales/th/messages.json index a15decaa9b3bd..2d498371dcd29 100644 --- a/platform/mv3/extension/_locales/th/messages.json +++ b/platform/mv3/extension/_locales/th/messages.json @@ -233,8 +233,16 @@ }, "enableStrictBlockLegend": { "message": "การนำทางไปยังเว็บไซต์ที่ไม่พึงประสงค์จะถูกบล็อก และคุณจะได้รับตัวเลือกเพื่อดำเนินการต่อ", + "description": "Short description for a checkbox in the options page" + }, + "developerModeLabel": { + "message": "Developer mode", "description": "Label for a checkbox in the options page" }, + "developerModeLegend": { + "message": "Enable access to features suitable for technical users.", + "description": "Short description for a checkbox in the options page" + }, "findListsPlaceholder": { "message": "หารายการ", "description": "Placeholder for the input field used to find lists" @@ -282,5 +290,25 @@ "zapperTipQuit": { "message": "Exit element zapper mode", "description": "Tooltip for the button used to exit zapper mode" + }, + "saveButton": { + "message": "Save", + "description": "Text for buttons used to save changes" + }, + "revertButton": { + "message": "Revert", + "description": "Text for buttons used to revert changes" + }, + "importAndAppendButton": { + "message": "Import and append…", + "description": "Text for buttons used to import and append content" + }, + "exportButton": { + "message": "Export…", + "description": "Text for buttons used to export content" + }, + "dnrRulesSummary": { + "message": "Enter your own DNR rules below. Do not add content from untrusted sources.", + "description": "Short description of the DNR rules editor pane" } } diff --git a/platform/mv3/extension/_locales/tr/messages.json b/platform/mv3/extension/_locales/tr/messages.json index 077a95202692b..c8e8a857f181f 100644 --- a/platform/mv3/extension/_locales/tr/messages.json +++ b/platform/mv3/extension/_locales/tr/messages.json @@ -233,8 +233,16 @@ }, "enableStrictBlockLegend": { "message": "İstenmeyebilecek sitelere erişim engellenecek ve devam etme seçeneği sunulacaktır.", + "description": "Short description for a checkbox in the options page" + }, + "developerModeLabel": { + "message": "Developer mode", "description": "Label for a checkbox in the options page" }, + "developerModeLegend": { + "message": "Enable access to features suitable for technical users.", + "description": "Short description for a checkbox in the options page" + }, "findListsPlaceholder": { "message": "Liste bul", "description": "Placeholder for the input field used to find lists" @@ -282,5 +290,25 @@ "zapperTipQuit": { "message": "Öge silme modundan çık", "description": "Tooltip for the button used to exit zapper mode" + }, + "saveButton": { + "message": "Save", + "description": "Text for buttons used to save changes" + }, + "revertButton": { + "message": "Revert", + "description": "Text for buttons used to revert changes" + }, + "importAndAppendButton": { + "message": "Import and append…", + "description": "Text for buttons used to import and append content" + }, + "exportButton": { + "message": "Export…", + "description": "Text for buttons used to export content" + }, + "dnrRulesSummary": { + "message": "Enter your own DNR rules below. Do not add content from untrusted sources.", + "description": "Short description of the DNR rules editor pane" } } diff --git a/platform/mv3/extension/_locales/uk/messages.json b/platform/mv3/extension/_locales/uk/messages.json index cfba6ab1fc5ae..5046caa794218 100644 --- a/platform/mv3/extension/_locales/uk/messages.json +++ b/platform/mv3/extension/_locales/uk/messages.json @@ -233,8 +233,16 @@ }, "enableStrictBlockLegend": { "message": "Перехід до потенційно небажаних сайтів буде заблоковано, та вам буде запропоновано можливість продовжити", + "description": "Short description for a checkbox in the options page" + }, + "developerModeLabel": { + "message": "Developer mode", "description": "Label for a checkbox in the options page" }, + "developerModeLegend": { + "message": "Enable access to features suitable for technical users.", + "description": "Short description for a checkbox in the options page" + }, "findListsPlaceholder": { "message": "Знайти списки", "description": "Placeholder for the input field used to find lists" @@ -282,5 +290,25 @@ "zapperTipQuit": { "message": "Вийти з режиму тимчасового приховування елементів", "description": "Tooltip for the button used to exit zapper mode" + }, + "saveButton": { + "message": "Save", + "description": "Text for buttons used to save changes" + }, + "revertButton": { + "message": "Revert", + "description": "Text for buttons used to revert changes" + }, + "importAndAppendButton": { + "message": "Import and append…", + "description": "Text for buttons used to import and append content" + }, + "exportButton": { + "message": "Export…", + "description": "Text for buttons used to export content" + }, + "dnrRulesSummary": { + "message": "Enter your own DNR rules below. Do not add content from untrusted sources.", + "description": "Short description of the DNR rules editor pane" } } diff --git a/platform/mv3/extension/_locales/ur/messages.json b/platform/mv3/extension/_locales/ur/messages.json index b433dd5a0f4a7..1a11f8f4e91de 100644 --- a/platform/mv3/extension/_locales/ur/messages.json +++ b/platform/mv3/extension/_locales/ur/messages.json @@ -233,8 +233,16 @@ }, "enableStrictBlockLegend": { "message": "Navigation to potentially undesirable sites will be blocked, and you will be offered the option to proceed.", + "description": "Short description for a checkbox in the options page" + }, + "developerModeLabel": { + "message": "Developer mode", "description": "Label for a checkbox in the options page" }, + "developerModeLegend": { + "message": "Enable access to features suitable for technical users.", + "description": "Short description for a checkbox in the options page" + }, "findListsPlaceholder": { "message": "Find lists", "description": "Placeholder for the input field used to find lists" @@ -282,5 +290,25 @@ "zapperTipQuit": { "message": "Exit element zapper mode", "description": "Tooltip for the button used to exit zapper mode" + }, + "saveButton": { + "message": "Save", + "description": "Text for buttons used to save changes" + }, + "revertButton": { + "message": "Revert", + "description": "Text for buttons used to revert changes" + }, + "importAndAppendButton": { + "message": "Import and append…", + "description": "Text for buttons used to import and append content" + }, + "exportButton": { + "message": "Export…", + "description": "Text for buttons used to export content" + }, + "dnrRulesSummary": { + "message": "Enter your own DNR rules below. Do not add content from untrusted sources.", + "description": "Short description of the DNR rules editor pane" } } diff --git a/platform/mv3/extension/_locales/vi/messages.json b/platform/mv3/extension/_locales/vi/messages.json index 548be6fe60d6a..bbe6c7d4fbf20 100644 --- a/platform/mv3/extension/_locales/vi/messages.json +++ b/platform/mv3/extension/_locales/vi/messages.json @@ -233,8 +233,16 @@ }, "enableStrictBlockLegend": { "message": "Việc điều hướng đến các trang web có khả năng không mong muốn sẽ bị chặn và bạn sẽ được cung cấp tùy chọn để tiếp tục.", + "description": "Short description for a checkbox in the options page" + }, + "developerModeLabel": { + "message": "Developer mode", "description": "Label for a checkbox in the options page" }, + "developerModeLegend": { + "message": "Enable access to features suitable for technical users.", + "description": "Short description for a checkbox in the options page" + }, "findListsPlaceholder": { "message": "Tìm danh sách", "description": "Placeholder for the input field used to find lists" @@ -282,5 +290,25 @@ "zapperTipQuit": { "message": "Thoát khỏi chế độ chặn phần tử tạm thời", "description": "Tooltip for the button used to exit zapper mode" + }, + "saveButton": { + "message": "Save", + "description": "Text for buttons used to save changes" + }, + "revertButton": { + "message": "Revert", + "description": "Text for buttons used to revert changes" + }, + "importAndAppendButton": { + "message": "Import and append…", + "description": "Text for buttons used to import and append content" + }, + "exportButton": { + "message": "Export…", + "description": "Text for buttons used to export content" + }, + "dnrRulesSummary": { + "message": "Enter your own DNR rules below. Do not add content from untrusted sources.", + "description": "Short description of the DNR rules editor pane" } } diff --git a/platform/mv3/extension/_locales/zh_CN/messages.json b/platform/mv3/extension/_locales/zh_CN/messages.json index f133d63364053..69640caf43edd 100644 --- a/platform/mv3/extension/_locales/zh_CN/messages.json +++ b/platform/mv3/extension/_locales/zh_CN/messages.json @@ -233,8 +233,16 @@ }, "enableStrictBlockLegend": { "message": "导航至潜在不良网站的操作将被拦截,并且您将可以选择继续前往。", + "description": "Short description for a checkbox in the options page" + }, + "developerModeLabel": { + "message": "Developer mode", "description": "Label for a checkbox in the options page" }, + "developerModeLegend": { + "message": "Enable access to features suitable for technical users.", + "description": "Short description for a checkbox in the options page" + }, "findListsPlaceholder": { "message": "查找列表", "description": "Placeholder for the input field used to find lists" @@ -282,5 +290,25 @@ "zapperTipQuit": { "message": "退出临时元素移除模式", "description": "Tooltip for the button used to exit zapper mode" + }, + "saveButton": { + "message": "Save", + "description": "Text for buttons used to save changes" + }, + "revertButton": { + "message": "Revert", + "description": "Text for buttons used to revert changes" + }, + "importAndAppendButton": { + "message": "Import and append…", + "description": "Text for buttons used to import and append content" + }, + "exportButton": { + "message": "Export…", + "description": "Text for buttons used to export content" + }, + "dnrRulesSummary": { + "message": "Enter your own DNR rules below. Do not add content from untrusted sources.", + "description": "Short description of the DNR rules editor pane" } } diff --git a/platform/mv3/extension/_locales/zh_TW/messages.json b/platform/mv3/extension/_locales/zh_TW/messages.json index 3c9005fa3bc19..76c75bcea1b49 100644 --- a/platform/mv3/extension/_locales/zh_TW/messages.json +++ b/platform/mv3/extension/_locales/zh_TW/messages.json @@ -233,8 +233,16 @@ }, "enableStrictBlockLegend": { "message": "前往潛在不良網站的導航將被阻止,您可以選擇是否繼續前往。", + "description": "Short description for a checkbox in the options page" + }, + "developerModeLabel": { + "message": "Developer mode", "description": "Label for a checkbox in the options page" }, + "developerModeLegend": { + "message": "Enable access to features suitable for technical users.", + "description": "Short description for a checkbox in the options page" + }, "findListsPlaceholder": { "message": "尋找清單", "description": "Placeholder for the input field used to find lists" @@ -282,5 +290,25 @@ "zapperTipQuit": { "message": "離開元素臨時移除模式", "description": "Tooltip for the button used to exit zapper mode" + }, + "saveButton": { + "message": "Save", + "description": "Text for buttons used to save changes" + }, + "revertButton": { + "message": "Revert", + "description": "Text for buttons used to revert changes" + }, + "importAndAppendButton": { + "message": "Import and append…", + "description": "Text for buttons used to import and append content" + }, + "exportButton": { + "message": "Export…", + "description": "Text for buttons used to export content" + }, + "dnrRulesSummary": { + "message": "Enter your own DNR rules below. Do not add content from untrusted sources.", + "description": "Short description of the DNR rules editor pane" } } diff --git a/platform/mv3/extension/css/dashboard-common.css b/platform/mv3/extension/css/dashboard-common.css index 8621d08704053..ebef32f609a2d 100644 --- a/platform/mv3/extension/css/dashboard-common.css +++ b/platform/mv3/extension/css/dashboard-common.css @@ -39,9 +39,15 @@ a { color: var(--info3-ink); fill: var(--info3-ink); } + input[type="number"] { width: 5em; } + +input[type="file"] { + display: none; + } + @media (max-height: 640px), (max-height: 800px) and (max-width: 480px) { .body > p, .body > ul { diff --git a/platform/mv3/extension/css/dashboard.css b/platform/mv3/extension/css/dashboard.css index 63aaeaa1559ae..84f75d158cc7c 100644 --- a/platform/mv3/extension/css/dashboard.css +++ b/platform/mv3/extension/css/dashboard.css @@ -1,4 +1,4 @@ -#dashboard-nav { +nav { background-color: var(--surface-1); border: 0; border-bottom: 1px solid var(--border-1); @@ -11,7 +11,7 @@ top: 0; z-index: 100; } -.tabButton { +nav > .tabButton { background-color: transparent; border: 0; border-bottom: 3px solid transparent; @@ -24,29 +24,34 @@ text-decoration: none; white-space: nowrap; } -.tabButton:focus { +nav > .tabButton:focus { outline: 0; } -.tabButton:hover { +nav > .tabButton:hover { background-color: var(--dashboard-tab-hover-surface); border-bottom-color: var(--dashboard-tab-hover-border); } body[data-pane="settings"] #dashboard-nav .tabButton[data-pane="settings"], body[data-pane="rulesets"] #dashboard-nav .tabButton[data-pane="rulesets"], +body[data-pane="develop"] #dashboard-nav .tabButton[data-pane="develop"], body[data-pane="about"] #dashboard-nav .tabButton[data-pane="about"] { background-color: var(--dashboard-tab-active-surface); border-bottom: 3px solid var(--dashboard-tab-active-ink); color: var(--dashboard-tab-active-ink); fill: var(--dashboard-tab-active-ink); } +body:not([data-develop="true"]) #dashboard-nav .tabButton[data-pane="develop"] { + display: none; + } body > section { display: none; - padding-bottom: 8rem; + padding-bottom: 2rem; } body[data-pane="settings"] > section[data-pane="settings"], body[data-pane="rulesets"] > section[data-pane="rulesets"], +body[data-pane="develop"] > section[data-pane="develop"], body[data-pane="about"] > section[data-pane="about"] { display: block; } @@ -59,10 +64,10 @@ body[data-pane="about"] > section[data-pane="about"] { } /* touch-screen devices */ -:root.mobile #dashboard-nav { +:root.mobile nav { flex-wrap: nowrap; overflow-x: auto; } -:root.mobile #dashboard-nav .logo { +:root.mobile nav .logo { display: none; } diff --git a/platform/mv3/extension/css/develop.css b/platform/mv3/extension/css/develop.css new file mode 100644 index 0000000000000..616849509dc96 --- /dev/null +++ b/platform/mv3/extension/css/develop.css @@ -0,0 +1,112 @@ +body[data-pane="develop"] { + height: 100vh; + } + +section[data-pane="develop"] { + display: none; + flex-grow: 1; + overflow: hidden; + } + +section[data-pane="develop"] > div { + display: flex; + flex-direction: column; + height: 100%; + } + +section[data-pane="develop"] > div > * { + margin-bottom: 0; + margin-top: 1em; + } + +#cm-dnrRules { + flex-grow: 1; + font-size: var(--monospace-size); + overflow: hidden; + } + +/* https://discuss.codemirror.net/t/how-to-set-max-height-of-the-editor/2882/2 */ +#cm-dnrRules .cm-editor { + background-color: var(--surface-0); + height: 100%; + } + +#cm-dnrRules .cm-editor .cm-line { + border-bottom: 1px dotted transparent; + border-top: 1px dotted transparent; + } + +#cm-dnrRules .cm-editor .cm-line:has(.ͼ5) { + border-bottom: 1px dotted var(--border-1); + border-top: 1px dotted var(--border-1); + } + +#cm-dnrRules .cm-editor .cm-line.badline:not(.cm-activeLine) { + background-color: color-mix(in srgb, var(--info3-ink) 15%, transparent 85%); + } + +#cm-dnrRules .cm-editor .cm-line .badmark { + text-decoration: underline var(--cm-negative) wavy; + text-decoration-skip-ink: none; + } + +#cm-dnrRules .cm-editor .cm-panel.cm-search { + display: flex; + flex-wrap: wrap; + font-family: sans-serif; + font-size: var(--font-size); + gap: 0.5em 1em; + padding: 0.5em 1.5em 0.5em 0.5em; + } + +#cm-dnrRules .cm-editor .cm-panel.cm-search > * { + margin: 0; + } + +#cm-dnrRules .cm-editor .cm-panel.cm-search .cm-textfield, +#cm-dnrRules .cm-editor .cm-panel.cm-search .cm-button, +#cm-dnrRules .cm-editor .cm-panel.cm-search label { + background-image: inherit; + border: inherit; + flex-grow: 0; + font-size: var(--button-font-size); + min-height: calc(var(--button-font-size) * 1.8); + } + +#cm-dnrRules .cm-editor .cm-panel.info-panel { + display: flex; + flex-wrap: nowrap; + font-size: var(--font-size); + padding: var(--default-gap-xxsmall) var(--default-gap-xsmall); + } +#cm-dnrRules .cm-editor .cm-panel.info-panel .info { + flex-grow: 1; + overflow: auto; + } +#cm-dnrRules .cm-editor .cm-panel.info-panel .close { + cursor: default; + flex-shrink: 0; + padding-inline-start: 1em; + } +#cm-dnrRules .cm-editor .cm-panel.info-panel .close::after { + content: '\2715'; + } +#cm-dnrRules .cm-editor .cm-panel.summary-panel { + background-color: color-mix(in srgb, var(--info1-ink) 15%, transparent 85%); + } + +#cm-dnrRules .cm-editor .cm-panel.feedback-panel { + background-color: color-mix(in srgb, var(--info3-ink) 15%, transparent 85%); + white-space: pre; + max-height: 10cqh; + } + +#cm-dnrRules .cm-editor .cm-gutterElement { + cursor: default; + user-select: none; + } + +#cm-dnrRules .cm-editor .cm-tooltip .badmark-tooltip { + background-color: color-mix(in srgb, var(--info3-ink) 15%, transparent 85%); + padding: var(--default-gap-xxsmall) var(--default-gap-xsmall); + } \ No newline at end of file diff --git a/platform/mv3/extension/css/settings.css b/platform/mv3/extension/css/settings.css index a9b4ad6d5433c..4e6618da67215 100644 --- a/platform/mv3/extension/css/settings.css +++ b/platform/mv3/extension/css/settings.css @@ -25,6 +25,9 @@ body[data-forbid~="filteringMode"] section[data-pane="settings"] > div:has(> h3[ body[data-forbid~="filteringMode"] section[data-pane="settings"] > div:has(> h3[data-i18n="filteringMode0Name"]) { display: none; } +body[data-forbid~="develop"] #developerMode { + display: none; + } label:has(input[type="checkbox"][disabled]), label:has(input[type="checkbox"][disabled]) + legend { diff --git a/platform/mv3/extension/dashboard.html b/platform/mv3/extension/dashboard.html index 3009215578011..3acecd56949f1 100644 --- a/platform/mv3/extension/dashboard.html +++ b/platform/mv3/extension/dashboard.html @@ -13,18 +13,20 @@ + - + -
+
+
@@ -85,7 +87,7 @@

-

@@ -103,6 +105,20 @@

+
+
+

+ + +   + + +

+

+
+
+
+
@@ -155,12 +172,28 @@

+ + + + diff --git a/platform/mv3/extension/js/background.js b/platform/mv3/extension/js/background.js index 7d7661a37e700..5cd59ce8d86c4 100644 --- a/platform/mv3/extension/js/background.js +++ b/platform/mv3/extension/js/background.js @@ -59,6 +59,7 @@ import { setStrictBlockMode, updateDynamicRules, updateSessionRules, + updateUserRules, } from './ruleset-manager.js'; import { @@ -135,6 +136,17 @@ async function onPermissionsAdded(permissions) { /******************************************************************************/ +function setDeveloperMode(state) { + rulesetConfig.developerMode = state === true; + toggleDeveloperMode(rulesetConfig.developerMode); + return Promise.all([ + updateUserRules(), + saveRulesetConfig(), + ]); +} + +/******************************************************************************/ + function onMessage(request, sender, callback) { // Does not require trusted origin. @@ -275,9 +287,7 @@ function onMessage(request, sender, callback) { return true; case 'setDeveloperMode': - rulesetConfig.developerMode = request.state; - toggleDeveloperMode(rulesetConfig.developerMode); - saveRulesetConfig().then(( ) => { + setDeveloperMode(request.state).then(( ) => { callback(); }); return true; @@ -390,6 +400,12 @@ function onMessage(request, sender, callback) { }); break; + case 'updateUserDnrRules': + updateUserRules().then(result => { + callback(result); + }); + return true; + default: break; } @@ -472,8 +488,15 @@ async function startSession() { } } - // Required to ensure the up to date property is available when needed - adminReadEx('disabledFeatures'); + // Required to ensure up to date properties are available when needed + adminReadEx('disabledFeatures').then(items => { + if ( Array.isArray(items) === false ) { return; } + if ( items.includes('develop') ) { + if ( rulesetConfig.developerMode ) { + setDeveloperMode(false); + } + } + }); } /******************************************************************************/ diff --git a/platform/mv3/extension/js/dashboard.js b/platform/mv3/extension/js/dashboard.js index d43f45c5b7a8d..20f6b5ed55c70 100644 --- a/platform/mv3/extension/js/dashboard.js +++ b/platform/mv3/extension/js/dashboard.js @@ -19,6 +19,12 @@ Home: https://github.com/gorhill/uBlock */ +import { + localRead, + localRemove, + localWrite, +} from './ext.js'; + import { dom } from './dom.js'; import { runtime } from './ext.js'; @@ -32,7 +38,18 @@ import { runtime } from './ext.js'; dom.attr('a', 'target', '_blank'); dom.on('#dashboard-nav', 'click', '.tabButton', ev => { - dom.body.dataset.pane = ev.target.dataset.pane; + const { pane } = ev.target.dataset; + dom.body.dataset.pane = pane; + if ( pane === 'settings' ) { + localRemove('activeDashboardPane'); + } else { + localWrite('activeDashboardPane', pane); + } +}); + +localRead('activeDashboardPane').then(pane => { + if ( typeof pane !== 'string' ) { return; } + dom.body.dataset.pane = pane; }); /******************************************************************************/ diff --git a/platform/mv3/extension/js/develop.js b/platform/mv3/extension/js/develop.js new file mode 100644 index 0000000000000..86f60bccfd63c --- /dev/null +++ b/platform/mv3/extension/js/develop.js @@ -0,0 +1,687 @@ +/******************************************************************************* + + uBlock Origin Lite - a comprehensive, MV3-compliant content blocker + Copyright (C) 2014-present Raymond Hill + + 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/}. + + Home: https://github.com/gorhill/uBlock +*/ + +import { + browser, + localRead, + localRemove, + localWrite, + sendMessage, +} from './ext.js'; +import { dom, qs$ } from './dom.js'; +import { rulesFromText, textFromRules } from './dnr-parser.js'; +import { dnr } from './ext-compat.js'; +import { i18n$ } from './i18n.js'; + +/******************************************************************************/ + +// Details of YAML document(s) intersecting with a text span. If the text span +// starts on a YAML document divider, the previous YAML document will be +// included. If the text span ends on a YAML document divider, the next YAML +// document will be included. + +function snapToYamlDocument(doc, start, end) { + let yamlDocStart = doc.lineAt(start).number; + if ( reYamlDocSeparator.test(doc.line(yamlDocStart).text) ) { + if ( yamlDocStart > 1 ) { + yamlDocStart -= 1; + } + } + while ( yamlDocStart > 1 ) { + const line = doc.line(yamlDocStart); + if ( reYamlDocSeparator.test(line.text) ) { break; } + yamlDocStart -= 1; + } + const lastLine = doc.lines; + let yamlDocEnd = doc.lineAt(end).number; + if ( reYamlDocSeparator.test(doc.line(yamlDocEnd).text) ) { + if ( yamlDocEnd < lastLine ) { + yamlDocEnd += 1; + } + } + while ( yamlDocEnd < lastLine ) { + const line = doc.line(yamlDocEnd); + if ( reYamlDocSeparator.test(line.text) ) { break; } + yamlDocEnd += 1; + } + return { yamlDocStart, yamlDocEnd }; +} + +function addToModifiedRange(doc, start, end) { + const { yamlDocStart, yamlDocEnd } = snapToYamlDocument(doc, start, end); + if ( modifiedRange.start === -1 || yamlDocStart < modifiedRange.start ) { + modifiedRange.start = yamlDocStart; + } + if ( modifiedRange.end === -1 || yamlDocEnd > modifiedRange.end ) { + modifiedRange.end = yamlDocEnd; + } +} + +const reYamlDocSeparator = /^(?:---|...)\s*$/; +const modifiedRange = { start: -1, end: -1 }; + +/******************************************************************************/ + +function rulesFromJSON(json) { + let content = json.trim(); + if ( /^[[{]/.test(content) === false ) { + const match = /^[^[{]+/.exec(content); + if ( match === null ) { return; } + content = content.slice(match[0].length); + } + const firstChar = content.charAt(0); + const expectedLastChar = firstChar === '[' ? ']' : '}'; + if ( content.at(-1) !== expectedLastChar ) { + const re = new RegExp(`\\${expectedLastChar}[^\\${expectedLastChar}]+$`); + const match = re.exec(content); + if ( match === null ) { return; } + content = content.slice(0, match.index+1); + } + if ( content.startsWith('{') && content.endsWith('}') ) { + content = `[${content}]`; + } + try { + const rules = JSON.parse(content); + if ( Array.isArray(rules) ) { return rules; } + } + catch { + } +} + +/******************************************************************************/ + +function lineIndentAt(line) { + const match = /^(?: {2})*/.exec(line.text); + const indent = match !== null ? match[0].length : -1; + if ( indent === -1 || (indent & 1) !== 0 ) { return -1; } + return indent / 2; +} + +function getScopeAt(from) { + const { doc } = cmRules.state; + const lineFrom = doc.lineAt(from); + let depth = lineIndentAt(lineFrom); + if ( depth === -1 ) { return; } + const text = lineFrom.text.trim(); + if ( text.startsWith('#') ) { return; } + const path = []; + const pos = text.indexOf(':'); + if ( pos !== -1 ) { + path.push(text.slice(0, pos+1)); + } + let lineNo = lineFrom.number; + while ( depth > 0 && lineNo > 1 ) { + lineNo -= 1; + const lineBefore = doc.line(lineNo); + const text = lineBefore.text.trim(); + if ( text.startsWith('#') ) { continue; } + if ( lineIndentAt(lineBefore) > (depth-1) ) { continue; } + const match = /^- ([^:]+:)/.exec(text); + if ( match !== null ) { + path.unshift(match[1]); + } else { + path.unshift(text); + } + depth -= 1; + } + return path.join(''); +} + +function getAutocompleteCandidates(from) { + const scope = getScopeAt(from); + switch ( scope ) { + case '': + return { + before: /^$/, + candidates: [ + { token: 'action:', after: '\n ' }, + { token: 'condition:', after: '\n ' }, + { token: 'priority:', after: ' ' }, + { token: '---', after: '\n' }, + ] + }; + case 'action:': + return { + before: /^ {2}$/, + candidates: [ + { token: 'type:', after: ' ' }, + { token: 'redirect:', after: '\n ' }, + { token: 'requestHeaders:', after: '\n - header: ' }, + { token: 'responseHeaders:', after: '\n - header: ' }, + ], + }; + case 'action:type:': + return { + before: /: $/, + candidates: [ + { token: 'block', after: '\n ' }, + { token: 'redirect', after: '\n ' }, + { token: 'allow', after: '\n ' }, + { token: 'modifyHeaders', after: '\n ' }, + { token: 'upgradeScheme', after: '\n ' }, + { token: 'allowAllRequest', after: '\n ' }, + ], + }; + case 'action:redirect:': + return { + before: /^ {4}$/, + candidates: [ + { token: 'extensionPath:', after: ' ' }, + { token: 'regexSubstitution:', after: ' ' }, + { token: 'transform:', after: '\n ' }, + { token: 'url:', after: ' ' }, + ], + }; + case 'action:redirect:transform:': + return { + before: /^ {6}$/, + candidates: [ + { token: 'fragment:', after: ' ' }, + { token: 'host:', after: ' ' }, + { token: 'path:', after: ' ' }, + { token: 'port:', after: ' ' }, + { token: 'query:', after: ' ' }, + { token: 'scheme:', after: ' ' }, + { token: 'queryTransform:', after: '\n ' }, + ], + }; + case 'action:redirect:transform:queryTransform:': + return { + before: /^ {8}$/, + candidates: [ + { token: 'addOrReplaceParams:', after: '\n - ' }, + { token: 'removeParams:', after: '\n - ' }, + ], + }; + case 'action:responseHeaders:': + return { + before: /^ {4}- $/, + candidates: [ + { token: 'header:', after: ' ' }, + ], + }; + case 'action:responseHeaders:header:': + return { + before: /^ {6}$/, + candidates: [ + { token: 'operation:', after: ' ' }, + { token: 'value:', after: ' ' }, + ], + }; + case 'action:responseHeaders:header:operation:': + return { + before: /: $/, + candidates: [ + { token: 'append', after: '\n value: ' }, + { token: 'set', after: '\n value: ' }, + { token: 'remove', after: '\n ' }, + ], + }; + case 'condition:': + return { + before: /^ {2}$/, + candidates: [ + { token: 'domainType:', after: ' ' }, + { token: 'isUrlFilterCaseSensitive:', after: ' ' }, + { token: 'regexFilter:', after: ' ' }, + { token: 'urlFilter:', after: ' ' }, + { token: 'initiatorDomains:', after: '\n - ' }, + { token: 'excludedInitiatorDomains:', after: '\n - ' }, + { token: 'requestDomains:', after: '\n - ' }, + { token: 'excludedRequestDomains:', after: '\n - ' }, + { token: 'resourceTypes:', after: '\n - ' }, + { token: 'excludedResourceTypes:', after: '\n - ' }, + { token: 'requestMethods:', after: '\n - ' }, + { token: 'excludedRequestMethods:', after: '\n - ' }, + { token: 'responseHeaders:', after: '\n - ' }, + { token: 'excludedResponseHeaders:', after: '\n - ' }, + ], + }; + case 'condition:domainType:': + return { + before: /: $/, + candidates: [ + { token: 'firstParty', after: '\n ' }, + { token: 'thirdParty', after: '\n ' }, + ], + }; + case 'condition:isUrlFilterCaseSensitive:': + return { + before: /: $/, + candidates: [ + { token: 'true', after: '\n ' }, + { token: 'false', after: '\n ' }, + ], + }; + case 'condition:requestMethods:': + case 'condition:excludedRequestMethods:': + return { + before: /^ {4}- $/, + candidates: [ + { token: 'connect', after: '\n - ' }, + { token: 'delete', after: '\n - ' }, + { token: 'get', after: '\n - ' }, + { token: 'head', after: '\n - ' }, + { token: 'options', after: '\n - ' }, + { token: 'patch', after: '\n - ' }, + { token: 'post', after: '\n - ' }, + { token: 'put', after: '\n - ' }, + { token: 'other', after: '\n ' }, + ], + }; + case 'condition:resourceTypes:': + case 'condition:excludedResourceTypes:': + return { + before: /^ {4}- $/, + candidates: [ + { token: 'main_frame', after: '\n - ' }, + { token: 'sub_frame', after: '\n - ' }, + { token: 'stylesheet', after: '\n - ' }, + { token: 'script', after: '\n - ' }, + { token: 'image', after: '\n - ' }, + { token: 'font', after: '\n - ' }, + { token: 'object', after: '\n - ' }, + { token: 'xmlhttprequest', after: '\n - ' }, + { token: 'ping', after: '\n - ' }, + { token: 'csp_report', after: '\n - ' }, + { token: 'media', after: '\n - ' }, + { token: 'websocket', after: '\n - ' }, + { token: 'webtransport', after: '\n - ' }, + { token: 'webbundle', after: '\n - ' }, + { token: 'other', after: '\n ' }, + ], + }; + } +} + +function autoComplete(context) { + const match = context.matchBefore(/[\w-]*/); + if ( match === undefined ) { return null; } + const result = getAutocompleteCandidates(match.from); + if ( result === undefined ) { return null; } + if ( result.before !== undefined ) { + const { doc } = context.state; + const line = doc.lineAt(context.pos); + const before = doc.sliceString(line.from, match.from); + if ( result.before.test(before) === false ) { return null; } + } + const filtered = result.candidates.filter(e => + e.token !== match.text || e.after !== '\n' + ); + return { + from: match.from, + options: filtered.map(e => ({ label: e.token, apply: `${e.token}${e.after}` })), + validFor: /\w*/, + }; +} + +/******************************************************************************/ + +function setEditorText(text) { + if ( text === undefined ) { return; } + if ( text !== '' ) { text += '\n'; } + cmRules.dispatch({ + changes: { + from: 0, to: cmRules.state.doc.length, + insert: text + }, + }); +} + +function getEditorText() { + return cmRules.state.doc.toString(); +} + +/******************************************************************************/ + +function saveEditorText() { + const text = getEditorText().trim(); + const promise = text.length !== 0 + ? localWrite('userDnrRules', text) + : localRemove('userDnrRules'); + promise.then(( ) => { + lastSavedText = text; + updateView(); + }).then(( ) => + sendMessage({ what: 'updateUserDnrRules' }) + ).then(result => { + if ( result instanceof Object === false ) { return; } + updateFeedbackPanel(result); + }); +} + +/******************************************************************************/ + +async function validateRegexes(regexes) { + if ( regexes.length === 0 ) { return; } + const promises = regexes.map(regex => validateRegex(regex)); + await Promise.all(promises); + for ( const regex of regexes ) { + const i = validatedRegexes.regexes.indexOf(regex); + if ( i === -1 ) { continue; } + const reason = validatedRegexes.results[i]; + if ( reason === true ) { continue; } + const entries = self.cm6.findAll(cmRules, + `(?<=\\bregexFilter: )${RegExp.escape(regex)}` + ); + for ( const entry of entries ) { + self.cm6.spanErrorAdd(cmRules, entry.from, entry.to, reason); + } + } +} + +async function validateRegex(regex) { + const details = await dnr.isRegexSupported({ regex }); + const result = details.isSupported || details.reason; + if ( validatedRegexes.regexes.length > 32 ) { + validatedRegexes.regexes.pop(); + validatedRegexes.results.pop(); + } + validatedRegexes.regexes.unshift(regex); + validatedRegexes.results.unshift(result); +} + +const validatedRegexes = { + regexes: [], + results: [], +}; + +/******************************************************************************/ + +function updateView() { + const { doc } = cmRules.state; + const changed = doc.toString().trim() !== + lastSavedText.trim(); + dom.attr('#dnrRulesApply', 'disabled', changed ? null : ''); + dom.attr('#dnrRulesRevert', 'disabled', changed ? null : ''); + const { start, end } = modifiedRange; + if ( start === -1 || end === -1 ) { return; } + modifiedRange.start = modifiedRange.end = -1; + self.cm6.lineErrorClear(cmRules, start, end); + self.cm6.spanErrorClear(cmRules, start, end); + const firstLine = doc.line(start); + const lastLine = doc.line(end); + const text = doc.sliceString(firstLine.from, lastLine.to); + const { bad } = rulesFromText(text); + if ( Array.isArray(bad) && bad.length !== 0 ) { + self.cm6.lineErrorAdd(cmRules, bad.map(i => i + start)); + } + const entries = self.cm6.findAll( + cmRules, + '\\bregexFilter: (\\S+)', + firstLine.from, + lastLine.to + ); + const regexes = []; + for ( const entry of entries ) { + const regex = entry.match[1]; + const i = validatedRegexes.regexes.indexOf(regex); + if ( i !== -1 ) { + const reason = validatedRegexes.results[i]; + if ( reason === true ) { continue; } + self.cm6.spanErrorAdd(cmRules, entry.from+13, entry.to, reason); + } else { + regexes.push(regex); + } + } + validateRegexes(regexes); +} + +function updateViewAsync() { + if ( updateViewAsync.timer !== undefined ) { return; } + updateViewAsync.timer = self.setTimeout(( ) => { + updateViewAsync.timer = undefined; + updateView(); + }, 71); +} + +/******************************************************************************/ + +function updateSummaryPanel(info) { + self.cm6.showSummaryPanel(cmRules, { + template: '.summary-panel', + text: i18n$('dnrRulesCountInfo') + .replace('{count}', (info.userDnrRuleCount || 0).toLocaleString()), + }); +} + +browser.storage.onChanged.addListener((changes, area) => { + if ( area !== 'local' ) { return; } + const { userDnrRuleCount } = changes; + if ( userDnrRuleCount instanceof Object === false ) { return; } + const { newValue } = changes.userDnrRuleCount; + updateSummaryPanel({ userDnrRuleCount: newValue }); +}); + +localRead('userDnrRuleCount').then(userDnrRuleCount => { + updateSummaryPanel({ userDnrRuleCount }) +}); + +function updateFeedbackPanel(info) { + const errors = []; + if ( Array.isArray(info.errors) ) { + info.errors.forEach(e => errors.push(e)); + } + const text = errors.join('\n'); + self.cm6.showFeedbackPanel(cmRules, { template: '.feedback-panel', text }); +} + +/******************************************************************************/ + +function importRulesFromFile() { + const input = qs$('input[type="file"]'); + input.onchange = ev => { + input.onchange = null; + const file = ev.target.files[0]; + if ( file === undefined || file.name === '' ) { return; } + if ( file.type !== 'application/json' ) { return; } + const fr = new FileReader(); + fr.onload = ( ) => { + if ( typeof fr.result !== 'string' ) { return; } + const rules = rulesFromJSON(fr.result); + if ( rules === undefined ) { return; } + const text = textFromRules(rules); + if ( text === undefined ) { return; } + const { doc } = cmRules.state; + const lastChars = doc.toString().trimEnd().slice(-4); + const lastLine = doc.line(doc.lines); + let from = lastLine.to; + let prepend = ''; + if ( lastLine.text !== '' ) { + prepend = '\n'; + } else { + from = lastLine.from; + } + if ( /(?:^|\n)---$/.test(lastChars) === false ) { + prepend = `${prepend}---\n`; + } + cmRules.dispatch({ changes: { from, insert: `${prepend}${text}` } }); + cmRules.focus(); + }; + fr.readAsText(file); + }; + // Reset to empty string, this will ensure a change event is properly + // triggered if the user pick a file, even if it's the same as the last + // one picked. + input.value = ''; + input.click(); +} + +dom.on('#dnrRulesImport', 'click', importRulesFromFile); + +/******************************************************************************/ + +function exportRulesToFile() { + const text = getEditorText(); + const { rules } = rulesFromText(text); + if ( Array.isArray(rules) === false ) { return; } + let ruleId = 1; + for ( const rule of rules ) { + rule.id = ruleId++; + } + const filename = 'my-ubol-dnr-rules.json'; + const a = document.createElement('a'); + a.href = `data:application/json;charset=utf-8,${JSON.stringify(rules, null, 2)}`; + dom.attr(a, 'download', filename || ''); + dom.attr(a, 'type', 'application/json'); + a.click(); +} + +dom.on('#dnrRulesExport', 'click', exportRulesToFile); + +/******************************************************************************/ + +function importRulesFromPaste(from, to) { + if ( from === undefined || to === undefined ) { return; } + // Paste position must match start of a line + const { doc } = cmRules.state; + const lineFrom = doc.lineAt(from); + if ( lineFrom.from !== from ) { return; } + // Paste position must match a rule boundary + if ( lineFrom.number !== 1 ) { + const lineBefore = doc.line(lineFrom.number-1); + if ( /^---\s*$/.test(lineBefore.text) === false ) { return; } + } + const pastedText = doc.sliceString(from, to); + const rules = rulesFromJSON(pastedText); + if ( rules === undefined ) { return; } + const yamlText = textFromRules(rules); + if ( yamlText === undefined ) { return; } + cmRules.dispatch({ changes: { from, to, insert: yamlText } }); +} + +/******************************************************************************/ + +function cmUpdateListener(info) { + if ( info.docChanged === false ) { return; } + const doc = info.state.doc; + info.changes.iterChangedRanges((fromA, toA, fromB, toB) => { + addToModifiedRange(doc, fromB, toB); + }); + for ( const transaction of info.transactions ) { + if ( transaction.isUserEvent('input.paste') === false ) { continue; } + if ( transaction.docChanged === false ) { continue; } + let from, to; + transaction.changes.iterChangedRanges((fromA, toA, fromB, toB) => { + if ( from === undefined || fromB < from ) { from = fromB; } + if ( to === undefined || toB > to ) { to = toB; } + }); + importRulesFromPaste(from, to); + break; + } + updateViewAsync(); +} + +/******************************************************************************/ + +function gutterClick(view, info) { + const reSeparator = /^---\s*/; + const { doc } = view.state; + const lineFirst = doc.lineAt(info.from); + if ( lineFirst.text === '' ) { return false; } + let { from, to } = lineFirst; + if ( reSeparator.test(lineFirst.text) ) { + let lineNo = lineFirst.number + 1; + while ( lineNo < doc.lines ) { + const line = doc.line(lineNo); + if ( reSeparator.test(line.text) ) { break; } + to = line.to; + lineNo += 1; + } + } + view.dispatch({ + selection: { anchor: from, head: to+1 } + }); + view.focus(); + return true; +} + +/******************************************************************************/ + +function hoverTooltip(view, pos, side) { + const details = view.domAtPos(pos); + const textNode = details.node; + if ( textNode.nodeType !== 3 ) { return null; } + const { parentElement } = textNode; + const targetElement = parentElement.closest('[data-tooltip]'); + if ( targetElement === null ) { return null; } + const tooltipText = targetElement.getAttribute('data-tooltip'); + if ( Boolean(tooltipText) === false ) { return null; } + const start = pos - details.offset; + const end = start + textNode.nodeValue.length; + if ( start === pos && side < 0 || end === pos && side > 0 ) { return null; } + return { + above: true, + pos: start, + end, + create() { + const template = document.querySelector('.badmark-tooltip'); + const fragment = template.content.cloneNode(true); + const dom = fragment.querySelector('.badmark-tooltip'); + dom.textContent = tooltipText; + return { dom }; + }, + }; +} + +/******************************************************************************/ + +const cmRules = (( ) => { + return self.cm6.createEditorView({ + dnrRules: true, + oneDark: dom.cl.has(':root', 'dark'), + updateListener: cmUpdateListener, + saveListener: ( ) => { + saveEditorText(); + }, + lineError: true, + spanError: true, + // https://codemirror.net/examples/autocompletion/ + autocompletion: { + override: [ autoComplete ], + activateOnCompletion: ( ) => true, + }, + gutterClick, + hoverTooltip, + }, qs$('#cm-dnrRules')); +})(); + +/******************************************************************************/ + +let lastSavedText = ''; + +localRead('userDnrRules').then(text => { + text ||= ''; + setEditorText(text); + lastSavedText = text; + self.cm6.resetUndoRedo(cmRules); + + dom.on('#dnrRulesApply', 'click', ( ) => { + saveEditorText(); + }); + + dom.on('#dnrRulesRevert', 'click', ( ) => { + setEditorText(lastSavedText); + sendMessage({ what: 'updateUserDnrRules' }); + }); +}); + +/******************************************************************************/ diff --git a/platform/mv3/extension/js/dnr-parser.js b/platform/mv3/extension/js/dnr-parser.js new file mode 100644 index 0000000000000..8c4799b8816cd --- /dev/null +++ b/platform/mv3/extension/js/dnr-parser.js @@ -0,0 +1,607 @@ +/******************************************************************************* + + uBlock Origin Lite - a comprehensive, MV3-compliant content blocker + Copyright (C) 2014-present Raymond Hill + + 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/}. + + Home: https://github.com/gorhill/uBlock +*/ + +/******************************************************************************/ + +const validActionValues = [ + 'block', + 'redirect', + 'allow', + 'upgradeScheme', + 'modifyHeaders', + 'allowAllRequests', +]; + +const validBoolValues = [ + 'false', + 'true', +]; + +const validHeaderOpValues = [ + 'append', + 'remove', + 'set', +]; + +const validDomainTypeValues = [ + 'firstParty', + 'thirdParty', +]; + +const validRequestMethodValues = [ + 'connect', + 'delete', + 'get', + 'head', + 'options', + 'patch', + 'post', + 'put', + 'other', +]; + +const validResourceTypeValues = [ + 'main_frame', + 'sub_frame', + 'stylesheet', + 'script', + 'image', + 'font', + 'object', + 'xmlhttprequest', + 'ping', + 'csp_report', + 'media', + 'websocket', + 'webtransport', + 'webbundle', + 'other', +]; + +/******************************************************************************/ + +function selectParser(scope, rule, node) { + const parser = perScopeParsers[scope.join('.')]; + if ( parser === undefined ) { return false; } + return parser(scope, rule, node); +} + +const perScopeParsers = { + '': function(scope, rule, node) { + const { key, val } = node; + switch ( key ) { + case 'action': + if ( val !== undefined ) { return false; } + rule.action = {}; + scope.push('action'); + break; + case 'condition': + if ( val !== undefined ) { return false; } + rule.condition = {}; + scope.push('condition'); + break; + case 'priority': { + const n = parseInt(val, 10); + if ( isNaN(n) || n <= 1 ) { return false; } + rule.priority = n; + break; + } + default: + return false; + } + return true; + }, + 'action': function(scope, rule, node) { + const { key, val } = node; + switch ( key ) { + case 'type': + if ( validActionValues.includes(val) === false ) { return false; } + rule.action.type = val; + break; + case 'redirect': + rule.action.redirect = {}; + scope.push('redirect'); + break; + case 'requestHeaders': + rule.action.requestHeaders = []; + scope.push('requestHeaders'); + break; + case 'responseHeaders': + rule.action.responseHeaders = []; + scope.push('responseHeaders'); + break; + default: + return false; + } + return true; + }, + 'action.redirect': function(scope, rule, node) { + const { key, val } = node; + switch ( key ) { + case 'extensionPath': + rule.action.redirect.extensionPath = val; + break; + case 'regexSubstitution': + rule.action.redirect.regexSubstitution = val; + break; + case 'transform': + rule.action.redirect.transform = {}; + scope.push('transform'); + break; + case 'url': + rule.action.redirect.url = val; + break; + default: + return false; + } + return true; + }, + 'action.redirect.transform': function(scope, rule, node) { + const { key, val } = node; + switch ( key ) { + case 'fragment': + case 'host': + case 'path': + case 'port': + case 'query': + case 'scheme': { + if ( val === undefined ) { return false; } + rule.action.redirect.transform[key] = val; + break; + } + case 'queryTransform': + rule.action.redirect.transform.queryTransform = {}; + scope.push('queryTransform'); + break; + default: + return false; + } + return true; + }, + 'action.redirect.transform.queryTransform': function(scope, rule, node) { + const { key, val } = node; + if ( val !== undefined ) { return false; } + switch ( key ) { + case 'addOrReplaceParams': + rule.action.redirect.transform.queryTransform.addOrReplaceParams = []; + scope.push('addOrReplaceParams'); + break; + case 'removeParams': + rule.action.redirect.transform.queryTransform.removeParams = []; + scope.push('removeParams'); + break; + default: + return false; + } + return true; + }, + 'action.redirect.transform.queryTransform.addOrReplaceParams': function(scope, rule, node) { + if ( node.list !== true ) { return false; } + rule.action.redirect.transform.queryTransform.addOrReplaceParams.push({}); + scope.push('@'); + return selectParser(scope, rule, node); + }, + 'action.redirect.transform.queryTransform.addOrReplaceParams.@': function(scope, rule, node) { + const { key, val } = node; + if ( val === undefined ) { return false; } + const item = rule.action.redirect.transform.queryTransform.addOrReplaceParams.at(-1); + switch ( key ) { + case 'key': + item.key = val; + break; + case 'value': + item.value = val; + break; + case 'replaceOnly': + if ( validBoolValues.includes(val) === false ) { return false; } + item.replaceOnly = val === 'true'; + break; + default: + return false; + } + return true; + }, + 'action.redirect.transform.queryTransform.removeParams': function(scope, rule, node) { + if ( node.list !== true ) { return false; } + rule.action.redirect.transform.queryTransform.removeParams.push(node.val); + return true; + }, + 'action.requestHeaders': function(scope, rule, node) { + if ( node.list !== true ) { return false; } + rule.action.requestHeaders.push({}); + scope.push('@'); + return selectParser(scope, rule, node); + }, + 'action.requestHeaders.@': function(scope, rule, node) { + const { key, val } = node; + const item = rule.action.requestHeaders.at(-1); + switch ( key ) { + case 'header': + item.header = val; + break; + case 'value': + item.value = val; + break; + case 'operation': + if ( validHeaderOpValues.includes(val) === false ) { return false; } + item.operation = val; + break; + default: + return false; + } + return true; + }, + 'action.responseHeaders': function(scope, rule, node) { + if ( node.list !== true ) { return false; } + rule.action.responseHeaders.push({}); + scope.push('@'); + return selectParser(scope, rule, node); + }, + 'action.responseHeaders.@': function(scope, rule, node) { + const { key, val } = node; + const item = rule.action.responseHeaders.at(-1); + switch ( key ) { + case 'header': + item.header = val; + break; + case 'value': + item.value = val; + break; + case 'operation': + if ( validHeaderOpValues.includes(val) === false ) { return false; } + item.operation = val; + break; + default: + return false; + } + return true; + }, + 'condition': function(scope, rule, node) { + const { key, val } = node; + switch ( key ) { + case 'domainType': + if ( validDomainTypeValues.includes(val) === false ) { return false; } + rule.condition.domainType = val; + break; + case 'isUrlFilterCaseSensitive': + if ( validBoolValues.includes(val) === false ) { return false; } + rule.condition.isUrlFilterCaseSensitive = val === 'true'; + break; + case 'regexFilter': + if ( val === undefined ) { return false; } + rule.condition.regexFilter = val; + break; + case 'urlFilter': + if ( val === undefined ) { return false; } + rule.condition.urlFilter = val; + break; + case 'initiatorDomains': + rule.condition.initiatorDomains = []; + scope.push('initiatorDomains'); + break; + case 'excludedInitiatorDomains': + rule.condition.excludedInitiatorDomains = []; + scope.push('excludedInitiatorDomains'); + break; + case 'requestDomains': + rule.condition.requestDomains = []; + scope.push('requestDomains'); + break; + case 'excludedRequestDomains': + rule.condition.excludedRequestDomains = []; + scope.push('excludedRequestDomains'); + break; + case 'resourceTypes': + rule.condition.resourceTypes = []; + scope.push('resourceTypes'); + break; + case 'excludedResourceTypes': + rule.condition.excludedResourceTypes = []; + scope.push('excludedResourceTypes'); + break; + case 'requestMethods': + rule.condition.requestMethods = []; + scope.push('requestMethods'); + break; + case 'excludedRequestMethods': + rule.condition.excludedRequestMethods = []; + scope.push('excludedRequestMethods'); + break; + case 'responseHeaders': + rule.condition.responseHeaders = []; + scope.push('responseHeaders'); + break; + case 'excludedResponseHeaders': + rule.condition.excludedResponseHeaders = []; + scope.push('excludedResponseHeaders'); + break; + default: + return false; + } + return true; + }, + 'condition.initiatorDomains': function(scope, rule, node) { + if ( node.list !== true ) { return false; } + rule.condition.initiatorDomains.push(node.val); + return true; + }, + 'condition.excludedInitiatorDomains': function(scope, rule, node) { + if ( node.list !== true ) { return false; } + rule.condition.excludedInitiatorDomains.push(node.val); + return true; + }, + 'condition.requestDomains': function(scope, rule, node) { + if ( node.list !== true ) { return false; } + rule.condition.requestDomains.push(node.val); + return true; + }, + 'condition.excludedRequestDomains': function(scope, rule, node) { + if ( node.list !== true ) { return false; } + rule.condition.excludedRequestDomains.push(node.val); + return true; + }, + 'condition.resourceTypes': function(scope, rule, node) { + if ( node.list !== true ) { return false; } + if ( validResourceTypeValues.includes(node.val) === false ) { return false; } + rule.condition.resourceTypes.push(node.val); + return true; + }, + 'condition.excludedResourceTypes': function(scope, rule, node) { + if ( node.list !== true ) { return false; } + if ( validResourceTypeValues.includes(node.val) === false ) { return false; } + rule.condition.excludedResourceTypes.push(node.val); + return true; + }, + 'condition.requestMethods': function(scope, rule, node) { + if ( node.list !== true ) { return false; } + if ( validRequestMethodValues.includes(node.val) === false ) { return false; } + rule.condition.requestMethods.push(node.val); + return true; + }, + 'condition.excludedRequestMethods': function(scope, rule, node) { + if ( node.list !== true ) { return false; } + if ( validRequestMethodValues.includes(node.val) === false ) { return false; } + rule.condition.excludedRequestMethods.push(node.val); + return true; + }, + 'condition.responseHeaders': function(scope, rule, node) { + if ( node.list !== true ) { return false; } + rule.condition.responseHeaders.push({}); + scope.push('@'); + return selectParser(scope, rule, node); + }, + 'condition.responseHeaders.@': function(scope, rule, node) { + const item = rule.condition.responseHeaders.at(-1); + switch ( node.key ) { + case 'header': + if ( node.val === undefined ) { return false; } + item.header = node.val; + break; + case 'values': + item.values = []; + scope.push('values'); + break; + case 'excludedValues': + item.excludedValues = []; + scope.push('excludedValues'); + break; + default: + return false; + } + return true; + }, + 'condition.responseHeaders.@.values': function(scope, rule, node) { + if ( node.list !== true ) { return false; } + const item = rule.condition.responseHeaders.at(-1); + item.values.push(node.val); + return true; + }, + 'condition.responseHeaders.@.excludedValues': function(scope, rule, node) { + if ( node.list !== true ) { return false; } + const item = rule.condition.responseHeaders.at(-1); + item.excludedValues.push(node.val); + return true; + }, + 'condition.excludedResponseHeaders': function(scope, rule, node) { + if ( node.list !== true ) { return false; } + rule.condition.excludedResponseHeaders.push({}); + scope.push('@'); + return selectParser(scope, rule, node); + }, + 'condition.excludedResponseHeaders.@': function(scope, rule, node) { + const item = rule.condition.excludedResponseHeaders.at(-1); + switch ( node.key ) { + case 'header': + if ( node.val === undefined ) { return false; } + item.header = node.val; + break; + case 'values': + item.values = []; + scope.push('values'); + break; + case 'excludedValues': + item.excludedValues = []; + scope.push('excludedValues'); + break; + default: + return false; + } + return true; + }, + 'condition.excludedResponseHeaders.@.values': function(scope, rule, node) { + if ( node.list !== true ) { return false; } + const item = rule.condition.excludedResponseHeaders.at(-1); + item.values.push(node.val); + return true; + }, + 'condition.excludedResponseHeaders.@.excludedValues': function(scope, rule, node) { + if ( node.list !== true ) { return false; } + const item = rule.condition.excludedResponseHeaders.at(-1); + item.excludedValues.push(node.val); + return true; + }, +}; + +/******************************************************************************/ + +function depthFromIndent(line) { + const match = /^\s*/.exec(line); + const count = match[0].length; + if ( (count & 1) !== 0 ) { return -1; } + return count / 2; +} + +/******************************************************************************/ + +function nodeFromLine(line) { + const match = reNodeParser.exec(line); + const out = {}; + if ( match === null ) { return out; } + if ( match[1] ) { + out.list = true; + } + if ( match[4] ) { + out.val = match[4].trim(); + } else if ( match[3] ) { + out.key = match[2]; + out.val = match[3].trim(); + if ( out.val === "''" ) { out.val = '' }; + } else { + out.key = match[2]; + } + return out; +} + +const reNodeParser = /^\s*(- )?(?:(\S+):( \S.*)?|(\S.*))$/; + +/******************************************************************************/ + +function ruleFromLines(lines, indices) { + const rule = {}; + const bad = []; + const scope = []; + for ( const i of indices ) { + const line = lines[i]; + const depth = depthFromIndent(line); + if ( depth < 0 ) { + bad.push(i); + continue; + } + scope.length = depth; + const node = nodeFromLine(line); + const result = selectParser(scope, rule, node); + if ( result === false ) { + bad.push(i); + } + } + if ( bad.length !== 0 ) { return { bad }; } + return { rule }; +} + +/******************************************************************************/ + +export function rulesFromText(text) { + const rules = []; + const bad = []; + const lines = [ ...text.split(/\n\r|\r\n|\n|\r/), '---' ]; + const indices = []; + for ( let i = 0; i < lines.length; i++ ) { + const line = lines[i].trimEnd(); + const trimmed = line.trimStart(); + if ( trimmed.startsWith('#') ) { continue; } + // Discard leading empty lines + if ( trimmed === '' ) { + if ( indices.length === 0 ) { continue; } + } + if ( line !== '---' && line !== '...' ) { + indices.push(i); + continue; + } + // Discard trailing empty lines + while ( indices.length !== 0 ) { + const s = lines[indices.at(-1)].trim(); + if ( s.length !== 0 ) { break; } + indices.pop(); + } + if ( indices.length === 0 ) { continue; } + const result = ruleFromLines(lines, indices); + if ( result.bad ) { + bad.push(...result.bad); + } else if ( result.rule ) { + rules.push(result.rule); + } + indices.length = 0; + } + return { rules, bad }; +} + +/******************************************************************************/ + +function textFromValue(val, depth) { + const indent = ' '.repeat(depth); + switch ( typeof val ) { + case 'boolean': + case 'number': + return `${val}`; + case 'string': + if ( val === '' ) { return "''"; } + return val; + } + const out = []; + if ( Array.isArray(val) ) { + for ( const a of val ) { + const s = textFromValue(a, depth+1); + if ( s === undefined ) { continue; } + out.push(`${indent}- ${s.trimStart()}`); + } + return out.join('\n'); + } + if ( val instanceof Object ) { + for ( const [ a, b ] of Object.entries(val) ) { + const s = textFromValue(b, depth+1); + if ( s === undefined ) { continue; } + if ( b instanceof Object ) { + out.push(`${indent}${a}:\n${s}`); + } else { + out.push(`${indent}${a}: ${s}`); + } + } + return out.join('\n'); + } +} + +/******************************************************************************/ + +export function textFromRules(rules) { + if ( Array.isArray(rules) === false ) { + if ( rules instanceof Object === false ) { return; } + rules = [ rules ]; + } + const out = []; + for ( const rule of rules ) { + if ( rule.id ) { rule.id = undefined }; + const text = textFromValue(rule, 0); + if ( text === undefined ) { continue; } + out.push(text, '---' ); + } + out.push(''); + return out.join('\n'); +} diff --git a/platform/mv3/extension/js/ext-compat.js b/platform/mv3/extension/js/ext-compat.js index 9e8469ce76321..06f0c8c91c992 100644 --- a/platform/mv3/extension/js/ext-compat.js +++ b/platform/mv3/extension/js/ext-compat.js @@ -36,7 +36,7 @@ const isSameRules = (a, b) => { /******************************************************************************/ -dnr.setAllowAllRules = async function(id, allowed, notAllowed, reverse) { +dnr.setAllowAllRules = async function(id, allowed, notAllowed, reverse, priority) { const [ beforeDynamicRules, beforeSessionRules, @@ -53,7 +53,7 @@ dnr.setAllowAllRules = async function(id, allowed, notAllowed, reverse) { condition: { resourceTypes: [ 'main_frame' ], }, - priority: 1000000, + priority, }; if ( allowed.length ) { rule0.condition.requestDomains = allowed.slice(); @@ -69,7 +69,7 @@ dnr.setAllowAllRules = async function(id, allowed, notAllowed, reverse) { condition: { tabIds: [ webext.tabs.TAB_ID_NONE ], }, - priority: 1000000, + priority, }; if ( allowed.length ) { rule1.condition.initiatorDomains = allowed.slice(); diff --git a/platform/mv3/extension/js/ruleset-manager.js b/platform/mv3/extension/js/ruleset-manager.js index d87133fc9a48c..7876ab821a459 100644 --- a/platform/mv3/extension/js/ruleset-manager.js +++ b/platform/mv3/extension/js/ruleset-manager.js @@ -35,11 +35,16 @@ import { dnr } from './ext-compat.js'; import { fetchJSON } from './fetch.js'; import { getAdminRulesets } from './admin.js'; import { hasBroadHostPermissions } from './utils.js'; +import { rulesFromText } from './dnr-parser.js'; import { ubolLog } from './debug.js'; /******************************************************************************/ +const SPECIAL_RULES_REALM = 5000000; +const USER_RULES_BASE_RULE_ID = 9000000; +const USER_RULES_PRIORITY = 1000000; const TRUSTED_DIRECTIVE_BASE_RULE_ID = 8000000; +const TRUSTED_DIRECTIVE_PRIORITY = USER_RULES_PRIORITY + 1000000; const STRICTBLOCK_PRIORITY = 29; let dynamicRegexCount = 0; @@ -80,15 +85,12 @@ function getRulesetDetails() { /******************************************************************************/ -async function pruneInvalidRegexRules(realm, rulesIn) { - const rejectedRegexRules = []; - +async function pruneInvalidRegexRules(realm, rulesIn, rejected = []) { const validateRegex = regex => { return dnr.isRegexSupported({ regex, isCaseSensitive: false }).then(result => { - const isSupported = result?.isSupported || false; - pruneInvalidRegexRules.validated.set(regex, isSupported); - if ( isSupported ) { return true; } - rejectedRegexRules.push(`\t${regex} ${result?.reason}`); + pruneInvalidRegexRules.validated.set(regex, result?.reason || true); + if ( result.isSupported ) { return true; } + rejected.push({ regex, reason: result?.reason }); return false; }); }; @@ -101,8 +103,11 @@ async function pruneInvalidRegexRules(realm, rulesIn) { continue; } const { regexFilter } = rule.condition; - if ( pruneInvalidRegexRules.validated.has(regexFilter) ) { - toCheck.push(pruneInvalidRegexRules.validated.get(regexFilter)); + const reason = pruneInvalidRegexRules.validated.get(regexFilter); + if ( reason !== undefined ) { + toCheck.push(reason === true); + if ( reason === true ) { continue; } + rejected.push({ regex: regexFilter, reason }); continue; } toCheck.push(validateRegex(regexFilter)); @@ -111,9 +116,9 @@ async function pruneInvalidRegexRules(realm, rulesIn) { // Collate results const isValid = await Promise.all(toCheck); - if ( rejectedRegexRules.length !== 0 ) { + if ( rejected.length !== 0 ) { ubolLog(`${realm} realm: rejected regexes:\n`, - rejectedRegexRules.join('\n') + rejected.map(e => `${e.regex} → ${e.reason}`).join('\n') ); } @@ -126,6 +131,7 @@ pruneInvalidRegexRules.validated = new Map(); async function updateRegexRules(currentRules, addRules, removeRuleIds) { // Remove existing regex-related block rules for ( const rule of currentRules ) { + if ( rule.id >= SPECIAL_RULES_REALM ) { continue; } const { type } = rule.action; if ( type !== 'block' && type !== 'allow' ) { continue; } if ( rule.condition.regexFilter === undefined ) { continue; } @@ -164,6 +170,7 @@ async function updateRegexRules(currentRules, addRules, removeRuleIds) { async function updateRemoveparamRules(currentRules, addRules, removeRuleIds) { // Remove existing removeparam-related rules for ( const rule of currentRules ) { + if ( rule.id >= SPECIAL_RULES_REALM ) { continue; } if ( rule.action.type !== 'redirect' ) { continue; } if ( rule.action.redirect.transform === undefined ) { continue; } removeRuleIds.push(rule.id); @@ -179,7 +186,6 @@ async function updateRemoveparamRules(currentRules, addRules, removeRuleIds) { } const removeparamRulesets = await Promise.all(toFetch); - // Removeparam rules can only be enforced with omnipotence const allRules = []; for ( const rules of removeparamRulesets ) { if ( Array.isArray(rules) === false ) { continue; } @@ -201,6 +207,7 @@ async function updateRemoveparamRules(currentRules, addRules, removeRuleIds) { async function updateRedirectRules(currentRules, addRules, removeRuleIds) { // Remove existing redirect-related rules for ( const rule of currentRules ) { + if ( rule.id >= SPECIAL_RULES_REALM ) { continue; } if ( rule.action.type !== 'redirect' ) { continue; } if ( rule.action.redirect.extensionPath === undefined ) { continue; } removeRuleIds.push(rule.id); @@ -216,7 +223,6 @@ async function updateRedirectRules(currentRules, addRules, removeRuleIds) { } const redirectRulesets = await Promise.all(toFetch); - // Redirect rules can only be enforced with omnipotence const allRules = []; for ( const rules of redirectRulesets ) { if ( Array.isArray(rules) === false ) { continue; } @@ -238,6 +244,7 @@ async function updateRedirectRules(currentRules, addRules, removeRuleIds) { async function updateModifyHeadersRules(currentRules, addRules, removeRuleIds) { // Remove existing header modification-related rules for ( const rule of currentRules ) { + if ( rule.id >= SPECIAL_RULES_REALM ) { continue; } if ( rule.action.type !== 'modifyHeaders' ) { continue; } removeRuleIds.push(rule.id); } @@ -252,7 +259,6 @@ async function updateModifyHeadersRules(currentRules, addRules, removeRuleIds) { } const rulesets = await Promise.all(toFetch); - // Redirect rules can only be enforced with omnipotence const allRules = []; for ( const rules of rulesets ) { if ( Array.isArray(rules) === false ) { continue; } @@ -278,6 +284,7 @@ async function updateDynamicRules() { // Remove potentially left-over strict-block rules from previous version for ( const rule of currentRules ) { + if ( rule.id >= SPECIAL_RULES_REALM ) { continue; } if ( isStrictBlockRule(rule) === false ) { continue; } removeRuleIds.push(rule.id); } @@ -294,7 +301,6 @@ async function updateDynamicRules() { let ruleId = 1; for ( const rule of addRules ) { if ( rule?.condition.regexFilter ) { dynamicRegexCount += 1; } - if ( (rule.id || 0) >= TRUSTED_DIRECTIVE_BASE_RULE_ID ) { continue; } rule.id = ruleId++; } if ( dynamicRegexCount !== 0 ) { @@ -467,7 +473,8 @@ async function filteringModesToDNR(modes) { TRUSTED_DIRECTIVE_BASE_RULE_ID, requestDomains.sort(), excludedRequestDomains.sort(), - allowEverywhere + allowEverywhere, + TRUSTED_DIRECTIVE_PRIORITY ).then(modified => { if ( modified === false ) { return; } ubolLog(`${allowEverywhere ? 'Enabled' : 'Disabled'} DNR filtering for ${noneCount} sites`); @@ -662,6 +669,84 @@ async function getEnabledRulesetsDetails() { /******************************************************************************/ +async function getUserRules() { + const allRules = await dnr.getDynamicRules(); + const userRules = []; + for ( const rule of allRules ) { + if ( rule.id < USER_RULES_BASE_RULE_ID ) { continue; } + userRules.push(rule); + } + return userRules; +} + +async function updateUserRules() { + const [ + userRules, + userRulesText = '', + ] = await Promise.all([ + getUserRules(), + localRead('userDnrRules'), + ]); + + const effectiveRulesText = rulesetConfig.developerMode + ? userRulesText + : ''; + + const parsed = rulesFromText(effectiveRulesText); + const { rules } = parsed; + const removeRuleIds = [ ...userRules.map(a => a.id) ]; + const rejectedRegexes = []; + const addRules = await pruneInvalidRegexRules('user', rules, rejectedRegexes); + const out = { added: 0, removed: 0, errors: [] }; + + if ( rejectedRegexes.length !== 0 ) { + rejectedRegexes.forEach(e => + out.errors.push(`regexFilter: ${e.regex} → ${e.reason}`) + ); + } + + if ( removeRuleIds.length === 0 && addRules.length === 0 ) { + await localRemove('userDnrRuleCount'); + return out; + } + + let ruleId = 0; + for ( const rule of addRules ) { + rule.id = USER_RULES_BASE_RULE_ID + ruleId++; + rule.priority = (rule.priority || 1) + USER_RULES_PRIORITY; + } + + // Rules are first removed separately to ensure registered rules match + // user rules text. A bad rule in user rules text would prevent the + // rules from being removed if the removal was done at the same time as + // adding rules. + try { + await dnr.updateDynamicRules({ removeRuleIds }); + await dnr.updateDynamicRules({ addRules }); + if ( removeRuleIds.length !== 0 ) { + ubolLog(`updateUserRules() / Removed ${removeRuleIds.length} dynamic DNR rules`); + } + if ( addRules.length !== 0 ) { + ubolLog(`updateUserRules() / Added ${addRules.length} DNR rules`); + } + out.added = addRules.length; + out.removed = removeRuleIds.length; + } catch(reason) { + console.info(`updateUserRules() / ${reason}`); + out.errors.push(`${reason}`); + } finally { + const userRules = await getUserRules(); + if ( userRules.length === 0 ) { + await localRemove('userDnrRuleCount'); + } else { + await localWrite('userDnrRuleCount', addRules.length); + } + } + return out; +} + +/******************************************************************************/ + export { enableRulesets, excludeFromStrictBlock, @@ -672,4 +757,5 @@ export { setStrictBlockMode, updateDynamicRules, updateSessionRules, + updateUserRules, }; diff --git a/platform/mv3/extension/js/settings.js b/platform/mv3/extension/js/settings.js index 19303e4068f5f..ed1e9a99e972f 100644 --- a/platform/mv3/extension/js/settings.js +++ b/platform/mv3/extension/js/settings.js @@ -29,17 +29,16 @@ import { renderFilterLists } from './filter-lists.js'; /******************************************************************************/ const cm6 = self.cm6; -const cmView = (( ) => { + +const cmTrustedSites = (( ) => { const options = {}; if ( dom.cl.has(':root', 'dark') ) { options.oneDark = true; } options.placeholder = i18n$('noFilteringModePlaceholder'); - return cm6.createEditorView( - cm6.createEditorState('', options), - qs$('#trustedSites') - ); + return cm6.createEditorView(options, qs$('#trustedSites')); })(); + let cachedRulesetData = {}; /******************************************************************************/ @@ -83,12 +82,10 @@ function renderWidgets() { } { - dom.prop('#developerMode input[type="checkbox"]', 'checked', - Boolean(cachedRulesetData.developerMode) - ); - if ( cachedRulesetData.isSideloaded ) { - dom.attr('#developerMode', 'hidden', null); - } + const state = Boolean(cachedRulesetData.developerMode) && + cachedRulesetData.disabledFeatures?.includes('develop') !== true; + dom.body.dataset.develop = `${state}`; + dom.prop('#developerMode input[type="checkbox"]', 'checked', state); } } @@ -171,10 +168,9 @@ dom.on('#strictBlockMode input[type="checkbox"]', 'change', ev => { }); dom.on('#developerMode input[type="checkbox"]', 'change', ev => { - sendMessage({ - what: 'setDeveloperMode', - state: ev.target.checked, - }); + const state = ev.target.checked; + sendMessage({ what: 'setDeveloperMode', state }); + dom.body.dataset.develop = `${state}`; }); /******************************************************************************/ @@ -183,9 +179,9 @@ function renderTrustedSites() { const hostnames = cachedRulesetData.trustedSites || []; let text = hostnames.map(hn => punycode.toUnicode(hn)).join('\n'); if ( text !== '' ) { text += '\n'; } - cmView.dispatch({ + cmTrustedSites.dispatch({ changes: { - from: 0, to: cmView.state.doc.length, + from: 0, to: cmTrustedSites.state.doc.length, insert: text }, }); @@ -202,7 +198,7 @@ function changeTrustedSites() { } function getStagedTrustedSites() { - const text = cmView.state.doc.toString(); + const text = cmTrustedSites.state.doc.toString(); return text.split(/\s/).map(hn => { try { return punycode.toASCII( @@ -214,7 +210,7 @@ function getStagedTrustedSites() { }).filter(hn => hn !== ''); } -dom.on(cmView.contentDOM, 'blur', changeTrustedSites); +dom.on(cmTrustedSites.contentDOM, 'blur', changeTrustedSites); self.addEventListener('beforeunload', changeTrustedSites); diff --git a/platform/mv3/extension/lib/codemirror/codemirror-ubol b/platform/mv3/extension/lib/codemirror/codemirror-ubol index 7067d0c2a9745..1d352d4489fea 160000 --- a/platform/mv3/extension/lib/codemirror/codemirror-ubol +++ b/platform/mv3/extension/lib/codemirror/codemirror-ubol @@ -1 +1 @@ -Subproject commit 7067d0c2a9745bfddceedb46e1428a0b545954bd +Subproject commit 1d352d4489fea6f4aa10a32417eba4abd67db5c9 diff --git a/platform/mv3/safari/ext-compat.js b/platform/mv3/safari/ext-compat.js index 5e89492f7e82b..50c8dd0e49146 100644 --- a/platform/mv3/safari/ext-compat.js +++ b/platform/mv3/safari/ext-compat.js @@ -122,7 +122,7 @@ export const dnr = { if ( optionsAfter === undefined ) { return; } return nativeDNR.updateSessionRules(optionsAfter); }, - async setAllowAllRules(id, allowed, notAllowed, reverse) { + async setAllowAllRules(id, allowed, notAllowed, reverse, priority) { const beforeRules = await this.getDynamicRules({ ruleIds: [ id+0 ] }); const addRules = []; if ( reverse || allowed.length || notAllowed.length ) { @@ -130,7 +130,7 @@ export const dnr = { id: id+0, action: { type: 'allow' }, condition: { urlFilter: '*' }, - priority: 1000000, + priority, }; if ( allowed.length ) { rule0.condition.domains = allowed; diff --git a/src/css/common.css b/src/css/common.css index fae4bb5d6205e..42b196dfd25a6 100644 --- a/src/css/common.css +++ b/src/css/common.css @@ -37,6 +37,7 @@ --default-gap-small: 12px; --default-gap-xsmall: 8px; --default-gap-xxsmall: 4px; + --button-font-size: max(calc(var(--font-size) * 0.875), 14px); } /* Common uBO styles */ @@ -85,7 +86,7 @@ button { color: var(--button-ink); display: inline-flex; fill: var(--button-ink); - font-size: max(calc(var(--font-size) * 0.875), 14px); + font-size: var(--button-font-size); justify-content: center; min-height: 36px; padding: 0 var(--font-size); diff --git a/tools/jsonpath-tool.html b/tools/jsonpath-tool.html index 17a5877dd0566..2da7f78974f14 100644 --- a/tools/jsonpath-tool.html +++ b/tools/jsonpath-tool.html @@ -1,4 +1,13 @@ + @@ -43,8 +52,39 @@

uBO-flavored JSONPath tool

- - + +
 
@@ -57,6 +97,18 @@

uBO-flavored JSONPath tool

import { JSONPath } from '../src/js/jsonpath.js'; + function readJSON() { + const textarea = document.querySelector('#json-data'); + try { + jsonData = JSON.parse(textarea.value); + } catch { + jsonData = {}; + } + if ( typeof jsonData !== 'object' || jsonData === null ) { + jsonData = {}; + } + } + function formatResult(a) { if ( a === undefined ) { return 'undefined'; } if ( jsonp.valid === false ) { return 'bad expression'; } @@ -85,13 +137,7 @@

uBO-flavored JSONPath tool

{ const textarea = document.querySelector('#json-data'); textarea.addEventListener('input', ( ) => { - try { - jsonData = JSON.parse(textarea.value); - } catch { - } - if ( typeof jsonData !== 'object' || jsonData === null ) { - jsonData = {}; - } + readJSON(); process(); }); } @@ -102,6 +148,9 @@

uBO-flavored JSONPath tool

process(); }); } + + readJSON(); + process(); diff --git a/tools/make-mv3.sh b/tools/make-mv3.sh index 8791487b83dcc..cb2f19c56e562 100755 --- a/tools/make-mv3.sh +++ b/tools/make-mv3.sh @@ -107,6 +107,8 @@ cp platform/mv3/extension/lib/codemirror/* \ "$UBOL_DIR"/lib/codemirror/ 2>/dev/null || : cp platform/mv3/extension/lib/codemirror/codemirror-ubol/dist/cm6.bundle.ubol.min.js \ "$UBOL_DIR"/lib/codemirror/ +cp platform/mv3/extension/lib/codemirror/codemirror-ubol/LICENSE \ + "$UBOL_DIR"/lib/codemirror/ echo "*** uBOLite.mv3: Generating rulesets" UBOL_BUILD_DIR=$(mktemp -d) From cac62c6dc07a99515ed1c908baee2dc7bf9a787e Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Thu, 29 May 2025 10:05:13 -0400 Subject: [PATCH 0966/1099] Import translation work from https://crowdin.com/project/ublock --- platform/mv3/description/webstore.fi.txt | 2 +- platform/mv3/description/webstore.lv.txt | 6 ++--- platform/mv3/description/webstore.mr.txt | 12 +++++----- platform/mv3/description/webstore.nb.txt | 2 +- platform/mv3/description/webstore.nl.txt | 2 +- .../mv3/extension/_locales/bg/messages.json | 14 +++++------ .../mv3/extension/_locales/bn/messages.json | 10 ++++---- .../extension/_locales/br_FR/messages.json | 20 ++++++++-------- .../mv3/extension/_locales/ca/messages.json | 14 +++++------ .../mv3/extension/_locales/da/messages.json | 16 ++++++------- .../mv3/extension/_locales/de/messages.json | 14 +++++------ .../mv3/extension/_locales/el/messages.json | 14 +++++------ .../mv3/extension/_locales/es/messages.json | 14 +++++------ .../mv3/extension/_locales/et/messages.json | 14 +++++------ .../mv3/extension/_locales/fi/messages.json | 24 +++++++++---------- .../mv3/extension/_locales/fr/messages.json | 14 +++++------ .../mv3/extension/_locales/fy/messages.json | 14 +++++------ .../mv3/extension/_locales/gl/messages.json | 14 +++++------ .../mv3/extension/_locales/he/messages.json | 14 +++++------ .../mv3/extension/_locales/hr/messages.json | 16 ++++++------- .../mv3/extension/_locales/it/messages.json | 14 +++++------ .../mv3/extension/_locales/lv/messages.json | 14 +++++------ .../mv3/extension/_locales/nb/messages.json | 22 ++++++++--------- .../mv3/extension/_locales/nl/messages.json | 20 ++++++++-------- .../mv3/extension/_locales/pl/messages.json | 14 +++++------ .../extension/_locales/pt_BR/messages.json | 14 +++++------ .../mv3/extension/_locales/ru/messages.json | 14 +++++------ .../mv3/extension/_locales/sk/messages.json | 14 +++++------ .../mv3/extension/_locales/sv/messages.json | 14 +++++------ .../mv3/extension/_locales/tr/messages.json | 14 +++++------ .../mv3/extension/_locales/uk/messages.json | 14 +++++------ .../extension/_locales/zh_TW/messages.json | 14 +++++------ src/_locales/br_FR/messages.json | 8 +++---- src/_locales/de/messages.json | 4 ++-- src/_locales/fi/messages.json | 6 ++--- src/_locales/lv/messages.json | 20 ++++++++-------- src/_locales/nl/messages.json | 2 +- 37 files changed, 236 insertions(+), 236 deletions(-) diff --git a/platform/mv3/description/webstore.fi.txt b/platform/mv3/description/webstore.fi.txt index e1b1b2061a139..a5784b1df14c8 100644 --- a/platform/mv3/description/webstore.fi.txt +++ b/platform/mv3/description/webstore.fi.txt @@ -1,4 +1,4 @@ -uBO Lite (uBOL) is an MV3-based content blocker. +uBO Lite (uBOL) on MV3-pohjainen sisällönestotyökalu. Oletusarvoiset sääntömääritykset vastaavat uBlock Origin -laajennuksen oletuksia: diff --git a/platform/mv3/description/webstore.lv.txt b/platform/mv3/description/webstore.lv.txt index aa4932f7be810..129af3d1bbb85 100644 --- a/platform/mv3/description/webstore.lv.txt +++ b/platform/mv3/description/webstore.lv.txt @@ -1,11 +1,11 @@ -uBO Lite (uBOL) ir uz MV3 balstīts satura aizturētājs. +uBO Lite (uBOL) — uz MV3 balstīts satura aizturētājs. -Noklusējuma nosacījumu kopa atbilst uBokc Origin noklusējuma aizturēšanas kopai: +Noklusējuma nosacījumu kopa atbilst uBock Origin noklusējuma aizturēšanas kopai: - uBlock Origin iebūvētie aizturēšanas saraksti - EasyList - EasyPrivacy -- Pētera Lova (Peter Lowe) reklāmu un izsakošanas serveru saraksts +- Pētera Lova (Peter Lowe) reklāmu un izsekošanas serveru saraksts Vairāk nosacījumu kopu var iespējot iestatījumu sadaļā -- jāklikšķina _Zobratu_ ikona uznirstošajā logā. diff --git a/platform/mv3/description/webstore.mr.txt b/platform/mv3/description/webstore.mr.txt index ef089202b9565..1747ea06e6335 100644 --- a/platform/mv3/description/webstore.mr.txt +++ b/platform/mv3/description/webstore.mr.txt @@ -1,12 +1,12 @@ -uBO Lite (uBOL) is an MV3-based content blocker. +uBO Lite (uBOL) हे MV3-आधारित कंटेंट ब्लॉकर आहे. -The default ruleset corresponds to uBlock Origin's default filterset: +डीफॉल्ट नियमसंच uBlock Origin च्या डीफॉल्ट फिल्टरसेटशी संबंधित आहे: -- uBlock Origin's built-in filter lists +- uBlock Origin च्या बिल्ट-इन फिल्टर लिस्ट - EasyList - EasyPrivacy -- Peter Lowe’s Ad and tracking server list +- पीटर लोवची जाहिरात आणि ट्रॅकिंग सर्व्हर यादी -You can enable more rulesets by visiting the options page -- click the _Cogs_ icon in the popup panel. +तुम्ही पर्याय पृष्ठाला भेट देऊन अधिक नियम संच सक्षम करू शकता -- पॉपअप पॅनेलमधील _Cogs_ चिन्हावर क्लिक करा. -uBOL is entirely declarative, meaning there is no need for a permanent uBOL process for the filtering to occur, and CSS/JS injection-based content filtering is performed reliably by the browser itself rather than by the extension. This means that uBOL itself does not consume CPU/memory resources while content blocking is ongoing -- uBOL's service worker process is required _only_ when you interact with the popup panel or the option pages. +uBOL पूर्णपणे घोषणात्मक आहे, म्हणजे फिल्टरिंगसाठी कायमस्वरूपी uBOL प्रक्रियेची गरज नाही, आणि CSS/JS इंजेक्शनवर आधारित सामग्री फिल्टरिंग ब्राउझरच्याच मदतीने विश्वासार्हपणे होते, विस्ताराद्वारे नव्हे. याचा अर्थ असा की सामग्री ब्लॉकिंग चालू असताना uBOL स्वतः CPU/मेमरी संसाधने वापरत नाही — uBOL चा सर्व्हिस वर्कर प्रोसेस फक्त तेव्हाच लागतो जेव्हा तुम्ही पॉपअप पॅनल किंवा पर्याय पृष्ठांशी संवाद साधता. diff --git a/platform/mv3/description/webstore.nb.txt b/platform/mv3/description/webstore.nb.txt index ffd3b82369d89..eab6ff5fe700e 100644 --- a/platform/mv3/description/webstore.nb.txt +++ b/platform/mv3/description/webstore.nb.txt @@ -1,4 +1,4 @@ -uBO Lite (uBOL) is an MV3-based content blocker. +uBO Lite (uBOL) er en MV3-basert innholdsblokkerer. Standardregelsettet tilsvarer standardfiltersettet til uBlock Origin: diff --git a/platform/mv3/description/webstore.nl.txt b/platform/mv3/description/webstore.nl.txt index 5268218e33ea9..fbf0c5c6a3284 100644 --- a/platform/mv3/description/webstore.nl.txt +++ b/platform/mv3/description/webstore.nl.txt @@ -1,4 +1,4 @@ -uBO Lite (uBOL) is een MV3-gebaseerde inhoudsblokkeerder. +uBO Lite (uBOL) is een op MV3 gebaseerde inhoudsblokkeerder. De standaard regelset komt overeen met de standaard filterset van uBlock Origin: diff --git a/platform/mv3/extension/_locales/bg/messages.json b/platform/mv3/extension/_locales/bg/messages.json index 753cd2fe9fe31..451ca9fd2972e 100644 --- a/platform/mv3/extension/_locales/bg/messages.json +++ b/platform/mv3/extension/_locales/bg/messages.json @@ -236,11 +236,11 @@ "description": "Short description for a checkbox in the options page" }, "developerModeLabel": { - "message": "Developer mode", + "message": "Режим за програмисти", "description": "Label for a checkbox in the options page" }, "developerModeLegend": { - "message": "Enable access to features suitable for technical users.", + "message": "Активиране на достъпа до функции, подходящи за технически потребители.", "description": "Short description for a checkbox in the options page" }, "findListsPlaceholder": { @@ -292,23 +292,23 @@ "description": "Tooltip for the button used to exit zapper mode" }, "saveButton": { - "message": "Save", + "message": "Запазване", "description": "Text for buttons used to save changes" }, "revertButton": { - "message": "Revert", + "message": "Връщане", "description": "Text for buttons used to revert changes" }, "importAndAppendButton": { - "message": "Import and append…", + "message": "Импортиране и добавяне…", "description": "Text for buttons used to import and append content" }, "exportButton": { - "message": "Export…", + "message": "Експортиране…", "description": "Text for buttons used to export content" }, "dnrRulesSummary": { - "message": "Enter your own DNR rules below. Do not add content from untrusted sources.", + "message": "Въведете вашите собствени правила за DNR по-долу. Не добавяйте съдържание от ненадеждни източници.", "description": "Short description of the DNR rules editor pane" } } diff --git a/platform/mv3/extension/_locales/bn/messages.json b/platform/mv3/extension/_locales/bn/messages.json index bc87ca5830b7d..8e89bdde80f6a 100644 --- a/platform/mv3/extension/_locales/bn/messages.json +++ b/platform/mv3/extension/_locales/bn/messages.json @@ -112,7 +112,7 @@ "description": "First paragraph of 'Filter issues' section in Support pane" }, "supportS5H": { - "message": "Troubleshooting information", + "message": "সমস্যা সমাধানের তথ্য", "description": "Label of 'Troubleshooting information' section in 'Report a filter issue' page" }, "supportS6P1S1": { @@ -236,11 +236,11 @@ "description": "Short description for a checkbox in the options page" }, "developerModeLabel": { - "message": "Developer mode", + "message": "ডেভেলপার মোড", "description": "Label for a checkbox in the options page" }, "developerModeLegend": { - "message": "Enable access to features suitable for technical users.", + "message": "প্রযুক্তিগত ব্যবহারকারীদের জন্য উপযুক্ত বৈশিষ্ট্যগুলিতে অ্যাক্সেস সক্ষম করুন", "description": "Short description for a checkbox in the options page" }, "findListsPlaceholder": { @@ -284,7 +284,7 @@ "description": "A button to navigate to the blocked page" }, "zapperTipEnter": { - "message": "Enter element zapper mode", + "message": "এলিমেন্ট জ্যাপার মোডে প্রবেশ করুন", "description": "Tooltip for the button used to enter zapper mode" }, "zapperTipQuit": { @@ -292,7 +292,7 @@ "description": "Tooltip for the button used to exit zapper mode" }, "saveButton": { - "message": "Save", + "message": "সেভ করুন", "description": "Text for buttons used to save changes" }, "revertButton": { diff --git a/platform/mv3/extension/_locales/br_FR/messages.json b/platform/mv3/extension/_locales/br_FR/messages.json index 72e2d5e8f93d2..3a152d7e2fec6 100644 --- a/platform/mv3/extension/_locales/br_FR/messages.json +++ b/platform/mv3/extension/_locales/br_FR/messages.json @@ -24,7 +24,7 @@ "description": "appears as tab name in dashboard" }, "aboutPrivacyPolicy": { - "message": "Politikerezh prevezded", + "message": "Politikerezh ar vuhez prevez", "description": "Link to privacy policy on GitHub (English)" }, "popupFilteringModeLabel": { @@ -56,7 +56,7 @@ "description": "Header for a ruleset section in 'Filter lists pane'" }, "3pGroupPrivacy": { - "message": "Prevezded", + "message": "Buhez prevez", "description": "Header for a ruleset section in 'Filter lists pane'" }, "3pGroupMalware": { @@ -148,7 +148,7 @@ "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option4": { - "message": "Kudennoù a-fed prevezded he deus", + "message": "Kudennoù a-fed ar vuhez prevez he deus", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option5": { @@ -236,11 +236,11 @@ "description": "Short description for a checkbox in the options page" }, "developerModeLabel": { - "message": "Developer mode", + "message": "Mod diorroer", "description": "Label for a checkbox in the options page" }, "developerModeLegend": { - "message": "Enable access to features suitable for technical users.", + "message": "Gweredekaat ar fonksionelezhioù azasaet d'an implijerien deknikel.", "description": "Short description for a checkbox in the options page" }, "findListsPlaceholder": { @@ -292,23 +292,23 @@ "description": "Tooltip for the button used to exit zapper mode" }, "saveButton": { - "message": "Save", + "message": "Enrollañ", "description": "Text for buttons used to save changes" }, "revertButton": { - "message": "Revert", + "message": "Nullañ", "description": "Text for buttons used to revert changes" }, "importAndAppendButton": { - "message": "Import and append…", + "message": "Enporzhiañ hag ouzhpennañ", "description": "Text for buttons used to import and append content" }, "exportButton": { - "message": "Export…", + "message": "Ezporzhiañ", "description": "Text for buttons used to export content" }, "dnrRulesSummary": { - "message": "Enter your own DNR rules below. Do not add content from untrusted sources.", + "message": "Lakait ho reolennoù DNR deoc'h-c'hwi amañ-dindan. Arabat ouzhpennañ danvez a zeu diouzh mammennoù douetus.", "description": "Short description of the DNR rules editor pane" } } diff --git a/platform/mv3/extension/_locales/ca/messages.json b/platform/mv3/extension/_locales/ca/messages.json index 250e1308830f7..9726dcc62ed6a 100644 --- a/platform/mv3/extension/_locales/ca/messages.json +++ b/platform/mv3/extension/_locales/ca/messages.json @@ -236,11 +236,11 @@ "description": "Short description for a checkbox in the options page" }, "developerModeLabel": { - "message": "Developer mode", + "message": "Mode de desenvolupador", "description": "Label for a checkbox in the options page" }, "developerModeLegend": { - "message": "Enable access to features suitable for technical users.", + "message": "Habilita l'accés a funcions adequades per a usuaris tècnics.", "description": "Short description for a checkbox in the options page" }, "findListsPlaceholder": { @@ -292,23 +292,23 @@ "description": "Tooltip for the button used to exit zapper mode" }, "saveButton": { - "message": "Save", + "message": "Desa", "description": "Text for buttons used to save changes" }, "revertButton": { - "message": "Revert", + "message": "Reverteix", "description": "Text for buttons used to revert changes" }, "importAndAppendButton": { - "message": "Import and append…", + "message": "Importa i annexa...", "description": "Text for buttons used to import and append content" }, "exportButton": { - "message": "Export…", + "message": "Exporta...", "description": "Text for buttons used to export content" }, "dnrRulesSummary": { - "message": "Enter your own DNR rules below. Do not add content from untrusted sources.", + "message": "Introduïu les vostres pròpies regles de DNR a continuació. No afegiu contingut de fonts no fiables.", "description": "Short description of the DNR rules editor pane" } } diff --git a/platform/mv3/extension/_locales/da/messages.json b/platform/mv3/extension/_locales/da/messages.json index 8e0dc1661b0c2..556f09f2311dd 100644 --- a/platform/mv3/extension/_locales/da/messages.json +++ b/platform/mv3/extension/_locales/da/messages.json @@ -4,7 +4,7 @@ "description": "extension name." }, "extShortDesc": { - "message": "En effektiv indholdsblokering. Blokerer annoncer, trackere, minere og mere lige efter installation.", + "message": "En effektiv indholdsblocker. Blokerer annoncer, trackere, minere mm. umiddelbart efter installation.", "description": "this will be in the Chrome web store: must be 132 characters or less" }, "perRulesetStats": { @@ -236,11 +236,11 @@ "description": "Short description for a checkbox in the options page" }, "developerModeLabel": { - "message": "Developer mode", + "message": "Udviklertilstand", "description": "Label for a checkbox in the options page" }, "developerModeLegend": { - "message": "Enable access to features suitable for technical users.", + "message": "Aktivér adgang til funktioner egnede for tekniske brugere.", "description": "Short description for a checkbox in the options page" }, "findListsPlaceholder": { @@ -292,23 +292,23 @@ "description": "Tooltip for the button used to exit zapper mode" }, "saveButton": { - "message": "Save", + "message": "Gem", "description": "Text for buttons used to save changes" }, "revertButton": { - "message": "Revert", + "message": "Tilbagefør", "description": "Text for buttons used to revert changes" }, "importAndAppendButton": { - "message": "Import and append…", + "message": "Importér og tilføj…", "description": "Text for buttons used to import and append content" }, "exportButton": { - "message": "Export…", + "message": "Eksportér…", "description": "Text for buttons used to export content" }, "dnrRulesSummary": { - "message": "Enter your own DNR rules below. Do not add content from untrusted sources.", + "message": "Angiv egne DNR-regler nedenfor. Tilføj ikke indhold fra ikke-betroede kilder.", "description": "Short description of the DNR rules editor pane" } } diff --git a/platform/mv3/extension/_locales/de/messages.json b/platform/mv3/extension/_locales/de/messages.json index 22bb53da49665..3c1f8aaab341a 100644 --- a/platform/mv3/extension/_locales/de/messages.json +++ b/platform/mv3/extension/_locales/de/messages.json @@ -236,11 +236,11 @@ "description": "Short description for a checkbox in the options page" }, "developerModeLabel": { - "message": "Developer mode", + "message": "Entwicklermodus", "description": "Label for a checkbox in the options page" }, "developerModeLegend": { - "message": "Enable access to features suitable for technical users.", + "message": "Ermöglicht den Zugriff auf Funktionen, die für technisch Versierte bestimmt sind.", "description": "Short description for a checkbox in the options page" }, "findListsPlaceholder": { @@ -292,23 +292,23 @@ "description": "Tooltip for the button used to exit zapper mode" }, "saveButton": { - "message": "Save", + "message": "Speichern", "description": "Text for buttons used to save changes" }, "revertButton": { - "message": "Revert", + "message": "Rückgängig machen", "description": "Text for buttons used to revert changes" }, "importAndAppendButton": { - "message": "Import and append…", + "message": "Importieren und ergänzen …", "description": "Text for buttons used to import and append content" }, "exportButton": { - "message": "Export…", + "message": "Exportieren …", "description": "Text for buttons used to export content" }, "dnrRulesSummary": { - "message": "Enter your own DNR rules below. Do not add content from untrusted sources.", + "message": "Eigene DNR-Regeln können unten eingetragen werden. Verwenden Sie keine Regeln aus unseriösen Quellen.", "description": "Short description of the DNR rules editor pane" } } diff --git a/platform/mv3/extension/_locales/el/messages.json b/platform/mv3/extension/_locales/el/messages.json index d9f343099f908..b9838cd38b037 100644 --- a/platform/mv3/extension/_locales/el/messages.json +++ b/platform/mv3/extension/_locales/el/messages.json @@ -236,11 +236,11 @@ "description": "Short description for a checkbox in the options page" }, "developerModeLabel": { - "message": "Developer mode", + "message": "Λειτουργία προγραμματιστή", "description": "Label for a checkbox in the options page" }, "developerModeLegend": { - "message": "Enable access to features suitable for technical users.", + "message": "Ενεργοποίηση πρόσβασης σε δυνατότητες κατάλληλες για τεχνικούς χρήστες.", "description": "Short description for a checkbox in the options page" }, "findListsPlaceholder": { @@ -292,23 +292,23 @@ "description": "Tooltip for the button used to exit zapper mode" }, "saveButton": { - "message": "Save", + "message": "Αποθήκευση", "description": "Text for buttons used to save changes" }, "revertButton": { - "message": "Revert", + "message": "Επαναφορά", "description": "Text for buttons used to revert changes" }, "importAndAppendButton": { - "message": "Import and append…", + "message": "Εισαγωγή και προσθήκη…", "description": "Text for buttons used to import and append content" }, "exportButton": { - "message": "Export…", + "message": "Εξαγωγή…", "description": "Text for buttons used to export content" }, "dnrRulesSummary": { - "message": "Enter your own DNR rules below. Do not add content from untrusted sources.", + "message": "Εισάγετε παρακάτω τους δικούς σας κανόνες DNR. Μην προσθέσετε περιεχόμενο από πηγές που δεν είναι αξιόπιστες.", "description": "Short description of the DNR rules editor pane" } } diff --git a/platform/mv3/extension/_locales/es/messages.json b/platform/mv3/extension/_locales/es/messages.json index 13f08971f7b7d..b990345af142f 100644 --- a/platform/mv3/extension/_locales/es/messages.json +++ b/platform/mv3/extension/_locales/es/messages.json @@ -236,11 +236,11 @@ "description": "Short description for a checkbox in the options page" }, "developerModeLabel": { - "message": "Developer mode", + "message": "Modo desarrollador", "description": "Label for a checkbox in the options page" }, "developerModeLegend": { - "message": "Enable access to features suitable for technical users.", + "message": "Habilitar acceso a características adecuadas para usuarios técnicos.", "description": "Short description for a checkbox in the options page" }, "findListsPlaceholder": { @@ -292,23 +292,23 @@ "description": "Tooltip for the button used to exit zapper mode" }, "saveButton": { - "message": "Save", + "message": "Guardar", "description": "Text for buttons used to save changes" }, "revertButton": { - "message": "Revert", + "message": "Revertir", "description": "Text for buttons used to revert changes" }, "importAndAppendButton": { - "message": "Import and append…", + "message": "Importar y anexar…", "description": "Text for buttons used to import and append content" }, "exportButton": { - "message": "Export…", + "message": "Exportar…", "description": "Text for buttons used to export content" }, "dnrRulesSummary": { - "message": "Enter your own DNR rules below. Do not add content from untrusted sources.", + "message": "Ingresa tus propias reglas DNR abajo. No agregues contenido desde fuentes no confiables.", "description": "Short description of the DNR rules editor pane" } } diff --git a/platform/mv3/extension/_locales/et/messages.json b/platform/mv3/extension/_locales/et/messages.json index 3fe467a1de793..e0eba83e234aa 100644 --- a/platform/mv3/extension/_locales/et/messages.json +++ b/platform/mv3/extension/_locales/et/messages.json @@ -236,11 +236,11 @@ "description": "Short description for a checkbox in the options page" }, "developerModeLabel": { - "message": "Developer mode", + "message": "Arendaja režiim", "description": "Label for a checkbox in the options page" }, "developerModeLegend": { - "message": "Enable access to features suitable for technical users.", + "message": "Luba juurdepääs tehnilise taibuga kasutajatele mõeldud võimalustele.", "description": "Short description for a checkbox in the options page" }, "findListsPlaceholder": { @@ -292,23 +292,23 @@ "description": "Tooltip for the button used to exit zapper mode" }, "saveButton": { - "message": "Save", + "message": "Salvesta", "description": "Text for buttons used to save changes" }, "revertButton": { - "message": "Revert", + "message": "Taasta", "description": "Text for buttons used to revert changes" }, "importAndAppendButton": { - "message": "Import and append…", + "message": "Impordi ja lisa…", "description": "Text for buttons used to import and append content" }, "exportButton": { - "message": "Export…", + "message": "Ekspordi…", "description": "Text for buttons used to export content" }, "dnrRulesSummary": { - "message": "Enter your own DNR rules below. Do not add content from untrusted sources.", + "message": "Enda aadressiresolverite avastamise reegli sisestamiseks klõpsake siia. Ärge lisage kahtlaste allikate sisu.", "description": "Short description of the DNR rules editor pane" } } diff --git a/platform/mv3/extension/_locales/fi/messages.json b/platform/mv3/extension/_locales/fi/messages.json index e29c903ff4993..4e7836a408042 100644 --- a/platform/mv3/extension/_locales/fi/messages.json +++ b/platform/mv3/extension/_locales/fi/messages.json @@ -4,7 +4,7 @@ "description": "extension name." }, "extShortDesc": { - "message": "Käyttöoikeudeton estotyökalu, joka estää välittömästi asennuksesta lähtien mm. mainokset, seurannat ja kryptolouhijat.", + "message": "Tehokas estotyökalu, joka estää heti asennuksen jälkeen mm. mainokset, seurannat ja kryptolouhijat.", "description": "this will be in the Chrome web store: must be 132 characters or less" }, "perRulesetStats": { @@ -112,11 +112,11 @@ "description": "First paragraph of 'Filter issues' section in Support pane" }, "supportS5H": { - "message": "Troubleshooting information", + "message": "Vianselvitystiedot", "description": "Label of 'Troubleshooting information' section in 'Report a filter issue' page" }, "supportS6P1S1": { - "message": "Välttääksesi vapaaehtoisten kuormittamisen ylimääräisillä ilmoituksilla, tarkasta ensin onko ongelmasta jo ilmoitettu. Huomioi: painikkeen painalluksen seurauksena sivun osoite lähetetään GitHubiin.", + "message": "Välttääksesi vapaaehtoisten kuormittamisen ylimääräisillä ilmoituksilla, tarkasta ensin onko ongelmasta jo ilmoitettu. Huomioi: Painikkeen painallus lähettää sivun osoitteen GitHubiin.", "description": "A paragraph in the filter issue reporter section" }, "supportFindSpecificButton": { @@ -236,11 +236,11 @@ "description": "Short description for a checkbox in the options page" }, "developerModeLabel": { - "message": "Developer mode", + "message": "Kehittäjätila", "description": "Label for a checkbox in the options page" }, "developerModeLegend": { - "message": "Enable access to features suitable for technical users.", + "message": "Aktivoi teknisille käyttäjille suunnatut ominaisuudet.", "description": "Short description for a checkbox in the options page" }, "findListsPlaceholder": { @@ -284,31 +284,31 @@ "description": "A button to navigate to the blocked page" }, "zapperTipEnter": { - "message": "Enter element zapper mode", + "message": "Avaa elementtien piilotustila", "description": "Tooltip for the button used to enter zapper mode" }, "zapperTipQuit": { - "message": "Exit element zapper mode", + "message": "Poistu elementtien piilotustilasta", "description": "Tooltip for the button used to exit zapper mode" }, "saveButton": { - "message": "Save", + "message": "Tallenna", "description": "Text for buttons used to save changes" }, "revertButton": { - "message": "Revert", + "message": "Palauta", "description": "Text for buttons used to revert changes" }, "importAndAppendButton": { - "message": "Import and append…", + "message": "Tuo ja lisää…", "description": "Text for buttons used to import and append content" }, "exportButton": { - "message": "Export…", + "message": "Vie…", "description": "Text for buttons used to export content" }, "dnrRulesSummary": { - "message": "Enter your own DNR rules below. Do not add content from untrusted sources.", + "message": "Syötä omat DNS-sääntösi alle. Älä lisää sisältöä lähteistä, joihin et luota.", "description": "Short description of the DNR rules editor pane" } } diff --git a/platform/mv3/extension/_locales/fr/messages.json b/platform/mv3/extension/_locales/fr/messages.json index 7bc28e98aa5bf..fd73fe61043d4 100644 --- a/platform/mv3/extension/_locales/fr/messages.json +++ b/platform/mv3/extension/_locales/fr/messages.json @@ -236,11 +236,11 @@ "description": "Short description for a checkbox in the options page" }, "developerModeLabel": { - "message": "Developer mode", + "message": "Mode développeur", "description": "Label for a checkbox in the options page" }, "developerModeLegend": { - "message": "Enable access to features suitable for technical users.", + "message": "Activer l'accès aux fonctionnalités adaptées aux utilisateurs techniques.", "description": "Short description for a checkbox in the options page" }, "findListsPlaceholder": { @@ -292,23 +292,23 @@ "description": "Tooltip for the button used to exit zapper mode" }, "saveButton": { - "message": "Save", + "message": "Enregistrer", "description": "Text for buttons used to save changes" }, "revertButton": { - "message": "Revert", + "message": "Rétablir", "description": "Text for buttons used to revert changes" }, "importAndAppendButton": { - "message": "Import and append…", + "message": "Importer", "description": "Text for buttons used to import and append content" }, "exportButton": { - "message": "Export…", + "message": "Exporter", "description": "Text for buttons used to export content" }, "dnrRulesSummary": { - "message": "Enter your own DNR rules below. Do not add content from untrusted sources.", + "message": "Entrez vos propres règles DNR (Declarative Net Request, ou requête réseau déclarative) ci-dessous. N'ajoutez pas de contenu provenant de sources non-fiables.", "description": "Short description of the DNR rules editor pane" } } diff --git a/platform/mv3/extension/_locales/fy/messages.json b/platform/mv3/extension/_locales/fy/messages.json index b2d0f0b1b8910..5d8d96ef0504e 100644 --- a/platform/mv3/extension/_locales/fy/messages.json +++ b/platform/mv3/extension/_locales/fy/messages.json @@ -236,11 +236,11 @@ "description": "Short description for a checkbox in the options page" }, "developerModeLabel": { - "message": "Developer mode", + "message": "Untwikkelersmodus", "description": "Label for a checkbox in the options page" }, "developerModeLegend": { - "message": "Enable access to features suitable for technical users.", + "message": "Tagong ta foar technyske brûkers geskikte funksjes ynskeakelje.", "description": "Short description for a checkbox in the options page" }, "findListsPlaceholder": { @@ -292,23 +292,23 @@ "description": "Tooltip for the button used to exit zapper mode" }, "saveButton": { - "message": "Save", + "message": "Bewarje", "description": "Text for buttons used to save changes" }, "revertButton": { - "message": "Revert", + "message": "Ungedien meitsje", "description": "Text for buttons used to revert changes" }, "importAndAppendButton": { - "message": "Import and append…", + "message": "Ymportearje en tafoegje…", "description": "Text for buttons used to import and append content" }, "exportButton": { - "message": "Export…", + "message": "Eksportearje…", "description": "Text for buttons used to export content" }, "dnrRulesSummary": { - "message": "Enter your own DNR rules below. Do not add content from untrusted sources.", + "message": "Fier hjirûnder jo eigen DNR-regels yn. Foegje gjin ynhâld fan net-fertroude boarnen ta.", "description": "Short description of the DNR rules editor pane" } } diff --git a/platform/mv3/extension/_locales/gl/messages.json b/platform/mv3/extension/_locales/gl/messages.json index 4640caa738291..3f12d28e6c5f3 100644 --- a/platform/mv3/extension/_locales/gl/messages.json +++ b/platform/mv3/extension/_locales/gl/messages.json @@ -236,11 +236,11 @@ "description": "Short description for a checkbox in the options page" }, "developerModeLabel": { - "message": "Developer mode", + "message": "Modo desenvolvemento", "description": "Label for a checkbox in the options page" }, "developerModeLegend": { - "message": "Enable access to features suitable for technical users.", + "message": "Permitir acceso a funcións pensadas para persoas con experiencia técnica.", "description": "Short description for a checkbox in the options page" }, "findListsPlaceholder": { @@ -292,23 +292,23 @@ "description": "Tooltip for the button used to exit zapper mode" }, "saveButton": { - "message": "Save", + "message": "Gardar", "description": "Text for buttons used to save changes" }, "revertButton": { - "message": "Revert", + "message": "Reverter", "description": "Text for buttons used to revert changes" }, "importAndAppendButton": { - "message": "Import and append…", + "message": "Importar e engadir…", "description": "Text for buttons used to import and append content" }, "exportButton": { - "message": "Export…", + "message": "Exportar…", "description": "Text for buttons used to export content" }, "dnrRulesSummary": { - "message": "Enter your own DNR rules below. Do not add content from untrusted sources.", + "message": "Escribe as regras DNR embaixo. Non engadas contido desde orixes non confiables.", "description": "Short description of the DNR rules editor pane" } } diff --git a/platform/mv3/extension/_locales/he/messages.json b/platform/mv3/extension/_locales/he/messages.json index c2739016386c5..e2a0cc55b01cb 100644 --- a/platform/mv3/extension/_locales/he/messages.json +++ b/platform/mv3/extension/_locales/he/messages.json @@ -236,11 +236,11 @@ "description": "Short description for a checkbox in the options page" }, "developerModeLabel": { - "message": "Developer mode", + "message": "מצב מפתחים", "description": "Label for a checkbox in the options page" }, "developerModeLegend": { - "message": "Enable access to features suitable for technical users.", + "message": "מאפשר גישה ליכולות עבור משתמשים טכניים.", "description": "Short description for a checkbox in the options page" }, "findListsPlaceholder": { @@ -292,23 +292,23 @@ "description": "Tooltip for the button used to exit zapper mode" }, "saveButton": { - "message": "Save", + "message": "שמירה", "description": "Text for buttons used to save changes" }, "revertButton": { - "message": "Revert", + "message": "ביטול שינויים", "description": "Text for buttons used to revert changes" }, "importAndAppendButton": { - "message": "Import and append…", + "message": "ייבא וצרף…", "description": "Text for buttons used to import and append content" }, "exportButton": { - "message": "Export…", + "message": "ייצוא…", "description": "Text for buttons used to export content" }, "dnrRulesSummary": { - "message": "Enter your own DNR rules below. Do not add content from untrusted sources.", + "message": "הכניסו את חוקי ה DNR שלכם למטה. אל תכניסו תוכן ממקורות לא בטוחים.", "description": "Short description of the DNR rules editor pane" } } diff --git a/platform/mv3/extension/_locales/hr/messages.json b/platform/mv3/extension/_locales/hr/messages.json index ed9ea6c1da36d..f2cd12f9776a0 100644 --- a/platform/mv3/extension/_locales/hr/messages.json +++ b/platform/mv3/extension/_locales/hr/messages.json @@ -4,7 +4,7 @@ "description": "extension name." }, "extShortDesc": { - "message": "Bloker sadržaja bez dopuštenja. Blokira oglase, oglasne pratitelje, kripto \"rudare\" i ostalo odmah nakon instalacije.", + "message": "Učinkovit blokator sadržaja. Blokira oglase, oglasne pratitelje, kripto \"rudare\" i ostalo odmah nakon instalacije.", "description": "this will be in the Chrome web store: must be 132 characters or less" }, "perRulesetStats": { @@ -236,11 +236,11 @@ "description": "Short description for a checkbox in the options page" }, "developerModeLabel": { - "message": "Developer mode", + "message": "Način rada za programere", "description": "Label for a checkbox in the options page" }, "developerModeLegend": { - "message": "Enable access to features suitable for technical users.", + "message": "Omogućite pristup značajkama prikladnim za tehničke korisnike.", "description": "Short description for a checkbox in the options page" }, "findListsPlaceholder": { @@ -292,23 +292,23 @@ "description": "Tooltip for the button used to exit zapper mode" }, "saveButton": { - "message": "Save", + "message": "Spremi", "description": "Text for buttons used to save changes" }, "revertButton": { - "message": "Revert", + "message": "Poništi", "description": "Text for buttons used to revert changes" }, "importAndAppendButton": { - "message": "Import and append…", + "message": "Uvesti i dodati...", "description": "Text for buttons used to import and append content" }, "exportButton": { - "message": "Export…", + "message": "Izvoz...", "description": "Text for buttons used to export content" }, "dnrRulesSummary": { - "message": "Enter your own DNR rules below. Do not add content from untrusted sources.", + "message": "Unesite svoja DNR pravila u nastavku. Nemojte dodavati sadržaj iz nepouzdanih izvora.", "description": "Short description of the DNR rules editor pane" } } diff --git a/platform/mv3/extension/_locales/it/messages.json b/platform/mv3/extension/_locales/it/messages.json index 53b04b95cfb3a..c30cc01a4d954 100644 --- a/platform/mv3/extension/_locales/it/messages.json +++ b/platform/mv3/extension/_locales/it/messages.json @@ -236,11 +236,11 @@ "description": "Short description for a checkbox in the options page" }, "developerModeLabel": { - "message": "Developer mode", + "message": "Modalità sviluppatore", "description": "Label for a checkbox in the options page" }, "developerModeLegend": { - "message": "Enable access to features suitable for technical users.", + "message": "Abilita l'accesso a funzioni adatte a utenti tecnici.", "description": "Short description for a checkbox in the options page" }, "findListsPlaceholder": { @@ -292,23 +292,23 @@ "description": "Tooltip for the button used to exit zapper mode" }, "saveButton": { - "message": "Save", + "message": "Salva", "description": "Text for buttons used to save changes" }, "revertButton": { - "message": "Revert", + "message": "Ritorna", "description": "Text for buttons used to revert changes" }, "importAndAppendButton": { - "message": "Import and append…", + "message": "Importa e accoda…", "description": "Text for buttons used to import and append content" }, "exportButton": { - "message": "Export…", + "message": "Esporta…", "description": "Text for buttons used to export content" }, "dnrRulesSummary": { - "message": "Enter your own DNR rules below. Do not add content from untrusted sources.", + "message": "Inserisci le tue regole DNR qui sotto. Non aggiungere contenuti da fonti non attendibili.", "description": "Short description of the DNR rules editor pane" } } diff --git a/platform/mv3/extension/_locales/lv/messages.json b/platform/mv3/extension/_locales/lv/messages.json index c308f2c463dd1..2a84699f74cff 100644 --- a/platform/mv3/extension/_locales/lv/messages.json +++ b/platform/mv3/extension/_locales/lv/messages.json @@ -236,11 +236,11 @@ "description": "Short description for a checkbox in the options page" }, "developerModeLabel": { - "message": "Developer mode", + "message": "Izstrādātāja režīms", "description": "Label for a checkbox in the options page" }, "developerModeLegend": { - "message": "Enable access to features suitable for technical users.", + "message": "Iespējot piekļuvi iespējām, kas piemērotas tehniskiem lietotājiem.", "description": "Short description for a checkbox in the options page" }, "findListsPlaceholder": { @@ -292,23 +292,23 @@ "description": "Tooltip for the button used to exit zapper mode" }, "saveButton": { - "message": "Save", + "message": "Saglabāt", "description": "Text for buttons used to save changes" }, "revertButton": { - "message": "Revert", + "message": "Atjaunot", "description": "Text for buttons used to revert changes" }, "importAndAppendButton": { - "message": "Import and append…", + "message": "Ievietot un pievienot…", "description": "Text for buttons used to import and append content" }, "exportButton": { - "message": "Export…", + "message": "Izgūt…", "description": "Text for buttons used to export content" }, "dnrRulesSummary": { - "message": "Enter your own DNR rules below. Do not add content from untrusted sources.", + "message": "Zemāk ir ievadi savas DNR kārtulas! Nepievieno saturu no neuzticamiem avotiem!", "description": "Short description of the DNR rules editor pane" } } diff --git a/platform/mv3/extension/_locales/nb/messages.json b/platform/mv3/extension/_locales/nb/messages.json index 7282364c22462..25d28af42cc40 100644 --- a/platform/mv3/extension/_locales/nb/messages.json +++ b/platform/mv3/extension/_locales/nb/messages.json @@ -112,11 +112,11 @@ "description": "First paragraph of 'Filter issues' section in Support pane" }, "supportS5H": { - "message": "Troubleshooting information", + "message": "Feilsøkingsinformasjon", "description": "Label of 'Troubleshooting information' section in 'Report a filter issue' page" }, "supportS6P1S1": { - "message": "For å unngå å belaste de frivillige med dobbeltrapporter, må du kontrollere at problemet ikke allerede har blitt rapportert. Noter: ved å klikke på knappen vil sidens opprinnelse bli sendt til GitHub..", + "message": "For å unngå å belaste de frivillige med dobbeltrapporter, må du kontrollere at problemet ikke allerede har blitt rapportert. Noter: ved å klikke på knappen vil sidens opprinnelse bli sendt til GitHub.", "description": "A paragraph in the filter issue reporter section" }, "supportFindSpecificButton": { @@ -236,11 +236,11 @@ "description": "Short description for a checkbox in the options page" }, "developerModeLabel": { - "message": "Developer mode", + "message": "Utviklermodus", "description": "Label for a checkbox in the options page" }, "developerModeLegend": { - "message": "Enable access to features suitable for technical users.", + "message": "Aktiver tilgang til funksjoner som er egnet for tekniske brukere.", "description": "Short description for a checkbox in the options page" }, "findListsPlaceholder": { @@ -284,31 +284,31 @@ "description": "A button to navigate to the blocked page" }, "zapperTipEnter": { - "message": "Enter element zapper mode", + "message": "Gå til element­fjernings­modus", "description": "Tooltip for the button used to enter zapper mode" }, "zapperTipQuit": { - "message": "Exit element zapper mode", + "message": "Forlat elementfjerningsmodus", "description": "Tooltip for the button used to exit zapper mode" }, "saveButton": { - "message": "Save", + "message": "Lagre", "description": "Text for buttons used to save changes" }, "revertButton": { - "message": "Revert", + "message": "Tilbakestill", "description": "Text for buttons used to revert changes" }, "importAndAppendButton": { - "message": "Import and append…", + "message": "Importer og legg til...", "description": "Text for buttons used to import and append content" }, "exportButton": { - "message": "Export…", + "message": "Eksporter...", "description": "Text for buttons used to export content" }, "dnrRulesSummary": { - "message": "Enter your own DNR rules below. Do not add content from untrusted sources.", + "message": "Legg til dine egne DNR-regler under. Ikke legg til innhold fra usikre kilder.", "description": "Short description of the DNR rules editor pane" } } diff --git a/platform/mv3/extension/_locales/nl/messages.json b/platform/mv3/extension/_locales/nl/messages.json index 1de06d4a04583..a4bb05b72d741 100644 --- a/platform/mv3/extension/_locales/nl/messages.json +++ b/platform/mv3/extension/_locales/nl/messages.json @@ -4,7 +4,7 @@ "description": "extension name." }, "extShortDesc": { - "message": "Een toestemmingsloze inhoudsblokkeerder. Blokkeert direct na installatie advertenties, trackers, miners en meer.", + "message": "Een efficiënte inhoudsblokkeerder. Blokkeert direct na installatie advertenties, trackers, miners en meer.", "description": "this will be in the Chrome web store: must be 132 characters or less" }, "perRulesetStats": { @@ -120,7 +120,7 @@ "description": "A paragraph in the filter issue reporter section" }, "supportFindSpecificButton": { - "message": "Soortgelijke meldingen zoeken", + "message": "Soortgelijke meldingen op GitHub zoeken", "description": "A clickable link in the filter issue reporter section" }, "supportS6URL": { @@ -168,7 +168,7 @@ "description": "A checkbox to use for NSFW sites" }, "supportReportSpecificButton": { - "message": "Nieuwe melding maken", + "message": "Nieuwe melding op GitHub maken", "description": "Text for button which open an external webpage in Support pane" }, "defaultFilteringModeSectionLabel": { @@ -236,11 +236,11 @@ "description": "Short description for a checkbox in the options page" }, "developerModeLabel": { - "message": "Developer mode", + "message": "Ontwikkelaarsmodus", "description": "Label for a checkbox in the options page" }, "developerModeLegend": { - "message": "Enable access to features suitable for technical users.", + "message": "Toegang tot voor technische gebruikers geschikte functies inschaken.", "description": "Short description for a checkbox in the options page" }, "findListsPlaceholder": { @@ -292,23 +292,23 @@ "description": "Tooltip for the button used to exit zapper mode" }, "saveButton": { - "message": "Save", + "message": "Opslaan", "description": "Text for buttons used to save changes" }, "revertButton": { - "message": "Revert", + "message": "Ongedaan maken", "description": "Text for buttons used to revert changes" }, "importAndAppendButton": { - "message": "Import and append…", + "message": "Importeren en toevoegen…", "description": "Text for buttons used to import and append content" }, "exportButton": { - "message": "Export…", + "message": "Exporteren…", "description": "Text for buttons used to export content" }, "dnrRulesSummary": { - "message": "Enter your own DNR rules below. Do not add content from untrusted sources.", + "message": "Voer hieronder uw eigen DNR-regels in. Voeg geen inhoud van niet-vertrouwde bronnen toe.", "description": "Short description of the DNR rules editor pane" } } diff --git a/platform/mv3/extension/_locales/pl/messages.json b/platform/mv3/extension/_locales/pl/messages.json index 46e37a83ca6dc..e39d4d6501ae9 100644 --- a/platform/mv3/extension/_locales/pl/messages.json +++ b/platform/mv3/extension/_locales/pl/messages.json @@ -236,11 +236,11 @@ "description": "Short description for a checkbox in the options page" }, "developerModeLabel": { - "message": "Developer mode", + "message": "Tryb programisty", "description": "Label for a checkbox in the options page" }, "developerModeLegend": { - "message": "Enable access to features suitable for technical users.", + "message": "Włącz dostęp do odpowiednich funkcji dla użytkowników technicznych.", "description": "Short description for a checkbox in the options page" }, "findListsPlaceholder": { @@ -292,23 +292,23 @@ "description": "Tooltip for the button used to exit zapper mode" }, "saveButton": { - "message": "Save", + "message": "Zapisz", "description": "Text for buttons used to save changes" }, "revertButton": { - "message": "Revert", + "message": "Przywróć", "description": "Text for buttons used to revert changes" }, "importAndAppendButton": { - "message": "Import and append…", + "message": "Importuj i dołącz…", "description": "Text for buttons used to import and append content" }, "exportButton": { - "message": "Export…", + "message": "Eksportuj…", "description": "Text for buttons used to export content" }, "dnrRulesSummary": { - "message": "Enter your own DNR rules below. Do not add content from untrusted sources.", + "message": "Wprowadź poniżej własne zasady DNR. Nie dodawaj treści z niezaufanych źródeł.", "description": "Short description of the DNR rules editor pane" } } diff --git a/platform/mv3/extension/_locales/pt_BR/messages.json b/platform/mv3/extension/_locales/pt_BR/messages.json index c852e5b9e1645..a3b43ef3e5828 100644 --- a/platform/mv3/extension/_locales/pt_BR/messages.json +++ b/platform/mv3/extension/_locales/pt_BR/messages.json @@ -236,11 +236,11 @@ "description": "Short description for a checkbox in the options page" }, "developerModeLabel": { - "message": "Developer mode", + "message": "Modo de desenvolvedor", "description": "Label for a checkbox in the options page" }, "developerModeLegend": { - "message": "Enable access to features suitable for technical users.", + "message": "Ativar acesso as funções adequadas pra usuários técnicos.", "description": "Short description for a checkbox in the options page" }, "findListsPlaceholder": { @@ -292,23 +292,23 @@ "description": "Tooltip for the button used to exit zapper mode" }, "saveButton": { - "message": "Save", + "message": "Salvar", "description": "Text for buttons used to save changes" }, "revertButton": { - "message": "Revert", + "message": "Reverter", "description": "Text for buttons used to revert changes" }, "importAndAppendButton": { - "message": "Import and append…", + "message": "Importar e anexar…", "description": "Text for buttons used to import and append content" }, "exportButton": { - "message": "Export…", + "message": "Exportar…", "description": "Text for buttons used to export content" }, "dnrRulesSummary": { - "message": "Enter your own DNR rules below. Do not add content from untrusted sources.", + "message": "Insira suas próprias regras DNR abaixo. Não adicione conteúdo de fontes não confiáveis.", "description": "Short description of the DNR rules editor pane" } } diff --git a/platform/mv3/extension/_locales/ru/messages.json b/platform/mv3/extension/_locales/ru/messages.json index 0eca35d9ec705..6cafaf01bcc85 100644 --- a/platform/mv3/extension/_locales/ru/messages.json +++ b/platform/mv3/extension/_locales/ru/messages.json @@ -236,11 +236,11 @@ "description": "Short description for a checkbox in the options page" }, "developerModeLabel": { - "message": "Developer mode", + "message": "Режим разработчика", "description": "Label for a checkbox in the options page" }, "developerModeLegend": { - "message": "Enable access to features suitable for technical users.", + "message": "Включить доступ к функциям, предназначенным для технических пользователей.", "description": "Short description for a checkbox in the options page" }, "findListsPlaceholder": { @@ -292,23 +292,23 @@ "description": "Tooltip for the button used to exit zapper mode" }, "saveButton": { - "message": "Save", + "message": "Сохранить", "description": "Text for buttons used to save changes" }, "revertButton": { - "message": "Revert", + "message": "Вернуть", "description": "Text for buttons used to revert changes" }, "importAndAppendButton": { - "message": "Import and append…", + "message": "Импортировать и добавить…", "description": "Text for buttons used to import and append content" }, "exportButton": { - "message": "Export…", + "message": "Экспортировать…", "description": "Text for buttons used to export content" }, "dnrRulesSummary": { - "message": "Enter your own DNR rules below. Do not add content from untrusted sources.", + "message": "Введите свои правила в формате DNR ниже. Не добавляйте содержимое из ненадёжных источников.", "description": "Short description of the DNR rules editor pane" } } diff --git a/platform/mv3/extension/_locales/sk/messages.json b/platform/mv3/extension/_locales/sk/messages.json index 22aad57c39746..5e8db88b075de 100644 --- a/platform/mv3/extension/_locales/sk/messages.json +++ b/platform/mv3/extension/_locales/sk/messages.json @@ -236,11 +236,11 @@ "description": "Short description for a checkbox in the options page" }, "developerModeLabel": { - "message": "Developer mode", + "message": "Vývojársky režim", "description": "Label for a checkbox in the options page" }, "developerModeLegend": { - "message": "Enable access to features suitable for technical users.", + "message": "Povoliť prístup k funkciám vhodným pre technických používateľov.", "description": "Short description for a checkbox in the options page" }, "findListsPlaceholder": { @@ -292,23 +292,23 @@ "description": "Tooltip for the button used to exit zapper mode" }, "saveButton": { - "message": "Save", + "message": "Uložiť", "description": "Text for buttons used to save changes" }, "revertButton": { - "message": "Revert", + "message": "Vrátiť späť", "description": "Text for buttons used to revert changes" }, "importAndAppendButton": { - "message": "Import and append…", + "message": "Importovať a pripojiť…", "description": "Text for buttons used to import and append content" }, "exportButton": { - "message": "Export…", + "message": "Exportovať…", "description": "Text for buttons used to export content" }, "dnrRulesSummary": { - "message": "Enter your own DNR rules below. Do not add content from untrusted sources.", + "message": "Zadajte svoje vlastné pravidlá DNR. Nepridávajte obsah z nedôveryhodných zdrojov.", "description": "Short description of the DNR rules editor pane" } } diff --git a/platform/mv3/extension/_locales/sv/messages.json b/platform/mv3/extension/_locales/sv/messages.json index 79a345a5edda7..e8df0696c76dd 100644 --- a/platform/mv3/extension/_locales/sv/messages.json +++ b/platform/mv3/extension/_locales/sv/messages.json @@ -236,11 +236,11 @@ "description": "Short description for a checkbox in the options page" }, "developerModeLabel": { - "message": "Developer mode", + "message": "Utvecklarläge", "description": "Label for a checkbox in the options page" }, "developerModeLegend": { - "message": "Enable access to features suitable for technical users.", + "message": "Aktivera åtkomst till funktioner som är lämpliga för tekniska användare.", "description": "Short description for a checkbox in the options page" }, "findListsPlaceholder": { @@ -292,23 +292,23 @@ "description": "Tooltip for the button used to exit zapper mode" }, "saveButton": { - "message": "Save", + "message": "Spara", "description": "Text for buttons used to save changes" }, "revertButton": { - "message": "Revert", + "message": "Ångra", "description": "Text for buttons used to revert changes" }, "importAndAppendButton": { - "message": "Import and append…", + "message": "Importera och lägg till…", "description": "Text for buttons used to import and append content" }, "exportButton": { - "message": "Export…", + "message": "Exportera…", "description": "Text for buttons used to export content" }, "dnrRulesSummary": { - "message": "Enter your own DNR rules below. Do not add content from untrusted sources.", + "message": "Ange dina egna DNR-regler nedan. Lägg inte till innehåll från opålitliga källor.", "description": "Short description of the DNR rules editor pane" } } diff --git a/platform/mv3/extension/_locales/tr/messages.json b/platform/mv3/extension/_locales/tr/messages.json index c8e8a857f181f..4f0cc0cbce05d 100644 --- a/platform/mv3/extension/_locales/tr/messages.json +++ b/platform/mv3/extension/_locales/tr/messages.json @@ -236,11 +236,11 @@ "description": "Short description for a checkbox in the options page" }, "developerModeLabel": { - "message": "Developer mode", + "message": "Geliştirici modu", "description": "Label for a checkbox in the options page" }, "developerModeLegend": { - "message": "Enable access to features suitable for technical users.", + "message": "Teknik kullanıcılara uygun özelliklere erişime izin ver", "description": "Short description for a checkbox in the options page" }, "findListsPlaceholder": { @@ -292,23 +292,23 @@ "description": "Tooltip for the button used to exit zapper mode" }, "saveButton": { - "message": "Save", + "message": "Kaydet", "description": "Text for buttons used to save changes" }, "revertButton": { - "message": "Revert", + "message": "Geri al", "description": "Text for buttons used to revert changes" }, "importAndAppendButton": { - "message": "Import and append…", + "message": "İçe aktar ve ekle…", "description": "Text for buttons used to import and append content" }, "exportButton": { - "message": "Export…", + "message": "Dışa aktar…", "description": "Text for buttons used to export content" }, "dnrRulesSummary": { - "message": "Enter your own DNR rules below. Do not add content from untrusted sources.", + "message": "Aşağıya kendi DNR kurallarınızı girin. Güvenilmeyen kaynaklardan içerik eklemeyin.", "description": "Short description of the DNR rules editor pane" } } diff --git a/platform/mv3/extension/_locales/uk/messages.json b/platform/mv3/extension/_locales/uk/messages.json index 5046caa794218..4d733448a99ce 100644 --- a/platform/mv3/extension/_locales/uk/messages.json +++ b/platform/mv3/extension/_locales/uk/messages.json @@ -236,11 +236,11 @@ "description": "Short description for a checkbox in the options page" }, "developerModeLabel": { - "message": "Developer mode", + "message": "Режим розробника", "description": "Label for a checkbox in the options page" }, "developerModeLegend": { - "message": "Enable access to features suitable for technical users.", + "message": "Відкрити доступ до можливостей, які актуальні для технічних користувачів", "description": "Short description for a checkbox in the options page" }, "findListsPlaceholder": { @@ -292,23 +292,23 @@ "description": "Tooltip for the button used to exit zapper mode" }, "saveButton": { - "message": "Save", + "message": "Зберегти", "description": "Text for buttons used to save changes" }, "revertButton": { - "message": "Revert", + "message": "Повернути", "description": "Text for buttons used to revert changes" }, "importAndAppendButton": { - "message": "Import and append…", + "message": "Імпортувати та додати…", "description": "Text for buttons used to import and append content" }, "exportButton": { - "message": "Export…", + "message": "Експортувати…", "description": "Text for buttons used to export content" }, "dnrRulesSummary": { - "message": "Enter your own DNR rules below. Do not add content from untrusted sources.", + "message": "Введіть свої правила в форматі DNR нижче. Не додавайте вміст з ненадійних джерел.", "description": "Short description of the DNR rules editor pane" } } diff --git a/platform/mv3/extension/_locales/zh_TW/messages.json b/platform/mv3/extension/_locales/zh_TW/messages.json index 76c75bcea1b49..a37fc514a217a 100644 --- a/platform/mv3/extension/_locales/zh_TW/messages.json +++ b/platform/mv3/extension/_locales/zh_TW/messages.json @@ -236,11 +236,11 @@ "description": "Short description for a checkbox in the options page" }, "developerModeLabel": { - "message": "Developer mode", + "message": "開發人員模式", "description": "Label for a checkbox in the options page" }, "developerModeLegend": { - "message": "Enable access to features suitable for technical users.", + "message": "啟用適合技術使用者使用的功能。", "description": "Short description for a checkbox in the options page" }, "findListsPlaceholder": { @@ -292,23 +292,23 @@ "description": "Tooltip for the button used to exit zapper mode" }, "saveButton": { - "message": "Save", + "message": "儲存", "description": "Text for buttons used to save changes" }, "revertButton": { - "message": "Revert", + "message": "還原", "description": "Text for buttons used to revert changes" }, "importAndAppendButton": { - "message": "Import and append…", + "message": "匯入並加入…", "description": "Text for buttons used to import and append content" }, "exportButton": { - "message": "Export…", + "message": "匯出…", "description": "Text for buttons used to export content" }, "dnrRulesSummary": { - "message": "Enter your own DNR rules below. Do not add content from untrusted sources.", + "message": "請在下方輸入您的 DNR 規則。請勿新增來自不受信任來源的內容。", "description": "Short description of the DNR rules editor pane" } } diff --git a/src/_locales/br_FR/messages.json b/src/_locales/br_FR/messages.json index 62c931cd81ee2..f15baeeae560b 100644 --- a/src/_locales/br_FR/messages.json +++ b/src/_locales/br_FR/messages.json @@ -476,7 +476,7 @@ "description": "Filter lists section name" }, "3pGroupPrivacy": { - "message": "Prevezded", + "message": "Buhez prevez", "description": "Filter lists section name" }, "3pGroupMalware": { @@ -964,7 +964,7 @@ "description": "Header of 'Report a filter issue' section in Support pane" }, "supportS6P1S1": { - "message": "Evit nompas sammañ ar genlabourerien a-youl vat gant meur a zanevell heñvel, gwiriit ma n'eo ket bet danevellet ho kudenn en ar-raok mar plij.", + "message": "Evit nompas sammañ ar genlabourerien a-youl vat gant meur a zanevell heñvel, gwiriit ma n'eo ket bet danevellet ho kudenn en a-raok mar plij.", "description": "A paragraph in the filter issue reporter section" }, "supportS6P2S1": { @@ -1000,7 +1000,7 @@ "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option4": { - "message": "Kudennoù a-fed prevezded he deus", + "message": "Kudennoù a-fed ar vuhez prevez he deus", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option5": { @@ -1028,7 +1028,7 @@ "description": "Text for 'Unredact' button" }, "aboutPrivacyPolicy": { - "message": "Politikerezh prevezded", + "message": "Politikerezh ar vuhez prevez", "description": "Link to privacy policy on GitHub (English)" }, "aboutChangelog": { diff --git a/src/_locales/de/messages.json b/src/_locales/de/messages.json index 39ffd4e739cae..aab0059cf0bc3 100644 --- a/src/_locales/de/messages.json +++ b/src/_locales/de/messages.json @@ -548,7 +548,7 @@ "description": "Label for the checkbox use to trust the content of 'My filters' list" }, "1pImport": { - "message": "Importieren und anfügen …", + "message": "Importieren und ergänzen …", "description": "Button in the 'My filters' pane" }, "1pExport": { @@ -632,7 +632,7 @@ "description": "A concise description of the 'Trusted sites' pane." }, "whitelistImport": { - "message": "Importieren und anfügen …", + "message": "Importieren und ergänzen …", "description": "Button in the 'Trusted sites' pane" }, "whitelistExport": { diff --git a/src/_locales/fi/messages.json b/src/_locales/fi/messages.json index 05bcd595474c6..98b58ae3d676b 100644 --- a/src/_locales/fi/messages.json +++ b/src/_locales/fi/messages.json @@ -948,11 +948,11 @@ "description": "First paragraph of 'Bug report' section in Support pane" }, "supportS5H": { - "message": "Vianmääritystiedot", + "message": "Vianselvitystiedot", "description": "Header of 'Troubleshooting Information' section in Support pane" }, "supportS5P1": { - "message": "Alla on teknisiä tietoja, joista voi olla hyötyä vapaaehtoisille, jotka pyrkivät auttamaan ongelmasi ratkaisussa.", + "message": "Alla on teknisiä tietoja, jotka saattavat auttaa ongelmasi ratkonnassa auttavia vapaaehtoisia.", "description": "First paragraph of 'Troubleshooting Information' section in Support pane" }, "supportS5P2": { @@ -964,7 +964,7 @@ "description": "Header of 'Report a filter issue' section in Support pane" }, "supportS6P1S1": { - "message": "Välttääksesi vapaaehtoisten kuormittamisen ylimääräisillä ilmoituksilla, tarkasta ensin onko ongelmasta jo ilmoitettu. Huomioi: painikkeen painalluksen seurauksena sivun osoite lähetetään GitHubiin.", + "message": "Välttääksesi vapaaehtoisten kuormittamisen ylimääräisillä ilmoituksilla, tarkasta ensin onko ongelmasta jo ilmoitettu. Huomioi: Painikkeen painallus lähettää sivun osoitteen GitHubiin.", "description": "A paragraph in the filter issue reporter section" }, "supportS6P2S1": { diff --git a/src/_locales/lv/messages.json b/src/_locales/lv/messages.json index 4159b070549da..bb9beb0449ea7 100644 --- a/src/_locales/lv/messages.json +++ b/src/_locales/lv/messages.json @@ -76,7 +76,7 @@ "description": "Message to be read by screen readers" }, "popupPowerSwitchInfo2": { - "message": "Klikšķināt, lai šai vietnei ieslēgtu uBlock₀.", + "message": "Klikšķināt, lai šajā vietnē iespējotu uBlock₀.", "description": "Message to be read by screen readers" }, "popupBlockedRequestPrompt": { @@ -340,7 +340,7 @@ "description": "Section for controlling user interface appearance" }, "settingsThemeLabel": { - "message": "Motīvs", + "message": "Izskats", "description": "Label for checkbox to enable a custom dark theme" }, "settingsThemeAccent0Label": { @@ -508,11 +508,11 @@ "description": "Filter lists section name" }, "3pImport": { - "message": "Importēt…", + "message": "Ievietot…", "description": "The label for the checkbox used to import external filter lists" }, "3pExternalListsHint": { - "message": "Viens URL katrā rinda. Nederīgi URL netiks ņemti vērā.", + "message": "Viens URL katrā rindā. Nederīgi URL netiks ņemti vērā.", "description": "Short information about how to use the textarea to import external filter lists by URL" }, "3pExternalListObsolete": { @@ -552,7 +552,7 @@ "description": "Button in the 'My filters' pane" }, "1pExport": { - "message": "Eksportēt…", + "message": "Izgūt…", "description": "Button in the 'My filters' pane" }, "1pExportFilename": { @@ -592,11 +592,11 @@ "description": "Will discard manually-edited content and exit manual-edit mode" }, "rulesImport": { - "message": "Importēt no faila…", + "message": "Ievietot no datnes…", "description": "" }, "rulesExport": { - "message": "Eksportēt uz failu", + "message": "Izgūt datnē…", "description": "Button in the 'My rules' pane" }, "rulesDefaultFileName": { @@ -636,7 +636,7 @@ "description": "Button in the 'Trusted sites' pane" }, "whitelistExport": { - "message": "Eksportēt…", + "message": "Izgūt…", "description": "Button in the 'Trusted sites' pane" }, "whitelistExportFilename": { @@ -932,7 +932,7 @@ "description": "First paragraph of 'Filter issues' section in Support pane" }, "supportS3P2": { - "message": "Svarīgi: ir jāizvairas no līdzīgu aizturētāju izmantošanas vienlaicīgi ar uBlock Origin, jo tas var radīt aizturēšanas kļūmes noteiktās vietnēs.", + "message": "Svarīgi: ir jāizvairās no līdzīgu aizturētāju izmantošanas vienlaicīgi ar uBlock Origin, jo tas var radīt aizturēšanas kļūmes noteiktās vietnēs.", "description": "Second paragraph of 'Filter issues' section in Support pane" }, "supportS3P3": { @@ -956,7 +956,7 @@ "description": "First paragraph of 'Troubleshooting Information' section in Support pane" }, "supportS5P2": { - "message": "Svarīgi: iespējami privāta vai sensitīva informācija pēc noklusējuma tiek izņemta. Aizvākta informācija var apgrūtināt sarežģījuma atrisināšanu.", + "message": "Svarīgi: iespējami privāta vai jūtīga informācija pēc noklusējuma tiek izņemta. Aizvākta informācija var apgrūtināt sarežģījuma atrisināšanu.", "description": "Second paragraph of 'Troubleshooting Information' section in Support pane" }, "supportS6H": { diff --git a/src/_locales/nl/messages.json b/src/_locales/nl/messages.json index e0ab0747789a4..657df8ca49d60 100644 --- a/src/_locales/nl/messages.json +++ b/src/_locales/nl/messages.json @@ -904,7 +904,7 @@ "description": "Text for button which open an external webpage in Support pane" }, "supportFindSpecificButton": { - "message": "Soortgelijke meldingen zoeken", + "message": "Soortgelijke meldingen op GitHub zoeken", "description": "A clickable link in the filter issue reporter section" }, "supportS1H": { From f2c063321632d622feec0aa92058af830f76d11f Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Thu, 29 May 2025 10:09:02 -0400 Subject: [PATCH 0967/1099] [mv3] Add missing translatable string --- platform/mv3/extension/_locales/en/messages.json | 4 ++++ platform/mv3/extension/dashboard.html | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/platform/mv3/extension/_locales/en/messages.json b/platform/mv3/extension/_locales/en/messages.json index 03e28740de124..e238f3a0180a8 100644 --- a/platform/mv3/extension/_locales/en/messages.json +++ b/platform/mv3/extension/_locales/en/messages.json @@ -19,6 +19,10 @@ "message": "Settings", "description": "appears as tab name in dashboard" }, + "developPageName": { + "message": "Develop", + "description": "appears as tab name in dashboard. Inspired from 'Develop' menu in Safari, see " + }, "aboutPageName": { "message": "About", "description": "appears as tab name in dashboard" diff --git a/platform/mv3/extension/dashboard.html b/platform/mv3/extension/dashboard.html index 3acecd56949f1..91306b845bebb 100644 --- a/platform/mv3/extension/dashboard.html +++ b/platform/mv3/extension/dashboard.html @@ -24,7 +24,7 @@ From 6647ae55a9367d9954f4b275db100c6efd862eae Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Thu, 29 May 2025 10:10:27 -0400 Subject: [PATCH 0968/1099] Import translation work from https://crowdin.com/project/ublock --- platform/mv3/extension/_locales/ar/messages.json | 10 +++++++++- platform/mv3/extension/_locales/az/messages.json | 10 +++++++++- platform/mv3/extension/_locales/be/messages.json | 10 +++++++++- platform/mv3/extension/_locales/bg/messages.json | 8 ++++++++ platform/mv3/extension/_locales/bn/messages.json | 8 ++++++++ platform/mv3/extension/_locales/br_FR/messages.json | 8 ++++++++ platform/mv3/extension/_locales/bs/messages.json | 10 +++++++++- platform/mv3/extension/_locales/ca/messages.json | 8 ++++++++ platform/mv3/extension/_locales/cs/messages.json | 10 +++++++++- platform/mv3/extension/_locales/cv/messages.json | 10 +++++++++- platform/mv3/extension/_locales/cy/messages.json | 10 +++++++++- platform/mv3/extension/_locales/da/messages.json | 8 ++++++++ platform/mv3/extension/_locales/de/messages.json | 8 ++++++++ platform/mv3/extension/_locales/el/messages.json | 8 ++++++++ platform/mv3/extension/_locales/en_GB/messages.json | 8 ++++++++ platform/mv3/extension/_locales/eo/messages.json | 10 +++++++++- platform/mv3/extension/_locales/es/messages.json | 8 ++++++++ platform/mv3/extension/_locales/et/messages.json | 8 ++++++++ platform/mv3/extension/_locales/eu/messages.json | 10 +++++++++- platform/mv3/extension/_locales/fa/messages.json | 10 +++++++++- platform/mv3/extension/_locales/fi/messages.json | 8 ++++++++ platform/mv3/extension/_locales/fil/messages.json | 10 +++++++++- platform/mv3/extension/_locales/fr/messages.json | 8 ++++++++ platform/mv3/extension/_locales/fy/messages.json | 8 ++++++++ platform/mv3/extension/_locales/gl/messages.json | 8 ++++++++ platform/mv3/extension/_locales/gu/messages.json | 10 +++++++++- platform/mv3/extension/_locales/he/messages.json | 8 ++++++++ platform/mv3/extension/_locales/hi/messages.json | 10 +++++++++- platform/mv3/extension/_locales/hr/messages.json | 8 ++++++++ platform/mv3/extension/_locales/hu/messages.json | 10 +++++++++- platform/mv3/extension/_locales/hy/messages.json | 10 +++++++++- platform/mv3/extension/_locales/id/messages.json | 10 +++++++++- platform/mv3/extension/_locales/it/messages.json | 8 ++++++++ platform/mv3/extension/_locales/ja/messages.json | 10 +++++++++- platform/mv3/extension/_locales/ka/messages.json | 10 +++++++++- platform/mv3/extension/_locales/kk/messages.json | 10 +++++++++- platform/mv3/extension/_locales/kn/messages.json | 10 +++++++++- platform/mv3/extension/_locales/ko/messages.json | 10 +++++++++- platform/mv3/extension/_locales/lt/messages.json | 10 +++++++++- platform/mv3/extension/_locales/lv/messages.json | 8 ++++++++ platform/mv3/extension/_locales/mk/messages.json | 10 +++++++++- platform/mv3/extension/_locales/ml/messages.json | 10 +++++++++- platform/mv3/extension/_locales/mr/messages.json | 10 +++++++++- platform/mv3/extension/_locales/ms/messages.json | 10 +++++++++- platform/mv3/extension/_locales/nb/messages.json | 8 ++++++++ platform/mv3/extension/_locales/nl/messages.json | 8 ++++++++ platform/mv3/extension/_locales/oc/messages.json | 10 +++++++++- platform/mv3/extension/_locales/pa/messages.json | 10 +++++++++- platform/mv3/extension/_locales/pl/messages.json | 8 ++++++++ platform/mv3/extension/_locales/pt_BR/messages.json | 8 ++++++++ platform/mv3/extension/_locales/pt_PT/messages.json | 10 +++++++++- platform/mv3/extension/_locales/ro/messages.json | 10 +++++++++- platform/mv3/extension/_locales/ru/messages.json | 8 ++++++++ platform/mv3/extension/_locales/si/messages.json | 10 +++++++++- platform/mv3/extension/_locales/sk/messages.json | 8 ++++++++ platform/mv3/extension/_locales/sl/messages.json | 10 +++++++++- platform/mv3/extension/_locales/so/messages.json | 10 +++++++++- platform/mv3/extension/_locales/sq/messages.json | 10 +++++++++- platform/mv3/extension/_locales/sr/messages.json | 10 +++++++++- platform/mv3/extension/_locales/sv/messages.json | 8 ++++++++ platform/mv3/extension/_locales/sw/messages.json | 10 +++++++++- platform/mv3/extension/_locales/ta/messages.json | 10 +++++++++- platform/mv3/extension/_locales/te/messages.json | 10 +++++++++- platform/mv3/extension/_locales/th/messages.json | 10 +++++++++- platform/mv3/extension/_locales/tr/messages.json | 8 ++++++++ platform/mv3/extension/_locales/uk/messages.json | 8 ++++++++ platform/mv3/extension/_locales/ur/messages.json | 10 +++++++++- platform/mv3/extension/_locales/vi/messages.json | 10 +++++++++- platform/mv3/extension/_locales/zh_CN/messages.json | 10 +++++++++- platform/mv3/extension/_locales/zh_TW/messages.json | 8 ++++++++ 70 files changed, 602 insertions(+), 42 deletions(-) diff --git a/platform/mv3/extension/_locales/ar/messages.json b/platform/mv3/extension/_locales/ar/messages.json index f7b0a4906e3ac..5e73020ecd5a3 100644 --- a/platform/mv3/extension/_locales/ar/messages.json +++ b/platform/mv3/extension/_locales/ar/messages.json @@ -19,6 +19,10 @@ "message": "الإعدادات", "description": "appears as tab name in dashboard" }, + "developPageName": { + "message": "Develop", + "description": "appears as tab name in dashboard. Inspired from 'Develop' menu in Safari, see " + }, "aboutPageName": { "message": "حول البرنامج", "description": "appears as tab name in dashboard" @@ -240,7 +244,7 @@ "description": "Label for a checkbox in the options page" }, "developerModeLegend": { - "message": "Enable access to features suitable for technical users.", + "message": "Enables access to features suitable for technical users.", "description": "Short description for a checkbox in the options page" }, "findListsPlaceholder": { @@ -310,5 +314,9 @@ "dnrRulesSummary": { "message": "Enter your own DNR rules below. Do not add content from untrusted sources.", "description": "Short description of the DNR rules editor pane" + }, + "dnrRulesCountInfo": { + "message": "Number of registered rules: {count}", + "description": "Short sentence to report the number of currently registered DNR rules" } } diff --git a/platform/mv3/extension/_locales/az/messages.json b/platform/mv3/extension/_locales/az/messages.json index 27f7fc1b6cf44..7df2037d0aff5 100644 --- a/platform/mv3/extension/_locales/az/messages.json +++ b/platform/mv3/extension/_locales/az/messages.json @@ -19,6 +19,10 @@ "message": "Tənzimləmələr", "description": "appears as tab name in dashboard" }, + "developPageName": { + "message": "Develop", + "description": "appears as tab name in dashboard. Inspired from 'Develop' menu in Safari, see " + }, "aboutPageName": { "message": "Haqqında", "description": "appears as tab name in dashboard" @@ -240,7 +244,7 @@ "description": "Label for a checkbox in the options page" }, "developerModeLegend": { - "message": "Enable access to features suitable for technical users.", + "message": "Enables access to features suitable for technical users.", "description": "Short description for a checkbox in the options page" }, "findListsPlaceholder": { @@ -310,5 +314,9 @@ "dnrRulesSummary": { "message": "Enter your own DNR rules below. Do not add content from untrusted sources.", "description": "Short description of the DNR rules editor pane" + }, + "dnrRulesCountInfo": { + "message": "Number of registered rules: {count}", + "description": "Short sentence to report the number of currently registered DNR rules" } } diff --git a/platform/mv3/extension/_locales/be/messages.json b/platform/mv3/extension/_locales/be/messages.json index b35b66c6531a4..f7fa0450cf541 100644 --- a/platform/mv3/extension/_locales/be/messages.json +++ b/platform/mv3/extension/_locales/be/messages.json @@ -19,6 +19,10 @@ "message": "Налады", "description": "appears as tab name in dashboard" }, + "developPageName": { + "message": "Develop", + "description": "appears as tab name in dashboard. Inspired from 'Develop' menu in Safari, see " + }, "aboutPageName": { "message": "Пра пашырэнне", "description": "appears as tab name in dashboard" @@ -240,7 +244,7 @@ "description": "Label for a checkbox in the options page" }, "developerModeLegend": { - "message": "Enable access to features suitable for technical users.", + "message": "Enables access to features suitable for technical users.", "description": "Short description for a checkbox in the options page" }, "findListsPlaceholder": { @@ -310,5 +314,9 @@ "dnrRulesSummary": { "message": "Enter your own DNR rules below. Do not add content from untrusted sources.", "description": "Short description of the DNR rules editor pane" + }, + "dnrRulesCountInfo": { + "message": "Number of registered rules: {count}", + "description": "Short sentence to report the number of currently registered DNR rules" } } diff --git a/platform/mv3/extension/_locales/bg/messages.json b/platform/mv3/extension/_locales/bg/messages.json index 451ca9fd2972e..4617b1f35440f 100644 --- a/platform/mv3/extension/_locales/bg/messages.json +++ b/platform/mv3/extension/_locales/bg/messages.json @@ -19,6 +19,10 @@ "message": "Настройки", "description": "appears as tab name in dashboard" }, + "developPageName": { + "message": "Develop", + "description": "appears as tab name in dashboard. Inspired from 'Develop' menu in Safari, see " + }, "aboutPageName": { "message": "Относно", "description": "appears as tab name in dashboard" @@ -310,5 +314,9 @@ "dnrRulesSummary": { "message": "Въведете вашите собствени правила за DNR по-долу. Не добавяйте съдържание от ненадеждни източници.", "description": "Short description of the DNR rules editor pane" + }, + "dnrRulesCountInfo": { + "message": "Number of registered rules: {count}", + "description": "Short sentence to report the number of currently registered DNR rules" } } diff --git a/platform/mv3/extension/_locales/bn/messages.json b/platform/mv3/extension/_locales/bn/messages.json index 8e89bdde80f6a..0d5160f518756 100644 --- a/platform/mv3/extension/_locales/bn/messages.json +++ b/platform/mv3/extension/_locales/bn/messages.json @@ -19,6 +19,10 @@ "message": "সেটিংস", "description": "appears as tab name in dashboard" }, + "developPageName": { + "message": "Develop", + "description": "appears as tab name in dashboard. Inspired from 'Develop' menu in Safari, see " + }, "aboutPageName": { "message": "সম্পর্কে", "description": "appears as tab name in dashboard" @@ -310,5 +314,9 @@ "dnrRulesSummary": { "message": "Enter your own DNR rules below. Do not add content from untrusted sources.", "description": "Short description of the DNR rules editor pane" + }, + "dnrRulesCountInfo": { + "message": "Number of registered rules: {count}", + "description": "Short sentence to report the number of currently registered DNR rules" } } diff --git a/platform/mv3/extension/_locales/br_FR/messages.json b/platform/mv3/extension/_locales/br_FR/messages.json index 3a152d7e2fec6..3a9cb7c2a8cb9 100644 --- a/platform/mv3/extension/_locales/br_FR/messages.json +++ b/platform/mv3/extension/_locales/br_FR/messages.json @@ -19,6 +19,10 @@ "message": "Arventennoù", "description": "appears as tab name in dashboard" }, + "developPageName": { + "message": "Develop", + "description": "appears as tab name in dashboard. Inspired from 'Develop' menu in Safari, see " + }, "aboutPageName": { "message": "Diwar-benn", "description": "appears as tab name in dashboard" @@ -310,5 +314,9 @@ "dnrRulesSummary": { "message": "Lakait ho reolennoù DNR deoc'h-c'hwi amañ-dindan. Arabat ouzhpennañ danvez a zeu diouzh mammennoù douetus.", "description": "Short description of the DNR rules editor pane" + }, + "dnrRulesCountInfo": { + "message": "Number of registered rules: {count}", + "description": "Short sentence to report the number of currently registered DNR rules" } } diff --git a/platform/mv3/extension/_locales/bs/messages.json b/platform/mv3/extension/_locales/bs/messages.json index bded4c96425c5..f6fd54177a069 100644 --- a/platform/mv3/extension/_locales/bs/messages.json +++ b/platform/mv3/extension/_locales/bs/messages.json @@ -19,6 +19,10 @@ "message": "Postavke", "description": "appears as tab name in dashboard" }, + "developPageName": { + "message": "Develop", + "description": "appears as tab name in dashboard. Inspired from 'Develop' menu in Safari, see " + }, "aboutPageName": { "message": "O aplikaciji", "description": "appears as tab name in dashboard" @@ -240,7 +244,7 @@ "description": "Label for a checkbox in the options page" }, "developerModeLegend": { - "message": "Enable access to features suitable for technical users.", + "message": "Enables access to features suitable for technical users.", "description": "Short description for a checkbox in the options page" }, "findListsPlaceholder": { @@ -310,5 +314,9 @@ "dnrRulesSummary": { "message": "Enter your own DNR rules below. Do not add content from untrusted sources.", "description": "Short description of the DNR rules editor pane" + }, + "dnrRulesCountInfo": { + "message": "Number of registered rules: {count}", + "description": "Short sentence to report the number of currently registered DNR rules" } } diff --git a/platform/mv3/extension/_locales/ca/messages.json b/platform/mv3/extension/_locales/ca/messages.json index 9726dcc62ed6a..0997c9c02128d 100644 --- a/platform/mv3/extension/_locales/ca/messages.json +++ b/platform/mv3/extension/_locales/ca/messages.json @@ -19,6 +19,10 @@ "message": "Configuració", "description": "appears as tab name in dashboard" }, + "developPageName": { + "message": "Develop", + "description": "appears as tab name in dashboard. Inspired from 'Develop' menu in Safari, see " + }, "aboutPageName": { "message": "Quant a", "description": "appears as tab name in dashboard" @@ -310,5 +314,9 @@ "dnrRulesSummary": { "message": "Introduïu les vostres pròpies regles de DNR a continuació. No afegiu contingut de fonts no fiables.", "description": "Short description of the DNR rules editor pane" + }, + "dnrRulesCountInfo": { + "message": "Number of registered rules: {count}", + "description": "Short sentence to report the number of currently registered DNR rules" } } diff --git a/platform/mv3/extension/_locales/cs/messages.json b/platform/mv3/extension/_locales/cs/messages.json index 43324e823d35e..47609e3b6d75e 100644 --- a/platform/mv3/extension/_locales/cs/messages.json +++ b/platform/mv3/extension/_locales/cs/messages.json @@ -19,6 +19,10 @@ "message": "Nastavení", "description": "appears as tab name in dashboard" }, + "developPageName": { + "message": "Develop", + "description": "appears as tab name in dashboard. Inspired from 'Develop' menu in Safari, see " + }, "aboutPageName": { "message": "O rozšíření", "description": "appears as tab name in dashboard" @@ -240,7 +244,7 @@ "description": "Label for a checkbox in the options page" }, "developerModeLegend": { - "message": "Enable access to features suitable for technical users.", + "message": "Enables access to features suitable for technical users.", "description": "Short description for a checkbox in the options page" }, "findListsPlaceholder": { @@ -310,5 +314,9 @@ "dnrRulesSummary": { "message": "Enter your own DNR rules below. Do not add content from untrusted sources.", "description": "Short description of the DNR rules editor pane" + }, + "dnrRulesCountInfo": { + "message": "Number of registered rules: {count}", + "description": "Short sentence to report the number of currently registered DNR rules" } } diff --git a/platform/mv3/extension/_locales/cv/messages.json b/platform/mv3/extension/_locales/cv/messages.json index 136a025b98095..484529aa58de1 100644 --- a/platform/mv3/extension/_locales/cv/messages.json +++ b/platform/mv3/extension/_locales/cv/messages.json @@ -19,6 +19,10 @@ "message": "Settings", "description": "appears as tab name in dashboard" }, + "developPageName": { + "message": "Develop", + "description": "appears as tab name in dashboard. Inspired from 'Develop' menu in Safari, see " + }, "aboutPageName": { "message": "About", "description": "appears as tab name in dashboard" @@ -240,7 +244,7 @@ "description": "Label for a checkbox in the options page" }, "developerModeLegend": { - "message": "Enable access to features suitable for technical users.", + "message": "Enables access to features suitable for technical users.", "description": "Short description for a checkbox in the options page" }, "findListsPlaceholder": { @@ -310,5 +314,9 @@ "dnrRulesSummary": { "message": "Enter your own DNR rules below. Do not add content from untrusted sources.", "description": "Short description of the DNR rules editor pane" + }, + "dnrRulesCountInfo": { + "message": "Number of registered rules: {count}", + "description": "Short sentence to report the number of currently registered DNR rules" } } diff --git a/platform/mv3/extension/_locales/cy/messages.json b/platform/mv3/extension/_locales/cy/messages.json index e034be8972287..ff4d9108c39b3 100644 --- a/platform/mv3/extension/_locales/cy/messages.json +++ b/platform/mv3/extension/_locales/cy/messages.json @@ -19,6 +19,10 @@ "message": "Gosodiadau", "description": "appears as tab name in dashboard" }, + "developPageName": { + "message": "Develop", + "description": "appears as tab name in dashboard. Inspired from 'Develop' menu in Safari, see " + }, "aboutPageName": { "message": "Ynghylch", "description": "appears as tab name in dashboard" @@ -240,7 +244,7 @@ "description": "Label for a checkbox in the options page" }, "developerModeLegend": { - "message": "Enable access to features suitable for technical users.", + "message": "Enables access to features suitable for technical users.", "description": "Short description for a checkbox in the options page" }, "findListsPlaceholder": { @@ -310,5 +314,9 @@ "dnrRulesSummary": { "message": "Enter your own DNR rules below. Do not add content from untrusted sources.", "description": "Short description of the DNR rules editor pane" + }, + "dnrRulesCountInfo": { + "message": "Number of registered rules: {count}", + "description": "Short sentence to report the number of currently registered DNR rules" } } diff --git a/platform/mv3/extension/_locales/da/messages.json b/platform/mv3/extension/_locales/da/messages.json index 556f09f2311dd..63ab8e82e6660 100644 --- a/platform/mv3/extension/_locales/da/messages.json +++ b/platform/mv3/extension/_locales/da/messages.json @@ -19,6 +19,10 @@ "message": "Indstillinger", "description": "appears as tab name in dashboard" }, + "developPageName": { + "message": "Develop", + "description": "appears as tab name in dashboard. Inspired from 'Develop' menu in Safari, see " + }, "aboutPageName": { "message": "Om", "description": "appears as tab name in dashboard" @@ -310,5 +314,9 @@ "dnrRulesSummary": { "message": "Angiv egne DNR-regler nedenfor. Tilføj ikke indhold fra ikke-betroede kilder.", "description": "Short description of the DNR rules editor pane" + }, + "dnrRulesCountInfo": { + "message": "Number of registered rules: {count}", + "description": "Short sentence to report the number of currently registered DNR rules" } } diff --git a/platform/mv3/extension/_locales/de/messages.json b/platform/mv3/extension/_locales/de/messages.json index 3c1f8aaab341a..c2ca03e3a1fd3 100644 --- a/platform/mv3/extension/_locales/de/messages.json +++ b/platform/mv3/extension/_locales/de/messages.json @@ -19,6 +19,10 @@ "message": "Einstellungen", "description": "appears as tab name in dashboard" }, + "developPageName": { + "message": "Develop", + "description": "appears as tab name in dashboard. Inspired from 'Develop' menu in Safari, see " + }, "aboutPageName": { "message": "Über", "description": "appears as tab name in dashboard" @@ -310,5 +314,9 @@ "dnrRulesSummary": { "message": "Eigene DNR-Regeln können unten eingetragen werden. Verwenden Sie keine Regeln aus unseriösen Quellen.", "description": "Short description of the DNR rules editor pane" + }, + "dnrRulesCountInfo": { + "message": "Number of registered rules: {count}", + "description": "Short sentence to report the number of currently registered DNR rules" } } diff --git a/platform/mv3/extension/_locales/el/messages.json b/platform/mv3/extension/_locales/el/messages.json index b9838cd38b037..e23bbea8198c8 100644 --- a/platform/mv3/extension/_locales/el/messages.json +++ b/platform/mv3/extension/_locales/el/messages.json @@ -19,6 +19,10 @@ "message": "Ρυθμίσεις", "description": "appears as tab name in dashboard" }, + "developPageName": { + "message": "Develop", + "description": "appears as tab name in dashboard. Inspired from 'Develop' menu in Safari, see " + }, "aboutPageName": { "message": "Σχετικά", "description": "appears as tab name in dashboard" @@ -310,5 +314,9 @@ "dnrRulesSummary": { "message": "Εισάγετε παρακάτω τους δικούς σας κανόνες DNR. Μην προσθέσετε περιεχόμενο από πηγές που δεν είναι αξιόπιστες.", "description": "Short description of the DNR rules editor pane" + }, + "dnrRulesCountInfo": { + "message": "Number of registered rules: {count}", + "description": "Short sentence to report the number of currently registered DNR rules" } } diff --git a/platform/mv3/extension/_locales/en_GB/messages.json b/platform/mv3/extension/_locales/en_GB/messages.json index f61236cc6d9f4..0fb1647494c51 100644 --- a/platform/mv3/extension/_locales/en_GB/messages.json +++ b/platform/mv3/extension/_locales/en_GB/messages.json @@ -19,6 +19,10 @@ "message": "Settings", "description": "appears as tab name in dashboard" }, + "developPageName": { + "message": "Develop", + "description": "appears as tab name in dashboard. Inspired from 'Develop' menu in Safari, see " + }, "aboutPageName": { "message": "About", "description": "appears as tab name in dashboard" @@ -310,5 +314,9 @@ "dnrRulesSummary": { "message": "Enter your own DNR rules below. Do not add content from untrusted sources.", "description": "Short description of the DNR rules editor pane" + }, + "dnrRulesCountInfo": { + "message": "Number of registered rules: {count}", + "description": "Short sentence to report the number of currently registered DNR rules" } } diff --git a/platform/mv3/extension/_locales/eo/messages.json b/platform/mv3/extension/_locales/eo/messages.json index acc785f1d8fb2..56d1d3006cb2d 100644 --- a/platform/mv3/extension/_locales/eo/messages.json +++ b/platform/mv3/extension/_locales/eo/messages.json @@ -19,6 +19,10 @@ "message": "Agordoj", "description": "appears as tab name in dashboard" }, + "developPageName": { + "message": "Develop", + "description": "appears as tab name in dashboard. Inspired from 'Develop' menu in Safari, see " + }, "aboutPageName": { "message": "Pri", "description": "appears as tab name in dashboard" @@ -240,7 +244,7 @@ "description": "Label for a checkbox in the options page" }, "developerModeLegend": { - "message": "Enable access to features suitable for technical users.", + "message": "Enables access to features suitable for technical users.", "description": "Short description for a checkbox in the options page" }, "findListsPlaceholder": { @@ -310,5 +314,9 @@ "dnrRulesSummary": { "message": "Enter your own DNR rules below. Do not add content from untrusted sources.", "description": "Short description of the DNR rules editor pane" + }, + "dnrRulesCountInfo": { + "message": "Number of registered rules: {count}", + "description": "Short sentence to report the number of currently registered DNR rules" } } diff --git a/platform/mv3/extension/_locales/es/messages.json b/platform/mv3/extension/_locales/es/messages.json index b990345af142f..cd0175a56d07e 100644 --- a/platform/mv3/extension/_locales/es/messages.json +++ b/platform/mv3/extension/_locales/es/messages.json @@ -19,6 +19,10 @@ "message": "Configuración", "description": "appears as tab name in dashboard" }, + "developPageName": { + "message": "Develop", + "description": "appears as tab name in dashboard. Inspired from 'Develop' menu in Safari, see " + }, "aboutPageName": { "message": "Acerca de", "description": "appears as tab name in dashboard" @@ -310,5 +314,9 @@ "dnrRulesSummary": { "message": "Ingresa tus propias reglas DNR abajo. No agregues contenido desde fuentes no confiables.", "description": "Short description of the DNR rules editor pane" + }, + "dnrRulesCountInfo": { + "message": "Number of registered rules: {count}", + "description": "Short sentence to report the number of currently registered DNR rules" } } diff --git a/platform/mv3/extension/_locales/et/messages.json b/platform/mv3/extension/_locales/et/messages.json index e0eba83e234aa..5e97968f71e7f 100644 --- a/platform/mv3/extension/_locales/et/messages.json +++ b/platform/mv3/extension/_locales/et/messages.json @@ -19,6 +19,10 @@ "message": "Sätted", "description": "appears as tab name in dashboard" }, + "developPageName": { + "message": "Develop", + "description": "appears as tab name in dashboard. Inspired from 'Develop' menu in Safari, see " + }, "aboutPageName": { "message": "Teave", "description": "appears as tab name in dashboard" @@ -310,5 +314,9 @@ "dnrRulesSummary": { "message": "Enda aadressiresolverite avastamise reegli sisestamiseks klõpsake siia. Ärge lisage kahtlaste allikate sisu.", "description": "Short description of the DNR rules editor pane" + }, + "dnrRulesCountInfo": { + "message": "Number of registered rules: {count}", + "description": "Short sentence to report the number of currently registered DNR rules" } } diff --git a/platform/mv3/extension/_locales/eu/messages.json b/platform/mv3/extension/_locales/eu/messages.json index 22594ceb43f3c..aba7d7f69dbc7 100644 --- a/platform/mv3/extension/_locales/eu/messages.json +++ b/platform/mv3/extension/_locales/eu/messages.json @@ -19,6 +19,10 @@ "message": "Ezarpenak", "description": "appears as tab name in dashboard" }, + "developPageName": { + "message": "Develop", + "description": "appears as tab name in dashboard. Inspired from 'Develop' menu in Safari, see " + }, "aboutPageName": { "message": "Honi buruz", "description": "appears as tab name in dashboard" @@ -240,7 +244,7 @@ "description": "Label for a checkbox in the options page" }, "developerModeLegend": { - "message": "Enable access to features suitable for technical users.", + "message": "Enables access to features suitable for technical users.", "description": "Short description for a checkbox in the options page" }, "findListsPlaceholder": { @@ -310,5 +314,9 @@ "dnrRulesSummary": { "message": "Enter your own DNR rules below. Do not add content from untrusted sources.", "description": "Short description of the DNR rules editor pane" + }, + "dnrRulesCountInfo": { + "message": "Number of registered rules: {count}", + "description": "Short sentence to report the number of currently registered DNR rules" } } diff --git a/platform/mv3/extension/_locales/fa/messages.json b/platform/mv3/extension/_locales/fa/messages.json index 743cc736ad029..10f156aa0f045 100644 --- a/platform/mv3/extension/_locales/fa/messages.json +++ b/platform/mv3/extension/_locales/fa/messages.json @@ -19,6 +19,10 @@ "message": "تنظیمات", "description": "appears as tab name in dashboard" }, + "developPageName": { + "message": "Develop", + "description": "appears as tab name in dashboard. Inspired from 'Develop' menu in Safari, see " + }, "aboutPageName": { "message": "درباره", "description": "appears as tab name in dashboard" @@ -240,7 +244,7 @@ "description": "Label for a checkbox in the options page" }, "developerModeLegend": { - "message": "Enable access to features suitable for technical users.", + "message": "Enables access to features suitable for technical users.", "description": "Short description for a checkbox in the options page" }, "findListsPlaceholder": { @@ -310,5 +314,9 @@ "dnrRulesSummary": { "message": "Enter your own DNR rules below. Do not add content from untrusted sources.", "description": "Short description of the DNR rules editor pane" + }, + "dnrRulesCountInfo": { + "message": "Number of registered rules: {count}", + "description": "Short sentence to report the number of currently registered DNR rules" } } diff --git a/platform/mv3/extension/_locales/fi/messages.json b/platform/mv3/extension/_locales/fi/messages.json index 4e7836a408042..742511be07ad9 100644 --- a/platform/mv3/extension/_locales/fi/messages.json +++ b/platform/mv3/extension/_locales/fi/messages.json @@ -19,6 +19,10 @@ "message": "Asetukset", "description": "appears as tab name in dashboard" }, + "developPageName": { + "message": "Develop", + "description": "appears as tab name in dashboard. Inspired from 'Develop' menu in Safari, see " + }, "aboutPageName": { "message": "Tietoja", "description": "appears as tab name in dashboard" @@ -310,5 +314,9 @@ "dnrRulesSummary": { "message": "Syötä omat DNS-sääntösi alle. Älä lisää sisältöä lähteistä, joihin et luota.", "description": "Short description of the DNR rules editor pane" + }, + "dnrRulesCountInfo": { + "message": "Number of registered rules: {count}", + "description": "Short sentence to report the number of currently registered DNR rules" } } diff --git a/platform/mv3/extension/_locales/fil/messages.json b/platform/mv3/extension/_locales/fil/messages.json index a9ed6840d1b5b..4be70d504de97 100644 --- a/platform/mv3/extension/_locales/fil/messages.json +++ b/platform/mv3/extension/_locales/fil/messages.json @@ -19,6 +19,10 @@ "message": "Mga Setting", "description": "appears as tab name in dashboard" }, + "developPageName": { + "message": "Develop", + "description": "appears as tab name in dashboard. Inspired from 'Develop' menu in Safari, see " + }, "aboutPageName": { "message": "Tungkol", "description": "appears as tab name in dashboard" @@ -240,7 +244,7 @@ "description": "Label for a checkbox in the options page" }, "developerModeLegend": { - "message": "Enable access to features suitable for technical users.", + "message": "Enables access to features suitable for technical users.", "description": "Short description for a checkbox in the options page" }, "findListsPlaceholder": { @@ -310,5 +314,9 @@ "dnrRulesSummary": { "message": "Enter your own DNR rules below. Do not add content from untrusted sources.", "description": "Short description of the DNR rules editor pane" + }, + "dnrRulesCountInfo": { + "message": "Number of registered rules: {count}", + "description": "Short sentence to report the number of currently registered DNR rules" } } diff --git a/platform/mv3/extension/_locales/fr/messages.json b/platform/mv3/extension/_locales/fr/messages.json index fd73fe61043d4..926f06b763515 100644 --- a/platform/mv3/extension/_locales/fr/messages.json +++ b/platform/mv3/extension/_locales/fr/messages.json @@ -19,6 +19,10 @@ "message": "Paramètres", "description": "appears as tab name in dashboard" }, + "developPageName": { + "message": "Develop", + "description": "appears as tab name in dashboard. Inspired from 'Develop' menu in Safari, see " + }, "aboutPageName": { "message": "À propos", "description": "appears as tab name in dashboard" @@ -310,5 +314,9 @@ "dnrRulesSummary": { "message": "Entrez vos propres règles DNR (Declarative Net Request, ou requête réseau déclarative) ci-dessous. N'ajoutez pas de contenu provenant de sources non-fiables.", "description": "Short description of the DNR rules editor pane" + }, + "dnrRulesCountInfo": { + "message": "Number of registered rules: {count}", + "description": "Short sentence to report the number of currently registered DNR rules" } } diff --git a/platform/mv3/extension/_locales/fy/messages.json b/platform/mv3/extension/_locales/fy/messages.json index 5d8d96ef0504e..fbcee7d6364c8 100644 --- a/platform/mv3/extension/_locales/fy/messages.json +++ b/platform/mv3/extension/_locales/fy/messages.json @@ -19,6 +19,10 @@ "message": "Ynstellingen", "description": "appears as tab name in dashboard" }, + "developPageName": { + "message": "Develop", + "description": "appears as tab name in dashboard. Inspired from 'Develop' menu in Safari, see " + }, "aboutPageName": { "message": "Oer", "description": "appears as tab name in dashboard" @@ -310,5 +314,9 @@ "dnrRulesSummary": { "message": "Fier hjirûnder jo eigen DNR-regels yn. Foegje gjin ynhâld fan net-fertroude boarnen ta.", "description": "Short description of the DNR rules editor pane" + }, + "dnrRulesCountInfo": { + "message": "Number of registered rules: {count}", + "description": "Short sentence to report the number of currently registered DNR rules" } } diff --git a/platform/mv3/extension/_locales/gl/messages.json b/platform/mv3/extension/_locales/gl/messages.json index 3f12d28e6c5f3..d4238032787d3 100644 --- a/platform/mv3/extension/_locales/gl/messages.json +++ b/platform/mv3/extension/_locales/gl/messages.json @@ -19,6 +19,10 @@ "message": "Axustes", "description": "appears as tab name in dashboard" }, + "developPageName": { + "message": "Develop", + "description": "appears as tab name in dashboard. Inspired from 'Develop' menu in Safari, see " + }, "aboutPageName": { "message": "Acerca de", "description": "appears as tab name in dashboard" @@ -310,5 +314,9 @@ "dnrRulesSummary": { "message": "Escribe as regras DNR embaixo. Non engadas contido desde orixes non confiables.", "description": "Short description of the DNR rules editor pane" + }, + "dnrRulesCountInfo": { + "message": "Number of registered rules: {count}", + "description": "Short sentence to report the number of currently registered DNR rules" } } diff --git a/platform/mv3/extension/_locales/gu/messages.json b/platform/mv3/extension/_locales/gu/messages.json index 136a025b98095..484529aa58de1 100644 --- a/platform/mv3/extension/_locales/gu/messages.json +++ b/platform/mv3/extension/_locales/gu/messages.json @@ -19,6 +19,10 @@ "message": "Settings", "description": "appears as tab name in dashboard" }, + "developPageName": { + "message": "Develop", + "description": "appears as tab name in dashboard. Inspired from 'Develop' menu in Safari, see " + }, "aboutPageName": { "message": "About", "description": "appears as tab name in dashboard" @@ -240,7 +244,7 @@ "description": "Label for a checkbox in the options page" }, "developerModeLegend": { - "message": "Enable access to features suitable for technical users.", + "message": "Enables access to features suitable for technical users.", "description": "Short description for a checkbox in the options page" }, "findListsPlaceholder": { @@ -310,5 +314,9 @@ "dnrRulesSummary": { "message": "Enter your own DNR rules below. Do not add content from untrusted sources.", "description": "Short description of the DNR rules editor pane" + }, + "dnrRulesCountInfo": { + "message": "Number of registered rules: {count}", + "description": "Short sentence to report the number of currently registered DNR rules" } } diff --git a/platform/mv3/extension/_locales/he/messages.json b/platform/mv3/extension/_locales/he/messages.json index e2a0cc55b01cb..dfe42dcdbdc9f 100644 --- a/platform/mv3/extension/_locales/he/messages.json +++ b/platform/mv3/extension/_locales/he/messages.json @@ -19,6 +19,10 @@ "message": "הגדרות", "description": "appears as tab name in dashboard" }, + "developPageName": { + "message": "Develop", + "description": "appears as tab name in dashboard. Inspired from 'Develop' menu in Safari, see " + }, "aboutPageName": { "message": "על אודות", "description": "appears as tab name in dashboard" @@ -310,5 +314,9 @@ "dnrRulesSummary": { "message": "הכניסו את חוקי ה DNR שלכם למטה. אל תכניסו תוכן ממקורות לא בטוחים.", "description": "Short description of the DNR rules editor pane" + }, + "dnrRulesCountInfo": { + "message": "Number of registered rules: {count}", + "description": "Short sentence to report the number of currently registered DNR rules" } } diff --git a/platform/mv3/extension/_locales/hi/messages.json b/platform/mv3/extension/_locales/hi/messages.json index 42fae58002f1e..a1664a6aef2b4 100644 --- a/platform/mv3/extension/_locales/hi/messages.json +++ b/platform/mv3/extension/_locales/hi/messages.json @@ -19,6 +19,10 @@ "message": "सेटिंग्स", "description": "appears as tab name in dashboard" }, + "developPageName": { + "message": "Develop", + "description": "appears as tab name in dashboard. Inspired from 'Develop' menu in Safari, see " + }, "aboutPageName": { "message": "जानकारी", "description": "appears as tab name in dashboard" @@ -240,7 +244,7 @@ "description": "Label for a checkbox in the options page" }, "developerModeLegend": { - "message": "Enable access to features suitable for technical users.", + "message": "Enables access to features suitable for technical users.", "description": "Short description for a checkbox in the options page" }, "findListsPlaceholder": { @@ -310,5 +314,9 @@ "dnrRulesSummary": { "message": "Enter your own DNR rules below. Do not add content from untrusted sources.", "description": "Short description of the DNR rules editor pane" + }, + "dnrRulesCountInfo": { + "message": "Number of registered rules: {count}", + "description": "Short sentence to report the number of currently registered DNR rules" } } diff --git a/platform/mv3/extension/_locales/hr/messages.json b/platform/mv3/extension/_locales/hr/messages.json index f2cd12f9776a0..e3d703f208bc4 100644 --- a/platform/mv3/extension/_locales/hr/messages.json +++ b/platform/mv3/extension/_locales/hr/messages.json @@ -19,6 +19,10 @@ "message": "Postavke", "description": "appears as tab name in dashboard" }, + "developPageName": { + "message": "Develop", + "description": "appears as tab name in dashboard. Inspired from 'Develop' menu in Safari, see " + }, "aboutPageName": { "message": "O aplikaciji", "description": "appears as tab name in dashboard" @@ -310,5 +314,9 @@ "dnrRulesSummary": { "message": "Unesite svoja DNR pravila u nastavku. Nemojte dodavati sadržaj iz nepouzdanih izvora.", "description": "Short description of the DNR rules editor pane" + }, + "dnrRulesCountInfo": { + "message": "Number of registered rules: {count}", + "description": "Short sentence to report the number of currently registered DNR rules" } } diff --git a/platform/mv3/extension/_locales/hu/messages.json b/platform/mv3/extension/_locales/hu/messages.json index d7eee7347cdb5..82a6cc5e691cc 100644 --- a/platform/mv3/extension/_locales/hu/messages.json +++ b/platform/mv3/extension/_locales/hu/messages.json @@ -19,6 +19,10 @@ "message": "Beállítások", "description": "appears as tab name in dashboard" }, + "developPageName": { + "message": "Develop", + "description": "appears as tab name in dashboard. Inspired from 'Develop' menu in Safari, see " + }, "aboutPageName": { "message": "Névjegy", "description": "appears as tab name in dashboard" @@ -240,7 +244,7 @@ "description": "Label for a checkbox in the options page" }, "developerModeLegend": { - "message": "Enable access to features suitable for technical users.", + "message": "Enables access to features suitable for technical users.", "description": "Short description for a checkbox in the options page" }, "findListsPlaceholder": { @@ -310,5 +314,9 @@ "dnrRulesSummary": { "message": "Enter your own DNR rules below. Do not add content from untrusted sources.", "description": "Short description of the DNR rules editor pane" + }, + "dnrRulesCountInfo": { + "message": "Number of registered rules: {count}", + "description": "Short sentence to report the number of currently registered DNR rules" } } diff --git a/platform/mv3/extension/_locales/hy/messages.json b/platform/mv3/extension/_locales/hy/messages.json index 853b020711f1b..f9a45929fffc2 100644 --- a/platform/mv3/extension/_locales/hy/messages.json +++ b/platform/mv3/extension/_locales/hy/messages.json @@ -19,6 +19,10 @@ "message": "Կարգավորումներ", "description": "appears as tab name in dashboard" }, + "developPageName": { + "message": "Develop", + "description": "appears as tab name in dashboard. Inspired from 'Develop' menu in Safari, see " + }, "aboutPageName": { "message": "Ընդլայնման մասին", "description": "appears as tab name in dashboard" @@ -240,7 +244,7 @@ "description": "Label for a checkbox in the options page" }, "developerModeLegend": { - "message": "Enable access to features suitable for technical users.", + "message": "Enables access to features suitable for technical users.", "description": "Short description for a checkbox in the options page" }, "findListsPlaceholder": { @@ -310,5 +314,9 @@ "dnrRulesSummary": { "message": "Enter your own DNR rules below. Do not add content from untrusted sources.", "description": "Short description of the DNR rules editor pane" + }, + "dnrRulesCountInfo": { + "message": "Number of registered rules: {count}", + "description": "Short sentence to report the number of currently registered DNR rules" } } diff --git a/platform/mv3/extension/_locales/id/messages.json b/platform/mv3/extension/_locales/id/messages.json index 9760f0e834002..3c5f42fe73182 100644 --- a/platform/mv3/extension/_locales/id/messages.json +++ b/platform/mv3/extension/_locales/id/messages.json @@ -19,6 +19,10 @@ "message": "Pengaturan", "description": "appears as tab name in dashboard" }, + "developPageName": { + "message": "Develop", + "description": "appears as tab name in dashboard. Inspired from 'Develop' menu in Safari, see " + }, "aboutPageName": { "message": "Tentang", "description": "appears as tab name in dashboard" @@ -240,7 +244,7 @@ "description": "Label for a checkbox in the options page" }, "developerModeLegend": { - "message": "Enable access to features suitable for technical users.", + "message": "Enables access to features suitable for technical users.", "description": "Short description for a checkbox in the options page" }, "findListsPlaceholder": { @@ -310,5 +314,9 @@ "dnrRulesSummary": { "message": "Enter your own DNR rules below. Do not add content from untrusted sources.", "description": "Short description of the DNR rules editor pane" + }, + "dnrRulesCountInfo": { + "message": "Number of registered rules: {count}", + "description": "Short sentence to report the number of currently registered DNR rules" } } diff --git a/platform/mv3/extension/_locales/it/messages.json b/platform/mv3/extension/_locales/it/messages.json index c30cc01a4d954..df26250965130 100644 --- a/platform/mv3/extension/_locales/it/messages.json +++ b/platform/mv3/extension/_locales/it/messages.json @@ -19,6 +19,10 @@ "message": "Impostazioni", "description": "appears as tab name in dashboard" }, + "developPageName": { + "message": "Develop", + "description": "appears as tab name in dashboard. Inspired from 'Develop' menu in Safari, see " + }, "aboutPageName": { "message": "Informazioni", "description": "appears as tab name in dashboard" @@ -310,5 +314,9 @@ "dnrRulesSummary": { "message": "Inserisci le tue regole DNR qui sotto. Non aggiungere contenuti da fonti non attendibili.", "description": "Short description of the DNR rules editor pane" + }, + "dnrRulesCountInfo": { + "message": "Number of registered rules: {count}", + "description": "Short sentence to report the number of currently registered DNR rules" } } diff --git a/platform/mv3/extension/_locales/ja/messages.json b/platform/mv3/extension/_locales/ja/messages.json index 0e00e22a75e59..629b69f510cba 100644 --- a/platform/mv3/extension/_locales/ja/messages.json +++ b/platform/mv3/extension/_locales/ja/messages.json @@ -19,6 +19,10 @@ "message": "設定", "description": "appears as tab name in dashboard" }, + "developPageName": { + "message": "Develop", + "description": "appears as tab name in dashboard. Inspired from 'Develop' menu in Safari, see " + }, "aboutPageName": { "message": "uBO Lite について", "description": "appears as tab name in dashboard" @@ -240,7 +244,7 @@ "description": "Label for a checkbox in the options page" }, "developerModeLegend": { - "message": "Enable access to features suitable for technical users.", + "message": "Enables access to features suitable for technical users.", "description": "Short description for a checkbox in the options page" }, "findListsPlaceholder": { @@ -310,5 +314,9 @@ "dnrRulesSummary": { "message": "Enter your own DNR rules below. Do not add content from untrusted sources.", "description": "Short description of the DNR rules editor pane" + }, + "dnrRulesCountInfo": { + "message": "Number of registered rules: {count}", + "description": "Short sentence to report the number of currently registered DNR rules" } } diff --git a/platform/mv3/extension/_locales/ka/messages.json b/platform/mv3/extension/_locales/ka/messages.json index 59b33f61bd415..eb407fb0e9109 100644 --- a/platform/mv3/extension/_locales/ka/messages.json +++ b/platform/mv3/extension/_locales/ka/messages.json @@ -19,6 +19,10 @@ "message": "პარამეტრები", "description": "appears as tab name in dashboard" }, + "developPageName": { + "message": "Develop", + "description": "appears as tab name in dashboard. Inspired from 'Develop' menu in Safari, see " + }, "aboutPageName": { "message": "შესახებ", "description": "appears as tab name in dashboard" @@ -240,7 +244,7 @@ "description": "Label for a checkbox in the options page" }, "developerModeLegend": { - "message": "Enable access to features suitable for technical users.", + "message": "Enables access to features suitable for technical users.", "description": "Short description for a checkbox in the options page" }, "findListsPlaceholder": { @@ -310,5 +314,9 @@ "dnrRulesSummary": { "message": "Enter your own DNR rules below. Do not add content from untrusted sources.", "description": "Short description of the DNR rules editor pane" + }, + "dnrRulesCountInfo": { + "message": "Number of registered rules: {count}", + "description": "Short sentence to report the number of currently registered DNR rules" } } diff --git a/platform/mv3/extension/_locales/kk/messages.json b/platform/mv3/extension/_locales/kk/messages.json index 136a025b98095..484529aa58de1 100644 --- a/platform/mv3/extension/_locales/kk/messages.json +++ b/platform/mv3/extension/_locales/kk/messages.json @@ -19,6 +19,10 @@ "message": "Settings", "description": "appears as tab name in dashboard" }, + "developPageName": { + "message": "Develop", + "description": "appears as tab name in dashboard. Inspired from 'Develop' menu in Safari, see " + }, "aboutPageName": { "message": "About", "description": "appears as tab name in dashboard" @@ -240,7 +244,7 @@ "description": "Label for a checkbox in the options page" }, "developerModeLegend": { - "message": "Enable access to features suitable for technical users.", + "message": "Enables access to features suitable for technical users.", "description": "Short description for a checkbox in the options page" }, "findListsPlaceholder": { @@ -310,5 +314,9 @@ "dnrRulesSummary": { "message": "Enter your own DNR rules below. Do not add content from untrusted sources.", "description": "Short description of the DNR rules editor pane" + }, + "dnrRulesCountInfo": { + "message": "Number of registered rules: {count}", + "description": "Short sentence to report the number of currently registered DNR rules" } } diff --git a/platform/mv3/extension/_locales/kn/messages.json b/platform/mv3/extension/_locales/kn/messages.json index 357c70f7edb7f..fe69ab48ce819 100644 --- a/platform/mv3/extension/_locales/kn/messages.json +++ b/platform/mv3/extension/_locales/kn/messages.json @@ -19,6 +19,10 @@ "message": "ಸಂಯೋಜನೆಗಳು", "description": "appears as tab name in dashboard" }, + "developPageName": { + "message": "Develop", + "description": "appears as tab name in dashboard. Inspired from 'Develop' menu in Safari, see " + }, "aboutPageName": { "message": "ಬಗ್ಗೆ", "description": "appears as tab name in dashboard" @@ -240,7 +244,7 @@ "description": "Label for a checkbox in the options page" }, "developerModeLegend": { - "message": "Enable access to features suitable for technical users.", + "message": "Enables access to features suitable for technical users.", "description": "Short description for a checkbox in the options page" }, "findListsPlaceholder": { @@ -310,5 +314,9 @@ "dnrRulesSummary": { "message": "Enter your own DNR rules below. Do not add content from untrusted sources.", "description": "Short description of the DNR rules editor pane" + }, + "dnrRulesCountInfo": { + "message": "Number of registered rules: {count}", + "description": "Short sentence to report the number of currently registered DNR rules" } } diff --git a/platform/mv3/extension/_locales/ko/messages.json b/platform/mv3/extension/_locales/ko/messages.json index f18e86be17b09..3af874c101cc2 100644 --- a/platform/mv3/extension/_locales/ko/messages.json +++ b/platform/mv3/extension/_locales/ko/messages.json @@ -19,6 +19,10 @@ "message": "설정", "description": "appears as tab name in dashboard" }, + "developPageName": { + "message": "Develop", + "description": "appears as tab name in dashboard. Inspired from 'Develop' menu in Safari, see " + }, "aboutPageName": { "message": "정보", "description": "appears as tab name in dashboard" @@ -240,7 +244,7 @@ "description": "Label for a checkbox in the options page" }, "developerModeLegend": { - "message": "Enable access to features suitable for technical users.", + "message": "Enables access to features suitable for technical users.", "description": "Short description for a checkbox in the options page" }, "findListsPlaceholder": { @@ -310,5 +314,9 @@ "dnrRulesSummary": { "message": "Enter your own DNR rules below. Do not add content from untrusted sources.", "description": "Short description of the DNR rules editor pane" + }, + "dnrRulesCountInfo": { + "message": "Number of registered rules: {count}", + "description": "Short sentence to report the number of currently registered DNR rules" } } diff --git a/platform/mv3/extension/_locales/lt/messages.json b/platform/mv3/extension/_locales/lt/messages.json index 60955d7cdf7ad..fdca5a9907bff 100644 --- a/platform/mv3/extension/_locales/lt/messages.json +++ b/platform/mv3/extension/_locales/lt/messages.json @@ -19,6 +19,10 @@ "message": "Nustatymai", "description": "appears as tab name in dashboard" }, + "developPageName": { + "message": "Develop", + "description": "appears as tab name in dashboard. Inspired from 'Develop' menu in Safari, see " + }, "aboutPageName": { "message": "Apie", "description": "appears as tab name in dashboard" @@ -240,7 +244,7 @@ "description": "Label for a checkbox in the options page" }, "developerModeLegend": { - "message": "Enable access to features suitable for technical users.", + "message": "Enables access to features suitable for technical users.", "description": "Short description for a checkbox in the options page" }, "findListsPlaceholder": { @@ -310,5 +314,9 @@ "dnrRulesSummary": { "message": "Enter your own DNR rules below. Do not add content from untrusted sources.", "description": "Short description of the DNR rules editor pane" + }, + "dnrRulesCountInfo": { + "message": "Number of registered rules: {count}", + "description": "Short sentence to report the number of currently registered DNR rules" } } diff --git a/platform/mv3/extension/_locales/lv/messages.json b/platform/mv3/extension/_locales/lv/messages.json index 2a84699f74cff..bf857a5b21206 100644 --- a/platform/mv3/extension/_locales/lv/messages.json +++ b/platform/mv3/extension/_locales/lv/messages.json @@ -19,6 +19,10 @@ "message": "Iestatījumi", "description": "appears as tab name in dashboard" }, + "developPageName": { + "message": "Develop", + "description": "appears as tab name in dashboard. Inspired from 'Develop' menu in Safari, see " + }, "aboutPageName": { "message": "Par", "description": "appears as tab name in dashboard" @@ -310,5 +314,9 @@ "dnrRulesSummary": { "message": "Zemāk ir ievadi savas DNR kārtulas! Nepievieno saturu no neuzticamiem avotiem!", "description": "Short description of the DNR rules editor pane" + }, + "dnrRulesCountInfo": { + "message": "Number of registered rules: {count}", + "description": "Short sentence to report the number of currently registered DNR rules" } } diff --git a/platform/mv3/extension/_locales/mk/messages.json b/platform/mv3/extension/_locales/mk/messages.json index 431dba89a64c9..79a1a924b708e 100644 --- a/platform/mv3/extension/_locales/mk/messages.json +++ b/platform/mv3/extension/_locales/mk/messages.json @@ -19,6 +19,10 @@ "message": "Прилагодби", "description": "appears as tab name in dashboard" }, + "developPageName": { + "message": "Develop", + "description": "appears as tab name in dashboard. Inspired from 'Develop' menu in Safari, see " + }, "aboutPageName": { "message": "Информации за...", "description": "appears as tab name in dashboard" @@ -240,7 +244,7 @@ "description": "Label for a checkbox in the options page" }, "developerModeLegend": { - "message": "Enable access to features suitable for technical users.", + "message": "Enables access to features suitable for technical users.", "description": "Short description for a checkbox in the options page" }, "findListsPlaceholder": { @@ -310,5 +314,9 @@ "dnrRulesSummary": { "message": "Enter your own DNR rules below. Do not add content from untrusted sources.", "description": "Short description of the DNR rules editor pane" + }, + "dnrRulesCountInfo": { + "message": "Number of registered rules: {count}", + "description": "Short sentence to report the number of currently registered DNR rules" } } diff --git a/platform/mv3/extension/_locales/ml/messages.json b/platform/mv3/extension/_locales/ml/messages.json index 4e6a284b44cc7..e266c865d26ae 100644 --- a/platform/mv3/extension/_locales/ml/messages.json +++ b/platform/mv3/extension/_locales/ml/messages.json @@ -19,6 +19,10 @@ "message": "ക്രമീകരണങ്ങൾ", "description": "appears as tab name in dashboard" }, + "developPageName": { + "message": "Develop", + "description": "appears as tab name in dashboard. Inspired from 'Develop' menu in Safari, see " + }, "aboutPageName": { "message": "കുറിച്ച്", "description": "appears as tab name in dashboard" @@ -240,7 +244,7 @@ "description": "Label for a checkbox in the options page" }, "developerModeLegend": { - "message": "Enable access to features suitable for technical users.", + "message": "Enables access to features suitable for technical users.", "description": "Short description for a checkbox in the options page" }, "findListsPlaceholder": { @@ -310,5 +314,9 @@ "dnrRulesSummary": { "message": "Enter your own DNR rules below. Do not add content from untrusted sources.", "description": "Short description of the DNR rules editor pane" + }, + "dnrRulesCountInfo": { + "message": "Number of registered rules: {count}", + "description": "Short sentence to report the number of currently registered DNR rules" } } diff --git a/platform/mv3/extension/_locales/mr/messages.json b/platform/mv3/extension/_locales/mr/messages.json index 136a025b98095..484529aa58de1 100644 --- a/platform/mv3/extension/_locales/mr/messages.json +++ b/platform/mv3/extension/_locales/mr/messages.json @@ -19,6 +19,10 @@ "message": "Settings", "description": "appears as tab name in dashboard" }, + "developPageName": { + "message": "Develop", + "description": "appears as tab name in dashboard. Inspired from 'Develop' menu in Safari, see " + }, "aboutPageName": { "message": "About", "description": "appears as tab name in dashboard" @@ -240,7 +244,7 @@ "description": "Label for a checkbox in the options page" }, "developerModeLegend": { - "message": "Enable access to features suitable for technical users.", + "message": "Enables access to features suitable for technical users.", "description": "Short description for a checkbox in the options page" }, "findListsPlaceholder": { @@ -310,5 +314,9 @@ "dnrRulesSummary": { "message": "Enter your own DNR rules below. Do not add content from untrusted sources.", "description": "Short description of the DNR rules editor pane" + }, + "dnrRulesCountInfo": { + "message": "Number of registered rules: {count}", + "description": "Short sentence to report the number of currently registered DNR rules" } } diff --git a/platform/mv3/extension/_locales/ms/messages.json b/platform/mv3/extension/_locales/ms/messages.json index a0c5b761b95f7..48bd33ff41fb8 100644 --- a/platform/mv3/extension/_locales/ms/messages.json +++ b/platform/mv3/extension/_locales/ms/messages.json @@ -19,6 +19,10 @@ "message": "Tetapan", "description": "appears as tab name in dashboard" }, + "developPageName": { + "message": "Develop", + "description": "appears as tab name in dashboard. Inspired from 'Develop' menu in Safari, see " + }, "aboutPageName": { "message": "Mengenai", "description": "appears as tab name in dashboard" @@ -240,7 +244,7 @@ "description": "Label for a checkbox in the options page" }, "developerModeLegend": { - "message": "Enable access to features suitable for technical users.", + "message": "Enables access to features suitable for technical users.", "description": "Short description for a checkbox in the options page" }, "findListsPlaceholder": { @@ -310,5 +314,9 @@ "dnrRulesSummary": { "message": "Enter your own DNR rules below. Do not add content from untrusted sources.", "description": "Short description of the DNR rules editor pane" + }, + "dnrRulesCountInfo": { + "message": "Number of registered rules: {count}", + "description": "Short sentence to report the number of currently registered DNR rules" } } diff --git a/platform/mv3/extension/_locales/nb/messages.json b/platform/mv3/extension/_locales/nb/messages.json index 25d28af42cc40..2100015b88991 100644 --- a/platform/mv3/extension/_locales/nb/messages.json +++ b/platform/mv3/extension/_locales/nb/messages.json @@ -19,6 +19,10 @@ "message": "Innstillinger", "description": "appears as tab name in dashboard" }, + "developPageName": { + "message": "Develop", + "description": "appears as tab name in dashboard. Inspired from 'Develop' menu in Safari, see " + }, "aboutPageName": { "message": "Om", "description": "appears as tab name in dashboard" @@ -310,5 +314,9 @@ "dnrRulesSummary": { "message": "Legg til dine egne DNR-regler under. Ikke legg til innhold fra usikre kilder.", "description": "Short description of the DNR rules editor pane" + }, + "dnrRulesCountInfo": { + "message": "Number of registered rules: {count}", + "description": "Short sentence to report the number of currently registered DNR rules" } } diff --git a/platform/mv3/extension/_locales/nl/messages.json b/platform/mv3/extension/_locales/nl/messages.json index a4bb05b72d741..fe07adcb28691 100644 --- a/platform/mv3/extension/_locales/nl/messages.json +++ b/platform/mv3/extension/_locales/nl/messages.json @@ -19,6 +19,10 @@ "message": "Instellingen", "description": "appears as tab name in dashboard" }, + "developPageName": { + "message": "Develop", + "description": "appears as tab name in dashboard. Inspired from 'Develop' menu in Safari, see " + }, "aboutPageName": { "message": "Over", "description": "appears as tab name in dashboard" @@ -310,5 +314,9 @@ "dnrRulesSummary": { "message": "Voer hieronder uw eigen DNR-regels in. Voeg geen inhoud van niet-vertrouwde bronnen toe.", "description": "Short description of the DNR rules editor pane" + }, + "dnrRulesCountInfo": { + "message": "Number of registered rules: {count}", + "description": "Short sentence to report the number of currently registered DNR rules" } } diff --git a/platform/mv3/extension/_locales/oc/messages.json b/platform/mv3/extension/_locales/oc/messages.json index 136a025b98095..484529aa58de1 100644 --- a/platform/mv3/extension/_locales/oc/messages.json +++ b/platform/mv3/extension/_locales/oc/messages.json @@ -19,6 +19,10 @@ "message": "Settings", "description": "appears as tab name in dashboard" }, + "developPageName": { + "message": "Develop", + "description": "appears as tab name in dashboard. Inspired from 'Develop' menu in Safari, see " + }, "aboutPageName": { "message": "About", "description": "appears as tab name in dashboard" @@ -240,7 +244,7 @@ "description": "Label for a checkbox in the options page" }, "developerModeLegend": { - "message": "Enable access to features suitable for technical users.", + "message": "Enables access to features suitable for technical users.", "description": "Short description for a checkbox in the options page" }, "findListsPlaceholder": { @@ -310,5 +314,9 @@ "dnrRulesSummary": { "message": "Enter your own DNR rules below. Do not add content from untrusted sources.", "description": "Short description of the DNR rules editor pane" + }, + "dnrRulesCountInfo": { + "message": "Number of registered rules: {count}", + "description": "Short sentence to report the number of currently registered DNR rules" } } diff --git a/platform/mv3/extension/_locales/pa/messages.json b/platform/mv3/extension/_locales/pa/messages.json index 035a82fc7f6fe..ce043fb5cbafc 100644 --- a/platform/mv3/extension/_locales/pa/messages.json +++ b/platform/mv3/extension/_locales/pa/messages.json @@ -19,6 +19,10 @@ "message": "ਸੈਟਿੰਗਾਂ", "description": "appears as tab name in dashboard" }, + "developPageName": { + "message": "Develop", + "description": "appears as tab name in dashboard. Inspired from 'Develop' menu in Safari, see " + }, "aboutPageName": { "message": "ਇਸ ਬਾਰੇ", "description": "appears as tab name in dashboard" @@ -240,7 +244,7 @@ "description": "Label for a checkbox in the options page" }, "developerModeLegend": { - "message": "Enable access to features suitable for technical users.", + "message": "Enables access to features suitable for technical users.", "description": "Short description for a checkbox in the options page" }, "findListsPlaceholder": { @@ -310,5 +314,9 @@ "dnrRulesSummary": { "message": "Enter your own DNR rules below. Do not add content from untrusted sources.", "description": "Short description of the DNR rules editor pane" + }, + "dnrRulesCountInfo": { + "message": "Number of registered rules: {count}", + "description": "Short sentence to report the number of currently registered DNR rules" } } diff --git a/platform/mv3/extension/_locales/pl/messages.json b/platform/mv3/extension/_locales/pl/messages.json index e39d4d6501ae9..150a1a47d39d3 100644 --- a/platform/mv3/extension/_locales/pl/messages.json +++ b/platform/mv3/extension/_locales/pl/messages.json @@ -19,6 +19,10 @@ "message": "Ustawienia", "description": "appears as tab name in dashboard" }, + "developPageName": { + "message": "Develop", + "description": "appears as tab name in dashboard. Inspired from 'Develop' menu in Safari, see " + }, "aboutPageName": { "message": "O rozszerzeniu", "description": "appears as tab name in dashboard" @@ -310,5 +314,9 @@ "dnrRulesSummary": { "message": "Wprowadź poniżej własne zasady DNR. Nie dodawaj treści z niezaufanych źródeł.", "description": "Short description of the DNR rules editor pane" + }, + "dnrRulesCountInfo": { + "message": "Number of registered rules: {count}", + "description": "Short sentence to report the number of currently registered DNR rules" } } diff --git a/platform/mv3/extension/_locales/pt_BR/messages.json b/platform/mv3/extension/_locales/pt_BR/messages.json index a3b43ef3e5828..ea305ce1993c4 100644 --- a/platform/mv3/extension/_locales/pt_BR/messages.json +++ b/platform/mv3/extension/_locales/pt_BR/messages.json @@ -19,6 +19,10 @@ "message": "Configurações", "description": "appears as tab name in dashboard" }, + "developPageName": { + "message": "Develop", + "description": "appears as tab name in dashboard. Inspired from 'Develop' menu in Safari, see " + }, "aboutPageName": { "message": "Sobre", "description": "appears as tab name in dashboard" @@ -310,5 +314,9 @@ "dnrRulesSummary": { "message": "Insira suas próprias regras DNR abaixo. Não adicione conteúdo de fontes não confiáveis.", "description": "Short description of the DNR rules editor pane" + }, + "dnrRulesCountInfo": { + "message": "Number of registered rules: {count}", + "description": "Short sentence to report the number of currently registered DNR rules" } } diff --git a/platform/mv3/extension/_locales/pt_PT/messages.json b/platform/mv3/extension/_locales/pt_PT/messages.json index a3cadf1958c11..e5006ce8a9a48 100644 --- a/platform/mv3/extension/_locales/pt_PT/messages.json +++ b/platform/mv3/extension/_locales/pt_PT/messages.json @@ -19,6 +19,10 @@ "message": "Definições", "description": "appears as tab name in dashboard" }, + "developPageName": { + "message": "Develop", + "description": "appears as tab name in dashboard. Inspired from 'Develop' menu in Safari, see " + }, "aboutPageName": { "message": "Acerca", "description": "appears as tab name in dashboard" @@ -240,7 +244,7 @@ "description": "Label for a checkbox in the options page" }, "developerModeLegend": { - "message": "Enable access to features suitable for technical users.", + "message": "Enables access to features suitable for technical users.", "description": "Short description for a checkbox in the options page" }, "findListsPlaceholder": { @@ -310,5 +314,9 @@ "dnrRulesSummary": { "message": "Enter your own DNR rules below. Do not add content from untrusted sources.", "description": "Short description of the DNR rules editor pane" + }, + "dnrRulesCountInfo": { + "message": "Number of registered rules: {count}", + "description": "Short sentence to report the number of currently registered DNR rules" } } diff --git a/platform/mv3/extension/_locales/ro/messages.json b/platform/mv3/extension/_locales/ro/messages.json index ce522a2bd10e7..4da91a9e5ea70 100644 --- a/platform/mv3/extension/_locales/ro/messages.json +++ b/platform/mv3/extension/_locales/ro/messages.json @@ -19,6 +19,10 @@ "message": "Opțiuni", "description": "appears as tab name in dashboard" }, + "developPageName": { + "message": "Develop", + "description": "appears as tab name in dashboard. Inspired from 'Develop' menu in Safari, see " + }, "aboutPageName": { "message": "Despre", "description": "appears as tab name in dashboard" @@ -240,7 +244,7 @@ "description": "Label for a checkbox in the options page" }, "developerModeLegend": { - "message": "Enable access to features suitable for technical users.", + "message": "Enables access to features suitable for technical users.", "description": "Short description for a checkbox in the options page" }, "findListsPlaceholder": { @@ -310,5 +314,9 @@ "dnrRulesSummary": { "message": "Enter your own DNR rules below. Do not add content from untrusted sources.", "description": "Short description of the DNR rules editor pane" + }, + "dnrRulesCountInfo": { + "message": "Number of registered rules: {count}", + "description": "Short sentence to report the number of currently registered DNR rules" } } diff --git a/platform/mv3/extension/_locales/ru/messages.json b/platform/mv3/extension/_locales/ru/messages.json index 6cafaf01bcc85..9a9f6a15d66ab 100644 --- a/platform/mv3/extension/_locales/ru/messages.json +++ b/platform/mv3/extension/_locales/ru/messages.json @@ -19,6 +19,10 @@ "message": "Настройки", "description": "appears as tab name in dashboard" }, + "developPageName": { + "message": "Develop", + "description": "appears as tab name in dashboard. Inspired from 'Develop' menu in Safari, see " + }, "aboutPageName": { "message": "О расширении", "description": "appears as tab name in dashboard" @@ -310,5 +314,9 @@ "dnrRulesSummary": { "message": "Введите свои правила в формате DNR ниже. Не добавляйте содержимое из ненадёжных источников.", "description": "Short description of the DNR rules editor pane" + }, + "dnrRulesCountInfo": { + "message": "Number of registered rules: {count}", + "description": "Short sentence to report the number of currently registered DNR rules" } } diff --git a/platform/mv3/extension/_locales/si/messages.json b/platform/mv3/extension/_locales/si/messages.json index 5d6467aa9cfe8..30d5306378852 100644 --- a/platform/mv3/extension/_locales/si/messages.json +++ b/platform/mv3/extension/_locales/si/messages.json @@ -19,6 +19,10 @@ "message": "සැකසුම්", "description": "appears as tab name in dashboard" }, + "developPageName": { + "message": "Develop", + "description": "appears as tab name in dashboard. Inspired from 'Develop' menu in Safari, see " + }, "aboutPageName": { "message": "පිළිබඳ", "description": "appears as tab name in dashboard" @@ -240,7 +244,7 @@ "description": "Label for a checkbox in the options page" }, "developerModeLegend": { - "message": "Enable access to features suitable for technical users.", + "message": "Enables access to features suitable for technical users.", "description": "Short description for a checkbox in the options page" }, "findListsPlaceholder": { @@ -310,5 +314,9 @@ "dnrRulesSummary": { "message": "Enter your own DNR rules below. Do not add content from untrusted sources.", "description": "Short description of the DNR rules editor pane" + }, + "dnrRulesCountInfo": { + "message": "Number of registered rules: {count}", + "description": "Short sentence to report the number of currently registered DNR rules" } } diff --git a/platform/mv3/extension/_locales/sk/messages.json b/platform/mv3/extension/_locales/sk/messages.json index 5e8db88b075de..74a3243d57a41 100644 --- a/platform/mv3/extension/_locales/sk/messages.json +++ b/platform/mv3/extension/_locales/sk/messages.json @@ -19,6 +19,10 @@ "message": "Nastavenia", "description": "appears as tab name in dashboard" }, + "developPageName": { + "message": "Develop", + "description": "appears as tab name in dashboard. Inspired from 'Develop' menu in Safari, see " + }, "aboutPageName": { "message": "O doplnku", "description": "appears as tab name in dashboard" @@ -310,5 +314,9 @@ "dnrRulesSummary": { "message": "Zadajte svoje vlastné pravidlá DNR. Nepridávajte obsah z nedôveryhodných zdrojov.", "description": "Short description of the DNR rules editor pane" + }, + "dnrRulesCountInfo": { + "message": "Number of registered rules: {count}", + "description": "Short sentence to report the number of currently registered DNR rules" } } diff --git a/platform/mv3/extension/_locales/sl/messages.json b/platform/mv3/extension/_locales/sl/messages.json index 136a025b98095..484529aa58de1 100644 --- a/platform/mv3/extension/_locales/sl/messages.json +++ b/platform/mv3/extension/_locales/sl/messages.json @@ -19,6 +19,10 @@ "message": "Settings", "description": "appears as tab name in dashboard" }, + "developPageName": { + "message": "Develop", + "description": "appears as tab name in dashboard. Inspired from 'Develop' menu in Safari, see " + }, "aboutPageName": { "message": "About", "description": "appears as tab name in dashboard" @@ -240,7 +244,7 @@ "description": "Label for a checkbox in the options page" }, "developerModeLegend": { - "message": "Enable access to features suitable for technical users.", + "message": "Enables access to features suitable for technical users.", "description": "Short description for a checkbox in the options page" }, "findListsPlaceholder": { @@ -310,5 +314,9 @@ "dnrRulesSummary": { "message": "Enter your own DNR rules below. Do not add content from untrusted sources.", "description": "Short description of the DNR rules editor pane" + }, + "dnrRulesCountInfo": { + "message": "Number of registered rules: {count}", + "description": "Short sentence to report the number of currently registered DNR rules" } } diff --git a/platform/mv3/extension/_locales/so/messages.json b/platform/mv3/extension/_locales/so/messages.json index 136a025b98095..484529aa58de1 100644 --- a/platform/mv3/extension/_locales/so/messages.json +++ b/platform/mv3/extension/_locales/so/messages.json @@ -19,6 +19,10 @@ "message": "Settings", "description": "appears as tab name in dashboard" }, + "developPageName": { + "message": "Develop", + "description": "appears as tab name in dashboard. Inspired from 'Develop' menu in Safari, see " + }, "aboutPageName": { "message": "About", "description": "appears as tab name in dashboard" @@ -240,7 +244,7 @@ "description": "Label for a checkbox in the options page" }, "developerModeLegend": { - "message": "Enable access to features suitable for technical users.", + "message": "Enables access to features suitable for technical users.", "description": "Short description for a checkbox in the options page" }, "findListsPlaceholder": { @@ -310,5 +314,9 @@ "dnrRulesSummary": { "message": "Enter your own DNR rules below. Do not add content from untrusted sources.", "description": "Short description of the DNR rules editor pane" + }, + "dnrRulesCountInfo": { + "message": "Number of registered rules: {count}", + "description": "Short sentence to report the number of currently registered DNR rules" } } diff --git a/platform/mv3/extension/_locales/sq/messages.json b/platform/mv3/extension/_locales/sq/messages.json index 97675a6b69f27..3a953418c93f7 100644 --- a/platform/mv3/extension/_locales/sq/messages.json +++ b/platform/mv3/extension/_locales/sq/messages.json @@ -19,6 +19,10 @@ "message": "Parametrat", "description": "appears as tab name in dashboard" }, + "developPageName": { + "message": "Develop", + "description": "appears as tab name in dashboard. Inspired from 'Develop' menu in Safari, see " + }, "aboutPageName": { "message": "Info", "description": "appears as tab name in dashboard" @@ -240,7 +244,7 @@ "description": "Label for a checkbox in the options page" }, "developerModeLegend": { - "message": "Enable access to features suitable for technical users.", + "message": "Enables access to features suitable for technical users.", "description": "Short description for a checkbox in the options page" }, "findListsPlaceholder": { @@ -310,5 +314,9 @@ "dnrRulesSummary": { "message": "Enter your own DNR rules below. Do not add content from untrusted sources.", "description": "Short description of the DNR rules editor pane" + }, + "dnrRulesCountInfo": { + "message": "Number of registered rules: {count}", + "description": "Short sentence to report the number of currently registered DNR rules" } } diff --git a/platform/mv3/extension/_locales/sr/messages.json b/platform/mv3/extension/_locales/sr/messages.json index 2ba9996532391..7d60f2faed66c 100644 --- a/platform/mv3/extension/_locales/sr/messages.json +++ b/platform/mv3/extension/_locales/sr/messages.json @@ -19,6 +19,10 @@ "message": "Подешавања", "description": "appears as tab name in dashboard" }, + "developPageName": { + "message": "Develop", + "description": "appears as tab name in dashboard. Inspired from 'Develop' menu in Safari, see " + }, "aboutPageName": { "message": "О апликацији", "description": "appears as tab name in dashboard" @@ -240,7 +244,7 @@ "description": "Label for a checkbox in the options page" }, "developerModeLegend": { - "message": "Enable access to features suitable for technical users.", + "message": "Enables access to features suitable for technical users.", "description": "Short description for a checkbox in the options page" }, "findListsPlaceholder": { @@ -310,5 +314,9 @@ "dnrRulesSummary": { "message": "Enter your own DNR rules below. Do not add content from untrusted sources.", "description": "Short description of the DNR rules editor pane" + }, + "dnrRulesCountInfo": { + "message": "Number of registered rules: {count}", + "description": "Short sentence to report the number of currently registered DNR rules" } } diff --git a/platform/mv3/extension/_locales/sv/messages.json b/platform/mv3/extension/_locales/sv/messages.json index e8df0696c76dd..5eee1f5599ce0 100644 --- a/platform/mv3/extension/_locales/sv/messages.json +++ b/platform/mv3/extension/_locales/sv/messages.json @@ -19,6 +19,10 @@ "message": "Inställningar", "description": "appears as tab name in dashboard" }, + "developPageName": { + "message": "Develop", + "description": "appears as tab name in dashboard. Inspired from 'Develop' menu in Safari, see " + }, "aboutPageName": { "message": "Om", "description": "appears as tab name in dashboard" @@ -310,5 +314,9 @@ "dnrRulesSummary": { "message": "Ange dina egna DNR-regler nedan. Lägg inte till innehåll från opålitliga källor.", "description": "Short description of the DNR rules editor pane" + }, + "dnrRulesCountInfo": { + "message": "Number of registered rules: {count}", + "description": "Short sentence to report the number of currently registered DNR rules" } } diff --git a/platform/mv3/extension/_locales/sw/messages.json b/platform/mv3/extension/_locales/sw/messages.json index 136a025b98095..484529aa58de1 100644 --- a/platform/mv3/extension/_locales/sw/messages.json +++ b/platform/mv3/extension/_locales/sw/messages.json @@ -19,6 +19,10 @@ "message": "Settings", "description": "appears as tab name in dashboard" }, + "developPageName": { + "message": "Develop", + "description": "appears as tab name in dashboard. Inspired from 'Develop' menu in Safari, see " + }, "aboutPageName": { "message": "About", "description": "appears as tab name in dashboard" @@ -240,7 +244,7 @@ "description": "Label for a checkbox in the options page" }, "developerModeLegend": { - "message": "Enable access to features suitable for technical users.", + "message": "Enables access to features suitable for technical users.", "description": "Short description for a checkbox in the options page" }, "findListsPlaceholder": { @@ -310,5 +314,9 @@ "dnrRulesSummary": { "message": "Enter your own DNR rules below. Do not add content from untrusted sources.", "description": "Short description of the DNR rules editor pane" + }, + "dnrRulesCountInfo": { + "message": "Number of registered rules: {count}", + "description": "Short sentence to report the number of currently registered DNR rules" } } diff --git a/platform/mv3/extension/_locales/ta/messages.json b/platform/mv3/extension/_locales/ta/messages.json index 136a025b98095..484529aa58de1 100644 --- a/platform/mv3/extension/_locales/ta/messages.json +++ b/platform/mv3/extension/_locales/ta/messages.json @@ -19,6 +19,10 @@ "message": "Settings", "description": "appears as tab name in dashboard" }, + "developPageName": { + "message": "Develop", + "description": "appears as tab name in dashboard. Inspired from 'Develop' menu in Safari, see " + }, "aboutPageName": { "message": "About", "description": "appears as tab name in dashboard" @@ -240,7 +244,7 @@ "description": "Label for a checkbox in the options page" }, "developerModeLegend": { - "message": "Enable access to features suitable for technical users.", + "message": "Enables access to features suitable for technical users.", "description": "Short description for a checkbox in the options page" }, "findListsPlaceholder": { @@ -310,5 +314,9 @@ "dnrRulesSummary": { "message": "Enter your own DNR rules below. Do not add content from untrusted sources.", "description": "Short description of the DNR rules editor pane" + }, + "dnrRulesCountInfo": { + "message": "Number of registered rules: {count}", + "description": "Short sentence to report the number of currently registered DNR rules" } } diff --git a/platform/mv3/extension/_locales/te/messages.json b/platform/mv3/extension/_locales/te/messages.json index e96f320082090..003dd51ae97eb 100644 --- a/platform/mv3/extension/_locales/te/messages.json +++ b/platform/mv3/extension/_locales/te/messages.json @@ -19,6 +19,10 @@ "message": "ఐచ్చికాలు", "description": "appears as tab name in dashboard" }, + "developPageName": { + "message": "Develop", + "description": "appears as tab name in dashboard. Inspired from 'Develop' menu in Safari, see " + }, "aboutPageName": { "message": "మా గురించి", "description": "appears as tab name in dashboard" @@ -240,7 +244,7 @@ "description": "Label for a checkbox in the options page" }, "developerModeLegend": { - "message": "Enable access to features suitable for technical users.", + "message": "Enables access to features suitable for technical users.", "description": "Short description for a checkbox in the options page" }, "findListsPlaceholder": { @@ -310,5 +314,9 @@ "dnrRulesSummary": { "message": "Enter your own DNR rules below. Do not add content from untrusted sources.", "description": "Short description of the DNR rules editor pane" + }, + "dnrRulesCountInfo": { + "message": "Number of registered rules: {count}", + "description": "Short sentence to report the number of currently registered DNR rules" } } diff --git a/platform/mv3/extension/_locales/th/messages.json b/platform/mv3/extension/_locales/th/messages.json index 2d498371dcd29..9d9a317f3c720 100644 --- a/platform/mv3/extension/_locales/th/messages.json +++ b/platform/mv3/extension/_locales/th/messages.json @@ -19,6 +19,10 @@ "message": "การตั้งค่า", "description": "appears as tab name in dashboard" }, + "developPageName": { + "message": "Develop", + "description": "appears as tab name in dashboard. Inspired from 'Develop' menu in Safari, see " + }, "aboutPageName": { "message": "เกี่ยวกับเรา", "description": "appears as tab name in dashboard" @@ -240,7 +244,7 @@ "description": "Label for a checkbox in the options page" }, "developerModeLegend": { - "message": "Enable access to features suitable for technical users.", + "message": "Enables access to features suitable for technical users.", "description": "Short description for a checkbox in the options page" }, "findListsPlaceholder": { @@ -310,5 +314,9 @@ "dnrRulesSummary": { "message": "Enter your own DNR rules below. Do not add content from untrusted sources.", "description": "Short description of the DNR rules editor pane" + }, + "dnrRulesCountInfo": { + "message": "Number of registered rules: {count}", + "description": "Short sentence to report the number of currently registered DNR rules" } } diff --git a/platform/mv3/extension/_locales/tr/messages.json b/platform/mv3/extension/_locales/tr/messages.json index 4f0cc0cbce05d..8b3ef0095988d 100644 --- a/platform/mv3/extension/_locales/tr/messages.json +++ b/platform/mv3/extension/_locales/tr/messages.json @@ -19,6 +19,10 @@ "message": "Ayarlar", "description": "appears as tab name in dashboard" }, + "developPageName": { + "message": "Develop", + "description": "appears as tab name in dashboard. Inspired from 'Develop' menu in Safari, see " + }, "aboutPageName": { "message": "Hakkında", "description": "appears as tab name in dashboard" @@ -310,5 +314,9 @@ "dnrRulesSummary": { "message": "Aşağıya kendi DNR kurallarınızı girin. Güvenilmeyen kaynaklardan içerik eklemeyin.", "description": "Short description of the DNR rules editor pane" + }, + "dnrRulesCountInfo": { + "message": "Number of registered rules: {count}", + "description": "Short sentence to report the number of currently registered DNR rules" } } diff --git a/platform/mv3/extension/_locales/uk/messages.json b/platform/mv3/extension/_locales/uk/messages.json index 4d733448a99ce..0cc407c49db69 100644 --- a/platform/mv3/extension/_locales/uk/messages.json +++ b/platform/mv3/extension/_locales/uk/messages.json @@ -19,6 +19,10 @@ "message": "Налаштування", "description": "appears as tab name in dashboard" }, + "developPageName": { + "message": "Develop", + "description": "appears as tab name in dashboard. Inspired from 'Develop' menu in Safari, see " + }, "aboutPageName": { "message": "Про застосунок", "description": "appears as tab name in dashboard" @@ -310,5 +314,9 @@ "dnrRulesSummary": { "message": "Введіть свої правила в форматі DNR нижче. Не додавайте вміст з ненадійних джерел.", "description": "Short description of the DNR rules editor pane" + }, + "dnrRulesCountInfo": { + "message": "Number of registered rules: {count}", + "description": "Short sentence to report the number of currently registered DNR rules" } } diff --git a/platform/mv3/extension/_locales/ur/messages.json b/platform/mv3/extension/_locales/ur/messages.json index 1a11f8f4e91de..7265fe9ace015 100644 --- a/platform/mv3/extension/_locales/ur/messages.json +++ b/platform/mv3/extension/_locales/ur/messages.json @@ -19,6 +19,10 @@ "message": "ترتیبات", "description": "appears as tab name in dashboard" }, + "developPageName": { + "message": "Develop", + "description": "appears as tab name in dashboard. Inspired from 'Develop' menu in Safari, see " + }, "aboutPageName": { "message": "تعارف", "description": "appears as tab name in dashboard" @@ -240,7 +244,7 @@ "description": "Label for a checkbox in the options page" }, "developerModeLegend": { - "message": "Enable access to features suitable for technical users.", + "message": "Enables access to features suitable for technical users.", "description": "Short description for a checkbox in the options page" }, "findListsPlaceholder": { @@ -310,5 +314,9 @@ "dnrRulesSummary": { "message": "Enter your own DNR rules below. Do not add content from untrusted sources.", "description": "Short description of the DNR rules editor pane" + }, + "dnrRulesCountInfo": { + "message": "Number of registered rules: {count}", + "description": "Short sentence to report the number of currently registered DNR rules" } } diff --git a/platform/mv3/extension/_locales/vi/messages.json b/platform/mv3/extension/_locales/vi/messages.json index bbe6c7d4fbf20..d1121754ce7c9 100644 --- a/platform/mv3/extension/_locales/vi/messages.json +++ b/platform/mv3/extension/_locales/vi/messages.json @@ -19,6 +19,10 @@ "message": "Cài đặt", "description": "appears as tab name in dashboard" }, + "developPageName": { + "message": "Develop", + "description": "appears as tab name in dashboard. Inspired from 'Develop' menu in Safari, see " + }, "aboutPageName": { "message": "Giới thiệu", "description": "appears as tab name in dashboard" @@ -240,7 +244,7 @@ "description": "Label for a checkbox in the options page" }, "developerModeLegend": { - "message": "Enable access to features suitable for technical users.", + "message": "Enables access to features suitable for technical users.", "description": "Short description for a checkbox in the options page" }, "findListsPlaceholder": { @@ -310,5 +314,9 @@ "dnrRulesSummary": { "message": "Enter your own DNR rules below. Do not add content from untrusted sources.", "description": "Short description of the DNR rules editor pane" + }, + "dnrRulesCountInfo": { + "message": "Number of registered rules: {count}", + "description": "Short sentence to report the number of currently registered DNR rules" } } diff --git a/platform/mv3/extension/_locales/zh_CN/messages.json b/platform/mv3/extension/_locales/zh_CN/messages.json index 69640caf43edd..3a4ca55db9831 100644 --- a/platform/mv3/extension/_locales/zh_CN/messages.json +++ b/platform/mv3/extension/_locales/zh_CN/messages.json @@ -19,6 +19,10 @@ "message": "设置", "description": "appears as tab name in dashboard" }, + "developPageName": { + "message": "Develop", + "description": "appears as tab name in dashboard. Inspired from 'Develop' menu in Safari, see " + }, "aboutPageName": { "message": "关于", "description": "appears as tab name in dashboard" @@ -240,7 +244,7 @@ "description": "Label for a checkbox in the options page" }, "developerModeLegend": { - "message": "Enable access to features suitable for technical users.", + "message": "Enables access to features suitable for technical users.", "description": "Short description for a checkbox in the options page" }, "findListsPlaceholder": { @@ -310,5 +314,9 @@ "dnrRulesSummary": { "message": "Enter your own DNR rules below. Do not add content from untrusted sources.", "description": "Short description of the DNR rules editor pane" + }, + "dnrRulesCountInfo": { + "message": "Number of registered rules: {count}", + "description": "Short sentence to report the number of currently registered DNR rules" } } diff --git a/platform/mv3/extension/_locales/zh_TW/messages.json b/platform/mv3/extension/_locales/zh_TW/messages.json index a37fc514a217a..e2cacb9d3dc5a 100644 --- a/platform/mv3/extension/_locales/zh_TW/messages.json +++ b/platform/mv3/extension/_locales/zh_TW/messages.json @@ -19,6 +19,10 @@ "message": "設定", "description": "appears as tab name in dashboard" }, + "developPageName": { + "message": "Develop", + "description": "appears as tab name in dashboard. Inspired from 'Develop' menu in Safari, see " + }, "aboutPageName": { "message": "關於", "description": "appears as tab name in dashboard" @@ -310,5 +314,9 @@ "dnrRulesSummary": { "message": "請在下方輸入您的 DNR 規則。請勿新增來自不受信任來源的內容。", "description": "Short description of the DNR rules editor pane" + }, + "dnrRulesCountInfo": { + "message": "Number of registered rules: {count}", + "description": "Short sentence to report the number of currently registered DNR rules" } } From eb3f5a44a96531bb42d190598a433bcb283a08ee Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Thu, 29 May 2025 10:13:32 -0400 Subject: [PATCH 0969/1099] Import translation work from https://crowdin.com/project/ublock --- platform/mv3/extension/_locales/ar/messages.json | 2 +- platform/mv3/extension/_locales/az/messages.json | 2 +- platform/mv3/extension/_locales/be/messages.json | 2 +- platform/mv3/extension/_locales/bg/messages.json | 2 +- platform/mv3/extension/_locales/bn/messages.json | 2 +- platform/mv3/extension/_locales/br_FR/messages.json | 2 +- platform/mv3/extension/_locales/bs/messages.json | 2 +- platform/mv3/extension/_locales/ca/messages.json | 2 +- platform/mv3/extension/_locales/cs/messages.json | 2 +- platform/mv3/extension/_locales/cv/messages.json | 2 +- platform/mv3/extension/_locales/cy/messages.json | 2 +- platform/mv3/extension/_locales/da/messages.json | 2 +- platform/mv3/extension/_locales/de/messages.json | 2 +- platform/mv3/extension/_locales/el/messages.json | 2 +- platform/mv3/extension/_locales/en/messages.json | 2 +- platform/mv3/extension/_locales/en_GB/messages.json | 2 +- platform/mv3/extension/_locales/eo/messages.json | 2 +- platform/mv3/extension/_locales/es/messages.json | 2 +- platform/mv3/extension/_locales/et/messages.json | 2 +- platform/mv3/extension/_locales/eu/messages.json | 2 +- platform/mv3/extension/_locales/fa/messages.json | 2 +- platform/mv3/extension/_locales/fi/messages.json | 2 +- platform/mv3/extension/_locales/fil/messages.json | 2 +- platform/mv3/extension/_locales/fr/messages.json | 2 +- platform/mv3/extension/_locales/fy/messages.json | 2 +- platform/mv3/extension/_locales/gl/messages.json | 2 +- platform/mv3/extension/_locales/gu/messages.json | 2 +- platform/mv3/extension/_locales/he/messages.json | 2 +- platform/mv3/extension/_locales/hi/messages.json | 2 +- platform/mv3/extension/_locales/hr/messages.json | 2 +- platform/mv3/extension/_locales/hu/messages.json | 2 +- platform/mv3/extension/_locales/hy/messages.json | 2 +- platform/mv3/extension/_locales/id/messages.json | 2 +- platform/mv3/extension/_locales/it/messages.json | 2 +- platform/mv3/extension/_locales/ja/messages.json | 2 +- platform/mv3/extension/_locales/ka/messages.json | 2 +- platform/mv3/extension/_locales/kk/messages.json | 2 +- platform/mv3/extension/_locales/kn/messages.json | 2 +- platform/mv3/extension/_locales/ko/messages.json | 2 +- platform/mv3/extension/_locales/lt/messages.json | 2 +- platform/mv3/extension/_locales/lv/messages.json | 2 +- platform/mv3/extension/_locales/mk/messages.json | 2 +- platform/mv3/extension/_locales/ml/messages.json | 2 +- platform/mv3/extension/_locales/mr/messages.json | 2 +- platform/mv3/extension/_locales/ms/messages.json | 2 +- platform/mv3/extension/_locales/nb/messages.json | 2 +- platform/mv3/extension/_locales/nl/messages.json | 2 +- platform/mv3/extension/_locales/oc/messages.json | 2 +- platform/mv3/extension/_locales/pa/messages.json | 2 +- platform/mv3/extension/_locales/pl/messages.json | 2 +- platform/mv3/extension/_locales/pt_BR/messages.json | 2 +- platform/mv3/extension/_locales/pt_PT/messages.json | 2 +- platform/mv3/extension/_locales/ro/messages.json | 2 +- platform/mv3/extension/_locales/ru/messages.json | 2 +- platform/mv3/extension/_locales/si/messages.json | 2 +- platform/mv3/extension/_locales/sk/messages.json | 2 +- platform/mv3/extension/_locales/sl/messages.json | 2 +- platform/mv3/extension/_locales/so/messages.json | 2 +- platform/mv3/extension/_locales/sq/messages.json | 2 +- platform/mv3/extension/_locales/sr/messages.json | 2 +- platform/mv3/extension/_locales/sv/messages.json | 2 +- platform/mv3/extension/_locales/sw/messages.json | 2 +- platform/mv3/extension/_locales/ta/messages.json | 2 +- platform/mv3/extension/_locales/te/messages.json | 2 +- platform/mv3/extension/_locales/th/messages.json | 2 +- platform/mv3/extension/_locales/tr/messages.json | 2 +- platform/mv3/extension/_locales/uk/messages.json | 2 +- platform/mv3/extension/_locales/ur/messages.json | 2 +- platform/mv3/extension/_locales/vi/messages.json | 2 +- platform/mv3/extension/_locales/zh_CN/messages.json | 2 +- platform/mv3/extension/_locales/zh_TW/messages.json | 2 +- 71 files changed, 71 insertions(+), 71 deletions(-) diff --git a/platform/mv3/extension/_locales/ar/messages.json b/platform/mv3/extension/_locales/ar/messages.json index 5e73020ecd5a3..775872e5f0b25 100644 --- a/platform/mv3/extension/_locales/ar/messages.json +++ b/platform/mv3/extension/_locales/ar/messages.json @@ -21,7 +21,7 @@ }, "developPageName": { "message": "Develop", - "description": "appears as tab name in dashboard. Inspired from 'Develop' menu in Safari, see " + "description": "appears as tab name in dashboard. Inspired from 'Develop' menu in Safari, see https://developer.apple.com/documentation/safari-developer-tools/develop-menu" }, "aboutPageName": { "message": "حول البرنامج", diff --git a/platform/mv3/extension/_locales/az/messages.json b/platform/mv3/extension/_locales/az/messages.json index 7df2037d0aff5..12a075471b6ef 100644 --- a/platform/mv3/extension/_locales/az/messages.json +++ b/platform/mv3/extension/_locales/az/messages.json @@ -21,7 +21,7 @@ }, "developPageName": { "message": "Develop", - "description": "appears as tab name in dashboard. Inspired from 'Develop' menu in Safari, see " + "description": "appears as tab name in dashboard. Inspired from 'Develop' menu in Safari, see https://developer.apple.com/documentation/safari-developer-tools/develop-menu" }, "aboutPageName": { "message": "Haqqında", diff --git a/platform/mv3/extension/_locales/be/messages.json b/platform/mv3/extension/_locales/be/messages.json index f7fa0450cf541..3b6a0d8644275 100644 --- a/platform/mv3/extension/_locales/be/messages.json +++ b/platform/mv3/extension/_locales/be/messages.json @@ -21,7 +21,7 @@ }, "developPageName": { "message": "Develop", - "description": "appears as tab name in dashboard. Inspired from 'Develop' menu in Safari, see " + "description": "appears as tab name in dashboard. Inspired from 'Develop' menu in Safari, see https://developer.apple.com/documentation/safari-developer-tools/develop-menu" }, "aboutPageName": { "message": "Пра пашырэнне", diff --git a/platform/mv3/extension/_locales/bg/messages.json b/platform/mv3/extension/_locales/bg/messages.json index 4617b1f35440f..7961a49d63dd8 100644 --- a/platform/mv3/extension/_locales/bg/messages.json +++ b/platform/mv3/extension/_locales/bg/messages.json @@ -21,7 +21,7 @@ }, "developPageName": { "message": "Develop", - "description": "appears as tab name in dashboard. Inspired from 'Develop' menu in Safari, see " + "description": "appears as tab name in dashboard. Inspired from 'Develop' menu in Safari, see https://developer.apple.com/documentation/safari-developer-tools/develop-menu" }, "aboutPageName": { "message": "Относно", diff --git a/platform/mv3/extension/_locales/bn/messages.json b/platform/mv3/extension/_locales/bn/messages.json index 0d5160f518756..67e0eab9c722a 100644 --- a/platform/mv3/extension/_locales/bn/messages.json +++ b/platform/mv3/extension/_locales/bn/messages.json @@ -21,7 +21,7 @@ }, "developPageName": { "message": "Develop", - "description": "appears as tab name in dashboard. Inspired from 'Develop' menu in Safari, see " + "description": "appears as tab name in dashboard. Inspired from 'Develop' menu in Safari, see https://developer.apple.com/documentation/safari-developer-tools/develop-menu" }, "aboutPageName": { "message": "সম্পর্কে", diff --git a/platform/mv3/extension/_locales/br_FR/messages.json b/platform/mv3/extension/_locales/br_FR/messages.json index 3a9cb7c2a8cb9..4575762a64024 100644 --- a/platform/mv3/extension/_locales/br_FR/messages.json +++ b/platform/mv3/extension/_locales/br_FR/messages.json @@ -21,7 +21,7 @@ }, "developPageName": { "message": "Develop", - "description": "appears as tab name in dashboard. Inspired from 'Develop' menu in Safari, see " + "description": "appears as tab name in dashboard. Inspired from 'Develop' menu in Safari, see https://developer.apple.com/documentation/safari-developer-tools/develop-menu" }, "aboutPageName": { "message": "Diwar-benn", diff --git a/platform/mv3/extension/_locales/bs/messages.json b/platform/mv3/extension/_locales/bs/messages.json index f6fd54177a069..c30d16ef4e9d3 100644 --- a/platform/mv3/extension/_locales/bs/messages.json +++ b/platform/mv3/extension/_locales/bs/messages.json @@ -21,7 +21,7 @@ }, "developPageName": { "message": "Develop", - "description": "appears as tab name in dashboard. Inspired from 'Develop' menu in Safari, see " + "description": "appears as tab name in dashboard. Inspired from 'Develop' menu in Safari, see https://developer.apple.com/documentation/safari-developer-tools/develop-menu" }, "aboutPageName": { "message": "O aplikaciji", diff --git a/platform/mv3/extension/_locales/ca/messages.json b/platform/mv3/extension/_locales/ca/messages.json index 0997c9c02128d..6cbc30e85c335 100644 --- a/platform/mv3/extension/_locales/ca/messages.json +++ b/platform/mv3/extension/_locales/ca/messages.json @@ -21,7 +21,7 @@ }, "developPageName": { "message": "Develop", - "description": "appears as tab name in dashboard. Inspired from 'Develop' menu in Safari, see " + "description": "appears as tab name in dashboard. Inspired from 'Develop' menu in Safari, see https://developer.apple.com/documentation/safari-developer-tools/develop-menu" }, "aboutPageName": { "message": "Quant a", diff --git a/platform/mv3/extension/_locales/cs/messages.json b/platform/mv3/extension/_locales/cs/messages.json index 47609e3b6d75e..751c8adf2027a 100644 --- a/platform/mv3/extension/_locales/cs/messages.json +++ b/platform/mv3/extension/_locales/cs/messages.json @@ -21,7 +21,7 @@ }, "developPageName": { "message": "Develop", - "description": "appears as tab name in dashboard. Inspired from 'Develop' menu in Safari, see " + "description": "appears as tab name in dashboard. Inspired from 'Develop' menu in Safari, see https://developer.apple.com/documentation/safari-developer-tools/develop-menu" }, "aboutPageName": { "message": "O rozšíření", diff --git a/platform/mv3/extension/_locales/cv/messages.json b/platform/mv3/extension/_locales/cv/messages.json index 484529aa58de1..4316ba863b234 100644 --- a/platform/mv3/extension/_locales/cv/messages.json +++ b/platform/mv3/extension/_locales/cv/messages.json @@ -21,7 +21,7 @@ }, "developPageName": { "message": "Develop", - "description": "appears as tab name in dashboard. Inspired from 'Develop' menu in Safari, see " + "description": "appears as tab name in dashboard. Inspired from 'Develop' menu in Safari, see https://developer.apple.com/documentation/safari-developer-tools/develop-menu" }, "aboutPageName": { "message": "About", diff --git a/platform/mv3/extension/_locales/cy/messages.json b/platform/mv3/extension/_locales/cy/messages.json index ff4d9108c39b3..c10d0f119bc7f 100644 --- a/platform/mv3/extension/_locales/cy/messages.json +++ b/platform/mv3/extension/_locales/cy/messages.json @@ -21,7 +21,7 @@ }, "developPageName": { "message": "Develop", - "description": "appears as tab name in dashboard. Inspired from 'Develop' menu in Safari, see " + "description": "appears as tab name in dashboard. Inspired from 'Develop' menu in Safari, see https://developer.apple.com/documentation/safari-developer-tools/develop-menu" }, "aboutPageName": { "message": "Ynghylch", diff --git a/platform/mv3/extension/_locales/da/messages.json b/platform/mv3/extension/_locales/da/messages.json index 63ab8e82e6660..88a2dbe834530 100644 --- a/platform/mv3/extension/_locales/da/messages.json +++ b/platform/mv3/extension/_locales/da/messages.json @@ -21,7 +21,7 @@ }, "developPageName": { "message": "Develop", - "description": "appears as tab name in dashboard. Inspired from 'Develop' menu in Safari, see " + "description": "appears as tab name in dashboard. Inspired from 'Develop' menu in Safari, see https://developer.apple.com/documentation/safari-developer-tools/develop-menu" }, "aboutPageName": { "message": "Om", diff --git a/platform/mv3/extension/_locales/de/messages.json b/platform/mv3/extension/_locales/de/messages.json index c2ca03e3a1fd3..62dd91341809e 100644 --- a/platform/mv3/extension/_locales/de/messages.json +++ b/platform/mv3/extension/_locales/de/messages.json @@ -21,7 +21,7 @@ }, "developPageName": { "message": "Develop", - "description": "appears as tab name in dashboard. Inspired from 'Develop' menu in Safari, see " + "description": "appears as tab name in dashboard. Inspired from 'Develop' menu in Safari, see https://developer.apple.com/documentation/safari-developer-tools/develop-menu" }, "aboutPageName": { "message": "Über", diff --git a/platform/mv3/extension/_locales/el/messages.json b/platform/mv3/extension/_locales/el/messages.json index e23bbea8198c8..5397069e0c6bd 100644 --- a/platform/mv3/extension/_locales/el/messages.json +++ b/platform/mv3/extension/_locales/el/messages.json @@ -21,7 +21,7 @@ }, "developPageName": { "message": "Develop", - "description": "appears as tab name in dashboard. Inspired from 'Develop' menu in Safari, see " + "description": "appears as tab name in dashboard. Inspired from 'Develop' menu in Safari, see https://developer.apple.com/documentation/safari-developer-tools/develop-menu" }, "aboutPageName": { "message": "Σχετικά", diff --git a/platform/mv3/extension/_locales/en/messages.json b/platform/mv3/extension/_locales/en/messages.json index e238f3a0180a8..e0c2038f2bf95 100644 --- a/platform/mv3/extension/_locales/en/messages.json +++ b/platform/mv3/extension/_locales/en/messages.json @@ -21,7 +21,7 @@ }, "developPageName": { "message": "Develop", - "description": "appears as tab name in dashboard. Inspired from 'Develop' menu in Safari, see " + "description": "appears as tab name in dashboard. Inspired from 'Develop' menu in Safari, see https://developer.apple.com/documentation/safari-developer-tools/develop-menu" }, "aboutPageName": { "message": "About", diff --git a/platform/mv3/extension/_locales/en_GB/messages.json b/platform/mv3/extension/_locales/en_GB/messages.json index 0fb1647494c51..f86fe626a95bd 100644 --- a/platform/mv3/extension/_locales/en_GB/messages.json +++ b/platform/mv3/extension/_locales/en_GB/messages.json @@ -21,7 +21,7 @@ }, "developPageName": { "message": "Develop", - "description": "appears as tab name in dashboard. Inspired from 'Develop' menu in Safari, see " + "description": "appears as tab name in dashboard. Inspired from 'Develop' menu in Safari, see https://developer.apple.com/documentation/safari-developer-tools/develop-menu" }, "aboutPageName": { "message": "About", diff --git a/platform/mv3/extension/_locales/eo/messages.json b/platform/mv3/extension/_locales/eo/messages.json index 56d1d3006cb2d..1401ff4346fdc 100644 --- a/platform/mv3/extension/_locales/eo/messages.json +++ b/platform/mv3/extension/_locales/eo/messages.json @@ -21,7 +21,7 @@ }, "developPageName": { "message": "Develop", - "description": "appears as tab name in dashboard. Inspired from 'Develop' menu in Safari, see " + "description": "appears as tab name in dashboard. Inspired from 'Develop' menu in Safari, see https://developer.apple.com/documentation/safari-developer-tools/develop-menu" }, "aboutPageName": { "message": "Pri", diff --git a/platform/mv3/extension/_locales/es/messages.json b/platform/mv3/extension/_locales/es/messages.json index cd0175a56d07e..545fcea4cb2c2 100644 --- a/platform/mv3/extension/_locales/es/messages.json +++ b/platform/mv3/extension/_locales/es/messages.json @@ -21,7 +21,7 @@ }, "developPageName": { "message": "Develop", - "description": "appears as tab name in dashboard. Inspired from 'Develop' menu in Safari, see " + "description": "appears as tab name in dashboard. Inspired from 'Develop' menu in Safari, see https://developer.apple.com/documentation/safari-developer-tools/develop-menu" }, "aboutPageName": { "message": "Acerca de", diff --git a/platform/mv3/extension/_locales/et/messages.json b/platform/mv3/extension/_locales/et/messages.json index 5e97968f71e7f..3dc19e11edb28 100644 --- a/platform/mv3/extension/_locales/et/messages.json +++ b/platform/mv3/extension/_locales/et/messages.json @@ -21,7 +21,7 @@ }, "developPageName": { "message": "Develop", - "description": "appears as tab name in dashboard. Inspired from 'Develop' menu in Safari, see " + "description": "appears as tab name in dashboard. Inspired from 'Develop' menu in Safari, see https://developer.apple.com/documentation/safari-developer-tools/develop-menu" }, "aboutPageName": { "message": "Teave", diff --git a/platform/mv3/extension/_locales/eu/messages.json b/platform/mv3/extension/_locales/eu/messages.json index aba7d7f69dbc7..fe97f96264b0c 100644 --- a/platform/mv3/extension/_locales/eu/messages.json +++ b/platform/mv3/extension/_locales/eu/messages.json @@ -21,7 +21,7 @@ }, "developPageName": { "message": "Develop", - "description": "appears as tab name in dashboard. Inspired from 'Develop' menu in Safari, see " + "description": "appears as tab name in dashboard. Inspired from 'Develop' menu in Safari, see https://developer.apple.com/documentation/safari-developer-tools/develop-menu" }, "aboutPageName": { "message": "Honi buruz", diff --git a/platform/mv3/extension/_locales/fa/messages.json b/platform/mv3/extension/_locales/fa/messages.json index 10f156aa0f045..7330c92e97568 100644 --- a/platform/mv3/extension/_locales/fa/messages.json +++ b/platform/mv3/extension/_locales/fa/messages.json @@ -21,7 +21,7 @@ }, "developPageName": { "message": "Develop", - "description": "appears as tab name in dashboard. Inspired from 'Develop' menu in Safari, see " + "description": "appears as tab name in dashboard. Inspired from 'Develop' menu in Safari, see https://developer.apple.com/documentation/safari-developer-tools/develop-menu" }, "aboutPageName": { "message": "درباره", diff --git a/platform/mv3/extension/_locales/fi/messages.json b/platform/mv3/extension/_locales/fi/messages.json index 742511be07ad9..4858173b77502 100644 --- a/platform/mv3/extension/_locales/fi/messages.json +++ b/platform/mv3/extension/_locales/fi/messages.json @@ -21,7 +21,7 @@ }, "developPageName": { "message": "Develop", - "description": "appears as tab name in dashboard. Inspired from 'Develop' menu in Safari, see " + "description": "appears as tab name in dashboard. Inspired from 'Develop' menu in Safari, see https://developer.apple.com/documentation/safari-developer-tools/develop-menu" }, "aboutPageName": { "message": "Tietoja", diff --git a/platform/mv3/extension/_locales/fil/messages.json b/platform/mv3/extension/_locales/fil/messages.json index 4be70d504de97..d1ba182b1be6c 100644 --- a/platform/mv3/extension/_locales/fil/messages.json +++ b/platform/mv3/extension/_locales/fil/messages.json @@ -21,7 +21,7 @@ }, "developPageName": { "message": "Develop", - "description": "appears as tab name in dashboard. Inspired from 'Develop' menu in Safari, see " + "description": "appears as tab name in dashboard. Inspired from 'Develop' menu in Safari, see https://developer.apple.com/documentation/safari-developer-tools/develop-menu" }, "aboutPageName": { "message": "Tungkol", diff --git a/platform/mv3/extension/_locales/fr/messages.json b/platform/mv3/extension/_locales/fr/messages.json index 926f06b763515..7d1a32a151353 100644 --- a/platform/mv3/extension/_locales/fr/messages.json +++ b/platform/mv3/extension/_locales/fr/messages.json @@ -21,7 +21,7 @@ }, "developPageName": { "message": "Develop", - "description": "appears as tab name in dashboard. Inspired from 'Develop' menu in Safari, see " + "description": "appears as tab name in dashboard. Inspired from 'Develop' menu in Safari, see https://developer.apple.com/documentation/safari-developer-tools/develop-menu" }, "aboutPageName": { "message": "À propos", diff --git a/platform/mv3/extension/_locales/fy/messages.json b/platform/mv3/extension/_locales/fy/messages.json index fbcee7d6364c8..30a57ddfa15ab 100644 --- a/platform/mv3/extension/_locales/fy/messages.json +++ b/platform/mv3/extension/_locales/fy/messages.json @@ -21,7 +21,7 @@ }, "developPageName": { "message": "Develop", - "description": "appears as tab name in dashboard. Inspired from 'Develop' menu in Safari, see " + "description": "appears as tab name in dashboard. Inspired from 'Develop' menu in Safari, see https://developer.apple.com/documentation/safari-developer-tools/develop-menu" }, "aboutPageName": { "message": "Oer", diff --git a/platform/mv3/extension/_locales/gl/messages.json b/platform/mv3/extension/_locales/gl/messages.json index d4238032787d3..663aad5d1149a 100644 --- a/platform/mv3/extension/_locales/gl/messages.json +++ b/platform/mv3/extension/_locales/gl/messages.json @@ -21,7 +21,7 @@ }, "developPageName": { "message": "Develop", - "description": "appears as tab name in dashboard. Inspired from 'Develop' menu in Safari, see " + "description": "appears as tab name in dashboard. Inspired from 'Develop' menu in Safari, see https://developer.apple.com/documentation/safari-developer-tools/develop-menu" }, "aboutPageName": { "message": "Acerca de", diff --git a/platform/mv3/extension/_locales/gu/messages.json b/platform/mv3/extension/_locales/gu/messages.json index 484529aa58de1..4316ba863b234 100644 --- a/platform/mv3/extension/_locales/gu/messages.json +++ b/platform/mv3/extension/_locales/gu/messages.json @@ -21,7 +21,7 @@ }, "developPageName": { "message": "Develop", - "description": "appears as tab name in dashboard. Inspired from 'Develop' menu in Safari, see " + "description": "appears as tab name in dashboard. Inspired from 'Develop' menu in Safari, see https://developer.apple.com/documentation/safari-developer-tools/develop-menu" }, "aboutPageName": { "message": "About", diff --git a/platform/mv3/extension/_locales/he/messages.json b/platform/mv3/extension/_locales/he/messages.json index dfe42dcdbdc9f..e360181580ee8 100644 --- a/platform/mv3/extension/_locales/he/messages.json +++ b/platform/mv3/extension/_locales/he/messages.json @@ -21,7 +21,7 @@ }, "developPageName": { "message": "Develop", - "description": "appears as tab name in dashboard. Inspired from 'Develop' menu in Safari, see " + "description": "appears as tab name in dashboard. Inspired from 'Develop' menu in Safari, see https://developer.apple.com/documentation/safari-developer-tools/develop-menu" }, "aboutPageName": { "message": "על אודות", diff --git a/platform/mv3/extension/_locales/hi/messages.json b/platform/mv3/extension/_locales/hi/messages.json index a1664a6aef2b4..ace22258113b6 100644 --- a/platform/mv3/extension/_locales/hi/messages.json +++ b/platform/mv3/extension/_locales/hi/messages.json @@ -21,7 +21,7 @@ }, "developPageName": { "message": "Develop", - "description": "appears as tab name in dashboard. Inspired from 'Develop' menu in Safari, see " + "description": "appears as tab name in dashboard. Inspired from 'Develop' menu in Safari, see https://developer.apple.com/documentation/safari-developer-tools/develop-menu" }, "aboutPageName": { "message": "जानकारी", diff --git a/platform/mv3/extension/_locales/hr/messages.json b/platform/mv3/extension/_locales/hr/messages.json index e3d703f208bc4..d7aa10abb0bbe 100644 --- a/platform/mv3/extension/_locales/hr/messages.json +++ b/platform/mv3/extension/_locales/hr/messages.json @@ -21,7 +21,7 @@ }, "developPageName": { "message": "Develop", - "description": "appears as tab name in dashboard. Inspired from 'Develop' menu in Safari, see " + "description": "appears as tab name in dashboard. Inspired from 'Develop' menu in Safari, see https://developer.apple.com/documentation/safari-developer-tools/develop-menu" }, "aboutPageName": { "message": "O aplikaciji", diff --git a/platform/mv3/extension/_locales/hu/messages.json b/platform/mv3/extension/_locales/hu/messages.json index 82a6cc5e691cc..dc6421a5131e0 100644 --- a/platform/mv3/extension/_locales/hu/messages.json +++ b/platform/mv3/extension/_locales/hu/messages.json @@ -21,7 +21,7 @@ }, "developPageName": { "message": "Develop", - "description": "appears as tab name in dashboard. Inspired from 'Develop' menu in Safari, see " + "description": "appears as tab name in dashboard. Inspired from 'Develop' menu in Safari, see https://developer.apple.com/documentation/safari-developer-tools/develop-menu" }, "aboutPageName": { "message": "Névjegy", diff --git a/platform/mv3/extension/_locales/hy/messages.json b/platform/mv3/extension/_locales/hy/messages.json index f9a45929fffc2..35dfc19247d5c 100644 --- a/platform/mv3/extension/_locales/hy/messages.json +++ b/platform/mv3/extension/_locales/hy/messages.json @@ -21,7 +21,7 @@ }, "developPageName": { "message": "Develop", - "description": "appears as tab name in dashboard. Inspired from 'Develop' menu in Safari, see " + "description": "appears as tab name in dashboard. Inspired from 'Develop' menu in Safari, see https://developer.apple.com/documentation/safari-developer-tools/develop-menu" }, "aboutPageName": { "message": "Ընդլայնման մասին", diff --git a/platform/mv3/extension/_locales/id/messages.json b/platform/mv3/extension/_locales/id/messages.json index 3c5f42fe73182..2a947994e1c18 100644 --- a/platform/mv3/extension/_locales/id/messages.json +++ b/platform/mv3/extension/_locales/id/messages.json @@ -21,7 +21,7 @@ }, "developPageName": { "message": "Develop", - "description": "appears as tab name in dashboard. Inspired from 'Develop' menu in Safari, see " + "description": "appears as tab name in dashboard. Inspired from 'Develop' menu in Safari, see https://developer.apple.com/documentation/safari-developer-tools/develop-menu" }, "aboutPageName": { "message": "Tentang", diff --git a/platform/mv3/extension/_locales/it/messages.json b/platform/mv3/extension/_locales/it/messages.json index df26250965130..9db0c41a4d2ac 100644 --- a/platform/mv3/extension/_locales/it/messages.json +++ b/platform/mv3/extension/_locales/it/messages.json @@ -21,7 +21,7 @@ }, "developPageName": { "message": "Develop", - "description": "appears as tab name in dashboard. Inspired from 'Develop' menu in Safari, see " + "description": "appears as tab name in dashboard. Inspired from 'Develop' menu in Safari, see https://developer.apple.com/documentation/safari-developer-tools/develop-menu" }, "aboutPageName": { "message": "Informazioni", diff --git a/platform/mv3/extension/_locales/ja/messages.json b/platform/mv3/extension/_locales/ja/messages.json index 629b69f510cba..94058ecfb7420 100644 --- a/platform/mv3/extension/_locales/ja/messages.json +++ b/platform/mv3/extension/_locales/ja/messages.json @@ -21,7 +21,7 @@ }, "developPageName": { "message": "Develop", - "description": "appears as tab name in dashboard. Inspired from 'Develop' menu in Safari, see " + "description": "appears as tab name in dashboard. Inspired from 'Develop' menu in Safari, see https://developer.apple.com/documentation/safari-developer-tools/develop-menu" }, "aboutPageName": { "message": "uBO Lite について", diff --git a/platform/mv3/extension/_locales/ka/messages.json b/platform/mv3/extension/_locales/ka/messages.json index eb407fb0e9109..c601acfd498ce 100644 --- a/platform/mv3/extension/_locales/ka/messages.json +++ b/platform/mv3/extension/_locales/ka/messages.json @@ -21,7 +21,7 @@ }, "developPageName": { "message": "Develop", - "description": "appears as tab name in dashboard. Inspired from 'Develop' menu in Safari, see " + "description": "appears as tab name in dashboard. Inspired from 'Develop' menu in Safari, see https://developer.apple.com/documentation/safari-developer-tools/develop-menu" }, "aboutPageName": { "message": "შესახებ", diff --git a/platform/mv3/extension/_locales/kk/messages.json b/platform/mv3/extension/_locales/kk/messages.json index 484529aa58de1..4316ba863b234 100644 --- a/platform/mv3/extension/_locales/kk/messages.json +++ b/platform/mv3/extension/_locales/kk/messages.json @@ -21,7 +21,7 @@ }, "developPageName": { "message": "Develop", - "description": "appears as tab name in dashboard. Inspired from 'Develop' menu in Safari, see " + "description": "appears as tab name in dashboard. Inspired from 'Develop' menu in Safari, see https://developer.apple.com/documentation/safari-developer-tools/develop-menu" }, "aboutPageName": { "message": "About", diff --git a/platform/mv3/extension/_locales/kn/messages.json b/platform/mv3/extension/_locales/kn/messages.json index fe69ab48ce819..9fef8758fadeb 100644 --- a/platform/mv3/extension/_locales/kn/messages.json +++ b/platform/mv3/extension/_locales/kn/messages.json @@ -21,7 +21,7 @@ }, "developPageName": { "message": "Develop", - "description": "appears as tab name in dashboard. Inspired from 'Develop' menu in Safari, see " + "description": "appears as tab name in dashboard. Inspired from 'Develop' menu in Safari, see https://developer.apple.com/documentation/safari-developer-tools/develop-menu" }, "aboutPageName": { "message": "ಬಗ್ಗೆ", diff --git a/platform/mv3/extension/_locales/ko/messages.json b/platform/mv3/extension/_locales/ko/messages.json index 3af874c101cc2..8d28d88f357b4 100644 --- a/platform/mv3/extension/_locales/ko/messages.json +++ b/platform/mv3/extension/_locales/ko/messages.json @@ -21,7 +21,7 @@ }, "developPageName": { "message": "Develop", - "description": "appears as tab name in dashboard. Inspired from 'Develop' menu in Safari, see " + "description": "appears as tab name in dashboard. Inspired from 'Develop' menu in Safari, see https://developer.apple.com/documentation/safari-developer-tools/develop-menu" }, "aboutPageName": { "message": "정보", diff --git a/platform/mv3/extension/_locales/lt/messages.json b/platform/mv3/extension/_locales/lt/messages.json index fdca5a9907bff..32291238edf99 100644 --- a/platform/mv3/extension/_locales/lt/messages.json +++ b/platform/mv3/extension/_locales/lt/messages.json @@ -21,7 +21,7 @@ }, "developPageName": { "message": "Develop", - "description": "appears as tab name in dashboard. Inspired from 'Develop' menu in Safari, see " + "description": "appears as tab name in dashboard. Inspired from 'Develop' menu in Safari, see https://developer.apple.com/documentation/safari-developer-tools/develop-menu" }, "aboutPageName": { "message": "Apie", diff --git a/platform/mv3/extension/_locales/lv/messages.json b/platform/mv3/extension/_locales/lv/messages.json index bf857a5b21206..96b18972e16e2 100644 --- a/platform/mv3/extension/_locales/lv/messages.json +++ b/platform/mv3/extension/_locales/lv/messages.json @@ -21,7 +21,7 @@ }, "developPageName": { "message": "Develop", - "description": "appears as tab name in dashboard. Inspired from 'Develop' menu in Safari, see " + "description": "appears as tab name in dashboard. Inspired from 'Develop' menu in Safari, see https://developer.apple.com/documentation/safari-developer-tools/develop-menu" }, "aboutPageName": { "message": "Par", diff --git a/platform/mv3/extension/_locales/mk/messages.json b/platform/mv3/extension/_locales/mk/messages.json index 79a1a924b708e..248e16d7a5c48 100644 --- a/platform/mv3/extension/_locales/mk/messages.json +++ b/platform/mv3/extension/_locales/mk/messages.json @@ -21,7 +21,7 @@ }, "developPageName": { "message": "Develop", - "description": "appears as tab name in dashboard. Inspired from 'Develop' menu in Safari, see " + "description": "appears as tab name in dashboard. Inspired from 'Develop' menu in Safari, see https://developer.apple.com/documentation/safari-developer-tools/develop-menu" }, "aboutPageName": { "message": "Информации за...", diff --git a/platform/mv3/extension/_locales/ml/messages.json b/platform/mv3/extension/_locales/ml/messages.json index e266c865d26ae..d01758513037b 100644 --- a/platform/mv3/extension/_locales/ml/messages.json +++ b/platform/mv3/extension/_locales/ml/messages.json @@ -21,7 +21,7 @@ }, "developPageName": { "message": "Develop", - "description": "appears as tab name in dashboard. Inspired from 'Develop' menu in Safari, see " + "description": "appears as tab name in dashboard. Inspired from 'Develop' menu in Safari, see https://developer.apple.com/documentation/safari-developer-tools/develop-menu" }, "aboutPageName": { "message": "കുറിച്ച്", diff --git a/platform/mv3/extension/_locales/mr/messages.json b/platform/mv3/extension/_locales/mr/messages.json index 484529aa58de1..4316ba863b234 100644 --- a/platform/mv3/extension/_locales/mr/messages.json +++ b/platform/mv3/extension/_locales/mr/messages.json @@ -21,7 +21,7 @@ }, "developPageName": { "message": "Develop", - "description": "appears as tab name in dashboard. Inspired from 'Develop' menu in Safari, see " + "description": "appears as tab name in dashboard. Inspired from 'Develop' menu in Safari, see https://developer.apple.com/documentation/safari-developer-tools/develop-menu" }, "aboutPageName": { "message": "About", diff --git a/platform/mv3/extension/_locales/ms/messages.json b/platform/mv3/extension/_locales/ms/messages.json index 48bd33ff41fb8..fe528eed94717 100644 --- a/platform/mv3/extension/_locales/ms/messages.json +++ b/platform/mv3/extension/_locales/ms/messages.json @@ -21,7 +21,7 @@ }, "developPageName": { "message": "Develop", - "description": "appears as tab name in dashboard. Inspired from 'Develop' menu in Safari, see " + "description": "appears as tab name in dashboard. Inspired from 'Develop' menu in Safari, see https://developer.apple.com/documentation/safari-developer-tools/develop-menu" }, "aboutPageName": { "message": "Mengenai", diff --git a/platform/mv3/extension/_locales/nb/messages.json b/platform/mv3/extension/_locales/nb/messages.json index 2100015b88991..46bf699d00361 100644 --- a/platform/mv3/extension/_locales/nb/messages.json +++ b/platform/mv3/extension/_locales/nb/messages.json @@ -21,7 +21,7 @@ }, "developPageName": { "message": "Develop", - "description": "appears as tab name in dashboard. Inspired from 'Develop' menu in Safari, see " + "description": "appears as tab name in dashboard. Inspired from 'Develop' menu in Safari, see https://developer.apple.com/documentation/safari-developer-tools/develop-menu" }, "aboutPageName": { "message": "Om", diff --git a/platform/mv3/extension/_locales/nl/messages.json b/platform/mv3/extension/_locales/nl/messages.json index fe07adcb28691..27dc548bf6bc1 100644 --- a/platform/mv3/extension/_locales/nl/messages.json +++ b/platform/mv3/extension/_locales/nl/messages.json @@ -21,7 +21,7 @@ }, "developPageName": { "message": "Develop", - "description": "appears as tab name in dashboard. Inspired from 'Develop' menu in Safari, see " + "description": "appears as tab name in dashboard. Inspired from 'Develop' menu in Safari, see https://developer.apple.com/documentation/safari-developer-tools/develop-menu" }, "aboutPageName": { "message": "Over", diff --git a/platform/mv3/extension/_locales/oc/messages.json b/platform/mv3/extension/_locales/oc/messages.json index 484529aa58de1..4316ba863b234 100644 --- a/platform/mv3/extension/_locales/oc/messages.json +++ b/platform/mv3/extension/_locales/oc/messages.json @@ -21,7 +21,7 @@ }, "developPageName": { "message": "Develop", - "description": "appears as tab name in dashboard. Inspired from 'Develop' menu in Safari, see " + "description": "appears as tab name in dashboard. Inspired from 'Develop' menu in Safari, see https://developer.apple.com/documentation/safari-developer-tools/develop-menu" }, "aboutPageName": { "message": "About", diff --git a/platform/mv3/extension/_locales/pa/messages.json b/platform/mv3/extension/_locales/pa/messages.json index ce043fb5cbafc..b3cb0d570f058 100644 --- a/platform/mv3/extension/_locales/pa/messages.json +++ b/platform/mv3/extension/_locales/pa/messages.json @@ -21,7 +21,7 @@ }, "developPageName": { "message": "Develop", - "description": "appears as tab name in dashboard. Inspired from 'Develop' menu in Safari, see " + "description": "appears as tab name in dashboard. Inspired from 'Develop' menu in Safari, see https://developer.apple.com/documentation/safari-developer-tools/develop-menu" }, "aboutPageName": { "message": "ਇਸ ਬਾਰੇ", diff --git a/platform/mv3/extension/_locales/pl/messages.json b/platform/mv3/extension/_locales/pl/messages.json index 150a1a47d39d3..f7fe13b2cefcc 100644 --- a/platform/mv3/extension/_locales/pl/messages.json +++ b/platform/mv3/extension/_locales/pl/messages.json @@ -21,7 +21,7 @@ }, "developPageName": { "message": "Develop", - "description": "appears as tab name in dashboard. Inspired from 'Develop' menu in Safari, see " + "description": "appears as tab name in dashboard. Inspired from 'Develop' menu in Safari, see https://developer.apple.com/documentation/safari-developer-tools/develop-menu" }, "aboutPageName": { "message": "O rozszerzeniu", diff --git a/platform/mv3/extension/_locales/pt_BR/messages.json b/platform/mv3/extension/_locales/pt_BR/messages.json index ea305ce1993c4..8d9a3d39f7d4a 100644 --- a/platform/mv3/extension/_locales/pt_BR/messages.json +++ b/platform/mv3/extension/_locales/pt_BR/messages.json @@ -21,7 +21,7 @@ }, "developPageName": { "message": "Develop", - "description": "appears as tab name in dashboard. Inspired from 'Develop' menu in Safari, see " + "description": "appears as tab name in dashboard. Inspired from 'Develop' menu in Safari, see https://developer.apple.com/documentation/safari-developer-tools/develop-menu" }, "aboutPageName": { "message": "Sobre", diff --git a/platform/mv3/extension/_locales/pt_PT/messages.json b/platform/mv3/extension/_locales/pt_PT/messages.json index e5006ce8a9a48..c167022eee290 100644 --- a/platform/mv3/extension/_locales/pt_PT/messages.json +++ b/platform/mv3/extension/_locales/pt_PT/messages.json @@ -21,7 +21,7 @@ }, "developPageName": { "message": "Develop", - "description": "appears as tab name in dashboard. Inspired from 'Develop' menu in Safari, see " + "description": "appears as tab name in dashboard. Inspired from 'Develop' menu in Safari, see https://developer.apple.com/documentation/safari-developer-tools/develop-menu" }, "aboutPageName": { "message": "Acerca", diff --git a/platform/mv3/extension/_locales/ro/messages.json b/platform/mv3/extension/_locales/ro/messages.json index 4da91a9e5ea70..f1693d34ed1a3 100644 --- a/platform/mv3/extension/_locales/ro/messages.json +++ b/platform/mv3/extension/_locales/ro/messages.json @@ -21,7 +21,7 @@ }, "developPageName": { "message": "Develop", - "description": "appears as tab name in dashboard. Inspired from 'Develop' menu in Safari, see " + "description": "appears as tab name in dashboard. Inspired from 'Develop' menu in Safari, see https://developer.apple.com/documentation/safari-developer-tools/develop-menu" }, "aboutPageName": { "message": "Despre", diff --git a/platform/mv3/extension/_locales/ru/messages.json b/platform/mv3/extension/_locales/ru/messages.json index 9a9f6a15d66ab..7382002040621 100644 --- a/platform/mv3/extension/_locales/ru/messages.json +++ b/platform/mv3/extension/_locales/ru/messages.json @@ -21,7 +21,7 @@ }, "developPageName": { "message": "Develop", - "description": "appears as tab name in dashboard. Inspired from 'Develop' menu in Safari, see " + "description": "appears as tab name in dashboard. Inspired from 'Develop' menu in Safari, see https://developer.apple.com/documentation/safari-developer-tools/develop-menu" }, "aboutPageName": { "message": "О расширении", diff --git a/platform/mv3/extension/_locales/si/messages.json b/platform/mv3/extension/_locales/si/messages.json index 30d5306378852..0eb8b9b678f44 100644 --- a/platform/mv3/extension/_locales/si/messages.json +++ b/platform/mv3/extension/_locales/si/messages.json @@ -21,7 +21,7 @@ }, "developPageName": { "message": "Develop", - "description": "appears as tab name in dashboard. Inspired from 'Develop' menu in Safari, see " + "description": "appears as tab name in dashboard. Inspired from 'Develop' menu in Safari, see https://developer.apple.com/documentation/safari-developer-tools/develop-menu" }, "aboutPageName": { "message": "පිළිබඳ", diff --git a/platform/mv3/extension/_locales/sk/messages.json b/platform/mv3/extension/_locales/sk/messages.json index 74a3243d57a41..1d10107dac508 100644 --- a/platform/mv3/extension/_locales/sk/messages.json +++ b/platform/mv3/extension/_locales/sk/messages.json @@ -21,7 +21,7 @@ }, "developPageName": { "message": "Develop", - "description": "appears as tab name in dashboard. Inspired from 'Develop' menu in Safari, see " + "description": "appears as tab name in dashboard. Inspired from 'Develop' menu in Safari, see https://developer.apple.com/documentation/safari-developer-tools/develop-menu" }, "aboutPageName": { "message": "O doplnku", diff --git a/platform/mv3/extension/_locales/sl/messages.json b/platform/mv3/extension/_locales/sl/messages.json index 484529aa58de1..4316ba863b234 100644 --- a/platform/mv3/extension/_locales/sl/messages.json +++ b/platform/mv3/extension/_locales/sl/messages.json @@ -21,7 +21,7 @@ }, "developPageName": { "message": "Develop", - "description": "appears as tab name in dashboard. Inspired from 'Develop' menu in Safari, see " + "description": "appears as tab name in dashboard. Inspired from 'Develop' menu in Safari, see https://developer.apple.com/documentation/safari-developer-tools/develop-menu" }, "aboutPageName": { "message": "About", diff --git a/platform/mv3/extension/_locales/so/messages.json b/platform/mv3/extension/_locales/so/messages.json index 484529aa58de1..4316ba863b234 100644 --- a/platform/mv3/extension/_locales/so/messages.json +++ b/platform/mv3/extension/_locales/so/messages.json @@ -21,7 +21,7 @@ }, "developPageName": { "message": "Develop", - "description": "appears as tab name in dashboard. Inspired from 'Develop' menu in Safari, see " + "description": "appears as tab name in dashboard. Inspired from 'Develop' menu in Safari, see https://developer.apple.com/documentation/safari-developer-tools/develop-menu" }, "aboutPageName": { "message": "About", diff --git a/platform/mv3/extension/_locales/sq/messages.json b/platform/mv3/extension/_locales/sq/messages.json index 3a953418c93f7..4118451f4ce49 100644 --- a/platform/mv3/extension/_locales/sq/messages.json +++ b/platform/mv3/extension/_locales/sq/messages.json @@ -21,7 +21,7 @@ }, "developPageName": { "message": "Develop", - "description": "appears as tab name in dashboard. Inspired from 'Develop' menu in Safari, see " + "description": "appears as tab name in dashboard. Inspired from 'Develop' menu in Safari, see https://developer.apple.com/documentation/safari-developer-tools/develop-menu" }, "aboutPageName": { "message": "Info", diff --git a/platform/mv3/extension/_locales/sr/messages.json b/platform/mv3/extension/_locales/sr/messages.json index 7d60f2faed66c..8affffc7c76da 100644 --- a/platform/mv3/extension/_locales/sr/messages.json +++ b/platform/mv3/extension/_locales/sr/messages.json @@ -21,7 +21,7 @@ }, "developPageName": { "message": "Develop", - "description": "appears as tab name in dashboard. Inspired from 'Develop' menu in Safari, see " + "description": "appears as tab name in dashboard. Inspired from 'Develop' menu in Safari, see https://developer.apple.com/documentation/safari-developer-tools/develop-menu" }, "aboutPageName": { "message": "О апликацији", diff --git a/platform/mv3/extension/_locales/sv/messages.json b/platform/mv3/extension/_locales/sv/messages.json index 5eee1f5599ce0..7b5e087d6ac5c 100644 --- a/platform/mv3/extension/_locales/sv/messages.json +++ b/platform/mv3/extension/_locales/sv/messages.json @@ -21,7 +21,7 @@ }, "developPageName": { "message": "Develop", - "description": "appears as tab name in dashboard. Inspired from 'Develop' menu in Safari, see " + "description": "appears as tab name in dashboard. Inspired from 'Develop' menu in Safari, see https://developer.apple.com/documentation/safari-developer-tools/develop-menu" }, "aboutPageName": { "message": "Om", diff --git a/platform/mv3/extension/_locales/sw/messages.json b/platform/mv3/extension/_locales/sw/messages.json index 484529aa58de1..4316ba863b234 100644 --- a/platform/mv3/extension/_locales/sw/messages.json +++ b/platform/mv3/extension/_locales/sw/messages.json @@ -21,7 +21,7 @@ }, "developPageName": { "message": "Develop", - "description": "appears as tab name in dashboard. Inspired from 'Develop' menu in Safari, see " + "description": "appears as tab name in dashboard. Inspired from 'Develop' menu in Safari, see https://developer.apple.com/documentation/safari-developer-tools/develop-menu" }, "aboutPageName": { "message": "About", diff --git a/platform/mv3/extension/_locales/ta/messages.json b/platform/mv3/extension/_locales/ta/messages.json index 484529aa58de1..4316ba863b234 100644 --- a/platform/mv3/extension/_locales/ta/messages.json +++ b/platform/mv3/extension/_locales/ta/messages.json @@ -21,7 +21,7 @@ }, "developPageName": { "message": "Develop", - "description": "appears as tab name in dashboard. Inspired from 'Develop' menu in Safari, see " + "description": "appears as tab name in dashboard. Inspired from 'Develop' menu in Safari, see https://developer.apple.com/documentation/safari-developer-tools/develop-menu" }, "aboutPageName": { "message": "About", diff --git a/platform/mv3/extension/_locales/te/messages.json b/platform/mv3/extension/_locales/te/messages.json index 003dd51ae97eb..94a09153bd1c1 100644 --- a/platform/mv3/extension/_locales/te/messages.json +++ b/platform/mv3/extension/_locales/te/messages.json @@ -21,7 +21,7 @@ }, "developPageName": { "message": "Develop", - "description": "appears as tab name in dashboard. Inspired from 'Develop' menu in Safari, see " + "description": "appears as tab name in dashboard. Inspired from 'Develop' menu in Safari, see https://developer.apple.com/documentation/safari-developer-tools/develop-menu" }, "aboutPageName": { "message": "మా గురించి", diff --git a/platform/mv3/extension/_locales/th/messages.json b/platform/mv3/extension/_locales/th/messages.json index 9d9a317f3c720..1878d4d5ddaab 100644 --- a/platform/mv3/extension/_locales/th/messages.json +++ b/platform/mv3/extension/_locales/th/messages.json @@ -21,7 +21,7 @@ }, "developPageName": { "message": "Develop", - "description": "appears as tab name in dashboard. Inspired from 'Develop' menu in Safari, see " + "description": "appears as tab name in dashboard. Inspired from 'Develop' menu in Safari, see https://developer.apple.com/documentation/safari-developer-tools/develop-menu" }, "aboutPageName": { "message": "เกี่ยวกับเรา", diff --git a/platform/mv3/extension/_locales/tr/messages.json b/platform/mv3/extension/_locales/tr/messages.json index 8b3ef0095988d..ebdd129556216 100644 --- a/platform/mv3/extension/_locales/tr/messages.json +++ b/platform/mv3/extension/_locales/tr/messages.json @@ -21,7 +21,7 @@ }, "developPageName": { "message": "Develop", - "description": "appears as tab name in dashboard. Inspired from 'Develop' menu in Safari, see " + "description": "appears as tab name in dashboard. Inspired from 'Develop' menu in Safari, see https://developer.apple.com/documentation/safari-developer-tools/develop-menu" }, "aboutPageName": { "message": "Hakkında", diff --git a/platform/mv3/extension/_locales/uk/messages.json b/platform/mv3/extension/_locales/uk/messages.json index 0cc407c49db69..c9c6896c0b5c4 100644 --- a/platform/mv3/extension/_locales/uk/messages.json +++ b/platform/mv3/extension/_locales/uk/messages.json @@ -21,7 +21,7 @@ }, "developPageName": { "message": "Develop", - "description": "appears as tab name in dashboard. Inspired from 'Develop' menu in Safari, see " + "description": "appears as tab name in dashboard. Inspired from 'Develop' menu in Safari, see https://developer.apple.com/documentation/safari-developer-tools/develop-menu" }, "aboutPageName": { "message": "Про застосунок", diff --git a/platform/mv3/extension/_locales/ur/messages.json b/platform/mv3/extension/_locales/ur/messages.json index 7265fe9ace015..463df4a4b364d 100644 --- a/platform/mv3/extension/_locales/ur/messages.json +++ b/platform/mv3/extension/_locales/ur/messages.json @@ -21,7 +21,7 @@ }, "developPageName": { "message": "Develop", - "description": "appears as tab name in dashboard. Inspired from 'Develop' menu in Safari, see " + "description": "appears as tab name in dashboard. Inspired from 'Develop' menu in Safari, see https://developer.apple.com/documentation/safari-developer-tools/develop-menu" }, "aboutPageName": { "message": "تعارف", diff --git a/platform/mv3/extension/_locales/vi/messages.json b/platform/mv3/extension/_locales/vi/messages.json index d1121754ce7c9..40f3ceff8c8aa 100644 --- a/platform/mv3/extension/_locales/vi/messages.json +++ b/platform/mv3/extension/_locales/vi/messages.json @@ -21,7 +21,7 @@ }, "developPageName": { "message": "Develop", - "description": "appears as tab name in dashboard. Inspired from 'Develop' menu in Safari, see " + "description": "appears as tab name in dashboard. Inspired from 'Develop' menu in Safari, see https://developer.apple.com/documentation/safari-developer-tools/develop-menu" }, "aboutPageName": { "message": "Giới thiệu", diff --git a/platform/mv3/extension/_locales/zh_CN/messages.json b/platform/mv3/extension/_locales/zh_CN/messages.json index 3a4ca55db9831..e9c5a7ecee09d 100644 --- a/platform/mv3/extension/_locales/zh_CN/messages.json +++ b/platform/mv3/extension/_locales/zh_CN/messages.json @@ -21,7 +21,7 @@ }, "developPageName": { "message": "Develop", - "description": "appears as tab name in dashboard. Inspired from 'Develop' menu in Safari, see " + "description": "appears as tab name in dashboard. Inspired from 'Develop' menu in Safari, see https://developer.apple.com/documentation/safari-developer-tools/develop-menu" }, "aboutPageName": { "message": "关于", diff --git a/platform/mv3/extension/_locales/zh_TW/messages.json b/platform/mv3/extension/_locales/zh_TW/messages.json index e2cacb9d3dc5a..8582b85200a93 100644 --- a/platform/mv3/extension/_locales/zh_TW/messages.json +++ b/platform/mv3/extension/_locales/zh_TW/messages.json @@ -21,7 +21,7 @@ }, "developPageName": { "message": "Develop", - "description": "appears as tab name in dashboard. Inspired from 'Develop' menu in Safari, see " + "description": "appears as tab name in dashboard. Inspired from 'Develop' menu in Safari, see https://developer.apple.com/documentation/safari-developer-tools/develop-menu" }, "aboutPageName": { "message": "關於", From d8298bb067fc6036117275dfa99be4dc66cd2b8b Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Thu, 29 May 2025 13:23:56 -0400 Subject: [PATCH 0970/1099] Add support for network filter option `message` Related issue: https://github.com/uBlockOrigin/uBlock-issues/issues/1195 --- src/js/static-filtering-parser.js | 6 +++ src/js/static-net-filtering.js | 64 ++++++++++++++++++++++++++----- 2 files changed, 60 insertions(+), 10 deletions(-) diff --git a/src/js/static-filtering-parser.js b/src/js/static-filtering-parser.js index f9dca000054dc..7ff107786f03b 100644 --- a/src/js/static-filtering-parser.js +++ b/src/js/static-filtering-parser.js @@ -174,6 +174,7 @@ export const NODE_TYPE_NET_OPTION_NAME_INLINESCRIPT = iota++; export const NODE_TYPE_NET_OPTION_NAME_IPADDRESS = iota++; export const NODE_TYPE_NET_OPTION_NAME_MATCHCASE = iota++; export const NODE_TYPE_NET_OPTION_NAME_MEDIA = iota++; +export const NODE_TYPE_NET_OPTION_NAME_MESSAGE = iota++; export const NODE_TYPE_NET_OPTION_NAME_METHOD = iota++; export const NODE_TYPE_NET_OPTION_NAME_MP4 = iota++; export const NODE_TYPE_NET_OPTION_NAME_NOOP = iota++; @@ -255,6 +256,7 @@ export const nodeTypeFromOptionName = new Map([ [ 'ipaddress', NODE_TYPE_NET_OPTION_NAME_IPADDRESS ], [ 'match-case', NODE_TYPE_NET_OPTION_NAME_MATCHCASE ], [ 'media', NODE_TYPE_NET_OPTION_NAME_MEDIA ], + [ 'message', NODE_TYPE_NET_OPTION_NAME_MESSAGE ], [ 'method', NODE_TYPE_NET_OPTION_NAME_METHOD ], [ 'mp4', NODE_TYPE_NET_OPTION_NAME_MP4 ], [ '_', NODE_TYPE_NET_OPTION_NAME_NOOP ], @@ -1350,6 +1352,9 @@ export class AstFilterParser { case NODE_TYPE_NET_OPTION_NAME_MATCHCASE: realBad = this.isRegexPattern() === false; break; + case NODE_TYPE_NET_OPTION_NAME_MESSAGE: + realBad = hasValue === false; + break; case NODE_TYPE_NET_OPTION_NAME_PERMISSIONS: realBad = modifierType !== 0 || (hasValue || isException) === false || @@ -3149,6 +3154,7 @@ export const netOptionTokenDescriptors = new Map([ [ 'ipaddress', { mustAssign: true } ], [ 'match-case', { } ], [ 'media', { canNegate: true } ], + [ 'message', { mustAssign: true } ], [ 'method', { mustAssign: true } ], [ 'mp4', { blockOnly: true } ], [ '_', { } ], diff --git a/src/js/static-net-filtering.js b/src/js/static-net-filtering.js index 65eabf94c9ed1..2f0bd8e3f80bc 100644 --- a/src/js/static-net-filtering.js +++ b/src/js/static-net-filtering.js @@ -3103,6 +3103,39 @@ class FilterIPAddress { registerFilterClass(FilterIPAddress); +/******************************************************************************/ + +class FilterMessage { + static match() { + return true; + } + + static compile(details) { + return [ + FilterMessage.fid, + encodeURIComponent(details.optionValues.get('message')), + ]; + } + + static fromCompiled(args) { + const msg = args[1]; + return filterDataAlloc(args[0], bidiTrie.storeString(msg), msg.length); + } + + static keyFromArgs() { + } + + static logData(idata, details) { + const msg = bidiTrie.extractString( + filterData[idata+1], + filterData[idata+2] + ); + details.options.push(`message=${decodeURIComponent(msg)}`); + } +} + +registerFilterClass(FilterMessage); + /******************************************************************************/ /******************************************************************************/ @@ -3578,6 +3611,10 @@ class FilterCompiler { this.optionValues.set('ipaddress', parser.getNetOptionValue(id) || ''); this.optionUnitBits |= IPADDRESS_BIT; break; + case sfp.NODE_TYPE_NET_OPTION_NAME_MESSAGE: + this.optionValues.set('message', parser.getNetOptionValue(id)); + this.optionUnitBits |= MESSAGE_BIT; + break; case sfp.NODE_TYPE_NET_OPTION_NAME_METHOD: this.processMethodOption(parser.getNetOptionValue(id)); this.optionUnitBits |= METHOD_BIT; @@ -3699,6 +3736,7 @@ class FilterCompiler { case sfp.NODE_TYPE_NET_OPTION_NAME_FROM: case sfp.NODE_TYPE_NET_OPTION_NAME_HEADER: case sfp.NODE_TYPE_NET_OPTION_NAME_IPADDRESS: + case sfp.NODE_TYPE_NET_OPTION_NAME_MESSAGE: case sfp.NODE_TYPE_NET_OPTION_NAME_METHOD: case sfp.NODE_TYPE_NET_OPTION_NAME_PERMISSIONS: case sfp.NODE_TYPE_NET_OPTION_NAME_REDIRECT: @@ -4081,6 +4119,11 @@ class FilterCompiler { this.action |= HEADERS_REALM; } + // Message + if ( (this.optionUnitBits & MESSAGE_BIT) !== 0 ) { + units.push(FilterMessage.compile(this)); + } + // Important // // IMPORTANT: must always appear at the end of the sequence, so as to @@ -4175,16 +4218,17 @@ class FilterCompiler { } // These are to quickly test whether a filter is composite -const FROM_BIT = 0b0000000001; -const TO_BIT = 0b0000000010; -const DENYALLOW_BIT = 0b0000000100; -const HEADER_BIT = 0b0000001000; -const STRICT_PARTY_BIT = 0b0000010000; -const MODIFY_BIT = 0b0000100000; -const NOT_TYPE_BIT = 0b0001000000; -const IMPORTANT_BIT = 0b0010000000; -const METHOD_BIT = 0b0100000000; -const IPADDRESS_BIT = 0b1000000000; +const FROM_BIT = 0b00000000001; +const TO_BIT = 0b00000000010; +const DENYALLOW_BIT = 0b00000000100; +const HEADER_BIT = 0b00000001000; +const STRICT_PARTY_BIT = 0b00000010000; +const MODIFY_BIT = 0b00000100000; +const NOT_TYPE_BIT = 0b00001000000; +const IMPORTANT_BIT = 0b00010000000; +const METHOD_BIT = 0b00100000000; +const IPADDRESS_BIT = 0b01000000000; +const MESSAGE_BIT = 0b10000000000 FilterCompiler.prototype.FILTER_OK = 0; FilterCompiler.prototype.FILTER_INVALID = 1; From 90547cdbf3e9b48ed7626dcd1c63b5e19761d4e2 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Thu, 29 May 2025 13:26:13 -0400 Subject: [PATCH 0971/1099] Update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 910a84f942565..4835af53a2441 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,4 @@ +- [Add support for network filter option `message`](https://github.com/gorhill/uBlock/commit/d8298bb067) - [Make `header=` syntax compatible with DNR rules](https://github.com/gorhill/uBlock/commit/408b538e75) - [Counter CodeMirror's `pointer-events: none` on scrollbars](https://github.com/gorhill/uBlock/commit/c44f043ed3) - [Fix element picker issue with explicit dark theme](https://github.com/gorhill/uBlock/commit/0130fdf4a1) From 1824a2bccf2b69441a8463d2da2dd4471f99a080 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Thu, 29 May 2025 13:26:57 -0400 Subject: [PATCH 0972/1099] New revision for dev build --- dist/version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dist/version b/dist/version index ba5694df5f89a..8e1e615855022 100644 --- a/dist/version +++ b/dist/version @@ -1 +1 @@ -1.64.1.1 \ No newline at end of file +1.64.1.2 \ No newline at end of file From 03f06ab6a9ff68ef290481fe8d53c998346df96c Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Thu, 29 May 2025 13:31:30 -0400 Subject: [PATCH 0973/1099] Make Firefox dev build auto-update --- dist/firefox/updates.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dist/firefox/updates.json b/dist/firefox/updates.json index b25fb5dce1d90..d809ee14f424a 100644 --- a/dist/firefox/updates.json +++ b/dist/firefox/updates.json @@ -3,9 +3,9 @@ "uBlock0@raymondhill.net": { "updates": [ { - "version": "1.64.1.1", + "version": "1.64.1.2", "browser_specific_settings": { "gecko": { "strict_min_version": "92.0" } }, - "update_link": "https://github.com/gorhill/uBlock/releases/download/1.64.1b1/uBlock0_1.64.1b1.firefox.signed.xpi" + "update_link": "https://github.com/gorhill/uBlock/releases/download/1.64.1b2/uBlock0_1.64.1b2.firefox.signed.xpi" } ] } From 6221cecf570c5978532cc08305d82275c1c18659 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Thu, 29 May 2025 14:18:46 -0400 Subject: [PATCH 0974/1099] [mv3] Fix Safari's compatiblity layer --- platform/mv3/safari/ext-compat.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform/mv3/safari/ext-compat.js b/platform/mv3/safari/ext-compat.js index 50c8dd0e49146..0d56155e21189 100644 --- a/platform/mv3/safari/ext-compat.js +++ b/platform/mv3/safari/ext-compat.js @@ -53,7 +53,7 @@ const prepareUpdateRules = optionsBefore => { const { addRules, removeRuleIds } = optionsBefore; const addRulesAfter = addRules?.filter(isSupportedRule); if ( Boolean(addRulesAfter?.length || removeRuleIds?.length) === false ) { return; } - addRulesAfter.forEach(r => { + addRulesAfter?.forEach(r => { if ( r.action.redirect?.regexSubstitution === undefined ) { return; } if ( r.condition.requestDomains === undefined ) { return; } r.condition.domains = r.condition.requestDomains; From c142d9af67079d8e2179295035859f70309ff334 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Thu, 29 May 2025 14:33:30 -0400 Subject: [PATCH 0975/1099] [mv3] Fix dnr-parser for Safari compatibility --- platform/mv3/extension/js/develop.js | 2 ++ platform/mv3/extension/js/dnr-parser.js | 20 ++++++++++++++++---- 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/platform/mv3/extension/js/develop.js b/platform/mv3/extension/js/develop.js index 86f60bccfd63c..25a2374551557 100644 --- a/platform/mv3/extension/js/develop.js +++ b/platform/mv3/extension/js/develop.js @@ -245,6 +245,8 @@ function getAutocompleteCandidates(from) { { token: 'urlFilter:', after: ' ' }, { token: 'initiatorDomains:', after: '\n - ' }, { token: 'excludedInitiatorDomains:', after: '\n - ' }, + { token: 'domains:', after: '\n - ' }, + { token: 'excludedDomains:', after: '\n - ' }, { token: 'requestDomains:', after: '\n - ' }, { token: 'excludedRequestDomains:', after: '\n - ' }, { token: 'resourceTypes:', after: '\n - ' }, diff --git a/platform/mv3/extension/js/dnr-parser.js b/platform/mv3/extension/js/dnr-parser.js index 8c4799b8816cd..68d243df0e069 100644 --- a/platform/mv3/extension/js/dnr-parser.js +++ b/platform/mv3/extension/js/dnr-parser.js @@ -294,12 +294,14 @@ const perScopeParsers = { rule.condition.urlFilter = val; break; case 'initiatorDomains': - rule.condition.initiatorDomains = []; - scope.push('initiatorDomains'); + case 'domains': + rule.condition[key] = []; + scope.push(key); break; case 'excludedInitiatorDomains': - rule.condition.excludedInitiatorDomains = []; - scope.push('excludedInitiatorDomains'); + case 'excludedDomains': + rule.condition[key] = []; + scope.push(key); break; case 'requestDomains': rule.condition.requestDomains = []; @@ -348,6 +350,16 @@ const perScopeParsers = { rule.condition.excludedInitiatorDomains.push(node.val); return true; }, + 'condition.domains': function(scope, rule, node) { + if ( node.list !== true ) { return false; } + rule.condition.domains.push(node.val); + return true; + }, + 'condition.excludedDomains': function(scope, rule, node) { + if ( node.list !== true ) { return false; } + rule.condition.excludedDomains.push(node.val); + return true; + }, 'condition.requestDomains': function(scope, rule, node) { if ( node.list !== true ) { return false; } rule.condition.requestDomains.push(node.val); From b69f809d91750727c04fa46350897885a5cd612c Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Thu, 29 May 2025 15:19:03 -0400 Subject: [PATCH 0976/1099] Need this to distinguish FilterMessage instances --- src/js/static-net-filtering.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/js/static-net-filtering.js b/src/js/static-net-filtering.js index 2f0bd8e3f80bc..96b9729153002 100644 --- a/src/js/static-net-filtering.js +++ b/src/js/static-net-filtering.js @@ -3122,7 +3122,8 @@ class FilterMessage { return filterDataAlloc(args[0], bidiTrie.storeString(msg), msg.length); } - static keyFromArgs() { + static keyFromArgs(args) { + return `${args[1]}`; } static logData(idata, details) { From 16a42566ff133f9476cefea2c29532f31825ea21 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Thu, 29 May 2025 18:08:56 -0400 Subject: [PATCH 0977/1099] Update jsonpath tool to show transformation --- tools/jsonpath-tool.html | 57 ++++++++++++++++++++++++++++++---------- 1 file changed, 43 insertions(+), 14 deletions(-) diff --git a/tools/jsonpath-tool.html b/tools/jsonpath-tool.html index 2da7f78974f14..f514842959817 100644 --- a/tools/jsonpath-tool.html +++ b/tools/jsonpath-tool.html @@ -18,6 +18,7 @@ box-sizing: border-box; display: flex; flex-direction: column; + font-size: 16px; gap: 0; height: 100vh; margin: 0; @@ -34,13 +35,29 @@ flex-grow: 1; gap: 0.5em; } -#json-data { +section { + box-sizing: border-box; + display: flex; + gap: 0.5em; +} +section > * { + border: 1px solid gray; box-sizing: border-box; - min-height: 20vh; + flex-grow: 1; + font-family: monospace; + font-size: small; + line-height: 1.2; + max-height: 60cqh; + overflow: auto; + white-space: pre; + width: 50cqw; +} +#jsondata-in { + min-height: 50vh; resize: vertical; } #jsonpath-input { - font-size: 16px; + font-size: medium; } #jsonpath-result { background-color: #eee; @@ -52,7 +69,8 @@

uBO-flavored JSONPath tool

- +
+
 
@@ -98,15 +118,17 @@

uBO-flavored JSONPath tool

import { JSONPath } from '../src/js/jsonpath.js'; function readJSON() { - const textarea = document.querySelector('#json-data'); + const textarea = document.querySelector('#jsondata-in'); + let data; try { - jsonData = JSON.parse(textarea.value); + data = JSON.parse(textarea.value); } catch { - jsonData = {}; + data = {}; } - if ( typeof jsonData !== 'object' || jsonData === null ) { - jsonData = {}; + if ( typeof data !== 'object' || data === null ) { + data = {}; } + return data; } function formatResult(a) { @@ -126,16 +148,23 @@

uBO-flavored JSONPath tool

const input = document.querySelector('#jsonpath-input'); const jsonpath = input.value; jsonp.compile(jsonpath); - const result = formatResult(jsonp.evaluate(jsonData)); - const div = document.querySelector('#jsonpath-result'); - div.textContent = result; + const jsonDataIn = readJSON(); + //const datainDiv = document.querySelector('#jsondata-in'); + //datainDiv.textContent = JSON.stringify(jsonDataIn, null, 2); + const result = formatResult(jsonp.evaluate(jsonDataIn)); + const pathsDiv = document.querySelector('#jsonpath-result'); + pathsDiv.textContent = result; + const jsonDataOut = readJSON(); + jsonp.apply(jsonDataOut); + const dataoutDiv = document.querySelector('#jsondata-out'); + dataoutDiv.textContent = JSON.stringify(jsonDataOut, null, 2); } const jsonp = new JSONPath(); - let jsonData = {}; + let jsonDataIn = {}; { - const textarea = document.querySelector('#json-data'); + const textarea = document.querySelector('#jsondata-in'); textarea.addEventListener('input', ( ) => { readJSON(); process(); From 3d5d8e8220641050487ad143556668cd5082f247 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Fri, 30 May 2025 06:56:10 -0400 Subject: [PATCH 0978/1099] Remove obsolete "shortcut" token from `link rel="icon"` Related issue: https://github.com/uBlockOrigin/uBlock-issues/issues/3634 --- src/advanced-settings.html | 2 +- src/asset-viewer.html | 2 +- src/dashboard.html | 2 +- src/document-blocked.html | 2 +- src/logger-ui.html | 2 +- src/popup-fenix.html | 2 +- src/support.html | 2 +- 7 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/advanced-settings.html b/src/advanced-settings.html index fffd92b4ccd7a..b55bdbad6cf58 100644 --- a/src/advanced-settings.html +++ b/src/advanced-settings.html @@ -14,7 +14,7 @@ - + diff --git a/src/asset-viewer.html b/src/asset-viewer.html index fe12a8c1b3960..5cfc73ebe06e8 100644 --- a/src/asset-viewer.html +++ b/src/asset-viewer.html @@ -12,7 +12,7 @@ - + diff --git a/src/dashboard.html b/src/dashboard.html index 7614d9299c300..1d401c19fc7c1 100644 --- a/src/dashboard.html +++ b/src/dashboard.html @@ -9,7 +9,7 @@ - + diff --git a/src/document-blocked.html b/src/document-blocked.html index da7fdb847e3a1..436ede4eff530 100644 --- a/src/document-blocked.html +++ b/src/document-blocked.html @@ -9,7 +9,7 @@ - +
diff --git a/src/logger-ui.html b/src/logger-ui.html index 06b9c49dc8901..25a00caded6b9 100644 --- a/src/logger-ui.html +++ b/src/logger-ui.html @@ -9,7 +9,7 @@ - + diff --git a/src/popup-fenix.html b/src/popup-fenix.html index ee7ab415a73b3..00eb661ea25d8 100644 --- a/src/popup-fenix.html +++ b/src/popup-fenix.html @@ -9,7 +9,7 @@ - + diff --git a/src/support.html b/src/support.html index 63ca054210b6b..d044646aa86d8 100644 --- a/src/support.html +++ b/src/support.html @@ -14,7 +14,7 @@ - + From fbb96c7234920460f2081ff8b4c57ce2e0615b28 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Fri, 30 May 2025 08:06:25 -0400 Subject: [PATCH 0979/1099] Import translation work from https://crowdin.com/project/ublock --- .../mv3/extension/_locales/ar/messages.json | 2 +- .../mv3/extension/_locales/bg/messages.json | 4 ++-- .../extension/_locales/br_FR/messages.json | 6 ++--- .../mv3/extension/_locales/ca/messages.json | 10 ++++----- .../mv3/extension/_locales/de/messages.json | 4 ++-- .../mv3/extension/_locales/el/messages.json | 2 +- .../mv3/extension/_locales/es/messages.json | 4 ++-- .../mv3/extension/_locales/et/messages.json | 4 ++-- .../mv3/extension/_locales/fr/messages.json | 6 ++--- .../mv3/extension/_locales/fy/messages.json | 6 ++--- .../mv3/extension/_locales/he/messages.json | 4 ++-- .../mv3/extension/_locales/hr/messages.json | 6 ++--- .../mv3/extension/_locales/hu/messages.json | 18 +++++++-------- .../mv3/extension/_locales/it/messages.json | 4 ++-- .../mv3/extension/_locales/lv/messages.json | 4 ++-- .../mv3/extension/_locales/nl/messages.json | 8 +++---- .../mv3/extension/_locales/pl/messages.json | 6 ++--- .../extension/_locales/pt_BR/messages.json | 4 ++-- .../extension/_locales/pt_PT/messages.json | 22 +++++++++---------- .../mv3/extension/_locales/ru/messages.json | 6 ++--- .../mv3/extension/_locales/sk/messages.json | 4 ++-- .../mv3/extension/_locales/sv/messages.json | 4 ++-- .../mv3/extension/_locales/tr/messages.json | 6 ++--- .../mv3/extension/_locales/uk/messages.json | 4 ++-- .../extension/_locales/zh_TW/messages.json | 4 ++-- src/_locales/nl/messages.json | 2 +- 26 files changed, 77 insertions(+), 77 deletions(-) diff --git a/platform/mv3/extension/_locales/ar/messages.json b/platform/mv3/extension/_locales/ar/messages.json index 775872e5f0b25..3d89a51db27ef 100644 --- a/platform/mv3/extension/_locales/ar/messages.json +++ b/platform/mv3/extension/_locales/ar/messages.json @@ -296,7 +296,7 @@ "description": "Tooltip for the button used to exit zapper mode" }, "saveButton": { - "message": "Save", + "message": "حفظ", "description": "Text for buttons used to save changes" }, "revertButton": { diff --git a/platform/mv3/extension/_locales/bg/messages.json b/platform/mv3/extension/_locales/bg/messages.json index 7961a49d63dd8..34e80d6a7ad35 100644 --- a/platform/mv3/extension/_locales/bg/messages.json +++ b/platform/mv3/extension/_locales/bg/messages.json @@ -20,7 +20,7 @@ "description": "appears as tab name in dashboard" }, "developPageName": { - "message": "Develop", + "message": "Разработка", "description": "appears as tab name in dashboard. Inspired from 'Develop' menu in Safari, see https://developer.apple.com/documentation/safari-developer-tools/develop-menu" }, "aboutPageName": { @@ -316,7 +316,7 @@ "description": "Short description of the DNR rules editor pane" }, "dnrRulesCountInfo": { - "message": "Number of registered rules: {count}", + "message": "Брой регистрирани правила: {count}", "description": "Short sentence to report the number of currently registered DNR rules" } } diff --git a/platform/mv3/extension/_locales/br_FR/messages.json b/platform/mv3/extension/_locales/br_FR/messages.json index 4575762a64024..eeac1f758e3cf 100644 --- a/platform/mv3/extension/_locales/br_FR/messages.json +++ b/platform/mv3/extension/_locales/br_FR/messages.json @@ -20,7 +20,7 @@ "description": "appears as tab name in dashboard" }, "developPageName": { - "message": "Develop", + "message": "Diorren", "description": "appears as tab name in dashboard. Inspired from 'Develop' menu in Safari, see https://developer.apple.com/documentation/safari-developer-tools/develop-menu" }, "aboutPageName": { @@ -244,7 +244,7 @@ "description": "Label for a checkbox in the options page" }, "developerModeLegend": { - "message": "Gweredekaat ar fonksionelezhioù azasaet d'an implijerien deknikel.", + "message": "Gweredekaat a ra ar fonksionelezhioù azasaet d'an implijerien deknikel.", "description": "Short description for a checkbox in the options page" }, "findListsPlaceholder": { @@ -316,7 +316,7 @@ "description": "Short description of the DNR rules editor pane" }, "dnrRulesCountInfo": { - "message": "Number of registered rules: {count}", + "message": "Niver a reolennoù marilhet: {count}", "description": "Short sentence to report the number of currently registered DNR rules" } } diff --git a/platform/mv3/extension/_locales/ca/messages.json b/platform/mv3/extension/_locales/ca/messages.json index 6cbc30e85c335..2084539a7b1d7 100644 --- a/platform/mv3/extension/_locales/ca/messages.json +++ b/platform/mv3/extension/_locales/ca/messages.json @@ -20,7 +20,7 @@ "description": "appears as tab name in dashboard" }, "developPageName": { - "message": "Develop", + "message": "Desenvolupament", "description": "appears as tab name in dashboard. Inspired from 'Develop' menu in Safari, see https://developer.apple.com/documentation/safari-developer-tools/develop-menu" }, "aboutPageName": { @@ -300,15 +300,15 @@ "description": "Text for buttons used to save changes" }, "revertButton": { - "message": "Reverteix", + "message": "Restaura", "description": "Text for buttons used to revert changes" }, "importAndAppendButton": { - "message": "Importa i annexa...", + "message": "Importa i annexa…", "description": "Text for buttons used to import and append content" }, "exportButton": { - "message": "Exporta...", + "message": "Exporta…", "description": "Text for buttons used to export content" }, "dnrRulesSummary": { @@ -316,7 +316,7 @@ "description": "Short description of the DNR rules editor pane" }, "dnrRulesCountInfo": { - "message": "Number of registered rules: {count}", + "message": "Nombre de regles registrades: {count}", "description": "Short sentence to report the number of currently registered DNR rules" } } diff --git a/platform/mv3/extension/_locales/de/messages.json b/platform/mv3/extension/_locales/de/messages.json index 62dd91341809e..47092414c00fb 100644 --- a/platform/mv3/extension/_locales/de/messages.json +++ b/platform/mv3/extension/_locales/de/messages.json @@ -20,7 +20,7 @@ "description": "appears as tab name in dashboard" }, "developPageName": { - "message": "Develop", + "message": "Entwickeln", "description": "appears as tab name in dashboard. Inspired from 'Develop' menu in Safari, see https://developer.apple.com/documentation/safari-developer-tools/develop-menu" }, "aboutPageName": { @@ -316,7 +316,7 @@ "description": "Short description of the DNR rules editor pane" }, "dnrRulesCountInfo": { - "message": "Number of registered rules: {count}", + "message": "Anzahl registrierter Regeln: {count}", "description": "Short sentence to report the number of currently registered DNR rules" } } diff --git a/platform/mv3/extension/_locales/el/messages.json b/platform/mv3/extension/_locales/el/messages.json index 5397069e0c6bd..5fcb35fb71a69 100644 --- a/platform/mv3/extension/_locales/el/messages.json +++ b/platform/mv3/extension/_locales/el/messages.json @@ -20,7 +20,7 @@ "description": "appears as tab name in dashboard" }, "developPageName": { - "message": "Develop", + "message": "Ανάπτυξη", "description": "appears as tab name in dashboard. Inspired from 'Develop' menu in Safari, see https://developer.apple.com/documentation/safari-developer-tools/develop-menu" }, "aboutPageName": { diff --git a/platform/mv3/extension/_locales/es/messages.json b/platform/mv3/extension/_locales/es/messages.json index 545fcea4cb2c2..46af90107a6c4 100644 --- a/platform/mv3/extension/_locales/es/messages.json +++ b/platform/mv3/extension/_locales/es/messages.json @@ -20,7 +20,7 @@ "description": "appears as tab name in dashboard" }, "developPageName": { - "message": "Develop", + "message": "Desarrollo", "description": "appears as tab name in dashboard. Inspired from 'Develop' menu in Safari, see https://developer.apple.com/documentation/safari-developer-tools/develop-menu" }, "aboutPageName": { @@ -316,7 +316,7 @@ "description": "Short description of the DNR rules editor pane" }, "dnrRulesCountInfo": { - "message": "Number of registered rules: {count}", + "message": "Número de reglas registradas: {count}", "description": "Short sentence to report the number of currently registered DNR rules" } } diff --git a/platform/mv3/extension/_locales/et/messages.json b/platform/mv3/extension/_locales/et/messages.json index 3dc19e11edb28..77bf8558841a6 100644 --- a/platform/mv3/extension/_locales/et/messages.json +++ b/platform/mv3/extension/_locales/et/messages.json @@ -20,7 +20,7 @@ "description": "appears as tab name in dashboard" }, "developPageName": { - "message": "Develop", + "message": "Arendajale", "description": "appears as tab name in dashboard. Inspired from 'Develop' menu in Safari, see https://developer.apple.com/documentation/safari-developer-tools/develop-menu" }, "aboutPageName": { @@ -316,7 +316,7 @@ "description": "Short description of the DNR rules editor pane" }, "dnrRulesCountInfo": { - "message": "Number of registered rules: {count}", + "message": "Registreeritud reeglite arv: {count}", "description": "Short sentence to report the number of currently registered DNR rules" } } diff --git a/platform/mv3/extension/_locales/fr/messages.json b/platform/mv3/extension/_locales/fr/messages.json index 7d1a32a151353..5af39f688c240 100644 --- a/platform/mv3/extension/_locales/fr/messages.json +++ b/platform/mv3/extension/_locales/fr/messages.json @@ -20,7 +20,7 @@ "description": "appears as tab name in dashboard" }, "developPageName": { - "message": "Develop", + "message": "Développement", "description": "appears as tab name in dashboard. Inspired from 'Develop' menu in Safari, see https://developer.apple.com/documentation/safari-developer-tools/develop-menu" }, "aboutPageName": { @@ -244,7 +244,7 @@ "description": "Label for a checkbox in the options page" }, "developerModeLegend": { - "message": "Activer l'accès aux fonctionnalités adaptées aux utilisateurs techniques.", + "message": "Active l'accès aux fonctionnalités adaptées aux utilisateurs techniques.", "description": "Short description for a checkbox in the options page" }, "findListsPlaceholder": { @@ -316,7 +316,7 @@ "description": "Short description of the DNR rules editor pane" }, "dnrRulesCountInfo": { - "message": "Number of registered rules: {count}", + "message": "Nombre de règles enregistrées : {count}", "description": "Short sentence to report the number of currently registered DNR rules" } } diff --git a/platform/mv3/extension/_locales/fy/messages.json b/platform/mv3/extension/_locales/fy/messages.json index 30a57ddfa15ab..56da0e6d38afa 100644 --- a/platform/mv3/extension/_locales/fy/messages.json +++ b/platform/mv3/extension/_locales/fy/messages.json @@ -20,7 +20,7 @@ "description": "appears as tab name in dashboard" }, "developPageName": { - "message": "Develop", + "message": "Untwikkelje", "description": "appears as tab name in dashboard. Inspired from 'Develop' menu in Safari, see https://developer.apple.com/documentation/safari-developer-tools/develop-menu" }, "aboutPageName": { @@ -244,7 +244,7 @@ "description": "Label for a checkbox in the options page" }, "developerModeLegend": { - "message": "Tagong ta foar technyske brûkers geskikte funksjes ynskeakelje.", + "message": "Skeakelet tagong ta foar technyske brûkers geskikte funksjes yn.", "description": "Short description for a checkbox in the options page" }, "findListsPlaceholder": { @@ -316,7 +316,7 @@ "description": "Short description of the DNR rules editor pane" }, "dnrRulesCountInfo": { - "message": "Number of registered rules: {count}", + "message": "Oantal registrearre regels: {count}", "description": "Short sentence to report the number of currently registered DNR rules" } } diff --git a/platform/mv3/extension/_locales/he/messages.json b/platform/mv3/extension/_locales/he/messages.json index e360181580ee8..ad98a609554e4 100644 --- a/platform/mv3/extension/_locales/he/messages.json +++ b/platform/mv3/extension/_locales/he/messages.json @@ -20,7 +20,7 @@ "description": "appears as tab name in dashboard" }, "developPageName": { - "message": "Develop", + "message": "פיתוח", "description": "appears as tab name in dashboard. Inspired from 'Develop' menu in Safari, see https://developer.apple.com/documentation/safari-developer-tools/develop-menu" }, "aboutPageName": { @@ -316,7 +316,7 @@ "description": "Short description of the DNR rules editor pane" }, "dnrRulesCountInfo": { - "message": "Number of registered rules: {count}", + "message": "מספר החוקים שנרשמו: {count}", "description": "Short sentence to report the number of currently registered DNR rules" } } diff --git a/platform/mv3/extension/_locales/hr/messages.json b/platform/mv3/extension/_locales/hr/messages.json index d7aa10abb0bbe..5d831d996a840 100644 --- a/platform/mv3/extension/_locales/hr/messages.json +++ b/platform/mv3/extension/_locales/hr/messages.json @@ -20,7 +20,7 @@ "description": "appears as tab name in dashboard" }, "developPageName": { - "message": "Develop", + "message": "Razvoj", "description": "appears as tab name in dashboard. Inspired from 'Develop' menu in Safari, see https://developer.apple.com/documentation/safari-developer-tools/develop-menu" }, "aboutPageName": { @@ -244,7 +244,7 @@ "description": "Label for a checkbox in the options page" }, "developerModeLegend": { - "message": "Omogućite pristup značajkama prikladnim za tehničke korisnike.", + "message": "Omogućuje pristup značajkama prikladnim za tehničke korisnike.", "description": "Short description for a checkbox in the options page" }, "findListsPlaceholder": { @@ -316,7 +316,7 @@ "description": "Short description of the DNR rules editor pane" }, "dnrRulesCountInfo": { - "message": "Number of registered rules: {count}", + "message": "Broj registriranih pravila: {count}", "description": "Short sentence to report the number of currently registered DNR rules" } } diff --git a/platform/mv3/extension/_locales/hu/messages.json b/platform/mv3/extension/_locales/hu/messages.json index dc6421a5131e0..2b06a70e29454 100644 --- a/platform/mv3/extension/_locales/hu/messages.json +++ b/platform/mv3/extension/_locales/hu/messages.json @@ -20,7 +20,7 @@ "description": "appears as tab name in dashboard" }, "developPageName": { - "message": "Develop", + "message": "Fejlesztés", "description": "appears as tab name in dashboard. Inspired from 'Develop' menu in Safari, see https://developer.apple.com/documentation/safari-developer-tools/develop-menu" }, "aboutPageName": { @@ -240,11 +240,11 @@ "description": "Short description for a checkbox in the options page" }, "developerModeLabel": { - "message": "Developer mode", + "message": "Fejlesztői mód", "description": "Label for a checkbox in the options page" }, "developerModeLegend": { - "message": "Enables access to features suitable for technical users.", + "message": "Hozzáférést biztosít a képzettebb felhasználóknak való funkciókhoz.", "description": "Short description for a checkbox in the options page" }, "findListsPlaceholder": { @@ -296,27 +296,27 @@ "description": "Tooltip for the button used to exit zapper mode" }, "saveButton": { - "message": "Save", + "message": "Mentés", "description": "Text for buttons used to save changes" }, "revertButton": { - "message": "Revert", + "message": "Visszaállítás", "description": "Text for buttons used to revert changes" }, "importAndAppendButton": { - "message": "Import and append…", + "message": "Importálás és hozzáadás…", "description": "Text for buttons used to import and append content" }, "exportButton": { - "message": "Export…", + "message": "Exportálás…", "description": "Text for buttons used to export content" }, "dnrRulesSummary": { - "message": "Enter your own DNR rules below. Do not add content from untrusted sources.", + "message": "Adja meg alább a saját DNR szabályait. Ne adjon hozzá nem megbízható forrásokból származó tartalmat.", "description": "Short description of the DNR rules editor pane" }, "dnrRulesCountInfo": { - "message": "Number of registered rules: {count}", + "message": "Regisztrált szabályok száma: {count}", "description": "Short sentence to report the number of currently registered DNR rules" } } diff --git a/platform/mv3/extension/_locales/it/messages.json b/platform/mv3/extension/_locales/it/messages.json index 9db0c41a4d2ac..a41a55842a791 100644 --- a/platform/mv3/extension/_locales/it/messages.json +++ b/platform/mv3/extension/_locales/it/messages.json @@ -20,7 +20,7 @@ "description": "appears as tab name in dashboard" }, "developPageName": { - "message": "Develop", + "message": "Sviluppo", "description": "appears as tab name in dashboard. Inspired from 'Develop' menu in Safari, see https://developer.apple.com/documentation/safari-developer-tools/develop-menu" }, "aboutPageName": { @@ -316,7 +316,7 @@ "description": "Short description of the DNR rules editor pane" }, "dnrRulesCountInfo": { - "message": "Number of registered rules: {count}", + "message": "Numero di regole registrate: {count}", "description": "Short sentence to report the number of currently registered DNR rules" } } diff --git a/platform/mv3/extension/_locales/lv/messages.json b/platform/mv3/extension/_locales/lv/messages.json index 96b18972e16e2..8f6978e9f2843 100644 --- a/platform/mv3/extension/_locales/lv/messages.json +++ b/platform/mv3/extension/_locales/lv/messages.json @@ -20,7 +20,7 @@ "description": "appears as tab name in dashboard" }, "developPageName": { - "message": "Develop", + "message": "Izstrādāt", "description": "appears as tab name in dashboard. Inspired from 'Develop' menu in Safari, see https://developer.apple.com/documentation/safari-developer-tools/develop-menu" }, "aboutPageName": { @@ -316,7 +316,7 @@ "description": "Short description of the DNR rules editor pane" }, "dnrRulesCountInfo": { - "message": "Number of registered rules: {count}", + "message": "Reģistrēto kārtulu skaits: {count}", "description": "Short sentence to report the number of currently registered DNR rules" } } diff --git a/platform/mv3/extension/_locales/nl/messages.json b/platform/mv3/extension/_locales/nl/messages.json index 27dc548bf6bc1..c6f9f026bf57a 100644 --- a/platform/mv3/extension/_locales/nl/messages.json +++ b/platform/mv3/extension/_locales/nl/messages.json @@ -20,7 +20,7 @@ "description": "appears as tab name in dashboard" }, "developPageName": { - "message": "Develop", + "message": "Ontwikkelen", "description": "appears as tab name in dashboard. Inspired from 'Develop' menu in Safari, see https://developer.apple.com/documentation/safari-developer-tools/develop-menu" }, "aboutPageName": { @@ -120,7 +120,7 @@ "description": "Label of 'Troubleshooting information' section in 'Report a filter issue' page" }, "supportS6P1S1": { - "message": "Controleer of het probleem niet eerder is gemeld om te voorkomen dat vrijwilligers met dubbele meldingen worden belast.", + "message": "Controleer of het probleem niet eerder is gemeld om te voorkomen dat vrijwilligers met dubbele meldingen worden belast. Noot: op de knop klikken zorgt ervoor dat de oorsprong van de pagina naar GitHub wordt verzonden.", "description": "A paragraph in the filter issue reporter section" }, "supportFindSpecificButton": { @@ -244,7 +244,7 @@ "description": "Label for a checkbox in the options page" }, "developerModeLegend": { - "message": "Toegang tot voor technische gebruikers geschikte functies inschaken.", + "message": "Schakelt toegang tot voor technische gebruikers geschikte functies in.", "description": "Short description for a checkbox in the options page" }, "findListsPlaceholder": { @@ -316,7 +316,7 @@ "description": "Short description of the DNR rules editor pane" }, "dnrRulesCountInfo": { - "message": "Number of registered rules: {count}", + "message": "Aantal geregistreerde regels: {count}", "description": "Short sentence to report the number of currently registered DNR rules" } } diff --git a/platform/mv3/extension/_locales/pl/messages.json b/platform/mv3/extension/_locales/pl/messages.json index f7fe13b2cefcc..089a51cdb51f7 100644 --- a/platform/mv3/extension/_locales/pl/messages.json +++ b/platform/mv3/extension/_locales/pl/messages.json @@ -20,7 +20,7 @@ "description": "appears as tab name in dashboard" }, "developPageName": { - "message": "Develop", + "message": "Programowanie", "description": "appears as tab name in dashboard. Inspired from 'Develop' menu in Safari, see https://developer.apple.com/documentation/safari-developer-tools/develop-menu" }, "aboutPageName": { @@ -312,11 +312,11 @@ "description": "Text for buttons used to export content" }, "dnrRulesSummary": { - "message": "Wprowadź poniżej własne zasady DNR. Nie dodawaj treści z niezaufanych źródeł.", + "message": "Wprowadź poniżej własne reguły DNR. Nie dodawaj treści z niezaufanych źródeł.", "description": "Short description of the DNR rules editor pane" }, "dnrRulesCountInfo": { - "message": "Number of registered rules: {count}", + "message": "Liczba zarejestrowanych reguł: {count}", "description": "Short sentence to report the number of currently registered DNR rules" } } diff --git a/platform/mv3/extension/_locales/pt_BR/messages.json b/platform/mv3/extension/_locales/pt_BR/messages.json index 8d9a3d39f7d4a..8c1c4d583be5d 100644 --- a/platform/mv3/extension/_locales/pt_BR/messages.json +++ b/platform/mv3/extension/_locales/pt_BR/messages.json @@ -20,7 +20,7 @@ "description": "appears as tab name in dashboard" }, "developPageName": { - "message": "Develop", + "message": "Desenvolvedor", "description": "appears as tab name in dashboard. Inspired from 'Develop' menu in Safari, see https://developer.apple.com/documentation/safari-developer-tools/develop-menu" }, "aboutPageName": { @@ -316,7 +316,7 @@ "description": "Short description of the DNR rules editor pane" }, "dnrRulesCountInfo": { - "message": "Number of registered rules: {count}", + "message": "Número de regras registradas: {count}", "description": "Short sentence to report the number of currently registered DNR rules" } } diff --git a/platform/mv3/extension/_locales/pt_PT/messages.json b/platform/mv3/extension/_locales/pt_PT/messages.json index c167022eee290..36553b171f11a 100644 --- a/platform/mv3/extension/_locales/pt_PT/messages.json +++ b/platform/mv3/extension/_locales/pt_PT/messages.json @@ -4,7 +4,7 @@ "description": "extension name." }, "extShortDesc": { - "message": "Um bloqueador de conteúdo sem permissões. Bloqueia anúncios, rastreadores e muito mais, imediatamente após a instalação.", + "message": "Um bloqueador de conteúdo eficiente. Bloqueia anúncios, rastreadores, mineradores e muito mais imediatamente após a instalação.", "description": "this will be in the Chrome web store: must be 132 characters or less" }, "perRulesetStats": { @@ -20,7 +20,7 @@ "description": "appears as tab name in dashboard" }, "developPageName": { - "message": "Develop", + "message": "Desenvolver", "description": "appears as tab name in dashboard. Inspired from 'Develop' menu in Safari, see https://developer.apple.com/documentation/safari-developer-tools/develop-menu" }, "aboutPageName": { @@ -116,7 +116,7 @@ "description": "First paragraph of 'Filter issues' section in Support pane" }, "supportS5H": { - "message": "Informações sobre a resolução de problemas", + "message": "Informação sobre resolução de problemas", "description": "Label of 'Troubleshooting information' section in 'Report a filter issue' page" }, "supportS6P1S1": { @@ -240,11 +240,11 @@ "description": "Short description for a checkbox in the options page" }, "developerModeLabel": { - "message": "Developer mode", + "message": "Modo de programador", "description": "Label for a checkbox in the options page" }, "developerModeLegend": { - "message": "Enables access to features suitable for technical users.", + "message": "Permite acesso a funcionalidades adequadas a utilizadores técnicos", "description": "Short description for a checkbox in the options page" }, "findListsPlaceholder": { @@ -296,27 +296,27 @@ "description": "Tooltip for the button used to exit zapper mode" }, "saveButton": { - "message": "Save", + "message": "Guardar", "description": "Text for buttons used to save changes" }, "revertButton": { - "message": "Revert", + "message": "Reverter", "description": "Text for buttons used to revert changes" }, "importAndAppendButton": { - "message": "Import and append…", + "message": "Importar e anexar…", "description": "Text for buttons used to import and append content" }, "exportButton": { - "message": "Export…", + "message": "Exportar…", "description": "Text for buttons used to export content" }, "dnrRulesSummary": { - "message": "Enter your own DNR rules below. Do not add content from untrusted sources.", + "message": "Insira as suas próprias regras de DNR abaixo. Não adicione conteúdo de fontes não confiáveis.", "description": "Short description of the DNR rules editor pane" }, "dnrRulesCountInfo": { - "message": "Number of registered rules: {count}", + "message": "Número de regras registadas: {count}", "description": "Short sentence to report the number of currently registered DNR rules" } } diff --git a/platform/mv3/extension/_locales/ru/messages.json b/platform/mv3/extension/_locales/ru/messages.json index 7382002040621..4c58f5027967a 100644 --- a/platform/mv3/extension/_locales/ru/messages.json +++ b/platform/mv3/extension/_locales/ru/messages.json @@ -20,7 +20,7 @@ "description": "appears as tab name in dashboard" }, "developPageName": { - "message": "Develop", + "message": "Разработка", "description": "appears as tab name in dashboard. Inspired from 'Develop' menu in Safari, see https://developer.apple.com/documentation/safari-developer-tools/develop-menu" }, "aboutPageName": { @@ -244,7 +244,7 @@ "description": "Label for a checkbox in the options page" }, "developerModeLegend": { - "message": "Включить доступ к функциям, предназначенным для технических пользователей.", + "message": "Включает доступ к функциям, предназначенным для технических пользователей.", "description": "Short description for a checkbox in the options page" }, "findListsPlaceholder": { @@ -316,7 +316,7 @@ "description": "Short description of the DNR rules editor pane" }, "dnrRulesCountInfo": { - "message": "Number of registered rules: {count}", + "message": "Число зарегистрированных правил: {count}", "description": "Short sentence to report the number of currently registered DNR rules" } } diff --git a/platform/mv3/extension/_locales/sk/messages.json b/platform/mv3/extension/_locales/sk/messages.json index 1d10107dac508..de46718e121f6 100644 --- a/platform/mv3/extension/_locales/sk/messages.json +++ b/platform/mv3/extension/_locales/sk/messages.json @@ -20,7 +20,7 @@ "description": "appears as tab name in dashboard" }, "developPageName": { - "message": "Develop", + "message": "Vývoj", "description": "appears as tab name in dashboard. Inspired from 'Develop' menu in Safari, see https://developer.apple.com/documentation/safari-developer-tools/develop-menu" }, "aboutPageName": { @@ -316,7 +316,7 @@ "description": "Short description of the DNR rules editor pane" }, "dnrRulesCountInfo": { - "message": "Number of registered rules: {count}", + "message": "Počet zaregistrovaných pravidiel: {count}", "description": "Short sentence to report the number of currently registered DNR rules" } } diff --git a/platform/mv3/extension/_locales/sv/messages.json b/platform/mv3/extension/_locales/sv/messages.json index 7b5e087d6ac5c..646ca4b8ac9a7 100644 --- a/platform/mv3/extension/_locales/sv/messages.json +++ b/platform/mv3/extension/_locales/sv/messages.json @@ -20,7 +20,7 @@ "description": "appears as tab name in dashboard" }, "developPageName": { - "message": "Develop", + "message": "Utveckla", "description": "appears as tab name in dashboard. Inspired from 'Develop' menu in Safari, see https://developer.apple.com/documentation/safari-developer-tools/develop-menu" }, "aboutPageName": { @@ -316,7 +316,7 @@ "description": "Short description of the DNR rules editor pane" }, "dnrRulesCountInfo": { - "message": "Number of registered rules: {count}", + "message": "Antal registrerade regler: {count}", "description": "Short sentence to report the number of currently registered DNR rules" } } diff --git a/platform/mv3/extension/_locales/tr/messages.json b/platform/mv3/extension/_locales/tr/messages.json index ebdd129556216..af1422aabb74d 100644 --- a/platform/mv3/extension/_locales/tr/messages.json +++ b/platform/mv3/extension/_locales/tr/messages.json @@ -4,7 +4,7 @@ "description": "extension name." }, "extShortDesc": { - "message": "A permission-less content blocker. Blocks ads, trackers, miners, and more immediately upon installation.", + "message": "Etkili bir içerik engelleyici. Reklamları, izleyicileri, madencileri ve daha fazlasını kurulumdan hemen sonra engeller.", "description": "this will be in the Chrome web store: must be 132 characters or less" }, "perRulesetStats": { @@ -20,7 +20,7 @@ "description": "appears as tab name in dashboard" }, "developPageName": { - "message": "Develop", + "message": "Geliştir", "description": "appears as tab name in dashboard. Inspired from 'Develop' menu in Safari, see https://developer.apple.com/documentation/safari-developer-tools/develop-menu" }, "aboutPageName": { @@ -316,7 +316,7 @@ "description": "Short description of the DNR rules editor pane" }, "dnrRulesCountInfo": { - "message": "Number of registered rules: {count}", + "message": "Kayıtlı kural sayısı: {count}", "description": "Short sentence to report the number of currently registered DNR rules" } } diff --git a/platform/mv3/extension/_locales/uk/messages.json b/platform/mv3/extension/_locales/uk/messages.json index c9c6896c0b5c4..8a9f927fcdb21 100644 --- a/platform/mv3/extension/_locales/uk/messages.json +++ b/platform/mv3/extension/_locales/uk/messages.json @@ -20,7 +20,7 @@ "description": "appears as tab name in dashboard" }, "developPageName": { - "message": "Develop", + "message": "Розробка", "description": "appears as tab name in dashboard. Inspired from 'Develop' menu in Safari, see https://developer.apple.com/documentation/safari-developer-tools/develop-menu" }, "aboutPageName": { @@ -316,7 +316,7 @@ "description": "Short description of the DNR rules editor pane" }, "dnrRulesCountInfo": { - "message": "Number of registered rules: {count}", + "message": "Кількість зареєстрованих правил: {count}", "description": "Short sentence to report the number of currently registered DNR rules" } } diff --git a/platform/mv3/extension/_locales/zh_TW/messages.json b/platform/mv3/extension/_locales/zh_TW/messages.json index 8582b85200a93..ba093a1fc9da4 100644 --- a/platform/mv3/extension/_locales/zh_TW/messages.json +++ b/platform/mv3/extension/_locales/zh_TW/messages.json @@ -20,7 +20,7 @@ "description": "appears as tab name in dashboard" }, "developPageName": { - "message": "Develop", + "message": "開發", "description": "appears as tab name in dashboard. Inspired from 'Develop' menu in Safari, see https://developer.apple.com/documentation/safari-developer-tools/develop-menu" }, "aboutPageName": { @@ -316,7 +316,7 @@ "description": "Short description of the DNR rules editor pane" }, "dnrRulesCountInfo": { - "message": "Number of registered rules: {count}", + "message": "已登錄規則數:{count}", "description": "Short sentence to report the number of currently registered DNR rules" } } diff --git a/src/_locales/nl/messages.json b/src/_locales/nl/messages.json index 657df8ca49d60..691690677810d 100644 --- a/src/_locales/nl/messages.json +++ b/src/_locales/nl/messages.json @@ -900,7 +900,7 @@ "description": "Text for button which open an external webpage in Support pane" }, "supportReportSpecificButton": { - "message": "Nieuwe melding maken", + "message": "Nieuwe melding op GitHub maken", "description": "Text for button which open an external webpage in Support pane" }, "supportFindSpecificButton": { From ed9999efd64662005c1c11eed0724635b908d121 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Fri, 30 May 2025 11:06:33 -0400 Subject: [PATCH 0980/1099] Improve `jsonl[...]` suite of scriptlets Reuse original line separator when reassembling JSONL lines. --- src/js/resources/json-edit.js | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/js/resources/json-edit.js b/src/js/resources/json-edit.js index 8a2af5928f215..4b03bfd08d43f 100644 --- a/src/js/resources/json-edit.js +++ b/src/js/resources/json-edit.js @@ -374,7 +374,8 @@ registerScriptlet(trustedJsonEditFetchResponse, { function jsonlEditFn(jsonp, text = '') { const safe = safeSelf(); - const linesBefore = text.split(/\n+/); + const lineSeparator = /\r?\n/.exec(text)?.[0] || '\n'; + const linesBefore = text.split(lineSeparator); const linesAfter = []; for ( const lineBefore of linesBefore ) { let obj; @@ -387,9 +388,10 @@ function jsonlEditFn(jsonp, text = '') { linesAfter.push(lineBefore); continue; } - linesAfter.push(JSONPath.toJSON(obj, safe.JSON_stringify)); + const lineAfter = safe.JSON_stringify(obj); + linesAfter.push(lineAfter); } - return linesAfter.join('\n'); + return linesAfter.join(lineSeparator); } registerScriptlet(jsonlEditFn, { name: 'jsonl-edit.fn', From 44f04e3d3b4d13559b5d61b3c93fb354b6efd76f Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Fri, 30 May 2025 11:10:19 -0400 Subject: [PATCH 0981/1099] Update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4835af53a2441..70696616d830f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,4 @@ +- [Improve `jsonl[...\]` suite of scriptlets](https://github.com/gorhill/uBlock/commit/ed9999efd6) - [Add support for network filter option `message`](https://github.com/gorhill/uBlock/commit/d8298bb067) - [Make `header=` syntax compatible with DNR rules](https://github.com/gorhill/uBlock/commit/408b538e75) - [Counter CodeMirror's `pointer-events: none` on scrollbars](https://github.com/gorhill/uBlock/commit/c44f043ed3) From bc32794a47b4afd0870ce64175a5ae13340dd0be Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Fri, 30 May 2025 11:10:39 -0400 Subject: [PATCH 0982/1099] New revision for dev build --- dist/version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dist/version b/dist/version index 8e1e615855022..c1eb358f1c237 100644 --- a/dist/version +++ b/dist/version @@ -1 +1 @@ -1.64.1.2 \ No newline at end of file +1.64.1.3 \ No newline at end of file From 008fc488c17f77dd8165137668a4feffd39478a3 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Fri, 30 May 2025 11:16:22 -0400 Subject: [PATCH 0983/1099] Make Firefox dev build auto-update --- dist/firefox/updates.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dist/firefox/updates.json b/dist/firefox/updates.json index d809ee14f424a..7f14531ac0fdc 100644 --- a/dist/firefox/updates.json +++ b/dist/firefox/updates.json @@ -3,9 +3,9 @@ "uBlock0@raymondhill.net": { "updates": [ { - "version": "1.64.1.2", + "version": "1.64.1.3", "browser_specific_settings": { "gecko": { "strict_min_version": "92.0" } }, - "update_link": "https://github.com/gorhill/uBlock/releases/download/1.64.1b2/uBlock0_1.64.1b2.firefox.signed.xpi" + "update_link": "https://github.com/gorhill/uBlock/releases/download/1.64.1b3/uBlock0_1.64.1b3.firefox.signed.xpi" } ] } From 55d45a6fb156dbfe85588878a1e4dc2109699fef Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Fri, 30 May 2025 11:19:12 -0400 Subject: [PATCH 0984/1099] Code review: safer to split on official line separator Will still join lines using the detected line separator. --- src/js/resources/json-edit.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/js/resources/json-edit.js b/src/js/resources/json-edit.js index 4b03bfd08d43f..39dec68c223df 100644 --- a/src/js/resources/json-edit.js +++ b/src/js/resources/json-edit.js @@ -375,7 +375,7 @@ registerScriptlet(trustedJsonEditFetchResponse, { function jsonlEditFn(jsonp, text = '') { const safe = safeSelf(); const lineSeparator = /\r?\n/.exec(text)?.[0] || '\n'; - const linesBefore = text.split(lineSeparator); + const linesBefore = text.split('\n'); const linesAfter = []; for ( const lineBefore of linesBefore ) { let obj; From aa1c8137635051642561465cf60ae347f6fa8aba Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Fri, 30 May 2025 11:20:08 -0400 Subject: [PATCH 0985/1099] Update changelog --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 70696616d830f..bed3c42a4db8b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,4 @@ -- [Improve `jsonl[...\]` suite of scriptlets](https://github.com/gorhill/uBlock/commit/ed9999efd6) +- [Improve `jsonl[...]` suite of scriptlets](https://github.com/gorhill/uBlock/commit/ed9999efd6) - [Add support for network filter option `message`](https://github.com/gorhill/uBlock/commit/d8298bb067) - [Make `header=` syntax compatible with DNR rules](https://github.com/gorhill/uBlock/commit/408b538e75) - [Counter CodeMirror's `pointer-events: none` on scrollbars](https://github.com/gorhill/uBlock/commit/c44f043ed3) From ffb3fef0731cc0af32ffb728d726a083ab95d017 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Fri, 30 May 2025 11:23:45 -0400 Subject: [PATCH 0986/1099] [mv3] Add smart backspace in DNR rules editor --- platform/mv3/extension/js/develop.js | 52 +++++++++++++++++++--------- 1 file changed, 36 insertions(+), 16 deletions(-) diff --git a/platform/mv3/extension/js/develop.js b/platform/mv3/extension/js/develop.js index 25a2374551557..f4b1c83114135 100644 --- a/platform/mv3/extension/js/develop.js +++ b/platform/mv3/extension/js/develop.js @@ -65,8 +65,20 @@ function snapToYamlDocument(doc, start, end) { return { yamlDocStart, yamlDocEnd }; } -function addToModifiedRange(doc, start, end) { - const { yamlDocStart, yamlDocEnd } = snapToYamlDocument(doc, start, end); +function rangeFromTransaction(transaction) { + let from, to; + transaction.changes.iterChangedRanges((fromA, toA, fromB, toB) => { + if ( from === undefined || fromB < from ) { from = fromB; } + if ( to === undefined || toB > to ) { to = toB; } + }); + return { from, to }; +} + +function addToModifiedRange(transaction) { + const { newDoc } = transaction; + const { from, to } = rangeFromTransaction(transaction); + if ( from === undefined || to === undefined ) { return; } + const { yamlDocStart, yamlDocEnd } = snapToYamlDocument(newDoc, from, to); if ( modifiedRange.start === -1 || yamlDocStart < modifiedRange.start ) { modifiedRange.start = yamlDocStart; } @@ -551,10 +563,11 @@ dom.on('#dnrRulesExport', 'click', exportRulesToFile); /******************************************************************************/ -function importRulesFromPaste(from, to) { +function importRulesFromPaste(transaction) { + const { from, to } = rangeFromTransaction(transaction); if ( from === undefined || to === undefined ) { return; } // Paste position must match start of a line - const { doc } = cmRules.state; + const { doc } = transaction.newDoc; const lineFrom = doc.lineAt(from); if ( lineFrom.from !== from ) { return; } // Paste position must match a rule boundary @@ -568,26 +581,33 @@ function importRulesFromPaste(from, to) { const yamlText = textFromRules(rules); if ( yamlText === undefined ) { return; } cmRules.dispatch({ changes: { from, to, insert: yamlText } }); + return true; +} + +/******************************************************************************/ + +function smartBackspace(transaction) { + const { from, to } = rangeFromTransaction(transaction); + if ( from === undefined || to === undefined ) { return; } + const { newDoc } = transaction; + const line = newDoc.lineAt(from); + if ( /^(?: {2})+-$/.test(line.text) === false ) { return; } + cmRules.dispatch({ changes: { from: from-3, to: from, insert: '' } }); + return true; } /******************************************************************************/ function cmUpdateListener(info) { if ( info.docChanged === false ) { return; } - const doc = info.state.doc; - info.changes.iterChangedRanges((fromA, toA, fromB, toB) => { - addToModifiedRange(doc, fromB, toB); - }); for ( const transaction of info.transactions ) { - if ( transaction.isUserEvent('input.paste') === false ) { continue; } if ( transaction.docChanged === false ) { continue; } - let from, to; - transaction.changes.iterChangedRanges((fromA, toA, fromB, toB) => { - if ( from === undefined || fromB < from ) { from = fromB; } - if ( to === undefined || toB > to ) { to = toB; } - }); - importRulesFromPaste(from, to); - break; + addToModifiedRange(transaction); + if ( transaction.isUserEvent('delete.backward') ) { + if ( smartBackspace(transaction) ) { break; } + } else if ( transaction.isUserEvent('input.paste') ) { + if ( importRulesFromPaste(transaction) ) { break; } + } } updateViewAsync(); } From e1f2c6f88d87c91faa5e3864adc0d0b361dd303e Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Fri, 30 May 2025 18:15:25 -0400 Subject: [PATCH 0987/1099] [mv3] Code review --- platform/mv3/extension/js/develop.js | 2 - platform/mv3/extension/js/dnr-parser.js | 94 +++++-------------------- platform/mv3/safari/ext-compat.js | 19 +++-- 3 files changed, 33 insertions(+), 82 deletions(-) diff --git a/platform/mv3/extension/js/develop.js b/platform/mv3/extension/js/develop.js index f4b1c83114135..e79580b152805 100644 --- a/platform/mv3/extension/js/develop.js +++ b/platform/mv3/extension/js/develop.js @@ -257,8 +257,6 @@ function getAutocompleteCandidates(from) { { token: 'urlFilter:', after: ' ' }, { token: 'initiatorDomains:', after: '\n - ' }, { token: 'excludedInitiatorDomains:', after: '\n - ' }, - { token: 'domains:', after: '\n - ' }, - { token: 'excludedDomains:', after: '\n - ' }, { token: 'requestDomains:', after: '\n - ' }, { token: 'excludedRequestDomains:', after: '\n - ' }, { token: 'resourceTypes:', after: '\n - ' }, diff --git a/platform/mv3/extension/js/dnr-parser.js b/platform/mv3/extension/js/dnr-parser.js index 68d243df0e069..a852a5f03704e 100644 --- a/platform/mv3/extension/js/dnr-parser.js +++ b/platform/mv3/extension/js/dnr-parser.js @@ -89,14 +89,10 @@ const perScopeParsers = { const { key, val } = node; switch ( key ) { case 'action': - if ( val !== undefined ) { return false; } - rule.action = {}; - scope.push('action'); - break; case 'condition': if ( val !== undefined ) { return false; } - rule.condition = {}; - scope.push('condition'); + rule[key] = {}; + scope.push(key); break; case 'priority': { const n = parseInt(val, 10); @@ -121,12 +117,9 @@ const perScopeParsers = { scope.push('redirect'); break; case 'requestHeaders': - rule.action.requestHeaders = []; - scope.push('requestHeaders'); - break; case 'responseHeaders': - rule.action.responseHeaders = []; - scope.push('responseHeaders'); + rule.action[key] = []; + scope.push(key); break; default: return false; @@ -137,18 +130,14 @@ const perScopeParsers = { const { key, val } = node; switch ( key ) { case 'extensionPath': - rule.action.redirect.extensionPath = val; - break; case 'regexSubstitution': - rule.action.redirect.regexSubstitution = val; + case 'url': + rule.action.redirect[key] = val; break; case 'transform': rule.action.redirect.transform = {}; scope.push('transform'); break; - case 'url': - rule.action.redirect.url = val; - break; default: return false; } @@ -181,12 +170,9 @@ const perScopeParsers = { if ( val !== undefined ) { return false; } switch ( key ) { case 'addOrReplaceParams': - rule.action.redirect.transform.queryTransform.addOrReplaceParams = []; - scope.push('addOrReplaceParams'); - break; case 'removeParams': - rule.action.redirect.transform.queryTransform.removeParams = []; - scope.push('removeParams'); + rule.action.redirect.transform.queryTransform[key] = []; + scope.push(key); break; default: return false; @@ -205,10 +191,8 @@ const perScopeParsers = { const item = rule.action.redirect.transform.queryTransform.addOrReplaceParams.at(-1); switch ( key ) { case 'key': - item.key = val; - break; case 'value': - item.value = val; + item[key] = val; break; case 'replaceOnly': if ( validBoolValues.includes(val) === false ) { return false; } @@ -235,10 +219,8 @@ const perScopeParsers = { const item = rule.action.requestHeaders.at(-1); switch ( key ) { case 'header': - item.header = val; - break; case 'value': - item.value = val; + item[key] = val; break; case 'operation': if ( validHeaderOpValues.includes(val) === false ) { return false; } @@ -260,10 +242,8 @@ const perScopeParsers = { const item = rule.action.responseHeaders.at(-1); switch ( key ) { case 'header': - item.header = val; - break; case 'value': - item.value = val; + item[key] = val; break; case 'operation': if ( validHeaderOpValues.includes(val) === false ) { return false; } @@ -286,54 +266,22 @@ const perScopeParsers = { rule.condition.isUrlFilterCaseSensitive = val === 'true'; break; case 'regexFilter': - if ( val === undefined ) { return false; } - rule.condition.regexFilter = val; - break; case 'urlFilter': if ( val === undefined ) { return false; } - rule.condition.urlFilter = val; + rule.condition[key] = val; break; case 'initiatorDomains': - case 'domains': - rule.condition[key] = []; - scope.push(key); - break; case 'excludedInitiatorDomains': - case 'excludedDomains': - rule.condition[key] = []; - scope.push(key); - break; case 'requestDomains': - rule.condition.requestDomains = []; - scope.push('requestDomains'); - break; case 'excludedRequestDomains': - rule.condition.excludedRequestDomains = []; - scope.push('excludedRequestDomains'); - break; case 'resourceTypes': - rule.condition.resourceTypes = []; - scope.push('resourceTypes'); - break; case 'excludedResourceTypes': - rule.condition.excludedResourceTypes = []; - scope.push('excludedResourceTypes'); - break; case 'requestMethods': - rule.condition.requestMethods = []; - scope.push('requestMethods'); - break; case 'excludedRequestMethods': - rule.condition.excludedRequestMethods = []; - scope.push('excludedRequestMethods'); - break; case 'responseHeaders': - rule.condition.responseHeaders = []; - scope.push('responseHeaders'); - break; case 'excludedResponseHeaders': - rule.condition.excludedResponseHeaders = []; - scope.push('excludedResponseHeaders'); + rule.condition[key] = []; + scope.push(key); break; default: return false; @@ -408,12 +356,9 @@ const perScopeParsers = { item.header = node.val; break; case 'values': - item.values = []; - scope.push('values'); - break; case 'excludedValues': - item.excludedValues = []; - scope.push('excludedValues'); + item[node.key] = []; + scope.push(node.key); break; default: return false; @@ -446,12 +391,9 @@ const perScopeParsers = { item.header = node.val; break; case 'values': - item.values = []; - scope.push('values'); - break; case 'excludedValues': - item.excludedValues = []; - scope.push('excludedValues'); + item[node.key] = []; + scope.push(node.key); break; default: return false; diff --git a/platform/mv3/safari/ext-compat.js b/platform/mv3/safari/ext-compat.js index 0d56155e21189..a88c20c7c14e9 100644 --- a/platform/mv3/safari/ext-compat.js +++ b/platform/mv3/safari/ext-compat.js @@ -54,10 +54,21 @@ const prepareUpdateRules = optionsBefore => { const addRulesAfter = addRules?.filter(isSupportedRule); if ( Boolean(addRulesAfter?.length || removeRuleIds?.length) === false ) { return; } addRulesAfter?.forEach(r => { - if ( r.action.redirect?.regexSubstitution === undefined ) { return; } - if ( r.condition.requestDomains === undefined ) { return; } - r.condition.domains = r.condition.requestDomains; - delete r.condition.requestDomains; + if ( r.action?.redirect?.regexSubstitution ) { + if ( r.condition?.requestDomains ) { + r.condition.domains = r.condition.requestDomains; + delete r.condition.requestDomains; + return; + } + } + if ( r.condition?.initiatorDomains ) { + r.condition.domains = r.condition.initiatorDomains; + delete r.condition.initiatorDomains; + } + if ( r.condition?.excludedInitiatorDomains ) { + r.condition.excludedDomains = r.condition.excludedInitiatorDomains; + delete r.condition.excludedInitiatorDomains; + } }); const optionsAfter = {}; if ( addRulesAfter?.length ) { optionsAfter.addRules = addRulesAfter; } From 670e8dc36751825eff989799de562b7effec5710 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sat, 31 May 2025 09:42:53 -0400 Subject: [PATCH 0988/1099] [mv3] Add smart spacebar/return auto completion --- platform/mv3/extension/js/develop.js | 64 +++++++++++++++++++++++++++- 1 file changed, 63 insertions(+), 1 deletion(-) diff --git a/platform/mv3/extension/js/develop.js b/platform/mv3/extension/js/develop.js index e79580b152805..cc96cbfb7ec8c 100644 --- a/platform/mv3/extension/js/develop.js +++ b/platform/mv3/extension/js/develop.js @@ -75,9 +75,9 @@ function rangeFromTransaction(transaction) { } function addToModifiedRange(transaction) { - const { newDoc } = transaction; const { from, to } = rangeFromTransaction(transaction); if ( from === undefined || to === undefined ) { return; } + const { newDoc } = transaction; const { yamlDocStart, yamlDocEnd } = snapToYamlDocument(newDoc, from, to); if ( modifiedRange.start === -1 || yamlDocStart < modifiedRange.start ) { modifiedRange.start = yamlDocStart; @@ -224,6 +224,7 @@ function getAutocompleteCandidates(from) { ], }; case 'action:responseHeaders:': + case 'action:requestHeaders:': return { before: /^ {4}- $/, candidates: [ @@ -231,6 +232,7 @@ function getAutocompleteCandidates(from) { ], }; case 'action:responseHeaders:header:': + case 'action:requestHeaders:header:': return { before: /^ {6}$/, candidates: [ @@ -239,6 +241,7 @@ function getAutocompleteCandidates(from) { ], }; case 'action:responseHeaders:header:operation:': + case 'action:requestHeaders:header:operation:': return { before: /: $/, candidates: [ @@ -587,6 +590,7 @@ function importRulesFromPaste(transaction) { function smartBackspace(transaction) { const { from, to } = rangeFromTransaction(transaction); if ( from === undefined || to === undefined ) { return; } + if ( to !== from ) { return; } const { newDoc } = transaction; const line = newDoc.lineAt(from); if ( /^(?: {2})+-$/.test(line.text) === false ) { return; } @@ -596,6 +600,61 @@ function smartBackspace(transaction) { /******************************************************************************/ +function lineIsArrayItem(doc, lineNo) { + if ( lineNo < 1 || lineNo > doc.lines ) { return false; } + const line = doc.line(lineNo); + return line.text.startsWith(' - '); +} + +/******************************************************************************/ + +function smartArrayItem(doc, from) { + const line = doc.lineAt(from); + if ( lineIsArrayItem(doc, line.number-1) === false ) { + if ( lineIsArrayItem(doc, line.number+1) === false ) { return ''; } + } + const blanks = /^ {2,4}$/.exec(line.text); + if ( blanks === null ) { return ''; } + const count = blanks[0].length; + return `${' '.repeat(4-count)}- `; +} + +/******************************************************************************/ + +function smartReturn(transaction) { + const { from, to } = rangeFromTransaction(transaction); + if ( from === undefined || to === undefined ) { return; } + const { newDoc } = transaction; + const insert = smartArrayItem(newDoc, to); + if ( insert === '' ) { return; } + cmRules.dispatch({ + changes: { from: to, insert }, + selection: { anchor: to + insert.length }, + }); + return true; +} + +/******************************************************************************/ + +function smartSpacebar(transaction) { + const { from, to } = rangeFromTransaction(transaction); + if ( from === undefined || to === undefined ) { return; } + if ( (to - from) !== 1 ) { return; } + const { newDoc } = transaction; + const line = newDoc.lineAt(to); + const localTo = to - line.from; + const before = line.text.slice(0, localTo); + if ( /^(?: {1}| {3})$/.test(before) === false ) { return; } + const insert = smartArrayItem(newDoc, to) || ' '; + cmRules.dispatch({ + changes: { from: to, insert }, + selection: { anchor: to + insert.length }, + }); + return true; +} + +/******************************************************************************/ + function cmUpdateListener(info) { if ( info.docChanged === false ) { return; } for ( const transaction of info.transactions ) { @@ -605,6 +664,9 @@ function cmUpdateListener(info) { if ( smartBackspace(transaction) ) { break; } } else if ( transaction.isUserEvent('input.paste') ) { if ( importRulesFromPaste(transaction) ) { break; } + } else if ( transaction.isUserEvent('input') ) { + if ( smartReturn(transaction) ) { break; } + if ( smartSpacebar(transaction) ) { break; } } } updateViewAsync(); From 837451c17a2d952b95ba4c575caca64ee57fe552 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sat, 31 May 2025 11:32:54 -0400 Subject: [PATCH 0989/1099] Import translation work from https://crowdin.com/project/ublock --- .../mv3/extension/_locales/be/messages.json | 12 ++++++------ .../mv3/extension/_locales/da/messages.json | 6 +++--- .../mv3/extension/_locales/es/messages.json | 4 ++-- .../mv3/extension/_locales/fi/messages.json | 4 ++-- .../mv3/extension/_locales/pt_BR/messages.json | 4 ++-- .../mv3/extension/_locales/sq/messages.json | 18 +++++++++--------- src/_locales/es/messages.json | 2 +- 7 files changed, 25 insertions(+), 25 deletions(-) diff --git a/platform/mv3/extension/_locales/be/messages.json b/platform/mv3/extension/_locales/be/messages.json index 3b6a0d8644275..52c8de5258e98 100644 --- a/platform/mv3/extension/_locales/be/messages.json +++ b/platform/mv3/extension/_locales/be/messages.json @@ -20,7 +20,7 @@ "description": "appears as tab name in dashboard" }, "developPageName": { - "message": "Develop", + "message": "Распрацоўка", "description": "appears as tab name in dashboard. Inspired from 'Develop' menu in Safari, see https://developer.apple.com/documentation/safari-developer-tools/develop-menu" }, "aboutPageName": { @@ -240,7 +240,7 @@ "description": "Short description for a checkbox in the options page" }, "developerModeLabel": { - "message": "Developer mode", + "message": "Рэжым распрацоўніка", "description": "Label for a checkbox in the options page" }, "developerModeLegend": { @@ -296,19 +296,19 @@ "description": "Tooltip for the button used to exit zapper mode" }, "saveButton": { - "message": "Save", + "message": "Захаваць", "description": "Text for buttons used to save changes" }, "revertButton": { - "message": "Revert", + "message": "Вярнуць", "description": "Text for buttons used to revert changes" }, "importAndAppendButton": { - "message": "Import and append…", + "message": "Імпартаваць ды дадаць…", "description": "Text for buttons used to import and append content" }, "exportButton": { - "message": "Export…", + "message": "Экспартаваць…", "description": "Text for buttons used to export content" }, "dnrRulesSummary": { diff --git a/platform/mv3/extension/_locales/da/messages.json b/platform/mv3/extension/_locales/da/messages.json index 88a2dbe834530..e264b77d5692a 100644 --- a/platform/mv3/extension/_locales/da/messages.json +++ b/platform/mv3/extension/_locales/da/messages.json @@ -20,7 +20,7 @@ "description": "appears as tab name in dashboard" }, "developPageName": { - "message": "Develop", + "message": "Udvikl", "description": "appears as tab name in dashboard. Inspired from 'Develop' menu in Safari, see https://developer.apple.com/documentation/safari-developer-tools/develop-menu" }, "aboutPageName": { @@ -244,7 +244,7 @@ "description": "Label for a checkbox in the options page" }, "developerModeLegend": { - "message": "Aktivér adgang til funktioner egnede for tekniske brugere.", + "message": "Aktiverer adgang til funktioner egnede for tekniske brugere.", "description": "Short description for a checkbox in the options page" }, "findListsPlaceholder": { @@ -316,7 +316,7 @@ "description": "Short description of the DNR rules editor pane" }, "dnrRulesCountInfo": { - "message": "Number of registered rules: {count}", + "message": "Antal registrerede regler : {count}", "description": "Short sentence to report the number of currently registered DNR rules" } } diff --git a/platform/mv3/extension/_locales/es/messages.json b/platform/mv3/extension/_locales/es/messages.json index 46af90107a6c4..15669a4242f09 100644 --- a/platform/mv3/extension/_locales/es/messages.json +++ b/platform/mv3/extension/_locales/es/messages.json @@ -244,7 +244,7 @@ "description": "Label for a checkbox in the options page" }, "developerModeLegend": { - "message": "Habilitar acceso a características adecuadas para usuarios técnicos.", + "message": "Habilita acceso a características aptas para usuarios técnicos.", "description": "Short description for a checkbox in the options page" }, "findListsPlaceholder": { @@ -312,7 +312,7 @@ "description": "Text for buttons used to export content" }, "dnrRulesSummary": { - "message": "Ingresa tus propias reglas DNR abajo. No agregues contenido desde fuentes no confiables.", + "message": "Ingresa tus propias reglas DNR abajo. No añadas contenido de fuentes no confiables.", "description": "Short description of the DNR rules editor pane" }, "dnrRulesCountInfo": { diff --git a/platform/mv3/extension/_locales/fi/messages.json b/platform/mv3/extension/_locales/fi/messages.json index 4858173b77502..bd22ecdc08326 100644 --- a/platform/mv3/extension/_locales/fi/messages.json +++ b/platform/mv3/extension/_locales/fi/messages.json @@ -20,7 +20,7 @@ "description": "appears as tab name in dashboard" }, "developPageName": { - "message": "Develop", + "message": "Kehittäjille", "description": "appears as tab name in dashboard. Inspired from 'Develop' menu in Safari, see https://developer.apple.com/documentation/safari-developer-tools/develop-menu" }, "aboutPageName": { @@ -316,7 +316,7 @@ "description": "Short description of the DNR rules editor pane" }, "dnrRulesCountInfo": { - "message": "Number of registered rules: {count}", + "message": "Rekisteröityjen sääntöjen määrä: {count}", "description": "Short sentence to report the number of currently registered DNR rules" } } diff --git a/platform/mv3/extension/_locales/pt_BR/messages.json b/platform/mv3/extension/_locales/pt_BR/messages.json index 8c1c4d583be5d..21c5379274861 100644 --- a/platform/mv3/extension/_locales/pt_BR/messages.json +++ b/platform/mv3/extension/_locales/pt_BR/messages.json @@ -20,7 +20,7 @@ "description": "appears as tab name in dashboard" }, "developPageName": { - "message": "Desenvolvedor", + "message": "Desenvolva", "description": "appears as tab name in dashboard. Inspired from 'Develop' menu in Safari, see https://developer.apple.com/documentation/safari-developer-tools/develop-menu" }, "aboutPageName": { @@ -244,7 +244,7 @@ "description": "Label for a checkbox in the options page" }, "developerModeLegend": { - "message": "Ativar acesso as funções adequadas pra usuários técnicos.", + "message": "Ativa o acesso as funções adequadas pra usuários técnicos.", "description": "Short description for a checkbox in the options page" }, "findListsPlaceholder": { diff --git a/platform/mv3/extension/_locales/sq/messages.json b/platform/mv3/extension/_locales/sq/messages.json index 4118451f4ce49..0b7af97a0311f 100644 --- a/platform/mv3/extension/_locales/sq/messages.json +++ b/platform/mv3/extension/_locales/sq/messages.json @@ -20,7 +20,7 @@ "description": "appears as tab name in dashboard" }, "developPageName": { - "message": "Develop", + "message": "Zhvilloni", "description": "appears as tab name in dashboard. Inspired from 'Develop' menu in Safari, see https://developer.apple.com/documentation/safari-developer-tools/develop-menu" }, "aboutPageName": { @@ -240,11 +240,11 @@ "description": "Short description for a checkbox in the options page" }, "developerModeLabel": { - "message": "Developer mode", + "message": "Modaliteti i zhvilluesit", "description": "Label for a checkbox in the options page" }, "developerModeLegend": { - "message": "Enables access to features suitable for technical users.", + "message": "Mundëson qasjen në funksione të përshtatshme për përdorues teknik.", "description": "Short description for a checkbox in the options page" }, "findListsPlaceholder": { @@ -296,27 +296,27 @@ "description": "Tooltip for the button used to exit zapper mode" }, "saveButton": { - "message": "Save", + "message": "Ruaj", "description": "Text for buttons used to save changes" }, "revertButton": { - "message": "Revert", + "message": "Rikthej", "description": "Text for buttons used to revert changes" }, "importAndAppendButton": { - "message": "Import and append…", + "message": "Importim e shtim", "description": "Text for buttons used to import and append content" }, "exportButton": { - "message": "Export…", + "message": "Eksportim", "description": "Text for buttons used to export content" }, "dnrRulesSummary": { - "message": "Enter your own DNR rules below. Do not add content from untrusted sources.", + "message": "Shkruani rregullat tuaja DNR më poshtë. Mos shtoni përmbajtje nga burime të paverifikuara.", "description": "Short description of the DNR rules editor pane" }, "dnrRulesCountInfo": { - "message": "Number of registered rules: {count}", + "message": "Numrat e regullave të rregjistruara: {count}", "description": "Short sentence to report the number of currently registered DNR rules" } } diff --git a/src/_locales/es/messages.json b/src/_locales/es/messages.json index 390aba48dd65f..f909545b356e8 100644 --- a/src/_locales/es/messages.json +++ b/src/_locales/es/messages.json @@ -536,7 +536,7 @@ "description": "used as a tooltip for error icon beside a list" }, "1pTrustWarning": { - "message": "No añadir filtros de fuentes no confiables.", + "message": "No añadas filtros de fuentes no confiables.", "description": "Warning against copy-pasting filters from random sources" }, "1pEnableMyFiltersLabel": { From fad39c6ce51147addc1ddd12e5a53a4a0782432d Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sun, 1 Jun 2025 13:29:46 -0400 Subject: [PATCH 0990/1099] [mv3] Add support for code-folding and use custom stream parser --- platform/mv3/extension/css/develop.css | 2 +- platform/mv3/extension/js/develop.js | 141 ++++++++++++++++-- platform/mv3/extension/js/settings.js | 1 + .../extension/lib/codemirror/codemirror-ubol | 2 +- 4 files changed, 135 insertions(+), 11 deletions(-) diff --git a/platform/mv3/extension/css/develop.css b/platform/mv3/extension/css/develop.css index 616849509dc96..1ecc3ffd368cc 100644 --- a/platform/mv3/extension/css/develop.css +++ b/platform/mv3/extension/css/develop.css @@ -36,7 +36,7 @@ section[data-pane="develop"] > div > * { border-top: 1px dotted transparent; } -#cm-dnrRules .cm-editor .cm-line:has(.ͼ5) { +#cm-dnrRules .cm-editor .cm-line:has(.ͼc) { border-bottom: 1px dotted var(--border-1); border-top: 1px dotted var(--border-1); } diff --git a/platform/mv3/extension/js/develop.js b/platform/mv3/extension/js/develop.js index cc96cbfb7ec8c..7c143bec07edd 100644 --- a/platform/mv3/extension/js/develop.js +++ b/platform/mv3/extension/js/develop.js @@ -356,9 +356,10 @@ function setEditorText(text) { cmRules.dispatch({ changes: { from: 0, to: cmRules.state.doc.length, - insert: text + insert: text, }, }); + cmRules.focus(); } function getEditorText() { @@ -529,6 +530,7 @@ function importRulesFromFile() { prepend = `${prepend}---\n`; } cmRules.dispatch({ changes: { from, insert: `${prepend}${text}` } }); + self.cm6.foldAll(cmRules); cmRules.focus(); }; fr.readAsText(file); @@ -568,25 +570,57 @@ function importRulesFromPaste(transaction) { const { from, to } = rangeFromTransaction(transaction); if ( from === undefined || to === undefined ) { return; } // Paste position must match start of a line - const { doc } = transaction.newDoc; - const lineFrom = doc.lineAt(from); + const { newDoc } = transaction; + const lineFrom = newDoc.lineAt(from); if ( lineFrom.from !== from ) { return; } // Paste position must match a rule boundary if ( lineFrom.number !== 1 ) { - const lineBefore = doc.line(lineFrom.number-1); + const lineBefore = newDoc.line(lineFrom.number-1); if ( /^---\s*$/.test(lineBefore.text) === false ) { return; } } - const pastedText = doc.sliceString(from, to); + const pastedText = newDoc.sliceString(from, to); const rules = rulesFromJSON(pastedText); if ( rules === undefined ) { return; } const yamlText = textFromRules(rules); if ( yamlText === undefined ) { return; } cmRules.dispatch({ changes: { from, to, insert: yamlText } }); + self.cm6.foldAll(cmRules); return true; } /******************************************************************************/ +function foldService(state, from) { + const { doc } = state; + const lineFrom = doc.lineAt(from); + if ( reFoldable.test(lineFrom.text) === false ) { return null; } + if ( lineFrom.number <= 5 ) { return null ; } + const lineBlockStart = doc.line(lineFrom.number - 5); + if ( reFoldCandidates.test(lineBlockStart.text) === false ) { return null; } + for ( let i = lineFrom.number-4; i < lineFrom.number; i++ ) { + const line = doc.line(i); + if ( reFoldable.test(line.text) === false ) { return null; } + } + for ( let i = lineFrom.number; i < doc.lines; i++ ) { + const lineNext = doc.line(i+1); + if ( reFoldable.test(lineNext.text) ) { continue; } + if ( i === lineFrom.number ) { return null; } + const lineFoldEnd = doc.line(i); + return { from: lineFrom.from+6, to: lineFoldEnd.to }; + } + return null; +} + +const reFoldable = /^ {4}- \S/; +const reFoldCandidates = new RegExp(`^(?: {2})+${[ + 'initiatorDomains', + 'excludedInitiatorDomains', + 'requestDomains', + 'excludedRequestDomains', +].join('|')}:$`); + +/******************************************************************************/ + function smartBackspace(transaction) { const { from, to } = rangeFromTransaction(transaction); if ( from === undefined || to === undefined ) { return; } @@ -655,18 +689,104 @@ function smartSpacebar(transaction) { /******************************************************************************/ +const dnryamlStreamParser = { + name: 'dnryaml', + startState() { + return { + scope: 0, + reKeywords: new RegExp(`\\b(${[ + 'block', + 'redirect', + 'allow', + 'modifyHeaders', + 'upgradeScheme', + 'allowAllRequest', + 'append', + 'set', + 'remove', + 'firstParty', + 'thirdParty', + 'true', + 'false', + 'connect', + 'delete', + 'get', + 'head', + 'options', + 'patch', + 'post', + 'put', + 'other', + 'main_frame', + 'sub_frame', + 'stylesheet', + 'script', + 'image', + 'font', + 'object', + 'xmlhttprequest', + 'ping', + 'csp_report', + 'media', + 'websocket', + 'webtransport', + 'webbundle', + 'other', + ].join('|')})\\b`), + }; + }, + token(stream, state) { + const c = stream.peek(); + if ( c === '#' ) { + if ( (stream.pos === 0 || /\s/.test(stream.string.charAt(stream.pos - 1))) ) { + stream.skipToEnd(); + return 'comment'; + } + } + if ( stream.sol() ) { + if ( stream.match('---') ) { return 'contentSeparator'; } + if ( stream.match('...') ) { return 'contentSeparator'; } + } + if ( stream.eatSpace() ) { + return null; + } + const { scope } = state; + state.scope = 0; + if ( scope === 0 && stream.match(/^[^:]+(?=:)/) ) { + state.scope = 1; + return 'keyword'; + } + if ( scope === 1 && stream.match(/^:(?: |$)/) ) { + return 'meta'; + } + if ( stream.match(/^- /) ) { + return 'meta'; + } + if ( stream.match(state.reKeywords) ) { + return 'literal'; + } + if ( stream.match(/^\S+/) ) { + return null; + } + stream.next(); + return null; + }, +}; + +/******************************************************************************/ + function cmUpdateListener(info) { if ( info.docChanged === false ) { return; } for ( const transaction of info.transactions ) { if ( transaction.docChanged === false ) { continue; } addToModifiedRange(transaction); if ( transaction.isUserEvent('delete.backward') ) { - if ( smartBackspace(transaction) ) { break; } + smartBackspace(transaction); } else if ( transaction.isUserEvent('input.paste') ) { - if ( importRulesFromPaste(transaction) ) { break; } + importRulesFromPaste(transaction); } else if ( transaction.isUserEvent('input') ) { - if ( smartReturn(transaction) ) { break; } - if ( smartSpacebar(transaction) ) { break; } + if ( smartReturn(transaction) ) { continue; } + smartSpacebar(transaction); } } updateViewAsync(); @@ -743,6 +863,8 @@ const cmRules = (( ) => { }, gutterClick, hoverTooltip, + streamParser: dnryamlStreamParser, + foldService, }, qs$('#cm-dnrRules')); })(); @@ -754,6 +876,7 @@ localRead('userDnrRules').then(text => { text ||= ''; setEditorText(text); lastSavedText = text; + self.cm6.foldAll(cmRules); self.cm6.resetUndoRedo(cmRules); dom.on('#dnrRulesApply', 'click', ( ) => { diff --git a/platform/mv3/extension/js/settings.js b/platform/mv3/extension/js/settings.js index ed1e9a99e972f..1d1c1b5821377 100644 --- a/platform/mv3/extension/js/settings.js +++ b/platform/mv3/extension/js/settings.js @@ -200,6 +200,7 @@ function changeTrustedSites() { function getStagedTrustedSites() { const text = cmTrustedSites.state.doc.toString(); return text.split(/\s/).map(hn => { + if ( hn === '' ) { return ''; } try { return punycode.toASCII( (new URL(`https://${hn}/`)).hostname diff --git a/platform/mv3/extension/lib/codemirror/codemirror-ubol b/platform/mv3/extension/lib/codemirror/codemirror-ubol index 1d352d4489fea..58a4bae6af823 160000 --- a/platform/mv3/extension/lib/codemirror/codemirror-ubol +++ b/platform/mv3/extension/lib/codemirror/codemirror-ubol @@ -1 +1 @@ -Subproject commit 1d352d4489fea6f4aa10a32417eba4abd67db5c9 +Subproject commit 58a4bae6af82358754da665d3056d7af295bbb7b From 4affe343dd103195ce60e5f59898a87e03d14a11 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sun, 1 Jun 2025 13:48:23 -0400 Subject: [PATCH 0991/1099] Force cache bypass reload when no-scripting switch is toggled Related issue: https://github.com/uBlockOrigin/uBlock-issues/issues/3652 --- src/js/popup-fenix.js | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/js/popup-fenix.js b/src/js/popup-fenix.js index 298afe28f71a1..4ffbac395fa4f 100644 --- a/src/js/popup-fenix.js +++ b/src/js/popup-fenix.js @@ -56,6 +56,7 @@ let dfPaneBuilt = false; let dfHotspots = null; const allHostnameRows = []; let cachedPopupHash = ''; +let forceReloadFlag = 0; // https://github.com/gorhill/uBlock/issues/2550 // Solution inspired from @@ -130,6 +131,7 @@ const hashFromPopupData = function(reset = false) { const hash = hasher.join(''); if ( reset ) { cachedPopupHash = hash; + forceReloadFlag = 0; } dom.cl.toggle(dom.body, 'needReload', hash !== cachedPopupHash || popupData.hasUnprocessedRequest === true @@ -1167,7 +1169,7 @@ const reloadTab = function(bypassCache = false) { tabId: popupData.tabId, url: popupData.rawURL, select: vAPI.webextFlavor.soup.has('mobile'), - bypassCache, + bypassCache: bypassCache || forceReloadFlag !== 0, }); // Polling will take care of refreshing the popup content @@ -1344,6 +1346,10 @@ const toggleHostnameSwitch = async function(ev) { persist: ev.ctrlKey || ev.metaKey, }); + if ( switchName === 'no-scripting' ) { + forceReloadFlag ^= 1; + } + cachePopupData(response); hashFromPopupData(); From 30a271a7ff0b1135c4b94002b95d35f33358a800 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sun, 1 Jun 2025 13:50:00 -0400 Subject: [PATCH 0992/1099] Update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index bed3c42a4db8b..d3eaf36d5e1e7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,4 @@ +- [Force cache bypass reload when no-scripting switch is toggled](https://github.com/gorhill/uBlock/commit/4affe343dd) - [Improve `jsonl[...]` suite of scriptlets](https://github.com/gorhill/uBlock/commit/ed9999efd6) - [Add support for network filter option `message`](https://github.com/gorhill/uBlock/commit/d8298bb067) - [Make `header=` syntax compatible with DNR rules](https://github.com/gorhill/uBlock/commit/408b538e75) From 5de5d8c4a7008318594b47a106bb52be1280eb9f Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sun, 1 Jun 2025 13:50:35 -0400 Subject: [PATCH 0993/1099] New revision for dev build --- dist/version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dist/version b/dist/version index c1eb358f1c237..9ee5079267736 100644 --- a/dist/version +++ b/dist/version @@ -1 +1 @@ -1.64.1.3 \ No newline at end of file +1.64.1.4 \ No newline at end of file From 0dac2206c302113d159fee1064d3369a3dcc87bd Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sun, 1 Jun 2025 13:52:48 -0400 Subject: [PATCH 0994/1099] Import translation work from https://crowdin.com/project/ublock --- platform/mv3/description/webstore.ko.txt | 2 +- .../mv3/extension/_locales/de/messages.json | 2 +- .../mv3/extension/_locales/ko/messages.json | 20 +++++++++---------- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/platform/mv3/description/webstore.ko.txt b/platform/mv3/description/webstore.ko.txt index 9a8b17f7a3fb9..98c8a3f801056 100644 --- a/platform/mv3/description/webstore.ko.txt +++ b/platform/mv3/description/webstore.ko.txt @@ -1,4 +1,4 @@ -uBO Lite (uBOL) is an MV3-based content blocker. +uBO Lite (uBOL)는 MV3 기반 콘텐츠 차단기입니다. 기본 규칙 목록은 uBlock Origin의 기본 필터 목록과 대응됩니다. diff --git a/platform/mv3/extension/_locales/de/messages.json b/platform/mv3/extension/_locales/de/messages.json index 47092414c00fb..4990ea4ab4659 100644 --- a/platform/mv3/extension/_locales/de/messages.json +++ b/platform/mv3/extension/_locales/de/messages.json @@ -316,7 +316,7 @@ "description": "Short description of the DNR rules editor pane" }, "dnrRulesCountInfo": { - "message": "Anzahl registrierter Regeln: {count}", + "message": "Anzahl erfasster Regeln: {count}", "description": "Short sentence to report the number of currently registered DNR rules" } } diff --git a/platform/mv3/extension/_locales/ko/messages.json b/platform/mv3/extension/_locales/ko/messages.json index 8d28d88f357b4..6e38cf8e22d35 100644 --- a/platform/mv3/extension/_locales/ko/messages.json +++ b/platform/mv3/extension/_locales/ko/messages.json @@ -20,7 +20,7 @@ "description": "appears as tab name in dashboard" }, "developPageName": { - "message": "Develop", + "message": "개발자용", "description": "appears as tab name in dashboard. Inspired from 'Develop' menu in Safari, see https://developer.apple.com/documentation/safari-developer-tools/develop-menu" }, "aboutPageName": { @@ -116,7 +116,7 @@ "description": "First paragraph of 'Filter issues' section in Support pane" }, "supportS5H": { - "message": "Troubleshooting information", + "message": "문제 해결 정보", "description": "Label of 'Troubleshooting information' section in 'Report a filter issue' page" }, "supportS6P1S1": { @@ -240,11 +240,11 @@ "description": "Short description for a checkbox in the options page" }, "developerModeLabel": { - "message": "Developer mode", + "message": "개발자 모드", "description": "Label for a checkbox in the options page" }, "developerModeLegend": { - "message": "Enables access to features suitable for technical users.", + "message": "기술적 사용자를 위한 기능에 접근할 수 있도록 합니다.", "description": "Short description for a checkbox in the options page" }, "findListsPlaceholder": { @@ -296,27 +296,27 @@ "description": "Tooltip for the button used to exit zapper mode" }, "saveButton": { - "message": "Save", + "message": "저장", "description": "Text for buttons used to save changes" }, "revertButton": { - "message": "Revert", + "message": "되돌리기", "description": "Text for buttons used to revert changes" }, "importAndAppendButton": { - "message": "Import and append…", + "message": "가져오기 및 추가하기…", "description": "Text for buttons used to import and append content" }, "exportButton": { - "message": "Export…", + "message": "내보내기…", "description": "Text for buttons used to export content" }, "dnrRulesSummary": { - "message": "Enter your own DNR rules below. Do not add content from untrusted sources.", + "message": "아래에 직접 DNR 규칙을 입력하세요. 신뢰할 수 없는 출처의 콘텐츠를 추가하지 마세요.", "description": "Short description of the DNR rules editor pane" }, "dnrRulesCountInfo": { - "message": "Number of registered rules: {count}", + "message": "등록된 규칙 수: {count}", "description": "Short sentence to report the number of currently registered DNR rules" } } From 0983e624372fc193b6b894b81deeeaf1b25b4fe8 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sun, 1 Jun 2025 14:34:57 -0400 Subject: [PATCH 0995/1099] [mv3] Fine tuning editor code --- platform/mv3/extension/js/develop.js | 69 +++++++++---------- .../extension/lib/codemirror/codemirror-ubol | 2 +- 2 files changed, 34 insertions(+), 37 deletions(-) diff --git a/platform/mv3/extension/js/develop.js b/platform/mv3/extension/js/develop.js index 7c143bec07edd..5fdee6e297d90 100644 --- a/platform/mv3/extension/js/develop.js +++ b/platform/mv3/extension/js/develop.js @@ -479,18 +479,6 @@ function updateSummaryPanel(info) { }); } -browser.storage.onChanged.addListener((changes, area) => { - if ( area !== 'local' ) { return; } - const { userDnrRuleCount } = changes; - if ( userDnrRuleCount instanceof Object === false ) { return; } - const { newValue } = changes.userDnrRuleCount; - updateSummaryPanel({ userDnrRuleCount: newValue }); -}); - -localRead('userDnrRuleCount').then(userDnrRuleCount => { - updateSummaryPanel({ userDnrRuleCount }) -}); - function updateFeedbackPanel(info) { const errors = []; if ( Array.isArray(info.errors) ) { @@ -542,8 +530,6 @@ function importRulesFromFile() { input.click(); } -dom.on('#dnrRulesImport', 'click', importRulesFromFile); - /******************************************************************************/ function exportRulesToFile() { @@ -562,8 +548,6 @@ function exportRulesToFile() { a.click(); } -dom.on('#dnrRulesExport', 'click', exportRulesToFile); - /******************************************************************************/ function importRulesFromPaste(transaction) { @@ -601,14 +585,15 @@ function foldService(state, from) { const line = doc.line(i); if ( reFoldable.test(line.text) === false ) { return null; } } - for ( let i = lineFrom.number; i < doc.lines; i++ ) { - const lineNext = doc.line(i+1); - if ( reFoldable.test(lineNext.text) ) { continue; } - if ( i === lineFrom.number ) { return null; } - const lineFoldEnd = doc.line(i); - return { from: lineFrom.from+6, to: lineFoldEnd.to }; + let i = lineFrom.number + 1; + for ( ; i <= doc.lines; i++ ) { + const lineNext = doc.line(i); + if ( reFoldable.test(lineNext.text) === false ) { break; } } - return null; + i -= 1; + if ( i === lineFrom.number ) { return null; } + const lineFoldEnd = doc.line(i); + return { from: lineFrom.from+6, to: lineFoldEnd.to }; } const reFoldable = /^ {4}- \S/; @@ -846,8 +831,13 @@ function hoverTooltip(view, pos, side) { /******************************************************************************/ -const cmRules = (( ) => { - return self.cm6.createEditorView({ +let lastSavedText = ''; + +const cmRules = await localRead('userDnrRules').then(text => { + text ||= ''; + + const view = self.cm6.createEditorView({ + text, dnrRules: true, oneDark: dom.cl.has(':root', 'dark'), updateListener: cmUpdateListener, @@ -866,27 +856,34 @@ const cmRules = (( ) => { streamParser: dnryamlStreamParser, foldService, }, qs$('#cm-dnrRules')); -})(); -/******************************************************************************/ - -let lastSavedText = ''; - -localRead('userDnrRules').then(text => { - text ||= ''; - setEditorText(text); lastSavedText = text; - self.cm6.foldAll(cmRules); - self.cm6.resetUndoRedo(cmRules); + self.cm6.foldAll(view); + self.cm6.resetUndoRedo(view); + + browser.storage.onChanged.addListener((changes, area) => { + if ( area !== 'local' ) { return; } + const { userDnrRuleCount } = changes; + if ( userDnrRuleCount instanceof Object === false ) { return; } + const { newValue } = changes.userDnrRuleCount; + updateSummaryPanel({ userDnrRuleCount: newValue }); + }); + + localRead('userDnrRuleCount').then(userDnrRuleCount => { + updateSummaryPanel({ userDnrRuleCount }) + }); dom.on('#dnrRulesApply', 'click', ( ) => { saveEditorText(); }); - dom.on('#dnrRulesRevert', 'click', ( ) => { setEditorText(lastSavedText); sendMessage({ what: 'updateUserDnrRules' }); }); + dom.on('#dnrRulesImport', 'click', importRulesFromFile); + dom.on('#dnrRulesExport', 'click', exportRulesToFile); + + return view; }); /******************************************************************************/ diff --git a/platform/mv3/extension/lib/codemirror/codemirror-ubol b/platform/mv3/extension/lib/codemirror/codemirror-ubol index 58a4bae6af823..94860205aae82 160000 --- a/platform/mv3/extension/lib/codemirror/codemirror-ubol +++ b/platform/mv3/extension/lib/codemirror/codemirror-ubol @@ -1 +1 @@ -Subproject commit 58a4bae6af82358754da665d3056d7af295bbb7b +Subproject commit 94860205aae82ef3d8e66f3a2cc5a5c96262d028 From eb46f4241913c98c1853bb1434cde6594bb63b0d Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sun, 1 Jun 2025 14:46:02 -0400 Subject: [PATCH 0996/1099] Make Firefox dev build auto-update --- dist/firefox/updates.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dist/firefox/updates.json b/dist/firefox/updates.json index 7f14531ac0fdc..d8f0104bcc24f 100644 --- a/dist/firefox/updates.json +++ b/dist/firefox/updates.json @@ -3,9 +3,9 @@ "uBlock0@raymondhill.net": { "updates": [ { - "version": "1.64.1.3", + "version": "1.64.1.4", "browser_specific_settings": { "gecko": { "strict_min_version": "92.0" } }, - "update_link": "https://github.com/gorhill/uBlock/releases/download/1.64.1b3/uBlock0_1.64.1b3.firefox.signed.xpi" + "update_link": "https://github.com/gorhill/uBlock/releases/download/1.64.1b4/uBlock0_1.64.1b4.firefox.signed.xpi" } ] } From 1a9c63254c3819d1fc7cdf7f84fd0cb501325d4f Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Tue, 3 Jun 2025 06:05:56 -0400 Subject: [PATCH 0997/1099] [mv3] Report custom DNR rule count in troubleshooting information As discussed internally. The custom DNR rule count will be reported only when it's not zero, and the count is only for effective DNR rules, i.e. it will not be reported if "Developer mode" is not enabled. --- platform/mv3/extension/js/background.js | 7 +++++++ platform/mv3/extension/js/report.js | 7 ++++++- platform/mv3/extension/js/ruleset-manager.js | 7 ++++--- 3 files changed, 17 insertions(+), 4 deletions(-) diff --git a/platform/mv3/extension/js/background.js b/platform/mv3/extension/js/background.js index 5cd59ce8d86c4..bee7b51b2b313 100644 --- a/platform/mv3/extension/js/background.js +++ b/platform/mv3/extension/js/background.js @@ -53,6 +53,7 @@ import { import { enableRulesets, excludeFromStrictBlock, + getEffectiveUserRules, getEnabledRulesetsDetails, getRulesetDetails, patchDefaultRulesets, @@ -400,6 +401,12 @@ function onMessage(request, sender, callback) { }); break; + case 'getEffectiveUserRules': + getEffectiveUserRules().then(result => { + callback(result); + }); + return true; + case 'updateUserDnrRules': updateUserRules().then(result => { callback(result); diff --git a/platform/mv3/extension/js/report.js b/platform/mv3/extension/js/report.js index e31914bc4d697..126d251d87152 100644 --- a/platform/mv3/extension/js/report.js +++ b/platform/mv3/extension/js/report.js @@ -106,12 +106,14 @@ async function getConfigData() { platformInfo, rulesets, defaultMode, + userRules, registerContentScriptsReason, unregisterContentScriptsReason, ] = await Promise.all([ runtime.getPlatformInfo(), dnr.getEnabledRulesets(), sendMessage({ what: 'getDefaultFilteringMode' }), + sendMessage({ what: 'getEffectiveUserRules' }), localRead('$scripting.registerContentScripts'), localRead('$scripting.unregisterContentScripts'), ]); @@ -148,8 +150,11 @@ async function getConfigData() { 'site': `${modes[reportedPage.mode]}`, 'default': `${modes[defaultMode]}`, }, - rulesets, }; + if ( userRules.length !== 0 ) { + config['user rules'] = userRules.length; + } + config.rulesets = rulesets; if ( registerContentScriptsReason !== undefined ) { config.registerContentScripts = registerContentScriptsReason; } diff --git a/platform/mv3/extension/js/ruleset-manager.js b/platform/mv3/extension/js/ruleset-manager.js index 7876ab821a459..16e041f3470e7 100644 --- a/platform/mv3/extension/js/ruleset-manager.js +++ b/platform/mv3/extension/js/ruleset-manager.js @@ -669,7 +669,7 @@ async function getEnabledRulesetsDetails() { /******************************************************************************/ -async function getUserRules() { +async function getEffectiveUserRules() { const allRules = await dnr.getDynamicRules(); const userRules = []; for ( const rule of allRules ) { @@ -684,7 +684,7 @@ async function updateUserRules() { userRules, userRulesText = '', ] = await Promise.all([ - getUserRules(), + getEffectiveUserRules(), localRead('userDnrRules'), ]); @@ -735,7 +735,7 @@ async function updateUserRules() { console.info(`updateUserRules() / ${reason}`); out.errors.push(`${reason}`); } finally { - const userRules = await getUserRules(); + const userRules = await getEffectiveUserRules(); if ( userRules.length === 0 ) { await localRemove('userDnrRuleCount'); } else { @@ -751,6 +751,7 @@ export { enableRulesets, excludeFromStrictBlock, filteringModesToDNR, + getEffectiveUserRules, getEnabledRulesetsDetails, getRulesetDetails, patchDefaultRulesets, From b2c4242138084c818b23af5ccf32617d1194d4d7 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Tue, 3 Jun 2025 07:39:38 -0400 Subject: [PATCH 0998/1099] [mv3] Fix error reporting in registerInjectables() --- platform/mv3/extension/js/scripting-manager.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/platform/mv3/extension/js/scripting-manager.js b/platform/mv3/extension/js/scripting-manager.js index 4b0b0d59223c6..4cbf52d070d2f 100644 --- a/platform/mv3/extension/js/scripting-manager.js +++ b/platform/mv3/extension/js/scripting-manager.js @@ -616,7 +616,7 @@ async function registerInjectables() { await browser.scripting.unregisterContentScripts({ ids: toRemove }); localRemove('$scripting.unregisterContentScripts'); } catch(reason) { - localWrite('$scripting.unregisterContentScripts', reason); + localWrite('$scripting.unregisterContentScripts', `${reason}`); console.info(reason); } } @@ -627,7 +627,7 @@ async function registerInjectables() { await browser.scripting.registerContentScripts(toAdd); localRemove('$scripting.registerContentScripts'); } catch(reason) { - localWrite('$scripting.registerContentScripts', reason); + localWrite('$scripting.registerContentScripts', `${reason}`); console.info(reason); } } From e96e380ad150ef3905578723b667b55fef7a6064 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Tue, 3 Jun 2025 10:21:53 -0400 Subject: [PATCH 0999/1099] Use CodeMirror's MergeView to highlight differences between before/after To make it easier to analyze the effect of a jsonpath. --- .../extension/lib/codemirror/codemirror-ubol | 2 +- tools/jsonpath-tool.html | 94 +++++++++++-------- 2 files changed, 57 insertions(+), 39 deletions(-) diff --git a/platform/mv3/extension/lib/codemirror/codemirror-ubol b/platform/mv3/extension/lib/codemirror/codemirror-ubol index 94860205aae82..4b9666da6d3cf 160000 --- a/platform/mv3/extension/lib/codemirror/codemirror-ubol +++ b/platform/mv3/extension/lib/codemirror/codemirror-ubol @@ -1 +1 @@ -Subproject commit 94860205aae82ef3d8e66f3a2cc5a5c96262d028 +Subproject commit 4b9666da6d3cf0321493c6a267b11cee88677411 diff --git a/tools/jsonpath-tool.html b/tools/jsonpath-tool.html index f514842959817..88efdd938705d 100644 --- a/tools/jsonpath-tool.html +++ b/tools/jsonpath-tool.html @@ -39,6 +39,8 @@ box-sizing: border-box; display: flex; gap: 0.5em; + max-height: 80cqh; + overflow: auto; } section > * { border: 1px solid gray; @@ -46,11 +48,9 @@ flex-grow: 1; font-family: monospace; font-size: small; - line-height: 1.2; - max-height: 60cqh; - overflow: auto; - white-space: pre; - width: 50cqw; +} +section .cm-lineWrapping { + word-break: break-all !important; } #jsondata-in { min-height: 50vh; @@ -70,26 +70,45 @@

uBO-flavored JSONPath tool

- -
-
- -
 
-
- - From a960140c3036b4725c889729b9eff9e4461c93ad Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Tue, 3 Jun 2025 16:56:10 -0400 Subject: [PATCH 1003/1099] Import translation work from https://crowdin.com/project/ublock --- .../mv3/extension/_locales/ar/messages.json | 2 +- .../mv3/extension/_locales/cs/messages.json | 18 +++++++++--------- .../mv3/extension/_locales/gl/messages.json | 6 +++--- .../mv3/extension/_locales/ka/messages.json | 18 +++++++++--------- .../mv3/extension/_locales/sq/messages.json | 8 ++++---- .../mv3/extension/_locales/zh_CN/messages.json | 4 ++-- src/_locales/gl/messages.json | 4 ++-- 7 files changed, 30 insertions(+), 30 deletions(-) diff --git a/platform/mv3/extension/_locales/ar/messages.json b/platform/mv3/extension/_locales/ar/messages.json index 3d89a51db27ef..d8f33ce545ddf 100644 --- a/platform/mv3/extension/_locales/ar/messages.json +++ b/platform/mv3/extension/_locales/ar/messages.json @@ -308,7 +308,7 @@ "description": "Text for buttons used to import and append content" }, "exportButton": { - "message": "Export…", + "message": "تصدير...", "description": "Text for buttons used to export content" }, "dnrRulesSummary": { diff --git a/platform/mv3/extension/_locales/cs/messages.json b/platform/mv3/extension/_locales/cs/messages.json index 751c8adf2027a..5d91b74c0a40b 100644 --- a/platform/mv3/extension/_locales/cs/messages.json +++ b/platform/mv3/extension/_locales/cs/messages.json @@ -20,7 +20,7 @@ "description": "appears as tab name in dashboard" }, "developPageName": { - "message": "Develop", + "message": "Vývoj", "description": "appears as tab name in dashboard. Inspired from 'Develop' menu in Safari, see https://developer.apple.com/documentation/safari-developer-tools/develop-menu" }, "aboutPageName": { @@ -240,11 +240,11 @@ "description": "Short description for a checkbox in the options page" }, "developerModeLabel": { - "message": "Developer mode", + "message": "Vývojářský režim", "description": "Label for a checkbox in the options page" }, "developerModeLegend": { - "message": "Enables access to features suitable for technical users.", + "message": "Umožňuje přístup k funkcím vhodným pro technicky zdatné uživatele.", "description": "Short description for a checkbox in the options page" }, "findListsPlaceholder": { @@ -296,27 +296,27 @@ "description": "Tooltip for the button used to exit zapper mode" }, "saveButton": { - "message": "Save", + "message": "Uložit", "description": "Text for buttons used to save changes" }, "revertButton": { - "message": "Revert", + "message": "Vrátit", "description": "Text for buttons used to revert changes" }, "importAndAppendButton": { - "message": "Import and append…", + "message": "Importovat a připojit…", "description": "Text for buttons used to import and append content" }, "exportButton": { - "message": "Export…", + "message": "Exportovat…", "description": "Text for buttons used to export content" }, "dnrRulesSummary": { - "message": "Enter your own DNR rules below. Do not add content from untrusted sources.", + "message": "Níže zadejte svoje vlastní DNR pravidla. Nepřidávejte obsah z nedůvěryhodných zdrojů.", "description": "Short description of the DNR rules editor pane" }, "dnrRulesCountInfo": { - "message": "Number of registered rules: {count}", + "message": "Počet registrovaných pravidel: {count}", "description": "Short sentence to report the number of currently registered DNR rules" } } diff --git a/platform/mv3/extension/_locales/gl/messages.json b/platform/mv3/extension/_locales/gl/messages.json index 663aad5d1149a..83e5af9256e4f 100644 --- a/platform/mv3/extension/_locales/gl/messages.json +++ b/platform/mv3/extension/_locales/gl/messages.json @@ -76,7 +76,7 @@ "description": "Header for a ruleset section in 'Filter lists pane'" }, "3pGroupRegions": { - "message": "Rexións, linguaxes", + "message": "Rexións, linguas", "description": "Header for a ruleset section in 'Filter lists pane'" }, "aboutChangelog": { @@ -204,7 +204,7 @@ "description": "This describes the 'basic' filtering mode" }, "optimalFilteringModeDescription": { - "message": "Filtrado avanzado da rede e filtrado extendido específico usando listas de filtrado seleccionadas.\n\nRequire permisos máis amplos para ler e modificar datos en todas as webs.", + "message": "Filtrado avanzado da rede e filtrado estendido específico usando listas de filtrado seleccionadas.\n\nRequire permisos máis amplos para ler e modificar datos en todas as webs.", "description": "This describes the 'optimal' filtering mode" }, "completeFilteringModeDescription": { @@ -316,7 +316,7 @@ "description": "Short description of the DNR rules editor pane" }, "dnrRulesCountInfo": { - "message": "Number of registered rules: {count}", + "message": "Número de regras rexistradas: {count}", "description": "Short sentence to report the number of currently registered DNR rules" } } diff --git a/platform/mv3/extension/_locales/ka/messages.json b/platform/mv3/extension/_locales/ka/messages.json index c601acfd498ce..bb9eb432a2b71 100644 --- a/platform/mv3/extension/_locales/ka/messages.json +++ b/platform/mv3/extension/_locales/ka/messages.json @@ -20,7 +20,7 @@ "description": "appears as tab name in dashboard" }, "developPageName": { - "message": "Develop", + "message": "შემუშავება", "description": "appears as tab name in dashboard. Inspired from 'Develop' menu in Safari, see https://developer.apple.com/documentation/safari-developer-tools/develop-menu" }, "aboutPageName": { @@ -240,11 +240,11 @@ "description": "Short description for a checkbox in the options page" }, "developerModeLabel": { - "message": "Developer mode", + "message": "შემმუშვებლის რეჟიმი", "description": "Label for a checkbox in the options page" }, "developerModeLegend": { - "message": "Enables access to features suitable for technical users.", + "message": "შესაძლებლობები გამოცდილი მომხმარებლებისთვის.", "description": "Short description for a checkbox in the options page" }, "findListsPlaceholder": { @@ -296,27 +296,27 @@ "description": "Tooltip for the button used to exit zapper mode" }, "saveButton": { - "message": "Save", + "message": "შენახვა", "description": "Text for buttons used to save changes" }, "revertButton": { - "message": "Revert", + "message": "დაბრუნება", "description": "Text for buttons used to revert changes" }, "importAndAppendButton": { - "message": "Import and append…", + "message": "შეტანა და დამატება…", "description": "Text for buttons used to import and append content" }, "exportButton": { - "message": "Export…", + "message": "გატანა…", "description": "Text for buttons used to export content" }, "dnrRulesSummary": { - "message": "Enter your own DNR rules below. Do not add content from untrusted sources.", + "message": "შეიყვანეთ საკუთარი DNR-წესები ქვემოთ. ნუ დაამატებთ შიგთავსს არასანდო წყაროებიდან.", "description": "Short description of the DNR rules editor pane" }, "dnrRulesCountInfo": { - "message": "Number of registered rules: {count}", + "message": "დამოწმებული წესები სულ: {count}", "description": "Short sentence to report the number of currently registered DNR rules" } } diff --git a/platform/mv3/extension/_locales/sq/messages.json b/platform/mv3/extension/_locales/sq/messages.json index 0b7af97a0311f..943a27e5c4a7b 100644 --- a/platform/mv3/extension/_locales/sq/messages.json +++ b/platform/mv3/extension/_locales/sq/messages.json @@ -244,7 +244,7 @@ "description": "Label for a checkbox in the options page" }, "developerModeLegend": { - "message": "Mundëson qasjen në funksione të përshtatshme për përdorues teknik.", + "message": "Mundëson qasjen në funksione të përshtatshme për përdoruesit teknikë.", "description": "Short description for a checkbox in the options page" }, "findListsPlaceholder": { @@ -304,11 +304,11 @@ "description": "Text for buttons used to revert changes" }, "importAndAppendButton": { - "message": "Importim e shtim", + "message": "Importoj dhe shtoj…", "description": "Text for buttons used to import and append content" }, "exportButton": { - "message": "Eksportim", + "message": "Eksportoj…", "description": "Text for buttons used to export content" }, "dnrRulesSummary": { @@ -316,7 +316,7 @@ "description": "Short description of the DNR rules editor pane" }, "dnrRulesCountInfo": { - "message": "Numrat e regullave të rregjistruara: {count}", + "message": "Numri i rregullave të regjistruara: {count}", "description": "Short sentence to report the number of currently registered DNR rules" } } diff --git a/platform/mv3/extension/_locales/zh_CN/messages.json b/platform/mv3/extension/_locales/zh_CN/messages.json index e9c5a7ecee09d..4067220fa0cda 100644 --- a/platform/mv3/extension/_locales/zh_CN/messages.json +++ b/platform/mv3/extension/_locales/zh_CN/messages.json @@ -312,11 +312,11 @@ "description": "Text for buttons used to export content" }, "dnrRulesSummary": { - "message": "Enter your own DNR rules below. Do not add content from untrusted sources.", + "message": "在下面输入您自己的DNR规则 不要添加来自不可信来源的内容。", "description": "Short description of the DNR rules editor pane" }, "dnrRulesCountInfo": { - "message": "Number of registered rules: {count}", + "message": "已注册规则的数量:", "description": "Short sentence to report the number of currently registered DNR rules" } } diff --git a/src/_locales/gl/messages.json b/src/_locales/gl/messages.json index e1104c0ebe33f..1689c958bb21d 100644 --- a/src/_locales/gl/messages.json +++ b/src/_locales/gl/messages.json @@ -108,7 +108,7 @@ "description": "For the new mobile-friendly popup design" }, "popupDomainsConnected_v2": { - "message": "Dominos conectados", + "message": "Dominios conectados", "description": "For the new mobile-friendly popup design" }, "popupTipDashboard": { @@ -184,7 +184,7 @@ "description": "Tooltip for the no-scripting per-site switch" }, "popupTipNoScripting2": { - "message": "Preme para volver a activar JavaScript nesta web", + "message": "Preme para volver activar JavaScript nesta web", "description": "Tooltip for the no-scripting per-site switch" }, "popupNoPopups_v2": { From 508138764dd3e6ff1fb4face069d4b379d56c9c4 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Fri, 6 Jun 2025 08:45:19 -0400 Subject: [PATCH 1004/1099] [mv3] Properly refresh "Developer mode" checkbox on changes --- platform/mv3/extension/js/background.js | 1 + platform/mv3/extension/js/settings.js | 7 +++++++ 2 files changed, 8 insertions(+) diff --git a/platform/mv3/extension/js/background.js b/platform/mv3/extension/js/background.js index bee7b51b2b313..f07adb3011865 100644 --- a/platform/mv3/extension/js/background.js +++ b/platform/mv3/extension/js/background.js @@ -140,6 +140,7 @@ async function onPermissionsAdded(permissions) { function setDeveloperMode(state) { rulesetConfig.developerMode = state === true; toggleDeveloperMode(rulesetConfig.developerMode); + broadcastMessage({ developerMode: rulesetConfig.developerMode }); return Promise.all([ updateUserRules(), saveRulesetConfig(), diff --git a/platform/mv3/extension/js/settings.js b/platform/mv3/extension/js/settings.js index 1d1c1b5821377..10815a9fb8294 100644 --- a/platform/mv3/extension/js/settings.js +++ b/platform/mv3/extension/js/settings.js @@ -278,6 +278,13 @@ listen.onmessage = ev => { } } + if ( message.developerMode !== undefined ) { + if ( message.developerMode !== local.developerMode ) { + local.developerMode = message.developerMode; + render = true; + } + } + if ( message.adminRulesets !== undefined ) { if ( hashFromIterable(message.adminRulesets) !== hashFromIterable(local.adminRulesets) ) { local.adminRulesets = message.adminRulesets; From 4803e6f69bc068be8899012200fe1aa3ec3f327f Mon Sep 17 00:00:00 2001 From: u-RraaLL <92610420+u-RraaLL@users.noreply.github.com> Date: Sat, 7 Jun 2025 12:39:46 +0200 Subject: [PATCH 1005/1099] Edge status update (#3940) --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index d55ce59a1dd67..9d6d2a50593f3 100644 --- a/README.md +++ b/README.md @@ -19,9 +19,9 @@ uBlock Origin (uBO) | Browser | Install from ... | Status | | :-------: | ---------------- | ------ | | Get uBlock Origin for Firefox | Firefox Add-ons | [uBO works best on Firefox](https://github.com/gorhill/uBlock/wiki/uBlock-Origin-works-best-on-Firefox) | +| Get uBlock Origin for Microsoft Edge | Edge Add-ons | | Get uBlock Origin for Opera | Opera Add-ons | -| Get uBlock Origin for Chromium | Chrome Web Store | About Google Chrome's "This extension may soon no longer be supported" | -| Get uBlock Origin for Microsoft Edge | Edge Add-ons | [No longer updated and stuck at 1.62.0](https://github.com/uBlockOrigin/uBlock-issues/discussions/3641) | +| Get uBlock Origin for Chromium | Chrome Web Store | About Google Chrome's "This extension may soon no longer be supported"
End of support on Chrome 139 | | Get uBlock Origin for Thunderbird | Thunderbird Add-ons | [No longer updated and stuck at 1.49.2](https://github.com/uBlockOrigin/uBlock-issues/issues/2928) | *** @@ -94,7 +94,7 @@ In Thunderbird, uBlock Origin does not affect emails, just feeds. [Chrome Web Store][Chrome] -[Microsoft Edge Add-ons][Edge] (Published by: [Nicole Rolls][Nicole Rolls]) +[Microsoft Edge Add-ons][Edge] (Published by [Nicole Rolls][Nicole Rolls] until version 1.62. Ownership transfer at version 1.64.) [Opera Add-ons][Opera] From deb3247ea2c03b7f9bb8c3ee76806cac5c66689d Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Tue, 10 Jun 2025 09:37:01 -0400 Subject: [PATCH 1006/1099] Update README.md --- platform/mv3/README.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/platform/mv3/README.md b/platform/mv3/README.md index d80f9ceaa5096..2a2fcf01c21bc 100644 --- a/platform/mv3/README.md +++ b/platform/mv3/README.md @@ -7,8 +7,10 @@ The following assumes a linux environment. 1. Open Bash console 2. `git clone https://github.com/gorhill/uBlock.git` 3. `cd uBlock` -4. `make mv3-[platform]`, where `[platform]` is either `chromium`, `edge`, `firefox`, or `safari` -5. This will fully build uBO Lite, and during the process filter lists will be downloaded from their respective remote servers +4. `git submodule init` +5. `git submodule update` +6. `make mv3-[platform]`, where `[platform]` is either `chromium`, `edge`, `firefox`, or `safari` +7. This will fully build uBO Lite, and during the process filter lists will be downloaded from their respective remote servers Upon completion of the script, the resulting extension package will become present in: From 76d8b97869e500d5cb07644ec8fb395a8a040b91 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Tue, 10 Jun 2025 14:57:41 -0400 Subject: [PATCH 1007/1099] [mv3] Collect/apply highly generic cosmetic exceptions across lists Related issues: - https://github.com/uBlockOrigin/uBOL-home/issues/365 - https://github.com/uBlockOrigin/uAssets/issues/28770 - https://github.com/uBlockOrigin/uAssets/issues/28129 --- platform/mv3/make-rulesets.js | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/platform/mv3/make-rulesets.js b/platform/mv3/make-rulesets.js index 8a9b0c7124517..3a5ed28248a27 100644 --- a/platform/mv3/make-rulesets.js +++ b/platform/mv3/make-rulesets.js @@ -854,18 +854,18 @@ async function processGenericHighCosmeticFilters( .filter(a => a.key === undefined) .map(a => a.selector) ); + // https://github.com/uBlockOrigin/uBOL-home/issues/365 if ( genericExceptionList ) { - const genericExceptionSet = new Set( - genericExceptionList - .filter(a => a.key === undefined) - .map(a => a.selector) - ); - for ( const selector of genericExceptionSet ) { - if ( genericSelectorSet.has(selector) === false ) { continue; } - genericSelectorSet.delete(selector); - log(`\tRemoving excepted highly generic filter ##${selector}`); + for ( const entry of genericExceptionList ) { + if ( entry.key !== undefined ) { continue; } + globalHighlyGenericExceptionSet.add(entry.selector); } } + for ( const selector of globalHighlyGenericExceptionSet ) { + if ( genericSelectorSet.has(selector) === false ) { continue; } + genericSelectorSet.delete(selector); + log(`\tRemoving excepted highly generic filter ##${selector}`); + } if ( genericSelectorSet.size === 0 ) { return 0; } const selectorLists = Array.from(genericSelectorSet).sort().join(',\n'); const originalScriptletMap = await loadAllSourceScriptlets(); @@ -888,6 +888,8 @@ async function processGenericHighCosmeticFilters( return genericSelectorSet.size; } +const globalHighlyGenericExceptionSet = new Set(); + /******************************************************************************/ // This merges selectors which are used by the same hostnames From 9f3282d74f8a7edc0f326140b115bb49c776ef24 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Tue, 10 Jun 2025 15:18:47 -0400 Subject: [PATCH 1008/1099] [mv3] Safari: expand rsach `requestDomains` entries into own rule Related issue: https://github.com/uBlockOrigin/uBOL-home/issues/358 --- platform/mv3/make-rulesets.js | 50 ++++++----------------- platform/mv3/safari/patch-ruleset.js | 60 ++++++++++++++++++++++++++++ 2 files changed, 72 insertions(+), 38 deletions(-) create mode 100644 platform/mv3/safari/patch-ruleset.js diff --git a/platform/mv3/make-rulesets.js b/platform/mv3/make-rulesets.js index 3a5ed28248a27..89e240448328f 100644 --- a/platform/mv3/make-rulesets.js +++ b/platform/mv3/make-rulesets.js @@ -324,38 +324,12 @@ const isURLSkip = rule => /******************************************************************************/ -function patchRuleset(ruleset) { - if ( platform !== 'safari' ) { return ruleset; } - const out = []; - for ( const rule of ruleset ) { - const condition = rule.condition; - if ( rule.action.type === 'modifyHeaders' ) { - log(`Safari's incomplete API: ${JSON.stringify(rule)}`, true); - continue; - } - if ( Array.isArray(rule.condition.responseHeaders) ) { - log(`Safari's incomplete API: ${JSON.stringify(rule)}`, true); - continue; - } - if ( Array.isArray(condition.requestMethods) ) { - log(`Safari's incomplete API: ${JSON.stringify(rule)}`, true); - continue; - } - if ( Array.isArray(condition.excludedRequestMethods) ) { - log(`Safari's incomplete API: ${JSON.stringify(rule)}`, true); - continue; - } - if ( Array.isArray(condition.initiatorDomains) ) { - condition.domains = condition.initiatorDomains; - delete condition.initiatorDomains; - } - if ( Array.isArray(condition.excludedInitiatorDomains) ) { - condition.excludedDomains = condition.excludedInitiatorDomains; - delete condition.excludedInitiatorDomains; - } - out.push(rule); - } - return out; +async function patchRuleset(ruleset) { + return import(`./${platform}/patch-ruleset.js`).then(module => { + return module.patchRuleset(ruleset) + }).catch(( ) => { + return ruleset; + }); } /******************************************************************************/ @@ -550,7 +524,7 @@ async function processNetworkFilters(assetDetails, network) { } } - const plainGood = patchRuleset( + const plainGood = await patchRuleset( rules.filter(rule => isSafe(rule) && isRegex(rule) === false) ); log(`\tPlain good: ${plainGood.length}`); @@ -560,12 +534,12 @@ async function processNetworkFilters(assetDetails, network) { .join('\n'), true ); - const regexes = patchRuleset( + const regexes = await patchRuleset( rules.filter(rule => isSafe(rule) && isRegex(rule)) ); log(`\tMaybe good (regexes): ${regexes.length}`); - const redirects = patchRuleset( + const redirects = await patchRuleset( rules.filter(rule => isUnsupported(rule) === false && isRedirect(rule) @@ -579,19 +553,19 @@ async function processNetworkFilters(assetDetails, network) { }); log(`\tredirect=: ${redirects.length}`); - const removeparamsGood = patchRuleset( + const removeparamsGood = await patchRuleset( rules.filter(rule => isUnsupported(rule) === false && isRemoveparam(rule) ) ); - const removeparamsBad = patchRuleset( + const removeparamsBad = await patchRuleset( rules.filter(rule => isUnsupported(rule) && isRemoveparam(rule) ) ); log(`\tremoveparams= (accepted/discarded): ${removeparamsGood.length}/${removeparamsBad.length}`); - const modifyHeaders = patchRuleset( + const modifyHeaders = await patchRuleset( rules.filter(rule => isUnsupported(rule) === false && isModifyHeaders(rule) diff --git a/platform/mv3/safari/patch-ruleset.js b/platform/mv3/safari/patch-ruleset.js new file mode 100644 index 0000000000000..44bbce061ffa4 --- /dev/null +++ b/platform/mv3/safari/patch-ruleset.js @@ -0,0 +1,60 @@ +/******************************************************************************* + + uBlock Origin - a comprehensive, efficient content blocker + Copyright (C) 2025-present Raymond Hill + + 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/}. + + Home: https://github.com/gorhill/uBlock +*/ + +function patchRuleWithRequestDomains(rule, out) { + const requestDomains = rule.condition.requestDomains; + delete rule.condition.requestDomains; + for ( const domain of requestDomains ) { + const newRule = structuredClone(rule); + newRule.condition.urlFilter = `||${domain}^`; + out.push(newRule); + } +} + +export function patchRuleset(ruleset) { + const out = []; + for ( const rule of ruleset ) { + const condition = rule.condition; + if ( rule.action.type === 'modifyHeaders' ) { continue; } + if ( Array.isArray(rule.condition.responseHeaders) ) { continue; } + if ( Array.isArray(condition.requestMethods) ) { continue; } + if ( Array.isArray(condition.excludedRequestMethods) ) { continue; } + if ( Array.isArray(condition.initiatorDomains) ) { + condition.domains = condition.initiatorDomains; + delete condition.initiatorDomains; + } + if ( Array.isArray(condition.excludedInitiatorDomains) ) { + condition.excludedDomains = condition.excludedInitiatorDomains; + delete condition.excludedInitiatorDomains; + } + if ( + Array.isArray(condition.requestDomains) && + Array.isArray(condition.excludedRequestDomains) === false && + Boolean(condition.regexFilter) === false && + Boolean(condition.urlFilter) === false + ) { + patchRuleWithRequestDomains(rule, out); + } else { + out.push(rule); + } + } + return out; +} From 950f7920b34351d8151efc18d000c8ddbd1dfbae Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Tue, 10 Jun 2025 15:24:53 -0400 Subject: [PATCH 1009/1099] [mv3] Update build script --- tools/make-mv3.sh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tools/make-mv3.sh b/tools/make-mv3.sh index cb2f19c56e562..5f59afbd23701 100755 --- a/tools/make-mv3.sh +++ b/tools/make-mv3.sh @@ -122,6 +122,8 @@ cp -R "$UBO_DIR"/src/js/resources "$UBOL_BUILD_DIR"/js/ cp -R platform/mv3/scriptlets "$UBOL_BUILD_DIR"/ mkdir -p "$UBOL_BUILD_DIR"/web_accessible_resources cp "$UBO_DIR"/src/web_accessible_resources/* "$UBOL_BUILD_DIR"/web_accessible_resources/ +cp -R platform/mv3/"$PLATFORM" "$UBOL_BUILD_DIR"/ + cd "$UBOL_BUILD_DIR" node --no-warnings make-rulesets.js output="$UBOL_DIR" platform="$PLATFORM" if [ -n "$BEFORE" ]; then From e43de8c185496ab97a1eaf2974b91a7214294432 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Wed, 11 Jun 2025 09:40:52 -0400 Subject: [PATCH 1010/1099] [mv3] Firefox doesn't yet support `condition.responseHeaders` --- platform/mv3/firefox/patch-ruleset.js | 30 +++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 platform/mv3/firefox/patch-ruleset.js diff --git a/platform/mv3/firefox/patch-ruleset.js b/platform/mv3/firefox/patch-ruleset.js new file mode 100644 index 0000000000000..a8bd25c1b6f72 --- /dev/null +++ b/platform/mv3/firefox/patch-ruleset.js @@ -0,0 +1,30 @@ +/******************************************************************************* + + uBlock Origin - a comprehensive, efficient content blocker + Copyright (C) 2025-present Raymond Hill + + 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/}. + + Home: https://github.com/gorhill/uBlock +*/ + +export function patchRuleset(ruleset) { + const out = []; + for ( const rule of ruleset ) { + const condition = rule.condition; + if ( Array.isArray(condition.responseHeaders) ) { continue; } + out.push(rule); + } + return out; +} From 7060be775c6552d17a4785cfcc0e20212379686b Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Wed, 11 Jun 2025 15:23:14 -0400 Subject: [PATCH 1011/1099] Import translation work from https://crowdin.com/project/ublock --- platform/mv3/description/webstore.ar.txt | 2 +- platform/mv3/description/webstore.fil.txt | 4 +- platform/mv3/description/webstore.id.txt | 6 +- platform/mv3/description/webstore.pt_PT.txt | 6 +- .../mv3/extension/_locales/ar/messages.json | 18 ++-- .../mv3/extension/_locales/fil/messages.json | 86 +++++++++---------- .../mv3/extension/_locales/gl/messages.json | 2 +- .../mv3/extension/_locales/id/messages.json | 24 +++--- .../extension/_locales/pt_PT/messages.json | 8 +- .../extension/_locales/zh_CN/messages.json | 14 +-- src/_locales/fil/messages.json | 14 +-- src/_locales/nl/messages.json | 2 +- src/_locales/pt_PT/messages.json | 26 +++--- src/_locales/sk/messages.json | 2 +- 14 files changed, 107 insertions(+), 107 deletions(-) diff --git a/platform/mv3/description/webstore.ar.txt b/platform/mv3/description/webstore.ar.txt index 5720a73c503ea..459716f83ec6e 100644 --- a/platform/mv3/description/webstore.ar.txt +++ b/platform/mv3/description/webstore.ar.txt @@ -1,4 +1,4 @@ -uBO Lite (uBOL) is an MV3-based content blocker. +uBO Lite (uBOL) هو مانع محتوى يعتمد على MV3. تتوافق مجموعة القواعد الافتراضية مع مجموعة عوامل التصفية الافتراضية لـ uBlock Origin: diff --git a/platform/mv3/description/webstore.fil.txt b/platform/mv3/description/webstore.fil.txt index 2b379421823e6..7da60386564c1 100644 --- a/platform/mv3/description/webstore.fil.txt +++ b/platform/mv3/description/webstore.fil.txt @@ -1,4 +1,4 @@ -uBO Lite (uBOL) is an MV3-based content blocker. +Isang content blocker na nakabase sa MV3 ang uBO Lite (uBOL). Tulad ng uBlock Origin, ito rin ang mga default na listahan ng mga filter: @@ -7,6 +7,6 @@ Tulad ng uBlock Origin, ito rin ang mga default na listahan ng mga filter: - EasyPrivacy - Listahan ni Peter Lowe sa mga ad at tracking server (Peter Lowe’s Ad and tracking server list) -You can enable more rulesets by visiting the options page -- click the _Cogs_ icon in the popup panel. +Maaring magpagana ng mas maraming ruleset sa page ng options -- pindutin ang _Cogs_ sa popup na panel. Deklaratibo lamang ang uBOL, kaya hindi nito kailangan ng permanenteng proseso upang mag-filter, at mainam na ginagawa ng browser mismo imbes na ekstensyon ang pagfi-filter sa content na nakabase sa CSS o JS. Ibig-sabihin, hindi kumokonsyumo ng CPU o memorya ang uBOL habang nanghaharang -- ang proseso ng trabahante ng serbisyo ay kailangan _lang_ kung nasa popup panel o pahina ng opsyon ka. diff --git a/platform/mv3/description/webstore.id.txt b/platform/mv3/description/webstore.id.txt index 1ea41ec30bc5f..27f5a944fcf8c 100644 --- a/platform/mv3/description/webstore.id.txt +++ b/platform/mv3/description/webstore.id.txt @@ -1,4 +1,4 @@ -uBO Lite (uBOL) is an MV3-based content blocker. +uBO Lite (uBOL) adalah pemblokir konten berbasis MV3. Kumpulan aturan bawaan sesuai dengan kumpulan penyaringan bawaan uBlock Origin: @@ -7,6 +7,6 @@ Kumpulan aturan bawaan sesuai dengan kumpulan penyaringan bawaan uBlock Origin: - EasyPrivacy - Daftar server iklan dan pelacak Peter Lowe -Anda dapat mengaktifkan lebih banyak kumpulan pengaturan dengan mengunjungi halaman opsi - klik ikon _Cogs_ pada panel popup. +Anda dapat mengaktifkan lebih banyak rangkaian aturan dengan mengunjungi halaman opsi -- klik ikon _Cogs_ di panel popup. -uBOL sepenuhnya deklaratif, yang mana tidak membutuhkan proses permanen uBOL agar penyaringan dapat terjadi, dan penyaringan konten berbasis injeksi CSS/JS dilakukan sepenuhnya oleh peramban itu sendiri ketimbang oleh ekstensi. Ini berarti bahwa uBOL sendiri tidak mengkonsumsi sumber daya CPU/memori selama melakukan pemblokiran konten -- proses pekerja layanan uBOL dibutuhkan _hanya_ ketika Anda berinteraksi dengan panel popup atau opsi halaman. +uBOL sepenuhnya bersifat deklaratif, artinya tidak diperlukan proses uBOL permanen agar penyaringan dapat terjadi, dan penyaringan konten berbasis injeksi CSS/JS dilakukan secara andal oleh browser itu sendiri dan bukan oleh ekstensi. Artinya uBOL sendiri tidak mengonsumsi sumber daya CPU/memori saat pemblokiran konten sedang berlangsung -- proses pekerja layanan uBOL diperlukan _hanya_ saat Anda berinteraksi dengan panel popup atau halaman opsi. diff --git a/platform/mv3/description/webstore.pt_PT.txt b/platform/mv3/description/webstore.pt_PT.txt index 96547aeb1c5e4..9a317c90d2c68 100644 --- a/platform/mv3/description/webstore.pt_PT.txt +++ b/platform/mv3/description/webstore.pt_PT.txt @@ -1,12 +1,12 @@ O uBO Lite (uBOL) é um bloqueador de conteúdo baseado em MV3. -O conjunto de regras padrão corresponde ao conjunto de filtros padrão do uBlock Origin: +O conjunto de regras predefinido corresponde ao conjunto de filtros predefinido do uBlock Origin: - Listas de filtros integrados do uBlock Origin - EasyList - EasyPrivacy - Peter Lowe’s Ad and tracking server list -Pode ativar mais conjuntos de regras visitando a página de opções -- clique no ícone _Cogs_ na janela flutuante. +Pode ativar mais conjuntos de regras visitando a página de opções -- clique no ícone _Cogs_ no painel popup. -O uBOL é totalmente declarativo, o que elimina a necessidade de um processo ativo constante para a filtragem ocorrer. A injeção de CSS e JS para filtragem de conteúdo é efetuada de maneira confiável pelo navegador, não dependendo da extensão. Isso significa que o uBOL por si só não gasta recursos de CPU/memória enquanto o bloqueio de conteúdo está a acontecer -- o processo do trabalhador de serviço do uBOL é necessário apenas quando se interage com a janela flutuante ou as páginas de opções. +O uBOL é totalmente declarativo, o que significa que não é necessário um processo permanente do uBOL para que a filtragem ocorra, e a filtragem de conteúdos baseada em injeção de CSS/JS é realizada de forma fiável pelo próprio navegador, e não pela extensão. Isto significa que o próprio uBOL não consome recursos de CPU/memória enquanto o bloqueio de conteúdo está ativo -- o processo do service worker do uBOL é necessário apenas quando interage com o painel popup ou com as páginas de opções. diff --git a/platform/mv3/extension/_locales/ar/messages.json b/platform/mv3/extension/_locales/ar/messages.json index d8f33ce545ddf..4cc2b76f45c99 100644 --- a/platform/mv3/extension/_locales/ar/messages.json +++ b/platform/mv3/extension/_locales/ar/messages.json @@ -20,7 +20,7 @@ "description": "appears as tab name in dashboard" }, "developPageName": { - "message": "Develop", + "message": "تطوير", "description": "appears as tab name in dashboard. Inspired from 'Develop' menu in Safari, see https://developer.apple.com/documentation/safari-developer-tools/develop-menu" }, "aboutPageName": { @@ -240,11 +240,11 @@ "description": "Short description for a checkbox in the options page" }, "developerModeLabel": { - "message": "Developer mode", + "message": "وضع المطور", "description": "Label for a checkbox in the options page" }, "developerModeLegend": { - "message": "Enables access to features suitable for technical users.", + "message": "يمكن الوصول إلى الميزات المناسبة للمستخدمين التقنيين.", "description": "Short description for a checkbox in the options page" }, "findListsPlaceholder": { @@ -288,11 +288,11 @@ "description": "A button to navigate to the blocked page" }, "zapperTipEnter": { - "message": "Enter element zapper mode", + "message": "دخول وضع تحديد العناصر السريع", "description": "Tooltip for the button used to enter zapper mode" }, "zapperTipQuit": { - "message": "Exit element zapper mode", + "message": "الخروج من وضع تحديد العناصر السريع", "description": "Tooltip for the button used to exit zapper mode" }, "saveButton": { @@ -300,11 +300,11 @@ "description": "Text for buttons used to save changes" }, "revertButton": { - "message": "Revert", + "message": "إرجاع", "description": "Text for buttons used to revert changes" }, "importAndAppendButton": { - "message": "Import and append…", + "message": "استيراد واضافة…", "description": "Text for buttons used to import and append content" }, "exportButton": { @@ -312,11 +312,11 @@ "description": "Text for buttons used to export content" }, "dnrRulesSummary": { - "message": "Enter your own DNR rules below. Do not add content from untrusted sources.", + "message": "أدخل قواعد DNR الخاصة بك أدناه. لا تدرج محتوى من مصادر غير موثوق بها. ", "description": "Short description of the DNR rules editor pane" }, "dnrRulesCountInfo": { - "message": "Number of registered rules: {count}", + "message": "عدد القواعد المسجلة: {count}", "description": "Short sentence to report the number of currently registered DNR rules" } } diff --git a/platform/mv3/extension/_locales/fil/messages.json b/platform/mv3/extension/_locales/fil/messages.json index d1ba182b1be6c..2f946e9b03dd9 100644 --- a/platform/mv3/extension/_locales/fil/messages.json +++ b/platform/mv3/extension/_locales/fil/messages.json @@ -20,7 +20,7 @@ "description": "appears as tab name in dashboard" }, "developPageName": { - "message": "Develop", + "message": "Bumuo", "description": "appears as tab name in dashboard. Inspired from 'Develop' menu in Safari, see https://developer.apple.com/documentation/safari-developer-tools/develop-menu" }, "aboutPageName": { @@ -36,7 +36,7 @@ "description": "Label in the popup panel for the current filtering mode" }, "popupTipReport": { - "message": "Report an issue on this website", + "message": "Magulat ng problema sa website na ito", "description": "Tooltip used for the 'chat' icon in the panel" }, "popupTipDashboard": { @@ -108,71 +108,71 @@ "description": "Shown in the About pane" }, "supportS6H": { - "message": "Report a filter issue", + "message": "Magulat ng problema sa filter ", "description": "Header of 'Report a filter issue' section in Support pane" }, "supportS3P1": { - "message": "Report filter issues with specific websites to the uBlockOrigin/uAssets issue tracker. Requires a GitHub account.", + "message": "Magreklamo dito ng mga isyu sa filter sa mga website: uBlockOrigin/uAssets issue tracker. Nangangailangan ng account sa GitHub.", "description": "First paragraph of 'Filter issues' section in Support pane" }, "supportS5H": { - "message": "Troubleshooting information", + "message": "Inpormasyon para sa troubleshooting", "description": "Label of 'Troubleshooting information' section in 'Report a filter issue' page" }, "supportS6P1S1": { - "message": "To avoid burdening volunteers with duplicate reports, please verify that the issue has not already been reported. Note: clicking the button will cause the page's origin to be sent to GitHub.", + "message": "Upang hindi makagambala ng mga volunteer sa mga umuulit na ulat, pakisigurado na hindi pa narereklamo ang iyong isyu. Paalala: Mapapadala sa Github ang origin ng page na ito pagpindot dito.", "description": "A paragraph in the filter issue reporter section" }, "supportFindSpecificButton": { - "message": "Find similar reports on GitHub", + "message": "Maghanap ng katulad ng ulat sa GitHub", "description": "A clickable link in the filter issue reporter section" }, "supportS6URL": { - "message": "Address of the webpage:", + "message": "Lokasyon ng webpage:", "description": "Label for the URL of the page" }, "supportS6Select1": { - "message": "The webpage…", + "message": "Ang webpage", "description": "Label for widget to select type of issue" }, "supportS6Select1Option0": { - "message": "-- Pick an entry --", + "message": "-- Mamili --", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option1": { - "message": "Shows ads or ad leftovers", + "message": "Nagpapakita ng ads o mga bakas ng ads", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option2": { - "message": "Has overlays or other nuisances", + "message": "May mga overlay o iba pang harang", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option3": { - "message": "Detects uBO Lite", + "message": "Nakadedetect ng uBO Lite", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option4": { - "message": "Has privacy-related issues", + "message": "May mga isyu sa privacy", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option5": { - "message": "Malfunctions when uBO Lite is enabled", + "message": "Hindi gumagana nang maayos kapag nakabukas ang uBO Lite", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option6": { - "message": "Opens unwanted tabs or windows", + "message": "Nagbubukas ng mga hindi kailangang tab o window", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option7": { - "message": "Leads to badware, phishing", + "message": "Patungo sa badware o phishing", "description": "An entry in the widget used to select the type of issue" }, "supportS6Checkbox1": { - "message": "Label the webpage as “NSFW” (“Not Safe For Work”)", + "message": "Markahan ang webpage na \"NSFW\" (“Not Safe For Work”)", "description": "A checkbox to use for NSFW sites" }, "supportReportSpecificButton": { - "message": "Create new report on GitHub", + "message": "Gumawa ng bagong ulat sa Github", "description": "Text for button which open an external webpage in Support pane" }, "defaultFilteringModeSectionLabel": { @@ -212,11 +212,11 @@ "description": "This describes the 'complete' filtering mode" }, "noFilteringModeDescription": { - "message": "List of websites for which no filtering will take place.", + "message": "Listahan ng mga website kung saan walang magagawang filtering. ", "description": "A short description for the editable field which lists trusted sites" }, "noFilteringModePlaceholder": { - "message": "[hostnames only]\nexample.com\ngames.example\n...", + "message": "[hostnames lamang]\nexample.com\ngames.example \n...", "description": "Default text for in edit field" }, "behaviorSectionLabel": { @@ -228,15 +228,15 @@ "description": "Label for a checkbox in the options page" }, "showBlockedCountLabel": { - "message": "Show the number of blocked requests on the toolbar icon", + "message": "Ipakita ang dami ng napigilang mga request sa toolbar na icon", "description": "Label for a checkbox in the options page" }, "enableStrictBlockLabel": { - "message": "Enable strict blocking", + "message": "Paganahin ang striktong pagharang", "description": "Label for a checkbox in the options page" }, "enableStrictBlockLegend": { - "message": "Navigation to potentially undesirable sites will be blocked, and you will be offered the option to proceed.", + "message": "Haharangan ang pagpunta sa mga hindi nais na mga site, at bibigyan ka ng pagkakataon na magpatuloy.", "description": "Short description for a checkbox in the options page" }, "developerModeLabel": { @@ -244,79 +244,79 @@ "description": "Label for a checkbox in the options page" }, "developerModeLegend": { - "message": "Enables access to features suitable for technical users.", + "message": "Nagbibigay ng mga katangian para sa mga teknikal na gumagamit.", "description": "Short description for a checkbox in the options page" }, "findListsPlaceholder": { - "message": "Find lists", + "message": "Maghanap ng mga listahan", "description": "Placeholder for the input field used to find lists" }, "strictblockTitle": { - "message": "Page blocked", + "message": "Nakaharang na page", "description": "Webpage title for the strict-blocked page" }, "strictblockSentence1": { - "message": "uBO Lite has prevented the following page from loading:", + "message": "Hindi pinayagan ng uBO Lite ang pagpunta sa page dahil:", "description": "Sentence used in the strict-blocked page" }, "strictblockReasonSentence1": { - "message": "The page was blocked because of a matching filter in {{listname}}.", + "message": "Hinarang ang page na ito dahil sa isang filter sa {{listname}}", "description": "Text informing about what is causing the page to be blocked" }, "strictblockRedirectSentence1": { - "message": "The blocked page wants to redirect to another site. If you choose to proceed, you will navigate directly to: {{url}}", + "message": "Nais magpatuloy ng page na ito sa isa pang site. Kung magpapatuloy ka, dadalhin ka sa: {{url}}", "description": "Text warning about an incoming redirect" }, "strictblockNoParamsPrompt": { - "message": "without parameters", + "message": "walang mga parameter", "description": "Label to be used for the parameter-less URL" }, "strictblockBack": { - "message": "Go back", + "message": "Bumalik", "description": "A button to go back to the previous webpage" }, "strictblockClose": { - "message": "Close this window", + "message": "Isara ang window na ito", "description": "A button to close the current tab" }, "strictblockDontWarn": { - "message": "Don't warn me again about this site", + "message": "Huwag mo na akong balaan tungkol sa site na ito", "description": "Label for checkbox in document-blocked page" }, "strictblockProceed": { - "message": "Proceed", + "message": "Magpatuloy", "description": "A button to navigate to the blocked page" }, "zapperTipEnter": { - "message": "Enter element zapper mode", + "message": "Paganahin ang element zapper mode", "description": "Tooltip for the button used to enter zapper mode" }, "zapperTipQuit": { - "message": "Exit element zapper mode", + "message": "Umalis sa element zapper mode", "description": "Tooltip for the button used to exit zapper mode" }, "saveButton": { - "message": "Save", + "message": "I-save", "description": "Text for buttons used to save changes" }, "revertButton": { - "message": "Revert", + "message": "Ibalik", "description": "Text for buttons used to revert changes" }, "importAndAppendButton": { - "message": "Import and append…", + "message": "I-import at idagdag...", "description": "Text for buttons used to import and append content" }, "exportButton": { - "message": "Export…", + "message": "I-export...", "description": "Text for buttons used to export content" }, "dnrRulesSummary": { - "message": "Enter your own DNR rules below. Do not add content from untrusted sources.", + "message": "Maglagay ng mga patakaran para sa DNR. Huwag magdadagdag mula sa mga source na hindi mapagkakatiwalaan.", "description": "Short description of the DNR rules editor pane" }, "dnrRulesCountInfo": { - "message": "Number of registered rules: {count}", + "message": "Dami ng mga nakaregister na patakaran: {count}", "description": "Short sentence to report the number of currently registered DNR rules" } } diff --git a/platform/mv3/extension/_locales/gl/messages.json b/platform/mv3/extension/_locales/gl/messages.json index 83e5af9256e4f..4f4e1a94c381b 100644 --- a/platform/mv3/extension/_locales/gl/messages.json +++ b/platform/mv3/extension/_locales/gl/messages.json @@ -20,7 +20,7 @@ "description": "appears as tab name in dashboard" }, "developPageName": { - "message": "Develop", + "message": "Desenvolvemento", "description": "appears as tab name in dashboard. Inspired from 'Develop' menu in Safari, see https://developer.apple.com/documentation/safari-developer-tools/develop-menu" }, "aboutPageName": { diff --git a/platform/mv3/extension/_locales/id/messages.json b/platform/mv3/extension/_locales/id/messages.json index 2a947994e1c18..1d201a6e97ffb 100644 --- a/platform/mv3/extension/_locales/id/messages.json +++ b/platform/mv3/extension/_locales/id/messages.json @@ -20,7 +20,7 @@ "description": "appears as tab name in dashboard" }, "developPageName": { - "message": "Develop", + "message": "Mengembangkan", "description": "appears as tab name in dashboard. Inspired from 'Develop' menu in Safari, see https://developer.apple.com/documentation/safari-developer-tools/develop-menu" }, "aboutPageName": { @@ -116,7 +116,7 @@ "description": "First paragraph of 'Filter issues' section in Support pane" }, "supportS5H": { - "message": "Troubleshooting information", + "message": "Informasi pemecahan masalah", "description": "Label of 'Troubleshooting information' section in 'Report a filter issue' page" }, "supportS6P1S1": { @@ -240,11 +240,11 @@ "description": "Short description for a checkbox in the options page" }, "developerModeLabel": { - "message": "Developer mode", + "message": "Mode pengembang", "description": "Label for a checkbox in the options page" }, "developerModeLegend": { - "message": "Enables access to features suitable for technical users.", + "message": "Mengaktifkan akses ke fitur yang sesuai untuk pengguna teknis.", "description": "Short description for a checkbox in the options page" }, "findListsPlaceholder": { @@ -288,35 +288,35 @@ "description": "A button to navigate to the blocked page" }, "zapperTipEnter": { - "message": "Enter element zapper mode", + "message": "Masuk ke mode penghapus elemen", "description": "Tooltip for the button used to enter zapper mode" }, "zapperTipQuit": { - "message": "Exit element zapper mode", + "message": "Keluar dari mode penghapus elemen", "description": "Tooltip for the button used to exit zapper mode" }, "saveButton": { - "message": "Save", + "message": "Simpan", "description": "Text for buttons used to save changes" }, "revertButton": { - "message": "Revert", + "message": "Kembalikan", "description": "Text for buttons used to revert changes" }, "importAndAppendButton": { - "message": "Import and append…", + "message": "Impor dan tambahkan…", "description": "Text for buttons used to import and append content" }, "exportButton": { - "message": "Export…", + "message": "Ekspor…", "description": "Text for buttons used to export content" }, "dnrRulesSummary": { - "message": "Enter your own DNR rules below. Do not add content from untrusted sources.", + "message": "Masukkan aturan DNR Anda sendiri di bawah ini. Jangan menambahkan konten dari sumber yang tidak tepercaya.", "description": "Short description of the DNR rules editor pane" }, "dnrRulesCountInfo": { - "message": "Number of registered rules: {count}", + "message": "Jumlah aturan yang terdaftar: {count}", "description": "Short sentence to report the number of currently registered DNR rules" } } diff --git a/platform/mv3/extension/_locales/pt_PT/messages.json b/platform/mv3/extension/_locales/pt_PT/messages.json index 36553b171f11a..2cf92f9a00ec4 100644 --- a/platform/mv3/extension/_locales/pt_PT/messages.json +++ b/platform/mv3/extension/_locales/pt_PT/messages.json @@ -20,7 +20,7 @@ "description": "appears as tab name in dashboard" }, "developPageName": { - "message": "Desenvolver", + "message": "Programação", "description": "appears as tab name in dashboard. Inspired from 'Develop' menu in Safari, see https://developer.apple.com/documentation/safari-developer-tools/develop-menu" }, "aboutPageName": { @@ -288,11 +288,11 @@ "description": "A button to navigate to the blocked page" }, "zapperTipEnter": { - "message": "Entrar no modo \"element zapper\"", + "message": "Entrar no modo de remoção rápida de elemento", "description": "Tooltip for the button used to enter zapper mode" }, "zapperTipQuit": { - "message": "Sair do modo \"element zapper\"", + "message": "Sair do modo de remoção rápida de elemento", "description": "Tooltip for the button used to exit zapper mode" }, "saveButton": { @@ -312,7 +312,7 @@ "description": "Text for buttons used to export content" }, "dnrRulesSummary": { - "message": "Insira as suas próprias regras de DNR abaixo. Não adicione conteúdo de fontes não confiáveis.", + "message": "Introduza as suas próprias regras DNR abaixo. Não adicione conteúdo de fontes não confiáveis.", "description": "Short description of the DNR rules editor pane" }, "dnrRulesCountInfo": { diff --git a/platform/mv3/extension/_locales/zh_CN/messages.json b/platform/mv3/extension/_locales/zh_CN/messages.json index 4067220fa0cda..7dba484955701 100644 --- a/platform/mv3/extension/_locales/zh_CN/messages.json +++ b/platform/mv3/extension/_locales/zh_CN/messages.json @@ -20,7 +20,7 @@ "description": "appears as tab name in dashboard" }, "developPageName": { - "message": "Develop", + "message": "开发", "description": "appears as tab name in dashboard. Inspired from 'Develop' menu in Safari, see https://developer.apple.com/documentation/safari-developer-tools/develop-menu" }, "aboutPageName": { @@ -240,11 +240,11 @@ "description": "Short description for a checkbox in the options page" }, "developerModeLabel": { - "message": "Developer mode", + "message": "开发者模式", "description": "Label for a checkbox in the options page" }, "developerModeLegend": { - "message": "Enables access to features suitable for technical users.", + "message": "启用访问适合技术用户的功能。", "description": "Short description for a checkbox in the options page" }, "findListsPlaceholder": { @@ -296,19 +296,19 @@ "description": "Tooltip for the button used to exit zapper mode" }, "saveButton": { - "message": "Save", + "message": "保存", "description": "Text for buttons used to save changes" }, "revertButton": { - "message": "Revert", + "message": "还原", "description": "Text for buttons used to revert changes" }, "importAndAppendButton": { - "message": "Import and append…", + "message": "导入并添加…", "description": "Text for buttons used to import and append content" }, "exportButton": { - "message": "Export…", + "message": "导出…", "description": "Text for buttons used to export content" }, "dnrRulesSummary": { diff --git a/src/_locales/fil/messages.json b/src/_locales/fil/messages.json index cc8937f5ead86..ccedc949e0831 100644 --- a/src/_locales/fil/messages.json +++ b/src/_locales/fil/messages.json @@ -484,11 +484,11 @@ "description": "Filter lists section name" }, "3pGroupSocial": { - "message": "Social widgets", + "message": "Panlipunang mga widget", "description": "Filter lists section name" }, "3pGroupCookies": { - "message": "Cookie notices", + "message": "Pabatid para sa mga cookie", "description": "Filter lists section name" }, "3pGroupAnnoyances": { @@ -540,11 +540,11 @@ "description": "Warning against copy-pasting filters from random sources" }, "1pEnableMyFiltersLabel": { - "message": "Enable my custom filters", + "message": "Paganahin ang mga pasadyang filter", "description": "Label for the checkbox use to enable/disable 'My filters' list" }, "1pTrustMyFiltersLabel": { - "message": "Allow custom filters requiring trust", + "message": "Hayaan ang mga pansariling filter na kailangan ng tiwala", "description": "Label for the checkbox use to trust the content of 'My filters' list" }, "1pImport": { @@ -1012,7 +1012,7 @@ "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option7": { - "message": "Leads to badware, phishing", + "message": "Patungo sa badware o phishing", "description": "An entry in the widget used to select the type of issue" }, "supportS6Checkbox1": { @@ -1192,7 +1192,7 @@ "description": "Button text to navigate to the blocked page" }, "docblockedRedirectPrompt": { - "message": "The blocked page wants to redirect to another site. If you choose to proceed, you will navigate directly to: {{url}}", + "message": "Nais nitong website na dalhin ka sa iss pang site. Kung tutuloy ka, pupunta ka sa: {{url}}", "description": "Text warning about an incoming redirect" }, "cloudPush": { @@ -1272,7 +1272,7 @@ "description": "Label for keyboard shortcut used to toggle cosmetic filtering" }, "toggleJavascript": { - "message": "Toggle JavaScript", + "message": "Paganahin ang JavaScript ", "description": "Label for keyboard shortcut used to toggle no-scripting switch" }, "relaxBlockingMode": { diff --git a/src/_locales/nl/messages.json b/src/_locales/nl/messages.json index 691690677810d..c6c06859b0955 100644 --- a/src/_locales/nl/messages.json +++ b/src/_locales/nl/messages.json @@ -964,7 +964,7 @@ "description": "Header of 'Report a filter issue' section in Support pane" }, "supportS6P1S1": { - "message": "Controleer of het probleem niet eerder is gemeld om te voorkomen dat vrijwilligers met dubbele meldingen worden belast.", + "message": "Controleer of het probleem niet eerder is gemeld om te voorkomen dat vrijwilligers met dubbele meldingen worden belast. Noot: op de knop klikken zorgt ervoor dat de oorsprong van de pagina naar GitHub wordt verzonden.", "description": "A paragraph in the filter issue reporter section" }, "supportS6P2S1": { diff --git a/src/_locales/pt_PT/messages.json b/src/_locales/pt_PT/messages.json index 26be572860377..24827a31fdb4c 100644 --- a/src/_locales/pt_PT/messages.json +++ b/src/_locales/pt_PT/messages.json @@ -4,7 +4,7 @@ "description": "extension name." }, "extShortDesc": { - "message": "Finalmente, um bloqueador eficiente. Leve na CPU e memória.", + "message": "Finalmente, um bloqueador eficiente. Leve para a CPU e a memória.", "description": "this will be in the Chrome web store: must be 132 characters or less" }, "dashboardName": { @@ -48,7 +48,7 @@ "description": "appears as tab name in dashboard" }, "statsPageName": { - "message": "uBlock₀ — Registador", + "message": "uBlock₀ — Registo", "description": "Title for the logger window" }, "aboutPageName": { @@ -116,7 +116,7 @@ "description": "English: Click to open the dashboard" }, "popupTipZapper": { - "message": "Entrar no modo \"zapper\" de elemento", + "message": "Entrar no modo de remoção rápida de elemento", "description": "Tooltip for the element-zapper icon in the popup panel" }, "popupTipPicker": { @@ -124,7 +124,7 @@ "description": "English: Enter element picker mode" }, "popupTipLog": { - "message": "Abrir o registador", + "message": "Abrir o registo", "description": "Tooltip used for the logger icon in the panel" }, "popupTipReport": { @@ -688,23 +688,23 @@ "description": "Tooltip for the popup panel button in the logger page" }, "loggerInfoTip": { - "message": "uBlock Origin wiki: O registador", + "message": "uBlock Origin wiki: O registo", "description": "Tooltip for the top-right info label in the logger page" }, "loggerClearTip": { - "message": "Limpar registador", + "message": "Limpar registo", "description": "Tooltip for the eraser in the logger page; used to blank the content of the logger" }, "loggerPauseTip": { - "message": "Pausar registador (descartar todos os dados a receber)", + "message": "Pausar registo (descartar todos os dados a receber)", "description": "Tooltip for the pause button in the logger page" }, "loggerUnpauseTip": { - "message": "Retomar registador", + "message": "Retomar registo", "description": "Tooltip for the play button in the logger page" }, "loggerRowFiltererButtonTip": { - "message": "Alternar filtragem do registador", + "message": "Alternar filtragem do registo", "description": "Tooltip for the row filterer button in the logger page" }, "logFilterPrompt": { @@ -712,7 +712,7 @@ "description": "Placeholder string for logger output filtering input field" }, "loggerRowFiltererBuiltinTip": { - "message": "Opções de filtragem do registador", + "message": "Opções de filtragem do registo", "description": "Tooltip for the button to bring up logger output filtering options" }, "loggerRowFiltererBuiltinNot": { @@ -840,7 +840,7 @@ "description": "Message to show when a filter cannot be found in any filter lists" }, "loggerSettingDiscardPrompt": { - "message": "Entradas do registador que não preenchem todas as três condições abaixo serão automaticamente descartadas:", + "message": "Entradas do registo que não preenchem todas as três condições abaixo serão automaticamente descartadas:", "description": "Logger setting: A sentence to describe the purpose of the settings below" }, "loggerSettingPerEntryMaxAge": { @@ -936,7 +936,7 @@ "description": "Second paragraph of 'Filter issues' section in Support pane" }, "supportS3P3": { - "message": "Dicas: certifique-se de que as suas listas de filtros estão atualizadas. O registador é a ferramenta principal para diagnosticar problemas relacionados com filtros.", + "message": "Dicas: certifique-se de que as suas listas de filtros estão atualizadas. O registo é a ferramenta principal para diagnosticar problemas relacionados com filtros.", "description": "Third paragraph of 'Filter issues' section in Support pane" }, "supportS4H": { @@ -1136,7 +1136,7 @@ "description": "Firefox/Fennec-specific: Show Dashboard" }, "showNetworkLogButton": { - "message": "Mostrar registador", + "message": "Mostrar registo", "description": "Firefox/Fennec-specific: Show Logger" }, "fennecMenuItemBlockingOff": { diff --git a/src/_locales/sk/messages.json b/src/_locales/sk/messages.json index 331c1721c5437..b8dde7b7e1668 100644 --- a/src/_locales/sk/messages.json +++ b/src/_locales/sk/messages.json @@ -1084,7 +1084,7 @@ "description": "English: Reset to default settings..." }, "aboutRestoreDataConfirm": { - "message": "Všetky vaše nastavenia budú prepísané pomocou dáta zálohovaných dňa {{time}} a uBlock sa reštartne.\n\nPrepísať všetky existujúce nastavenia pomocou zálohovaných dát?", + "message": "Všetky vaše nastavenia budú prepísané dátami zálohovanými {{time}} a uBlock sa reštartuje.\n\nPrepísať všetky existujúce nastavenia pomocou zálohovaných dát?", "description": "Message asking user to confirm restore" }, "aboutRestoreDataError": { From ff8c527b993e31256ecef3b267aef7c6c9e6ebfd Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Thu, 12 Jun 2025 09:32:59 -0400 Subject: [PATCH 1012/1099] [mv3] Revert trying to transpose `requestDomains` This breaks uBOL -- unclear error message but disabling rulesets eventually unbreak the extension, thus possibly a case of going over the rule limit as a result of transposition. The `requestDomains` issue will have to wait for the official Safari fix. --- platform/mv3/safari/patch-ruleset.js | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/platform/mv3/safari/patch-ruleset.js b/platform/mv3/safari/patch-ruleset.js index 44bbce061ffa4..27cc6f1defb05 100644 --- a/platform/mv3/safari/patch-ruleset.js +++ b/platform/mv3/safari/patch-ruleset.js @@ -45,16 +45,7 @@ export function patchRuleset(ruleset) { condition.excludedDomains = condition.excludedInitiatorDomains; delete condition.excludedInitiatorDomains; } - if ( - Array.isArray(condition.requestDomains) && - Array.isArray(condition.excludedRequestDomains) === false && - Boolean(condition.regexFilter) === false && - Boolean(condition.urlFilter) === false - ) { - patchRuleWithRequestDomains(rule, out); - } else { - out.push(rule); - } + out.push(rule); } return out; } From 43a883396d57e3311b2ef5f2fc228bd04117a3c1 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Fri, 13 Jun 2025 11:10:16 -0400 Subject: [PATCH 1013/1099] [mv3] Fix regression in handling `important` option Related issue: https://github.com/uBlockOrigin/uBOL-home/issues/368 --- src/js/static-net-filtering.js | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/js/static-net-filtering.js b/src/js/static-net-filtering.js index 96b9729153002..3c0d3f73f8c97 100644 --- a/src/js/static-net-filtering.js +++ b/src/js/static-net-filtering.js @@ -4525,6 +4525,7 @@ StaticNetFilteringEngine.prototype.dnrFromCompiled = function(op, context, ...ar const realms = new Map([ [ BLOCK_REALM, { type: 'block', priority: 10 } ], [ ALLOW_REALM, { type: 'allow', priority: 30 } ], + [ BLOCK_REALM | IMPORTANT_REALM, { type: 'block', priority: 10 } ], [ REDIRECT_REALM, { type: 'redirect', priority: 11 } ], [ REMOVEPARAM_REALM, { type: 'removeparam', priority: 0 } ], [ CSP_REALM, { type: 'csp', priority: 0 } ], @@ -4532,7 +4533,7 @@ StaticNetFilteringEngine.prototype.dnrFromCompiled = function(op, context, ...ar [ URLTRANSFORM_REALM, { type: 'uritransform', priority: 0 } ], [ HEADERS_REALM, { type: 'block', priority: 10 } ], [ HEADERS_REALM | ALLOW_REALM, { type: 'allow', priority: 30 } ], - [ HEADERS_REALM | IMPORTANT_REALM, { type: 'allow', priority: 40 } ], + [ HEADERS_REALM | IMPORTANT_REALM, { type: 'allow', priority: 10 } ], [ URLSKIP_REALM, { type: 'urlskip', priority: 0 } ], ]); const partyness = new Map([ @@ -4556,6 +4557,7 @@ StaticNetFilteringEngine.prototype.dnrFromCompiled = function(op, context, ...ar 'other', ]); const ruleset = []; + const seen = new Set(); for ( const [ realmBits, realmDetails ] of realms ) { for ( const [ partyBits, partyName ] of partyness ) { for ( const typeName in typeNameToTypeValue ) { @@ -4579,12 +4581,16 @@ StaticNetFilteringEngine.prototype.dnrFromCompiled = function(op, context, ...ar rule.condition = rule.condition || {}; rule.condition.resourceTypes = [ typeName ]; } + const hash = JSON.stringify(rule); + if ( seen.has(hash) ) { continue; } + seen.add(hash); ruleset.push(rule); } } } } } + seen.clear(); // Adjust `important` priority for ( const rule of ruleset ) { From a12ed895dd96021b19962ce638dd55f2f6171b3b Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Fri, 13 Jun 2025 12:37:25 -0400 Subject: [PATCH 1014/1099] Import translation work from https://crowdin.com/project/ublock --- platform/mv3/extension/_locales/vi/messages.json | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/platform/mv3/extension/_locales/vi/messages.json b/platform/mv3/extension/_locales/vi/messages.json index 40f3ceff8c8aa..8fe4c4fcbe381 100644 --- a/platform/mv3/extension/_locales/vi/messages.json +++ b/platform/mv3/extension/_locales/vi/messages.json @@ -20,7 +20,7 @@ "description": "appears as tab name in dashboard" }, "developPageName": { - "message": "Develop", + "message": "Phát triển", "description": "appears as tab name in dashboard. Inspired from 'Develop' menu in Safari, see https://developer.apple.com/documentation/safari-developer-tools/develop-menu" }, "aboutPageName": { @@ -240,11 +240,11 @@ "description": "Short description for a checkbox in the options page" }, "developerModeLabel": { - "message": "Developer mode", + "message": "Chế độ nhà phát triển", "description": "Label for a checkbox in the options page" }, "developerModeLegend": { - "message": "Enables access to features suitable for technical users.", + "message": "Cho phép truy cập các tính năng dành cho người dùng nâng cao", "description": "Short description for a checkbox in the options page" }, "findListsPlaceholder": { @@ -296,23 +296,23 @@ "description": "Tooltip for the button used to exit zapper mode" }, "saveButton": { - "message": "Save", + "message": "Lưu", "description": "Text for buttons used to save changes" }, "revertButton": { - "message": "Revert", + "message": "Đặt lại", "description": "Text for buttons used to revert changes" }, "importAndAppendButton": { - "message": "Import and append…", + "message": "Nhập và thêm vào", "description": "Text for buttons used to import and append content" }, "exportButton": { - "message": "Export…", + "message": "Xuất...", "description": "Text for buttons used to export content" }, "dnrRulesSummary": { - "message": "Enter your own DNR rules below. Do not add content from untrusted sources.", + "message": "Nhập các luật lệ DNR của bạn vào ô bên dưới. Không nhập thông tin từ các nguồn không tin cậy.", "description": "Short description of the DNR rules editor pane" }, "dnrRulesCountInfo": { From b50341089dc0075ea6bb5e46d66822f012f0344f Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Fri, 13 Jun 2025 12:46:05 -0400 Subject: [PATCH 1015/1099] [mv3] Expand "Develop" pane Move "No filtering" section in "Settings" to "Develop" pane. It is now possible to view/edit the list of hostnames for any of the filtering mode. This takes care of these issues: - https://github.com/uBlockOrigin/uBOL-home/issues/270 - https://github.com/uBlockOrigin/uBOL-home/issues/297 Add ability to see all rulesets (read-only), to assist in investigating filtering issues. --- .../mv3/extension/_locales/en/messages.json | 28 +- platform/mv3/extension/css/develop.css | 84 +- .../mv3/extension/css/line-hor-dashed.png | Bin 0 -> 80 bytes platform/mv3/extension/dashboard.html | 41 +- platform/mv3/extension/js/admin.js | 15 +- platform/mv3/extension/js/background.js | 56 +- platform/mv3/extension/js/dashboard.js | 6 +- platform/mv3/extension/js/develop.js | 1325 +++++++---------- platform/mv3/extension/js/dnr-editor.js | 180 +++ platform/mv3/extension/js/dnr-parser.js | 40 +- platform/mv3/extension/js/mode-editor.js | 76 + platform/mv3/extension/js/mode-manager.js | 59 +- platform/mv3/extension/js/mode-parser.js | 211 +++ platform/mv3/extension/js/ro-dnr-editor.js | 95 ++ platform/mv3/extension/js/ruleset-manager.js | 58 +- platform/mv3/extension/js/rw-dnr-editor.js | 401 +++++ platform/mv3/extension/js/settings.js | 71 - .../extension/lib/codemirror/codemirror-ubol | 2 +- src/js/i18n.js | 5 + 19 files changed, 1735 insertions(+), 1018 deletions(-) create mode 100644 platform/mv3/extension/css/line-hor-dashed.png create mode 100644 platform/mv3/extension/js/dnr-editor.js create mode 100644 platform/mv3/extension/js/mode-editor.js create mode 100644 platform/mv3/extension/js/mode-parser.js create mode 100644 platform/mv3/extension/js/ro-dnr-editor.js create mode 100644 platform/mv3/extension/js/rw-dnr-editor.js diff --git a/platform/mv3/extension/_locales/en/messages.json b/platform/mv3/extension/_locales/en/messages.json index e0c2038f2bf95..f248475c1e708 100644 --- a/platform/mv3/extension/_locales/en/messages.json +++ b/platform/mv3/extension/_locales/en/messages.json @@ -295,6 +295,30 @@ "message": "Exit element zapper mode", "description": "Tooltip for the button used to exit zapper mode" }, + "developDropdownLabel": { + "message": "View:", + "description": "A label of a dropdown list" + }, + "developOptionFilteringModeDetails": { + "message": "Filtering mode details", + "description": "An option in a dropdown list" + }, + "developOptionCustomDnrRules": { + "message": "Custom DNR rules", + "description": "An option in a dropdown list" + }, + "developOptionDnrRulesOf": { + "message": "DNR rules of …", + "description": "A section header in a dropdown list" + }, + "developOptionDynamicRuleset": { + "message": "Dynamic ruleset", + "description": "An option in a dropdown list" + }, + "developOptionSessionRuleset": { + "message": "Session ruleset", + "description": "An option in a dropdown list" + }, "saveButton": { "message": "Save", "description": "Text for buttons used to save changes" @@ -311,8 +335,8 @@ "message": "Export…", "description": "Text for buttons used to export content" }, - "dnrRulesSummary": { - "message": "Enter your own DNR rules below. Do not add content from untrusted sources.", + "dnrRulesWarning": { + "message": "Do not add content from untrusted sources", "description": "Short description of the DNR rules editor pane" }, "dnrRulesCountInfo": { diff --git a/platform/mv3/extension/css/develop.css b/platform/mv3/extension/css/develop.css index 1ecc3ffd368cc..665206d00dc8f 100644 --- a/platform/mv3/extension/css/develop.css +++ b/platform/mv3/extension/css/develop.css @@ -19,38 +19,35 @@ section[data-pane="develop"] > div > * { margin-top: 1em; } -#cm-dnrRules { +#cm-container { flex-grow: 1; font-size: var(--monospace-size); overflow: hidden; } /* https://discuss.codemirror.net/t/how-to-set-max-height-of-the-editor/2882/2 */ -#cm-dnrRules .cm-editor { +#cm-container .cm-editor { background-color: var(--surface-0); height: 100%; } -#cm-dnrRules .cm-editor .cm-line { - border-bottom: 1px dotted transparent; - border-top: 1px dotted transparent; +#cm-container .cm-editor .cm-line:has(.ͼ5), +#cm-container .cm-editor .cm-line:has(.ͼw) { + background-image: url('line-hor-dashed.png'), url('line-hor-dashed.png'); + background-position: left 3px, left calc(100% - 3px); + background-repeat: repeat-x; } -#cm-dnrRules .cm-editor .cm-line:has(.ͼc) { - border-bottom: 1px dotted var(--border-1); - border-top: 1px dotted var(--border-1); - } - -#cm-dnrRules .cm-editor .cm-line.badline:not(.cm-activeLine) { +#cm-container .cm-editor .cm-line.badline:not(.cm-activeLine) { background-color: color-mix(in srgb, var(--info3-ink) 15%, transparent 85%); } -#cm-dnrRules .cm-editor .cm-line .badmark { +#cm-container .cm-editor .cm-line .badmark { text-decoration: underline var(--cm-negative) wavy; text-decoration-skip-ink: none; } -#cm-dnrRules .cm-editor .cm-panel.cm-search { +#cm-container .cm-editor .cm-panel.cm-search { display: flex; flex-wrap: wrap; font-family: sans-serif; @@ -59,13 +56,13 @@ section[data-pane="develop"] > div > * { padding: 0.5em 1.5em 0.5em 0.5em; } -#cm-dnrRules .cm-editor .cm-panel.cm-search > * { +#cm-container .cm-editor .cm-panel.cm-search > * { margin: 0; } -#cm-dnrRules .cm-editor .cm-panel.cm-search .cm-textfield, -#cm-dnrRules .cm-editor .cm-panel.cm-search .cm-button, -#cm-dnrRules .cm-editor .cm-panel.cm-search label { +#cm-container .cm-editor .cm-panel.cm-search .cm-textfield, +#cm-container .cm-editor .cm-panel.cm-search .cm-button, +#cm-container .cm-editor .cm-panel.cm-search label { background-image: inherit; border: inherit; flex-grow: 0; @@ -73,40 +70,77 @@ section[data-pane="develop"] > div > * { min-height: calc(var(--button-font-size) * 1.8); } -#cm-dnrRules .cm-editor .cm-panel.info-panel { +#cm-container .cm-editor .cm-panel .warning { + color: var(--info3-ink); + } + +#cm-container .cm-editor .cm-panel.io-panel { + background-color: var(--surface-1); + box-sizing: border-box; + display: inline-flex; + gap: 0.25em; + padding: 0.25em; + padding-inline-start: 0; + width: 100%; + } +#cm-container .cm-editor .cm-panel.io-panel button { + min-height: 30px; + } +#cm-container .cm-editor .cm-panel.io-panel button#revert { + margin-inline-end: 1em; + } +#cm-container .cm-editor .cm-panel.io-panel:not([data-io~="apply"]) button#apply { + display: none; + } +#cm-container .cm-editor .cm-panel.io-panel:not([data-io~="revert"]) button#revert { + display: none; + } +#cm-container .cm-editor .cm-panel.io-panel:not([data-io~="import"]) button#import { + display: none; + } +#cm-container .cm-editor .cm-panel.io-panel:not([data-io~="export"]) button#export { + display: none; + } + +#cm-container .cm-editor .cm-panel.info-panel { display: flex; flex-wrap: nowrap; font-size: var(--font-size); padding: var(--default-gap-xxsmall) var(--default-gap-xsmall); } -#cm-dnrRules .cm-editor .cm-panel.info-panel .info { +#cm-container .cm-editor .cm-panel.info-panel .info { flex-grow: 1; overflow: auto; } -#cm-dnrRules .cm-editor .cm-panel.info-panel .close { +#cm-container .cm-editor .cm-panel.info-panel .close { cursor: default; flex-shrink: 0; padding-inline-start: 1em; } -#cm-dnrRules .cm-editor .cm-panel.info-panel .close::after { +#cm-container .cm-editor .cm-panel.info-panel .close::after { content: '\2715'; } -#cm-dnrRules .cm-editor .cm-panel.summary-panel { + +#cm-container .cm-editor .cm-panel.summary-panel { background-color: color-mix(in srgb, var(--info1-ink) 15%, transparent 85%); + gap: 1em; + } +#cm-container .cm-editor .cm-panel.summary-panel .info { + flex-shrink: 0; } -#cm-dnrRules .cm-editor .cm-panel.feedback-panel { +#cm-container .cm-editor .cm-panel.feedback-panel { background-color: color-mix(in srgb, var(--info3-ink) 15%, transparent 85%); white-space: pre; max-height: 10cqh; } -#cm-dnrRules .cm-editor .cm-gutterElement { +#cm-container .cm-editor .cm-gutterElement { cursor: default; user-select: none; } -#cm-dnrRules .cm-editor .cm-tooltip .badmark-tooltip { +#cm-container .cm-editor .cm-tooltip .badmark-tooltip { background-color: color-mix(in srgb, var(--info3-ink) 15%, transparent 85%); padding: var(--default-gap-xxsmall) var(--default-gap-xsmall); } \ No newline at end of file diff --git a/platform/mv3/extension/css/line-hor-dashed.png b/platform/mv3/extension/css/line-hor-dashed.png new file mode 100644 index 0000000000000000000000000000000000000000..cb124a2b5426cd775e45ce56e395c48040ad7317 GIT binary patch literal 80 zcmeAS@N?(olHy`uVBq!ia0vp^3P8-r!3HGvcdbbWQsSO2jv*Y^lXdj<^AjAH=FXj~ dQ4@8VnIWl8fXRAptPxNNgQu&X%Q~loCIEY65^Vqg literal 0 HcmV?d00001 diff --git a/platform/mv3/extension/dashboard.html b/platform/mv3/extension/dashboard.html index 91306b845bebb..ee64de6bfb061 100644 --- a/platform/mv3/extension/dashboard.html +++ b/platform/mv3/extension/dashboard.html @@ -89,12 +89,6 @@

- -
-

-

_

-
-
@@ -108,14 +102,17 @@

- - -   - - +  

-

-
+
@@ -165,16 +162,24 @@

+ + diff --git a/src/js/document-blocked.js b/src/js/document-blocked.js index aabcf2eb361b7..561c224377e35 100644 --- a/src/js/document-blocked.js +++ b/src/js/document-blocked.js @@ -21,60 +21,22 @@ import { dom, qs$ } from './dom.js'; import { i18n, i18n$ } from './i18n.js'; +import { faIconsInit } from './fa-icons.js'; /******************************************************************************/ const messaging = vAPI.messaging; -let details = {}; +const details = {}; { const matches = /details=([^&]+)/.exec(window.location.search); if ( matches !== null ) { - details = JSON.parse(decodeURIComponent(matches[1])); + Object.assign(details, JSON.parse(decodeURIComponent(matches[1]))); } } /******************************************************************************/ -(async ( ) => { - const response = await messaging.send('documentBlocked', { - what: 'listsFromNetFilter', - rawFilter: details.fs, - }); - if ( response instanceof Object === false ) { return; } - - let lists; - for ( const rawFilter in response ) { - if ( Object.hasOwn(response, rawFilter) ) { - lists = response[rawFilter]; - break; - } - } - - if ( Array.isArray(lists) === false || lists.length === 0 ) { - qs$('#whyex').style.setProperty('visibility', 'collapse'); - return; - } - - const parent = qs$('#whyex > ul'); - parent.firstElementChild.remove(); // remove placeholder element - for ( const list of lists ) { - const listElem = dom.clone('#templates .filterList'); - const sourceElem = qs$(listElem, '.filterListSource'); - sourceElem.href += encodeURIComponent(list.assetKey); - sourceElem.append(i18n.patchUnicodeFlags(list.title)); - if ( typeof list.supportURL === 'string' && list.supportURL !== '' ) { - const supportElem = qs$(listElem, '.filterListSupport'); - dom.attr(supportElem, 'href', list.supportURL); - dom.cl.remove(supportElem, 'hidden'); - } - parent.appendChild(listElem); - } - qs$('#whyex').style.removeProperty('visibility'); -})(); - -/******************************************************************************/ - const urlToFragment = raw => { try { const fragment = new DocumentFragment(); @@ -94,7 +56,26 @@ const urlToFragment = raw => { dom.clear('#theURL > p > span:first-of-type'); qs$('#theURL > p > span:first-of-type').append(urlToFragment(details.url)); -dom.text('#why', details.fs); + +/******************************************************************************/ + +const lookupFilterLists = async ( ) => { + const response = await messaging.send('documentBlocked', { + what: 'listsFromNetFilter', + rawFilter: details.fs, + }); + if ( response instanceof Object === false ) { return; } + let lists; + for ( const rawFilter in response ) { + if ( Object.hasOwn(response, rawFilter) ) { + lists = response[rawFilter]; + break; + } + } + return lists; +}; + +/******************************************************************************/ if ( typeof details.to === 'string' && details.to.length !== 0 ) { const fragment = new DocumentFragment(); @@ -221,10 +202,6 @@ if ( window.history.length > 1 ) { /******************************************************************************/ -const getTargetHostname = function() { - return details.hn; -}; - const proceedToURL = function() { window.location.replace(details.url); }; @@ -232,7 +209,7 @@ const proceedToURL = function() { const proceedTemporary = async function() { await messaging.send('documentBlocked', { what: 'temporarilyWhitelistDocument', - hostname: getTargetHostname(), + hostname: details.hn, }); proceedToURL(); }; @@ -241,7 +218,7 @@ const proceedPermanent = async function() { await messaging.send('documentBlocked', { what: 'toggleHostnameSwitch', name: 'no-strict-blocking', - hostname: getTargetHostname(), + hostname: details.hn, deep: true, state: true, persist: true, @@ -263,4 +240,49 @@ dom.on('#proceed', 'click', ( ) => { } }); +lookupFilterLists().then((lists = []) => { + let reason = details.reason; + if ( Boolean(reason) === false ) { + reason = lists.reduce((a, b) => a || b.reason, undefined); + } + if ( reason ) { + const msg = i18n$(`docblockedReason${reason.charAt(0).toUpperCase()}${reason.slice(1)}`); + if ( msg ) { reason = msg }; + } + const why = qs$(reason ? 'template.why-reason' : 'template.why') + .content + .cloneNode(true); + i18n.render(why); + dom.text(qs$(why, '.why'), details.fs); + if ( reason ) { + dom.text(qs$(why, 'summary'), `Reason: ${reason}`); + } + qs$('#why').append(why); + dom.cl.remove(dom.body, 'loading'); + + if ( lists.length === 0 ) { return; } + + const whyExtra = qs$('template.why-extra').content.cloneNode(true); + i18n.render(whyExtra); + + const listTemplate = qs$('template.filterList'); + const parent = qs$(whyExtra, '.why-extra'); + let separator = ''; + for ( const list of lists ) { + const listElem = listTemplate.content.cloneNode(true); + const sourceElem = qs$(listElem, '.filterListSource'); + sourceElem.href += encodeURIComponent(list.assetKey); + sourceElem.append(i18n.patchUnicodeFlags(list.title)); + if ( typeof list.supportURL === 'string' && list.supportURL !== '' ) { + const supportElem = qs$(listElem, '.filterListSupport'); + dom.attr(supportElem, 'href', list.supportURL); + dom.cl.remove(supportElem, 'hidden'); + } + parent.append(separator, listElem); + separator = '\u00A0\u2022\u00A0'; + } + faIconsInit(whyExtra); + qs$('#why .why').after(whyExtra); +}); + /******************************************************************************/ diff --git a/src/js/reverselookup-worker.js b/src/js/reverselookup-worker.js index 1f14dd4efaace..c8d3dc3630aa6 100644 --- a/src/js/reverselookup-worker.js +++ b/src/js/reverselookup-worker.js @@ -73,9 +73,10 @@ const fromNetFilter = function(details) { continue; } lists.push({ - assetKey: assetKey, + assetKey, title: entry.title, - supportURL: entry.supportURL + supportURL: entry.supportURL, + reason: entry.reason, }); break; } diff --git a/src/js/static-filtering-parser.js b/src/js/static-filtering-parser.js index 7ff107786f03b..2087d360d805f 100644 --- a/src/js/static-filtering-parser.js +++ b/src/js/static-filtering-parser.js @@ -174,7 +174,6 @@ export const NODE_TYPE_NET_OPTION_NAME_INLINESCRIPT = iota++; export const NODE_TYPE_NET_OPTION_NAME_IPADDRESS = iota++; export const NODE_TYPE_NET_OPTION_NAME_MATCHCASE = iota++; export const NODE_TYPE_NET_OPTION_NAME_MEDIA = iota++; -export const NODE_TYPE_NET_OPTION_NAME_MESSAGE = iota++; export const NODE_TYPE_NET_OPTION_NAME_METHOD = iota++; export const NODE_TYPE_NET_OPTION_NAME_MP4 = iota++; export const NODE_TYPE_NET_OPTION_NAME_NOOP = iota++; @@ -184,6 +183,7 @@ export const NODE_TYPE_NET_OPTION_NAME_PERMISSIONS = iota++; export const NODE_TYPE_NET_OPTION_NAME_PING = iota++; export const NODE_TYPE_NET_OPTION_NAME_POPUNDER = iota++; export const NODE_TYPE_NET_OPTION_NAME_POPUP = iota++; +export const NODE_TYPE_NET_OPTION_NAME_REASON = iota++; export const NODE_TYPE_NET_OPTION_NAME_REDIRECT = iota++; export const NODE_TYPE_NET_OPTION_NAME_REDIRECTRULE = iota++; export const NODE_TYPE_NET_OPTION_NAME_REMOVEPARAM = iota++; @@ -256,7 +256,6 @@ export const nodeTypeFromOptionName = new Map([ [ 'ipaddress', NODE_TYPE_NET_OPTION_NAME_IPADDRESS ], [ 'match-case', NODE_TYPE_NET_OPTION_NAME_MATCHCASE ], [ 'media', NODE_TYPE_NET_OPTION_NAME_MEDIA ], - [ 'message', NODE_TYPE_NET_OPTION_NAME_MESSAGE ], [ 'method', NODE_TYPE_NET_OPTION_NAME_METHOD ], [ 'mp4', NODE_TYPE_NET_OPTION_NAME_MP4 ], [ '_', NODE_TYPE_NET_OPTION_NAME_NOOP ], @@ -268,6 +267,7 @@ export const nodeTypeFromOptionName = new Map([ /* synonym */ [ 'beacon', NODE_TYPE_NET_OPTION_NAME_PING ], [ 'popunder', NODE_TYPE_NET_OPTION_NAME_POPUNDER ], [ 'popup', NODE_TYPE_NET_OPTION_NAME_POPUP ], + [ 'reason', NODE_TYPE_NET_OPTION_NAME_REASON ], [ 'redirect', NODE_TYPE_NET_OPTION_NAME_REDIRECT ], /* synonym */ [ 'rewrite', NODE_TYPE_NET_OPTION_NAME_REDIRECT ], [ 'redirect-rule', NODE_TYPE_NET_OPTION_NAME_REDIRECTRULE ], @@ -1352,9 +1352,6 @@ export class AstFilterParser { case NODE_TYPE_NET_OPTION_NAME_MATCHCASE: realBad = this.isRegexPattern() === false; break; - case NODE_TYPE_NET_OPTION_NAME_MESSAGE: - realBad = hasValue === false; - break; case NODE_TYPE_NET_OPTION_NAME_PERMISSIONS: realBad = modifierType !== 0 || (hasValue || isException) === false || @@ -1378,6 +1375,9 @@ export class AstFilterParser { abstractTypeCount += 1; unredirectableTypeCount += 1; break; + case NODE_TYPE_NET_OPTION_NAME_REASON: + realBad = hasValue === false; + break; case NODE_TYPE_NET_OPTION_NAME_REDIRECT: case NODE_TYPE_NET_OPTION_NAME_REDIRECTRULE: case NODE_TYPE_NET_OPTION_NAME_REPLACE: @@ -3154,7 +3154,6 @@ export const netOptionTokenDescriptors = new Map([ [ 'ipaddress', { mustAssign: true } ], [ 'match-case', { } ], [ 'media', { canNegate: true } ], - [ 'message', { mustAssign: true } ], [ 'method', { mustAssign: true } ], [ 'mp4', { blockOnly: true } ], [ '_', { } ], @@ -3166,6 +3165,7 @@ export const netOptionTokenDescriptors = new Map([ /* synonym */ [ 'beacon', { canNegate: true } ], [ 'popunder', { } ], [ 'popup', { canNegate: true } ], + [ 'reason', { mustAssign: true } ], [ 'redirect', { mustAssign: true } ], /* synonym */ [ 'rewrite', { mustAssign: true } ], [ 'redirect-rule', { mustAssign: true } ], diff --git a/src/js/static-net-filtering.js b/src/js/static-net-filtering.js index 3c0d3f73f8c97..4596f78864e63 100644 --- a/src/js/static-net-filtering.js +++ b/src/js/static-net-filtering.js @@ -411,6 +411,9 @@ class LogData { } this.raw = raw; this.regex = logData.regex.join(''); + if ( logData.reason ) { + this.reason = logData.reason; + } } isUntokenized() { return this.tokenHash === NO_TOKEN_HASH; @@ -3113,7 +3116,7 @@ class FilterMessage { static compile(details) { return [ FilterMessage.fid, - encodeURIComponent(details.optionValues.get('message')), + encodeURIComponent(details.optionValues.get('reason')), ]; } @@ -3127,11 +3130,11 @@ class FilterMessage { } static logData(idata, details) { - const msg = bidiTrie.extractString( - filterData[idata+1], - filterData[idata+2] + const reason = decodeURIComponent( + bidiTrie.extractString(filterData[idata+1], filterData[idata+2]) ); - details.options.push(`message=${decodeURIComponent(msg)}`); + details.reason = reason; + details.options.push(`reason=${reason}`); } } @@ -3612,10 +3615,6 @@ class FilterCompiler { this.optionValues.set('ipaddress', parser.getNetOptionValue(id) || ''); this.optionUnitBits |= IPADDRESS_BIT; break; - case sfp.NODE_TYPE_NET_OPTION_NAME_MESSAGE: - this.optionValues.set('message', parser.getNetOptionValue(id)); - this.optionUnitBits |= MESSAGE_BIT; - break; case sfp.NODE_TYPE_NET_OPTION_NAME_METHOD: this.processMethodOption(parser.getNetOptionValue(id)); this.optionUnitBits |= METHOD_BIT; @@ -3631,6 +3630,10 @@ class FilterCompiler { } this.optionUnitBits |= MODIFY_BIT; break; + case sfp.NODE_TYPE_NET_OPTION_NAME_REASON: + this.optionValues.set('reason', parser.getNetOptionValue(id)); + this.optionUnitBits |= MESSAGE_BIT; + break; case sfp.NODE_TYPE_NET_OPTION_NAME_REDIRECT: { const actualId = this.action === ALLOW_REALM ? sfp.NODE_TYPE_NET_OPTION_NAME_REDIRECTRULE @@ -3737,9 +3740,9 @@ class FilterCompiler { case sfp.NODE_TYPE_NET_OPTION_NAME_FROM: case sfp.NODE_TYPE_NET_OPTION_NAME_HEADER: case sfp.NODE_TYPE_NET_OPTION_NAME_IPADDRESS: - case sfp.NODE_TYPE_NET_OPTION_NAME_MESSAGE: case sfp.NODE_TYPE_NET_OPTION_NAME_METHOD: case sfp.NODE_TYPE_NET_OPTION_NAME_PERMISSIONS: + case sfp.NODE_TYPE_NET_OPTION_NAME_REASON: case sfp.NODE_TYPE_NET_OPTION_NAME_REDIRECT: case sfp.NODE_TYPE_NET_OPTION_NAME_REDIRECTRULE: case sfp.NODE_TYPE_NET_OPTION_NAME_REMOVEPARAM: diff --git a/src/js/traffic.js b/src/js/traffic.js index 827c94d51ef44..478487e77d04e 100644 --- a/src/js/traffic.js +++ b/src/js/traffic.js @@ -224,17 +224,20 @@ const onBeforeRootFrameRequest = function(fctxt) { // Find out the URL navigated to should the document not be strict-blocked pageStore.skipMainDocument(fctxt, false); - const query = encodeURIComponent(JSON.stringify({ + const query = { url: requestURL, dn: fctxt.getDomain() || requestHostname, fs: logData.raw, hn: requestHostname, to: fctxt.redirectURL || '', - })); + }; + if ( logData.reason ) { + query.reason = logData.reason; + } vAPI.tabs.replace( fctxt.tabId, - vAPI.getURL('document-blocked.html?details=') + query + `${vAPI.getURL('document-blocked.html?details=')}${encodeURIComponent(JSON.stringify(query))}` ); return { cancel: true }; From 0b6eea542fc3d3bec054ef4eb2e50f2aa5b6f45d Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sun, 22 Jun 2025 11:38:47 -0400 Subject: [PATCH 1045/1099] Update chengelog --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 303bb2df9c7b7..edc5a1931f5e1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,4 @@ +- [Fix broken reverse lookup of filter lists](https://github.com/gorhill/uBlock/commit/527b4a201f) - [Add `[trusted-]edit-inbound-object` scriptlets](https://github.com/gorhill/uBlock/commit/6e466cf945) - [Improve `remove-cookie` scriptlet](https://github.com/gorhill/uBlock/commit/0a8ea58bb7) - [Add `json-edit`-related scriptlets](https://github.com/gorhill/uBlock/commit/87e0434c90) @@ -5,6 +6,7 @@ - [Force cache bypass reload when no-scripting switch is toggled](https://github.com/gorhill/uBlock/commit/4affe343dd) - [Improve `jsonl[...]` suite of scriptlets](https://github.com/gorhill/uBlock/commit/ed9999efd6) - [Add support for network filter option `message`](https://github.com/gorhill/uBlock/commit/d8298bb067) + - [Complete support for reporing strict-block messages](https://github.com/gorhill/uBlock/commit/253ef7ade3) - [Make `header=` syntax compatible with DNR rules](https://github.com/gorhill/uBlock/commit/408b538e75) - [Counter CodeMirror's `pointer-events: none` on scrollbars](https://github.com/gorhill/uBlock/commit/c44f043ed3) - [Fix element picker issue with explicit dark theme](https://github.com/gorhill/uBlock/commit/0130fdf4a1) From 219c07609fa6360164a58471eb934e723361a7b9 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sun, 22 Jun 2025 11:39:06 -0400 Subject: [PATCH 1046/1099] New revision for dev build --- dist/version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dist/version b/dist/version index 188f475942b0b..a36ec4e96a299 100644 --- a/dist/version +++ b/dist/version @@ -1 +1 @@ -1.64.1.7 \ No newline at end of file +1.64.1.8 \ No newline at end of file From e33bfc1f0176e56d479f0b6866b2e5bd38d1871b Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sun, 22 Jun 2025 12:07:06 -0400 Subject: [PATCH 1047/1099] [mv3] Add support to exclude lists from specific platforms Related discussion: https://github.com/uBlockOrigin/uBOL-home/issues/358#issuecomment-2993687480 --- platform/mv3/make-rulesets.js | 1 + platform/mv3/rulesets.json | 3 +++ 2 files changed, 4 insertions(+) diff --git a/platform/mv3/make-rulesets.js b/platform/mv3/make-rulesets.js index 89e240448328f..2a1e3d94a268f 100644 --- a/platform/mv3/make-rulesets.js +++ b/platform/mv3/make-rulesets.js @@ -1413,6 +1413,7 @@ async function main() { ); for ( const ruleset of rulesets ) { + if ( ruleset.excludedPlatforms?.includes(platform) ) { continue; } await rulesetFromURLs(ruleset); } diff --git a/platform/mv3/rulesets.json b/platform/mv3/rulesets.json index 8f8a6f8f208eb..c2dff26d1c6da 100644 --- a/platform/mv3/rulesets.json +++ b/platform/mv3/rulesets.json @@ -60,6 +60,7 @@ "name": "Malicious URL Blocklist", "group": "malware", "enabled": true, + "excludedPlatforms": [ "safari" ], "urls": [ "https://malware-filter.gitlab.io/malware-filter/urlhaus-filter-hosts.txt" ], @@ -90,6 +91,7 @@ "id": "dpollock-0", "name": "Dan Pollock’s hosts file", "enabled": false, + "excludedPlatforms": [ "safari" ], "urls": [ "https://someonewhocares.org/hosts/hosts" ], @@ -161,6 +163,7 @@ "id": "stevenblack-hosts", "name": "Steven Black’s Unified Hosts (adware + malware)", "enabled": false, + "excludedPlatforms": [ "safari" ], "urls": [ "https://raw.githubusercontent.com/StevenBlack/hosts/master/hosts" ], From 56ecd04816482fe655ee9b3caadc490860d4a5be Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sun, 22 Jun 2025 13:56:16 -0400 Subject: [PATCH 1048/1099] Make Firefox dev build auto-update --- dist/firefox/updates.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dist/firefox/updates.json b/dist/firefox/updates.json index 27cee35d09cd5..20fab8d2ac7d7 100644 --- a/dist/firefox/updates.json +++ b/dist/firefox/updates.json @@ -3,9 +3,9 @@ "uBlock0@raymondhill.net": { "updates": [ { - "version": "1.64.1.7", + "version": "1.64.1.8", "browser_specific_settings": { "gecko": { "strict_min_version": "92.0" } }, - "update_link": "https://github.com/gorhill/uBlock/releases/download/1.64.1b7/uBlock0_1.64.1b7.firefox.signed.xpi" + "update_link": "https://github.com/gorhill/uBlock/releases/download/1.64.1b8/uBlock0_1.64.1b8.firefox.signed.xpi" } ] } From 8bcf533fc07f1bc4b09ad950dbfc2db8fd9552ba Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sun, 22 Jun 2025 17:06:35 -0400 Subject: [PATCH 1049/1099] [mv3] Code review of uBO filter converter Related commit: https://github.com/gorhill/uBlock/commit/e8fb0e1cc9c43c2b55c40c1f4cc4c82cf561643c --- platform/mv3/extension/js/ubo-parser.js | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/platform/mv3/extension/js/ubo-parser.js b/platform/mv3/extension/js/ubo-parser.js index dbfb91994c236..a62d2626ea32a 100644 --- a/platform/mv3/extension/js/ubo-parser.js +++ b/platform/mv3/extension/js/ubo-parser.js @@ -93,7 +93,7 @@ function parseHostnameList(iter) { /******************************************************************************/ -function mergeIncludeExclude(rules) { +export function mergeIncludeExclude(rules) { const includeExcludes = [ { includeName: 'requestDomains', excludeName: 'excludedRequestDomains' }, { includeName: 'initiatorDomains', excludeName: 'excludedInitiatorDomains' }, @@ -104,7 +104,7 @@ function mergeIncludeExclude(rules) { const out = []; const distinctRules = new Map(); for ( const rule of rules ) { - const { condition } = rule; + const { id, condition } = rule; if ( Boolean(condition[includeName]?.length) === false ) { if ( Boolean(condition[excludeName]?.length) === false ) { out.push(rule); @@ -115,9 +115,10 @@ function mergeIncludeExclude(rules) { condition[includeName] = undefined; const excluded = condition[excludeName] || []; condition[excludeName] = undefined; + rule.id = undefined; const hash = JSON.stringify(rule); const details = distinctRules.get(hash) || - { included: new Set(), excluded: new Set() }; + { id, included: new Set(), excluded: new Set() }; if ( details.included.size === 0 && details.excluded.size === 0 ) { distinctRules.set(hash, details); } @@ -129,13 +130,16 @@ function mergeIncludeExclude(rules) { details.excluded.add(hn); } } - for ( const [ hash, details ] of distinctRules ) { + for ( const [ hash, { id, included, excluded } ] of distinctRules ) { const rule = JSON.parse(hash); - if ( details.included.size !== 0 ) { - rule.condition[includeName] = Array.from(details.included); + if ( id ) { + rule.id = id; } - if ( details.excluded.size !== 0 ) { - rule.condition[excludeName] = Array.from(details.excluded); + if ( included.size !== 0 ) { + rule.condition[includeName] = Array.from(included); + } + if ( excluded.size !== 0 ) { + rule.condition[excludeName] = Array.from(excluded); } out.push(rule); } @@ -161,7 +165,7 @@ function parseNetworkFilter(parser) { let pattern = parser.getNetPattern(); if ( parser.isHostnamePattern() ) { rule.condition.requestDomains = [ pattern ]; - } else if ( parser.isGenericPattern() ) { + } else if ( parser.isPlainPattern() || parser.isGenericPattern() ) { if ( parser.isLeftHnAnchored() ) { pattern = `||${pattern}`; } else if ( parser.isLeftAnchored() ) { @@ -203,7 +207,7 @@ function parseNetworkFilter(parser) { for ( const type of parser.getNodeTypes() ) { switch ( type ) { case sfp.NODE_TYPE_NET_OPTION_NAME_1P: - rule.domainType = parser.isNegatedOption(type) + rule.condition.domainType = parser.isNegatedOption(type) ? 'thirdParty' : 'firstParty'; break; From d9aba4acca69e90891b9ac4b8e0ab962faeb4f0f Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Tue, 24 Jun 2025 10:41:39 -0400 Subject: [PATCH 1050/1099] [mv3] Use custom tokens for YAML document boundaries --- platform/mv3/extension/css/develop.css | 3 +-- platform/mv3/extension/js/develop.js | 9 ++++++--- platform/mv3/extension/lib/codemirror/codemirror-ubol | 2 +- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/platform/mv3/extension/css/develop.css b/platform/mv3/extension/css/develop.css index 665206d00dc8f..7f2eb00e8e569 100644 --- a/platform/mv3/extension/css/develop.css +++ b/platform/mv3/extension/css/develop.css @@ -31,8 +31,7 @@ section[data-pane="develop"] > div > * { height: 100%; } -#cm-container .cm-editor .cm-line:has(.ͼ5), -#cm-container .cm-editor .cm-line:has(.ͼw) { +#cm-container .cm-editor .cm-line:has(.yamlboundary) { background-image: url('line-hor-dashed.png'), url('line-hor-dashed.png'); background-position: left 3px, left calc(100% - 3px); background-repeat: repeat-x; diff --git a/platform/mv3/extension/js/develop.js b/platform/mv3/extension/js/develop.js index f04316504ab95..e36240a27bc92 100644 --- a/platform/mv3/extension/js/develop.js +++ b/platform/mv3/extension/js/develop.js @@ -557,9 +557,9 @@ class Editor { }, token: (stream, state) => { if ( stream.sol() ) { - if ( stream.match(/^---\s*$/) ) { return 'meta'; } - if ( stream.match(/^# ---\s*$/) ) { return 'meta comment'; } - if ( stream.match(/\.\.\.\s*$/) ) { return 'meta'; } + if ( stream.match(/^---\s*$/) ) { return 'yamlboundary'; } + if ( stream.match(/^# ---\s*$/) ) { return 'yamlboundary comment'; } + if ( stream.match(/\.\.\.\s*$/) ) { return 'yamlboundary'; } } const c = stream.peek(); if ( c === '#' ) { @@ -597,6 +597,9 @@ class Editor { languageData: { commentTokens: { line: '#' }, }, + tokenTable: [ + 'yamlboundary', + ], }; } diff --git a/platform/mv3/extension/lib/codemirror/codemirror-ubol b/platform/mv3/extension/lib/codemirror/codemirror-ubol index 4352f572758bc..4bacc0ca79f3e 160000 --- a/platform/mv3/extension/lib/codemirror/codemirror-ubol +++ b/platform/mv3/extension/lib/codemirror/codemirror-ubol @@ -1 +1 @@ -Subproject commit 4352f572758bc43ebabddb67b98875e8fde7c297 +Subproject commit 4bacc0ca79f3eb4c6e395bf54d3f5efe4da4e07c From 3a473f8c48b5fb2af5791cd841744088b43a0586 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Tue, 24 Jun 2025 10:56:43 -0400 Subject: [PATCH 1051/1099] Import translation work from https://crowdin.com/project/ublock --- .../mv3/extension/_locales/hi/messages.json | 2 +- .../mv3/extension/_locales/ko/messages.json | 14 ++--- .../mv3/extension/_locales/vi/messages.json | 16 +++--- .../extension/_locales/zh_TW/messages.json | 14 ++--- src/_locales/ar/messages.json | 52 +++++++++---------- src/_locales/bg/messages.json | 8 +-- src/_locales/br_FR/messages.json | 8 +-- src/_locales/ca/messages.json | 8 +-- src/_locales/cs/messages.json | 8 +-- src/_locales/da/messages.json | 6 +-- src/_locales/de/messages.json | 6 +-- src/_locales/el/messages.json | 8 +-- src/_locales/es/messages.json | 8 +-- src/_locales/fr/messages.json | 8 +-- src/_locales/fy/messages.json | 8 +-- src/_locales/gl/messages.json | 8 +-- src/_locales/he/messages.json | 8 +-- src/_locales/hr/messages.json | 12 ++--- src/_locales/hu/messages.json | 8 +-- src/_locales/it/messages.json | 8 +-- src/_locales/ka/messages.json | 8 +-- src/_locales/ko/messages.json | 8 +-- src/_locales/lv/messages.json | 8 +-- src/_locales/nl/messages.json | 8 +-- src/_locales/pl/messages.json | 8 +-- src/_locales/pt_BR/messages.json | 8 +-- src/_locales/ru/messages.json | 8 +-- src/_locales/sk/messages.json | 8 +-- src/_locales/sv/messages.json | 8 +-- src/_locales/tr/messages.json | 8 +-- src/_locales/uk/messages.json | 8 +-- src/_locales/zh_TW/messages.json | 8 +-- 32 files changed, 157 insertions(+), 157 deletions(-) diff --git a/platform/mv3/extension/_locales/hi/messages.json b/platform/mv3/extension/_locales/hi/messages.json index 801fec1498beb..56ec38ea0925a 100644 --- a/platform/mv3/extension/_locales/hi/messages.json +++ b/platform/mv3/extension/_locales/hi/messages.json @@ -20,7 +20,7 @@ "description": "appears as tab name in dashboard" }, "developPageName": { - "message": "Develop", + "message": "विकास", "description": "appears as tab name in dashboard. Inspired from 'Develop' menu in Safari, see https://developer.apple.com/documentation/safari-developer-tools/develop-menu" }, "aboutPageName": { diff --git a/platform/mv3/extension/_locales/ko/messages.json b/platform/mv3/extension/_locales/ko/messages.json index e7873c15af6f8..21e1a827ae140 100644 --- a/platform/mv3/extension/_locales/ko/messages.json +++ b/platform/mv3/extension/_locales/ko/messages.json @@ -296,27 +296,27 @@ "description": "Tooltip for the button used to exit zapper mode" }, "developDropdownLabel": { - "message": "View:", + "message": "보기:", "description": "A label of a dropdown list" }, "developOptionFilteringModeDetails": { - "message": "Filtering mode details", + "message": "필터링 모드 상세정보", "description": "An option in a dropdown list" }, "developOptionCustomDnrRules": { - "message": "Custom DNR rules", + "message": "사용자 지정 DNR 규칙", "description": "An option in a dropdown list" }, "developOptionDnrRulesOf": { - "message": "DNR rules of …", + "message": "DNR 규칙…", "description": "A section header in a dropdown list" }, "developOptionDynamicRuleset": { - "message": "Dynamic ruleset", + "message": "동적 규칙 목록", "description": "An option in a dropdown list" }, "developOptionSessionRuleset": { - "message": "Session ruleset", + "message": "세션 규칙 목록", "description": "An option in a dropdown list" }, "saveButton": { @@ -336,7 +336,7 @@ "description": "Text for buttons used to export content" }, "dnrRulesWarning": { - "message": "Do not add content from untrusted sources", + "message": "신뢰할 수 없는 출처의 콘텐츠를 추가하지 마십시오", "description": "Short description of the DNR rules editor pane" }, "dnrRulesCountInfo": { diff --git a/platform/mv3/extension/_locales/vi/messages.json b/platform/mv3/extension/_locales/vi/messages.json index 0205325dfaffb..21d8999ee7bad 100644 --- a/platform/mv3/extension/_locales/vi/messages.json +++ b/platform/mv3/extension/_locales/vi/messages.json @@ -296,27 +296,27 @@ "description": "Tooltip for the button used to exit zapper mode" }, "developDropdownLabel": { - "message": "View:", + "message": "Xem:", "description": "A label of a dropdown list" }, "developOptionFilteringModeDetails": { - "message": "Filtering mode details", + "message": "Chi tiết chế độ chặn", "description": "An option in a dropdown list" }, "developOptionCustomDnrRules": { - "message": "Custom DNR rules", + "message": "Quy tắc DRN tùy chỉnh", "description": "An option in a dropdown list" }, "developOptionDnrRulesOf": { - "message": "DNR rules of …", + "message": "Quy tắc DRN cho ...", "description": "A section header in a dropdown list" }, "developOptionDynamicRuleset": { - "message": "Dynamic ruleset", + "message": "Danh sách quy tắc động", "description": "An option in a dropdown list" }, "developOptionSessionRuleset": { - "message": "Session ruleset", + "message": "Danh sách quy tắc phiên", "description": "An option in a dropdown list" }, "saveButton": { @@ -336,11 +336,11 @@ "description": "Text for buttons used to export content" }, "dnrRulesWarning": { - "message": "Do not add content from untrusted sources", + "message": "Không thêm các bộ lọc từ các nguồn không đáng tin cậy", "description": "Short description of the DNR rules editor pane" }, "dnrRulesCountInfo": { - "message": "Number of registered rules: {count}", + "message": "Số quy tắc đã đăng ký: {count}", "description": "Short sentence to report the number of currently registered DNR rules" } } diff --git a/platform/mv3/extension/_locales/zh_TW/messages.json b/platform/mv3/extension/_locales/zh_TW/messages.json index c6cfea0a517e5..71eedaeae3f84 100644 --- a/platform/mv3/extension/_locales/zh_TW/messages.json +++ b/platform/mv3/extension/_locales/zh_TW/messages.json @@ -296,27 +296,27 @@ "description": "Tooltip for the button used to exit zapper mode" }, "developDropdownLabel": { - "message": "View:", + "message": "檢視:", "description": "A label of a dropdown list" }, "developOptionFilteringModeDetails": { - "message": "Filtering mode details", + "message": "過濾模式詳細資訊", "description": "An option in a dropdown list" }, "developOptionCustomDnrRules": { - "message": "Custom DNR rules", + "message": "自訂 DNR 規則", "description": "An option in a dropdown list" }, "developOptionDnrRulesOf": { - "message": "DNR rules of …", + "message": "DNR 規則……", "description": "A section header in a dropdown list" }, "developOptionDynamicRuleset": { - "message": "Dynamic ruleset", + "message": "動態規則集", "description": "An option in a dropdown list" }, "developOptionSessionRuleset": { - "message": "Session ruleset", + "message": "工作階段規則集", "description": "An option in a dropdown list" }, "saveButton": { @@ -336,7 +336,7 @@ "description": "Text for buttons used to export content" }, "dnrRulesWarning": { - "message": "Do not add content from untrusted sources", + "message": "切勿新增來自不受信任來源的內容", "description": "Short description of the DNR rules editor pane" }, "dnrRulesCountInfo": { diff --git a/src/_locales/ar/messages.json b/src/_locales/ar/messages.json index f55a4e347afa9..eaf1cc271a263 100644 --- a/src/_locales/ar/messages.json +++ b/src/_locales/ar/messages.json @@ -100,7 +100,7 @@ "description": "English: or" }, "popupBlockedOnThisPage_v2": { - "message": "محجوب من هذه الصفحة", + "message": "محجوب على هذه الصفحة", "description": "For the new mobile-friendly popup design" }, "popupBlockedSinceInstall_v2": { @@ -444,7 +444,7 @@ "description": "English: Parse and enforce Adblock+ element hiding filters." }, "3pParseAllABPHideFiltersInfo": { - "message": "الفلاتر التجميلية تعمل لإزالة العناصر في صفحة الويب التي تعتبر إزعاج بصريًا و تلك التي لا يمكن حجبها عن طريق محركات التصفية القائمة على طلبات الشبكة.", + "message": "التشريحات التجميلية تعمل لإزالة العناصر في صفحة الويب التي تعتبر إزعاج بصريًا و تلك التي لا يمكن حجبها عن طريق محركات التشريحات القائمة على طلبات الشبكة.", "description": "Describes the purpose of the 'Parse and enforce cosmetic filters' feature." }, "3pIgnoreGenericCosmeticFilters": { @@ -452,11 +452,11 @@ "description": "This will cause uBO to ignore all generic cosmetic filters." }, "3pIgnoreGenericCosmeticFiltersInfo": { - "message": "الفلاتر التجميلية العمومية هي الفلاتر التجميلية التي تنطبق على كل مواقع الإنترنت. تفعيل هذا الخيار سوف يترك مساحة أقل من الذاكرة و قوة المعالجة للإستعمال من قبل صفحات الويب كنتيجة لمعالجة الفلاتر التجميلية العمومية.\n\nمن الأفضل تفعيل هذا الخيار على الأجهزة الأقل قوة.", + "message": "التشريحات التجميلية العمومية هي الفلاتر التجميلية التي تنطبق على كل مواقع الإنترنت. تفعيل هذا الخيار سوف يترك مساحة أقل من الذاكرة و قوة المعالجة للإستعمال من قبل صفحات الويب كنتيجة لمعالجة التشريحات التجميلية العمومية.\n\nمن الأفضل تفعيل هذا الخيار على الأجهزة الأقل قوة.", "description": "Describes the purpose of the 'Ignore generic cosmetic filters' feature." }, "3pSuspendUntilListsAreLoaded": { - "message": "تعليق نشاط الشبكة حتى يتم تحميل كافة قوائم عوامل التصفية", + "message": "تعليق نشاط الشبكة حتى يتم تحميل كافة قوائم عوامل التشريحات", "description": "A checkbox in the 'Filter lists' pane" }, "3pListsOfBlockedHostsHeader": { @@ -540,15 +540,15 @@ "description": "Warning against copy-pasting filters from random sources" }, "1pEnableMyFiltersLabel": { - "message": "تفعيل تصفياتي المخصصة", + "message": "مكّن تشريحاتي المخصصة", "description": "Label for the checkbox use to enable/disable 'My filters' list" }, "1pTrustMyFiltersLabel": { - "message": "السماح بالفلاتر المخصصة التي تتطلب الثقة", + "message": "السماح بالتشريحات المخصصة التي تتطلب الثقة", "description": "Label for the checkbox use to trust the content of 'My filters' list" }, "1pImport": { - "message": "استيراد و إضافة", + "message": "استورد وألحق…", "description": "Button in the 'My filters' pane" }, "1pExport": { @@ -592,7 +592,7 @@ "description": "Will discard manually-edited content and exit manual-edit mode" }, "rulesImport": { - "message": "الاستيراد من ملف...", + "message": "استورد من ملف...", "description": "" }, "rulesExport": { @@ -632,11 +632,11 @@ "description": "A concise description of the 'Trusted sites' pane." }, "whitelistImport": { - "message": "استيراد و إضافة", + "message": "استورد وألحق…", "description": "Button in the 'Trusted sites' pane" }, "whitelistExport": { - "message": "تصدير", + "message": "صدّر…", "description": "Button in the 'Trusted sites' pane" }, "whitelistExportFilename": { @@ -676,11 +676,11 @@ "description": "Appears in the logger's tab selector" }, "loggerReloadTip": { - "message": "إعادة تحميل محتوى علامة التبويب", + "message": "أعد تحميل محتوى علامة التبويب", "description": "Tooltip for the reload button in the logger page" }, "loggerDomInspectorTip": { - "message": "تفعيل أو تعطيل مراقب DOM", + "message": "بدّل فاحص DOM", "description": "Tooltip for the DOM inspector button in the logger page" }, "loggerPopupPanelTip": { @@ -692,11 +692,11 @@ "description": "Tooltip for the top-right info label in the logger page" }, "loggerClearTip": { - "message": "امسح السجلات", + "message": "امسح المُسجّل", "description": "Tooltip for the eraser in the logger page; used to blank the content of the logger" }, "loggerPauseTip": { - "message": "ايقاف التسجيل مؤقتا (استبعاد كل البيانات الواردة) ", + "message": "أوقف المُسجّل مؤقتا (استبعاد كل البيانات الواردة) ", "description": "Tooltip for the pause button in the logger page" }, "loggerUnpauseTip": { @@ -704,7 +704,7 @@ "description": "Tooltip for the play button in the logger page" }, "loggerRowFiltererButtonTip": { - "message": "تبديل ترشيح بيانات التسجيل ", + "message": "بدّل ترشيح المُسجّل ", "description": "Tooltip for the row filterer button in the logger page" }, "logFilterPrompt": { @@ -748,11 +748,11 @@ "description": "Small header to identify the 'Details' pane for a specific logger entry" }, "loggerEntryDetailsFilter": { - "message": "فلتر", + "message": "المرشّح", "description": "Label to identify a filter field" }, "loggerEntryDetailsFilterList": { - "message": "قائمة الفلتر", + "message": "قائمة المرشحات", "description": "Label to identify a filter list field" }, "loggerEntryDetailsRule": { @@ -832,15 +832,15 @@ "description": "Used in the static filtering wizard" }, "loggerStaticFilteringFinderSentence1": { - "message": "فلتر ثابت {{filter}} موجود في:", + "message": "تصفية ثابتة {{filter}} موجود في:", "description": "Below this sentence, the filter list(s) in which the filter was found" }, "loggerStaticFilteringFinderSentence2": { - "message": "الفلتر الثابت لا يوجد في أي من قوائم الفلاتر المفعلة", + "message": "الترشيح الثابت لا يوجد في أي من قوائم التشريحات المفعلة", "description": "Message to show when a filter cannot be found in any filter lists" }, "loggerSettingDiscardPrompt": { - "message": "مدخلات السجل التي لا تطابق أيا من المعايير ستحذف تلقائيا:", + "message": "مدخلات المُسجّل التي لا تطابق أيا من المعايير ستتجاهل تلقائيا:", "description": "Logger setting: A sentence to describe the purpose of the settings below" }, "loggerSettingPerEntryMaxAge": { @@ -852,7 +852,7 @@ "description": "A logger setting" }, "loggerSettingPerTabMaxEntries": { - "message": "أبق بحد أقصى على {{input}} مدخلات في كل تبويبة", + "message": "أبق بحد أقصى على {{input}} مدخلات في كل لسان", "description": "A logger setting" }, "loggerSettingPerEntryLineCount": { @@ -924,7 +924,7 @@ "description": "First paragraph of 'Questions and support' section in Support pane" }, "supportS3H": { - "message": "تصفية المسائل/موقع الويب معطل", + "message": "تشريح المسائل/موقع الويب معطوب", "description": "Header of 'Filter issues' section in Support pane" }, "supportS3P1": { @@ -1196,19 +1196,19 @@ "description": "Text warning about an incoming redirect" }, "docblockedReasonLabel": { - "message": "Reason:", + "message": "السبب:", "description": "The label which prepend the actual reason why a page was blocked" }, "docblockedReasonMalicious": { - "message": "Malicious", + "message": "ضار", "description": "An actual reason why a page was blocked" }, "docblockedReasonTracker": { - "message": "Tracker", + "message": "متتبع", "description": "An actual reason why a page was blocked" }, "docblockedReasonDisreputable": { - "message": "Disreputable", + "message": "سيئ السمعة", "description": "An actual reason why a page was blocked" }, "cloudPush": { diff --git a/src/_locales/bg/messages.json b/src/_locales/bg/messages.json index 0c9ececa95119..b0d4ddaa22744 100644 --- a/src/_locales/bg/messages.json +++ b/src/_locales/bg/messages.json @@ -1196,19 +1196,19 @@ "description": "Text warning about an incoming redirect" }, "docblockedReasonLabel": { - "message": "Reason:", + "message": "Причина:", "description": "The label which prepend the actual reason why a page was blocked" }, "docblockedReasonMalicious": { - "message": "Malicious", + "message": "Злонамерена", "description": "An actual reason why a page was blocked" }, "docblockedReasonTracker": { - "message": "Tracker", + "message": "Проследяване", "description": "An actual reason why a page was blocked" }, "docblockedReasonDisreputable": { - "message": "Disreputable", + "message": "Неблагонадеждна", "description": "An actual reason why a page was blocked" }, "cloudPush": { diff --git a/src/_locales/br_FR/messages.json b/src/_locales/br_FR/messages.json index 0b5284a2f077f..04e3bc91715df 100644 --- a/src/_locales/br_FR/messages.json +++ b/src/_locales/br_FR/messages.json @@ -1196,19 +1196,19 @@ "description": "Text warning about an incoming redirect" }, "docblockedReasonLabel": { - "message": "Reason:", + "message": "Abeg:", "description": "The label which prepend the actual reason why a page was blocked" }, "docblockedReasonMalicious": { - "message": "Malicious", + "message": "Malisius", "description": "An actual reason why a page was blocked" }, "docblockedReasonTracker": { - "message": "Tracker", + "message": "Heulier", "description": "An actual reason why a page was blocked" }, "docblockedReasonDisreputable": { - "message": "Disreputable", + "message": "Douetus", "description": "An actual reason why a page was blocked" }, "cloudPush": { diff --git a/src/_locales/ca/messages.json b/src/_locales/ca/messages.json index 93642c509e1ac..c1823bd0eb676 100644 --- a/src/_locales/ca/messages.json +++ b/src/_locales/ca/messages.json @@ -1196,19 +1196,19 @@ "description": "Text warning about an incoming redirect" }, "docblockedReasonLabel": { - "message": "Reason:", + "message": "Motiu:", "description": "The label which prepend the actual reason why a page was blocked" }, "docblockedReasonMalicious": { - "message": "Malicious", + "message": "Maliciós", "description": "An actual reason why a page was blocked" }, "docblockedReasonTracker": { - "message": "Tracker", + "message": "Seguidor", "description": "An actual reason why a page was blocked" }, "docblockedReasonDisreputable": { - "message": "Disreputable", + "message": "De mala reputació", "description": "An actual reason why a page was blocked" }, "cloudPush": { diff --git a/src/_locales/cs/messages.json b/src/_locales/cs/messages.json index bf5295b0deaa4..e41e4f0fa3c94 100644 --- a/src/_locales/cs/messages.json +++ b/src/_locales/cs/messages.json @@ -1196,19 +1196,19 @@ "description": "Text warning about an incoming redirect" }, "docblockedReasonLabel": { - "message": "Reason:", + "message": "Důvod:", "description": "The label which prepend the actual reason why a page was blocked" }, "docblockedReasonMalicious": { - "message": "Malicious", + "message": "Zlomyslný", "description": "An actual reason why a page was blocked" }, "docblockedReasonTracker": { - "message": "Tracker", + "message": "Sledovač", "description": "An actual reason why a page was blocked" }, "docblockedReasonDisreputable": { - "message": "Disreputable", + "message": "Nereputabilní", "description": "An actual reason why a page was blocked" }, "cloudPush": { diff --git a/src/_locales/da/messages.json b/src/_locales/da/messages.json index 780a843cc9303..b21025b52078a 100644 --- a/src/_locales/da/messages.json +++ b/src/_locales/da/messages.json @@ -1196,11 +1196,11 @@ "description": "Text warning about an incoming redirect" }, "docblockedReasonLabel": { - "message": "Reason:", + "message": "Årsag:", "description": "The label which prepend the actual reason why a page was blocked" }, "docblockedReasonMalicious": { - "message": "Malicious", + "message": "Ondsindet", "description": "An actual reason why a page was blocked" }, "docblockedReasonTracker": { @@ -1208,7 +1208,7 @@ "description": "An actual reason why a page was blocked" }, "docblockedReasonDisreputable": { - "message": "Disreputable", + "message": "Ikke-velrenommeret", "description": "An actual reason why a page was blocked" }, "cloudPush": { diff --git a/src/_locales/de/messages.json b/src/_locales/de/messages.json index 523889f785f84..9a87dc09dcc52 100644 --- a/src/_locales/de/messages.json +++ b/src/_locales/de/messages.json @@ -1196,11 +1196,11 @@ "description": "Text warning about an incoming redirect" }, "docblockedReasonLabel": { - "message": "Reason:", + "message": "Grund:", "description": "The label which prepend the actual reason why a page was blocked" }, "docblockedReasonMalicious": { - "message": "Malicious", + "message": "Schädlich", "description": "An actual reason why a page was blocked" }, "docblockedReasonTracker": { @@ -1208,7 +1208,7 @@ "description": "An actual reason why a page was blocked" }, "docblockedReasonDisreputable": { - "message": "Disreputable", + "message": "Unseriös", "description": "An actual reason why a page was blocked" }, "cloudPush": { diff --git a/src/_locales/el/messages.json b/src/_locales/el/messages.json index b1fffedc94850..871750f30e2ac 100644 --- a/src/_locales/el/messages.json +++ b/src/_locales/el/messages.json @@ -1196,19 +1196,19 @@ "description": "Text warning about an incoming redirect" }, "docblockedReasonLabel": { - "message": "Reason:", + "message": "Αιτία:", "description": "The label which prepend the actual reason why a page was blocked" }, "docblockedReasonMalicious": { - "message": "Malicious", + "message": "Κακόβουλος ιστότοπος", "description": "An actual reason why a page was blocked" }, "docblockedReasonTracker": { - "message": "Tracker", + "message": "Ιχνηλάτης", "description": "An actual reason why a page was blocked" }, "docblockedReasonDisreputable": { - "message": "Disreputable", + "message": "Κακόφημος ιστότοπος", "description": "An actual reason why a page was blocked" }, "cloudPush": { diff --git a/src/_locales/es/messages.json b/src/_locales/es/messages.json index f2445c7cd3d24..92df1efa939d4 100644 --- a/src/_locales/es/messages.json +++ b/src/_locales/es/messages.json @@ -1196,19 +1196,19 @@ "description": "Text warning about an incoming redirect" }, "docblockedReasonLabel": { - "message": "Reason:", + "message": "Razón:", "description": "The label which prepend the actual reason why a page was blocked" }, "docblockedReasonMalicious": { - "message": "Malicious", + "message": "Malicioso", "description": "An actual reason why a page was blocked" }, "docblockedReasonTracker": { - "message": "Tracker", + "message": "Rastreador", "description": "An actual reason why a page was blocked" }, "docblockedReasonDisreputable": { - "message": "Disreputable", + "message": "De mala reputación", "description": "An actual reason why a page was blocked" }, "cloudPush": { diff --git a/src/_locales/fr/messages.json b/src/_locales/fr/messages.json index 7e6b9a770743d..a67c9f2e4fd38 100644 --- a/src/_locales/fr/messages.json +++ b/src/_locales/fr/messages.json @@ -1196,19 +1196,19 @@ "description": "Text warning about an incoming redirect" }, "docblockedReasonLabel": { - "message": "Reason:", + "message": "Motif du blocage :", "description": "The label which prepend the actual reason why a page was blocked" }, "docblockedReasonMalicious": { - "message": "Malicious", + "message": "Malveillant", "description": "An actual reason why a page was blocked" }, "docblockedReasonTracker": { - "message": "Tracker", + "message": "Pisteur", "description": "An actual reason why a page was blocked" }, "docblockedReasonDisreputable": { - "message": "Disreputable", + "message": "Douteux", "description": "An actual reason why a page was blocked" }, "cloudPush": { diff --git a/src/_locales/fy/messages.json b/src/_locales/fy/messages.json index 68d02e85b8150..56649a48728ba 100644 --- a/src/_locales/fy/messages.json +++ b/src/_locales/fy/messages.json @@ -1196,19 +1196,19 @@ "description": "Text warning about an incoming redirect" }, "docblockedReasonLabel": { - "message": "Reason:", + "message": "Reden:", "description": "The label which prepend the actual reason why a page was blocked" }, "docblockedReasonMalicious": { - "message": "Malicious", + "message": "kweawillich", "description": "An actual reason why a page was blocked" }, "docblockedReasonTracker": { - "message": "Tracker", + "message": "tracker", "description": "An actual reason why a page was blocked" }, "docblockedReasonDisreputable": { - "message": "Disreputable", + "message": "berucht", "description": "An actual reason why a page was blocked" }, "cloudPush": { diff --git a/src/_locales/gl/messages.json b/src/_locales/gl/messages.json index 902ef802e53a3..314aba10856b3 100644 --- a/src/_locales/gl/messages.json +++ b/src/_locales/gl/messages.json @@ -1196,19 +1196,19 @@ "description": "Text warning about an incoming redirect" }, "docblockedReasonLabel": { - "message": "Reason:", + "message": "Razón:", "description": "The label which prepend the actual reason why a page was blocked" }, "docblockedReasonMalicious": { - "message": "Malicious", + "message": "Daniña", "description": "An actual reason why a page was blocked" }, "docblockedReasonTracker": { - "message": "Tracker", + "message": "Rastrexo", "description": "An actual reason why a page was blocked" }, "docblockedReasonDisreputable": { - "message": "Disreputable", + "message": "Mala reputación", "description": "An actual reason why a page was blocked" }, "cloudPush": { diff --git a/src/_locales/he/messages.json b/src/_locales/he/messages.json index 3fa365fa076e3..274d15c83be2c 100644 --- a/src/_locales/he/messages.json +++ b/src/_locales/he/messages.json @@ -1196,19 +1196,19 @@ "description": "Text warning about an incoming redirect" }, "docblockedReasonLabel": { - "message": "Reason:", + "message": "הסיבה:", "description": "The label which prepend the actual reason why a page was blocked" }, "docblockedReasonMalicious": { - "message": "Malicious", + "message": "זדוני", "description": "An actual reason why a page was blocked" }, "docblockedReasonTracker": { - "message": "Tracker", + "message": "מעקב", "description": "An actual reason why a page was blocked" }, "docblockedReasonDisreputable": { - "message": "Disreputable", + "message": "מפוקפק", "description": "An actual reason why a page was blocked" }, "cloudPush": { diff --git a/src/_locales/hr/messages.json b/src/_locales/hr/messages.json index bb04eac5c4be7..4af22495b574b 100644 --- a/src/_locales/hr/messages.json +++ b/src/_locales/hr/messages.json @@ -140,11 +140,11 @@ "description": "Tooltip for the no-popups per-site switch" }, "popupTipNoPopups2": { - "message": "Kliknite za ukloniti blokiranje svih pop-up prozora na ovom sajtu", + "message": "Kliknite za ukloniti blokiranje svih pop-up prozora na ovoj stranici", "description": "Tooltip for the no-popups per-site switch" }, "popupTipNoLargeMedia": { - "message": "Namjestite blokiranje velikih medijskih elemenata za ovo mjesto", + "message": "Namjestite blokiranje velikih medijskih elemenata za ovu stranicu", "description": "Tooltip for the no-large-media per-site switch" }, "popupTipNoLargeMedia1": { @@ -1196,19 +1196,19 @@ "description": "Text warning about an incoming redirect" }, "docblockedReasonLabel": { - "message": "Reason:", + "message": "Razlog:", "description": "The label which prepend the actual reason why a page was blocked" }, "docblockedReasonMalicious": { - "message": "Malicious", + "message": "Zlonamjerno", "description": "An actual reason why a page was blocked" }, "docblockedReasonTracker": { - "message": "Tracker", + "message": "Pratioc", "description": "An actual reason why a page was blocked" }, "docblockedReasonDisreputable": { - "message": "Disreputable", + "message": "Zloglasno", "description": "An actual reason why a page was blocked" }, "cloudPush": { diff --git a/src/_locales/hu/messages.json b/src/_locales/hu/messages.json index ae7a249aa29e7..c464637dc0f8c 100644 --- a/src/_locales/hu/messages.json +++ b/src/_locales/hu/messages.json @@ -1196,19 +1196,19 @@ "description": "Text warning about an incoming redirect" }, "docblockedReasonLabel": { - "message": "Reason:", + "message": "Ok:", "description": "The label which prepend the actual reason why a page was blocked" }, "docblockedReasonMalicious": { - "message": "Malicious", + "message": "Káros", "description": "An actual reason why a page was blocked" }, "docblockedReasonTracker": { - "message": "Tracker", + "message": "Nyomkövető", "description": "An actual reason why a page was blocked" }, "docblockedReasonDisreputable": { - "message": "Disreputable", + "message": "Rossz hírnevű", "description": "An actual reason why a page was blocked" }, "cloudPush": { diff --git a/src/_locales/it/messages.json b/src/_locales/it/messages.json index 7b6f276d9784f..6cce7a94ffc6c 100644 --- a/src/_locales/it/messages.json +++ b/src/_locales/it/messages.json @@ -1196,19 +1196,19 @@ "description": "Text warning about an incoming redirect" }, "docblockedReasonLabel": { - "message": "Reason:", + "message": "Motivo:", "description": "The label which prepend the actual reason why a page was blocked" }, "docblockedReasonMalicious": { - "message": "Malicious", + "message": "Maligno", "description": "An actual reason why a page was blocked" }, "docblockedReasonTracker": { - "message": "Tracker", + "message": "Localizzatore", "description": "An actual reason why a page was blocked" }, "docblockedReasonDisreputable": { - "message": "Disreputable", + "message": "Disdicevole", "description": "An actual reason why a page was blocked" }, "cloudPush": { diff --git a/src/_locales/ka/messages.json b/src/_locales/ka/messages.json index 4dd70ab320520..10c0c72200055 100644 --- a/src/_locales/ka/messages.json +++ b/src/_locales/ka/messages.json @@ -1196,19 +1196,19 @@ "description": "Text warning about an incoming redirect" }, "docblockedReasonLabel": { - "message": "Reason:", + "message": "მიზეზი:", "description": "The label which prepend the actual reason why a page was blocked" }, "docblockedReasonMalicious": { - "message": "Malicious", + "message": "მავნებელი", "description": "An actual reason why a page was blocked" }, "docblockedReasonTracker": { - "message": "Tracker", + "message": "მეთვალყურე", "description": "An actual reason why a page was blocked" }, "docblockedReasonDisreputable": { - "message": "Disreputable", + "message": "სახელგატეხილი", "description": "An actual reason why a page was blocked" }, "cloudPush": { diff --git a/src/_locales/ko/messages.json b/src/_locales/ko/messages.json index 838fe0f29117d..53c728da7c375 100644 --- a/src/_locales/ko/messages.json +++ b/src/_locales/ko/messages.json @@ -1196,19 +1196,19 @@ "description": "Text warning about an incoming redirect" }, "docblockedReasonLabel": { - "message": "Reason:", + "message": "사유:", "description": "The label which prepend the actual reason why a page was blocked" }, "docblockedReasonMalicious": { - "message": "Malicious", + "message": "악성", "description": "An actual reason why a page was blocked" }, "docblockedReasonTracker": { - "message": "Tracker", + "message": "추적기", "description": "An actual reason why a page was blocked" }, "docblockedReasonDisreputable": { - "message": "Disreputable", + "message": "낮은 평판", "description": "An actual reason why a page was blocked" }, "cloudPush": { diff --git a/src/_locales/lv/messages.json b/src/_locales/lv/messages.json index 280a75d6b4cee..ae78a4e8ffda3 100644 --- a/src/_locales/lv/messages.json +++ b/src/_locales/lv/messages.json @@ -1196,19 +1196,19 @@ "description": "Text warning about an incoming redirect" }, "docblockedReasonLabel": { - "message": "Reason:", + "message": "Iemesls:", "description": "The label which prepend the actual reason why a page was blocked" }, "docblockedReasonMalicious": { - "message": "Malicious", + "message": "Ļaunprātīgas darbības", "description": "An actual reason why a page was blocked" }, "docblockedReasonTracker": { - "message": "Tracker", + "message": "Izsekotājs", "description": "An actual reason why a page was blocked" }, "docblockedReasonDisreputable": { - "message": "Disreputable", + "message": "Slikta slava", "description": "An actual reason why a page was blocked" }, "cloudPush": { diff --git a/src/_locales/nl/messages.json b/src/_locales/nl/messages.json index b91cca63f4c20..1f18a0da1e53f 100644 --- a/src/_locales/nl/messages.json +++ b/src/_locales/nl/messages.json @@ -1196,19 +1196,19 @@ "description": "Text warning about an incoming redirect" }, "docblockedReasonLabel": { - "message": "Reason:", + "message": "Reden:", "description": "The label which prepend the actual reason why a page was blocked" }, "docblockedReasonMalicious": { - "message": "Malicious", + "message": "kwaadwillend", "description": "An actual reason why a page was blocked" }, "docblockedReasonTracker": { - "message": "Tracker", + "message": "tracker", "description": "An actual reason why a page was blocked" }, "docblockedReasonDisreputable": { - "message": "Disreputable", + "message": "berucht", "description": "An actual reason why a page was blocked" }, "cloudPush": { diff --git a/src/_locales/pl/messages.json b/src/_locales/pl/messages.json index 483c4577f2072..fc0d77b7ff879 100644 --- a/src/_locales/pl/messages.json +++ b/src/_locales/pl/messages.json @@ -1196,19 +1196,19 @@ "description": "Text warning about an incoming redirect" }, "docblockedReasonLabel": { - "message": "Reason:", + "message": "Powód:", "description": "The label which prepend the actual reason why a page was blocked" }, "docblockedReasonMalicious": { - "message": "Malicious", + "message": "złośliwa", "description": "An actual reason why a page was blocked" }, "docblockedReasonTracker": { - "message": "Tracker", + "message": "śledząca", "description": "An actual reason why a page was blocked" }, "docblockedReasonDisreputable": { - "message": "Disreputable", + "message": "podejrzana", "description": "An actual reason why a page was blocked" }, "cloudPush": { diff --git a/src/_locales/pt_BR/messages.json b/src/_locales/pt_BR/messages.json index 8d837e3d694c0..22e4ff6d8f935 100644 --- a/src/_locales/pt_BR/messages.json +++ b/src/_locales/pt_BR/messages.json @@ -1196,19 +1196,19 @@ "description": "Text warning about an incoming redirect" }, "docblockedReasonLabel": { - "message": "Reason:", + "message": "Motivo:", "description": "The label which prepend the actual reason why a page was blocked" }, "docblockedReasonMalicious": { - "message": "Malicious", + "message": "Malicioso", "description": "An actual reason why a page was blocked" }, "docblockedReasonTracker": { - "message": "Tracker", + "message": "Rastreador", "description": "An actual reason why a page was blocked" }, "docblockedReasonDisreputable": { - "message": "Disreputable", + "message": "Má reputação", "description": "An actual reason why a page was blocked" }, "cloudPush": { diff --git a/src/_locales/ru/messages.json b/src/_locales/ru/messages.json index c3dabd2bec583..16561e3393a46 100644 --- a/src/_locales/ru/messages.json +++ b/src/_locales/ru/messages.json @@ -1196,19 +1196,19 @@ "description": "Text warning about an incoming redirect" }, "docblockedReasonLabel": { - "message": "Reason:", + "message": "Причина:", "description": "The label which prepend the actual reason why a page was blocked" }, "docblockedReasonMalicious": { - "message": "Malicious", + "message": "Вредоносная активность", "description": "An actual reason why a page was blocked" }, "docblockedReasonTracker": { - "message": "Tracker", + "message": "Трекер", "description": "An actual reason why a page was blocked" }, "docblockedReasonDisreputable": { - "message": "Disreputable", + "message": "Сомнительное содержание", "description": "An actual reason why a page was blocked" }, "cloudPush": { diff --git a/src/_locales/sk/messages.json b/src/_locales/sk/messages.json index 7d70505ba7fb1..997b1c7bec35e 100644 --- a/src/_locales/sk/messages.json +++ b/src/_locales/sk/messages.json @@ -1196,19 +1196,19 @@ "description": "Text warning about an incoming redirect" }, "docblockedReasonLabel": { - "message": "Reason:", + "message": "Dôvod:", "description": "The label which prepend the actual reason why a page was blocked" }, "docblockedReasonMalicious": { - "message": "Malicious", + "message": "Škodlivé", "description": "An actual reason why a page was blocked" }, "docblockedReasonTracker": { - "message": "Tracker", + "message": "Sledovač", "description": "An actual reason why a page was blocked" }, "docblockedReasonDisreputable": { - "message": "Disreputable", + "message": "Pochybné", "description": "An actual reason why a page was blocked" }, "cloudPush": { diff --git a/src/_locales/sv/messages.json b/src/_locales/sv/messages.json index de84a9afbd78a..18242867a9372 100644 --- a/src/_locales/sv/messages.json +++ b/src/_locales/sv/messages.json @@ -1196,19 +1196,19 @@ "description": "Text warning about an incoming redirect" }, "docblockedReasonLabel": { - "message": "Reason:", + "message": "Anledning:", "description": "The label which prepend the actual reason why a page was blocked" }, "docblockedReasonMalicious": { - "message": "Malicious", + "message": "Illvillig", "description": "An actual reason why a page was blocked" }, "docblockedReasonTracker": { - "message": "Tracker", + "message": "Spårare", "description": "An actual reason why a page was blocked" }, "docblockedReasonDisreputable": { - "message": "Disreputable", + "message": "Vanhedrande", "description": "An actual reason why a page was blocked" }, "cloudPush": { diff --git a/src/_locales/tr/messages.json b/src/_locales/tr/messages.json index ac9b6ec7023f2..114616b189bb7 100644 --- a/src/_locales/tr/messages.json +++ b/src/_locales/tr/messages.json @@ -1196,19 +1196,19 @@ "description": "Text warning about an incoming redirect" }, "docblockedReasonLabel": { - "message": "Reason:", + "message": "Sebep:", "description": "The label which prepend the actual reason why a page was blocked" }, "docblockedReasonMalicious": { - "message": "Malicious", + "message": "Kötü niyetli", "description": "An actual reason why a page was blocked" }, "docblockedReasonTracker": { - "message": "Tracker", + "message": "İzleyici", "description": "An actual reason why a page was blocked" }, "docblockedReasonDisreputable": { - "message": "Disreputable", + "message": "Güvenilmez", "description": "An actual reason why a page was blocked" }, "cloudPush": { diff --git a/src/_locales/uk/messages.json b/src/_locales/uk/messages.json index a1d3705f661e7..f985433fc4ee7 100644 --- a/src/_locales/uk/messages.json +++ b/src/_locales/uk/messages.json @@ -1196,19 +1196,19 @@ "description": "Text warning about an incoming redirect" }, "docblockedReasonLabel": { - "message": "Reason:", + "message": "Причина:", "description": "The label which prepend the actual reason why a page was blocked" }, "docblockedReasonMalicious": { - "message": "Malicious", + "message": "Зловмисні", "description": "An actual reason why a page was blocked" }, "docblockedReasonTracker": { - "message": "Tracker", + "message": "Трекер", "description": "An actual reason why a page was blocked" }, "docblockedReasonDisreputable": { - "message": "Disreputable", + "message": "Сумнівний вміст", "description": "An actual reason why a page was blocked" }, "cloudPush": { diff --git a/src/_locales/zh_TW/messages.json b/src/_locales/zh_TW/messages.json index 27e03112a083c..996a89c157381 100644 --- a/src/_locales/zh_TW/messages.json +++ b/src/_locales/zh_TW/messages.json @@ -1196,19 +1196,19 @@ "description": "Text warning about an incoming redirect" }, "docblockedReasonLabel": { - "message": "Reason:", + "message": "理由:", "description": "The label which prepend the actual reason why a page was blocked" }, "docblockedReasonMalicious": { - "message": "Malicious", + "message": "惡意程式碼", "description": "An actual reason why a page was blocked" }, "docblockedReasonTracker": { - "message": "Tracker", + "message": "追蹤器", "description": "An actual reason why a page was blocked" }, "docblockedReasonDisreputable": { - "message": "Disreputable", + "message": "聲名狼藉", "description": "An actual reason why a page was blocked" }, "cloudPush": { From 27936f476e83ac3e7749b02265c35e4cf3c10ce4 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Tue, 24 Jun 2025 11:45:44 -0400 Subject: [PATCH 1052/1099] [mv3] Fix newline assistant in mode editor --- platform/mv3/extension/js/mode-editor.js | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/platform/mv3/extension/js/mode-editor.js b/platform/mv3/extension/js/mode-editor.js index e88f5441181ac..9e86eade503a7 100644 --- a/platform/mv3/extension/js/mode-editor.js +++ b/platform/mv3/extension/js/mode-editor.js @@ -76,12 +76,16 @@ export class ModeEditor { } } - sequenceScopes = [ - `${i18n$('filteringMode0Name')}:`, 'none:', - `${i18n$('filteringMode1Name')}:`, 'basic:', - `${i18n$('filteringMode2Name')}:`, 'optimal:', - `${i18n$('filteringMode3Name')}:`, 'complete:', - ]; + newlineAssistant = { + 'no filtering:': ' - ', + 'basic:': ' - ', + 'optimal:': ' - ', + 'complete:': ' - ', + [`${i18n$('filteringMode0Name')}:`]: ' - ', + [`${i18n$('filteringMode1Name')}:`]: ' - ', + [`${i18n$('filteringMode2Name')}:`]: ' - ', + [`${i18n$('filteringMode3Name')}:`]: ' - ', + }; ioAccept = '.json,application/json'; }; From 9bcfc8ff1c614b1c4ded9cc4ca8e5c1f283bcee4 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Tue, 24 Jun 2025 17:19:49 -0400 Subject: [PATCH 1053/1099] [mv3] Add access to troubleshooting info in About pane --- platform/mv3/extension/dashboard.html | 4 + platform/mv3/extension/js/dashboard.js | 7 +- platform/mv3/extension/js/report.js | 109 +--------------- platform/mv3/extension/js/troubleshooting.js | 123 +++++++++++++++++++ platform/mv3/extension/report.html | 2 +- 5 files changed, 138 insertions(+), 107 deletions(-) create mode 100644 platform/mv3/extension/js/troubleshooting.js diff --git a/platform/mv3/extension/dashboard.html b/platform/mv3/extension/dashboard.html index ee64de6bfb061..bc71b3398a489 100644 --- a/platform/mv3/extension/dashboard.html +++ b/platform/mv3/extension/dashboard.html @@ -140,6 +140,10 @@

+
+
+

+            
diff --git a/platform/mv3/extension/js/dashboard.js b/platform/mv3/extension/js/dashboard.js index d5c27078e0928..113d7481f50e5 100644 --- a/platform/mv3/extension/js/dashboard.js +++ b/platform/mv3/extension/js/dashboard.js @@ -19,13 +19,14 @@ Home: https://github.com/gorhill/uBlock */ +import { dom, qs$ } from './dom.js'; import { localRead, localRemove, localWrite, } from './ext.js'; -import { dom } from './dom.js'; +import { getTroubleshootingInfo } from './troubleshooting.js'; import { runtime } from './ext.js'; /******************************************************************************/ @@ -52,6 +53,10 @@ localRead('dashboard.activePane').then(pane => { dom.body.dataset.pane = pane; }); +getTroubleshootingInfo().then(config => { + qs$('[data-i18n="supportS5H"] + pre').textContent = config; +}); + /******************************************************************************/ export function hashFromIterable(iter) { diff --git a/platform/mv3/extension/js/report.js b/platform/mv3/extension/js/report.js index 126d251d87152..3f5f50e07a2f4 100644 --- a/platform/mv3/extension/js/report.js +++ b/platform/mv3/extension/js/report.js @@ -19,18 +19,9 @@ Home: https://github.com/gorhill/uBlock */ -import { - dom, - qs$, -} from './dom.js'; - -import { - localRead, - runtime, - sendMessage, -} from './ext.js'; - -import { dnr } from './ext-compat.js'; +import { dom, qs$ } from './dom.js'; +import { getTroubleshootingInfo } from './troubleshooting.js'; +import { sendMessage } from './ext.js'; /******************************************************************************/ @@ -74,98 +65,6 @@ function reportSpecificFilterType() { /******************************************************************************/ -function renderData(data, depth = 0) { - const indent = ' '.repeat(depth); - if ( Array.isArray(data) ) { - const out = []; - for ( const value of data ) { - out.push(renderData(value, depth)); - } - return out.join('\n'); - } - if ( typeof data !== 'object' || data === null ) { - return `${indent}${data}`; - } - const out = []; - for ( const [ name, value ] of Object.entries(data) ) { - if ( typeof value === 'object' && value !== null ) { - out.push(`${indent}${name}:`); - out.push(renderData(value, depth + 1)); - continue; - } - out.push(`${indent}${name}: ${value}`); - } - return out.join('\n'); -} - -/******************************************************************************/ - -async function getConfigData() { - const manifest = runtime.getManifest(); - const [ - platformInfo, - rulesets, - defaultMode, - userRules, - registerContentScriptsReason, - unregisterContentScriptsReason, - ] = await Promise.all([ - runtime.getPlatformInfo(), - dnr.getEnabledRulesets(), - sendMessage({ what: 'getDefaultFilteringMode' }), - sendMessage({ what: 'getEffectiveUserRules' }), - localRead('$scripting.registerContentScripts'), - localRead('$scripting.unregisterContentScripts'), - ]); - const browser = (( ) => { - const extURL = runtime.getURL(''); - let agent = ''; - if ( extURL.startsWith('moz-extension:') ) { - agent = 'Firefox'; - } else if ( extURL.startsWith('safari-web-extension:') ) { - agent = 'Safari'; - } else if ( /\bEdg\/\b/.test(navigator.userAgent) ) { - agent = 'Edge'; - } else { - agent = 'Chrome'; - } - dom.cl.add('html', agent.toLowerCase()); - if ( /\bMobile\b/.test(navigator.userAgent) ) { - agent += ' Mobile'; - } - const reVersion = new RegExp(`\\b${agent.slice(0,3)}[^/]*/(\\d+)`); - const match = reVersion.exec(navigator.userAgent); - if ( match ) { - agent += ` ${match[1]}`; - } - agent += ` (${platformInfo.os})` - return agent; - })(); - const modes = [ 'no filtering', 'basic', 'optimal', 'complete' ]; - const config = { - name: manifest.name, - version: manifest.version, - browser, - filtering: { - 'site': `${modes[reportedPage.mode]}`, - 'default': `${modes[defaultMode]}`, - }, - }; - if ( userRules.length !== 0 ) { - config['user rules'] = userRules.length; - } - config.rulesets = rulesets; - if ( registerContentScriptsReason !== undefined ) { - config.registerContentScripts = registerContentScriptsReason; - } - if ( unregisterContentScriptsReason !== undefined ) { - config.unregisterContentScripts = unregisterContentScriptsReason; - } - return renderData(config); -} - -/******************************************************************************/ - async function reportSpecificFilterIssue() { const githubURL = new URL( 'https://github.com/uBlockOrigin/uAssets/issues/new?template=specific_report_from_ubol.yml' @@ -194,7 +93,7 @@ async function reportSpecificFilterIssue() { /******************************************************************************/ -getConfigData().then(config => { +getTroubleshootingInfo(reportedPage.mode).then(config => { qs$('[data-i18n="supportS5H"] + pre').textContent = config; dom.on('[data-url]', 'click', ev => { diff --git a/platform/mv3/extension/js/troubleshooting.js b/platform/mv3/extension/js/troubleshooting.js new file mode 100644 index 0000000000000..4300d1eb15d42 --- /dev/null +++ b/platform/mv3/extension/js/troubleshooting.js @@ -0,0 +1,123 @@ +/******************************************************************************* + + uBlock Origin - a comprehensive, efficient content blocker + Copyright (C) 2024-present Raymond Hill + + 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/}. + + Home: https://github.com/gorhill/uBlock +*/ + +import { + localRead, + runtime, + sendMessage, +} from './ext.js'; + +import { dnr } from './ext-compat.js'; +import { dom } from './dom.js'; + +/******************************************************************************/ + +function renderData(data, depth = 0) { + const indent = ' '.repeat(depth); + if ( Array.isArray(data) ) { + const out = []; + for ( const value of data ) { + out.push(renderData(value, depth)); + } + return out.join('\n'); + } + if ( typeof data !== 'object' || data === null ) { + return `${indent}${data}`; + } + const out = []; + for ( const [ name, value ] of Object.entries(data) ) { + if ( typeof value === 'object' && value !== null ) { + out.push(`${indent}${name}:`); + out.push(renderData(value, depth + 1)); + continue; + } + out.push(`${indent}${name}: ${value}`); + } + return out.join('\n'); +} + +/******************************************************************************/ + +export async function getTroubleshootingInfo(siteMode) { + const manifest = runtime.getManifest(); + const [ + platformInfo, + rulesets, + defaultMode, + userRules, + registerContentScriptsReason, + unregisterContentScriptsReason, + ] = await Promise.all([ + runtime.getPlatformInfo(), + dnr.getEnabledRulesets(), + sendMessage({ what: 'getDefaultFilteringMode' }), + sendMessage({ what: 'getEffectiveUserRules' }), + localRead('$scripting.registerContentScripts'), + localRead('$scripting.unregisterContentScripts'), + ]); + const browser = (( ) => { + const extURL = runtime.getURL(''); + let agent = ''; + if ( extURL.startsWith('moz-extension:') ) { + agent = 'Firefox'; + } else if ( extURL.startsWith('safari-web-extension:') ) { + agent = 'Safari'; + } else if ( /\bEdg\/\b/.test(navigator.userAgent) ) { + agent = 'Edge'; + } else { + agent = 'Chrome'; + } + dom.cl.add('html', agent.toLowerCase()); + if ( /\bMobile\b/.test(navigator.userAgent) ) { + agent += ' Mobile'; + } + const reVersion = new RegExp(`\\b${agent.slice(0,3)}[^/]*/(\\d+)`); + const match = reVersion.exec(navigator.userAgent); + if ( match ) { + agent += ` ${match[1]}`; + } + agent += ` (${platformInfo.os})` + return agent; + })(); + const modes = [ 'no filtering', 'basic', 'optimal', 'complete' ]; + const filtering = {}; + if ( siteMode ) { + filtering.site = `${modes[siteMode]}` + } + filtering.default = `${modes[defaultMode]}`; + const config = { + name: manifest.name, + version: manifest.version, + browser, + filtering, + }; + if ( userRules.length !== 0 ) { + config['user rules'] = userRules.length; + } + config.rulesets = rulesets; + if ( registerContentScriptsReason !== undefined ) { + config.registerContentScripts = registerContentScriptsReason; + } + if ( unregisterContentScriptsReason !== undefined ) { + config.unregisterContentScripts = unregisterContentScriptsReason; + } + return renderData(config); +} diff --git a/platform/mv3/extension/report.html b/platform/mv3/extension/report.html index 8632097f7619b..cf15492d7f452 100644 --- a/platform/mv3/extension/report.html +++ b/platform/mv3/extension/report.html @@ -55,7 +55,7 @@


-

+    

     
From 9c08e902bddcd5f424928c38d65c7b87f5c59336 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Tue, 24 Jun 2025 20:44:14 -0400 Subject: [PATCH 1054/1099] [mv3] Fix regression in syntax highlighting of dark theme --- platform/mv3/extension/css/develop.css | 27 +++++++++++++++++-- platform/mv3/extension/js/develop.js | 26 +++++++++--------- .../extension/lib/codemirror/codemirror-ubol | 2 +- 3 files changed, 40 insertions(+), 15 deletions(-) diff --git a/platform/mv3/extension/css/develop.css b/platform/mv3/extension/css/develop.css index 7f2eb00e8e569..55816b6d657c8 100644 --- a/platform/mv3/extension/css/develop.css +++ b/platform/mv3/extension/css/develop.css @@ -31,12 +31,35 @@ section[data-pane="develop"] > div > * { height: 100%; } -#cm-container .cm-editor .cm-line:has(.yamlboundary) { +#cm-container .cm-editor .cm-line:has(.ubol-boundary) { background-image: url('line-hor-dashed.png'), url('line-hor-dashed.png'); background-position: left 3px, left calc(100% - 3px); background-repeat: repeat-x; } - +#cm-container .cm-editor { + color: var(--ink-1); + } +:root.dark #cm-container .cm-editor { + color: var(--ink-2); + } +#cm-container .cm-editor .cm-line .ubol-comment { + color: #ba5300; + } +:root.dark #cm-container .cm-editor .cm-line .ubol-comment { + color: #fa7000; + } +#cm-container .cm-editor .cm-line .ubol-keyword { + color: #ae42be; + } +:root.dark #cm-container .cm-editor .cm-line .ubol-keyword { + color: #ea59ff; + } +#cm-container .cm-editor .cm-line .ubol-literal { + color: #168156; + } +:root.dark #cm-container .cm-editor .cm-line .ubol-literal { + color: #1dae74; + } #cm-container .cm-editor .cm-line.badline:not(.cm-activeLine) { background-color: color-mix(in srgb, var(--info3-ink) 15%, transparent 85%); } diff --git a/platform/mv3/extension/js/develop.js b/platform/mv3/extension/js/develop.js index e36240a27bc92..0413c708fcaa4 100644 --- a/platform/mv3/extension/js/develop.js +++ b/platform/mv3/extension/js/develop.js @@ -557,35 +557,33 @@ class Editor { }, token: (stream, state) => { if ( stream.sol() ) { - if ( stream.match(/^---\s*$/) ) { return 'yamlboundary'; } - if ( stream.match(/^# ---\s*$/) ) { return 'yamlboundary comment'; } - if ( stream.match(/\.\.\.\s*$/) ) { return 'yamlboundary'; } + if ( stream.match(/^---\s*$/) ) { return 'ubol-boundary'; } + if ( stream.match(/^# ---\s*$/) ) { return 'ubol-boundary ubol-comment'; } + if ( stream.match(/\.\.\.\s*$/) ) { return 'ubol-boundary'; } } const c = stream.peek(); if ( c === '#' ) { if ( (stream.pos === 0 || /\s/.test(stream.string.charAt(stream.pos - 1))) ) { stream.skipToEnd(); - return 'comment'; + return 'ubol-comment'; } } - if ( stream.eatSpace() ) { - return null; - } + if ( stream.eatSpace() ) { return null; } const { scope } = state; state.scope = 0; if ( scope === 0 && stream.match(/^[^:]+(?=:)/) ) { state.scope = 1; - return 'keyword'; + return 'ubol-keyword'; } if ( scope === 1 && stream.match(/^:(?: |$)/) ) { - return 'punctuation'; + return 'ubol-punctuation'; } if ( stream.match(/^- /) ) { - return 'punctuation'; + return 'ubol-punctuation'; } if ( this.editor.streamParserKeywords ) { if ( stream.match(this.editor.streamParserKeywords) ) { - return 'literal'; + return 'ubol-literal'; } } if ( stream.match(/^\S+/) ) { @@ -598,7 +596,11 @@ class Editor { commentTokens: { line: '#' }, }, tokenTable: [ - 'yamlboundary', + 'ubol-boundary', + 'ubol-keyword', + 'ubol-comment', + 'ubol-punctuation', + 'ubol-literal', ], }; } diff --git a/platform/mv3/extension/lib/codemirror/codemirror-ubol b/platform/mv3/extension/lib/codemirror/codemirror-ubol index 4bacc0ca79f3e..0cd9b7a77cd16 160000 --- a/platform/mv3/extension/lib/codemirror/codemirror-ubol +++ b/platform/mv3/extension/lib/codemirror/codemirror-ubol @@ -1 +1 @@ -Subproject commit 4bacc0ca79f3eb4c6e395bf54d3f5efe4da4e07c +Subproject commit 0cd9b7a77cd166186ebdb483d6aac82f616685df From 66b68b4442e8b3df73fcd60dda8baab429783223 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sat, 28 Jun 2025 06:07:09 -0400 Subject: [PATCH 1055/1099] Reset `important` option flag at `header` evaluation time --- src/js/static-net-filtering.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/js/static-net-filtering.js b/src/js/static-net-filtering.js index 4596f78864e63..7fd749045ab20 100644 --- a/src/js/static-net-filtering.js +++ b/src/js/static-net-filtering.js @@ -5426,6 +5426,7 @@ StaticNetFilteringEngine.prototype.matchHeaders = function(fctxt, headers) { $requestMethodBit = fctxt.method || 0; $requestTypeValue = (typeBits & TYPE_REALM) >>> TYPE_REALM_OFFSET; $requestAddress = fctxt.getIPAddress(); + $isBlockImportant = false; $httpHeaders.init(headers); let r = 0; From e27bb85222924881dbd502e312ab83c9aa6dafb8 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sat, 28 Jun 2025 06:20:23 -0400 Subject: [PATCH 1056/1099] Import translation work from https://crowdin.com/project/ublock --- src/_locales/hi/messages.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/_locales/hi/messages.json b/src/_locales/hi/messages.json index e386b715363de..9343efb4b9118 100644 --- a/src/_locales/hi/messages.json +++ b/src/_locales/hi/messages.json @@ -1192,7 +1192,7 @@ "description": "Button text to navigate to the blocked page" }, "docblockedRedirectPrompt": { - "message": "The blocked page wants to redirect to another site. If you choose to proceed, you will navigate directly to: {{url}}", + "message": "ब्लॉक किया गया पेज किसी दूसरी साइट पर ले जाना चाहता है. अगर आप आगे बढ़ना चुनते हैं, तो आप सीधे इस लिंक को खोल सकते हैं: {{url}}", "description": "Text warning about an incoming redirect" }, "docblockedReasonLabel": { From a25564559c099fbb70445bee094394d9731957a2 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sat, 28 Jun 2025 06:21:11 -0400 Subject: [PATCH 1057/1099] Update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index edc5a1931f5e1..43b2cdfb79273 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,4 @@ +- [Reset `important` option flag at `header` evaluation time](https://github.com/gorhill/uBlock/commit/66b68b4442) - [Fix broken reverse lookup of filter lists](https://github.com/gorhill/uBlock/commit/527b4a201f) - [Add `[trusted-]edit-inbound-object` scriptlets](https://github.com/gorhill/uBlock/commit/6e466cf945) - [Improve `remove-cookie` scriptlet](https://github.com/gorhill/uBlock/commit/0a8ea58bb7) From f23518173e96e8096667884e0b743d17a686ed23 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sat, 28 Jun 2025 06:21:31 -0400 Subject: [PATCH 1058/1099] New revision for dev build --- dist/version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dist/version b/dist/version index a36ec4e96a299..3dcbe800b96cb 100644 --- a/dist/version +++ b/dist/version @@ -1 +1 @@ -1.64.1.8 \ No newline at end of file +1.64.1.9 \ No newline at end of file From b5fae233292e71df411baa0ae0dd11ac76864128 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sat, 28 Jun 2025 06:41:18 -0400 Subject: [PATCH 1059/1099] Make Firefox dev build auto-update --- dist/firefox/updates.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dist/firefox/updates.json b/dist/firefox/updates.json index 20fab8d2ac7d7..7dbecf75028fa 100644 --- a/dist/firefox/updates.json +++ b/dist/firefox/updates.json @@ -3,9 +3,9 @@ "uBlock0@raymondhill.net": { "updates": [ { - "version": "1.64.1.8", + "version": "1.64.1.9", "browser_specific_settings": { "gecko": { "strict_min_version": "92.0" } }, - "update_link": "https://github.com/gorhill/uBlock/releases/download/1.64.1b8/uBlock0_1.64.1b8.firefox.signed.xpi" + "update_link": "https://github.com/gorhill/uBlock/releases/download/1.64.1b9/uBlock0_1.64.1b9.firefox.signed.xpi" } ] } From eec204564590013e6cd0f50128b35843a141cf40 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Thu, 3 Jul 2025 09:22:09 -0400 Subject: [PATCH 1060/1099] [mv3] Fix calculation of priority value when converting redirect filters --- src/js/static-net-filtering.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/js/static-net-filtering.js b/src/js/static-net-filtering.js index 7fd749045ab20..9f68e9993ab8c 100644 --- a/src/js/static-net-filtering.js +++ b/src/js/static-net-filtering.js @@ -4709,7 +4709,7 @@ StaticNetFilteringEngine.prototype.dnrFromCompiled = function(op, context, ...ar if ( token !== '' ) { const match = /:(\d+)$/.exec(token); if ( match !== null ) { - rule.priority += Math.min(rule.priority + parseInt(match[1], 10), 9); + rule.priority += Math.min(parseInt(match[1], 10), 8); token = token.slice(0, match.index); } } From 76b72c9e6511eaba05e803c09ea74e7d294ed16d Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Thu, 3 Jul 2025 09:46:52 -0400 Subject: [PATCH 1061/1099] Import translation work from https://crowdin.com/project/ublock --- platform/mv3/description/webstore.pt_BR.txt | 2 +- .../mv3/extension/_locales/be/messages.json | 2 +- .../extension/_locales/pt_BR/messages.json | 48 ++++++------ src/_locales/cs/messages.json | 2 +- src/_locales/fa/messages.json | 4 +- src/_locales/pt_BR/messages.json | 76 +++++++++---------- src/_locales/sq/messages.json | 8 +- 7 files changed, 71 insertions(+), 71 deletions(-) diff --git a/platform/mv3/description/webstore.pt_BR.txt b/platform/mv3/description/webstore.pt_BR.txt index 532581411fe45..7e04ac9f88d86 100644 --- a/platform/mv3/description/webstore.pt_BR.txt +++ b/platform/mv3/description/webstore.pt_BR.txt @@ -7,6 +7,6 @@ O conjunto de regras padrão corresponde ao conjunto de filtros padrão do uBloc - EasyPrivacy - Lista de servidores de anúncios e rastreadores do Peter Lowe -Você pode ativar mais conjuntos de regras visitando a página das opções — clique no ícone da _Engranagem_ no painel do popup. +Você pode ativar mais conjuntos de regras visitando a página das opções — clique no ícone da _Engranagem_ no painel do pop-up. O uBOL é totalmente declarativo, significando que não há necessidade de um processo permanente do uBOL para a filtragem ocorrer e a filtragem de conteúdo baseada em injeção do CSS/JS é realizada confiavelmente pelo próprio navegador ao invés da extensão. Isto significa que o próprio uBOL não consome recursos de CPU/memória enquanto o bloqueio de conteúdo está em andamento -- o processo do service worker do uBOL _só_ é necessário quando você interage com o painel do pop-up ou as páginas das opções. diff --git a/platform/mv3/extension/_locales/be/messages.json b/platform/mv3/extension/_locales/be/messages.json index ca19eb9b06a66..6e4e05d0c9334 100644 --- a/platform/mv3/extension/_locales/be/messages.json +++ b/platform/mv3/extension/_locales/be/messages.json @@ -304,7 +304,7 @@ "description": "An option in a dropdown list" }, "developOptionCustomDnrRules": { - "message": "Кастомныя правілы DNR", + "message": "Уласныя правілы DNR", "description": "An option in a dropdown list" }, "developOptionDnrRulesOf": { diff --git a/platform/mv3/extension/_locales/pt_BR/messages.json b/platform/mv3/extension/_locales/pt_BR/messages.json index 49271594a52fd..5974acd75efe3 100644 --- a/platform/mv3/extension/_locales/pt_BR/messages.json +++ b/platform/mv3/extension/_locales/pt_BR/messages.json @@ -4,7 +4,7 @@ "description": "extension name." }, "extShortDesc": { - "message": "Um bloqueador de conteúdo sem permissões. Bloqueia anúncios, rastreadores e muito mais, imediatamente após a instalação.", + "message": "Um bloqueador eficiente de conteúdo. Bloqueia anúncios, rastreadores e muito mais, imediatamente após a instalação.", "description": "this will be in the Chrome web store: must be 132 characters or less" }, "perRulesetStats": { @@ -20,7 +20,7 @@ "description": "appears as tab name in dashboard" }, "developPageName": { - "message": "Desenvolva", + "message": "Desenvolver", "description": "appears as tab name in dashboard. Inspired from 'Develop' menu in Safari, see https://developer.apple.com/documentation/safari-developer-tools/develop-menu" }, "aboutPageName": { @@ -36,7 +36,7 @@ "description": "Label in the popup panel for the current filtering mode" }, "popupTipReport": { - "message": "Reportar um problema neste site da web", + "message": "Relatar um problema neste site", "description": "Tooltip used for the 'chat' icon in the panel" }, "popupTipDashboard": { @@ -92,7 +92,7 @@ "description": "English: Contributors" }, "aboutSourceCode": { - "message": "Código fonte", + "message": "Código-fonte", "description": "Link text to source code repo" }, "aboutTranslations": { @@ -108,11 +108,11 @@ "description": "Shown in the About pane" }, "supportS6H": { - "message": "Reportar um problema com o filtro", + "message": "Relatar um problema com o filtro", "description": "Header of 'Report a filter issue' section in Support pane" }, "supportS3P1": { - "message": "Reporte problemas dos filtros com sites da web específicos no rastreador de problemas uBlockOrigin/uAssets. Requer uma conta no GitHub.", + "message": "Relate problemas dos filtros em sites específicos no rastreador de problemas douBlockOrigin/uAssets. Uma conta do GitHub é necessária.", "description": "First paragraph of 'Filter issues' section in Support pane" }, "supportS5H": { @@ -120,23 +120,23 @@ "description": "Label of 'Troubleshooting information' section in 'Report a filter issue' page" }, "supportS6P1S1": { - "message": "Para evitar sobrecarregar os voluntários com relatórios duplicados por favor verifique se o problema já não foi reportado.\nObservação: clicar no botão fará com que a origem da página seja enviada ao GitHub.", + "message": "Para evitar sobrecarregar os voluntários com relatórios duplicados por favor verifique se o problema já não foi relatado. Observação: clicar no botão fará com que a origem da página seja enviada ao GitHub.", "description": "A paragraph in the filter issue reporter section" }, "supportFindSpecificButton": { - "message": "Achar relatórios similares", + "message": "Achar relatórios similares no GitHub", "description": "A clickable link in the filter issue reporter section" }, "supportS6URL": { - "message": "Endereço da página da web:", + "message": "Endereço da página:", "description": "Label for the URL of the page" }, "supportS6Select1": { - "message": "A página da web…", + "message": "A página…", "description": "Label for widget to select type of issue" }, "supportS6Select1Option0": { - "message": "— Escolha uma entrada —", + "message": "— Selecione um tipo —", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option1": { @@ -144,7 +144,7 @@ "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option2": { - "message": "Tem sobreposições ou outros incômodos", + "message": "Tem sobreposições ou outras perturbações", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option3": { @@ -152,11 +152,11 @@ "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option4": { - "message": "Tem problemas relacionados a privacidade", + "message": "Tem problemas relacionados à privacidade", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option5": { - "message": "Funciona mal quando o uBO Lite está ativado", + "message": "Não funciona direito quando o uBO Lite está ativado", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option6": { @@ -168,11 +168,11 @@ "description": "An entry in the widget used to select the type of issue" }, "supportS6Checkbox1": { - "message": "Rotular a página da web como “NSFW” (“Not Safe For Work”)", + "message": "Rotular a página como “NSFW” (“Not Safe For Work”)", "description": "A checkbox to use for NSFW sites" }, "supportReportSpecificButton": { - "message": "Criar novo relatório", + "message": "Criar um novo relatório no GitHub", "description": "Text for button which open an external webpage in Support pane" }, "defaultFilteringModeSectionLabel": { @@ -224,11 +224,11 @@ "description": "The header text for the 'Behavior' section" }, "autoReloadLabel": { - "message": "Recarregar a página automaticamente quando mudar o modo de filtragem", + "message": "Recarregar a página automaticamente ao alterar o modo de filtragem", "description": "Label for a checkbox in the options page" }, "showBlockedCountLabel": { - "message": "Mostrar o número de requisições bloqueadas no ícone da barra de ferramentas", + "message": "Mostrar o número de solicitações bloqueadas no ícone da barra de ferramentas", "description": "Label for a checkbox in the options page" }, "enableStrictBlockLabel": { @@ -236,7 +236,7 @@ "description": "Label for a checkbox in the options page" }, "enableStrictBlockLegend": { - "message": "A navegação até sites potencialmente indesejáveis ​​será bloqueada e será oferecido a você a opção de prosseguir.", + "message": "A navegação para sites potencialmente indesejáveis ​​será bloqueada e você terá a opção de prosseguir.", "description": "Short description for a checkbox in the options page" }, "developerModeLabel": { @@ -244,7 +244,7 @@ "description": "Label for a checkbox in the options page" }, "developerModeLegend": { - "message": "Ativa o acesso as funções adequadas pra usuários técnicos.", + "message": "Ativa o acesso à funcionalidade destinada a usuários técnicos.", "description": "Short description for a checkbox in the options page" }, "findListsPlaceholder": { @@ -260,7 +260,7 @@ "description": "Sentence used in the strict-blocked page" }, "strictblockReasonSentence1": { - "message": "A página foi bloqueada por causa de um filtro que combina no {{listname}}.", + "message": "A página foi bloqueada devido a um filtro correspondente em {{listname}}.", "description": "Text informing about what is causing the page to be blocked" }, "strictblockRedirectSentence1": { @@ -288,11 +288,11 @@ "description": "A button to navigate to the blocked page" }, "zapperTipEnter": { - "message": "Entrar no modo do elemento zapper", + "message": "Entrar no modo de remoção de elementos", "description": "Tooltip for the button used to enter zapper mode" }, "zapperTipQuit": { - "message": "Sair do modo do elemento zapper", + "message": "Sair do modo de remoção de elementos", "description": "Tooltip for the button used to exit zapper mode" }, "developDropdownLabel": { @@ -336,7 +336,7 @@ "description": "Text for buttons used to export content" }, "dnrRulesWarning": { - "message": "Não adicionar conteúdo de fontes não confiáveis.", + "message": "Não adicione conteúdo de fontes não confiáveis.", "description": "Short description of the DNR rules editor pane" }, "dnrRulesCountInfo": { diff --git a/src/_locales/cs/messages.json b/src/_locales/cs/messages.json index e41e4f0fa3c94..2a895a3bfb813 100644 --- a/src/_locales/cs/messages.json +++ b/src/_locales/cs/messages.json @@ -1172,7 +1172,7 @@ "description": "English: Close this window" }, "docblockedDontWarn": { - "message": "Znova mě nevarujte ohladně této stránky", + "message": "Nevarujte mě znovu o této stránce", "description": "Label for checkbox in document-blocked page" }, "docblockedProceed": { diff --git a/src/_locales/fa/messages.json b/src/_locales/fa/messages.json index b9316a71c05ef..b48b35e137f3e 100644 --- a/src/_locales/fa/messages.json +++ b/src/_locales/fa/messages.json @@ -1196,7 +1196,7 @@ "description": "Text warning about an incoming redirect" }, "docblockedReasonLabel": { - "message": "Reason:", + "message": "دلیل", "description": "The label which prepend the actual reason why a page was blocked" }, "docblockedReasonMalicious": { @@ -1204,7 +1204,7 @@ "description": "An actual reason why a page was blocked" }, "docblockedReasonTracker": { - "message": "Tracker", + "message": "ردیاب", "description": "An actual reason why a page was blocked" }, "docblockedReasonDisreputable": { diff --git a/src/_locales/pt_BR/messages.json b/src/_locales/pt_BR/messages.json index 22e4ff6d8f935..38b3b2c63a08a 100644 --- a/src/_locales/pt_BR/messages.json +++ b/src/_locales/pt_BR/messages.json @@ -72,11 +72,11 @@ "description": "English: Click: disable/enable uBlock₀ for this site.\n\nCtrl+click: disable uBlock₀ only on this page." }, "popupPowerSwitchInfo1": { - "message": "Clique pra desativar o uBlock₀ neste site.\n\nCtrl+clique pra desativar o uBlock₀ só nesta página.", + "message": "Clique para desativar o uBlock₀ neste site.\n\nCtrl+clique para desativar o uBlock₀ só nesta página.", "description": "Message to be read by screen readers" }, "popupPowerSwitchInfo2": { - "message": "Clique pra ativar o uBlock₀ neste site.", + "message": "Clique para ativar o uBlock₀ neste site.", "description": "Message to be read by screen readers" }, "popupBlockedRequestPrompt": { @@ -116,7 +116,7 @@ "description": "English: Click to open the dashboard" }, "popupTipZapper": { - "message": "Entrar no modo do elemento zapper", + "message": "Entrar no modo de remoção de elementos", "description": "Tooltip for the element-zapper icon in the popup panel" }, "popupTipPicker": { @@ -128,7 +128,7 @@ "description": "Tooltip used for the logger icon in the panel" }, "popupTipReport": { - "message": "Reportar um problema com este site da web", + "message": "Relatar um problema neste site", "description": "Tooltip used for the 'chat' icon in the panel" }, "popupTipNoPopups": { @@ -176,7 +176,7 @@ "description": "Tooltip for the no-remote-fonts per-site switch" }, "popupTipNoRemoteFonts2": { - "message": "Clique pra não mais bloquear fontes remotas neste site", + "message": "Clique para deixar de bloquear fontes remotas neste site", "description": "Tooltip for the no-remote-fonts per-site switch" }, "popupTipNoScripting1": { @@ -184,7 +184,7 @@ "description": "Tooltip for the no-scripting per-site switch" }, "popupTipNoScripting2": { - "message": "Clique pra não mais desativar o JavaScript neste site", + "message": "Clique para deixar de desativar o JavaScript neste site", "description": "Tooltip for the no-scripting per-site switch" }, "popupNoPopups_v2": { @@ -244,7 +244,7 @@ "description": "" }, "popup3pPassiveRulePrompt": { - "message": "CSS/Imagens de terceiros", + "message": "CSS/imagens de terceiros", "description": "" }, "popupInlineScriptRulePrompt": { @@ -444,7 +444,7 @@ "description": "English: Parse and enforce Adblock+ element hiding filters." }, "3pParseAllABPHideFiltersInfo": { - "message": "Os filtros cosméticos servem pra esconder elementos de uma página da web os quais são considerados um incômodo visual e os quais não podem ser bloqueados pelas engines de filtragem baseadas em requisições de rede.", + "message": "Os filtros cosméticos servem para ocultar elementos em uma página os quais são considerados um incômodo visual e os quais não podem ser bloqueados pelos motores de filtragem baseadas em solicitações de rede.", "description": "Describes the purpose of the 'Parse and enforce cosmetic filters' feature." }, "3pIgnoreGenericCosmeticFilters": { @@ -452,7 +452,7 @@ "description": "This will cause uBO to ignore all generic cosmetic filters." }, "3pIgnoreGenericCosmeticFiltersInfo": { - "message": "Os filtros cosméticos genéricos são aqueles filtros cosméticos os quais são destinados a serem aplicados em todos os sites da web. Ativar esta opção eliminará a sobrecarga da memória e da CPU adicionada as páginas da web como um resultado de lidar com filtros cosméticos genéricos.\n\nÉ recomendado ativar esta opção em dispositivos menos poderosos.", + "message": "Os filtros cosméticos genéricos são aqueles filtros cosméticos os quais são destinados a serem aplicados em todos os sites. Ativar esta opção eliminará a sobrecarga da memória e da CPU adicionada as páginas como um resultado de lidar com filtros cosméticos genéricos.\n\nÉ recomendado ativar esta opção em dispositivos mais lentos.", "description": "Describes the purpose of the 'Ignore generic cosmetic filters' feature." }, "3pSuspendUntilListsAreLoaded": { @@ -536,7 +536,7 @@ "description": "used as a tooltip for error icon beside a list" }, "1pTrustWarning": { - "message": "Não adicionar filtros de fontes não confiáveis.", + "message": "Não adicione filtros de fontes não confiáveis.", "description": "Warning against copy-pasting filters from random sources" }, "1pEnableMyFiltersLabel": { @@ -548,7 +548,7 @@ "description": "Label for the checkbox use to trust the content of 'My filters' list" }, "1pImport": { - "message": "Importar e anexar", + "message": "Importar e anexar…", "description": "Button in the 'My filters' pane" }, "1pExport": { @@ -576,7 +576,7 @@ "description": "This will remove all temporary rules" }, "rulesCommit": { - "message": "Submeter", + "message": "Confirmar", "description": "This will persist temporary rules" }, "rulesEdit": { @@ -596,7 +596,7 @@ "description": "" }, "rulesExport": { - "message": "Exportar pro arquivo", + "message": "Exportar para um arquivo…", "description": "Button in the 'My rules' pane" }, "rulesDefaultFileName": { @@ -608,7 +608,7 @@ "description": "English: List of your dynamic filtering rules." }, "rulesFormatHint": { - "message": "Regras da sintaxe: ação do tipo de destino da origem (documentação completa).", + "message": "Regras de sintaxe: origem destino tipo ação (documentação completa).", "description": "English: dynamic rule syntax and full documentation." }, "rulesSort": { @@ -628,7 +628,7 @@ "description": "English: a sort option for list of rules." }, "whitelistPrompt": { - "message": "As diretivas dos sites confiáveis ditam em quais páginas da web o uBlock Origin deve ser desativado. Uma entrada por linha. ", + "message": "As diretivas dos sites confiáveis ditam em quais páginas o uBlock Origin deve ser desativado. Uma entrada por linha. ", "description": "A concise description of the 'Trusted sites' pane." }, "whitelistImport": { @@ -684,7 +684,7 @@ "description": "Tooltip for the DOM inspector button in the logger page" }, "loggerPopupPanelTip": { - "message": "Alternar o painel do pop-up", + "message": "Habilitar o painel pop-up", "description": "Tooltip for the popup panel button in the logger page" }, "loggerInfoTip": { @@ -704,7 +704,7 @@ "description": "Tooltip for the play button in the logger page" }, "loggerRowFiltererButtonTip": { - "message": "Alternar filtragem do coletor", + "message": "Habilitar filtragem do coletor", "description": "Tooltip for the row filterer button in the logger page" }, "logFilterPrompt": { @@ -796,7 +796,7 @@ "description": "Small header to identify the static filtering section" }, "loggerStaticFilteringSentence": { - "message": "{{action}} requisições da rede de {{type}} {{br}}cujo endereço de URL corresponde {{url}} {{br}}e o qual se origina em {{origin}},{{br}}{{importance}} há um filtro de exceção correspondente.", + "message": "{{action}} solicitações de rede do tipo {{type}} {{br}}onde o endereço da URL corresponde a {{url}} {{br}}e que origina de {{origin}},{{br}}{{importance}} há um filtro de exceção correspondente.", "description": "Used in the static filtering wizard" }, "loggerStaticFilteringSentencePartBlock": { @@ -900,11 +900,11 @@ "description": "Text for button which open an external webpage in Support pane" }, "supportReportSpecificButton": { - "message": "Criar um novo relatório", + "message": "Criar um novo relatório no GitHub", "description": "Text for button which open an external webpage in Support pane" }, "supportFindSpecificButton": { - "message": "Achar relatórios similares", + "message": "Achar relatórios similares no GitHub", "description": "A clickable link in the filter issue reporter section" }, "supportS1H": { @@ -924,7 +924,7 @@ "description": "First paragraph of 'Questions and support' section in Support pane" }, "supportS3H": { - "message": "Problemas com o filtro/o site da web está quebrado", + "message": "Problemas com filtros/o site não funciona", "description": "Header of 'Filter issues' section in Support pane" }, "supportS3P1": { @@ -932,7 +932,7 @@ "description": "First paragraph of 'Filter issues' section in Support pane" }, "supportS3P2": { - "message": "Importante: Evite usar outros bloqueadores com propósito similar junto do uBlock Origin pois isto pode causar problemas com filtros em sites da web específicos.", + "message": "Importante: Evite usar outros bloqueadores junto com o uBlock Origin pois isto pode causar problemas com filtros em sites específicos.", "description": "Second paragraph of 'Filter issues' section in Support pane" }, "supportS3P3": { @@ -944,7 +944,7 @@ "description": "Header of 'Bug report' section in Support pane" }, "supportS4P1": { - "message": "Reporte problemas com o próprio uBlock Origin no uBlockOrigin/uBlock-issuerastreador de problemas. Requer uma conta no GitHub.", + "message": "Relate problemas com o próprio uBlock Origin no rastreador de problemas do uBlockOrigin/uBlock-issue. Uma conta do GitHub é necessária.", "description": "First paragraph of 'Bug report' section in Support pane" }, "supportS5H": { @@ -956,7 +956,7 @@ "description": "First paragraph of 'Troubleshooting Information' section in Support pane" }, "supportS5P2": { - "message": "Atenção: Informações potencialmente privadas ou sensíveis são eliminadas por padrão. As informações eliminadas podem tornar mais difícil resolver um problema.", + "message": "Importante: Informações potencialmente privadas ou sensíveis são eliminadas por padrão. As informações eliminadas podem dificultar a resolução de um problema.", "description": "Second paragraph of 'Troubleshooting Information' section in Support pane" }, "supportS6H": { @@ -964,7 +964,7 @@ "description": "Header of 'Report a filter issue' section in Support pane" }, "supportS6P1S1": { - "message": "Pra evitar sobrecarregar os voluntários com relatórios duplicados por favor certifique-se que o problema já não foi reportado.", + "message": "Para evitar sobrecarregar os voluntários com relatórios duplicados por favor verifique se o problema já não foi relatado. Observação: clicar no botão fará com que a origem da página seja enviada ao GitHub.", "description": "A paragraph in the filter issue reporter section" }, "supportS6P2S1": { @@ -984,7 +984,7 @@ "description": "Label for widget to select type of issue" }, "supportS6Select1Option0": { - "message": "— Escolha uma entrada —", + "message": "— Selecione um tipo —", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option1": { @@ -992,7 +992,7 @@ "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option2": { - "message": "Tem sobreposições ou outros incômodos", + "message": "Tem sobreposições ou outras perturbações", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option3": { @@ -1004,7 +1004,7 @@ "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option5": { - "message": "Funciona mal quando o uBlock Origin está ativado", + "message": "Não funciona direito quando o uBlock Origin está ativado", "description": "An entry in the widget used to select the type of issue" }, "supportS6Select1Option6": { @@ -1016,7 +1016,7 @@ "description": "An entry in the widget used to select the type of issue" }, "supportS6Checkbox1": { - "message": "Rotula a página da web como \"NSFW\" (\"Não é Seguro no Trabalho\")", + "message": "Rotular a página da web como \"NSFW\" (\"Não é Seguro no Trabalho\")", "description": "A checkbox to use for NSFW sites" }, "supportRedact": { @@ -1060,15 +1060,15 @@ "description": "Shown in the About pane" }, "aboutCDNs": { - "message": "As próprias listas de filtros do uBO estão hospedadas gratuitamente nos seguintes CDNs: ", + "message": "As próprias listas de filtros do uBO estão hospedadas gratuitamente nas seguintes CDNs: ", "description": "Shown in the About pane" }, "aboutCDNsInfo": { - "message": "Um CDN escolhido aleatoriamente é usado quando uma lista de filtros precisa ser atualizada", + "message": "Uma CDN é escolhida automaticamente quando uma lista de filtros precisa ser atualizada.", "description": "Shown in the About pane" }, "aboutBackupDataButton": { - "message": "Fazer backup no arquivo", + "message": "Fazer backup para arquivo", "description": "Text for button to create a backup of all settings" }, "aboutBackupFilename": { @@ -1092,7 +1092,7 @@ "description": "Message to display when an error occurred during restore" }, "aboutResetDataConfirm": { - "message": "Todas as suas configurações serão removidas, e o uBlock₀ reiniciará.\n\nResetar o uBlock₀ para as configurações de fábrica?", + "message": "Todas as suas configurações serão removidas, e o uBlock₀ reiniciará.\n\nRedefinir o uBlock₀ para as configurações de fábrica?", "description": "Message asking user to confirm reset" }, "errorCantConnectTo": { @@ -1192,7 +1192,7 @@ "description": "Button text to navigate to the blocked page" }, "docblockedRedirectPrompt": { - "message": "A página bloqueada quer redirecionar pra outro site. Se você escolher prosseguir você navegará diretamente para: {{url}}", + "message": "A página bloqueada deseja redirecionar para outro site. Se você escolher continuar, você navegará diretamente para: {{url}}", "description": "Text warning about an incoming redirect" }, "docblockedReasonLabel": { @@ -1256,7 +1256,7 @@ "description": "An entry in the browser's contextual menu" }, "contextMenuSubscribeToList": { - "message": "Subscrever na lista de filtros... ", + "message": "Inscrever-se na lista de filtros...", "description": "An entry in the browser's contextual menu" }, "contextMenuTemporarilyAllowLargeMediaElements": { @@ -1264,7 +1264,7 @@ "description": "A context menu entry, present when large media elements have been blocked on the current site" }, "contextMenuViewSource": { - "message": "Visualizar código fonte…", + "message": "Visualizar código-fonte…", "description": "A context menu entry, to view the source code of the target resource" }, "shortcutCapturePlaceholder": { @@ -1276,7 +1276,7 @@ "description": "Tooltip for the button used to lock scrolling between the views in the 'My rules' pane" }, "genericCopyToClipboard": { - "message": "Copiar pra área de transferência", + "message": "Copiar para a área de transferência", "description": "Label for buttons used to copy something to the clipboard" }, "genericSelectAll": { @@ -1320,7 +1320,7 @@ "description": "Summary of number of errors as reported by the linter " }, "unprocessedRequestTooltip": { - "message": "Não conseguiu filtrar apropriadamente na inicialização do navegador. Recarregue a página pra garantir a filtragem apropriada.", + "message": "Não foi possível filtrar apropriadamente na inicialização do navegador. Recarregue a página para garantir a filtragem apropriada.", "description": "A warning which will appear in the popup panel if needed" }, "dummy": { diff --git a/src/_locales/sq/messages.json b/src/_locales/sq/messages.json index d32036f76986c..2e6af1c4620eb 100644 --- a/src/_locales/sq/messages.json +++ b/src/_locales/sq/messages.json @@ -1196,19 +1196,19 @@ "description": "Text warning about an incoming redirect" }, "docblockedReasonLabel": { - "message": "Reason:", + "message": "Arsye:", "description": "The label which prepend the actual reason why a page was blocked" }, "docblockedReasonMalicious": { - "message": "Malicious", + "message": "Me qëllim të keq", "description": "An actual reason why a page was blocked" }, "docblockedReasonTracker": { - "message": "Tracker", + "message": "Gjurmues", "description": "An actual reason why a page was blocked" }, "docblockedReasonDisreputable": { - "message": "Disreputable", + "message": "I pandershëm", "description": "An actual reason why a page was blocked" }, "cloudPush": { From b398bd848756aa9ee898a7e26defef77429665b2 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sat, 5 Jul 2025 10:59:22 -0400 Subject: [PATCH 1062/1099] [regression] Normalize header names to lowercase Related commit: https://github.com/gorhill/uBlock/commit/408b538e75 --- src/js/static-net-filtering.js | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/js/static-net-filtering.js b/src/js/static-net-filtering.js index 9f68e9993ab8c..6651f08a5aa6c 100644 --- a/src/js/static-net-filtering.js +++ b/src/js/static-net-filtering.js @@ -300,9 +300,8 @@ const $httpHeaders = { }, lookup(name) { if ( this.parsed.size === 0 ) { - for ( let i = 0, n = this.headers.length; i < n; i++ ) { - const { name, value } = this.headers[i]; - this.parsed.set(name, value); + for ( const { name, value } of this.headers ) { + this.parsed.set(name.toLowerCase(), value); } } return this.parsed.get(name); From 7c237e8217fbbb656b999e6fd676b17cca697abc Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sat, 5 Jul 2025 11:01:25 -0400 Subject: [PATCH 1063/1099] New revision for dev build --- dist/version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dist/version b/dist/version index 3dcbe800b96cb..85383defdab8a 100644 --- a/dist/version +++ b/dist/version @@ -1 +1 @@ -1.64.1.9 \ No newline at end of file +1.64.1.10 \ No newline at end of file From cf13c4b4dcc05260cb2fb3f8d645fb3eaaf48dc8 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sat, 5 Jul 2025 11:06:00 -0400 Subject: [PATCH 1064/1099] Make Firefox dev build auto-update --- dist/firefox/updates.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dist/firefox/updates.json b/dist/firefox/updates.json index 7dbecf75028fa..96ceec5b4c2a3 100644 --- a/dist/firefox/updates.json +++ b/dist/firefox/updates.json @@ -3,9 +3,9 @@ "uBlock0@raymondhill.net": { "updates": [ { - "version": "1.64.1.9", + "version": "1.64.1.10", "browser_specific_settings": { "gecko": { "strict_min_version": "92.0" } }, - "update_link": "https://github.com/gorhill/uBlock/releases/download/1.64.1b9/uBlock0_1.64.1b9.firefox.signed.xpi" + "update_link": "https://github.com/gorhill/uBlock/releases/download/1.64.1b10/uBlock0_1.64.1b10.firefox.signed.xpi" } ] } From 0b0294af4f3c68c82fc0af462c5e34888a0f157c Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sun, 6 Jul 2025 16:43:22 -0400 Subject: [PATCH 1065/1099] [mv3] Add support for custom CSS-based cosmetic filters Related issue: https://github.com/uBlockOrigin/uBOL-home/issues/325 This is a first version, with support only for custom filters which are plain CSS selectors. Future versions will extend support to style-based and procedural cosmetic filters. Manually text-editing existing custom filters is currently not supported, this will be added in a future version in the Develop pane. To remove existing custom filters, the "Remove a custom filter" tool can be used. --- platform/mv3/chromium/manifest.json | 18 + .../mv3/extension/_locales/en/messages.json | 36 +- platform/mv3/extension/css/dashboard.css | 1 - platform/mv3/extension/css/picker-ui.css | 146 ++++++ platform/mv3/extension/css/popup.css | 137 +----- .../mv3/extension/css/tool-overlay-ui.css | 99 ++++ platform/mv3/extension/css/unpicker-ui.css | 64 +++ platform/mv3/extension/css/zapper-ui.css | 46 +- platform/mv3/extension/dashboard.html | 1 - platform/mv3/extension/js/background.js | 80 +++- platform/mv3/extension/js/ext-compat.js | 2 +- platform/mv3/extension/js/ext.js | 11 + platform/mv3/extension/js/filter-manager.js | 157 +++++++ platform/mv3/extension/js/picker-ui.js | 431 ++++++++++++++++++ platform/mv3/extension/js/popup.js | 141 ++---- .../mv3/extension/js/scripting-manager.js | 18 +- .../mv3/extension/js/scripting/css-user.js | 39 ++ .../extension/js/scripting/isolated-api.js | 2 +- platform/mv3/extension/js/scripting/picker.js | 270 +++++++++++ .../extension/js/scripting/tool-overlay.js | 360 +++++++++++++++ .../mv3/extension/js/scripting/unpicker.js | 61 +++ platform/mv3/extension/js/scripting/zapper.js | 333 +------------- platform/mv3/extension/js/tool-overlay-ui.js | 241 ++++++++++ platform/mv3/extension/js/unpicker-ui.js | 164 +++++++ platform/mv3/extension/js/zapper-ui.js | 193 ++------ platform/mv3/extension/picker-ui.html | 56 +++ platform/mv3/extension/popup.html | 47 +- platform/mv3/extension/unpicker-ui.html | 40 ++ platform/mv3/extension/zapper-ui.html | 3 +- platform/mv3/firefox/manifest.json | 16 + platform/mv3/safari/manifest.json | 16 + src/css/fonts/Inter/Inter-Regular.woff2 | Bin 100368 -> 111268 bytes src/css/fonts/Inter/Inter-SemiBold.woff2 | Bin 106916 -> 114812 bytes src/css/themes/default.css | 2 +- src/web_accessible_resources/epicker-ui.html | 2 +- tools/make-mv3.sh | 2 +- 36 files changed, 2460 insertions(+), 775 deletions(-) create mode 100644 platform/mv3/extension/css/picker-ui.css create mode 100644 platform/mv3/extension/css/tool-overlay-ui.css create mode 100644 platform/mv3/extension/css/unpicker-ui.css create mode 100644 platform/mv3/extension/js/filter-manager.js create mode 100644 platform/mv3/extension/js/picker-ui.js create mode 100644 platform/mv3/extension/js/scripting/css-user.js create mode 100644 platform/mv3/extension/js/scripting/picker.js create mode 100644 platform/mv3/extension/js/scripting/tool-overlay.js create mode 100644 platform/mv3/extension/js/scripting/unpicker.js create mode 100644 platform/mv3/extension/js/tool-overlay-ui.js create mode 100644 platform/mv3/extension/js/unpicker-ui.js create mode 100644 platform/mv3/extension/picker-ui.html create mode 100644 platform/mv3/extension/unpicker-ui.html diff --git a/platform/mv3/chromium/manifest.json b/platform/mv3/chromium/manifest.json index f42001a9d06fa..2984baabc4c50 100644 --- a/platform/mv3/chromium/manifest.json +++ b/platform/mv3/chromium/manifest.json @@ -65,6 +65,24 @@ "" ], "use_dynamic_url": true + }, + { + "resources": [ + "/picker-ui.html" + ], + "matches": [ + "" + ], + "use_dynamic_url": true + }, + { + "resources": [ + "/unpicker-ui.html" + ], + "matches": [ + "" + ], + "use_dynamic_url": true } ] } diff --git a/platform/mv3/extension/_locales/en/messages.json b/platform/mv3/extension/_locales/en/messages.json index f248475c1e708..02f3939c3be63 100644 --- a/platform/mv3/extension/_locales/en/messages.json +++ b/platform/mv3/extension/_locales/en/messages.json @@ -35,8 +35,12 @@ "message": "filtering mode", "description": "Label in the popup panel for the current filtering mode" }, + "popupLocalToolsLabel": { + "message": "On this website", + "description": "Label in the popup panel got the local tools section" + }, "popupTipReport": { - "message": "Report an issue on this website", + "message": "Report an issue", "description": "Tooltip used for the 'chat' icon in the panel" }, "popupTipDashboard": { @@ -288,13 +292,21 @@ "description": "A button to navigate to the blocked page" }, "zapperTipEnter": { - "message": "Enter element zapper mode", + "message": "Remove an element", "description": "Tooltip for the button used to enter zapper mode" }, "zapperTipQuit": { "message": "Exit element zapper mode", "description": "Tooltip for the button used to exit zapper mode" }, + "pickerTipEnter": { + "message": "Create a custom filter", + "description": "Label for the menu entry to create cosmetic filters" + }, + "unpickerTipEnter": { + "message": "Remove a custom filter", + "description": "Label for the menu entry to delete cosmetic filters" + }, "developDropdownLabel": { "message": "View:", "description": "A label of a dropdown list" @@ -342,5 +354,25 @@ "dnrRulesCountInfo": { "message": "Number of registered rules: {count}", "description": "Short sentence to report the number of currently registered DNR rules" + }, + "pickerSliderLabel": { + "message": "Move the slider to select the best match", + "description": "Label to describe the purpose of the slider" + }, + "pickerPick": { + "message": "Pick", + "description": "Text for the button to re-enter element-picking mode" + }, + "pickerPreview": { + "message": "Preview", + "description": "Text for the button to activate preview mode" + }, + "pickerCreate": { + "message": "Create", + "description": "Text for the button to create the filter" + }, + "unpickerUsage": { + "message": "Select a filter below to highlight matching elements in the webpage. Click the trash can to remove a filter.", + "description": "Summary description on how to use the tool to remove custom filters" } } diff --git a/platform/mv3/extension/css/dashboard.css b/platform/mv3/extension/css/dashboard.css index 84f75d158cc7c..a33c13f426d9a 100644 --- a/platform/mv3/extension/css/dashboard.css +++ b/platform/mv3/extension/css/dashboard.css @@ -58,7 +58,6 @@ body[data-pane="about"] > section[data-pane="about"] { /* high dpi devices */ :root.hidpi .tabButton { - font-family: Metropolis, sans-serif; font-weight: 600; letter-spacing: 0.5px; } diff --git a/platform/mv3/extension/css/picker-ui.css b/platform/mv3/extension/css/picker-ui.css new file mode 100644 index 0000000000000..e21267895a9b0 --- /dev/null +++ b/platform/mv3/extension/css/picker-ui.css @@ -0,0 +1,146 @@ +:root#ubol-picker { + --ubol-overlay-fill: rgba(255,64,64,0.10); + --ubol-overlay-border: #F00; +} + +#ubol-picker.paused svg#overlay { + cursor: not-allowed; +} + +:root aside { + background-color: var(--surface-1); + border: 1px solid var(--border-2); + max-height: 40vh; + max-width: min(32rem, 100vw - 4px); + min-width: min(24rem, 100vw - 4px); + row-gap: 1em; + width: min(32rem, 100vw - 4px); +} + +#ubol-picker aside > section:last-of-type { + margin-block-end: 0; +} +#ubol-picker aside > section:not(#windowbar,#moreOrLess) { + padding: 0 4px; +} + +#ubol-picker[data-view="0"] aside section[data-view="1"], +#ubol-picker[data-view="0"] aside section[data-view="2"] { + display: none; +} +#ubol-picker[data-view="1"] aside section[data-view="2"] { + display: none; +} + +#ubol-picker:not(.paused) aside > section:not(#windowbar) { + display: none; +} + +#ubol-picker textarea { + border: 0; + box-sizing: border-box; + height: 6em; + min-height: 3em; + resize: none; + width: 100%; +} +#ubol-picker.mobile textarea { + height: unset; +} +#ubol-picker .resultsetWidgets { + color: var(--ink-2); + display: flex; + flex-direction: column; + font-size: smaller; + gap: 0.25em; +} +#ubol-picker .resultsetWidgets > span:first-of-type { + display: flex; + margin: 0 1em; +} +#ubol-picker .resultsetWidgets label { + flex-grow: 1; +} +#ubol-picker .resultsetWidgets #resultsetCount { + display: inline-block; + text-align: right; + width: 8ch; +} + +#ubol-picker #toolbar { + display: flex; + justify-content: space-between; +} +#ubol-picker #toolbar button { + min-width: 5em; +} + +#ubol-picker #candidateFilters { + font-family: monospace; + font-size: small; + min-height: 6em; + overflow-y: auto; + word-break: break-all; +} +#ubol-picker #candidateFilters ul { + margin: 0; + padding-inline-start: calc(2ch + 4px); + user-select: none; + -webkit-user-select: none; +} +#ubol-picker #candidateFilters ul > li { + list-style-type: '\25A0\00A0'; +} +#ubol-picker #candidateFilters ul >li:has(:not(span.on)) { + list-style-type: '\25A1\00A0'; +} +#ubol-picker #candidateFilters ul > li:nth-of-type(2n+1) { + background-color: var(--surface-2); +} +#ubol-picker #candidateFilters ul > li > span { + border: 1px solid transparent; + padding: 1px 2px; +} +#ubol-picker #candidateFilters ul > li > span.on { + background-color: var(--ink-1); + color: var(--surface-1); +} +#ubol-picker #candidateFilters ul > li > span:hover { + border: 1px solid var(--ink-1); +} + +#ubol-picker #moreOrLess { + color: var(--ink-2); + column-gap: 0; + display: grid; + font-size: small; + grid-template: auto / 1fr 1fr; + justify-items: stretch; + user-select: none; + -webkit-user-select: none; + white-space: nowrap; + } +#ubol-picker #moreOrLess > span { + cursor: pointer; + padding: var(--default-gap-xxsmall) var(--default-gap-xsmall); + } +#ubol-picker #moreOrLess > span:last-of-type { + text-align: end; + } +#ubol-picker[data-view="2"] aside #moreOrLess > span:first-of-type { + visibility: hidden; +} +#ubol-picker[data-view="0"] aside #moreOrLess > span:last-of-type { + visibility: hidden; +} +#ubol-picker.desktop aside #moreOrLess > span:hover { + background-color: var(--surface-2); +} + +#ubol-picker.preview #toolbar #preview { + color: var(--accent-ink-1); + background-color: var(--accent-surface-1); +} +#ubol-picker.preview #overlay path { + display: none; +} diff --git a/platform/mv3/extension/css/popup.css b/platform/mv3/extension/css/popup.css index 411ba3aa40889..d7d57e14cc652 100644 --- a/platform/mv3/extension/css/popup.css +++ b/platform/mv3/extension/css/popup.css @@ -21,7 +21,7 @@ min-width: var(--popup-min-width); } :root body.loading { - opacity: 0; + visibility: hidden; } a { color: var(--ink-1); @@ -36,16 +36,12 @@ a { align-self: flex-start; display: flex; flex-direction: column; - max-width: 340px; } :root.portrait #main { align-self: inherit; } hr { - border: 0; - border-top: 1px solid var(--hr-ink); - margin: 0; - padding: 0; + margin: 0.5em 0; } #hostname { @@ -69,8 +65,9 @@ body[data-forbid~="filteringMode"] .filteringModeSlider { pointer-events: none; } body[data-forbid~="dashboard"] #gotoDashboard, -body[data-forbid~="zapper"] #gotoZapper { - visibility: hidden; +body[data-forbid~="zapper"] #gotoZapper, +body[data-forbid~="picker"] #gotoPicker { + display: none; } #filteringModeText { @@ -131,102 +128,46 @@ body.needReload #refresh { visibility: visible; } -#rulesetStats { - padding: 0 var(--popup-gap-thin); - } -#rulesetStats .rulesetDetails h1 { - font-size: 1em; - font-weight: normal; - margin: 0.5em 0 0.25em 0; - } -#rulesetStats .rulesetDetails p { - color: var(--ink-2); - font-size: var(--font-size-smaller); - margin: 0.25em 0 0.5em 0.5em; - } - -.itemRibbon { - column-gap: var(--popup-gap); - display: grid; - grid-auto-columns: 1fr; - grid-auto-flow: column; - grid-template: auto / 1fr 1fr; - margin: var(--popup-gap); +.toolRibbon { + display: flex; + flex-direction: column; + font-size: calc(var(--font-size) - 1px); + line-height: calc(var(--font-size) * 2.2); + white-space: nowrap; } -.itemRibbon > span + span { - text-align: end; +:root:not(.isHTTP) .needHTTP { + display: none; } -.toolRibbon { - align-items: start; - background-color: var(--popup-toolbar-surface); - display: grid; - grid-auto-columns: 1fr; - grid-auto-flow: column; - grid-template: auto / repeat(4, 1fr); - justify-items: stretch; - white-space: normal; +.toolRibbon > label { + font-size: small; + color: var(--ink-2); + line-height: calc(var(--font-size) * 1.8); + margin-inline-start: 0.5em; } .toolRibbon .tool { cursor: pointer; display: flex; - flex-direction: column; - font-size: 1.4em; - min-width: 32px; - padding: var(--popup-gap) - var(--popup-gap-extra-thin); unicode-bidi: embed; - visibility: hidden; } .toolRibbon .tool:hover { color: var(--ink-1); fill: var(--ink-1); } -.toolRibbon .tool.enabled { - visibility: visible; +.toolRibbon .tool .fa-icon { + font-size: 1.4em; + min-width: 1.4em; } -.toolRibbon .tool .caption { - color: var(--ink-2); - font: 10px/12px sans-serif; - margin-top: 6px; - text-align: center; +.toolRibbon .tool:not(.enabled) { + display: none; } body.mobile.no-tooltips .toolRibbon .tool { font-size: 1.6em; } .toolRibbon.genericTools { - margin-bottom: 0; + margin-bottom: 0; } -#moreOrLess { - column-gap: 0; - display: grid; - grid-template: auto / 1fr 1fr; - justify-items: stretch; - margin: 1px 0 0 0; - } -#moreOrLess > span { - cursor: pointer; - margin: 0; - padding: var(--popup-gap-thin) var(--popup-gap); - user-select: none; - white-space: nowrap; - } -#moreButton .fa-icon { - transform: rotate(180deg); - } -#lessButton { - border-inline-start: 1px solid var(--surface-1); - text-align: end; - } -body[data-section="a b"] #moreButton { - pointer-events: none; - visibility: hidden; - } -body[data-section=""] #lessButton { - pointer-events: none; - visibility: hidden; - } body:not([data-section~="a"]) [data-section="a"] { display: none; } @@ -234,25 +175,6 @@ body:not([data-section~="b"]) [data-section="b"] { display: none; } -/* configurable UI elements */ -:root:not(.mobile) .toolRibbon .caption, -:root.mobile body.no-tooltips .toolRibbon .caption, -:root.mobile body[data-ui~="-captions"] .toolRibbon .caption { - display: none; - } -:root.mobile .toolRibbon .caption, -:root:not(.mobile) body[data-ui~="+captions"] .toolRibbon .caption { - display: inherit; - } -:root:not(.mobile) .toolRibbon .tool, -:root.mobile body.no-tooltips .toolRibbon .tool, -:root.mobile body[data-ui~="-captions"] .toolRibbon .tool { - padding: var(--popup-gap) var(--popup-gap-thin); - } -:root.mobile #moreOrLess > span { - padding: var(--popup-gap); - } - /* horizontally-constrained viewport */ :root.portrait body { overflow-y: auto; @@ -265,9 +187,6 @@ body:not([data-section~="b"]) [data-section="b"] { :root.desktop { display: flex; } -:root.desktop body { - --popup-gap: calc(var(--font-size) * 0.875); - } :root.desktop .rulesetTools [id]:hover { background-color: var(--popup-ruleset-tool-surface-hover); } @@ -277,11 +196,3 @@ body:not([data-section~="b"]) [data-section="b"] { :root.desktop .tool:hover { background-color: var(--popup-toolbar-surface-hover); } -:root.desktop #moreOrLess > span:hover { - background-color: var(--surface-2); - /* background-color: var(--popup-toolbar-surface-hover); */ - } - -#templates { - display: none; - } diff --git a/platform/mv3/extension/css/tool-overlay-ui.css b/platform/mv3/extension/css/tool-overlay-ui.css new file mode 100644 index 0000000000000..e049502388fe5 --- /dev/null +++ b/platform/mv3/extension/css/tool-overlay-ui.css @@ -0,0 +1,99 @@ +:root { + --ubol-overlay-fill: rgba(255,255,255,0.2); + --ubol-overlay-border: #FFF; + --ubol-overlay-canvas: rgba(128,128,128,0.3); +} +:root.dark aside { + color-scheme: dark; +} + +:root, +:root > body { + background: transparent; + height: 100vh; + height: 100svh; + margin: 0; + overflow: hidden; + width: 100vw; +} +:root > body.loading { + visibility: hidden; +} + +:root :focus { + outline: none; +} + +svg#overlay { + cursor: crosshair; + box-sizing: border-box; + height: 100%; + left: 0; + position: absolute; + top: 0; + width: 100%; +} +svg#overlay > path:first-child { + fill: var(--ubol-overlay-canvas); + fill-rule: evenodd; +} +svg#overlay > path + path { + stroke: var(--ubol-overlay-border); + stroke-width: 1.5px; + fill: var(--ubol-overlay-fill); +} + +:root aside { + box-sizing: border-box; + cursor: default; + display: flex; + flex-direction: column; + position: fixed; + z-index: 100; +} +:root body[dir="rtl"] aside { + left: 2px; + right: unset; +} + +:root.minimized aside > section:not(#windowbar) { + display: none !important; +} +:root.minimized aside { + min-width: min(12em, 100vw - 4px); + width: min(12em, 100vw - 4px); +} + +:root aside #windowbar { + border-bottom: 1px solid var(--border-1); + display: flex; +} +:root aside #windowbar > div { + fill: none; + height: 2em; + stroke: var(--ink-1); + stroke-width: 2px; + width: 2em; +} +:root.minimized aside #windowbar > div { + height: 3em; + width: 3em; +} +:root.minimized aside #windowbar > #minimize svg > path, +#windowbar #minimize svg > rect { + display: none; +} +:root.minimized aside #windowbar > #minimize svg > rect { + display: initial; +} +:root #windowbar > #move { + align-items: center; + background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAYAAAAECAYAAACtBE5DAAAAFElEQVQI12NgwAfKy8v/M5ANYLoBshgEyQo6H9UAAAAASUVORK5CYII='); + cursor: grab; + display: flex; + justify-content: center; + flex-grow: 1; +} +:root #windowbar > div:hover { + background-color: var(--surface-2) +} diff --git a/platform/mv3/extension/css/unpicker-ui.css b/platform/mv3/extension/css/unpicker-ui.css new file mode 100644 index 0000000000000..6762abdd64fab --- /dev/null +++ b/platform/mv3/extension/css/unpicker-ui.css @@ -0,0 +1,64 @@ +:root#ubol-unpicker { + --ubol-overlay-fill: rgba(64,255,64,0.10); + --ubol-overlay-border: #0F0; +} + +#ubol-unpicker svg#overlay { + cursor: not-allowed; +} + +:root aside { + background-color: var(--surface-1); + border: 1px solid var(--border-2); + max-height: 40vh; + max-width: min(32rem, 100vw - 4px); + min-width: min(24rem, 100vw - 4px); + width: min(32rem, 100vw - 4px); +} + +#ubol-unpicker aside > section:not(#windowbar) { + margin: 1em 1em 0 1em; +} +#ubol-unpicker aside > section:not(#windowbar):last-of-type { + margin-bottom: 1em; +} +#ubol-unpicker aside > section [data-i18n="unpickerUsage"] { + color: var(--ink-2); + font-size: small; +} + +#ubol-unpicker #customFilters { + font-family: monospace; +} +#ubol-unpicker .customFilter { + display: flex; +} +#ubol-unpicker .customFilter:nth-of-type(2n+1) { + background-color: var(--surface-2); +} +#ubol-unpicker .customFilter > span.selector { + flex-grow: 1; + font-size: small; + padding: 0.5em; +} +#ubol-unpicker .customFilter.on > span.selector { + background-color: var(--ink-1); + color: var(--surface-1); +} +#ubol-unpicker .customFilter.removed > span.selector { + text-decoration-line: line-through; +} +#ubol-unpicker .customFilter.removed > span.remove { + display: none; +} +#ubol-unpicker .customFilter.removed span.selector { + pointer-events: none; +} +#ubol-unpicker .customFilter > span.fa-icon { + flex-shrink: 0; + font-size: 1.25em; + padding: 0 0.5em; +} +#ubol-unpicker .customFilter:not(.removed) > span.undo { + display: none; +} diff --git a/platform/mv3/extension/css/zapper-ui.css b/platform/mv3/extension/css/zapper-ui.css index 5f66d49a5ee04..93c3227169a7c 100644 --- a/platform/mv3/extension/css/zapper-ui.css +++ b/platform/mv3/extension/css/zapper-ui.css @@ -1,54 +1,16 @@ -:root { +:root#ubol-zapper { --quit-button-size: max(4em, min(6em, calc(100vw / 8), calc(100vh / 8))); + --ubol-overlay-fill: rgba(255,255,63,0.10); + --ubol-overlay-border: #FF0; } -html#ubol-zapper, -#ubol-zapper body { - background: transparent; - cursor: not-allowed; - height: 100vh; - height: 100svh; - margin: 0; - overflow: hidden; - width: 100vw; -} -#ubol-zapper :focus { - outline: none; -} #ubol-zapper aside { - box-sizing: border-box; - cursor: default; - display: flex; - flex-direction: column; gap: 2px; - position: fixed; right: 2px; top: 50%; transform: translateY(-50%); - z-index: 100; -} -#ubol-zapper body[dir="rtl"] aside { - left: 2px; - right: unset; -} -#ubol-zapper svg#sea { - cursor: crosshair; - box-sizing: border-box; - height: 100%; - left: 0; - position: absolute; - top: 0; - width: 100%; -} -#ubol-zapper svg#sea > path:first-child { - fill: rgba(0,0,0,0.5); - fill-rule: evenodd; -} -#ubol-zapper svg#sea > path + path { - stroke: #FF0; - stroke-width: 0.5px; - fill: rgba(255,255,63,0.20); } + #ubol-zapper aside > div { background-color: var(--surface-1); border: 1px solid rgba(0,0,0,0.5); diff --git a/platform/mv3/extension/dashboard.html b/platform/mv3/extension/dashboard.html index bc71b3398a489..d45605c657e69 100644 --- a/platform/mv3/extension/dashboard.html +++ b/platform/mv3/extension/dashboard.html @@ -133,7 +133,6 @@

- diff --git a/platform/mv3/extension/js/background.js b/platform/mv3/extension/js/background.js index ffeb0314f4878..57e25b2ec5e8a 100644 --- a/platform/mv3/extension/js/background.js +++ b/platform/mv3/extension/js/background.js @@ -31,6 +31,15 @@ import { syncWithBrowserPermissions, } from './mode-manager.js'; +import { + addCustomFilter, + hasCustomFilters, + injectCustomFilters, + removeCustomFilter, + selectorsFromCustomFilters, + uninjectCustomFilters, +} from './filter-manager.js'; + import { adminReadEx, getAdminRulesets, @@ -56,7 +65,6 @@ import { getEffectiveDynamicRules, getEffectiveSessionRules, getEffectiveUserRules, - getEnabledRulesetsDetails, getRulesetDetails, patchDefaultRulesets, setStrictBlockMode, @@ -153,14 +161,15 @@ function setDeveloperMode(state) { function onMessage(request, sender, callback) { + const tabId = sender?.tab?.id ?? false; + const frameId = tabId && (sender?.frameId ?? false); + // Does not require trusted origin. switch ( request.what ) { case 'insertCSS': { - const tabId = sender?.tab?.id ?? false; - const frameId = sender?.frameId ?? false; - if ( tabId === false || frameId === false ) { return; } + if ( frameId === false ) { return false; } browser.scripting.insertCSS({ css: request.css, origin: 'USER', @@ -172,9 +181,7 @@ function onMessage(request, sender, callback) { } case 'removeCSS': { - const tabId = sender?.tab?.id ?? false; - const frameId = sender?.frameId ?? false; - if ( tabId === false || frameId === false ) { return; } + if ( frameId === false ) { return false; } browser.scripting.removeCSS({ css: request.css, origin: 'USER', @@ -186,13 +193,26 @@ function onMessage(request, sender, callback) { } case 'toggleToolbarIcon': { - const tabId = sender?.tab?.id ?? false; if ( tabId ) { toggleToolbarIcon(tabId); } return false; } + case 'injectCustomFilters': + if ( frameId === false ) { return false; } + injectCustomFilters(tabId, frameId, request.hostname).then(selectors => { + callback(selectors); + }); + return true; + + case 'uninjectCustomFilters': + if ( frameId === false ) { return false; } + uninjectCustomFilters(tabId, frameId, request.hostname).then(( ) => { + callback(); + }); + return true; + default: break; } @@ -301,24 +321,18 @@ function onMessage(request, sender, callback) { case 'popupPanelData': { Promise.all([ hasBroadHostPermissions(), - getFilteringMode(request.hostname), - getEnabledRulesetsDetails(), + getFilteringMode(request.normalHostname), adminReadEx('disabledFeatures'), + hasCustomFilters(request.hostname), ]).then(results => { - const [ - hasOmnipotence, - level, - rulesetDetails, - disabledFeatures, - ] = results; callback({ - hasOmnipotence, - level, + hasOmnipotence: results[0], + level: results[1], autoReload: rulesetConfig.autoReload, - rulesetDetails, isSideloaded, developerMode: rulesetConfig.developerMode, - disabledFeatures, + disabledFeatures: results[2], + hasCustomFilters: results[3], }); }); return true; @@ -432,6 +446,30 @@ function onMessage(request, sender, callback) { }); return true; + case 'addCustomFilter': + addCustomFilter(request.hostname, request.selector).then(modified => { + if ( modified !== true ) { return; } + return registerInjectables(); + }).then(( ) => { + callback(); + }) + return true; + + case 'removeCustomFilter': + removeCustomFilter(request.hostname, request.selector).then(modified => { + if ( modified !== true ) { return; } + return registerInjectables(); + }).then(( ) => { + callback(); + }); + return true; + + case 'selectorsFromCustomFilters': + selectorsFromCustomFilters(request.hostname).then(selectors => { + callback(selectors); + }); + return true; + default: break; } @@ -446,7 +484,7 @@ function onCommand(command, tab) { case 'enter-zapper-mode': { if ( browser.scripting === undefined ) { return; } browser.scripting.executeScript({ - files: [ '/js/scripting/zapper.js' ], + files: [ '/js/scripting/tool-overlay.js', '/js/scripting/zapper.js' ], target: { tabId: tab.id }, }); break; diff --git a/platform/mv3/extension/js/ext-compat.js b/platform/mv3/extension/js/ext-compat.js index 06f0c8c91c992..0d431c4720257 100644 --- a/platform/mv3/extension/js/ext-compat.js +++ b/platform/mv3/extension/js/ext-compat.js @@ -20,7 +20,7 @@ */ export const webext = self.browser || self.chrome; -export const dnr = webext.declarativeNetRequest; +export const dnr = webext.declarativeNetRequest || {}; export const INITIATOR_DOMAINS = 'initiatorDomains'; export const EXCLUDED_INITIATOR_DOMAINS = 'excludedInitiatorDomains'; diff --git a/platform/mv3/extension/js/ext.js b/platform/mv3/extension/js/ext.js index a2e6f18f500a4..ea0f90425795a 100644 --- a/platform/mv3/extension/js/ext.js +++ b/platform/mv3/extension/js/ext.js @@ -63,6 +63,17 @@ export async function localRemove(key) { return browser.storage.local.remove(key); } +export async function localKeys() { + if ( browser.storage instanceof Object === false ) { return; } + if ( browser.storage.local instanceof Object === false ) { return; } + if ( browser.storage.local.getKeys ) { + return browser.storage.local.getKeys(); + } + const bin = await browser.storage.local.get(null); + if ( bin instanceof Object === false ) { return; } + return Object.keys(bin); +} + /******************************************************************************/ export async function sessionRead(key) { diff --git a/platform/mv3/extension/js/filter-manager.js b/platform/mv3/extension/js/filter-manager.js new file mode 100644 index 0000000000000..282c972438cdb --- /dev/null +++ b/platform/mv3/extension/js/filter-manager.js @@ -0,0 +1,157 @@ +/******************************************************************************* + + uBlock Origin Lite - a comprehensive, MV3-compliant content blocker + Copyright (C) 2022-present Raymond Hill + + 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/}. + + Home: https://github.com/gorhill/uBlock +*/ + +import { + browser, + localKeys, + localRead, + localRemove, + localWrite, +} from './ext.js'; + +import { + intersectHostnameIters, + matchesFromHostnames, + strArrayEq, + subtractHostnameIters, +} from './utils.js'; + +/******************************************************************************/ + +export async function selectorsFromCustomFilters(hostname) { + const promises = []; + let hn = hostname; + while ( hn !== '' ) { + promises.push(localRead(`site.${hn}`)); + const pos = hn.indexOf('.'); + if ( pos === -1 ) { break; } + hn = hn.slice(pos + 1); + } + const results = await Promise.all(promises); + const out = []; + for ( let i = 0; i < promises.length; i++ ) { + const selectors = results[i]; + if ( selectors === undefined ) { continue; } + selectors.forEach(selector => { out.push(selector.slice(1)); }); + } + return out.sort(); +} + +/******************************************************************************/ + +export async function hasCustomFilters(hostname) { + const selectors = await selectorsFromCustomFilters(hostname); + return selectors?.length ?? 0; +} + +/******************************************************************************/ + +export async function injectCustomFilters(tabId, frameId, hostname) { + const selectors = await selectorsFromCustomFilters(hostname); + if ( selectors.length === 0 ) { return; } + await browser.scripting.insertCSS({ + css: `${selectors.join(',\n')}{display:none!important;}`, + origin: 'USER', + target: { tabId, frameIds: [ frameId ] }, + }).catch(reason => { + console.log(reason); + }); + return selectors; +} + +/******************************************************************************/ + +export async function uninjectCustomFilters(tabId, frameId, hostname) { + const selectors = await selectorsFromCustomFilters(hostname); + if ( selectors.length === 0 ) { return; } + return browser.scripting.removeCSS({ + css: `${selectors.join(',\n')}{display:none!important;}`, + origin: 'USER', + target: { tabId, frameIds: [ frameId ] }, + }).catch(reason => { + console.log(reason); + }); +} + +/******************************************************************************/ + +export async function registerCustomFilters(context) { + const storageKeys = await localKeys() || []; + const siteKeys = storageKeys.filter(a => a.startsWith('site.')); + if ( siteKeys.length === 0 ) { return; } + + const { none } = context.filteringModeDetails; + let hostnames = siteKeys.map(a => a.slice(5)); + if ( none.has('all-urls') ) { + const { basic, optimal, complete } = context.filteringModeDetails; + hostnames = intersectHostnameIters(hostnames, [ + ...basic, ...optimal, ...complete + ]); + } else if ( none.size !== 0 ) { + hostnames = [ ...subtractHostnameIters(hostnames, none) ]; + } + if ( hostnames.length === 0 ) { return; } + + const registered = context.before.get('css-user'); + context.before.delete('css-user'); // Important! + + const directive = { + id: 'css-user', + js: [ '/js/scripting/css-user.js' ], + matches: matchesFromHostnames(hostnames), + runAt: 'document_start', + }; + + if ( registered === undefined ) { + context.toAdd.push(directive); + } else if ( strArrayEq(registered.matches, directive.matches) === false ) { + context.toRemove.push('css-user'); + context.toAdd.push(directive); + } +} + +/******************************************************************************/ + +export async function addCustomFilter(hostname, selector) { + const key = `site.${hostname}`; + const selectors = await localRead(key) || []; + const filter = `0${selector}`; + if ( selectors.includes(filter) ) { return false; } + selectors.push(filter); + selectors.sort(); + await localWrite(key, selectors); + return true; +} + +/******************************************************************************/ + +export async function removeCustomFilter(hostname, selector) { + const key = `site.${hostname}`; + const selectors = await localRead(key); + if ( selectors === undefined ) { return false; } + const i = selectors.indexOf(`0${selector}`); + if ( i === -1 ) { return false; } + selectors.splice(i, 1); + await selectors.length !== 0 + ? localWrite(key, selectors) + : localRemove(key); + return true; +} diff --git a/platform/mv3/extension/js/picker-ui.js b/platform/mv3/extension/js/picker-ui.js new file mode 100644 index 0000000000000..abaf3c60f6c8a --- /dev/null +++ b/platform/mv3/extension/js/picker-ui.js @@ -0,0 +1,431 @@ +/******************************************************************************* + + uBlock Origin - a comprehensive, efficient content blocker + Copyright (C) 2025-present Raymond Hill + + 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/}. + + Home: https://github.com/gorhill/uBlock +*/ + +import { dom, qs$, qsa$ } from './dom.js'; +import { localRead, localWrite } from './ext.js'; +import { toolOverlay } from './tool-overlay-ui.js'; + +/******************************************************************************/ + +let selectorPartsDB = new Map(); +let sliderParts = []; +let previewCSS = ''; + +/******************************************************************************/ + +function isValidSelector(selector) { + isValidSelector.error = undefined; + if ( selector === '' ) { return false; } + try { + void document.querySelector(`${selector},a`); + } catch (reason) { + isValidSelector.error = reason; + return false; + } + return true; +} + +/******************************************************************************/ + +function onSvgTouch(ev) { + if ( ev.type === 'touchstart' ) { + onSvgTouch.x0 = ev.touches[0].screenX; + onSvgTouch.y0 = ev.touches[0].screenY; + onSvgTouch.t0 = ev.timeStamp; + return; + } + if ( onSvgTouch.x0 === undefined ) { return; } + const stopX = ev.changedTouches[0].screenX; + const stopY = ev.changedTouches[0].screenY; + const distance = Math.sqrt( + Math.pow(stopX - onSvgTouch.x0, 2) + + Math.pow(stopY - onSvgTouch.y0, 2) + ); + // Interpret touch events as a tap if: + // - Swipe is not valid; and + // - The time between start and stop was less than 200ms. + const duration = ev.timeStamp - onSvgTouch.t0; + if ( distance >= 32 || duration >= 200 ) { return; } + onSvgClicked({ + type: 'touch', + target: ev.target, + clientX: ev.changedTouches[0].pageX, + clientY: ev.changedTouches[0].pageY, + }); + ev.preventDefault(); +} +onSvgTouch.x0 = onSvgTouch.y0 = 0; +onSvgTouch.t0 = 0; + +/******************************************************************************/ + +function onSvgClicked(ev) { + // Unpause picker if: + // - click outside dialog AND + // - not in preview mode + if ( dom.cl.has(dom.root, 'paused') ) { + if ( dom.cl.has(dom.root, 'preview') ) { + updatePreview(false); + } + unpausePicker(); + return; + } + // Force dialog to always be visible when using a touch-driven device. + if ( ev.type === 'touch' ) { + dom.cl.add(dom.root, 'show'); + } + toolOverlay.postMessage({ + what: 'candidatesAtPoint', + mx: ev.clientX, + my: ev.clientY, + broad: ev.ctrlKey, + }).then(details => { + showDialog(details); + }); +} + +/******************************************************************************/ + +function onKeyPressed(ev) { + if ( ev.key === 'Escape' || ev.which === 27 ) { + quitPicker(); + return; + } +} + +/******************************************************************************/ + +function onMinimizeClicked() { + if ( dom.cl.has(dom.root, 'paused') === false ) { + pausePicker(); + highlightCandidate(); + return; + } + dom.cl.toggle(dom.root, 'minimized'); +} + +/******************************************************************************/ + +function onFilterTextChanged() { + highlightCandidate(); +} + +/******************************************************************************/ + +function toggleView(view, persist = false) { + dom.root.dataset.view = `${view}`; + if ( persist !== true ) { return; } + localWrite('picker.view', dom.root.dataset.view); +} + +function onViewToggled(dir) { + let view = parseInt(dom.root.dataset.view, 10); + view += dir; + if ( view < 0 ) { view = 0; } + if ( view > 2 ) { view = 2; } + toggleView(view, true); +} + +/******************************************************************************/ + +function selectorFromCandidates() { + const selectorParts = []; + let liPrevious = null; + for ( const li of qsa$('#candidateFilters li') ) { + const selector = []; + for ( const span of qsa$(li, '.on[data-part]') ) { + selector.push(span.textContent); + } + if ( selector.length !== 0 ) { + if ( liPrevious !== null ) { + if ( li.previousElementSibling === liPrevious ) { + selectorParts.unshift(' > '); + } else if ( liPrevious !== li ) { + selectorParts.unshift(' '); + } + } + liPrevious = li; + selectorParts.unshift(selector.join('')); + } + } + return selectorParts.join(''); +} + +/******************************************************************************/ + +function onSliderChanged(ev) { + updateSlider(ev.target.valueAsNumber); +} + +function updateSlider(i) { + qs$('#slider').value = i; + dom.cl.remove('#candidateFilters [data-part]', 'on'); + const parts = sliderParts[i]; + for ( const address of parts ) { + dom.cl.add(`#candidateFilters [data-part="${address}"]`, 'on'); + } + const selector = selectorFromCandidates(); + qs$('textarea').value = selector; + highlightCandidate(); +} + +/******************************************************************************/ + +function updateElementCount(details) { + const { count, error } = details; + const span = qs$('#resultsetCount'); + if ( error ) { + span.textContent = 'Error'; + span.setAttribute('title', error); + } else { + span.textContent = count; + span.removeAttribute('title'); + } + const disabled = Boolean(count) === false ? '' : null; + dom.attr('#create', 'disabled', disabled); + updatePreview(); +} + +/******************************************************************************/ + +function onPreviewClicked() { + dom.cl.toggle(dom.root, 'preview'); + updatePreview(); +} + +function updatePreview(state) { + if ( state === undefined ) { + state = dom.cl.has(dom.root, 'preview'); + } else { + dom.cl.toggle(dom.root, 'preview', state) + } + if ( previewCSS !== '' ) { + toolOverlay.postMessage({ what: 'removeCSS', css: previewCSS }); + previewCSS = ''; + } + if ( state === false ) { return; } + const selector = qs$('textarea').value; + if ( isValidSelector(selector) === false ) { return; } + previewCSS = `${selector}{display:none!important;}`; + toolOverlay.postMessage({ what: 'insertCSS', css: previewCSS }); +} + +/******************************************************************************/ + +async function onCreateClicked() { + const selector = qs$('textarea').value; + if ( isValidSelector(selector) === false ) { return; } + await toolOverlay.postMessage({ what: 'uninjectCustomFilters' }).then(( ) => + toolOverlay.sendMessage({ + what: 'addCustomFilter', + hostname: toolOverlay.url.hostname, + selector, + }) + ).then(( ) => + toolOverlay.postMessage({ what: 'injectCustomFilters' }) + ); + qs$('textarea').value = ''; + dom.cl.remove(dom.root, 'preview'); + quitPicker(); +} + +/******************************************************************************/ + +function attributeNameFromSelector(part) { + const pos = part.search(/\^?=/); + return part.slice(1, pos); +} + +/******************************************************************************/ + +function onCandidateClicked(ev) { + const target = ev.target; + if ( target.matches('[data-part]') ) { + const address = target.dataset.part; + const part = selectorPartsDB.get(parseInt(address, 10)); + if ( part.startsWith('[') ) { + if ( target.textContent === part ) { + target.textContent = `[${attributeNameFromSelector(part)}]`; + dom.cl.remove(target, 'on'); + } else if ( dom.cl.has(target, 'on') ) { + target.textContent = part; + } else { + dom.cl.add(target, 'on'); + } + } else { + dom.cl.toggle(target, 'on'); + } + } else if ( target.matches('li') ) { + if ( qs$(target, ':scope > span:not(.on)') !== null ) { + dom.cl.add(qsa$(target, ':scope > [data-part]:not(.on)'), 'on'); + } else { + dom.cl.remove(qsa$(target, ':scope > [data-part]'), 'on'); + } + } + const selector = selectorFromCandidates(); + qs$('textarea').value = selector; + highlightCandidate(); +} + +/******************************************************************************/ + +function showDialog(msg) { + pausePicker(); + + /* global */selectorPartsDB = new Map(msg.partsDB); + const { listParts } = msg; + const root = qs$('#candidateFilters'); + const ul = qs$(root, 'ul'); + while ( ul.firstChild !== null ) { + ul.firstChild.remove(); + } + for ( const parts of listParts ) { + const li = document.createElement('li'); + for ( const address of parts ) { + const span = document.createElement('span'); + const part = selectorPartsDB.get(address); + span.dataset.part = address; + if ( part.startsWith('[') ) { + span.textContent = `[${attributeNameFromSelector(part)}]`; + } else { + span.textContent = part; + } + li.append(span); + } + ul.appendChild(li); + } + + /* global */sliderParts = msg.sliderParts; + const slider = qs$('#slider'); + const last = sliderParts.length - 1; + dom.attr(slider, 'max', last); + dom.attr(slider, 'value', last); + dom.attr(slider, 'disabled', last !== 0 ? null : ''); + updateSlider(last); +} + +/******************************************************************************/ + +function highlightCandidate() { + const selector = qs$('textarea').value; + if ( isValidSelector(selector) === false ) { + toolOverlay.postMessage({ what: 'unhighlight' }); + updateElementCount({ count: 0, error: isValidSelector.error }); + return; + } + toolOverlay.postMessage({ + what: 'highlightFromSelector', + selector, + }).then(result => { + updateElementCount(result); + }); +} + +/******************************************************************************* + * + * paused: + * - select element mode disabled + * - preview mode enabled or disabled + * - dialog unminimized + * + * unpaused: + * - select element mode enabled + * - preview mode disabled + * - dialog minimized + * + * */ + +function pausePicker() { + dom.cl.add(dom.root, 'paused'); + dom.cl.remove(dom.root, 'minimized'); + toolOverlay.highlightElementUnderMouse(false); +} + +function unpausePicker() { + dom.cl.remove(dom.root, 'paused', 'preview'); + dom.cl.add(dom.root, 'minimized'); + updatePreview(); + toolOverlay.postMessage({ + what: 'togglePreview', + state: false, + }); + toolOverlay.highlightElementUnderMouse(true); +} + +/******************************************************************************/ + +function startPicker() { + toolOverlay.postMessage({ what: 'startTool' }); + + localRead('picker.view').then(value => { + if ( Boolean(value) === false ) { return; } + toggleView(value); + }); + + self.addEventListener('keydown', onKeyPressed, true); + dom.on('svg#overlay', 'click', onSvgClicked); + dom.on('svg#overlay', 'touchstart', onSvgTouch, { passive: true }); + dom.on('svg#overlay', 'touchend', onSvgTouch); + dom.on('#minimize', 'click', onMinimizeClicked); + dom.on('textarea', 'input', onFilterTextChanged); + dom.on('#quit', 'click', quitPicker); + dom.on('#slider', 'input', onSliderChanged); + dom.on('#pick', 'click', resetPicker); + dom.on('#preview', 'click', onPreviewClicked); + dom.on('#moreOrLess > span:first-of-type', 'click', ( ) => { onViewToggled(1); }); + dom.on('#moreOrLess > span:last-of-type', 'click', ( ) => { onViewToggled(-1); }); + dom.on('#create', 'click', ( ) => { onCreateClicked(); }); + dom.on('#candidateFilters ul', 'click', onCandidateClicked); + toolOverlay.highlightElementUnderMouse(true); +} + +/******************************************************************************/ + +function quitPicker() { + updatePreview(false); + toolOverlay.stop(); +} + +/******************************************************************************/ + +function resetPicker() { + toolOverlay.postMessage({ what: 'unhighlight' }); + unpausePicker(); +} + +/******************************************************************************/ + +function onMessage(msg) { + switch ( msg.what ) { + case 'startTool': + startPicker(); + break; + default: + break; + } +} + +/******************************************************************************/ + +// Wait for the content script to establish communication +toolOverlay.start(onMessage); + +/******************************************************************************/ diff --git a/platform/mv3/extension/js/popup.js b/platform/mv3/extension/js/popup.js index 0556d695350c4..ce0fe08da379b 100644 --- a/platform/mv3/extension/js/popup.js +++ b/platform/mv3/extension/js/popup.js @@ -19,15 +19,9 @@ Home: https://github.com/gorhill/uBlock */ -import { - browser, - localRead, localWrite, - runtime, - sendMessage, -} from './ext.js'; - +import { browser, runtime, sendMessage } from './ext.js'; import { dom, qs$ } from './dom.js'; -import { i18n, i18n$ } from './i18n.js'; +import { i18n$ } from './i18n.js'; import punycode from './punycode.js'; /******************************************************************************/ @@ -208,8 +202,7 @@ dom.on( ev => { const span = ev.target; const level = parseInt(span.dataset.level, 10); - dom.text( - '#filteringModeText > span:nth-of-type(2)', + dom.text('#filteringModeText > span:nth-of-type(2)', i18n$(`filteringMode${level}Name`) ); } @@ -226,65 +219,7 @@ dom.on( /******************************************************************************/ -// The popup panel is made of sections. Visibility of sections can be -// toggled on/off. - -const maxNumberOfSections = 2; - -const sectionBitsFromAttribute = function() { - const value = dom.body.dataset.section; - if ( value === '' ) { return 0; } - let bits = 0; - for ( const c of value.split(' ') ) { - bits |= 1 << (c.charCodeAt(0) - 97); - } - return bits; -}; - -const sectionBitsToAttribute = function(bits) { - if ( typeof bits !== 'number' ) { return; } - if ( isNaN(bits) ) { return; } - const value = []; - for ( let i = 0; i < maxNumberOfSections; i++ ) { - const bit = 1 << i; - if ( (bits & bit) === 0 ) { continue; } - value.push(String.fromCharCode(97 + i)); - } - dom.body.dataset.section = value.join(' '); -}; - -async function toggleSections(more) { - let currentBits = sectionBitsFromAttribute(); - let newBits = currentBits; - for ( let i = 0; i < maxNumberOfSections; i++ ) { - const bit = 1 << (more ? i : maxNumberOfSections - i - 1); - if ( more ) { - newBits |= bit; - } else { - newBits &= ~bit; - } - if ( newBits !== currentBits ) { break; } - } - if ( newBits === currentBits ) { return; } - sectionBitsToAttribute(newBits); - localWrite('popupPanelSections', newBits); -} - -localRead('popupPanelSections').then(bits => { - sectionBitsToAttribute(bits || 0); -}); - -dom.on('#moreButton', 'click', ( ) => { - toggleSections(true); -}); - -dom.on('#lessButton', 'click', ( ) => { - toggleSections(false); -}); - -/******************************************************************************/ - -dom.on('#showMatchedRules', 'click', ev => { +dom.on('#gotoMatchedRules', 'click', ev => { if ( ev.isTrusted !== true ) { return; } if ( ev.button !== 0 ) { return; } sendMessage({ @@ -295,7 +230,7 @@ dom.on('#showMatchedRules', 'click', ev => { /******************************************************************************/ -dom.on('[data-i18n-title="popupTipReport"]', 'click', ev => { +dom.on('#gotoReport', 'click', ev => { if ( ev.isTrusted !== true ) { return; } let url; try { @@ -314,7 +249,7 @@ dom.on('[data-i18n-title="popupTipReport"]', 'click', ev => { /******************************************************************************/ -dom.on('[data-i18n-title="popupTipDashboard"]', 'click', ev => { +dom.on('#gotoDashboard', 'click', ev => { if ( ev.isTrusted !== true ) { return; } if ( ev.button !== 0 ) { return; } runtime.openOptionsPage(); @@ -325,7 +260,29 @@ dom.on('[data-i18n-title="popupTipDashboard"]', 'click', ev => { dom.on('#gotoZapper', 'click', ( ) => { if ( browser.scripting === undefined ) { return; } browser.scripting.executeScript({ - files: [ '/js/scripting/zapper.js' ], + files: [ '/js/scripting/tool-overlay.js', '/js/scripting/zapper.js' ], + target: { tabId: currentTab.id }, + }); + self.close(); +}); + +/******************************************************************************/ + +dom.on('#gotoPicker', 'click', ( ) => { + if ( browser.scripting === undefined ) { return; } + browser.scripting.executeScript({ + files: [ '/js/scripting/tool-overlay.js', '/js/scripting/picker.js' ], + target: { tabId: currentTab.id }, + }); + self.close(); +}); + +/******************************************************************************/ + +dom.on('#gotoUnpicker', 'click', ( ) => { + if ( browser.scripting === undefined ) { return; } + browser.scripting.executeScript({ + files: [ '/js/scripting/tool-overlay.js', '/js/scripting/unpicker.js' ], target: { tabId: currentTab.id }, }); self.close(); @@ -357,7 +314,8 @@ async function init() { const response = await sendMessage({ what: 'popupPanelData', origin: url.origin, - hostname: normalizedHostname(tabURL.hostname), + normalHostname: normalizedHostname(tabURL.hostname), + hostname: tabURL.hostname, }); if ( response instanceof Object ) { Object.assign(popupPanelData, response); @@ -370,46 +328,17 @@ async function init() { dom.text('#hostname', punycode.toUnicode(tabURL.hostname)); - dom.cl.toggle('#showMatchedRules', 'enabled', + dom.cl.toggle('#gotoMatchedRules', 'enabled', popupPanelData.isSideloaded === true && popupPanelData.developerMode && typeof currentTab.id === 'number' && isNaN(currentTab.id) === false ); - const isNetworkPage = url.protocol === 'http:' || url.protocol === 'https:'; + const isHTTP = url.protocol === 'http:' || url.protocol === 'https:'; + dom.cl.toggle(dom.root, 'isHTTP', isHTTP); - dom.cl.toggle('#reportFilterIssue', 'enabled', isNetworkPage ); - dom.cl.toggle('#gotoZapper', 'enabled', isNetworkPage); - - const parent = qs$('#rulesetStats'); - for ( const details of popupPanelData.rulesetDetails || [] ) { - const div = dom.clone('#templates .rulesetDetails'); - qs$(div, 'h1').append(i18n.patchUnicodeFlags(details.name)); - const { rules, filters, css } = details; - let ruleCount = rules.plain + rules.regex; - if ( popupPanelData.hasOmnipotence ) { - ruleCount += rules.removeparam + rules.redirect + rules.modifyHeaders; - } - let specificCount = 0; - if ( typeof css.specific === 'number' ) { - specificCount += css.specific; - } - if ( typeof css.declarative === 'number' ) { - specificCount += css.declarative; - } - if ( typeof css.procedural === 'number' ) { - specificCount += css.procedural; - } - dom.text( - qs$(div, 'p'), - i18n$('perRulesetStats') - .replace('{{ruleCount}}', ruleCount.toLocaleString()) - .replace('{{filterCount}}', filters.accepted.toLocaleString()) - .replace('{{cssSpecificCount}}', specificCount.toLocaleString()) - ); - parent.append(div); - } + dom.cl.toggle('#gotoUnpicker', 'enabled', popupPanelData.hasCustomFilters); dom.cl.remove(dom.body, 'loading'); diff --git a/platform/mv3/extension/js/scripting-manager.js b/platform/mv3/extension/js/scripting-manager.js index 4cbf52d070d2f..17bd5ac8d4a25 100644 --- a/platform/mv3/extension/js/scripting-manager.js +++ b/platform/mv3/extension/js/scripting-manager.js @@ -30,6 +30,7 @@ import { import { fetchJSON } from './fetch.js'; import { getEnabledRulesetsDetails } from './ruleset-manager.js'; import { getFilteringModeDetails } from './mode-manager.js'; +import { registerCustomFilters } from './filter-manager.js'; import { registerToolbarIconToggler } from './action.js'; import { ubolLog } from './debug.js'; @@ -600,13 +601,16 @@ async function registerInjectables() { toRemove, }; - registerDeclarative(context); - registerProcedural(context); - registerScriptlet(context, scriptletDetails); - registerSpecific(context); - registerGeneric(context, genericDetails); - registerHighGeneric(context, genericDetails); - registerToolbarIconToggler(context); + await Promise.all([ + registerDeclarative(context), + registerProcedural(context), + registerScriptlet(context, scriptletDetails), + registerSpecific(context), + registerGeneric(context, genericDetails), + registerHighGeneric(context, genericDetails), + registerCustomFilters(context), + registerToolbarIconToggler(context), + ]); toRemove.push(...Array.from(before.keys())); diff --git a/platform/mv3/extension/js/scripting/css-user.js b/platform/mv3/extension/js/scripting/css-user.js new file mode 100644 index 0000000000000..55e728ab655eb --- /dev/null +++ b/platform/mv3/extension/js/scripting/css-user.js @@ -0,0 +1,39 @@ +/******************************************************************************* + + uBlock Origin Lite - a comprehensive, MV3-compliant content blocker + Copyright (C) 2019-present Raymond Hill + + 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/}. + + Home: https://github.com/gorhill/uBlock +*/ + +(async function uBOL_cssUser() { + +/******************************************************************************/ + +const docURL = new URL(document.baseURI); +chrome.runtime.sendMessage({ + what: 'injectCustomFilters', + hostname: docURL.hostname, +}).then(selectors => { + self.ubolInjectedCustomFilters = selectors; +}).catch(( ) => { +}); + +/******************************************************************************/ + +})(); + +void 0; diff --git a/platform/mv3/extension/js/scripting/isolated-api.js b/platform/mv3/extension/js/scripting/isolated-api.js index 9c867bc412fc5..4781f935c251f 100644 --- a/platform/mv3/extension/js/scripting/isolated-api.js +++ b/platform/mv3/extension/js/scripting/isolated-api.js @@ -51,7 +51,7 @@ const r = callback(`${hnparts.slice(i).join('.')}`, details); if ( r !== undefined ) { return r; } } - if ( details.hasEntities !== true ) { return; } + if ( details?.hasEntities !== true ) { return; } const n = hnpartslen - 1; for ( let i = 0; i < n; i++ ) { for ( let j = n; j > i; j-- ) { diff --git a/platform/mv3/extension/js/scripting/picker.js b/platform/mv3/extension/js/scripting/picker.js new file mode 100644 index 0000000000000..124d4dcb7e8ff --- /dev/null +++ b/platform/mv3/extension/js/scripting/picker.js @@ -0,0 +1,270 @@ +/******************************************************************************* + + uBlock Origin - a comprehensive, efficient content blocker + Copyright (C) 2025-present Raymond Hill + + 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/}. + + Home: https://github.com/gorhill/uBlock +*/ + +(async ( ) => { + +/******************************************************************************/ + +const ubolOverlay = self.ubolOverlay; +if ( ubolOverlay === undefined ) { return; } +if ( ubolOverlay.file === '/picker-ui.html' ) { return; } + +/******************************************************************************/ + +function attributeNameFromPart(part) { + const pos = part.search(/\^?=/); + return part.slice(1, pos); +} + +function selectorFromAddresses(partsDB, addresses) { + const selector = []; + let majorLast = -1; + for ( const address of addresses ) { + const major = address >>> 12; + if ( majorLast !== -1 ) { + const delta = majorLast - major; + if ( delta > 1 ) { + selector.push(' '); + } else if ( delta === 1 ) { + selector.push(' > '); + } + } + majorLast = major; + const part = partsDB.get(address); + selector.push( + (address & 0xF) === 3 + ? `[${attributeNameFromPart(part)}]` + : part + ); + } + return selector.join(''); +} + +/******************************************************************************* + * + * Selector part address: + * 0b00000000_00000000_0000 + * | | | + * | | +-- 4-bit: Descriptor + * | +------- 8-bit: Part index + * +---------------- 8-bit: List index + * Descriptor: + * - 0: tag name + * - 1: id + * - 2: class + * - 3: attribute + * - 4: :nth-of-type + * List index: 0 is deepest + * + * Selector part addresses are used to reference parts in associated database. + * + * */ + +function candidatesAtPoint(x, y) { + // We need at least one element. + let elem = null; + if ( typeof x === 'number' ) { + elem = ubolOverlay.elementFromPoint(x, y); + } else if ( x instanceof HTMLElement ) { + elem = x; + x = undefined; + } + + const partsDB = new Map(); + const listParts = []; + while ( elem && elem !== document.body ) { + const tagName = elem.localName; + const addressMajor = listParts.length << 12; + partsDB.set(addressMajor, CSS.escape(tagName)); + const parts = [ addressMajor ]; + // Id + if ( typeof elem.id === 'string' && elem.id !== '' ) { + const address = addressMajor | parts.length << 4 | 1; + partsDB.set(address, `#${CSS.escape(elem.id)}`); + parts.push(address); + } + // Classes + for ( const name of elem.classList.values() ) { + const address = addressMajor | parts.length << 4 | 2; + partsDB.set(address, `.${CSS.escape(name)}`); + parts.push(address); + } + // Attributes + for ( const name of elem.getAttributeNames() ) { + if ( name === 'id' || name === 'class' ) { continue; } + if ( excludedAttributeExpansion.includes(name) ) { + const address = addressMajor | parts.length << 4 | 3; + partsDB.set(address, `[${CSS.escape(name)}]`); + parts.push(address); + continue; + } + let value = elem.getAttribute(name); + const pos = value.search(/[\n\r]/); + if ( pos !== -1 ) { + value = value.slice(0, pos); + } + const address = addressMajor | parts.length << 4 | 3; + partsDB.set(address, `[${CSS.escape(name)}="${value}"]`); + parts.push(address); + } + // https://github.com/chrisaljoudi/uBlock/issues/637 + // If the selector is still ambiguous at this point, further narrow + // using `:nth-of-type`. + const parentNode = elem.parentNode; + if ( ubolOverlay.qsa(parentNode, `:scope > ${selectorFromAddresses(partsDB, parts)}`).length > 1 ) { + let i = 1; + while ( elem.previousSibling !== null ) { + elem = elem.previousSibling; + if ( typeof elem.localName !== 'string' ) { continue; } + if ( elem.localName !== tagName ) { continue; } + i++; + } + const address = addressMajor | parts.length << 4 | 4; + partsDB.set(address, `:nth-of-type(${i})`); + parts.push(address); + } + listParts.push(parts); + elem = elem.parentElement; + } + if ( listParts.length === 0 ) { return; } + + const sliderCandidates = []; + for ( let i = 0, n = listParts.length; i < n; i++ ) { + sliderCandidates.push(listParts[i]); + for ( let j = i + 1; j < n; j++ ) { + sliderCandidates.push([ + ...listParts[j], + ...sliderCandidates.at(-1), + ]); + } + } + const sliderMap = new Map(); + for ( const candidates of sliderCandidates ) { + if ( candidates.some(a => (a & 0xF) === 1) ) { + const selectorPath = candidates.filter(a => (a & 0xF) === 1); + sliderMap.set(JSON.stringify(selectorPath), 0); + } else if ( candidates.some(a => (a & 0xF) === 4) ) { + const selectorPath = candidates.filter(a => { + return a &= 0xF, a === 0 || a === 4; + }); + sliderMap.set(JSON.stringify(selectorPath), 0); + } + if ( candidates.some(a => (a & 0xF) === 2) ) { + const selectorPath = candidates.filter(a => { + return a &= 0xF, a === 0 || a === 2; + }); + sliderMap.set(JSON.stringify(selectorPath), 0); + } + const selectorPath = candidates.filter(a => { + return a &= 0xF, a === 0 || a === 3; + }); + sliderMap.set(JSON.stringify(selectorPath), 0); + } + sliderMap.delete('[]'); + const elemToIdMap = new Map(); + const resultSetMap = new Map(); + let elemId = 1; + for ( const json of sliderMap.keys() ) { + const addresses = JSON.parse(json); + const selector = selectorFromAddresses(partsDB, addresses); + if ( excludedSelectors.includes(selector) ) { continue; } + const elems = ubolOverlay.qsa(document, selector); + if ( elems.length === 0 ) { continue; } + const resultSet = []; + for ( const elem of elems ) { + if ( elemToIdMap.has(elem) === false ) { + elemToIdMap.set(elem, elemId++); + } + resultSet.push(elemToIdMap.get(elem)); + } + const resultSetKey = JSON.stringify(resultSet.sort()); + const current = resultSetMap.get(resultSetKey); + if ( current ) { + if ( current.length < addresses.length ) { continue; } + if ( current.length === addresses.length ) { + if ( addresses.some(a => (a & 0xF) === 2) === false ) { + if ( current.some(a => (a & 0xF) === 2) ) { continue; } + } + } + } + resultSetMap.set(resultSetKey, addresses); + } + const sliderParts = Array.from(resultSetMap).toSorted((a, b) => { + let amajor = a[1].at(-1) >>> 12; + let bmajor = b[1].at(-1) >>> 12; + if ( amajor !== bmajor ) { return bmajor - amajor; } + amajor = a[1].at(0) >>> 12; + bmajor = b[1].at(0) >>> 12; + if ( amajor !== bmajor ) { return bmajor - amajor; } + if ( a[0].length !== b[0].length ) { + return b[0].length - a[0].length; + } + return b[1].length - a[1].length; + }).map(a => a[1]); + return { + partsDB: Array.from(partsDB), + listParts, + sliderParts, + }; +} + +const excludedAttributeExpansion = [ + 'sizes', + 'srcset', +]; +const excludedSelectors = [ + 'div', + 'span', +]; + +/******************************************************************************/ + +function onMessage(msg) { + switch ( msg.what ) { + case 'injectCustomFilters': + return ubolOverlay.sendMessage({ what: 'injectCustomFilters', + hostname: ubolOverlay.url.hostname, + }); + case 'uninjectCustomFilters': + return ubolOverlay.sendMessage({ what: 'uninjectCustomFilters', + hostname: ubolOverlay.url.hostname, + }); + case 'candidatesAtPoint': + return candidatesAtPoint(msg.mx, msg.my, msg.broad); + case 'insertCSS': + return ubolOverlay.sendMessage(msg); + case 'removeCSS': + return ubolOverlay.sendMessage(msg); + default: + break; + } +} + +/******************************************************************************/ + +await ubolOverlay.install('/picker-ui.html', onMessage); + +/******************************************************************************/ + +})(); + + +void 0; diff --git a/platform/mv3/extension/js/scripting/tool-overlay.js b/platform/mv3/extension/js/scripting/tool-overlay.js new file mode 100644 index 0000000000000..e2708b15999a0 --- /dev/null +++ b/platform/mv3/extension/js/scripting/tool-overlay.js @@ -0,0 +1,360 @@ +/******************************************************************************* + + uBlock Origin - a comprehensive, efficient content blocker + Copyright (C) 2025-present Raymond Hill + + 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/}. + + Home: https://github.com/gorhill/uBlock +*/ + +(function uBOLOverlay() { + +/******************************************************************************/ + +if ( self.ubolOverlay ) { + self.ubolOverlay.stop(); + self.ubolOverlay = undefined; +} + +self.ubolOverlay = { + file: '', + webext: typeof browser === 'object' ? browser : chrome, + url: new URL(document.baseURI), + port: null, + highlightedElements: [], + secretAttr: (( ) => { + let secret = String.fromCharCode((Math.random() * 26) + 97); + do { + secret += (Math.floor(Math.random() * 2147483647) + 2147483647) + .toString(36) + .slice(2); + } while ( secret.length < 8 ); + return secret; + })(), + + start() { + const cssStyle = [ + 'background: transparent', + 'border: 0', + 'border-radius: 0', + 'box-shadow: none', + 'color-scheme: light dark', + 'display: block', + 'filter: none', + 'height: 100vh', + ' height: 100svh', + 'left: 0', + 'margin: 0', + 'max-height: none', + 'max-width: none', + 'min-height: unset', + 'min-width: unset', + 'opacity: 1', + 'outline: 0', + 'padding: 0', + 'pointer-events: auto', + 'position: fixed', + 'top: 0', + 'transform: none', + 'visibility: hidden', + 'width: 100%', + 'z-index: 2147483647', + '' + ].join(' !important;\n'); + this.pickerCSS = [ + `:root > [${this.secretAttr}] { ${cssStyle} }`, + `:root > [${this.secretAttr}-loaded] { visibility: visible !important; }`, + `:root > [${this.secretAttr}-click] { pointer-events: none !important; }`, + ].join('\n'); + this.sendMessage({ what: 'insertCSS', css: this.pickerCSS }); + self.addEventListener('scroll', this.onViewportChanged, { passive: true }); + self.addEventListener('resize', this.onViewportChanged, { passive: true }); + self.addEventListener('keydown', this.onKeyPressed, true); + }, + + stop() { + if ( this.pickerCSS ) { + this.sendMessage({ what: 'removeCSS', css: this.pickerCSS }); + this.pickerCSS = undefined; + } + self.removeEventListener('scroll', this.onViewportChanged, { passive: true }); + self.removeEventListener('resize', this.onViewportChanged, { passive: true }); + self.removeEventListener('keydown', this.onKeyPressed, true); + if ( this.frame ) { + this.frame.remove(); + this.frame = null; + } + if ( this.port ) { + this.port.close(); + this.port.onmessage = null; + this.port.onmessageerror = null; + this.port = null; + } + this.onmessage = null; + self.ubolOverlay = undefined; + }, + + onViewportChanged() { + self.ubolOverlay.highlightUpdate(); + }, + + onKeyPressed(ev) { + if ( ev.key !== 'Escape' && ev.which !== 27 ) { return; } + ev.stopPropagation(); + ev.preventDefault(); + if ( self.ubolOverlay.onmessage ) { + self.ubolOverlay.onmessage({ what: 'quitTool' }); + } + }, + + sendMessage(msg) { + try { + return this.webext.runtime.sendMessage(msg).catch(( ) => { }); + } catch { + } + }, + + onMessage(wrapped) { + // Response to script-initiated message? + if ( typeof wrapped?.fromScriptId === 'number' ) { + const resolve = this.pendingMessages.get(wrapped.fromScriptId); + if ( resolve ) { + this.pendingMessages.delete(wrapped.fromScriptId); + resolve(wrapped.msg); + } + return; + } + const onmessage = this.onmessage; + const msg = wrapped.msg || wrapped; + let response; + switch ( msg.what ) { + case 'startTool': + this.start(); + break; + case 'quitTool': + this.stop(); + break; + case 'highlightElementAtPoint': + this.highlightElementAtPoint(msg.mx, msg.my); + break; + case 'highlightFromSelector': { + const { elems, error } = this.elementsFromSelector(msg.selector); + this.highlightElements(elems); + if ( msg.scrollTo && elems.length !== 0 ) { + elems[0].scrollIntoView({ block: 'nearest', inline: 'nearest' }); + } + response = { count: elems.length, error }; + break; + } + case 'unhighlight': + this.unhighlight(); + break; + } + response = onmessage && onmessage(msg) || response; + // Send response if this is frame-initiated message + if ( wrapped?.fromFrameId && this.port ) { + const { fromFrameId } = wrapped; + if ( response instanceof Promise ) { + response.then(response => { + if ( this.port === null ) { return; } + this.port.postMessage({ fromFrameId, msg: response }); + }); + } else { + this.port.postMessage({ fromFrameId, msg: response }); + } + } + }, + postMessage(msg) { + if ( this.port === null ) { return; } + const wrapped = { + fromScriptId: this.messageId++, + msg, + }; + return new Promise(resolve => { + this.pendingMessages.set(wrapped.fromScriptId, resolve); + this.port.postMessage(wrapped); + }); + }, + messageId: 1, + pendingMessages: new Map(), + + getElementBoundingClientRect(elem) { + let rect = typeof elem.getBoundingClientRect === 'function' + ? elem.getBoundingClientRect() + : { height: 0, left: 0, top: 0, width: 0 }; + + // https://github.com/gorhill/uBlock/issues/1024 + // Try not returning an empty bounding rect. + if ( rect.width !== 0 && rect.height !== 0 ) { + return rect; + } + if ( elem.shadowRoot instanceof DocumentFragment ) { + return this.getElementBoundingClientRect(elem.shadowRoot); + } + let left = rect.left, + right = left + rect.width, + top = rect.top, + bottom = top + rect.height; + for ( const child of elem.children ) { + rect = this.getElementBoundingClientRect(child); + if ( rect.width === 0 || rect.height === 0 ) { continue; } + if ( rect.left < left ) { left = rect.left; } + if ( rect.right > right ) { right = rect.right; } + if ( rect.top < top ) { top = rect.top; } + if ( rect.bottom > bottom ) { bottom = rect.bottom; } + } + return { + left, right, + top, bottom, + width: right - left, + height: bottom - top, + }; + }, + + highlightUpdate() { + const ow = self.innerWidth; + const oh = self.innerHeight; + const islands = []; + for ( const elem of this.highlightedElements ) { + const rect = this.getElementBoundingClientRect(elem); + // Ignore offscreen areas + if ( rect.left > ow ) { continue; } + if ( rect.top > oh ) { continue; } + if ( rect.left + rect.width < 0 ) { continue; } + if ( rect.top + rect.height < 0 ) { continue; } + islands.push( + `M${rect.left} ${rect.top}h${rect.width}v${rect.height}h-${rect.width}z` + ); + } + this.port.postMessage({ + what: 'svgPaths', + ocean: `M0 0h${ow}v${oh}h-${ow}z`, + islands: islands.join(''), + }); + }, + + highlightElements(iter = []) { + this.highlightedElements = + Array.from(iter).filter(a => + a instanceof Element && a !== this.frame + ); + this.highlightUpdate(); + }, + + qsa(node, selector) { + if ( node !== null ) { + try { + const elems = node.querySelectorAll(selector); + this.qsa.error = undefined; + return elems; + } catch (reason) { + this.qsa.error = `${reason}`; + } + } + return []; + }, + + elementFromPoint(x, y) { + if ( x !== undefined ) { + this.lastX = x; this.lastY = y; + } else if ( this.lastX !== undefined ) { + x = this.lastX; y = this.lastY; + } else { + return null; + } + const magicAttr = `${this.secretAttr}-click`; + this.frame.setAttribute(magicAttr, ''); + let elem = document.elementFromPoint(x, y); + if ( elem === document.body || elem === document.documentElement ) { + elem = null; + } + // https://github.com/uBlockOrigin/uBlock-issues/issues/380 + this.frame.removeAttribute(magicAttr); + return elem; + }, + + elementsFromSelector(selector) { + const elems = this.qsa(document, selector); + return { elems, error: this.qsa.error }; + }, + + highlightElementAtPoint(x, y) { + const elem = self.ubolOverlay.elementFromPoint(x, y); + this.highlightElements([ elem ]); + }, + + unhighlight() { + this.highlightElements([]); + }, + + async install(file, onmessage) { + this.file = file; + const dynamicURL = new URL(this.webext.runtime.getURL(file)); + return new Promise(resolve => { + const frame = document.createElement('iframe'); + const secretAttr = this.secretAttr; + frame.setAttribute(secretAttr, ''); + const onLoad = ( ) => { + frame.onload = null; + frame.setAttribute(`${secretAttr}-loaded`, ''); + const channel = new MessageChannel(); + const port = channel.port1; + port.onmessage = ev => { + self.ubolOverlay && + self.ubolOverlay.onMessage(ev.data || {}) + }; + port.onmessageerror = ( ) => { + self.ubolOverlay && + self.ubolOverlay.onMessage({ what: 'quitTool' }) + }; + const realURL = new URL(dynamicURL); + realURL.hostname = + self.ubolOverlay.webext.i18n.getMessage('@@extension_id'); + frame.contentWindow.postMessage( + { + what: 'startOverlay', + url: document.baseURI, + width: self.innerWidth, + height: self.innerHeight, + }, + realURL.origin, + [ channel.port2 ] + ); + frame.contentWindow.focus(); + self.ubolOverlay.onmessage = onmessage; + self.ubolOverlay.port = port; + self.ubolOverlay.frame = frame; + resolve(true); + }; + if ( dynamicURL.protocol !== 'safari-web-extension:' ) { + frame.onload = ( ) => { + frame.onload = onLoad; + frame.contentWindow.location = dynamicURL.href; + }; + } else { + frame.onload = onLoad; + frame.setAttribute('src', dynamicURL.href); + } + document.documentElement.append(frame); + }); + }, +}; + +/******************************************************************************/ + +})(); + + +void 0; diff --git a/platform/mv3/extension/js/scripting/unpicker.js b/platform/mv3/extension/js/scripting/unpicker.js new file mode 100644 index 0000000000000..41c5fb71c514f --- /dev/null +++ b/platform/mv3/extension/js/scripting/unpicker.js @@ -0,0 +1,61 @@ +/******************************************************************************* + + uBlock Origin - a comprehensive, efficient content blocker + Copyright (C) 2025-present Raymond Hill + + 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/}. + + Home: https://github.com/gorhill/uBlock +*/ + +(async ( ) => { + +/******************************************************************************/ + +const ubolOverlay = self.ubolOverlay; +if ( ubolOverlay === undefined ) { return; } +if ( ubolOverlay.file === '/unpicker-ui.html' ) { return; } + +/******************************************************************************/ + +function onMessage(msg) { + switch ( msg.what ) { + case 'injectCustomFilters': + return ubolOverlay.sendMessage({ what: 'injectCustomFilters', + hostname: ubolOverlay.url.hostname, + }); + case 'uninjectCustomFilters': + return ubolOverlay.sendMessage({ what: 'uninjectCustomFilters', + hostname: ubolOverlay.url.hostname, + }); + case 'removeCustomFilter': + return ubolOverlay.sendMessage({ what: 'removeCustomFilter', + hostname: ubolOverlay.url.hostname, + selector: msg.selector, + }); + default: + break; + } +} + +/******************************************************************************/ + +await ubolOverlay.install('/unpicker-ui.html', onMessage); + +/******************************************************************************/ + +})(); + + +void 0; diff --git a/platform/mv3/extension/js/scripting/zapper.js b/platform/mv3/extension/js/scripting/zapper.js index d3b77b72386d7..9d9628f76ccee 100644 --- a/platform/mv3/extension/js/scripting/zapper.js +++ b/platform/mv3/extension/js/scripting/zapper.js @@ -23,187 +23,9 @@ /******************************************************************************/ -const zapper = self.uBOLZapper = self.uBOLZapper || {}; - -if ( zapper.injected ) { return; } -zapper.injected = true; - -const webext = typeof browser === 'object' ? browser : chrome; - -/******************************************************************************/ - -const sendMessage = msg => { - try { - webext.runtime.sendMessage(msg).catch(( ) => { }); - } catch { - } -}; - -/******************************************************************************/ - -const zapperSecret = (( ) => { - let secret = String.fromCharCode((Math.random() * 26) + 97); - do { - secret += (Math.floor(Math.random() * 2147483647) + 2147483647) - .toString(36) - .slice(2); - } while ( secret.length < 8 ); - return secret; -})(); - -const zapperCSSStyle = [ - 'background: transparent', - 'border: 0', - 'border-radius: 0', - 'box-shadow: none', - 'color-scheme: light dark', - 'display: block', - 'filter: none', - 'height: 100vh', - ' height: 100svh', - 'left: 0', - 'margin: 0', - 'max-height: none', - 'max-width: none', - 'min-height: unset', - 'min-width: unset', - 'opacity: 1', - 'outline: 0', - 'padding: 0', - 'pointer-events: auto', - 'position: fixed', - 'top: 0', - 'transform: none', - 'visibility: hidden', - 'width: 100%', - 'z-index: 2147483647', - '' -].join(' !important;\n'); - -const zapperCSS = ` -:root > [${zapperSecret}] { - ${zapperCSSStyle} -} -:root > [${zapperSecret}-loaded] { - visibility: visible !important; -} -:root [${zapperSecret}-click] { - pointer-events: none !important; -} -`; - -sendMessage({ what: 'insertCSS', css: zapperCSS }); - -/******************************************************************************/ - -const getElementBoundingClientRect = function(elem) { - let rect = typeof elem.getBoundingClientRect === 'function' - ? elem.getBoundingClientRect() - : { height: 0, left: 0, top: 0, width: 0 }; - - // https://github.com/gorhill/uBlock/issues/1024 - // Try not returning an empty bounding rect. - if ( rect.width !== 0 && rect.height !== 0 ) { - return rect; - } - if ( elem.shadowRoot instanceof DocumentFragment ) { - return getElementBoundingClientRect(elem.shadowRoot); - } - - let left = rect.left, - right = left + rect.width, - top = rect.top, - bottom = top + rect.height; - - for ( const child of elem.children ) { - rect = getElementBoundingClientRect(child); - if ( rect.width === 0 || rect.height === 0 ) { continue; } - if ( rect.left < left ) { left = rect.left; } - if ( rect.right > right ) { right = rect.right; } - if ( rect.top < top ) { top = rect.top; } - if ( rect.bottom > bottom ) { bottom = rect.bottom; } - } - - return { - bottom, - height: bottom - top, - left, - right, - top, - width: right - left - }; -}; - -/******************************************************************************/ - -const highlightElement = function(elem) { - if ( elem !== true ) { - if ( elem !== undefined ) { - if ( elem === highlightElement.current ) { return; } - if ( elem !== zapperFrame ) { - highlightElement.current = elem; - } - } - } - elem = highlightElement.current; - - const ow = self.innerWidth; - const oh = self.innerHeight; - const islands = []; - - if ( elem !== null ) { - const rect = getElementBoundingClientRect(elem); - // Ignore offscreen areas - if ( - rect.left <= ow && rect.top <= oh && - rect.left + rect.width >= 0 && rect.top + rect.height >= 0 - ) { - islands.push( - `M${rect.left} ${rect.top}h${rect.width}v${rect.height}h-${rect.width}z` - ); - } - } - - zapperFramePort.postMessage({ - what: 'svgPaths', - ocean: `M0 0h${ow}v${oh}h-${ow}z`, - islands: islands.join(''), - }); -}; -highlightElement.current = null; - -/******************************************************************************/ - -const elementFromPoint = (( ) => { - let lastX, lastY; - - return (x, y) => { - if ( x !== undefined ) { - lastX = x; lastY = y; - } else if ( lastX !== undefined ) { - x = lastX; y = lastY; - } else { - return null; - } - if ( !zapperFrame ) { return null; } - const magicAttr = `${zapperSecret}-click`; - zapperFrame.setAttribute(magicAttr, ''); - let elem = document.elementFromPoint(x, y); - if ( elem === document.body || elem === document.documentElement ) { - elem = null; - } - // https://github.com/uBlockOrigin/uBlock-issues/issues/380 - zapperFrame.removeAttribute(magicAttr); - return elem; - }; -})(); - -/******************************************************************************/ - -const highlightElementAtPoint = function(mx, my) { - const elem = elementFromPoint(mx, my); - highlightElement(elem); -}; +const ubolOverlay = self.ubolOverlay; +if ( ubolOverlay === undefined ) { return; } +if ( ubolOverlay.file === '/zapper-ui.html' ) { return; } /******************************************************************************/ @@ -213,18 +35,18 @@ const highlightElementAtPoint = function(mx, my) { // With touch-driven devices, first highlight the element and remove only // when tapping again the highlighted area. -const zapElementAtPoint = function(mx, my, options) { +function zapElementAtPoint(mx, my, options) { if ( options.highlight ) { - const elem = elementFromPoint(mx, my); + const elem = ubolOverlay.elementFromPoint(mx, my); if ( elem ) { - highlightElement(elem); + ubolOverlay.highlightElements([ elem ]); } return; } - let elemToRemove = highlightElement.current; + let elemToRemove = ubolOverlay.highlightedElements?.[0] ?? null; if ( elemToRemove === null && mx !== undefined ) { - elemToRemove = elementFromPoint(mx, my); + elemToRemove = ubolOverlay.elementFromPoint(mx, my); } if ( elemToRemove instanceof Element === false ) { return; } @@ -261,86 +83,38 @@ const zapElementAtPoint = function(mx, my, options) { } } elemToRemove.remove(); - highlightElementAtPoint(mx, my); -}; - -/******************************************************************************/ - -const onKeyPressed = function(ev) { - // Delete - if ( - (ev.key === 'Delete' || ev.key === 'Backspace') ) { - ev.stopPropagation(); - ev.preventDefault(); - zapElementAtPoint(); - return; - } - // Esc - if ( ev.key === 'Escape' || ev.which === 27 ) { - ev.stopPropagation(); - ev.preventDefault(); - quitZapper(); - return; - } -}; + ubolOverlay.highlightElementAtPoint(mx, my); +} /******************************************************************************/ -// https://github.com/chrisaljoudi/uBlock/issues/190 -// May need to dynamically adjust the height of the overlay + new position -// of highlighted elements. - -const onViewportChanged = function() { - highlightElement(true); -}; +function onKeyPressed(ev) { + if ( ev.key !== 'Delete' && ev.key !== 'Backspace' ) { return; } + ev.stopPropagation(); + ev.preventDefault(); + zapElementAtPoint(); +} /******************************************************************************/ -const startZapper = function() { - zapperFrame.focus(); - self.addEventListener('scroll', onViewportChanged, { passive: true }); - self.addEventListener('resize', onViewportChanged, { passive: true }); +function startZapper() { self.addEventListener('keydown', onKeyPressed, true); -}; - -/******************************************************************************/ +} -const quitZapper = function() { - self.removeEventListener('scroll', onViewportChanged, { passive: true }); - self.removeEventListener('resize', onViewportChanged, { passive: true }); +function quitZapper() { self.removeEventListener('keydown', onKeyPressed, true); - if ( zapperFramePort ) { - zapperFramePort.close(); - zapperFramePort.onmessage = null; - zapperFramePort.onmessageerror = null; - zapperFramePort = null; - } - if ( zapperFrame ) { - zapperFrame.remove(); - zapperFrame = null; - } - sendMessage({ what: 'removeCSS', css: zapperCSS }); - zapper.injected = false; -}; +} /******************************************************************************/ -const onFrameMessage = function(msg) { +function onMessage(msg) { switch ( msg.what ) { - case 'start': + case 'startTool': startZapper(); - if ( Boolean(zapperFramePort) === false ) { break; } - highlightElement(true); break; - case 'quitZapper': + case 'quitTool': quitZapper(); break; - case 'highlightElementAtPoint': - highlightElementAtPoint(msg.mx, msg.my); - break; - case 'unhighlight': - highlightElement(null); - break; case 'zapElementAtPoint': zapElementAtPoint(msg.mx, msg.my, msg.options); if ( msg.options.highlight !== true && msg.options.stay !== true ) { @@ -350,68 +124,11 @@ const onFrameMessage = function(msg) { default: break; } -}; +} /******************************************************************************/ -// zapper-ui.html will be injected in the page through an iframe, and -// is a sandboxed so as to prevent the page from interfering with its -// content and behavior. -// -// The purpose of zapper.js is to install the zapper UI, and wait for the -// component to establish a direct communication channel. -// -// When the zapper is installed on a page, the only change the page sees is an -// iframe with a random attribute. The page can't see the content of the -// iframe, and cannot interfere with its style properties. However the page -// can remove the iframe. - -const bootstrap = async ( ) => { - const dynamicURL = new URL(webext.runtime.getURL('/zapper-ui.html')); - return new Promise(resolve => { - const frame = document.createElement('iframe'); - frame.setAttribute(zapperSecret, ''); - const onZapperLoad = ( ) => { - frame.onload = null; - frame.setAttribute(`${zapperSecret}-loaded`, ''); - const channel = new MessageChannel(); - const port = channel.port1; - port.onmessage = ev => { - onFrameMessage(ev.data || {}); - }; - port.onmessageerror = ( ) => { - quitZapper(); - }; - const realURL = new URL(dynamicURL); - realURL.hostname = webext.i18n.getMessage('@@extension_id'); - frame.contentWindow.postMessage( - { what: 'zapperStart' }, - realURL.origin, - [ channel.port2 ] - ); - frame.contentWindow.focus(); - resolve({ - zapperFrame: frame, - zapperFramePort: port, - }); - }; - if ( dynamicURL.protocol !== 'safari-web-extension:' ) { - frame.onload = ( ) => { - frame.onload = onZapperLoad; - frame.contentWindow.location = dynamicURL.href; - }; - } else { - frame.onload = onZapperLoad; - frame.setAttribute('src', dynamicURL.href); - } - document.documentElement.append(frame); - }); -}; - -let { zapperFrame, zapperFramePort } = await bootstrap(); -if ( zapperFrame && zapperFramePort ) { return; } - -quitZapper(); +await ubolOverlay.install('/zapper-ui.html', onMessage); /******************************************************************************/ diff --git a/platform/mv3/extension/js/tool-overlay-ui.js b/platform/mv3/extension/js/tool-overlay-ui.js new file mode 100644 index 0000000000000..609d548bf6b28 --- /dev/null +++ b/platform/mv3/extension/js/tool-overlay-ui.js @@ -0,0 +1,241 @@ +/******************************************************************************* + + uBlock Origin - a comprehensive, efficient content blocker + Copyright (C) 2025-present Raymond Hill + + 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/}. + + Home: https://github.com/gorhill/uBlock +*/ + +import { dom, qs$ } from './dom.js'; +import { sendMessage } from './ext.js'; + +/******************************************************************************/ + +export const toolOverlay = { + url: new URL('about:blank'), + svgRoot: qs$('svg#overlay'), + svgOcean: qs$('svg#overlay > path'), + svgIslands: qs$('svg#overlay > path + path'), + emptyPath: 'M0 0', + port: null, + + start(onmessage) { + this.onmessage = onmessage; + globalThis.addEventListener('message', ev => { + const msg = ev.data || {}; + if ( msg.what !== 'startOverlay' ) { return; } + if ( Array.isArray(ev.ports) === false ) { return; } + if ( ev.ports.length === 0 ) { return; } + toolOverlay.port = ev.ports[0]; + toolOverlay.port.onmessage = ev => { + this.onMessage(ev.data || {}); + }; + toolOverlay.port.onmessageerror = ( ) => { + this.onmessage({ what: 'stopTool' }); + }; + this.moveable = qs$('aside:has(#move)'); + if ( this.moveable !== null ) { + dom.on('aside #move', 'pointerdown', ev => { + this.mover(ev); + }); + } + this.onMessage({ what: 'startTool', + url: msg.url, + width: msg.width, + height: msg.height, + }); + dom.cl.remove(dom.body, 'loading'); + }, { once: true }); + }, + + stop() { + this.highlightElementUnderMouse(false); + if ( this.port ) { + this.port.postMessage({ what: 'quitTool' }); + this.port.onmessage = null; + this.port.onmessageerror = null; + this.port = null; + } + }, + + onMessage(wrapped) { + // Response to frame-initiated message? + if ( typeof wrapped?.fromFrameId === 'number' ) { + const resolve = this.pendingMessages.get(wrapped.fromFrameId); + if ( resolve ) { + this.pendingMessages.delete(wrapped.fromFrameId); + resolve(wrapped.msg); + } + return; + } + const msg = wrapped.msg || wrapped; + switch ( msg.what ) { + case 'startTool': { + this.url.href = msg.url; + const ow = msg.width; + const oh = msg.height; + this.svgOcean.setAttribute('d', `M0 0h${ow}v${oh}h-${ow}z`); + break; + } + case 'svgPaths': + this.svgOcean.setAttribute('d', msg.ocean + msg.islands); + this.svgIslands.setAttribute('d', msg.islands || this.emptyPath); + break; + default: + break; + } + const response = this.onmessage && this.onmessage(msg) || undefined; + // Send response if this is script-initiated message + if ( wrapped?.fromScriptId && this.port ) { + const { fromScriptId } = wrapped; + if ( response instanceof Promise ) { + response.then(response => { + if ( this.port === null ) { return; } + this.port.postMessage({ fromScriptId, msg: response }); + }); + } else { + this.port.postMessage({ fromScriptId, msg: response }); + } + } + }, + postMessage(msg) { + if ( this.port === null ) { return; } + const wrapped = { + fromFrameId: this.messageId++, + msg, + }; + return new Promise(resolve => { + this.pendingMessages.set(wrapped.fromFrameId, resolve); + this.port.postMessage(wrapped); + }); + }, + messageId: 1, + pendingMessages: new Map(), + + sendMessage(msg) { + return sendMessage(msg); + }, + + highlightElementUnderMouse(state) { + if ( dom.cl.has(dom.root, 'mobile') ) { return; } + if ( state === this.mstrackerOn ) { return; } + this.mstrackerOn = state; + if ( this.mstrackerOn ) { + dom.on(document, 'mousemove', this.onHover, { passive: true }); + return; + } + dom.off(document, 'mousemove', this.onHover, { passive: true }); + if ( this.mstrackerTimer === undefined ) { return; } + self.cancelAnimationFrame(this.mstrackerTimer); + this.mstrackerTimer = undefined; + }, + onTimer() { + toolOverlay.mstrackerTimer = undefined; + if ( toolOverlay.port === null ) { return; } + toolOverlay.port.postMessage({ + what: 'highlightElementAtPoint', + mx: toolOverlay.mstrackerX, + my: toolOverlay.mstrackerY, + }); + }, + onHover(ev) { + toolOverlay.mstrackerX = ev.clientX; + toolOverlay.mstrackerY = ev.clientY; + if ( toolOverlay.mstrackerTimer !== undefined ) { return; } + toolOverlay.mstrackerTimer = + self.requestAnimationFrame(toolOverlay.onTimer); + }, + mstrackerOn: false, + mstrackerX: 0, mstrackerY: 0, + mstrackerTimer: undefined, + + mover(ev) { + const target = ev.target; + if ( target.matches('#move') === false ) { return; } + if ( dom.cl.has(this.moveable, 'moving') ) { return; } + target.setPointerCapture(ev.pointerId); + this.moverIsTouch = ev.type.startsWith('touch'); + if ( this.moverIsTouch ) { + const touch = ev.touches[0]; + this.moverX0 = touch.pageX; + this.moverY0 = touch.pageY; + } else { + this.moverX0 = ev.pageX; + this.moverY0 = ev.pageY; + } + const rect = this.moveable.getBoundingClientRect(); + this.moverCX0 = rect.x + rect.width / 2; + this.moverCY0 = rect.y + rect.height / 2; + dom.cl.add(this.moveable, 'moving'); + self.addEventListener('pointermove', this.moverMoveAsync, { capture: true }); + self.addEventListener('pointerup', this.moverStop, { capture: true, once: true }); + ev.stopPropagation(); + ev.preventDefault(); + }, + moverMove() { + this.moverTimer = undefined; + const cx1 = this.moverCX0 + this.moverX1 - this.moverX0; + const cy1 = this.moverCY0 + this.moverY1 - this.moverY0; + const rootW = dom.root.clientWidth; + const rootH = dom.root.clientHeight; + const moveableW = this.moveable.clientWidth; + const moveableH = this.moveable.clientHeight; + if ( cx1 < rootW / 2 ) { + this.moveable.style.setProperty('left', `${Math.max(cx1-moveableW/2,2)}px`); + this.moveable.style.removeProperty('right'); + } else { + this.moveable.style.removeProperty('left'); + this.moveable.style.setProperty('right', `${Math.max(rootW-cx1-moveableW/2,2)}px`); + } + if ( cy1 < rootH / 2 ) { + this.moveable.style.setProperty('top', `${Math.max(cy1-moveableH/2,2)}px`); + this.moveable.style.removeProperty('bottom'); + } else { + this.moveable.style.removeProperty('top'); + this.moveable.style.setProperty('bottom', `${Math.max(rootH-cy1-moveableH/2,2)}px`); + } + }, + moverMoveAsync(ev) { + if ( toolOverlay.moverTimer !== undefined ) { return; } + if ( toolOverlay.moverIsTouch ) { + const touch = ev.touches[0]; + toolOverlay.moverX1 = touch.pageX; + toolOverlay.moverY1 = touch.pageY; + } else { + toolOverlay.moverX1 = ev.pageX; + toolOverlay.moverY1 = ev.pageY; + } + toolOverlay.moverTimer = self.requestAnimationFrame(( ) => { + toolOverlay.moverMove(); + }); + }, + moverStop(ev) { + if ( dom.cl.has(toolOverlay.moveable, 'moving') === false ) { return; } + dom.cl.remove(toolOverlay.moveable, 'moving'); + self.removeEventListener('pointermove', toolOverlay.moverMoveAsync, { capture: true }); + ev.target.releasePointerCapture(ev.pointerId); + ev.stopPropagation(); + ev.preventDefault(); + }, + moveable: null, + moverIsTouch: false, + moverX0: 0, moverY0: 0, + moverX1: 0, moverY1: 0, + moverCX0: 0, moverCY0: 0, + moverTimer: undefined, +}; + +/******************************************************************************/ diff --git a/platform/mv3/extension/js/unpicker-ui.js b/platform/mv3/extension/js/unpicker-ui.js new file mode 100644 index 0000000000000..afc9f64bd347d --- /dev/null +++ b/platform/mv3/extension/js/unpicker-ui.js @@ -0,0 +1,164 @@ +/******************************************************************************* + + uBlock Origin - a comprehensive, efficient content blocker + Copyright (C) 2025-present Raymond Hill + + 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/}. + + Home: https://github.com/gorhill/uBlock +*/ + +import { dom, qs$, qsa$ } from './dom.js'; +import { faIconsInit } from './fa-icons.js'; +import { toolOverlay } from './tool-overlay-ui.js'; + +/******************************************************************************/ + +function onMinimizeClicked() { + dom.cl.toggle(dom.root, 'minimized'); +} + +/******************************************************************************/ + +function highlight() { + const selectors = []; + for ( const selectorElem of qsa$('#customFilters .customFilter.on > span.selector') ) { + selectors.push(selectorElem.textContent); + } + if ( selectors.length !== 0 ) { + toolOverlay.postMessage({ + what: 'highlightFromSelector', + selector: selectors.join(','), + scrollTo: true, + }); + } else { + toolOverlay.postMessage({ what: 'unhighlight' }); + } +} + +/******************************************************************************/ + +function onFilterClicked(ev) { + const target = ev.target; + const filterElem = target.closest('.customFilter'); + if ( filterElem === null ) { return; } + const selectorElem = qs$(filterElem, ':scope > span.selector'); + if ( target === selectorElem ) { + if ( dom.cl.has(filterElem, 'on') ) { + dom.cl.remove(filterElem, 'on'); + } else { + dom.cl.remove('.customFilter.on', 'on'); + dom.cl.add(filterElem, 'on'); + } + highlight(); + return; + } + const selector = selectorElem.textContent; + const trashElem = qs$(filterElem, ':scope > span.remove'); + if ( target === trashElem ) { + dom.cl.add(filterElem, 'removed'); + dom.cl.remove(filterElem, 'on'); + toolOverlay.sendMessage({ what: 'removeCustomFilter', + hostname: toolOverlay.url.hostname, + selector, + }).then(( ) => { + autoSelectFilter(); + }); + return; + } + const undoElem = qs$(filterElem, ':scope > span.undo'); + if ( target === undoElem ) { + dom.cl.remove(filterElem, 'removed'); + toolOverlay.sendMessage({ what: 'addCustomFilter', + hostname: toolOverlay.url.hostname, + selector, + }).then(( ) => { + dom.cl.remove('.customFilter.on', 'on'); + dom.cl.add(filterElem, 'on'); + highlight(); + }); + return; + } +} + +/******************************************************************************/ + +function autoSelectFilter() { + let filterElem = qs$('.customFilter.on'); + if ( filterElem !== null ) { return; } + filterElem = qs$('.customFilter:not(.removed)'); + if ( filterElem !== null ) { + dom.cl.add(filterElem, 'on'); + } + highlight(); +} + +/******************************************************************************/ + +function populateFilters(selectors) { + const container = qs$('#customFilters'); + dom.clear(container); + const rowTemplate = qs$('template#customFilterRow'); + for ( const selector of selectors ) { + const row = rowTemplate.content.cloneNode(true); + qs$(row, '.customFilter > span.selector').textContent = selector; + container.append(row); + } + faIconsInit(container); + autoSelectFilter(); +} + +/******************************************************************************/ + +async function startUnpicker() { + const selectors = await toolOverlay.sendMessage({ + what: 'selectorsFromCustomFilters', + hostname: toolOverlay.url.hostname, + }) + if ( selectors.length === 0 ) { + return quitUnpicker(); + } + await toolOverlay.postMessage({ what: 'startTool' }); + await toolOverlay.postMessage({ what: 'uninjectCustomFilters' }); + populateFilters(selectors); + dom.on('#minimize', 'click', onMinimizeClicked); + dom.on('#customFilters', 'click', onFilterClicked); + dom.on('#quit', 'click', quitUnpicker); +} + +/******************************************************************************/ + +async function quitUnpicker() { + await toolOverlay.postMessage({ what: 'injectCustomFilters' }); + toolOverlay.stop(); +} + +/******************************************************************************/ + +function onMessage(msg) { + switch ( msg.what ) { + case 'startTool': + startUnpicker(); + break; + default: + break; + } +} + +/******************************************************************************/ + +// Wait for the content script to establish communication +toolOverlay.start(onMessage); + +/******************************************************************************/ diff --git a/platform/mv3/extension/js/zapper-ui.js b/platform/mv3/extension/js/zapper-ui.js index ec9e08c19b339..3562f6b362a83 100644 --- a/platform/mv3/extension/js/zapper-ui.js +++ b/platform/mv3/extension/js/zapper-ui.js @@ -19,40 +19,26 @@ Home: https://github.com/gorhill/uBlock */ -/******************************************************************************/ - -const $id = id => document.getElementById(id); -const $stor = selector => document.querySelector(selector); - -const svgRoot = $stor('svg#sea'); -const svgOcean = svgRoot.children[0]; -const svgIslands = svgRoot.children[1]; -const NoPaths = 'M0 0'; - -let zapperScriptPort; +import { dom } from './dom.js'; +import { toolOverlay } from './tool-overlay-ui.js'; /******************************************************************************/ -const onSvgClicked = function(ev) { +function onSvgClicked(ev) { // If zap mode, highlight element under mouse, this makes the zapper usable // on touch screens. - zapperScriptPort.postMessage({ + toolOverlay.postMessage({ what: 'zapElementAtPoint', mx: ev.clientX, my: ev.clientY, options: { stay: true, - highlight: ev.target !== svgIslands, + highlight: ev.target !== toolOverlay.svgIslands, }, }); -}; - -/******************************************************************************* - - Swipe right: - Remove current highlight +} -*/ +/******************************************************************************/ const onSvgTouch = (( ) => { let startX = 0, startY = 0; @@ -67,7 +53,6 @@ const onSvgTouch = (( ) => { if ( startX === undefined ) { return; } const stopX = ev.changedTouches[0].screenX; const stopY = ev.changedTouches[0].screenY; - const angle = Math.abs(Math.atan2(stopY - startY, stopX - startX)); const distance = Math.sqrt( Math.pow(stopX - startX, 2) + Math.pow(stopY - startY, 2) @@ -76,77 +61,23 @@ const onSvgTouch = (( ) => { // - Swipe is not valid; and // - The time between start and stop was less than 200ms. const duration = ev.timeStamp - t0; - if ( distance < 32 && duration < 200 ) { - onSvgClicked({ - type: 'touch', - target: ev.target, - clientX: ev.changedTouches[0].pageX, - clientY: ev.changedTouches[0].pageY, - }); - ev.preventDefault(); - return; - } - if ( distance < 64 ) { return; } - const angleUpperBound = Math.PI * 0.25 * 0.5; - const swipeRight = angle < angleUpperBound; - if ( swipeRight === false ) { return; } - if ( ev.cancelable ) { - ev.preventDefault(); - } - // Swipe right. - if ( svgIslands.getAttribute('d') === NoPaths ) { return; } - zapperScriptPort.postMessage({ - what: 'unhighlight' + if ( distance >= 32 || duration >= 200 ) { return; } + onSvgClicked({ + type: 'touch', + target: ev.target, + clientX: ev.changedTouches[0].pageX, + clientY: ev.changedTouches[0].pageY, }); + ev.preventDefault(); }; })(); /******************************************************************************/ -const svgListening = (( ) => { - let on = false; - let timer; - let mx = 0, my = 0; - - const onTimer = ( ) => { - timer = undefined; - zapperScriptPort.postMessage({ - what: 'highlightElementAtPoint', - mx, - my, - }); - }; - - const onHover = ev => { - mx = ev.clientX; - my = ev.clientY; - if ( timer === undefined ) { - timer = self.requestAnimationFrame(onTimer); - } - }; - - return state => { - if ( $stor(':root.mobile') !== null ) { return; } - if ( state === on ) { return; } - on = state; - if ( on ) { - document.addEventListener('mousemove', onHover, { passive: true }); - return; - } - document.removeEventListener('mousemove', onHover, { passive: true }); - if ( timer !== undefined ) { - self.cancelAnimationFrame(timer); - timer = undefined; - } - }; -})(); - -/******************************************************************************/ - -const onKeyPressed = function(ev) { +function onKeyPressed(ev) { // Delete if ( ev.key === 'Delete' || ev.key === 'Backspace' ) { - zapperScriptPort.postMessage({ + toolOverlay.postMessage({ what: 'zapElementAtPoint', options: { stay: true }, }); @@ -157,81 +88,45 @@ const onKeyPressed = function(ev) { quitZapper(); return; } -}; +} /******************************************************************************/ -const onScriptMessage = function(msg) { - switch ( msg.what ) { - case 'svgPaths': { - let { ocean, islands } = msg; - ocean += islands; - svgOcean.setAttribute('d', ocean); - svgIslands.setAttribute('d', islands || NoPaths); - break; - } - default: - break; - } -}; - -/******************************************************************************/ - -const startZapper = function(port) { - zapperScriptPort = port; - zapperScriptPort.onmessage = ev => { - onScriptMessage(ev.data || {}); - }; - zapperScriptPort.onmessageerror = ( ) => { - quitZapper(); - }; - zapperScriptPort.postMessage({ what: 'start' }); +function startZapper() { + toolOverlay.postMessage({ what: 'startTool' }); self.addEventListener('keydown', onKeyPressed, true); - $stor('svg#sea').addEventListener('click', onSvgClicked); - $stor('svg#sea').addEventListener('touchstart', onSvgTouch, { passive: true }); - $stor('svg#sea').addEventListener('touchend', onSvgTouch); - $id('quit').addEventListener('click', quitZapper ); - $id('pick').addEventListener('click', resetZapper ); - svgListening(true); -}; - -/******************************************************************************/ - -const quitZapper = function() { + dom.on('svg#overlay', 'click', onSvgClicked); + dom.on('svg#overlay', 'touchstart', onSvgTouch, { passive: true }); + dom.on('svg#overlay', 'touchend', onSvgTouch); + dom.on('#quit', 'click', quitZapper ); + dom.on('#pick', 'click', resetZapper ); + toolOverlay.highlightElementUnderMouse(true); +} + +function quitZapper() { self.removeEventListener('keydown', onKeyPressed, true); - $stor('svg#sea').removeEventListener('click', onSvgClicked); - $stor('svg#sea').removeEventListener('touchstart', onSvgTouch, { passive: true }); - $stor('svg#sea').removeEventListener('touchend', onSvgTouch); - $id('quit').removeEventListener('click', quitZapper ); - $id('pick').removeEventListener('click', resetZapper ); - svgListening(false); - if ( zapperScriptPort ) { - zapperScriptPort.postMessage({ what: 'quitZapper' }); - zapperScriptPort.close(); - zapperScriptPort.onmessage = null; - zapperScriptPort.onmessageerror = null; - zapperScriptPort = null; - } -}; + toolOverlay.stop(); +} + +function resetZapper() { + toolOverlay.postMessage({ what: 'unhighlight' }); +} /******************************************************************************/ -const resetZapper = function() { - zapperScriptPort.postMessage({ - what: 'unhighlight' - }); -}; +function onMessage(msg) { + switch ( msg.what ) { + case 'startTool': + startZapper(); + break; + default: + break; + } +} /******************************************************************************/ // Wait for the content script to establish communication - -globalThis.addEventListener('message', ev => { - const msg = ev.data || {}; - if ( msg.what !== 'zapperStart' ) { return; } - if ( Array.isArray(ev.ports) === false ) { return; } - if ( ev.ports.length === 0 ) { return; } - startZapper(ev.ports[0]); -}, { once: true }); +toolOverlay.start(onMessage); /******************************************************************************/ diff --git a/platform/mv3/extension/picker-ui.html b/platform/mv3/extension/picker-ui.html new file mode 100644 index 0000000000000..2ec0df305305f --- /dev/null +++ b/platform/mv3/extension/picker-ui.html @@ -0,0 +1,56 @@ + + + + + + +uBO Lite Zapper + + + + + + + + + + + + + + + + + + + diff --git a/platform/mv3/extension/popup.html b/platform/mv3/extension/popup.html index 44387bead8b84..1fc18542d8fdb 100644 --- a/platform/mv3/extension/popup.html +++ b/platform/mv3/extension/popup.html @@ -28,31 +28,40 @@

_
-
- bolt - list-altShow matched rules - comment-alt - cogs -
- -
+
+
+ + + bolt + _ + + + eye-slash + _ + + + eye-open + _ + + + comment-alt + _ + + + list-alt + Show matched rules +
-
-
- - _angle-up - - - angle-up_ +
+
+ + cogs + _
-
-

-
- diff --git a/platform/mv3/extension/unpicker-ui.html b/platform/mv3/extension/unpicker-ui.html new file mode 100644 index 0000000000000..3402f0267c622 --- /dev/null +++ b/platform/mv3/extension/unpicker-ui.html @@ -0,0 +1,40 @@ + + + + + + +uBO Lite Zapper + + + + + + + + + + + + + + + + + + + + + diff --git a/platform/mv3/extension/zapper-ui.html b/platform/mv3/extension/zapper-ui.html index c80208fb6395e..7e51230e18cb0 100644 --- a/platform/mv3/extension/zapper-ui.html +++ b/platform/mv3/extension/zapper-ui.html @@ -6,6 +6,7 @@ uBO Lite Zapper + @@ -21,7 +22,7 @@
- + diff --git a/platform/mv3/firefox/manifest.json b/platform/mv3/firefox/manifest.json index c2dd5a3aac3a9..c4a9cc1c4bb6f 100644 --- a/platform/mv3/firefox/manifest.json +++ b/platform/mv3/firefox/manifest.json @@ -72,6 +72,22 @@ "matches": [ "" ] + }, + { + "resources": [ + "/picker-ui.html" + ], + "matches": [ + "" + ] + }, + { + "resources": [ + "/unpicker-ui.html" + ], + "matches": [ + "" + ] } ] } diff --git a/platform/mv3/safari/manifest.json b/platform/mv3/safari/manifest.json index d18525c1aa875..9fe5b3aafdc5a 100644 --- a/platform/mv3/safari/manifest.json +++ b/platform/mv3/safari/manifest.json @@ -65,6 +65,22 @@ "matches": [ "" ] + }, + { + "resources": [ + "picker-ui.html" + ], + "matches": [ + "" + ] + }, + { + "resources": [ + "unpicker-ui.html" + ], + "matches": [ + "" + ] } ] } diff --git a/src/css/fonts/Inter/Inter-Regular.woff2 b/src/css/fonts/Inter/Inter-Regular.woff2 index d5ffd2a1f13064605a4ce314e8d445161bf6d4c6..2bcd222ecfae996d035ff72bf70672305cc29261 100644 GIT binary patch literal 111268 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 YlP_pm ztM9Q_f^Ju1U)kTCx1hKLRy|sob}2&j2&ldb*lu^!byk+hq!~}+_;u0>%whduh(~N+;TSmC{wFV7$q?xsq7HHtMXK!Bh!IC?>zVs z4&UzGhlt&#PF~ytjIoG2{EVzwA9Ayn;0-nF>|uE@byk1q(c@|?JQr1hR#*>F-g@|3 z?d-u%RhtFtnGy!$p9|0OCFNRm^z%%F8(4ACq<8yCY_Hc|D8qZYSm*17Mn{Q9zjQ7{ zWfdcpJ30>$V|E`dKo#O~);}W*c`NIB=ixeXAWr4{M&@5$vHbWzt2~(8LaUJGqAPVJ)q4w>fZ@NJ>GjaD5Za+z3T(KUb6?k6#p55W& zzdE(3wqIfzb5xdWH)5pQl}g&^b7iso(49RpBYsl+z&Ctcv$65{CqiX_R~PMr-M*qk zMelENM32kx+LqlCdk3_bIp9<3eCw7gFi=ub6zCs=GQ3KL9W)6`Ru|96ft0=62$k8| zVOV(-+H(-=L$ddL>u0g3Jh$w-J3c;QaKf~Qd8czNBf7Gtu6nwvZPy`U^C3&S1G(q1 zr$34x5bWzPFd;1p#28_0z$r$+P1q9wJL13yW(6F|N~Me%`>)4TzsrGQFYie+@f8cJCrV-T8JxXD=5 zJrdSonp)91%bh=&Jz=jb$AhL5qzRPPEoM!%zEg%Kmt)s!=7$I^i}Dw`EWjbBL`$Y& zk}*nY;`rIChMh(Yow>6GBp+SYeW}>tp|!=!bB2q%Z(B3b*gui-0a^NY>l{__6Ya62 z|71g`ABa3sAy;yce)@2JibjfE+luE(DnDt`T(ig~O7}|uBO2@wCOa5tDaE8xe?iZE zt*pzaU9d^Q#vuY#Fso`TJdBjVVM`O0B}y8JDJ3sq03iLV_Cq=+3<|t~4a7Kk^7_}M zNH1LGzJt|m%v3vaW3xW38?;gCe1DPq#a+l$Qf($O*d_&#;zzJb8F%kd4 zDm2=jD7%Ui)nEr}CGT*xE9D?Qe9XWs&QympaHq}Xm6%HuY?yKrlB9-#hh`B0F<~8|0QHjtaY~aZX{1O<$ID_nw3iwV4}3~!{1*h z#VcpV3ah`-sxSN*|IWk;5<3FpWmT36p}{ zN_fRy`tLdWq)NUmPTqv$i!{@<^C%1Q5fW#+0e~-Qe3UT7t$|rg0u`OtsXsEBvxhWS zE7|kU_1*MtPEU9)`p2tm)MQ!Iupaeq*!y==LwwzSuC(eJo=E9lU?9YVujY_ik7wBL z>uSFMtJbM;yuQ)g)uzol9JNzU&LKDMQyh`YBqq@dSYk;Fn*r-fb-UVd+oWboS)6RI zN2|l~sj}br`}EUPX6D@7{fErZ{Qohkhw&fq7yDmSZp3$HueoH@=YQ$*Lvn;hQ2X>$ zcbzb2G^_jOuIW2hZ1hJP{J8UDaeecZPiFakc<1av@zkwVXooy%qiXxkXjWg3$`WM7 z0n_HVx11ns0NDwuqwC2AhFV8Qzo$uWXHOJNH2)mKMzM9HUoEAxekuC}&whSue;Cdq z@UwHPG3oEs->kut2X3FEob+#Fcj*Y!&Yfp6coz;Y_lx;sfB8PvFSHl$J^jQ_t(kY! zfyvDbe-{Au-kw>)S1XmVQET`c7FZ7JOEUN!m5Nsdt;|OyylC0Ik$+f)W+k_l)ViLB z4(@E`kX%u~|3$T`@7)c~e|j{nZJWXO^pV1}(oi0(v95cK8=LF5CVe}m;N@2>1pGff C!H4<) literal 100368 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�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

    eUvlpY^BQ;sc~Q~=U*%-q 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 a!Kymz`|Y2zy;Kxy`;^}w^N6qj0RIQ3n!o!1 literal 106916 zcmb4r1yCg0mM!iMjZ5L~?(Xi|xI^PkV6?(Xi;xVt+v?(qBG_h$Z^d4DG2pE!|u zvNEG8BhTJzt-VfFxyp+%0RaO60RiU~0>S^;gH?0<8H@h?+MmP!-mrtgZ~`9qaQ%vf zAoPL!j@@8FHr#@1WMNA*fPmRRNI<7)zyRRU_>le=M%vRtjmt{L9dDm&UGP4Xt;rDW;mj**X10iZJq6}A*PA>O0+IqFcNjj85Z?Y!)ZjN{dtCrBI*3EKy7&R6(o z6%OmZbQ{@a8z~=O|Jt4s-`ysw*y+b6zg{F08YQ(of|V#}52^f5!W~L0 `fz)4|( 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