Problem/Motivation
When inserting an image using the media browser and using ckeditors undo button you have to double click in quick succesion for it to correctly undo the inserting of the image. This does not happen with uploading images using the standard ckeditor image button.

Steps to reproduce
- Add undo buttons to ckeditor through configuration
- Create basic page
- Add image through media browser
- Press undo button once - image is removed and quickly added back
- Double click undo button - image is successfully removed
Proposed resolution
Remaining tasks
User interface changes
API changes
Data model changes
| Comment | File | Size | Author |
|---|---|---|---|
| #26 | 2813813-26.patch | 8.05 KB | wim leers |
| #26 | interdiff.txt | 1.98 KB | wim leers |
| #24 | interdiff-optional.txt | 1.98 KB | wim leers |
| #23 | 2813813-22.patch | 7.24 KB | wim leers |
| #23 | interdiff.txt | 3.06 KB | wim leers |
Comments
Comment #2
phenaproximaI looked into this -- admittedly quite briefly, but I think I have a handle on what the problem might be.
First and foremost, I'm convinced that this problem is firmly in Entity Embed's province. Obviously Lightning is affected, but Lightning is not specifically doing anything which should cause this. Our direct integration with CKEditor is very, very light. So I'm moving this to Entity Embed's issue queue.
Secondly, I suspect (gut feeling, I have no scientific evidence that this is the case) that this is a conflict between the way Entity Embed works and the way CKEditor expects its undo functionality to work.
When you embed an entity into CKEditor, Entity Embed does a covert AJAX request to the server to retrieve a rendered version of the entity. Meanwhile, CKEditor's undo system is periodically taking "snapshots" of the editor's state. As far as I know, these two processes are unaware of each other. At the time that you embed the entity, the editor gets an empty <drupal-entity> tag, which is filled with the rendered entity when the AJAX request completes. I believe that CKEditor is taking an undo snapshot during the AJAX request, before the rendered entity has arrived...and after the AJAX request has completed, and the rendered entity has been inserted into the editor. To the undo manager, these would look like two different states...which would explain why you have to click Undo twice, and very quickly, to be rid of the <drupal-entity> tag.
So why do you need to double-click quickly? Because I think that after the first click, the rendered entity is removed by the undo system...at which point Entity Embed sees an empty <drupal-entity> tag and thinks that it needs to retrieve the rendered entity again. So it does, and probably quite quickly (the browser may have cached the response). Thus it seems like the thing you undid, has been redone -- because it has.
Again, this is a theory. I have no idea how to begin to solve it. But hopefully it provides a place to start.
Comment #3
marcoscanoI can confirm the bug still exists, the "undo" button is not working for embedded entities the same way it works for core embedded images.
Comment #4
Kashterion commentedThis case still exist with entity_embed / 8.x-1.0-beta2.
Any progress on this issue?
Comment #5
wim leers#2823691: After adding an embed, hitting undo doesn't work reported the same problem:
Closed that as a duplicate.
Comment #6
wim leersIn core, we added undo support in #2071957: Cannot change existing images or links through Text Editor's image/link dialogs. It's quite simple: we just need to do
editor.fire('saveSnapshot');both before and after the changes are made.Unfortunately, this does not actually work. It looks like the CKEditor plugin's JS is doing some strange things that probably get in the way. Admittedly, the CKEditor plugin/widget APIs are quite strange too. So it's easy to get wrong. The easiest way to solve this would be to rewrite the
drupalentityCKEditor plugin to closely match core'sdrupalimageCKEditor plugin…Comment #7
wim leersComment #8
wim leersComment #9
wim leers(Like most Drupal 8 installations, I don't have an explicit "undo" button in CKEditor. It exists, but it's rather pointless for most of us, who are used to CTRL/CMD+Z to undo.)
Comment #11
wim leers#2544020 should happen first, see #2544020-8: CKEditor plugin not written with CKEditor >=4.4 in mind; rewrite it based on core's `drupalimage` plugin.
Comment #12
wim leersActually, having started to work on #2544020: CKEditor plugin not written with CKEditor >=4.4 in mind; rewrite it based on core's `drupalimage` plugin, this is probably easier to solve independently first.
It's quite easy to observe this misbehaving by looking at
CKEDITOR.instances['edit-body-0-value'].undoManager.snapshotsin a browser. It'll jump from 1 to 3 snapshots upon changing the embedded entity, instead of changing from 1 to 2.Specifically:
CKEDITOR.instances['edit-body-0-value'].undoManager.snapshots[0].contentsCKEDITOR.instances['edit-body-0-value'].undoManager.snapshots[1].contentscke_widget_focusedclassCKEDITOR.instances['edit-body-0-value'].undoManager.snapshots[2].contentscke_widget_focusedclass (because the updated embed gets focus automatically) and contains the updated HTML.This is using https://ckeditor.com/docs/ckeditor4/latest/api/CKEDITOR_editor.html#even....
Comment #13
wim leersPatch wasn't relative to HEAD, my bad.
Comment #14
phenaproximaI can definitely confirm the problem in manual testing. However, the patch doesn't fix it for me; the undo button simply becomes ineffective. No JavaScript errors are thrown.
Comment #15
phenaproximaI think this could probably use tests...
Comment #16
wim leersIt took me four hours to figure out a work-around for having CKEditor make its
Undobutton clickable, because despite an undo snapshot history having been accumulated, theUndobutton didn't become activated 😩 This is probably because CKEditor is making some assumptions and/or because Selenium/chromedriver are doing some things differently.Anyway, I did find a work-around at last! This test is now working reliably.
Comment #17
wim leers@phenaproxima was right, I was able to reproduce #13 not fixing the entire problem. #13 fixes changing existing embeds, and it also works when first typing text and then inserting an embed, but it fails if the first thing you do in CKEditor is inserting an embed.
That's also why the proposed fix is now failing in the test coverage.
Here's an improved fix.
Comment #18
wim leersNote that this also means we don't re-fetch previews when undoing/redoing! Big front-end performance win.
But we can clean up this code a little bit. Did that.
Comment #20
wim leersCreated patches from the wrong branch. Rerolled them from the right branch. Interdiffs and test-only patch were right.
Comment #22
oknatelol:
Excellent work with the helper function assertCkeditorUndoOrRedo().
One thing I noticed waitOnCkeditorInstance() duplicates and improves upon the method in the base class, waitForEditor().
Can we put waitOnCkeditorInstance into the base class EntityEmbedDialogTest.php as a replacement for waitForEditor() and update references to waitForEditor()?
Comment #23
wim leers#22: Glad you're entertained by the single line that came out of my four hours of trial and error 😜
Oh, hah, I copy/pasted that from
\Drupal\Tests\ckeditor\FunctionalJavascript\AjaxCssTest::waitOnCkeditorInstance()! Done.Comment #24
wim leersWe could go even further in test coverage: we could assert the specific undo snapshot count, but I think the above test coverage already asserts which snapshots exist, without going into as much detail, so I think #23 is preferable.
Comment #25
oknateIn regard to
That's funny. I independently came up with a very similar solution.
I manually tested #20 and it works great, marking RTBC.
In regards to #24. I think snapshot count addition is worthy and should be added. One of the best things about tests is it helps developers understand the functionality. By documenting the snapshot counts, you will teach future generations how to debug the snapshot count.
Comment #26
wim leersWFM.
Comment #27
oknateI ran #26 against the coder linter and didn't see any issues.
Comment #29
wim leers🚢
Comment #30
wim leersComment #31
wim leersOne more problem with "undo" found: #3060245: [upstream] CKEditor's "undo" functionality not working correctly for embeds: offers multiple undo steps where there should be only one.