In #1137920: Fix toolbar on small screen sizes and redesign toolbar for desktop, based on lots of UX discussion, the current goal is to allow users to navigate the complete Management menu without intermediary page loads (like Admin menu, though not relying on hover for touch devices). This brings up two performance concerns:

If we implement a client-side cache anyway, there's less Toolbar-specific need for some of the work being explored in #1805054: Cache localized, access filtered, URL resolved, and rendered menu trees, though that issue can still be useful for other menus.

This patch implements a client-side cache with JSONP. For each page, we add a SCRIPT tag with a src that contains the hash of a JSON array containing a rendered html subtree for each top-level toolbar link. Since a hash specifies its hashed content uniquely, the entire JSONP response is cached in Drupal's page cache. This allows efficient caching: multiple users who have the same Management links available to them (e.g., have the same roles, and none of the Management links are per-user access controlled or customized) can reuse the same cached JSONP data. A {cache_toolbar} bin is added to keep track of the mapping from user to hash. The per-user hash mapping and hash to page cache approach is the same as what's employed by Admin menu 3.x, though I simplified some of the implementation details in this patch.

Support from Acquia helps fund testing for Drupal Acquia logo

Comments

effulgentsia’s picture

+++ b/core/modules/toolbar/toolbar.css
@@ -127,3 +127,10 @@ body.toolbar-drawer {
+/**
+ * @todo Hide or show submenus based on some logic (e.g., hover)
+ */
+#toolbar-menu ul {
+  display: none;
+}
+++ b/core/modules/toolbar/toolbar.js
@@ -35,6 +35,13 @@ Drupal.toolbar.init = function() {
+  // Add subtrees.
+  if (Drupal.toolbar.subtrees) {
+    for (var id in Drupal.toolbar.subtrees) {
+      $('#toolbar-link-' + id).after(Drupal.toolbar.subtrees[id]);
+    }
+  }
+

The reason this patch has no visual impact to the current toolbar is because of this. The JSONP that's returned is a rendered subtree of each top-level link. For no-js users, this means the toolbar is just the same as it is in HEAD. For js users, the code above adds the HTML to its respective place in the DOM, but the CSS above hides it. The @todo indicates we'll want something more useful than that, but that's the job of #1137920: Fix toolbar on small screen sizes and redesign toolbar for desktop. This issue, meanwhile, is focused on addressing the performance/caching concerns only.

Note that the reason the JSON structure is per subtree is to enable further front-end optimizations, like only calling $().after() on demand per top-level link, and not adding lots of stuff to the DOM until the user needs it, which could help with memory/battery usage on phones.

Status: Needs review » Needs work

The last submitted patch, toolbar-cache.patch, failed testing.

effulgentsia’s picture

Status: Needs work » Needs review
FileSize
7.58 KB

Added update function.

Damien Tournoud’s picture

Have you considered the security implications? This *must not* be loadable cross-domain, so JSONP is very much a no-go.

effulgentsia’s picture

+++ b/core/modules/toolbar/toolbar.module
@@ -60,6 +62,13 @@ function toolbar_menu() {
+  $items['toolbar/subtrees/%'] = array(
+    'page callback' => 'toolbar_subtrees_jsonp',
+    'page arguments' => array(2),
+    'access callback' => '_toolbar_subtrees_access',
+    'access arguments' => array(2),
+    'type' => MENU_CALLBACK,
+  );
   return $items;
 }
 
@@ -77,6 +86,68 @@ function toolbar_toggle_page() {
 }
 
 /**
+ * Access callback: Returns if the user has access to the rendered subtree requested by the hash.
+ *
+ * @see toolbar_menu().
+ */
+function _toolbar_subtrees_access($hash) {
+  return user_access('access toolbar') && ($hash == _toolbar_get_subtree_hash());
+}

What's the cross domain risk? Is this access checking not sufficient?

Damien Tournoud’s picture

The hash seems unpredictable enough, so this looks ok.

JohnAlbin’s picture

Issue tags: +Feature freeze

I'm going to review this today or tomorrow while at BADCamp. :-)

This issue is also highly dependent on the solution we use for #1827296: Mark up the currently active menu trail in the menu list since a PHP-based build of the menu + active trail requires a different cache strategy from a JS-based lets-just-decorate-the-static-menu-tree approach. Edit: I should really read the issues summary better. :-)

JohnAlbin’s picture

Status: Needs review » Needs work

Hmmm…

I applied the patch, re-installed Drupal and I can verify that it is loading a script tag pointing at a page-cache-enabled url.

<script src="/toolbar/subtrees/ClWgGu2YKNPabI3KL9F6WN2uIxZBhkzOGXhiXGz-Tf0"></script>

However, when looking at that /toolbar/subtrees page, all I saw was this:

Drupal.toolbar.setSubtrees({"admin-content":null,"admin-structure":null,"admin-appearance":"","admin-people":"","admin-modules":"","admin-config":null,"admin-reports":null,"admin-help":""});

So the patch is failing to load the sub-trees properly.

effulgentsia’s picture

Status: Needs work » Needs review
FileSize
645 bytes
7.59 KB

Menu name changed from 'management' to 'admin', so here's a fix for that. CNR for bot, but I still want to implement some cache header feedback I got from msonnabaum.

effulgentsia’s picture

+++ b/core/modules/toolbar/toolbar.module
@@ -77,6 +86,68 @@ function toolbar_toggle_page() {
+  $max_age = 3600 * 24 * 365;
+  drupal_add_http_header('Expires', gmdate(DATE_RFC1123, REQUEST_TIME + $max_age));
+  drupal_add_http_header('Cache-Control', 'private, max-age=' . $max_age);

Actually, turns out these cache headers are fine as-is. And when I look at the net tab in firebug, I get confirmation that the browser does not re-request. It only does when I start an xdebug session (which is what prompted me to think something was wrong), because that causes the cookie to change. In this case, we don't need Vary: Cookie, but drupal_serve_page_from_cache() doesn't allow the Vary header to be removed: something we might want to fix elsewhere, but doesn't really cause a problem here, since under normal operation, the user's cookies don't change often.

The last submitted patch, toolbar-cache-9.patch, failed testing.

JohnAlbin’s picture

Status: Needs work » Needs review
Issue tags: +Feature freeze

#9: toolbar-cache-9.patch queued for re-testing.

JohnAlbin’s picture

Status: Needs review » Reviewed & tested by the community

The caching strategy is quite nice here. In the cache_toolbar table it is storing the hash of the full, expanded menu for each user. A specific user will actually share a hash with any user who has the same permission set. The hash is used in the URL to uniquely identify a page which contains a json response containing the full menu. Drupal will use normal page caching to cache that json response. And the browser will read the cache headers sent by Drupal to prevent it from ever checking to see if the page has been updated (it will never be updated because the hash itself is unique per unique versions of the menu.)

Really nice.

I've applied the patch and used a browser inspector to verify that the DOM elements are being loaded into the page. And I did a thorough review at the code level as well.

One should note that there's a @TODO that says:

+ * @todo Replace this hack with something better integrated with DrupalKernel
+ *   once Drupal's page caching itself is properly integrated.

which makes perfect sense to me. We'll definitely need to revisit that later.

RTBC!

webchick’s picture

Assigned: Unassigned » catch

Probably makes sense for catch to review this one.

effulgentsia’s picture

I don't think it makes sense to commit this on its own, as all it does is add code to give the browser an expanded toolbar that is display:none. But, leaving it at RTBC to get feedback from catch (and others) on whether this is an approved approach. If it is, I can roll it in to #1137920: Fix toolbar on small screen sizes and redesign toolbar for desktop.

jessebeach’s picture

I'd like to roll this into #1137920: Fix toolbar on small screen sizes and redesign toolbar for desktop once the cacheing strategy is approved. We'll need to rework the JavaScript to match the current stage of the ToolBar patch. I'll work with effulgentsia to do that.

catch’s picture

I have a few comments on the actual code, but the strategy itself seems good to me.

Not sure what status this should be at...

effulgentsia’s picture

Status: Reviewed & tested by the community » Closed (duplicate)

I think duplicate is fine then. Next step is to roll this in to #1137920: Fix toolbar on small screen sizes and redesign toolbar for desktop and code reviews can happen there.

effulgentsia’s picture

Project: Drupal core » Responsive admin toolbar
Version: 8.x-dev »
Component: toolbar.module » Code
Assigned: catch » Unassigned
Priority: Major » Critical
Status: Closed (duplicate) » Needs work
Issue tags: -Feature freeze

Maybe better yet, moving this to the sandbox queue.

effulgentsia’s picture

FileSize
7.42 KB

Rebased to #1137920-297: Fix toolbar on small screen sizes and redesign toolbar for desktop. Doesn't work yet, so leaving at "needs work".

effulgentsia’s picture

Status: Needs work » Needs review
FileSize
8.23 KB
effulgentsia’s picture

effulgentsia’s picture

FileSize
1.44 KB
8.77 KB

Removed one todo and added another one.

jessebeach’s picture

Status: Needs review » Closed (fixed)
jessebeach’s picture

Issue summary: View changes

Updated issue summary.