diff --git a/packages/core/src/compiler/htmlDocument.test.ts b/packages/core/src/compiler/htmlDocument.test.ts index 3c0c1a7331e..466d993e020 100644 --- a/packages/core/src/compiler/htmlDocument.test.ts +++ b/packages/core/src/compiler/htmlDocument.test.ts @@ -8,6 +8,7 @@ import { injectScriptsIntoHtml, injectTagsAtHeadStart, insertBeforeCloseTag, + insertRuntimeTag, hasCompositionOutsideTemplates, isFullHtmlDocument, parseHTMLContent, @@ -454,3 +455,29 @@ describe("hasCompositionOutsideTemplates", () => { expect(hasCompositionOutsideTemplates(html)).toBe(expected); }); }); + +describe("insertRuntimeTag", () => { + const runtime = ''; + const page = (attrs: string) => + `
`; + + it("puts the canvas tracker ahead of author head scripts for a page that opts into blur", () => { + const out = insertRuntimeTag(page('data-motion-blur="on"'), runtime); + const tracker = out.indexOf("data-hf-accel-tracker"); + expect(tracker).toBeGreaterThan(-1); + expect(tracker).toBeLessThan(out.indexOf("window.author")); + expect(out.indexOf("window.author")).toBeLessThan(out.indexOf(runtime)); + }); + + it("adds the tracker once, however many times the runtime is placed", () => { + const once = insertRuntimeTag(page('data-motion-blur="on"'), runtime); + const twice = insertRuntimeTag(once, runtime); + expect(twice.split("data-hf-accel-tracker").length).toBe(2); + }); + + it("leaves pages that did not opt in alone", () => { + for (const attrs of ["", 'data-motion-blur="off"', 'data-motion-blur-shutter="on"']) { + expect(insertRuntimeTag(page(attrs), runtime)).not.toContain("data-hf-accel-tracker"); + } + }); +}); diff --git a/packages/core/src/compiler/htmlDocument.ts b/packages/core/src/compiler/htmlDocument.ts index a1861a7d289..d0709058203 100644 --- a/packages/core/src/compiler/htmlDocument.ts +++ b/packages/core/src/compiler/htmlDocument.ts @@ -1,3 +1,4 @@ +import { acceleratedCanvasTrackerSource } from "../pageMotionBlur.js"; import { parseHTML } from "linkedom"; export const RUNTIME_BOOTSTRAP_ATTR = "data-hyperframes-preview-runtime"; @@ -347,7 +348,7 @@ export function insertBeforeCloseTag( return insertBeforeDocumentTag(html, `${name}`, markup); } -export function insertRuntimeTag(html: string, tag: string): string { +function insertTagInHead(html: string, tag: string): string { const withHead = insertBeforeCloseTag(html, "head", `${tag}\n`); if (withHead !== null) return withHead; const htmlOpenMatch = html.match(/]*>/i); @@ -363,6 +364,16 @@ export function insertRuntimeTag(html: string, tag: string): string { return tag + html; } +const ACCEL_TRACKER_ATTR = "data-hf-accel-tracker"; +const OPTS_INTO_MOTION_BLUR = /\sdata-motion-blur\s*=\s*["']?on[\s"'>]/i; + +export function insertRuntimeTag(html: string, tag: string): string { + const withRuntime = insertTagInHead(html, tag); + if (!OPTS_INTO_MOTION_BLUR.test(html) || html.includes(ACCEL_TRACKER_ATTR)) return withRuntime; + const tracker = ``; + return injectTagsAtHeadStart(withRuntime, tracker); +} + /** * Insert raw tag markup at the very start of ``, ahead of every author * script (inline or external). Falls back to just before ``, then to the diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index 75ead8efa5f..3cee99b92f7 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -437,4 +437,8 @@ export { type HfVfxParam, type HfVfxParamValues, } from "./vfx.js"; -export { PAGE_DEVICE_SCALE_GLOBAL, PAGE_MOTION_BLUR_OFF_GLOBAL } from "./pageMotionBlur.js"; +export { + PAGE_DEVICE_SCALE_GLOBAL, + PAGE_MOTION_BLUR_OFF_GLOBAL, + acceleratedCanvasTrackerSource, +} from "./pageMotionBlur.js"; diff --git a/packages/core/src/pageMotionBlur.test.ts b/packages/core/src/pageMotionBlur.test.ts new file mode 100644 index 00000000000..9c7e1e62cf4 --- /dev/null +++ b/packages/core/src/pageMotionBlur.test.ts @@ -0,0 +1,59 @@ +// @vitest-environment jsdom +import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; +import { + acceleratedCanvasTrackerSource, + installAcceleratedCanvasTracker, + isAcceleratedCanvas, +} from "./pageMotionBlur"; + +type Proto = Record