Skip to content

Commit

Permalink
Use option AllowInvalidSelectors to allow non-standard pseudo element…
Browse files Browse the repository at this point in the history
… selectors
  • Loading branch information
matherm-aboehm committed Oct 19, 2023
1 parent 719679d commit 3df8be9
Show file tree
Hide file tree
Showing 3 changed files with 60 additions and 5 deletions.
54 changes: 52 additions & 2 deletions src/ExCSS.Tests/SelectorsTests.cs
Expand Up @@ -52,10 +52,60 @@ public async Task FindAllStyleRulesWithCompoundSelector()
Assert.Equal(7, list.Count());
}

private async Task<Stylesheet> ParseBootstrapAsync()
private static readonly string[] _standardPseudoElementNames = new[] {
PseudoElementNames.After,
PseudoElementNames.Before,
PseudoElementNames.Content,
PseudoElementNames.FirstLetter,
PseudoElementNames.FirstLine,
PseudoElementNames.Selection
};

public static string[] StandardPseudoElementNames
{
get
{
return _standardPseudoElementNames;
}
}

[Theory]
[InlineData(false, false, 277)]
[InlineData(false, true, 0)]
[InlineData(true, false, 277)]
[InlineData(true, true, 6)]
public async Task FindAllStandardPseudoElementSelectors(bool allowInvalidSelectors,
bool nonStandard,
int expectedCount)
{
// Arrange
var sheet = await ParseBootstrapAsync(allowInvalidSelectors);

// Act
var list = sheet.StyleRules
.Where(r => HasStandardPseudoElementSelector(r.Selector, negate: nonStandard));

// Assert
Assert.Equal(expectedCount, list.Count());
}

private static bool HasStandardPseudoElementSelector(ISelector selector, bool negate = false)
{
if (selector is PseudoElementSelector pes)
return negate ^ StandardPseudoElementNames.Contains(pes.Name);
else if (selector is CompoundSelector comp)
return HasStandardPseudoElementSelector(comp.Last(), negate);
else if (selector is ListSelector list)
return list.Any(s => HasStandardPseudoElementSelector(s, negate));
else if (selector is ComplexSelector complex)
return HasStandardPseudoElementSelector(complex.Last().Selector, negate);
return false;
}

private async Task<Stylesheet> ParseBootstrapAsync(bool tolerateInvalidSelectors = false)
{
await using var stream = GetStream("bootstrap.css");
var parser = new StylesheetParser();
var parser = new StylesheetParser(tolerateInvalidSelectors: tolerateInvalidSelectors);
return await parser.ParseAsync(stream);
}

Expand Down
9 changes: 7 additions & 2 deletions src/ExCSS/Factories/PseudoElementSelectorFactory.cs
Expand Up @@ -9,6 +9,8 @@ public sealed class PseudoElementSelectorFactory
private static readonly Lazy<PseudoElementSelectorFactory> Lazy =
new(() => new PseudoElementSelectorFactory());

private readonly StylesheetParser _parser;

#region Selectors

private readonly Dictionary<string, ISelector> _selectors =
Expand All @@ -27,15 +29,18 @@ public sealed class PseudoElementSelectorFactory

#endregion

private PseudoElementSelectorFactory()
internal PseudoElementSelectorFactory(StylesheetParser parser = null)
{
_parser = parser;
}

internal static PseudoElementSelectorFactory Instance => Lazy.Value;

public ISelector Create(string name)
{
return _selectors.TryGetValue(name, out var selector) ? selector : null;
return _selectors.TryGetValue(name, out var selector) ? selector :
((_parser?.Options.AllowInvalidSelectors ?? false) ?
PseudoElementSelector.Create(name) : null);
}
}
}
2 changes: 1 addition & 1 deletion src/ExCSS/Parser/StylesheetParser.cs
Expand Up @@ -98,7 +98,7 @@ internal SelectorConstructor GetSelectorCreator()
{
var attributeSelector = AttributeSelectorFactory.Instance;
var pseudoClassSelector = PseudoClassSelectorFactory.Instance;
var pseudoElementSelector = PseudoElementSelectorFactory.Instance;
var pseudoElementSelector = new PseudoElementSelectorFactory(this);
return Pool.NewSelectorConstructor(attributeSelector, pseudoClassSelector, pseudoElementSelector);
}

Expand Down

0 comments on commit 3df8be9

Please sign in to comment.