CSS parsing library for C# based on Mozilla Firefox code [MPL]
https://github.com/Athari/CsCss
CsCss is a CSS Parser written in C#, based on Mozilla Firefox 22 code.
At the time of writing, it is the only C# CSS Parser capable of parsing all varieties of CSS supported by modern browsers. However, the choice of the library is not clear-cut, as some important features are missing. See below for details.
The library should be considered alpha-quality, as most of its features are not tested neither by tests no in real world. If you notice any issues, please report them!
-moz-*
) are supported too.FullStandards
(default) and Quirks
(units are optional etc.).background
property is converted into multiple longhand properties, one of which, background-image
, is represented by a list of values corresponding to multiple backgrounds; each value in the list can be a URL or a gradient structure; in case of a gradient it contains a list of gradient stops.TraceSource
named Alba.CsCss.CssParser
and are reported as event on CssLoader.ParsingError
.CssLoader
.Two URLs are specified: the first one is used for logging puposes; the second one is used as a base for resolving relative url()
expressions.
CssStyleSheet css = new CssLoader().ParseSheet("h1, h2 { color: #123; }",
new Uri("http://example.com/sheet.css"), new Uri("http://example.com/"));
Console.WriteLine(css.SheetUri); // http://example.com/sheet.css
// Get color property (equivalent code)
Console.WriteLine(css.StyleRules.Single().Declaration.Color.Color.R); // 17
Console.WriteLine(css.Rules.OfType<CssStyleRule>().Single().Declaration
.GetValue(CssProperty.Color).Color.R); // 17
// Get h1 selector
Console.WriteLine(css.StyleRules.Single().SelectorGroups.First().Selectors.Single().Tag);
This method will not analyze wether CSS file is valid, it will merely find all url()
expressions.
List<string> uris = new CssLoader().GetUris(source).ToList();
This method will extract URLs only from valid CSS expressions. It most closely matches behavior of web browsers.
CssStyleSheet css = new CssLoader().ParseSheet(source, sheetUri, baseUri);
// Get rules of CssStyleRule type on all levels (including style rules inside media rules)
List<string> uris = css.AllStyleRules
// Get property-value pairs, both non-important and important (marked with !important)
.SelectMany(styleRule => styleRule.Declaration.AllData)
// A property can be a list of values (background-image, for example, contains a list of URLs)
.SelectMany(prop => prop.Value.Unit == CssUnit.List ? prop.Value.List : new[] { prop.Value })
// Filter values of CssUrlValue type
.Where(val => val.Unit == CssUnit.Url)
// Get unresolved URLs (you can use Uri property to get resolved URLs)
.Select(val => val.OriginalUri)
.ToList();
Install using NuGet:
PM> Install-Package Alba.CsCss
Build from sources:
Alba.CsCss/Alba.CsCss.csproj
project to your solution.The major part of the library is ported by applying numerous regular expressions to original C++ code. C++ code and T4 files with regular expressions are included in the project (but are not included in the compiled DLL). The plan is to keep the code maintainable — when new version of Firefox is released, only minor modifications should be necessary, without trying to match converted code with new original code. Time will tell wether the idea is good.
Only parts which are expected to update regularly are converted with regular expressions. Parts of code which are considered relatively stable (style sheets, value types etc.) or are unpractical to convert with regex (unions, operator new overriding etc.), are rewritten manually.
Currently the biggest file (nsCSSParser.cpp
/CssParser.conv.cs
) containing around 10000 lines is converted using 400 lines of regular expressions (4%). Statistics for small files are worse. It helps a lot that Mozilla naming conventions for variables are very strict and pointer arithmetic is not heavily used. However, almost all public classes had to be renamed, as well as most enum values, to comply with C# coding standards.
Projects:
Alba.CsCss
— the library itself. Has no dependencies except .NET. If library is included in another solution, only this project is necessary.Alba.CsCss.Tests
— "unit" tests (more like integration tests). Very few.Alba.Framework
— personal framework. Debug version of the assembly must be built to be able to run T4 files. Necessary only if you want to modify source code transformations.Alba.Framework.CodeGeneration
— T4 part of the personal framework. Must be built under Administrator account (installs AttachT4
custom tool similar to T4 Toolbox's T4ScriptFileGenerator
). Necessary if you want T4 transformations to be applied when original source files are updated, or want ot add new source files.Alba.Framework.Testing
— testing part of the personal framework. Used in tests.TL;DR: As long as you include copyright notice, you can do whatever you want, but any source code file licensed under MPL must remain under MPL and freely available in source form.
CSS icon by David Vignoni.