diff --git a/src/Symfony/Bundle/WebProfilerBundle/CHANGELOG.md b/src/Symfony/Bundle/WebProfilerBundle/CHANGELOG.md
index 6d2f8eb554644..539d814d2a438 100644
--- a/src/Symfony/Bundle/WebProfilerBundle/CHANGELOG.md
+++ b/src/Symfony/Bundle/WebProfilerBundle/CHANGELOG.md
@@ -1,6 +1,11 @@
CHANGELOG
=========
+7.3
+---
+
+ * Add `ajax_replace` option for replacing toolbar on AJAX requests
+
7.2
---
@@ -65,7 +70,7 @@ CHANGELOG
-----
* added information about orphaned events
- * made the toolbar auto-update with info from ajax reponses when they set the
+ * made the toolbar auto-update with info from ajax responses when they set the
`Symfony-Debug-Toolbar-Replace header` to `1`
4.0.0
diff --git a/src/Symfony/Bundle/WebProfilerBundle/DependencyInjection/Configuration.php b/src/Symfony/Bundle/WebProfilerBundle/DependencyInjection/Configuration.php
index 51ddad76fdbea..d9ca50a27af21 100644
--- a/src/Symfony/Bundle/WebProfilerBundle/DependencyInjection/Configuration.php
+++ b/src/Symfony/Bundle/WebProfilerBundle/DependencyInjection/Configuration.php
@@ -33,7 +33,16 @@ public function getConfigTreeBuilder(): TreeBuilder
$treeBuilder->getRootNode()
->children()
- ->booleanNode('toolbar')->defaultFalse()->end()
+ ->arrayNode('toolbar')
+ ->info('Profiler toolbar configuration')
+ ->canBeEnabled()
+ ->children()
+ ->booleanNode('ajax_replace')
+ ->defaultFalse()
+ ->info('Replace toolbar on AJAX requests')
+ ->end()
+ ->end()
+ ->end()
->booleanNode('intercept_redirects')->defaultFalse()->end()
->scalarNode('excluded_ajax_paths')->defaultValue('^/((index|app(_[\w]+)?)\.php/)?_wdt')->end()
->end()
diff --git a/src/Symfony/Bundle/WebProfilerBundle/DependencyInjection/WebProfilerExtension.php b/src/Symfony/Bundle/WebProfilerBundle/DependencyInjection/WebProfilerExtension.php
index 6ad6982ce487b..d1867029d7ecd 100644
--- a/src/Symfony/Bundle/WebProfilerBundle/DependencyInjection/WebProfilerExtension.php
+++ b/src/Symfony/Bundle/WebProfilerBundle/DependencyInjection/WebProfilerExtension.php
@@ -46,11 +46,12 @@ public function load(array $configs, ContainerBuilder $container): void
$loader = new PhpFileLoader($container, new FileLocator(__DIR__.'/../Resources/config'));
$loader->load('profiler.php');
- if ($config['toolbar'] || $config['intercept_redirects']) {
+ if ($config['toolbar']['enabled'] || $config['intercept_redirects']) {
$loader->load('toolbar.php');
$container->getDefinition('web_profiler.debug_toolbar')->replaceArgument(4, $config['excluded_ajax_paths']);
+ $container->getDefinition('web_profiler.debug_toolbar')->replaceArgument(7, $config['toolbar']['ajax_replace']);
$container->setParameter('web_profiler.debug_toolbar.intercept_redirects', $config['intercept_redirects']);
- $container->setParameter('web_profiler.debug_toolbar.mode', $config['toolbar'] ? WebDebugToolbarListener::ENABLED : WebDebugToolbarListener::DISABLED);
+ $container->setParameter('web_profiler.debug_toolbar.mode', $config['toolbar']['enabled'] ? WebDebugToolbarListener::ENABLED : WebDebugToolbarListener::DISABLED);
}
$container->getDefinition('debug.file_link_formatter')
diff --git a/src/Symfony/Bundle/WebProfilerBundle/EventListener/WebDebugToolbarListener.php b/src/Symfony/Bundle/WebProfilerBundle/EventListener/WebDebugToolbarListener.php
index a13421e7ac63f..de7bb7b001ca0 100644
--- a/src/Symfony/Bundle/WebProfilerBundle/EventListener/WebDebugToolbarListener.php
+++ b/src/Symfony/Bundle/WebProfilerBundle/EventListener/WebDebugToolbarListener.php
@@ -48,6 +48,7 @@ public function __construct(
private string $excludedAjaxPaths = '^/bundles|^/_wdt',
private ?ContentSecurityPolicyHandler $cspHandler = null,
private ?DumpDataCollector $dumpDataCollector = null,
+ private bool $ajaxReplace = false,
) {
}
@@ -96,6 +97,10 @@ public function onKernelResponse(ResponseEvent $event): void
// do not capture redirects or modify XML HTTP Requests
if ($request->isXmlHttpRequest()) {
+ if (self::ENABLED === $this->mode && $this->ajaxReplace && !$response->headers->has('Symfony-Debug-Toolbar-Replace')) {
+ $response->headers->set('Symfony-Debug-Toolbar-Replace', '1');
+ }
+
return;
}
diff --git a/src/Symfony/Bundle/WebProfilerBundle/Resources/config/schema/webprofiler-1.0.xsd b/src/Symfony/Bundle/WebProfilerBundle/Resources/config/schema/webprofiler-1.0.xsd
index e22105a178fa7..0a3a0767f176c 100644
--- a/src/Symfony/Bundle/WebProfilerBundle/Resources/config/schema/webprofiler-1.0.xsd
+++ b/src/Symfony/Bundle/WebProfilerBundle/Resources/config/schema/webprofiler-1.0.xsd
@@ -9,6 +9,14 @@
+
+
+
+
+
+
+
+
diff --git a/src/Symfony/Bundle/WebProfilerBundle/Resources/config/toolbar.php b/src/Symfony/Bundle/WebProfilerBundle/Resources/config/toolbar.php
index 473b3630f7dd4..c264b77d6f6e7 100644
--- a/src/Symfony/Bundle/WebProfilerBundle/Resources/config/toolbar.php
+++ b/src/Symfony/Bundle/WebProfilerBundle/Resources/config/toolbar.php
@@ -25,6 +25,7 @@
abstract_arg('paths that should be excluded from the AJAX requests shown in the toolbar'),
service('web_profiler.csp.handler'),
service('data_collector.dump')->ignoreOnInvalid(),
+ abstract_arg('whether to replace toolbar on AJAX requests or not'),
])
->tag('kernel.event_subscriber')
;
diff --git a/src/Symfony/Bundle/WebProfilerBundle/Tests/DependencyInjection/ConfigurationTest.php b/src/Symfony/Bundle/WebProfilerBundle/Tests/DependencyInjection/ConfigurationTest.php
index d957cafc48616..6a9fc99f10281 100644
--- a/src/Symfony/Bundle/WebProfilerBundle/Tests/DependencyInjection/ConfigurationTest.php
+++ b/src/Symfony/Bundle/WebProfilerBundle/Tests/DependencyInjection/ConfigurationTest.php
@@ -36,7 +36,10 @@ public static function getDebugModes()
'options' => [],
'expectedResult' => [
'intercept_redirects' => false,
- 'toolbar' => false,
+ 'toolbar' => [
+ 'enabled' => false,
+ 'ajax_replace' => false,
+ ],
'excluded_ajax_paths' => '^/((index|app(_[\w]+)?)\.php/)?_wdt',
],
],
@@ -44,7 +47,10 @@ public static function getDebugModes()
'options' => ['toolbar' => true],
'expectedResult' => [
'intercept_redirects' => false,
- 'toolbar' => true,
+ 'toolbar' => [
+ 'enabled' => true,
+ 'ajax_replace' => false,
+ ],
'excluded_ajax_paths' => '^/((index|app(_[\w]+)?)\.php/)?_wdt',
],
],
@@ -52,10 +58,24 @@ public static function getDebugModes()
'options' => ['excluded_ajax_paths' => 'test'],
'expectedResult' => [
'intercept_redirects' => false,
- 'toolbar' => false,
+ 'toolbar' => [
+ 'enabled' => false,
+ 'ajax_replace' => false,
+ ],
'excluded_ajax_paths' => 'test',
],
],
+ [
+ 'options' => ['toolbar' => ['ajax_replace' => true]],
+ 'expectedResult' => [
+ 'intercept_redirects' => false,
+ 'toolbar' => [
+ 'enabled' => true,
+ 'ajax_replace' => true,
+ ],
+ 'excluded_ajax_paths' => '^/((index|app(_[\w]+)?)\.php/)?_wdt',
+ ],
+ ],
];
}
@@ -78,7 +98,10 @@ public static function getInterceptRedirectsConfiguration()
'interceptRedirects' => true,
'expectedResult' => [
'intercept_redirects' => true,
- 'toolbar' => false,
+ 'toolbar' => [
+ 'enabled' => false,
+ 'ajax_replace' => false,
+ ],
'excluded_ajax_paths' => '^/((index|app(_[\w]+)?)\.php/)?_wdt',
],
],
@@ -86,7 +109,10 @@ public static function getInterceptRedirectsConfiguration()
'interceptRedirects' => false,
'expectedResult' => [
'intercept_redirects' => false,
- 'toolbar' => false,
+ 'toolbar' => [
+ 'enabled' => false,
+ 'ajax_replace' => false,
+ ],
'excluded_ajax_paths' => '^/((index|app(_[\w]+)?)\.php/)?_wdt',
],
],
diff --git a/src/Symfony/Bundle/WebProfilerBundle/Tests/EventListener/WebDebugToolbarListenerTest.php b/src/Symfony/Bundle/WebProfilerBundle/Tests/EventListener/WebDebugToolbarListenerTest.php
index 33bf1a32d27f8..ff9bd096fb13f 100644
--- a/src/Symfony/Bundle/WebProfilerBundle/Tests/EventListener/WebDebugToolbarListenerTest.php
+++ b/src/Symfony/Bundle/WebProfilerBundle/Tests/EventListener/WebDebugToolbarListenerTest.php
@@ -357,6 +357,66 @@ public function testNullContentTypeWithNoDebugEnv()
$this->expectNotToPerformAssertions();
}
+ public function testAjaxReplaceHeaderOnDisabledToolbar()
+ {
+ $response = new Response();
+ $event = new ResponseEvent($this->createMock(Kernel::class), new Request(), HttpKernelInterface::MAIN_REQUEST, $response);
+
+ $listener = new WebDebugToolbarListener($this->getTwigMock(), false, WebDebugToolbarListener::DISABLED, null, '', null, null, true);
+ $listener->onKernelResponse($event);
+
+ $this->assertFalse($response->headers->has('Symfony-Debug-Toolbar-Replace'));
+ }
+
+ public function testAjaxReplaceHeaderOnDisabledReplace()
+ {
+ $response = new Response();
+ $event = new ResponseEvent($this->createMock(Kernel::class), new Request(), HttpKernelInterface::MAIN_REQUEST, $response);
+
+ $listener = new WebDebugToolbarListener($this->getTwigMock(), false, WebDebugToolbarListener::ENABLED, null, '', null, null);
+ $listener->onKernelResponse($event);
+
+ $this->assertFalse($response->headers->has('Symfony-Debug-Toolbar-Replace'));
+ }
+
+ public function testAjaxReplaceHeaderOnEnabledAndNonXHR()
+ {
+ $response = new Response();
+ $event = new ResponseEvent($this->createMock(Kernel::class), new Request(), HttpKernelInterface::MAIN_REQUEST, $response);
+
+ $listener = new WebDebugToolbarListener($this->getTwigMock(), false, WebDebugToolbarListener::ENABLED, null, '', null, null, true);
+ $listener->onKernelResponse($event);
+
+ $this->assertFalse($response->headers->has('Symfony-Debug-Toolbar-Replace'));
+ }
+
+ public function testAjaxReplaceHeaderOnEnabledAndXHR()
+ {
+ $request = new Request();
+ $request->headers->set('X-Requested-With', 'XMLHttpRequest');
+ $response = new Response();
+ $event = new ResponseEvent($this->createMock(Kernel::class), $request, HttpKernelInterface::MAIN_REQUEST, $response);
+
+ $listener = new WebDebugToolbarListener($this->getTwigMock(), false, WebDebugToolbarListener::ENABLED, null, '', null, null, true);
+ $listener->onKernelResponse($event);
+
+ $this->assertSame('1', $response->headers->get('Symfony-Debug-Toolbar-Replace'));
+ }
+
+ public function testAjaxReplaceHeaderOnEnabledAndXHRButPreviouslySet()
+ {
+ $request = new Request();
+ $request->headers->set('X-Requested-With', 'XMLHttpRequest');
+ $response = new Response();
+ $response->headers->set('Symfony-Debug-Toolbar-Replace', '0');
+ $event = new ResponseEvent($this->createMock(Kernel::class), $request, HttpKernelInterface::MAIN_REQUEST, $response);
+
+ $listener = new WebDebugToolbarListener($this->getTwigMock(), false, WebDebugToolbarListener::ENABLED, null, '', null, null, true);
+ $listener->onKernelResponse($event);
+
+ $this->assertSame('0', $response->headers->get('Symfony-Debug-Toolbar-Replace'));
+ }
+
protected function getTwigMock($render = 'WDT')
{
$templating = $this->createMock(Environment::class);