Skip to content

Inserting child in self-closing element instead inserts element as sibling #27

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
4 of 5 tasks
heinrich-ulbricht opened this issue Feb 18, 2025 · 2 comments
Open
4 of 5 tasks
Labels
bug Something isn't working help wanted Extra attention is needed

Comments

@heinrich-ulbricht
Copy link

heinrich-ulbricht commented Feb 18, 2025

Bug Report

Prerequisites

  • Can you reproduce the problem in a MWE?
  • Are you running the latest version of AngleSharp?
  • Did you check the FAQs to see if that helps you?
  • Are you reporting to the correct repository? (there are multiple AngleSharp libraries, e.g., AngleSharp.Css for CSS support)
  • Did you perform a search in the issues?

For more information, see the CONTRIBUTING guide.

Description

I have a self-closing element and now want to add a child element.

Steps to Reproduce

Here's a complete repo:

[TestMethod]
public void TestInsertChildToSelfClosingElementSimple()
{
    var xml =
    """
    <ac:structured-macro ac:name="children"/>
    """;

    var config = Configuration.Default.With(HtmlEntityProvider.Resolver).WithDefaultLoader(new LoaderOptions { IsResourceLoadingEnabled = true }).WithCss().WithXml();
    var context = BrowsingContext.New(config);
    var parser = new XmlParser(new XmlParserOptions(), context);

    var doc = parser.ParseDocument(xml);
    var parentElement = doc.DocumentElement;
    Assert.IsNotNull(parentElement);
    Assert.AreEqual(NodeFlags.SelfClosing, parentElement.Flags);

    var child = doc.CreateElement("ac:parameter");
    child.SetAttribute("ac:name", "dummy");
    parentElement.AppendChild(child);

    var parentElementXml = parentElement.ToXml();
    // it seems to be impossible to insert something into a self-closing element!? this test is here to document this fact
    Assert.AreEqual("""<ac:structured-macro ac:name="children" /><ac:parameter ac:name="dummy"></ac:parameter>""", parentElementXml);
}

Expected behavior: I expect the ac:parameter element to become a child element of the parent element. The outcome should look like this: <ac:structured-macro ac:name="children"><ac:parameter ac:name="dummy"></ac:parameter></ac:structured-macro>.

Actual behavior: The ac:parameter element is inserted as sibling instead: <ac:structured-macro ac:name="children" /><ac:parameter ac:name="dummy"></ac:parameter>.

Environment details: .NET 8, Windows 11 && Debian

Possible Solution

Cloning

I'd be fine with cloning the element if I could specify if the clone should be self-closing, or not. Basically optionally introduce the behavior that was once fixed here: #17 :D

I tried finding some specs that describe if it is valid to "transform" a self-closing element back to having children, but couldn't find anything. Cloning might introduce a compliant workaround if any specs prohibit changing the self-closing flag.

Changing the Flag

Maybe it is as easy as allowing to change the Flags property. Not sure about the consequences, though.

@heinrich-ulbricht heinrich-ulbricht added the bug Something isn't working label Feb 18, 2025
@FlorianRappl
Copy link
Contributor

Could be.

But I will most likely not have time to look into this in the near future. Contributions welcome.

@FlorianRappl FlorianRappl added the help wanted Extra attention is needed label Feb 18, 2025
@heinrich-ulbricht
Copy link
Author

I resorted to this extension method to toggle the SelfClosing flag off:

public static void AsSelfClosing(this IElement element, bool selfClosing)
{
    const uint SelfClosing = 0x1;

    var type = typeof(IElement).Assembly.GetType("AngleSharp.Dom.Node");
    var field = type?.GetField("_flags", BindingFlags.Instance | BindingFlags.NonPublic);

    if (null == field)
    {
        return;
    }

    var flags = (uint)field.GetValue(element)!;
    if (selfClosing)
    {
        flags |= SelfClosing;
    } else 
    {
        flags &= ~SelfClosing;

    }
    field.SetValue(element, Enum.ToObject(field.FieldType, flags));
}

I found AsSelfClosing in a FAQ of yours and added the bool to choose whether it's on or off.

Toggling this flag is the key.

For a proper solution the following decisions need to be made:

  • Should the SelfClosing flag auto-clear implicitly on insert/append operations?
  • If not: Where is the right place to introduce an explicit clear operation? In Clone? As a separate method on IElement?

Currently, I don't have the knowledge to make good decisions.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working help wanted Extra attention is needed
Projects
None yet
Development

No branches or pull requests

2 participants