diff --git a/.github/workflows/RELEASE.HEAD.md b/.github/workflows/RELEASE.HEAD.md index affc1ef1f07b3..7c3d9e2be5263 100644 --- a/.github/workflows/RELEASE.HEAD.md +++ b/.github/workflows/RELEASE.HEAD.md @@ -4,4 +4,4 @@ - **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 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) +- **Chromium**: Install directly from [signed CRX package](https://github.com/gorhill/uBlock/releases/download/%version%/uBlock0_%version%.chromium.crx) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5b8aebe1fe944..a2847a9609077 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,17 @@ +- [Improve `remove-attr` scriptlet](https://github.com/gorhill/uBlock/commit/bd98bcace0) +- [Fix parsing of invalid regex-like domain in static extended filters](https://github.com/gorhill/uBlock/commit/bfbd7f609e) +- [Improve `remove-node-text`/`replace-node-text` scriptlets](https://github.com/gorhill/uBlock/commit/71faa0b23f) +- [Improve `prevent-clipboard-write` scriptlet](https://github.com/gorhill/uBlock/commit/457c510093) +- [Address multiple static filter parser issues](https://github.com/gorhill/uBlock/commit/3ab731942e) +- [Treat resources with data as redirectable](https://github.com/gorhill/uBlock/commit/c9eb5b276d) +- [Add `mpegdash-prune` scriptlet](https://github.com/gorhill/uBlock/commit/323b4ce279) +- [Improve `xmlPrune` scriptlet](https://github.com/gorhill/uBlock/commit/1235e4dd27) +- [Improve procedural operator `:matches-path()`](https://github.com/gorhill/uBlock/commit/43d3c74ce7) + +---------- + +# 1.74.0 + - [Fix possible injection of scriptlets requiring trust from non-trusted sources](https://github.com/gorhill/uBlock/commit/a46d5c8e75) - [Import `google-ima-dai` shim from AdGuard shims](https://github.com/gorhill/uBlock/commit/933efff4dd) - [Replace List-KR with filterslists-KO](https://github.com/gorhill/uBlock/commit/20d3c5b9d0) diff --git a/Makefile b/Makefile index 830796543d2e1..e3e30d4bf3cb4 100644 --- a/Makefile +++ b/Makefile @@ -105,8 +105,7 @@ publish-chromium: ghowner=gorhill \ ghrepo=uBlock \ ghtag=$(version) \ - ghasset=chromium \ - storeid=cjpalhdlnbpafiamejdnhcphjbkeiagm \ + ghasset=chromium.zip \ crxupdatepath=dist/chromium/update.xml \ crxkeytoken=ubo_dev_key_path @@ -116,7 +115,7 @@ publish-edge: ghowner=gorhill \ ghrepo=uBlock \ ghtag=$(version) \ - ghasset=chromium \ + ghasset=chromium.zip \ datebasedmajor=1 \ storeid=odfafepnkmbhccpbejgmiehpchacaeak \ productid=$(shell secret-tool lookup token ubo_edge_id) \ @@ -138,8 +137,7 @@ publish-dev-chromium: ghowner=gorhill \ ghrepo=uBlock \ ghtag=$(version) \ - ghasset=chromium \ - storeid=cgbcahbpdhpcegmbfconppldiemgcoii \ + ghasset=chromium.zip \ crxupdatepath=dist/chromium/update-dev.xml \ crxkeytoken=ubo_dev_key_path diff --git a/README.md b/README.md index 94fd57edb4609..236e623ed5ce4 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ [![Badge License]][License] [![Badge NPM]][NPM] [![Badge Mozilla]][Mozilla] -[![Badge Chrome]][Chrome] +![Badge Chrome] [![Badge Edge]][Edge] *** @@ -21,7 +21,7 @@ uBlock Origin (uBO) | 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 | "Moving the Microsoft Edge extensions ecosystem forward with Manifest Version 3": "Beginning in August 2026, Microsoft Edge will start the consumer transition away from Manifest Version 2 (MV2) extensions and toward MV3. Our goal is to complete the consumer transition by the end of 2026, with enterprise deprecation following in early 2027." | | Get uBlock Origin for Opera | Opera Add-ons | -| Get uBlock Origin for Chromium | Chrome Web Store | "Manifest V2 support timeline": "Aug 31st 2026: All remaining Manifest V2 extensions removed from the Chrome Web Store"
About Google Chrome's "This extension may soon no longer be supported" | +| Get uBlock Origin for Chromium | Removed | "Manifest V2 support timeline": "Aug 31st 2026: All remaining Manifest V2 extensions removed from the Chrome Web Store"
About Google Chrome's "This extension may soon no longer be supported" | | 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) Later versions require "GitHub - Releases". | | Get uBlock Origin through GitHub | GitHub - Releases | Stable and development versions on Firefox, Chromium MV2, and Thunderbird. Must be placed manually into web browsers; the Chromium and Thunderbird versions usually won't auto-update. @@ -95,14 +95,12 @@ uBO [works best][Works Best] on Firefox and is available for desktop and Android #### Chromium -[Chrome Web Store][Chrome] (Removal on 2026-08-31) +Chrome Web Store: Removed on 2026-08-31 [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] -[Development Builds][Chrome Dev] - uBO should be compatible with any Chromium-based browser. #### Thunderbird @@ -149,11 +147,9 @@ If you ever want to contribute something, think about the people working hard to [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 [EasyList]: https://easylist.to/#easylist [Mozilla]: https://addons.mozilla.org/addon/ublock-origin/ [Crowdin]: https://crowdin.com/project/ublock -[Chrome]: https://chromewebstore.google.com/detail/ublock-origin/cjpalhdlnbpafiamejdnhcphjbkeiagm [Reddit]: https://www.reddit.com/r/uBlockOrigin/ [Theft]: https://x.com/LeaVerou/status/518154828166725632 [Opera]: https://addons.opera.com/extensions/details/ublock/ diff --git a/dist/chromium/update-dev.xml b/dist/chromium/update-dev.xml index fd44559212c43..69ecab8999218 100644 --- a/dist/chromium/update-dev.xml +++ b/dist/chromium/update-dev.xml @@ -1,6 +1,6 @@ - + diff --git a/dist/chromium/update.xml b/dist/chromium/update.xml index e69de29bb2d1d..ac29fcf913463 100644 --- a/dist/chromium/update.xml +++ b/dist/chromium/update.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/dist/firefox/updates.json b/dist/firefox/updates.json index cbf8ceee640d7..82f50dabc23a8 100644 --- a/dist/firefox/updates.json +++ b/dist/firefox/updates.json @@ -3,13 +3,13 @@ "uBlock0@raymondhill.net": { "updates": [ { - "version": "1.73.1.8", + "version": "1.74.1.7", "browser_specific_settings": { "gecko": { "strict_min_version": "115.0" } }, - "update_link": "https://github.com/gorhill/uBlock/releases/download/1.73.1b8/uBlock0_1.73.1b8.firefox.signed.xpi" + "update_link": "https://github.com/gorhill/uBlock/releases/download/1.74.1b7/uBlock0_1.74.1b7.firefox.signed.xpi" } ] } diff --git a/dist/version b/dist/version index 283edc6d723df..5c8c298b55e50 100644 --- a/dist/version +++ b/dist/version @@ -1 +1 @@ -1.74.0 \ No newline at end of file +1.75.0 \ No newline at end of file diff --git a/platform/mv3/README.md b/platform/mv3/README.md index 315821d47a7d3..7b2adb5a2cb6e 100644 --- a/platform/mv3/README.md +++ b/platform/mv3/README.md @@ -9,8 +9,8 @@ The following assumes a linux environment. 3. `cd uBlock` 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 +9. `make mv3-[platform]`, where `[platform]` is either `chromium`, `edge`, `firefox`, or `safari` +10. 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: diff --git a/platform/mv3/description/webstore.lv.txt b/platform/mv3/description/webstore.lv.txt index 129af3d1bbb85..ad3abd241cc5c 100644 --- a/platform/mv3/description/webstore.lv.txt +++ b/platform/mv3/description/webstore.lv.txt @@ -1,6 +1,6 @@ uBO Lite (uBOL) — uz MV3 balstīts satura aizturētājs. -Noklusējuma nosacījumu kopa atbilst uBock Origin noklusējuma aizturēšanas kopai: +Noklusējuma nosacījumu kopa atbilst uBock Origin noklusējuma atsijāšanas kopai: - uBlock Origin iebūvētie aizturēšanas saraksti - EasyList @@ -9,4 +9,4 @@ Noklusējuma nosacījumu kopa atbilst uBock Origin noklusējuma aizturēšanas k 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. +uBOL ir pilnībā vispārīgs, kas nozīmē, ka nav nepieciešamības pēc pastāvīga uBOL procesa, lai notiktu atsijāš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/extension/_locales/ar/messages.json b/platform/mv3/extension/_locales/ar/messages.json index 56944a2165bc1..db206afd900ba 100644 --- a/platform/mv3/extension/_locales/ar/messages.json +++ b/platform/mv3/extension/_locales/ar/messages.json @@ -104,7 +104,7 @@ "description": "Placeholder text which describes the purpose of the textarea widget" }, "customListImportUserScriptsInfo": { - "message": "To enforce cosmetic or scriptlet filters from imported lists, you must grant uBO Lite permission to run user scripts.", + "message": "لتطبيق مرشحات التجميل أو النصوص البرمجية الصغيرة من القوائم المستوردة، يجب منح uBO Lite إذنا لتشغيل نصوص المستخدم.", "description": "A notice to inform user that the enforcement of some filters requires permission to execute 'user scripts'" }, "customFiltersImportExportLabel": { @@ -116,7 +116,7 @@ "description": "Placeholder text which describes the purpose of the textarea widget" }, "userScriptsInfo": { - "message": "لتطبيق مرشحات التجميل أو السكريبت من القوائم المستوردة، يجب منح uBO Lite إذنا لتشغيل نصوص المستخدم. افتح صفحة الإضافات في متصفحك (chrome://extensions في Chrome أو about:addons في Firefox)، ثم افتح تفاصيل uBO Lite، وفعل خيار السماح بنصوص المستخدم (المعروف أيضا بـ \"نصوص الطرف الثالث غير الموثقة\").", + "message": "افتح صفحة الإضافات في متصفحك (chrome://extensions في Chrome أو about:addons في Firefox)، ثم افتح تفاصيل uBO Lite، وفعل خيار السماح بنصوص المستخدم (المعروف أيضا بـ \"نصوص الطرف الثالث غير الموثقة\").", "description": "A notice to inform user on how to enable 'user scripts' permission" }, "aboutChangelog": { @@ -292,7 +292,7 @@ "description": "Header for filter-creation section in the dashboard" }, "sandboxEditorUserScriptsInfo": { - "message": "To enforce cosmetic or scriptlet filters from the sandbox, you must grant uBO Lite permission to run user scripts.", + "message": "لتطبيق مرشحات التجميل أو النصوص البرمجية الصغيرة من بيئة الاختبار، يجب منح uBO Lite إذنا لتشغيل نصوص المستخدم.", "description": "A notice to inform user that the enforcement of some filters requires permission to execute 'user scripts'" }, "developerModeLabel": { diff --git a/platform/mv3/extension/_locales/az/messages.json b/platform/mv3/extension/_locales/az/messages.json index 6008907d4ff78..8162b8d3fc088 100644 --- a/platform/mv3/extension/_locales/az/messages.json +++ b/platform/mv3/extension/_locales/az/messages.json @@ -104,7 +104,7 @@ "description": "Placeholder text which describes the purpose of the textarea widget" }, "customListImportUserScriptsInfo": { - "message": "To enforce cosmetic or scriptlet filters from imported lists, you must grant uBO Lite permission to run user scripts.", + "message": "İdxal olunmuş siyahılardan kosmetik və ya skriptlet filtrlərini tətbiq etmək üçün uBO Lite-a istifadəçi skriptlərini icra etmək icazəsi verəsiniz.", "description": "A notice to inform user that the enforcement of some filters requires permission to execute 'user scripts'" }, "customFiltersImportExportLabel": { @@ -116,7 +116,7 @@ "description": "Placeholder text which describes the purpose of the textarea widget" }, "userScriptsInfo": { - "message": "İdxal edilmiş siyahılardan kosmetik və ya skriptlet filtrlərini tətbiq etmək üçün uBO Lite-a istifadəçi skriptlərini işlətmək icazəsi verməlisiniz. Brauzerinizin genişləndirmələr səhifəsini açın (Chrome-da chrome://extensions və ya Firefox-da about:addons), uBO Lite bölməsinin təfərrüatlarını açın və İstifadəçi skriptlərinə icazə ver seçimini aktivləşdirin (bu seçim “təsdiqlənməmiş üçüncü tərəf skriptləri” kimi də adlandırılır).", + "message": "Brauzerinizin genişləndirmələr səhifəsini açın (Chrome-da chrome://extensions və ya Firefox-da about:addons), uBO Lite bölməsinin təfərrüatlarını açın və İstifadəçi skriptlərinə icazə ver seçimini aktivləşdirin (bu seçim “təsdiqlənməmiş üçüncü tərəf skriptləri” kimi də adlandırılır).", "description": "A notice to inform user on how to enable 'user scripts' permission" }, "aboutChangelog": { @@ -292,7 +292,7 @@ "description": "Header for filter-creation section in the dashboard" }, "sandboxEditorUserScriptsInfo": { - "message": "To enforce cosmetic or scriptlet filters from the sandbox, you must grant uBO Lite permission to run user scripts.", + "message": "Sandbox-dan kosmetik və ya skriptlet filtrlərini tətbiq etmək üçün uBO Lite-a istifadəçi skriptlərini icra etmək icazəsi verəsiniz.", "description": "A notice to inform user that the enforcement of some filters requires permission to execute 'user scripts'" }, "developerModeLabel": { diff --git a/platform/mv3/extension/_locales/be/messages.json b/platform/mv3/extension/_locales/be/messages.json index b1ed647821567..b5158de374f2d 100644 --- a/platform/mv3/extension/_locales/be/messages.json +++ b/platform/mv3/extension/_locales/be/messages.json @@ -36,7 +36,7 @@ "description": "Link to privacy policy on GitHub (English)" }, "aboutDocumentation": { - "message": "Documentation", + "message": "Дакументацыя", "description": "Link to documentation in About pane" }, "popupFilteringModeLabel": { @@ -116,7 +116,7 @@ "description": "Placeholder text which describes the purpose of the textarea widget" }, "userScriptsInfo": { - "message": "Каб прымяніць касметычныя фільтры або фільтры скрыптлетаў да імпартаваных спісаў, вы павінны даць uBO Lite дазвол на запуск карыстальніцкіх скрыптоў. Адкрыйце старонку пашырэнняў вашага браўзера (chrome://extensions у Chrome або about:addons у Firefox), адкрыйце падрабязнасці uBO Lite і ўключыце Дазволіць карыстальніцкія скрыпты (таксама вядомыя як «неправераныя староннія скрыпты»).", + "message": "Адкрыйце старонку пашырэнняў вашага браўзера (chrome://extensions у Chrome або about:addons у Firefox), адкрыйце падрабязнасці uBO Lite і ўключыце Дазволіць карыстальніцкія скрыпты (таксама вядомыя як «неправераныя староннія скрыпты»).", "description": "A notice to inform user on how to enable 'user scripts' permission" }, "aboutChangelog": { @@ -384,7 +384,7 @@ "description": "An option in a dropdown list" }, "developOptionDnrRulesOf": { - "message": "Правілы у DNR", + "message": "Правілы у DNR …", "description": "A section header in a dropdown list" }, "developOptionDynamicRuleset": { diff --git a/platform/mv3/extension/_locales/bg/messages.json b/platform/mv3/extension/_locales/bg/messages.json index b31e44694cb6b..b5a5940cd29ca 100644 --- a/platform/mv3/extension/_locales/bg/messages.json +++ b/platform/mv3/extension/_locales/bg/messages.json @@ -384,7 +384,7 @@ "description": "An option in a dropdown list" }, "developOptionDnrRulesOf": { - "message": "Правила на DNR за ...", + "message": "Правила на DNR за …", "description": "A section header in a dropdown list" }, "developOptionDynamicRuleset": { diff --git a/platform/mv3/extension/_locales/bn/messages.json b/platform/mv3/extension/_locales/bn/messages.json index 7b9566f85bc89..31a53f76ca443 100644 --- a/platform/mv3/extension/_locales/bn/messages.json +++ b/platform/mv3/extension/_locales/bn/messages.json @@ -116,7 +116,7 @@ "description": "Placeholder text which describes the purpose of the textarea widget" }, "userScriptsInfo": { - "message": "আমদানিকৃত তালিকা থেকে কসমেটিক বা স্ক্রিপ্টলেট ফিল্টার প্রয়োগ করতে, আপনাকে uBO Lite-কে ব্যবহারকারী স্ক্রিপ্ট চালানোর অনুমতি দিতে হবে। আপনার ব্রাউজারের এক্সটেনশন পৃষ্ঠা খুলুন (Chrome-এ chrome://extensions অথবা Firefox-এ about:addons), uBO Lite-এর বিস্তারিত খুলুন, এবং Allow user scripts (যা \"অযাচাইকৃত তৃতীয়-পক্ষ স্ক্রিপ্ট\" নামেও পরিচিত) চালু করুন।", + "message": "আপনার ব্রাউজারের এক্সটেনশন পৃষ্ঠা খুলুন (Chrome-এ chrome://extensions অথবা Firefox-এ about:addons), uBO Lite-এর বিস্তারিত খুলুন, এবং Allow user scripts (যা \"অযাচাইকৃত তৃতীয়-পক্ষ স্ক্রিপ্ট\" নামেও পরিচিত) চালু করুন।", "description": "A notice to inform user on how to enable 'user scripts' permission" }, "aboutChangelog": { diff --git a/platform/mv3/extension/_locales/cy/messages.json b/platform/mv3/extension/_locales/cy/messages.json index dc68906692a9d..21ff1a448afa7 100644 --- a/platform/mv3/extension/_locales/cy/messages.json +++ b/platform/mv3/extension/_locales/cy/messages.json @@ -116,7 +116,7 @@ "description": "Placeholder text which describes the purpose of the textarea widget" }, "userScriptsInfo": { - "message": "I orfodi hidlau cosmetig neu sgriptlet o restrau wedi'u mewnforio, rhaid i chi roi caniatâd i uBO Lite redeg sgriptiau defnyddiwr. Agorwch dudalen estyniadau eich porwr (chrome://extensions yn Chrome neu about:addons yn Firefox), agorwch fanylion uBO Lite, a throwch Caniatáu sgriptiau defnyddiwr ymlaen (a elwir hefyd yn “sgriptiau trydydd parti heb eu gwirio”).", + "message": "Agorwch dudalen estyniadau eich porwr (chrome://extensions yn Chrome neu about:addons yn Firefox), agorwch fanylion uBO Lite, a throwch Caniatáu sgriptiau defnyddiwr ymlaen (a elwir hefyd yn “sgriptiau trydydd parti heb eu gwirio”).", "description": "A notice to inform user on how to enable 'user scripts' permission" }, "aboutChangelog": { diff --git a/platform/mv3/extension/_locales/et/messages.json b/platform/mv3/extension/_locales/et/messages.json index a22823faf5a0d..67b850da0c03b 100644 --- a/platform/mv3/extension/_locales/et/messages.json +++ b/platform/mv3/extension/_locales/et/messages.json @@ -116,7 +116,7 @@ "description": "Placeholder text which describes the purpose of the textarea widget" }, "userScriptsInfo": { - "message": "Ilufiltrite või scriplet filtrite kasutamiseks imporditud nimekirjast pead lubama uBO Lite'il käivitada kasutajaskripte. Ava veebilehitseja laiendite lehekülg (chrome://extensions Chrome'is või about:addons Firefoxis), ava uBO Lite'i andmed ja luba Luba kasutajaskriptid (tuntud ka kui „kinnitamata muu osapoole skriptid“).", + "message": "Ava veebilehitseja laiendite lehekülg (chrome://extensions Chrome'is või about:addons Firefoxis), ava uBO Lite'i andmed ja luba Luba kasutajaskriptid (tuntud ka kui „kinnitamata muu osapoole skriptid“).", "description": "A notice to inform user on how to enable 'user scripts' permission" }, "aboutChangelog": { diff --git a/platform/mv3/extension/_locales/fa/messages.json b/platform/mv3/extension/_locales/fa/messages.json index b17aa4f579f46..5ebdfa7062255 100644 --- a/platform/mv3/extension/_locales/fa/messages.json +++ b/platform/mv3/extension/_locales/fa/messages.json @@ -116,7 +116,7 @@ "description": "Placeholder text which describes the purpose of the textarea widget" }, "userScriptsInfo": { - "message": "برای اعمال فیلترهای ظاهری یا اسکریپتلت از لیست‌های وارد شده، باید به uBO Lite اجازه اجرای اسکریپت‌های کاربر را بدهید. صفحه افزونه‌های مرورگر خود را باز کنید (chrome://extensions در کروم یا about:addons در فایرفاکس)، جزئیات uBO Lite را باز کنید و گزینه اجازه به اسکریپت‌های کاربر (که به عنوان \"اسکریپت‌های شخص ثالث تایید نشده\" نیز شناخته می‌شود) را فعال کنید.", + "message": "صفحه افزونه‌های مرورگر خود را باز کنید (chrome://extensions در کروم یا about:addons در فایرفاکس)، جزئیات uBO Lite را باز کنید و گزینه اجازه به اسکریپت‌های کاربر (که به عنوان \"اسکریپت‌های شخص ثالث تایید نشده\" نیز شناخته می‌شود) را فعال کنید.", "description": "A notice to inform user on how to enable 'user scripts' permission" }, "aboutChangelog": { diff --git a/platform/mv3/extension/_locales/gu/messages.json b/platform/mv3/extension/_locales/gu/messages.json index ea08b7d2395bf..761ef5fa2df75 100644 --- a/platform/mv3/extension/_locales/gu/messages.json +++ b/platform/mv3/extension/_locales/gu/messages.json @@ -116,7 +116,7 @@ "description": "Placeholder text which describes the purpose of the textarea widget" }, "userScriptsInfo": { - "message": "આયાત કરેલ યાદીઓમાંથી કોસ્મેટિક અથવા સ્ક્રિપ્ટલેટ ફિલ્ટરો લાગુ કરવા માટે, તમારે uBO Lite ને વપરાશકર્તા સ્ક્રિપ્ટો ચલાવવાની પરવાનગી આપવી આવશ્યક છે. તમારા બ્રાઉઝરનું એક્સ્ટેંશન પેજ ખોલો (Chrome માં chrome://extensions અથવા Firefox માં about:addons), uBO Lite વિગતો ખોલો, અને વપરાશકર્તા સ્ક્રિપ્ટોને મંજૂરી આપો (જેને “અવેરિફાઇડ તૃતીય-પક્ષ સ્ક્રિપ્ટો” તરીકે પણ ઓળખવામાં આવે છે) ચાલુ કરો.", + "message": "તમારા બ્રાઉઝરનું એક્સ્ટેંશન પેજ ખોલો (Chrome માં chrome://extensions અથવા Firefox માં about:addons), uBO Lite વિગતો ખોલો, અને વપરાશકર્તા સ્ક્રિપ્ટોને મંજૂરી આપો (જેને “અવેરિફાઇડ તૃતીય-પક્ષ સ્ક્રિપ્ટો” તરીકે પણ ઓળખવામાં આવે છે) ચાલુ કરો.", "description": "A notice to inform user on how to enable 'user scripts' permission" }, "aboutChangelog": { diff --git a/platform/mv3/extension/_locales/he/messages.json b/platform/mv3/extension/_locales/he/messages.json index 4c7f10c4dfdfd..991fb5b3d7227 100644 --- a/platform/mv3/extension/_locales/he/messages.json +++ b/platform/mv3/extension/_locales/he/messages.json @@ -116,7 +116,7 @@ "description": "Placeholder text which describes the purpose of the textarea widget" }, "userScriptsInfo": { - "message": "בכדי לאפשר מסננים קוסמטיים או scriptlet מרשימות מיובאות, יש לאפשר ל uBO Lite להריץ סקריפטים של המשתמש. פתחו את התוספים או ההרחבות בדפדפן שלכם (chrome://extensions בכרום או chrome://extensions בפיירפוקס), פתחו את הפרטים של uBO Lite, ותנו אישור לסקריפטים של משתמשים (או \"לאפשר לתסריטי צד שלישי לא מאומתים לגשת לנתונים שלך\").", + "message": "פתחו את התוספים או ההרחבות בדפדפן שלכם (chrome://extensions בכרום או chrome://extensions בפיירפוקס), פתחו את הפרטים של uBO Lite, ותנו אישור לסקריפטים של משתמשים (או \"לאפשר לתסריטי צד שלישי לא מאומתים לגשת לנתונים שלך\").", "description": "A notice to inform user on how to enable 'user scripts' permission" }, "aboutChangelog": { diff --git a/platform/mv3/extension/_locales/hi/messages.json b/platform/mv3/extension/_locales/hi/messages.json index 2530bdabde985..9f2c0abdb6ce8 100644 --- a/platform/mv3/extension/_locales/hi/messages.json +++ b/platform/mv3/extension/_locales/hi/messages.json @@ -116,7 +116,7 @@ "description": "Placeholder text which describes the purpose of the textarea widget" }, "userScriptsInfo": { - "message": "आयातित सूचियों से कॉस्मेटिक या स्क्रिप्टलेट फ़िल्टर लागू करने के लिए, आपको uBO Lite को उपयोगकर्ता स्क्रिप्ट चलाने की अनुमति देनी होगी। अपने ब्राउज़र का एक्सटेंशन पेज खोलें (Chrome में chrome://extensions या Firefox में about:addons), uBO Lite विवरण खोलें, और उपयोगकर्ता स्क्रिप्ट की अनुमति दें (जिसे \"असत्यापित तृतीय-पक्ष स्क्रिप्ट\" भी कहा जाता है) को चालू करें।", + "message": "अपने ब्राउज़र का एक्सटेंशन पेज खोलें (Chrome में chrome://extensions या Firefox में about:addons), uBO Lite विवरण खोलें, और उपयोगकर्ता स्क्रिप्ट की अनुमति दें (जिसे \"असत्यापित तृतीय-पक्ष स्क्रिप्ट\" भी कहा जाता है) को चालू करें।", "description": "A notice to inform user on how to enable 'user scripts' permission" }, "aboutChangelog": { diff --git a/platform/mv3/extension/_locales/hu/messages.json b/platform/mv3/extension/_locales/hu/messages.json index 84eb31db8ccd5..7a9a280625f8f 100644 --- a/platform/mv3/extension/_locales/hu/messages.json +++ b/platform/mv3/extension/_locales/hu/messages.json @@ -116,7 +116,7 @@ "description": "Placeholder text which describes the purpose of the textarea widget" }, "userScriptsInfo": { - "message": "Az importált listákból származó kozmetikai szűrők alkalmazásához engedélyezni kell az uBO Lite számára, hogy felhasználói parancsfájlokat futtatsson. Nyissa meg a böngészőkiegészítők vagy bővítmények oldalát (chrome://extensions a Chrome-ban vagy about:addons a Firefoxban), nyissa meg a uBO Lite részleteit, és kapcsolja be a Felhasználói parancsfájlok engedélyezése lehetőséget (más néven „nem ellenőrzött külső parancsfájlok”).", + "message": "Nyissa meg a böngészőkiegészítők vagy bővítmények oldalát (chrome://extensions a Chrome-ban vagy about:addons a Firefoxban), nyissa meg a uBO Lite részleteit, és kapcsolja be a Felhasználói parancsfájlok engedélyezése lehetőséget (más néven „nem ellenőrzött külső parancsfájlok”).", "description": "A notice to inform user on how to enable 'user scripts' permission" }, "aboutChangelog": { diff --git a/platform/mv3/extension/_locales/hy/messages.json b/platform/mv3/extension/_locales/hy/messages.json index 106fc7da5cb31..41c8228a0d59b 100644 --- a/platform/mv3/extension/_locales/hy/messages.json +++ b/platform/mv3/extension/_locales/hy/messages.json @@ -116,7 +116,7 @@ "description": "Placeholder text which describes the purpose of the textarea widget" }, "userScriptsInfo": { - "message": "Ներմուծված ցանկերից կոսմետիկ կամ սկրիպտլետ զտիչները կիրառելու համար դուք պետք է uBO Lite-ին թույլատրեք գործարկել օգտատիրոջ սկրիպտները։ Բացեք ձեր դիտարկչի ընդլայնումների էջը (chrome://extensions Chrome-ում կամ about:addons Firefox-ում), բացեք uBO Lite-ի մանրամասները և միացրեք Թույլատրել օգտատիրոջ սկրիպտները (նաև կոչվում է “չստուգված երրորդ կողմի սկրիպտներ”)։", + "message": "Բացեք ձեր դիտարկչի ընդլայնումների էջը (chrome://extensions Chrome-ում կամ about:addons Firefox-ում), բացեք uBO Lite-ի մանրամասները և միացրեք Թույլատրել օգտատիրոջ սկրիպտները (նաև կոչվում է “չստուգված երրորդ կողմի սկրիպտներ”)։", "description": "A notice to inform user on how to enable 'user scripts' permission" }, "aboutChangelog": { @@ -164,7 +164,7 @@ "description": "A paragraph in the filter issue reporter section" }, "supportFindSpecificButton": { - "message": "Գտնել նմանատիպ հաղորդումներ GitHub-ում", + "message": "Գտնել նման զեկույցներ GitHub-ում", "description": "A clickable link in the filter issue reporter section" }, "supportS6URL": { @@ -212,7 +212,7 @@ "description": "A checkbox to use for NSFW sites" }, "supportReportSpecificButton": { - "message": "Ստեղծել նոր հաղորդում GitHub-ում", + "message": "Նոր զեկույց ստեղծել GitHub-ում", "description": "Text for button which opens an external web page in Support pane" }, "defaultFilteringModeSectionLabel": { diff --git a/platform/mv3/extension/_locales/ka/messages.json b/platform/mv3/extension/_locales/ka/messages.json index 91bd9b092c910..a526c6e0b4be7 100644 --- a/platform/mv3/extension/_locales/ka/messages.json +++ b/platform/mv3/extension/_locales/ka/messages.json @@ -116,7 +116,7 @@ "description": "Placeholder text which describes the purpose of the textarea widget" }, "userScriptsInfo": { - "message": "გარეგნული ან მცირე სკრიპტის ფილტრების იძულებით ასამოქმედებლად შემოტანილი სიებიდან, uBO Lite უნდა იყოს სკრიპტების გაშვების ნებართვის მქონე. გახსენით ბრაუზერის გაფართოებების გვერდი (chrome://extensions Chrome-ში ან about:addons Firefox-ში), იხილეთ uBO Lite ვრცლად და გადართეთ მომხმარებლის სკრიპტების ნებართვა (აგრეთვე შეიძლება ეწეროს „დაუმოწმებელი გარეშე სკრიპტები“).", + "message": "გახსენით ბრაუზერის გაფართოებების გვერდი (chrome://extensions Chrome-ში ან about:addons Firefox-ში), იხილეთ uBO Lite ვრცლად და გადართეთ მომხმარებლის სკრიპტების ნებართვა (აგრეთვე შეიძლება ეწეროს „დაუმოწმებელი გარეშე სკრიპტები“).", "description": "A notice to inform user on how to enable 'user scripts' permission" }, "aboutChangelog": { diff --git a/platform/mv3/extension/_locales/kk/messages.json b/platform/mv3/extension/_locales/kk/messages.json index fe6256604b07f..3db36a5dd5932 100644 --- a/platform/mv3/extension/_locales/kk/messages.json +++ b/platform/mv3/extension/_locales/kk/messages.json @@ -116,7 +116,7 @@ "description": "Placeholder text which describes the purpose of the textarea widget" }, "userScriptsInfo": { - "message": "Импортталған тізімдерден алынған көрнекілік немесе скрипт сүзгілерін қолдану үшін сіз uBO Lite-ке пайдаланушы скрипттерін іске қосу рұқсатын беруіңіз керек. Браузеріңіздің кеңейтулер бетін ашыңыз (chrome://extensions Chrome-да немесе about:addons Firefox-та), uBO Lite мәліметтерін ашып, Пайдаланушы скрипттеріне рұқсат ету (сонымен қатар “расталмаған үшінші тарап скрипттері” деп аталады) қосқышын қосыңыз.", + "message": "Браузеріңіздің кеңейтулер бетін ашыңыз (chrome://extensions Chrome-да немесе about:addons Firefox-та), uBO Lite мәліметтерін ашып, Пайдаланушы скрипттеріне рұқсат ету (сонымен қатар “расталмаған үшінші тарап скрипттері” деп аталады) қосқышын қосыңыз.", "description": "A notice to inform user on how to enable 'user scripts' permission" }, "aboutChangelog": { diff --git a/platform/mv3/extension/_locales/kn/messages.json b/platform/mv3/extension/_locales/kn/messages.json index fb6a282b500d7..fb1ddeb69e36d 100644 --- a/platform/mv3/extension/_locales/kn/messages.json +++ b/platform/mv3/extension/_locales/kn/messages.json @@ -116,7 +116,7 @@ "description": "Placeholder text which describes the purpose of the textarea widget" }, "userScriptsInfo": { - "message": "ಆಮದು ಮಾಡಿದ ಪಟ್ಟಿಗಳಿಂದ ಸೌಂದರ್ಯ ಅಥವಾ ಸ್ಕ್ರಿಪ್ಟ್ಲೆಟ್ ಶೋಧಕಗಳನ್ನು ಜಾರಿಗೊಳಿಸಲು, ನೀವು uBO Lite ಗೆ ಬಳಕೆದಾರ ಸ್ಕ್ರಿಪ್ಟ್ಗಳನ್ನು ಚಲಾಯಿಸಲು ಅನುಮತಿ ನೀಡಬೇಕು. ನಿಮ್ಮ ಬ್ರೌಸರ್ನ ವಿಸ್ತರಣೆಗಳ ಪುಟವನ್ನು ತೆರೆಯಿರಿ (Chrome ನಲ್ಲಿ chrome://extensions ಅಥವಾ Firefox ನಲ್ಲಿ about:addons), uBO Lite ವಿವರಗಳನ್ನು ತೆರೆಯಿರಿ, ಮತ್ತು ಬಳಕೆದಾರ ಸ್ಕ್ರಿಪ್ಟ್ಗಳನ್ನು ಅನುಮತಿಸು ಅನ್ನು ಟಾಗಲ್ ಆನ್ ಮಾಡಿ (ಇದನ್ನು “ಪರಿಶೀಲಿಸದ ಮೂರನೇ-ಪಕ್ಷ ಸ್ಕ್ರಿಪ್ಟ್ಗಳು” ಎಂದೂ ಕರೆಯಲಾಗುತ್ತದೆ).", + "message": "ನಿಮ್ಮ ಬ್ರೌಸರ್ನ ವಿಸ್ತರಣೆಗಳ ಪುಟವನ್ನು ತೆರೆಯಿರಿ (Chrome ನಲ್ಲಿ chrome://extensions ಅಥವಾ Firefox ನಲ್ಲಿ about:addons), uBO Lite ವಿವರಗಳನ್ನು ತೆರೆಯಿರಿ, ಮತ್ತು ಬಳಕೆದಾರ ಸ್ಕ್ರಿಪ್ಟ್ಗಳನ್ನು ಅನುಮತಿಸು ಅನ್ನು ಟಾಗಲ್ ಆನ್ ಮಾಡಿ (ಇದನ್ನು “ಪರಿಶೀಲಿಸದ ಮೂರನೇ-ಪಕ್ಷ ಸ್ಕ್ರಿಪ್ಟ್ಗಳು” ಎಂದೂ ಕರೆಯಲಾಗುತ್ತದೆ).", "description": "A notice to inform user on how to enable 'user scripts' permission" }, "aboutChangelog": { diff --git a/platform/mv3/extension/_locales/lt/messages.json b/platform/mv3/extension/_locales/lt/messages.json index f62681d2bd819..204119217a100 100644 --- a/platform/mv3/extension/_locales/lt/messages.json +++ b/platform/mv3/extension/_locales/lt/messages.json @@ -116,7 +116,7 @@ "description": "Placeholder text which describes the purpose of the textarea widget" }, "userScriptsInfo": { - "message": "Kad galėtumėte taikyti kosmetinius ar scenarijų filtrus iš importuotų sąrašų, turite suteikti uBO Lite leidimą vykdyti vartotojo scenarijus. Atidarykite naršyklės plėtinių puslapį (chrome://extensions sistemoje Chrome arba about:addons sistemoje Firefox), atidarykite uBO Lite išsamią informaciją ir įjunkite Leisti vartotojo scenarijus (taip pat vadinama „nepatikrintais trečiųjų šalių scenarijais“).", + "message": "Atidarykite naršyklės plėtinių puslapį (chrome://extensions sistemoje Chrome arba about:addons sistemoje Firefox), atidarykite uBO Lite išsamią informaciją ir įjunkite Leisti vartotojo scenarijus (taip pat vadinama „nepatikrintais trečiųjų šalių scenarijais“).", "description": "A notice to inform user on how to enable 'user scripts' permission" }, "aboutChangelog": { diff --git a/platform/mv3/extension/_locales/lv/messages.json b/platform/mv3/extension/_locales/lv/messages.json index 66b96d7d27f35..a0bdaa280ac45 100644 --- a/platform/mv3/extension/_locales/lv/messages.json +++ b/platform/mv3/extension/_locales/lv/messages.json @@ -92,15 +92,15 @@ "description": "Header for a ruleset section in 'Filter lists pane'" }, "3pGroupImported": { - "message": "Imported lists", + "message": "Ievietotie saraksti", "description": "Header for a ruleset section in 'Filter lists pane'" }, "customListImportLabel": { - "message": "Add filter list…", + "message": "Pievienot atsijāšanas sarakstu…", "description": "Text label heading the import area of custom filter lists" }, "customListImportPlaceholder": { - "message": "URL of the filter list to add", + "message": "Pievienojamā atsijāšanas saraksta URL", "description": "Placeholder text which describes the purpose of the textarea widget" }, "customListImportUserScriptsInfo": { diff --git a/platform/mv3/extension/_locales/mk/messages.json b/platform/mv3/extension/_locales/mk/messages.json index 09a38512e1e50..cf727102e5628 100644 --- a/platform/mv3/extension/_locales/mk/messages.json +++ b/platform/mv3/extension/_locales/mk/messages.json @@ -116,7 +116,7 @@ "description": "Placeholder text which describes the purpose of the textarea widget" }, "userScriptsInfo": { - "message": "За да ги примените козметичките или скриплет филтрите од увезените листи, мора да му доделите на uBO Lite дозвола за извршување на кориснички скрипти. Отворете ја страницата за проширувања на вашиот прелистувач (chrome://extensions во Chrome или about:addons во Firefox), отворете ги деталите за uBO Lite и вклучете ја опцијата Дозволи кориснички скрипти (исто така наречени „непроверени скрипти од трети страни“).", + "message": "Отворете ја страницата за проширувања на вашиот прелистувач (chrome://extensions во Chrome или about:addons во Firefox), отворете ги деталите за uBO Lite и вклучете ја опцијата Дозволи кориснички скрипти (исто така наречени „непроверени скрипти од трети страни“).", "description": "A notice to inform user on how to enable 'user scripts' permission" }, "aboutChangelog": { diff --git a/platform/mv3/extension/_locales/ml/messages.json b/platform/mv3/extension/_locales/ml/messages.json index a8ee09625a870..3cc50e122a232 100644 --- a/platform/mv3/extension/_locales/ml/messages.json +++ b/platform/mv3/extension/_locales/ml/messages.json @@ -116,7 +116,7 @@ "description": "Placeholder text which describes the purpose of the textarea widget" }, "userScriptsInfo": { - "message": "ഇറക്കുമതി ചെയ്ത ലിസ്റ്റുകളിൽ നിന്നുള്ള കോസ്മെറ്റിക് അല്ലെങ്കിൽ സ്ക്രിപ്റ്റ്ലെറ്റ് ഫിൽറ്ററുകൾ നടപ്പിലാക്കാൻ, യൂസർ സ്ക്രിപ്റ്റുകൾ പ്രവർത്തിപ്പിക്കാനുള്ള അനുമതി uBO Lite-ന് നിങ്ങൾ നൽകണം. നിങ്ങളുടെ ബ്രൗസറിന്റെ എക്സ്റ്റൻഷനുകൾ പേജ് തുറക്കുക (Chrome-ൽ chrome://extensions അല്ലെങ്കിൽ Firefox-ൽ about:addons), uBO Lite വിശദാംശങ്ങൾ തുറക്കുക, യൂസർ സ്ക്രിപ്റ്റുകൾ അനുവദിക്കുക (മറ്റൊരു വിധത്തിൽ “പരിശോധിക്കാത്ത മൂന്നാം കക്ഷി സ്ക്രിപ്റ്റുകൾ” എന്നും അറിയപ്പെടുന്നു) ടോഗിൾ ഓണാക്കുക.", + "message": "നിങ്ങളുടെ ബ്രൗസറിന്റെ എക്സ്റ്റൻഷനുകൾ പേജ് തുറക്കുക (Chrome-ൽ chrome://extensions അല്ലെങ്കിൽ Firefox-ൽ about:addons), uBO Lite വിശദാംശങ്ങൾ തുറക്കുക, യൂസർ സ്ക്രിപ്റ്റുകൾ അനുവദിക്കുക (മറ്റൊരു വിധത്തിൽ “പരിശോധിക്കാത്ത മൂന്നാം കക്ഷി സ്ക്രിപ്റ്റുകൾ” എന്നും അറിയപ്പെടുന്നു) ടോഗിൾ ഓണാക്കുക.", "description": "A notice to inform user on how to enable 'user scripts' permission" }, "aboutChangelog": { diff --git a/platform/mv3/extension/_locales/mr/messages.json b/platform/mv3/extension/_locales/mr/messages.json index ad62e720b3cec..410b3f8b85fb1 100644 --- a/platform/mv3/extension/_locales/mr/messages.json +++ b/platform/mv3/extension/_locales/mr/messages.json @@ -116,7 +116,7 @@ "description": "Placeholder text which describes the purpose of the textarea widget" }, "userScriptsInfo": { - "message": "आयात केलेल्या याद्यांमधील कॉस्मेटिक किंवा स्क्रिप्टलेट फिल्टर लागू करण्यासाठी, तुम्ही uBO Lite ला वापरकर्ता स्क्रिप्ट चालवण्याची परवानगी दिली पाहिजे. तुमच्या ब्राउझरचे विस्तारण पृष्ठ उघडा (Chrome मध्ये chrome://extensions किंवा Firefox मध्ये about:addons), uBO Lite तपशील उघडा, आणि Allow user scripts (ज्याला “unverified third-party scripts” असेही म्हणतात) टॉगल चालू करा.", + "message": "तुमच्या ब्राउझरचे विस्तारण पृष्ठ उघडा (Chrome मध्ये chrome://extensions किंवा Firefox मध्ये about:addons), uBO Lite तपशील उघडा, आणि Allow user scripts (ज्याला “unverified third-party scripts” असेही म्हणतात) टॉगल चालू करा.", "description": "A notice to inform user on how to enable 'user scripts' permission" }, "aboutChangelog": { diff --git a/platform/mv3/extension/_locales/ms/messages.json b/platform/mv3/extension/_locales/ms/messages.json index d7f7b71eded14..663a1a4e7702d 100644 --- a/platform/mv3/extension/_locales/ms/messages.json +++ b/platform/mv3/extension/_locales/ms/messages.json @@ -116,7 +116,7 @@ "description": "Placeholder text which describes the purpose of the textarea widget" }, "userScriptsInfo": { - "message": "Untuk menguatkuasakan penapis kosmetik atau scriptlet daripada senarai yang diimport, anda mesti memberikan kebenaran kepada uBO Lite untuk menjalankan skrip pengguna. Buka halaman sambungan pelayar anda (chrome://extensions dalam Chrome atau about:addons dalam Firefox), buka butiran uBO Lite, dan aktifkan Benarkan skrip pengguna (juga dirujuk sebagai \"skrip pihak ketiga yang tidak disahkan\").", + "message": "Buka halaman sambungan pelayar anda (chrome://extensions dalam Chrome atau about:addons dalam Firefox), buka butiran uBO Lite, dan aktifkan Benarkan skrip pengguna (juga dirujuk sebagai \"skrip pihak ketiga yang tidak disahkan\").", "description": "A notice to inform user on how to enable 'user scripts' permission" }, "aboutChangelog": { diff --git a/platform/mv3/extension/_locales/nb/messages.json b/platform/mv3/extension/_locales/nb/messages.json index 7cfb709a0fb00..59bf6ef199ab1 100644 --- a/platform/mv3/extension/_locales/nb/messages.json +++ b/platform/mv3/extension/_locales/nb/messages.json @@ -36,7 +36,7 @@ "description": "Link to privacy policy on GitHub (English)" }, "aboutDocumentation": { - "message": "Documentation", + "message": "Dokumentasjon", "description": "Link to documentation in About pane" }, "popupFilteringModeLabel": { @@ -92,11 +92,11 @@ "description": "Header for a ruleset section in 'Filter lists pane'" }, "3pGroupImported": { - "message": "Imported lists", + "message": "Importerte lister", "description": "Header for a ruleset section in 'Filter lists pane'" }, "customListImportLabel": { - "message": "Add filter list…", + "message": "Legg til filterlister…", "description": "Text label heading the import area of custom filter lists" }, "customListImportPlaceholder": { @@ -280,7 +280,7 @@ "description": "Short description for a checkbox in the options page" }, "enablePopupBlockLabel": { - "message": "Aktiver popup blokkering", + "message": "Aktiver popup-blokkering", "description": "Label for a checkbox in the options page" }, "enablePopupBlockLegend": { @@ -408,11 +408,11 @@ "description": "Text for buttons used to add content" }, "importAndAppendButton": { - "message": "Importer og legg til...", + "message": "Importer og legg til…", "description": "Text for buttons used to import and append content" }, "exportButton": { - "message": "Eksporter...", + "message": "Eksporter…", "description": "Text for buttons used to export content" }, "backupButton": { diff --git a/platform/mv3/extension/_locales/oc/messages.json b/platform/mv3/extension/_locales/oc/messages.json index 020106137c192..0f924a6ef94e3 100644 --- a/platform/mv3/extension/_locales/oc/messages.json +++ b/platform/mv3/extension/_locales/oc/messages.json @@ -116,7 +116,7 @@ "description": "Placeholder text which describes the purpose of the textarea widget" }, "userScriptsInfo": { - "message": "Per aplicar los filtres cosmetics o scriptlets de las listas importadas, deuatz acordar la permission a uBO Lite d'executar d'scripts utilizaire. Dobrissètz la pagina de las extensions de vòstre navigador (chrome://extensions dins Chrome o about:addons dins Firefox), dobrissètz los detalhs de uBO Lite, e activatz Permetre los scripts utilizaire (tanben nomenats “scripts tèrces pas verificats”).", + "message": "Dobrissètz la pagina de las extensions de vòstre navigador (chrome://extensions dins Chrome o about:addons dins Firefox), dobrissètz los detalhs de uBO Lite, e activatz Permetre los scripts utilizaire (tanben nomenats “scripts tèrces pas verificats”).", "description": "A notice to inform user on how to enable 'user scripts' permission" }, "aboutChangelog": { diff --git a/platform/mv3/extension/_locales/pa/messages.json b/platform/mv3/extension/_locales/pa/messages.json index 4d402ddcd36dc..3c5de53eedc7e 100644 --- a/platform/mv3/extension/_locales/pa/messages.json +++ b/platform/mv3/extension/_locales/pa/messages.json @@ -116,7 +116,7 @@ "description": "Placeholder text which describes the purpose of the textarea widget" }, "userScriptsInfo": { - "message": "ਆਯਾਤ ਕੀਤੀਆਂ ਸੂਚੀਆਂ ਤੋਂ ਕਾਸਮੈਟਿਕ ਜਾਂ ਸਕ੍ਰਿਪਟਲੈੱਟ ਫਿਲਟਰਾਂ ਨੂੰ ਲਾਗੂ ਕਰਨ ਲਈ, ਤੁਹਾਨੂੰ uBO Lite ਨੂੰ ਉਪਭੋਗਤਾ ਸਕ੍ਰਿਪਟਾਂ ਚਲਾਉਣ ਦੀ ਇਜਾਜ਼ਤ ਦੇਣੀ ਚਾਹੀਦੀ ਹੈ। ਆਪਣੇ ਬ੍ਰਾਊਜ਼ਰ ਦਾ ਐਕਸਟੈਂਸ਼ਨ ਪੰਨਾ ਖੋਲ੍ਹੋ (chrome://extensions Chrome ਵਿੱਚ ਜਾਂ about:addons Firefox ਵਿੱਚ), uBO Lite ਵੇਰਵੇ ਖੋਲ੍ਹੋ, ਅਤੇ Allow user scripts (ਜਿਸਨੂੰ “ਅਣਪ੍ਰਮਾਣਿਤ ਤੀਜੀ-ਧਿਰ ਸਕ੍ਰਿਪਟਾਂ” ਵੀ ਕਿਹਾ ਜਾਂਦਾ ਹੈ) ਨੂੰ ਚਾਲੂ ਕਰੋ।", + "message": "ਆਪਣੇ ਬ੍ਰਾਊਜ਼ਰ ਦਾ ਐਕਸਟੈਂਸ਼ਨ ਪੰਨਾ ਖੋਲ੍ਹੋ (chrome://extensions Chrome ਵਿੱਚ ਜਾਂ about:addons Firefox ਵਿੱਚ), uBO Lite ਵੇਰਵੇ ਖੋਲ੍ਹੋ, ਅਤੇ Allow user scripts (ਜਿਸਨੂੰ “ਅਣਪ੍ਰਮਾਣਿਤ ਤੀਜੀ-ਧਿਰ ਸਕ੍ਰਿਪਟਾਂ” ਵੀ ਕਿਹਾ ਜਾਂਦਾ ਹੈ) ਨੂੰ ਚਾਲੂ ਕਰੋ।", "description": "A notice to inform user on how to enable 'user scripts' permission" }, "aboutChangelog": { diff --git a/platform/mv3/extension/_locales/ro/messages.json b/platform/mv3/extension/_locales/ro/messages.json index 8443da5b7b00d..6dd6863b3963a 100644 --- a/platform/mv3/extension/_locales/ro/messages.json +++ b/platform/mv3/extension/_locales/ro/messages.json @@ -36,7 +36,7 @@ "description": "Link to privacy policy on GitHub (English)" }, "aboutDocumentation": { - "message": "Documentation", + "message": "Documentație", "description": "Link to documentation in About pane" }, "popupFilteringModeLabel": { @@ -116,7 +116,7 @@ "description": "Placeholder text which describes the purpose of the textarea widget" }, "userScriptsInfo": { - "message": "Pentru a aplica filtrele cosmetice sau de tip scriptlet din listele importate, trebuie să îi acorzi uBO Lite permisiunea de a rula scripturi de utilizator. Deschide pagina de extensii a browserului tău (chrome://extensions în Chrome sau about:addons în Firefox), deschide detaliile uBO Lite și activează opțiunea Permite scripturile de utilizator (numite și „scripturi de la terți neverificate”).", + "message": "Deschide pagina de extensii a browserului tău (chrome://extensions în Chrome sau about:addons în Firefox), deschide detaliile uBO Lite și activează opțiunea Permite scripturile de utilizator (numite și „scripturi de la terți neverificate”).", "description": "A notice to inform user on how to enable 'user scripts' permission" }, "aboutChangelog": { diff --git a/platform/mv3/extension/_locales/si/messages.json b/platform/mv3/extension/_locales/si/messages.json index 4bda33dd47728..9f33c06fdf743 100644 --- a/platform/mv3/extension/_locales/si/messages.json +++ b/platform/mv3/extension/_locales/si/messages.json @@ -116,7 +116,7 @@ "description": "Placeholder text which describes the purpose of the textarea widget" }, "userScriptsInfo": { - "message": "ආයාත කළ ලැයිස්තු වලින් ආලේපන හෝ ස්ක්‍රිප්ට්ලට් පෙරහන් බලාත්මක කිරීම සඳහා, ඔබ uBO Lite වෙත පරිශීලක ස්ක්‍රිප්ට් ක්‍රියාත්මක කිරීමට අවසර දිය යුතුය. ඔබගේ බ්‍රවුසරයේ දිගු පිටුව විවෘත කරන්න (Chrome හි chrome://extensions හෝ Firefox හි about:addons), uBO Lite විස්තර විවෘත කරන්න, සහ පරිශීලක ස්ක්‍රිප්ට් වලට ඉඩ දෙන්න (එය “සත්‍යාපනය නොකළ තෙවන පාර්ශවීය ස්ක්‍රිප්ට්” ලෙසද හැඳින්වේ) සක්‍රිය කරන්න.", + "message": "ඔබගේ බ්‍රවුසරයේ දිගු පිටුව විවෘත කරන්න (Chrome හි chrome://extensions හෝ Firefox හි about:addons), uBO Lite විස්තර විවෘත කරන්න, සහ පරිශීලක ස්ක්‍රිප්ට් වලට ඉඩ දෙන්න (එය “සත්‍යාපනය නොකළ තෙවන පාර්ශවීය ස්ක්‍රිප්ට්” ලෙසද හැඳින්වේ) සක්‍රිය කරන්න.", "description": "A notice to inform user on how to enable 'user scripts' permission" }, "aboutChangelog": { diff --git a/platform/mv3/extension/_locales/sl/messages.json b/platform/mv3/extension/_locales/sl/messages.json index f6b39ba843664..b97dd3f7d251d 100644 --- a/platform/mv3/extension/_locales/sl/messages.json +++ b/platform/mv3/extension/_locales/sl/messages.json @@ -116,7 +116,7 @@ "description": "Placeholder text which describes the purpose of the textarea widget" }, "userScriptsInfo": { - "message": "Če želite uveljaviti kozmetične ali skriptletne filtre iz uvoženih seznamov, morate podeliti dovoljenje za izvajanje uporabniških skriptov. Odprite stran z razširitvami v brskalniku (chrome://extensions v Chromu ali about:addons v Firefoxu), odprite podrobnosti za uBO Lite in omogočite Dovoli uporabniške skripte (imenovane tudi “nepreverjene skripte tretjih oseb”).", + "message": "Odprite stran z razširitvami v brskalniku (chrome://extensions v Chromu ali about:addons v Firefoxu), odprite podrobnosti za uBO Lite in omogočite Dovoli uporabniške skripte (imenovane tudi “nepreverjene skripte tretjih oseb”).", "description": "A notice to inform user on how to enable 'user scripts' permission" }, "aboutChangelog": { diff --git a/platform/mv3/extension/_locales/so/messages.json b/platform/mv3/extension/_locales/so/messages.json index c9d4961197d85..df25f9db04036 100644 --- a/platform/mv3/extension/_locales/so/messages.json +++ b/platform/mv3/extension/_locales/so/messages.json @@ -116,7 +116,7 @@ "description": "Placeholder text which describes the purpose of the textarea widget" }, "userScriptsInfo": { - "message": "Si loo dhaqan geliyo shaandhooyinka qurxinta ama qoraal-yar ee liisaska la soo dejiyay, waa inaad siisaa uBO Lite ogolaansho si ay u waddo qoraallada isticmaalaha. Fur bogga kordhinta biraawsarkaaga (chrome://extensions Chrome ama about:addons Firefox), fur faahfaahinta uBO Lite, oo daawo Oggolow qoraallada isticmaalaha (oo sidoo kale loo yaqaan “qoraallada dhinac-saddexaad ee aan la xaqiijin”).", + "message": "Fur bogga kordhinta biraawsarkaaga (chrome://extensions Chrome ama about:addons Firefox), fur faahfaahinta uBO Lite, oo daawo Oggolow qoraallada isticmaalaha (oo sidoo kale loo yaqaan “qoraallada dhinac-saddexaad ee aan la xaqiijin”).", "description": "A notice to inform user on how to enable 'user scripts' permission" }, "aboutChangelog": { diff --git a/platform/mv3/extension/_locales/sq/messages.json b/platform/mv3/extension/_locales/sq/messages.json index 34207fe513195..dc67cbbd0d53d 100644 --- a/platform/mv3/extension/_locales/sq/messages.json +++ b/platform/mv3/extension/_locales/sq/messages.json @@ -116,7 +116,7 @@ "description": "Placeholder text which describes the purpose of the textarea widget" }, "userScriptsInfo": { - "message": "Për të zbatuar filtrat kozmetikë ose skriptesh nga listat e importuara, duhet t'i jepni uBO Lite leje për të ekzekutuar skriptet e përdoruesit. Hapni faqen e zgjerimeve të shfletuesit tuaj (chrome://extensions në Chrome ose about:addons në Firefox), hapni detajet e uBO Lite dhe aktivizoni Lejo skriptet e përdoruesit (të referuara edhe si \"skripte të palëve të treta të paverifikuara\").", + "message": "Hapni faqen e zgjerimeve të shfletuesit tuaj (chrome://extensions në Chrome ose about:addons në Firefox), hapni detajet e uBO Lite dhe aktivizoni Lejo skriptet e përdoruesit (të referuara edhe si \"skripte të palëve të treta të paverifikuara\").", "description": "A notice to inform user on how to enable 'user scripts' permission" }, "aboutChangelog": { diff --git a/platform/mv3/extension/_locales/sw/messages.json b/platform/mv3/extension/_locales/sw/messages.json index c13f98f71d130..86058c8be7054 100644 --- a/platform/mv3/extension/_locales/sw/messages.json +++ b/platform/mv3/extension/_locales/sw/messages.json @@ -116,7 +116,7 @@ "description": "Placeholder text which describes the purpose of the textarea widget" }, "userScriptsInfo": { - "message": "Ili kutekeleza vichujio vya urembo au scriptlet kutoka kwenye orodha zilizoingizwa, lazima uipe uBO Lite ruhusa ya kuendesha hati za mtumiaji. Fungua ukurasa wa viendelezi vya kivinjari chako (chrome://extensions kwenye Chrome au about:addons kwenye Firefox), fungua maelezo ya uBO Lite, na washa Ruhusu hati za mtumiaji (pia hujulikana kama \"hati zisizoidhinishwa za watu wengine\").", + "message": "Fungua ukurasa wa viendelezi vya kivinjari chako (chrome://extensions kwenye Chrome au about:addons kwenye Firefox), fungua maelezo ya uBO Lite, na washa Ruhusu hati za mtumiaji (pia hujulikana kama \"hati zisizoidhinishwa za watu wengine\").", "description": "A notice to inform user on how to enable 'user scripts' permission" }, "aboutChangelog": { diff --git a/platform/mv3/extension/_locales/ta/messages.json b/platform/mv3/extension/_locales/ta/messages.json index 21f51c357f1bd..2f3469db9f8fe 100644 --- a/platform/mv3/extension/_locales/ta/messages.json +++ b/platform/mv3/extension/_locales/ta/messages.json @@ -36,7 +36,7 @@ "description": "Link to privacy policy on GitHub (English)" }, "aboutDocumentation": { - "message": "ஆவணங்கள்", + "message": "ஆவணமாக்கல்", "description": "Link to documentation in About pane" }, "popupFilteringModeLabel": { @@ -116,7 +116,7 @@ "description": "Placeholder text which describes the purpose of the textarea widget" }, "userScriptsInfo": { - "message": "இறக்குமதி செய்யப்பட்ட பட்டியல்களில் இருந்து அழகியல் அல்லது ஸ்கிரிப்ட்லெட் வடிப்பான்களை செயல்படுத்த, நீங்கள் uBO Lite க்கு பயனர் ஸ்கிரிப்ட்களை இயக்க அனுமதி வழங்க வேண்டும். உங்கள் உலாவியின் நீட்டிப்புகள் பக்கத்தைத் திறக்கவும் (Chrome இல் chrome://extensions அல்லது Firefox இல் about:addons), uBO Lite விவரங்களைத் திறக்கவும், மேலும் பயனர் ஸ்கிரிப்ட்களை அனுமதி (இது “சரிபார்க்கப்படாத மூன்றாம் தரப்பு ஸ்கிரிப்ட்கள்” என்றும் குறிப்பிடப்படுகிறது) என்பதை இயக்கவும்.", + "message": "உங்கள் உலாவியின் நீட்டிப்புகள் பக்கத்தைத் திறக்கவும் (Chrome இல் chrome://extensions அல்லது Firefox இல் about:addons), uBO Lite விவரங்களைத் திறக்கவும், மேலும் பயனர் ஸ்கிரிப்ட்களை அனுமதி (இது “சரிபார்க்கப்படாத மூன்றாம் தரப்பு ஸ்கிரிப்ட்கள்” என்றும் குறிப்பிடப்படுகிறது) என்பதை இயக்கவும்.", "description": "A notice to inform user on how to enable 'user scripts' permission" }, "aboutChangelog": { diff --git a/platform/mv3/extension/_locales/te/messages.json b/platform/mv3/extension/_locales/te/messages.json index e25460e43b7d9..39cce771fae87 100644 --- a/platform/mv3/extension/_locales/te/messages.json +++ b/platform/mv3/extension/_locales/te/messages.json @@ -116,7 +116,7 @@ "description": "Placeholder text which describes the purpose of the textarea widget" }, "userScriptsInfo": { - "message": "దిగుమతి చేసిన జాబితాల నుండి కాస్మెటిక్ లేదా స్క్రిప్ట్‌లెట్ ఫిల్టర్లను అమలు చేయడానికి, మీరు uBO Liteకి యూజర్ స్క్రిప్ట్‌లను అమలు చేసే అనుమతిని ఇవ్వాలి. మీ బ్రౌజర్ యొక్క ఎక్స్‌టెన్షన్ పేజీని తెరవండి (chrome://extensions Chromeలో లేదా about:addons Firefoxలో), uBO Lite వివరాలను తెరవండి, మరియు Allow user scripts (లేదా “unverified third-party scripts” అని కూడా పిలుస్తారు) టోగుల్ చేయండి.", + "message": "మీ బ్రౌజర్ యొక్క ఎక్స్‌టెన్షన్ పేజీని తెరవండి (chrome://extensions Chromeలో లేదా about:addons Firefoxలో), uBO Lite వివరాలను తెరవండి, మరియు Allow user scripts (లేదా “unverified third-party scripts” అని కూడా పిలుస్తారు) టోగుల్ చేయండి.", "description": "A notice to inform user on how to enable 'user scripts' permission" }, "aboutChangelog": { diff --git a/platform/mv3/extension/_locales/tr/messages.json b/platform/mv3/extension/_locales/tr/messages.json index 38309372954d9..af07e5a6b10e7 100644 --- a/platform/mv3/extension/_locales/tr/messages.json +++ b/platform/mv3/extension/_locales/tr/messages.json @@ -116,7 +116,7 @@ "description": "Placeholder text which describes the purpose of the textarea widget" }, "userScriptsInfo": { - "message": "İçeri aktarılmış listelerdeki kozmetik veya kod filtrelerini uygulamak için uBO Lite'a kullanıcı komut dosyalarını çalıştırma izni vermeniz gerekir. İzni vermek için tarayıcınızın uzantılar sayfasını açın (Chrome için:chrome://extensions Firefox için:about:addons), uBO Lite'ın ayrıntılar sayfasına tıklayın ve Kullanıcı komut dosyalarına izin ver seçeneğine tıklayın (\"doğrulanmamış 3. parti komutlar\" olarak da adlandırılabilir).", + "message": "Tarayıcınızın uzantılar sayfasını açın (Chrome için:chrome://extensions Firefox için:about:addons), uBO Lite'ın ayrıntılar sayfasına tıklayın ve Kullanıcı komut dosyalarına izin ver seçeneğine tıklayın (\"doğrulanmamış 3. parti komutlar\" olarak da adlandırılabilir).", "description": "A notice to inform user on how to enable 'user scripts' permission" }, "aboutChangelog": { diff --git a/platform/mv3/extension/_locales/uk/messages.json b/platform/mv3/extension/_locales/uk/messages.json index 734343493c1e0..5dcbc42a87e6d 100644 --- a/platform/mv3/extension/_locales/uk/messages.json +++ b/platform/mv3/extension/_locales/uk/messages.json @@ -100,7 +100,7 @@ "description": "Text label heading the import area of custom filter lists" }, "customListImportPlaceholder": { - "message": "Вставте сюди URL-адресу списку фільтрів, який потрібно додати", + "message": "URL-адреса списку фільтрів, який потрібно додати", "description": "Placeholder text which describes the purpose of the textarea widget" }, "customListImportUserScriptsInfo": { @@ -116,7 +116,7 @@ "description": "Placeholder text which describes the purpose of the textarea widget" }, "userScriptsInfo": { - "message": "Щоб застосувати косметичні фільтри або фільтри на основі скриптів із імпортованих списків, необхідно надати uBO Lite дозвіл на виконання користувацьких скриптів. Відкрийте сторінку розширень у браузері (chrome://extensions у Chrome або about:addons у Firefox), перейдіть до детальної інформації про uBO Lite та увімкніть опцію «Дозволити користувацькі скрипти» (також відомі як «неперевірені сторонні скрипти»).", + "message": "Відкрийте сторінку розширень у браузері (chrome://extensions у Chrome або about:addons у Firefox), перейдіть до детальної інформації про uBO Lite та увімкніть опцію «Дозволити користувацькі скрипти» (також відомі як «неперевірені сторонні скрипти»).", "description": "A notice to inform user on how to enable 'user scripts' permission" }, "aboutChangelog": { diff --git a/platform/mv3/extension/_locales/ur/messages.json b/platform/mv3/extension/_locales/ur/messages.json index 24f0abfed2136..d8918aa838bbf 100644 --- a/platform/mv3/extension/_locales/ur/messages.json +++ b/platform/mv3/extension/_locales/ur/messages.json @@ -116,7 +116,7 @@ "description": "Placeholder text which describes the purpose of the textarea widget" }, "userScriptsInfo": { - "message": "درآمد شدہ فہرستوں سے کاسمیٹک یا اسکرپٹلیٹ فلٹرز کو نافذ کرنے کے لیے، آپ کو uBO Lite کو صارف اسکرپٹس چلانے کی اجازت دینی ہوگی۔ اپنے براؤزر کے ایکسٹینشنز پیج (chrome://extensions Chrome میں یا about:addons Firefox میں) کو کھولیں، uBO Lite کی تفصیلات کھولیں، اور Allow user scripts کو آن کریں (جسے “unverified third-party scripts” بھی کہا جاتا ہے)۔", + "message": "اپنے براؤزر کے ایکسٹینشنز پیج (chrome://extensions Chrome میں یا about:addons Firefox میں) کو کھولیں، uBO Lite کی تفصیلات کھولیں، اور Allow user scripts کو آن کریں (جسے “unverified third-party scripts” بھی کہا جاتا ہے)۔", "description": "A notice to inform user on how to enable 'user scripts' permission" }, "aboutChangelog": { diff --git a/platform/mv3/extension/_locales/vi/messages.json b/platform/mv3/extension/_locales/vi/messages.json index eb111095d31e0..ef23b96375dba 100644 --- a/platform/mv3/extension/_locales/vi/messages.json +++ b/platform/mv3/extension/_locales/vi/messages.json @@ -116,7 +116,7 @@ "description": "Placeholder text which describes the purpose of the textarea widget" }, "userScriptsInfo": { - "message": "Để áp dụng các bộ lọc giao diện hoặc tập lệnh từ danh sách đã nhập, bạn phải cấp quyền cho uBO Lite chạy các tập lệnh người dùng. Mở trang tiện ích mở rộng của trình duyệt (chrome://extensions trong Chrome hoặc about:addons trong Firefox), mở chi tiết uBO Lite và bật Cho phép tập lệnh người dùng (còn được gọi là “tập lệnh của bên thứ ba chưa được xác minh”).", + "message": "Mở trang tiện ích mở rộng của trình duyệt (chrome://extensions trong Chrome hoặc about:addons trong Firefox), mở chi tiết uBO Lite và bật Cho phép tập lệnh người dùng (còn được gọi là “tập lệnh của bên thứ ba chưa được xác minh”).", "description": "A notice to inform user on how to enable 'user scripts' permission" }, "aboutChangelog": { @@ -160,11 +160,11 @@ "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 hai báo cáo tương tự, hãy chắc chắn rằng chưa từng có vấn đề tương tự được báo cáo. Lưu ý: bấm vào nút này sẽ khiến nguồn của trang web bị gửi tới Github.", + "message": "Để tránh tạo thêm gánh nặng trùng báo cáo cho tình nguyện viên, hãy chắc chắn rằng vấn đề tương tự chưa được báo cáo. Lưu ý: bấm vào nút này sẽ gửi nguồn của trang web đến GitHub.", "description": "A paragraph in the filter issue reporter section" }, "supportFindSpecificButton": { - "message": "Tìm các báo cáo tương tự trên Github", + "message": "Tìm báo cáo tương tự trên GitHub", "description": "A clickable link in the filter issue reporter section" }, "supportS6URL": { @@ -356,7 +356,7 @@ "description": "A button to navigate to the blocked page" }, "zapperTipEnter": { - "message": "Chuyển sang chế độ chặn phần tử tạm thời", + "message": "Xóa một phần tử", "description": "Tooltip for the button used to enter zapper mode" }, "zapperTipQuit": { diff --git a/platform/mv3/extension/_locales/zh_CN/messages.json b/platform/mv3/extension/_locales/zh_CN/messages.json index 309342e090505..fb8f0b6b5a557 100644 --- a/platform/mv3/extension/_locales/zh_CN/messages.json +++ b/platform/mv3/extension/_locales/zh_CN/messages.json @@ -116,7 +116,7 @@ "description": "Placeholder text which describes the purpose of the textarea widget" }, "userScriptsInfo": { - "message": "要强制执行来自导入列表的元素或脚本过滤规则,您必须授予 uBO Lite 运行用户脚本的权限。打开浏览器的扩展页面(Chrome 中为 chrome://extensions,Firefox 中为 about:addons),打开 uBO Lite 详情,然后开启 允许用户脚本(也称为“未经验证的第三方脚本”)。", + "message": "打开浏览器的扩展页面(Chrome 中为 chrome://extensions,Firefox 中为 about:addons),打开 uBO Lite 详情,然后开启 允许用户脚本(也称为“未经验证的第三方脚本”)。", "description": "A notice to inform user on how to enable 'user scripts' permission" }, "aboutChangelog": { diff --git a/platform/mv3/extension/js/admin.js b/platform/mv3/extension/js/admin.js index c8bc7d09c60da..94ff9b763cdc7 100644 --- a/platform/mv3/extension/js/admin.js +++ b/platform/mv3/extension/js/admin.js @@ -21,7 +21,7 @@ import { adminRead, - localRead, localRemove, localWrite, + localRead, localWrite, sessionRead, sessionWrite, } from './ext.js'; @@ -216,7 +216,6 @@ export async function adminReadEx(key) { if ( local ) { cacheValue = local.data; } - localRemove(`admin_${key}`); // TODO: remove eventually } adminRead(key).then(async value => { const adminKey = `admin.${key}`; diff --git a/platform/mv3/extension/js/background.js b/platform/mv3/extension/js/background.js index 1916765a8f624..e7eb34ea52c8d 100644 --- a/platform/mv3/extension/js/background.js +++ b/platform/mv3/extension/js/background.js @@ -130,6 +130,7 @@ import { updateCompiledFilters, } from './compiled-filters.js'; +import { deferredTasks } from './deferred-tasks.js'; import { dnr } from './ext-compat.js'; import { setPopupBlockMode } from './prevent-popup.js'; import { supportsOffscreenDocument } from './ext-offscreen.js'; @@ -269,6 +270,25 @@ async function setDeveloperMode(state) { /******************************************************************************/ +async function processDeferredTasks() { + if ( deferredTasks.size === 0 ) { return; } + const promises = []; + if ( deferredTasks.has('registerContentScripts') ) { + promises.push(registerContentScripts()); + } + if ( deferredTasks.has('registerUserScripts') ) { + promises.push(registerUserScripts()); + } + if ( deferredTasks.has('updateUserRules') ) { + promises.push(updateUserRules()); + } + deferredTasks.clear(); + if ( promises.length === 0 ) { return; } + return Promise.all(promises); +} + +/******************************************************************************/ + async function onMessage(request, sender) { const tabId = sender?.tab?.id ?? false; @@ -768,25 +788,20 @@ async function startSession() { // "When an extension updates, content scripts are cleared" // https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/userScripts#extension_updates // "User scripts are cleared when an extension updates" - const promises = []; const shouldInject = isNewVersion || permissionsUpdated || isSideloaded && rulesetConfig.developerMode; if ( shouldInject || stockUpdated ) { - promises.push(registerContentScripts()); + deferredTasks.add('registerContentScripts'); } if ( importedUpdated ) { - promises.push( - updateCompiledFilters().then(( ) => - Promise.all([ registerUserScripts(), updateUserRules() ]) - ) - ); + await updateCompiledFilters(); + deferredTasks.add('registerUserScripts'); + deferredTasks.add('updateUserRules'); } else if ( shouldInject ) { - promises.push(registerUserScripts(), updateUserRules()); + deferredTasks.add('registerUserScripts'); + deferredTasks.add('updateUserRules'); } else if ( userScriptsChanged ) { - promises.push(registerUserScripts()); - } - if ( promises.length ) { - await Promise.all(promises); + deferredTasks.add('registerUserScripts'); } // Cosmetic filtering-related content scripts cache fitlering data in @@ -808,12 +823,14 @@ async function startSession() { if ( enableOptimal === false ) { const afterLevel = await setDefaultFilteringMode(MODE_BASIC); if ( afterLevel === MODE_BASIC ) { - await registerContentScripts(); + deferredTasks.add('registerContentScripts'); process.firstRun = false; } } } + await processDeferredTasks(); + // Required to ensure up to date properties are available when needed adminReadEx('disabledFeatures').then(items => { if ( Array.isArray(items) === false ) { return; } diff --git a/platform/mv3/extension/js/deferred-tasks.js b/platform/mv3/extension/js/deferred-tasks.js new file mode 100644 index 0000000000000..2e24ba1481b0f --- /dev/null +++ b/platform/mv3/extension/js/deferred-tasks.js @@ -0,0 +1,22 @@ +/******************************************************************************* + + uBlock Origin Lite - a comprehensive, MV3-compliant content blocker + Copyright (C) 2026-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 deferredTasks = new Set(); diff --git a/platform/mv3/extension/js/mode-manager.js b/platform/mv3/extension/js/mode-manager.js index 8a7831a0dba81..3baddd866edbd 100644 --- a/platform/mv3/extension/js/mode-manager.js +++ b/platform/mv3/extension/js/mode-manager.js @@ -38,6 +38,7 @@ import { } from './config.js'; import { adminReadEx } from './admin.js'; +import { deferredTasks } from './deferred-tasks.js'; import { filteringModesToDNR } from './ruleset-manager.js'; import { hasBroadHostPermissions } from './ext-utils.js'; @@ -103,6 +104,22 @@ const unserializeModeDetails = details => { /******************************************************************************/ +function fixFilteringModeDetails(details) { + const { none, basic, optimal, complete } = unserializeModeDetails(details); + // Descendant hostnames cannot override no-filtering mode + for ( const exclude of none ) { + basic.delete(exclude); + pruneDescendantHostnamesFromSet(exclude, basic); + optimal.delete(exclude); + pruneDescendantHostnamesFromSet(exclude, optimal); + complete.delete(exclude); + pruneDescendantHostnamesFromSet(exclude, complete); + } + return { none, basic, optimal, complete }; +} + +/******************************************************************************/ + function lookupFilteringMode(filteringModes, hostname) { const { none, basic, optimal, complete } = filteringModes; if ( hostname === 'all-urls' ) { @@ -258,13 +275,23 @@ export async function readFilteringModeDetails(bypassCache = false) { if ( adminNoFiltering.includes('-*') ) { userModes.none.clear(); } - for ( const hn of adminNoFiltering ) { - if ( hn.charAt(0) === '-' ) { - userModes.none.delete(hn.slice(1)); - } else { - applyFilteringMode(userModes, hn, 0); + let modified = false; + for ( const token of adminNoFiltering ) { + if ( token.charAt(0) === '-' ) { + const hn = token.slice(1); + if ( userModes.none.has(hn) === false ) { continue; } + userModes.none.delete(hn); + modified = true; + } else if ( userModes.none.has(token) === false ) { + userModes.none.add(token); + modified = true; } } + if ( modified ) { + deferredTasks.add('registerContentScripts'); + deferredTasks.add('registerUserScripts'); + } + userModes = fixFilteringModeDetails(userModes); } filteringModesToDNR(userModes); sessionWrite('filteringModeDetails', serializeModeDetails(userModes)); @@ -308,7 +335,9 @@ export async function getFilteringModeDetails(serializable = false) { } export async function setFilteringModeDetails(details) { - await localWrite('filteringModeDetails', serializeModeDetails(details)); + await localWrite('filteringModeDetails', + serializeModeDetails(fixFilteringModeDetails(details)) + ); await readFilteringModeDetails(true); } diff --git a/platform/mv3/extension/js/offscreen/compile-filters.js b/platform/mv3/extension/js/offscreen/compile-filters.js index bd42caef7347d..2305a7ce7433e 100644 --- a/platform/mv3/extension/js/offscreen/compile-filters.js +++ b/platform/mv3/extension/js/offscreen/compile-filters.js @@ -24,6 +24,7 @@ import * as s14e from '../../lib/s14e-serializer.js'; import * as sfp from '../static-filtering-parser.js'; import { minimizeRules, minimizeRuleset, validateRules } from '../ubo-parser.js'; import { fetchList } from './fetch-list.js'; +import { getTrustedTokens } from '../trusted-tokens.js'; import { makeCosmeticScripts } from './make-cosmetic-filters.js'; import { parseNetworkFilter } from '../ubo-parser.js'; import { safeReplace } from './safe-replace.js'; @@ -346,6 +347,7 @@ async function updateList(list) { const compiled = compileFilters(list.id, text, { nativeCssHas: true, + trustedTokens: getTrustedTokens(), }); if ( Boolean(compiled) === false ) { return; } diff --git a/platform/mv3/extension/js/scripting/css-procedural-api.js b/platform/mv3/extension/js/scripting/css-procedural-api.js index 45656d3a35948..040eafdc89540 100644 --- a/platform/mv3/extension/js/scripting/css-procedural-api.js +++ b/platform/mv3/extension/js/scripting/css-procedural-api.js @@ -29,6 +29,8 @@ if ( self.ProceduralFiltererAPI !== undefined ) { /******************************************************************************/ +const chrome = self.chrome ?? self.browser; + const nonVisualElements = { head: true, link: true, @@ -195,15 +197,23 @@ class PSelectorMatchesMediaTask extends PSelectorTask { class PSelectorMatchesPathTask extends PSelectorTask { constructor(filterer, task) { super(); + this.filterer = filterer; this.needle = regexFromString( task[1].replace(/\P{ASCII}/gu, s => encodeURIComponent(s)) ); + if ( PSelectorMatchesPathTask.#listener ) { return; } + PSelectorMatchesPathTask.#listener = true; + if ( Boolean(self.navigation) === false ) { return; } + self.navigation.addEventListener('navigate', ( ) => { + this.filterer.uBOL_DOMChanged(); + }); } transpose(node, output) { if ( this.needle.test(self.location.pathname + self.location.search) ) { output.push(node); } } + static #listener; } /******************************************************************************/ diff --git a/platform/mv3/extension/lib/codemirror/codemirror-ubol b/platform/mv3/extension/lib/codemirror/codemirror-ubol index 30e88029af977..6a8aa61b29e49 160000 --- a/platform/mv3/extension/lib/codemirror/codemirror-ubol +++ b/platform/mv3/extension/lib/codemirror/codemirror-ubol @@ -1 +1 @@ -Subproject commit 30e88029af97777fc21a8b92044c2634612bddc9 +Subproject commit 6a8aa61b29e494ecede6e067ac3782c184a8008b diff --git a/publish-extension b/publish-extension index 990eb214e94ab..5798bcb1ce905 160000 --- a/publish-extension +++ b/publish-extension @@ -1 +1 @@ -Subproject commit 990eb214e94abdd97e7ef138d6d4c88fb1e5fa27 +Subproject commit 5798bcb1ce905222e1ca559c538eebb98108cd84 diff --git a/src/_locales/az/messages.json b/src/_locales/az/messages.json index b4cfdc2f6afe3..df1f4e7e7b782 100644 --- a/src/_locales/az/messages.json +++ b/src/_locales/az/messages.json @@ -4,7 +4,7 @@ "description": "extension name." }, "extShortDesc": { - "message": "Axır ki, prosessor və yaddaş yükünü azaldan səmərəli bir əngəlləyici var.", + "message": "Nəhayət, səmərəli bloklayıcı. Prosessor və yaddaşı yükləmir.", "description": "this will be in the Chrome web store: must be 132 characters or less" }, "dashboardName": { @@ -12,15 +12,15 @@ "description": "English: uBlock₀ — Dashboard" }, "dashboardUnsavedWarning": { - "message": "Xəbərdarlıq: dəyişiklikləriniz yadda saxlanılmayıb!", + "message": "Xəbərdarlıq! Saxlamadığınız dəyişikliklər var", "description": "A warning in the dashboard when navigating away from unsaved changes" }, "dashboardUnsavedWarningStay": { - "message": "Qal", + "message": "Burada qal", "description": "Label for button to prevent navigating away from unsaved changes" }, "dashboardUnsavedWarningIgnore": { - "message": "Əhəmiyyət vermə", + "message": "Məhəl qoyma", "description": "Label for button to ignore unsaved changes" }, "settingsPageName": { @@ -48,7 +48,7 @@ "description": "appears as tab name in dashboard" }, "statsPageName": { - "message": "uBlock₀ — Jurnal", + "message": "uBlock₀ — Qeydiyyat jurnalı", "description": "Title for the logger window" }, "aboutPageName": { @@ -60,7 +60,7 @@ "description": "appears as tab name in dashboard" }, "assetViewerPageName": { - "message": "uBlock₀ — Resurslar", + "message": "uBlock₀ — Resurs baxıcısı", "description": "Title for the asset viewer page" }, "advancedSettingsPageName": { @@ -68,19 +68,19 @@ "description": "Title for the advanced settings page" }, "popupPowerSwitchInfo": { - "message": "Klikləmə: Bu sayt üçün uBlock₀-u fəallaşdır/sıradan çıxart.\n\nCtrl+klikləmə: Yalnız bu səhifə üçün uBlock₀-u sıradan çıxart.", + "message": "Klikləmə: uBlock₀-u bu sayt üçün qapadın/aktivləşdirin.\n\n​Ctrl+klikləmə: uBlock₀-u yalnız bu səhifədə qapadın.", "description": "English: Click: disable/enable uBlock₀ for this site.\n\nCtrl+click: disable uBlock₀ only on this page." }, "popupPowerSwitchInfo1": { - "message": "Bu sayt üçün uBlock₀-u sıradan çıxartmaq üçün klikləyin.\n\nYalnız bu səhifə üçün uBlock₀-u sıradan çıxartmaq üçün Ctrl+klikləyin.", + "message": "Bu sayt üçün uBlock₀-u qapatmaq məqsədilə klikləyin.\n\nYalnız bu səhifədə uBlock₀-u qapatmaq məqsədilə Ctrl+klikləyin.", "description": "Message to be read by screen readers" }, "popupPowerSwitchInfo2": { - "message": "Bu saytda uBlock₀-u fəallaşdırmaq üçün klikləyin.", + "message": "Bu sayt üçün uBlock₀-u aktivləşdirmək məqsədilə klikləyin.", "description": "Message to be read by screen readers" }, "popupBlockedRequestPrompt": { - "message": "tələb(request) bloklandı", + "message": "bloklanmış sorğular", "description": "English: requests blocked" }, "popupBlockedOnThisPagePrompt": { @@ -88,11 +88,11 @@ "description": "English: on this page" }, "popupBlockedStats": { - "message": "{{count}} və ya {{percent}}%", + "message": "{{count}} ({{percent}}%)", "description": "Example: 15 (13%)" }, "popupBlockedSinceInstallPrompt": { - "message": "quraşdırmadan bəri", + "message": "quraşdırılandan bəri", "description": "English: since install" }, "popupOr": { @@ -100,15 +100,15 @@ "description": "English: or" }, "popupBlockedOnThisPage_v2": { - "message": "Bu səhifədə əngəllənən", + "message": "Bu səhifədə əngəllənmiş", "description": "For the new mobile-friendly popup design" }, "popupBlockedSinceInstall_v2": { - "message": "Quraşdırmadan bəri əngəllənən", + "message": "Quraşdırılandan bəri bloklanıb", "description": "For the new mobile-friendly popup design" }, "popupDomainsConnected_v2": { - "message": "Bağlantı qurulmuş domenlər", + "message": "Qoşulmuş domenlər", "description": "For the new mobile-friendly popup design" }, "popupTipDashboard": { @@ -116,7 +116,7 @@ "description": "English: Click to open the dashboard" }, "popupTipZapper": { - "message": "Element silmə rejiminə keç", + "message": "Element təmizləyicisi rejiminə keç", "description": "Tooltip for the element-zapper icon in the popup panel" }, "popupTipPicker": { @@ -124,7 +124,7 @@ "description": "English: Enter element picker mode" }, "popupTipLog": { - "message": "Jurnalı aç", + "message": "Qeydiyyat jurnalını aç", "description": "Tooltip used for the logger icon in the panel" }, "popupTipReport": { @@ -132,7 +132,7 @@ "description": "Tooltip used for the 'chat' icon in the panel" }, "popupTipNoPopups": { - "message": "Bu sayt üçün bütün açılan pəncələri aç/bağla", + "message": "​Bu sayt üçün bütün açılan pəncərələrin bloklanmasını tənzimlə", "description": "Tooltip for the no-popups per-site switch" }, "popupTipNoPopups1": { @@ -340,7 +340,7 @@ "description": "Section for controlling user interface appearance" }, "settingsThemeLabel": { - "message": "Mövzu", + "message": "Tema", "description": "Label for checkbox to enable a custom dark theme" }, "settingsThemeAccent0Label": { @@ -404,7 +404,7 @@ "description": "Section for controlling advanced-user settings" }, "settingsAdvancedSynopsis": { - "message": "Yalnız texniki istifadəçilərə uyğun parametrlər", + "message": "Yalnız təcrübəli istifadəçilər üçün nəzərdə tutulmuş funksiyalar", "description": "Description of section controlling advanced-user settings" }, "settingsAdvancedUserSettings": { @@ -456,7 +456,7 @@ "description": "Describes the purpose of the 'Ignore generic cosmetic filters' feature." }, "3pSuspendUntilListsAreLoaded": { - "message": "Filter siyahıları yüklənənə kimi şəbəkə fəaliyyətini dayandır", + "message": "Filtr siyahıları yüklənənə kimi şəbəkə fəaliyyətini dayandır", "description": "A checkbox in the 'Filter lists' pane" }, "3pListsOfBlockedHostsHeader": { @@ -472,7 +472,7 @@ "description": "Filter lists section name" }, "3pGroupAds": { - "message": "Reklam", + "message": "Reklamlar", "description": "Filter lists section name" }, "3pGroupPrivacy": { @@ -528,7 +528,7 @@ "description": "used as a tooltip for the clock icon beside a list" }, "3pUpdating": { - "message": "Yenilənir...", + "message": "Yenilənir…", "description": "used as a tooltip for the spinner icon beside a list" }, "3pNetworkError": { @@ -908,7 +908,7 @@ "description": "A clickable link in the filter issue reporter section" }, "supportS1H": { - "message": "Dokumentasiya", + "message": "Sənədləşmə", "description": "Header of 'Documentation' section in Support pane" }, "supportS1P1": { @@ -924,7 +924,7 @@ "description": "First paragraph of 'Questions and support' section in Support pane" }, "supportS3H": { - "message": "Filter problemləri / vebsəhifə işləmir", + "message": "Filtr problemləri / veb sayt işləmir", "description": "Header of 'Filter issues' section in Support pane" }, "supportS3P1": { @@ -952,7 +952,7 @@ "description": "Header of 'Troubleshooting Information' section in Support pane" }, "supportS5P1": { - "message": "Aşağıdaki könüllülər sizə probleminizi aradan qaldırmağa çalışırkən faydalı ola biləcək texniki məlumatdır.", + "message": "Aşağıda köməkçilər sizə kömək etmək üçün problemi həll etməyə çalışarkən faydalı ola biləcək texniki məlumatlar verilmişdir.", "description": "First paragraph of 'Troubleshooting Information' section in Support pane" }, "supportS6H": { @@ -976,7 +976,7 @@ "description": "Label for the URL of the page" }, "supportS6Select1": { - "message": "Səhifə...", + "message": "Səhifə…", "description": "Label for widget to select type of issue" }, "supportS6Select1Option0": { @@ -1056,7 +1056,7 @@ "description": "Shown in the About pane" }, "aboutBackupDataButton": { - "message": "Fayla yaz", + "message": "Fayla yaz…", "description": "Text for button to create a backup of all settings" }, "aboutBackupFilename": { @@ -1064,11 +1064,11 @@ "description": "English: my-ublock-backup_{{datetime}}.txt" }, "aboutRestoreDataButton": { - "message": "Fayldan bərpa et...", + "message": "Fayldan bərpa et…", "description": "English: Restore from file..." }, "aboutResetDataButton": { - "message": "Standart parametrləri yüklə...", + "message": "Standart parametrləri yüklə…", "description": "English: Reset to default settings..." }, "aboutRestoreDataConfirm": { @@ -1236,11 +1236,11 @@ "description": "" }, "contextMenuBlockElementInFrame": { - "message": "Çərçivədəki elementi əngəllə", + "message": "Çərçivədəki elementi əngəllə…", "description": "An entry in the browser's contextual menu" }, "contextMenuSubscribeToList": { - "message": "Filter siyahısına abunə ol", + "message": "Filter siyahısına abunə ol…", "description": "An entry in the browser's contextual menu" }, "contextMenuTemporarilyAllowLargeMediaElements": { @@ -1248,7 +1248,7 @@ "description": "A context menu entry, present when large media elements have been blocked on the current site" }, "contextMenuViewSource": { - "message": "Mənbə kodunu nəzərdən keçir...", + "message": "Mənbə kodunu nəzərdən keçir…", "description": "A context menu entry, to view the source code of the target resource" }, "shortcutCapturePlaceholder": { diff --git a/src/_locales/hy/messages.json b/src/_locales/hy/messages.json index 1d4369a2d41b0..552935f6ee43b 100644 --- a/src/_locales/hy/messages.json +++ b/src/_locales/hy/messages.json @@ -900,11 +900,11 @@ "description": "Text for button which open an external web page in Support pane" }, "supportReportSpecificButton": { - "message": "Նոր զեկույց ստեղծել", + "message": "Նոր զեկույց ստեղծել GitHub-ում", "description": "Text for button which open an external web page in Support pane" }, "supportFindSpecificButton": { - "message": "Գտնել նման զեկույցներ", + "message": "Գտնել նման զեկույցներ GitHub-ում", "description": "A clickable link in the filter issue reporter section" }, "supportS1H": { diff --git a/src/_locales/it/messages.json b/src/_locales/it/messages.json index f3beb10ea7638..343dada288788 100644 --- a/src/_locales/it/messages.json +++ b/src/_locales/it/messages.json @@ -76,7 +76,7 @@ "description": "Message to be read by screen readers" }, "popupPowerSwitchInfo2": { - "message": "Clicca per abilitare uBlock₀ per questo sito.", + "message": "Fai clic per attivare uBlock₀ per questo sito.", "description": "Message to be read by screen readers" }, "popupBlockedRequestPrompt": { diff --git a/src/_locales/ko/messages.json b/src/_locales/ko/messages.json index 7a41435e84bdd..83ddbd8dc9f1d 100644 --- a/src/_locales/ko/messages.json +++ b/src/_locales/ko/messages.json @@ -488,7 +488,7 @@ "description": "Filter lists section name" }, "3pGroupCookies": { - "message": "쿠키 공지", + "message": "쿠키 알림", "description": "Filter lists section name" }, "3pGroupAnnoyances": { @@ -528,7 +528,7 @@ "description": "used as a tooltip for the clock icon beside a list" }, "3pUpdating": { - "message": "업데이트 중...", + "message": "업데이트 중…", "description": "used as a tooltip for the spinner icon beside a list" }, "3pNetworkError": { diff --git a/src/_locales/lv/messages.json b/src/_locales/lv/messages.json index 716365db27385..e1427a53d6ae9 100644 --- a/src/_locales/lv/messages.json +++ b/src/_locales/lv/messages.json @@ -64,7 +64,7 @@ "description": "Title for the asset viewer page" }, "advancedSettingsPageName": { - "message": "Papildu iestatījumi", + "message": "Izvērstie iestatījumi", "description": "Title for the advanced settings page" }, "popupPowerSwitchInfo": { @@ -72,7 +72,7 @@ "description": "English: Click: disable/enable uBlock₀ for this site.\n\nCtrl+click: disable uBlock₀ only on this page." }, "popupPowerSwitchInfo1": { - "message": "Noklikšķināt, lai atslēgtu uBlock₀ šajā vietnē.\n\nCtrl + klikšķis, lai atslēgtu uBlock₀ tikai šajā lapā.", + "message": "Noklikšķināt, lai atspējotu uBlock₀ šajā vietnē.\n\nCtrl + klikšķis, lai atspējotu uBlock₀ tikai šajā lapā.", "description": "Message to be read by screen readers" }, "popupPowerSwitchInfo2": { @@ -104,7 +104,7 @@ "description": "For the new mobile-friendly popup design" }, "popupBlockedSinceInstall_v2": { - "message": "Pavisam aizturētas", + "message": "Pavisam aizturētas kopš uzstādīšanas", "description": "For the new mobile-friendly popup design" }, "popupDomainsConnected_v2": { diff --git a/src/_locales/ro/messages.json b/src/_locales/ro/messages.json index 90527d16296f4..fe9359ef8d1a4 100644 --- a/src/_locales/ro/messages.json +++ b/src/_locales/ro/messages.json @@ -40,7 +40,7 @@ "description": "appears as tab name in dashboard" }, "whitelistPageName": { - "message": "Situri de încredere", + "message": "Siteuri de încredere", "description": "appears as tab name in dashboard" }, "shortcutsPageName": { diff --git a/src/_locales/sl/messages.json b/src/_locales/sl/messages.json index 72725eae3e841..4cb1ddf97e564 100644 --- a/src/_locales/sl/messages.json +++ b/src/_locales/sl/messages.json @@ -12,7 +12,7 @@ "description": "English: uBlock₀ — Dashboard" }, "dashboardUnsavedWarning": { - "message": "Pozor! Spremembe niso shranjene.", + "message": "Pozor: spremembe niso shranjene!", "description": "A warning in the dashboard when navigating away from unsaved changes" }, "dashboardUnsavedWarningStay": { @@ -508,7 +508,7 @@ "description": "Filter lists section name" }, "3pImport": { - "message": "Uvozi …", + "message": "Uvozi…", "description": "The label for the checkbox used to import external filter lists" }, "3pExternalListsHint": { @@ -528,7 +528,7 @@ "description": "used as a tooltip for the clock icon beside a list" }, "3pUpdating": { - "message": "Posodabljanje …", + "message": "Posodabljanje…", "description": "used as a tooltip for the spinner icon beside a list" }, "3pNetworkError": { @@ -552,7 +552,7 @@ "description": "Button in the 'My filters' pane" }, "1pExport": { - "message": "Izvozi …", + "message": "Izvozi…", "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": "Uvozi iz datoteke …", + "message": "Uvozi iz datoteke…", "description": "" }, "rulesExport": { - "message": "Izvozi v datoteko …", + "message": "Izvozi v datoteko…", "description": "Button in the 'My rules' pane" }, "rulesDefaultFileName": { @@ -628,7 +628,7 @@ "description": "English: a sort option for list of rules." }, "whitelistPrompt": { - "message": "Vaš seznam gostiteljskih naslovov, za katere želite, da je uBlock₀ izklopljen. En vnos na vrstico. Neveljavna gostiteljska imena bodo brez opozoril ignorirana.", + "message": "Vaš seznam gostiteljskih naslovov, za katere želite, da je uBlock Origin izklopljen. En vnos na vrstico.", "description": "A concise description of the 'Trusted sites' pane." }, "whitelistImport": { @@ -976,7 +976,7 @@ "description": "Label for the URL of the page" }, "supportS6Select1": { - "message": "Spletna stran …", + "message": "Spletna stran…", "description": "Label for widget to select type of issue" }, "supportS6Select1Option0": { @@ -1240,7 +1240,7 @@ "description": "An entry in the browser's contextual menu" }, "contextMenuSubscribeToList": { - "message": "Naroči se na seznam filtrov …", + "message": "Naroči se na seznam filtrov…", "description": "An entry in the browser's contextual menu" }, "contextMenuTemporarilyAllowLargeMediaElements": { diff --git a/src/_locales/vi/messages.json b/src/_locales/vi/messages.json index 9b09b9f0f1794..6137b30a96315 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": "Đã bị chặn kể từ khi cài đặt", + "message": "Đã chặn từ khi cài đặt", "description": "For the new mobile-friendly popup design" }, "popupDomainsConnected_v2": { @@ -152,7 +152,7 @@ "description": "Tooltip for the no-large-media per-site switch" }, "popupTipNoLargeMedia2": { - "message": "Bấm để ngừng chặn các phần tử đa phương tiện kích thước lớn trên trang này", + "message": "Bấm để ngừng chặn các phần tử đa phương tiện nặng trên trang này", "description": "Tooltip for the no-large-media per-site switch" }, "popupTipNoCosmeticFiltering": { @@ -352,7 +352,7 @@ "description": "" }, "settingsAdvancedUserPrompt": { - "message": "Tôi là một người dùng có kinh nghiệm (Yêu cầu đọc qua)", + "message": "Tôi là một người dùng có kinh nghiệm", "description": "Checkbox to let user access advanced, technical features" }, "settingsPrefetchingDisabledPrompt": { @@ -452,7 +452,7 @@ "description": "This will cause uBO to ignore all generic cosmetic filters." }, "3pIgnoreGenericCosmeticFiltersInfo": { - "message": "Bộ lọc phần tử chung là những bộ lọc phần tử được áp dụng cho cho mọi trang web. Kích hoạt tùy chọn này sẽ giảm bớt sức nặng lên cpu và bộ nhớ do không còn phải xử lí các bộ lọc phần tử chung.\n\nTùy chọn này được khuyến nghị kích hoạt trên thiết bị cấu hình thấp.", + "message": "Bộ lọc phần tử chung là những bộ lọc phần tử áp dụng trên mọi trang web. Kích hoạt tùy chọn này để giảm bớt sức nặng lên CPU và bộ nhớ do không còn phải xử lí các bộ lọc phần tử chung.\n\nNên kích hoạt tùy chọn này trên thiết bị cấu hình thấp.", "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": "Một URL mỗi dòng. URL không hợp lệ sẽ âm thầm bỏ qua.", + "message": "Một URL trên dòng. URL không hợp lệ sẽ âm thầm bỏ qua.", "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": "Cập nhật lần cuối: {{ago}}.\nBấm để buộc cập nhật.", + "message": "Cập nhật lần cuối: {{ago}}.\nNhấn để cập nhật ngay.", "description": "used as a tooltip for the clock icon beside a list" }, "3pUpdating": { @@ -628,7 +628,7 @@ "description": "English: a sort option for list of rules." }, "whitelistPrompt": { - "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.", + "message": "Các đường dẫn trang web đáng tin cậy cố định vô hiệu hóa uBlock Origin. Một mục trên dòng.", "description": "A concise description of the 'Trusted sites' pane." }, "whitelistImport": { @@ -688,7 +688,7 @@ "description": "Tooltip for the popup panel button in the logger page" }, "loggerInfoTip": { - "message": "uBlock Origin wiki: Các logger", + "message": "uBlock Origin wiki: Các nhật ký", "description": "Tooltip for the top-right info label in the logger page" }, "loggerClearTip": { @@ -732,7 +732,7 @@ "description": "A keyword in the built-in row filtering expression" }, "loggerRowFiltererBuiltinModified": { - "message": "đã sửa đổi", + "message": "sửa đổi", "description": "A keyword in the built-in row filtering expression" }, "loggerRowFiltererBuiltin1p": { @@ -892,7 +892,7 @@ "description": "Label for radio-button to pick export text format" }, "loggerExportEncodeMarkdown": { - "message": "Đánh dấu", + "message": "Markdown", "description": "Label for radio-button to pick export text format" }, "supportOpenButton": { @@ -904,7 +904,7 @@ "description": "Text for button which open an external web page in Support pane" }, "supportFindSpecificButton": { - "message": "Tìm các báo cáo tương tự trên Github", + "message": "Tìm báo cáo tương tự trên GitHub", "description": "A clickable link in the filter issue reporter section" }, "supportS1H": { @@ -1012,7 +1012,7 @@ "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\")", + "message": "Đánh dấu trang web là \"NSFW\" (\"Not Safe For Work\", \"Không xem nơi đông người\")", "description": "A checkbox to use for NSFW sites" }, "aboutPrivacyPolicy": { @@ -1064,7 +1064,7 @@ "description": "English: my-ublock-backup_{{datetime}}.txt" }, "aboutRestoreDataButton": { - "message": "Khôi phục từ tệp tin…", + "message": "Khôi phục từ tệp…", "description": "English: Restore from file..." }, "aboutResetDataButton": { diff --git a/src/css/codemirror.css b/src/css/codemirror.css index d56d8c0da1a13..f0bd4d26d6b4d 100644 --- a/src/css/codemirror.css +++ b/src/css/codemirror.css @@ -320,9 +320,12 @@ html:not(.mobile) .cm-search-widget .fa-icon:not(.fa-icon-ro):hover { .CodeMirror-lintmarker > * { position: absolute; } -.CodeMirror-lintmarker[data-error="y"] { +.CodeMirror-lintmarker[data-lint="error"] { background-color: var(--sf-error-ink); } +.CodeMirror-lintmarker[data-lint="warning"] { + background-color: var(--sf-warning-ink); + } .CodeMirror-lintmarker .msg { background-color: var(--surface-0); border: 1px solid var(--sf-error-ink); @@ -340,7 +343,8 @@ html:not(.mobile) .cm-search-widget .fa-icon:not(.fa-icon-ro):hover { top: 15%; width: 70%; } -.CodeMirror-lintmarker[data-error="y"] svg { +.CodeMirror-lintmarker[data-lint="error"] svg, +.CodeMirror-lintmarker[data-lint="warning"] svg { display: none; } .CodeMirror-lintmarker[data-fold="start"] { @@ -352,8 +356,10 @@ html:not(.mobile) .cm-search-widget .fa-icon:not(.fa-icon-ro):hover { .CodeMirror-lintmarker[data-fold="end"] { fill: var(--border-2); } -.CodeMirror-lintmarker[data-error="y"]:hover > span, -.CodeMirror-lintmarker[data-error="y"] > span:hover { +.CodeMirror-lintmarker[data-lint="error"]:hover > span, +.CodeMirror-lintmarker[data-lint="error"] > span:hover, +.CodeMirror-lintmarker[data-lint="warning"]:hover > span, +.CodeMirror-lintmarker[data-lint="warning"] > span:hover { display: initial; } diff --git a/src/css/themes/default.css b/src/css/themes/default.css index c78b4a255c513..d03e1f1231b49 100644 --- a/src/css/themes/default.css +++ b/src/css/themes/default.css @@ -290,7 +290,7 @@ --sf-unicode-ink: var(--ink-1); --sf-value-ink: #974900 /* h:30 S:100 Luv:40 */; --sf-variable-ink: var(--ink-1); - --sf-warning-ink: #e49d00; /* h:50 S:100 Luv:70 */ + --sf-warning-ink: #ffbb03; /* h:50 S:100 Luv:70 */ --sf-warning-surface: #e49d0033; /* h:50 S:100 Luv:70 @ 20% */ /* syntax highlight: dynamic filtering */ diff --git a/src/js/1p-filters.js b/src/js/1p-filters.js index 934acd466d17b..9396dd117979d 100644 --- a/src/js/1p-filters.js +++ b/src/js/1p-filters.js @@ -80,9 +80,9 @@ uBlockDashboard.patchCodeMirrorEditor(cmEditor); } vAPI.messaging.send('dashboard', { - what: 'getTrustedScriptletTokens', + what: 'getTrustedTokens', }).then(tokens => { - cmEditor.setOption('trustedScriptletTokens', tokens); + cmEditor.setOption('trustedTokens', tokens); }); /******************************************************************************/ diff --git a/src/js/asset-viewer.js b/src/js/asset-viewer.js index 62f90d2795013..697c468ed4e5f 100644 --- a/src/js/asset-viewer.js +++ b/src/js/asset-viewer.js @@ -73,9 +73,9 @@ import { dom, qs$ } from './dom.js'; }); vAPI.messaging.send('dashboard', { - what: 'getTrustedScriptletTokens', + what: 'getTrustedTokens', }).then(tokens => { - cmEditor.setOption('trustedScriptletTokens', tokens); + cmEditor.setOption('trustedTokens', tokens); }); const details = await vAPI.messaging.send('default', { diff --git a/src/js/codemirror/ubo-static-filtering.js b/src/js/codemirror/ubo-static-filtering.js index 2dea1f8ab66ad..dcf128cb79856 100644 --- a/src/js/codemirror/ubo-static-filtering.js +++ b/src/js/codemirror/ubo-static-filtering.js @@ -46,10 +46,10 @@ CodeMirror.defineOption('trustedSource', false, (cm, trusted) => { })); }); -CodeMirror.defineOption('trustedScriptletTokens', undefined, (cm, tokens) => { +CodeMirror.defineOption('trustedTokens', undefined, (cm, tokens) => { if ( tokens === undefined || tokens === null ) { return; } if ( typeof tokens[Symbol.iterator] !== 'function' ) { return; } - self.dispatchEvent(new CustomEvent('trustedScriptletTokens', { + self.dispatchEvent(new CustomEvent('trustedTokens', { detail: new Set(tokens), })); }); @@ -226,6 +226,7 @@ const uBOStaticFilteringMode = (( ) => { this.astParser = new sfp.AstFilterParser({ interactive: true, nativeCssHas: vAPI.webextFlavor.env.includes('native_css_has'), + canFilterResponseBody: vAPI.webextFlavor.env.includes('html_filtering'), }); this.astWalker = this.astParser.getWalker(); this.currentWalkerNode = 0; @@ -234,8 +235,8 @@ const uBOStaticFilteringMode = (( ) => { const { trusted } = ev.detail; this.astParser.options.trustedSource = trusted; }); - self.addEventListener('trustedScriptletTokens', ev => { - this.astParser.options.trustedScriptletTokens = ev.detail; + self.addEventListener('trustedTokens', ev => { + this.astParser.options.trustedTokens = ev.detail; }); } } @@ -346,6 +347,7 @@ function initHints() { const astParser = new sfp.AstFilterParser({ interactive: true, nativeCssHas: vAPI.webextFlavor.env.includes('native_css_has'), + canFilterResponseBody: vAPI.webextFlavor.env.includes('html_filtering'), }); const proceduralOperatorNames = new Map( Array.from(sfp.proceduralOperatorTokens) @@ -715,6 +717,7 @@ CodeMirror.registerHelper('fold', 'ubo-static-filtering', (( ) => { const astParser = new sfp.AstFilterParser({ interactive: true, nativeCssHas: vAPI.webextFlavor.env.includes('native_css_has'), + canFilterResponseBody: vAPI.webextFlavor.env.includes('html_filtering'), }); const changeset = []; @@ -760,6 +763,9 @@ CodeMirror.registerHelper('fold', 'ubo-static-filtering', (( ) => { case sfp.AST_ERROR_UNTRUSTED_SOURCE: msg = `${msg}: Filter requires trusted source`; break; + case sfp.AST_ERROR_CAPABILITY: + msg = `Filter unsupported on current platform`; + return { lint: 'warning', msg }; default: if ( astParser.isCosmeticFilter() && astParser.result.error ) { msg = `${msg}: ${astParser.result.error}`; @@ -819,6 +825,14 @@ CodeMirror.registerHelper('fold', 'ubo-static-filtering', (( ) => { '', ], }, + 'warning': { + node: null, + html: [ + '
 ', + '', + '
', + ], + }, 'if-start': { node: null, html: [ @@ -963,8 +977,8 @@ CodeMirror.registerHelper('fold', 'ubo-static-filtering', (( ) => { }; const processDeletion = (doc, change) => { - let { from, to } = change; - doc.eachLine(from.line, to.line, lineHandle => { + const { from, to } = change; + doc.eachLine(from.line, to.line + (to.ch ? 1 : 0), lineHandle => { const marker = extractMarker(lineHandle); if ( marker === null ) { return; } if ( marker.dataset.error === 'y' ) { @@ -1116,8 +1130,8 @@ CodeMirror.registerHelper('fold', 'ubo-static-filtering', (( ) => { astParser.options.trustedSource = trusted; }); - self.addEventListener('trustedScriptletTokens', ev => { - astParser.options.trustedScriptletTokens = ev.detail; + self.addEventListener('trustedTokens', ev => { + astParser.options.trustedTokens = ev.detail; }); CodeMirror.defineInitHook(cm => { diff --git a/src/js/contentscript-extra.js b/src/js/contentscript-extra.js index 01b83e3d0af15..482cb62e54a02 100644 --- a/src/js/contentscript-extra.js +++ b/src/js/contentscript-extra.js @@ -45,6 +45,13 @@ const regexFromString = (s, exact = false) => { return new RegExp(exact ? `^${reStr}$` : reStr); }; +const triggerDOMChange = ( ) => { + if ( typeof vAPI !== 'object' ) { return; } + if ( vAPI === null ) { return; } + const filterer = vAPI.domFilterer && vAPI.domFilterer.proceduralFilterer; + filterer?.onDOMChanged([ null ]); +}; + // 'P' stands for 'Procedural' class PSelectorTask { @@ -159,13 +166,7 @@ class PSelectorMatchesMediaTask extends PSelectorTask { super(); this.mql = window.matchMedia(task[1]); if ( this.mql.media === 'not all' ) { return; } - this.mql.addEventListener('change', ( ) => { - if ( typeof vAPI !== 'object' ) { return; } - if ( vAPI === null ) { return; } - const filterer = vAPI.domFilterer && vAPI.domFilterer.proceduralFilterer; - if ( filterer instanceof Object === false ) { return; } - filterer.onDOMChanged([ null ]); - }); + this.mql.addEventListener('change', triggerDOMChange); } transpose(node, output) { if ( this.mql.matches === false ) { return; } @@ -179,12 +180,17 @@ class PSelectorMatchesPathTask extends PSelectorTask { this.needle = regexFromString( task[1].replace(/\P{ASCII}/gu, s => encodeURIComponent(s)) ); + if ( PSelectorMatchesPathTask.#listener ) { return; } + PSelectorMatchesPathTask.#listener = true; + if ( Boolean(self.navigation) === false ) { return; } + self.navigation.addEventListener('navigate', triggerDOMChange); } transpose(node, output) { if ( this.needle.test(self.location.pathname + self.location.search) ) { output.push(node); } } + static #listener; } class PSelectorMatchesPropTask extends PSelectorTask { @@ -379,7 +385,6 @@ PSelectorUpwardTask.prototype.s = ''; class PSelectorWatchAttrs extends PSelectorTask { constructor(task) { super(); - this.observer = null; this.observed = new WeakSet(); this.observerOptions = { attributes: true, @@ -390,23 +395,16 @@ class PSelectorWatchAttrs extends PSelectorTask { this.observerOptions.attributeFilter = task[1]; } } - // TODO: Is it worth trying to re-apply only the current selector? - handler() { - const filterer = - vAPI.domFilterer && vAPI.domFilterer.proceduralFilterer; - if ( filterer instanceof Object ) { - filterer.onDOMChanged([ null ]); - } - } transpose(node, output) { output.push(node); if ( this.observed.has(node) ) { return; } - if ( this.observer === null ) { - this.observer = new MutationObserver(this.handler); + if ( PSelectorWatchAttrs.#observer === undefined ) { + PSelectorWatchAttrs.#observer = new MutationObserver(triggerDOMChange); } - this.observer.observe(node, this.observerOptions); + PSelectorWatchAttrs.#observer.observe(node, this.observerOptions); this.observed.add(node); } + static #observer; } class PSelectorXpathTask extends PSelectorTask { diff --git a/src/js/messaging.js b/src/js/messaging.js index 7735d77ba0a88..e76bd7ac42b03 100644 --- a/src/js/messaging.js +++ b/src/js/messaging.js @@ -42,6 +42,7 @@ import cacheStorage from './cachestorage.js'; import cosmeticFilteringEngine from './cosmetic-filtering.js'; import { denseBase64 } from './base64-custom.js'; import { filteringBehaviorChanged } from './broadcast.js'; +import { getTrustedTokens } from './trusted-tokens.js'; import htmlFilteringEngine from './html-filtering.js'; import { i18n$ } from './i18n.js'; import io from './assets.js'; @@ -155,7 +156,7 @@ const onMessage = function(request, sender, callback) { case 'getAppData': response = { - name: browser.runtime.getManifest().name, + name: self.browser.runtime.getManifest().name, version: vAPI.app.version, canBenchmark: µb.hiddenSettings.benchmarkDatasetURL !== 'unset', }; @@ -165,8 +166,8 @@ const onMessage = function(request, sender, callback) { response = getDomainNames(request.targets); break; - case 'getTrustedScriptletTokens': - response = redirectEngine.getTrustedScriptletTokens(); + case 'getTrustedTokens': + response = Array.from(getTrustedTokens()); break; case 'getWhitelist': diff --git a/src/js/redirect-engine.js b/src/js/redirect-engine.js index d0f5fed4d4d7f..2c56ce48fe931 100644 --- a/src/js/redirect-engine.js +++ b/src/js/redirect-engine.js @@ -406,7 +406,7 @@ class RedirectEngine { for ( const [ name, entry ] of this.resources ) { out.set(name, { canInject: typeof entry.data === 'string', - canRedirect: entry.warURL !== undefined, + canRedirect: Boolean(entry.warURL ?? entry.data), aliasOf: '', extensionPath: entry.warURL, }); @@ -423,26 +423,6 @@ class RedirectEngine { }); } - getTrustedScriptletTokens() { - const out = []; - const isTrustedScriptlet = entry => { - if ( entry.requiresTrust !== true ) { return false; } - if ( entry.warURL !== undefined ) { return false; } - if ( typeof entry.data !== 'string' ) { return false; } - if ( entry.name.endsWith('.js') === false ) { return false; } - return true; - }; - for ( const [ name, entry ] of this.resources ) { - if ( isTrustedScriptlet(entry) === false ) { continue; } - out.push(name.slice(0, -3)); - } - for ( const [ alias, name ] of this.aliases ) { - if ( out.includes(name.slice(0, -3)) === false ) { continue; } - out.push(alias.slice(0, -3)); - } - return out; - } - selfieFromResources(storage) { return storage.toCache(RESOURCES_SELFIE_NAME, { version: RESOURCES_SELFIE_VERSION, diff --git a/src/js/redirect-resources.js b/src/js/redirect-resources.js index 323aa89eda124..313d50ba6a0f0 100644 --- a/src/js/redirect-resources.js +++ b/src/js/redirect-resources.js @@ -100,8 +100,9 @@ export default new Map([ data: 'text', } ], [ 'google-ima-dai.js', { - aliases: [ 'google-ima3-dai' ], /* adguard compatibility */ + alias: 'google-ima3-dai', /* adguard compatibility */ data: 'text', + requiresTrust: true, } ], [ 'googlesyndication_adsbygoogle.js', { alias: [ diff --git a/src/js/resources/attribute.js b/src/js/resources/attribute.js index 47f65c08d3efe..b7052790fc4f5 100644 --- a/src/js/resources/attribute.js +++ b/src/js/resources/attribute.js @@ -218,79 +218,106 @@ registerScriptlet(trustedSetAttr, { * * @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. + * + * @params [...varargs] + * Optional, any of following pairs of parameters: + * - `quitAfter, sec`: where `sec` is the number of seconds after which the + * scriptlet ceases to be active. This has precedence over `stay` behavior. * */ export function removeAttr( rawToken = '', rawSelector = '', - behavior = '' + behavior = '', + ...varargs ) { if ( typeof rawToken !== 'string' ) { return; } if ( rawToken === '' ) { return; } const safe = safeSelf(); - const logPrefix = safe.makeLogPrefix('remove-attr', rawToken, rawSelector, behavior); + const logPrefix = safe.makeLogPrefix('remove-attr', + rawToken, rawSelector, behavior, ...varargs + ); const tokens = safe.String_split.call(rawToken, /\s*\|\s*/); - const selector = tokens - .map(a => `${rawSelector}[${CSS.escape(a)}]`) - .join(','); + const selector = tokens.map(a => { + const b = CSS.escape(a); + return rawSelector.includes(`[${b}]`) ? rawSelector : `${rawSelector}[${b}]`; + }).join(','); + const lazily = /\basap\b/.test(behavior) === false; + const options = safe.parseVarargs(varargs); 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 = onIdleFn(( ) => { - timerId = undefined; + const rmattrFromNode = node => { + for ( const attr of tokens ) { + if ( node.hasAttribute(attr) === false ) { continue; } + node.removeAttribute(attr); + safe.uboLog(logPrefix, `Removed attribute '${attr}'`); + } + }; + const rmattr = nodes => { + for ( const node of nodes ?? document.querySelectorAll(selector) ) { + rmattrFromNode(node); + } + }; + const rmAttrLazily = ( ) => { + if ( rmAttrLazily.timer !== undefined ) { return; } + rmAttrLazily.timer = onIdleFn(( ) => { + rmAttrLazily.timer = undefined; rmattr(); }, { timeout: 17 }); }; - const rmattr = ( ) => { - if ( timerId !== undefined ) { - offIdleFn(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}'`); + const mutationHandler = mutations => { + for ( const { addedNodes, removedNodes } of mutations ) { + for ( const node of addedNodes ) { + if ( node.nodeType !== 1 ) { continue; } + if ( lazily ) { return rmAttrLazily(); } + if ( node.matches(selector) ) { + rmattrFromNode(node); + } + if ( node.childElementCount ) { + rmattr(node.querySelectorAll(selector)); + } + } + if ( lazily ) { return; } + for ( const node of removedNodes ) { + if ( node.nodeType !== 1 ) { continue; } + if ( node.matches(selector) ) { + rmattrFromNode(node); } } - } catch { } }; - 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; } - } + const stop = ( ) => { + if ( start.observer ) { + start.observer.disconnect(); + start.observer = undefined; + } + if ( rmAttrLazily.timer ) { + offIdleFn(rmAttrLazily.timer); + rmAttrLazily.timer = undefined; + } + if ( safe.logLevel > 1 ) { + safe.uboLog(logPrefix, 'Quitting'); } - if ( skip ) { return; } - asap ? rmattr() : rmattrAsync(); }; const start = ( ) => { rmattr(); - if ( /\bstay\b/.test(behavior) === false ) { return; } - const observer = new MutationObserver(mutationHandler); - observer.observe(document, { + if ( /\bstay\b/.test(behavior) === false ) { + if ( options.quitAfter === undefined ) { return; } + } + start.observer = new MutationObserver(mutationHandler); + start.observer.observe(document, { attributes: true, attributeFilter: tokens, childList: true, subtree: true, }); + if ( options.quitAfter ) { + runAt(( ) => { + self.setTimeout(stop, options.quitAfter * 1000); + }, 'load'); + } }; runAt(( ) => { start(); }, safe.String_split.call(behavior, /\s+/)); } diff --git a/src/js/resources/localstorage.js b/src/js/resources/localstorage.js index 601ea08b49e15..69c037090437a 100644 --- a/src/js/resources/localstorage.js +++ b/src/js/resources/localstorage.js @@ -127,57 +127,6 @@ registerScriptlet(setLocalStorageItemFn, { ], }); -/******************************************************************************/ - -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 diff --git a/src/js/resources/prevent-clipboard-write.js b/src/js/resources/prevent-clipboard-write.js index df6fbd1dea904..d5064a3b63439 100644 --- a/src/js/resources/prevent-clipboard-write.js +++ b/src/js/resources/prevent-clipboard-write.js @@ -21,6 +21,7 @@ import { proxyApplyFn } from './proxy-apply.js'; import { registerScriptlet } from './base.js'; +import { runAt } from './run-at.js'; import { safeSelf } from './safe-self.js'; /******************************************************************************/ @@ -54,22 +55,28 @@ function preventClipboardWrite(matches = '', ...varargs) { const extraArgs = safe.parseVarargs(varargs); const excludePattern = extraArgs.excludeMatches && safe.initPattern(extraArgs.excludeMatches); + const htmlTemplate = [ + '
', + '${warning}\n', + '', + '
', + ].join(''); const domAlert = clipboardText => { const doc = document; - const div = doc.createElement('div'); - const span = doc.createElement('span'); - span.style = 'flex-grow:1;padding:0.5em 0 0.5em 0.5em;'; const domAlert = extraArgs.domAlert.replace(/\\n/g, '\n'); - const placeholder = /\$\{text\}/.exec(domAlert); - if ( placeholder ) { + let html; + if ( domAlert.includes('${text}') ) { const code = doc.createElement('code'); const styles = [ 'background-color: #ddc', 'display: inline-block', 'font-family: monospace', + 'font-size: 100%', 'max-height: 8em', 'overflow: auto', 'padding: 0.25em', + 'user-select: all', + 'width: 100%;', 'word-break: break-all' ]; if ( Boolean(extraArgs.selectable ?? true) === false ) { @@ -77,29 +84,23 @@ function preventClipboardWrite(matches = '', ...varargs) { } code.style = styles.join(';'); code.textContent = clipboardText; - span.append( - domAlert.slice(0, placeholder.index), - code, - domAlert.slice(placeholder.index + placeholder[0].length) + html = htmlTemplate.replace('${warning}', + domAlert.replace('${text}', code.outerHTML) ); } else { - span.append(domAlert); + html = htmlTemplate.replace('${warning}', domAlert); } - const button = doc.createElement('button'); - button.style = 'font-size:32px;padding:0.5em'; - button.textContent = '×'; + if ( currentAlert ) { currentAlert.remove(); } + const domParser = new DOMParser(); + const fragment = domParser.parseFromString(html, 'text/html'); + currentAlert = fragment.querySelector('div'); + const button = currentAlert.querySelector('button'); button.addEventListener('click', ( ) => { if ( currentAlert === null ) { return; } currentAlert.remove(); currentAlert = null; }); - div.append(span, button); - div.style = 'background-color:beige;color:black;border:1px solid black;display:flex;font-family:sans-serif;font-size:medium;position:fixed;top:0;white-space:pre-wrap;width:100%;z-index:2147483647'; - doc.documentElement.append(div); - if ( currentAlert ) { - currentAlert.remove(); - } - currentAlert = div; + doc.documentElement.append(currentAlert); }; let currentAlert = null; const prevent = text => { @@ -130,16 +131,19 @@ function preventClipboardWrite(matches = '', ...varargs) { return context.reflect(); }, { skipToString: true }); }; - self.addEventListener('mousedown', installTraps, { - once: true, - capture: true, - }); + runAt(( ) => { + self.document.addEventListener('mousemove', installTraps, { + once: true, + capture: true, + }); + }, 'interactive') } registerScriptlet(preventClipboardWrite, { name: 'prevent-clipboard-write.js', requiresTrust: true, dependencies: [ proxyApplyFn, + runAt, safeSelf, ], }); diff --git a/src/js/resources/prevent-xhr.js b/src/js/resources/prevent-xhr.js index e0bce3b8c9840..478470872a70f 100644 --- a/src/js/resources/prevent-xhr.js +++ b/src/js/resources/prevent-xhr.js @@ -25,7 +25,10 @@ import { matchObjectPropertiesFn, parsePropertiesToMatchFn, } from './utils.js'; -import { proxyApplyFn } from './proxy-apply.js'; +import { + proxyApplyFn, + proxyToStringFn, +} from './proxy-apply.js'; import { registerScriptlet } from './base.js'; import { safeSelf } from './safe-self.js'; @@ -34,6 +37,62 @@ import { safeSelf } from './safe-self.js'; /******************************************************************************/ +export function modifyXhrResponseFn( + propsToMatch = '', + modifierFn = '' +) { + if ( typeof propsToMatch !== 'string' ) { return; } + const safe = safeSelf(); + if ( modifyXhrResponseFn.xhrInstances === undefined ) { + modifyXhrResponseFn.xhrInstances = new WeakMap(); + } + const propNeedles = parsePropertiesToMatchFn(propsToMatch, 'url'); + const NativeXMLHttpRequest = self.XMLHttpRequest; + const TrappedXMLHttpRequest = class XMLHttpRequest extends NativeXMLHttpRequest { + open(method, url, ...args) { + const haystack = { method, url }; + if ( propsToMatch === '' ) { + safe.uboLog(`modifyXhrResponseFn() / Called: ${safe.JSON_stringify(haystack, null, 2)}`); + } else if ( matchObjectPropertiesFn(propNeedles, haystack) ) { + modifyXhrResponseFn.xhrInstances.set(this, modifierFn); + } + return super.open(method, url, ...args); + } + get response() { + const modifierFn = modifyXhrResponseFn.xhrInstances.get(this); + return modifierFn + ? modifierFn(this, super.response) + : super.response; + } + get responseText() { + const modifierFn = modifyXhrResponseFn.xhrInstances.get(this); + return modifierFn + ? modifierFn(this, super.responseText) + : super.responseText; + } + get responseXML() { + const modifierFn = modifyXhrResponseFn.xhrInstances.get(this); + return modifierFn + ? modifierFn(this, super.responseXML) + : super.responseXML; + } + }; + proxyToStringFn(TrappedXMLHttpRequest.prototype.open, NativeXMLHttpRequest.prototype.open); + proxyToStringFn(TrappedXMLHttpRequest, NativeXMLHttpRequest); + self.XMLHttpRequest = TrappedXMLHttpRequest; +} +registerScriptlet(modifyXhrResponseFn, { + name: 'modify-xhr-response.fn', + dependencies: [ + matchObjectPropertiesFn, + parsePropertiesToMatchFn, + proxyToStringFn, + safeSelf, + ], +}); + +/******************************************************************************/ + function preventXhrFn( trusted = false, propsToMatch = '', diff --git a/src/js/resources/proxy-apply.js b/src/js/resources/proxy-apply.js index c64b043e341f9..9f32a643ff387 100644 --- a/src/js/resources/proxy-apply.js +++ b/src/js/resources/proxy-apply.js @@ -24,6 +24,32 @@ import { registerScriptlet } from './base.js'; /******************************************************************************/ +export function proxyToStringFn(proxiedFn, nativeFn) { + if ( proxyToStringFn.proxies === undefined ) { + proxyToStringFn.proxies = new WeakMap(); + proxyToStringFn.nativeToString = Function.prototype.toString; + const proxiedToString = new Proxy(Function.prototype.toString, { + apply(target, thisArg) { + let proxied = thisArg; + for(;;) { + const fn = proxyToStringFn.proxies.get(proxied); + if ( fn === undefined ) { break; } + proxied = fn; + } + return proxyToStringFn.nativeToString.call(proxied); + } + }); + proxyToStringFn.proxies.set(proxiedToString, proxyToStringFn.nativeToString); + Function.prototype.toString = proxiedToString; + } + proxyToStringFn.proxies.set(proxiedFn, nativeFn); +} +registerScriptlet(proxyToStringFn, { + name: 'proxy-tostring.fn', +}); + +/******************************************************************************/ + export function proxyApplyFn( target = '', handler = '', diff --git a/src/js/resources/scriptlets.js b/src/js/resources/scriptlets.js index 36503122fa1c0..cc733487d75aa 100755 --- a/src/js/resources/scriptlets.js +++ b/src/js/resources/scriptlets.js @@ -36,9 +36,9 @@ import './prevent-fetch.js'; import './prevent-innerHTML.js'; import './prevent-navigation.js'; import './prevent-settimeout.js'; -import './prevent-xhr.js'; import './replace-argument.js'; import './spoof-css.js'; +import './vod.js'; import { collateFetchArgumentsFn, @@ -49,11 +49,15 @@ import { onIdleFn, parsePropertiesToMatchFn, } from './utils.js'; -import { runAt, runAtHtmlElementFn } from './run-at.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 { modifyXhrResponseFn } from './prevent-xhr.js'; import { proxyApplyFn } from './proxy-apply.js'; import { registeredScriptlets } from './base.js'; import { safeSelf } from './safe-self.js'; @@ -97,15 +101,6 @@ function replaceNodeTextFn( const reExcludes = extraArgs.excludes ? safe.patternToRegex(extraArgs.excludes, 'ms') : null; - const stop = (takeRecord = true) => { - if ( takeRecord ) { - handleMutations(observer.takeRecords()); - } - observer.disconnect(); - if ( safe.logLevel > 1 ) { - safe.uboLog(logPrefix, 'Quitting'); - } - }; const textContentFactory = (( ) => { const out = { createScript: s => s }; const { trustedTypes: tt } = self; @@ -118,19 +113,19 @@ function replaceNodeTextFn( } return out; })(); - let sedCount = extraArgs.sedCount || 0; + let sedCount = extraArgs.sedCount ?? Number.MAX_SAFE_INTEGER; const handleNode = node => { const before = node.textContent; if ( reIncludes ) { reIncludes.lastIndex = 0; - if ( safe.RegExp_test(reIncludes, before) === false ) { return true; } + if ( safe.RegExp_test(reIncludes, before) === false ) { return; } } if ( reExcludes ) { reExcludes.lastIndex = 0; - if ( safe.RegExp_test(reExcludes, before) ) { return true; } + if ( safe.RegExp_test(reExcludes, before) ) { return; } } rePattern.lastIndex = 0; - if ( safe.RegExp_test(rePattern, before) === false ) { return true; } + if ( safe.RegExp_test(rePattern, before) === false ) { return; } rePattern.lastIndex = 0; const after = pattern !== '' ? before.replace(rePattern, replacement) @@ -142,44 +137,65 @@ function replaceNodeTextFn( safe.uboLog(logPrefix, `Text before:\n${before.trim()}`); } safe.uboLog(logPrefix, `Text after:\n${after.trim()}`); - return sedCount === 0 || (sedCount -= 1) !== 0; + sedCount -= 1; }; - const handleMutations = mutations => { - for ( const mutation of mutations ) { - for ( const node of mutation.addedNodes ) { - if ( reNodeName.test(node.nodeName) === false ) { continue; } - if ( handleNode(node) ) { continue; } - stop(false); return; - } - } - }; - const observer = new MutationObserver(handleMutations); - observer.observe(document, { childList: true, subtree: true }); - if ( document.documentElement ) { - const treeWalker = document.createTreeWalker( - document.documentElement, + const handleTree = root => { + const treeWalker = document.createTreeWalker(root, NodeFilter.SHOW_ELEMENT | NodeFilter.SHOW_TEXT ); + const { currentScript } = document; let count = 0; for (;;) { const node = treeWalker.nextNode(); - count += 1; if ( node === null ) { break; } - if ( reNodeName.test(node.nodeName) === false ) { continue; } - if ( node === document.currentScript ) { continue; } - if ( handleNode(node) ) { continue; } - stop(); break; + count += 1; + if ( node === currentScript ) { continue; } + if ( reNodeName.test(node.nodeName) ) { + handleNode(node); + } else if ( node.nodeName === 'TEMPLATE' ) { + count += handleTree(node.content); + } else { + continue; + } + if ( sedCount === 0 ) { break; } } + return count; + }; + if ( document.documentElement ) { + const count = handleTree(document.documentElement); safe.uboLog(logPrefix, `${count} nodes present before installing mutation observer`); } - if ( extraArgs.stay ) { return; } - runAt(( ) => { - const quitAfter = extraArgs.quitAfter || 0; - if ( quitAfter !== 0 ) { - setTimeout(( ) => { stop(); }, quitAfter); - } else { - stop(); + const stay = Boolean(extraArgs.stay); + if ( sedCount === 0 && stay === false ) { return; } + const stop = (takeRecord = true) => { + const mutations = takeRecord ? observer.takeRecords() : []; + observer.disconnect(); + handleMutations(mutations); + if ( safe.logLevel > 1 ) { + safe.uboLog(logPrefix, 'Quitting'); + } + }; + const handleMutations = mutations => { + for ( const mutation of mutations ) { + for ( const node of mutation.addedNodes ) { + if ( reNodeName.test(node.nodeName) ) { + handleNode(node); + } else if ( node.nodeName === 'TEMPLATE' ) { + handleTree(node.content); + } else { + continue; + } + if ( sedCount === 0 ) { return stop(false); } + } } + }; + const observer = new MutationObserver(handleMutations); + observer.observe(document, { childList: true, subtree: true }); + if ( stay ) { return; } + runAt(( ) => { + const quitAfter = extraArgs.quitAfter ?? 0; + if ( quitAfter === 0 ) { return stop(); } + setTimeout(( ) => { stop(); }, quitAfter); }, 'interactive'); } @@ -984,6 +1000,7 @@ builtinScriptlets.push({ name: 'xml-prune.js', fn: xmlPrune, dependencies: [ + 'modify-xhr-response.fn', 'safe-self.fn', ], }); @@ -1089,41 +1106,14 @@ function xmlPrune( }); } }); - self.XMLHttpRequest.prototype.open = new Proxy(self.XMLHttpRequest.prototype.open, { - apply: async (target, thisArg, args) => { - if ( reUrl.test(urlFromArg(args[1])) === false ) { - return Reflect.apply(target, thisArg, args); - } - thisArg.addEventListener('readystatechange', function() { - if ( thisArg.readyState !== 4 ) { return; } - const type = thisArg.responseType; - if ( - type === 'document' || - type === '' && thisArg.responseXML instanceof XMLDocument - ) { - pruneFromDoc(thisArg.responseXML); - 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 ( - type === 'text' || - type === '' && typeof thisArg.responseText === 'string' - ) { - const textin = thisArg.responseText; - const textout = pruneFromText(textin); - if ( textout === textin ) { return; } - Object.defineProperty(thisArg, 'response', { value: textout }); - Object.defineProperty(thisArg, 'responseText', { value: textout }); - return; - } - }); - return Reflect.apply(target, thisArg, args); + modifyXhrResponseFn(urlPattern, (xhr, before) => { + if ( before instanceof XMLDocument ) { + return pruneFromDoc(before); + } + if ( typeof before === 'string' ) { + return pruneFromText(before); } + return before; }); } diff --git a/src/js/resources/vod.js b/src/js/resources/vod.js new file mode 100644 index 0000000000000..f74d259579ce8 --- /dev/null +++ b/src/js/resources/vod.js @@ -0,0 +1,175 @@ +/******************************************************************************* + + uBlock Origin - a comprehensive, efficient content blocker + Copyright (C) 2026-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 { modifyXhrResponseFn } from './prevent-xhr.js'; +import { registerScriptlet } from './base.js'; +import { safeSelf } from './safe-self.js'; + +export function mpegdashPrune( + selector = '', + propsToMatch = '' +) { + if ( typeof selector !== 'string' ) { return; } + if ( selector === '' ) { return; } + const safe = safeSelf(); + const logPrefix = safe.makeLogPrefix('mpegdash-prune', selector, propsToMatch); + const queryAll = (xmlDoc, selector) => { + if ( selector.startsWith('xpath:') === false ) { + return Array.from(xmlDoc.querySelectorAll(selector)); + } + const xpr = xmlDoc.evaluate( + selector.slice(6), + xmlDoc, + null, + XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE, + null + ); + const out = []; + for ( let i = 0; i < xpr.snapshotLength; i++ ) { + const node = xpr.snapshotItem(i); + out.push(node); + } + return out; + }; + const rePTparse = /^PT(\d+D)?(\d+H)?(\d+M)?([\d.]+S)?$/; + const secondsPerDay = 24 * 60 * 60; + const secondsPerHour = 60 * 60; + const secondsPerMinute = 60; + const secondsFromPT = pt => { + const match = rePTparse.exec(pt); + if ( match === null ) { return; } + let seconds = 0; + if ( match[1] ) { + const d = parseFloat(match[1].slice(0, -1)); + if ( isNaN(d) ) { return; } + seconds += d * secondsPerDay; + } + if ( match[2] ) { + const h = parseFloat(match[2].slice(0, -1)); + if ( isNaN(h) ) { return; } + seconds += h * secondsPerHour; + } + if ( match[3] ) { + const m = parseFloat(match[3].slice(0, -1)); + if ( isNaN(m) ) { return; } + seconds += m * secondsPerMinute; + } + if ( match[4] ) { + const s = parseFloat(match[4].slice(0, -1)); + if ( isNaN(s) ) { return; } + seconds += s; + } + return seconds; + }; + const ptFromSeconds = seconds => { + const parts = [ 'PT' ]; + const d = Math.floor(seconds / secondsPerDay); + if ( d ) { + parts.push(`${d}D`); + seconds -= d * secondsPerDay; + } + const h = Math.floor(seconds / secondsPerHour); + if ( h ) { + parts.push(`${h}H`); + seconds -= h * secondsPerHour; + } + const m = Math.floor(seconds / secondsPerMinute); + if ( m ) { + parts.push(`${m}M`); + seconds -= m * secondsPerMinute; + } + parts.push(`${seconds}S`); + return parts.join(''); + }; + const fixTimeAttributes = xmlDoc => { + try { + const periods = queryAll(xmlDoc, 'MPD > Period'); + if ( periods.length === 0 ) { return; } + let seconds = 0; + for ( const period of periods ) { + const startAttrBefore = period.getAttribute('start'); + const durAttr = period.getAttribute('duration'); + if ( startAttrBefore === null || durAttr === null ) { continue; } + const startAttrAfter = ptFromSeconds(seconds); + period.setAttribute('start', startAttrAfter); + if ( period.hasAttribute('id') ) { + const idAttr = period.getAttribute('id'); + period.setAttribute('id', idAttr.replace(startAttrBefore, startAttrAfter)); + } + seconds += secondsFromPT(durAttr); + } + const mpds = queryAll(xmlDoc, 'MPD[mediaPresentationDuration]'); + if ( mpds.length !== 1 ) { return; } + mpds[0].setAttribute('mediaPresentationDuration', ptFromSeconds(seconds)); + } catch { + } + }; + const pruneFromDoc = xmlDoc => { + try { + if ( selector === '' ) { + const serializer = new XMLSerializer(); + safe.uboLog(logPrefix, `Document is\n\t${serializer.serializeToString(xmlDoc)}`); + } + const items = queryAll(xmlDoc, selector); + if ( items.length === 0 ) { return xmlDoc; } + safe.uboLog(logPrefix, `Patching ${items.length} items`); + for ( const item of items ) { + if ( item.nodeType !== 1 ) { continue; } + item.setAttribute('duration', 'PT0S'); + } + fixTimeAttributes(xmlDoc); + } catch(ex) { + safe.uboErr(logPrefix, `Error: ${ex}`); + } + return xmlDoc; + }; + const pruneFromText = text => { + if ( (/^\s*\s*$/.test(text)) === false ) { + return text; + } + try { + const xmlParser = new DOMParser(); + const xmlDoc = xmlParser.parseFromString(text, 'text/xml'); + pruneFromDoc(xmlDoc); + const serializer = new XMLSerializer(); + text = serializer.serializeToString(xmlDoc); + } catch { + } + return text; + }; + modifyXhrResponseFn(propsToMatch, (xhr, before) => { + if ( before instanceof XMLDocument ) { + return pruneFromDoc(before); + } + if ( typeof before === 'string' ) { + return pruneFromText(before); + } + return before; + }); +} +registerScriptlet(mpegdashPrune, { + name: 'mpegdash-prune.js', + dependencies: [ + modifyXhrResponseFn, + safeSelf, + ], +}); diff --git a/src/js/reverselookup.js b/src/js/reverselookup.js index c5bf10061fa80..1c4d83625d4dc 100644 --- a/src/js/reverselookup.js +++ b/src/js/reverselookup.js @@ -128,6 +128,7 @@ const fromNetFilter = async function(rawFilter) { trustedSource: true, maxTokenLength: staticNetFilteringEngine.MAX_TOKEN_LENGTH, nativeCssHas: vAPI.webextFlavor.env.includes('native_css_has'), + canFilterResponseBody: µb.canFilterResponseData, }); parser.parse(rawFilter); @@ -165,6 +166,7 @@ const fromExtendedFilter = async function(details) { const parser = new sfp.AstFilterParser({ trustedSource: true, nativeCssHas: vAPI.webextFlavor.env.includes('native_css_has'), + canFilterResponseBody: µb.canFilterResponseData, }); parser.parse(details.rawFilter); let needle; diff --git a/src/js/scriptlet-filtering-core.js b/src/js/scriptlet-filtering-core.js index fa65f7cde8a8a..d6960c863ee9b 100644 --- a/src/js/scriptlet-filtering-core.js +++ b/src/js/scriptlet-filtering-core.js @@ -25,18 +25,13 @@ import { redirectEngine as reng } from './redirect-engine.js'; /******************************************************************************/ -const normalizeRawFilter = (parser, sourceIsTrusted = false) => { +const normalizeRawFilter = parser => { const args = parser.getScriptletArgs(); if ( args.length !== 0 ) { let token = `${args[0]}.js`; if ( reng.aliases.has(token) ) { token = reng.aliases.get(token); } - if ( parser.isException() !== true ) { - if ( sourceIsTrusted !== true ) { - if ( reng.tokenRequiresTrust(token) ) { return; } - } - } args[0] = token.slice(0, -3); } return JSON.stringify(args); @@ -139,7 +134,7 @@ export class ScriptletFilteringEngine { // Only exception filters are allowed to be global. const isException = parser.isException(); - const normalized = normalizeRawFilter(parser, writer.properties.get('trustedSource')); + const normalized = normalizeRawFilter(parser); // Can fail if there is a mismatch with trust requirement if ( normalized === undefined ) { return; } diff --git a/src/js/static-dnr-filtering.js b/src/js/static-dnr-filtering.js index 149f4bd9d49a3..0183f49707b72 100644 --- a/src/js/static-dnr-filtering.js +++ b/src/js/static-dnr-filtering.js @@ -27,6 +27,7 @@ import { } from './static-filtering-io.js'; import { LineIterator } from './text-utils.js'; +import { getTrustedTokens } from './trusted-tokens.js'; import staticNetFilteringEngine from './static-net-filtering.js'; /******************************************************************************/ @@ -275,6 +276,7 @@ function addToDNR(context, list) { nativeCssHas: env.includes('native_css_has'), badTypes: [ sfp.NODE_TYPE_NET_OPTION_NAME_REDIRECTRULE ], trustedSource: list.trustedSource || undefined, + trustedTokens: getTrustedTokens(), }); const compiler = staticNetFilteringEngine.createCompiler(); diff --git a/src/js/static-filtering-parser.js b/src/js/static-filtering-parser.js index a8dd8bec8fb03..90a3c564fff96 100644 --- a/src/js/static-filtering-parser.js +++ b/src/js/static-filtering-parser.js @@ -100,6 +100,7 @@ export const AST_ERROR_OPTION_BADVALUE = 1 << iota++; export const AST_ERROR_OPTION_EXCLUDED = 1 << iota++; export const AST_ERROR_IF_TOKEN_UNKNOWN = 1 << iota++; export const AST_ERROR_UNTRUSTED_SOURCE = 1 << iota++; +export const AST_ERROR_CAPABILITY = 1 << iota++; iota = 0; const NODE_RIGHT_INDEX = iota++; @@ -1456,12 +1457,29 @@ export class AstFilterParser { case NODE_TYPE_NET_OPTION_NAME_REDIRECT: case NODE_TYPE_NET_OPTION_NAME_REDIRECTRULE: { realBad = abstractTypeCount || behaviorTypeCount || unredirectableTypeCount; + if ( realBad ) { break; } + if ( isException || isBadfilter ) { break; } + const { trustedSource, trustedTokens } = this.options; + if ( trustedSource ) { break; } + if ( trustedTokens instanceof Set === false ) { break; } + const value = this.getNetOptionValue(modifierType); + let { token } = parseRedirectValue(value); + if ( trustedTokens.has(token) ) { + this.astError = AST_ERROR_UNTRUSTED_SOURCE; + realBad = true; + } break; } case NODE_TYPE_NET_OPTION_NAME_REPLACE: { realBad = abstractTypeCount || behaviorTypeCount || unredirectableTypeCount; if ( realBad ) { break; } if ( isException || isBadfilter ) { break; } + if ( this.options.canFilterResponseBody !== true ) { + this.addFlags(AST_FLAG_HAS_ERROR); + this.astError = AST_ERROR_CAPABILITY; + realBad = true; + break; + } if ( this.options.trustedSource !== true ) { this.astError = AST_ERROR_UNTRUSTED_SOURCE; realBad = true; @@ -2189,7 +2207,11 @@ export class AstFilterParser { if ( c0 === 0x2F /* / */ ) { this.domainRegexValueParser.nextArg(this.raw, beg+1); end = this.domainRegexValueParser.separatorEnd; - isRegex = true; + if ( end <= parentEnd ) { + isRegex = true; + } else { + end = -1; + } } else if ( c0 === 0x5B /* [ */ && this.startsWith('[$domain=/', beg) ) { end = this.indexOf('/]', beg + 10, parentEnd); if ( end !== -1 ) { end += 2; } @@ -2342,12 +2364,12 @@ export class AstFilterParser { 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 { trustedSource, trustedTokens } = this.options; + if ( trustedSource ) { break; } + if ( trustedTokens instanceof Set === false ) { break; } const token = this.getNodeString(targetNode); - if ( trustedScriptletTokens.has(token) && trustedSource !== true ) { + if ( trustedTokens.has(token) ) { this.astError = AST_ERROR_UNTRUSTED_SOURCE; realBad = true; } @@ -2390,6 +2412,10 @@ export class AstFilterParser { return this.parseExtPatternResponseheader(parent); } this.astTypeFlavor = AST_TYPE_EXTENDED_HTML; + if ( this.options.canFilterResponseBody !== true ) { + this.astError = AST_ERROR_CAPABILITY; + return 0; + } return this.parseExtPatternHtml(parent); } // ##... diff --git a/src/js/storage.js b/src/js/storage.js index 66a3352394ab2..8de3e58f4cc1a 100644 --- a/src/js/storage.js +++ b/src/js/storage.js @@ -35,6 +35,7 @@ import { import { ubolog, ubologSet } from './console.js'; import cosmeticFilteringEngine from './cosmetic-filtering.js'; +import { getTrustedTokens } from './trusted-tokens.js'; import { hostnameFromURI } from './uri-utils.js'; import io from './assets.js'; import logger from './logger.js'; @@ -960,7 +961,6 @@ onBroadcast(msg => { if ( vAPI.Net.canSuspend() ) { vAPI.net.suspend(); } - redirectEngine.reset(); staticExtFilteringEngine.reset(); staticNetFilteringEngine.reset(); µb.selfieManager.destroy(); @@ -1107,16 +1107,16 @@ onBroadcast(msg => { // Populate the writer with information potentially useful to the // client compilers. - const trustedSource = details.trustedSource === true; if ( details.assetKey ) { writer.properties.set('name', details.assetKey); - writer.properties.set('trustedSource', trustedSource); } const assetName = details.assetKey ? details.assetKey : '?'; const parser = new sfp.AstFilterParser({ - trustedSource, + trustedSource: details.trustedSource === true, maxTokenLength: staticNetFilteringEngine.MAX_TOKEN_LENGTH, nativeCssHas: vAPI.webextFlavor.env.includes('native_css_has'), + canFilterResponseBody: µb.canFilterResponseData, + trustedTokens: getTrustedTokens(), }); const compiler = staticNetFilteringEngine.createCompiler(parser); const lineIter = new LineIterator( diff --git a/src/js/trusted-tokens.js b/src/js/trusted-tokens.js new file mode 100644 index 0000000000000..0e4b76ff1a800 --- /dev/null +++ b/src/js/trusted-tokens.js @@ -0,0 +1,60 @@ +/******************************************************************************* + + uBlock Origin Lite - a comprehensive, MV3-compliant content blocker + Copyright (C) 2026-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 { builtinScriptlets } from './resources/scriptlets.js'; +import redirectResources from './redirect-resources.js'; + +/******************************************************************************/ + +const addTrustedToken = (out, ...tokens) => { + for ( const token of tokens ) { + out.add(token); + if ( token.endsWith('.js') === false ) { continue; } + out.add(token.slice(0, -3)); + } +}; + +let trustedTokens; + +/******************************************************************************/ + +export function getTrustedTokens() { + if ( trustedTokens ) { return trustedTokens; } + const out = new Set(); + for ( const { name, requiresTrust, aliases } of builtinScriptlets ) { + if ( requiresTrust !== true ) { continue; } + addTrustedToken(out, name); + if ( Array.isArray(aliases) === false ) { continue; } + addTrustedToken(out, ...aliases); + } + for ( const [ name, { requiresTrust, alias } ] of redirectResources ) { + if ( requiresTrust !== true ) { continue; } + addTrustedToken(out, name); + if ( alias === undefined ) { continue; } + if ( typeof alias === 'string' ) { + addTrustedToken(out, alias); + } else if ( Array.isArray(alias) ) { + addTrustedToken(out, ...alias); + } + } + trustedTokens = out; + return out; +} diff --git a/tools/make-mv3.sh b/tools/make-mv3.sh index d9a5938beb33b..62925783bbdca 100755 --- a/tools/make-mv3.sh +++ b/tools/make-mv3.sh @@ -88,6 +88,7 @@ cp "$UBO_DIR"/src/js/i18n.js "$UBOL_DIR"/js/ cp "$UBO_DIR"/src/js/jsonpath.js "$UBOL_DIR"/js/ cp "$UBO_DIR"/src/js/redirect-resources.js "$UBOL_DIR"/js/ cp "$UBO_DIR"/src/js/regex-analyzer.js "$UBOL_DIR"/js/offscreen/ +cp "$UBO_DIR"/src/js/trusted-tokens.js "$UBOL_DIR"/js/ cp -R "$UBO_DIR"/src/js/resources "$UBOL_DIR"/js/ cp "$UBO_DIR"/src/js/static-filtering-parser.js "$UBOL_DIR"/js/ cp "$UBO_DIR"/src/js/urlskip.js "$UBOL_DIR"/js/ @@ -140,6 +141,7 @@ cp platform/mv3/extension/js/utils.js "$UBOL_BUILD_DIR"/js/ cp "$UBO_DIR"/src/lib/punycode.js "$UBOL_BUILD_DIR"/js/ cp -R "$UBO_DIR"/src/lib/regexanalyzer "$UBOL_BUILD_DIR"/js/ cp -R "$UBO_DIR"/src/js/resources "$UBOL_BUILD_DIR"/js/ +cp "$UBO_DIR"/src/js/trusted-tokens.js "$UBOL_BUILD_DIR"/js/ cp -R platform/mv3/scriptlets "$UBOL_BUILD_DIR"/ cp -R platform/mv3/extension/js/offscreen "$UBOL_BUILD_DIR"/js/ cp "$UBO_DIR"/src/js/regex-analyzer.js "$UBOL_BUILD_DIR"/js/offscreen/