Skip to content

Fix rendering of iframes that require dynamic JS (JSXGraph) and are asynchronously rendered via core/template::runTemplateJS() - #1900

Open
ngandrass wants to merge 2 commits into
maths:devfrom
ngandrass:fix/iframe-javascript-emebddings
Open

ngandrass wants to merge 2 commits into
maths:devfrom
ngandrass:fix/iframe-javascript-emebddings

Conversation

@ngandrass

@ngandrass ngandrass commented Oct 13, 2026 •

Copy link
Copy Markdown
Contributor

Hi,

this bug is quite an exotic one so I try to explain it with a lot of context in this PR while I wait for the caffeine to wear off that was required to find this ... 😅 It was found by one of our teachers during Moodle 5 testing but it affects the current Moodle 4.5 (LTS) release alike.

TL;DR: Javascript embedded inside [[iframe]] blocks fails to load properly if the question is rendered asynchronously via core/template::runTemplateJS(), e.g., when displaying it inside a modal dialog. This breaks JS logic and thereby also JSXGraph renderings in the question commenting view of the Moodle question bank.

How to reproduce

  1. Create a STACK question with an JSXGraph applet and save it to the question bank.
  2. Navigate to the question bank and find the question
  3. Click on the question comments number in the comments column (see image below)
  4. See that the JSXGraph applet does not render and a JS warning shows up in the browser console
image

Why the f*ck this breaks ...

Based on the headline you can probably tell that this took me quite some time to figure out ... To put it simple:

  1. stack/cas/castext2/blocks/iframe.block.php already does some sanitization of the JS contents.
  2. However, json_encode() leaves < and > fully intact by default, leaving <script in there.
  3. The result is passed to js_amd_inline() which ultimately ends up in core/template::runTemplateJS() (called by the modals setBody() method).
  4. core/template::runTempalteJS() delegates mounting the content into the DOM to jQuery.
  5. jQuery does some auto detection magic that sees the <script tag start and wants to do some optimization by separating plain HTML from script contents.
  6. This ultimately breaks the resulting mounted source code and [[javascript]] content fails.

This weird behavior is already known to the Moodle core and documented by a respective comment in core/templates::runTemplateJS():

const runTemplateJS = (source) => {
    if (source.trim() !== '') {
        // Note. We continue to use jQuery here because people are doing some dumb things
        // and we need to find, seek, and destroy first.
        // In particular, people are providing a mixture of JS, and HTML content here.
        // jQuery is someohow, magically, detecting this and putting tags into tags.

How the fix works

Adding JSON_HEX_TAG to json_encode() causes PHP to convert < and > characters to their raw unicode literals (see: https://www.php.net/manual/en/json.constants.php#constant.json-hex-tag). This prevents jQuery from ripping apart the embedded javascript and everything works as expected. Hooray 🎉

Validation

I tried the proposed patch on Moodle 4.5 (LTS) and Moodle 5.2. The following examples show how the proposed fix affects different [[iframe]] scenarios in both direct embedding as well as the asynchronously rendered JS modal of the question commenting view.

JSXGraph

Example from: https://docs.stack-assessment.org/en/Specialist_tools/JSXGraph/Basic_plots/

Question Preview Comment Modal
Before fix image image
After fix image image

Javascript within iframe

Example from: https://docs.stack-assessment.org/en/Specialist_tools/JSXGraph/Basic_plots/

Question Preview Comment Modal
Before fix image image
After fix image image

Plain iframe

Example from: https://docs.stack-assessment.org/en/Authoring/Question_blocks/Iframe_blocks/

Note: This was already working before the fix. It is just to show that normal iframes are unaffected by the changes :)

Question Preview Comment Modal
Before fix image image
After fix image image

…ly rendered via core/template::runTemplateJS()
@sangwinc
sangwinc changed the base branch from master to dev October 14, 2026 13:48
@sangwinc

Copy link
Copy Markdown
Member

Thanks @ngandrass I do appreciate you tracking this down. We will review this.

@sangwinc
sangwinc requested a review from aharjula October 14, 2026 13:49
@sangwinc sangwinc added this to the 4.14.0 milestone Oct 14, 2026
@adamant-pwn

Copy link
Copy Markdown
Collaborator

Updated the branch with current dev at cfe78121d; the PR still changes only the original iframe encoding.

The old run's blocking failures were the PRT "Node 2" visibility check and the Freetext MathJax comparison. Current dev already contains the explicit-fieldset-expansion and U+2062 fixes. The lint/doc diagnostics are configured as non-blocking.

PHP syntax and diff checks pass. A local browser fixture using Moodle's template renderer and jQuery reproduced corruption with the old encoding and preserved the original arguments in all six patched cases (plain HTML, inline JS and external-script content, each through direct and fragment insertion). This is a transport check, not a full Moodle/JSXGraph integration test.

Fresh CI is running; this is not yet an all-green result.

@sangwinc sangwinc modified the milestones: 4.14.0, 4.9.0, 4.15.0 Oct 15, 2026

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants