Reject ServiceWorker Auto (Simple)

Blocks ServiceWorker on all websites. Advanced whitelist management with manual domain removal. Prevents PWA installations and background sync.

您需要先安装一个扩展,例如 篡改猴Greasemonkey暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴Userscripts ,之后才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。

您需要先安装用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name        Reject ServiceWorker Auto (Simple)
// @namespace   rejectserviceWorkerAuto
// @version     1.7.5
// @description Blocks ServiceWorker on all websites. Advanced whitelist management with manual domain removal. Prevents PWA installations and background sync.
// @author      hongmd
// @license     MIT
// @homepageURL https://github.com/hongmd/userscript-improved
// @supportURL  https://github.com/hongmd/userscript-improved/issues
// @match       *://*/*
// @run-at      document-start
// @grant       GM_getValue
// @grant       GM_setValue
// @grant       GM_registerMenuCommand
// @compatible  ScriptCat
// @compatible  Tampermonkey
// @compatible  Greasemonkey
// @noframes
// ==/UserScript==

'use strict';

// Constants for consistent messaging and configuration
const MESSAGES = {
    // Status messages
    BLOCKED: "ServiceWorker registration blocked",
    WHITELISTED: "Domain whitelisted - ServiceWorker allowed",
    ALREADY_EXISTS: "Domain already in whitelist",
    NOT_IN_WHITELIST: "Domain is not in whitelist",

    // Alert messages
    ADDED_TO_WHITELIST: (hostname) => `✅ Added "${hostname}" to whitelist!\n\nServiceWorker will NOT be blocked here.\nReload page to take effect.`,
    REMOVED_FROM_WHITELIST: (hostname) => `❌ Removed "${hostname}" from whitelist!\n\nServiceWorker will be blocked here.\nReload page to take effect.`,
    ALREADY_WHITELISTED: (hostname) => `Info: "${hostname}" is already in whitelist.`,
    NOT_WHITELISTED: (hostname) => `"${hostname}" is not in whitelist.`,
    WHITELIST_EMPTY: "📋 Whitelist is empty!\n\nNo domains to remove.",
    DOMAIN_NOT_FOUND: (domain, currentList) => `❌ Domain "${domain}" not found in whitelist.\n\nCurrent whitelist: ${currentList}`,
    MANUAL_REMOVAL_SUCCESS: (domain) => `✅ Removed "${domain}" from whitelist!\n\nServiceWorker will be blocked on this domain.\nReload the page if you're currently on it.`,

    // Error messages
    INIT_ERROR: "Failed to initialize menu items - falling back to blocking mode",
    STORAGE_ERROR: "Invalid stored data, resetting to empty array",
    INJECTION_ERROR: "Failed to block ServiceWorker - using fallback method",

    // Menu commands
    SHOW_WHITELIST: "📋 Show Whitelist Info",
    BLOCK_HERE: "🚫 Block ServiceWorker Here",
    ALLOW_HERE: "✅ Allow ServiceWorker Here",
    MANUAL_BLOCK: "🔧 Manual Block Now",
    REMOVE_FROM_WHITELIST: "🗑️ Remove from Whitelist"
};

const SCRIPT_NAME = 'rejectserviceWorkerAuto';
const STORAGE_PREFIX = `autoinject${SCRIPT_NAME}`;
const LOG_PREFIX = 'RSA:';

console.log(`${LOG_PREFIX} Script loaded for:`, document.domain);

let injectedStatus = false;
let hostArray = [];

/**
 * Injects ServiceWorker blocking logic using primary and fallback methods
 * Primary method: Overrides navigator.serviceWorker with a blocking proxy object
 * Fallback method: Overrides the register method directly for older browsers
 * Prevents PWA installations and background sync functionality
 */
function inject() {
    // Skip if running in frames or already injected
    if (window.self !== window.top) return;
    if (injectedStatus) return;

    // Block ServiceWorker registration
    if (typeof navigator !== 'undefined' && navigator.serviceWorker) {
        try {
            Object.defineProperty(navigator, 'serviceWorker', {
                value: {
                    register: () => Promise.reject(new Error("ServiceWorker registration blocked by RSA script")),
                    getRegistration: () => Promise.resolve(undefined),
                    getRegistrations: () => Promise.resolve([]),
                    ready: Promise.reject(new Error("ServiceWorker blocked"))
                },
                writable: false,
                configurable: false
            });
            console.log(`${LOG_PREFIX} ${MESSAGES.BLOCKED} on`, document.domain);
        } catch (e) {
            // Fallback method for older browsers
            navigator.serviceWorker.register = () => Promise.reject(new Error("ServiceWorker registration blocked"));
            console.warn(`${LOG_PREFIX} ${MESSAGES.INJECTION_ERROR} on`, document.domain);
        }
    }
    injectedStatus = true;
}

/**
 * Adds the current domain to the whitelist, allowing ServiceWorker registration
 * Updates persistent storage and provides user feedback
 * Requires page reload to take effect
 */
function addHost() {
    const hostname = location.hostname;
    if (!hostArray.includes(hostname)) {
        hostArray.push(hostname);
        GM_setValue(STORAGE_PREFIX, JSON.stringify(hostArray));
        console.log(`${LOG_PREFIX} Added`, hostname, 'to whitelist');
        alert(MESSAGES.ADDED_TO_WHITELIST(hostname));
    } else {
        alert(MESSAGES.ALREADY_WHITELISTED(hostname));
    }
}

/**
 * Displays comprehensive whitelist information including current domain status
 * Shows total count and lists all whitelisted domains
 * Provides clear visual indicators for whitelist status
 */
function showWhitelistInfo() {
    const hostname = location.hostname;
    const isWhitelisted = hostArray.includes(hostname);
    const currentStatus = isWhitelisted ? "WHITELISTED" : "BLOCKED";
    const statusIcon = isWhitelisted ? "✅" : "🚫";
    const totalSites = hostArray.length;

    let message = `${statusIcon} Current domain: ${hostname}\n`;
    message += `Status: ${currentStatus}\n\n`;
    message += `📋 Total whitelisted sites: ${totalSites}\n`;

    if (totalSites > 0) {
        message += "🔸 " + hostArray.join("\n🔸 ");
    }

    alert(message);
}

/**
 * Removes the current domain from the whitelist, enabling ServiceWorker blocking
 * Updates persistent storage and provides user feedback
 * Requires page reload to take effect
 */
function removeHost() {
    const hostname = location.hostname;
    const index = hostArray.indexOf(hostname);
    if (index > -1) {
        hostArray.splice(index, 1);
        GM_setValue(STORAGE_PREFIX, JSON.stringify(hostArray));
        console.log(`${LOG_PREFIX} Removed`, hostname, 'from whitelist');
        alert(MESSAGES.REMOVED_FROM_WHITELIST(hostname));
    } else {
        alert(MESSAGES.NOT_WHITELISTED(hostname));
    }
}

/**
 * Allows user to remove any domain from the whitelist by entering it manually
 * Provides a text input dialog for domain removal
 * Useful for managing whitelist without visiting each domain
 */
function removeFromWhitelist() {
    if (hostArray.length === 0) {
        alert(MESSAGES.WHITELIST_EMPTY);
        return;
    }

    const currentList = hostArray.join(", ");
    const domainToRemove = prompt(
        `🗑️ Remove domain from whitelist:\n\nCurrent whitelist: ${currentList}\n\nEnter domain to remove (without https://):`,
        ""
    );

    if (!domainToRemove?.trim()) return;

    const cleanDomain = domainToRemove.trim().toLowerCase();
    const index = hostArray.findIndex(host => host.toLowerCase() === cleanDomain);

    if (index > -1) {
        const removedDomain = hostArray.splice(index, 1)[0];
        GM_setValue(STORAGE_PREFIX, JSON.stringify(hostArray));
        console.log(`${LOG_PREFIX} Manually removed`, removedDomain, 'from whitelist');
        alert(MESSAGES.MANUAL_REMOVAL_SUCCESS(removedDomain));
    } else {
        alert(MESSAGES.DOMAIN_NOT_FOUND(cleanDomain, currentList));
    }
}

/**
 * Initializes the script by loading stored whitelist data and setting up menu commands
 * Handles data validation, error recovery, and dynamic menu configuration
 * Automatically injects blocking logic for non-whitelisted domains
 */
function initializeScript() {
    try {
        // Safe JSON parsing with validation
        const storedData = GM_getValue(STORAGE_PREFIX, "[]");
        if (typeof storedData === 'string' && storedData.trim()) {
            hostArray = JSON.parse(storedData);
            // Validate that result is an array
            if (!Array.isArray(hostArray)) {
                console.warn(`${LOG_PREFIX} ${MESSAGES.STORAGE_ERROR}`);
                hostArray = [];
            }
        } else {
            hostArray = [];
        }

        const hostname = location.hostname;
        const isWhitelisted = hostArray.includes(hostname);

        // Always show status info
        GM_registerMenuCommand(MESSAGES.SHOW_WHITELIST, showWhitelistInfo);
        GM_registerMenuCommand(MESSAGES.REMOVE_FROM_WHITELIST, removeFromWhitelist);

        if (isWhitelisted) {
            // Current domain is whitelisted
            GM_registerMenuCommand(MESSAGES.BLOCK_HERE, removeHost);
            GM_registerMenuCommand(MESSAGES.MANUAL_BLOCK, inject);
            console.log(`${LOG_PREFIX}`, hostname, MESSAGES.WHITELISTED);
        } else {
            // Current domain is not whitelisted (blocked by default)
            inject();
            GM_registerMenuCommand(MESSAGES.ALLOW_HERE, addHost);
            console.log(`${LOG_PREFIX} Auto-blocked ServiceWorker for`, hostname);
        }
    } catch (err) {
        console.error(`${LOG_PREFIX} ${MESSAGES.INIT_ERROR}`);
        console.error(err);
        // Fallback: always inject if there's an error
        inject();
    }
}

// Start the script
initializeScript();