Greasy Fork

来自缓存

Greasy Fork is available in English.

请不要污染我的剪贴板

保护用户剪贴板内容,允许或禁止网站污染剪贴板。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

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

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

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

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name         请不要污染我的剪贴板
// @namespace    Violentmonkey Scripts
// @version      1.0
// @description  保护用户剪贴板内容,允许或禁止网站污染剪贴板。
// @match        *://*/*
// @grant        GM_notification
// @grant        GM_registerMenuCommand
// @grant        GM_setValue
// @grant        GM_getValue
// @run-at       document-start
// @license MIT
/* jshint esversion: 8 */
// ==/UserScript==

(function() {
    'use strict';
    
    const PERMISSION_KEY = 'CLIPBOARD_PERMISSIONS';
    let permissions = GM_getValue(PERMISSION_KEY, {});

    // 创建权限管理菜单
    GM_registerMenuCommand("剪贴板权限管理", showPermissionManager);

    /**************** 核心拦截逻辑 ****************/
    
    // 修复1:正确处理传统execCommand
    const originalExecCommand = document.execCommand.bind(document);
    document.execCommand = function(command, showUI, value) {
        if (/^(copy|cut|paste)$/i.test(command)) {
            const allowed = checkPermissionSync(
                'execCommand:' + command,
                `网站试图通过document.execCommand执行【${command.toUpperCase()}】操作`
            );
            return allowed ? originalExecCommand(command, showUI, value) : false;
        }
        return originalExecCommand(command, showUI, value);
    };

    // 修复2:正确处理现代Clipboard API
    if (navigator.clipboard) {
        // 写入拦截
        const originalWrite = navigator.clipboard.writeText.bind(navigator.clipboard);
        navigator.clipboard.writeText = async function(data) {
            const allowed = await checkPermissionAsync(
                'clipboard:write',
                `网站试图写入剪贴板内容:${truncateText(data)}`
            );
            return allowed ? originalWrite(data) : Promise.reject(new DOMException('Not allowed', 'NotAllowedError'));
        };

        // 读取拦截
        const originalRead = navigator.clipboard.readText.bind(navigator.clipboard);
        navigator.clipboard.readText = async function() {
            const allowed = await checkPermissionAsync(
                'clipboard:read',
                '网站试图读取剪贴板内容'
            );
            return allowed ? originalRead() : Promise.reject(new DOMException('Not allowed', 'NotAllowedError'));
        };
    }

    // 修复3:优化事件监听劫持
    const originalAddEventListener = EventTarget.prototype.addEventListener;
    EventTarget.prototype.addEventListener = function(type, listener, options) {
        if (['copy', 'cut', 'paste'].includes(type)) {
            const wrappedListener = (e) => {
                const operation = type === 'paste' ? '读取' : '写入';
                const allowed = checkPermissionSync(
                    'event:' + type,
                    `网站通过【${type}】事件进行剪贴板${operation}`
                );
                if (allowed) return listener(e);
                e.preventDefault();
                e.stopImmediatePropagation();
            };
            return originalAddEventListener.call(this, type, wrappedListener, options);
        }
        return originalAddEventListener.call(this, type, listener, options);
    };

    /**************** 工具函数 ****************/
    
    // 同步权限检查(用于同步API)
    function checkPermissionSync(permissionId, message) {
        const domain = window.location.hostname;
        const fullKey = `${domain}:${permissionId}`;
        
        // 检查已保存的权限
        if (permissions[fullKey] !== undefined) {
            return permissions[fullKey];
        }
        
        // 交互式询问
        const choice = confirm(`${message}\n\n是否允许本次操作?\n(确定=允许,取消=拒绝)`);
        const remember = confirm('是否永久记住此选择?');
        
        if (remember) {
            permissions[fullKey] = choice;
            GM_setValue(PERMISSION_KEY, permissions);
        }
        
        return choice;
    }

    // 异步权限检查(用于异步API)
    async function checkPermissionAsync(permissionId, message) {
        return new Promise(resolve => {
            resolve(checkPermissionSync(permissionId, message));
        });
    }

    function showPermissionManager() {
        let msg = '已保存的剪贴板权限:\n';
        Object.keys(permissions).forEach(k => {
            msg += `\n${k.replace(':', ' → ')}: ${permissions[k] ? '✓' : '×'}`;
        });
        alert(msg + '\n\n提示:在弹窗中选择"记住"可永久保存设置');
    }

    function truncateText(text, max = 50) {
        return text.length > max ? text.substring(0, max) + '...' : text;
    }
})();