diff --git a/public/iframe-intercept.js b/public/iframe-intercept.js new file mode 100644 index 0000000..e98b48c --- /dev/null +++ b/public/iframe-intercept.js @@ -0,0 +1,53 @@ +// This script intercepts window.open calls and link clicks to force them to open in the parent iframe +// It must be injected into the iframe context after UV/Scramjet initialization + +(function() { + 'use strict'; + + // Store reference to original window.open + const originalWindowOpen = window.open; + + // Override window.open to redirect to parent iframe + window.open = function(url, target, features) { + if (url && window.parent !== window) { + // We're in an iframe, communicate with parent to navigate the iframe + try { + window.parent.postMessage({ + type: 'navigate-iframe', + url: url.toString() + }, '*'); + // Return null since we're not actually opening a window + return null; + } catch (e) { + console.error('Failed to communicate with parent:', e); + } + } + // Fallback to original behavior if not in iframe or message failed + return originalWindowOpen.call(this, url, target, features); + }; + + // Intercept clicks on links with target="_blank" or similar + document.addEventListener('click', function(e) { + const anchor = e.target.closest('a'); + if (anchor && anchor.href) { + const target = anchor.getAttribute('target'); + // Intercept _blank, _new, and other new window targets + if (target === '_blank' || target === '_new' || target === '_parent' || target === '_top') { + e.preventDefault(); + e.stopPropagation(); + // Use window.open override which will message parent + window.open(anchor.href); + } + } + }, true); // Use capture phase to intercept early + + // Also set base target to prevent default new window behavior + const baseTag = document.querySelector('base'); + if (!baseTag) { + const newBase = document.createElement('base'); + newBase.target = '_self'; + document.head.insertBefore(newBase, document.head.firstChild); + } else if (!baseTag.hasAttribute('target')) { + baseTag.target = '_self'; + } +})(); diff --git a/public/sw.js b/public/sw.js index efad95a..e1daf2f 100644 --- a/public/sw.js +++ b/public/sw.js @@ -45,6 +45,92 @@ function enhanceCaptchaRequest(request) { }); } +// Script to inject at the very beginning of HTML documents +const INTERCEPT_SCRIPT = ``; + +// Helper to inject script into HTML responses +async function injectInterceptScript(response) { + const contentType = response.headers.get('content-type') || ''; + console.log('[SW] Response content-type:', contentType); + if (!contentType.includes('text/html')) { + console.log('[SW] Not HTML, skipping injection'); + return response; + } + + try { + let text = await response.text(); + console.log('[SW] Got response text, length:', text.length); + + // Inject the script right after the opening tag or at the start + if (text.includes(']*)>/, `${INTERCEPT_SCRIPT}`); + console.log('[SW] Injected after tag'); + } else if (text.includes(']*)>/, `${INTERCEPT_SCRIPT}`); + console.log('[SW] Injected after tag'); + } else { + text = INTERCEPT_SCRIPT + text; + console.log('[SW] Injected at start'); + } + + return new Response(text, { + status: response.status, + statusText: response.statusText, + headers: response.headers + }); + } catch (e) { + console.error('[SW] Failed to inject script:', e); + return response; + } +} + self.addEventListener("fetch", function (event) { event.respondWith( (async () => { @@ -59,13 +145,24 @@ self.addEventListener("fetch", function (event) { request = enhanceCaptchaRequest(event.request); } + let response; if (url.startsWith(location.origin + __uv$config.prefix)) { - return await uv.fetch(event); + response = await uv.fetch(event); + // Inject our script into proxied HTML content + if (event.request.destination === 'document' || event.request.destination === 'iframe') { + response = await injectInterceptScript(response); + } } else if (sj.route(event)) { - return await sj.fetch(event); + response = await sj.fetch(event); + // Inject our script into proxied HTML content + if (event.request.destination === 'document' || event.request.destination === 'iframe') { + response = await injectInterceptScript(response); + } } else { - return await fetch(request); + response = await fetch(request); } + + return response; })() ); }); diff --git a/src/pages/index.astro b/src/pages/index.astro index 737b9d0..1cc77de 100644 --- a/src/pages/index.astro +++ b/src/pages/index.astro @@ -36,6 +36,46 @@ const link = Astro.url.searchParams.get("redir"); + + + +