diff --git a/src/core/drive/prefetch_cache.js b/src/core/drive/prefetch_cache.js index ecca297b7..79b7331d1 100644 --- a/src/core/drive/prefetch_cache.js +++ b/src/core/drive/prefetch_cache.js @@ -10,14 +10,16 @@ class PrefetchCache { } } - setLater(url, request, ttl) { + setLater(url, request, ttl, delay) { this.clear() - this.#prefetchTimeout = setTimeout(() => { - request.perform() - this.set(url, request, ttl) - this.#prefetchTimeout = null - }, PREFETCH_DELAY) + if (delay === "0") { + this.#setNow(url, request, ttl) + } else { + delay = Number(delay) || PREFETCH_DELAY + + this.#enqueue(url, request, ttl, delay) + } } set(url, request, ttl) { @@ -28,6 +30,18 @@ class PrefetchCache { if (this.#prefetchTimeout) clearTimeout(this.#prefetchTimeout) this.#prefetched = null } + + #enqueue(url, request, ttl, delay) { + this.#prefetchTimeout = setTimeout(() => { + this.#setNow(url, request, ttl) + this.#prefetchTimeout = null + }, delay) + } + + #setNow(url, request, ttl) { + request.perform() + this.set(url, request, ttl) + } } export const cacheTtl = 10 * 1000 diff --git a/src/observers/link_prefetch_observer.js b/src/observers/link_prefetch_observer.js index 6265f075a..f3f100fa8 100644 --- a/src/observers/link_prefetch_observer.js +++ b/src/observers/link_prefetch_observer.js @@ -78,7 +78,9 @@ export class LinkPrefetchObserver { target ) - prefetchCache.setLater(location.toString(), fetchRequest, this.#cacheTtl) + const delay = link.dataset.turboPrefetchDelay || getMetaContent("turbo-prefetch-delay") + + prefetchCache.setLater(location.toString(), fetchRequest, this.#cacheTtl, delay) } } } diff --git a/src/tests/fixtures/hover_to_prefetch.html b/src/tests/fixtures/hover_to_prefetch.html index d47be49db..93d34ec62 100644 --- a/src/tests/fixtures/hover_to_prefetch.html +++ b/src/tests/fixtures/hover_to_prefetch.html @@ -14,6 +14,9 @@ Hover to prefetch me Hover to prefetch me + Hover to prefetch me + Hover to prefetch me + Hover to prefetch me
Won't prefetch when hovering me
diff --git a/src/tests/fixtures/hover_to_prefetch_with_delay_on_meta_tag.html b/src/tests/fixtures/hover_to_prefetch_with_delay_on_meta_tag.html new file mode 100644 index 000000000..2efc23882 --- /dev/null +++ b/src/tests/fixtures/hover_to_prefetch_with_delay_on_meta_tag.html @@ -0,0 +1,14 @@ + + + + + Hover to Prefetch + + + + + + + Hover to prefetch me + + diff --git a/src/tests/fixtures/hover_to_prefetch_with_invalid_delay_on_meta_tag.html b/src/tests/fixtures/hover_to_prefetch_with_invalid_delay_on_meta_tag.html new file mode 100644 index 000000000..3a6866769 --- /dev/null +++ b/src/tests/fixtures/hover_to_prefetch_with_invalid_delay_on_meta_tag.html @@ -0,0 +1,14 @@ + + + + + Hover to Prefetch + + + + + + + Hover to prefetch me + + diff --git a/src/tests/fixtures/hover_to_prefetch_with_zero_delay_on_meta_tag.html b/src/tests/fixtures/hover_to_prefetch_with_zero_delay_on_meta_tag.html new file mode 100644 index 000000000..0803b5a65 --- /dev/null +++ b/src/tests/fixtures/hover_to_prefetch_with_zero_delay_on_meta_tag.html @@ -0,0 +1,14 @@ + + + + + Hover to Prefetch + + + + + + + Hover to prefetch me + + diff --git a/src/tests/functional/link_prefetch_observer_tests.js b/src/tests/functional/link_prefetch_observer_tests.js index 735efb7fb..4d814fe5f 100644 --- a/src/tests/functional/link_prefetch_observer_tests.js +++ b/src/tests/functional/link_prefetch_observer_tests.js @@ -208,6 +208,93 @@ test("it prefetches links with a delay", async ({ page }) => { assertRequestMade(requestMade) }) +test("it allows to customize the delay with a data-turbo-prefetch-delay attribute", async ({ page }) => { + await goTo({ page, path: "/hover_to_prefetch.html" }) + + let requestMade = false + page.on("request", async (request) => (requestMade = true)) + + await page.hover("#anchor_with_custom_delay") + await sleep(200) + + assertRequestNotMade(requestMade) + + await sleep(150) + + assertRequestMade(requestMade) +}) + +test("it allows to customize the delay with a turbo-prefetch-delay a meta tag", async ({ page }) => { + await goTo({ page, path: "/hover_to_prefetch_with_delay_on_meta_tag.html" }) + + let requestMade = false + page.on("request", async (request) => (requestMade = true)) + + await page.hover("#anchor_for_prefetch") + + await sleep(200) + + assertRequestNotMade(requestMade) + + await sleep(150) + + assertRequestMade(requestMade) +}) + +test("it prefetches immediately with a data-turbo-prefetch-delay attribute set to 0", async ({ page }) => { + await goTo({ page, path: "/hover_to_prefetch.html" }) + + let requestMade = false + page.on("request", async (request) => (requestMade = true)) + + await page.hover("#anchor_with_zero_delay") + + assertRequestMade(requestMade) +}) + +test("it prefetches immediately with a turbo-prefetch-delay meta tag set to 0", async ({ page }) => { + await goTo({ page, path: "/hover_to_prefetch_with_zero_delay_on_meta_tag.html" }) + + let requestMade = false + page.on("request", async (request) => (requestMade = true)) + + await page.hover("#anchor_for_prefetch") + + assertRequestMade(requestMade) +}) + +test("it uses the default delay with a data-turbo-prefetch-delay attribute set to an invalid value", async ({ page }) => { + await goTo({ page, path: "/hover_to_prefetch.html" }) + + let requestMade = false + page.on("request", async (request) => (requestMade = true)) + + await page.hover("#anchor_with_invalid_delay") + await sleep(75) + + assertRequestNotMade(requestMade) + + await sleep(100) + + assertRequestMade(requestMade) +}) + +test("it uses the default delay with a turbo-prefetch-delay meta tag set to an invalid value", async ({ page }) => { + await goTo({ page, path: "/hover_to_prefetch_with_invalid_delay_on_meta_tag.html" }) + + let requestMade = false + page.on("request", async (request) => (requestMade = true)) + + await page.hover("#anchor_for_prefetch") + await sleep(75) + + assertRequestNotMade(requestMade) + + await sleep(100) + + assertRequestMade(requestMade) +}) + test("it cancels the prefetch request if the link is no longer hovered", async ({ page }) => { await goTo({ page, path: "/hover_to_prefetch.html" })