Reject ServiceWorker Auto (Simple)

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

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 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();