From 49d4b6dcf23f20bfb7f8771a2ae93bceb6a0ddc1 Mon Sep 17 00:00:00 2001 From: Jon Surrell Date: Thu, 7 Nov 2024 17:42:05 +0100 Subject: [PATCH 01/23] Add advanced fragment handling --- html-api-debugger/html-api-debugger.php | 14 ++-- html-api-debugger/html-api-integration.php | 62 ++++++----------- html-api-debugger/interactivity.php | 18 ++--- html-api-debugger/view.js | 79 +++++++++++----------- 4 files changed, 76 insertions(+), 97 deletions(-) diff --git a/html-api-debugger/html-api-debugger.php b/html-api-debugger/html-api-debugger.php index b1fa774..a58c464 100644 --- a/html-api-debugger/html-api-debugger.php +++ b/html-api-debugger/html-api-debugger.php @@ -44,8 +44,7 @@ function () { // phpcs:ignore Universal.Operators.DisallowShortTernary.Found $html = $request->get_json_params()['html'] ?: ''; $options = array( - 'quirks_mode' => $request->get_json_params()['quirksMode'] ?? false, - 'full_parser' => $request->get_json_params()['fullParser'] ?? false, + 'context_html' => $request->get_json_params()['contextHTML'] ?: null, ); return prepare_html_result_object( $html, $options ); }, @@ -111,14 +110,19 @@ function () { function () { require_once __DIR__ . '/interactivity.php'; + $options = array( + 'context_html' => null, + ); + $html = ''; // phpcs:disable WordPress.Security.NonceVerification.Recommended if ( isset( $_GET['html'] ) && is_string( $_GET['html'] ) ) { $html = stripslashes( $_GET['html'] ); } - - $options = array(); - // @todo Add query args for other options + if ( isset( $_GET['contextHTML'] ) && is_string( $_GET['contextHTML'] ) ) { + $options['context_html'] = stripslashes( $_GET['contextHTML'] ); + } + // phpcs:enable WordPress.Security.NonceVerification.Recommended // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped echo namespace\Interactivity\generate_page( $html, $options ); diff --git a/html-api-debugger/html-api-integration.php b/html-api-debugger/html-api-integration.php index 3e428ab..fd8ca9f 100644 --- a/html-api-debugger/html-api-integration.php +++ b/html-api-debugger/html-api-integration.php @@ -12,8 +12,7 @@ * Get information about HTML API supported features */ function get_supports(): array { - $html_processor_rc = new ReflectionClass( WP_HTML_Processor::class ); - $html_processor_state_rc = new ReflectionClass( WP_HTML_Processor_State::class ); + $html_processor_rc = new ReflectionClass( WP_HTML_Processor::class ); return array( 'is_virtual' => $html_processor_rc->hasMethod( 'is_virtual' ), @@ -21,6 +20,7 @@ function get_supports(): array { 'quirks_mode' => $html_processor_rc->hasProperty( 'compat_mode' ), 'doctype' => method_exists( WP_HTML_Processor::class, 'get_doctype_info' ), 'normalize' => method_exists( WP_HTML_Processor::class, 'normalize' ), + 'create_fragment_advanced' => method_exists( WP_HTML_Processor::class, 'create_fragment_at_current_node' ), ); } @@ -53,22 +53,25 @@ function get_tree( string $html, array $options ): array { $processor_bookmarks = new ReflectionProperty( WP_HTML_Processor::class, 'bookmarks' ); $processor_bookmarks->setAccessible( true ); - $use_full_parser = method_exists( WP_HTML_Processor::class, 'create_full_parser' ) && ( $options['full_parser'] ?? false ); - - $processor = $use_full_parser - ? WP_HTML_Processor::create_full_parser( $html ) - : WP_HTML_Processor::create_fragment( $html ); - - $doctype_value = $use_full_parser ? '' : 'html'; if ( - ! $use_full_parser && - ( $options['quirks_mode'] ?? false ) && - property_exists( WP_HTML_Processor::class, 'compat_mode' ) && - defined( WP_HTML_Processor::class . '::QUIRKS_MODE' ) + method_exists( WP_HTML_Processor::class, 'create_fragment_at_current_node' ) && + $options['context_html'] ) { - $processor_compat_mode = new ReflectionProperty( WP_HTML_Processor::class, 'compat_mode' ); - $processor_compat_mode->setValue( $processor, WP_HTML_Processor::QUIRKS_MODE ); - $doctype_value = ''; + $context_processor = WP_HTML_Processor::create_full_parser( $options['context_html'] ); + + while ( $context_processor->next_tag() ) { + $context_processor->set_bookmark( 'final_node' ); + } + if ( $context_processor->has_bookmark( 'final_node' ) ) { + $context_processor->seek( 'final_node' ); + $processor = $context_processor->create_fragment_at_current_node( $html ); + } + + if ( ! isset( $processor ) ) { + throw new Exception( 'Could not create processor from context HTML.' ); + } + } else { + $processor = WP_HTML_Processor::create_full_parser( $html ); } $rc = new ReflectionClass( WP_HTML_Processor::class ); @@ -120,33 +123,6 @@ function get_tree( string $html, array $options ): array { ); $cursor = array( 0 ); - if ( ! $use_full_parser ) { - $tree['childNodes'][] = array( - 'nodeType' => NODE_TYPE_DOCUMENT_TYPE, - 'nodeName' => $doctype_value, - 'nodeValue' => '', - ); - $tree['childNodes'][] = array( - 'nodeType' => NODE_TYPE_ELEMENT, - 'nodeName' => 'HTML', - 'attributes' => array(), - 'childNodes' => array( - array( - 'nodeType' => NODE_TYPE_ELEMENT, - 'nodeName' => 'HEAD', - 'attributes' => array(), - 'childNodes' => array(), - ), - array( - 'nodeType' => NODE_TYPE_ELEMENT, - 'nodeName' => 'BODY', - 'attributes' => array(), - 'childNodes' => array(), - ), - ), - ); - $cursor = array( 1, 1 ); - } $compat_mode = 'CSS1Compat'; $doctype_name = null; diff --git a/html-api-debugger/interactivity.php b/html-api-debugger/interactivity.php index a406b74..735fa86 100644 --- a/html-api-debugger/interactivity.php +++ b/html-api-debugger/interactivity.php @@ -56,8 +56,7 @@ function generate_page( string $html, array $options ): string { 'showClosers' => false, 'showInvisible' => false, 'showVirtual' => false, - 'quirksMode' => false, - 'fullParser' => false, + 'contextHTML' => $options['context_html'] ?? '', 'hoverInfo' => 'breadcrumbs', 'hoverBreadcrumbs' => true, @@ -76,6 +75,7 @@ function generate_page( string $html, array $options ): string { data-wp-interactive="" data-wp-watch--a="watch" data-wp-watch--b="watchDom" + data-wp-watch--url="watchURL" data-wp-run="run" class="html-api-debugger-container html-api-debugger--grid" > @@ -89,11 +89,6 @@ class="html-api-debugger-container html-api-debugger--grid" wrap="off" > -

- Note: Because HTML API operates in body at this time, this will be prepended: -
- -

Rendered output

@@ -153,8 +148,13 @@ class="html-api-debugger-container html-api-debugger--grid" - - +
+ +
@@ -151,6 +152,7 @@ class="html-api-debugger-container html-api-debugger--grid"
diff --git a/html-api-debugger/view.js b/html-api-debugger/view.js index c053733..1186357 100644 --- a/html-api-debugger/view.js +++ b/html-api-debugger/view.js @@ -31,7 +31,7 @@ let mutationObserver = null; * @property {string|undefined} doctypeName * @property {string|undefined} doctypeSystemId * @property {string|undefined} doctypePublicId - * + * @property {string|undefined} contextNode * * @typedef HTMLAPISpan * @property {number} start @@ -328,8 +328,22 @@ const store = createStore(NS, { store.state.DOM.doctypeSystemId = doc.doctype?.systemId; store.state.DOM.doctypePublicId = doc.doctype?.publicId; + /** @type {Element|null} */ + let contextElement = null; + if (store.state.contextHTML) { + const walker = doc.createTreeWalker(doc, NodeFilter.SHOW_ELEMENT); + while (walker.nextNode()) { + // @ts-expect-error It's an Element! + contextElement = walker.currentNode; + } + if (contextElement) { + store.state.DOM.contextNode = contextElement.outerHTML; + contextElement.innerHTML = store.state.html; + } + } + printHtmlApiTree( - doc, + contextElement ?? doc, // @ts-expect-error document.getElementById('dom_tree'), { @@ -339,6 +353,7 @@ const store = createStore(NS, { hoverInfo: store.state.hoverInfo, }, ); + mutationObserver?.observe(doc, { subtree: true, childList: true, @@ -540,7 +555,8 @@ const store = createStore(NS, { mutationObserver?.disconnect(); store.state.hasMutatedDom = false; - const html = store.state.playbackHTML ?? store.state.html; + const html = + store.state.playbackHTML ?? (store.state.contextHTML || store.state.html); iframeDocument.open(); iframeDocument.write(html); From 153a448c8be96a7d61fa04773bc685762084b0b0 Mon Sep 17 00:00:00 2001 From: Jon Surrell Date: Thu, 7 Nov 2024 18:27:04 +0100 Subject: [PATCH 03/23] Set innerHTML and read from context node in DOM --- html-api-debugger/html-api-integration.php | 2 ++ html-api-debugger/interactivity.php | 3 ++- html-api-debugger/view.js | 2 +- 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/html-api-debugger/html-api-integration.php b/html-api-debugger/html-api-integration.php index 255dff6..21959b9 100644 --- a/html-api-debugger/html-api-integration.php +++ b/html-api-debugger/html-api-integration.php @@ -133,6 +133,7 @@ function get_tree( string $html, array $options ): array { $doctype_name = null; $doctype_public_identifier = null; $doctype_system_identifier = null; + $context_node = $context_processor ? $context_processor->get_tag() : null; $playback = array(); @@ -388,6 +389,7 @@ function get_tree( string $html, array $options ): array { 'doctypeName' => $doctype_name, 'doctypePublicId' => $doctype_public_identifier, 'doctypeSystemId' => $doctype_system_identifier, + 'contextNode' => $context_node, ); } diff --git a/html-api-debugger/interactivity.php b/html-api-debugger/interactivity.php index cf79dc8..81248fc 100644 --- a/html-api-debugger/interactivity.php +++ b/html-api-debugger/interactivity.php @@ -111,7 +111,8 @@ class="html-api-debugger-container html-api-debugger--grid" Rendering mode: 
Doctype name: 
Doctype publicId: 
- Doctype systemId:  + Doctype systemId: 
+ Context node: 
Rendering mode: 
diff --git a/html-api-debugger/view.js b/html-api-debugger/view.js index 1186357..d675a51 100644 --- a/html-api-debugger/view.js +++ b/html-api-debugger/view.js @@ -337,7 +337,7 @@ const store = createStore(NS, { contextElement = walker.currentNode; } if (contextElement) { - store.state.DOM.contextNode = contextElement.outerHTML; + store.state.DOM.contextNode = contextElement.nodeName; contextElement.innerHTML = store.state.html; } } From 6b00a40248480637d09f35e32c8a81435ecff0a5 Mon Sep 17 00:00:00 2001 From: Jon Surrell Date: Thu, 7 Nov 2024 22:06:29 +0100 Subject: [PATCH 04/23] Improve rendering for fragment parsers --- html-api-debugger/html-api-integration.php | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/html-api-debugger/html-api-integration.php b/html-api-debugger/html-api-integration.php index 21959b9..1a885a2 100644 --- a/html-api-debugger/html-api-integration.php +++ b/html-api-debugger/html-api-integration.php @@ -53,6 +53,8 @@ function get_tree( string $html, array $options ): array { $processor_bookmarks = new ReflectionProperty( WP_HTML_Processor::class, 'bookmarks' ); $processor_bookmarks->setAccessible( true ); + $is_fragment_processor = false; + if ( method_exists( WP_HTML_Processor::class, 'create_fragment_at_current_node' ) && $options['context_html'] @@ -70,6 +72,8 @@ function get_tree( string $html, array $options ): array { if ( ! isset( $processor ) ) { throw new Exception( 'Could not create processor from context HTML.' ); } + + $is_fragment_processor = true; } else { $processor = WP_HTML_Processor::create_full_parser( $html ); } @@ -124,7 +128,7 @@ function get_tree( string $html, array $options ): array { $cursor = array( 0 ); - if ( $context_processor ) { + if ( $is_fragment_processor ) { $tree = array(); $cursor = array(); } @@ -133,7 +137,7 @@ function get_tree( string $html, array $options ): array { $doctype_name = null; $doctype_public_identifier = null; $doctype_system_identifier = null; - $context_node = $context_processor ? $context_processor->get_tag() : null; + $context_node = isset( $context_processor ) ? $context_processor->get_tag() : null; $playback = array(); @@ -152,7 +156,8 @@ function get_tree( string $html, array $options ): array { break; } - if ( ( count( $cursor ) + 1 ) > $get_current_depth() ) { + // Breadcrumbs and depth are off by one because they include an extra context node. + if ( ( count( $cursor ) + 1 ) > ( $get_current_depth() - ( $is_fragment_processor ? 1 : 0 ) ) ) { array_pop( $cursor ); } $current = &$tree; From 779b373a09c71c5bee263dd2201bad0f1c8ec9ef Mon Sep 17 00:00:00 2001 From: Jon Surrell Date: Thu, 7 Nov 2024 22:07:27 +0100 Subject: [PATCH 05/23] Simplofy flow when adding elements to tree --- html-api-debugger/html-api-integration.php | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/html-api-debugger/html-api-integration.php b/html-api-debugger/html-api-integration.php index 1a885a2..e2807c0 100644 --- a/html-api-debugger/html-api-integration.php +++ b/html-api-debugger/html-api-integration.php @@ -251,15 +251,13 @@ function get_tree( string $html, array $options ): array { if ( $processor->is_tag_closer() || + WP_HTML_Processor::is_void( $tag_name ) || ( $namespace !== 'html' && $processor->has_self_closing_flag() ) ) { break; } - if ( ! WP_HTML_Processor::is_void( $tag_name ) ) { - $cursor[] = count( $current['childNodes'] ) - 1; - } - + $cursor[] = count( $current['childNodes'] ) - 1; break; case '#text': From 08c08aca048f551060cb92cea8a8dae643e6c900 Mon Sep 17 00:00:00 2001 From: Jon Surrell Date: Thu, 7 Nov 2024 22:07:46 +0100 Subject: [PATCH 06/23] Remove redundant watchDom --- html-api-debugger/interactivity.php | 3 +-- html-api-debugger/view.js | 18 ------------------ 2 files changed, 1 insertion(+), 20 deletions(-) diff --git a/html-api-debugger/interactivity.php b/html-api-debugger/interactivity.php index 81248fc..2740a11 100644 --- a/html-api-debugger/interactivity.php +++ b/html-api-debugger/interactivity.php @@ -73,8 +73,7 @@ function generate_page( string $html, array $options ): string { ?>
void} onRenderedIframeLoad * * @property {()=>void} watch - * @property {()=>void} watchDom * @property {()=>void} watchURL */ @@ -531,23 +530,6 @@ const store = createStore(NS, { } }, - watchDom() { - const doc = - // @ts-expect-error - document.getElementById('rendered_iframe').contentWindow.document; - printHtmlApiTree( - doc, - // @ts-expect-error - document.getElementById('dom_tree'), - { - showClosers: store.state.showClosers, - showInvisible: store.state.showInvisible, - showVirtual: store.state.showVirtual, - hoverInfo: store.state.hoverInfo, - }, - ); - }, - render() { // @ts-expect-error This should not be null. const iframeDocument = RENDERED_IFRAME.contentWindow.document; From 4c338f30f39a604fec590c899b163496aba580f6 Mon Sep 17 00:00:00 2001 From: Jon Surrell Date: Thu, 7 Nov 2024 22:13:38 +0100 Subject: [PATCH 07/23] Fix playback to work with fragments --- html-api-debugger/view.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/html-api-debugger/view.js b/html-api-debugger/view.js index 671683d..5cbb41a 100644 --- a/html-api-debugger/view.js +++ b/html-api-debugger/view.js @@ -131,6 +131,7 @@ const store = createStore(NS, { store.state.playbackPoint ]?.[1]; }, + get playbackHTML() { if (store.state.playbackPoint === null) { return undefined; @@ -337,7 +338,7 @@ const store = createStore(NS, { } if (contextElement) { store.state.DOM.contextNode = contextElement.nodeName; - contextElement.innerHTML = store.state.html; + contextElement.innerHTML = store.state.playbackHTML ?? store.state.html; } } @@ -538,7 +539,7 @@ const store = createStore(NS, { store.state.hasMutatedDom = false; const html = - store.state.playbackHTML ?? (store.state.contextHTML || store.state.html); + store.state.contextHTML || (store.state.playbackHTML ?? store.state.html); iframeDocument.open(); iframeDocument.write(html); From 66a8cb5cf0a1a5ff85430433bccb38888d897ab5 Mon Sep 17 00:00:00 2001 From: Jon Surrell Date: Thu, 7 Nov 2024 22:37:55 +0100 Subject: [PATCH 08/23] Improve context html display --- html-api-debugger/interactivity.php | 4 +++- html-api-debugger/style.css | 11 ++++++++++- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/html-api-debugger/interactivity.php b/html-api-debugger/interactivity.php index 2740a11..11f6024 100644 --- a/html-api-debugger/interactivity.php +++ b/html-api-debugger/interactivity.php @@ -152,7 +152,9 @@ class="html-api-debugger-container html-api-debugger--grid"
diff --git a/html-api-debugger/style.css b/html-api-debugger/style.css index 0f93751..26884fd 100644 --- a/html-api-debugger/style.css +++ b/html-api-debugger/style.css @@ -41,12 +41,21 @@ font-size: inherit; } - textarea { + #input_html { width: 100%; min-height: 200px; font-family: monospace; } + .context-html { + width: 100%; + font-family: monospace; + + &:placeholder-shown { + font-style: italic; + } + } + .error-holder { padding: 1em; background: pink; From 4c18c6eef865d7caca01523eaf7bd394388bf915 Mon Sep 17 00:00:00 2001 From: Jon Surrell Date: Fri, 8 Nov 2024 11:01:33 +0100 Subject: [PATCH 09/23] Fix context_node name --- html-api-debugger/html-api-integration.php | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/html-api-debugger/html-api-integration.php b/html-api-debugger/html-api-integration.php index e2807c0..0ff47fa 100644 --- a/html-api-debugger/html-api-integration.php +++ b/html-api-debugger/html-api-integration.php @@ -137,7 +137,11 @@ function get_tree( string $html, array $options ): array { $doctype_name = null; $doctype_public_identifier = null; $doctype_system_identifier = null; - $context_node = isset( $context_processor ) ? $context_processor->get_tag() : null; + $context_node = isset( $context_processor ) + ? ( method_exists( $context_processor, 'get_qualified_tag_name' ) + ? $context_processor->get_qualified_tag_name() + : $context_processor->get_tag() ) + : null; $playback = array(); From 4b808601388534c7923b099c000bec19f6ad969d Mon Sep 17 00:00:00 2001 From: Jon Surrell Date: Fri, 8 Nov 2024 11:12:01 +0100 Subject: [PATCH 10/23] Fix bug with empty tree at context node --- html-api-debugger/html-api-integration.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/html-api-debugger/html-api-integration.php b/html-api-debugger/html-api-integration.php index 0ff47fa..8bc4ba7 100644 --- a/html-api-debugger/html-api-integration.php +++ b/html-api-debugger/html-api-integration.php @@ -129,7 +129,9 @@ function get_tree( string $html, array $options ): array { $cursor = array( 0 ); if ( $is_fragment_processor ) { - $tree = array(); + $tree = array( + 'childNodes' => array(), + ); $cursor = array(); } From 32ec32aac459ecd1fe81819e32b029eeffb47186 Mon Sep 17 00:00:00 2001 From: Jon Surrell Date: Fri, 8 Nov 2024 11:39:04 +0100 Subject: [PATCH 11/23] Prep for 2.2 --- html-api-debugger/html-api-debugger.php | 6 +++--- html-api-debugger/readme.txt | 8 ++++++-- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/html-api-debugger/html-api-debugger.php b/html-api-debugger/html-api-debugger.php index a58c464..878ce1c 100644 --- a/html-api-debugger/html-api-debugger.php +++ b/html-api-debugger/html-api-debugger.php @@ -3,9 +3,9 @@ * Plugin Name: HTML API Debugger * Plugin URI: https://github.com/sirreal/html-api-debugger * Description: Add a page to wp-admin for debugging the HTML API. - * Version: 2.1 + * Version: 2.2 * Requires at least: 6.6 - * Tested up to: 6.7 + * Tested up to: 6.8 * Author: Jon Surrell * Author URI: https://profiles.wordpress.org/jonsurrell/ * License: GPLv2 or later @@ -22,7 +22,7 @@ require_once __DIR__ . '/html-api-integration.php'; const SLUG = 'html-api-debugger'; -const VERSION = '2.1'; +const VERSION = '2.2'; /** Set up the plugin. */ function init() { diff --git a/html-api-debugger/readme.txt b/html-api-debugger/readme.txt index 5b6bbb0..57bfd06 100644 --- a/html-api-debugger/readme.txt +++ b/html-api-debugger/readme.txt @@ -2,8 +2,8 @@ Contributors: jonsurrell Tags: HTML API, development, debug Requires at least: 6.6 -Tested up to: 6.7 -Stable tag: 2.1 +Tested up to: 6.8 +Stable tag: 2.2 License: GPLv2 or later License URI: https://www.gnu.org/licenses/gpl-2.0.html @@ -11,6 +11,10 @@ Add a page to wp-admin for debugging the HTML API. == Changelog == += 2.2 = +* Drop basic fragment parsing and quirks mode support. +* Add advanced context and fragment parsing when supported (WordPress 6.8+). + = 2.1 = * Add text-wrap styling fallback for unsupported browsers. From e30a5d07191728f5b34f114511b7f89836a2c038 Mon Sep 17 00:00:00 2001 From: Jon Surrell Date: Fri, 8 Nov 2024 13:05:40 +0100 Subject: [PATCH 12/23] Update supports type --- html-api-debugger/view.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/html-api-debugger/view.js b/html-api-debugger/view.js index 5cbb41a..92fe405 100644 --- a/html-api-debugger/view.js +++ b/html-api-debugger/view.js @@ -39,9 +39,8 @@ let mutationObserver = null; * * * @typedef Supports + * @property {boolean} create_fragment_advanced * @property {boolean} is_virtual - * @property {boolean} quirks_mode - * @property {boolean} full_parser * @property {boolean} normalize * * From cc35878f4808fadde19c6da4d29a1c07b3b73386 Mon Sep 17 00:00:00 2001 From: Jon Surrell Date: Fri, 8 Nov 2024 13:08:04 +0100 Subject: [PATCH 13/23] Fix context usage in case its unsupported --- html-api-debugger/view.js | 32 +++++++++++++++++++++----------- 1 file changed, 21 insertions(+), 11 deletions(-) diff --git a/html-api-debugger/view.js b/html-api-debugger/view.js index 92fe405..002f979 100644 --- a/html-api-debugger/view.js +++ b/html-api-debugger/view.js @@ -68,6 +68,7 @@ let mutationObserver = null; * @property {boolean} showInvisible * @property {boolean} showVirtual * @property {string} contextHTML + * @property {string|null} contextHTMLForUse * @property {number|null} previewCorePrNumber * @property {number|null} previewGutenbergPrNumber * @property {Link|null} previewCoreLink @@ -140,6 +141,12 @@ const store = createStore(NS, { ]?.[0]; }, + get contextHTMLForUse() { + return store.state.htmlapiResponse.supports.create_fragment_advanced + ? store.state.contextHTML.trim() || null + : null; + }, + /** @type {Link|null} */ get previewCoreLink() { if (!store.state.previewCorePrNumber) { @@ -215,8 +222,8 @@ const store = createStore(NS, { if (store.state.html) { searchParams.set('html', store.state.html); } - if (store.state.contextHTML) { - searchParams.set('contextHTML', store.state.contextHTML); + if (store.state.contextHTMLForUse) { + searchParams.set('contextHTML', store.state.contextHTMLForUse); } const base = '/wp-admin/admin.php'; const u = new URL( @@ -329,7 +336,7 @@ const store = createStore(NS, { /** @type {Element|null} */ let contextElement = null; - if (store.state.contextHTML) { + if (store.state.contextHTMLForUse) { const walker = doc.createTreeWalker(doc, NodeFilter.SHOW_ELEMENT); while (walker.nextNode()) { // @ts-expect-error It's an Element! @@ -435,9 +442,12 @@ const store = createStore(NS, { watchURL() { const u = new URL(document.location.href); let shouldReplace = false; - for (const param of /** @type {const} */ (['html', 'contextHTML'])) { - if (store.state[param]) { - u.searchParams.set(param, store.state[param]); + for (const [param, prop] of /** @type {const} */ ([ + ['html', 'html'], + ['contextHTML', 'contextHTMLForUse'], + ])) { + if (store.state[prop]) { + u.searchParams.set(param, store.state[prop]); shouldReplace = true; } else if (u.searchParams.has(param)) { u.searchParams.delete(param); @@ -461,7 +471,7 @@ const store = createStore(NS, { method: 'POST', body: JSON.stringify({ html: store.state.html, - contextHTML: store.state.contextHTML, + contextHTML: store.state.contextHTMLForUse, }), headers: { 'Content-Type': 'application/json', @@ -538,7 +548,9 @@ const store = createStore(NS, { store.state.hasMutatedDom = false; const html = - store.state.contextHTML || (store.state.playbackHTML ?? store.state.html); + store.state.contextHTMLForUse ?? + store.state.playbackHTML ?? + store.state.html; iframeDocument.open(); iframeDocument.write(html); @@ -570,9 +582,7 @@ const store = createStore(NS, { /** @param {InputEvent} e */ handleContextHtmlInput: function* (e) { - const val = /** @type {HTMLInputElement} */ (e.target).value; - store.state.contextHTML = val.trim(); - + store.state.contextHTML = /** @type {HTMLInputElement} */ (e.target).value; yield store.callAPI(); }, From d3b96009c2e743c6e47cec37f09a32fad94cbc6c Mon Sep 17 00:00:00 2001 From: Jon Surrell Date: Fri, 8 Nov 2024 13:08:19 +0100 Subject: [PATCH 14/23] Remove unused function --- html-api-debugger/view.js | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/html-api-debugger/view.js b/html-api-debugger/view.js index 002f979..4237fcb 100644 --- a/html-api-debugger/view.js +++ b/html-api-debugger/view.js @@ -651,20 +651,3 @@ function getToggleHandler(stateKey) { } }; } - -/** - * @param {keyof State} stateKey - * @return {(e: Event) => Promise} - */ -function getToggleHandlerWithRefetch(stateKey) { - const f1 = getToggleHandler(stateKey); - - /** - * @param {Event} e - */ - // @ts-expect-error The iAPI runtime transforms the generator to an async function. - return function* (e) { - f1(e); - yield store.callAPI(); - }; -} From 8aba8e0a5b82dda8781960eeb89b39e5b6b3b80e Mon Sep 17 00:00:00 2001 From: Jon Surrell Date: Fri, 8 Nov 2024 13:14:05 +0100 Subject: [PATCH 15/23] Drop support for 6.6 --- html-api-debugger/html-api-debugger.php | 2 +- html-api-debugger/html-api-integration.php | 1 - html-api-debugger/interactivity.php | 2 -- html-api-debugger/readme.txt | 3 ++- 4 files changed, 3 insertions(+), 5 deletions(-) diff --git a/html-api-debugger/html-api-debugger.php b/html-api-debugger/html-api-debugger.php index 878ce1c..e9da626 100644 --- a/html-api-debugger/html-api-debugger.php +++ b/html-api-debugger/html-api-debugger.php @@ -4,7 +4,7 @@ * Plugin URI: https://github.com/sirreal/html-api-debugger * Description: Add a page to wp-admin for debugging the HTML API. * Version: 2.2 - * Requires at least: 6.6 + * Requires at least: 6.7 * Tested up to: 6.8 * Author: Jon Surrell * Author URI: https://profiles.wordpress.org/jonsurrell/ diff --git a/html-api-debugger/html-api-integration.php b/html-api-debugger/html-api-integration.php index 8bc4ba7..62b22e1 100644 --- a/html-api-debugger/html-api-integration.php +++ b/html-api-debugger/html-api-integration.php @@ -24,7 +24,6 @@ function get_supports(): array { ); } - /** * Get the normalized HTML. * diff --git a/html-api-debugger/interactivity.php b/html-api-debugger/interactivity.php index 11f6024..adf853e 100644 --- a/html-api-debugger/interactivity.php +++ b/html-api-debugger/interactivity.php @@ -196,8 +196,6 @@ class="context-html" - -
diff --git a/html-api-debugger/readme.txt b/html-api-debugger/readme.txt index 57bfd06..c2819dd 100644 --- a/html-api-debugger/readme.txt +++ b/html-api-debugger/readme.txt @@ -1,7 +1,7 @@ === HTML API Debugger === Contributors: jonsurrell Tags: HTML API, development, debug -Requires at least: 6.6 +Requires at least: 6.7 Tested up to: 6.8 Stable tag: 2.2 License: GPLv2 or later @@ -12,6 +12,7 @@ Add a page to wp-admin for debugging the HTML API. == Changelog == = 2.2 = +* Drop support for WordPress 6.7. * Drop basic fragment parsing and quirks mode support. * Add advanced context and fragment parsing when supported (WordPress 6.8+). From 9256fd2d11e335f58c59378387896a637c644b49 Mon Sep 17 00:00:00 2001 From: Jon Surrell Date: Fri, 8 Nov 2024 13:25:27 +0100 Subject: [PATCH 16/23] Cleanup unused supports --- html-api-debugger/html-api-integration.php | 4 ---- 1 file changed, 4 deletions(-) diff --git a/html-api-debugger/html-api-integration.php b/html-api-debugger/html-api-integration.php index 62b22e1..2e74ff5 100644 --- a/html-api-debugger/html-api-integration.php +++ b/html-api-debugger/html-api-integration.php @@ -6,7 +6,6 @@ use ReflectionMethod; use ReflectionProperty; use WP_HTML_Processor; -use WP_HTML_Processor_State; /** * Get information about HTML API supported features @@ -16,9 +15,6 @@ function get_supports(): array { return array( 'is_virtual' => $html_processor_rc->hasMethod( 'is_virtual' ), - 'full_parser' => method_exists( WP_HTML_Processor::class, 'create_full_parser' ), - 'quirks_mode' => $html_processor_rc->hasProperty( 'compat_mode' ), - 'doctype' => method_exists( WP_HTML_Processor::class, 'get_doctype_info' ), 'normalize' => method_exists( WP_HTML_Processor::class, 'normalize' ), 'create_fragment_advanced' => method_exists( WP_HTML_Processor::class, 'create_fragment_at_current_node' ), ); From 51f48858d715befb798f283e0ce4d51fd776aa19 Mon Sep 17 00:00:00 2001 From: Jon Surrell Date: Fri, 8 Nov 2024 13:59:47 +0100 Subject: [PATCH 17/23] Remove code to support WP < 6.7 --- html-api-debugger/html-api-integration.php | 128 ++++++++------------- html-api-debugger/interactivity.php | 4 +- html-api-debugger/view.js | 39 +++---- 3 files changed, 64 insertions(+), 107 deletions(-) diff --git a/html-api-debugger/html-api-integration.php b/html-api-debugger/html-api-integration.php index 2e74ff5..1ba28e5 100644 --- a/html-api-debugger/html-api-integration.php +++ b/html-api-debugger/html-api-integration.php @@ -2,7 +2,6 @@ namespace HTML_API_Debugger\HTML_API_Integration; use Exception; -use ReflectionClass; use ReflectionMethod; use ReflectionProperty; use WP_HTML_Processor; @@ -11,11 +10,7 @@ * Get information about HTML API supported features */ function get_supports(): array { - $html_processor_rc = new ReflectionClass( WP_HTML_Processor::class ); - return array( - 'is_virtual' => $html_processor_rc->hasMethod( 'is_virtual' ), - 'normalize' => method_exists( WP_HTML_Processor::class, 'normalize' ), 'create_fragment_advanced' => method_exists( WP_HTML_Processor::class, 'create_fragment_at_current_node' ), ); } @@ -27,9 +22,6 @@ function get_supports(): array { * @return string|null The normalized HTML or null if not supported. */ function get_normalized_html( string $html ): ?string { - if ( ! method_exists( WP_HTML_Processor::class, 'normalize' ) ) { - return null; - } return WP_HTML_Processor::normalize( $html ); } @@ -61,6 +53,12 @@ function get_tree( string $html, array $options ): array { } if ( $context_processor->has_bookmark( 'final_node' ) ) { $context_processor->seek( 'final_node' ); + /** + * The main processor used for tree building. + * + * @var WP_HTML_Processor|null $processor + * @disregard P1013 + */ $processor = $context_processor->create_fragment_at_current_node( $html ); } @@ -73,48 +71,16 @@ function get_tree( string $html, array $options ): array { $processor = WP_HTML_Processor::create_full_parser( $html ); } - $rc = new ReflectionClass( WP_HTML_Processor::class ); - - $is_virtual = function () { - return null; - }; - - if ( $rc->hasMethod( 'is_virtual' ) ) { - $processor_is_virtual = new ReflectionMethod( WP_HTML_Processor::class, 'is_virtual' ); - $processor_is_virtual->setAccessible( true ); - $is_virtual = function () use ( $processor_is_virtual, $processor ) { - return $processor_is_virtual->invoke( $processor ); - }; - } - - $get_current_depth = method_exists( WP_HTML_Processor::class, 'get_current_depth' ) - ? function () use ( $processor ): int { - return $processor->get_current_depth(); - } - : function () use ( $processor ): int { - return count( $processor->get_breadcrumbs() ); - }; - - $get_tag_name = method_exists( WP_HTML_Processor::class, 'get_qualified_tag_name' ) - ? function () use ( $processor ): string { - return $processor->get_qualified_tag_name(); - } - : function () use ( $processor ): string { - return $processor->get_tag(); - }; - - $get_attribute_name = method_exists( WP_HTML_Processor::class, 'get_qualified_attribute_name' ) - ? function ( string $attribute_name ) use ( $processor ): string { - return $processor->get_qualified_attribute_name( $attribute_name ); - } - : function ( string $attribute_name ): string { - return $attribute_name; - }; - if ( null === $processor ) { - throw new Exception( 'could not process html' ); + throw new Exception( 'Could not create processor.' ); } + $processor_is_virtual = new ReflectionMethod( WP_HTML_Processor::class, 'is_virtual' ); + $processor_is_virtual->setAccessible( true ); + $is_virtual = function () use ( $processor_is_virtual, $processor ) { + return $processor_is_virtual->invoke( $processor ); + }; + $tree = array( 'nodeType' => NODE_TYPE_DOCUMENT, 'nodeName' => '#document', @@ -135,9 +101,7 @@ function get_tree( string $html, array $options ): array { $doctype_public_identifier = null; $doctype_system_identifier = null; $context_node = isset( $context_processor ) - ? ( method_exists( $context_processor, 'get_qualified_tag_name' ) - ? $context_processor->get_qualified_tag_name() - : $context_processor->get_tag() ) + ? $context_processor->get_qualified_tag_name() : null; $playback = array(); @@ -158,7 +122,7 @@ function get_tree( string $html, array $options ): array { } // Breadcrumbs and depth are off by one because they include an extra context node. - if ( ( count( $cursor ) + 1 ) > ( $get_current_depth() - ( $is_fragment_processor ? 1 : 0 ) ) ) { + if ( ( count( $cursor ) + 1 ) > ( $processor->get_current_depth() - ( $is_fragment_processor ? 1 : 0 ) ) ) { array_pop( $cursor ); } $current = &$tree; @@ -169,31 +133,30 @@ function get_tree( string $html, array $options ): array { $token_type = $processor->get_token_type(); switch ( $token_type ) { + // @todo this should be set on the context processor if present. case '#doctype': - if ( method_exists( WP_HTML_Processor::class, 'get_doctype_info' ) ) { - $doctype = $processor->get_doctype_info(); + $doctype = $processor->get_doctype_info(); - $doctype_name = $doctype->name; - $doctype_public_identifier = $doctype->public_identifier; - $doctype_system_identifier = $doctype->system_identifier; - - if ( $doctype->indicated_compatability_mode === 'quirks' ) { - $compat_mode = 'BackCompat'; - } + $doctype_name = $doctype->name; + $doctype_public_identifier = $doctype->public_identifier; + $doctype_system_identifier = $doctype->system_identifier; - $current['childNodes'][] = array( - 'nodeType' => NODE_TYPE_DOCUMENT_TYPE, - 'nodeName' => $doctype_name, - '_span' => $bookmark, - '_mode' => $processor_state->getValue( $processor )->insertion_mode, - '_bc' => $processor->get_breadcrumbs(), - '_depth' => $get_current_depth(), - ); + if ( $doctype->indicated_compatability_mode === 'quirks' ) { + $compat_mode = 'BackCompat'; } + + $current['childNodes'][] = array( + 'nodeType' => NODE_TYPE_DOCUMENT_TYPE, + 'nodeName' => $doctype_name, + '_span' => $bookmark, + '_mode' => $processor_state->getValue( $processor )->insertion_mode, + '_bc' => $processor->get_breadcrumbs(), + '_depth' => $processor->get_current_depth(), + ); break; case '#tag': - $tag_name = $get_tag_name(); + $tag_name = $processor->get_qualified_tag_name(); $attributes = array(); $attribute_names = $processor->get_attribute_names_with_prefix( '' ); @@ -211,13 +174,13 @@ function get_tree( string $html, array $options ): array { $attributes[] = array( 'nodeType' => NODE_TYPE_ATTRIBUTE, 'specified' => true, - 'nodeName' => $get_attribute_name( $attribute_name ), + 'nodeName' => $processor->get_qualified_attribute_name( $attribute_name ), 'nodeValue' => $val, ); } } - $namespace = method_exists( WP_HTML_Processor::class, 'get_namespace' ) ? $processor->get_namespace() : 'html'; + $namespace = $processor->get_namespace(); $self = array( 'nodeType' => NODE_TYPE_ELEMENT, @@ -229,7 +192,7 @@ function get_tree( string $html, array $options ): array { '_mode' => $processor_state->getValue( $processor )->insertion_mode, '_bc' => $processor->get_breadcrumbs(), '_virtual' => $is_virtual(), - '_depth' => $get_current_depth(), + '_depth' => $processor->get_current_depth(), '_namespace' => $namespace, ); @@ -244,7 +207,7 @@ function get_tree( string $html, array $options ): array { '_mode' => $processor_state->getValue( $processor )->insertion_mode, '_bc' => array_merge( $processor->get_breadcrumbs(), array( '#text' ) ), '_virtual' => $is_virtual(), - '_depth' => $get_current_depth() + 1, + '_depth' => $processor->get_current_depth() + 1, ); } @@ -270,7 +233,7 @@ function get_tree( string $html, array $options ): array { '_mode' => $processor_state->getValue( $processor )->insertion_mode, '_bc' => $processor->get_breadcrumbs(), '_virtual' => $is_virtual(), - '_depth' => $get_current_depth(), + '_depth' => $processor->get_current_depth(), ); $current['childNodes'][] = $self; @@ -285,7 +248,7 @@ function get_tree( string $html, array $options ): array { '_mode' => $processor_state->getValue( $processor )->insertion_mode, '_bc' => $processor->get_breadcrumbs(), '_virtual' => $is_virtual(), - '_depth' => $get_current_depth(), + '_depth' => $processor->get_current_depth(), ); $current['childNodes'][] = $self; @@ -300,7 +263,7 @@ function get_tree( string $html, array $options ): array { '_mode' => $processor_state->getValue( $processor )->insertion_mode, '_bc' => $processor->get_breadcrumbs(), '_virtual' => $is_virtual(), - '_depth' => $get_current_depth(), + '_depth' => $processor->get_current_depth(), ); $current['childNodes'][] = $self; break; @@ -314,7 +277,7 @@ function get_tree( string $html, array $options ): array { '_mode' => $processor_state->getValue( $processor )->insertion_mode, '_bc' => $processor->get_breadcrumbs(), '_virtual' => $is_virtual(), - '_depth' => $get_current_depth(), + '_depth' => $processor->get_current_depth(), ); $current['childNodes'][] = $self; break; @@ -326,7 +289,7 @@ function get_tree( string $html, array $options ): array { '_mode' => $processor_state->getValue( $processor )->insertion_mode, '_bc' => $processor->get_breadcrumbs(), '_virtual' => $is_virtual(), - '_depth' => $get_current_depth(), + '_depth' => $processor->get_current_depth(), ); switch ( $processor->get_comment_type() ) { case WP_HTML_Processor::COMMENT_AS_ABRUPTLY_CLOSED_COMMENT: @@ -374,16 +337,19 @@ function get_tree( string $html, array $options ): array { $playback[] = array( $last_html, $tree ); if ( null !== $processor->get_last_error() ) { - if ( method_exists( WP_HTML_Processor::class, 'get_unsupported_exception' ) && $processor->get_unsupported_exception() ) { + if ( $processor->get_unsupported_exception() ) { throw $processor->get_unsupported_exception(); - } else { + } elseif ( $processor->get_last_error() ) { // phpcs:ignore WordPress.Security.EscapeOutput.ExceptionNotEscaped throw new Exception( $processor->get_last_error() ); + } else { + throw new Exception( 'Unknown error.' ); } } + // This could perhaps be ignored or surfaced in the response. if ( $processor->paused_at_incomplete_token() ) { - throw new Exception( 'Paused at incomplete token' ); + throw new Exception( 'Paused at incomplete token.' ); } return array( diff --git a/html-api-debugger/interactivity.php b/html-api-debugger/interactivity.php index adf853e..bad948a 100644 --- a/html-api-debugger/interactivity.php +++ b/html-api-debugger/interactivity.php @@ -98,7 +98,7 @@ class="html-api-debugger-container html-api-debugger--grid" referrerpolicy="no-referrer" sandbox="allow-forms allow-modals allow-popups allow-scripts allow-same-origin">
-
+
HTML API Normalized HTML

 	
@@ -148,7 +148,7 @@ class="html-api-debugger-container html-api-debugger--grid"
- +

Rendered output