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暴力猴,之后才能安装此脚本。

You will need to install an extension such as Tampermonkey to install this script.

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

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

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name         Amazon Page Smoother
// @namespace    http://tampermonkey.net/
// @version      0.0.2
// @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);
    }
})();