|
1 | | -// Make Chrome Web Store installs work on Ungoogled Chromium. |
| 1 | +// Make extension installs work on Ungoogled Chromium. |
2 | 2 | // |
3 | 3 | // Google disables its native "Add to Chrome" button on non-official Chrome, so |
4 | 4 | // it stays greyed out. We inject our own working button on extension detail |
|
7 | 7 | // `extension-mime-request-handling = Always prompt for install` flag. If the |
8 | 8 | // flag isn't active for some reason, the CRX simply downloads and can be |
9 | 9 | // dragged onto chrome://extensions (Developer mode) instead. |
| 10 | +// |
| 11 | +// TronBrowser does NOT publish on the Chrome Web Store, so the button checks the |
| 12 | +// TronBrowser store FIRST (by the page's slug/name, resolved in the background |
| 13 | +// service worker which has host permissions). When a live TronBrowser-store |
| 14 | +// listing exists we install from there; only when it doesn't do we fall back to |
| 15 | +// the Chrome Web Store CRX. |
10 | 16 |
|
11 | 17 | (function () { |
12 | 18 | // Chrome extension IDs are 32 chars in a-p. New store URL: |
13 | 19 | // https://chromewebstore.google.com/detail/<slug>/<id> |
14 | | - const ID_RE = /\/detail\/(?:[^/]+\/)?([a-p]{32})/; |
| 20 | + const DETAIL_RE = /\/detail\/(?:([^/]+)\/)?([a-p]{32})/; |
| 21 | + |
| 22 | + function parseDetail() { |
| 23 | + const m = location.pathname.match(DETAIL_RE); |
| 24 | + if (!m) return null; |
| 25 | + return { slug: m[1] || '', id: m[2] }; |
| 26 | + } |
15 | 27 |
|
16 | | - function extId() { |
17 | | - const m = location.pathname.match(ID_RE); |
18 | | - return m ? m[1] : ''; |
| 28 | + // The listing's human name, from the tab title ("uBlock Origin - Chrome Web |
| 29 | + // Store"), used as a secondary lookup key when the slug doesn't match. |
| 30 | + function extName() { |
| 31 | + return (document.title || '').replace(/\s*[-–|]\s*Chrome Web Store\s*$/i, '').trim(); |
19 | 32 | } |
20 | 33 |
|
21 | 34 | function chromeVersion() { |
|
29 | 42 | '&x=' + encodeURIComponent('id=' + id + '&installsource=ondemand&uc'); |
30 | 43 | } |
31 | 44 |
|
| 45 | + // Ask the background worker whether the TronBrowser store has this extension. |
| 46 | + // Resolves to a Tron-store download URL, or null to use the Chrome CRX. Never |
| 47 | + // rejects — any failure (SW asleep, offline, not listed) falls back to Chrome. |
| 48 | + function resolveTronDownload(slug, name) { |
| 49 | + return new Promise((resolve) => { |
| 50 | + let settled = false; |
| 51 | + const done = (v) => { if (!settled) { settled = true; resolve(v); } }; |
| 52 | + const timer = setTimeout(() => done(null), 5000); // never hang the click |
| 53 | + try { |
| 54 | + chrome.runtime.sendMessage({ type: 'resolve-tron-store', slug, name }, (resp) => { |
| 55 | + clearTimeout(timer); |
| 56 | + if (chrome.runtime.lastError || !resp || !resp.found || !resp.downloadUrl) { done(null); return; } |
| 57 | + done(resp.downloadUrl); |
| 58 | + }); |
| 59 | + } catch (_) { |
| 60 | + clearTimeout(timer); |
| 61 | + done(null); |
| 62 | + } |
| 63 | + }); |
| 64 | + } |
| 65 | + |
32 | 66 | function addButton() { |
33 | | - const id = extId(); |
34 | | - if (!id || document.getElementById('tron-install-btn')) return; |
| 67 | + const detail = parseDetail(); |
| 68 | + if (!detail || document.getElementById('tron-install-btn')) return; |
| 69 | + const { slug, id } = detail; |
35 | 70 | const btn = document.createElement('button'); |
36 | 71 | btn.id = 'tron-install-btn'; |
37 | 72 | btn.type = 'button'; |
|
44 | 79 | 'padding:12px 18px', 'font:700 14px ui-monospace,Menlo,monospace', |
45 | 80 | 'cursor:pointer', 'box-shadow:0 6px 24px rgba(0,0,0,.5)', |
46 | 81 | ].join(';'); |
47 | | - btn.addEventListener('click', () => { window.location.href = crxUrl(id); }); |
| 82 | + |
| 83 | + // Resolve the TronBrowser store up front so the button reflects where the |
| 84 | + // install will come from; cache the promise so a click never re-resolves. |
| 85 | + const tronTarget = resolveTronDownload(slug, extName()); |
| 86 | + tronTarget.then((url) => { |
| 87 | + if (url) { |
| 88 | + btn.textContent = '⬇ Add from TronBrowser Store'; |
| 89 | + btn.title = 'Install from the TronBrowser store (not published on the Chrome Web Store)'; |
| 90 | + } |
| 91 | + }); |
| 92 | + |
| 93 | + btn.addEventListener('click', async () => { |
| 94 | + // Tron store FIRST, Chrome Web Store CRX as the fallback. |
| 95 | + const url = (await tronTarget) || crxUrl(id); |
| 96 | + window.location.href = url; |
| 97 | + }); |
48 | 98 | document.body.appendChild(btn); |
49 | 99 | } |
50 | 100 |
|
|
0 commit comments