Repository: darkreader/darkreader
Pull request #3520 (2020-08-25 09:27:00)

Initial security policy draft
This adds a formal security policy.
@alexanderby @Gusted feedback requested.


Patch:
diff --git a/SECURITY.md b/SECURITY.md
new file mode 100644
index 00000000..cd9a5230
--- /dev/null
+++ b/SECURITY.md
@@ -0,0 +1,25 @@
+# Security Policy
+
+Dark Reader is known to have no intentional security vulnerabilities.
+If reported, vulnerabilites will be fixed in the shortest time possible,
+with minimal impact possible.
+
+## Supported Versions
+
+Latest version is supported.
+
+| Version   | Supported          |
+| --------- | ------------------ |
+| 4.9.16    | :white_check_mark: |
+| < 4.9.16  | :x:                |
+
+## Reporting a Vulnerability
+
+In case there is a vulnerability left behind, immediately notify
+to <darkreaderapp@gmail.com>; unless you can fix it yourself,
+in which case open a pull request on 
+<https://github.com/darkreader/darkreader/pulls>.
+
+Alexander Shutau, owner
+"Gusted" William Zijl, maintainer
+Erkin Alp Güney, security policy author
diff --git a/src/background/extension.ts b/src/background/extension.ts
index db636704..b081962e 100644
--- a/src/background/extension.ts
+++ b/src/background/extension.ts
@@ -207,11 +207,11 @@ export class Extension {
 
     private getConnectionMessage(url, frameURL) {
         if (this.ready) {
-            return this.isEnabled() && this.getTabMessage(url, frameURL);
+            return this.getTabMessage(url, frameURL);
         } else {
             return new Promise((resolve) => {
                 this.awaiting.push(() => {
-                    resolve(this.isEnabled() && this.getTabMessage(url, frameURL));
+                    resolve(this.getTabMessage(url, frameURL));
                 });
             });
         }
@@ -364,7 +364,7 @@ export class Extension {
     private getURLInfo(url: string): TabInfo {
         const {DARK_SITES} = this.config;
         const isInDarkList = isURLInList(url, DARK_SITES);
-        const isProtected = !(this.user.settings.enableForProtectedPages || canInjectScript(url));
+        const isProtected = !canInjectScript(url);
         return {
             url,
             isInDarkList,
@@ -425,9 +425,9 @@ export class Extension {
                     throw new Error(`Unknown engine ${theme.engine}`);
                 }
             }
-        } else {
-            console.log(`Site is not inverted: ${url}`);
         }
+
+        console.log(`Site is not inverted: ${url}`);
         return {
             type: 'clean-up',
         };
diff --git a/src/config/dark-sites.config b/src/config/dark-sites.config
index 835166fd..8215dfc6 100644
--- a/src/config/dark-sites.config
+++ b/src/config/dark-sites.config
@@ -125,9 +125,9 @@ daringfireball.net
 dark.diatr.us
 darkmodelist.com
 darkreader.org
+datomatic.no-intro.org
 dcards.snaz.in
 deepweblinks.net
-defanor.uberspace.net
 del.dog
 deltarune.com
 demonforums.net
@@ -225,6 +225,7 @@ game-debate.com
 gamebanana.com
 gamejolt.com
 gameloop.fun
+garamlee500.github.io
 gatsbyjs.org
 geekabit.nl
 geektyper.com
@@ -241,6 +242,7 @@ giantbomb.com
 gibber.cc
 gidonline.in
 gifrun.com
+gifyourgame.com
 gikken.co
 giphy.com
 gitkraken.com
@@ -332,6 +334,7 @@ lecantiche.com
 letterboxd.com
 lightweightpdf.com
 linux.org.ru
+liquidplus.com
 lollilol.cf
 lollilol.ml
 lookmovie.ag
@@ -449,6 +452,7 @@ pr0gramm.com
 pre.fyp.nl
 premid.app
 primevideo.com
+progettosnaps.net
 protogen.dev
 protondb.com
 proxx.app
diff --git a/src/config/dynamic-theme-fixes.config b/src/config/dynamic-theme-fixes.config
index f5a81ced..247b211e 100644
--- a/src/config/dynamic-theme-fixes.config
+++ b/src/config/dynamic-theme-fixes.config
@@ -899,6 +899,13 @@ CSS
 
 ================================
 
+chess.com
+
+IGNORE IMAGE ANALYSIS
+.piece
+
+================================
+
 chilkatsoft.com
 
 CSS
@@ -1704,6 +1711,15 @@ CSS
 
 ================================
 
+enduhub.com
+
+CSS
+.navbar .nav > li > a {
+    text-shadow: rgb(40, 43, 54) 0px 1px 0px !important;
+}
+
+================================
+
 enjen.net
 
 CSS
@@ -1726,6 +1742,22 @@ INVERT
 
 ================================
 
+eshot.gov.tr
+
+CSS
+a.navbar-brand img {
+    filter: hue-rotate(90deg) invert(1) hue-rotate(90deg) saturate(15);
+}
+#heading .left, #heading .sub-menu .left-xs, #heading .sub-menu .right-xs {
+    background-color: transparent !important;
+    background-image: none !important;
+}
+section#announcements-area .container {
+    background-image: none;
+}
+
+================================
+
 etsy.com
 
 INVERT
@@ -1985,6 +2017,9 @@ div.drm-container svg g
 
 fandom.com
 
+INVERT
+.wds-global-navigation__logo-fandom
+
 IGNORE INLINE STYLE
 .wds-global-navigation__logo-image g path
 .wds-global-footer__header-logo g path
@@ -2075,6 +2110,15 @@ img[src$="assets/images/bg-cloud.png"]
 
 ================================
 
+filmweb.pl
+
+CSS
+.filmInfo__info {
+    color: var(--darkreader-neutral-text) !important;
+}
+
+================================
+
 filterlists.com
 
 IGNORE INLINE STYLE
@@ -2439,6 +2483,23 @@ IGNORE IMAGE ANALYSIS
 
 ================================
 
+gls-pakete.de
+
+CSS
+.tracking--status .status-box::before, .tracking--status .status-box::after {
+    z-index: 0 !important;
+}
+
+.tracking--status .status-box.status--complete.status--lastcomplete .status-box--tooltip, .tracking--status .status-box.status--current .status-box--tooltip {
+    transform: none !important;
+    position: absolute !important;
+    margin-bottom: 0px !important;
+    margin-top: -10px !important;
+    bottom: 0 !important;
+}
+
+================================
+
 gnu.org
 
 INVERT
@@ -3139,6 +3200,11 @@ INVERT
 .CodeMirror-cursor
 .user-story-chapter-base .companies-showcase-base .logo
 
+CSS
+[class^=question-picker-detail] {
+    background: none !important;
+}
+
 ================================
 
 lenovo.com
@@ -3480,6 +3546,15 @@ CSS
 
 ================================
 
+matters.news
+
+CSS
+section[class$="container"][style^="background-image"] {
+    background-image: none !important;
+}
+
+================================
+
 medium.com
 
 INVERT
@@ -3527,6 +3602,15 @@ CSS
 
 ================================
 
+meituan.com
+
+INVERT
+.dpLogo
+.header-title
+.site-logo
+
+================================
+
 messages.android.com
 
 INVERT
@@ -3930,6 +4014,18 @@ CSS
 
 ================================
 
+nokia.com
+
+CSS
+.pds-background-cover > .pds-background-image-set {
+    background-color: var(--darkreader-neutral-background) !important;
+}
+.pds-background-cover video {
+    filter: brightness(50%) sepia(40%) !important;
+}
+
+================================
+
 notion.so
 
 CSS
@@ -3964,18 +4060,6 @@ CSS
 
 ================================
 
-nvidia.com
-
-INVERT
-.global-footer__logo
-
-CSS
-.brand-link.pull-left {
-    filter: invert(1) hue-rotate(170deg) brightness(4);
-}
-
-================================
-
 nvidia.com
 nvidia.in
 
@@ -4228,6 +4312,13 @@ div[dir="ltr", id="map"] {
 
 ================================
 
+openwall.com
+
+INVERT
+.logo
+
+================================
+
 overleaf.com
 
 INVERT
@@ -4465,6 +4556,15 @@ td.cell, td.label {
 
 ================================
 
+quora.com
+
+CSS
+.logo_fill {
+    fill: rgb(219, 87, 83) !important;
+}
+
+================================
+
 rachel53461.wordpress.com
 
 CSS
@@ -4643,6 +4743,14 @@ CSS
 
 ================================
 
+rottentomatoes.com
+
+IGNORE IMAGE ANALYSIS
+.icon__fresh
+.fresh
+
+================================
+
 rubjo.github.io
 
 CSS
@@ -4781,6 +4889,13 @@ CSS
 
 ================================
 
+servarica.com/clients
+
+INVERT
+.logo
+
+================================
+
 server.pro
 
 INVERT
@@ -4788,6 +4903,13 @@ svg.server-pro-logo
 
 ================================
 
+servercat.net
+
+INVERT
+.theme-header-logo
+
+================================
+
 share.dmhy.org
 
 CSS
@@ -4982,6 +5104,13 @@ CSS
 
 ================================
 
+sso.qiniu.com
+
+INVERT
+.navbar-brand
+
+================================
+
 stardewvalleywiki.com
 
 CSS
@@ -5358,6 +5487,7 @@ theguardian.com
 
 INVERT
 .inline-the-guardian-logo__svg
+a[data-link-name$="logo"] > svg
 
 ================================
 
@@ -6531,6 +6661,13 @@ IGNORE INLINE STYLE
 
 ================================
 
+www.jiqizhixin.com
+
+INVERT
+.header__logo
+
+================================
+
 www.realtek.com
 
 CSS
@@ -6910,6 +7047,15 @@ CSS
 
 ================================
 
+zdic.net
+
+CSS
+.zisong img {
+    background-color: ${black} !important;
+}
+
+================================
+
 zeptovm.com
 
 INVERT
diff --git a/src/config/inversion-fixes.config b/src/config/inversion-fixes.config
index 5900c92d..3ec281c4 100644
--- a/src/config/inversion-fixes.config
+++ b/src/config/inversion-fixes.config
@@ -749,6 +749,23 @@ pre code {
 
 ================================
 
+gls-pakete.de
+
+CSS
+.tracking--status .status-box::before, .tracking--status .status-box::after {
+    z-index: 0 !important;
+}
+
+.tracking--status .status-box.status--complete.status--lastcomplete .status-box--tooltip, .tracking--status .status-box.status--current .status-box--tooltip {
+    transform: none !important;
+    position: absolute !important;
+    margin-bottom: 0px !important;
+    margin-top: -10px !important;
+    bottom: 0 !important;
+}
+
+================================
+
 google.*
 google.*.*
 
diff --git a/src/defaults.ts b/src/defaults.ts
index 09bf4855..af512d25 100644
--- a/src/defaults.ts
+++ b/src/defaults.ts
@@ -30,6 +30,7 @@ export const DEFAULT_THEME: Theme = {
     lightSchemeTextColor: DEFAULT_COLORS.lightScheme.text,
     scrollbarColor: isMacOS() ? '' : 'auto',
     selectionColor: 'auto',
+    styleSystemControls: true,
 };
 
 export const DEFAULT_SETTINGS: UserSettings = {
diff --git a/src/definitions.d.ts b/src/definitions.d.ts
index 3b20391d..85c0c643 100644
--- a/src/definitions.d.ts
+++ b/src/definitions.d.ts
@@ -54,6 +54,7 @@ export interface Theme {
     lightSchemeTextColor: string;
     scrollbarColor: '' | 'auto' | string;
     selectionColor: '' | 'auto' | string;
+    styleSystemControls: boolean;
 }
 
 export type FilterConfig = Theme;
diff --git a/src/inject/dynamic-theme/css-collection.ts b/src/inject/dynamic-theme/css-collection.ts
index abb97dcd..21ea0aad 100644
--- a/src/inject/dynamic-theme/css-collection.ts
+++ b/src/inject/dynamic-theme/css-collection.ts
@@ -39,7 +39,7 @@ export async function collectCSS() {
     const css = [banner];
 
     function addStaticCSS(selector: string, comment: string) {
-        const staticStyle = document.head.querySelector(selector);
+        const staticStyle = document.querySelector(selector);
         if (staticStyle && staticStyle.textContent) {
             css.push(`/* ${comment} */`);
             css.push(staticStyle.textContent);
diff --git a/src/inject/dynamic-theme/index.ts b/src/inject/dynamic-theme/index.ts
index 8a03cd16..204a2181 100644
--- a/src/inject/dynamic-theme/index.ts
+++ b/src/inject/dynamic-theme/index.ts
@@ -49,13 +49,13 @@ function stopStylePositionWatchers() {
 }
 
 function createStaticStyleOverrides() {
-    const fallbackStyle = createOrUpdateStyle('darkreader--fallback');
+    const fallbackStyle = createOrUpdateStyle('darkreader--fallback', document);
     fallbackStyle.textContent = getModifiedFallbackStyle(filter, {strict: true});
     document.head.insertBefore(fallbackStyle, document.head.firstChild);
     setupStylePositionWatcher(fallbackStyle, 'fallback');
 
     const userAgentStyle = createOrUpdateStyle('darkreader--user-agent');
-    userAgentStyle.textContent = getModifiedUserAgentStyle(filter, isIFrame);
+    userAgentStyle.textContent = getModifiedUserAgentStyle(filter, isIFrame, filter.styleSystemControls);
     document.head.insertBefore(userAgentStyle, fallbackStyle.nextSibling);
     setupStylePositionWatcher(userAgentStyle, 'user-agent');
 
@@ -131,7 +131,7 @@ function replaceCSSTemplates($cssText: string) {
 }
 
 function cleanFallbackStyle() {
-    const fallback = document.head.querySelector('.darkreader--fallback');
+    const fallback = document.querySelector('.darkreader--fallback');
     if (fallback) {
         fallback.textContent = '';
     }
diff --git a/src/inject/dynamic-theme/modify-css.ts b/src/inject/dynamic-theme/modify-css.ts
index 0e551b50..51db894d 100644
--- a/src/inject/dynamic-theme/modify-css.ts
+++ b/src/inject/dynamic-theme/modify-css.ts
@@ -51,40 +51,40 @@ export function getModifiableCSSDeclaration(property: string, value: string, rul
     return null;
 }
 
-export function getModifiedUserAgentStyle(filter: FilterConfig, isIFrame: boolean) {
+export function getModifiedUserAgentStyle(theme: Theme, isIFrame: boolean, styleSystemControls: boolean) {
     const lines: string[] = [];
     if (!isIFrame) {
         lines.push('html {');
-        lines.push(`    background-color: ${modifyBackgroundColor({r: 255, g: 255, b: 255}, filter)} !important;`);
+        lines.push(`    background-color: ${modifyBackgroundColor({r: 255, g: 255, b: 255}, theme)} !important;`);
         lines.push('}');
     }
-    lines.push(`${isIFrame ? '' : 'html, body, '}input, textarea, select, button {`);
-    lines.push(`    background-color: ${modifyBackgroundColor({r: 255, g: 255, b: 255}, filter)};`);
+    lines.push(`${isIFrame ? '' : 'html, body, '}${styleSystemControls ? 'input, textarea, select, button' : ''} {`);
+    lines.push(`    background-color: ${modifyBackgroundColor({r: 255, g: 255, b: 255}, theme)};`);
     lines.push('}');
-    lines.push('html, body, input, textarea, select, button {');
-    lines.push(`    border-color: ${modifyBorderColor({r: 76, g: 76, b: 76}, filter)};`);
-    lines.push(`    color: ${modifyForegroundColor({r: 0, g: 0, b: 0}, filter)};`);
+    lines.push(`html, body, ${styleSystemControls ? 'input, textarea, select, button' : ''} {`);
+    lines.push(`    border-color: ${modifyBorderColor({r: 76, g: 76, b: 76}, theme)};`);
+    lines.push(`    color: ${modifyForegroundColor({r: 0, g: 0, b: 0}, theme)};`);
     lines.push('}');
     lines.push('a {');
-    lines.push(`    color: ${modifyForegroundColor({r: 0, g: 64, b: 255}, filter)};`);
+    lines.push(`    color: ${modifyForegroundColor({r: 0, g: 64, b: 255}, theme)};`);
     lines.push('}');
     lines.push('table {');
-    lines.push(`    border-color: ${modifyBorderColor({r: 128, g: 128, b: 128}, filter)};`);
+    lines.push(`    border-color: ${modifyBorderColor({r: 128, g: 128, b: 128}, theme)};`);
     lines.push('}');
     lines.push('::placeholder {');
-    lines.push(`    color: ${modifyForegroundColor({r: 169, g: 169, b: 169}, filter)};`);
+    lines.push(`    color: ${modifyForegroundColor({r: 169, g: 169, b: 169}, theme)};`);
     lines.push('}');
     lines.push('input:-webkit-autofill,');
     lines.push('textarea:-webkit-autofill,');
     lines.push('select:-webkit-autofill {');
-    lines.push(`    background-color: ${modifyBackgroundColor({r: 250, g: 255, b: 189}, filter)} !important;`);
-    lines.push(`    color: ${modifyForegroundColor({r: 0, g: 0, b: 0}, filter)} !important;`);
+    lines.push(`    background-color: ${modifyBackgroundColor({r: 250, g: 255, b: 189}, theme)} !important;`);
+    lines.push(`    color: ${modifyForegroundColor({r: 0, g: 0, b: 0}, theme)} !important;`);
     lines.push('}');
-    if (filter.scrollbarColor) {
-        lines.push(getModifiedScrollbarStyle(filter));
+    if (theme.scrollbarColor) {
+        lines.push(getModifiedScrollbarStyle(theme));
     }
-    if (filter.selectionColor) {
-        lines.push(getModifiedSelectionStyle(filter));
+    if (theme.selectionColor) {
+        lines.push(getModifiedSelectionStyle(theme));
     }
     return lines.join('\n');
 }
@@ -107,6 +107,7 @@ export function getSelectionColor(theme: Theme) {
     }
     return {backgroundColorSelection, foregroundColorSelection};
 }
+
 function getModifiedSelectionStyle(theme: Theme) {
     const lines: string[] = [];
     const modifiedSelectionColor = getSelectionColor(theme);
diff --git a/src/inject/fallback.ts b/src/inject/fallback.ts
new file mode 100644
index 00000000..a3eb38a1
--- /dev/null
+++ b/src/inject/fallback.ts
@@ -0,0 +1,11 @@
+if (
+    matchMedia('(prefers-color-scheme: dark)').matches &&
+    !document.querySelector('.darkreader--fallback')
+) {
+    const fallbackStyle = document.createElement('style');
+    fallbackStyle.textContent = 'html, body, body :not(iframe) { background-color: #181a1b !important; border-color: #776e62 !important; color: #e8e6e3 !important; }';
+    document.documentElement.appendChild(fallbackStyle);
+    fallbackStyle.classList.add('darkreader');
+    fallbackStyle.classList.add('darkreader--fallback');
+    fallbackStyle.media = 'screen';
+}
diff --git a/src/manifest.json b/src/manifest.json
index 70f6c82d..b13b5d54 100644
--- a/src/manifest.json
+++ b/src/manifest.json
@@ -28,6 +28,7 @@
                 "<all_urls>"
             ],
             "js": [
+                "inject/fallback.js",
                 "inject/index.js"
             ],
             "run_at": "document_start",
diff --git a/src/ui/connect/mock.ts b/src/ui/connect/mock.ts
index 14b38ea0..ce0d8490 100644
--- a/src/ui/connect/mock.ts
+++ b/src/ui/connect/mock.ts
@@ -20,6 +20,7 @@ export function getMockData(override = {} as Partial<ExtensionData>): ExtensionD
                 engine: 'cssFilter',
                 stylesheet: '',
                 scrollbarColor: 'auto',
+                styleSystemControls: true,
             } as Theme,
             customThemes: [],
             siteList: [],
diff --git a/src/ui/controls/shortcut/index.tsx b/src/ui/controls/shortcut/index.tsx
index 27ca0b8c..c00474c2 100644
--- a/src/ui/controls/shortcut/index.tsx
+++ b/src/ui/controls/shortcut/index.tsx
@@ -1,6 +1,6 @@
 import {m} from 'malevic';
 import {mergeClass} from '../utils';
-import {isFirefox} from '../../../utils/platform';
+import {isFirefox, isEdge} from '../../../utils/platform';
 import {Shortcuts} from '../../../definitions';
 
 interface ShortcutLinkProps {
@@ -83,6 +83,13 @@ export default function ShortcutLink(props: ShortcutLinkProps) {
             startEnteringShortcut(e.target as HTMLAnchorElement);
             return;
         }
+        if (isEdge()) {
+            chrome.tabs.create({
+                url: `edge://extensions/shortcuts`,
+                active: true
+            });
+            return;
+        }
         chrome.tabs.create({
             url: `chrome://extensions/configureCommands#command-${chrome.runtime.id}-${props.commandName}`,
             active: true
diff --git a/src/ui/icons.less b/src/ui/icons.less
index 1c5e39ef..7faabb22 100644
--- a/src/ui/icons.less
+++ b/src/ui/icons.less
@@ -1,6 +1,8 @@
+@icon-escaped-color: escape(@color-control-fore-inactive);
 @icon-check: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 8 8"><path fill="white" d="M0,4 l2,2 l5,-5 v1.5 l-5,5 l-2,-2 z"/></svg>');
-@icon-cross-escaped-color: escape(@color-control-fore-inactive);
-@icon-cross: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 8 8"><path fill="@{icon-cross-escaped-color}" d="M1,0.5 l6,6 v1.5 l-6,-6 Z M1,6.5 l6,-6 v1.5 l-6,6 z"/></svg>');
+@icon-close: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 8 8"><path fill="none" stroke="white" stroke-width="1" d="M1,1 l6,6 M1,7 l6,-6"/></svg>');
+@icon-close-inactive: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 8 8"><path fill="none" stroke="@{icon-escaped-color}" stroke-width="1" d="M1,1 l6,6 M1,7 l6,-6"/></svg>');
+@icon-cross: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 8 8"><path fill="@{icon-escaped-color}" d="M1,0.5 l6,6 v1.5 l-6,-6 Z M1,6.5 l6,-6 v1.5 l-6,6 z"/></svg>');
 @icon-down: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 8 8"><path fill="white" d="M1,2 l3,3 l3,-3 v1.5 l-3,3 l-3,-3 z"/></svg>');
 @icon-left: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 8 8"><path fill="white" d="M6,1 h-1.5 l-3,3 l3,3 h1.5 l-3,-3 z"/></svg>');
 @icon-list: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 8 8"><path fill="white" d="M0,0 h1 v1 h-1 z m2,0 h6 v1 h-6 z m-2,3.25 h1 v1 h-1 z m2,0 h6 v1 h-6 z m-2,3.25 h1 v1 h-1 z m2,0 h6 v1 h-6 z"/></svg>');
diff --git a/src/ui/popup/components/site-toggle/index.tsx b/src/ui/popup/components/site-toggle/index.tsx
index 23d423dc..247207a3 100644
--- a/src/ui/popup/components/site-toggle/index.tsx
+++ b/src/ui/popup/components/site-toggle/index.tsx
@@ -14,7 +14,7 @@ export default function SiteToggleButton({data, tab, actions}: ExtWrapper & {tab
         }
     }
     const toggleHasEffect = (
-        data.isEnabled &&
+        data.settings.enableForProtectedPages || 
         !tab.isProtected
     );
     const pdf = isPDF(tab.url);
diff --git a/src/ui/popup/site-list-page/site-list.tsx b/src/ui/popup/site-list-page/site-list.tsx
index bf1a10b1..a4987168 100644
--- a/src/ui/popup/site-list-page/site-list.tsx
+++ b/src/ui/popup/site-list-page/site-list.tsx
@@ -55,6 +55,15 @@ export default function SiteList(props: SiteListProps) {
         props.onChange(values);
     }
 
+    function removeValue(event: MouseEvent) {
+        const previousSibling = ((event.target as HTMLInputElement).previousSibling as HTMLInputElement);
+        const index = store.indices.get(previousSibling);
+        const filtered = props.siteList.slice();
+        filtered.splice(index, 1);
+        store.shouldFocusAtIndex = index;
+        props.onChange(filtered);
+    }
+
     function createTextBox(text: string, index: number) {
         const onRender = (node: HTMLInputElement) => {
             store.indices.set(node, index);
@@ -64,12 +73,19 @@ export default function SiteList(props: SiteListProps) {
             }
         };
         return (
-            <TextBox
-                class="site-list__textbox"
-                value={text}
-                onrender={onRender}
-                placeholder="google.com/maps"
-            />
+            <div class="site-list__item">
+                <TextBox
+                    class="site-list__textbox"
+                    value={text}
+                    onrender={onRender}
+                    placeholder="google.com/maps"
+                />
+                <span
+                    class="site-list__item__remove"
+                    role="button"
+                    onclick={removeValue}
+                />
+            </div>
         );
     }
 
diff --git a/src/ui/popup/site-list-page/style.less b/src/ui/popup/site-list-page/style.less
index 606d48c5..8d7cdd4e 100644
--- a/src/ui/popup/site-list-page/style.less
+++ b/src/ui/popup/site-list-page/style.less
@@ -1,4 +1,5 @@
 @import "../../theme.less";
+@import "../../icons.less";
 
 .site-list-page {
     align-items: center;
@@ -45,4 +46,33 @@
             z-index: 1;
         }
     }
+
+    &__item {
+        position: relative;
+
+        &__remove {
+            background-color: transparent;
+            background-image: @icon-close-inactive;
+            background-position: center;
+            background-repeat: no-repeat;
+            background-size: 0.75rem;
+            border-radius: 50%;
+            cursor: pointer;
+            display: none;
+            height: 1.25rem;
+            position: absolute;
+            right: 0.125rem;
+            top: 0.125rem;
+            width: 1.25rem;
+
+            &:hover {
+                background-color: @color-control-active;
+                background-image: @icon-close;
+            }
+        }
+
+        &:hover .site-list__item__remove {
+            display: inline-block;
+        }
+    }
 }
diff --git a/src/ui/popup/theme/controls/index.tsx b/src/ui/popup/theme/controls/index.tsx
index a8d18c25..cc6fad19 100644
--- a/src/ui/popup/theme/controls/index.tsx
+++ b/src/ui/popup/theme/controls/index.tsx
@@ -9,6 +9,7 @@ import Scheme from './scheme';
 import Scrollbar from './scrollbar';
 import SelectionColorEditor from './selection';
 import Sepia from './sepia';
+import StyleSystemControls from './style-system-controls';
 import TextColor from './text-color';
 import TextStroke from './text-stroke';
 import UseFont from './use-font';
@@ -25,6 +26,7 @@ export {
     Scrollbar,
     SelectionColorEditor,
     Sepia,
+    StyleSystemControls,
     TextColor,
     TextStroke,
     UseFont,
diff --git a/src/ui/popup/theme/controls/style-system-controls.tsx b/src/ui/popup/theme/controls/style-system-controls.tsx
new file mode 100644
index 00000000..83fe4c21
--- /dev/null
+++ b/src/ui/popup/theme/controls/style-system-controls.tsx
@@ -0,0 +1,16 @@
+import {m} from 'malevic';
+import {DropDown} from '../../../controls';
+import ThemeControl from './theme-control';
+
+export default function StyleSystemControls(props: {value: boolean; onChange: (boolean: any) => void}) {
+    const options = [{id: true, content: 'Yes'}, {id: false, content: 'No'}];
+    return (
+        <ThemeControl label="Style system controls">
+            <DropDown
+                options={options}
+                onChange={props.onChange}
+                selected={props.value}
+            />
+        </ThemeControl>
+    );
+}
diff --git a/src/ui/popup/theme/page/index.tsx b/src/ui/popup/theme/page/index.tsx
index d61666d9..d12d91cf 100644
--- a/src/ui/popup/theme/page/index.tsx
+++ b/src/ui/popup/theme/page/index.tsx
@@ -2,7 +2,7 @@ import {m} from 'malevic';
 import {DEFAULT_SETTINGS, DEFAULT_THEME, DEFAULT_COLORS} from '../../../../defaults';
 import {Theme} from '../../../../definitions';
 import {ViewProps} from '../../types';
-import {BackgroundColor, Brightness, Contrast, FontPicker, Grayscale, Mode, ResetButton, Scheme, Scrollbar, SelectionColorEditor, Sepia, TextColor, TextStroke, UseFont} from '../controls';
+import {BackgroundColor, Brightness, Contrast, FontPicker, Grayscale, Mode, ResetButton, Scheme, Scrollbar, SelectionColorEditor, Sepia, TextColor, TextStroke, UseFont, StyleSystemControls} from '../controls';
 import ThemePresetPicker from '../preset-picker';
 import {getCurrentThemePreset} from '../utils';
 import Collapsible from './collapsible-panel';
@@ -98,6 +98,10 @@ function FontGroup({theme, fonts, change}: FontGroupsProps) {
                 value={theme.textStroke}
                 onChange={(textStroke) => change({textStroke})}
             />
+            <StyleSystemControls
+                value={theme.styleSystemControls}
+                onChange={(styleSystemControls) => change({styleSystemControls})}
+            />
         </Array>
     );
 }
@@ -115,7 +119,7 @@ export default function ThemePage(props: ViewProps) {
                 <Collapsible.Group id="colors" label="Colors">
                     <ColorsGroup theme={theme} change={change} />
                 </Collapsible.Group>
-                <Collapsible.Group id="font" label="Font, text stroke">
+                <Collapsible.Group id="font" label="Font & more">
                     <FontGroup theme={theme} fonts={props.data.fonts} change={change} />
                 </Collapsible.Group>
             </Collapsible>
diff --git a/src/utils/url.ts b/src/utils/url.ts
index 7470ce7d..3ffc5fb9 100644
--- a/src/utils/url.ts
+++ b/src/utils/url.ts
@@ -137,7 +137,7 @@ export function isPDF(url: string) {
 }
 
 export function isURLEnabled(url: string, userSettings: UserSettings, {isProtected, isInDarkList}) {
-    if (isProtected && userSettings.enableForProtectedPages) {
+    if (isProtected && !userSettings.enableForProtectedPages) {
         return false;
     }
     if (isPDF(url) && userSettings.enableForPDF) {
diff --git a/tasks/bundle-js.js b/tasks/bundle-js.js
index 1f6cfe1d..8a7aed2a 100644
--- a/tasks/bundle-js.js
+++ b/tasks/bundle-js.js
@@ -59,6 +59,15 @@ const jsEntries = [
         },
         watchFiles: null,
     },
+    {
+        src: 'src/inject/fallback.ts',
+        dest: 'inject/fallback.js',
+        reloadType: reload.FULL,
+        async postBuild({debug}) {
+            await copyToFF({cwdPath: this.dest, debug});
+        },
+        watchFiles: null,
+    },
     {
         src: 'src/ui/devtools/index.tsx',
         dest: 'ui/devtools/index.js',


Test patch:
diff --git a/tests/utils/url.tests.ts b/tests/utils/url.tests.ts
index 743b107f..7ab7a334 100644
--- a/tests/utils/url.tests.ts
+++ b/tests/utils/url.tests.ts
@@ -66,12 +66,12 @@ test('URL is enabled', () => {
         'https://chrome.google.com/webstore',
         {siteList: ['chrome.google.com'], siteListEnabled: [], applyToListedOnly: true, enableForProtectedPages: true} as UserSettings,
         {isProtected: true, isInDarkList: false},
-    )).toBe(false);
+    )).toBe(true);
     expect(isURLEnabled(
         'https://chrome.google.com/webstore',
         {siteList: [], siteListEnabled: [], applyToListedOnly: false, enableForProtectedPages: false} as UserSettings,
         {isProtected: true, isInDarkList: false},
-    )).toBe(true);
+    )).toBe(false);
     expect(isURLEnabled(
         'https://chrome.google.com/webstore',
         {siteList: ['chrome.google.com'], siteListEnabled: [], applyToListedOnly: false, enableForProtectedPages: true} as UserSettings,
@@ -86,7 +86,7 @@ test('URL is enabled', () => {
         'https://microsoftedge.microsoft.com/addons',
         {siteList: ['microsoftedge.microsoft.com'], siteListEnabled: [], applyToListedOnly: true, enableForProtectedPages: true} as UserSettings,
         {isProtected: true, isInDarkList: false},
-    )).toBe(false);
+    )).toBe(true);
     expect(isURLEnabled(
         'https://duckduckgo.com',
         {siteList: [], siteListEnabled: [], applyToListedOnly: false, enableForProtectedPages: true} as UserSettings,