From 9a05f6687c14c7464ecb378575f7bafcfc86b52d Mon Sep 17 00:00:00 2001 From: Sean Doyle Date: Thu, 14 Sep 2023 13:55:51 -0400 Subject: [PATCH] Support `FrameElement.reload()` without an initial `[src]` attribute The problem --- If a `` element is rendered without a `[src]` attribute, calls to `.reload()` will have no effect. If a `` is to be its own browsing context, it should be able to apply its current location (that is, it's owning document's current location) to its browsing context. For example, if a page has a `` element that contains text that's typically updated by a Web Socket-delivered ``, it might be useful to [gracefully degrade][] to periodic long-polling if that Web Socket connection were to fail. That might involve something like a `reload` Stimulus controller with a delay: ```html

This data will refresh every 30 seconds

``` The fact that the `` element doesn't have a `[src]` attribute shouldn't prevent the page from being able to re-fetch its content. The solution --- When `FrameElement.reload()` is invoked, it delegates to its delegate instance's `sourceURLReloaded()` method. In all cases, `FrameElement.delegate` is an instance of `FrameController`. This commit extends the `FrameController.sourceURLReloaded()` implementation to set the element's `[src]` attribute to the element's [baseURI][] value, which sets off the usual attribute change listeners and `` navigation logic. [baseURI]: https://developer.mozilla.org/en-US/docs/Web/API/Node/baseURI [gracefully degrade]: https://developer.mozilla.org/en-US/docs/Glossary/Graceful_degradation --- src/core/frames/frame_controller.js | 2 +- src/tests/functional/frame_tests.js | 34 +++++++++++++++++++++++++++-- 2 files changed, 33 insertions(+), 3 deletions(-) diff --git a/src/core/frames/frame_controller.js b/src/core/frames/frame_controller.js index 5940ba761..ea56aeb55 100644 --- a/src/core/frames/frame_controller.js +++ b/src/core/frames/frame_controller.js @@ -94,7 +94,7 @@ export class FrameController { this.element.removeAttribute("complete") }) this.element.src = null - this.element.src = src + this.element.src = src || this.element.baseURI return this.element.loaded } diff --git a/src/tests/functional/frame_tests.js b/src/tests/functional/frame_tests.js index b090aff8a..610e8c232 100644 --- a/src/tests/functional/frame_tests.js +++ b/src/tests/functional/frame_tests.js @@ -515,7 +515,7 @@ test("navigating a frame targeting _top from an outer link fires events", async assert.equal(otherEvents.length, 0, "no more events") }) -test("invoking .reload() re-fetches the frame's content", async ({ page }) => { +test("invoking .reload() re-fetches the content of a element with a [src] attribute", async ({ page }) => { await page.click("#link-frame") await nextEventOnTarget(page, "frame", "turbo:frame-load") await page.evaluate(() => document.getElementById("frame").reload()) @@ -523,7 +523,37 @@ test("invoking .reload() re-fetches the frame's content", async ({ page }) => { const dispatchedEvents = await readEventLogs(page) assert.deepEqual( - dispatchedEvents.map(([name, _, id]) => [id, name]), + dispatchedEvents + .map(([name, _, id]) => [id, name]) + .filter(([id]) => id === "frame"), + [ + ["frame", "turbo:before-fetch-request"], + ["frame", "turbo:before-fetch-response"], + ["frame", "turbo:before-frame-render"], + ["frame", "turbo:frame-render"], + ["frame", "turbo:frame-load"] + ] + ) +}) + +test("invoking .reload() re-fetches the content of a element without a [src] attribute", async ({ page }) => { + const frame = await page.locator("turbo-frame#frame") + const heading = await frame.locator("h2") + + assert.match(await heading.textContent(), /Frames: #frame/) + + await heading.evaluate((element) => element.textContent = "Not yet refreshed") + + assert.match(await heading.textContent(), /Not yet refreshed/) + + await frame.evaluate((element) => element.reload()) + + const dispatchedEvents = await readEventLogs(page) + + assert.deepEqual( + dispatchedEvents + .map(([name, _, id]) => [id, name]) + .filter(([id]) => id === "frame"), [ ["frame", "turbo:before-fetch-request"], ["frame", "turbo:before-fetch-response"],