Skip to content

CSS file is deferred when CSS element has defer attribute in default_head_blocks.xml. #37814

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

Open
wants to merge 13 commits into
base: 2.4-develop
Choose a base branch
from

Conversation

nurullah
Copy link

@nurullah nurullah commented Jul 28, 2023

Description (*)

While I searching for ways to defer non-critical CSS files, I found that magento2 has a defer attribute in the head.xsd file. This means I can write the following code in the default_head_blocks.xml without any errors.

<css src="css/intl-tel-input.css" defer="true" />

But I realized that this defer attribute has no effect on the CSS element. Because HTML link element doesn't have defer attribute. It only works with the <script> element. This pull request aims to provide defer feature for CSS files with defer non-critical render-blocking CSS hacky method.

Related Pull Requests

  1. Fixes [View] Removed extra space in link and script tag #32919

Fixed Issues (if relevant)

  1. Fixes What is the purpose of the defer attribute in the linkType elements (css, link, font)? #37657

Manual testing scenarios (*)

  1. Create/Open default_head_blocks.xml file and add a CSS element like below;
<css src="css/intl-tel-input.css" defer="true" />
  1. Flush cache (bin/magento cache:flush)
  2. Open your Magento2 site in the browser and press ctrl + u combination.
  3. See that the CSS file is defined as deferred like below.
<link rel="preload" type="text/css" as="style" onload="this.onload=null;this.rel=&#039;stylesheet&#039;" href="https://magento.test/static/version1690544175/frontend/ExampleVendor/base/tr_TR/css/intl-tel-input.css" />
<noscript><link rel="stylesheet" href="https://magento.test/static/version1690544175/frontend/ExampleVendor/base/tr_TR/css/intl-tel-input.css"></noscript>
  1. Disable Javascript in site settings on the browser. Be sure that the css file loaded properly.

Questions or comments

I am not entirely sure that these changes are correct and I don't know if it will affect other code. I tested it on my browser and it works fine. Please review my code and let me know if I made any mistakes.

Contribution checklist (*)

  • Pull request has a meaningful description of its purpose
  • All commits are accompanied by meaningful commit messages
  • All new or changed code is covered with unit/integration tests (if applicable)
  • README.md files for modified modules are updated and included in the pull request if any README.md predefined sections require an update
  • All automated tests passed successfully (all builds are green)

@m2-assistant
Copy link

m2-assistant bot commented Jul 28, 2023

Hi @nurullah. Thank you for your contribution!
Here are some useful tips on how you can test your changes using Magento test environment.

Add the comment under your pull request to deploy test or vanilla Magento instance:
  • @magento give me test instance - deploy test instance based on PR changes
  • @magento give me 2.4-develop instance - deploy vanilla Magento instance

❗ Automated tests can be triggered manually with an appropriate comment:

  • @magento run all tests - run or re-run all required tests against the PR changes
  • @magento run <test-build(s)> - run or re-run specific test build(s)
    For example: @magento run Unit Tests

<test-build(s)> is a comma-separated list of build names.

Allowed build names are:
  1. Database Compare
  2. Functional Tests CE
  3. Functional Tests EE
  4. Functional Tests B2B
  5. Integration Tests
  6. Magento Health Index
  7. Sample Data Tests CE
  8. Sample Data Tests EE
  9. Sample Data Tests B2B
  10. Static Tests
  11. Unit Tests
  12. WebAPI Tests
  13. Semantic Version Checker

You can find more information about the builds here
ℹ️ Run only required test builds during development. Run all test builds before sending your pull request for review.


For more details, review the Code Contributions documentation.
Join Magento Community Engineering Slack and ask your questions in #github channel.

@mrtuvn mrtuvn added Area: Perf/Frontend All tickets related with improving frontend performance. Area: Framework Priority: P2 A defect with this priority could have functionality issues which are not to expectations. labels Jul 29, 2023
@mrtuvn
Copy link
Contributor

mrtuvn commented Jul 29, 2023

Seem good fix inmy opinion. Can you able to add some automate test cover new changes

@mrtuvn mrtuvn added the Auto-Tests: Not Covered Changes in Pull Request requires coverage by auto-tests label Jul 29, 2023
@mrtuvn
Copy link
Contributor

mrtuvn commented Jul 30, 2023

lib/internal/Magento/Framework/View/Test/Unit/Page/Config/RendererTest.php
Unit test stay in here

* @return string
*/
protected function getAssetTemplate($contentType, $attributes)
{
$attributesString = '';
foreach ($attributes as $name => $value) {
$attributesString .= ' ' . $name . '="' . $this->escaper->escapeHtml($value) . '"';
Copy link
Contributor

Choose a reason for hiding this comment

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

Should be escapeHtmlAttr instead of escapeHtml I think?


And in the case of the onload one, it should be escapeJs inside a escapeHtmlAttr according to the docs:

Case: All JavaScript inside attributes must be escaped by escapeJs before escapeHtmlAttr:

Copy link
Author

@nurullah nurullah Aug 4, 2023

Choose a reason for hiding this comment

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

The escapeHtmlAttr function escapes slash character and it looks like the below;

image

It's pretty useful for body element attributes but not for head element attributes. Also, before this pull request, the same escapeHtml function was used for the attribute value.

foreach ($attributes as $name => $value) {
$attributesString .= ' ' . $name . '="' . $this->escaper->escapeHtml($value) . '"';
}

Copy link
Contributor

Choose a reason for hiding this comment

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

Thanks for the feedback! I know too little about this, would be great if somebody with a little bit of security knowledge could double check this here then.

Copy link
Contributor

Choose a reason for hiding this comment

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

@nathanjosiah: if you have a little bit of time, do you have some input here?

Copy link
Contributor

@nathanjosiah nathanjosiah Aug 7, 2023

Choose a reason for hiding this comment

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

If the main issue is that escapeHtmlAttr escaped all chars then that is correct behavior even for head.

For example:

<html>
<head>
<style id="foo&#x2F;bar"></style>
</head>
<body>
Test succeeds if this is green
<script>document.getElementById('foo/bar').innerHTML = 'body { color: green; }';</script>
</body>
</html>

The text would be green.

Here is a small demo that shows this off.

Copy link
Contributor

@nathanjosiah nathanjosiah Sep 26, 2023

Choose a reason for hiding this comment

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

escapeHtmlAttr is appropriate for all attributes including script attributes. You would only need to combine escapeJs with escapeHtmlAttr if there is additional content being injected into the JS. For example from our devdocs

<div
    onclick="<?= $escaper->escapeHtmlAttr('handler("' . $escaper->escapeJs($aString) . '", ' . $escaper->escapeJs($aVar) .')') ?>">
    My DIV
</div>

As far as I can see, this PR is using the escaper methods correctly.

Copy link
Author

Choose a reason for hiding this comment

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

Looks like this PR is still waiting for your review @hostep. If there are no other problems, can you take this to the next step? Thanks.

Copy link
Contributor

Choose a reason for hiding this comment

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

I don't have a lot of expert knowledge on the subject but since somebody forced me to review this (I wasn't able to unassign myself), I approved it anyways.

Copy link
Member

@sky-hub sky-hub Dec 5, 2023

Choose a reason for hiding this comment

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

Please be advised that because of too much escaping we end up with this in page source:

image

Copy link
Contributor

@hostep hostep Dec 26, 2023

Choose a reason for hiding this comment

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

@sky-hub: and how is that a problem? A browser should be able to decode those without an issue. It's like writing &gt; or &lt; in the html source to display > or <. In this case &#x2F; translates to / for example. It's redundant and will increase the payload size a bit sure, but it shouldn't break anything.

@nurullah
Copy link
Author

nurullah commented Aug 4, 2023

Seem good fix inmy opinion. Can you able to add some automate test cover new changes

I added unit test for defer attribute.

@magento run Unit Tests

@magento-automated-testing
Copy link

The requested builds are added to the queue. You should be able to see them here within a few minutes. Please message the #magento-devops slack channel if they don't show in a reasonable amount of time and a representative will look into any issues.

@nurullah
Copy link
Author

nurullah commented Aug 4, 2023

I opened the unit tests report but I didn't see any error. All 3 reports detail shows RendererTest.php passed the test. But unit tests failed surprisingly. So, I restart the unit test.

image

@magento run Unit Tests

@hostep hostep removed their assignment Aug 23, 2023
@nurullah
Copy link
Author

@magento run all tests

@magento-automated-testing
Copy link

The requested builds are added to the queue. You should be able to see them here within a few minutes. Please message the #magento-devops slack channel if they don't show in a reasonable amount of time and a representative will look into any issues.

@nurullah
Copy link
Author

@magento run all Unit Tests, Functional Tests

@magento-automated-testing
Copy link

Failed to run the builds. Please try to re-run them later.

@nurullah
Copy link
Author

@magento run all Unit Tests, Static Tests

@magento-automated-testing
Copy link

The requested builds are added to the queue. You should be able to see them here within a few minutes. Please message the #magento-devops slack channel if they don't show in a reasonable amount of time and a representative will look into any issues.

@nurullah
Copy link
Author

@magento run all Unit Tests

@magento-automated-testing
Copy link

Failed to run the builds. Please try to re-run them later.

@nurullah
Copy link
Author

@magento run all tests

@magento-automated-testing
Copy link

The requested builds are added to the queue. You should be able to see them here within a few minutes. Please message the #magento-devops slack channel if they don't show in a reasonable amount of time and a representative will look into any issues.

@nurullah
Copy link
Author

@magento run Functional Tests CE, Functional Tests EE, Functional Tests B2B

@magento-automated-testing
Copy link

The requested builds are added to the queue. You should be able to see them here within a few minutes. Please message the #magento-devops slack channel if they don't show in a reasonable amount of time and a representative will look into any issues.

@hostep hostep removed their assignment Sep 26, 2023
@nurullah
Copy link
Author

@magento run all tests

Copy link

The requested builds are added to the queue. You should be able to see them here within a few minutes. Please message the #magento-devops slack channel if they don't show in a reasonable amount of time and a representative will look into any issues.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Area: Framework Area: Perf/Frontend All tickets related with improving frontend performance. Priority: P3 May be fixed according to the position in the backlog. Progress: review
Projects
None yet
Development

Successfully merging this pull request may close these issues.

What is the purpose of the defer attribute in the linkType elements (css, link, font)?
5 participants