Amazon Page Smoother

Improves performance on Amazon pages by throttling background tasks and pausing off-screen iframe rendering. Reduces CPU usage and improves page responsiveness.

当前为 2025-11-23 提交的版本,查看 最新版本

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

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

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

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

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name         Amazon Page Smoother
// @namespace    http://tampermonkey.net/
// @version      0.0.3
// @description  Improves performance on Amazon pages by throttling background tasks and pausing off-screen iframe rendering. Reduces CPU usage and improves page responsiveness.
// @license      Unlicense
// @author       VeleSila
// @match        https://www.amazon.com/*
// @match        https://www.amazon.co.jp/*
// @match        https://www.amazon.co.uk/*
// @match        https://www.amazon.es/*
// @match        https://www.amazon.fr/*
// @match        https://www.amazon.de/*
// @match        https://www.amazon.it/*
// @match        https://www.amazon.ca/*
// @match        https://www.amazon.com.au/*
// @exclude      */cart/*
// @exclude      */buy/*
// @grant        none
// @run-at       document-start
// ==/UserScript==

(function() {
    'use strict';

    // --- Configuration ---
    const CONFIG = {
        // Foreground tick rate (ms) - lower = more responsive but higher CPU
        FOREGROUND_TICK_RATE_MS: 150,
        // Background tick rate (ms) - higher = less CPU usage when tab is inactive
        BACKGROUND_TICK_RATE_MS: 2000,
        // Minimum delay for throttled intervals (prevents too-frequent execution)
        MIN_INTERVAL_DELAY_MS: 50,
        // Maximum number of tasks to process per tick (prevents long frames)
        MAX_TASKS_PER_TICK: 20,
        // Enable iframe optimization (can be disabled if causing issues)
        ENABLE_IFRAME_OPTIMIZATION: true,
        // Enable timer throttling (can be disabled if causing issues)
        ENABLE_TIMER_THROTTLING: true
    };

    /**
     * Feature detection for modern browser APIs
     */
    const SUPPORTS = {
        intersectionObserver: 'IntersectionObserver' in window,
        contentVisibility: CSS.supports('content-visibility', 'auto') || CSS.supports('content-visibility', 'hidden')
    };

    /**
     * Safe DOM element checker
     */
    function isElementInDOM(element) {
        try {
            return element && element.isConnected && document.body.contains(element);
        } catch (e) {
            return false;
        }
    }

    /**
     * Consolidates multiple setInterval calls into a single, more efficient loop.
     * Uses requestIdleCallback when available for better background performance.
     */
    function initializeTimerThrottler() {
        if (!CONFIG.ENABLE_TIMER_THROTTLING) return;
        if (window.self !== window.top) return; // Only run in the top-level window

        const nativeSetInterval = window.setInterval;
        const nativeClearInterval = window.clearInterval;
        const nativeRequestIdleCallback = window.requestIdleCallback || 
            ((cb) => setTimeout(cb, 1));
        
        let nextTaskId = 1;
        const scheduledTasks = new Map();
        let isMasterLoopRunning = false;
        const cleanupQueue = new Set();

        /**
         * Master loop that processes scheduled tasks based on tab visibility
         */
        const masterLoop = () => {
            if (!isMasterLoopRunning) return;
            
            const now = performance.now();
            const isBackground = document.hidden;
            const tickRate = isBackground ? CONFIG.BACKGROUND_TICK_RATE_MS : CONFIG.FOREGROUND_TICK_RATE_MS;
            const maxTasks = isBackground ? Math.max(1, Math.floor(CONFIG.MAX_TASKS_PER_TICK / 2)) : CONFIG.MAX_TASKS_PER_TICK;
            
            let tasksProcessed = 0;
            
            // Process cleanup queue first
            cleanupQueue.forEach(id => {
                scheduledTasks.delete(id);
            });
            cleanupQueue.clear();
            
            // Process tasks
            for (const [id, task] of scheduledTasks) {
                if (tasksProcessed >= maxTasks) break;
                
                if (now - task.lastExecution >= task.delay) {
                    try {
                        if (isBackground && 'requestIdleCallback' in window) {
                            nativeRequestIdleCallback(() => {
                                if (scheduledTasks.has(id)) {
                                    task.callback();
                                    task.lastExecution = performance.now();
                                }
                            }, { timeout: 500 });
                        } else {
                            task.callback();
                            task.lastExecution = now;
                        }
                        tasksProcessed++;
                    } catch (error) {
                        console.warn(`[Amazon Page Smoother] Error in throttled task ${id}:`, error);
                        cleanupQueue.add(id);
                    }
                }
            }
            
            // Schedule next tick
            setTimeout(masterLoop, tickRate);
        };

        // Override setInterval with throttling logic
        window.setInterval = (callback, delay = 0, ...args) => {
            if (typeof callback !== 'function') {
                return nativeSetInterval(callback, delay, ...args);
            }

            const id = nextTaskId++;
            const adjustedDelay = Math.max(delay, CONFIG.MIN_INTERVAL_DELAY_MS);
            
            scheduledTasks.set(id, {
                callback: () => callback(...args),
                delay: adjustedDelay,
                lastExecution: performance.now(),
                originalDelay: delay
            });

            if (!isMasterLoopRunning) {
                isMasterLoopRunning = true;
                setTimeout(masterLoop, 0);
            }

            return id;
        };

        // Override clearInterval with proper cleanup
        window.clearInterval = (id) => {
            if (scheduledTasks.has(id)) {
                cleanupQueue.add(id);
                return;
            }
            nativeClearInterval(id);
        };

        // Handle page unload to clean up
        window.addEventListener('beforeunload', () => {
            isMasterLoopRunning = false;
            scheduledTasks.clear();
            cleanupQueue.clear();
        }, { passive: true });

        // Handle page visibility changes
        document.addEventListener('visibilitychange', () => {
            if (!document.hidden && !isMasterLoopRunning && scheduledTasks.size > 0) {
                isMasterLoopRunning = true;
                setTimeout(masterLoop, 0);
            }
        }, { passive: true });
    }

    /**
     * Pauses rendering for iframes that are not currently visible on screen.
     * Uses content-visibility: hidden for off-screen iframes to save rendering resources.
     */
    function initializeIframeManager() {
        if (!CONFIG.ENABLE_IFRAME_OPTIMIZATION) return;
        if (!SUPPORTS.intersectionObserver || !SUPPORTS.contentVisibility) {
            console.debug('[Amazon Page Smoother] Skipping iframe optimization - browser lacks required features');
            return;
        }
        if (window.self !== window.top) return;

        let visibilityWatcher;
        let domWatcher;
        const observedElements = new WeakMap();
        const iframeSelector = 'iframe:not([src^="javascript:"]):not([src^="data:"])';
        
        /**
         * Setup IntersectionObserver for iframe visibility tracking
         */
        function setupVisibilityObserver() {
            visibilityWatcher = new IntersectionObserver(
                (entries) => {
                    entries.forEach(entry => {
                        if (!isElementInDOM(entry.target)) {
                            visibilityWatcher.unobserve(entry.target);
                            return;
                        }
                        
                        const isVisible = entry.isIntersecting || entry.intersectionRatio > 0.1;
                        const currentState = observedElements.get(entry.target) || 'visible';
                        
                        if (isVisible && currentState !== 'visible') {
                            entry.target.style.contentVisibility = 'visible';
                            observedElements.set(entry.target, 'visible');
                        } else if (!isVisible && currentState !== 'hidden') {
                            // Only hide if not actively being interacted with
                            if (!entry.target.matches(':hover,:focus,:active')) {
                                entry.target.style.contentVisibility = 'hidden';
                                observedElements.set(entry.target, 'hidden');
                            }
                        }
                    });
                }, 
                { 
                    root: null, 
                    threshold: [0, 0.1, 1.0],
                    rootMargin: '100px' // Start observing before iframe enters viewport
                }
            );
        }

        /**
         * Observe a single iframe element
         */
        function observeIframe(iframe) {
            if (!iframe || !isElementInDOM(iframe) || observedElements.has(iframe)) return;
            
            try {
                // Skip iframes that are likely critical or have specific requirements
                if (iframe.src && (
                    iframe.src.includes('captcha') || 
                    iframe.src.includes('login') ||
                    iframe.src.includes('checkout') ||
                    iframe.hasAttribute('data-critical')
                )) {
                    return;
                }
                
                observedElements.set(iframe, 'visible');
                visibilityWatcher.observe(iframe);
                iframe.dataset.amazonSmoother = 'managed';
            } catch (e) {
                console.warn('[Amazon Page Smoother] Failed to observe iframe:', e);
            }
        }

        /**
         * Setup MutationObserver to handle dynamically added iframes
         */
        function setupDOMObserver() {
            domWatcher = new MutationObserver((mutations) => {
                mutations.forEach(mutation => {
                    if (mutation.type === 'childList') {
                        mutation.addedNodes.forEach(node => {
                            if (node.nodeType === Node.ELEMENT_NODE) {
                                if (node.matches(iframeSelector)) {
                                    observeIframe(node);
                                } else {
                                    node.querySelectorAll(iframeSelector).forEach(observeIframe);
                                }
                            }
                        });
                    }
                });
            });
        }

        /**
         * Start managing iframes
         */
        function startManaging() {
            if (!document.body) return;
            
            setupVisibilityObserver();
            setupDOMObserver();
            
            // Observe existing iframes
            document.querySelectorAll(iframeSelector).forEach(observeIframe);
            
            // Start observing DOM changes
            domWatcher.observe(document.body, { 
                childList: true, 
                subtree: true,
                attributes: false,
                characterData: false
            });
        }

        /**
         * Cleanup function
         */
        function cleanup() {
            if (domWatcher) {
                domWatcher.disconnect();
            }
            if (visibilityWatcher) {
                visibilityWatcher.disconnect();
            }
            // Reset contentVisibility on all managed iframes
            document.querySelectorAll('[data-amazon-smoother="managed"]').forEach(iframe => {
                iframe.style.contentVisibility = 'visible';
                iframe.removeAttribute('data-amazon-smoother');
            });
            observedElements = new WeakMap();
        }

        // Initialize when DOM is ready
        if (document.readyState === 'loading') {
            document.addEventListener('DOMContentLoaded', startManaging, { once: true, passive: true });
        } else {
            startManaging();
        }

        // Cleanup on page unload
        window.addEventListener('beforeunload', cleanup, { passive: true });
    }

    // --- Script Initialization ---
    try {
        console.debug('[Amazon Page Smoother] Initializing performance optimizations...');
        
        const initStart = performance.now();
        
        if (CONFIG.ENABLE_TIMER_THROTTLING) {
            initializeTimerThrottler();
        }
        
        if (CONFIG.ENABLE_IFRAME_OPTIMIZATION && SUPPORTS.intersectionObserver && SUPPORTS.contentVisibility) {
            initializeIframeManager();
        }
        
        const initTime = performance.now() - initStart;
        console.debug(`[Amazon Page Smoother] Initialized in ${initTime.toFixed(2)}ms`);
        
    } catch (error) {
        console.error('[Amazon Page Smoother] Critical initialization error:', error);
    }
})();