Problem/Motivation
When having multiple formats which allow separate profiles, and at least one format is not loaded until after the initial page load, the module includes all CKEditor plugin translations in the AJAX response.
If JS aggregation is not used, this could lead to the webserver or a reverse proxy returning 429 "Too many requests" because of all the files.
Steps to reproduce
- Install a Standard Drupal profile.
- Download and enable a module which can cause editor instances to be loaded on demand, like Inline Entity Forms (or Paragraphs).
- Ensure the following modules are enabled: Locale, CKEditor 5, Inline Entity Forms.
- Disable JS aggregation
- Create two formats with separate CKEditor profiles, let's call them Simple and Advanced. Ensure Advanced uses more plugins than Simple.
- Set up two content types, also Simple and Advanced. Configure their body fields to only use the respective format.
- Give the Page content type an entity reference field, configured to reference the Simple and Advanced content types, and make sure the form uses the Complex widget
- Add at least one other language and switch to using it for the interface from here.
- Add a new Page node
- Select to add an inline Simple node, the first set of CKEditor plugins and their translations will be loaded.
- Remove the added node (can be skipped if cardinality was set >1)
- Add a new Advanced node, the next set of plugins will be loaded, but translations for all languages will be downloaded.
The problem lies in Drupal\ckeditor5\Hook\Ckeditor5Hooks::jsAlter, which checks if a placeholder script/library was added to the response, and then filters out all CKEditor plugin translations.
On the AJAX request loading the second instance the placeholder library is in the list of already loaded libraries, and the placeholder script is therefor not included in the $javascript array, so no filtering is performed for the newly added plugin translation libraries.
Proposed resolution
If the placeholder script is not present, look for an already loaded translation library and perform the filtering anyway if one was found.
Remaining tasks
Look for side-effects of the proposed resolution.
User interface changes
None
Introduced terminology
API changes
Data model changes
Release notes snippet
| Comment | File | Size | Author |
|---|---|---|---|
| #50 | Screenshot 2026-02-14 at 11.49.45 AM.png | 144.03 KB | godotislate |
Issue fork drupal-3516264
Show commands
Start within a Git clone of the project using the version control instructions.
Or, if you do not have SSH keys set up on git.drupalcode.org:
Comments
Comment #3
twodTest-only patch.
Comment #4
twodPushed up a test-only branch instead, and noticed we have the option of manually running a "Test-only changes" stage, which failed as expected.
Comment #5
twodOk, third time is the charm of trying to make this "Needs review"?
Comment #6
quietone commentedChanges are made on on 11.x (our main development branch) first, and are then back ported as needed according to the Core change policies.
Comment #7
smustgrave commented@twod mind switching the branch
Comment #8
twodSorry, I did make it against 11.x locally but didn't notice it picked 11.1.x in the PR.
Comment #9
smustgrave commentedI’d recommend rebasing too
Comment #10
smustgrave commentedLeft some comments and questions.
Comment #11
catchI just discovered this in #3232810: [PP-1] Allow setting aggregation groups for js files in library definitions and an issue search brought me here. For that issue, it would be ideal if we could remove the placeholder library/file altogether - I think the bug here shows it's conceptually wrong to rely on a 'global' placeholder when js can be loaded by the AJAX system and big pipe. Given the logic here works without the placeholder necessarily being there, that might be enough?
Comment #12
catchJust discussed this with @Wim Leers at DrupalCon.
He had the idea of a
loads: falsekey for library definitions, we could then exclude any library with that set from the ajax page state already loaded libraries - and this would then allow all the logic in locale and ckeditor5 to work without any further changes.Comment #13
twodSounds interesting! I'll be in the contribution room tomorrow, so let's chat.
Comment #15
catchI started work on the approach outlined in #12, but realised I wasn't convinced about adding anything to the libraries/asset API for this.
Then I had another idea about altering every relevant library to add a placeholder file or similar, and using that instead of the presence of the library, and that sort of worked.
Then I had another look at the original MR here, and realised the only thing I wasn't sure about was looking for the file sometimes and the library others - we can check for the library all the time in the two places it's likely to be.
Updated https://git.drupalcode.org/project/drupal/-/merge_requests/11678 for that, had to update the unit test for changes in HEAD since and a slight adjustment for the new logic, but this seems fine.
Might try the same on #3525743: Locale JavaScript translation doesn't take into account AJAX too.
Comment #16
smustgrave commentedWhich MR should be reviewed?
Comment #19
catchHid two of the MRs. Given that https://git.drupalcode.org/project/drupal/-/merge_requests/11678 is passing tests, I don't think we need to explore the 'add the fake file to every library' approach.
Comment #20
godotislateA couple comments about the test, one about mocking that I think needs fixing, the other a nit about whitespace.
I also tested this manually and confirmed that the translation JS files were downloaded per reproduction step 12 on 11.x while they were not on the MR branch.
Comment #21
catchThanks. Applied both of the MR comments.
Comment #22
godotislateLgtm.
Comment #23
alexpottCommitted and pushed 266483960f2 to 11.x and 0d063d19c8d to 11.3.x. Thanks!
Comment #27
catchWe might need to revert this.
In Umami, all 847 ckeditor translation files are being loaded on https://drupal-dev.ddev.site/en/node/add/page
I have a performance test from another issue that would have detected this, not sure why the logic doesn't actually work - seems like the library we're looking for is not actually included in $assets->getLibraries() by the time we get to hook_js_alter().
Comment #31
catchReverted for now.
Comment #33
catchThere's a quick fix - we need to get all the dependencies of the libraries for the assets, not only the libraries in the assets themselves.
https://git.drupalcode.org/issue/drupal-3516264/-/jobs/8116203#L479 is a new performance test that I added with the commit that was reverted.
That test passes again when we check against libraries with their dependencies.
Comment #35
catchThis is back to green again with additional test coverage.
The problem was the original fix didn't check whether ckeditor5.translations was in the dependencies of the libraries from the assets passed in. This meant there were still some (different) cases where ckeditor5_js_alter() wouldn't filter out all of the translation libraries. Once we check dependencies every case should be covered.
I added a new method to AssetAggregationAcrossPagesTest borrowed from #3565258: Support library-specific aggregates (which is how I found out the fix was incomplete). Also had to update the unit test anyway and that now covers when the library is only in the dependencies and not explicitly passed in.
I've also opened #3568295: Only create ckeditor5 translation libraries for enabled languages because the symptom of this bug is that approximately 900 files get added to a page, even if the bug is fixed, I don't think we need to add all of those files and then remove them again, when it's only ever possible that we'd return a subset.
At some point it would be worth looking to see if there's a completely different way we could handle these translations without the placeholder file etc., but this at least should be an improvement.
Bumping to major because this is blocking generic improvements to the asset aggregation system that expose the bug in different ways.
Comment #36
twodI tested this in a very complex site running 11.3.2 by taking the patched
Ckeditor5Hooks.phpand it's working well for us - with and without JS/CSS aggregation enabled.As was noted by the earlier investigations, it does look like this is the best we can do for now to mitigating the problem without completely rethinking the approach,.
Comment #37
catch@twod the change since the last RTBC/commit/revert is very small, would you feel comfortable re-RTBCing?
Comment #38
twodYes, I thought I did that but didn't notice it didn't change.
Comment #39
needs-review-queue-bot commentedThe Needs Review Queue Bot tested this issue. It fails the Drupal core commit checks. Therefore, this issue status is now "Needs work".
This does not mean that the patch necessarily needs to be re-rolled or the MR rebased. Read the Issue Summary, the issue tags and the latest discussion here to determine what needs to be done.
Consult the Drupal Contributor Guide to find step-by-step guides for working with issues.
Comment #40
smustgrave commentedBot rebellion
Comment #43
alexpottCommitted c8c8cb5 and pushed to main. Thanks!
Committed 69e93b3 and pushed to 11.x. Thanks!
I think we should backport this to 11.3.x
Comment #44
alexpottActually lets not fix in 11.3x unless someone thinks it is worth it.
Comment #47
nicxvan commentedThis is broken in the UI only.
I used git bisect to identify this issue.
This breaks install with:
It ends up with only 59 database tables too.
Comment #48
nicxvan commentedReopening for now, if we need a new separate issue let me know.
Comment #50
godotislateMR with the fix: https://git.drupalcode.org/project/drupal/-/merge_requests/14770
Changing the renderer and library dependency resolver arguments to service closures prevents loading the kernel as a dependency during install. I'm not sure why the error did not show in StandardInstallerTest, but I won't be able to look into tests till later.
For now:
(on main) test by installing Drupal Standard (or Umami) via UI at core/install.php and observe error

Screenshot of what the error in the installer looks like:
(on MR branch) error does not appear and user is redirected to front page.
Comment #51
godotislateThinking about this a little more: we maybe should investigate whether
kernelshould still be a synthetic service at this point in the install process. Changing the arguments in the hooks class to service closures avoids the error, but it might surface again in other hooks classes.In which case, maybe it does make more sense to close this issue again and open a new one?
Comment #52
godotislateDid investigation and it really should be a new issue: #3573856: SiteConfigureForm properties need to be reset after module install in submitForm()