diff --git a/Gruntfile.js b/Gruntfile.js index 1c4280aff213b..5f9109fac3cb0 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -1837,6 +1837,7 @@ module.exports = function(grunt) { 'clean:js', 'build:webpack', 'copy:js', + 'copy-vendor-scripts', 'file_append', 'uglify:all', 'concat:tinymce', @@ -2133,7 +2134,6 @@ module.exports = function(grunt) { 'build:css', 'build:codemirror', 'build:gutenberg', - 'copy-vendor-scripts', 'build:certificates' ] ); } else { @@ -2145,7 +2145,6 @@ module.exports = function(grunt) { 'build:css', 'build:codemirror', 'build:gutenberg', - 'copy-vendor-scripts', 'replace:source-maps', 'verify:build' ] ); diff --git a/composer.json b/composer.json index ee5c5d0c0aa03..50ece961d9d63 100644 --- a/composer.json +++ b/composer.json @@ -24,7 +24,8 @@ "wp-coding-standards/wpcs": "~3.3.0", "phpcompatibility/phpcompatibility-wp": "~2.1.3", "phpstan/phpstan": "2.1.39", - "yoast/phpunit-polyfills": "^1.1.0" + "yoast/phpunit-polyfills": "^1.1.0", + "phpbench/phpbench": "^1.5" }, "config": { "allow-plugins": { diff --git a/phpbench-bootstrap.php b/phpbench-bootstrap.php new file mode 100644 index 0000000000000..bdda1b11b8dbb --- /dev/null +++ b/phpbench-bootstrap.php @@ -0,0 +1,74 @@ + /tests/phpunit/tests/* + /tests/benchmarks/benchmarks/* /src/wp-admin/includes/class-wp-filesystem-ftpsockets\.php /src/wp-includes/class-wp-oembed\.php @@ -329,6 +330,7 @@ /tests/phpunit/* + /tests/benchmarks/benchmarks/* diff --git a/src/wp-admin/css/color-picker.css b/src/wp-admin/css/color-picker.css index 1e7525799e855..8264432dd39cc 100644 --- a/src/wp-admin/css/color-picker.css +++ b/src/wp-admin/css/color-picker.css @@ -10,7 +10,7 @@ /* Needs higher specificity to override `.wp-core-ui .button`. */ .wp-picker-container .wp-color-result.button { - min-height: 30px; + min-height: 32px; margin: 0 6px 6px 0; padding: 0 0 0 30px; font-size: 11px; @@ -22,7 +22,7 @@ border-left: 1px solid #c3c4c7; color: #50575e; display: block; - line-height: 2.54545455; /* 28px */ + line-height: 2.72727273; /* 30px */ padding: 0 6px; text-align: center; } @@ -76,8 +76,8 @@ .wp-customizer .wp-picker-input-wrap .button.wp-picker-clear { margin-left: 6px; padding: 0 8px; - line-height: 2.54545455; /* 28px */ - min-height: 30px; + line-height: 2.72727273; /* 30px */ + min-height: 32px; } .wp-picker-container .iris-square-slider .ui-slider-handle:focus { @@ -97,7 +97,7 @@ margin: 0; padding: 0 5px; vertical-align: top; - min-height: 30px; + min-height: 32px; } .wp-color-picker::-webkit-input-placeholder { diff --git a/src/wp-admin/css/common.css b/src/wp-admin/css/common.css index 211cf0022c1e0..b317af45e023e 100644 --- a/src/wp-admin/css/common.css +++ b/src/wp-admin/css/common.css @@ -1473,22 +1473,22 @@ div.error p, color: #1e1e1e; } -.notice a, -.error a, -.updated a { +div.notice a, +div.error a, +div.updated a { color: var(--wp-admin-theme-color-darker-10); text-decoration: underline; } -.notice a:hover, -.error a:hover, -.updated a:hover { +div.notice a:hover, +div.error a:hover, +div.updated a:hover { color: var(--wp-admin-theme-color-darker-20); } -.notice a:focus, -.error a:focus, -.updated a:focus { +div.notice a:focus, +div.error a:focus, +div.updated a:focus { box-shadow: 0 0 0 var(--wp-admin-border-width-focus) var(--wp-admin-theme-color); outline: 2px solid transparent; border-radius: 2px; @@ -2077,17 +2077,6 @@ p.auto-update-status { box-shadow: 0 2px 0 rgba(0, 0, 0, 0.02), 0 1px 0 rgba(0, 0, 0, 0.02); } -.contextual-help-tabs .active::after { - content: ""; - position: absolute; - top: 0; - right: -1px; - width: 2px; - height: 100%; - background: inherit; - z-index: 2; -} - .contextual-help-tabs .active a { border-color: #c3c4c7; color: #2c3338; @@ -2292,7 +2281,7 @@ html.wp-toolbar { line-height: 1; } -.postbox.closed { +.postbox.closed .postbox-header { border-bottom: 0; } diff --git a/src/wp-admin/css/edit.css b/src/wp-admin/css/edit.css index f2ff6a485767a..b98dd889c59fe 100644 --- a/src/wp-admin/css/edit.css +++ b/src/wp-admin/css/edit.css @@ -994,15 +994,16 @@ form#tags-filter { } .privacy-settings-accordion-actions { - text-align: right; - display: block; + justify-content: right; + display: flex; + align-items: center; + flex-wrap: wrap; + gap: 1em; } .privacy-settings-accordion-actions .success { display: none; color: #007017; - padding-right: 1em; - padding-top: 6px; } .privacy-settings-accordion-actions .success.visible { diff --git a/src/wp-admin/includes/class-wp-posts-list-table.php b/src/wp-admin/includes/class-wp-posts-list-table.php index fc039a7573f19..c7d10fca217ef 100644 --- a/src/wp-admin/includes/class-wp-posts-list-table.php +++ b/src/wp-admin/includes/class-wp-posts-list-table.php @@ -437,7 +437,7 @@ protected function get_bulk_actions() { if ( $this->is_trash ) { $actions['untrash'] = __( 'Restore' ); } else { - $actions['edit'] = __( 'Bulk edit' ); + $actions['edit'] = _x( 'Bulk edit', 'verb' ); } } diff --git a/src/wp-admin/includes/meta-boxes.php b/src/wp-admin/includes/meta-boxes.php index a1859f45c7422..0884c110b65bd 100644 --- a/src/wp-admin/includes/meta-boxes.php +++ b/src/wp-admin/includes/meta-boxes.php @@ -1453,7 +1453,14 @@ function link_advanced_meta_box( $link ) { - + + + diff --git a/src/wp-includes/class-wp-block-patterns-registry.php b/src/wp-includes/class-wp-block-patterns-registry.php index c9bcd63549ab4..782ee9030c19e 100644 --- a/src/wp-includes/class-wp-block-patterns-registry.php +++ b/src/wp-includes/class-wp-block-patterns-registry.php @@ -227,10 +227,9 @@ public function get_registered( $pattern_name ) { * and per style. */ public function get_all_registered( $outside_init_only = false ) { - $patterns = $outside_init_only - ? $this->registered_patterns_outside_init - : $this->registered_patterns; - $hooked_blocks = get_hooked_blocks(); + $patterns = $outside_init_only + ? $this->registered_patterns_outside_init + : $this->registered_patterns; foreach ( $patterns as $index => $pattern ) { $content = $this->get_content( $pattern['name'], $outside_init_only ); diff --git a/src/wp-includes/class-wp-connector-registry.php b/src/wp-includes/class-wp-connector-registry.php index 18a5f80c94dbd..d7643360efeeb 100644 --- a/src/wp-includes/class-wp-connector-registry.php +++ b/src/wp-includes/class-wp-connector-registry.php @@ -40,7 +40,7 @@ * env_var_name?: non-empty-string * }, * plugin?: array{ - * slug: non-empty-string + * file: non-empty-string * } * } */ @@ -109,7 +109,8 @@ final class WP_Connector_Registry { * @type array $plugin { * Optional. Plugin data for install/activate UI. * - * @type string $slug The WordPress.org plugin slug. + * @type string $file The plugin's main file path relative to the plugins + * directory (e.g. 'akismet/akismet.php' or 'hello.php'). * } * } * @return array|null The registered connector data on success, null on failure. @@ -242,8 +243,8 @@ public function register( string $id, array $args ): ?array { } } - if ( ! empty( $args['plugin'] ) && is_array( $args['plugin'] ) ) { - $connector['plugin'] = $args['plugin']; + if ( ! empty( $args['plugin'] ) && is_array( $args['plugin'] ) && ! empty( $args['plugin']['file'] ) ) { + $connector['plugin'] = array( 'file' => $args['plugin']['file'] ); } $this->registered_connectors[ $id ] = $connector; diff --git a/src/wp-includes/collaboration/class-wp-http-polling-sync-server.php b/src/wp-includes/collaboration/class-wp-http-polling-sync-server.php index 88554a48c7d54..a90821ab78d3e 100644 --- a/src/wp-includes/collaboration/class-wp-http-polling-sync-server.php +++ b/src/wp-includes/collaboration/class-wp-http-polling-sync-server.php @@ -37,6 +37,30 @@ class WP_HTTP_Polling_Sync_Server { */ const COMPACTION_THRESHOLD = 50; + /** + * Maximum total size (in bytes) of the request body. + * + * @since 7.0.0 + * @var int + */ + const MAX_BODY_SIZE = 16 * MB_IN_BYTES; + + /** + * Maximum number of rooms allowed per request. + * + * @since 7.0.0 + * @var int + */ + const MAX_ROOMS_PER_REQUEST = 50; + + /** + * Maximum length of a single update data string. + * + * @since 7.0.0 + * @var int + */ + const MAX_UPDATE_DATA_SIZE = MB_IN_BYTES; + /** * Sync update type: compaction. * @@ -96,8 +120,9 @@ public function register_routes(): void { $typed_update_args = array( 'properties' => array( 'data' => array( - 'type' => 'string', - 'required' => true, + 'type' => 'string', + 'required' => true, + 'maxLength' => self::MAX_UPDATE_DATA_SIZE, ), 'type' => array( 'type' => 'string', @@ -149,12 +174,14 @@ public function register_routes(): void { 'methods' => array( WP_REST_Server::CREATABLE ), 'callback' => array( $this, 'handle_request' ), 'permission_callback' => array( $this, 'check_permissions' ), + 'validate_callback' => array( $this, 'validate_request' ), 'args' => array( 'rooms' => array( 'items' => array( 'properties' => $room_args, 'type' => 'object', ), + 'maxItems' => self::MAX_ROOMS_PER_REQUEST, 'required' => true, 'type' => 'array', ), @@ -223,6 +250,30 @@ public function check_permissions( WP_REST_Request $request ) { return true; } + /** + * Validates that the request body does not exceed the maximum allowed size. + * + * Runs as the route-level validate_callback, after per-arg schema + * validation has already passed. + * + * @since 7.0.0 + * + * @param WP_REST_Request $request The REST request. + * @return true|WP_Error True if valid, WP_Error if the body is too large. + */ + public function validate_request( WP_REST_Request $request ) { + $body = $request->get_body(); + if ( is_string( $body ) && strlen( $body ) > self::MAX_BODY_SIZE ) { + return new WP_Error( + 'rest_sync_body_too_large', + __( 'Request body is too large.' ), + array( 'status' => 413 ) + ); + } + + return true; + } + /** * Handles request: stores sync updates and awareness data, and returns * updates the client is missing. @@ -278,24 +329,47 @@ public function handle_request( WP_REST_Request $request ) { * * @param string $entity_kind The entity kind, e.g. 'postType', 'taxonomy', 'root'. * @param string $entity_name The entity name, e.g. 'post', 'category', 'site'. - * @param string|null $object_id The object ID / entity key for single entities, null for collections. + * @param string|null $object_id The numeric object ID / entity key for single entities, null for collections. * @return bool True if user has permission, otherwise false. */ private function can_user_sync_entity_type( string $entity_kind, string $entity_name, ?string $object_id ): bool { - // Handle single post type entities with a defined object ID. - if ( 'postType' === $entity_kind && is_numeric( $object_id ) ) { - return current_user_can( 'edit_post', (int) $object_id ); + if ( is_string( $object_id ) ) { + if ( ! ctype_digit( $object_id ) ) { + return false; + } + $object_id = (int) $object_id; } - - // Handle single taxonomy term entities with a defined object ID. - if ( 'taxonomy' === $entity_kind && is_numeric( $object_id ) ) { - $taxonomy = get_taxonomy( $entity_name ); - return isset( $taxonomy->cap->assign_terms ) && current_user_can( $taxonomy->cap->assign_terms ); + if ( null !== $object_id && $object_id <= 0 ) { + // Object ID must be numeric if provided. + return false; } - // Handle single comment entities with a defined object ID. - if ( 'root' === $entity_kind && 'comment' === $entity_name && is_numeric( $object_id ) ) { - return current_user_can( 'edit_comment', (int) $object_id ); + // Validate permissions for the provided object ID. + if ( is_int( $object_id ) ) { + // Handle single post type entities with a defined object ID. + if ( 'postType' === $entity_kind ) { + if ( get_post_type( $object_id ) !== $entity_name ) { + // Post is not of the specified post type. + return false; + } + return current_user_can( 'edit_post', $object_id ); + } + + // Handle single taxonomy term entities with a defined object ID. + if ( 'taxonomy' === $entity_kind ) { + $term_exists = term_exists( $object_id, $entity_name ); + if ( ! is_array( $term_exists ) || ! isset( $term_exists['term_id'] ) ) { + // Either term doesn't exist OR term is not in specified taxonomy. + return false; + } + + return current_user_can( 'edit_term', $object_id ); + } + + // Handle single comment entities with a defined object ID. + if ( 'root' === $entity_kind && 'comment' === $entity_name ) { + return current_user_can( 'edit_comment', $object_id ); + } } // All the remaining checks are for collections. If an object ID is provided, diff --git a/src/wp-includes/connectors.php b/src/wp-includes/connectors.php index 06683ccaaa25c..a11faeb637623 100644 --- a/src/wp-includes/connectors.php +++ b/src/wp-includes/connectors.php @@ -58,7 +58,8 @@ function wp_is_connector_registered( string $id ): bool { * @type array $plugin { * Optional. Plugin data for install/activate UI. * - * @type string $slug The WordPress.org plugin slug. + * @type string $file The plugin's main file path relative to the plugins + * directory (e.g. 'akismet/akismet.php' or 'hello.php'). * } * } * @phpstan-return ?array{ @@ -74,7 +75,7 @@ function wp_is_connector_registered( string $id ): bool { * env_var_name?: non-empty-string * }, * plugin?: array{ - * slug: non-empty-string + * file: non-empty-string * } * } */ @@ -118,7 +119,8 @@ function wp_get_connector( string $id ): ?array { * @type array $plugin { * Optional. Plugin data for install/activate UI. * - * @type string $slug The WordPress.org plugin slug. + * @type string $file The plugin's main file path relative to the plugins + * directory (e.g. 'akismet/akismet.php' or 'hello.php'). * } * } * } @@ -135,7 +137,7 @@ function wp_get_connector( string $id ): ?array { * env_var_name?: non-empty-string * }, * plugin?: array{ - * slug: non-empty-string + * file: non-empty-string * } * }> */ @@ -208,6 +210,25 @@ function _wp_connectors_init(): void { _wp_connectors_register_default_ai_providers( $registry ); } + // Non-AI default connectors. + $registry->register( + 'akismet', + array( + 'name' => __( 'Akismet Anti-spam' ), + 'description' => __( 'Protect your site from spam.' ), + 'type' => 'spam_filtering', + 'plugin' => array( + 'file' => 'akismet/akismet.php', + ), + 'authentication' => array( + 'method' => 'api_key', + 'credentials_url' => 'https://akismet.com/get/', + 'setting_name' => 'wordpress_api_key', + 'constant_name' => 'WPCOM_API_KEY', + ), + ) + ); + /** * Fires when the connector registry is ready for plugins to register connectors. * @@ -256,7 +277,7 @@ function _wp_connectors_register_default_ai_providers( WP_Connector_Registry $re 'description' => __( 'Text generation with Claude.' ), 'type' => 'ai_provider', 'plugin' => array( - 'slug' => 'ai-provider-for-anthropic', + 'file' => 'ai-provider-for-anthropic/plugin.php', ), 'authentication' => array( 'method' => 'api_key', @@ -268,7 +289,7 @@ function _wp_connectors_register_default_ai_providers( WP_Connector_Registry $re 'description' => __( 'Text and image generation with Gemini and Imagen.' ), 'type' => 'ai_provider', 'plugin' => array( - 'slug' => 'ai-provider-for-google', + 'file' => 'ai-provider-for-google/plugin.php', ), 'authentication' => array( 'method' => 'api_key', @@ -280,7 +301,7 @@ function _wp_connectors_register_default_ai_providers( WP_Connector_Registry $re 'description' => __( 'Text and image generation with GPT and Dall-E.' ), 'type' => 'ai_provider', 'plugin' => array( - 'slug' => 'ai-provider-for-openai', + 'file' => 'ai-provider-for-openai/plugin.php', ), 'authentication' => array( 'method' => 'api_key', @@ -636,15 +657,9 @@ function _wp_connectors_pass_default_keys_to_ai_client(): void { function _wp_connectors_get_connector_script_module_data( array $data ): array { $registry = AiClient::defaultRegistry(); - // Build a slug-to-file map for plugin installation status. - if ( ! function_exists( 'get_plugins' ) ) { + if ( ! function_exists( 'is_plugin_active' ) ) { require_once ABSPATH . 'wp-admin/includes/plugin.php'; } - $plugin_files_by_slug = array(); - foreach ( array_keys( get_plugins() ) as $plugin_file ) { - $slug = str_contains( $plugin_file, '/' ) ? dirname( $plugin_file ) : str_replace( '.php', '', $plugin_file ); - $plugin_files_by_slug[ $slug ] = $plugin_file; - } $connectors = array(); foreach ( wp_get_connectors() as $connector_id => $connector_data ) { @@ -676,18 +691,14 @@ function _wp_connectors_get_connector_script_module_data( array $data ): array { 'authentication' => $auth_out, ); - if ( ! empty( $connector_data['plugin']['slug'] ) ) { - $plugin_slug = $connector_data['plugin']['slug']; - $plugin_file = $plugin_files_by_slug[ $plugin_slug ] ?? null; - - $is_installed = null !== $plugin_file; - $is_activated = $is_installed && is_plugin_active( $plugin_file ); + if ( ! empty( $connector_data['plugin']['file'] ) ) { + $file = $connector_data['plugin']['file']; + $is_installed = file_exists( wp_normalize_path( WP_PLUGIN_DIR . '/' . $file ) ); + $is_activated = $is_installed && is_plugin_active( $file ); $connector_out['plugin'] = array( - 'slug' => $plugin_slug, - 'pluginFile' => $is_installed - ? ( str_ends_with( $plugin_file, '.php' ) ? substr( $plugin_file, 0, -4 ) : $plugin_file ) - : null, + 'file' => $file, + 'isInstalled' => $is_installed, 'isActivated' => $is_activated, ); } diff --git a/src/wp-includes/css/media-views.css b/src/wp-includes/css/media-views.css index 1b3c6edd7678f..f78a946c260f7 100644 --- a/src/wp-includes/css/media-views.css +++ b/src/wp-includes/css/media-views.css @@ -56,7 +56,7 @@ .media-frame a:focus { border-radius: 2px; box-shadow: 0 0 0 var(--wp-admin-border-width-focus, 1.5px) var(--wp-admin-theme-color, #3858e9); - color: #043959; + color: var(--wp-admin-theme-color-darker-20, #183ad6); /* Only visible in Windows High Contrast mode */ outline: 2px solid transparent; } @@ -244,13 +244,13 @@ .media-modal-close:hover, .media-modal-close:active { - color: #135e96; + color: var(--wp-admin-theme-color, #3858e9); } .media-modal-close:focus { - color: #135e96; - border-color: #4f94d4; - box-shadow: 0 0 3px rgba(34, 113, 177, 0.8); + color: var(--wp-admin-theme-color, #3858e9); + border-color: var(--wp-admin-theme-color, #3858e9); + box-shadow: 0 0 3px rgba(var(--wp-admin-theme-color--rgb, 56, 88, 233), 0.8); /* Only visible in Windows High Contrast mode */ outline: 2px solid transparent; } @@ -673,7 +673,7 @@ font-size: 14px; line-height: 1.28571428; background: transparent; - color: #2271b1; + color: var(--wp-admin-theme-color, #3858e9); text-align: left; text-decoration: none; cursor: pointer; @@ -684,7 +684,7 @@ } .media-menu .media-menu-item:active { - color: #2271b1; + color: var(--wp-admin-theme-color, #3858e9); outline: none; } @@ -696,7 +696,7 @@ .media-menu .media-menu-item:focus { box-shadow: 0 0 0 var(--wp-admin-border-width-focus, 1.5px) var(--wp-admin-theme-color, #3858e9); - color: #043959; + color: var(--wp-admin-theme-color-darker-20, #183ad6); /* Only visible in Windows High Contrast mode */ outline: 2px solid transparent; } @@ -739,7 +739,7 @@ .media-router .media-menu-item:hover, .media-router .media-menu-item:active { - color: #2271b1; + color: var(--wp-admin-theme-color, #3858e9); } .media-router .active, @@ -749,7 +749,7 @@ .media-router .media-menu-item:focus { box-shadow: 0 0 0 var(--wp-admin-border-width-focus, 1.5px) var(--wp-admin-theme-color, #3858e9); - color: #043959; + color: var(--wp-admin-theme-color-darker-20, #183ad6); /* Only visible in Windows High Contrast mode */ outline: 2px solid transparent; z-index: 1; @@ -1321,8 +1321,8 @@ } .uploader-inline .close:focus { - outline: 1px solid #4f94d4; - box-shadow: 0 0 3px rgba(34, 113, 177, 0.8); + outline: 1px solid var(--wp-admin-theme-color, #3858e9); + box-shadow: 0 0 3px rgba(var(--wp-admin-theme-color--rgb, 56, 88, 233), 0.8); } .attachments-browser.hide-sidebar .attachments, @@ -1409,7 +1409,7 @@ height: 10px; min-width: 20px; width: 0; - background: #2271b1; + background: var(--wp-admin-theme-color, #3858e9); border-radius: 10px; transition: width 300ms; } @@ -1527,7 +1527,7 @@ .uploader-window, .wp-editor-wrap .uploader-editor.droppable { - background: rgba(10, 75, 120, 0.9); + background-color: rgba(var(--wp-admin-theme-color--rgb, 56, 88, 233), 0.9); } .uploader-window-content, @@ -1688,13 +1688,13 @@ margin: 1px 8px 1px -8px; line-height: 1.4; border-right: 1px solid #dcdcde; - color: #2271b1; + color: var(--wp-admin-theme-color, #3858e9); text-decoration: none; } .media-selection .button-link:hover, .media-selection .button-link:focus { - color: #135e96; + color: var(--wp-admin-theme-color-darker-20, #183ad6); } .media-selection .button-link:last-child { @@ -1752,7 +1752,7 @@ .wp-core-ui .media-selection .attachment.details:focus { box-shadow: 0 0 0 1px #fff, - 0 0 2px 3px #4f94d4; + 0 0 2px 3px var(--wp-admin-theme-color, #3858e9); /* Only visible in Windows High Contrast mode */ outline: 2px solid transparent; } @@ -1764,7 +1764,7 @@ .wp-core-ui .media-selection .attachment.details { box-shadow: 0 0 0 1px #fff, - 0 0 0 3px #2271b1; + 0 0 0 3px var(--wp-admin-theme-color, #3858e9); } .media-selection:after { @@ -2044,7 +2044,7 @@ margin: 0; padding: 0; background: transparent; - color: #2271b1; + color: var(--wp-admin-theme-color, #3858e9); font-size: 20px; line-height: 1; cursor: pointer; @@ -2053,9 +2053,9 @@ } .wp-core-ui.media-modal .image-editor .imgedit-help-toggle:focus { - color: #2271b1; - border-color: #2271b1; - box-shadow: 0 0 0 1px #2271b1; + color: var(--wp-admin-theme-color, #3858e9); + border-color: var(--wp-admin-theme-color, #3858e9); + box-shadow: 0 0 0 1px var(--wp-admin-theme-color, #3858e9); /* Only visible in Windows High Contrast mode */ outline: 2px solid transparent; } diff --git a/src/wp-includes/script-loader.php b/src/wp-includes/script-loader.php index e164da51bc248..42d42b3f8781d 100644 --- a/src/wp-includes/script-loader.php +++ b/src/wp-includes/script-loader.php @@ -2020,8 +2020,8 @@ function wp_localize_jquery_ui_datepicker() { 'currentText' => __( 'Today' ), 'monthNames' => array_values( $wp_locale->month ), 'monthNamesShort' => array_values( $wp_locale->month_abbrev ), - 'nextText' => __( 'Next' ), - 'prevText' => __( 'Previous' ), + 'nextText' => _x( 'Next', 'datepicker: navigate to next month' ), + 'prevText' => _x( 'Previous', 'datepicker: navigate to previous month' ), 'dayNames' => array_values( $wp_locale->weekday ), 'dayNamesShort' => array_values( $wp_locale->weekday_abbrev ), 'dayNamesMin' => array_values( $wp_locale->weekday_initial ), diff --git a/tests/benchmarks/benchmarks/html-api/WpHtmlTagProcessorBench.php b/tests/benchmarks/benchmarks/html-api/WpHtmlTagProcessorBench.php new file mode 100644 index 0000000000000..7422235a845a7 --- /dev/null +++ b/tests/benchmarks/benchmarks/html-api/WpHtmlTagProcessorBench.php @@ -0,0 +1,177 @@ +processor = null; + } + + /** + * Benchmark normalizing simple Unix paths. + * @param array{0: string} $params + */ + #[Bench\Warmup( 2 )] + #[Bench\Iterations( 50 )] + #[Bench\Revs( 10 )] + #[Bench\BeforeMethods( 'set_up_script_tag_processor' )] + #[Bench\AfterMethods( 'clean_up_processor' )] + #[Bench\ParamProviders( 'provide_script_tag_contents' )] + public function bench_javascript_custom_escape( array $params ): void { + [ $source_text] = $params; + assert( $this->processor->set_modifiable_text( $source_text ), 'Failed to set modifiable text.' ); + } + + public function set_up_script_tag_processor(): void { + $this->processor = new WP_HTML_Tag_Processor( '' ); + $this->processor->next_tag(); + } + + /** + * @return iterable + */ + public static function provide_script_tag_contents(): iterable { + yield 'empty' => array( '' ); + + yield 'short' => array( 'console.log("Hello, World!");' ); + + yield 'many replacements' => array( + <<<'JS' + /*