Skip to content

Commit 21fd3d1

Browse files
committed
Merge branch '2.2-develop' of https://github.com/magento/magento2ce into MAGETWO-99655
2 parents 70c47cf + ef1ad60 commit 21fd3d1

File tree

20 files changed

+363
-41
lines changed

20 files changed

+363
-41
lines changed
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
<?php
2+
/**
3+
* Copyright © Magento, Inc. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
7+
declare(strict_types=1);
8+
9+
namespace Magento\Theme\Block\Html\Header;
10+
11+
use Magento\Framework\View\Element\Block\ArgumentInterface;
12+
use Magento\Framework\View\Asset\Repository;
13+
use Magento\Framework\View\Asset\File\NotFoundException;
14+
15+
/**
16+
* This ViewModel will add inline critical css in case dev/css/use_css_critical_path is enabled.
17+
*/
18+
class CriticalCss implements ArgumentInterface
19+
{
20+
/**
21+
* @var Repository
22+
*/
23+
private $assetRepo;
24+
25+
/**
26+
* @var string
27+
*/
28+
private $filePath;
29+
30+
/**
31+
* @param Repository $assetRepo
32+
* @param string $filePath
33+
*/
34+
public function __construct(
35+
Repository $assetRepo,
36+
string $filePath = ''
37+
) {
38+
$this->assetRepo = $assetRepo;
39+
$this->filePath = $filePath;
40+
}
41+
42+
/**
43+
* Returns critical css data as string.
44+
*
45+
* @return bool|string
46+
*/
47+
public function getCriticalCssData()
48+
{
49+
try {
50+
$asset = $this->assetRepo->createAsset($this->filePath, ['_secure' => 'false']);
51+
$content = $asset->getContent();
52+
} catch (NotFoundException $e) {
53+
$content = '';
54+
}
55+
56+
return $content;
57+
}
58+
}
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
<?php
2+
/**
3+
* Copyright © Magento, Inc. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
declare(strict_types=1);
7+
8+
namespace Magento\Theme\Controller\Result;
9+
10+
use Magento\Framework\App\Config\ScopeConfigInterface;
11+
use Magento\Store\Model\ScopeInterface;
12+
use Magento\Framework\App\Response\Http;
13+
14+
/**
15+
* Plugin for asynchronous CSS loading.
16+
*/
17+
class AsyncCssPlugin
18+
{
19+
const XML_PATH_USE_CSS_CRITICAL_PATH = 'dev/css/use_css_critical_path';
20+
21+
/**
22+
* @var ScopeConfigInterface
23+
*/
24+
private $scopeConfig;
25+
26+
/**
27+
* @param ScopeConfigInterface $scopeConfig
28+
*/
29+
public function __construct(ScopeConfigInterface $scopeConfig)
30+
{
31+
$this->scopeConfig = $scopeConfig;
32+
}
33+
34+
/**
35+
* Load CSS asynchronously if it is enabled in configuration.
36+
*
37+
* @param Http $subject
38+
* @return void
39+
*/
40+
public function beforeSendResponse(Http $subject)
41+
{
42+
$content = $subject->getContent();
43+
44+
if (\is_string($content) && strpos($content, '</body') !== false && $this->scopeConfig->isSetFlag(
45+
self::XML_PATH_USE_CSS_CRITICAL_PATH,
46+
ScopeInterface::SCOPE_STORE
47+
)) {
48+
$cssMatches = [];
49+
// add link rel preload to style sheets
50+
$content = preg_replace_callback(
51+
'@<link\b.*?rel=("|\')stylesheet\1.*?/>@',
52+
function ($matches) use (&$cssMatches) {
53+
$cssMatches[] = $matches[0];
54+
preg_match('@href=("|\')(.*?)\1@', $matches[0], $hrefAttribute);
55+
$href = $hrefAttribute[2];
56+
if (preg_match('@media=("|\')(.*?)\1@', $matches[0], $mediaAttribute)) {
57+
$media = $mediaAttribute[2];
58+
}
59+
$media = $media ?? 'all';
60+
$loadCssAsync = sprintf(
61+
'<link rel="preload" as="style" media="%s" .
62+
onload="this.onload=null;this.rel=\'stylesheet\'"' .
63+
'href="%s">',
64+
$media,
65+
$href
66+
);
67+
68+
return $loadCssAsync;
69+
},
70+
$content
71+
);
72+
73+
if (!empty($cssMatches)) {
74+
$content = str_replace('</body', implode("\n", $cssMatches) . "\n</body", $content);
75+
$subject->setContent($content);
76+
}
77+
}
78+
}
79+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
<?xml version="1.0"?>
2+
<!--
3+
/**
4+
* Copyright © Magento, Inc. All rights reserved.
5+
* See COPYING.txt for license details.
6+
*/
7+
-->
8+
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Config:etc/system_file.xsd">
9+
<system>
10+
<section id="dev">
11+
<group id="css">
12+
<field id="use_css_critical_path" translate="label comment" type="select" sortOrder="30" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1">
13+
<label>Use CSS critical path</label>
14+
<source_model>Magento\Config\Model\Config\Source\Yesno</source_model>
15+
<comment>CSS files are loading synchronously by default.</comment>
16+
</field>
17+
</group>
18+
</section>
19+
</system>
20+
</config>

app/code/Magento/Theme/etc/config.xml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,9 @@ Disallow: /*SID=
6666
<static>
6767
<sign>1</sign>
6868
</static>
69+
<css>
70+
<use_css_critical_path>0</use_css_critical_path>
71+
</css>
6972
</dev>
7073
</default>
7174
</config>

app/code/Magento/Theme/etc/frontend/di.xml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,4 +26,12 @@
2626
<type name="Magento\Framework\Controller\ResultInterface">
2727
<plugin name="result-messages" type="Magento\Theme\Controller\Result\MessagePlugin"/>
2828
</type>
29+
<type name="Magento\Framework\App\Response\Http">
30+
<plugin name="asyncCssLoad" type="Magento\Theme\Controller\Result\AsyncCssPlugin"/>
31+
</type>
32+
<type name="Magento\Theme\Block\Html\Header\CriticalCss">
33+
<arguments>
34+
<argument name="filePath" xsi:type="string">css/critical.css</argument>
35+
</arguments>
36+
</type>
2937
</config>

app/code/Magento/Theme/view/frontend/layout/default.xml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,9 @@
102102
</container>
103103
</referenceContainer>
104104
<referenceContainer name="main">
105-
<container name="content.top" label="Main Content Top"/>
105+
<container name="content.top" label="Main Content Top">
106+
<block name="main_css_preloader" as="main_css_preloader" template="Magento_Theme::html/main_css_preloader.phtml" ifconfig="dev/css/use_css_critical_path"/>
107+
</container>
106108
<container name="content" label="Main Content Area"/>
107109
<container name="content.aside" label="Main Content Aside"/>
108110
<container name="content.bottom" label="Main Content Bottom"/>

app/code/Magento/Theme/view/frontend/layout/default_head_blocks.xml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,14 @@
1212
<script src="requirejs/require.js"/>
1313
</head>
1414
<body>
15+
<referenceBlock name="head.additional">
16+
<block name="critical_css_block" as="critical_css" template="Magento_Theme::html/header/criticalCss.phtml" ifconfig="dev/css/use_css_critical_path">
17+
<arguments>
18+
<argument name="criticalCssViewModel" xsi:type="object">Magento\Theme\Block\Html\Header\CriticalCss</argument>
19+
</arguments>
20+
</block>
21+
<block name="css_rel_preload_script" ifconfig="dev/css/use_css_critical_path" template="Magento_Theme::js/css_rel_preload.phtml"/>
22+
</referenceBlock>
1523
<referenceContainer name="after.body.start">
1624
<block class="Magento\Framework\View\Element\Template" name="head.polyfill" as="polyfill" template="Magento_Theme::js/polyfill.phtml" before="-"/>
1725
<block class="Magento\Framework\View\Element\Js\Components" name="head.components" as="components" template="Magento_Theme::js/components.phtml" before="-"/>

app/code/Magento/Theme/view/frontend/requirejs-config.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,8 @@ var config = {
2828
'popupWindow': 'mage/popup-window',
2929
'validation': 'mage/validation/validation',
3030
'welcome': 'Magento_Theme/js/view/welcome',
31-
'breadcrumbs': 'Magento_Theme/js/view/breadcrumbs'
31+
'breadcrumbs': 'Magento_Theme/js/view/breadcrumbs',
32+
'criticalCssLoader': 'Magento_Theme/js/view/critical-css-loader'
3233
}
3334
},
3435
paths: {
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
<?php
2+
/**
3+
* Copyright © Magento, Inc. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
7+
/**
8+
* @var \Magento\Theme\Block\Html\Header\CriticalCss $criticalCssViewModel
9+
*/
10+
?>
11+
<?php $criticalCssViewModel = $block->getData('criticalCssViewModel'); ?>
12+
13+
<style type="text/css" data-type="criticalCss">
14+
<?= /* @noEscape */ $criticalCssViewModel->getCriticalCssData() ?>
15+
</style>
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<?php
2+
/**
3+
* Copyright © Magento, Inc. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
?>
7+
<div data-role="main-css-loader" class="loading-mask">
8+
<div class="loader">
9+
<img src="<?= $block->escapeUrl($block->getViewFileUrl('images/loader-1.gif')); ?>"
10+
alt="<?= $block->escapeHtml(__('Loading...')); ?>"
11+
style="position: absolute;">
12+
</div>
13+
</div>

0 commit comments

Comments
 (0)