diff --git a/src/core/drive/page_snapshot.ts b/src/core/drive/page_snapshot.ts index 8b0172e7b..4da80ca31 100644 --- a/src/core/drive/page_snapshot.ts +++ b/src/core/drive/page_snapshot.ts @@ -56,17 +56,21 @@ export class PageSnapshot extends Snapshot { } get isPreviewable() { - return this.cacheControlValue != "no-preview" + return this.cacheControlValue != "no-preview" && !this.prefersViewTransitions } get isCacheable() { - return this.cacheControlValue != "no-cache" + return this.cacheControlValue != "no-cache" && !this.prefersViewTransitions } get isVisitable() { return this.getSetting("visit-control") != "reload" } + get prefersViewTransitions() { + return this.headSnapshot.getMetaValue("view-transition") === "same-origin" + } + // Private getSetting(name: string) { diff --git a/src/core/drive/page_view.ts b/src/core/drive/page_view.ts index 80635ab38..632b4665e 100644 --- a/src/core/drive/page_view.ts +++ b/src/core/drive/page_view.ts @@ -4,6 +4,7 @@ import { ErrorRenderer } from "./error_renderer" import { PageRenderer } from "./page_renderer" import { PageSnapshot } from "./page_snapshot" import { SnapshotCache } from "./snapshot_cache" +import { withViewTransition } from "./view_transitions" import { Visit } from "./visit" export type PageViewRenderOptions = ViewRenderOptions @@ -20,13 +21,16 @@ export class PageView extends View this.render(renderer)) } renderError(snapshot: PageSnapshot, visit?: Visit) { diff --git a/src/core/drive/view_transitions.ts b/src/core/drive/view_transitions.ts new file mode 100644 index 000000000..1f6c0f89d --- /dev/null +++ b/src/core/drive/view_transitions.ts @@ -0,0 +1,17 @@ +declare global { + type ViewTransition = { + finished: Promise + } + + interface Document { + startViewTransition?(callback: () => void): ViewTransition + } +} + +export function withViewTransition(shouldTransition: boolean, callback: () => Promise): Promise { + if (shouldTransition && document.startViewTransition) { + return document.startViewTransition(callback).finished + } else { + return callback() + } +} diff --git a/src/tests/fixtures/transitions/left.html b/src/tests/fixtures/transitions/left.html new file mode 100644 index 000000000..b45a070b1 --- /dev/null +++ b/src/tests/fixtures/transitions/left.html @@ -0,0 +1,31 @@ + + + + + Left + + + + + + + +

Left

+

go right

+
+

go other

+ + diff --git a/src/tests/fixtures/transitions/other.html b/src/tests/fixtures/transitions/other.html new file mode 100644 index 000000000..2310d8fcc --- /dev/null +++ b/src/tests/fixtures/transitions/other.html @@ -0,0 +1,13 @@ + + + + + Other + + + + +

Other

+

go left

+ + diff --git a/src/tests/fixtures/transitions/right.html b/src/tests/fixtures/transitions/right.html new file mode 100644 index 000000000..a986bc777 --- /dev/null +++ b/src/tests/fixtures/transitions/right.html @@ -0,0 +1,30 @@ + + + + + Right + + + + + + + +

Right

+

go left

+
+ + diff --git a/src/tests/functional/drive_view_transition_tests.ts b/src/tests/functional/drive_view_transition_tests.ts new file mode 100644 index 000000000..e4c9f48fd --- /dev/null +++ b/src/tests/functional/drive_view_transition_tests.ts @@ -0,0 +1,30 @@ +import { test } from "@playwright/test" +import { assert } from "chai" +import { nextBody } from "../helpers/page" + +test.beforeEach(async ({ page }) => { + await page.goto("/src/tests/fixtures/transitions/left.html") + + await page.evaluate(` + document.startViewTransition = (callback) => { + window.startViewTransitionCalled = true + callback() + } + `) +}) + +test("navigating triggers the view transition", async ({ page }) => { + await page.locator("#go-right").click() + await nextBody(page) + + const called = await page.evaluate(`window.startViewTransitionCalled`) + assert.isTrue(called) +}) + +test("navigating does not trigger a view transition when meta tag not present", async ({ page }) => { + await page.locator("#go-other").click() + await nextBody(page) + + const called = await page.evaluate(`window.startViewTransitionCalled`) + assert.isUndefined(called) +})