Repository: darkreader/darkreader
Pull request #3055 (2020-07-09 11:15:06)

Fixed resolving absolute paths
- Fixed resolving "/index.html" base paths again.
- Use native URL class for parsing URLs.
- Note: it can throw an error for invalid URLs.

Gusted: Resolves #2543

Patch:
diff --git a/package.json b/package.json
index e59e3a69..c01a79c4 100644
--- a/package.json
+++ b/package.json
@@ -10,8 +10,11 @@
     "debug-watch": "node tasks/build.js --debug --watch",
     "prepublishOnly": "npm test && npm run api",
     "release": "npm test && node tasks/build.js --release",
-    "test": "jest --config=tests/jest.config.js",
-    "test:inject": "jest --config=tests/inject/jest.config.js"
+    "test": "npm run test:config",
+    "test:config": "jest --config=tests/jest.config.js",
+    "test:config:debug": "node --inspect-brk ./node_modules/jest/bin/jest --config=tests/jest.config.js --runInBand --no-cache --watch",
+    "test:inject": "jest --config=tests/inject/jest.config.js",
+    "test:inject:debug": "node --inspect-brk ./node_modules/jest/bin/jest --config=tests/inject/jest.config.js --runInBand --no-cache --watch"
   },
   "main": "darkreader.js",
   "repository": {
diff --git a/src/inject/dynamic-theme/url.ts b/src/inject/dynamic-theme/url.ts
index a442fcd5..1596c402 100644
--- a/src/inject/dynamic-theme/url.ts
+++ b/src/inject/dynamic-theme/url.ts
@@ -1,41 +1,24 @@
-export function parseURL(url: string) {
+function fixBaseURL($url: string) {
     const a = document.createElement('a');
-    a.href = url;
-    return a;
+    a.href = $url;
+    return a.href;
 }
 
-function backwards($base: string, $relative: string) {
-    const b = parseURL($base);
-    let pathParts = b.pathname.split('/');
-    pathParts = pathParts.concat(...$relative.split('/')).filter((p) => p);
-    let backwardIndex: number;
-    while ((backwardIndex = pathParts.indexOf('..')) > 0) {
-        pathParts.splice(backwardIndex - 1, 2);
+export function parseURL($url: string, $base: string = null) {
+    if ($base) {
+        $base = fixBaseURL($base);
+        return new URL($url, $base);
     }
-    return pathParts;
+    $url = fixBaseURL($url);
+    return new URL($url);
 }
 
 export function getAbsoluteURL($base: string, $relative: string) {
-    if ($relative.match(/^.*?\/\//) || $relative.match(/^data\:/)) {
-        if ($relative.startsWith('//')) {
-            if ($relative.includes('..')) {
-                return `${location.protocol}${backwards($base, $relative).join('/')}`;
-            }
-            return `${location.protocol}${$relative}`;
-        }
+    if ($relative.match(/^data\:/)) {
         return $relative;
     }
+
     const b = parseURL($base);
-    if ($relative.startsWith('/')) {
-        const u = parseURL(`${b.protocol}//${b.host}${$relative}`);
-        return u.href;
-    }
-    let pathParts = b.pathname.split('/');
-    const lastPathPart = pathParts[pathParts.length - 1];
-    if (lastPathPart.match(/\.[a-z]+$/i)) {
-        pathParts.pop();
-    }
-    pathParts = backwards($base, $relative);
-    const u = parseURL(`${b.protocol}//${b.host}/${pathParts.join('/')}`);
-    return u.href;
+    const a = parseURL($relative, b.href);
+    return a.href;
 }


Test patch:
diff --git a/tests/inject/dynamic.tests.ts b/tests/inject/dynamic.tests.ts
index 45fcc458..62740cd9 100644
--- a/tests/inject/dynamic.tests.ts
+++ b/tests/inject/dynamic.tests.ts
@@ -1,10 +1,24 @@
 import {getAbsoluteURL} from '../../src/inject/dynamic-theme/url';
 
 test('Absolute URL', () => {
+    expect(getAbsoluteURL('https://www.google.com', 'image.jpg')).toBe('https://www.google.com/image.jpg');
     expect(getAbsoluteURL('https://www.google.com', '/image.jpg')).toBe('https://www.google.com/image.jpg');
     expect(getAbsoluteURL('https://www.google.com/path', '/image.jpg')).toBe('https://www.google.com/image.jpg');
-    expect(getAbsoluteURL('https://www.google.com/path', 'image.jpg')).toBe('https://www.google.com/path/image.jpg');
-    expect(getAbsoluteURL('https://www.google.com/long/path', '../image.jpg')).toBe('https://www.google.com/long/image.jpg');
+    expect(getAbsoluteURL('//www.google.com', '/image.jpg')).toBe(`${location.protocol}//www.google.com/image.jpg`);
+    expect(getAbsoluteURL('https://www.google.com', 'image.jpg?size=128')).toBe('https://www.google.com/image.jpg?size=128');
+    expect(getAbsoluteURL('https://www.google.com/path', 'image.jpg')).toBe('https://www.google.com/image.jpg');
+    expect(getAbsoluteURL('https://www.google.com/path/', 'image.jpg')).toBe('https://www.google.com/path/image.jpg');
+    expect(getAbsoluteURL('https://www.google.com/long/path', '../image.jpg')).toBe('https://www.google.com/image.jpg');
+    expect(getAbsoluteURL('https://www.google.com/long/path/', '../image.jpg')).toBe('https://www.google.com/long/image.jpg');
+    expect(getAbsoluteURL('https://www.google.com/long/path/', '../another/image.jpg')).toBe('https://www.google.com/long/another/image.jpg');
+    expect(getAbsoluteURL('https://www.google.com/path/page.html', 'image.jpg')).toBe('https://www.google.com/path/image.jpg');
+    expect(getAbsoluteURL('https://www.google.com/path/page.html', '/image.jpg')).toBe('https://www.google.com/image.jpg');
+    expect(getAbsoluteURL('https://www.google.com', '//www.google.com/path/image.jpg')).toBe('https://www.google.com/path/image.jpg');
+    expect(getAbsoluteURL('https://www.google.com', '//www.google.com/path/../another/image.jpg')).toBe('https://www.google.com/another/image.jpg');
+    expect(getAbsoluteURL('https://www.google.com', 'https://www.google.com/path/image.jpg')).toBe('https://www.google.com/path/image.jpg');
+    expect(getAbsoluteURL('https://www.google.com', 'https://www.google.com/path/../another/image.jpg')).toBe('https://www.google.com/another/image.jpg');
     expect(getAbsoluteURL('https://www.google.com/path/page.html', 'image.jpg')).toBe('https://www.google.com/path/image.jpg');
     expect(getAbsoluteURL('https://www.google.com/path/page.html', '../image.jpg')).toBe('https://www.google.com/image.jpg');
+    expect(getAbsoluteURL('path/index.html', 'image.jpg')).toBe(`${location.origin}/path/image.jpg`);
+    expect(getAbsoluteURL('path/index.html', '/image.jpg?size=128')).toBe(`${location.origin}/image.jpg?size=128`);
 });