core/core.libraries.yml | 1 + core/misc/drupal.js | 16 ++++++++++++++++ core/modules/contextual/js/contextual.js | 10 +++++----- core/modules/history/js/history.js | 5 +++++ core/modules/quickedit/js/quickedit.js | 6 +----- core/modules/system/system.module | 7 +++++++ core/modules/toolbar/js/toolbar.js | 5 +++++ sites/development.services.yml | 13 +++++++++++++ 8 files changed, 53 insertions(+), 10 deletions(-) diff --git a/core/core.libraries.yml b/core/core.libraries.yml index 90d3cc3..830e3b5 100644 --- a/core/core.libraries.yml +++ b/core/core.libraries.yml @@ -54,6 +54,7 @@ drupalSettings: version: VERSION drupalSettings: # These placeholder values will be set by system_js_settings_alter(). + clientSideCaching: true path: baseUrl: null scriptPath: null diff --git a/core/misc/drupal.js b/core/misc/drupal.js index dcfad00..70a9356 100644 --- a/core/misc/drupal.js +++ b/core/misc/drupal.js @@ -428,4 +428,20 @@ if (window.jQuery) { return '' + Drupal.checkPlain(str) + ''; }; + /** + * Deletes either from localStorage or sessionStorage by prefix. + * + * @param Storage storage + * Either window.localStorage or window.sessionStorage + * @param String prefix + * The prefix to use to determine which key-value pairs to delete. + */ + Drupal.deleteFromClientSideCacheByPrefix = function(storage, prefix) { + Object.keys(storage).forEach(function (key) { + if (key.substring(0, prefix.length) === prefix) { + storage.removeItem(key); + } + }); + }; + })(domready, Drupal, window.drupalSettings); diff --git a/core/modules/contextual/js/contextual.js b/core/modules/contextual/js/contextual.js index 505b2a8..42debee 100644 --- a/core/modules/contextual/js/contextual.js +++ b/core/modules/contextual/js/contextual.js @@ -23,11 +23,7 @@ var permissionsHash = drupalSettings.user.permissionsHash; if (cachedPermissionsHash !== permissionsHash) { if (typeof permissionsHash === 'string') { - _.chain(storage).keys().each(function (key) { - if (key.substring(0, 18) === 'Drupal.contextual.') { - storage.removeItem(key); - } - }); + Drupal.deleteFromClientSideCacheByPrefix(storage, 'Drupal.contextual.'); } storage.setItem('Drupal.contextual.permissionsHash', permissionsHash); } @@ -157,6 +153,10 @@ // Update all contextual links placeholders whose HTML is cached. var uncachedIDs = _.filter(ids, function initIfCached(contextualID) { + // Return early if client-side caching is disabled. + if (!drupalSettings.clientSideCaching) { + return true; + } var html = storage.getItem('Drupal.contextual.' + contextualID); if (html !== null) { // Initialize after the current executation cycle, to make the AJAX diff --git a/core/modules/history/js/history.js b/core/modules/history/js/history.js index c598dd4..61a0420 100644 --- a/core/modules/history/js/history.js +++ b/core/modules/history/js/history.js @@ -19,6 +19,11 @@ embeddedLastReadTimestamps = drupalSettings.history.lastReadTimestamps; } + // If client-side caching is disabled, remove existing cache items. + if (!drupalSettings.clientSideCaching) { + Drupal.deleteFromClientSideCacheByPrefix(storage, 'Drupal.history.'); + } + Drupal.history = { /** diff --git a/core/modules/quickedit/js/quickedit.js b/core/modules/quickedit/js/quickedit.js index d722bea..ebb9c97 100644 --- a/core/modules/quickedit/js/quickedit.js +++ b/core/modules/quickedit/js/quickedit.js @@ -164,11 +164,7 @@ var permissionsHash = drupalSettings.user.permissionsHash; if (permissionsHashValue !== permissionsHash) { if (typeof permissionsHash === 'string') { - _.chain(storage).keys().each(function (key) { - if (key.substring(0, 26) === 'Drupal.quickedit.metadata.') { - storage.removeItem(key); - } - }); + Drupal.deleteFromClientSideCacheByPrefix(storage, 'Drupal.quickedit.metadata.'); } storage.setItem(permissionsHashKey, permissionsHash); } diff --git a/core/modules/system/system.module b/core/modules/system/system.module index 8f2a8ab..262067e 100644 --- a/core/modules/system/system.module +++ b/core/modules/system/system.module @@ -657,6 +657,13 @@ function system_js_settings_alter(&$settings) { if (!isset($settings['locale']['pluralDelimiter'])) { $settings['locale']['pluralDelimiter'] = LOCALE_PLURAL_DELIMITER; } + + // Allow a 'clientSideCaching' container parameter to override the default + // drupalSettings.clientSideCaching = true setting. + $container = \Drupal::getContainer(); + if ($container->hasParameter('clientSideCaching')) { + $settings['clientSideCaching'] = $container->getParameter('clientSideCaching'); + } } /** diff --git a/core/modules/toolbar/js/toolbar.js b/core/modules/toolbar/js/toolbar.js index e5b244c..c4f84b9 100644 --- a/core/modules/toolbar/js/toolbar.js +++ b/core/modules/toolbar/js/toolbar.js @@ -26,6 +26,11 @@ } ); + // If client-side caching is disabled, remove the cached subtree and its hash. + if (!drupalSettings.clientSideCaching) { + Drupal.deleteFromClientSideCacheByPrefix(localStorage, 'Drupal.toolbar.subtrees'); + } + /** * Registers tabs with the toolbar. * diff --git a/sites/development.services.yml b/sites/development.services.yml index cc21211..5a60687 100644 --- a/sites/development.services.yml +++ b/sites/development.services.yml @@ -7,3 +7,16 @@ services: class: Drupal\Core\Cache\MemoryBackendFactory cache.backend.null: class: Drupal\Core\Cache\NullBackendFactory + +parameters: + # Client-side caching. + # + # By default, client-side caching (e.g. in localStorage and sessionStorage) is + # enabled. While developing, you may want to disable this. + # + # Note: JavaScript code can disable its client-side caching by checking if + # drupalSettings.clientSideCaching === false. + # + # Not recommended in production environments + # @default true + clientSideCaching: false