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

Re-introduce #627 #711

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft
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
39 changes: 33 additions & 6 deletions src/core/drive/page_renderer.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,22 @@
import { Renderer } from "../renderer"
import { PageSnapshot } from "./page_snapshot"
import { ReloadReason } from "../native/browser_adapter"
import { activateScriptElement, waitForLoad } from "../../util"
import { activateScriptElement, waitForLoad, getBodyElementId } from "../../util"

export class PageRenderer extends Renderer<HTMLBodyElement, PageSnapshot> {
static renderElement(currentElement: HTMLBodyElement, newElement: HTMLBodyElement) {
if (document.body && newElement instanceof HTMLBodyElement) {
document.body.replaceWith(newElement)
static renderElement(currentBody: HTMLBodyElement, newBody: HTMLBodyElement) {
if (document.body && newBody instanceof HTMLBodyElement) {
const currentElement = PageRenderer.getRenderedElement(currentBody) || currentBody
const newElement = PageRenderer.getRenderedElement(newBody) || newBody

currentElement.replaceWith(newElement)
} else {
document.documentElement.appendChild(newElement)
document.documentElement.appendChild(newBody)
}
}

get shouldRender() {
return this.newSnapshot.isVisitable && this.trackedElementsAreIdentical
return this.newSnapshot.isVisitable && this.trackedElementsAreIdentical && this.renderedElementMatches
}

get reloadReason(): ReloadReason {
Expand All @@ -28,6 +31,12 @@ export class PageRenderer extends Renderer<HTMLBodyElement, PageSnapshot> {
reason: "tracked_element_mismatch",
}
}

if (!this.renderedElementMatches) {
return {
reason: "rendered_element_mismatch",
}
}
}

async prepareToRender() {
Expand Down Expand Up @@ -78,6 +87,24 @@ export class PageRenderer extends Renderer<HTMLBodyElement, PageSnapshot> {
return this.currentHeadSnapshot.trackedElementSignature == this.newHeadSnapshot.trackedElementSignature
}

get renderedElementMatches() {
return PageRenderer.getRenderedElement(this.newElement) !== null
}

static get bodySelector() {
const bodyId = getBodyElementId()

return bodyId ? `#${bodyId}` : "body"
}

static getRenderedElement(element: HTMLElement): HTMLElement | null {
if (element.matches(this.bodySelector)) {
return element
} else {
return element.querySelector(this.bodySelector)
}
}

async copyNewHeadStylesheetElements() {
const loadingElements = []

Expand Down
4 changes: 1 addition & 3 deletions src/core/drive/page_view.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { nextEventLoopTick } from "../../util"
import { View, ViewDelegate, ViewRenderOptions } from "../view"
import { ErrorRenderer } from "./error_renderer"
import { PageRenderer } from "./page_renderer"
Expand Down Expand Up @@ -39,11 +38,10 @@ export class PageView extends View<HTMLBodyElement, PageSnapshot, PageViewRender
this.snapshotCache.clear()
}

async cacheSnapshot() {
cacheSnapshot() {
if (this.shouldCacheSnapshot) {
this.delegate.viewWillCacheSnapshot()
const { snapshot, lastRenderedLocation: location } = this
await nextEventLoopTick()
const cachedSnapshot = snapshot.clone()
this.snapshotCache.put(location, cachedSnapshot)
return cachedSnapshot
Expand Down
3 changes: 2 additions & 1 deletion src/core/drive/visit.ts
Original file line number Diff line number Diff line change
Expand Up @@ -465,7 +465,8 @@ export class Visit implements FetchRequestDelegate {

cacheSnapshot() {
if (!this.snapshotCached) {
this.view.cacheSnapshot().then((snapshot) => snapshot && this.visitCachedSnapshot(snapshot))
const snapshot = this.view.cacheSnapshot()
if (snapshot) this.visitCachedSnapshot(snapshot)
this.snapshotCached = true
}
}
Expand Down
21 changes: 21 additions & 0 deletions src/tests/fixtures/drive_custom_body.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="turbo-body" content="app">
<title>Drive (with custom body)</title>
<script src="/dist/turbo.es2017-umd.js" data-turbo-track="reload"></script>
<script src="/src/tests/fixtures/test.js"></script>
</head>
<body>
<h1>Drive (with custom body)</h1>
<div id="app">
<div>
<a id="drive" href="/src/tests/fixtures/drive_custom_body_2.html">Drive enabled link</a>
<a id="mismatch" href="/src/tests/fixtures/drive_custom_body_3.html">Drive enabled link to page with mismatched custom body</a>
</div>

<p id="different-content">Drive 1</p>
</div>
</body>
</html>
21 changes: 21 additions & 0 deletions src/tests/fixtures/drive_custom_body_2.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="turbo-body" content="app">
<title>Drive (with custom body)</title>
<script src="/dist/turbo.es2017-umd.js" data-turbo-track="reload"></script>
<script src="/src/tests/fixtures/test.js"></script>
</head>
<body>
<h1>Drive (with custom body 2)</h1>

<div id="app">
<div>
<a id="drive" href="/src/tests/fixtures/drive_custom_body.html">Drive enabled link</a>
</div>

<p id="different-content">Drive 2</p>
</div>
</body>
</html>
15 changes: 15 additions & 0 deletions src/tests/fixtures/drive_custom_body_3.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="turbo-body" content="main">
<title>Drive (with custom body)</title>
<script src="/dist/turbo.es2017-umd.js" data-turbo-track="reload"></script>
<script src="/src/tests/fixtures/test.js"></script>
</head>
<body>
<main id="main">
<h1>Drive (with custom body 3)</h1>
</main>
</body>
</html>
40 changes: 40 additions & 0 deletions src/tests/functional/drive_custom_body_tests.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { test } from "@playwright/test"
import { assert } from "chai"
import { nextEventNamed, pathname, getFromLocalStorage, setLocalStorageFromEvent } from "../helpers/page"

test.beforeEach(async ({ page }) => {
await page.goto("/src/tests/fixtures/drive_custom_body.html")
})

test("test drive with a custom body element", async ({ page }) => {
await page.click("#drive")
await nextEventNamed(page, "turbo:load")

assert.equal(pathname(page.url()), "/src/tests/fixtures/drive_custom_body_2.html")
assert.equal(await page.textContent("h1"), "Drive (with custom body)")
assert.equal(await page.textContent("#different-content"), "Drive 2")

await page.goBack()
await nextEventNamed(page, "turbo:load")

assert.equal(pathname(page.url()), "/src/tests/fixtures/drive_custom_body.html")
assert.equal(await page.textContent("h1"), "Drive (with custom body)")
assert.equal(await page.textContent("#different-content"), "Drive 1")

await page.goForward()
await nextEventNamed(page, "turbo:load")

assert.equal(pathname(page.url()), "/src/tests/fixtures/drive_custom_body_2.html")
assert.equal(await page.textContent("h1"), "Drive (with custom body)")
assert.equal(await page.textContent("#different-content"), "Drive 2")
})

test("test drive with mismatched custom body elements", async ({ page }) => {
await setLocalStorageFromEvent(page, "turbo:reload", "reloaded", "true")
await page.click("#mismatch")
await page.waitForEvent("load")

assert.equal(await page.textContent("h1"), "Drive (with custom body 3)")
assert.equal(await getFromLocalStorage(page, "reloaded"), "true", "dispatches turbo:reload event")
assert.equal(pathname(page.url()), "/src/tests/fixtures/drive_custom_body_3.html")
})
4 changes: 4 additions & 0 deletions src/util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,10 @@ export function getVisitAction(...elements: (Element | undefined)[]): Action | n
return isAction(action) ? action : null
}

export function getBodyElementId(): string | null {
return getMetaContent("turbo-body")
}

export function getMetaElement(name: string): HTMLMetaElement | null {
return document.querySelector(`meta[name="${name}"]`)
}
Expand Down