Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 42 additions & 0 deletions php-transformer/src/HtmlToBlocks/DocumentNormalizationPolicy.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
<?php
declare(strict_types=1);

namespace Automattic\BlocksEngine\PhpTransformer\HtmlToBlocks;

/**
* Extracts the usable fallback from complete legacy frameset documents before
* DOM parsing can retain frame navigation as page content.
*/
final class DocumentNormalizationPolicy
{
/**
* @return array{html: string, fallback: array<string, mixed>|null}
*/
public function normalize(string $html): array
{
if (
! preg_match('/<frameset\b[^>]*>/i', $html)
|| ! preg_match('/<\/frameset\s*>/i', $html)
|| ! preg_match('/<noframes\b[^>]*>(.*?)<\/noframes\s*>/is', $html, $matches)
) {
return array(
'html' => $html,
'fallback' => null,
);
}

$fallbackHtml = preg_replace('/<\/?frameset\b[^>]*>/i', '', $matches[1]) ?? $matches[1];
$fallbackHtml = preg_replace('/<frame\b[^>]*>/i', '', $fallbackHtml) ?? $fallbackHtml;

return array(
'html' => $fallbackHtml,
'fallback' => FallbackDiagnostic::build(array(
'type' => 'legacy_frameset_navigation',
'reason' => 'unsupported_legacy_frameset_navigation',
'diagnostic_code' => 'html_legacy_frameset_navigation',
'message' => 'Legacy frameset navigation was removed and its noframes fallback content was converted.',
'tag' => 'frameset',
)),
);
}
}
13 changes: 13 additions & 0 deletions php-transformer/src/HtmlToBlocks/FallbackDiagnostic.php
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,19 @@ private static function defaults(array $fields): array
'suggested_primitive' => 'core/html',
'materialization_hint' => 'preserve_sanitized_markup_until_a_specific_block_mapping_exists',
),
'html_legacy_frameset_navigation' => array(
'severity' => 'warning',
'conversion_classification' => 'unsupported_loss',
'loss_class' => 'unsupported_legacy_navigation_loss',
'diagnostic_class' => 'unsupported_legacy_navigation',
'preservation_strategy' => 'convert_noframes_fallback_content',
'runtime_requirement' => 'none',
'recoverability' => 'recoverable_with_manual_navigation_mapping',
'actionability' => 'map_legacy_frame_destinations_to_pages_or_navigation_blocks',
'suggested_repair_class' => 'map_legacy_frameset_navigation',
'suggested_primitive' => 'navigation',
'materialization_hint' => 'convert_the_noframes_fallback_content_and_rebuild_frame_destinations_as_standard_navigation',
),
'interactive_control_behavior_lost' => array(
'severity' => 'warning',
'conversion_classification' => 'behavior_loss',
Expand Down
5 changes: 3 additions & 2 deletions php-transformer/src/HtmlToBlocks/HtmlTransformer.php
Original file line number Diff line number Diff line change
Expand Up @@ -406,7 +406,8 @@ public function transform(string $html, array $options = array()): TransformerRe
), $this->fallbackProvenance),
);

$normalizedHtml = $this->normalizeHtml5VoidElements($this->documentBodyHtml($html));
$documentNormalization = ( new DocumentNormalizationPolicy() )->normalize($html);
$normalizedHtml = $this->normalizeHtml5VoidElements($this->documentBodyHtml($documentNormalization['html']));
$document = new DOMDocument();
$previous = libxml_use_internal_errors(true);
$loaded = $document->loadHTML('<?xml encoding="utf-8" ?><body>' . $normalizedHtml . '</body>', LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD);
Expand Down Expand Up @@ -461,7 +462,7 @@ public function transform(string $html, array $options = array()): TransformerRe
);
}

$fallbacks = array();
$fallbacks = null === $documentNormalization['fallback'] ? array() : array( $documentNormalization['fallback'] );
$interactionCandidates = $this->interactionCandidates($body);
$this->collectSupersededNavToggleSelectors($body);
$blocks = $this->deduplicateNavigationBlocks($this->convertChildren($body, $fallbacks, true));
Expand Down
21 changes: 21 additions & 0 deletions php-transformer/tests/contract/finding-contract.php
Original file line number Diff line number Diff line change
Expand Up @@ -257,6 +257,27 @@
$totalFindings += $walk($result, "html:{$name}");
}

$framesetResult = ( new HtmlTransformer() )->transform(
'<!doctype html><html><frameset cols="180,*"><frame src="#main-content" name="main"><noframes><body><main id="main-content"><p>Kept fallback</p></main></body></noframes></frameset></html>'
)->toArray();
$totalFindings += $walk($framesetResult, 'html:legacy-frameset');
$assert(
'html_legacy_frameset_navigation' === ($framesetResult['fallbacks'][0]['diagnostic_code'] ?? null),
'legacy frameset navigation emits a typed diagnostic'
);
$assert(
str_contains((string) ($framesetResult['serialized_blocks'] ?? ''), 'Kept fallback'),
'legacy frameset conversion retains noframes fallback content'
);
$assert(
! str_contains((string) ($framesetResult['serialized_blocks'] ?? ''), '<frame'),
'legacy frameset conversion excludes frame navigation markup'
);
$assert(
! str_contains((string) ($framesetResult['serialized_blocks'] ?? ''), '#main-content'),
'legacy frameset conversion excludes frame source values'
);

// Canvas requires a runtime selector hint to be flagged as a runtime island.
$canvasResult = ( new HtmlTransformer() )->transform(
'<main><canvas id="scene">Fallback</canvas><script>document.getElementById("scene").getContext("2d");</script></main>',
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
{
"schema": "blocks-engine/php-transformer/parity-fixture/v1",
"name": "html-legacy-frameset-noframes-fallback",
"description": "Converts noframes fallback content while removing unsupported self-referential legacy frame navigation.",
"operation": "html_transformer.transform",
"input": {
"content": "<!doctype html><html><head><title>Legacy frame page</title></head><frameset cols=\"180,*\"><frame src=\"#main-content\" name=\"main\"><noframes><body><main id=\"main-content\"><p>Kept fallback</p></main></body></noframes></frameset></html>"
},
"expected_blocks": [
{ "path": "blocks.0", "name": "core/group" },
{ "path": "blocks.0.innerBlocks.0", "name": "core/paragraph", "attrs": { "content": "Kept fallback" } }
],
"expected_fallbacks": [
{ "type": "legacy_frameset_navigation", "reason": "unsupported_legacy_frameset_navigation", "diagnostic_code": "html_legacy_frameset_navigation", "tag": "frameset" }
],
"expect": [
{ "path": "status", "assert": "equals", "value": "success" },
{ "path": "fallbacks", "assert": "count", "count": 1 },
{ "path": "diagnostics.1.code", "assert": "equals", "value": "html_legacy_frameset_navigation" },
{ "path": "serialized_blocks", "assert": "contains", "value": "Kept fallback" },
{ "path": "serialized_blocks", "assert": "not_contains", "value": "<frame" },
{ "path": "serialized_blocks", "assert": "not_contains", "value": "<frameset" },
{ "path": "serialized_blocks", "assert": "not_contains", "value": "#main-content" }
]
}
Loading