The filter "Convert line breaks into HTML (i.e. <br> and <p>)" turns line breaks inside <li> tags into unmatched closing </p> tags

Created on 11 April 2024, over 1 year ago
Updated 23 August 2025, 27 days ago

When line breaks are included inside <li> tags, unmatched closing </p> tags are generated.

Reproduce steps:
1.Go to /admin/config/content/formats/full_html, Enable filter "Convert line breaks into HTML (i.e. < br > and < p >)" under section "Enabled filters"
2.Add below html to node's body field with text format "Full HTML" and save

<ul><li>Here is a list Item.</li>
<li>Here is another list item, this time containing a line break which is converted by the filter to a br tag.
This is some more text followed by two line breaks, which the filter converts to a p tag.

And now some more text.</li>
</ul>

Expect:

  • Here is a list Item.
  • Here is another list item, this time containing a line break which is converted by the filter to a br tag.
    Then some more text followed by two line breaks, which the filter converts to a p tag.

    And now some more text.

What happened instead?
We're getting an extra </p> tag as follows:

<ul>
<li>Here is a list Item.</li>
<li>Here is another list item, this time containing a line break which is converted by the filter to a br tag.<br />
Then some more text followed by two line breaks, which the filter converts to a p tag.</p>
<p>And now some more text.</li>
</ul>

Repeat the experiement with unclosed <li> tags, and two unclosed <p> tags are generated, as follows:

<ul>
<li>Here is a list Item.</p>
<li>Here is another list item, this time containing a line break which is converted by the filter to a br tag.<br />
This is some more text followed by two line breaks, which the filter converts to a p tag.</p>
<p>And now some more text.
</ul>
🐛 Bug report
Status

Needs work

Version

11.0 🔥

Component
Filter 

Last updated 23 days ago

No maintainer
Created by

🇬🇧United Kingdom John_B London (UK), Worthing (UK), Innsbruck (Tirol)

Live updates comments and jobs are added and updated live.
  • Needs tests

    The change is currently missing an automated test that fails when run with the original code, and succeeds when the bug has been fixed.

  • Needs issue summary update

    Issue summaries save everyone time if they are kept up-to-date. See Update issue summary task instructions.

Sign in to follow issues

Merge Requests

Comments & Activities

  • Issue created by @John_B
  • 🇸🇪Sweden peter törnstrand

    peter törnstrand made their first commit to this issue’s fork.

  • 🇸🇪Sweden peter törnstrand

    Was able to reproduce this with core 11.2.3. Fix attached.

    Markup after fix applied:

    <ul>
    <li>Here is a list Item.</li>
    <li><p>Here is another list item, this time containing a line break which is converted by the filter to a br tag.<br />
    This is some more text followed by two line breaks, which the filter converts to a p tag.</p>
    <p>And now some more text.</p></li>
    </ul>
    
  • 🇺🇸United States smustgrave

    Don't see anything to review

    Was previously tagged for summary update and tests too so those will need to be completed.

  • 🇸🇪Sweden peter törnstrand

    Sorry, forgot MR. Will have a look at creating a test. Question, can I include the test in the same MR?

  • 🇸🇪Sweden peter törnstrand

    I'm working on writing a test for this.

  • 🇸🇪Sweden peter törnstrand

    So this was a bit more complex than I imagined as the test requires a call to the global function _filter_autop(). I looked at the documentation at https://www.drupal.org/docs/automated-testing/phpunit-in-drupal/unit-tes... but I am not sure I got this right. I tried looking at how this is solved in other core modules and found what I believe is relevant examples in system/tests/fixtures.

    I also posted in the Drupal Slack channel #contribute but have not gotten any answers yet.

    I did however have lots of fun doing this.

  • Pipeline finished with Failed
    24 days ago
    #581928
  • Pipeline finished with Failed
    24 days ago
    Total: 126s
    #581932
  • Pipeline finished with Failed
    24 days ago
    Total: 124s
    #581933
  • 🇸🇪Sweden peter törnstrand

    From @berdir in #contribute slack channel

    we want to move all functions to classes anyway. with underscored ones, it's a bit unclear if we want to provide BC or not, but you could play it safe and add a deprecated message. and then depending on where it is used, move it to the class, or a trait or static method.

    My gut feeling was to put _filter_autop() in the Drupal\filter\Plugin\Filter\FilterAutoP class .. perhaps directly in the ::process() method.

    And then just add a deprecation note on _filter_autop() in filter.module:

    @deprecated in drupal:11.x.x and is removed from drupal:12.x.x. Use Drupal\filter\Plugin\FilterAutoP::process() instead.

  • 🇺🇸United States smustgrave

    I did however have lots of fun doing this.

    That's awesome!

    But looking at the MR not sure we need fixtures but instead could use a data provider.

    No hard rule of thumb but I typically only see fixtures used in migrations or update hooks

  • 🇸🇪Sweden peter törnstrand

    Yeah, I tried having the input/output directly in the test case but it was completely unreadable. Concatenated strings with lots of PHP_EOL. It was difficult to spot and fix errors caused by formatting.

    I'm using a data provider but the provider reads the input/output from the fixture files.

    Regarding the function _filter_autop() in a separate file; that was just something i noticed in the System module.

    Should I move the input/output into the test case again?

    And perhaps I should go with @berdir's input and move the _filter_autop() to the FilterAutoP plugin and deprecate the global function?

  • 🇺🇸United States smustgrave

    Think moving the function is out of scope of this issue.

    There's already a kernel test checking this function testLineBreakFilter, would just look into expanding that test.

  • 🇸🇪Sweden peter törnstrand

    Yes. That is probably true. I will have a look at the test you mentioned.

  • Pipeline finished with Failed
    24 days ago
    Total: 149s
    #582231
  • 🇸🇪Sweden peter törnstrand

    Wish I had found that test before. Was a minor fix adding a test for this scenario. Well, I learned something at least.

  • The Needs Review Queue Bot tested this issue. It fails the Drupal core commit checks. Therefore, this issue status is now "Needs work".

    This does not mean that the patch necessarily needs to be re-rolled or the MR rebased. Read the Issue Summary, the issue tags and the latest discussion here to determine what needs to be done.

    Consult the Drupal Contributor Guide to find step-by-step guides for working with issues.

  • Pipeline finished with Failed
    23 days ago
    Total: 719s
    #582354
  • Pipeline finished with Failed
    7 days ago
    Total: 696s
    #596391
  • Pipeline finished with Success
    6 days ago
    Total: 588s
    #597340
Production build 0.71.5 2024