Skip to content

Commit bb286e9

Browse files
committed
Prevent proxied pages from opening new tabs in your browser
1 parent 98a6839 commit bb286e9

File tree

1 file changed

+154
-0
lines changed

1 file changed

+154
-0
lines changed

src/components/Browser.tsx

Lines changed: 154 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,160 @@ export default function Browser() {
9090
};
9191
}, [activeTab, url]);
9292

93+
useEffect(() => {
94+
if (!activeTab) return;
95+
const iframe = iframeRefs.current[activeTab.id];
96+
if (!iframe) return;
97+
98+
const setupIntercept = () => {
99+
try {
100+
const iframeWindow = iframe.contentWindow as any;
101+
if (!iframeWindow || iframeWindow.__tabInterceptSetup) return;
102+
103+
iframeWindow.__tabInterceptSetup = true;
104+
105+
const config = (window as any).__uv$config;
106+
107+
const originalOpen = iframeWindow.open;
108+
iframeWindow.open = function(url?: string, target?: string, features?: string) {
109+
if (!target || target === "_blank" || target === "_new") {
110+
try {
111+
const fullUrl = url ? new URL(url, iframeWindow.location.href).href : "about:blank";
112+
113+
let actualUrl = fullUrl;
114+
if (config && fullUrl.includes(config.prefix)) {
115+
const encoded = fullUrl.substring(fullUrl.indexOf(config.prefix) + config.prefix.length);
116+
actualUrl = config.decodeUrl(encoded);
117+
}
118+
119+
const newId = Date.now();
120+
setTabs((prev) => [
121+
...prev.map((t) => ({ ...t, active: false })),
122+
{ id: newId, title: "New Tab", url: actualUrl, active: true, reloadKey: 0 }
123+
]);
124+
125+
return null;
126+
} catch (err) {
127+
console.warn("Failed to intercept window.open:", err);
128+
}
129+
}
130+
return originalOpen.call(iframeWindow, url, target, features);
131+
};
132+
133+
iframeWindow.addEventListener("click", (e: MouseEvent) => {
134+
const target = e.target as HTMLElement;
135+
const anchor = target.closest("a");
136+
137+
if (anchor) {
138+
const linkTarget = anchor.getAttribute("target");
139+
const hasModifier = e.ctrlKey || e.metaKey;
140+
141+
if (linkTarget === "_blank" || linkTarget === "_new" || hasModifier) {
142+
e.preventDefault();
143+
e.stopPropagation();
144+
145+
const href = anchor.getAttribute("href");
146+
if (href) {
147+
try {
148+
const fullUrl = new URL(href, iframeWindow.location.href).href;
149+
150+
let actualUrl = fullUrl;
151+
if (config && fullUrl.includes(config.prefix)) {
152+
const encoded = fullUrl.substring(fullUrl.indexOf(config.prefix) + config.prefix.length);
153+
actualUrl = config.decodeUrl(encoded);
154+
}
155+
156+
const newId = Date.now();
157+
setTabs((prev) => [
158+
...prev.map((t) => ({ ...t, active: false })),
159+
{ id: newId, title: "New Tab", url: actualUrl, active: true, reloadKey: 0 }
160+
]);
161+
} catch (err) {
162+
console.warn("Failed to intercept link click:", err);
163+
}
164+
}
165+
}
166+
}
167+
}, true);
168+
169+
iframeWindow.addEventListener("auxclick", (e: MouseEvent) => {
170+
if (e.button !== 1) return;
171+
172+
const target = e.target as HTMLElement;
173+
const anchor = target.closest("a");
174+
175+
if (anchor) {
176+
e.preventDefault();
177+
e.stopPropagation();
178+
179+
const href = anchor.getAttribute("href");
180+
if (href) {
181+
try {
182+
const fullUrl = new URL(href, iframeWindow.location.href).href;
183+
184+
let actualUrl = fullUrl;
185+
if (config && fullUrl.includes(config.prefix)) {
186+
const encoded = fullUrl.substring(fullUrl.indexOf(config.prefix) + config.prefix.length);
187+
actualUrl = config.decodeUrl(encoded);
188+
}
189+
190+
const newId = Date.now();
191+
setTabs((prev) => [
192+
...prev.map((t) => ({ ...t, active: false })),
193+
{ id: newId, title: "New Tab", url: actualUrl, active: true, reloadKey: 0 }
194+
]);
195+
} catch (err) {
196+
console.warn("Failed to intercept middle-click:", err);
197+
}
198+
}
199+
}
200+
}, true);
201+
202+
} catch (err) {
203+
}
204+
};
205+
206+
const handleLoad = () => {
207+
setupIntercept();
208+
};
209+
210+
iframe.addEventListener("load", handleLoad);
211+
setupIntercept();
212+
213+
return () => {
214+
iframe.removeEventListener("load", handleLoad);
215+
};
216+
}, [activeTab]);
217+
218+
useEffect(() => {
219+
const handleKeyDown = (e: KeyboardEvent) => {
220+
if ((e.ctrlKey || e.metaKey) && (e.key === "t" || e.key === "n")) {
221+
e.preventDefault();
222+
}
223+
};
224+
225+
const handleAuxClick = (e: MouseEvent) => {
226+
if (e.button === 1) {
227+
e.preventDefault();
228+
}
229+
};
230+
231+
window.addEventListener("keydown", handleKeyDown);
232+
window.addEventListener("auxclick", handleAuxClick);
233+
234+
const originalWindowOpen = window.open;
235+
window.open = function() {
236+
console.warn("Blocked external window.open call");
237+
return null;
238+
};
239+
240+
return () => {
241+
window.removeEventListener("keydown", handleKeyDown);
242+
window.removeEventListener("auxclick", handleAuxClick);
243+
window.open = originalWindowOpen;
244+
};
245+
}, []);
246+
93247
const setActiveTab = (id: number) => {
94248
setTabs((prev) => prev.map((tab) => ({ ...tab, active: tab.id === id })));
95249
};

0 commit comments

Comments
 (0)