diff --git a/src/contentScripts/index.ts b/src/contentScripts/index.ts index 7db6c5f8..821c63f5 100644 --- a/src/contentScripts/index.ts +++ b/src/contentScripts/index.ts @@ -7,6 +7,7 @@ import App from './views/App.vue' import { setupApp } from '~/logic/common-setup' import { SVG_ICONS } from '~/utils/svgIcons' import { delay, injectCSS } from '~/utils/main' +import { runWhenIdle } from '~/utils/lazy' import { settings } from '~/logic' const currentUrl = document.URL @@ -80,15 +81,17 @@ if (isFirefox) { if (!isFirstScriptExecute) return - setTimeout(() => { + runWhenIdle(() => { injectApp() - }, 1000) + }) isFirstScriptExecute = false }) } else { document.addEventListener('DOMContentLoaded', () => { - injectApp() + runWhenIdle(() => { + injectApp() + }) }) } diff --git a/src/utils/lazy.ts b/src/utils/lazy.ts new file mode 100644 index 00000000..ffdbe6c9 --- /dev/null +++ b/src/utils/lazy.ts @@ -0,0 +1,75 @@ +// copy with vscode + +interface IdleDeadline { + readonly didTimeout: boolean + timeRemaining(): number +} + +interface IDisposable { + dispose(): void +} + +/** + * Execute the callback the next time the browser is idle, returning an + * {@link IDisposable} that will cancel the callback when disposed. This wraps + * [requestIdleCallback] so it will fallback to [setTimeout] if the environment + * doesn't support it. + * + * @param callback The callback to run when idle, this includes an + * [IdleDeadline] that provides the time alloted for the idle callback by the + * browser. Not respecting this deadline will result in a degraded user + * experience. + * @param timeout A timeout at which point to queue no longer wait for an idle + * callback but queue it on the regular event loop (like setTimeout). Typically + * this should not be used. + * + * [IdleDeadline]: https://developer.mozilla.org/en-US/docs/Web/API/IdleDeadline + * [requestIdleCallback]: https://developer.mozilla.org/en-US/docs/Web/API/Window/requestIdleCallback + * [setTimeout]: https://developer.mozilla.org/en-US/docs/Web/API/Window/setTimeout + */ + +export let runWhenIdle: (callback: (idle: IdleDeadline) => void, timeout?: number) => IDisposable + +declare function requestIdleCallback(callback: (args: IdleDeadline) => void, options?: { timeout: number }): number +declare function cancelIdleCallback(handle: number): void; + +(function () { + if (typeof requestIdleCallback !== 'function' || typeof cancelIdleCallback !== 'function') { + runWhenIdle = (runner) => { + let disposed = false + setTimeout(() => { + if (disposed) + return + const end = Date.now() + 15 // one frame at 64fps + runner(Object.freeze({ + didTimeout: true, + timeRemaining() { + return Math.max(0, end - Date.now()) + }, + })) + }) + return { + dispose() { + if (disposed) + return + disposed = true + }, + } + } + } + else { + runWhenIdle = (runner, timeout?) => { + const handle: number = requestIdleCallback(runner, typeof timeout === 'number' ? { timeout } : undefined) + let disposed = false + return { + dispose() { + if (disposed) + return + + disposed = true + cancelIdleCallback(handle) + }, + } + } + } +})()