attachinline-8.x-1.2/src/Asset/CssCollectionRendererDecorator.php
src/Asset/CssCollectionRendererDecorator.php
<?php
namespace Drupal\attachinline\Asset;
use Drupal\attachinline\EventSubscriber\CspSubscriber;
use Drupal\attachinline\Render\AttachInlineMarkup;
use Drupal\Core\Asset\AssetCollectionRendererInterface;
use Drupal\Core\Config\ConfigFactoryInterface;
use Drupal\Core\Extension\ModuleHandlerInterface;
use Drupal\csp\Csp;
use Drupal\csp\Nonce;
/**
* Render Inline CSS snippets attached to the page.
*/
class CssCollectionRendererDecorator implements AssetCollectionRendererInterface {
/**
* The decorated asset collection renderer.
*
* @var \Drupal\Core\Asset\AssetCollectionRendererInterface
*/
private $decorated;
/**
* The config factory service.
*
* @var \Drupal\Core\Config\ConfigFactoryInterface
*/
private $config;
/**
* The Module Handler service.
*
* @var \Drupal\Core\Extension\ModuleHandlerInterface
*/
private $moduleHandler;
/**
* The CSP Subscriber service.
*
* @var \Drupal\attachinline\EventSubscriber\CspSubscriber
*/
private $cspSubscriber;
/**
* The CSP Nonce service.
*
* @var ?\Drupal\csp\Nonce
*/
private ?Nonce $nonce;
/**
* CssCollectionRendererDecorator constructor.
*
* @param \Drupal\Core\Asset\AssetCollectionRendererInterface $assetCollectionRenderer
* The decorated Asset Collection Renderer.
* @param \Drupal\Core\Config\ConfigFactoryInterface $config
* The Config Factory service.
* @param \Drupal\Core\Extension\ModuleHandlerInterface $moduleHandler
* The Module Handler service.
* @param \Drupal\attachinline\EventSubscriber\CspSubscriber $cspSubscriber
* Attach Inline's CSP Event Subscriber service.
* @param \Drupal\csp\Nonce $nonce
* The CSP Nonce service if the module is enabled.
*/
public function __construct(
AssetCollectionRendererInterface $assetCollectionRenderer,
ConfigFactoryInterface $config,
ModuleHandlerInterface $moduleHandler,
CspSubscriber $cspSubscriber,
?Nonce $nonce = NULL,
) {
$this->decorated = $assetCollectionRenderer;
$this->config = $config;
$this->moduleHandler = $moduleHandler;
$this->cspSubscriber = $cspSubscriber;
$this->nonce = $nonce;
}
/**
* {@inheritDoc}
*
* Override core's renderer to allow inline style elements.
*
* @see \Drupal\Core\Asset\CssCollectionRenderer
*/
public function render(array $css_assets) {
$elements = [];
// Defaults for each STYLE element.
$element_defaults = [
'#type' => 'html_tag',
'#tag' => 'style',
'#value' => '',
'#attributes' => [],
];
// Loop through all JS assets.
foreach ($css_assets as $key => $css_asset) {
if ($css_asset['type'] != 'inline') {
continue;
}
$element = $element_defaults;
$element['#value'] = AttachInlineMarkup::create($css_asset['data']);
if (!empty($css_asset['attributes'])) {
$element['#attributes'] += $css_asset['attributes'];
}
if ($this->moduleHandler->moduleExists('csp')) {
$allowMethod = $this->config->get('attachinline.settings')->get('csp-allow-method') ?? 'hash';
if ($allowMethod == 'nonce') {
$element['#attributes']['nonce'] = $this->nonce->getValue();
$this->cspSubscriber->registerNonce('style-src');
$this->cspSubscriber->registerNonce('style-src-elem');
}
else {
$cspHash = Csp::calculateHash($css_asset['data']);
$this->cspSubscriber->registerHash('style-src', $cspHash);
$this->cspSubscriber->registerHash('style-src-elem', $cspHash);
}
}
$elements[] = $element;
// Remove the snippet so that the remaining assets can be passed to the
// core renderer.
unset($css_assets[$key]);
}
// Add inline snippets to the end.
return array_merge($this->decorated->render($css_assets), $elements);
}
}
