Skip to content

Commit ee67b09

Browse files
committed
Bar: forwards bar from hidden iframe to parent window via postMessage [Closes #427]
When a form posts into a hidden iframe, Tracy used to render its bar inside the invisible iframe and the request was effectively lost. Tracy now detects iframe context (Sec-Fetch-Dest header), stores the bar as an ajax-style partial in session and emits a tiny postMessage to the parent. The parent's bar listens for these messages and loads the content like any AJAX request.
1 parent be2fa53 commit ee67b09

6 files changed

Lines changed: 90 additions & 8 deletions

File tree

examples/iframe-form.php

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
<?php declare(strict_types=1);
2+
3+
require __DIR__ . '/../src/tracy.php';
4+
5+
use Tracy\Debugger;
6+
7+
// For security reasons, Tracy is visible only on localhost.
8+
// You may force Tracy to run in development mode by passing the Debugger::Development instead of Debugger::Detect.
9+
Debugger::enable(Debugger::Detect, __DIR__ . '/log');
10+
11+
12+
// Form posted into a hidden iframe – Tracy starts a new bar instance inside
13+
// the iframe, so the request is effectively invisible (issue nette/tracy#427).
14+
if (!empty($_POST['data'])) {
15+
bdump($_POST['data'], 'received in hidden iframe');
16+
echo '<script>parent.setStatus(' . json_encode($_POST['data']) . ' + " received!")</script>';
17+
exit;
18+
}
19+
20+
?>
21+
<!DOCTYPE html><html class=arrow><link rel="stylesheet" href="assets/style.css">
22+
23+
<h1>Tracy: hidden iframe form demo (issue #427)</h1>
24+
25+
<p>Submitting the form posts into a hidden iframe. The request <em>is</em> handled
26+
by Tracy, but the bar is rendered inside the invisible iframe, so it is not
27+
shown in the parent page.</p>
28+
29+
<iframe name="hiddenIframe"></iframe>
30+
31+
<form target="hiddenIframe" action="iframe-form.php" method="post">
32+
<input type="text" name="data" value="hello">
33+
<input type="submit">
34+
</form>
35+
36+
<h2 id="status">Waiting for submit...</h2>
37+
38+
<script>
39+
function setStatus(text) {
40+
document.getElementById('status').innerText = text;
41+
}
42+
</script>
43+
44+
<?php
45+
if (Debugger::$productionMode) {
46+
echo '<p><b>For security reasons, Tracy is visible only on localhost. Look into the source code to see how to enable Tracy.</b></p>';
47+
}

src/Tracy/Bar/Bar.php

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,10 @@ public function renderLoader(DeferredContent $defer): void
5757
}
5858

5959
$this->loaderRendered = true;
60+
if (Helpers::isIframe()) {
61+
return;
62+
}
63+
6064
$requestId = $defer->getRequestId();
6165
$async = true;
6266
require __DIR__ . '/dist/loader.phtml';
@@ -87,6 +91,13 @@ public function render(DeferredContent $defer): void
8791
'time' => time(),
8892
];
8993
}
94+
} elseif (Helpers::isHtmlMode() && Helpers::isIframe()) {
95+
if ($defer->isAvailable()) {
96+
$defer->addSetup('Tracy.Debug.loadAjax', $this->renderPartial('iframe', '-iframe:' . $requestId));
97+
echo '<script' . Helpers::getNonce(attr: true) . '>'
98+
. '(window.parent !== window) && parent.postMessage({tracyIframeBar:' . Helpers::jsonEncode($requestId) . '}, "*")'
99+
. '</script>';
100+
}
90101
} elseif (Helpers::isHtmlMode()) {
91102
if (preg_match('#^Content-Length:#im', implode("\n", headers_list()))) {
92103
Debugger::log(new \LogicException('Tracy cannot display the Bar because the Content-Length header is being sent'), Debugger::EXCEPTION);

src/Tracy/Bar/assets/bar.js

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -411,6 +411,7 @@ class Debug {
411411

412412
Debug.captureWindow();
413413
Debug.captureAjax();
414+
Debug.captureIframe();
414415

415416
Tracy.TableSort.init();
416417
Tracy.Tabs.init();
@@ -523,6 +524,16 @@ class Debug {
523524
}
524525

525526

527+
static captureIframe() {
528+
window.addEventListener('message', (e) => {
529+
let id = e.data && e.data.tracyIframeBar;
530+
if (typeof id === 'string' && /^\w{10,15}$/.test(id)) {
531+
Debug.loadScript(baseUrl + '_tracy_bar=content-ajax.' + id + '&XDEBUG_SESSION_STOP=1&v=' + Math.random());
532+
}
533+
});
534+
}
535+
536+
526537
static loadScript(url) {
527538
if (Debug.scriptElem) {
528539
Debug.scriptElem.remove();

src/Tracy/Bar/assets/bar.latte

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@
1111
<li><span title="Previous request before redirect">redirect</span></li>
1212
{case ajax}
1313
<li>AJAX</li>
14+
{case iframe}
15+
<li>iframe</li>
1416
{/switch}
1517

1618
{foreach $panels as $panel}

src/Tracy/Bar/dist/bar.phtml

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -22,21 +22,24 @@ if ($ʟ_switch === ('main')) /* pos 6:3 */ {
2222
} elseif ($ʟ_switch === ('ajax')) /* pos 12:3 */ {
2323
echo ' <li>AJAX</li>
2424
';
25+
} elseif ($ʟ_switch === ('iframe')) /* pos 14:3 */ {
26+
echo ' <li>iframe</li>
27+
';
2528
}
2629
echo "\n";
27-
foreach ($panels as $panel) /* pos 16:2 */ {
28-
if ($panel->tab) /* pos 17:7 */ {
30+
foreach ($panels as $panel) /* pos 18:2 */ {
31+
if ($panel->tab) /* pos 19:7 */ {
2932
echo ' <li>';
30-
if ($panel->panel) /* pos 17:26 */ {
33+
if ($panel->panel) /* pos 19:26 */ {
3134
echo '<a href="#" rel="tracy-debug-panel-';
32-
echo Tracy\Helpers::escapeHtml($panel->id) /* pos 17:79 */;
35+
echo Tracy\Helpers::escapeHtml($panel->id) /* pos 19:79 */;
3336
echo '">';
34-
echo trim($panel->tab) /* pos 17:93 */;
37+
echo trim($panel->tab) /* pos 19:93 */;
3538
echo '</a>
3639
';
37-
} else /* pos 18:3 */ {
40+
} else /* pos 20:3 */ {
3841
echo ' <span>';
39-
echo trim($panel->tab) /* pos 18:15 */;
42+
echo trim($panel->tab) /* pos 20:15 */;
4043
echo '</span>
4144
';
4245
}
@@ -47,7 +50,7 @@ foreach ($panels as $panel) /* pos 16:2 */ {
4750
}
4851

4952
echo "\n";
50-
if ($type === 'main') /* pos 22:6 */ {
53+
if ($type === 'main') /* pos 24:6 */ {
5154
echo ' <li><a href="#" data-tracy-action="close" title="close debug bar">&times;</a></li>
5255
';
5356
}

src/Tracy/Helpers.php

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -396,6 +396,14 @@ public static function isRedirect(): bool
396396
}
397397

398398

399+
/** @internal */
400+
public static function isIframe(): bool
401+
{
402+
$dest = $_SERVER['HTTP_SEC_FETCH_DEST'] ?? '';
403+
return $dest === 'iframe' || $dest === 'frame';
404+
}
405+
406+
399407
/** @internal */
400408
public static function createId(): string
401409
{

0 commit comments

Comments
 (0)