Skip to content
New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

Documentation: Document how to add a new CSS property #3564

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
63 changes: 63 additions & 0 deletions Documentation/CSSProperties.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
# Adding or Modifying a CSS Property

There are several different places you need to make changes in order to add or modify a CSS property.
These are listed below in the order that Ladybird deals with them, starting at parsing and ending with them being used.

## Data

The first place you will need to go to is `CSS/Properties.json`. This file contains the definition for each
property, and is used to generate the `PropertyID` enum and a selection of functions. You may also need to
modify `CSS/Keywords.json` and `CSS/Enums.json`. See [CSSGeneratedFiles.md](CSSGeneratedFiles.md) for details.

## Parsing

For many properties, there is no need to add custom parsing code. Properties that take a single value, or shorthands
that are a list of their longhand properties, will be parsed automatically using the data in `Properties.json`.
However, there are many CSS properties with more complicated grammar and so they require custom parsing.

Property-parsing code goes in `CSS/Parser/PropertyParsing.cpp`, and `CSS/Parser/Parser.h`. First,
`Parser::parse_css_value()` is called, which has a switch for specific properties. Call your method from there. It
should return a `RefPtr` to a `CSSStyleValue` or one of its subclasses.

For shorthands, you should normally use `ShorthandStyleValue`, which automatically expands its longhand values. You
might need to modify `ShorthandStyleValue::to_string` if your shorthand has special serialization rules. For example,
`border-radius` serializes with a `/` separating the horizontal and vertical components.

If your property's value can't be represented with an existing type, you might need to add a new style value class.
If you need to do this, pester @AtkinsSJ until he gets around to documenting it. ;^)
Copy link
Contributor

Choose a reason for hiding this comment

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

Maybe something about custom serialization rules here?

Copy link
Member Author

Choose a reason for hiding this comment

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

I'm not 100% sure what you mean, but I've added an example of a custom serialization rule, does that work?


## Computed style

After parsing and style computation, longhand properties are stored as `CSSStyleValue` pointers in
`ComputedProperties`. Any shorthands have been expanded out, and so we do not need to store them directly.

These longhands then need to be converted to a more usable form. To do this, add a getter to `ComputedProperties` with
the same name as the property. It should return a type that holds the value in a compact form. Be aware that anything
involving numbers or dimensions may be a calculation, so store it in one of the `FooOrCalculated` types.

Then, `CSS/ComputedValues.h` contains three classes that are relevant:
- `ComputedValues` holds the computed value of each property, in a flat format. Depending on whether the property is
inherited or not, it needs adding to the `m_inherited` or `m_noninherited` structs, with a corresponding getter.
- `MutableComputedValues` also needs a setter for the value.
- `InitialValues` has a getter for the default value of the property. This isn't always needed, for example if the
default computed value is an empty `Optional` or `Vector`.

Style is copied from `ComputedProperties` to `ComputedValues` in `NodeWithStyle::apply_style()`. Each property is
copied individually.

Then, read the value of your property with that `ComputedValues` getter we added. For example, this code reads the
computed values of `visibility` and `opacity`:

```c++
bool Paintable::is_visible() const
{
auto const& computed_values = this->computed_values();
return computed_values.visibility() == CSS::Visibility::Visible && computed_values.opacity() != 0;
}
```

## JavaScript

Some properties have special rules for getting the computed value from JS. For these, you will need to add to
`ResolvedCSSStyleDeclaration::style_value_for_property()`. Shorthands that are constructed in an unusual way (as in, not
using `ShorthandStyleValue`) also need handling inside `CSSStyleDeclaration::get_property_internal()`.
1 change: 1 addition & 0 deletions Documentation/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ you are welcome to ask on [Discord](../README.md#get-in-touch-and-participate).
* [General Architecture](ProcessArchitecture.md)
* [LibWeb: From Loading to Painting](LibWebFromLoadingToPainting.md)
* [LibWeb: Browsing Contexts and Navigables](BrowsingContextsAndNavigables.md)
* [How to Add a CSS Property](CSSProperties.md)
* [How to Add an IDL File](AddNewIDLFile.md)
* [LibWeb Code Style & Patterns](LibWebPatterns.md)
* [CSS Generated Files](CSSGeneratedFiles.md)
Loading