fix: prevent XBlock container crash when JS resources are blocked by browser extensions#46
Open
bra-i-am wants to merge 1 commit into
Open
Conversation
efortish
approved these changes
Jun 25, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Note
This fix is intentionally scoped to NAU's fork targeting the
teakrelease. It is not being submitted upstream because the issue no longer reproduces onmaster(Verawood), meaning the upstream codebase is already clean of this bug — likely resolved as a side effect of broader refactors between releases.Fixes https://github.com/fccn/nau-technical/issues/838
Fixes a resilience bug in Studio's XBlock container page where Chrome extensions (e.g. AdBlock) that block certain JS resources cause an infinite loading spinner and crash the entire unit — preventing all XBlocks from rendering, not just the one whose resource was blocked.
Root cause analysis:
When any JS resource fails to load,
addXBlockFragmentResourcescalleddeferred.reject()— and sincerenderXBlockFragmentonly hooks.done(), the XBlock HTML was never injected into the page. The server always sends the spinner as initial HTML; JavaScript is responsible for replacing it. With JS blocked, the spinner stayed forever.For the Drag & Drop v2 XBlock specifically, AdBlock filter lists target these two scripts by filename pattern:
drag_and_drop_v2/public/js/vendor/virtual-dom-*.jsdrag_and_drop_v2/public/js/drag_and_drop.*.jsBoth are served from the same domain (no CDN), so there is no cross-origin workaround available.
Four root causes fixed:
loadResourceusedViewUtils.loadJavaScript(backed by$script/scriptjs), which invokes its callback on bothonloadandonerror. When AdBlock blocks a script, the promise always resolved — making failures invisible to the application. Fixed by replacing it with a raw<script>element that wiresonerror → rejectdirectly.addXBlockFragmentResourcesrejected the deferred on first JS failure, which preventedrenderXBlockFragment's.done()handler from running — so the XBlock HTML was never injected and the spinner persisted. Fixed by resolving the deferred with a{failedJs: true}flag instead of rejecting, so the HTML is always injected regardless of whether the JS loaded. Awindow.failedXBlockResourcescache (mirroring the existingwindow.loadedXBlockResourcespattern) prevents redundant network requests for known-blocked resources on subsequent XBlock renders within the same page session.constructBlockincore.jscrashed withTypeErrorwhen an XBlock'sinitFnwas undefined (because its JS never loaded). The crash propagated to the outertry/catchinhandleXBlockFragment, which callederrorCallback()instead ofsuccessCallback()— leaving the container stuck. Fixed with a null check: ifinitFnis missing, the affected XBlock is marked withxblock-initialization-failedand a stub is returned, allowing the rest of the vertical to initialize normally. Additionally,fragmentsRendered.always()was changed to.done()so that XBlock initialization is only attempted when the fragment rendered successfully.container.jsmessage handler logged a spurious warning loop for everypostMessageevent withtype: undefinedsent by browser extension proxies (e.g. Redux DevTools) viasetInterval. Fixed by only logging for defined, unrecognized message types.Behavior after this fix:
xblock-initialization-failedCSS class. Their JS is not initialized, so interactive behavior is unavailable — but the block is visible and does not crash the page.console.warnis emitted for each blocked resource and each skipped init function, replacing the previous silent freeze.Before: Chrome + AdBlock → infinite spinner,
TypeError: Cannot read properties of undefined (reading 'prototype'), all XBlocks in the unit broken.After: Chrome + AdBlock → affected XBlock shows its server-rendered HTML with
xblock-initialization-failed, all other XBlocks render and initialize normally, no JS crash.Testing instructions
Prerequisites: A Studio unit with a Drag & Drop v2 XBlock alongside at least one other XBlock (e.g. Problem/Single Select).
We'll test this in
devenvironment usingnau-tutor-configs: run the environment with the commandbin/tutor dev startOpen Studio and navigate to a unit with a Drag & Drop v2 XBlock and some more XBlocks
Block the URLs manually through the dev tools of the browser, and you'll be able to replicate the bug like this
Change the
edx-platformbranch to this PR, enter the container by runningbin/tutor dev exec cms bashand compile the assets runningnpm run webpack-devin the containerFixed!