Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 53 additions & 0 deletions public/iframe-intercept.js
Original file line number Diff line number Diff line change
@@ -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';
}
})();
103 changes: 100 additions & 3 deletions public/sw.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,92 @@ function enhanceCaptchaRequest(request) {
});
}

// Script to inject at the very beginning of HTML documents
const INTERCEPT_SCRIPT = `<script>
(function() {
'use strict';
console.log('[Iframe Intercept] Script injected by service worker');
const originalWindowOpen = window.open;
window.open = function(url, target, features) {
console.log('[Iframe Intercept] window.open called:', url, target);
if (url && window.parent !== window) {
try {
console.log('[Iframe Intercept] Posting message to parent');
window.parent.postMessage({
type: 'navigate-iframe',
url: url.toString()
}, '*');
return null;
} catch (e) {
console.error('[Iframe Intercept] Failed to communicate with parent:', e);
}
}
return originalWindowOpen.call(this, url, target, features);
};

document.addEventListener('click', function(e) {
const anchor = e.target.closest('a');
if (anchor && anchor.href) {
const target = anchor.getAttribute('target');
console.log('[Iframe Intercept] Link clicked:', anchor.href, 'target:', target);
if (target === '_blank' || target === '_new' || target === '_parent' || target === '_top') {
e.preventDefault();
e.stopPropagation();
console.log('[Iframe Intercept] Intercepting, will call window.open');
window.open(anchor.href);
}
}
}, true);

// Set base target to prevent new windows
setTimeout(function() {
const baseTag = document.querySelector('base');
if (!baseTag && document.head) {
const newBase = document.createElement('base');
newBase.target = '_self';
document.head.insertBefore(newBase, document.head.firstChild);
console.log('[Iframe Intercept] Created base tag with target="_self"');
}
}, 0);
})();
</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 <html> tag or at the start
if (text.includes('<html')) {
text = text.replace(/<html([^>]*)>/, `<html$1>${INTERCEPT_SCRIPT}`);
console.log('[SW] Injected after <html> tag');
} else if (text.includes('<head')) {
text = text.replace(/<head([^>]*)>/, `<head$1>${INTERCEPT_SCRIPT}`);
console.log('[SW] Injected after <head> 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 () => {
Expand All @@ -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;
})()
);
});
129 changes: 129 additions & 0 deletions src/pages/index.astro
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,46 @@ const link = Astro.url.searchParams.get("redir");
<link-element data-link={link} />
</div>
</Layout>

<!-- Inline script to intercept window.open globally and set up message handling -->
<script is:inline>
// This runs before any modules load
(function() {
// Store reference to original window.open
const originalWindowOpen = window.open;

// Track if we're currently opening a window from our code
let isInternalOpen = false;

// Override window.open globally
window.open = function(url, target, features) {
// If this is an internal call, allow it
if (isInternalOpen) {
return originalWindowOpen.call(this, url, target, features);
}

// Otherwise, navigate the iframe instead
const iframe = document.getElementById('iframe');
if (iframe && url) {
// Signal to navigate the iframe
window.postMessage({
type: 'navigate-iframe-internal',
url: url.toString()
}, '*');
return null;
}

// Fallback to original behavior
return originalWindowOpen.call(this, url, target, features);
};

// Make the flag available globally for internal use
window.__setInternalOpen = function(value) {
isInternalOpen = value;
};
})();
</script>

<script>
import { SW } from "@utils/proxy.ts";
import { Settings } from "@utils/settings.ts";
Expand Down Expand Up @@ -63,6 +103,23 @@ const link = Astro.url.searchParams.get("redir");
const sw = SW.getInstance().next().value!;
await sw.ready();

// Listen for messages from iframe to navigate it
window.addEventListener('message', (event) => {
console.log('Received message:', event.data);
if (event.data && event.data.type === 'navigate-iframe' && event.data.url) {
// Navigate the iframe to the requested URL
console.log('Navigating iframe to:', event.data.url);
iframe.src = sw.encodeURL(event.data.url);
}
// Handle internal navigation requests (from our global window.open override)
if (event.data && event.data.type === 'navigate-iframe-internal' && event.data.url) {
// Navigate the iframe to the requested URL
console.log('Navigating iframe (internal) to:', event.data.url);
iframe.classList.remove("hidden");
iframe.src = sw.encodeURL(event.data.url);
}
});

input.addEventListener("keypress", async (event: any) => {
if (event.key === "Enter") {
const settings = await Settings.getInstance();
Expand Down Expand Up @@ -111,6 +168,78 @@ const link = Astro.url.searchParams.get("redir");
phlImage.src = object;
bhl.classList.add("hidden");
phl.classList.remove("hidden");

// Inject the interception script into the iframe
try {
if (iframeWin && iframeWin.document) {
console.log("Injecting interception script into iframe");
const script = iframeWin.document.createElement('script');
script.textContent = `
(function() {
'use strict';
console.log('[Iframe Intercept] Script loaded');
const originalWindowOpen = window.open;
window.open = function(url, target, features) {
console.log('[Iframe Intercept] window.open called with:', url, target);
if (url && window.parent !== window) {
try {
console.log('[Iframe Intercept] Sending navigate message to parent');
window.parent.postMessage({
type: 'navigate-iframe',
url: url.toString()
}, '*');
return null;
} catch (e) {
console.error('[Iframe Intercept] Failed to communicate with parent:', e);
}
}
return originalWindowOpen.call(this, url, target, features);
};

document.addEventListener('click', function(e) {
const anchor = e.target.closest('a');
if (anchor && anchor.href) {
const target = anchor.getAttribute('target');
console.log('[Iframe Intercept] Link clicked:', anchor.href, 'target:', target);
if (target === '_blank' || target === '_new' || target === '_parent' || target === '_top') {
e.preventDefault();
e.stopPropagation();
console.log('[Iframe Intercept] Intercepting link, calling window.open');
window.open(anchor.href);
}
}
}, true);

// Set base target to _self to prevent new windows by default
const setBaseTarget = () => {
const baseTag = document.querySelector('base');
if (!baseTag) {
const newBase = document.createElement('base');
newBase.target = '_self';
if (document.head) {
document.head.insertBefore(newBase, document.head.firstChild);
}
console.log('[Iframe Intercept] Created base tag with target="_self"');
} else if (!baseTag.hasAttribute('target')) {
baseTag.target = '_self';
console.log('[Iframe Intercept] Set existing base tag target to "_self"');
}
};
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', setBaseTarget);
} else {
setBaseTarget();
}
})();
`;
iframeWin.document.head.appendChild(script);
console.log("Interception script injected successfully");
}
} catch (e) {
// Cross-origin restrictions may prevent this during initial load
// This is expected when loading proxied content
console.log("Could not inject script directly:", e);
}
});
}

Expand Down