Skip to content

Commit

Permalink
WIP: HTML API: Add support for H1-H6 elements in HTML Processor
Browse files Browse the repository at this point in the history
  • Loading branch information
dmsnell committed Oct 19, 2023
1 parent 6528d98 commit 81ef71d
Show file tree
Hide file tree
Showing 6 changed files with 234 additions and 24 deletions.
107 changes: 107 additions & 0 deletions src/wp-config.local.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
<?php

Check failure on line 1 in src/wp-config.local.php

View workflow job for this annotation

GitHub Actions / PHP coding standards

Filenames should be all lowercase with hyphens as word separators. Expected wp-config-local.php, but found wp-config.local.php.
/**
* The base configuration for WordPress
*
* The wp-config.php creation script uses this file during the installation.
* You don't have to use the web site, you can copy this file to "wp-config.php"
* and fill in the values.
*
* This file contains the following configurations:
*
* * Database settings
* * Secret keys
* * Database table prefix
* * Localized language
* * ABSPATH
*
* @link https://wordpress.org/support/article/editing-wp-config-php/
*
* @package WordPress
*/

// ** Database settings - You can get this info from your web host ** //
/** The name of the database for WordPress */
define( 'DB_NAME', 'wordpress' );

/** Database username */
define( 'DB_USER', 'wpuser' );

/** Database password */
define( 'DB_PASSWORD', 'wpuser' );

/** Database hostname */
define( 'DB_HOST', 'localhost' );

/** Database charset to use in creating database tables. */
define( 'DB_CHARSET', 'utf8' );

/** The database collate type. Don't change this if in doubt. */
define( 'DB_COLLATE', '' );

/**#@+
* Authentication unique keys and salts.
*
* Change these to different unique phrases! You can generate these using
* the {@link https://api.wordpress.org/secret-key/1.1/salt/ WordPress.org secret-key service}.
*
* You can change these at any point in time to invalidate all existing cookies.
* This will force all users to have to log in again.
*
* @since 2.6.0
*/
define( 'AUTH_KEY', 'MFk^p/>nn@&%|l0flI6:VON4WelK EeRqf)a&EbFfG8@ ]R} )D`N12^th4_{TNi' );

Check failure on line 52 in src/wp-config.local.php

View workflow job for this annotation

GitHub Actions / PHP coding standards

Expected 1 space after comma in argument list; 10 found
define( 'SECURE_AUTH_KEY', 'qD9}:B2$6*w7.{;!pZk]]D?y=D[zz#lpqm4c&+j049^v</e* <RsC=g|z_3#dfz/' );

Check failure on line 53 in src/wp-config.local.php

View workflow job for this annotation

GitHub Actions / PHP coding standards

Expected 1 space after comma in argument list; 3 found
define( 'LOGGED_IN_KEY', '@ cguVQ){hG;:44OKIkU>BVf8 Y]IxSA+2x%n<xSO;vXchw3B*{t_7t9.<02/eaE' );

Check failure on line 54 in src/wp-config.local.php

View workflow job for this annotation

GitHub Actions / PHP coding standards

Expected 1 space after comma in argument list; 5 found
define( 'NONCE_KEY', 'N@B>H_[KqJk|nbOyBKF}Ir[VF8`qC Zk+g_$C1XL@C|WMRv*Q<4R,]f6Lbu_)#ml' );

Check failure on line 55 in src/wp-config.local.php

View workflow job for this annotation

GitHub Actions / PHP coding standards

Expected 1 space after comma in argument list; 9 found
define( 'AUTH_SALT', '^o|a:{J &rTHA?qheBwZE?;2xh&P_S{D:nxI!/|+@n[z-C^z*rr,fxhg *,&7vrB' );

Check failure on line 56 in src/wp-config.local.php

View workflow job for this annotation

GitHub Actions / PHP coding standards

Expected 1 space after comma in argument list; 9 found
define( 'SECURE_AUTH_SALT', 'fhk&Z4_rl|=U,kC!pMlJz@afe#]uRH>:I6k>Q bcz/3E9TV`JEyN.Rk)baXDJM&8' );

Check failure on line 57 in src/wp-config.local.php

View workflow job for this annotation

GitHub Actions / PHP coding standards

Expected 1 space after comma in argument list; 2 found
define( 'LOGGED_IN_SALT', 'w2Dl23NuZNJU<9+ -8F{l%eZAjXN,D,8=k#<x]qD]i:<iZ%5eXw2!minSwH[HeR<' );

Check failure on line 58 in src/wp-config.local.php

View workflow job for this annotation

GitHub Actions / PHP coding standards

Expected 1 space after comma in argument list; 4 found
define( 'NONCE_SALT', '2!<_+:wZY-u}n/3LKW2WQ@/jbznNOp{AQ*W({%v1lK4vUh| 9KBPERun/iWrfp$b' );

Check failure on line 59 in src/wp-config.local.php

View workflow job for this annotation

GitHub Actions / PHP coding standards

Expected 1 space after comma in argument list; 8 found
define( 'WP_CACHE_KEY_SALT', ')5>>.k$IuAf/ZtoryWohrIgGRpiv{t<?sg~lLJZu)|+4h(Cu3FBY:E7gl}PPw|/3' );


/**#@-*/

/**
* WordPress database table prefix.
*
* You can have multiple installations in one database if you give each
* a unique prefix. Only numbers, letters, and underscores please!
*/
$table_prefix = 'wp_';


/* Add any custom values between this line and the "stop editing" line. */



/**
* For developers: WordPress debugging mode.
*
* Change this to true to enable the display of notices during development.
* It is strongly recommended that plugin and theme developers use WP_DEBUG
* in their development environments.
*
* For information on other constants that can be used for debugging,
* visit the documentation.
*
* @link https://wordpress.org/support/article/debugging-in-wordpress/
*/
if ( ! defined( 'WP_DEBUG' ) ) {
define( 'WP_DEBUG', true );
}

define( 'WP_DEBUG_LOG', true );
define( 'WP_DEBUG_DISPLAY', true );
define( 'SCRIPT_DEBUG', true );
define( 'WP_ENVIRONMENT_TYPE', 'local' );
define( 'WP_DEVELOPMENT_MODE', 'core' );
/* That's all, stop editing! Happy publishing. */

/** Absolute path to the WordPress directory. */
if ( ! defined( 'ABSPATH' ) ) {
define( 'ABSPATH', __DIR__ . '/' );
}

/** Sets up WordPress vars and included files. */
require_once ABSPATH . 'wp-settings.php';
30 changes: 28 additions & 2 deletions src/wp-includes/html-api/class-wp-html-open-elements.php
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ public function current_node() {
*
* @see https://html.spec.whatwg.org/#has-an-element-in-the-specific-scope
*
* @param string $tag_name Name of tag check.
* @param string $tag_name Name of tag check, or the class constant HEADING_ELEMENTS to specify H1-H6.
* @param string[] $termination_list List of elements that terminate the search.
* @return bool Whether the element was found in a specific scope.
*/
Expand All @@ -116,6 +116,13 @@ public function has_element_in_specific_scope( $tag_name, $termination_list ) {
return true;
}

if (
self::HEADING_ELEMENTS === $tag_name &&
in_array( $node->node_name, array( 'H1', 'H2', 'H3', 'H4', 'H5', 'H6' ), true )
) {
return true;
}

switch ( $node->node_name ) {
case 'HTML':
return false;
Expand Down Expand Up @@ -263,13 +270,21 @@ public function pop() {
*
* @see WP_HTML_Open_Elements::pop
*
* @param string $tag_name Name of tag that needs to be popped off of the stack of open elements.
* @param string $tag_name Name of tag that needs to be popped off of the stack of open elements,
* or the class constant HEADING_ELEMENTS to specify any of H1-H6.
* @return bool Whether a tag of the given name was found and popped off of the stack of open elements.
*/
public function pop_until( $tag_name ) {
foreach ( $this->walk_up() as $item ) {
$this->pop();

if (
self::HEADING_ELEMENTS === $tag_name &&
in_array( $item->node_name, array( 'H1', 'H2', 'H3', 'H4', 'H5', 'H6' ), true )
) {
return true;
}

if ( $tag_name === $item->node_name ) {
return true;
}
Expand Down Expand Up @@ -429,4 +444,15 @@ public function after_element_pop( $item ) {
break;
}
}

/**
* Represents the collection of H1-H6 elements.
*
* @since 6.5.0
*
* @see has_element_in_scope()
*
* @var string
*/
const HEADING_ELEMENTS = 'heading-elements';
}
59 changes: 59 additions & 0 deletions src/wp-includes/html-api/class-wp-html-processor.php
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@
* - The formatting elements: B, BIG, CODE, EM, FONT, I, SMALL, STRIKE, STRONG, TT, U.
* - Containers: DIV, FIGCAPTION, FIGURE, SPAN.
* - Form elements: BUTTON.
* - Heading elements: H1, H2, H3, H4, H5, H6.
* - Paragraph: P.
* - Void elements: IMG.
*
Expand Down Expand Up @@ -657,6 +658,64 @@ private function step_in_body() {
$this->state->stack_of_open_elements->pop_until( $tag_name );
return true;

/*
* > A start tag whose tag name is one of: "h1", "h2", "h3", "h4", "h5", "h6"
*/
case '+H1':
case '+H2':
case '+H3':
case '+H4':
case '+H5':
case '+H6':
if ( $this->state->stack_of_open_elements->has_p_in_button_scope() ) {
$this->close_a_p_element();
}

if (
in_array(
$this->state->stack_of_open_elements->current_node()->node_name,
array( 'H1', 'H2', 'H3', 'H4', 'H5', 'H6' ),
true
)
) {
// @TODO: Indicate a parse error once it's possible.
$this->state->stack_of_open_elements->pop();
}

$this->insert_html_element( $this->state->current_token );
return true;

/*
* > An end tag whose tag name is one of: "h1", "h2", "h3", "h4", "h5", "h6"
*/
case '-H1':
case '-H2':
case '-H3':
case '-H4':
case '-H5':
case '-H6':
if (
! $this->state->stack_of_open_elements->has_element_in_scope(
WP_HTML_Open_Elements::HEADING_ELEMENTS
)
) {
/*
* This is a parse error; ignore the token.
*
* @TODO: Indicate a parse error once it's possible.
*/
return $this->step();
}

$this->generate_implied_end_tags();

if ( $this->state->stack_of_open_elements->current_node()->node_name !== $tag_name ) {
// @TODO: Record parse error: this error doesn't impact parsing.
}

$this->state->stack_of_open_elements->pop_until( WP_HTML_Open_Elements::HEADING_ELEMENTS );
return true;

/*
* > An end tag whose tag name is "p"
*/
Expand Down
2 changes: 0 additions & 2 deletions tests/phpunit/tests/html-api/wpHtmlProcessor.php
Original file line number Diff line number Diff line change
Expand Up @@ -91,8 +91,6 @@ public function test_stops_processing_after_unsupported_elements() {
*
* @covers WP_HTML_Processor::next_tag
* @covers WP_HTML_Processor::seek
*
* @throws WP_HTML_Unsupported_Exception
*/
public function test_clear_to_navigate_after_seeking() {
$p = WP_HTML_Processor::create_fragment( '<div one><strong></strong></div><p><strong two></strong></p>' );
Expand Down
12 changes: 6 additions & 6 deletions tests/phpunit/tests/html-api/wpHtmlProcessorBreadcrumbs.php
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,12 @@ public function data_single_tag_of_supported_elements() {
'FIGCAPTION',
'FIGURE',
'FONT',
'H1',
'H2',
'H3',
'H4',
'H5',
'H6',
'I',
'IMG',
'P',
Expand Down Expand Up @@ -133,12 +139,6 @@ public function data_unsupported_elements() {
'FORM',
'FRAME',
'FRAMESET',
'H1',
'H2',
'H3',
'H4',
'H5',
'H6',
'HEAD',
'HEADER',
'HGROUP',
Expand Down
48 changes: 34 additions & 14 deletions tests/phpunit/tests/html-api/wpHtmlProcessorSemanticRules.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,6 @@ class Tests_HtmlApi_WpHtmlProcessorSemanticRules extends WP_UnitTestCase {
* element in scope, that it skips the tag entirely.
*
* @ticket 58961
*
* @since 6.4.0
*
* @throws Exception
*/
public function test_in_body_skips_unexpected_button_closer() {
$p = WP_HTML_Processor::create_fragment( '<div>Test</button></div>' );
Expand All @@ -46,10 +42,6 @@ public function test_in_body_skips_unexpected_button_closer() {
* Verifies insertion of a BUTTON element when no existing BUTTON is already in scope.
*
* @ticket 58961
*
* @since 6.4.0
*
* @throws WP_HTML_Unsupported_Exception
*/
public function test_in_body_button_with_no_button_in_scope() {
$p = WP_HTML_Processor::create_fragment( '<div><p>Click the button <button one>here</button>!</p></div><button two>not here</button>' );
Expand All @@ -75,8 +67,6 @@ public function test_in_body_button_with_no_button_in_scope() {
* @ticket 58961
*
* @since 6.4.0
*
* @throws WP_HTML_Unsupported_Exception
*/
public function test_in_body_button_with_button_in_scope_as_parent() {
$p = WP_HTML_Processor::create_fragment( '<div><p>Click the button <button one>almost<button two>here</button>!</p></div><button three>not here</button>' );
Expand Down Expand Up @@ -110,8 +100,6 @@ public function test_in_body_button_with_button_in_scope_as_parent() {
* @ticket 58961
*
* @since 6.4.0
*
* @throws WP_HTML_Unsupported_Exception
*/
public function test_in_body_button_with_button_in_scope_as_ancestor() {
$p = WP_HTML_Processor::create_fragment( '<div><button one><p>Click the button <span><button two>here</button>!</span></p></div><button three>not here</button>' );
Expand All @@ -137,7 +125,39 @@ public function test_in_body_button_with_button_in_scope_as_ancestor() {
$this->assertSame( array( 'HTML', 'BODY', 'BUTTON' ), $p->get_breadcrumbs(), 'Failed to produce expected DOM nesting for third button.' );
}

/*
/**
* Verifies that H1 through H6 elements close an open P element.
*
* @ticket {TICKET_NUMBER}
*
* @dataProvider data_heading_elements
*
* @param string $tag_name Name of H1 - H6 element under test.
*/
public function test_heading_element_closes_open_p_tag( $tag_name ) {
$p = WP_HTML_Processor::create_fragment( "<p>Open<{$tag_name}>Closed P</{$tag_name}></p>" );

$p->next_tag( $tag_name );
$this->assertSame( array( 'HTML', 'BODY', $tag_name ), $p->get_breadcrumbs() );
}

/**
* Data provider.
*
* @return array[].
*/
public function data_heading_elements() {
return array(
array( 'H1' ),
array( 'H2' ),
array( 'H3' ),
array( 'H4' ),
array( 'H5' ),
array( 'H5' ),
);
}

/**
* Verifies that when "in body" and encountering "any other end tag"
* that the HTML processor ignores the end tag if there's a special
* element on the stack of open elements before the matching opening.
Expand All @@ -160,7 +180,7 @@ public function test_in_body_any_other_end_tag_with_unclosed_special_element() {
$this->assertSame( array( 'HTML', 'BODY', 'DIV', 'SPAN', 'DIV' ), $p->get_breadcrumbs(), 'Failed to produce expected DOM nesting: SPAN should still be open and DIV should be its child.' );
}

/*
/**
* Verifies that when "in body" and encountering "any other end tag"
* that the HTML processor closes appropriate elements on the stack of
* open elements up to the matching opening.
Expand Down

0 comments on commit 81ef71d

Please sign in to comment.