Every interactive instrument's iframe stays in memory after it closes, retained by a theme observer on the parent document
Los mantenedores suelen responder en 3 días
Nadie ha tomado este issue todavía.
Evaluación
- Dificultad
- 2/5
- Tiempo estimado
- 1-3 horas
- Aptitud para principiantes
- 83/100
- Tipo de issue
- Error
- Claridad
- Bien especificado
- Estado de actividad
- Activo
- Stack tecnológico
- typescript
- Área
- frontend, performance, testing
Línea de trabajo
Lee packages/runtime-internal/src/interactive/bootstrap.js:18-35 y la prueba indicada en packages/runtime-internal/src/interactive/__tests__/bootstrap.test.ts. Empieza ejecutando esa prueba y comprobando cómo prepara el documento padre simulado y envía pagehide; el objetivo se cumple cuando el observador deja de reflejar el tema del documento padre después de pagehide. El issue también propone una comprobación de extremo a extremo en testing/src/specs/instrument-completion.spec.ts, pero no la exige para verificar el comportamiento de la prueba unitaria.
Escrito por el modelo de indexación a partir del texto del issue.
Descripción
Every interactive instrument's iframe stays in memory after it is removed, until the host page is reloaded. bootstrap.js runs inside the iframe and registers a MutationObserver on the parent's <html> element to mirror the theme, and nothing ever calls disconnect(). Per the DOM spec a node holds a strong reference to the observers registered on it. So the parent's <html>, which lives for the whole SPA session, keeps the iframe's observer alive. The observer keeps its callback alive, and the callback is a closure in the iframe's realm (it reads the iframe's document), so the iframe's whole global stays reachable. That global holds every module the task loaded (jsPsych, React, the instrument, its data URL assets) and, through frameElement, the iframe element with the full bundle in data-bundle. In a clinic session that administers several interactive tasks, or a series with interactive items, each finished task's memory accumulates. On the tablets used for remote and in-person sessions this grows until the tab is reloaded or killed.
Where
packages/runtime-internal/src/interactive/bootstrap.js:18-35:
// Synchronize the iframe theme with the parent document
const observer = new MutationObserver((mutations) => {
mutations.forEach((mutation) => {
if (mutation.attributeName === THEME_ATTRIBUTE) {
// ...
document.documentElement.setAttribute(THEME_ATTRIBUTE, updatedTheme);
// ...
});
observer.observe(parent.document.documentElement, {
attributes: true
});
Reproduce
I confirmed this in Chromium (Playwright's bundled build) with a parent page that mounts and removes five same-origin iframes. Each iframe runs the observer code above verbatim and stores a 2M-element array on its global, and the parent keeps a WeakRef to that array. After the iframes were removed I forced GC with HeapProfiler.collectGarbage:
- without the observer: 0 of 5 iframe globals alive, heap 0.6 MB
- with the observer as written: 5 of 5 iframe globals alive, heap 42.3 MB
- with the observer plus
addEventListener('pagehide', () => observer.disconnect()): 0 of 5 alive
In the app:
- In
apps/web, start a session and complete an interactive instrument (for example the Breakout Task). Then complete another one, or a series with two interactive items. - In DevTools > Memory, take a heap snapshot after each one and filter for
WindoworDetached.
Actual: each completed task leaves a detached iframe Window behind, retained through MutationObserver from the parent's HTMLHtmlElement, and heap usage grows with every task.
Expected: once its iframe is removed, nothing keeps an interactive instrument's realm alive.
Tests
packages/runtime-internal/src/interactive/__tests__/bootstrap.test.ts: it('should stop observing the parent theme on pagehide, so a removed iframe is not kept alive by the parent document'). Bootstrap against the existing fake parent document, dispatch pagehide on window, change the parent's data-mode, and assert that the iframe's data-mode no longer follows it.
e2e, in testing/src/specs/instrument-completion.spec.ts: after completing an interactive instrument, assert through CDP (HeapProfiler.collectGarbage, then Runtime.queryObjects on Window or a WeakRef probe) that the removed iframe's window has been collected.
Suggested fix
In bootstrap.js, add addEventListener('pagehide', () => observer.disconnect(), { once: true }). pagehide fires in the iframe when its element is removed. Also pass attributeFilter: [THEME_ATTRIBUTE] to observe, so the callback stops running on every other attribute change on the parent's <html>.
- Lenguaje dominante
- TypeScript
- Estrellas
- 119
- Forks
- 19
- Merge medio
- 5 d 18 h
- PR fusionados (30 d)
- 38
Preparar el entorno
- Incluye un Dockerfile o un archivo de Docker Compose
- Sin plantilla de pull request
- Leer la guía de contribución
Primeros pasos
- Lee el issue completo y luego la guía de contribución del proyecto.
- Comenta en el issue que vas a ocuparte — evita que dos personas hagan lo mismo.
- Haz un fork del repositorio y trabaja en una rama.
- Abre un pull request que haga referencia al número del issue.
Más de DouglasNeuroInformatics/OpenDataCapture
-
Area: Infrastructure Chore Difficulty: Low Priority: Medium
Dificultad 2/5 1-3 horas Aptitud para principiantes 70/100
DouglasNeuroInformatics/OpenDataCapture#1835 ·
Los mantenedores suelen responder en 3 días
-
Area: Instruments Bug Difficulty: Low Priority: Low
Dificultad 2/5 1-3 horas Aptitud para principiantes 78/100
DouglasNeuroInformatics/OpenDataCapture#1824 · 1 comentario ·
Los mantenedores suelen responder en 3 días
-
Area: Shared Bug Difficulty: Low Priority: Low
Dificultad 2/5 1-3 horas Aptitud para principiantes 70/100
DouglasNeuroInformatics/OpenDataCapture#1813 · 1 comentario ·
Los mantenedores suelen responder en 3 días
-
Multiple instructions show as one comma-joined paragraph ("questions., Conditional")Posiblemente ocupada @Sanilas-ai la tomó hace 5 días. AbiertoArea: Shared Bug Difficulty: Low Good First Issue Priority: Low
Dificultad 2/5 1-3 horas Aptitud para principiantes 78/100
DouglasNeuroInformatics/OpenDataCapture#1812 · 1 comentario ·
Los mantenedores suelen responder en 3 días
-
Area: Instruments Bug Difficulty: Low Priority: Low
Dificultad 2/5 1-3 horas Aptitud para principiantes 88/100
DouglasNeuroInformatics/OpenDataCapture#1801 · 1 comentario ·
Los mantenedores suelen responder en 3 días
Todos los issues de DouglasNeuroInformatics/OpenDataCapture
Issues similares
-
Chip source filter and denial markers miss Storage rows when a Storage activity source is pickedAbiertobug
Dificultad 2/5 1-3 horas Aptitud para principiantes 78/100
Los mantenedores suelen responder en 1 día
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 77/100
omdsh-dev/DSH-better-sidebar#1141 ·
Los mantenedores suelen responder en 2 días
-
desktop linux photos
Dificultad 2/5 1-3 horas Aptitud para principiantes 78/100
Los mantenedores suelen responder en 1 día
-
Cleanup SetupPosiblemente ocupada @AlexanderKaran la tomó hoy. Abiertodocumentation enhancement
Dificultad 2/5 1-3 horas Aptitud para principiantes 64/100
e18e/framework-tracker#769 ·
Los mantenedores suelen responder en 1 día
-
fix(docker): correct stale --db-path comments in fetch/git Dockerfiles and clarify git READMEAbiertobug v2
Dificultad 1/5 Menos de una hora Aptitud para principiantes 88/100
modelcontextprotocol/servers#5483 ·
Los mantenedores suelen responder en 1 día