diff --git a/src/wp-admin/options-privacy.php b/src/wp-admin/options-privacy.php index 6441a43491ac4..92de2ca9b9b68 100644 --- a/src/wp-admin/options-privacy.php +++ b/src/wp-admin/options-privacy.php @@ -270,7 +270,7 @@ static function ( $body_class ) { -
+ - +
- +

'inactive-widgets-control-remove' ); diff --git a/src/wp-content/themes/twentyfifteen/css/editor-blocks.css b/src/wp-content/themes/twentyfifteen/css/editor-blocks.css index 71afab38b7a14..1041ff5f14741 100644 --- a/src/wp-content/themes/twentyfifteen/css/editor-blocks.css +++ b/src/wp-content/themes/twentyfifteen/css/editor-blocks.css @@ -803,7 +803,7 @@ p.has-drop-cap:not(:focus)::first-letter { } } -/* Seperator */ +/* Separator */ .wp-block-separator { max-width: 100px; diff --git a/src/wp-content/themes/twentynineteen/inc/color-patterns.php b/src/wp-content/themes/twentynineteen/inc/color-patterns.php index 2295436897ff3..abe3419fa9f14 100644 --- a/src/wp-content/themes/twentynineteen/inc/color-patterns.php +++ b/src/wp-content/themes/twentynineteen/inc/color-patterns.php @@ -72,8 +72,8 @@ function twentynineteen_custom_colors_css() { * Set background for: * - featured image :before * - featured image :before - * - post thumbmail :before - * - post thumbmail :before + * - post thumbnail :before + * - post thumbnail :before * - Submenu * - Sticky Post * - buttons diff --git a/src/wp-content/themes/twentyseventeen/functions.php b/src/wp-content/themes/twentyseventeen/functions.php index 9c54612d6a548..df9d3b381d2c2 100644 --- a/src/wp-content/themes/twentyseventeen/functions.php +++ b/src/wp-content/themes/twentyseventeen/functions.php @@ -452,7 +452,7 @@ function twentyseventeen_scripts() { wp_enqueue_style( 'twentyseventeen-fonts', twentyseventeen_fonts_url(), array(), $font_version ); // Theme stylesheet. - wp_enqueue_style( 'twentyseventeen-style', get_stylesheet_uri(), array(), '20231107' ); + wp_enqueue_style( 'twentyseventeen-style', get_stylesheet_uri(), array(), '20240116' ); // Theme block stylesheet. wp_enqueue_style( 'twentyseventeen-block-style', get_theme_file_uri( '/assets/css/blocks.css' ), array( 'twentyseventeen-style' ), '20220912' ); diff --git a/src/wp-content/themes/twentyseventeen/readme.txt b/src/wp-content/themes/twentyseventeen/readme.txt index 18bca952bff7c..a2693c72d93c8 100644 --- a/src/wp-content/themes/twentyseventeen/readme.txt +++ b/src/wp-content/themes/twentyseventeen/readme.txt @@ -3,7 +3,7 @@ Contributors: wordpressdotorg Requires at least: 4.7 Tested up to: 6.4 Requires PHP: 5.2.4 -Version: 3.4 +Version: 3.5 License: GPLv2 or later License URI: http://www.gnu.org/licenses/gpl-2.0.html Tags: one-column, two-columns, right-sidebar, flexible-header, accessibility-ready, custom-colors, custom-header, custom-menu, custom-logo, editor-style, featured-images, footer-widgets, post-formats, rtl-language-support, sticky-post, theme-options, threaded-comments, translation-ready, block-patterns @@ -75,6 +75,11 @@ Source: https://stocksnap.io/photo/striped-fabric-9CBVWF2CDU == Changelog == += 3.5 = +* Released: January 16, 2024 + +https://codex.wordpress.org/Twenty_Seventeen_Theme_Changelog#Version_3.5 + = 3.4 = * Released: November 7, 2023 diff --git a/src/wp-content/themes/twentyseventeen/style.css b/src/wp-content/themes/twentyseventeen/style.css index e87313433ed7d..a9e1d5a45aea3 100644 --- a/src/wp-content/themes/twentyseventeen/style.css +++ b/src/wp-content/themes/twentyseventeen/style.css @@ -4,7 +4,7 @@ Theme URI: https://wordpress.org/themes/twentyseventeen/ Author: the WordPress team Author URI: https://wordpress.org/ Description: Twenty Seventeen brings your site to life with header video and immersive featured images. With a focus on business sites, it features multiple sections on the front page as well as widgets, navigation and social menus, a logo, and more. Personalize its asymmetrical grid with a custom color scheme and showcase your multimedia content with post formats. Our default theme for 2017 works great in many languages, for any abilities, and on any device. -Version: 3.4 +Version: 3.5 Tested up to: 6.4 Requires at least: 4.7 Requires PHP: 5.2.4 diff --git a/src/wp-content/themes/twentysixteen/css/blocks.css b/src/wp-content/themes/twentysixteen/css/blocks.css index de6f19fd5359d..3b6257acf0e55 100644 --- a/src/wp-content/themes/twentysixteen/css/blocks.css +++ b/src/wp-content/themes/twentysixteen/css/blocks.css @@ -292,7 +292,7 @@ p.has-drop-cap:not(:focus)::first-letter { outline-offset: -4px; } -/* Seperator */ +/* Separator */ hr.wp-block-separator { border: 0; diff --git a/src/wp-content/themes/twentytwenty/assets/css/editor-style-block-rtl.css b/src/wp-content/themes/twentytwenty/assets/css/editor-style-block-rtl.css index fcc5246aff2cf..b4dfac35c0a2a 100644 --- a/src/wp-content/themes/twentytwenty/assets/css/editor-style-block-rtl.css +++ b/src/wp-content/themes/twentytwenty/assets/css/editor-style-block-rtl.css @@ -44,28 +44,6 @@ src: url(data:application/font-woff2;charset=utf-8;base64,d09GMgABAAAAAAMoAA0AAAAACDQAAALTAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP0ZGVE0cGh4GYACCahEICjx3CywAATYCJANUBCAFhiEHgWwbXQfILgpsY+rQRRARwyAs6uL7pxzYhxEE+32b3aeHmifR6tklkS9hiZA0ewkqGRJE+H7/+6378ASViK/PGeavqJyOzsceKi1s3BCiQsiOdn1r/RBgIJYEgCUhbm/8/8/h4saPssnTNkkiWUBrTRtjmQSajw3Ui3pZ3LYDPD+XG2C3JA/yKAS8/rU5eNfuGqRf4eNNgV4YAlIIgxglEkWe6FYpq10+wi3g+/nUgvgPFczNrz/RsTgVm/zfbPuHZlsuQECxuyqBcQwKFBjFgKO8AqP4bAN9tFJtnM9xPcbNjeXS/x1wY/xU52f5W/X1+9cnH4YwKIaoRRAkUkj/YlAAeF/624foiIDBgBmgQBeGAyhBljUPZUm/l2dTvmpqcBDUOHdbPZWd8JsBAsGr4w8/EDn82/bUPx4eh0YNrQTBuHO2FjQEAGBwK0DeI37DpQVqdERS4gZBhpeUhWCfLFz7J99aEBgsJCHvUGAdAPp4IADDCAPCEFMGpMZ9AQpTfQtQGhLbGVBZFV8BaqNyP68oTZgHNj3M8kBPfXTTC9t90UuzYhy9ciH0grVlOcqyCytisvbsERsEYztiznR0WCrmTksJwbSNK6fd1Rvr25I9oLvctUoEbNOmXJbqgYgPXEHJ82IUsrCnpkxh23F1rfZ2zcRnJYoXtauB3VTFkFXQg3uoZYD5qE0kdjDtoDoF1h2bulGmev5HbYhbrjtohQSRI4aNOkffIcT+d3v6atpaYh3JvPoQsztCcqvaBkppDSPcQ3bw3KaCBo1f5CJWTZEgW3LjLofYg51MaVezrx8xZitYbQ9KYeoRaqQdVLwSEfrKXLK1otCWOKNdR/YwYAfon5Yk8O2MJfSD10dPGA5PIJJQMkah0ugMJiv6x4Dm7LEa8xnrRGGGLAg4sAlbsA07sAt76DOsXKO3hIjtIlpnnFrt1qW4kh6NhS83P/6HB/fl1SMAAA==) format("woff2"), url(data:application/font-woff;charset=utf-8;base64,d09GRgABAAAAAAUQAA0AAAAACDQAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAABGRlRNAAAE9AAAABwAAAAchf5yU0dERUYAAATYAAAAHAAAAB4AJwAbT1MvMgAAAaAAAABJAAAAYJAcgU5jbWFwAAACIAAAAF4AAAFqUUxBZ2dhc3AAAATQAAAACAAAAAgAAAAQZ2x5ZgAAApAAAAAyAAAAPL0n8y9oZWFkAAABMAAAADAAAAA2Fi93Z2hoZWEAAAFgAAAAHQAAACQOSgWaaG10eAAAAewAAAAzAAAAVC7TAQBsb2NhAAACgAAAABAAAAAsAOQBAm1heHAAAAGAAAAAHQAAACAAWQALbmFtZQAAAsQAAAF6AAADIYvD/Adwb3N0AAAEQAAAAI4AAADsapk2o3jaY2BkYGAA4ov5mwzj+W2+MnCzXwCKMNzgCBSB0LfbQDQ7AxuI4mBgAlEAFKQIRHjaY2BkYGD3+NvCwMDBAALsDAyMDKhAFAA3+wH3AAAAeNpjYGRgYBBl4GBgYgABEMnIABJzAPMZAAVmAGUAAAB42mNgZlJhnMDAysDCKsKygYGBYRqEZtrDYMT4D8gHSmEHjgUFOQwODAqqf9g9/rYwMLB7MNUAhRlBcsxBrMlASoGBEQAj8QtyAAAAeNrjYGBkAAGmWQwMjO8gmBnIZ2NA0ExAzNjAAFYJVn0ASBsD6VAIDZb7AtELAgANIgb9AHjaY2BgYGaAYBkGRgYQSAHyGMF8FgYPIM3HwMHAxMDGoMCwQIFLQV8hXvXP//9AcRCfAcb///h/ygPW+w/vb7olBjUHCTCyMcAFGZmABBO6AogThgZgIUsXAEDcEzcAAHjaY2BgECMCyoEgACZaAed42mNgYmRgYGBnYGNgYAZSDJqMgorCgoqCjECRXwwNrCAKSP5mAAFGBiRgyAAAi/YFBQAAeNqtkc1OwkAUhU/5M25cEhcsZick0AwlBJq6MWwgJkAgYV/KAA2lJeUn+hY+gktXvpKv4dLTMqKycGHsTZNv7px7z50ZAFd4hYHjdw1Ls4EiHjVncIFnzVnc4F1zDkWjrzmPW+NNcwGlzIRKI3fJlUyrEjZQxb3mDH2fNGfRx4vmHKqG0JzHg6E0F9DOlFBGBxUI1GEzLNT4S0aLuTtsGAEUuYcQHkyg3KmIum1bNUvKlrjbbAIleqHHnS4iSudpQcySMYtdFiXlAxzSbAwfMxK6kZoHKhbjjespMTioOPZnzI+4ucCeTVyKMVKLfeAS6vSWaTinuZwzyy/Dc7vaed+6KaV0kukdPUk6yOcctZPvvxxqksq2lEW8RvHjMEO2FCl/zy6p3NEm0R9OFSafJdldc4QVeyaaObMBO0/5cCaa6d9Ggyubxire+lEojscdjoWUR1xGOy8KD8mG2ZLO2l2paDc3A39qmU2z2W5YNv5+u79e6QfGJY/hAAB42m3NywrCMBQE0DupWp/1AYI7/6DEaLQu66Mrd35BKUWKJSlFv1+rue4cGM7shgR981qSon+ZNwUJ8iDgoYU2OvDRRQ99DDDECAHGmGCKmf80hZSx/Kik/LliFbtmN6xmt+yOjdg9GztV4tROnRwX/Bsaaw51nt4Lc7tWaZYHp/MlzKx51LZs5htNri+2AAAAAQAB//8AD3jaY2BkYGDgAWIxIGZiYARCESBmAfMYAAR6AEMAAAABAAAAANXtRbgAAAAA2AhRFAAAAADYCNuG) format("woff"); } -/* ---------------------------------------------- -Inter variable font. Usage: - -@supports (font-variation-settings: normal) { - html { font-family: "Inter var", sans-serif; } -} ----------------------------------------------- */ - -@font-face { - font-family: "Inter var"; - font-weight: 100 900; /* stylelint-disable-line font-weight-notation */ - font-style: normal; - src: url(../fonts/inter/Inter-upright-var.woff2) format("woff2"); -} - -@font-face { - font-family: "Inter var"; - font-weight: 100 900; /* stylelint-disable-line font-weight-notation */ - font-style: italic; - src: url(../fonts/inter/Inter-italic-var.woff2) format("woff2"); -} - /* Structure --------------------------------- */ .wp-block { diff --git a/src/wp-content/themes/twentytwenty/assets/css/editor-style-block.css b/src/wp-content/themes/twentytwenty/assets/css/editor-style-block.css index ffe0b803343de..eb498e149047b 100644 --- a/src/wp-content/themes/twentytwenty/assets/css/editor-style-block.css +++ b/src/wp-content/themes/twentytwenty/assets/css/editor-style-block.css @@ -44,28 +44,6 @@ src: url(data:application/font-woff2;charset=utf-8;base64,d09GMgABAAAAAAMoAA0AAAAACDQAAALTAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP0ZGVE0cGh4GYACCahEICjx3CywAATYCJANUBCAFhiEHgWwbXQfILgpsY+rQRRARwyAs6uL7pxzYhxEE+32b3aeHmifR6tklkS9hiZA0ewkqGRJE+H7/+6378ASViK/PGeavqJyOzsceKi1s3BCiQsiOdn1r/RBgIJYEgCUhbm/8/8/h4saPssnTNkkiWUBrTRtjmQSajw3Ui3pZ3LYDPD+XG2C3JA/yKAS8/rU5eNfuGqRf4eNNgV4YAlIIgxglEkWe6FYpq10+wi3g+/nUgvgPFczNrz/RsTgVm/zfbPuHZlsuQECxuyqBcQwKFBjFgKO8AqP4bAN9tFJtnM9xPcbNjeXS/x1wY/xU52f5W/X1+9cnH4YwKIaoRRAkUkj/YlAAeF/624foiIDBgBmgQBeGAyhBljUPZUm/l2dTvmpqcBDUOHdbPZWd8JsBAsGr4w8/EDn82/bUPx4eh0YNrQTBuHO2FjQEAGBwK0DeI37DpQVqdERS4gZBhpeUhWCfLFz7J99aEBgsJCHvUGAdAPp4IADDCAPCEFMGpMZ9AQpTfQtQGhLbGVBZFV8BaqNyP68oTZgHNj3M8kBPfXTTC9t90UuzYhy9ciH0grVlOcqyCytisvbsERsEYztiznR0WCrmTksJwbSNK6fd1Rvr25I9oLvctUoEbNOmXJbqgYgPXEHJ82IUsrCnpkxh23F1rfZ2zcRnJYoXtauB3VTFkFXQg3uoZYD5qE0kdjDtoDoF1h2bulGmev5HbYhbrjtohQSRI4aNOkffIcT+d3v6atpaYh3JvPoQsztCcqvaBkppDSPcQ3bw3KaCBo1f5CJWTZEgW3LjLofYg51MaVezrx8xZitYbQ9KYeoRaqQdVLwSEfrKXLK1otCWOKNdR/YwYAfon5Yk8O2MJfSD10dPGA5PIJJQMkah0ugMJiv6x4Dm7LEa8xnrRGGGLAg4sAlbsA07sAt76DOsXKO3hIjtIlpnnFrt1qW4kh6NhS83P/6HB/fl1SMAAA==) format("woff2"), url(data:application/font-woff;charset=utf-8;base64,d09GRgABAAAAAAUQAA0AAAAACDQAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAABGRlRNAAAE9AAAABwAAAAchf5yU0dERUYAAATYAAAAHAAAAB4AJwAbT1MvMgAAAaAAAABJAAAAYJAcgU5jbWFwAAACIAAAAF4AAAFqUUxBZ2dhc3AAAATQAAAACAAAAAgAAAAQZ2x5ZgAAApAAAAAyAAAAPL0n8y9oZWFkAAABMAAAADAAAAA2Fi93Z2hoZWEAAAFgAAAAHQAAACQOSgWaaG10eAAAAewAAAAzAAAAVC7TAQBsb2NhAAACgAAAABAAAAAsAOQBAm1heHAAAAGAAAAAHQAAACAAWQALbmFtZQAAAsQAAAF6AAADIYvD/Adwb3N0AAAEQAAAAI4AAADsapk2o3jaY2BkYGAA4ov5mwzj+W2+MnCzXwCKMNzgCBSB0LfbQDQ7AxuI4mBgAlEAFKQIRHjaY2BkYGD3+NvCwMDBAALsDAyMDKhAFAA3+wH3AAAAeNpjYGRgYBBl4GBgYgABEMnIABJzAPMZAAVmAGUAAAB42mNgZlJhnMDAysDCKsKygYGBYRqEZtrDYMT4D8gHSmEHjgUFOQwODAqqf9g9/rYwMLB7MNUAhRlBcsxBrMlASoGBEQAj8QtyAAAAeNrjYGBkAAGmWQwMjO8gmBnIZ2NA0ExAzNjAAFYJVn0ASBsD6VAIDZb7AtELAgANIgb9AHjaY2BgYGaAYBkGRgYQSAHyGMF8FgYPIM3HwMHAxMDGoMCwQIFLQV8hXvXP//9AcRCfAcb///h/ygPW+w/vb7olBjUHCTCyMcAFGZmABBO6AogThgZgIUsXAEDcEzcAAHjaY2BgECMCyoEgACZaAed42mNgYmRgYGBnYGNgYAZSDJqMgorCgoqCjECRXwwNrCAKSP5mAAFGBiRgyAAAi/YFBQAAeNqtkc1OwkAUhU/5M25cEhcsZick0AwlBJq6MWwgJkAgYV/KAA2lJeUn+hY+gktXvpKv4dLTMqKycGHsTZNv7px7z50ZAFd4hYHjdw1Ls4EiHjVncIFnzVnc4F1zDkWjrzmPW+NNcwGlzIRKI3fJlUyrEjZQxb3mDH2fNGfRx4vmHKqG0JzHg6E0F9DOlFBGBxUI1GEzLNT4S0aLuTtsGAEUuYcQHkyg3KmIum1bNUvKlrjbbAIleqHHnS4iSudpQcySMYtdFiXlAxzSbAwfMxK6kZoHKhbjjespMTioOPZnzI+4ucCeTVyKMVKLfeAS6vSWaTinuZwzyy/Dc7vaed+6KaV0kukdPUk6yOcctZPvvxxqksq2lEW8RvHjMEO2FCl/zy6p3NEm0R9OFSafJdldc4QVeyaaObMBO0/5cCaa6d9Ggyubxire+lEojscdjoWUR1xGOy8KD8mG2ZLO2l2paDc3A39qmU2z2W5YNv5+u79e6QfGJY/hAAB42m3NywrCMBQE0DupWp/1AYI7/6DEaLQu66Mrd35BKUWKJSlFv1+rue4cGM7shgR981qSon+ZNwUJ8iDgoYU2OvDRRQ99DDDECAHGmGCKmf80hZSx/Kik/LliFbtmN6xmt+yOjdg9GztV4tROnRwX/Bsaaw51nt4Lc7tWaZYHp/MlzKx51LZs5htNri+2AAAAAQAB//8AD3jaY2BkYGDgAWIxIGZiYARCESBmAfMYAAR6AEMAAAABAAAAANXtRbgAAAAA2AhRFAAAAADYCNuG) format("woff"); } -/* ---------------------------------------------- -Inter variable font. Usage: - -@supports (font-variation-settings: normal) { - html { font-family: "Inter var", sans-serif; } -} ----------------------------------------------- */ - -@font-face { - font-family: "Inter var"; - font-weight: 100 900; /* stylelint-disable-line font-weight-notation */ - font-style: normal; - src: url(../fonts/inter/Inter-upright-var.woff2) format("woff2"); -} - -@font-face { - font-family: "Inter var"; - font-weight: 100 900; /* stylelint-disable-line font-weight-notation */ - font-style: italic; - src: url(../fonts/inter/Inter-italic-var.woff2) format("woff2"); -} - /* Structure --------------------------------- */ .wp-block { diff --git a/src/wp-content/themes/twentytwenty/assets/css/editor-style-classic-rtl.css b/src/wp-content/themes/twentytwenty/assets/css/editor-style-classic-rtl.css index de2d9f87ddb76..a87a965c099d6 100644 --- a/src/wp-content/themes/twentytwenty/assets/css/editor-style-classic-rtl.css +++ b/src/wp-content/themes/twentytwenty/assets/css/editor-style-classic-rtl.css @@ -18,28 +18,6 @@ src: url(data:application/font-woff2;charset=utf-8;base64,d09GMgABAAAAAAMoAA0AAAAACDQAAALTAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP0ZGVE0cGh4GYACCahEICjx3CywAATYCJANUBCAFhiEHgWwbXQfILgpsY+rQRRARwyAs6uL7pxzYhxEE+32b3aeHmifR6tklkS9hiZA0ewkqGRJE+H7/+6378ASViK/PGeavqJyOzsceKi1s3BCiQsiOdn1r/RBgIJYEgCUhbm/8/8/h4saPssnTNkkiWUBrTRtjmQSajw3Ui3pZ3LYDPD+XG2C3JA/yKAS8/rU5eNfuGqRf4eNNgV4YAlIIgxglEkWe6FYpq10+wi3g+/nUgvgPFczNrz/RsTgVm/zfbPuHZlsuQECxuyqBcQwKFBjFgKO8AqP4bAN9tFJtnM9xPcbNjeXS/x1wY/xU52f5W/X1+9cnH4YwKIaoRRAkUkj/YlAAeF/624foiIDBgBmgQBeGAyhBljUPZUm/l2dTvmpqcBDUOHdbPZWd8JsBAsGr4w8/EDn82/bUPx4eh0YNrQTBuHO2FjQEAGBwK0DeI37DpQVqdERS4gZBhpeUhWCfLFz7J99aEBgsJCHvUGAdAPp4IADDCAPCEFMGpMZ9AQpTfQtQGhLbGVBZFV8BaqNyP68oTZgHNj3M8kBPfXTTC9t90UuzYhy9ciH0grVlOcqyCytisvbsERsEYztiznR0WCrmTksJwbSNK6fd1Rvr25I9oLvctUoEbNOmXJbqgYgPXEHJ82IUsrCnpkxh23F1rfZ2zcRnJYoXtauB3VTFkFXQg3uoZYD5qE0kdjDtoDoF1h2bulGmev5HbYhbrjtohQSRI4aNOkffIcT+d3v6atpaYh3JvPoQsztCcqvaBkppDSPcQ3bw3KaCBo1f5CJWTZEgW3LjLofYg51MaVezrx8xZitYbQ9KYeoRaqQdVLwSEfrKXLK1otCWOKNdR/YwYAfon5Yk8O2MJfSD10dPGA5PIJJQMkah0ugMJiv6x4Dm7LEa8xnrRGGGLAg4sAlbsA07sAt76DOsXKO3hIjtIlpnnFrt1qW4kh6NhS83P/6HB/fl1SMAAA==) format("woff2"), url(data:application/font-woff;charset=utf-8;base64,d09GRgABAAAAAAUQAA0AAAAACDQAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAABGRlRNAAAE9AAAABwAAAAchf5yU0dERUYAAATYAAAAHAAAAB4AJwAbT1MvMgAAAaAAAABJAAAAYJAcgU5jbWFwAAACIAAAAF4AAAFqUUxBZ2dhc3AAAATQAAAACAAAAAgAAAAQZ2x5ZgAAApAAAAAyAAAAPL0n8y9oZWFkAAABMAAAADAAAAA2Fi93Z2hoZWEAAAFgAAAAHQAAACQOSgWaaG10eAAAAewAAAAzAAAAVC7TAQBsb2NhAAACgAAAABAAAAAsAOQBAm1heHAAAAGAAAAAHQAAACAAWQALbmFtZQAAAsQAAAF6AAADIYvD/Adwb3N0AAAEQAAAAI4AAADsapk2o3jaY2BkYGAA4ov5mwzj+W2+MnCzXwCKMNzgCBSB0LfbQDQ7AxuI4mBgAlEAFKQIRHjaY2BkYGD3+NvCwMDBAALsDAyMDKhAFAA3+wH3AAAAeNpjYGRgYBBl4GBgYgABEMnIABJzAPMZAAVmAGUAAAB42mNgZlJhnMDAysDCKsKygYGBYRqEZtrDYMT4D8gHSmEHjgUFOQwODAqqf9g9/rYwMLB7MNUAhRlBcsxBrMlASoGBEQAj8QtyAAAAeNrjYGBkAAGmWQwMjO8gmBnIZ2NA0ExAzNjAAFYJVn0ASBsD6VAIDZb7AtELAgANIgb9AHjaY2BgYGaAYBkGRgYQSAHyGMF8FgYPIM3HwMHAxMDGoMCwQIFLQV8hXvXP//9AcRCfAcb///h/ygPW+w/vb7olBjUHCTCyMcAFGZmABBO6AogThgZgIUsXAEDcEzcAAHjaY2BgECMCyoEgACZaAed42mNgYmRgYGBnYGNgYAZSDJqMgorCgoqCjECRXwwNrCAKSP5mAAFGBiRgyAAAi/YFBQAAeNqtkc1OwkAUhU/5M25cEhcsZick0AwlBJq6MWwgJkAgYV/KAA2lJeUn+hY+gktXvpKv4dLTMqKycGHsTZNv7px7z50ZAFd4hYHjdw1Ls4EiHjVncIFnzVnc4F1zDkWjrzmPW+NNcwGlzIRKI3fJlUyrEjZQxb3mDH2fNGfRx4vmHKqG0JzHg6E0F9DOlFBGBxUI1GEzLNT4S0aLuTtsGAEUuYcQHkyg3KmIum1bNUvKlrjbbAIleqHHnS4iSudpQcySMYtdFiXlAxzSbAwfMxK6kZoHKhbjjespMTioOPZnzI+4ucCeTVyKMVKLfeAS6vSWaTinuZwzyy/Dc7vaed+6KaV0kukdPUk6yOcctZPvvxxqksq2lEW8RvHjMEO2FCl/zy6p3NEm0R9OFSafJdldc4QVeyaaObMBO0/5cCaa6d9Ggyubxire+lEojscdjoWUR1xGOy8KD8mG2ZLO2l2paDc3A39qmU2z2W5YNv5+u79e6QfGJY/hAAB42m3NywrCMBQE0DupWp/1AYI7/6DEaLQu66Mrd35BKUWKJSlFv1+rue4cGM7shgR981qSon+ZNwUJ8iDgoYU2OvDRRQ99DDDECAHGmGCKmf80hZSx/Kik/LliFbtmN6xmt+yOjdg9GztV4tROnRwX/Bsaaw51nt4Lc7tWaZYHp/MlzKx51LZs5htNri+2AAAAAQAB//8AD3jaY2BkYGDgAWIxIGZiYARCESBmAfMYAAR6AEMAAAABAAAAANXtRbgAAAAA2AhRFAAAAADYCNuG) format("woff"); } -/* ---------------------------------------------- -Inter variable font. Usage: - -@supports (font-variation-settings: normal) { - html { font-family: "Inter var", sans-serif; } -} ----------------------------------------------- */ - -@font-face { - font-family: "Inter var"; - font-weight: 100 900; /* stylelint-disable-line font-weight-notation */ - font-style: normal; - src: url(../fonts/inter/Inter-upright-var.woff2) format("woff2"); -} - -@font-face { - font-family: "Inter var"; - font-weight: 100 900; /* stylelint-disable-line font-weight-notation */ - font-style: italic; - src: url(../fonts/inter/Inter-italic-var.woff2) format("woff2"); -} - /* Structure --------------------------------- */ body#tinymce.wp-editor.content { /* stylelint-disable-line no-duplicate-selectors */ diff --git a/src/wp-content/themes/twentytwenty/assets/css/editor-style-classic.css b/src/wp-content/themes/twentytwenty/assets/css/editor-style-classic.css index d471b3723382c..28d487d659b67 100644 --- a/src/wp-content/themes/twentytwenty/assets/css/editor-style-classic.css +++ b/src/wp-content/themes/twentytwenty/assets/css/editor-style-classic.css @@ -18,28 +18,6 @@ src: url(data:application/font-woff2;charset=utf-8;base64,d09GMgABAAAAAAMoAA0AAAAACDQAAALTAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP0ZGVE0cGh4GYACCahEICjx3CywAATYCJANUBCAFhiEHgWwbXQfILgpsY+rQRRARwyAs6uL7pxzYhxEE+32b3aeHmifR6tklkS9hiZA0ewkqGRJE+H7/+6378ASViK/PGeavqJyOzsceKi1s3BCiQsiOdn1r/RBgIJYEgCUhbm/8/8/h4saPssnTNkkiWUBrTRtjmQSajw3Ui3pZ3LYDPD+XG2C3JA/yKAS8/rU5eNfuGqRf4eNNgV4YAlIIgxglEkWe6FYpq10+wi3g+/nUgvgPFczNrz/RsTgVm/zfbPuHZlsuQECxuyqBcQwKFBjFgKO8AqP4bAN9tFJtnM9xPcbNjeXS/x1wY/xU52f5W/X1+9cnH4YwKIaoRRAkUkj/YlAAeF/624foiIDBgBmgQBeGAyhBljUPZUm/l2dTvmpqcBDUOHdbPZWd8JsBAsGr4w8/EDn82/bUPx4eh0YNrQTBuHO2FjQEAGBwK0DeI37DpQVqdERS4gZBhpeUhWCfLFz7J99aEBgsJCHvUGAdAPp4IADDCAPCEFMGpMZ9AQpTfQtQGhLbGVBZFV8BaqNyP68oTZgHNj3M8kBPfXTTC9t90UuzYhy9ciH0grVlOcqyCytisvbsERsEYztiznR0WCrmTksJwbSNK6fd1Rvr25I9oLvctUoEbNOmXJbqgYgPXEHJ82IUsrCnpkxh23F1rfZ2zcRnJYoXtauB3VTFkFXQg3uoZYD5qE0kdjDtoDoF1h2bulGmev5HbYhbrjtohQSRI4aNOkffIcT+d3v6atpaYh3JvPoQsztCcqvaBkppDSPcQ3bw3KaCBo1f5CJWTZEgW3LjLofYg51MaVezrx8xZitYbQ9KYeoRaqQdVLwSEfrKXLK1otCWOKNdR/YwYAfon5Yk8O2MJfSD10dPGA5PIJJQMkah0ugMJiv6x4Dm7LEa8xnrRGGGLAg4sAlbsA07sAt76DOsXKO3hIjtIlpnnFrt1qW4kh6NhS83P/6HB/fl1SMAAA==) format("woff2"), url(data:application/font-woff;charset=utf-8;base64,d09GRgABAAAAAAUQAA0AAAAACDQAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAABGRlRNAAAE9AAAABwAAAAchf5yU0dERUYAAATYAAAAHAAAAB4AJwAbT1MvMgAAAaAAAABJAAAAYJAcgU5jbWFwAAACIAAAAF4AAAFqUUxBZ2dhc3AAAATQAAAACAAAAAgAAAAQZ2x5ZgAAApAAAAAyAAAAPL0n8y9oZWFkAAABMAAAADAAAAA2Fi93Z2hoZWEAAAFgAAAAHQAAACQOSgWaaG10eAAAAewAAAAzAAAAVC7TAQBsb2NhAAACgAAAABAAAAAsAOQBAm1heHAAAAGAAAAAHQAAACAAWQALbmFtZQAAAsQAAAF6AAADIYvD/Adwb3N0AAAEQAAAAI4AAADsapk2o3jaY2BkYGAA4ov5mwzj+W2+MnCzXwCKMNzgCBSB0LfbQDQ7AxuI4mBgAlEAFKQIRHjaY2BkYGD3+NvCwMDBAALsDAyMDKhAFAA3+wH3AAAAeNpjYGRgYBBl4GBgYgABEMnIABJzAPMZAAVmAGUAAAB42mNgZlJhnMDAysDCKsKygYGBYRqEZtrDYMT4D8gHSmEHjgUFOQwODAqqf9g9/rYwMLB7MNUAhRlBcsxBrMlASoGBEQAj8QtyAAAAeNrjYGBkAAGmWQwMjO8gmBnIZ2NA0ExAzNjAAFYJVn0ASBsD6VAIDZb7AtELAgANIgb9AHjaY2BgYGaAYBkGRgYQSAHyGMF8FgYPIM3HwMHAxMDGoMCwQIFLQV8hXvXP//9AcRCfAcb///h/ygPW+w/vb7olBjUHCTCyMcAFGZmABBO6AogThgZgIUsXAEDcEzcAAHjaY2BgECMCyoEgACZaAed42mNgYmRgYGBnYGNgYAZSDJqMgorCgoqCjECRXwwNrCAKSP5mAAFGBiRgyAAAi/YFBQAAeNqtkc1OwkAUhU/5M25cEhcsZick0AwlBJq6MWwgJkAgYV/KAA2lJeUn+hY+gktXvpKv4dLTMqKycGHsTZNv7px7z50ZAFd4hYHjdw1Ls4EiHjVncIFnzVnc4F1zDkWjrzmPW+NNcwGlzIRKI3fJlUyrEjZQxb3mDH2fNGfRx4vmHKqG0JzHg6E0F9DOlFBGBxUI1GEzLNT4S0aLuTtsGAEUuYcQHkyg3KmIum1bNUvKlrjbbAIleqHHnS4iSudpQcySMYtdFiXlAxzSbAwfMxK6kZoHKhbjjespMTioOPZnzI+4ucCeTVyKMVKLfeAS6vSWaTinuZwzyy/Dc7vaed+6KaV0kukdPUk6yOcctZPvvxxqksq2lEW8RvHjMEO2FCl/zy6p3NEm0R9OFSafJdldc4QVeyaaObMBO0/5cCaa6d9Ggyubxire+lEojscdjoWUR1xGOy8KD8mG2ZLO2l2paDc3A39qmU2z2W5YNv5+u79e6QfGJY/hAAB42m3NywrCMBQE0DupWp/1AYI7/6DEaLQu66Mrd35BKUWKJSlFv1+rue4cGM7shgR981qSon+ZNwUJ8iDgoYU2OvDRRQ99DDDECAHGmGCKmf80hZSx/Kik/LliFbtmN6xmt+yOjdg9GztV4tROnRwX/Bsaaw51nt4Lc7tWaZYHp/MlzKx51LZs5htNri+2AAAAAQAB//8AD3jaY2BkYGDgAWIxIGZiYARCESBmAfMYAAR6AEMAAAABAAAAANXtRbgAAAAA2AhRFAAAAADYCNuG) format("woff"); } -/* ---------------------------------------------- -Inter variable font. Usage: - -@supports (font-variation-settings: normal) { - html { font-family: "Inter var", sans-serif; } -} ----------------------------------------------- */ - -@font-face { - font-family: "Inter var"; - font-weight: 100 900; /* stylelint-disable-line font-weight-notation */ - font-style: normal; - src: url(../fonts/inter/Inter-upright-var.woff2) format("woff2"); -} - -@font-face { - font-family: "Inter var"; - font-weight: 100 900; /* stylelint-disable-line font-weight-notation */ - font-style: italic; - src: url(../fonts/inter/Inter-italic-var.woff2) format("woff2"); -} - /* Structure --------------------------------- */ body#tinymce.wp-editor.content { /* stylelint-disable-line no-duplicate-selectors */ diff --git a/src/wp-content/themes/twentytwenty/assets/css/font-inter.css b/src/wp-content/themes/twentytwenty/assets/css/font-inter.css new file mode 100644 index 0000000000000..658db43fc0fab --- /dev/null +++ b/src/wp-content/themes/twentytwenty/assets/css/font-inter.css @@ -0,0 +1,23 @@ +/* ---------------------------------------------- +Inter variable font. Usage: + +@supports (font-variation-settings: normal) { + html { font-family: 'Inter var', sans-serif; } +} +---------------------------------------------- */ + +@font-face { + font-family: "Inter var"; + font-weight: 100 900; /* stylelint-disable-line font-weight-notation */ + font-style: normal; + font-display: swap; + src: url(../fonts/inter/Inter-upright-var.woff2) format("woff2"); +} + +@font-face { + font-family: "Inter var"; + font-weight: 100 900; /* stylelint-disable-line font-weight-notation */ + font-style: italic; + font-display: swap; + src: url(../fonts/inter/Inter-italic-var.woff2) format("woff2"); +} diff --git a/src/wp-content/themes/twentytwenty/footer.php b/src/wp-content/themes/twentytwenty/footer.php index fb3bc2f2c373a..860266e52a367 100644 --- a/src/wp-content/themes/twentytwenty/footer.php +++ b/src/wp-content/themes/twentytwenty/footer.php @@ -20,10 +20,13 @@

diff --git a/src/wp-content/themes/twentytwenty/functions.php b/src/wp-content/themes/twentytwenty/functions.php index 3f1a556d1dcaa..2ce8fffb77bac 100644 --- a/src/wp-content/themes/twentytwenty/functions.php +++ b/src/wp-content/themes/twentytwenty/functions.php @@ -182,6 +182,7 @@ function twentytwenty_theme_support() { * Register and Enqueue Styles. * * @since Twenty Twenty 1.0 + * @since Twenty Twenty 2.6 Enqueue the CSS file for the variable font. */ function twentytwenty_register_styles() { @@ -190,6 +191,9 @@ function twentytwenty_register_styles() { wp_enqueue_style( 'twentytwenty-style', get_stylesheet_uri(), array(), $theme_version ); wp_style_add_data( 'twentytwenty-style', 'rtl', 'replace' ); + // Enqueue the CSS file for the variable font, Inter. + wp_enqueue_style( 'twentytwenty-fonts', get_theme_file_uri( '/assets/css/font-inter.css' ), array(), wp_get_theme()->get( 'Version' ), 'all' ); + // Add output of Customizer settings as inline style. $customizer_css = twentytwenty_get_customizer_css( 'front-end' ); if ( $customizer_css ) { @@ -417,6 +421,7 @@ function twentytwenty_sidebar_registration() { * * @since Twenty Twenty 1.0 * @since Twenty Twenty 2.4 Removed a script related to the obsolete Squared style of Button blocks. + * @since Twenty Twenty 2.6 Enqueue the CSS file for the variable font. */ function twentytwenty_block_editor_styles() { @@ -430,6 +435,9 @@ function twentytwenty_block_editor_styles() { wp_add_inline_style( 'twentytwenty-block-editor-styles', $customizer_css ); } + // Enqueue the CSS file for the variable font, Inter. + wp_enqueue_style( 'twentytwenty-fonts', get_theme_file_uri( '/assets/css/font-inter.css' ), array(), wp_get_theme()->get( 'Version' ), 'all' ); + // Add inline style for non-latin fonts. $custom_css = TwentyTwenty_Non_Latin_Languages::get_non_latin_css( 'block-editor' ); if ( $custom_css ) { @@ -447,11 +455,13 @@ function twentytwenty_block_editor_styles() { * Enqueue classic editor styles. * * @since Twenty Twenty 1.0 + * @since Twenty Twenty 2.6 Enqueue the CSS file for the variable font. */ function twentytwenty_classic_editor_styles() { $classic_editor_styles = array( '/assets/css/editor-style-classic.css', + '/assets/css/font-inter.css', ); add_editor_style( $classic_editor_styles ); diff --git a/src/wp-content/themes/twentytwenty/package-lock.json b/src/wp-content/themes/twentytwenty/package-lock.json index 683d260eff121..d748ea60ecf1a 100644 --- a/src/wp-content/themes/twentytwenty/package-lock.json +++ b/src/wp-content/themes/twentytwenty/package-lock.json @@ -1,12 +1,12 @@ { "name": "twentytwenty", - "version": "2.4.0", + "version": "2.5.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "twentytwenty", - "version": "2.4.0", + "version": "2.5.0", "license": "GPL-2.0-or-later", "devDependencies": { "@wordpress/browserslist-config": "^5.31.0", diff --git a/src/wp-content/themes/twentytwenty/package.json b/src/wp-content/themes/twentytwenty/package.json index 94237a969a673..92c516d93503b 100644 --- a/src/wp-content/themes/twentytwenty/package.json +++ b/src/wp-content/themes/twentytwenty/package.json @@ -1,6 +1,6 @@ { "name": "twentytwenty", - "version": "2.4.0", + "version": "2.5.0", "description": "Default WP Theme", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/src/wp-content/themes/twentytwenty/readme.txt b/src/wp-content/themes/twentytwenty/readme.txt index facf99dddbf4a..db934203fc4a0 100644 --- a/src/wp-content/themes/twentytwenty/readme.txt +++ b/src/wp-content/themes/twentytwenty/readme.txt @@ -3,7 +3,7 @@ Contributors: the WordPress team Requires at least: 4.7 Tested up to: 6.4 Requires PHP: 5.2.4 -Stable tag: 2.4 +Stable tag: 2.5 License: GPLv2 or later License URI: http://www.gnu.org/licenses/gpl-2.0.html @@ -24,6 +24,11 @@ you pick, ensuring a high, accessible color contrast for your visitors. == Changelog == += 2.5 = +* Released: January 16, 2024 + +https://wordpress.org/documentation/article/twenty-twenty-changelog/#Version_2.5 + = 2.4 = * Released: November 7, 2023 diff --git a/src/wp-content/themes/twentytwenty/style-rtl.css b/src/wp-content/themes/twentytwenty/style-rtl.css index dd4b929007e5a..b7ae83944c4eb 100644 --- a/src/wp-content/themes/twentytwenty/style-rtl.css +++ b/src/wp-content/themes/twentytwenty/style-rtl.css @@ -1,7 +1,7 @@ /* Theme Name: Twenty Twenty Text Domain: twentytwenty -Version: 2.4 +Version: 2.5 Tested up to: 6.4 Requires at least: 4.7 Requires PHP: 5.2.4 diff --git a/src/wp-content/themes/twentytwenty/style.css b/src/wp-content/themes/twentytwenty/style.css index 99c3b293d7692..0f874531395fa 100644 --- a/src/wp-content/themes/twentytwenty/style.css +++ b/src/wp-content/themes/twentytwenty/style.css @@ -1,7 +1,7 @@ /* Theme Name: Twenty Twenty Text Domain: twentytwenty -Version: 2.4 +Version: 2.5 Tested up to: 6.4 Requires at least: 4.7 Requires PHP: 5.2.4 diff --git a/src/wp-content/themes/twentytwentyfour/patterns/hidden-portfolio-hero.php b/src/wp-content/themes/twentytwentyfour/patterns/hidden-portfolio-hero.php index b0922d2ec27f4..1af3e7dbd17c1 100644 --- a/src/wp-content/themes/twentytwentyfour/patterns/hidden-portfolio-hero.php +++ b/src/wp-content/themes/twentytwentyfour/patterns/hidden-portfolio-hero.php @@ -13,7 +13,7 @@
-

Leia Acosta, a passionate photographer who finds inspiration in capturing the fleeting beauty of life.', 'twentytwentyfour ' ) ); ?>

+

Leia Acosta, a passionate photographer who finds inspiration in capturing the fleeting beauty of life.', 'twentytwentyfour' ) ); ?>

diff --git a/src/wp-content/themes/twentytwentyone/assets/css/ie.css b/src/wp-content/themes/twentytwentyone/assets/css/ie.css index 8b178c9af745f..801540b2abe27 100644 --- a/src/wp-content/themes/twentytwentyone/assets/css/ie.css +++ b/src/wp-content/themes/twentytwentyone/assets/css/ie.css @@ -9,7 +9,7 @@ Description: Twenty Twenty-One is a blank canvas for your ideas and it makes the Requires at least: 5.3 Tested up to: 6.4 Requires PHP: 5.6 -Version: 2.0 +Version: 2.1 License: GNU General Public License v2 or later License URI: http://www.gnu.org/licenses/gpl-2.0.html Text Domain: twentytwentyone diff --git a/src/wp-content/themes/twentytwentyone/assets/sass/01-settings/file-header.scss b/src/wp-content/themes/twentytwentyone/assets/sass/01-settings/file-header.scss index e9346282c39e0..c6bd00e26a191 100644 --- a/src/wp-content/themes/twentytwentyone/assets/sass/01-settings/file-header.scss +++ b/src/wp-content/themes/twentytwentyone/assets/sass/01-settings/file-header.scss @@ -7,7 +7,7 @@ Description: Twenty Twenty-One is a blank canvas for your ideas and it makes the Requires at least: 5.3 Tested up to: 6.4 Requires PHP: 5.6 -Version: 2.0 +Version: 2.1 License: GNU General Public License v2 or later License URI: http://www.gnu.org/licenses/gpl-2.0.html Text Domain: twentytwentyone diff --git a/src/wp-content/themes/twentytwentyone/package-lock.json b/src/wp-content/themes/twentytwentyone/package-lock.json index 4491a8f893c77..a3243bfbff4d9 100644 --- a/src/wp-content/themes/twentytwentyone/package-lock.json +++ b/src/wp-content/themes/twentytwentyone/package-lock.json @@ -1,12 +1,12 @@ { "name": "twentytwentyone", - "version": "2.0.0", + "version": "2.1.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "twentytwentyone", - "version": "2.0.0", + "version": "2.1.0", "license": "GPL-2.0-or-later", "devDependencies": { "@wordpress/browserslist-config": "^5.30.0", diff --git a/src/wp-content/themes/twentytwentyone/package.json b/src/wp-content/themes/twentytwentyone/package.json index c182dc5b4c9b9..c39e8942072ae 100644 --- a/src/wp-content/themes/twentytwentyone/package.json +++ b/src/wp-content/themes/twentytwentyone/package.json @@ -1,6 +1,6 @@ { "name": "twentytwentyone", - "version": "2.0.0", + "version": "2.1.0", "description": "Default WP Theme", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", diff --git a/src/wp-content/themes/twentytwentyone/readme.txt b/src/wp-content/themes/twentytwentyone/readme.txt index b622f46f33590..a8063407569ae 100644 --- a/src/wp-content/themes/twentytwentyone/readme.txt +++ b/src/wp-content/themes/twentytwentyone/readme.txt @@ -3,7 +3,7 @@ Contributors: wordpressdotorg Requires at least: 5.3 Tested up to: 6.4 Requires PHP: 5.6 -Stable tag: 2.0 +Stable tag: 2.1 License: GPLv2 or later License URI: http://www.gnu.org/licenses/gpl-2.0.html @@ -30,6 +30,11 @@ No data is saved in the database or transferred. == Changelog == += 2.1 = +* Released: January 16, 2024 + +https://wordpress.org/documentation/article/twenty-twenty-one-changelog/#Version_2.1 + = 2.0 = * Released: November 7, 2023 diff --git a/src/wp-content/themes/twentytwentyone/style-rtl.css b/src/wp-content/themes/twentytwentyone/style-rtl.css index c03602b1380e8..142bd60ad8cd4 100644 --- a/src/wp-content/themes/twentytwentyone/style-rtl.css +++ b/src/wp-content/themes/twentytwentyone/style-rtl.css @@ -9,7 +9,7 @@ Description: Twenty Twenty-One is a blank canvas for your ideas and it makes the Requires at least: 5.3 Tested up to: 6.4 Requires PHP: 5.6 -Version: 2.0 +Version: 2.1 License: GNU General Public License v2 or later License URI: http://www.gnu.org/licenses/gpl-2.0.html Text Domain: twentytwentyone diff --git a/src/wp-content/themes/twentytwentyone/style.css b/src/wp-content/themes/twentytwentyone/style.css index ccd8cf702d1f1..e3a3af852140f 100644 --- a/src/wp-content/themes/twentytwentyone/style.css +++ b/src/wp-content/themes/twentytwentyone/style.css @@ -9,7 +9,7 @@ Description: Twenty Twenty-One is a blank canvas for your ideas and it makes the Requires at least: 5.3 Tested up to: 6.4 Requires PHP: 5.6 -Version: 2.0 +Version: 2.1 License: GNU General Public License v2 or later License URI: http://www.gnu.org/licenses/gpl-2.0.html Text Domain: twentytwentyone diff --git a/src/wp-includes/Text/Diff.php b/src/wp-includes/Text/Diff.php index 40dba1a4a37cf..eee4e4f8ea531 100644 --- a/src/wp-includes/Text/Diff.php +++ b/src/wp-includes/Text/Diff.php @@ -296,7 +296,7 @@ class Text_MappedDiff extends Text_Diff { /** * Computes a diff between sequences of strings. * - * This can be used to compute things like case-insensitve diffs, or diffs + * This can be used to compute things like case-insensitive diffs, or diffs * which ignore changes in white-space. * * @param array $from_lines An array of strings. diff --git a/src/wp-includes/class-wp-block-type.php b/src/wp-includes/class-wp-block-type.php index 9caa366032a75..6ad8ebcd6790e 100644 --- a/src/wp-includes/class-wp-block-type.php +++ b/src/wp-includes/class-wp-block-type.php @@ -113,9 +113,18 @@ class WP_Block_Type { * Block variations. * * @since 5.8.0 - * @var array[] + * @since 6.5.0 Only accessible through magic getter. null by default. + * @var array[]|null */ - public $variations = array(); + private $variations = null; + + /** + * Block variations callback. + * + * @since 6.5.0 + * @var callable|null + */ + public $variation_callback = null; /** * Custom CSS selectors for theme.json style generation. @@ -296,6 +305,7 @@ class WP_Block_Type { * @type array|null $supports Supported features. * @type array|null $example Structured data for the block preview. * @type callable|null $render_callback Block type render callback. + * @type callable|null $variation_callback Block type variations callback. * @type array|null $attributes Block type attributes property schemas. * @type string[] $uses_context Context values inherited by blocks of this type. * @type string[]|null $provides_context Context provided by blocks of this type. @@ -325,6 +335,10 @@ public function __construct( $block_type, $args = array() ) { * null when value not found, or void when unknown property name provided. */ public function __get( $name ) { + if ( 'variations' === $name ) { + return $this->get_variations(); + } + if ( ! in_array( $name, $this->deprecated_properties, true ) ) { return; } @@ -353,6 +367,10 @@ public function __get( $name ) { * or false otherwise. */ public function __isset( $name ) { + if ( 'variations' === $name ) { + return true; + } + if ( ! in_array( $name, $this->deprecated_properties, true ) ) { return false; } @@ -372,6 +390,11 @@ public function __isset( $name ) { * @param mixed $value Property value. */ public function __set( $name, $value ) { + if ( 'variations' === $name ) { + $this->variations = $value; + return; + } + if ( ! in_array( $name, $this->deprecated_properties, true ) ) { $this->{$name} = $value; return; @@ -540,4 +563,30 @@ public function get_attributes() { $this->attributes : array(); } + + /** + * Get block variations. + * + * @since 6.5.0 + * + * @return array[] + */ + public function get_variations() { + if ( ! isset( $this->variations ) ) { + $this->variations = array(); + if ( is_callable( $this->variation_callback ) ) { + $this->variations = call_user_func( $this->variation_callback ); + } + } + + /** + * Filters the registered variations for a block type. + * + * @since 6.5.0 + * + * @param array $variations Array of registered variations for a block type. + * @param WP_Block_Type $block_type The full block type object. + */ + return apply_filters( 'get_block_type_variations', $this->variations, $this ); + } } diff --git a/src/wp-includes/class-wp-customize-widgets.php b/src/wp-includes/class-wp-customize-widgets.php index c8a00c741ff8a..8c822ea28f1f5 100644 --- a/src/wp-includes/class-wp-customize-widgets.php +++ b/src/wp-includes/class-wp-customize-widgets.php @@ -823,7 +823,7 @@ public function enqueue_scripts() { ); foreach ( $settings['registeredWidgets'] as &$registered_widget ) { - unset( $registered_widget['callback'] ); // May not be JSON-serializeable. + unset( $registered_widget['callback'] ); // May not be JSON-serializable. } $wp_scripts->add_data( @@ -1308,7 +1308,7 @@ public function export_preview_data() { ); foreach ( $settings['registeredWidgets'] as &$registered_widget ) { - unset( $registered_widget['callback'] ); // May not be JSON-serializeable. + unset( $registered_widget['callback'] ); // May not be JSON-serializable. } wp_print_inline_script_tag( sprintf( 'var _wpWidgetCustomizerPreviewSettings = %s;', wp_json_encode( $settings ) ) diff --git a/src/wp-includes/class-wp-query.php b/src/wp-includes/class-wp-query.php index e6a3e6b743450..f683a52bd1080 100644 --- a/src/wp-includes/class-wp-query.php +++ b/src/wp-includes/class-wp-query.php @@ -1027,7 +1027,7 @@ public function parse_query( $query = '' ) { } if ( ! ( $this->is_singular || $this->is_archive || $this->is_search || $this->is_feed - || ( defined( 'REST_REQUEST' ) && REST_REQUEST && $this->is_main_query() ) + || ( wp_is_serving_rest_request() && $this->is_main_query() ) || $this->is_trackback || $this->is_404 || $this->is_admin || $this->is_robots || $this->is_favicon ) ) { $this->is_home = true; } diff --git a/src/wp-includes/class-wp-textdomain-registry.php b/src/wp-includes/class-wp-textdomain-registry.php index 4121ecb0911c9..54f5f15c4d069 100644 --- a/src/wp-includes/class-wp-textdomain-registry.php +++ b/src/wp-includes/class-wp-textdomain-registry.php @@ -228,7 +228,11 @@ public function get_language_files_from_path( $path ) { * @return void */ public function invalidate_mo_files_cache( $upgrader, $hook_extra ) { - if ( 'translation' !== $hook_extra['type'] || array() === $hook_extra['translations'] ) { + if ( + ! isset( $hook_extra['type'] ) || + 'translation' !== $hook_extra['type'] || + array() === $hook_extra['translations'] + ) { return; } @@ -237,13 +241,13 @@ public function invalidate_mo_files_cache( $upgrader, $hook_extra ) { foreach ( $translation_types as $type ) { switch ( $type ) { case 'plugin': - wp_cache_delete( 'cached_mo_files_' . md5( WP_LANG_DIR . '/plugins' ), 'translations' ); + wp_cache_delete( 'cached_mo_files_' . md5( WP_LANG_DIR . '/plugins/' ), 'translations' ); break; case 'theme': - wp_cache_delete( 'cached_mo_files_' . md5( WP_LANG_DIR . '/themes' ), 'translations' ); + wp_cache_delete( 'cached_mo_files_' . md5( WP_LANG_DIR . '/themes/' ), 'translations' ); break; default: - wp_cache_delete( 'cached_mo_files_' . md5( WP_LANG_DIR ), 'translations' ); + wp_cache_delete( 'cached_mo_files_' . md5( WP_LANG_DIR . '/' ), 'translations' ); break; } } diff --git a/src/wp-includes/default-filters.php b/src/wp-includes/default-filters.php index 9cb447181aefd..7ebc3a1d3bfe1 100644 --- a/src/wp-includes/default-filters.php +++ b/src/wp-includes/default-filters.php @@ -687,6 +687,7 @@ add_action( 'embed_head', 'wp_robots' ); add_action( 'embed_head', 'rel_canonical' ); add_action( 'embed_head', 'locale_stylesheet', 30 ); +add_action( 'enqueue_embed_scripts', 'wp_enqueue_emoji_styles' ); add_action( 'embed_content_meta', 'print_embed_comments_button' ); add_action( 'embed_content_meta', 'print_embed_sharing_button' ); diff --git a/src/wp-includes/deprecated.php b/src/wp-includes/deprecated.php index e63708f91bb50..ec7b33f360396 100644 --- a/src/wp-includes/deprecated.php +++ b/src/wp-includes/deprecated.php @@ -5436,7 +5436,7 @@ function _wp_theme_json_webfonts_handler() { $settings = WP_Theme_JSON_Resolver::get_merged_data()->get_settings(); // If in the editor, add webfonts defined in variations. - if ( is_admin() || ( defined( 'REST_REQUEST' ) && REST_REQUEST ) ) { + if ( is_admin() || wp_is_rest_endpoint() ) { $variations = WP_Theme_JSON_Resolver::get_style_variations(); foreach ( $variations as $variation ) { // Skip if fontFamilies are not defined in the variation. diff --git a/src/wp-includes/formatting.php b/src/wp-includes/formatting.php index 83938306b0774..05b103e6f755f 100644 --- a/src/wp-includes/formatting.php +++ b/src/wp-includes/formatting.php @@ -4846,8 +4846,8 @@ function wp_make_link_relative( $link ) { * @global wpdb $wpdb WordPress database abstraction object. * * @param string $option The name of the option. - * @param string $value The unsanitized value. - * @return string Sanitized value. + * @param mixed $value The unsanitized value. + * @return mixed Sanitized value. */ function sanitize_option( $option, $value ) { global $wpdb; @@ -5119,9 +5119,9 @@ function sanitize_option( $option, $value ) { * @since 2.3.0 * @since 4.3.0 Added the `$original_value` parameter. * - * @param string $value The sanitized option value. + * @param mixed $value The sanitized option value. * @param string $option The option name. - * @param string $original_value The original value passed to the function. + * @param mixed $original_value The original value passed to the function. */ return apply_filters( "sanitize_option_{$option}", $value, $option, $original_value ); } diff --git a/src/wp-includes/functions.php b/src/wp-includes/functions.php index a3a5e56bfad18..ff55251d7d760 100644 --- a/src/wp-includes/functions.php +++ b/src/wp-includes/functions.php @@ -3718,7 +3718,7 @@ function wp_die( $message = '', $title = '', $args = array() ) { * @param callable $callback Callback function name. */ $callback = apply_filters( 'wp_die_json_handler', '_json_wp_die_handler' ); - } elseif ( defined( 'REST_REQUEST' ) && REST_REQUEST && wp_is_jsonp_request() ) { + } elseif ( wp_is_serving_rest_request() && wp_is_jsonp_request() ) { /** * Filters the callback for killing WordPress execution for JSONP REST requests. * @@ -4441,7 +4441,7 @@ function _wp_json_prepare_data( $value ) { * @param int $flags Optional. Options to be passed to json_encode(). Default 0. */ function wp_send_json( $response, $status_code = null, $flags = 0 ) { - if ( defined( 'REST_REQUEST' ) && REST_REQUEST ) { + if ( wp_is_serving_rest_request() ) { _doing_it_wrong( __FUNCTION__, sprintf( @@ -4697,6 +4697,23 @@ function _mce_set_direction( $mce_init ) { return $mce_init; } +/** + * Determines whether WordPress is currently serving a REST API request. + * + * The function relies on the 'REST_REQUEST' global. As such, it only returns true when an actual REST _request_ is + * being made. It does not return true when a REST endpoint is hit as part of another request, e.g. for preloading a + * REST response. See {@see wp_is_rest_endpoint()} for that purpose. + * + * This function should not be called until the {@see 'parse_request'} action, as the constant is only defined then, + * even for an actual REST request. + * + * @since 6.5.0 + * + * @return bool True if it's a WordPress REST API request, false otherwise. + */ +function wp_is_serving_rest_request() { + return defined( 'REST_REQUEST' ) && REST_REQUEST; +} /** * Converts smiley code to the icon graphic file equivalent. diff --git a/src/wp-includes/global-styles-and-settings.php b/src/wp-includes/global-styles-and-settings.php index acca33be1e844..6ccf02f5776b1 100644 --- a/src/wp-includes/global-styles-and-settings.php +++ b/src/wp-includes/global-styles-and-settings.php @@ -54,7 +54,7 @@ function wp_get_global_settings( $path = array(), $context = array() ) { * is always fresh from the potential modifications done via hooks * that can use dynamic data (modify the stylesheet depending on some option, * settings depending on user permissions, etc.). - * See some of the existing hooks to modify theme.json behaviour: + * See some of the existing hooks to modify theme.json behavior: * https://make.wordpress.org/core/2022/10/10/filters-for-theme-json-data/ * * A different alternative considered was to invalidate the cache upon certain diff --git a/src/wp-includes/html-api/class-wp-html-processor.php b/src/wp-includes/html-api/class-wp-html-processor.php index cce26a60c5350..acf884f47738f 100644 --- a/src/wp-includes/html-api/class-wp-html-processor.php +++ b/src/wp-includes/html-api/class-wp-html-processor.php @@ -102,17 +102,17 @@ * - Containers: ADDRESS, BLOCKQUOTE, DETAILS, DIALOG, DIV, FOOTER, HEADER, MAIN, MENU, SPAN, SUMMARY. * - Custom elements: All custom elements are supported. :) * - Form elements: BUTTON, DATALIST, FIELDSET, LABEL, LEGEND, METER, PROGRESS, SEARCH. - * - Formatting elements: B, BIG, CODE, EM, FONT, I, SMALL, STRIKE, STRONG, TT, U. + * - Formatting elements: B, BIG, CODE, EM, FONT, I, PRE, SMALL, STRIKE, STRONG, TT, U, WBR. * - Heading elements: H1, H2, H3, H4, H5, H6, HGROUP. * - Links: A. * - Lists: DD, DL, DT, LI, OL, LI. - * - Media elements: AUDIO, CANVAS, FIGCAPTION, FIGURE, IMG, MAP, PICTURE, VIDEO. - * - Paragraph: P. - * - Phrasing elements: ABBR, BDI, BDO, CITE, DATA, DEL, DFN, INS, MARK, OUTPUT, Q, SAMP, SUB, SUP, TIME, VAR. - * - Sectioning elements: ARTICLE, ASIDE, NAV, SECTION. + * - Media elements: AUDIO, CANVAS, EMBED, FIGCAPTION, FIGURE, IMG, MAP, PICTURE, VIDEO. + * - Paragraph: BR, P. + * - Phrasing elements: AREA, ABBR, BDI, BDO, CITE, DATA, DEL, DFN, INS, MARK, OUTPUT, Q, SAMP, SUB, SUP, TIME, VAR. + * - Sectioning elements: ARTICLE, ASIDE, HR, NAV, SECTION. * - Templating elements: SLOT. * - Text decoration: RUBY. - * - Deprecated elements: ACRONYM, BLINK, CENTER, DIR, ISINDEX, MULTICOL, NEXTID, SPACER. + * - Deprecated elements: ACRONYM, BLINK, CENTER, DIR, ISINDEX, KEYGEN, LISTING, MULTICOL, NEXTID, SPACER. * * ### Supported markup * @@ -149,17 +149,6 @@ class WP_HTML_Processor extends WP_HTML_Tag_Processor { */ const MAX_BOOKMARKS = 100; - /** - * Static query for instructing the Tag Processor to visit every token. - * - * @access private - * - * @since 6.4.0 - * - * @var array - */ - const VISIT_EVERYTHING = array( 'tag_closers' => 'visit' ); - /** * Holds the working state of the parser, including the stack of * open elements and the stack of active formatting elements. @@ -424,6 +413,23 @@ public function next_tag( $query = null ) { return false; } + /** + * Steps through the HTML document and stop at the next token, if any. + * + * Currently only supports stepping through tags. + * + * @return bool + */ + public function next_token() { + $found_a_token = parent::next_token(); + + if ( '#tag' === $this->get_token_type() ) { + $this->step( self::REPROCESS_CURRENT_NODE ); + } + + return $found_a_token; + } + /** * Indicates if the currently-matched tag matches the given breadcrumbs. * @@ -520,7 +526,9 @@ public function step( $node_to_process = self::PROCESS_NEXT_NODE ) { $this->state->stack_of_open_elements->pop(); } - parent::next_tag( self::VISIT_EVERYTHING ); + while ( parent::next_token() && '#tag' !== $this->get_token_type() ) { + continue; + } } // Finish stepping when there are no more tokens in the document. @@ -684,10 +692,12 @@ private function step_in_body() { case '-FOOTER': case '-HEADER': case '-HGROUP': + case '-LISTING': case '-MAIN': case '-MENU': case '-NAV': case '-OL': + case '-PRE': case '-SEARCH': case '-SECTION': case '-SUMMARY': @@ -732,6 +742,18 @@ private function step_in_body() { $this->insert_html_element( $this->state->current_token ); return true; + /* + * > A start tag whose tag name is one of: "pre", "listing" + */ + case '+PRE': + case '+LISTING': + if ( $this->state->stack_of_open_elements->has_p_in_button_scope() ) { + $this->close_a_p_element(); + } + $this->insert_html_element( $this->state->current_token ); + $this->state->frameset_ok = false; + return true; + /* * > An end tag whose tag name is one of: "h1", "h2", "h3", "h4", "h5", "h6" */ @@ -934,12 +956,39 @@ private function step_in_body() { $this->run_adoption_agency_algorithm(); return true; + /* + * > An end tag whose tag name is "br" + * > Parse error. Drop the attributes from the token, and act as described in the next + * > entry; i.e. act as if this was a "br" start tag token with no attributes, rather + * > than the end tag token that it actually is. + */ + case '-BR': + $this->last_error = self::ERROR_UNSUPPORTED; + throw new WP_HTML_Unsupported_Exception( 'Closing BR tags require unimplemented special handling.' ); + /* * > A start tag whose tag name is one of: "area", "br", "embed", "img", "keygen", "wbr" */ + case '+AREA': + case '+BR': + case '+EMBED': case '+IMG': + case '+KEYGEN': + case '+WBR': $this->reconstruct_active_formatting_elements(); $this->insert_html_element( $this->state->current_token ); + $this->state->frameset_ok = false; + return true; + + /* + * > A start tag whose tag name is "hr" + */ + case '+HR': + if ( $this->state->stack_of_open_elements->has_p_in_button_scope() ) { + $this->close_a_p_element(); + } + $this->insert_html_element( $this->state->current_token ); + $this->state->frameset_ok = false; return true; } @@ -961,30 +1010,21 @@ private function step_in_body() { */ switch ( $tag_name ) { case 'APPLET': - case 'AREA': case 'BASE': case 'BASEFONT': case 'BGSOUND': case 'BODY': - case 'BR': case 'CAPTION': case 'COL': case 'COLGROUP': - case 'DD': - case 'DT': - case 'EMBED': case 'FORM': case 'FRAME': case 'FRAMESET': case 'HEAD': - case 'HR': case 'HTML': case 'IFRAME': case 'INPUT': - case 'KEYGEN': - case 'LI': case 'LINK': - case 'LISTING': case 'MARQUEE': case 'MATH': case 'META': @@ -993,12 +1033,10 @@ private function step_in_body() { case 'NOFRAMES': case 'NOSCRIPT': case 'OBJECT': - case 'OL': case 'OPTGROUP': case 'OPTION': case 'PARAM': case 'PLAINTEXT': - case 'PRE': case 'RB': case 'RP': case 'RT': @@ -1020,8 +1058,6 @@ private function step_in_body() { case 'TITLE': case 'TR': case 'TRACK': - case 'UL': - case 'WBR': case 'XMP': $this->last_error = self::ERROR_UNSUPPORTED; throw new WP_HTML_Unsupported_Exception( "Cannot process {$tag_name} element." ); @@ -1675,14 +1711,19 @@ public static function is_void( $tag_name ) { return ( 'AREA' === $tag_name || 'BASE' === $tag_name || + 'BASEFONT' === $tag_name || // Obsolete but still treated as void. + 'BGSOUND' === $tag_name || // Obsolete but still treated as void. 'BR' === $tag_name || 'COL' === $tag_name || 'EMBED' === $tag_name || + 'FRAME' === $tag_name || 'HR' === $tag_name || 'IMG' === $tag_name || 'INPUT' === $tag_name || 'LINK' === $tag_name || + 'KEYGEN' === $tag_name || // Obsolete but still treated as void. 'META' === $tag_name || + 'PARAM' === $tag_name || // Obsolete but still treated as void. 'SOURCE' === $tag_name || 'TRACK' === $tag_name || 'WBR' === $tag_name diff --git a/src/wp-includes/html-api/class-wp-html-tag-processor.php b/src/wp-includes/html-api/class-wp-html-tag-processor.php index ee6209c69e0ae..1e56834d4eda6 100644 --- a/src/wp-includes/html-api/class-wp-html-tag-processor.php +++ b/src/wp-includes/html-api/class-wp-html-tag-processor.php @@ -247,6 +247,95 @@ * } * } * + * ## Tokens and finer-grained processing. + * + * It's possible to scan through every lexical token in the + * HTML document using the `next_token()` function. This + * alternative form takes no argument and provides no built-in + * query syntax. + * + * Example: + * + * $title = '(untitled)'; + * $text = ''; + * while ( $processor->next_token() ) { + * switch ( $processor->get_token_name() ) { + * case '#text': + * $text .= $processor->get_modifiable_text(); + * break; + * + * case 'BR': + * $text .= "\n"; + * break; + * + * case 'TITLE': + * $title = $processor->get_modifiable_text(); + * break; + * } + * } + * return trim( "# {$title}\n\n{$text}" ); + * + * ### Tokens and _modifiable text_. + * + * #### Special "atomic" HTML elements. + * + * Not all HTML elements are able to contain other elements inside of them. + * For instance, the contents inside a TITLE element are plaintext (except + * that character references like & will be decoded). This means that + * if the string `` appears inside a TITLE element, then it's not an + * image tag, but rather it's text describing an image tag. Likewise, the + * contents of a SCRIPT or STYLE element are handled entirely separately in + * a browser than the contents of other elements because they represent a + * different language than HTML. + * + * For these elements the Tag Processor treats the entire sequence as one, + * from the opening tag, including its contents, through its closing tag. + * This means that the it's not possible to match the closing tag for a + * SCRIPT element unless it's unexpected; the Tag Processor already matched + * it when it found the opening tag. + * + * The inner contents of these elements are that element's _modifiable text_. + * + * The special elements are: + * - `SCRIPT` whose contents are treated as raw plaintext but supports a legacy + * style of including Javascript inside of HTML comments to avoid accidentally + * closing the SCRIPT from inside a Javascript string. E.g. `console.log( '' )`. + * - `TITLE` and `TEXTAREA` whose contents are treated as plaintext and then any + * character references are decoded. E.g. `1 < 2 < 3` becomes `1 < 2 < 3`. + * - `IFRAME`, `NOSCRIPT`, `NOEMBED`, `NOFRAME`, `STYLE` whose contents are treated as + * raw plaintext and left as-is. E.g. `1 < 2 < 3` remains `1 < 2 < 3`. + * + * #### Other tokens with modifiable text. + * + * There are also non-elements which are void/self-closing in nature and contain + * modifiable text that is part of that individual syntax token itself. + * + * - `#text` nodes, whose entire token _is_ the modifiable text. + * - HTML comments and tokens that become comments due to some syntax error. The + * text for these tokens is the portion of the comment inside of the syntax. + * E.g. for `` the text is `" comment "` (note the spaces are included). + * - `CDATA` sections, whose text is the content inside of the section itself. E.g. for + * `` the text is `"some content"` (with restrictions [1]). + * - "Funky comments," which are a special case of invalid closing tags whose name is + * invalid. The text for these nodes is the text that a browser would transform into + * an HTML comment when parsing. E.g. for `` the text is `%post_author`. + * - `DOCTYPE` declarations like `` which have no closing tag. + * - XML Processing instruction nodes like `` (with restrictions [2]). + * - The empty end tag `` which is ignored in the browser and DOM. + * + * [1]: There are no CDATA sections in HTML. When encountering `` becomes a bogus HTML comment, meaning there can be no CDATA + * section in an HTML document containing `>`. The Tag Processor will first find + * all valid and bogus HTML comments, and then if the comment _would_ have been a + * CDATA section _were they to exist_, it will re-classify the bogus comment as such. + * + * [2]: XML allows a broader range of characters in a processing instruction's target name + * and disallows "xml" as a name, since it's special. The Tag Processor only recognizes + * target names with an ASCII-representable subset of characters. It also exhibits the + * same constraint as with CDATA sections, in that `>` cannot exist within the token + * since Processing Instructions do no exist within HTML and their syntax transforms + * into a bogus comment in the DOM. + * * ## Design and limitations * * The Tag Processor is designed to linearly scan HTML documents and tokenize @@ -320,7 +409,8 @@ * @since 6.2.1 Fix: Support for various invalid comments; attribute updates are case-insensitive. * @since 6.3.2 Fix: Skip HTML-like content inside rawtext elements such as STYLE. * @since 6.5.0 Pauses processor when input ends in an incomplete syntax token. - * Introduces "special" elements which act like void elements, e.g. STYLE. + * Introduces "special" elements which act like void elements, e.g. TITLE, STYLE. + * Allows scanning through all tokens and processing modifiable text, where applicable. */ class WP_HTML_Tag_Processor { /** @@ -396,23 +486,36 @@ class WP_HTML_Tag_Processor { /** * Specifies mode of operation of the parser at any given time. * - * | State | Meaning | - * | --------------|----------------------------------------------------------------------| - * | *Ready* | The parser is ready to run. | - * | *Complete* | There is nothing left to parse. | - * | *Incomplete* | The HTML ended in the middle of a token; nothing more can be parsed. | - * | *Matched tag* | Found an HTML tag; it's possible to modify its attributes. | + * | State | Meaning | + * | ----------------|----------------------------------------------------------------------| + * | *Ready* | The parser is ready to run. | + * | *Complete* | There is nothing left to parse. | + * | *Incomplete* | The HTML ended in the middle of a token; nothing more can be parsed. | + * | *Matched tag* | Found an HTML tag; it's possible to modify its attributes. | + * | *Text node* | Found a #text node; this is plaintext and modifiable. | + * | *CDATA node* | Found a CDATA section; this is modifiable. | + * | *PI node* | Found a Processing Instruction; this is modifiable. | + * | *Comment* | Found a comment or bogus comment; this is modifiable. | + * | *Presumptuous* | Found an empty tag closer: ``. | + * | *Funky comment* | Found a tag closer with an invalid tag name; this is modifiable. | * * @since 6.5.0 * * @see WP_HTML_Tag_Processor::STATE_READY * @see WP_HTML_Tag_Processor::STATE_COMPLETE - * @see WP_HTML_Tag_Processor::STATE_INCOMPLETE + * @see WP_HTML_Tag_Processor::STATE_INCOMPLETE_INPUT * @see WP_HTML_Tag_Processor::STATE_MATCHED_TAG + * @see WP_HTML_Tag_Processor::STATE_TEXT_NODE + * @see WP_HTML_Tag_Processor::STATE_CDATA_NODE + * @see WP_HTML_Tag_Processor::STATE_PI_NODE + * @see WP_HTML_Tag_Processor::STATE_COMMENT + * @see WP_HTML_Tag_Processor::STATE_DOCTYPE + * @see WP_HTML_Tag_Processor::STATE_PRESUMPTUOUS_TAG + * @see WP_HTML_Tag_Processor::STATE_FUNKY_COMMENT * * @var string */ - private $parser_state = self::STATE_READY; + protected $parser_state = self::STATE_READY; /** * How many bytes from the original HTML document have been read and parsed. @@ -490,6 +593,24 @@ class WP_HTML_Tag_Processor { */ private $tag_name_length; + /** + * Byte offset into input document where current modifiable text starts. + * + * @since 6.5.0 + * + * @var int + */ + private $text_starts_at; + + /** + * Byte length of modifiable text. + * + * @since 6.5.0 + * + * @var string + */ + private $text_length; + /** * Whether the current tag is an opening tag, e.g.
, or a closing tag, e.g.
. * @@ -705,13 +826,13 @@ public function next_tag( $query = null ) { * @return bool Whether a token was parsed. */ public function next_token() { - $this->get_updated_html(); $was_at = $this->bytes_already_parsed; + $this->get_updated_html(); // Don't proceed if there's nothing more to scan. if ( self::STATE_COMPLETE === $this->parser_state || - self::STATE_INCOMPLETE === $this->parser_state + self::STATE_INCOMPLETE_INPUT === $this->parser_state ) { return false; } @@ -729,13 +850,27 @@ public function next_token() { // Find the next tag if it exists. if ( false === $this->parse_next_tag() ) { - if ( self::STATE_INCOMPLETE === $this->parser_state ) { + if ( self::STATE_INCOMPLETE_INPUT === $this->parser_state ) { $this->bytes_already_parsed = $was_at; } return false; } + /* + * For legacy reasons the rest of this function handles tags and their + * attributes. If the processor has reached the end of the document + * or if it matched any other token then it should return here to avoid + * attempting to process tag-specific syntax. + */ + if ( + self::STATE_INCOMPLETE_INPUT !== $this->parser_state && + self::STATE_COMPLETE !== $this->parser_state && + self::STATE_MATCHED_TAG !== $this->parser_state + ) { + return true; + } + // Parse all of its attributes. while ( $this->parse_next_attribute() ) { continue; @@ -743,11 +878,11 @@ public function next_token() { // Ensure that the tag closes before the end of the document. if ( - self::STATE_INCOMPLETE === $this->parser_state || + self::STATE_INCOMPLETE_INPUT === $this->parser_state || $this->bytes_already_parsed >= strlen( $this->html ) ) { // Does this appropriately clear state (parsed attributes)? - $this->parser_state = self::STATE_INCOMPLETE; + $this->parser_state = self::STATE_INCOMPLETE_INPUT; $this->bytes_already_parsed = $was_at; return false; @@ -755,14 +890,14 @@ public function next_token() { $tag_ends_at = strpos( $this->html, '>', $this->bytes_already_parsed ); if ( false === $tag_ends_at ) { - $this->parser_state = self::STATE_INCOMPLETE; + $this->parser_state = self::STATE_INCOMPLETE_INPUT; $this->bytes_already_parsed = $was_at; return false; } $this->parser_state = self::STATE_MATCHED_TAG; $this->token_length = $tag_ends_at - $this->token_starts_at; - $this->bytes_already_parsed = $tag_ends_at; + $this->bytes_already_parsed = $tag_ends_at + 1; /* * For non-DATA sections which might contain text that looks like HTML tags but @@ -771,8 +906,8 @@ public function next_token() { */ $t = $this->html[ $this->tag_name_starts_at ]; if ( - ! $this->is_closing_tag && - ( + $this->is_closing_tag || + ! ( 'i' === $t || 'I' === $t || 'n' === $t || 'N' === $t || 's' === $t || 'S' === $t || @@ -780,38 +915,81 @@ public function next_token() { 'x' === $t || 'X' === $t ) ) { - $tag_name = $this->get_tag(); + return true; + } - if ( 'SCRIPT' === $tag_name && ! $this->skip_script_data() ) { - $this->parser_state = self::STATE_INCOMPLETE; - $this->bytes_already_parsed = $was_at; + $tag_name = $this->get_tag(); - return false; - } elseif ( - ( 'TEXTAREA' === $tag_name || 'TITLE' === $tag_name ) && - ! $this->skip_rcdata( $tag_name ) - ) { - $this->parser_state = self::STATE_INCOMPLETE; - $this->bytes_already_parsed = $was_at; + /* + * Preserve the opening tag pointers, as these will be overwritten + * when finding the closing tag. They will be reset after finding + * the closing to tag to point to the opening of the special atomic + * tag sequence. + */ + $tag_name_starts_at = $this->tag_name_starts_at; + $tag_name_length = $this->tag_name_length; + $tag_ends_at = $this->token_starts_at + $this->token_length; + $attributes = $this->attributes; + $duplicate_attributes = $this->duplicate_attributes; + + // Find the closing tag if necessary. + $found_closer = false; + switch ( $tag_name ) { + case 'SCRIPT': + $found_closer = $this->skip_script_data(); + break; - return false; - } elseif ( - ( - 'IFRAME' === $tag_name || - 'NOEMBED' === $tag_name || - 'NOFRAMES' === $tag_name || - 'STYLE' === $tag_name || - 'XMP' === $tag_name - ) && - ! $this->skip_rawtext( $tag_name ) - ) { - $this->parser_state = self::STATE_INCOMPLETE; - $this->bytes_already_parsed = $was_at; + case 'TEXTAREA': + case 'TITLE': + $found_closer = $this->skip_rcdata( $tag_name ); + break; - return false; - } + /* + * In the browser this list would include the NOSCRIPT element, + * but the Tag Processor is an environment with the scripting + * flag disabled, meaning that it needs to descend into the + * NOSCRIPT element to be able to properly process what will be + * sent to a browser. + * + * Note that this rule makes HTML5 syntax incompatible with XML, + * because the parsing of this token depends on client application. + * The NOSCRIPT element cannot be represented in the XHTML syntax. + */ + case 'IFRAME': + case 'NOEMBED': + case 'NOFRAMES': + case 'STYLE': + case 'XMP': + $found_closer = $this->skip_rawtext( $tag_name ); + break; + + // No other tags should be treated in their entirety here. + default: + return true; + } + + if ( ! $found_closer ) { + $this->parser_state = self::STATE_INCOMPLETE_INPUT; + $this->bytes_already_parsed = $was_at; + return false; } + /* + * The values here look like they reference the opening tag but they reference + * the closing tag instead. This is why the opening tag values were stored + * above in a variable. It reads confusingly here, but that's because the + * functions that skip the contents have moved all the internal cursors past + * the inner content of the tag. + */ + $this->token_starts_at = $was_at; + $this->token_length = $this->bytes_already_parsed - $this->token_starts_at; + $this->text_starts_at = $tag_ends_at + 1; + $this->text_length = $this->tag_name_starts_at - $this->text_starts_at; + $this->tag_name_starts_at = $tag_name_starts_at; + $this->tag_name_length = $tag_name_length; + $this->attributes = $attributes; + $this->duplicate_attributes = $duplicate_attributes; + return true; } @@ -830,7 +1008,7 @@ public function next_token() { * @return bool Whether the parse paused at the start of an incomplete token. */ public function paused_at_incomplete_token() { - return self::STATE_INCOMPLETE === $this->parser_state; + return self::STATE_INCOMPLETE_INPUT === $this->parser_state; } /** @@ -1007,7 +1185,10 @@ public function has_class( $wanted_class ) { */ public function set_bookmark( $name ) { // It only makes sense to set a bookmark if the parser has paused on a concrete token. - if ( self::STATE_MATCHED_TAG !== $this->parser_state ) { + if ( + self::STATE_COMPLETE === $this->parser_state || + self::STATE_INCOMPLETE_INPUT === $this->parser_state + ) { return false; } @@ -1082,15 +1263,15 @@ private function skip_rcdata( $tag_name ) { $at = $this->bytes_already_parsed; while ( false !== $at && $at < $doc_length ) { - $at = strpos( $this->html, 'html, 'tag_name_starts_at = $at; // Fail if there is no possible tag closer. if ( false === $at || ( $at + $tag_length ) >= $doc_length ) { return false; } - $closer_potentially_starts_at = $at; - $at += 2; + $at += 2; /* * Find a case-insensitive match to the tag name. @@ -1131,13 +1312,23 @@ private function skip_rcdata( $tag_name ) { while ( $this->parse_next_attribute() ) { continue; } + $at = $this->bytes_already_parsed; if ( $at >= strlen( $this->html ) ) { return false; } - if ( '>' === $html[ $at ] || '/' === $html[ $at ] ) { - $this->bytes_already_parsed = $closer_potentially_starts_at; + if ( '>' === $html[ $at ] ) { + $this->bytes_already_parsed = $at + 1; + return true; + } + + if ( $at + 1 >= strlen( $this->html ) ) { + return false; + } + + if ( '/' === $html[ $at ] && '>' === $html[ $at + 1 ] ) { + $this->bytes_already_parsed = $at + 2; return true; } } @@ -1259,6 +1450,7 @@ private function skip_script_data() { if ( $is_closing ) { $this->bytes_already_parsed = $closer_potentially_starts_at; + $this->tag_name_starts_at = $closer_potentially_starts_at; if ( $this->bytes_already_parsed >= $doc_length ) { return false; } @@ -1268,13 +1460,13 @@ private function skip_script_data() { } if ( $this->bytes_already_parsed >= $doc_length ) { - $this->parser_state = self::STATE_INCOMPLETE; + $this->parser_state = self::STATE_INCOMPLETE_INPUT; return false; } if ( '>' === $html[ $this->bytes_already_parsed ] ) { - $this->bytes_already_parsed = $closer_potentially_starts_at; + ++$this->bytes_already_parsed; return true; } } @@ -1303,17 +1495,34 @@ private function parse_next_tag() { $html = $this->html; $doc_length = strlen( $html ); - $at = $this->bytes_already_parsed; + $was_at = $this->bytes_already_parsed; + $at = $was_at; while ( false !== $at && $at < $doc_length ) { $at = strpos( $html, '<', $at ); + if ( $at > $was_at ) { + $this->parser_state = self::STATE_TEXT_NODE; + $this->token_starts_at = $was_at; + $this->token_length = $at - $was_at; + $this->text_starts_at = $was_at; + $this->text_length = $this->token_length; + $this->bytes_already_parsed = $at; + return true; + } + /* * This does not imply an incomplete parse; it indicates that there * can be nothing left in the document other than a #text node. */ if ( false === $at ) { - return false; + $this->parser_state = self::STATE_TEXT_NODE; + $this->token_starts_at = $was_at; + $this->token_length = strlen( $html ) - $was_at; + $this->text_starts_at = $was_at; + $this->text_length = $this->token_length; + $this->bytes_already_parsed = strlen( $html ); + return true; } $this->token_starts_at = $at; @@ -1342,8 +1551,9 @@ private function parse_next_tag() { $tag_name_prefix_length = strspn( $html, 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ', $at + 1 ); if ( $tag_name_prefix_length > 0 ) { ++$at; - $this->tag_name_length = $tag_name_prefix_length + strcspn( $html, " \t\f\r\n/>", $at + $tag_name_prefix_length ); + $this->parser_state = self::STATE_MATCHED_TAG; $this->tag_name_starts_at = $at; + $this->tag_name_length = $tag_name_prefix_length + strcspn( $html, " \t\f\r\n/>", $at + $tag_name_prefix_length ); $this->bytes_already_parsed = $at + $this->tag_name_length; return true; } @@ -1353,18 +1563,18 @@ private function parse_next_tag() { * the document. There is nothing left to parse. */ if ( $at + 1 >= $doc_length ) { - $this->parser_state = self::STATE_INCOMPLETE; + $this->parser_state = self::STATE_INCOMPLETE_INPUT; return false; } /* - * + * ``. Unlike other comment + * and bogus comment syntax, these leave no clear insertion point for text and + * they need to be modified specially in order to contain text. E.g. to store + * `?` as the modifiable text, the `` needs to become ``, which + * involves inserting an additional `-` into the token after the modifiable text. + */ + $this->parser_state = self::STATE_COMMENT; + $this->token_length = $closer_at + $span_of_dashes + 1 - $this->token_starts_at; + + // Only provide modifiable text if the token is long enough to contain it. + if ( $span_of_dashes >= 2 ) { + $this->text_starts_at = $this->token_starts_at + 4; + $this->text_length = $span_of_dashes - 2; + } + + $this->bytes_already_parsed = $closer_at + $span_of_dashes + 1; + return true; } /* @@ -1397,51 +1624,37 @@ private function parse_next_tag() { while ( ++$closer_at < $doc_length ) { $closer_at = strpos( $html, '--', $closer_at ); if ( false === $closer_at ) { - $this->parser_state = self::STATE_INCOMPLETE; + $this->parser_state = self::STATE_INCOMPLETE_INPUT; return false; } if ( $closer_at + 2 < $doc_length && '>' === $html[ $closer_at + 2 ] ) { - $at = $closer_at + 3; - continue 2; + $this->parser_state = self::STATE_COMMENT; + $this->token_length = $closer_at + 3 - $this->token_starts_at; + $this->text_starts_at = $this->token_starts_at + 4; + $this->text_length = $closer_at - $this->text_starts_at; + $this->bytes_already_parsed = $closer_at + 3; + return true; } - if ( $closer_at + 3 < $doc_length && '!' === $html[ $closer_at + 2 ] && '>' === $html[ $closer_at + 3 ] ) { - $at = $closer_at + 4; - continue 2; + if ( + $closer_at + 3 < $doc_length && + '!' === $html[ $closer_at + 2 ] && + '>' === $html[ $closer_at + 3 ] + ) { + $this->parser_state = self::STATE_COMMENT; + $this->token_length = $closer_at + 4 - $this->token_starts_at; + $this->text_starts_at = $this->token_starts_at + 4; + $this->text_length = $closer_at - $this->text_starts_at; + $this->bytes_already_parsed = $closer_at + 4; + return true; } } } /* - * - * The CDATA is case-sensitive. - * https://html.spec.whatwg.org/multipage/parsing.html#tag-open-state - */ - if ( - $doc_length > $at + 8 && - '[' === $html[ $at + 2 ] && - 'C' === $html[ $at + 3 ] && - 'D' === $html[ $at + 4 ] && - 'A' === $html[ $at + 5 ] && - 'T' === $html[ $at + 6 ] && - 'A' === $html[ $at + 7 ] && - '[' === $html[ $at + 8 ] - ) { - $closer_at = strpos( $html, ']]>', $at + 9 ); - if ( false === $closer_at ) { - $this->parser_state = self::STATE_INCOMPLETE; - - return false; - } - - $at = $closer_at + 3; - continue; - } - - /* - * + * ` * These are ASCII-case-insensitive. * https://html.spec.whatwg.org/multipage/parsing.html#tag-open-state */ @@ -1457,13 +1670,17 @@ private function parse_next_tag() { ) { $closer_at = strpos( $html, '>', $at + 9 ); if ( false === $closer_at ) { - $this->parser_state = self::STATE_INCOMPLETE; + $this->parser_state = self::STATE_INCOMPLETE_INPUT; return false; } - $at = $closer_at + 1; - continue; + $this->parser_state = self::STATE_DOCTYPE; + $this->token_length = $closer_at + 1 - $this->token_starts_at; + $this->text_starts_at = $this->token_starts_at + 9; + $this->text_length = $closer_at - $this->text_starts_at; + $this->bytes_already_parsed = $closer_at + 1; + return true; } /* @@ -1471,14 +1688,51 @@ private function parse_next_tag() { * to the bogus comment state - skip to the nearest >. If no closer is * found then the HTML was truncated inside the markup declaration. */ - $at = strpos( $html, '>', $at + 1 ); - if ( false === $at ) { - $this->parser_state = self::STATE_INCOMPLETE; + $closer_at = strpos( $html, '>', $at + 1 ); + if ( false === $closer_at ) { + $this->parser_state = self::STATE_INCOMPLETE_INPUT; return false; } - continue; + $this->parser_state = self::STATE_COMMENT; + $this->token_length = $closer_at + 1 - $this->token_starts_at; + $this->text_starts_at = $this->token_starts_at + 2; + $this->text_length = $closer_at - $this->text_starts_at; + $this->bytes_already_parsed = $closer_at + 1; + + /* + * Identify nodes that would be CDATA if HTML had CDATA sections. + * + * This section must occur after identifying the bogus comment end + * because in an HTML parser it will span to the nearest `>`, even + * if there's no `]]>` as would be required in an XML document. It + * is therefore not possible to parse a CDATA section containing + * a `>` in the HTML syntax. + * + * Inside foreign elements there is a discrepancy between browsers + * and the specification on this. + * + * @todo Track whether the Tag Processor is inside a foreign element + * and require the proper closing `]]>` in those cases. + */ + if ( + $this->token_length >= 10 && + '[' === $html[ $this->token_starts_at + 2 ] && + 'C' === $html[ $this->token_starts_at + 3 ] && + 'D' === $html[ $this->token_starts_at + 4 ] && + 'A' === $html[ $this->token_starts_at + 5 ] && + 'T' === $html[ $this->token_starts_at + 6 ] && + 'A' === $html[ $this->token_starts_at + 7 ] && + '[' === $html[ $this->token_starts_at + 8 ] && + ']' === $html[ $closer_at - 1 ] + ) { + $this->parser_state = self::STATE_CDATA_NODE; + $this->text_starts_at += 7; + $this->text_length -= 9; + } + + return true; } /* @@ -1491,30 +1745,79 @@ private function parse_next_tag() { * See https://html.spec.whatwg.org/#parse-error-missing-end-tag-name */ if ( '>' === $html[ $at + 1 ] ) { - ++$at; - continue; + $this->parser_state = self::STATE_PRESUMPTUOUS_TAG; + $this->token_length = $at + 2 - $this->token_starts_at; + $this->bytes_already_parsed = $at + 2; + return true; } /* - * + * ` * See https://html.spec.whatwg.org/multipage/parsing.html#tag-open-state */ if ( '?' === $html[ $at + 1 ] ) { $closer_at = strpos( $html, '>', $at + 2 ); if ( false === $closer_at ) { - $this->parser_state = self::STATE_INCOMPLETE; + $this->parser_state = self::STATE_INCOMPLETE_INPUT; return false; } - $at = $closer_at + 1; - continue; + $this->parser_state = self::STATE_COMMENT; + $this->token_length = $closer_at + 1 - $this->token_starts_at; + $this->text_starts_at = $this->token_starts_at + 2; + $this->text_length = $closer_at - $this->text_starts_at; + $this->bytes_already_parsed = $closer_at + 1; + + /* + * Identify a Processing Instruction node were HTML to have them. + * + * This section must occur after identifying the bogus comment end + * because in an HTML parser it will span to the nearest `>`, even + * if there's no `?>` as would be required in an XML document. It + * is therefore not possible to parse a Processing Instruction node + * containing a `>` in the HTML syntax. + * + * XML allows for more target names, but this code only identifies + * those with ASCII-representable target names. This means that it + * may identify some Processing Instruction nodes as bogus comments, + * but it will not misinterpret the HTML structure. By limiting the + * identification to these target names the Tag Processor can avoid + * the need to start parsing UTF-8 sequences. + * + * > NameStartChar ::= ":" | [A-Z] | "_" | [a-z] | [#xC0-#xD6] | [#xD8-#xF6] | [#xF8-#x2FF] | + * [#x370-#x37D] | [#x37F-#x1FFF] | [#x200C-#x200D] | [#x2070-#x218F] | + * [#x2C00-#x2FEF] | [#x3001-#xD7FF] | [#xF900-#xFDCF] | [#xFDF0-#xFFFD] | + * [#x10000-#xEFFFF] + * > NameChar ::= NameStartChar | "-" | "." | [0-9] | #xB7 | [#x0300-#x036F] | [#x203F-#x2040] + * + * @see https://www.w3.org/TR/2006/REC-xml11-20060816/#NT-PITarget + */ + if ( $this->token_length >= 5 && '?' === $html[ $closer_at - 1 ] ) { + $comment_text = substr( $html, $this->token_starts_at + 2, $this->token_length - 4 ); + $pi_target_length = strspn( $comment_text, 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ:_' ); + + if ( 0 < $pi_target_length ) { + $pi_target_length += strspn( $comment_text, 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789:_-.', $pi_target_length ); + + $this->parser_state = self::STATE_PI_NODE; + $this->tag_name_starts_at = $this->token_starts_at + 2; + $this->tag_name_length = $pi_target_length; + $this->text_starts_at += $pi_target_length; + $this->text_length -= $pi_target_length + 1; + } + } + + return true; } /* * If a non-alpha starts the tag name in a tag closer it's a comment. * Find the first `>`, which closes the comment. * + * This parser classifies these particular comments as special "funky comments" + * which are made available for further processing. + * * See https://html.spec.whatwg.org/#parse-error-invalid-first-character-of-tag-name */ if ( $this->is_closing_tag ) { @@ -1525,13 +1828,17 @@ private function parse_next_tag() { $closer_at = strpos( $html, '>', $at + 3 ); if ( false === $closer_at ) { - $this->parser_state = self::STATE_INCOMPLETE; + $this->parser_state = self::STATE_INCOMPLETE_INPUT; return false; } - $at = $closer_at + 1; - continue; + $this->parser_state = self::STATE_FUNKY_COMMENT; + $this->token_length = $closer_at + 1 - $this->token_starts_at; + $this->text_starts_at = $this->token_starts_at + 2; + $this->text_length = $closer_at - $this->text_starts_at; + $this->bytes_already_parsed = $closer_at + 1; + return true; } ++$at; @@ -1551,7 +1858,7 @@ private function parse_next_attribute() { // Skip whitespace and slashes. $this->bytes_already_parsed += strspn( $this->html, " \t\f\r\n/", $this->bytes_already_parsed ); if ( $this->bytes_already_parsed >= strlen( $this->html ) ) { - $this->parser_state = self::STATE_INCOMPLETE; + $this->parser_state = self::STATE_INCOMPLETE_INPUT; return false; } @@ -1575,14 +1882,14 @@ private function parse_next_attribute() { $attribute_name = substr( $this->html, $attribute_start, $name_length ); $this->bytes_already_parsed += $name_length; if ( $this->bytes_already_parsed >= strlen( $this->html ) ) { - $this->parser_state = self::STATE_INCOMPLETE; + $this->parser_state = self::STATE_INCOMPLETE_INPUT; return false; } $this->skip_whitespace(); if ( $this->bytes_already_parsed >= strlen( $this->html ) ) { - $this->parser_state = self::STATE_INCOMPLETE; + $this->parser_state = self::STATE_INCOMPLETE_INPUT; return false; } @@ -1592,7 +1899,7 @@ private function parse_next_attribute() { ++$this->bytes_already_parsed; $this->skip_whitespace(); if ( $this->bytes_already_parsed >= strlen( $this->html ) ) { - $this->parser_state = self::STATE_INCOMPLETE; + $this->parser_state = self::STATE_INCOMPLETE_INPUT; return false; } @@ -1620,7 +1927,7 @@ private function parse_next_attribute() { } if ( $attribute_end >= strlen( $this->html ) ) { - $this->parser_state = self::STATE_INCOMPLETE; + $this->parser_state = self::STATE_INCOMPLETE_INPUT; return false; } @@ -1692,6 +1999,8 @@ private function after_tag() { $this->token_length = null; $this->tag_name_starts_at = null; $this->tag_name_length = null; + $this->text_starts_at = 0; + $this->text_length = 0; $this->is_closing_tag = null; $this->attributes = array(); $this->duplicate_attributes = null; @@ -1985,7 +2294,7 @@ public function seek( $bookmark_name ) { // Point this tag processor before the sought tag opener and consume it. $this->bytes_already_parsed = $this->bookmarks[ $bookmark_name ]->start; - return $this->next_tag( array( 'tag_closers' => 'visit' ) ); + return $this->next_token(); } /** @@ -2281,6 +2590,171 @@ public function is_tag_closer() { ); } + /** + * Indicates the kind of matched token, if any. + * + * This differs from `get_token_name()` in that it always + * returns a static string indicating the type, whereas + * `get_token_name()` may return values derived from the + * token itself, such as a tag name or processing + * instruction tag. + * + * Possible values: + * - `#tag` when matched on a tag. + * - `#text` when matched on a text node. + * - `#cdata-section` when matched on a CDATA node. + * - `#processing-instruction` when matched on a processing instruction. + * - `#comment` when matched on a comment. + * - `#doctype` when matched on a DOCTYPE declaration. + * - `#presumptuous-tag` when matched on an empty tag closer. + * - `#funky-comment` when matched on a funky comment. + * + * @since 6.5.0 + * + * @return string|null What kind of token is matched, or null. + */ + public function get_token_type() { + switch ( $this->parser_state ) { + case self::STATE_MATCHED_TAG: + return '#tag'; + + case self::STATE_DOCTYPE: + return '#doctype'; + + case self::STATE_PI_NODE: + return '#processing-instruction'; + + default: + return $this->get_token_name(); + } + } + + /** + * Returns the node name represented by the token. + * + * This matches the DOM API value `nodeName`. Some values + * are static, such as `#text` for a text node, while others + * are dynamically generated from the token itself. + * + * Dynamic names: + * - Uppercase tag name for tag matches. + * - Target name for processing instructions. + * - `html` for DOCTYPE declarations. + * + * Note that if the Tag Processor is not matched on a token + * then this function will return `null`, either because it + * hasn't yet found a token or because it reached the end + * of the document without matching a token. + * + * @since 6.5.0 + * + * @return string|null Name of the matched token. + */ + public function get_token_name() { + switch ( $this->parser_state ) { + case self::STATE_MATCHED_TAG: + return $this->get_tag(); + + case self::STATE_TEXT_NODE: + return '#text'; + + case self::STATE_CDATA_NODE: + return '#cdata-section'; + + case self::STATE_PI_NODE: + return substr( $this->html, $this->tag_name_starts_at, $this->tag_name_length ); + + case self::STATE_COMMENT: + return '#comment'; + + case self::STATE_DOCTYPE: + return 'html'; + + case self::STATE_PRESUMPTUOUS_TAG: + return '#presumptuous-tag'; + + case self::STATE_FUNKY_COMMENT: + return '#funky-comment'; + } + } + + /** + * Returns the modifiable text for a matched token, or an empty string. + * + * Modifiable text is text content that may be read and changed without + * changing the HTML structure of the document around it. This includes + * the contents of `#text` nodes in the HTML as well as the inner + * contents of HTML comments, Processing Instructions, and others, even + * though these nodes aren't part of a parsed DOM tree. They also contain + * the contents of SCRIPT and STYLE tags, of TEXTAREA tags, and of any + * other section in an HTML document which cannot contain HTML markup (DATA). + * + * If a token has no modifiable text then an empty string is returned to + * avoid needless crashing or type errors. An empty string does not mean + * that a token has modifiable text, and a token with modifiable text may + * have an empty string (e.g. a comment with no contents). + * + * @return string + */ + public function get_modifiable_text() { + if ( null === $this->text_starts_at ) { + return ''; + } + + $text = substr( $this->html, $this->text_starts_at, $this->text_length ); + + // Comment data is not decoded. + if ( + self::STATE_CDATA_NODE === $this->parser_state || + self::STATE_COMMENT === $this->parser_state || + self::STATE_DOCTYPE === $this->parser_state || + self::STATE_PI_NODE === $this->parser_state || + self::STATE_FUNKY_COMMENT === $this->parser_state + ) { + return $text; + } + + $tag_name = $this->get_tag(); + if ( + // Script data is not decoded. + 'SCRIPT' === $tag_name || + + // RAWTEXT data is not decoded. + 'IFRAME' === $tag_name || + 'NOEMBED' === $tag_name || + 'NOFRAMES' === $tag_name || + 'STYLE' === $tag_name || + 'XMP' === $tag_name + ) { + return $text; + } + + $decoded = html_entity_decode( $text, ENT_QUOTES | ENT_HTML5 | ENT_SUBSTITUTE ); + + if ( empty( $decoded ) ) { + return ''; + } + + /* + * PRE and TEXTAREA skip a leading newline, but this newline may not be a `\n`. + * It could be a character reference, such as ``. + * For these cases it's important to first decode the text content before checking + * for a leading newline and removing it. + */ + if ( self::STATE_MATCHED_TAG === $this->parser_state ) { + switch ( $tag_name ) { + case 'PRE': + case 'TEXTAREA': + if ( "\n" === $decoded[0] ) { + return substr( $decoded, 1 ); + } + break; + } + } + + return $decoded; + } + /** * Updates or creates a new attribute on the currently matched tag with the passed value. * @@ -2746,7 +3220,7 @@ private function matches() { } /** - * Parser Ready State + * Parser Ready State. * * Indicates that the parser is ready to run and waiting for a state transition. * It may not have started yet, or it may have just finished parsing a token and @@ -2759,7 +3233,7 @@ private function matches() { const STATE_READY = 'STATE_READY'; /** - * Parser Complete State + * Parser Complete State. * * Indicates that the parser has reached the end of the document and there is * nothing left to scan. It finished parsing the last token completely. @@ -2771,7 +3245,7 @@ private function matches() { const STATE_COMPLETE = 'STATE_COMPLETE'; /** - * Parser Incomplete State + * Parser Incomplete Input State. * * Indicates that the parser has reached the end of the document before finishing * a token. It started parsing a token but there is a possibility that the input @@ -2784,10 +3258,10 @@ private function matches() { * * @access private */ - const STATE_INCOMPLETE = 'STATE_INCOMPLETE'; + const STATE_INCOMPLETE_INPUT = 'STATE_INCOMPLETE_INPUT'; /** - * Parser Matched Tag State + * Parser Matched Tag State. * * Indicates that the parser has found an HTML tag and it's possible to get * the tag name and read or modify its attributes (if it's not a closing tag). @@ -2797,4 +3271,106 @@ private function matches() { * @access private */ const STATE_MATCHED_TAG = 'STATE_MATCHED_TAG'; + + /** + * Parser Text Node State. + * + * Indicates that the parser has found a text node and it's possible + * to read and modify that text. + * + * @since 6.5.0 + * + * @access private + */ + const STATE_TEXT_NODE = 'STATE_TEXT_NODE'; + + /** + * Parser CDATA Node State. + * + * Indicates that the parser has found a CDADA node and it's possible + * to read and modify its modifiable text. Note that in HTML there are + * no CDATA nodes outside foreign elements (SVG and MathML). Outside + * of foreign elements, they are treated as HTML comments. Nonetheless, + * the Tag Processor still recognizes them as they appear in the HTML + * stream and exposes them for inspection and modification. + * + * @since 6.5.0 + * + * @access private + */ + const STATE_CDATA_NODE = 'STATE_CDATA_NODE'; + + /** + * Parser Processing Instruction State. + * + * Indicates that the parser has found a Processing Instruction and + * it's possible to read and modify its modifiable text. Note that in + * HTML there are no Processing Instruction nodes and they are treated + * as HTML comments. Nonetheless, the Tag Processor still recognizes + * them as they appear in the HTML stream and exposes them for + * inspection and modification. + * + * @since 6.5.0 + * + * @access private + */ + const STATE_PI_NODE = 'STATE_PI_NODE'; + + /** + * Indicates that the parser has found an HTML comment and it's + * possible to read and modify its modifiable text. + * + * @since 6.5.0 + * + * @access private + */ + const STATE_COMMENT = 'STATE_COMMENT'; + + /** + * Indicates that the parser has found a DOCTYPE node and it's + * possible to read and modify its modifiable text. + * + * @since 6.5.0 + * + * @access private + */ + const STATE_DOCTYPE = 'STATE_DOCTYPE'; + + /** + * Indicates that the parser has found an empty tag closer ``. + * + * Note that in HTML there are no empty tag closers, and they + * are ignored. Nonetheless, the Tag Processor still + * recognizes them as they appear in the HTML stream. + * + * These were historically discussed as a "presumptuous tag + * closer," which would close the nearest open tag, but were + * dismissed in favor of explicitly-closing tags. + * + * @since 6.5.0 + * + * @access private + */ + const STATE_PRESUMPTUOUS_TAG = 'STATE_PRESUMPTUOUS_TAG'; + + /** + * Indicates that the parser has found a "funky comment" + * and it's possible to read and modify its modifiable text. + * + * Example: + * + * + * + * + * + * Funky comments are tag closers with invalid tag names. Note + * that in HTML these are turn into bogus comments. Nonetheless, + * the Tag Processor recognizes them in a stream of HTML and + * exposes them for inspection and modification. + * + * @since 6.5.0 + * + * @access private + */ + const STATE_FUNKY_COMMENT = 'STATE_WP_FUNKY'; } diff --git a/src/wp-includes/load.php b/src/wp-includes/load.php index 520902cdd64ba..0dbbc187cfce2 100644 --- a/src/wp-includes/load.php +++ b/src/wp-includes/load.php @@ -598,6 +598,10 @@ function wp_debug_mode() { error_reporting( E_CORE_ERROR | E_CORE_WARNING | E_COMPILE_ERROR | E_ERROR | E_WARNING | E_PARSE | E_USER_ERROR | E_USER_WARNING | E_RECOVERABLE_ERROR ); } + /* + * The 'REST_REQUEST' check here is optimistic as the constant is most + * likely not set at this point even if it is in fact a REST request. + */ if ( defined( 'XMLRPC_REQUEST' ) || defined( 'REST_REQUEST' ) || defined( 'MS_FILES_REQUEST' ) || ( defined( 'WP_INSTALLING' ) && WP_INSTALLING ) || wp_doing_ajax() || wp_is_json_request() diff --git a/src/wp-includes/media.php b/src/wp-includes/media.php index 9cf301e95c11a..38ec2213b7506 100644 --- a/src/wp-includes/media.php +++ b/src/wp-includes/media.php @@ -2117,6 +2117,13 @@ function wp_img_tag_add_width_and_height_attr( $image, $context, $attachment_id $size_array = wp_image_src_get_dimensions( $image_src, $image_meta, $attachment_id ); if ( $size_array ) { + // If the width is enforced through style (e.g. in an inline image), calculate the dimension attributes. + $style_width = preg_match( '/style="width:\s*(\d+)px;"/', $image, $match_width ) ? (int) $match_width[1] : 0; + if ( $style_width ) { + $size_array[1] = (int) round( $size_array[1] * $style_width / $size_array[0] ); + $size_array[0] = $style_width; + } + $hw = trim( image_hwstring( $size_array[0], $size_array[1] ) ); return str_replace( 'is_dispatching(); + } + + /** + * Filters whether a REST endpoint request is currently being handled. + * + * This may be a standalone REST API request, or an internal request dispatched from within a regular page load. + * + * @since 6.5.0 + * + * @param bool $is_request_endpoint Whether a REST endpoint request is currently being handled. + */ + return (bool) apply_filters( 'wp_is_rest_endpoint', $is_rest_endpoint ); +} diff --git a/src/wp-includes/rest-api/class-wp-rest-server.php b/src/wp-includes/rest-api/class-wp-rest-server.php index 6838579ce8609..861f5115e6ae9 100644 --- a/src/wp-includes/rest-api/class-wp-rest-server.php +++ b/src/wp-includes/rest-api/class-wp-rest-server.php @@ -87,6 +87,14 @@ class WP_REST_Server { */ protected $embed_cache = array(); + /** + * Stores request objects that are currently being handled. + * + * @since 6.5.0 + * @var array + */ + protected $dispatching_requests = array(); + /** * Instantiates the REST server. * @@ -983,6 +991,8 @@ public function get_route_options( $route ) { * @return WP_REST_Response Response returned by the callback. */ public function dispatch( $request ) { + $this->dispatching_requests[] = $request; + /** * Filters the pre-calculated result of a REST API dispatch request. * @@ -1008,6 +1018,7 @@ public function dispatch( $request ) { $result = $this->error_to_response( $result ); } + array_pop( $this->dispatching_requests ); return $result; } @@ -1015,7 +1026,9 @@ public function dispatch( $request ) { $matched = $this->match_request_to_handler( $request ); if ( is_wp_error( $matched ) ) { - return $this->error_to_response( $matched ); + $response = $this->error_to_response( $matched ); + array_pop( $this->dispatching_requests ); + return $response; } list( $route, $handler ) = $matched; @@ -1040,7 +1053,22 @@ public function dispatch( $request ) { } } - return $this->respond_to_request( $request, $route, $handler, $error ); + $response = $this->respond_to_request( $request, $route, $handler, $error ); + array_pop( $this->dispatching_requests ); + return $response; + } + + /** + * Returns whether the REST server is currently dispatching / responding to a request. + * + * This may be a standalone REST API request, or an internal request dispatched from within a regular page load. + * + * @since 6.5.0 + * + * @return bool Whether the REST server is currently handling a request. + */ + public function is_dispatching() { + return (bool) $this->dispatching_requests; } /** diff --git a/src/wp-includes/rest-api/endpoints/class-wp-rest-attachments-controller.php b/src/wp-includes/rest-api/endpoints/class-wp-rest-attachments-controller.php index 7367c0fc57a07..c7da6d068b29d 100644 --- a/src/wp-includes/rest-api/endpoints/class-wp-rest-attachments-controller.php +++ b/src/wp-includes/rest-api/endpoints/class-wp-rest-attachments-controller.php @@ -201,7 +201,7 @@ public function create_item( $request ) { wp_after_insert_post( $attachment, false, null ); - if ( defined( 'REST_REQUEST' ) && REST_REQUEST ) { + if ( wp_is_serving_rest_request() ) { /* * Set a custom header with the attachment_id. * Used by the browser/client to resume creating image sub-sizes after a PHP fatal error. @@ -630,7 +630,7 @@ public function edit_media_item( $request ) { update_post_meta( $new_attachment_id, '_wp_attachment_image_alt', wp_slash( $image_alt ) ); } - if ( defined( 'REST_REQUEST' ) && REST_REQUEST ) { + if ( wp_is_serving_rest_request() ) { /* * Set a custom header with the attachment_id. * Used by the browser/client to resume creating image sub-sizes after a PHP fatal error. diff --git a/src/wp-includes/rest-api/endpoints/class-wp-rest-url-details-controller.php b/src/wp-includes/rest-api/endpoints/class-wp-rest-url-details-controller.php index c9ac6675d093f..7dcc4d79236a1 100644 --- a/src/wp-includes/rest-api/endpoints/class-wp-rest-url-details-controller.php +++ b/src/wp-includes/rest-api/endpoints/class-wp-rest-url-details-controller.php @@ -128,7 +128,7 @@ public function get_item_schema() { * * @since 5.9.0 * - * @param WP_REST_REQUEST $request Full details about the request. + * @param WP_REST_Request $request Full details about the request. * @return WP_REST_Response|WP_Error The parsed details as a response object. WP_Error if there are errors. */ public function parse_url_details( $request ) { diff --git a/src/wp-includes/script-loader.php b/src/wp-includes/script-loader.php index 8886cb587b170..6425afe87425d 100644 --- a/src/wp-includes/script-loader.php +++ b/src/wp-includes/script-loader.php @@ -2586,7 +2586,7 @@ function wp_should_load_block_editor_scripts_and_styles() { * @return bool Whether separate assets will be loaded. */ function wp_should_load_separate_core_block_assets() { - if ( is_admin() || is_feed() || ( defined( 'REST_REQUEST' ) && REST_REQUEST ) ) { + if ( is_admin() || is_feed() || wp_is_rest_endpoint() ) { return false; } diff --git a/src/wp-includes/user.php b/src/wp-includes/user.php index 5dbff2b929b2e..5d8cd9f57c64c 100644 --- a/src/wp-includes/user.php +++ b/src/wp-includes/user.php @@ -333,6 +333,7 @@ function wp_authenticate_application_password( $input_user, $username, $password return $input_user; } + // The 'REST_REQUEST' check here may happen too early for the constant to be available. $is_api_request = ( ( defined( 'XMLRPC_REQUEST' ) && XMLRPC_REQUEST ) || ( defined( 'REST_REQUEST' ) && REST_REQUEST ) ); /** diff --git a/src/wp-login.php b/src/wp-login.php index 0641cd52e6a40..9eeac4a96ce46 100644 --- a/src/wp-login.php +++ b/src/wp-login.php @@ -364,7 +364,7 @@ function login_footer( $input_id = '' ) { if ( ! empty( $languages ) ) { ?>
- +