While porting the CKEditor Widget to Drupal core in #2994696: Render embedded media items in CKEditor, I encountered this remaining @todo:

        * @todo Since previews use the downcasted representation, and `downcast()` relies 100% on `this.data`, and
        * `_hashData()` knows which changes are immaterial, we should be able to cache preview responses.

We could:

  1. implement our own URL-based cache! Cool! But more code to maintain and test. 👎
  2. What if we'd instead rely on this pre-existing URL-based cache called the browser and allow it do its thing by switching to GET requests and ensuring the response has a Cache-Control header that allows for caching? That sounds lovely!

    But Drupal AJAX requests currently always use POST, not GET. Which means the browser won't cache it. There's a Drupal core issue to make this happen: #956186: Allow AJAX to use GET requests.

  3. We could force this today, but that seems risky (wrt security, maintainability) — the code to do so is simple:
    +          previewLoaderAjax.options.type = 'GET';
    +          previewLoaderAjax.beforeSerialize = function () {};
    
  4. We could also just switch to plain (non-Drupal) AJAX GET requests, but then we effectively break BC for sites relying on Entity Embed's CKEditor Widget loading CSS + JS — see #2844822: The preview in CKEditor does not use the same Twig template as the one on the front end (default theme).
Support from Acquia helps fund testing for Drupal Acquia logo

Comments

Wim Leers created an issue. See original summary.

Wim Leers’s picture

Title: Consider either using client-side caching or switching to GET requests + cacheable responses to accelerate previews » [PP-1] Consider either using client-side caching or switching to GET requests + cacheable responses to accelerate previews
Component: Code » CKEditor integration
Status: Active » Postponed
FileSize
856 bytes

A few hours later, this happened: #2844822-25: The preview in CKEditor does not use the same Twig template as the one on the front end (default theme). In part spawned by this issue causing me to take another look at it 🤓 🥳

If that lands, then this can simply delete the @todo.

Wim Leers’s picture

Title: [PP-1] Consider either using client-side caching or switching to GET requests + cacheable responses to accelerate previews » Consider either using client-side caching or switching to GET requests + cacheable responses to accelerate previews
Status: Postponed » Needs review
Issue tags: +Media Initiative
FileSize
2.63 KB

#2844822: The preview in CKEditor does not use the same Twig template as the one on the front end (default theme) landed.

I've got an exciting new patch for y'all … thanks to #2844822-25: The preview in CKEditor does not use the same Twig template as the one on the front end (default theme) we know we don't need to use the AJAX system anymore (and we don't anymore as of #2844822 landing), we also have solid test coverage even before #2844822 but with #2844822-40: The preview in CKEditor does not use the same Twig template as the one on the front end (default theme) even more so, and in #2844822-41: The preview in CKEditor does not use the same Twig template as the one on the front end (default theme) I pointed out that we're now using nothing of the Drupal AJAX system anymore which opens the door to making the response cacheable…

So without further ado, here we go: a test asserting using the Resource Timing API (https://developer.mozilla.org/en-US/docs/Web/API/Resource_Timing_API) to verify that the browser is indeed caching the response and is hence resulting in a much improved UX. Of course, this will fail right now.

oknate’s picture

Nice test! Very exciting stuff.

Status: Needs review » Needs work

The last submitted patch, 3: 3064340-3-test_only_FAIL.patch, failed testing. View results

Wim Leers’s picture

Status: Needs work » Needs review
FileSize
3.63 MB
3.46 MB
4.85 KB
7.45 KB
-            success: function (data) {
-              for (var i = 0; i < data.length; i++) {
-                if (data[i].command === 'embed_insert') {
-                  widget.element.setHtml(data[i].data);
-                  callback(widget);
-                }
-              }
-            }

Since #2844822: The preview in CKEditor does not use the same Twig template as the one on the front end (default theme), the Drupal AJAX response was merely a means of transportation; we were picking out the data for a single AJAX command, and were ignoring the add_css AJAX command for example if that existed. (Of course, prior to #2844822, that command was executed, but didn't have any effect on <iframe> CKEditor instances: the add_css command operated in the main frame!

So, since there's no point in sending that data, there's also no point in using a controller that returns AJAX responses that we then have to parse manually anyway. If we simplify that, we simultaneously gain the opportunity to cache the response on the client side, which we know is fine for Entity Embed responses, but we can't say that for sure about \Drupal\embed\Controller\EmbedController::preview(), which anything in contrib/custom code could be using for non publicly known things.

Did all that, and in doing so, this yielded the UX improvement we all were hoping for:

Before
After

(Those screencasts are with usleep(500000); in the controller, to simulate real-world less-than-ideal circumstances: response times of a little over half a second.)

Wim Leers’s picture

Cleanup.

Wim Leers’s picture

Status: Needs review » Reviewed & tested by the community
+++ b/entity_embed.routing.yml
@@ -5,3 +5,10 @@ entity_embed.dialog:
+entity_embed.preview:
+  path: '/entity-embed/preview/{filter_format}'
+  defaults:
+    _controller: '\Drupal\entity_embed\Controller\PreviewController::preview'
+  requirements:
+    _entity_access: 'filter_format.use'

+++ b/src/Controller/PreviewController.php
@@ -0,0 +1,89 @@
+  public function preview(Request $request, FilterFormatInterface $filter_format) {

Given this is 99% identical to embed.preview/\Drupal\embed\Controller\EmbedController::preview(), I feel confident moving ahead with this. Even more so thanks to our test coverage :)

Wim Leers’s picture

Title: Consider either using client-side caching or switching to GET requests + cacheable responses to accelerate previews » Make preview responses cacheable to accelerate previews
Priority: Normal » Major

To thinking about users on high-latency connections! To better user experiences!

Wim Leers’s picture

Status: Reviewed & tested by the community » Fixed

🚢

  • Wim Leers committed 7354f6c on 8.x-1.x
    Issue #3064340 by Wim Leers: Make preview responses cacheable to...
Wim Leers’s picture

Status: Reviewed & tested by the community » Fixed

🤷‍♂️

oknate’s picture

That second video is really impressive. Nice work!

Wim Leers’s picture

😊

Status: Fixed » Closed (fixed)

Automatically closed - issue fixed for 2 weeks with no activity.

Dave Reid’s picture

This seems to have a regression that no attached libraries or CSS/JS are included when the preview embedded entity is rendered?

maximpodorov’s picture

Yes, and this is the major issue for embedded content which is just a wrapper filled by the data stored in drupalSettings. We have to be able to turn off this optimization.

maximpodorov’s picture

FileSize
7.65 KB

Here is the patch that reverts the commit.

maximpodorov’s picture

Reverting the commit is not enough.
I have solved the problem of inability to preview JS-based media entities by invoking an additional AJAX request to /embed/preview/rich_text - the response is processed in the usual Drupal way.