// ==UserScript== // @name 网页重定向 // @namespace https://viayoo.com/bza5z2 // @version 2.6.2 // @description 按域名管理重定向,默认禁用,启用后仅允许同源跳转 // @author ChatGPT & DeepSeek // @license MIT // @match *://*/* // @exclude *://*.baidu.com/* // @exclude *://*.bing.com/* // @exclude *://*.bilibili.com/* // @exclude *://*.deepseek.com/* // @exclude *://*gitee.com/* // @exclude *://*github.com/* // @exclude *://*.google.com/* // @exclude *://*.sogou.com/* // @exclude *://*.qq.com/* // @run-at document-start // @grant GM_registerMenuCommand // @grant GM_unregisterMenuCommand // @grant GM_getValue // @grant GM_setValue // @grant GM_notification // ==/UserScript== (() => { 'use strict'; const CURRENT_DOMAIN = location.hostname; let menuCommandIds = []; let interceptorsInstalled = false; let enabledDomains = GM_getValue('enabledDomains', []); function isDomainEnabled(domain) { return enabledDomains.includes(domain); } function setDomainEnabled(domain, enabled) { if (enabled) { if (!enabledDomains.includes(domain)) { enabledDomains.push(domain); } } else { enabledDomains = enabledDomains.filter(d => d !== domain); } GM_setValue('enabledDomains', enabledDomains); } function getAllDomainsSettings() { return enabledDomains.map(domain => ({ domain: domain, enabled: true })).sort((a, b) => a.domain.localeCompare(b.domain)); } function clearDomainSettings(domain) { const wasEnabled = isDomainEnabled(domain); setDomainEnabled(domain, false); if (domain === CURRENT_DOMAIN && wasEnabled) { location.reload(); } } function showNotification(message) { if (window.via && typeof window.via.toast === 'function') { try { window.via.toast(message); return; } catch (e) {} } if (typeof GM_notification === 'function') { try { GM_notification({ text: message, title: '网页重定向', timeout: 3000 }); return; } catch (e) {} } } function showBlockedNotification() { showNotification('已触发仅允许同源重定向'); } function shouldBlockRedirect(href) { try { if (!href) { return false; } const targetUrl = new URL(href, location.href); const currentUrl = new URL(location.href); return targetUrl.origin !== currentUrl.origin; } catch (e) { return false; } } function installRedirectManagers() { if (interceptorsInstalled) { return; } try { interceptorsInstalled = true; const original = { windowOpen: window.open, assign: location.assign, replace: location.replace, pushState: history.pushState, replaceState: history.replaceState }; try { window.open = function (...args) { try { const url = args[0]; if (shouldBlockRedirect(url)) { showBlockedNotification(); return null; } } catch (e) {} return original.windowOpen.apply(this, args); }; } catch (e) {} try { if (typeof original.assign === 'function') { location.assign = function (url) { try { if (shouldBlockRedirect(url)) { showBlockedNotification(); return; } } catch (e) {} return original.assign.call(location, url); }; } if (typeof original.replace === 'function') { location.replace = function (url) { try { if (shouldBlockRedirect(url)) { showBlockedNotification(); return; } } catch (e) {} return original.replace.call(location, url); }; } } catch (e) {} try { history.pushState = function (state, title, url) { try { if (shouldBlockRedirect(url)) { showBlockedNotification(); return; } } catch (e) {} return original.pushState.apply(this, arguments); }; history.replaceState = function (state, title, url) { try { if (shouldBlockRedirect(url)) { showBlockedNotification(); return; } } catch (e) {} return original.replaceState.apply(this, arguments); }; } catch (e) {} try { const anchorProto = HTMLAnchorElement.prototype; const origAnchorHrefDesc = Object.getOwnPropertyDescriptor(anchorProto, 'href'); if (origAnchorHrefDesc && origAnchorHrefDesc.set && origAnchorHrefDesc.configurable) { Object.defineProperty(anchorProto, 'href', { configurable: true, enumerable: true, get() { return origAnchorHrefDesc.get.call(this); }, set(v) { try { if (shouldBlockRedirect(v)) { showBlockedNotification(); this.setAttribute('data-blocked-redirect', v); return; } } catch (e) {} return origAnchorHrefDesc.set.call(this, v); } }); } const origSetAttr = Element.prototype.setAttribute; Element.prototype.setAttribute = function (name, value) { try { const n = String(name).toLowerCase(); if ((this instanceof HTMLAnchorElement && (n === 'href')) || (this instanceof HTMLFormElement && (n === 'action'))) { if (shouldBlockRedirect(value)) { showBlockedNotification(); this.setAttribute('data-blocked-' + n, String(value)); return; } } } catch (e) {} return origSetAttr.apply(this, arguments); }; } catch (e) {} try { const formProto = HTMLFormElement.prototype; const origSubmit = formProto.submit; formProto.submit = function () { try { const action = this.getAttribute('action') || location.href; if (shouldBlockRedirect(action)) { showBlockedNotification(); return; } } catch (e) {} return origSubmit.apply(this, arguments); }; const actionDesc = Object.getOwnPropertyDescriptor(formProto, 'action'); if (actionDesc && actionDesc.set && actionDesc.configurable) { Object.defineProperty(formProto, 'action', { configurable: true, enumerable: true, get() { return actionDesc.get.call(this); }, set(val) { try { if (shouldBlockRedirect(val)) { showBlockedNotification(); this.setAttribute('data-blocked-action', val); return; } } catch (e) {} return actionDesc.set.call(this, val); } }); } document.addEventListener('submit', function(e) { try { const form = e.target; if (form && form.tagName === 'FORM') { const action = form.getAttribute('action') || form.action || location.href; if (shouldBlockRedirect(action)) { showBlockedNotification(); e.preventDefault(); e.stopImmediatePropagation(); return false; } } } catch (err) {} }, true); } catch (e) {} try { const mo = new MutationObserver((mutations) => { for (const m of mutations) { if (!m.addedNodes) continue; m.addedNodes.forEach((node) => { try { if (!(node instanceof Element)) return; if (node.tagName === 'META' && node.getAttribute('http-equiv') && /refresh/i.test(node.getAttribute('http-equiv'))) { const content = node.getAttribute('content') || ''; if (/url\s*=/i.test(content)) { node.remove(); } } if (node.tagName === 'A') { const href = node.getAttribute('href') || node.href; if (shouldBlockRedirect(href)) { showBlockedNotification(); node.setAttribute('data-blocked-redirect', href); node.removeAttribute('href'); } } if (node.tagName === 'FORM') { const action = node.getAttribute('action') || ''; if (shouldBlockRedirect(action)) { showBlockedNotification(); node.setAttribute('data-blocked-action', action); node.removeAttribute('action'); } } if (node.querySelectorAll) { const metas = node.querySelectorAll('meta[http-equiv="refresh"]'); metas.forEach((m) => { m.remove(); }); } } catch (inner) {} }); } }); mo.observe(document.documentElement || document, { childList: true, subtree: true }); } catch (e) {} try { window.addEventListener('click', (e) => { try { const link = e.target && e.target.closest ? e.target.closest('a') : null; if (link) { const href = link.getAttribute('href') || link.href; if (shouldBlockRedirect(href)) { showBlockedNotification(); console.log(`[网页重定向] 阻止点击事件重定向到: ${href}`); e.preventDefault(); e.stopImmediatePropagation(); return false; } } } catch (e) {} }, true); } catch (e) {} } catch (e) {} } async function showDomainManagement() { const domainSettings = getAllDomainsSettings(); if (domainSettings.length === 0) { showNotification('当前没有已配置的域名'); return; } let message = '已启用重定向管理的域名:\n\n'; domainSettings.forEach((setting, index) => { message += `${index + 1}. ${setting.domain}\n`; }); message += '\n请输入要清除的域名编号:'; const choice = prompt(message); if (choice && /^\d+$/.test(choice)) { const index = parseInt(choice) - 1; if (index >= 0 && index < domainSettings.length) { const domain = domainSettings[index].domain; clearDomainSettings(domain); showNotification(`域名 "${domain}" 的设置已清除`); rebuildMenu(); } } } function rebuildMenu() { menuCommandIds.forEach(id => { try { GM_unregisterMenuCommand(id); } catch (e) {} }); menuCommandIds = []; (async () => { try { const enabled = isDomainEnabled(CURRENT_DOMAIN); const enableDisableId = GM_registerMenuCommand( enabled ? `🔴 禁用仅允许同源重定向` : `🟢 启用仅允许同源重定向`, async () => { const newEnabled = !enabled; setDomainEnabled(CURRENT_DOMAIN, newEnabled); if (newEnabled) { showNotification('已启用仅允许同源重定向'); if (!interceptorsInstalled) { installRedirectManagers(); } } else { showNotification('已禁用仅允许同源重定向'); location.reload(); } rebuildMenu(); } ); menuCommandIds.push(enableDisableId); const manageDomainsId = GM_registerMenuCommand('管理域名设置', showDomainManagement); menuCommandIds.push(manageDomainsId); } catch (e) {} })(); } (async () => { try { rebuildMenu(); if (isDomainEnabled(CURRENT_DOMAIN)) { installRedirectManagers(); } } catch (e) {} })(); })();