Repository: darkreader/darkreader (ts)

Issue:
[BUG] Toggle status is ignored if query string contains square braces
<!--
Thank you for taking the time to report a bug.
Please make sure there is no existing issue about this kind of bug.
-->

**Describe the bug**
If you navigate to a page you have disabled DarkReader for (via the toggle) and the URI for that page contains a `[` (in the query string) then the rule you have set will be ignored.  
When the braces are encoded this issue does not occur.

**To Reproduce**
Steps to reproduce the behavior:
1. Navigate to `google.co.uk`
1. Disable DarkReader via the quick toggle
1. Notice DarkReader isn't applied
1. Navigate to `google.co.uk?foo=[bar]`
1. Notice DarkReader is applied

**Expected behavior**
DarkReader should not be applied when it is toggled off

**Screenshots**
If applicable, add screenshots to help explain your problem.
![image](https://user-images.githubusercontent.com/13659818/83259152-72776f00-a1af-11ea-91f5-a73015491bbe.png)
![image](https://user-images.githubusercontent.com/13659818/83259199-858a3f00-a1af-11ea-89a2-401e90954901.png)



**System Information:**
 - MacOS 10.15.4
 - Chrome 83.0.4103.61

**Additional context**
Note: Between my first and second screenshot, DarkReader was **not** toggle manually


Pull request description:
Pre-Release and fixing bugs
- Resolves #2660

Patch:
diff --git a/.eslintplugin.js b/.eslintplugin.js
index 59eb11a6..96dd89ac 100644
--- a/.eslintplugin.js
+++ b/.eslintplugin.js
@@ -13,10 +13,16 @@ module.exports.rules = {
         create(context) {
             return {
                 JSXOpeningElement(node) {
-                    const variable = node.name.name;
-                    if (variable) {
-                        context.markVariableAsUsed(variable);
+                    let variable;
+                    const jsxTag = node.name;
+                    if (jsxTag.type === 'JSXIdentifier') {
+                        variable = jsxTag.name;
+                    } else if (jsxTag.type === 'JSXMemberExpression') {
+                        variable = jsxTag.object.name;
+                    } else {
+                        console.warn('Unsupported JSX identifier', jsxTag);
                     }
+                    context.markVariableAsUsed(variable);
                 },
             };
         },
diff --git a/package-lock.json b/package-lock.json
index 456cb866..eeae26e9 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -5768,7 +5768,7 @@
     },
     "safe-regex": {
       "version": "1.1.0",
-      "resolved": "http://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz",
+      "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz",
       "integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=",
       "dev": true,
       "requires": {
@@ -6364,7 +6364,7 @@
     },
     "strip-eof": {
       "version": "1.0.0",
-      "resolved": "http://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz",
+      "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz",
       "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=",
       "dev": true
     },
diff --git a/src/config/dynamic-theme-fixes.config b/src/config/dynamic-theme-fixes.config
index 981c3605..4bb8da06 100644
--- a/src/config/dynamic-theme-fixes.config
+++ b/src/config/dynamic-theme-fixes.config
@@ -4400,6 +4400,13 @@ a.showMoreItems {
 
 ================================
 
+type-together.com
+
+INVERT
+.carousel-inner
+
+================================
+
 ubuntu.com
 
 INVERT
diff --git a/src/generators/modify-colors.ts b/src/generators/modify-colors.ts
index 9b4f7a58..14bc9a70 100644
--- a/src/generators/modify-colors.ts
+++ b/src/generators/modify-colors.ts
@@ -1,4 +1,3 @@
-import {DEFAULT_COLORS} from '../defaults';
 import {FilterConfig, Theme} from '../definitions';
 import {parse, rgbToHSL, hslToRGB, rgbToString, rgbToHexString, RGBA, HSLA} from '../utils/color';
 import {scale} from '../utils/math';
@@ -92,7 +91,13 @@ function modifyLightSchemeColor(rgb: RGBA, theme: Theme) {
 
 function modifyLightModeHSL({h, s, l, a}, poleFg: HSLA, poleBg: HSLA) {
     const isDark = l < 0.5;
-    const isNeutral = l < 0.2 || l > 0.8 || s < 0.36;
+    let isNeutral: boolean;
+    if (isDark) {
+        isNeutral = l < 0.2 || s < 0.12;
+    } else {
+        const isBlue = h > 200 && h < 280;
+        isNeutral = s < 0.24 || (l > 0.8 && isBlue);
+    }
 
     let hx = h;
     let sx = l;
@@ -160,7 +165,7 @@ export function modifyBackgroundColor(rgb: RGBA, theme: Theme) {
 const MIN_FG_LIGHTNESS = 0.55;
 
 function modifyBlueFgHue(hue: number) {
-    return scale(hue, 205, 245, 205, 220)
+    return scale(hue, 205, 245, 205, 220);
 }
 
 function modifyFgHSL({h, s, l, a}: HSLA, pole: HSLA) {
diff --git a/src/inject/dynamic-theme/index.ts b/src/inject/dynamic-theme/index.ts
index 0f77a1de..5752aafb 100644
--- a/src/inject/dynamic-theme/index.ts
+++ b/src/inject/dynamic-theme/index.ts
@@ -286,10 +286,12 @@ function createThemeAndWatchForUpdates() {
 }
 
 function handleAdoptedStyleSheets(node: ShadowRoot | Document) {
-    if (node.adoptedStyleSheets.length > 0) {
-        const newManger = createAdoptedStyleSheetOverride(node);
-        adoptedStyleManagers.push(newManger);
-        newManger.render(filter, variables);
+    if (Array.isArray(node.adoptedStyleSheets)) {
+        if (node.adoptedStyleSheets.length > 0) {
+            const newManger = createAdoptedStyleSheetOverride(node);
+            adoptedStyleManagers.push(newManger);
+            newManger.render(filter, variables);
+        }
     }
 }
 
diff --git a/src/ui/controls/color-dropdown/index.tsx b/src/ui/controls/color-dropdown/index.tsx
index aa8ab67d..68284384 100644
--- a/src/ui/controls/color-dropdown/index.tsx
+++ b/src/ui/controls/color-dropdown/index.tsx
@@ -93,6 +93,7 @@ export default function ColorDropDown(props: ColorDropDownProps) {
                 }}
                 color={props.value}
                 onChange={props.onChange}
+                canReset={true}
                 onReset={props.onReset}
             />
         </span>
diff --git a/src/utils/ipv6.ts b/src/utils/ipv6.ts
index e62509ba..c0248c6a 100644
--- a/src/utils/ipv6.ts
+++ b/src/utils/ipv6.ts
@@ -1,17 +1,19 @@
-/**
- * Compares if IPV6 addresses are the same.
- * @param firstIP First IP to compare with
- * @param secondIP Second IP to compare with
- */
-export function compareIPV6(firstIP: string, secondIP: string) {
-    firstIP = (firstIP
-        .replace(/^\^/, '') // Remove ^ at start
-        .replace(/\$$/, '') // Remove $ at end
-        .replace(/^.*?\/{2,3}/, '') // Remove scheme
-        .replace(/\?.*$/, '') // Remove query
-        .replace(/\/$/, '') // Remove last slash
-    );
-    const ip = secondIP.toLowerCase();
-    const ip2 = firstIP.toLowerCase();
-    return ip === ip2;
+export function isIPV6(url: string) {
+    const openingBracketIndex = url.indexOf('[');
+    if (openingBracketIndex < 0) {
+        return false;
+    }
+    const queryIndex = url.indexOf('?');
+    if (queryIndex >= 0 && openingBracketIndex > queryIndex) {
+        return false;
+    }
+    return true;
+}
+
+const ipV6HostRegex = /\[.*?\](\:\d+)?/;
+
+export function compareIPV6(firstURL: string, secondURL: string) {
+    const firstHost = firstURL.match(ipV6HostRegex)[0];
+    const secondHost = secondURL.match(ipV6HostRegex)[0];
+    return firstHost === secondHost;
 }
diff --git a/src/utils/url.ts b/src/utils/url.ts
index 9b2374ee..e1d7dbfc 100644
--- a/src/utils/url.ts
+++ b/src/utils/url.ts
@@ -1,5 +1,5 @@
 import {UserSettings} from '../definitions';
-import {compareIPV6} from './ipv6';
+import {isIPV6, compareIPV6} from './ipv6';
 
 export function getURLHost(url: string) {
     return url.match(/^(.*?\/{2,3})?(.+?)(\/|$)/)[2];
@@ -29,11 +29,11 @@ export function isURLInList(url: string, list: string[]) {
  * @param urlTemplate URL template ("google.*", "youtube.com" etc).
  */
 export function isURLMatched(url: string, urlTemplate: string): boolean {
-    const isFirstIPV6 = url.includes('[');
-    const isSecondIPV6 = urlTemplate.includes('[');
+    const isFirstIPV6 = isIPV6(url);
+    const isSecondIPV6 = isIPV6(urlTemplate);
     if (isFirstIPV6 && isSecondIPV6) {
         return compareIPV6(url, urlTemplate);
-    } else if (!isFirstIPV6 && ! isSecondIPV6){
+    } else if (!isSecondIPV6 && !isSecondIPV6) {
         const regex = createUrlRegex(urlTemplate);
         return Boolean(url.match(regex));
     } else {


Test patch:
diff --git a/tests/url.tests.ts b/tests/url.tests.ts
index 6b59afbe..e0d0c31f 100644
--- a/tests/url.tests.ts
+++ b/tests/url.tests.ts
@@ -1,4 +1,4 @@
-import {isURLEnabled, isPDF} from '../src/utils/url';
+import {isURLEnabled, isURLMatched, isPDF} from '../src/utils/url';
 import {UserSettings} from '../src/definitions';
 
 test('URL is enabled', () => {
@@ -167,6 +167,24 @@ test('URL is enabled', () => {
         {isProtected: false, isInDarkList: true},
     )).toEqual(false);
 
+    // Some URLs can have unescaped [] in query
+    expect(isURLMatched(
+        'google.co.uk/order.php?bar=[foo]',
+        'google.co.uk',
+    )).toEqual(true);
+    expect(isURLMatched(
+        '[2001:4860:4860::8844]/order.php?bar=foo',
+        '[2001:4860:4860::8844]',
+    )).toEqual(true);
+    expect(isURLMatched(
+        '[2001:4860:4860::8844]/order.php?bar=[foo]',
+        '[2001:4860:4860::8844]',
+    )).toEqual(true);
+    expect(isURLMatched(
+        'google.co.uk/order.php?bar=[foo]',
+        '[2001:4860:4860::8844]',
+    )).toEqual(false);
+
     // Temporary Dark Sites list fix
     expect(isURLEnabled(
         'https://darkreader.org/',