From 9339a759528d0e80dd7a91c8b660756b101f964d Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Thu, 29 May 2025 09:06:02 -0400 Subject: [PATCH] [mv3] Add support for custom DNR rules This feature is hidden behind the "Developer mode" setting in the dashboard. When "Developer mode" is enabled, a tab named "Develop" will become available in the dashboard. This tab is meant to contain tools for technical users. At the moment, the "Develop" pane allows to create custom DNR rules through a (CodeMirror-based) editor. For the sake of convenience, the DNR rule must be entered in YAML-like format. The format is not really full compliant YAML, just YAML-like, and very strict in order to ensure the parser stays simple enough. Lines starting with `#` are comments and will be ignored by the parser. Any line which do not match the parser's expectation will be marked as invalid, and the whole DNR rule containing such invalid lines will be discarded. There must not be empty lines inside a rule definition. Each DNR rule must be separated with a `---` line, which is known as a YAML document separator. String values must not be quoted, otherwise the quotes will be considered part of the value. There is one exception: `''` will be parsed as "an empty string". The editor will attempt to auto-complete known DNR keywords. That feature will improve over time. Though the parser will identify some errors, not all invalid DNR rules are currently identified by the parser, and these will be reported when the rules are registered through the DNR API. Better identifying invalid DNR rules at edit time will improve over time. The editor will report `regexFilter` values which are not supported by the DNR engine on the current platform. The editor reacts to instances of `regexFilter: ...` to report whether a regex value is supported. This means you can test for a regex value by using `# regexFilter: ...` so that you do not have to create an actual DNR rules just for the sake of testing. Custom DNR rules can be exported into a JSON file (a format known by the DNR API as a "static ruleset"). JSON-based ruleset can be imported, the content will be converted to YAML-like syntax. The editor will attempt to convert to YAML pasted content which can be JSON-parsed. It's possible to paste partially or wholly JSON-based rulesets. When disabling "Developer mode", all custom DNR rules will be unregistered from the DNR API. The DNR rules content will be left intact in such case. Existing DNR rules will be registered into the DNR API when re-enabling "Developer mode". Administrators can prevent "Developer mode" from being enabled by adding `develop` token to `disabledFeatures` setting. Related discussion: https://github.com/uBlockOrigin/uBOL-home/discussions/323 The main motivation is to give list maintainers a tool to assist with resolving filter issues. Custom DNR rules can assist in crafting and validating filters meant to work with uBOL. A secondary motivation is to provide technical users the ability to further customize their content blocker. More conveniences will be added over time, this is a first version. --- Makefile | 19 +- .../mv3/extension/_locales/ar/messages.json | 28 + .../mv3/extension/_locales/az/messages.json | 28 + .../mv3/extension/_locales/be/messages.json | 28 + .../mv3/extension/_locales/bg/messages.json | 28 + .../mv3/extension/_locales/bn/messages.json | 28 + .../extension/_locales/br_FR/messages.json | 28 + .../mv3/extension/_locales/bs/messages.json | 28 + .../mv3/extension/_locales/ca/messages.json | 28 + .../mv3/extension/_locales/cs/messages.json | 28 + .../mv3/extension/_locales/cv/messages.json | 28 + .../mv3/extension/_locales/cy/messages.json | 28 + .../mv3/extension/_locales/da/messages.json | 28 + .../mv3/extension/_locales/de/messages.json | 28 + .../mv3/extension/_locales/el/messages.json | 28 + .../mv3/extension/_locales/en/messages.json | 32 + .../extension/_locales/en_GB/messages.json | 28 + .../mv3/extension/_locales/eo/messages.json | 28 + .../mv3/extension/_locales/es/messages.json | 28 + .../mv3/extension/_locales/et/messages.json | 28 + .../mv3/extension/_locales/eu/messages.json | 28 + .../mv3/extension/_locales/fa/messages.json | 28 + .../mv3/extension/_locales/fi/messages.json | 28 + .../mv3/extension/_locales/fil/messages.json | 28 + .../mv3/extension/_locales/fr/messages.json | 28 + .../mv3/extension/_locales/fy/messages.json | 28 + .../mv3/extension/_locales/gl/messages.json | 28 + .../mv3/extension/_locales/gu/messages.json | 28 + .../mv3/extension/_locales/he/messages.json | 28 + .../mv3/extension/_locales/hi/messages.json | 28 + .../mv3/extension/_locales/hr/messages.json | 28 + .../mv3/extension/_locales/hu/messages.json | 28 + .../mv3/extension/_locales/hy/messages.json | 28 + .../mv3/extension/_locales/id/messages.json | 28 + .../mv3/extension/_locales/it/messages.json | 28 + .../mv3/extension/_locales/ja/messages.json | 28 + .../mv3/extension/_locales/ka/messages.json | 28 + .../mv3/extension/_locales/kk/messages.json | 28 + .../mv3/extension/_locales/kn/messages.json | 28 + .../mv3/extension/_locales/ko/messages.json | 28 + .../mv3/extension/_locales/lt/messages.json | 28 + .../mv3/extension/_locales/lv/messages.json | 28 + .../mv3/extension/_locales/mk/messages.json | 28 + .../mv3/extension/_locales/ml/messages.json | 28 + .../mv3/extension/_locales/mr/messages.json | 28 + .../mv3/extension/_locales/ms/messages.json | 28 + .../mv3/extension/_locales/nb/messages.json | 28 + .../mv3/extension/_locales/nl/messages.json | 28 + .../mv3/extension/_locales/oc/messages.json | 28 + .../mv3/extension/_locales/pa/messages.json | 28 + .../mv3/extension/_locales/pl/messages.json | 28 + .../extension/_locales/pt_BR/messages.json | 28 + .../extension/_locales/pt_PT/messages.json | 28 + .../mv3/extension/_locales/ro/messages.json | 28 + .../mv3/extension/_locales/ru/messages.json | 28 + .../mv3/extension/_locales/si/messages.json | 28 + .../mv3/extension/_locales/sk/messages.json | 28 + .../mv3/extension/_locales/sl/messages.json | 28 + .../mv3/extension/_locales/so/messages.json | 28 + .../mv3/extension/_locales/sq/messages.json | 28 + .../mv3/extension/_locales/sr/messages.json | 28 + .../mv3/extension/_locales/sv/messages.json | 28 + .../mv3/extension/_locales/sw/messages.json | 28 + .../mv3/extension/_locales/ta/messages.json | 28 + .../mv3/extension/_locales/te/messages.json | 28 + .../mv3/extension/_locales/th/messages.json | 28 + .../mv3/extension/_locales/tr/messages.json | 28 + .../mv3/extension/_locales/uk/messages.json | 28 + .../mv3/extension/_locales/ur/messages.json | 28 + .../mv3/extension/_locales/vi/messages.json | 28 + .../extension/_locales/zh_CN/messages.json | 28 + .../extension/_locales/zh_TW/messages.json | 28 + .../mv3/extension/css/dashboard-common.css | 6 + platform/mv3/extension/css/dashboard.css | 19 +- platform/mv3/extension/css/develop.css | 112 +++ platform/mv3/extension/css/settings.css | 3 + platform/mv3/extension/dashboard.html | 41 +- platform/mv3/extension/js/background.js | 33 +- platform/mv3/extension/js/dashboard.js | 19 +- platform/mv3/extension/js/develop.js | 687 ++++++++++++++++++ platform/mv3/extension/js/dnr-parser.js | 607 ++++++++++++++++ platform/mv3/extension/js/ext-compat.js | 6 +- platform/mv3/extension/js/ruleset-manager.js | 118 ++- platform/mv3/extension/js/settings.js | 34 +- .../extension/lib/codemirror/codemirror-ubol | 2 +- platform/mv3/safari/ext-compat.js | 4 +- src/css/common.css | 3 +- tools/jsonpath-tool.html | 67 +- tools/make-mv3.sh | 2 + 89 files changed, 3700 insertions(+), 74 deletions(-) create mode 100644 platform/mv3/extension/css/develop.css create mode 100644 platform/mv3/extension/js/develop.js create mode 100644 platform/mv3/extension/js/dnr-parser.js diff --git a/Makefile b/Makefile index 2ce9beb54..ddec8e4dc 100644 --- a/Makefile +++ b/Makefile @@ -2,13 +2,17 @@ run_options := $(filter-out $@,$(MAKECMDGOALS)) .PHONY: all clean cleanassets test lint chromium opera firefox npm dig \ - mv3-chromium mv3-firefox mv3-edge mv3-safari \ + mv3-chromium mv3-firefox mv3-edge mv3-safari ubol-codemirror \ compare maxcost medcost mincost modifiers record wasm sources := ./dist/version $(shell find ./assets -type f) $(shell find ./src -type f) platform := $(wildcard platform/*/*) assets := dist/build/uAssets -mv3-sources := $(shell find ./src -type f) $(wildcard platform/mv3/*) $(shell find ./platform/mv3/extension -type f) +mv3-sources := \ + $(shell find ./src -type f) \ + $(wildcard platform/mv3/*) \ + $(shell find ./platform/mv3/extension -name codemirror-ubol -prune -o -type f) \ + platform/mv3/extension/lib/codemirror/codemirror-ubol/dist/cm6.bundle.ubol.min.js mv3-data := $(shell find ./dist/build/mv3-data -type f) mv3-edge-deps := $(wildcard platform/mv3/edge/*) @@ -69,25 +73,28 @@ dig-snfe: dig dist/build/mv3-data: mkdir -p dist/build/mv3-data +ubol-codemirror: + $(MAKE) -sC platform/mv3/extension/lib/codemirror/codemirror-ubol/ ubol.bundle + dist/build/uBOLite.chromium: tools/make-mv3.sh $(mv3-sources) $(platform) $(mv3-data) dist/build/mv3-data tools/make-mv3.sh chromium -mv3-chromium: dist/build/uBOLite.chromium +mv3-chromium: ubol-codemirror dist/build/uBOLite.chromium dist/build/uBOLite.firefox: tools/make-mv3.sh $(mv3-sources) $(platform) $(mv3-data) dist/build/mv3-data tools/make-mv3.sh firefox -mv3-firefox: dist/build/uBOLite.firefox +mv3-firefox: ubol-codemirror dist/build/uBOLite.firefox dist/build/uBOLite.edge: tools/make-mv3.sh $(mv3-sources) $(mv3-edge-deps) $(mv3-data) dist/build/mv3-data tools/make-mv3.sh edge -mv3-edge: dist/build/uBOLite.edge +mv3-edge: ubol-codemirror dist/build/uBOLite.edge dist/build/uBOLite.safari: tools/make-mv3.sh $(mv3-sources) $(mv3-safari-deps) $(mv3-data) dist/build/mv3-data tools/make-mv3.sh safari -mv3-safari: dist/build/uBOLite.safari +mv3-safari: ubol-codemirror dist/build/uBOLite.safari dist/build/uAssets: tools/pull-assets.sh diff --git a/platform/mv3/extension/_locales/ar/messages.json b/platform/mv3/extension/_locales/ar/messages.json index 58bd71e00..f7b0a4906 100644 --- a/platform/mv3/extension/_locales/ar/messages.json +++ b/platform/mv3/extension/_locales/ar/messages.json @@ -233,8 +233,16 @@ }, "enableStrictBlockLegend": { "message": "سيتم حظر التنقل إلى المواقع غير المرغوب فيها، وسيتم تقديم خيار لك للمتابعة.", + "description": "Short description for a checkbox in the options page" + }, + "developerModeLabel": { + "message": "Developer mode", "description": "Label for a checkbox in the options page" }, + "developerModeLegend": { + "message": "Enable access to features suitable for technical users.", + "description": "Short description for a checkbox in the options page" + }, "findListsPlaceholder": { "message": "البحث عن القوائم", "description": "Placeholder for the input field used to find lists" @@ -282,5 +290,25 @@ "zapperTipQuit": { "message": "Exit element zapper mode", "description": "Tooltip for the button used to exit zapper mode" + }, + "saveButton": { + "message": "Save", + "description": "Text for buttons used to save changes" + }, + "revertButton": { + "message": "Revert", + "description": "Text for buttons used to revert changes" + }, + "importAndAppendButton": { + "message": "Import and append…", + "description": "Text for buttons used to import and append content" + }, + "exportButton": { + "message": "Export…", + "description": "Text for buttons used to export content" + }, + "dnrRulesSummary": { + "message": "Enter your own DNR rules below. Do not add content from untrusted sources.", + "description": "Short description of the DNR rules editor pane" } } diff --git a/platform/mv3/extension/_locales/az/messages.json b/platform/mv3/extension/_locales/az/messages.json index 31968c558..27f7fc1b6 100644 --- a/platform/mv3/extension/_locales/az/messages.json +++ b/platform/mv3/extension/_locales/az/messages.json @@ -233,8 +233,16 @@ }, "enableStrictBlockLegend": { "message": "Navigation to potentially undesirable sites will be blocked, and you will be offered the option to proceed.", + "description": "Short description for a checkbox in the options page" + }, + "developerModeLabel": { + "message": "Developer mode", "description": "Label for a checkbox in the options page" }, + "developerModeLegend": { + "message": "Enable access to features suitable for technical users.", + "description": "Short description for a checkbox in the options page" + }, "findListsPlaceholder": { "message": "Find lists", "description": "Placeholder for the input field used to find lists" @@ -282,5 +290,25 @@ "zapperTipQuit": { "message": "Exit element zapper mode", "description": "Tooltip for the button used to exit zapper mode" + }, + "saveButton": { + "message": "Save", + "description": "Text for buttons used to save changes" + }, + "revertButton": { + "message": "Revert", + "description": "Text for buttons used to revert changes" + }, + "importAndAppendButton": { + "message": "Import and append…", + "description": "Text for buttons used to import and append content" + }, + "exportButton": { + "message": "Export…", + "description": "Text for buttons used to export content" + }, + "dnrRulesSummary": { + "message": "Enter your own DNR rules below. Do not add content from untrusted sources.", + "description": "Short description of the DNR rules editor pane" } } diff --git a/platform/mv3/extension/_locales/be/messages.json b/platform/mv3/extension/_locales/be/messages.json index c41e4e05a..b35b66c65 100644 --- a/platform/mv3/extension/_locales/be/messages.json +++ b/platform/mv3/extension/_locales/be/messages.json @@ -233,8 +233,16 @@ }, "enableStrictBlockLegend": { "message": "Пераход да патэнцыйна непажаданых сайтаў будзе заблакаваны, і вам будзе прапанавана магчымасць працягнуць.", + "description": "Short description for a checkbox in the options page" + }, + "developerModeLabel": { + "message": "Developer mode", "description": "Label for a checkbox in the options page" }, + "developerModeLegend": { + "message": "Enable access to features suitable for technical users.", + "description": "Short description for a checkbox in the options page" + }, "findListsPlaceholder": { "message": "Знайсці спісы", "description": "Placeholder for the input field used to find lists" @@ -282,5 +290,25 @@ "zapperTipQuit": { "message": "Выйсці з рэжыму імгненнага хавання элементаў", "description": "Tooltip for the button used to exit zapper mode" + }, + "saveButton": { + "message": "Save", + "description": "Text for buttons used to save changes" + }, + "revertButton": { + "message": "Revert", + "description": "Text for buttons used to revert changes" + }, + "importAndAppendButton": { + "message": "Import and append…", + "description": "Text for buttons used to import and append content" + }, + "exportButton": { + "message": "Export…", + "description": "Text for buttons used to export content" + }, + "dnrRulesSummary": { + "message": "Enter your own DNR rules below. Do not add content from untrusted sources.", + "description": "Short description of the DNR rules editor pane" } } diff --git a/platform/mv3/extension/_locales/bg/messages.json b/platform/mv3/extension/_locales/bg/messages.json index fde7add85..753cd2fe9 100644 --- a/platform/mv3/extension/_locales/bg/messages.json +++ b/platform/mv3/extension/_locales/bg/messages.json @@ -233,8 +233,16 @@ }, "enableStrictBlockLegend": { "message": "Навигацията към потенциално нежелани сайтове ще бъде блокирана и ще ви бъде предложена възможността да продължите.", + "description": "Short description for a checkbox in the options page" + }, + "developerModeLabel": { + "message": "Developer mode", "description": "Label for a checkbox in the options page" }, + "developerModeLegend": { + "message": "Enable access to features suitable for technical users.", + "description": "Short description for a checkbox in the options page" + }, "findListsPlaceholder": { "message": "Намиране на списъци", "description": "Placeholder for the input field used to find lists" @@ -282,5 +290,25 @@ "zapperTipQuit": { "message": "Излизане от режима на временно скриване на елемента", "description": "Tooltip for the button used to exit zapper mode" + }, + "saveButton": { + "message": "Save", + "description": "Text for buttons used to save changes" + }, + "revertButton": { + "message": "Revert", + "description": "Text for buttons used to revert changes" + }, + "importAndAppendButton": { + "message": "Import and append…", + "description": "Text for buttons used to import and append content" + }, + "exportButton": { + "message": "Export…", + "description": "Text for buttons used to export content" + }, + "dnrRulesSummary": { + "message": "Enter your own DNR rules below. Do not add content from untrusted sources.", + "description": "Short description of the DNR rules editor pane" } } diff --git a/platform/mv3/extension/_locales/bn/messages.json b/platform/mv3/extension/_locales/bn/messages.json index 9bad885a7..bc87ca583 100644 --- a/platform/mv3/extension/_locales/bn/messages.json +++ b/platform/mv3/extension/_locales/bn/messages.json @@ -233,8 +233,16 @@ }, "enableStrictBlockLegend": { "message": "সম্ভাব্য অযাচিত ওয়েবসাইট অবরুদ্ধ করা হবে, আর সেখানে আগানোর উপায় দেওয়া থাকবে।", + "description": "Short description for a checkbox in the options page" + }, + "developerModeLabel": { + "message": "Developer mode", "description": "Label for a checkbox in the options page" }, + "developerModeLegend": { + "message": "Enable access to features suitable for technical users.", + "description": "Short description for a checkbox in the options page" + }, "findListsPlaceholder": { "message": "তালিকা খুঁজো", "description": "Placeholder for the input field used to find lists" @@ -282,5 +290,25 @@ "zapperTipQuit": { "message": "Exit element zapper mode", "description": "Tooltip for the button used to exit zapper mode" + }, + "saveButton": { + "message": "Save", + "description": "Text for buttons used to save changes" + }, + "revertButton": { + "message": "Revert", + "description": "Text for buttons used to revert changes" + }, + "importAndAppendButton": { + "message": "Import and append…", + "description": "Text for buttons used to import and append content" + }, + "exportButton": { + "message": "Export…", + "description": "Text for buttons used to export content" + }, + "dnrRulesSummary": { + "message": "Enter your own DNR rules below. Do not add content from untrusted sources.", + "description": "Short description of the DNR rules editor pane" } } diff --git a/platform/mv3/extension/_locales/br_FR/messages.json b/platform/mv3/extension/_locales/br_FR/messages.json index e8fbb70c5..72e2d5e8f 100644 --- a/platform/mv3/extension/_locales/br_FR/messages.json +++ b/platform/mv3/extension/_locales/br_FR/messages.json @@ -233,8 +233,16 @@ }, "enableStrictBlockLegend": { "message": "Stanket e vo ar merdeiñ etrezek lec'hiennoù a c'hallfe bezañ dañjerus, ha moaien a vo deoc'h dibab da genderc'hel pe get.", + "description": "Short description for a checkbox in the options page" + }, + "developerModeLabel": { + "message": "Developer mode", "description": "Label for a checkbox in the options page" }, + "developerModeLegend": { + "message": "Enable access to features suitable for technical users.", + "description": "Short description for a checkbox in the options page" + }, "findListsPlaceholder": { "message": "Kavout rolloù", "description": "Placeholder for the input field used to find lists" @@ -282,5 +290,25 @@ "zapperTipQuit": { "message": "Kuitaat ar mod \"dilemel elfennoù\"", "description": "Tooltip for the button used to exit zapper mode" + }, + "saveButton": { + "message": "Save", + "description": "Text for buttons used to save changes" + }, + "revertButton": { + "message": "Revert", + "description": "Text for buttons used to revert changes" + }, + "importAndAppendButton": { + "message": "Import and append…", + "description": "Text for buttons used to import and append content" + }, + "exportButton": { + "message": "Export…", + "description": "Text for buttons used to export content" + }, + "dnrRulesSummary": { + "message": "Enter your own DNR rules below. Do not add content from untrusted sources.", + "description": "Short description of the DNR rules editor pane" } } diff --git a/platform/mv3/extension/_locales/bs/messages.json b/platform/mv3/extension/_locales/bs/messages.json index 2694cb074..bded4c964 100644 --- a/platform/mv3/extension/_locales/bs/messages.json +++ b/platform/mv3/extension/_locales/bs/messages.json @@ -233,8 +233,16 @@ }, "enableStrictBlockLegend": { "message": "Navigation to potentially undesirable sites will be blocked, and you will be offered the option to proceed.", + "description": "Short description for a checkbox in the options page" + }, + "developerModeLabel": { + "message": "Developer mode", "description": "Label for a checkbox in the options page" }, + "developerModeLegend": { + "message": "Enable access to features suitable for technical users.", + "description": "Short description for a checkbox in the options page" + }, "findListsPlaceholder": { "message": "Find lists", "description": "Placeholder for the input field used to find lists" @@ -282,5 +290,25 @@ "zapperTipQuit": { "message": "Exit element zapper mode", "description": "Tooltip for the button used to exit zapper mode" + }, + "saveButton": { + "message": "Save", + "description": "Text for buttons used to save changes" + }, + "revertButton": { + "message": "Revert", + "description": "Text for buttons used to revert changes" + }, + "importAndAppendButton": { + "message": "Import and append…", + "description": "Text for buttons used to import and append content" + }, + "exportButton": { + "message": "Export…", + "description": "Text for buttons used to export content" + }, + "dnrRulesSummary": { + "message": "Enter your own DNR rules below. Do not add content from untrusted sources.", + "description": "Short description of the DNR rules editor pane" } } diff --git a/platform/mv3/extension/_locales/ca/messages.json b/platform/mv3/extension/_locales/ca/messages.json index 1c65daf6f..250e13088 100644 --- a/platform/mv3/extension/_locales/ca/messages.json +++ b/platform/mv3/extension/_locales/ca/messages.json @@ -233,8 +233,16 @@ }, "enableStrictBlockLegend": { "message": "Es blocarà la navegació en webs potencialment no desitjables, oferint-vos la possibilitat de continuar.", + "description": "Short description for a checkbox in the options page" + }, + "developerModeLabel": { + "message": "Developer mode", "description": "Label for a checkbox in the options page" }, + "developerModeLegend": { + "message": "Enable access to features suitable for technical users.", + "description": "Short description for a checkbox in the options page" + }, "findListsPlaceholder": { "message": "Cerca llistes", "description": "Placeholder for the input field used to find lists" @@ -282,5 +290,25 @@ "zapperTipQuit": { "message": "Surt del mode d'eliminació d'elements", "description": "Tooltip for the button used to exit zapper mode" + }, + "saveButton": { + "message": "Save", + "description": "Text for buttons used to save changes" + }, + "revertButton": { + "message": "Revert", + "description": "Text for buttons used to revert changes" + }, + "importAndAppendButton": { + "message": "Import and append…", + "description": "Text for buttons used to import and append content" + }, + "exportButton": { + "message": "Export…", + "description": "Text for buttons used to export content" + }, + "dnrRulesSummary": { + "message": "Enter your own DNR rules below. Do not add content from untrusted sources.", + "description": "Short description of the DNR rules editor pane" } } diff --git a/platform/mv3/extension/_locales/cs/messages.json b/platform/mv3/extension/_locales/cs/messages.json index c60b63a9d..43324e823 100644 --- a/platform/mv3/extension/_locales/cs/messages.json +++ b/platform/mv3/extension/_locales/cs/messages.json @@ -233,8 +233,16 @@ }, "enableStrictBlockLegend": { "message": "Navigace na potenciálně nežádoucí stránky bude zablokována a bude vám nabídnuta možnost pokračovat.", + "description": "Short description for a checkbox in the options page" + }, + "developerModeLabel": { + "message": "Developer mode", "description": "Label for a checkbox in the options page" }, + "developerModeLegend": { + "message": "Enable access to features suitable for technical users.", + "description": "Short description for a checkbox in the options page" + }, "findListsPlaceholder": { "message": "Najít seznamy", "description": "Placeholder for the input field used to find lists" @@ -282,5 +290,25 @@ "zapperTipQuit": { "message": "Opustit režim dočasného skrytí prvků", "description": "Tooltip for the button used to exit zapper mode" + }, + "saveButton": { + "message": "Save", + "description": "Text for buttons used to save changes" + }, + "revertButton": { + "message": "Revert", + "description": "Text for buttons used to revert changes" + }, + "importAndAppendButton": { + "message": "Import and append…", + "description": "Text for buttons used to import and append content" + }, + "exportButton": { + "message": "Export…", + "description": "Text for buttons used to export content" + }, + "dnrRulesSummary": { + "message": "Enter your own DNR rules below. Do not add content from untrusted sources.", + "description": "Short description of the DNR rules editor pane" } } diff --git a/platform/mv3/extension/_locales/cv/messages.json b/platform/mv3/extension/_locales/cv/messages.json index ace2c1d9f..136a025b9 100644 --- a/platform/mv3/extension/_locales/cv/messages.json +++ b/platform/mv3/extension/_locales/cv/messages.json @@ -233,8 +233,16 @@ }, "enableStrictBlockLegend": { "message": "Navigation to potentially undesirable sites will be blocked, and you will be offered the option to proceed.", + "description": "Short description for a checkbox in the options page" + }, + "developerModeLabel": { + "message": "Developer mode", "description": "Label for a checkbox in the options page" }, + "developerModeLegend": { + "message": "Enable access to features suitable for technical users.", + "description": "Short description for a checkbox in the options page" + }, "findListsPlaceholder": { "message": "Find lists", "description": "Placeholder for the input field used to find lists" @@ -282,5 +290,25 @@ "zapperTipQuit": { "message": "Exit element zapper mode", "description": "Tooltip for the button used to exit zapper mode" + }, + "saveButton": { + "message": "Save", + "description": "Text for buttons used to save changes" + }, + "revertButton": { + "message": "Revert", + "description": "Text for buttons used to revert changes" + }, + "importAndAppendButton": { + "message": "Import and append…", + "description": "Text for buttons used to import and append content" + }, + "exportButton": { + "message": "Export…", + "description": "Text for buttons used to export content" + }, + "dnrRulesSummary": { + "message": "Enter your own DNR rules below. Do not add content from untrusted sources.", + "description": "Short description of the DNR rules editor pane" } } diff --git a/platform/mv3/extension/_locales/cy/messages.json b/platform/mv3/extension/_locales/cy/messages.json index 65790d0d8..e034be897 100644 --- a/platform/mv3/extension/_locales/cy/messages.json +++ b/platform/mv3/extension/_locales/cy/messages.json @@ -233,8 +233,16 @@ }, "enableStrictBlockLegend": { "message": "Navigation to potentially undesirable sites will be blocked, and you will be offered the option to proceed.", + "description": "Short description for a checkbox in the options page" + }, + "developerModeLabel": { + "message": "Developer mode", "description": "Label for a checkbox in the options page" }, + "developerModeLegend": { + "message": "Enable access to features suitable for technical users.", + "description": "Short description for a checkbox in the options page" + }, "findListsPlaceholder": { "message": "Find lists", "description": "Placeholder for the input field used to find lists" @@ -282,5 +290,25 @@ "zapperTipQuit": { "message": "Exit element zapper mode", "description": "Tooltip for the button used to exit zapper mode" + }, + "saveButton": { + "message": "Save", + "description": "Text for buttons used to save changes" + }, + "revertButton": { + "message": "Revert", + "description": "Text for buttons used to revert changes" + }, + "importAndAppendButton": { + "message": "Import and append…", + "description": "Text for buttons used to import and append content" + }, + "exportButton": { + "message": "Export…", + "description": "Text for buttons used to export content" + }, + "dnrRulesSummary": { + "message": "Enter your own DNR rules below. Do not add content from untrusted sources.", + "description": "Short description of the DNR rules editor pane" } } diff --git a/platform/mv3/extension/_locales/da/messages.json b/platform/mv3/extension/_locales/da/messages.json index 0422b4f55..8e0dc1661 100644 --- a/platform/mv3/extension/_locales/da/messages.json +++ b/platform/mv3/extension/_locales/da/messages.json @@ -233,8 +233,16 @@ }, "enableStrictBlockLegend": { "message": "Navigering til potentielt uønskede websteder blokeres, men man tilbydes muligheden for at fortsætte.", + "description": "Short description for a checkbox in the options page" + }, + "developerModeLabel": { + "message": "Developer mode", "description": "Label for a checkbox in the options page" }, + "developerModeLegend": { + "message": "Enable access to features suitable for technical users.", + "description": "Short description for a checkbox in the options page" + }, "findListsPlaceholder": { "message": "Find lister", "description": "Placeholder for the input field used to find lists" @@ -282,5 +290,25 @@ "zapperTipQuit": { "message": "Forlad elementdræber­tilstand", "description": "Tooltip for the button used to exit zapper mode" + }, + "saveButton": { + "message": "Save", + "description": "Text for buttons used to save changes" + }, + "revertButton": { + "message": "Revert", + "description": "Text for buttons used to revert changes" + }, + "importAndAppendButton": { + "message": "Import and append…", + "description": "Text for buttons used to import and append content" + }, + "exportButton": { + "message": "Export…", + "description": "Text for buttons used to export content" + }, + "dnrRulesSummary": { + "message": "Enter your own DNR rules below. Do not add content from untrusted sources.", + "description": "Short description of the DNR rules editor pane" } } diff --git a/platform/mv3/extension/_locales/de/messages.json b/platform/mv3/extension/_locales/de/messages.json index 0e73a10db..22bb53da4 100644 --- a/platform/mv3/extension/_locales/de/messages.json +++ b/platform/mv3/extension/_locales/de/messages.json @@ -233,8 +233,16 @@ }, "enableStrictBlockLegend": { "message": "Die Navigation zu potenziell unerwünschten Websites wird verhindert, jedoch wird eine Möglichkeit zum Fortfahren angeboten.", + "description": "Short description for a checkbox in the options page" + }, + "developerModeLabel": { + "message": "Developer mode", "description": "Label for a checkbox in the options page" }, + "developerModeLegend": { + "message": "Enable access to features suitable for technical users.", + "description": "Short description for a checkbox in the options page" + }, "findListsPlaceholder": { "message": "Listen suchen", "description": "Placeholder for the input field used to find lists" @@ -282,5 +290,25 @@ "zapperTipQuit": { "message": "Temporären Modus beenden", "description": "Tooltip for the button used to exit zapper mode" + }, + "saveButton": { + "message": "Save", + "description": "Text for buttons used to save changes" + }, + "revertButton": { + "message": "Revert", + "description": "Text for buttons used to revert changes" + }, + "importAndAppendButton": { + "message": "Import and append…", + "description": "Text for buttons used to import and append content" + }, + "exportButton": { + "message": "Export…", + "description": "Text for buttons used to export content" + }, + "dnrRulesSummary": { + "message": "Enter your own DNR rules below. Do not add content from untrusted sources.", + "description": "Short description of the DNR rules editor pane" } } diff --git a/platform/mv3/extension/_locales/el/messages.json b/platform/mv3/extension/_locales/el/messages.json index b451e0fde..d9f343099 100644 --- a/platform/mv3/extension/_locales/el/messages.json +++ b/platform/mv3/extension/_locales/el/messages.json @@ -233,8 +233,16 @@ }, "enableStrictBlockLegend": { "message": "Θα απετραπεί η πρόσβαση σε πιθανά ανεπιθύμητους ιστοτόπους. Θα σας προσφερθεί η επιλογή να συνεχίσετε.", + "description": "Short description for a checkbox in the options page" + }, + "developerModeLabel": { + "message": "Developer mode", "description": "Label for a checkbox in the options page" }, + "developerModeLegend": { + "message": "Enable access to features suitable for technical users.", + "description": "Short description for a checkbox in the options page" + }, "findListsPlaceholder": { "message": "Εύρεση λιστών", "description": "Placeholder for the input field used to find lists" @@ -282,5 +290,25 @@ "zapperTipQuit": { "message": "Έξοδος από λειτουργία αφαίρεσης στοιχείων", "description": "Tooltip for the button used to exit zapper mode" + }, + "saveButton": { + "message": "Save", + "description": "Text for buttons used to save changes" + }, + "revertButton": { + "message": "Revert", + "description": "Text for buttons used to revert changes" + }, + "importAndAppendButton": { + "message": "Import and append…", + "description": "Text for buttons used to import and append content" + }, + "exportButton": { + "message": "Export…", + "description": "Text for buttons used to export content" + }, + "dnrRulesSummary": { + "message": "Enter your own DNR rules below. Do not add content from untrusted sources.", + "description": "Short description of the DNR rules editor pane" } } diff --git a/platform/mv3/extension/_locales/en/messages.json b/platform/mv3/extension/_locales/en/messages.json index f4dc76f3c..03e28740d 100644 --- a/platform/mv3/extension/_locales/en/messages.json +++ b/platform/mv3/extension/_locales/en/messages.json @@ -233,8 +233,16 @@ }, "enableStrictBlockLegend": { "message": "Navigation to potentially undesirable sites will be blocked, and you will be offered the option to proceed.", + "description": "Short description for a checkbox in the options page" + }, + "developerModeLabel": { + "message": "Developer mode", "description": "Label for a checkbox in the options page" }, + "developerModeLegend": { + "message": "Enables access to features suitable for technical users.", + "description": "Short description for a checkbox in the options page" + }, "findListsPlaceholder": { "message": "Find lists", "description": "Placeholder for the input field used to find lists" @@ -282,5 +290,29 @@ "zapperTipQuit": { "message": "Exit element zapper mode", "description": "Tooltip for the button used to exit zapper mode" + }, + "saveButton": { + "message": "Save", + "description": "Text for buttons used to save changes" + }, + "revertButton": { + "message": "Revert", + "description": "Text for buttons used to revert changes" + }, + "importAndAppendButton": { + "message": "Import and append…", + "description": "Text for buttons used to import and append content" + }, + "exportButton": { + "message": "Export…", + "description": "Text for buttons used to export content" + }, + "dnrRulesSummary": { + "message": "Enter your own DNR rules below. Do not add content from untrusted sources.", + "description": "Short description of the DNR rules editor pane" + }, + "dnrRulesCountInfo": { + "message": "Number of registered rules: {count}", + "description": "Short sentence to report the number of currently registered DNR rules" } } diff --git a/platform/mv3/extension/_locales/en_GB/messages.json b/platform/mv3/extension/_locales/en_GB/messages.json index 36e895564..f61236cc6 100644 --- a/platform/mv3/extension/_locales/en_GB/messages.json +++ b/platform/mv3/extension/_locales/en_GB/messages.json @@ -233,8 +233,16 @@ }, "enableStrictBlockLegend": { "message": "Navigation to potentially undesirable sites will be blocked, and you will be offered the option to proceed.", + "description": "Short description for a checkbox in the options page" + }, + "developerModeLabel": { + "message": "Developer mode", "description": "Label for a checkbox in the options page" }, + "developerModeLegend": { + "message": "Enable access to features suitable for technical users.", + "description": "Short description for a checkbox in the options page" + }, "findListsPlaceholder": { "message": "Find lists", "description": "Placeholder for the input field used to find lists" @@ -282,5 +290,25 @@ "zapperTipQuit": { "message": "Exit element zapper mode", "description": "Tooltip for the button used to exit zapper mode" + }, + "saveButton": { + "message": "Save", + "description": "Text for buttons used to save changes" + }, + "revertButton": { + "message": "Revert", + "description": "Text for buttons used to revert changes" + }, + "importAndAppendButton": { + "message": "Import and append…", + "description": "Text for buttons used to import and append content" + }, + "exportButton": { + "message": "Export…", + "description": "Text for buttons used to export content" + }, + "dnrRulesSummary": { + "message": "Enter your own DNR rules below. Do not add content from untrusted sources.", + "description": "Short description of the DNR rules editor pane" } } diff --git a/platform/mv3/extension/_locales/eo/messages.json b/platform/mv3/extension/_locales/eo/messages.json index 726dcd0bd..acc785f1d 100644 --- a/platform/mv3/extension/_locales/eo/messages.json +++ b/platform/mv3/extension/_locales/eo/messages.json @@ -233,8 +233,16 @@ }, "enableStrictBlockLegend": { "message": "Navigation to potentially undesirable sites will be blocked, and you will be offered the option to proceed.", + "description": "Short description for a checkbox in the options page" + }, + "developerModeLabel": { + "message": "Developer mode", "description": "Label for a checkbox in the options page" }, + "developerModeLegend": { + "message": "Enable access to features suitable for technical users.", + "description": "Short description for a checkbox in the options page" + }, "findListsPlaceholder": { "message": "Find lists", "description": "Placeholder for the input field used to find lists" @@ -282,5 +290,25 @@ "zapperTipQuit": { "message": "Exit element zapper mode", "description": "Tooltip for the button used to exit zapper mode" + }, + "saveButton": { + "message": "Save", + "description": "Text for buttons used to save changes" + }, + "revertButton": { + "message": "Revert", + "description": "Text for buttons used to revert changes" + }, + "importAndAppendButton": { + "message": "Import and append…", + "description": "Text for buttons used to import and append content" + }, + "exportButton": { + "message": "Export…", + "description": "Text for buttons used to export content" + }, + "dnrRulesSummary": { + "message": "Enter your own DNR rules below. Do not add content from untrusted sources.", + "description": "Short description of the DNR rules editor pane" } } diff --git a/platform/mv3/extension/_locales/es/messages.json b/platform/mv3/extension/_locales/es/messages.json index 67af6f640..13f08971f 100644 --- a/platform/mv3/extension/_locales/es/messages.json +++ b/platform/mv3/extension/_locales/es/messages.json @@ -233,8 +233,16 @@ }, "enableStrictBlockLegend": { "message": "La navegación a sitios potencialmente no deseados será bloqueada, y se te ofrecerá la opción de continuar.", + "description": "Short description for a checkbox in the options page" + }, + "developerModeLabel": { + "message": "Developer mode", "description": "Label for a checkbox in the options page" }, + "developerModeLegend": { + "message": "Enable access to features suitable for technical users.", + "description": "Short description for a checkbox in the options page" + }, "findListsPlaceholder": { "message": "Encontrar listas", "description": "Placeholder for the input field used to find lists" @@ -282,5 +290,25 @@ "zapperTipQuit": { "message": "Salir del modo eliminación de elementos", "description": "Tooltip for the button used to exit zapper mode" + }, + "saveButton": { + "message": "Save", + "description": "Text for buttons used to save changes" + }, + "revertButton": { + "message": "Revert", + "description": "Text for buttons used to revert changes" + }, + "importAndAppendButton": { + "message": "Import and append…", + "description": "Text for buttons used to import and append content" + }, + "exportButton": { + "message": "Export…", + "description": "Text for buttons used to export content" + }, + "dnrRulesSummary": { + "message": "Enter your own DNR rules below. Do not add content from untrusted sources.", + "description": "Short description of the DNR rules editor pane" } } diff --git a/platform/mv3/extension/_locales/et/messages.json b/platform/mv3/extension/_locales/et/messages.json index 7718b5b06..3fe467a1d 100644 --- a/platform/mv3/extension/_locales/et/messages.json +++ b/platform/mv3/extension/_locales/et/messages.json @@ -233,8 +233,16 @@ }, "enableStrictBlockLegend": { "message": "Kahtlastele veebilehtedele suunamist takistatakse ja pakutakse võimalust jätkamiseks.", + "description": "Short description for a checkbox in the options page" + }, + "developerModeLabel": { + "message": "Developer mode", "description": "Label for a checkbox in the options page" }, + "developerModeLegend": { + "message": "Enable access to features suitable for technical users.", + "description": "Short description for a checkbox in the options page" + }, "findListsPlaceholder": { "message": "Otsi nimekirju", "description": "Placeholder for the input field used to find lists" @@ -282,5 +290,25 @@ "zapperTipQuit": { "message": "Välju elemendi hävitusrežiimist", "description": "Tooltip for the button used to exit zapper mode" + }, + "saveButton": { + "message": "Save", + "description": "Text for buttons used to save changes" + }, + "revertButton": { + "message": "Revert", + "description": "Text for buttons used to revert changes" + }, + "importAndAppendButton": { + "message": "Import and append…", + "description": "Text for buttons used to import and append content" + }, + "exportButton": { + "message": "Export…", + "description": "Text for buttons used to export content" + }, + "dnrRulesSummary": { + "message": "Enter your own DNR rules below. Do not add content from untrusted sources.", + "description": "Short description of the DNR rules editor pane" } } diff --git a/platform/mv3/extension/_locales/eu/messages.json b/platform/mv3/extension/_locales/eu/messages.json index 9b189acec..22594ceb4 100644 --- a/platform/mv3/extension/_locales/eu/messages.json +++ b/platform/mv3/extension/_locales/eu/messages.json @@ -233,8 +233,16 @@ }, "enableStrictBlockLegend": { "message": "Navigation to potentially undesirable sites will be blocked, and you will be offered the option to proceed.", + "description": "Short description for a checkbox in the options page" + }, + "developerModeLabel": { + "message": "Developer mode", "description": "Label for a checkbox in the options page" }, + "developerModeLegend": { + "message": "Enable access to features suitable for technical users.", + "description": "Short description for a checkbox in the options page" + }, "findListsPlaceholder": { "message": "Find lists", "description": "Placeholder for the input field used to find lists" @@ -282,5 +290,25 @@ "zapperTipQuit": { "message": "Irten elementua zapper moduan", "description": "Tooltip for the button used to exit zapper mode" + }, + "saveButton": { + "message": "Save", + "description": "Text for buttons used to save changes" + }, + "revertButton": { + "message": "Revert", + "description": "Text for buttons used to revert changes" + }, + "importAndAppendButton": { + "message": "Import and append…", + "description": "Text for buttons used to import and append content" + }, + "exportButton": { + "message": "Export…", + "description": "Text for buttons used to export content" + }, + "dnrRulesSummary": { + "message": "Enter your own DNR rules below. Do not add content from untrusted sources.", + "description": "Short description of the DNR rules editor pane" } } diff --git a/platform/mv3/extension/_locales/fa/messages.json b/platform/mv3/extension/_locales/fa/messages.json index 28c60c1b4..743cc736a 100644 --- a/platform/mv3/extension/_locales/fa/messages.json +++ b/platform/mv3/extension/_locales/fa/messages.json @@ -233,8 +233,16 @@ }, "enableStrictBlockLegend": { "message": "Navigation to potentially undesirable sites will be blocked, and you will be offered the option to proceed.", + "description": "Short description for a checkbox in the options page" + }, + "developerModeLabel": { + "message": "Developer mode", "description": "Label for a checkbox in the options page" }, + "developerModeLegend": { + "message": "Enable access to features suitable for technical users.", + "description": "Short description for a checkbox in the options page" + }, "findListsPlaceholder": { "message": "Find lists", "description": "Placeholder for the input field used to find lists" @@ -282,5 +290,25 @@ "zapperTipQuit": { "message": "Exit element zapper mode", "description": "Tooltip for the button used to exit zapper mode" + }, + "saveButton": { + "message": "Save", + "description": "Text for buttons used to save changes" + }, + "revertButton": { + "message": "Revert", + "description": "Text for buttons used to revert changes" + }, + "importAndAppendButton": { + "message": "Import and append…", + "description": "Text for buttons used to import and append content" + }, + "exportButton": { + "message": "Export…", + "description": "Text for buttons used to export content" + }, + "dnrRulesSummary": { + "message": "Enter your own DNR rules below. Do not add content from untrusted sources.", + "description": "Short description of the DNR rules editor pane" } } diff --git a/platform/mv3/extension/_locales/fi/messages.json b/platform/mv3/extension/_locales/fi/messages.json index 92bee243e..e29c903ff 100644 --- a/platform/mv3/extension/_locales/fi/messages.json +++ b/platform/mv3/extension/_locales/fi/messages.json @@ -233,8 +233,16 @@ }, "enableStrictBlockLegend": { "message": "Potentiaalisesti ei-toivottujen sivustojen avaaminen estetään mahdollisuudella jatkaa.", + "description": "Short description for a checkbox in the options page" + }, + "developerModeLabel": { + "message": "Developer mode", "description": "Label for a checkbox in the options page" }, + "developerModeLegend": { + "message": "Enable access to features suitable for technical users.", + "description": "Short description for a checkbox in the options page" + }, "findListsPlaceholder": { "message": "Etsi listoja", "description": "Placeholder for the input field used to find lists" @@ -282,5 +290,25 @@ "zapperTipQuit": { "message": "Exit element zapper mode", "description": "Tooltip for the button used to exit zapper mode" + }, + "saveButton": { + "message": "Save", + "description": "Text for buttons used to save changes" + }, + "revertButton": { + "message": "Revert", + "description": "Text for buttons used to revert changes" + }, + "importAndAppendButton": { + "message": "Import and append…", + "description": "Text for buttons used to import and append content" + }, + "exportButton": { + "message": "Export…", + "description": "Text for buttons used to export content" + }, + "dnrRulesSummary": { + "message": "Enter your own DNR rules below. Do not add content from untrusted sources.", + "description": "Short description of the DNR rules editor pane" } } diff --git a/platform/mv3/extension/_locales/fil/messages.json b/platform/mv3/extension/_locales/fil/messages.json index abd45087f..a9ed6840d 100644 --- a/platform/mv3/extension/_locales/fil/messages.json +++ b/platform/mv3/extension/_locales/fil/messages.json @@ -233,8 +233,16 @@ }, "enableStrictBlockLegend": { "message": "Navigation to potentially undesirable sites will be blocked, and you will be offered the option to proceed.", + "description": "Short description for a checkbox in the options page" + }, + "developerModeLabel": { + "message": "Developer mode", "description": "Label for a checkbox in the options page" }, + "developerModeLegend": { + "message": "Enable access to features suitable for technical users.", + "description": "Short description for a checkbox in the options page" + }, "findListsPlaceholder": { "message": "Find lists", "description": "Placeholder for the input field used to find lists" @@ -282,5 +290,25 @@ "zapperTipQuit": { "message": "Exit element zapper mode", "description": "Tooltip for the button used to exit zapper mode" + }, + "saveButton": { + "message": "Save", + "description": "Text for buttons used to save changes" + }, + "revertButton": { + "message": "Revert", + "description": "Text for buttons used to revert changes" + }, + "importAndAppendButton": { + "message": "Import and append…", + "description": "Text for buttons used to import and append content" + }, + "exportButton": { + "message": "Export…", + "description": "Text for buttons used to export content" + }, + "dnrRulesSummary": { + "message": "Enter your own DNR rules below. Do not add content from untrusted sources.", + "description": "Short description of the DNR rules editor pane" } } diff --git a/platform/mv3/extension/_locales/fr/messages.json b/platform/mv3/extension/_locales/fr/messages.json index 1b103c3fe..7bc28e98a 100644 --- a/platform/mv3/extension/_locales/fr/messages.json +++ b/platform/mv3/extension/_locales/fr/messages.json @@ -233,8 +233,16 @@ }, "enableStrictBlockLegend": { "message": "La navigation vers des sites potentiellement indésirables sera bloquée, et vous aurez la possibilité de continuer", + "description": "Short description for a checkbox in the options page" + }, + "developerModeLabel": { + "message": "Developer mode", "description": "Label for a checkbox in the options page" }, + "developerModeLegend": { + "message": "Enable access to features suitable for technical users.", + "description": "Short description for a checkbox in the options page" + }, "findListsPlaceholder": { "message": "Trouver des listes", "description": "Placeholder for the input field used to find lists" @@ -282,5 +290,25 @@ "zapperTipQuit": { "message": "Quitter le mode Zappeur d'élément", "description": "Tooltip for the button used to exit zapper mode" + }, + "saveButton": { + "message": "Save", + "description": "Text for buttons used to save changes" + }, + "revertButton": { + "message": "Revert", + "description": "Text for buttons used to revert changes" + }, + "importAndAppendButton": { + "message": "Import and append…", + "description": "Text for buttons used to import and append content" + }, + "exportButton": { + "message": "Export…", + "description": "Text for buttons used to export content" + }, + "dnrRulesSummary": { + "message": "Enter your own DNR rules below. Do not add content from untrusted sources.", + "description": "Short description of the DNR rules editor pane" } } diff --git a/platform/mv3/extension/_locales/fy/messages.json b/platform/mv3/extension/_locales/fy/messages.json index 37f87477d..b2d0f0b1b 100644 --- a/platform/mv3/extension/_locales/fy/messages.json +++ b/platform/mv3/extension/_locales/fy/messages.json @@ -233,8 +233,16 @@ }, "enableStrictBlockLegend": { "message": "Navigaasje nei potinsjeel net-winske websites wurdt blokkearre, en jo krije de opsje om troch te gean.", + "description": "Short description for a checkbox in the options page" + }, + "developerModeLabel": { + "message": "Developer mode", "description": "Label for a checkbox in the options page" }, + "developerModeLegend": { + "message": "Enable access to features suitable for technical users.", + "description": "Short description for a checkbox in the options page" + }, "findListsPlaceholder": { "message": "Listen sykje", "description": "Placeholder for the input field used to find lists" @@ -282,5 +290,25 @@ "zapperTipQuit": { "message": "Elemint­wisker­modus slute", "description": "Tooltip for the button used to exit zapper mode" + }, + "saveButton": { + "message": "Save", + "description": "Text for buttons used to save changes" + }, + "revertButton": { + "message": "Revert", + "description": "Text for buttons used to revert changes" + }, + "importAndAppendButton": { + "message": "Import and append…", + "description": "Text for buttons used to import and append content" + }, + "exportButton": { + "message": "Export…", + "description": "Text for buttons used to export content" + }, + "dnrRulesSummary": { + "message": "Enter your own DNR rules below. Do not add content from untrusted sources.", + "description": "Short description of the DNR rules editor pane" } } diff --git a/platform/mv3/extension/_locales/gl/messages.json b/platform/mv3/extension/_locales/gl/messages.json index b6d4e0084..4640caa73 100644 --- a/platform/mv3/extension/_locales/gl/messages.json +++ b/platform/mv3/extension/_locales/gl/messages.json @@ -233,8 +233,16 @@ }, "enableStrictBlockLegend": { "message": "Vaise bloquear a navegación en webs potencialmente non desexables, e ofrecerase a opción de bloquealas.", + "description": "Short description for a checkbox in the options page" + }, + "developerModeLabel": { + "message": "Developer mode", "description": "Label for a checkbox in the options page" }, + "developerModeLegend": { + "message": "Enable access to features suitable for technical users.", + "description": "Short description for a checkbox in the options page" + }, "findListsPlaceholder": { "message": "Atopa listas", "description": "Placeholder for the input field used to find lists" @@ -282,5 +290,25 @@ "zapperTipQuit": { "message": "Sair do modo eliminador de elementos", "description": "Tooltip for the button used to exit zapper mode" + }, + "saveButton": { + "message": "Save", + "description": "Text for buttons used to save changes" + }, + "revertButton": { + "message": "Revert", + "description": "Text for buttons used to revert changes" + }, + "importAndAppendButton": { + "message": "Import and append…", + "description": "Text for buttons used to import and append content" + }, + "exportButton": { + "message": "Export…", + "description": "Text for buttons used to export content" + }, + "dnrRulesSummary": { + "message": "Enter your own DNR rules below. Do not add content from untrusted sources.", + "description": "Short description of the DNR rules editor pane" } } diff --git a/platform/mv3/extension/_locales/gu/messages.json b/platform/mv3/extension/_locales/gu/messages.json index ace2c1d9f..136a025b9 100644 --- a/platform/mv3/extension/_locales/gu/messages.json +++ b/platform/mv3/extension/_locales/gu/messages.json @@ -233,8 +233,16 @@ }, "enableStrictBlockLegend": { "message": "Navigation to potentially undesirable sites will be blocked, and you will be offered the option to proceed.", + "description": "Short description for a checkbox in the options page" + }, + "developerModeLabel": { + "message": "Developer mode", "description": "Label for a checkbox in the options page" }, + "developerModeLegend": { + "message": "Enable access to features suitable for technical users.", + "description": "Short description for a checkbox in the options page" + }, "findListsPlaceholder": { "message": "Find lists", "description": "Placeholder for the input field used to find lists" @@ -282,5 +290,25 @@ "zapperTipQuit": { "message": "Exit element zapper mode", "description": "Tooltip for the button used to exit zapper mode" + }, + "saveButton": { + "message": "Save", + "description": "Text for buttons used to save changes" + }, + "revertButton": { + "message": "Revert", + "description": "Text for buttons used to revert changes" + }, + "importAndAppendButton": { + "message": "Import and append…", + "description": "Text for buttons used to import and append content" + }, + "exportButton": { + "message": "Export…", + "description": "Text for buttons used to export content" + }, + "dnrRulesSummary": { + "message": "Enter your own DNR rules below. Do not add content from untrusted sources.", + "description": "Short description of the DNR rules editor pane" } } diff --git a/platform/mv3/extension/_locales/he/messages.json b/platform/mv3/extension/_locales/he/messages.json index 7118496ce..c27390163 100644 --- a/platform/mv3/extension/_locales/he/messages.json +++ b/platform/mv3/extension/_locales/he/messages.json @@ -233,8 +233,16 @@ }, "enableStrictBlockLegend": { "message": "ניווט אפשרי לאתרים לא רצויים יחסם ותהיה אפשרות להחליט להמשיך.", + "description": "Short description for a checkbox in the options page" + }, + "developerModeLabel": { + "message": "Developer mode", "description": "Label for a checkbox in the options page" }, + "developerModeLegend": { + "message": "Enable access to features suitable for technical users.", + "description": "Short description for a checkbox in the options page" + }, "findListsPlaceholder": { "message": "חיפוש רשימות", "description": "Placeholder for the input field used to find lists" @@ -282,5 +290,25 @@ "zapperTipQuit": { "message": "Exit element zapper mode", "description": "Tooltip for the button used to exit zapper mode" + }, + "saveButton": { + "message": "Save", + "description": "Text for buttons used to save changes" + }, + "revertButton": { + "message": "Revert", + "description": "Text for buttons used to revert changes" + }, + "importAndAppendButton": { + "message": "Import and append…", + "description": "Text for buttons used to import and append content" + }, + "exportButton": { + "message": "Export…", + "description": "Text for buttons used to export content" + }, + "dnrRulesSummary": { + "message": "Enter your own DNR rules below. Do not add content from untrusted sources.", + "description": "Short description of the DNR rules editor pane" } } diff --git a/platform/mv3/extension/_locales/hi/messages.json b/platform/mv3/extension/_locales/hi/messages.json index f28896b99..42fae5800 100644 --- a/platform/mv3/extension/_locales/hi/messages.json +++ b/platform/mv3/extension/_locales/hi/messages.json @@ -233,8 +233,16 @@ }, "enableStrictBlockLegend": { "message": "Navigation to potentially undesirable sites will be blocked, and you will be offered the option to proceed.", + "description": "Short description for a checkbox in the options page" + }, + "developerModeLabel": { + "message": "Developer mode", "description": "Label for a checkbox in the options page" }, + "developerModeLegend": { + "message": "Enable access to features suitable for technical users.", + "description": "Short description for a checkbox in the options page" + }, "findListsPlaceholder": { "message": "Find lists", "description": "Placeholder for the input field used to find lists" @@ -282,5 +290,25 @@ "zapperTipQuit": { "message": "Exit element zapper mode", "description": "Tooltip for the button used to exit zapper mode" + }, + "saveButton": { + "message": "Save", + "description": "Text for buttons used to save changes" + }, + "revertButton": { + "message": "Revert", + "description": "Text for buttons used to revert changes" + }, + "importAndAppendButton": { + "message": "Import and append…", + "description": "Text for buttons used to import and append content" + }, + "exportButton": { + "message": "Export…", + "description": "Text for buttons used to export content" + }, + "dnrRulesSummary": { + "message": "Enter your own DNR rules below. Do not add content from untrusted sources.", + "description": "Short description of the DNR rules editor pane" } } diff --git a/platform/mv3/extension/_locales/hr/messages.json b/platform/mv3/extension/_locales/hr/messages.json index f3086f263..ed9ea6c1d 100644 --- a/platform/mv3/extension/_locales/hr/messages.json +++ b/platform/mv3/extension/_locales/hr/messages.json @@ -233,8 +233,16 @@ }, "enableStrictBlockLegend": { "message": "Navigacija do potencijalno nepoželjnih stranica bit će blokirana i bit će vam ponuđena opcija za nastavak.", + "description": "Short description for a checkbox in the options page" + }, + "developerModeLabel": { + "message": "Developer mode", "description": "Label for a checkbox in the options page" }, + "developerModeLegend": { + "message": "Enable access to features suitable for technical users.", + "description": "Short description for a checkbox in the options page" + }, "findListsPlaceholder": { "message": "Pronađi liste", "description": "Placeholder for the input field used to find lists" @@ -282,5 +290,25 @@ "zapperTipQuit": { "message": "Zatvori način rada uklanjanja elementa", "description": "Tooltip for the button used to exit zapper mode" + }, + "saveButton": { + "message": "Save", + "description": "Text for buttons used to save changes" + }, + "revertButton": { + "message": "Revert", + "description": "Text for buttons used to revert changes" + }, + "importAndAppendButton": { + "message": "Import and append…", + "description": "Text for buttons used to import and append content" + }, + "exportButton": { + "message": "Export…", + "description": "Text for buttons used to export content" + }, + "dnrRulesSummary": { + "message": "Enter your own DNR rules below. Do not add content from untrusted sources.", + "description": "Short description of the DNR rules editor pane" } } diff --git a/platform/mv3/extension/_locales/hu/messages.json b/platform/mv3/extension/_locales/hu/messages.json index fa53a4550..d7eee7347 100644 --- a/platform/mv3/extension/_locales/hu/messages.json +++ b/platform/mv3/extension/_locales/hu/messages.json @@ -233,8 +233,16 @@ }, "enableStrictBlockLegend": { "message": "Az esetleges nem kívánatos webhelyekre való navigáció blokkolva lesz, és rákérdez arra, hogy folytatja-e.", + "description": "Short description for a checkbox in the options page" + }, + "developerModeLabel": { + "message": "Developer mode", "description": "Label for a checkbox in the options page" }, + "developerModeLegend": { + "message": "Enable access to features suitable for technical users.", + "description": "Short description for a checkbox in the options page" + }, "findListsPlaceholder": { "message": "Listák keresése", "description": "Placeholder for the input field used to find lists" @@ -282,5 +290,25 @@ "zapperTipQuit": { "message": "Kilépés az elemeltávolító módból", "description": "Tooltip for the button used to exit zapper mode" + }, + "saveButton": { + "message": "Save", + "description": "Text for buttons used to save changes" + }, + "revertButton": { + "message": "Revert", + "description": "Text for buttons used to revert changes" + }, + "importAndAppendButton": { + "message": "Import and append…", + "description": "Text for buttons used to import and append content" + }, + "exportButton": { + "message": "Export…", + "description": "Text for buttons used to export content" + }, + "dnrRulesSummary": { + "message": "Enter your own DNR rules below. Do not add content from untrusted sources.", + "description": "Short description of the DNR rules editor pane" } } diff --git a/platform/mv3/extension/_locales/hy/messages.json b/platform/mv3/extension/_locales/hy/messages.json index 60c32c406..853b02071 100644 --- a/platform/mv3/extension/_locales/hy/messages.json +++ b/platform/mv3/extension/_locales/hy/messages.json @@ -233,8 +233,16 @@ }, "enableStrictBlockLegend": { "message": "Navigation to potentially undesirable sites will be blocked, and you will be offered the option to proceed.", + "description": "Short description for a checkbox in the options page" + }, + "developerModeLabel": { + "message": "Developer mode", "description": "Label for a checkbox in the options page" }, + "developerModeLegend": { + "message": "Enable access to features suitable for technical users.", + "description": "Short description for a checkbox in the options page" + }, "findListsPlaceholder": { "message": "Find lists", "description": "Placeholder for the input field used to find lists" @@ -282,5 +290,25 @@ "zapperTipQuit": { "message": "Exit element zapper mode", "description": "Tooltip for the button used to exit zapper mode" + }, + "saveButton": { + "message": "Save", + "description": "Text for buttons used to save changes" + }, + "revertButton": { + "message": "Revert", + "description": "Text for buttons used to revert changes" + }, + "importAndAppendButton": { + "message": "Import and append…", + "description": "Text for buttons used to import and append content" + }, + "exportButton": { + "message": "Export…", + "description": "Text for buttons used to export content" + }, + "dnrRulesSummary": { + "message": "Enter your own DNR rules below. Do not add content from untrusted sources.", + "description": "Short description of the DNR rules editor pane" } } diff --git a/platform/mv3/extension/_locales/id/messages.json b/platform/mv3/extension/_locales/id/messages.json index 1f0e33840..9760f0e83 100644 --- a/platform/mv3/extension/_locales/id/messages.json +++ b/platform/mv3/extension/_locales/id/messages.json @@ -233,8 +233,16 @@ }, "enableStrictBlockLegend": { "message": "Navigasi ke situs yang mungkin tidak diinginkan akan diblokir, dan Anda akan ditawari opsi untuk melanjutkan.", + "description": "Short description for a checkbox in the options page" + }, + "developerModeLabel": { + "message": "Developer mode", "description": "Label for a checkbox in the options page" }, + "developerModeLegend": { + "message": "Enable access to features suitable for technical users.", + "description": "Short description for a checkbox in the options page" + }, "findListsPlaceholder": { "message": "Temukan daftar", "description": "Placeholder for the input field used to find lists" @@ -282,5 +290,25 @@ "zapperTipQuit": { "message": "Exit element zapper mode", "description": "Tooltip for the button used to exit zapper mode" + }, + "saveButton": { + "message": "Save", + "description": "Text for buttons used to save changes" + }, + "revertButton": { + "message": "Revert", + "description": "Text for buttons used to revert changes" + }, + "importAndAppendButton": { + "message": "Import and append…", + "description": "Text for buttons used to import and append content" + }, + "exportButton": { + "message": "Export…", + "description": "Text for buttons used to export content" + }, + "dnrRulesSummary": { + "message": "Enter your own DNR rules below. Do not add content from untrusted sources.", + "description": "Short description of the DNR rules editor pane" } } diff --git a/platform/mv3/extension/_locales/it/messages.json b/platform/mv3/extension/_locales/it/messages.json index b0a15c153..53b04b95c 100644 --- a/platform/mv3/extension/_locales/it/messages.json +++ b/platform/mv3/extension/_locales/it/messages.json @@ -233,8 +233,16 @@ }, "enableStrictBlockLegend": { "message": "La navigazione verso siti potenzialmente indesiderati verrà bloccata e ti verrà offerta la possibilità di procedere.", + "description": "Short description for a checkbox in the options page" + }, + "developerModeLabel": { + "message": "Developer mode", "description": "Label for a checkbox in the options page" }, + "developerModeLegend": { + "message": "Enable access to features suitable for technical users.", + "description": "Short description for a checkbox in the options page" + }, "findListsPlaceholder": { "message": "Trova elenchi", "description": "Placeholder for the input field used to find lists" @@ -282,5 +290,25 @@ "zapperTipQuit": { "message": "Esci dalla modalità blocco rapido", "description": "Tooltip for the button used to exit zapper mode" + }, + "saveButton": { + "message": "Save", + "description": "Text for buttons used to save changes" + }, + "revertButton": { + "message": "Revert", + "description": "Text for buttons used to revert changes" + }, + "importAndAppendButton": { + "message": "Import and append…", + "description": "Text for buttons used to import and append content" + }, + "exportButton": { + "message": "Export…", + "description": "Text for buttons used to export content" + }, + "dnrRulesSummary": { + "message": "Enter your own DNR rules below. Do not add content from untrusted sources.", + "description": "Short description of the DNR rules editor pane" } } diff --git a/platform/mv3/extension/_locales/ja/messages.json b/platform/mv3/extension/_locales/ja/messages.json index 454941642..0e00e22a7 100644 --- a/platform/mv3/extension/_locales/ja/messages.json +++ b/platform/mv3/extension/_locales/ja/messages.json @@ -233,8 +233,16 @@ }, "enableStrictBlockLegend": { "message": "望ましくない可能性のあるサイトへのナビゲーションはブロックされ、次に進むためのオプションが表示されます。", + "description": "Short description for a checkbox in the options page" + }, + "developerModeLabel": { + "message": "Developer mode", "description": "Label for a checkbox in the options page" }, + "developerModeLegend": { + "message": "Enable access to features suitable for technical users.", + "description": "Short description for a checkbox in the options page" + }, "findListsPlaceholder": { "message": "リストを検索", "description": "Placeholder for the input field used to find lists" @@ -282,5 +290,25 @@ "zapperTipQuit": { "message": "要素抹消モードを終了", "description": "Tooltip for the button used to exit zapper mode" + }, + "saveButton": { + "message": "Save", + "description": "Text for buttons used to save changes" + }, + "revertButton": { + "message": "Revert", + "description": "Text for buttons used to revert changes" + }, + "importAndAppendButton": { + "message": "Import and append…", + "description": "Text for buttons used to import and append content" + }, + "exportButton": { + "message": "Export…", + "description": "Text for buttons used to export content" + }, + "dnrRulesSummary": { + "message": "Enter your own DNR rules below. Do not add content from untrusted sources.", + "description": "Short description of the DNR rules editor pane" } } diff --git a/platform/mv3/extension/_locales/ka/messages.json b/platform/mv3/extension/_locales/ka/messages.json index b47d3bae9..59b33f61b 100644 --- a/platform/mv3/extension/_locales/ka/messages.json +++ b/platform/mv3/extension/_locales/ka/messages.json @@ -233,8 +233,16 @@ }, "enableStrictBlockLegend": { "message": "შეიზღუდა სავარაუდოდ არასასურველ გვერდებზე გადასვლა, საშუალება გეძლევათ, მაინც განაგრძოთ.", + "description": "Short description for a checkbox in the options page" + }, + "developerModeLabel": { + "message": "Developer mode", "description": "Label for a checkbox in the options page" }, + "developerModeLegend": { + "message": "Enable access to features suitable for technical users.", + "description": "Short description for a checkbox in the options page" + }, "findListsPlaceholder": { "message": "სიების მოძიება", "description": "Placeholder for the input field used to find lists" @@ -282,5 +290,25 @@ "zapperTipQuit": { "message": "ნაწილების ამოჭრის რეჟიმიდან გასვლა", "description": "Tooltip for the button used to exit zapper mode" + }, + "saveButton": { + "message": "Save", + "description": "Text for buttons used to save changes" + }, + "revertButton": { + "message": "Revert", + "description": "Text for buttons used to revert changes" + }, + "importAndAppendButton": { + "message": "Import and append…", + "description": "Text for buttons used to import and append content" + }, + "exportButton": { + "message": "Export…", + "description": "Text for buttons used to export content" + }, + "dnrRulesSummary": { + "message": "Enter your own DNR rules below. Do not add content from untrusted sources.", + "description": "Short description of the DNR rules editor pane" } } diff --git a/platform/mv3/extension/_locales/kk/messages.json b/platform/mv3/extension/_locales/kk/messages.json index ace2c1d9f..136a025b9 100644 --- a/platform/mv3/extension/_locales/kk/messages.json +++ b/platform/mv3/extension/_locales/kk/messages.json @@ -233,8 +233,16 @@ }, "enableStrictBlockLegend": { "message": "Navigation to potentially undesirable sites will be blocked, and you will be offered the option to proceed.", + "description": "Short description for a checkbox in the options page" + }, + "developerModeLabel": { + "message": "Developer mode", "description": "Label for a checkbox in the options page" }, + "developerModeLegend": { + "message": "Enable access to features suitable for technical users.", + "description": "Short description for a checkbox in the options page" + }, "findListsPlaceholder": { "message": "Find lists", "description": "Placeholder for the input field used to find lists" @@ -282,5 +290,25 @@ "zapperTipQuit": { "message": "Exit element zapper mode", "description": "Tooltip for the button used to exit zapper mode" + }, + "saveButton": { + "message": "Save", + "description": "Text for buttons used to save changes" + }, + "revertButton": { + "message": "Revert", + "description": "Text for buttons used to revert changes" + }, + "importAndAppendButton": { + "message": "Import and append…", + "description": "Text for buttons used to import and append content" + }, + "exportButton": { + "message": "Export…", + "description": "Text for buttons used to export content" + }, + "dnrRulesSummary": { + "message": "Enter your own DNR rules below. Do not add content from untrusted sources.", + "description": "Short description of the DNR rules editor pane" } } diff --git a/platform/mv3/extension/_locales/kn/messages.json b/platform/mv3/extension/_locales/kn/messages.json index fca0ab51c..357c70f7e 100644 --- a/platform/mv3/extension/_locales/kn/messages.json +++ b/platform/mv3/extension/_locales/kn/messages.json @@ -233,8 +233,16 @@ }, "enableStrictBlockLegend": { "message": "Navigation to potentially undesirable sites will be blocked, and you will be offered the option to proceed.", + "description": "Short description for a checkbox in the options page" + }, + "developerModeLabel": { + "message": "Developer mode", "description": "Label for a checkbox in the options page" }, + "developerModeLegend": { + "message": "Enable access to features suitable for technical users.", + "description": "Short description for a checkbox in the options page" + }, "findListsPlaceholder": { "message": "Find lists", "description": "Placeholder for the input field used to find lists" @@ -282,5 +290,25 @@ "zapperTipQuit": { "message": "Exit element zapper mode", "description": "Tooltip for the button used to exit zapper mode" + }, + "saveButton": { + "message": "Save", + "description": "Text for buttons used to save changes" + }, + "revertButton": { + "message": "Revert", + "description": "Text for buttons used to revert changes" + }, + "importAndAppendButton": { + "message": "Import and append…", + "description": "Text for buttons used to import and append content" + }, + "exportButton": { + "message": "Export…", + "description": "Text for buttons used to export content" + }, + "dnrRulesSummary": { + "message": "Enter your own DNR rules below. Do not add content from untrusted sources.", + "description": "Short description of the DNR rules editor pane" } } diff --git a/platform/mv3/extension/_locales/ko/messages.json b/platform/mv3/extension/_locales/ko/messages.json index 4776053f3..f18e86be1 100644 --- a/platform/mv3/extension/_locales/ko/messages.json +++ b/platform/mv3/extension/_locales/ko/messages.json @@ -233,8 +233,16 @@ }, "enableStrictBlockLegend": { "message": "잠재적으로 좋지 않은 사이트로의 접속을 차단하고, 사용자가 진행할지 선택하도록 합니다.", + "description": "Short description for a checkbox in the options page" + }, + "developerModeLabel": { + "message": "Developer mode", "description": "Label for a checkbox in the options page" }, + "developerModeLegend": { + "message": "Enable access to features suitable for technical users.", + "description": "Short description for a checkbox in the options page" + }, "findListsPlaceholder": { "message": "목록 찾기", "description": "Placeholder for the input field used to find lists" @@ -282,5 +290,25 @@ "zapperTipQuit": { "message": "구성 요소 제거 모드 종료", "description": "Tooltip for the button used to exit zapper mode" + }, + "saveButton": { + "message": "Save", + "description": "Text for buttons used to save changes" + }, + "revertButton": { + "message": "Revert", + "description": "Text for buttons used to revert changes" + }, + "importAndAppendButton": { + "message": "Import and append…", + "description": "Text for buttons used to import and append content" + }, + "exportButton": { + "message": "Export…", + "description": "Text for buttons used to export content" + }, + "dnrRulesSummary": { + "message": "Enter your own DNR rules below. Do not add content from untrusted sources.", + "description": "Short description of the DNR rules editor pane" } } diff --git a/platform/mv3/extension/_locales/lt/messages.json b/platform/mv3/extension/_locales/lt/messages.json index 810d9a3a6..60955d7cd 100644 --- a/platform/mv3/extension/_locales/lt/messages.json +++ b/platform/mv3/extension/_locales/lt/messages.json @@ -233,8 +233,16 @@ }, "enableStrictBlockLegend": { "message": "Navigation to potentially undesirable sites will be blocked, and you will be offered the option to proceed.", + "description": "Short description for a checkbox in the options page" + }, + "developerModeLabel": { + "message": "Developer mode", "description": "Label for a checkbox in the options page" }, + "developerModeLegend": { + "message": "Enable access to features suitable for technical users.", + "description": "Short description for a checkbox in the options page" + }, "findListsPlaceholder": { "message": "Find lists", "description": "Placeholder for the input field used to find lists" @@ -282,5 +290,25 @@ "zapperTipQuit": { "message": "Exit element zapper mode", "description": "Tooltip for the button used to exit zapper mode" + }, + "saveButton": { + "message": "Save", + "description": "Text for buttons used to save changes" + }, + "revertButton": { + "message": "Revert", + "description": "Text for buttons used to revert changes" + }, + "importAndAppendButton": { + "message": "Import and append…", + "description": "Text for buttons used to import and append content" + }, + "exportButton": { + "message": "Export…", + "description": "Text for buttons used to export content" + }, + "dnrRulesSummary": { + "message": "Enter your own DNR rules below. Do not add content from untrusted sources.", + "description": "Short description of the DNR rules editor pane" } } diff --git a/platform/mv3/extension/_locales/lv/messages.json b/platform/mv3/extension/_locales/lv/messages.json index 204e683bc..c308f2c46 100644 --- a/platform/mv3/extension/_locales/lv/messages.json +++ b/platform/mv3/extension/_locales/lv/messages.json @@ -233,8 +233,16 @@ }, "enableStrictBlockLegend": { "message": "Iespējami nevēlamu vietņu apmeklēšana tiks aizturēta, un tiks piedāvāta iespēja turpināt.", + "description": "Short description for a checkbox in the options page" + }, + "developerModeLabel": { + "message": "Developer mode", "description": "Label for a checkbox in the options page" }, + "developerModeLegend": { + "message": "Enable access to features suitable for technical users.", + "description": "Short description for a checkbox in the options page" + }, "findListsPlaceholder": { "message": "Atrast sarakstus", "description": "Placeholder for the input field used to find lists" @@ -282,5 +290,25 @@ "zapperTipQuit": { "message": "Iziet no elementu iznīcināšanas", "description": "Tooltip for the button used to exit zapper mode" + }, + "saveButton": { + "message": "Save", + "description": "Text for buttons used to save changes" + }, + "revertButton": { + "message": "Revert", + "description": "Text for buttons used to revert changes" + }, + "importAndAppendButton": { + "message": "Import and append…", + "description": "Text for buttons used to import and append content" + }, + "exportButton": { + "message": "Export…", + "description": "Text for buttons used to export content" + }, + "dnrRulesSummary": { + "message": "Enter your own DNR rules below. Do not add content from untrusted sources.", + "description": "Short description of the DNR rules editor pane" } } diff --git a/platform/mv3/extension/_locales/mk/messages.json b/platform/mv3/extension/_locales/mk/messages.json index 0d6146d37..431dba89a 100644 --- a/platform/mv3/extension/_locales/mk/messages.json +++ b/platform/mv3/extension/_locales/mk/messages.json @@ -233,8 +233,16 @@ }, "enableStrictBlockLegend": { "message": "Navigation to potentially undesirable sites will be blocked, and you will be offered the option to proceed.", + "description": "Short description for a checkbox in the options page" + }, + "developerModeLabel": { + "message": "Developer mode", "description": "Label for a checkbox in the options page" }, + "developerModeLegend": { + "message": "Enable access to features suitable for technical users.", + "description": "Short description for a checkbox in the options page" + }, "findListsPlaceholder": { "message": "Најди листи", "description": "Placeholder for the input field used to find lists" @@ -282,5 +290,25 @@ "zapperTipQuit": { "message": "Exit element zapper mode", "description": "Tooltip for the button used to exit zapper mode" + }, + "saveButton": { + "message": "Save", + "description": "Text for buttons used to save changes" + }, + "revertButton": { + "message": "Revert", + "description": "Text for buttons used to revert changes" + }, + "importAndAppendButton": { + "message": "Import and append…", + "description": "Text for buttons used to import and append content" + }, + "exportButton": { + "message": "Export…", + "description": "Text for buttons used to export content" + }, + "dnrRulesSummary": { + "message": "Enter your own DNR rules below. Do not add content from untrusted sources.", + "description": "Short description of the DNR rules editor pane" } } diff --git a/platform/mv3/extension/_locales/ml/messages.json b/platform/mv3/extension/_locales/ml/messages.json index 8bb8f3961..4e6a284b4 100644 --- a/platform/mv3/extension/_locales/ml/messages.json +++ b/platform/mv3/extension/_locales/ml/messages.json @@ -233,8 +233,16 @@ }, "enableStrictBlockLegend": { "message": "Navigation to potentially undesirable sites will be blocked, and you will be offered the option to proceed.", + "description": "Short description for a checkbox in the options page" + }, + "developerModeLabel": { + "message": "Developer mode", "description": "Label for a checkbox in the options page" }, + "developerModeLegend": { + "message": "Enable access to features suitable for technical users.", + "description": "Short description for a checkbox in the options page" + }, "findListsPlaceholder": { "message": "Find lists", "description": "Placeholder for the input field used to find lists" @@ -282,5 +290,25 @@ "zapperTipQuit": { "message": "Exit element zapper mode", "description": "Tooltip for the button used to exit zapper mode" + }, + "saveButton": { + "message": "Save", + "description": "Text for buttons used to save changes" + }, + "revertButton": { + "message": "Revert", + "description": "Text for buttons used to revert changes" + }, + "importAndAppendButton": { + "message": "Import and append…", + "description": "Text for buttons used to import and append content" + }, + "exportButton": { + "message": "Export…", + "description": "Text for buttons used to export content" + }, + "dnrRulesSummary": { + "message": "Enter your own DNR rules below. Do not add content from untrusted sources.", + "description": "Short description of the DNR rules editor pane" } } diff --git a/platform/mv3/extension/_locales/mr/messages.json b/platform/mv3/extension/_locales/mr/messages.json index ace2c1d9f..136a025b9 100644 --- a/platform/mv3/extension/_locales/mr/messages.json +++ b/platform/mv3/extension/_locales/mr/messages.json @@ -233,8 +233,16 @@ }, "enableStrictBlockLegend": { "message": "Navigation to potentially undesirable sites will be blocked, and you will be offered the option to proceed.", + "description": "Short description for a checkbox in the options page" + }, + "developerModeLabel": { + "message": "Developer mode", "description": "Label for a checkbox in the options page" }, + "developerModeLegend": { + "message": "Enable access to features suitable for technical users.", + "description": "Short description for a checkbox in the options page" + }, "findListsPlaceholder": { "message": "Find lists", "description": "Placeholder for the input field used to find lists" @@ -282,5 +290,25 @@ "zapperTipQuit": { "message": "Exit element zapper mode", "description": "Tooltip for the button used to exit zapper mode" + }, + "saveButton": { + "message": "Save", + "description": "Text for buttons used to save changes" + }, + "revertButton": { + "message": "Revert", + "description": "Text for buttons used to revert changes" + }, + "importAndAppendButton": { + "message": "Import and append…", + "description": "Text for buttons used to import and append content" + }, + "exportButton": { + "message": "Export…", + "description": "Text for buttons used to export content" + }, + "dnrRulesSummary": { + "message": "Enter your own DNR rules below. Do not add content from untrusted sources.", + "description": "Short description of the DNR rules editor pane" } } diff --git a/platform/mv3/extension/_locales/ms/messages.json b/platform/mv3/extension/_locales/ms/messages.json index a1031f761..a0c5b761b 100644 --- a/platform/mv3/extension/_locales/ms/messages.json +++ b/platform/mv3/extension/_locales/ms/messages.json @@ -233,8 +233,16 @@ }, "enableStrictBlockLegend": { "message": "Navigation to potentially undesirable sites will be blocked, and you will be offered the option to proceed.", + "description": "Short description for a checkbox in the options page" + }, + "developerModeLabel": { + "message": "Developer mode", "description": "Label for a checkbox in the options page" }, + "developerModeLegend": { + "message": "Enable access to features suitable for technical users.", + "description": "Short description for a checkbox in the options page" + }, "findListsPlaceholder": { "message": "Find lists", "description": "Placeholder for the input field used to find lists" @@ -282,5 +290,25 @@ "zapperTipQuit": { "message": "Exit element zapper mode", "description": "Tooltip for the button used to exit zapper mode" + }, + "saveButton": { + "message": "Save", + "description": "Text for buttons used to save changes" + }, + "revertButton": { + "message": "Revert", + "description": "Text for buttons used to revert changes" + }, + "importAndAppendButton": { + "message": "Import and append…", + "description": "Text for buttons used to import and append content" + }, + "exportButton": { + "message": "Export…", + "description": "Text for buttons used to export content" + }, + "dnrRulesSummary": { + "message": "Enter your own DNR rules below. Do not add content from untrusted sources.", + "description": "Short description of the DNR rules editor pane" } } diff --git a/platform/mv3/extension/_locales/nb/messages.json b/platform/mv3/extension/_locales/nb/messages.json index 6d9ac5503..7282364c2 100644 --- a/platform/mv3/extension/_locales/nb/messages.json +++ b/platform/mv3/extension/_locales/nb/messages.json @@ -233,8 +233,16 @@ }, "enableStrictBlockLegend": { "message": "Navigering til potensielt uønskede nettsteder blir blokkert, og du får tilbud om å fortsette.", + "description": "Short description for a checkbox in the options page" + }, + "developerModeLabel": { + "message": "Developer mode", "description": "Label for a checkbox in the options page" }, + "developerModeLegend": { + "message": "Enable access to features suitable for technical users.", + "description": "Short description for a checkbox in the options page" + }, "findListsPlaceholder": { "message": "Finn lister", "description": "Placeholder for the input field used to find lists" @@ -282,5 +290,25 @@ "zapperTipQuit": { "message": "Exit element zapper mode", "description": "Tooltip for the button used to exit zapper mode" + }, + "saveButton": { + "message": "Save", + "description": "Text for buttons used to save changes" + }, + "revertButton": { + "message": "Revert", + "description": "Text for buttons used to revert changes" + }, + "importAndAppendButton": { + "message": "Import and append…", + "description": "Text for buttons used to import and append content" + }, + "exportButton": { + "message": "Export…", + "description": "Text for buttons used to export content" + }, + "dnrRulesSummary": { + "message": "Enter your own DNR rules below. Do not add content from untrusted sources.", + "description": "Short description of the DNR rules editor pane" } } diff --git a/platform/mv3/extension/_locales/nl/messages.json b/platform/mv3/extension/_locales/nl/messages.json index 9d2692b86..1de06d4a0 100644 --- a/platform/mv3/extension/_locales/nl/messages.json +++ b/platform/mv3/extension/_locales/nl/messages.json @@ -233,8 +233,16 @@ }, "enableStrictBlockLegend": { "message": "Navigatie naar mogelijk ongewenste websites wordt geblokkeerd, en u krijgt de mogelijkheid om door te gaan.", + "description": "Short description for a checkbox in the options page" + }, + "developerModeLabel": { + "message": "Developer mode", "description": "Label for a checkbox in the options page" }, + "developerModeLegend": { + "message": "Enable access to features suitable for technical users.", + "description": "Short description for a checkbox in the options page" + }, "findListsPlaceholder": { "message": "Lijsten zoeken", "description": "Placeholder for the input field used to find lists" @@ -282,5 +290,25 @@ "zapperTipQuit": { "message": "Element­wisser­modus sluiten", "description": "Tooltip for the button used to exit zapper mode" + }, + "saveButton": { + "message": "Save", + "description": "Text for buttons used to save changes" + }, + "revertButton": { + "message": "Revert", + "description": "Text for buttons used to revert changes" + }, + "importAndAppendButton": { + "message": "Import and append…", + "description": "Text for buttons used to import and append content" + }, + "exportButton": { + "message": "Export…", + "description": "Text for buttons used to export content" + }, + "dnrRulesSummary": { + "message": "Enter your own DNR rules below. Do not add content from untrusted sources.", + "description": "Short description of the DNR rules editor pane" } } diff --git a/platform/mv3/extension/_locales/oc/messages.json b/platform/mv3/extension/_locales/oc/messages.json index ace2c1d9f..136a025b9 100644 --- a/platform/mv3/extension/_locales/oc/messages.json +++ b/platform/mv3/extension/_locales/oc/messages.json @@ -233,8 +233,16 @@ }, "enableStrictBlockLegend": { "message": "Navigation to potentially undesirable sites will be blocked, and you will be offered the option to proceed.", + "description": "Short description for a checkbox in the options page" + }, + "developerModeLabel": { + "message": "Developer mode", "description": "Label for a checkbox in the options page" }, + "developerModeLegend": { + "message": "Enable access to features suitable for technical users.", + "description": "Short description for a checkbox in the options page" + }, "findListsPlaceholder": { "message": "Find lists", "description": "Placeholder for the input field used to find lists" @@ -282,5 +290,25 @@ "zapperTipQuit": { "message": "Exit element zapper mode", "description": "Tooltip for the button used to exit zapper mode" + }, + "saveButton": { + "message": "Save", + "description": "Text for buttons used to save changes" + }, + "revertButton": { + "message": "Revert", + "description": "Text for buttons used to revert changes" + }, + "importAndAppendButton": { + "message": "Import and append…", + "description": "Text for buttons used to import and append content" + }, + "exportButton": { + "message": "Export…", + "description": "Text for buttons used to export content" + }, + "dnrRulesSummary": { + "message": "Enter your own DNR rules below. Do not add content from untrusted sources.", + "description": "Short description of the DNR rules editor pane" } } diff --git a/platform/mv3/extension/_locales/pa/messages.json b/platform/mv3/extension/_locales/pa/messages.json index 107f4b98e..035a82fc7 100644 --- a/platform/mv3/extension/_locales/pa/messages.json +++ b/platform/mv3/extension/_locales/pa/messages.json @@ -233,8 +233,16 @@ }, "enableStrictBlockLegend": { "message": "Navigation to potentially undesirable sites will be blocked, and you will be offered the option to proceed.", + "description": "Short description for a checkbox in the options page" + }, + "developerModeLabel": { + "message": "Developer mode", "description": "Label for a checkbox in the options page" }, + "developerModeLegend": { + "message": "Enable access to features suitable for technical users.", + "description": "Short description for a checkbox in the options page" + }, "findListsPlaceholder": { "message": "Find lists", "description": "Placeholder for the input field used to find lists" @@ -282,5 +290,25 @@ "zapperTipQuit": { "message": "Exit element zapper mode", "description": "Tooltip for the button used to exit zapper mode" + }, + "saveButton": { + "message": "Save", + "description": "Text for buttons used to save changes" + }, + "revertButton": { + "message": "Revert", + "description": "Text for buttons used to revert changes" + }, + "importAndAppendButton": { + "message": "Import and append…", + "description": "Text for buttons used to import and append content" + }, + "exportButton": { + "message": "Export…", + "description": "Text for buttons used to export content" + }, + "dnrRulesSummary": { + "message": "Enter your own DNR rules below. Do not add content from untrusted sources.", + "description": "Short description of the DNR rules editor pane" } } diff --git a/platform/mv3/extension/_locales/pl/messages.json b/platform/mv3/extension/_locales/pl/messages.json index e36a317bc..46e37a83c 100644 --- a/platform/mv3/extension/_locales/pl/messages.json +++ b/platform/mv3/extension/_locales/pl/messages.json @@ -233,8 +233,16 @@ }, "enableStrictBlockLegend": { "message": "Nawigacja do potencjalnie niepożądanych witryn zostanie zablokowana i pojawi się opcja kontynuowania.", + "description": "Short description for a checkbox in the options page" + }, + "developerModeLabel": { + "message": "Developer mode", "description": "Label for a checkbox in the options page" }, + "developerModeLegend": { + "message": "Enable access to features suitable for technical users.", + "description": "Short description for a checkbox in the options page" + }, "findListsPlaceholder": { "message": "Znajdź listy", "description": "Placeholder for the input field used to find lists" @@ -282,5 +290,25 @@ "zapperTipQuit": { "message": "Wyjdź z trybu usuwania elementów", "description": "Tooltip for the button used to exit zapper mode" + }, + "saveButton": { + "message": "Save", + "description": "Text for buttons used to save changes" + }, + "revertButton": { + "message": "Revert", + "description": "Text for buttons used to revert changes" + }, + "importAndAppendButton": { + "message": "Import and append…", + "description": "Text for buttons used to import and append content" + }, + "exportButton": { + "message": "Export…", + "description": "Text for buttons used to export content" + }, + "dnrRulesSummary": { + "message": "Enter your own DNR rules below. Do not add content from untrusted sources.", + "description": "Short description of the DNR rules editor pane" } } diff --git a/platform/mv3/extension/_locales/pt_BR/messages.json b/platform/mv3/extension/_locales/pt_BR/messages.json index 56f2e6b65..c852e5b9e 100644 --- a/platform/mv3/extension/_locales/pt_BR/messages.json +++ b/platform/mv3/extension/_locales/pt_BR/messages.json @@ -233,8 +233,16 @@ }, "enableStrictBlockLegend": { "message": "A navegação até sites potencialmente indesejáveis ​​será bloqueada e será oferecido a você a opção de prosseguir.", + "description": "Short description for a checkbox in the options page" + }, + "developerModeLabel": { + "message": "Developer mode", "description": "Label for a checkbox in the options page" }, + "developerModeLegend": { + "message": "Enable access to features suitable for technical users.", + "description": "Short description for a checkbox in the options page" + }, "findListsPlaceholder": { "message": "Achar listas", "description": "Placeholder for the input field used to find lists" @@ -282,5 +290,25 @@ "zapperTipQuit": { "message": "Sair do modo do elemento zapper", "description": "Tooltip for the button used to exit zapper mode" + }, + "saveButton": { + "message": "Save", + "description": "Text for buttons used to save changes" + }, + "revertButton": { + "message": "Revert", + "description": "Text for buttons used to revert changes" + }, + "importAndAppendButton": { + "message": "Import and append…", + "description": "Text for buttons used to import and append content" + }, + "exportButton": { + "message": "Export…", + "description": "Text for buttons used to export content" + }, + "dnrRulesSummary": { + "message": "Enter your own DNR rules below. Do not add content from untrusted sources.", + "description": "Short description of the DNR rules editor pane" } } diff --git a/platform/mv3/extension/_locales/pt_PT/messages.json b/platform/mv3/extension/_locales/pt_PT/messages.json index dbd8d3949..a3cadf195 100644 --- a/platform/mv3/extension/_locales/pt_PT/messages.json +++ b/platform/mv3/extension/_locales/pt_PT/messages.json @@ -233,8 +233,16 @@ }, "enableStrictBlockLegend": { "message": "A navegação para sites potencialmente indesejáveis será bloqueada e ser-lhe-á dada a opção de proceder.", + "description": "Short description for a checkbox in the options page" + }, + "developerModeLabel": { + "message": "Developer mode", "description": "Label for a checkbox in the options page" }, + "developerModeLegend": { + "message": "Enable access to features suitable for technical users.", + "description": "Short description for a checkbox in the options page" + }, "findListsPlaceholder": { "message": "Encontrar listas", "description": "Placeholder for the input field used to find lists" @@ -282,5 +290,25 @@ "zapperTipQuit": { "message": "Sair do modo \"element zapper\"", "description": "Tooltip for the button used to exit zapper mode" + }, + "saveButton": { + "message": "Save", + "description": "Text for buttons used to save changes" + }, + "revertButton": { + "message": "Revert", + "description": "Text for buttons used to revert changes" + }, + "importAndAppendButton": { + "message": "Import and append…", + "description": "Text for buttons used to import and append content" + }, + "exportButton": { + "message": "Export…", + "description": "Text for buttons used to export content" + }, + "dnrRulesSummary": { + "message": "Enter your own DNR rules below. Do not add content from untrusted sources.", + "description": "Short description of the DNR rules editor pane" } } diff --git a/platform/mv3/extension/_locales/ro/messages.json b/platform/mv3/extension/_locales/ro/messages.json index 7213dc0ab..ce522a2bd 100644 --- a/platform/mv3/extension/_locales/ro/messages.json +++ b/platform/mv3/extension/_locales/ro/messages.json @@ -233,8 +233,16 @@ }, "enableStrictBlockLegend": { "message": "Navigarea către site-uri potențial nedorite va fi blocată și vi se va oferi opțiunea de a continua.", + "description": "Short description for a checkbox in the options page" + }, + "developerModeLabel": { + "message": "Developer mode", "description": "Label for a checkbox in the options page" }, + "developerModeLegend": { + "message": "Enable access to features suitable for technical users.", + "description": "Short description for a checkbox in the options page" + }, "findListsPlaceholder": { "message": "Căutați liste", "description": "Placeholder for the input field used to find lists" @@ -282,5 +290,25 @@ "zapperTipQuit": { "message": "Exit element zapper mode", "description": "Tooltip for the button used to exit zapper mode" + }, + "saveButton": { + "message": "Save", + "description": "Text for buttons used to save changes" + }, + "revertButton": { + "message": "Revert", + "description": "Text for buttons used to revert changes" + }, + "importAndAppendButton": { + "message": "Import and append…", + "description": "Text for buttons used to import and append content" + }, + "exportButton": { + "message": "Export…", + "description": "Text for buttons used to export content" + }, + "dnrRulesSummary": { + "message": "Enter your own DNR rules below. Do not add content from untrusted sources.", + "description": "Short description of the DNR rules editor pane" } } diff --git a/platform/mv3/extension/_locales/ru/messages.json b/platform/mv3/extension/_locales/ru/messages.json index adb7c1470..0eca35d9e 100644 --- a/platform/mv3/extension/_locales/ru/messages.json +++ b/platform/mv3/extension/_locales/ru/messages.json @@ -233,8 +233,16 @@ }, "enableStrictBlockLegend": { "message": "Переход к потенциально нежелательным сайтам будет заблокирован, и будет дана возможность продолжить переход на сайт", + "description": "Short description for a checkbox in the options page" + }, + "developerModeLabel": { + "message": "Developer mode", "description": "Label for a checkbox in the options page" }, + "developerModeLegend": { + "message": "Enable access to features suitable for technical users.", + "description": "Short description for a checkbox in the options page" + }, "findListsPlaceholder": { "message": "Найти списки", "description": "Placeholder for the input field used to find lists" @@ -282,5 +290,25 @@ "zapperTipQuit": { "message": "Покинуть режим временного скрытия элемента", "description": "Tooltip for the button used to exit zapper mode" + }, + "saveButton": { + "message": "Save", + "description": "Text for buttons used to save changes" + }, + "revertButton": { + "message": "Revert", + "description": "Text for buttons used to revert changes" + }, + "importAndAppendButton": { + "message": "Import and append…", + "description": "Text for buttons used to import and append content" + }, + "exportButton": { + "message": "Export…", + "description": "Text for buttons used to export content" + }, + "dnrRulesSummary": { + "message": "Enter your own DNR rules below. Do not add content from untrusted sources.", + "description": "Short description of the DNR rules editor pane" } } diff --git a/platform/mv3/extension/_locales/si/messages.json b/platform/mv3/extension/_locales/si/messages.json index 841ea098c..5d6467aa9 100644 --- a/platform/mv3/extension/_locales/si/messages.json +++ b/platform/mv3/extension/_locales/si/messages.json @@ -233,8 +233,16 @@ }, "enableStrictBlockLegend": { "message": "අනවශ්‍ය විය හැකි අඩවි වෙත සංචාලනය අවහිර කරනු ලබන අතර, ඉදිරියට යාමට ඔබට විකල්පය ලබා දෙනු ඇත.", + "description": "Short description for a checkbox in the options page" + }, + "developerModeLabel": { + "message": "Developer mode", "description": "Label for a checkbox in the options page" }, + "developerModeLegend": { + "message": "Enable access to features suitable for technical users.", + "description": "Short description for a checkbox in the options page" + }, "findListsPlaceholder": { "message": "ලැයිස්තු සොයන්න", "description": "Placeholder for the input field used to find lists" @@ -282,5 +290,25 @@ "zapperTipQuit": { "message": "මූලද්‍රව්‍ය zapper ප්‍රකාරයෙන් ඉවත් වන්න", "description": "Tooltip for the button used to exit zapper mode" + }, + "saveButton": { + "message": "Save", + "description": "Text for buttons used to save changes" + }, + "revertButton": { + "message": "Revert", + "description": "Text for buttons used to revert changes" + }, + "importAndAppendButton": { + "message": "Import and append…", + "description": "Text for buttons used to import and append content" + }, + "exportButton": { + "message": "Export…", + "description": "Text for buttons used to export content" + }, + "dnrRulesSummary": { + "message": "Enter your own DNR rules below. Do not add content from untrusted sources.", + "description": "Short description of the DNR rules editor pane" } } diff --git a/platform/mv3/extension/_locales/sk/messages.json b/platform/mv3/extension/_locales/sk/messages.json index 79d5336bd..22aad57c3 100644 --- a/platform/mv3/extension/_locales/sk/messages.json +++ b/platform/mv3/extension/_locales/sk/messages.json @@ -233,8 +233,16 @@ }, "enableStrictBlockLegend": { "message": "Navigácia na potenciálne nežiaduce stránky sa zablokuje a ponúkne sa vám možnosť pokračovať.", + "description": "Short description for a checkbox in the options page" + }, + "developerModeLabel": { + "message": "Developer mode", "description": "Label for a checkbox in the options page" }, + "developerModeLegend": { + "message": "Enable access to features suitable for technical users.", + "description": "Short description for a checkbox in the options page" + }, "findListsPlaceholder": { "message": "Nájsť zoznamy", "description": "Placeholder for the input field used to find lists" @@ -282,5 +290,25 @@ "zapperTipQuit": { "message": "Ukončiť režim dočasného skrytia prvkov", "description": "Tooltip for the button used to exit zapper mode" + }, + "saveButton": { + "message": "Save", + "description": "Text for buttons used to save changes" + }, + "revertButton": { + "message": "Revert", + "description": "Text for buttons used to revert changes" + }, + "importAndAppendButton": { + "message": "Import and append…", + "description": "Text for buttons used to import and append content" + }, + "exportButton": { + "message": "Export…", + "description": "Text for buttons used to export content" + }, + "dnrRulesSummary": { + "message": "Enter your own DNR rules below. Do not add content from untrusted sources.", + "description": "Short description of the DNR rules editor pane" } } diff --git a/platform/mv3/extension/_locales/sl/messages.json b/platform/mv3/extension/_locales/sl/messages.json index ace2c1d9f..136a025b9 100644 --- a/platform/mv3/extension/_locales/sl/messages.json +++ b/platform/mv3/extension/_locales/sl/messages.json @@ -233,8 +233,16 @@ }, "enableStrictBlockLegend": { "message": "Navigation to potentially undesirable sites will be blocked, and you will be offered the option to proceed.", + "description": "Short description for a checkbox in the options page" + }, + "developerModeLabel": { + "message": "Developer mode", "description": "Label for a checkbox in the options page" }, + "developerModeLegend": { + "message": "Enable access to features suitable for technical users.", + "description": "Short description for a checkbox in the options page" + }, "findListsPlaceholder": { "message": "Find lists", "description": "Placeholder for the input field used to find lists" @@ -282,5 +290,25 @@ "zapperTipQuit": { "message": "Exit element zapper mode", "description": "Tooltip for the button used to exit zapper mode" + }, + "saveButton": { + "message": "Save", + "description": "Text for buttons used to save changes" + }, + "revertButton": { + "message": "Revert", + "description": "Text for buttons used to revert changes" + }, + "importAndAppendButton": { + "message": "Import and append…", + "description": "Text for buttons used to import and append content" + }, + "exportButton": { + "message": "Export…", + "description": "Text for buttons used to export content" + }, + "dnrRulesSummary": { + "message": "Enter your own DNR rules below. Do not add content from untrusted sources.", + "description": "Short description of the DNR rules editor pane" } } diff --git a/platform/mv3/extension/_locales/so/messages.json b/platform/mv3/extension/_locales/so/messages.json index ace2c1d9f..136a025b9 100644 --- a/platform/mv3/extension/_locales/so/messages.json +++ b/platform/mv3/extension/_locales/so/messages.json @@ -233,8 +233,16 @@ }, "enableStrictBlockLegend": { "message": "Navigation to potentially undesirable sites will be blocked, and you will be offered the option to proceed.", + "description": "Short description for a checkbox in the options page" + }, + "developerModeLabel": { + "message": "Developer mode", "description": "Label for a checkbox in the options page" }, + "developerModeLegend": { + "message": "Enable access to features suitable for technical users.", + "description": "Short description for a checkbox in the options page" + }, "findListsPlaceholder": { "message": "Find lists", "description": "Placeholder for the input field used to find lists" @@ -282,5 +290,25 @@ "zapperTipQuit": { "message": "Exit element zapper mode", "description": "Tooltip for the button used to exit zapper mode" + }, + "saveButton": { + "message": "Save", + "description": "Text for buttons used to save changes" + }, + "revertButton": { + "message": "Revert", + "description": "Text for buttons used to revert changes" + }, + "importAndAppendButton": { + "message": "Import and append…", + "description": "Text for buttons used to import and append content" + }, + "exportButton": { + "message": "Export…", + "description": "Text for buttons used to export content" + }, + "dnrRulesSummary": { + "message": "Enter your own DNR rules below. Do not add content from untrusted sources.", + "description": "Short description of the DNR rules editor pane" } } diff --git a/platform/mv3/extension/_locales/sq/messages.json b/platform/mv3/extension/_locales/sq/messages.json index 85591ef8b..97675a6b6 100644 --- a/platform/mv3/extension/_locales/sq/messages.json +++ b/platform/mv3/extension/_locales/sq/messages.json @@ -233,8 +233,16 @@ }, "enableStrictBlockLegend": { "message": "Uebsajtet potencialisht të padëshirueshme do të bllokohen dhe do t'ju jepet mundësia për të vazhduar.", + "description": "Short description for a checkbox in the options page" + }, + "developerModeLabel": { + "message": "Developer mode", "description": "Label for a checkbox in the options page" }, + "developerModeLegend": { + "message": "Enable access to features suitable for technical users.", + "description": "Short description for a checkbox in the options page" + }, "findListsPlaceholder": { "message": "Gjej listat", "description": "Placeholder for the input field used to find lists" @@ -282,5 +290,25 @@ "zapperTipQuit": { "message": "Mbyll asgjësuesin e elementeve", "description": "Tooltip for the button used to exit zapper mode" + }, + "saveButton": { + "message": "Save", + "description": "Text for buttons used to save changes" + }, + "revertButton": { + "message": "Revert", + "description": "Text for buttons used to revert changes" + }, + "importAndAppendButton": { + "message": "Import and append…", + "description": "Text for buttons used to import and append content" + }, + "exportButton": { + "message": "Export…", + "description": "Text for buttons used to export content" + }, + "dnrRulesSummary": { + "message": "Enter your own DNR rules below. Do not add content from untrusted sources.", + "description": "Short description of the DNR rules editor pane" } } diff --git a/platform/mv3/extension/_locales/sr/messages.json b/platform/mv3/extension/_locales/sr/messages.json index f94879cd9..2ba999653 100644 --- a/platform/mv3/extension/_locales/sr/messages.json +++ b/platform/mv3/extension/_locales/sr/messages.json @@ -233,8 +233,16 @@ }, "enableStrictBlockLegend": { "message": "Навигација до потенцијално непожељних сајтова ће бити блокирана и биће вам понуђена опција да наставите.", + "description": "Short description for a checkbox in the options page" + }, + "developerModeLabel": { + "message": "Developer mode", "description": "Label for a checkbox in the options page" }, + "developerModeLegend": { + "message": "Enable access to features suitable for technical users.", + "description": "Short description for a checkbox in the options page" + }, "findListsPlaceholder": { "message": "Пронађи листе", "description": "Placeholder for the input field used to find lists" @@ -282,5 +290,25 @@ "zapperTipQuit": { "message": "Exit element zapper mode", "description": "Tooltip for the button used to exit zapper mode" + }, + "saveButton": { + "message": "Save", + "description": "Text for buttons used to save changes" + }, + "revertButton": { + "message": "Revert", + "description": "Text for buttons used to revert changes" + }, + "importAndAppendButton": { + "message": "Import and append…", + "description": "Text for buttons used to import and append content" + }, + "exportButton": { + "message": "Export…", + "description": "Text for buttons used to export content" + }, + "dnrRulesSummary": { + "message": "Enter your own DNR rules below. Do not add content from untrusted sources.", + "description": "Short description of the DNR rules editor pane" } } diff --git a/platform/mv3/extension/_locales/sv/messages.json b/platform/mv3/extension/_locales/sv/messages.json index 93c337c91..79a345a5e 100644 --- a/platform/mv3/extension/_locales/sv/messages.json +++ b/platform/mv3/extension/_locales/sv/messages.json @@ -233,8 +233,16 @@ }, "enableStrictBlockLegend": { "message": "Navigering till potentiellt oönskade webbplatser kommer att blockeras och du kommer att erbjudas alternativet att fortsätta.", + "description": "Short description for a checkbox in the options page" + }, + "developerModeLabel": { + "message": "Developer mode", "description": "Label for a checkbox in the options page" }, + "developerModeLegend": { + "message": "Enable access to features suitable for technical users.", + "description": "Short description for a checkbox in the options page" + }, "findListsPlaceholder": { "message": "Hitta listor", "description": "Placeholder for the input field used to find lists" @@ -282,5 +290,25 @@ "zapperTipQuit": { "message": "Lämna elementzapperläge", "description": "Tooltip for the button used to exit zapper mode" + }, + "saveButton": { + "message": "Save", + "description": "Text for buttons used to save changes" + }, + "revertButton": { + "message": "Revert", + "description": "Text for buttons used to revert changes" + }, + "importAndAppendButton": { + "message": "Import and append…", + "description": "Text for buttons used to import and append content" + }, + "exportButton": { + "message": "Export…", + "description": "Text for buttons used to export content" + }, + "dnrRulesSummary": { + "message": "Enter your own DNR rules below. Do not add content from untrusted sources.", + "description": "Short description of the DNR rules editor pane" } } diff --git a/platform/mv3/extension/_locales/sw/messages.json b/platform/mv3/extension/_locales/sw/messages.json index ace2c1d9f..136a025b9 100644 --- a/platform/mv3/extension/_locales/sw/messages.json +++ b/platform/mv3/extension/_locales/sw/messages.json @@ -233,8 +233,16 @@ }, "enableStrictBlockLegend": { "message": "Navigation to potentially undesirable sites will be blocked, and you will be offered the option to proceed.", + "description": "Short description for a checkbox in the options page" + }, + "developerModeLabel": { + "message": "Developer mode", "description": "Label for a checkbox in the options page" }, + "developerModeLegend": { + "message": "Enable access to features suitable for technical users.", + "description": "Short description for a checkbox in the options page" + }, "findListsPlaceholder": { "message": "Find lists", "description": "Placeholder for the input field used to find lists" @@ -282,5 +290,25 @@ "zapperTipQuit": { "message": "Exit element zapper mode", "description": "Tooltip for the button used to exit zapper mode" + }, + "saveButton": { + "message": "Save", + "description": "Text for buttons used to save changes" + }, + "revertButton": { + "message": "Revert", + "description": "Text for buttons used to revert changes" + }, + "importAndAppendButton": { + "message": "Import and append…", + "description": "Text for buttons used to import and append content" + }, + "exportButton": { + "message": "Export…", + "description": "Text for buttons used to export content" + }, + "dnrRulesSummary": { + "message": "Enter your own DNR rules below. Do not add content from untrusted sources.", + "description": "Short description of the DNR rules editor pane" } } diff --git a/platform/mv3/extension/_locales/ta/messages.json b/platform/mv3/extension/_locales/ta/messages.json index ace2c1d9f..136a025b9 100644 --- a/platform/mv3/extension/_locales/ta/messages.json +++ b/platform/mv3/extension/_locales/ta/messages.json @@ -233,8 +233,16 @@ }, "enableStrictBlockLegend": { "message": "Navigation to potentially undesirable sites will be blocked, and you will be offered the option to proceed.", + "description": "Short description for a checkbox in the options page" + }, + "developerModeLabel": { + "message": "Developer mode", "description": "Label for a checkbox in the options page" }, + "developerModeLegend": { + "message": "Enable access to features suitable for technical users.", + "description": "Short description for a checkbox in the options page" + }, "findListsPlaceholder": { "message": "Find lists", "description": "Placeholder for the input field used to find lists" @@ -282,5 +290,25 @@ "zapperTipQuit": { "message": "Exit element zapper mode", "description": "Tooltip for the button used to exit zapper mode" + }, + "saveButton": { + "message": "Save", + "description": "Text for buttons used to save changes" + }, + "revertButton": { + "message": "Revert", + "description": "Text for buttons used to revert changes" + }, + "importAndAppendButton": { + "message": "Import and append…", + "description": "Text for buttons used to import and append content" + }, + "exportButton": { + "message": "Export…", + "description": "Text for buttons used to export content" + }, + "dnrRulesSummary": { + "message": "Enter your own DNR rules below. Do not add content from untrusted sources.", + "description": "Short description of the DNR rules editor pane" } } diff --git a/platform/mv3/extension/_locales/te/messages.json b/platform/mv3/extension/_locales/te/messages.json index 7f3f7d115..e96f32008 100644 --- a/platform/mv3/extension/_locales/te/messages.json +++ b/platform/mv3/extension/_locales/te/messages.json @@ -233,8 +233,16 @@ }, "enableStrictBlockLegend": { "message": "Navigation to potentially undesirable sites will be blocked, and you will be offered the option to proceed.", + "description": "Short description for a checkbox in the options page" + }, + "developerModeLabel": { + "message": "Developer mode", "description": "Label for a checkbox in the options page" }, + "developerModeLegend": { + "message": "Enable access to features suitable for technical users.", + "description": "Short description for a checkbox in the options page" + }, "findListsPlaceholder": { "message": "Find lists", "description": "Placeholder for the input field used to find lists" @@ -282,5 +290,25 @@ "zapperTipQuit": { "message": "Exit element zapper mode", "description": "Tooltip for the button used to exit zapper mode" + }, + "saveButton": { + "message": "Save", + "description": "Text for buttons used to save changes" + }, + "revertButton": { + "message": "Revert", + "description": "Text for buttons used to revert changes" + }, + "importAndAppendButton": { + "message": "Import and append…", + "description": "Text for buttons used to import and append content" + }, + "exportButton": { + "message": "Export…", + "description": "Text for buttons used to export content" + }, + "dnrRulesSummary": { + "message": "Enter your own DNR rules below. Do not add content from untrusted sources.", + "description": "Short description of the DNR rules editor pane" } } diff --git a/platform/mv3/extension/_locales/th/messages.json b/platform/mv3/extension/_locales/th/messages.json index a15decaa9..2d498371d 100644 --- a/platform/mv3/extension/_locales/th/messages.json +++ b/platform/mv3/extension/_locales/th/messages.json @@ -233,8 +233,16 @@ }, "enableStrictBlockLegend": { "message": "การนำทางไปยังเว็บไซต์ที่ไม่พึงประสงค์จะถูกบล็อก และคุณจะได้รับตัวเลือกเพื่อดำเนินการต่อ", + "description": "Short description for a checkbox in the options page" + }, + "developerModeLabel": { + "message": "Developer mode", "description": "Label for a checkbox in the options page" }, + "developerModeLegend": { + "message": "Enable access to features suitable for technical users.", + "description": "Short description for a checkbox in the options page" + }, "findListsPlaceholder": { "message": "หารายการ", "description": "Placeholder for the input field used to find lists" @@ -282,5 +290,25 @@ "zapperTipQuit": { "message": "Exit element zapper mode", "description": "Tooltip for the button used to exit zapper mode" + }, + "saveButton": { + "message": "Save", + "description": "Text for buttons used to save changes" + }, + "revertButton": { + "message": "Revert", + "description": "Text for buttons used to revert changes" + }, + "importAndAppendButton": { + "message": "Import and append…", + "description": "Text for buttons used to import and append content" + }, + "exportButton": { + "message": "Export…", + "description": "Text for buttons used to export content" + }, + "dnrRulesSummary": { + "message": "Enter your own DNR rules below. Do not add content from untrusted sources.", + "description": "Short description of the DNR rules editor pane" } } diff --git a/platform/mv3/extension/_locales/tr/messages.json b/platform/mv3/extension/_locales/tr/messages.json index 077a95202..c8e8a857f 100644 --- a/platform/mv3/extension/_locales/tr/messages.json +++ b/platform/mv3/extension/_locales/tr/messages.json @@ -233,8 +233,16 @@ }, "enableStrictBlockLegend": { "message": "İstenmeyebilecek sitelere erişim engellenecek ve devam etme seçeneği sunulacaktır.", + "description": "Short description for a checkbox in the options page" + }, + "developerModeLabel": { + "message": "Developer mode", "description": "Label for a checkbox in the options page" }, + "developerModeLegend": { + "message": "Enable access to features suitable for technical users.", + "description": "Short description for a checkbox in the options page" + }, "findListsPlaceholder": { "message": "Liste bul", "description": "Placeholder for the input field used to find lists" @@ -282,5 +290,25 @@ "zapperTipQuit": { "message": "Öge silme modundan çık", "description": "Tooltip for the button used to exit zapper mode" + }, + "saveButton": { + "message": "Save", + "description": "Text for buttons used to save changes" + }, + "revertButton": { + "message": "Revert", + "description": "Text for buttons used to revert changes" + }, + "importAndAppendButton": { + "message": "Import and append…", + "description": "Text for buttons used to import and append content" + }, + "exportButton": { + "message": "Export…", + "description": "Text for buttons used to export content" + }, + "dnrRulesSummary": { + "message": "Enter your own DNR rules below. Do not add content from untrusted sources.", + "description": "Short description of the DNR rules editor pane" } } diff --git a/platform/mv3/extension/_locales/uk/messages.json b/platform/mv3/extension/_locales/uk/messages.json index cfba6ab1f..5046caa79 100644 --- a/platform/mv3/extension/_locales/uk/messages.json +++ b/platform/mv3/extension/_locales/uk/messages.json @@ -233,8 +233,16 @@ }, "enableStrictBlockLegend": { "message": "Перехід до потенційно небажаних сайтів буде заблоковано, та вам буде запропоновано можливість продовжити", + "description": "Short description for a checkbox in the options page" + }, + "developerModeLabel": { + "message": "Developer mode", "description": "Label for a checkbox in the options page" }, + "developerModeLegend": { + "message": "Enable access to features suitable for technical users.", + "description": "Short description for a checkbox in the options page" + }, "findListsPlaceholder": { "message": "Знайти списки", "description": "Placeholder for the input field used to find lists" @@ -282,5 +290,25 @@ "zapperTipQuit": { "message": "Вийти з режиму тимчасового приховування елементів", "description": "Tooltip for the button used to exit zapper mode" + }, + "saveButton": { + "message": "Save", + "description": "Text for buttons used to save changes" + }, + "revertButton": { + "message": "Revert", + "description": "Text for buttons used to revert changes" + }, + "importAndAppendButton": { + "message": "Import and append…", + "description": "Text for buttons used to import and append content" + }, + "exportButton": { + "message": "Export…", + "description": "Text for buttons used to export content" + }, + "dnrRulesSummary": { + "message": "Enter your own DNR rules below. Do not add content from untrusted sources.", + "description": "Short description of the DNR rules editor pane" } } diff --git a/platform/mv3/extension/_locales/ur/messages.json b/platform/mv3/extension/_locales/ur/messages.json index b433dd5a0..1a11f8f4e 100644 --- a/platform/mv3/extension/_locales/ur/messages.json +++ b/platform/mv3/extension/_locales/ur/messages.json @@ -233,8 +233,16 @@ }, "enableStrictBlockLegend": { "message": "Navigation to potentially undesirable sites will be blocked, and you will be offered the option to proceed.", + "description": "Short description for a checkbox in the options page" + }, + "developerModeLabel": { + "message": "Developer mode", "description": "Label for a checkbox in the options page" }, + "developerModeLegend": { + "message": "Enable access to features suitable for technical users.", + "description": "Short description for a checkbox in the options page" + }, "findListsPlaceholder": { "message": "Find lists", "description": "Placeholder for the input field used to find lists" @@ -282,5 +290,25 @@ "zapperTipQuit": { "message": "Exit element zapper mode", "description": "Tooltip for the button used to exit zapper mode" + }, + "saveButton": { + "message": "Save", + "description": "Text for buttons used to save changes" + }, + "revertButton": { + "message": "Revert", + "description": "Text for buttons used to revert changes" + }, + "importAndAppendButton": { + "message": "Import and append…", + "description": "Text for buttons used to import and append content" + }, + "exportButton": { + "message": "Export…", + "description": "Text for buttons used to export content" + }, + "dnrRulesSummary": { + "message": "Enter your own DNR rules below. Do not add content from untrusted sources.", + "description": "Short description of the DNR rules editor pane" } } diff --git a/platform/mv3/extension/_locales/vi/messages.json b/platform/mv3/extension/_locales/vi/messages.json index 548be6fe6..bbe6c7d4f 100644 --- a/platform/mv3/extension/_locales/vi/messages.json +++ b/platform/mv3/extension/_locales/vi/messages.json @@ -233,8 +233,16 @@ }, "enableStrictBlockLegend": { "message": "Việc điều hướng đến các trang web có khả năng không mong muốn sẽ bị chặn và bạn sẽ được cung cấp tùy chọn để tiếp tục.", + "description": "Short description for a checkbox in the options page" + }, + "developerModeLabel": { + "message": "Developer mode", "description": "Label for a checkbox in the options page" }, + "developerModeLegend": { + "message": "Enable access to features suitable for technical users.", + "description": "Short description for a checkbox in the options page" + }, "findListsPlaceholder": { "message": "Tìm danh sách", "description": "Placeholder for the input field used to find lists" @@ -282,5 +290,25 @@ "zapperTipQuit": { "message": "Thoát khỏi chế độ chặn phần tử tạm thời", "description": "Tooltip for the button used to exit zapper mode" + }, + "saveButton": { + "message": "Save", + "description": "Text for buttons used to save changes" + }, + "revertButton": { + "message": "Revert", + "description": "Text for buttons used to revert changes" + }, + "importAndAppendButton": { + "message": "Import and append…", + "description": "Text for buttons used to import and append content" + }, + "exportButton": { + "message": "Export…", + "description": "Text for buttons used to export content" + }, + "dnrRulesSummary": { + "message": "Enter your own DNR rules below. Do not add content from untrusted sources.", + "description": "Short description of the DNR rules editor pane" } } diff --git a/platform/mv3/extension/_locales/zh_CN/messages.json b/platform/mv3/extension/_locales/zh_CN/messages.json index f133d6336..69640caf4 100644 --- a/platform/mv3/extension/_locales/zh_CN/messages.json +++ b/platform/mv3/extension/_locales/zh_CN/messages.json @@ -233,8 +233,16 @@ }, "enableStrictBlockLegend": { "message": "导航至潜在不良网站的操作将被拦截,并且您将可以选择继续前往。", + "description": "Short description for a checkbox in the options page" + }, + "developerModeLabel": { + "message": "Developer mode", "description": "Label for a checkbox in the options page" }, + "developerModeLegend": { + "message": "Enable access to features suitable for technical users.", + "description": "Short description for a checkbox in the options page" + }, "findListsPlaceholder": { "message": "查找列表", "description": "Placeholder for the input field used to find lists" @@ -282,5 +290,25 @@ "zapperTipQuit": { "message": "退出临时元素移除模式", "description": "Tooltip for the button used to exit zapper mode" + }, + "saveButton": { + "message": "Save", + "description": "Text for buttons used to save changes" + }, + "revertButton": { + "message": "Revert", + "description": "Text for buttons used to revert changes" + }, + "importAndAppendButton": { + "message": "Import and append…", + "description": "Text for buttons used to import and append content" + }, + "exportButton": { + "message": "Export…", + "description": "Text for buttons used to export content" + }, + "dnrRulesSummary": { + "message": "Enter your own DNR rules below. Do not add content from untrusted sources.", + "description": "Short description of the DNR rules editor pane" } } diff --git a/platform/mv3/extension/_locales/zh_TW/messages.json b/platform/mv3/extension/_locales/zh_TW/messages.json index 3c9005fa3..76c75bcea 100644 --- a/platform/mv3/extension/_locales/zh_TW/messages.json +++ b/platform/mv3/extension/_locales/zh_TW/messages.json @@ -233,8 +233,16 @@ }, "enableStrictBlockLegend": { "message": "前往潛在不良網站的導航將被阻止,您可以選擇是否繼續前往。", + "description": "Short description for a checkbox in the options page" + }, + "developerModeLabel": { + "message": "Developer mode", "description": "Label for a checkbox in the options page" }, + "developerModeLegend": { + "message": "Enable access to features suitable for technical users.", + "description": "Short description for a checkbox in the options page" + }, "findListsPlaceholder": { "message": "尋找清單", "description": "Placeholder for the input field used to find lists" @@ -282,5 +290,25 @@ "zapperTipQuit": { "message": "離開元素臨時移除模式", "description": "Tooltip for the button used to exit zapper mode" + }, + "saveButton": { + "message": "Save", + "description": "Text for buttons used to save changes" + }, + "revertButton": { + "message": "Revert", + "description": "Text for buttons used to revert changes" + }, + "importAndAppendButton": { + "message": "Import and append…", + "description": "Text for buttons used to import and append content" + }, + "exportButton": { + "message": "Export…", + "description": "Text for buttons used to export content" + }, + "dnrRulesSummary": { + "message": "Enter your own DNR rules below. Do not add content from untrusted sources.", + "description": "Short description of the DNR rules editor pane" } } diff --git a/platform/mv3/extension/css/dashboard-common.css b/platform/mv3/extension/css/dashboard-common.css index 8621d0870..ebef32f60 100644 --- a/platform/mv3/extension/css/dashboard-common.css +++ b/platform/mv3/extension/css/dashboard-common.css @@ -39,9 +39,15 @@ a { color: var(--info3-ink); fill: var(--info3-ink); } + input[type="number"] { width: 5em; } + +input[type="file"] { + display: none; + } + @media (max-height: 640px), (max-height: 800px) and (max-width: 480px) { .body > p, .body > ul { diff --git a/platform/mv3/extension/css/dashboard.css b/platform/mv3/extension/css/dashboard.css index 63aaeaa15..84f75d158 100644 --- a/platform/mv3/extension/css/dashboard.css +++ b/platform/mv3/extension/css/dashboard.css @@ -1,4 +1,4 @@ -#dashboard-nav { +nav { background-color: var(--surface-1); border: 0; border-bottom: 1px solid var(--border-1); @@ -11,7 +11,7 @@ top: 0; z-index: 100; } -.tabButton { +nav > .tabButton { background-color: transparent; border: 0; border-bottom: 3px solid transparent; @@ -24,29 +24,34 @@ text-decoration: none; white-space: nowrap; } -.tabButton:focus { +nav > .tabButton:focus { outline: 0; } -.tabButton:hover { +nav > .tabButton:hover { background-color: var(--dashboard-tab-hover-surface); border-bottom-color: var(--dashboard-tab-hover-border); } body[data-pane="settings"] #dashboard-nav .tabButton[data-pane="settings"], body[data-pane="rulesets"] #dashboard-nav .tabButton[data-pane="rulesets"], +body[data-pane="develop"] #dashboard-nav .tabButton[data-pane="develop"], body[data-pane="about"] #dashboard-nav .tabButton[data-pane="about"] { background-color: var(--dashboard-tab-active-surface); border-bottom: 3px solid var(--dashboard-tab-active-ink); color: var(--dashboard-tab-active-ink); fill: var(--dashboard-tab-active-ink); } +body:not([data-develop="true"]) #dashboard-nav .tabButton[data-pane="develop"] { + display: none; + } body > section { display: none; - padding-bottom: 8rem; + padding-bottom: 2rem; } body[data-pane="settings"] > section[data-pane="settings"], body[data-pane="rulesets"] > section[data-pane="rulesets"], +body[data-pane="develop"] > section[data-pane="develop"], body[data-pane="about"] > section[data-pane="about"] { display: block; } @@ -59,10 +64,10 @@ body[data-pane="about"] > section[data-pane="about"] { } /* touch-screen devices */ -:root.mobile #dashboard-nav { +:root.mobile nav { flex-wrap: nowrap; overflow-x: auto; } -:root.mobile #dashboard-nav .logo { +:root.mobile nav .logo { display: none; } diff --git a/platform/mv3/extension/css/develop.css b/platform/mv3/extension/css/develop.css new file mode 100644 index 000000000..616849509 --- /dev/null +++ b/platform/mv3/extension/css/develop.css @@ -0,0 +1,112 @@ +body[data-pane="develop"] { + height: 100vh; + } + +section[data-pane="develop"] { + display: none; + flex-grow: 1; + overflow: hidden; + } + +section[data-pane="develop"] > div { + display: flex; + flex-direction: column; + height: 100%; + } + +section[data-pane="develop"] > div > * { + margin-bottom: 0; + margin-top: 1em; + } + +#cm-dnrRules { + flex-grow: 1; + font-size: var(--monospace-size); + overflow: hidden; + } + +/* https://discuss.codemirror.net/t/how-to-set-max-height-of-the-editor/2882/2 */ +#cm-dnrRules .cm-editor { + background-color: var(--surface-0); + height: 100%; + } + +#cm-dnrRules .cm-editor .cm-line { + border-bottom: 1px dotted transparent; + border-top: 1px dotted transparent; + } + +#cm-dnrRules .cm-editor .cm-line:has(.ͼ5) { + border-bottom: 1px dotted var(--border-1); + border-top: 1px dotted var(--border-1); + } + +#cm-dnrRules .cm-editor .cm-line.badline:not(.cm-activeLine) { + background-color: color-mix(in srgb, var(--info3-ink) 15%, transparent 85%); + } + +#cm-dnrRules .cm-editor .cm-line .badmark { + text-decoration: underline var(--cm-negative) wavy; + text-decoration-skip-ink: none; + } + +#cm-dnrRules .cm-editor .cm-panel.cm-search { + display: flex; + flex-wrap: wrap; + font-family: sans-serif; + font-size: var(--font-size); + gap: 0.5em 1em; + padding: 0.5em 1.5em 0.5em 0.5em; + } + +#cm-dnrRules .cm-editor .cm-panel.cm-search > * { + margin: 0; + } + +#cm-dnrRules .cm-editor .cm-panel.cm-search .cm-textfield, +#cm-dnrRules .cm-editor .cm-panel.cm-search .cm-button, +#cm-dnrRules .cm-editor .cm-panel.cm-search label { + background-image: inherit; + border: inherit; + flex-grow: 0; + font-size: var(--button-font-size); + min-height: calc(var(--button-font-size) * 1.8); + } + +#cm-dnrRules .cm-editor .cm-panel.info-panel { + display: flex; + flex-wrap: nowrap; + font-size: var(--font-size); + padding: var(--default-gap-xxsmall) var(--default-gap-xsmall); + } +#cm-dnrRules .cm-editor .cm-panel.info-panel .info { + flex-grow: 1; + overflow: auto; + } +#cm-dnrRules .cm-editor .cm-panel.info-panel .close { + cursor: default; + flex-shrink: 0; + padding-inline-start: 1em; + } +#cm-dnrRules .cm-editor .cm-panel.info-panel .close::after { + content: '\2715'; + } +#cm-dnrRules .cm-editor .cm-panel.summary-panel { + background-color: color-mix(in srgb, var(--info1-ink) 15%, transparent 85%); + } + +#cm-dnrRules .cm-editor .cm-panel.feedback-panel { + background-color: color-mix(in srgb, var(--info3-ink) 15%, transparent 85%); + white-space: pre; + max-height: 10cqh; + } + +#cm-dnrRules .cm-editor .cm-gutterElement { + cursor: default; + user-select: none; + } + +#cm-dnrRules .cm-editor .cm-tooltip .badmark-tooltip { + background-color: color-mix(in srgb, var(--info3-ink) 15%, transparent 85%); + padding: var(--default-gap-xxsmall) var(--default-gap-xsmall); + } \ No newline at end of file diff --git a/platform/mv3/extension/css/settings.css b/platform/mv3/extension/css/settings.css index a9b4ad6d5..4e6618da6 100644 --- a/platform/mv3/extension/css/settings.css +++ b/platform/mv3/extension/css/settings.css @@ -25,6 +25,9 @@ body[data-forbid~="filteringMode"] section[data-pane="settings"] > div:has(> h3[ body[data-forbid~="filteringMode"] section[data-pane="settings"] > div:has(> h3[data-i18n="filteringMode0Name"]) { display: none; } +body[data-forbid~="develop"] #developerMode { + display: none; + } label:has(input[type="checkbox"][disabled]), label:has(input[type="checkbox"][disabled]) + legend { diff --git a/platform/mv3/extension/dashboard.html b/platform/mv3/extension/dashboard.html index 300921557..3acecd569 100644 --- a/platform/mv3/extension/dashboard.html +++ b/platform/mv3/extension/dashboard.html @@ -13,18 +13,20 @@ + - + -
+
+
@@ -85,7 +87,7 @@

-

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

+ + +   + + +

+

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

uBO-flavored JSONPath tool

- - + +
 
@@ -57,6 +97,18 @@ main { import { JSONPath } from '../src/js/jsonpath.js'; + function readJSON() { + const textarea = document.querySelector('#json-data'); + try { + jsonData = JSON.parse(textarea.value); + } catch { + jsonData = {}; + } + if ( typeof jsonData !== 'object' || jsonData === null ) { + jsonData = {}; + } + } + function formatResult(a) { if ( a === undefined ) { return 'undefined'; } if ( jsonp.valid === false ) { return 'bad expression'; } @@ -85,13 +137,7 @@ main { { const textarea = document.querySelector('#json-data'); textarea.addEventListener('input', ( ) => { - try { - jsonData = JSON.parse(textarea.value); - } catch { - } - if ( typeof jsonData !== 'object' || jsonData === null ) { - jsonData = {}; - } + readJSON(); process(); }); } @@ -102,6 +148,9 @@ main { process(); }); } + + readJSON(); + process(); diff --git a/tools/make-mv3.sh b/tools/make-mv3.sh index 8791487b8..cb2f19c56 100755 --- a/tools/make-mv3.sh +++ b/tools/make-mv3.sh @@ -107,6 +107,8 @@ cp platform/mv3/extension/lib/codemirror/* \ "$UBOL_DIR"/lib/codemirror/ 2>/dev/null || : cp platform/mv3/extension/lib/codemirror/codemirror-ubol/dist/cm6.bundle.ubol.min.js \ "$UBOL_DIR"/lib/codemirror/ +cp platform/mv3/extension/lib/codemirror/codemirror-ubol/LICENSE \ + "$UBOL_DIR"/lib/codemirror/ echo "*** uBOLite.mv3: Generating rulesets" UBOL_BUILD_DIR=$(mktemp -d)