diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index a9114fe089e41..23653f2a47ce8 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -593,6 +593,9 @@ importers: webpack: 5.65.0_webpack-cli@4.9.1 webpack-cli: 4.9.1_webpack@5.65.0 + projects/packages/global-styles-webfonts-introspector: + specifiers: {} + projects/packages/google-fonts-provider: specifiers: {} diff --git a/projects/packages/global-styles-webfonts-introspector/.gitattributes b/projects/packages/global-styles-webfonts-introspector/.gitattributes new file mode 100644 index 0000000000000..9f76af3f06e7e --- /dev/null +++ b/projects/packages/global-styles-webfonts-introspector/.gitattributes @@ -0,0 +1,16 @@ +# Files not needed to be distributed in the package. +.gitattributes export-ignore +.github/ export-ignore +package.json export-ignore + +# Files to include in the mirror repo, but excluded via gitignore +# Remember to end all directories with `/**` to properly tag every file. +# /src/js/example.min.js production-include + +# Files to exclude from the mirror repo, but included in the monorepo. +# Remember to end all directories with `/**` to properly tag every file. +.gitignore production-exclude +changelog/** production-exclude +phpunit.xml.dist production-exclude +.phpcs.dir.xml production-exclude +tests/** production-exclude diff --git a/projects/packages/global-styles-webfonts-introspector/.gitignore b/projects/packages/global-styles-webfonts-introspector/.gitignore new file mode 100644 index 0000000000000..140fd587d2d52 --- /dev/null +++ b/projects/packages/global-styles-webfonts-introspector/.gitignore @@ -0,0 +1,2 @@ +vendor/ +node_modules/ diff --git a/projects/packages/global-styles-webfonts-introspector/.phpcs.dir.xml b/projects/packages/global-styles-webfonts-introspector/.phpcs.dir.xml new file mode 100644 index 0000000000000..c01a67f3899b0 --- /dev/null +++ b/projects/packages/global-styles-webfonts-introspector/.phpcs.dir.xml @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/projects/packages/global-styles-webfonts-introspector/CHANGELOG.md b/projects/packages/global-styles-webfonts-introspector/CHANGELOG.md new file mode 100644 index 0000000000000..721294abd00ad --- /dev/null +++ b/projects/packages/global-styles-webfonts-introspector/CHANGELOG.md @@ -0,0 +1,7 @@ +# Changelog + +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + diff --git a/projects/packages/global-styles-webfonts-introspector/README.md b/projects/packages/global-styles-webfonts-introspector/README.md new file mode 100644 index 0000000000000..274918e4764aa --- /dev/null +++ b/projects/packages/global-styles-webfonts-introspector/README.md @@ -0,0 +1,20 @@ +# global-styles-webfonts-introspector + +Looks for webfonts picked in global styles + +## How to install global-styles-webfonts-introspector + +### Installation From Git Repo + +## Contribute + +## Get Help + +## Security + +Need to report a security vulnerability? Go to [https://automattic.com/security/](https://automattic.com/security/) or directly to our security bug bounty site [https://hackerone.com/automattic](https://hackerone.com/automattic). + +## License + +global-styles-webfonts-introspector is licensed under [GNU General Public License v2 (or later)](./LICENSE.txt) + diff --git a/projects/packages/global-styles-webfonts-introspector/changelog/.gitkeep b/projects/packages/global-styles-webfonts-introspector/changelog/.gitkeep new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/projects/packages/global-styles-webfonts-introspector/changelog/update-instrospect-google-fonts-instead-of-enqueueing-them-all b/projects/packages/global-styles-webfonts-introspector/changelog/update-instrospect-google-fonts-instead-of-enqueueing-them-all new file mode 100644 index 0000000000000..17403e499ea97 --- /dev/null +++ b/projects/packages/global-styles-webfonts-introspector/changelog/update-instrospect-google-fonts-instead-of-enqueueing-them-all @@ -0,0 +1,4 @@ +Significance: minor +Type: added + +Add Google fonts global styles introspection package. diff --git a/projects/packages/global-styles-webfonts-introspector/composer.json b/projects/packages/global-styles-webfonts-introspector/composer.json new file mode 100644 index 0000000000000..b807927221641 --- /dev/null +++ b/projects/packages/global-styles-webfonts-introspector/composer.json @@ -0,0 +1,44 @@ +{ + "name": "automattic/jetpack-global-styles-webfonts-introspector", + "description": "Looks for webfonts picked in global styles", + "type": "jetpack-library", + "license": "GPL-2.0-or-later", + "require": {}, + "require-dev": { + "yoast/phpunit-polyfills": "1.0.3", + "automattic/jetpack-changelogger": "^3.0" + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "scripts": { + "phpunit": [ + "./vendor/phpunit/phpunit/phpunit --colors=always" + ], + "test-coverage": [ + "php -dpcov.directory=. ./vendor/bin/phpunit --coverage-clover \"$COVERAGE_DIR/clover.xml\"" + ], + "test-php": [ + "@composer phpunit" + ] + }, + "repositories": [ + { + "type": "path", + "url": "../../packages/*", + "options": { + "monorepo": true + } + } + ], + "minimum-stability": "dev", + "prefer-stable": true, + "extra": { + "branch-alias": { + "dev-master": "0.1.x-dev" + }, + "textdomain": "jetpack-global-styles-webfonts-introspector" + } +} diff --git a/projects/packages/global-styles-webfonts-introspector/package.json b/projects/packages/global-styles-webfonts-introspector/package.json new file mode 100644 index 0000000000000..bd2f649e683e4 --- /dev/null +++ b/projects/packages/global-styles-webfonts-introspector/package.json @@ -0,0 +1,29 @@ +{ + "private": true, + "name": "@automattic/jetpack-global-styles-webfonts-introspector", + "version": "0.1.0-alpha", + "description": "Looks for webfonts picked in global styles", + "homepage": "https://jetpack.com", + "bugs": { + "url": "https://github.com/Automattic/jetpack/issues" + }, + "repository": { + "type": "git", + "url": "https://github.com/Automattic/jetpack.git" + }, + "license": "GPL-2.0-or-later", + "author": "Automattic", + "scripts": { + "build": "echo 'Not implemented.'", + "build-js": "echo 'Not implemented.'", + "build-production": "echo 'Not implemented.'", + "build-production-js": "echo 'Not implemented.'", + "clean": "true" + }, + "devDependencies": {}, + "engines": { + "node": "^14.18.3 || ^16.13.2", + "pnpm": "^6.32.3", + "yarn": "use pnpm instead - see docs/yarn-upgrade.md" + } +} diff --git a/projects/packages/global-styles-webfonts-introspector/phpunit.xml.dist b/projects/packages/global-styles-webfonts-introspector/phpunit.xml.dist new file mode 100644 index 0000000000000..3223c32458db2 --- /dev/null +++ b/projects/packages/global-styles-webfonts-introspector/phpunit.xml.dist @@ -0,0 +1,14 @@ + + + + tests/php + + + + + + + src + + + diff --git a/projects/packages/global-styles-webfonts-introspector/src/class-global-styles-webfonts-introspector.php b/projects/packages/global-styles-webfonts-introspector/src/class-global-styles-webfonts-introspector.php new file mode 100644 index 0000000000000..78a4458186ecd --- /dev/null +++ b/projects/packages/global-styles-webfonts-introspector/src/class-global-styles-webfonts-introspector.php @@ -0,0 +1,90 @@ +.+)\)$/', $font_family, $matches ); + + if ( isset( $matches['slug'] ) ) { + return $matches['slug']; + } + + // Full string: var:preset|font-family|slug + // We do not care about the origin of the font, only its slug. + preg_match( '/font-family\|(?P.+)$/', $font_family, $matches ); + + if ( isset( $matches['slug'] ) ) { + return $matches['slug']; + } + + return $font_family; + } + } + + /** + * Introspect global styles for webfonts. + */ + public static function introspect_global_styles_webfonts() { + if ( ! function_exists( 'gutenberg_get_global_styles' ) ) { + return; + } + + $global_styles = gutenberg_get_global_styles(); + + $found_webfonts = array(); + + // Look for fonts in block presets... + if ( isset( $global_styles['blocks'] ) ) { + foreach ( $global_styles['blocks'] as $setting ) { + $font_slug = self::extract_font_slug_from_setting( $setting ); + + if ( $font_slug ) { + $found_webfonts[ $font_slug ] = 1; + } + } + } + + // Look for fonts in HTML element presets... + if ( isset( $global_styles['elements'] ) ) { + foreach ( $global_styles['elements'] as $setting ) { + $font_slug = self::extract_font_slug_from_setting( $setting ); + + if ( $font_slug ) { + $found_webfonts[ $font_slug ] = 1; + } + } + } + + // Check if a global typography setting was defined. + $font_slug = self::extract_font_slug_from_setting( $global_styles ); + + if ( $font_slug ) { + $found_webfonts[ $font_slug ] = 1; + } + + return array_keys( $found_webfonts ); + } + +} diff --git a/projects/packages/global-styles-webfonts-introspector/tests/php/bootstrap.php b/projects/packages/global-styles-webfonts-introspector/tests/php/bootstrap.php new file mode 100644 index 0000000000000..46763b04a2cdb --- /dev/null +++ b/projects/packages/global-styles-webfonts-introspector/tests/php/bootstrap.php @@ -0,0 +1,11 @@ + $font_family, - 'font-weight' => '100 900', - 'font-style' => 'normal', - 'font-display' => 'fallback', - 'provider' => 'jetpack-google-fonts', - ), - array( - 'font-family' => $font_family, - 'font-weight' => '100 900', - 'font-style' => 'italic', - 'font-display' => 'fallback', - 'provider' => 'jetpack-google-fonts', - ), - ) - ); - } -} -add_action( 'after_setup_theme', 'jetpack_add_google_fonts_provider' ); - -add_filter( 'wp_resource_hints', '\Automattic\Jetpack\Fonts\Utils::font_source_resource_hint', 10, 2 ); +require __DIR__ . '/google-fonts/jetpack-google-fonts.php'; diff --git a/projects/plugins/jetpack/modules/google-fonts/class-jetpack-google-fonts.php b/projects/plugins/jetpack/modules/google-fonts/class-jetpack-google-fonts.php new file mode 100644 index 0000000000000..70a342cfa7c55 --- /dev/null +++ b/projects/plugins/jetpack/modules/google-fonts/class-jetpack-google-fonts.php @@ -0,0 +1,152 @@ +register_google_fonts_provider(); + + add_action( 'after_setup_theme', array( $this, 'register_google_fonts' ) ); + + /** + * We are already enqueueing all registered fonts by default when loading the block editor, + * so we only need to scan for webfonts when browsing as a guest. + */ + if ( ! is_admin() ) { + add_action( 'wp_loaded', array( $this, 'enqueue_global_styles_google_fonts' ) ); + add_filter( 'pre_render_block', array( $this, 'scan_block_for_google_fonts' ), 10, 2 ); + } + } + + /** + * Register the provider for Google fonts registration. + */ + private function register_google_fonts_provider() { + if ( ! function_exists( 'wp_register_webfont_provider' ) ) { + return; + } + + wp_register_webfont_provider( 'jetpack-google-fonts', '\Automattic\Jetpack\Fonts\Google_Fonts_Provider' ); + } + + /** + * Register Google fonts. + */ + public function register_google_fonts() { + if ( ! function_exists( 'wp_register_webfonts' ) ) { + return; + } + + /** + * Curated list of Google Fonts. + * + * @module google-fonts + * + * @since 10.8 + * + * @param array $fonts_to_register Array of Google Font names to register. + */ + $fonts_to_register = apply_filters( 'jetpack_google_fonts_list', JETPACK_GOOGLE_FONTS_LIST ); + + foreach ( $fonts_to_register as $font_family ) { + $font_family_slugs = wp_register_webfonts( + array( + array( + 'font-family' => $font_family, + 'font-weight' => '100 900', + 'font-style' => 'normal', + 'font-display' => 'fallback', + 'provider' => 'jetpack-google-fonts', + ), + array( + 'font-family' => $font_family, + 'font-weight' => '100 900', + 'font-style' => 'italic', + 'font-display' => 'fallback', + 'provider' => 'jetpack-google-fonts', + ), + ) + ); + + if ( empty( $font_family_slugs ) ) { + // Fonts were not registered. + continue; + } + + /** + * As we're registering faces for the same font family, + * let's just pick the first one as they must be equal. + */ + $font_family_slug = $font_family_slugs[0]; + + /** + * When introspecting, all we have is the slug, + * so we need to keep track of the registered families + * to check whether the font was filtered after + * running the `jetpack_google_fonts_list` hook. + */ + $this->registered_google_fonts[ $font_family_slug ] = $font_family; + } + } + + /** + * Scan block for Google fonts. + * + * @param string $content The block content. + * @param array $parsed_block The parsed block attributes. + * + * @return string The block content. + */ + public function scan_block_for_google_fonts( $content, $parsed_block ) { + if ( isset( $parsed_block['attrs']['fontFamily'] ) ) { + $this->maybe_enqueue_font_family( $parsed_block['attrs']['fontFamily'] ); + } + + return $content; + } + + /** + * Enqueue a font family if it was not removed from the curated list. + * + * @param string $font_family_slug The block content. + */ + private function maybe_enqueue_font_family( $font_family_slug ) { + if ( ! isset( $this->registered_google_fonts[ $font_family_slug ] ) ) { + // Font not allow-listed. + return; + } + + wp_enqueue_webfont( $font_family_slug ); + } + + /** + * Enqueue Google fonts used in global styles. + */ + public function enqueue_global_styles_google_fonts() { + $webfonts_found = Global_Styles_Webfonts_Introspector::introspect_global_styles_webfonts(); + + foreach ( $webfonts_found as $font_family_slug ) { + $this->maybe_enqueue_font_family( $font_family_slug ); + } + } +} diff --git a/projects/plugins/jetpack/modules/google-fonts/jetpack-google-fonts.php b/projects/plugins/jetpack/modules/google-fonts/jetpack-google-fonts.php new file mode 100644 index 0000000000000..c0bd12067cbd1 --- /dev/null +++ b/projects/plugins/jetpack/modules/google-fonts/jetpack-google-fonts.php @@ -0,0 +1,13 @@ +