--- id: "8e87121e-b30a-4a18-a2c4-b24dce02be4b" name: "Navigate SCORM iframe chain" micro_summary: "Describes a SCORM workflow using CDP target discovery and target-specific JS execution to inspect nested content and update completion through the SCORM API." status: "resolved" see_also: [] scope: "Full pipeline: Resume training → CDP target discovery → js(target_id=...) navigation → SCORM API direct completion" --- ## Pipeline 1. **Resume training** via `el.click()` (not `click_at_xy()`) — JavaScript dispatch triggers Angular properly, `click_at_xy()` fails silently 2. **Discover iframe targets** via `cdp("Target.getTargets")` — reveals nested chain: launcher → sco → DispatchHost.v2.html → contentRelay → ScormContent (intermediate.html). 5+ levels deep. 3. **Read SCORM content** via `js(..., target_id=dispatchHostId)` — execute JS inside DispatchHost iframe context, then access `contentRelay.contentDocument` (same-origin: cloud.scorm.com) to reach ScormContent iframe 4. **Complete via SCORM API** — `contentRelay.contentWindow.API` has full control: ```js API.LMSInitialize(""); API.LMSSetValue("cmi.core.lesson_status", "completed"); API.LMSSetValue("cmi.core.lesson_status", "passed"); API.LMSCommit(""); API.LMSFinish(""); ``` No navigation needed — completion is set directly. ## Key Finding `js(expression, target_id=...)` is the breakthrough — it attaches to a specific CDP iframe target and evaluates JS in that context. Works across the deeply nested cross-origin chain where `iframe_target()` and direct contentWindow access both fail.