Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use speculation rules prefetch/prerender when available #126

Merged
merged 1 commit into from
Mar 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 40 additions & 3 deletions instantpage.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
/*! instant.page v5.2.0 - (C) 2019-2023 Alexandre Dieulot - https://instant.page/license */

let _chromiumMajorVersionInUserAgent = null
, _speculationRulesType
, _allowQueryString
, _allowExternalLinks
, _useWhitelist
Expand Down Expand Up @@ -53,6 +54,16 @@ function init() {
return
}

_speculationRulesType = 'none'
if (HTMLScriptElement.supports && HTMLScriptElement.supports('speculationrules')) {
const speculationRulesConfig = document.body.dataset.instantSpecrules;
if (speculationRulesConfig == 'prerender') {
_speculationRulesType = 'prerender'
} else if (speculationRulesConfig != 'no') {
_speculationRulesType = 'prefetch'
}
}

const mousedownShortcut = 'instantMousedownShortcut' in document.body.dataset
_allowQueryString = 'instantAllowQueryString' in document.body.dataset
_allowExternalLinks = 'instantAllowExternalLinks' in document.body.dataset
Expand Down Expand Up @@ -264,7 +275,7 @@ function isPreloadable(anchorElement) {
if (anchorElement.origin != location.origin) {
let allowed = _allowExternalLinks || 'instant' in anchorElement.dataset
if (!allowed || !_chromiumMajorVersionInUserAgent) {
// Chromium-only: see comment on “restrictive prefetch”
// Chromium-only: see comment on “restrictive prefetch” and “cross-site speculation rules prefetch”
return
}
}
Expand Down Expand Up @@ -297,6 +308,34 @@ function preload(url, fetchPriority = 'auto') {
return
}

if (_speculationRulesType != 'none') {
preloadUsingSpeculationRules(url)
} else {
preloadUsingLinkElement(url, fetchPriority)
}

_preloadedList.add(url)
}

function preloadUsingSpeculationRules(url) {
const scriptElement = document.createElement('script')
scriptElement.type = 'speculationrules'

scriptElement.textContent = JSON.stringify({
[_speculationRulesType]: [{
source: 'list',
urls: [url]
}]
})

// When using speculation rules, cross-site prefetch is supported, but will
// only work if the user has no cookies for the destination site. The
// prefetch will not be sent, if the user does have such cookies.

document.head.appendChild(scriptElement)
}

function preloadUsingLinkElement(url, fetchPriority = 'auto') {
const linkElement = document.createElement('link')
linkElement.rel = 'prefetch'
linkElement.href = url
Expand Down Expand Up @@ -325,6 +364,4 @@ function preload(url, fetchPriority = 'auto') {
// event, but might be bad when prefetching every link in the viewport.

document.head.appendChild(linkElement)

_preloadedList.add(url)
}
4 changes: 3 additions & 1 deletion test/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,9 @@ function init() {
}

async function requestListener(req, res) {
const isPrefetched = req.headers['x-moz'] == 'prefetch' /* Firefox 109 */ || req.headers['purpose'] == 'prefetch' /* Chrome 110 & Safari 16.3 */
const isPrefetched = req.headers['x-moz'] == 'prefetch' /* Firefox 109 */ ||
req.headers['purpose'] == 'prefetch' /* Chrome 110 & Safari 16.3 */ ||
req.headers['sec-purpose'].startsWith('prefetch') /* Chrome 110 speculation rules */
const prefetchIndicator = isPrefetched ? 'PF' : ' F'
const type = req.headers['sec-fetch-dest'] ? req.headers['sec-fetch-dest'].toUpperCase()[0] : '.'
const spaces = ' '.repeat(Math.max(0, 16 - req.url.length))
Expand Down
1 change: 1 addition & 0 deletions test/tests/hover-long-enough-then-click/2.html
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({
transferSize: navigationPerformanceEntry.transferSize,
deliveryType: navigationPerformanceEntry.deliveryType,
}),
})

Expand Down
2 changes: 1 addition & 1 deletion test/tests/hover-long-enough-then-click/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,5 @@ export const environment = {
}

export function checkExpectation(data) {
return data.transferSize === 0
return data.transferSize === 0 || data.deliveryType === 'navigational-prefetch'
}
1 change: 1 addition & 0 deletions test/tests/no-double-download/2.html
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({
transferSize: navigationPerformanceEntry.transferSize,
deliveryType: navigationPerformanceEntry.deliveryType,
}),
})

Expand Down
2 changes: 1 addition & 1 deletion test/tests/no-double-download/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,5 @@ export const environment = {
}

export function checkExpectation(data) {
return data.transferSize === 0
return data.transferSize === 0 || data.deliveryType === 'navigational-prefetch'
}