Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Webfonts: Add internal-only theme.json webfonts handler - WP 6.0 stopgap #40493

Closed
wants to merge 14 commits into from

Conversation

hellofromtonya
Copy link
Contributor

@hellofromtonya hellofromtonya commented Apr 20, 2022

Part of #40472.

Props to @aristath who pulled together the key elements of this PR.

What?

Using the Webfonts API from PR #37140, this PR adds a theme.json webfonts handler (albeit funky design) to:

  • handle only theme.json webfonts defined in fontFace.
  • prevent access to the webfonts handling mechanisms by encapsulating all logic into one function that serves as a private container, i.e. _wp_theme_json_webfonts_handler().

This is a stopgap approach.

Why?

See the issue for the reasons.

How?

It uses the Webfonts API from PR #37140 but smushes the code together into $fn = function() {} closures that are nested inside of a global function. This global function is the container that prevents access to the handler, thus ensuring the mechanisms are hidden and not accessible for extenders to interact with the handler.

Testing Instructions

Set up instructions:

Using Twenty Twenty-Two theme, either:

			"fontFamilies": [
				{
					"fontFamily": "-apple-system,BlinkMacSystemFont,\"Segoe UI\",Roboto,Oxygen-Sans,Ubuntu,Cantarell,\"Helvetica Neue\",sans-serif",
					"name": "System Font",
					"slug": "system-font"
				},
				{
					"fontFamily": "\"Source Serif Pero\", serif",
					"name": "Source Serif Pero",
					"slug": "source-serif-pero",
					"fontFace": [
						{
							"fontFamily": "Source Serif Pero",
							"fontWeight": "200 900",
							"fontStyle": "normal",
							"fontStretch": "normal",
							"src": [ "file:./assets/fonts/SourceSerif4Variable-Roman.ttf.woff2" ]
						},
						{
							"fontFamily": "Source Serif Pero",
							"fontWeight": "200 900",
							"fontStyle": "italic",
							"fontStretch": "normal",
							"src": [ "file:./assets/fonts/SourceSerif4Variable-Italic.ttf.woff2" ]
						}
					]
				}
			],

How to Test

How to Test on the front-end

  1. Activate TT2
  2. Load the site's home page in the browser.
  3. In the browser, right click to open the dev tools UI and then select "Inspect" menu item.
  4. In the HTML inspector, open the <head> element to view all of the nodes within the head.
  5. Search for the <style> element that has an ID of wp-webfonts-inline-css.
  6. Open that element.

Expected Results:
The <head> should have one instance of the following HTML style element:

<style id="wp-webfonts-inline-css">
@font-face{font-family:"Source Serif Pro";font-style:normal;font-weight:200 900;font-display:fallback;src:local("Source Serif Pro"), url('/wp-content/themes/twentytwentytwo/assets/fonts/source-serif-pro/SourceSerif4Variable-Roman.ttf.woff2') format('woff2');font-stretch:normal;}
@font-face{font-family:"Source Serif Pro";font-style:italic;font-weight:200 900;font-display:fallback;src:local("Source Serif Pro"), url('/wp-content/themes/twentytwentytwo/assets/fonts/source-serif-pro/SourceSerif4Variable-Italic.ttf.woff2') format('woff2');font-stretch:normal;}
</style>

This element should have

  • 2 @font-face declarations to match what was defined in the theme.json file.
  • The url should no longer have the file:./ placeholder in it.

How to Test in the back-end

  1. Go to "Appearance" > Themes and make sure TT2 is activated.
  2. Go to "Appearance" > Editor (beta) to load the site editor.
  3. In the browser, right click to open the dev tools UI and then select "Inspect" menu item.
  4. In the HTML inspector, open the <head> element to view all of the nodes within the head.
  5. Search for the <style> element that has an ID of wp-block-library-inline-css. Open that element (see Expected Results for the CSS).
  6. In the Network tab, select "Disable Cache" and "Fonts".
  7. Refresh the page. Notice the font file(s) that are loaded.

Expected Results:
The <head> should have one instance of the following HTML style element:

<style id="wp-block-library-inline-css">
@font-face{font-family:"Source Serif Pro";font-style:normal;font-weight:200 900;font-display:fallback;src:local("Source Serif Pro"), url('/wp-content/themes/twentytwentytwo/assets/fonts/source-serif-pro/SourceSerif4Variable-Roman.ttf.woff2') format('woff2');font-stretch:normal;}@font-face{font-family:"Source Serif Pro";font-style:italic;font-weight:200 900;font-display:fallback;src:local("Source Serif Pro"), url('/wp-content/themes/twentytwentytwo/assets/fonts/source-serif-pro/SourceSerif4Variable-Italic.ttf.woff2') format('woff2');font-stretch:normal;}
</style>

This element should have

  • 2 @font-face declarations to match what was defined in the theme.json file.
  • The url should no longer have the file:./ placeholder in it.

Design Considerations

  • To enable testing, this PR comments out loading the Webfonts API files in the lib/load.php file. In doing so, this makes the PHPUnit tests fail as those files are missing.

  • A mechanism is needed in Gutenberg to continue developing, testing, and maturing the Webfonts API.

    • This PR currently hooks _wp_theme_json_webfonts_handler() into 'wp_plugins' action event, giving Gutenberg's plugin the opportunity to remove that callback and load its files.
    • Another way to accomplish this is to create a short-circuit within the container. In doing that, the container could be an unnamed static closure registered to the 'wp_plugins' action event. Doing this would eliminate deprecating the function once the Webfonts API is merged into Core.

@hellofromtonya hellofromtonya marked this pull request as ready for review April 20, 2022 23:14
Comment on lines +119 to +128

/*
* DO NOT MERGE THIS CHANGE.
* Don't load the Webfonts API.
require __DIR__ . '/experimental/register-webfonts-from-theme-json.php';
require __DIR__ . '/experimental/class-wp-webfonts.php';
require __DIR__ . '/experimental/class-wp-webfonts-provider.php';
require __DIR__ . '/experimental/class-wp-webfonts-provider-local.php';
require __DIR__ . '/experimental/webfonts.php';
*/
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a temporary workaround for testers to test this PR. Once the handler is backported to Core, then (in a separate PR), remove_action( 'plugins_loaded', '_wp_theme_json_webfonts_handler' ); can be added to turn off the stopgate handler in Core and use the Webfonts API.

@hellofromtonya
Copy link
Contributor Author

@peterwilsoncc @desrosj This PR is using the concepts we talked about. What do you think?

@kjellr @jffng I tested it using the TT2 alternative style variations PR. Can you please test it too and share feedback?

@peterwilsoncc
Copy link
Contributor

@hellofromtonya I'm getting a fatal error attempting to test this:

Fatal error: Uncaught Error: Call to undefined function gutenberg_add_registered_webfonts_to_theme_json() in /vagrant/content/plugins/gutenberg/lib/experimental/class-wp-theme-json-resolver-gutenberg.php on line 39

My config is:

I tried excluding class-wp-theme-json-resolver-gutenberg.php in the load file but that just caused a different fatal error.

Copy link
Contributor

@costdev costdev left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Currently unable to test due to internet troubles, so in the meantime, here's some suggestions for docblocks, polyfill usage and additional validation.

I'll get testing when I'm back on a stable connection.

lib/experimental/class-theme-json-webfonts-handler.php Outdated Show resolved Hide resolved
lib/experimental/class-theme-json-webfonts-handler.php Outdated Show resolved Hide resolved
lib/experimental/class-theme-json-webfonts-handler.php Outdated Show resolved Hide resolved
lib/experimental/class-theme-json-webfonts-handler.php Outdated Show resolved Hide resolved
lib/experimental/class-theme-json-webfonts-handler.php Outdated Show resolved Hide resolved
lib/experimental/class-theme-json-webfonts-handler.php Outdated Show resolved Hide resolved
lib/experimental/class-theme-json-webfonts-handler.php Outdated Show resolved Hide resolved
lib/experimental/class-theme-json-webfonts-handler.php Outdated Show resolved Hide resolved
lib/experimental/class-theme-json-webfonts-handler.php Outdated Show resolved Hide resolved
lib/experimental/class-theme-json-webfonts-handler.php Outdated Show resolved Hide resolved
@peterwilsoncc
Copy link
Contributor

I'm getting a fatal error attempting to test this:

I resolved this by applying this diff, I think it's correct to limit everything to the stopgap only.

diff --git a/lib/experimental/class-wp-theme-json-resolver-gutenberg.php b/lib/experimental/class-wp-theme-json-resolver-gutenberg.php
index 960ea659e8..119489e665 100644
--- a/lib/experimental/class-wp-theme-json-resolver-gutenberg.php
+++ b/lib/experimental/class-wp-theme-json-resolver-gutenberg.php
@@ -36,14 +36,14 @@ class WP_Theme_JSON_Resolver_Gutenberg extends WP_Theme_JSON_Resolver_6_0 {
 		if ( null === static::$theme ) {
 			$theme_json_data = static::read_json_file( static::get_file_path_from_theme( 'theme.json' ) );
 			$theme_json_data = static::translate( $theme_json_data, wp_get_theme()->get( 'TextDomain' ) );
-			$theme_json_data = gutenberg_add_registered_webfonts_to_theme_json( $theme_json_data );
+			// $theme_json_data = gutenberg_add_registered_webfonts_to_theme_json( $theme_json_data );
 			static::$theme   = new WP_Theme_JSON_Gutenberg( $theme_json_data );
 
 			if ( wp_get_theme()->parent() ) {
 				// Get parent theme.json.
 				$parent_theme_json_data = static::read_json_file( static::get_file_path_from_theme( 'theme.json', true ) );
 				$parent_theme_json_data = static::translate( $parent_theme_json_data, wp_get_theme()->parent()->get( 'TextDomain' ) );
-				$parent_theme_json_data = gutenberg_add_registered_webfonts_to_theme_json( $parent_theme_json_data );
+				// $parent_theme_json_data = gutenberg_add_registered_webfonts_to_theme_json( $parent_theme_json_data );
 				$parent_theme           = new WP_Theme_JSON_Gutenberg( $parent_theme_json_data );
 
 				// Merge the child theme.json into the parent theme.json.

@costdev
Copy link
Contributor

costdev commented Apr 21, 2022

Tested using the instructions in the PR's description.

Results:

  • 2 @font-face declarations to match what was defined in the theme.json file. ✅
  • The url should no longer have the file:./ placeholder in it. ✅

Will continue testing.


For others testing by making the manual changes to functions.php and theme.json in TT2, note that you may get a 404 error in console for the font files.

If so, move:

assets/fonts/SourceSerif4Variable-Roman.ttf.woff2 (and other SourceSerif font files)

to

assets/fonts/source-serif-pro/SourceSerif4Variable-Roman.ttf.woff2

Copy link
Contributor

@peterwilsoncc peterwilsoncc left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've added a few notes inline.

I tried forcing my way in to using the API from WP CLI (ie, not in theme.json) and wasn't able to find a reasonable way to do so.

Testing with the TT2 branch of WordPress-Develop shows what I'd expect in the source code.

lib/experimental/class-theme-json-webfonts-handler.php Outdated Show resolved Hide resolved
lib/experimental/class-theme-json-webfonts-handler.php Outdated Show resolved Hide resolved
lib/experimental/class-theme-json-webfonts-handler.php Outdated Show resolved Hide resolved
@hellofromtonya
Copy link
Contributor Author

hellofromtonya commented Apr 21, 2022

In reviewing with @costdev, an enterprising developer who really wants to gain access could use reflection to get access to within the container's class. This means using a class isn't truly non-accessible.

I've switched the design to use anonymous functions (closures) that are stored to variables within the container's scope (as originally suggested by @peterwilsoncc).

@hellofromtonya
Copy link
Contributor Author

@peterwilsoncc Thanks for spotting the fatal error. I had commented those 2 lines out in my testing environment but didn't include them in the PR.

Those 2 lines are now commented out in fb00786.

Copy link
Contributor

@jffng jffng left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👋

I gave this test using TT2's style variations.

The fonts are loading for each variation on the front-end. However in the site editor, only the fonts for the currently saved style variation load:

Kapture.2022-04-21.at.11.12.14.mp4

This issue was at one point fixed here but I'm not sure if that is still relevant to this PR's implementation.

@hellofromtonya
Copy link
Contributor Author

hellofromtonya commented Apr 21, 2022

Thanks @jffng for pointing that out. I pushed an update 8295f15 that brings PR #39886 and #40262 into this PR. Can you retest please?

@hellofromtonya
Copy link
Contributor Author

Whoops, sorry I missed the variable name changes. Fixed now in 8295f15.

@jffng
Copy link
Contributor

jffng commented Apr 21, 2022

Getting the following fatal:

Fatal error: Uncaught Error: Call to undefined method WP_Theme_JSON_Resolver::get_style_variations() in /Users/jeffreyong/Documents/THEMES/a8c-projects/gutenberg/lib/experimental/class-theme-json-webfonts-handler.php on line 47

@hellofromtonya
Copy link
Contributor Author

Hmm, that new method has been merged into Core (in trunk) WP_Theme_JSON_Resolver::get_style_variations() https://github.com/WordPress/wordpress-develop/blob/trunk/src/wp-includes/class-wp-theme-json-resolver.php#L472. I wonder if Gutenberg's Docker image isn't using trunk but instead is using 5.9 release.

I'll add a temporary check to load either Core's version or WP_Theme_JSON_Resolver_6_0 version to accommodate testers who use either testing environment.

@hellofromtonya
Copy link
Contributor Author

Hey @jffng, I switched to use Gutenberg's copy of what's in WP Core trunk 6fb945b. Can you retest please?

@costdev
Copy link
Contributor

costdev commented Apr 21, 2022

Getting the following fatal:

Fatal error: Uncaught Error: Call to undefined method WP_Theme_JSON_Resolver::get_style_variations() in /Users/jeffreyong/Documents/THEMES/a8c-projects/gutenberg/lib/experimental/class-theme-json-webfonts-handler.php on line 47

I got the same error and resolved it by changing the call from WP_Theme_JSON_Resolver::get_style_variations() to WP_Theme_JSON_Resolver_6_0::get_style_variations()

Edit: Ah, just saw the commit you linked to Tonya 😛

@jffng
Copy link
Contributor

jffng commented Apr 21, 2022

I switched to use Gutenberg's copy of what's in WP Core trunk 6fb945b. Can you retest please?

That did the trick, thank you! All variations and their respective fonts working in the site editor and front-end for me.

@bph
Copy link
Contributor

bph commented Apr 21, 2022

Tested it with TT2 and modifications according to above instructions today's modifications.

Font face as expected in the header / front end
Screen Shot 2022-04-21 at 12 18 55
Font face as expected in the header / front end
Screen Shot 2022-04-21 at 18 44 34

Font Family in dropdown for Typography was available (Styles, Site Title block)
Screen Shot 2022-04-21 at 13 56 06

@hellofromtonya hellofromtonya added the Backport to WP 6.7 Beta/RC Pull request that needs to be backported to the WordPress major release that's currently in beta label Apr 22, 2022
@costdev
Copy link
Contributor

costdev commented Apr 22, 2022

I've been testing with the design change to use anonymous functions as variables within the container. This appears to fully prevent direct interaction with the API. In other words, I can't find a way to use the API in a backwards-incompatible way.

LGTM 👍

@ndiego
Copy link
Member

ndiego commented Apr 25, 2022

Hi all, do we have an ETA on when this will be merged?

@hellofromtonya
Copy link
Contributor Author

Hi all, do we have an ETA on when this will be merged?

@ndiego before Beta 3's (April 26th) release party. Here's the backport PR in WP Core WordPress/wordpress-develop#2622.

pento pushed a commit to WordPress/wordpress-develop that referenced this pull request Apr 26, 2022
Adds `_wp_theme_json_webfonts_handler()` for handling `fontFace` declarations in a theme's `theme.json` file to generate the `@font-face` styles for both the editor and front-end.

Design notes:
* It is not a public API, but rather an internal, Core-only handler.
* It is a stopgap implementation that will be replaced when the public Webfonts API is introduced in Core.
* The code design is intentional, albeit funky, with the purpose of avoiding backwards-compatibility issues when the public Webfonts API is introduced in Core.
   * It hides the inter-workings.
   * Does not exposing API ins and outs for external consumption.
   * Only works for `theme.json`.
   * Does not provide registration or enqueuing access for plugins.

For more context on the decision to include this stopgap and the Webfonts API, see:
* Core's PR 40493 WordPress/gutenberg#40493
* Gutenberg's tracking issue 40472 WordPress/gutenberg#40472

Props aristath, hellofromTonya, peterwilsoncc, costdev, jffng, zieladam, gziolo, bph, jonoaldersonwp, desrosj.

See #55567, #46370.

git-svn-id: https://develop.svn.wordpress.org/trunk@53282 602fd350-edb4-49c9-b593-d223f7449a82
markjaquith pushed a commit to markjaquith/WordPress that referenced this pull request Apr 26, 2022
Adds `_wp_theme_json_webfonts_handler()` for handling `fontFace` declarations in a theme's `theme.json` file to generate the `@font-face` styles for both the editor and front-end.

Design notes:
* It is not a public API, but rather an internal, Core-only handler.
* It is a stopgap implementation that will be replaced when the public Webfonts API is introduced in Core.
* The code design is intentional, albeit funky, with the purpose of avoiding backwards-compatibility issues when the public Webfonts API is introduced in Core.
   * It hides the inter-workings.
   * Does not exposing API ins and outs for external consumption.
   * Only works for `theme.json`.
   * Does not provide registration or enqueuing access for plugins.

For more context on the decision to include this stopgap and the Webfonts API, see:
* Core's PR 40493 WordPress/gutenberg#40493
* Gutenberg's tracking issue 40472 WordPress/gutenberg#40472

Props aristath, hellofromTonya, peterwilsoncc, costdev, jffng, zieladam, gziolo, bph, jonoaldersonwp, desrosj.

See #55567, #46370.
Built from https://develop.svn.wordpress.org/trunk@53282


git-svn-id: http://core.svn.wordpress.org/trunk@52871 1a063a9b-81f0-0310-95a4-ce76da25c4cd
gMagicScott pushed a commit to gMagicScott/core.wordpress-mirror that referenced this pull request Apr 26, 2022
Adds `_wp_theme_json_webfonts_handler()` for handling `fontFace` declarations in a theme's `theme.json` file to generate the `@font-face` styles for both the editor and front-end.

Design notes:
* It is not a public API, but rather an internal, Core-only handler.
* It is a stopgap implementation that will be replaced when the public Webfonts API is introduced in Core.
* The code design is intentional, albeit funky, with the purpose of avoiding backwards-compatibility issues when the public Webfonts API is introduced in Core.
   * It hides the inter-workings.
   * Does not exposing API ins and outs for external consumption.
   * Only works for `theme.json`.
   * Does not provide registration or enqueuing access for plugins.

For more context on the decision to include this stopgap and the Webfonts API, see:
* Core's PR 40493 WordPress/gutenberg#40493
* Gutenberg's tracking issue 40472 WordPress/gutenberg#40472

Props aristath, hellofromTonya, peterwilsoncc, costdev, jffng, zieladam, gziolo, bph, jonoaldersonwp, desrosj.

See #55567, #46370.
Built from https://develop.svn.wordpress.org/trunk@53282


git-svn-id: https://core.svn.wordpress.org/trunk@52871 1a063a9b-81f0-0310-95a4-ce76da25c4cd
@hellofromtonya
Copy link
Contributor Author

Closing this PR. Why?

Thank you everyone for contributing!

@hellofromtonya hellofromtonya removed the Backport to WP 6.7 Beta/RC Pull request that needs to be backported to the WordPress major release that's currently in beta label Apr 26, 2022
}

// Bail out early if there are no settings for webfonts.
if ( empty( $settings['typography'] ) || empty( $settings['typography']['fontFamilies'] ) ) {
Copy link
Member

@oandregal oandregal Apr 27, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Font families can be defined at the top-level, as in:

{
  "settings": {
      "typography": {
          "fontFamilies": []
      }
  }
}

But also can be defined per block, as in:

{
  "settings": {
      "typography": {
          "fontFamilies": [] // Fonts for all.
      },
      "blocks": {
          "some/block": {
              "typography": {
                  "fontFamilies": [] // A block can define its own.
              }
          }
      }
  }
}

Is this problematic for webfonts? How can a webfont be defined for a single block?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this problematic for webfonts? How can a webfont be defined for a single block?

@oandregal Top-level only in this first version.

I remember a discussion thread (not sure where it happened) where @mtias indicated not having block-level web font selection. Also see #40362.

@hellofromtonya hellofromtonya deleted the try/stopgap-themejson-webfonts branch August 9, 2022 21:36
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
No open projects
Archived in project
Development

Successfully merging this pull request may close these issues.

Move webfonts handling to internal only (not accessible) - WP 6.0 Stopgap
7 participants