Skip to content
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
51 changes: 51 additions & 0 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
name: CI

on:
push:
branches: ["main"]
pull_request:
workflow_dispatch:
merge_group:
types: [checks_requested]


jobs:
linux-debug:
name: Linux (Debug)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install Rust
uses: dtolnay/rust-toolchain@stable
- name: Run Tests
run: cargo build --features servo
env:
RUST_BACKTRACE: 1

linux-release:
name: Linux (Release)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install Rust
uses: dtolnay/rust-toolchain@stable
- name: Run Tests
run: cargo build --release --features servo
env:
RUST_BACKTRACE: 1

build-result:
name: Result
runs-on: ubuntu-latest
if: ${{ always() }}
needs:
- linux-debug
- linux-release
steps:
- name: Success
if: ${{ !contains(needs.*.result, 'failure') && !contains(needs.*.result, 'cancelled') }}
run: exit 0
- name: Failure
if: ${{ contains(needs.*.result, 'failure') || contains(needs.*.result, 'cancelled') }}
run: exit 1

26 changes: 26 additions & 0 deletions .github/workflows/mirror-to-release-branch.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
name: 🪞 Mirror `main`
on:
push:
branches:
- main

jobs:
mirror:
name: Mirror
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Get branch name
id: branch-name
run: |
first_commit=$(git log --pretty=\%H --grep='Servo initial downstream commit')
upstream_base="$first_commit~"
echo BRANCH_NAME=$(git log -n1 --pretty='%as' $upstream_base) >> $GITHUB_OUTPUT
- uses: google/[email protected]
name: Mirror to ${{ steps.branch-name.outputs.BRANCH_NAME }}
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
source: main
dest: ${{ steps.branch-name.outputs.BRANCH_NAME }}
23 changes: 23 additions & 0 deletions .github/workflows/sync-upstream.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
name: Sync upstream with mozilla-central

on:
schedule:
- cron: '0 13 * * *'
workflow_dispatch:

jobs:
sync:
name: Sync
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v3
with:
fetch-depth: 1
- uses: actions/cache@v3
with:
path: _cache/upstream
key: upstream
- run: |
./sync.sh _filtered
git fetch -f --progress ./_filtered main:upstream
git push -fu --progress origin upstream
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
/_cache/
/_filtered/
/target/
/style/properties/__pycache__/
Cargo.lock
18 changes: 18 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
[workspace]
resolver = "2"
members = [
"stylo_atoms",
"stylo_dom",
"malloc_size_of",
"rustfmt.toml",
"selectors",
"servo_arc",
"style",
"style_derive",
"stylo_config",
"stylo_static_prefs",
"style_traits",
"to_shmem",
"to_shmem_derive",
]
default-members = ["style"]
107 changes: 107 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
Stylo
=====

**High-Performance CSS Style Engine**

[![Build Status](https://github.com/servo/stylo/actions/workflows/main.yml/badge.svg)](https://github.com/servo/stylo/actions)
[![Crates.io](https://img.shields.io/crates/v/stylo.svg)](https://crates.io/crates/stylo)
[![Docs](https://docs.rs/stylo/badge.svg)](https://docs.rs/stylo)
![Crates.io License](https://img.shields.io/crates/l/stylo)

Stylo is a high-performance, browser-grade CSS style engine written in Rust that powers [Servo](https://servo.org) and [Firefox](https://firefox.com). This repo contains Servo’s downstream version of Stylo. The upstream version lives in mozilla-central with the rest of the Gecko/Firefox codebase.

Coordination of Stylo development happens:

- Here in Github Issues
- In the [#stylo](https://servo.zulipchat.com/#narrow/channel/417109-stylo) channel of the [Servo Zulip](https://servo.zulipchat.com/)
- In the [#layout](https://chat.mozilla.org/#/room/#layout:mozilla.org) room of the Mozilla Matrix instance (matrix.mozilla.org)

## High-Level Documentation

- This [Mozilla Hacks article](https://hacks.mozilla.org/2017/08/inside-a-super-fast-css-engine-quantum-css-aka-stylo) contains a high-level overview of the Stylo architecture.
- There is a [chapter](https://book.servo.org/architecture/style.html) in the Servo Book (although it is a little out of date).

## Branches

The branches are as follows:

- [**upstream**](https://github.com/servo/style/tree/upstream) has upstream [mozilla-central](https://searchfox.org/mozilla-central/source/servo) filtered to the paths we care about ([style.paths](style.paths)), but is otherwise unmodified.
- [**main**](https://github.com/servo/style/tree/ci) adds our downstream patches, plus the scripts and workflows for syncing with mozilla-central on top of **upstream**.

> [!WARNING]
> This repo syncs from upstream by creating a new branch and then rebasing our changes on top of it. This means that `git pull` will not work across syncs (you will need to use `git fetch`, `git reset` and `git rebase`).

More information on the syncing process is available in [SYNCING.md](SYNCING.md)

## Crates

A guide to the crates contained within this repo

### Stylo Crates

These crates are largely implementation details of Stylo, although you may need to use some of them directly if you use Stylo.

| Directory | Crate | Notes |
| --- | --- | --- |
| style | [![Crates.io](https://img.shields.io/crates/v/stylo.svg)](https://crates.io/crates/stylo) | The main Stylo crate containing the entire CSS engine |
| style_traits | [![Crates.io](https://img.shields.io/crates/v/stylo_traits.svg)](https://crates.io/crates/stylo_traits) | Types and traits which allow other code to interoperate with Stylo without depending on the main crate directly. |
| stylo_dom | [![Crates.io](https://img.shields.io/crates/v/stylo_dom.svg)](https://crates.io/crates/stylo_dom) | Similar to stylo_traits (but much smaller) |
| stylo_atoms | [![Crates.io](https://img.shields.io/crates/v/stylo_atoms.svg)](https://crates.io/crates/stylo_atoms) | [Atoms](https://docs.rs/string_cache/latest/string_cache/struct.Atom.html) for CSS and HTML event related strings |
| stylo_config | [![Crates.io](https://img.shields.io/crates/v/stylo_config.svg)](https://crates.io/crates/stylo_config) | Configuration for Stylo. Can be used to set runtime preferences (enabling/disabling properties, etc) |
| stylo_static_prefs | [![Crates.io](https://img.shields.io/crates/v/stylo_static_prefs.svg)](https://crates.io/crates/stylo_static_prefs) | Static configuration for Stylo. Config be overridden by patching in a replacement crate. |
| style_derive | [![Crates.io](https://img.shields.io/crates/v/stylo_derive.svg)](https://crates.io/crates/stylo_derive) | Internal derive macro for stylo crate |

### Standalone Crates

These crates form part of Stylo but are also be useful standalone.

| Directory | Crate | Notes |
| --- | --- | --- |
| selectors | [![Crates.io](https://img.shields.io/crates/v/selectors.svg)](https://crates.io/crates/selectors) | CSS Selector matching |
| servo_arc | [![Crates.io](https://img.shields.io/crates/v/servo_arc.svg)](https://crates.io/crates/servo_arc) | A variant on `std::Arc` |

You may also be interested in the `cssparser` crate which lives in the [servo/rust-cssparser](https://github.com/servo/rust-cssparser) repo.

### Support Crates

Low-level crates which could technically be used standalone but are unlikely to be generally useful in practice.

| Directory | Crate | Notes |
| --- | --- | --- |
| malloc_size_of | [![Crates.io](https://img.shields.io/crates/v/stylo_malloc_size_of.svg)](https://crates.io/crates/stylo_malloc_size_of) | Heap size measurement for Stylo values |
| to_shmem | [![Crates.io](https://img.shields.io/crates/v/to_shmem.svg)](https://crates.io/crates/to_shmem) | Internal utility crate for sharing memory across processes. |
| to_shmem_derive | [![Crates.io](https://img.shields.io/crates/v/to_shmem_derive.svg)](https://crates.io/crates/to_shmem_derive) | Internal derive macro for to_shmem crate |

## Building Servo Against a Local Copy of Stylo

Assuming your local `servo` and `stylo` directories are siblings, you can build `servo` against `stylo` by adding the following to `servo/Cargo.toml`:

```toml
[patch."https://github.com/servo/stylo"]
selectors = { path = "../stylo/selectors" }
servo_arc = { path = "../stylo/servo_arc" }
stylo_atoms = { path = "../stylo/stylo_atoms" }
stylo = { path = "../stylo/style" }
stylo_config = { path = "../stylo/stylo_config" }
stylo_dom = { path = "../stylo/stylo_dom" }
stylo_malloc_size_of = { path = "../stylo/malloc_size_of" }
stylo_traits = { path = "../stylo/style_traits" }
```

## Releases

Releases are made every time this repository rebases its changes on top of the latest version of upstream Stylo. There are a lot of crates here. In order to publish them, they must be done in order. One order that works is:

- selectors
- stylo_static_prefs
- stylo_config
- stylo_atoms
- stylo_malloc_size_of
- stylo_dom
- stylo_derive
- stylo_traits
- stylo

## License

Stylo is licensed under MPL 2.0
63 changes: 63 additions & 0 deletions SYNCING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
# Syncing

This file documents the process of syncing this repository with the upstream copy of Stylo in mozilla-central.

## Syncing `upstream` with mozilla-central

Start by generating a filtered copy of mozilla-central. This will cache the raw mozilla-central in `_cache/upstream`, storing the result in `_filtered`:

```sh
$ ./sync.sh _filtered
```

If `_filtered` already exists, you will need to delete it and try again:

```sh
$ rm -Rf _filtered
```

Now overwrite our `upstream` with those commits and push:

```sh
$ git fetch -f --progress ./_filtered main:upstream
$ git push -fu --progress origin upstream
```

## Rebasing `main` onto `upstream`

Start by fetching `upstream` into your local repo:

```sh
$ git fetch -f origin upstream:upstream
```

In general, the filtering process is deterministic, yielding the same commit hashes each time, so we can rebase normally:

```sh
$ git rebase upstream
```

But if the filtering config changes or Mozilla moves to GitHub, the commit hashes on `upstream` may change. In this case, we need to tell git where the old upstream ends and our own commits start (notice the `~`):

```sh
$ git log --pretty=\%H --grep='Servo initial downstream commit'
e62d7f0090941496e392e1dc91df103a38e3f488

$ git rebase --onto upstream e62d7f0090941496e392e1dc91df103a38e3f488~
Successfully rebased and updated refs/heads/main.
```

`start-rebase.sh` takes care of this automatically, but you should still use `git rebase` for subsequent steps like `--continue` and `--abort`:

```sh
$ ./start-rebase.sh upstream
$ ./start-rebase.sh upstream -i # interactive
$ git rebase --continue # not ./start-rebase.sh --continue
$ git rebase --abort # not ./start-rebase.sh --abort
```

Or if we aren’t ready to rebase onto the tip of upstream:

```sh
$ ./start-rebase.sh upstream~10 -i
```
15 changes: 15 additions & 0 deletions commit-from-merge.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
#!/bin/sh
# Usage: commit-from-merge.sh <path/to/servo/servo> <merge commit> [extra git-commit(1) arguments ...]
# Given a merge commit made by bors, runs git-commit(1) with your local changes
# while borrowing the author name/email from the right-hand parent of the merge,
# and the author date from the committer date of the merge.
set -eu

lookup_repo=$1; shift
merge_commit=$1; shift
author_name_email=$(git -C "$lookup_repo" log -n1 --pretty='%aN <%aE>' "$merge_commit"\^2)
committer_date=$(git -C "$lookup_repo" log -n1 --pretty='%cd' "$merge_commit")

set -- git commit --author="$author_name_email" --date="$committer_date" "$@"
echo "$@"
"$@"
14 changes: 14 additions & 0 deletions commit-from-squashed.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
#!/bin/sh
# Usage: commit-from-squashed.sh <squashed commit> [extra git-commit(1) arguments ...]
# Given a squashed commit made by the GitHub merge queue, runs git-commit(1) with your local changes
# while borrowing our author name/email from that commit, our author date from its committer date,
# and our commit message from that commit.
set -eu

squashed_commit=$1; shift
committer_date=$(git log -n1 --pretty='%cd' "$squashed_commit")

# -c is equivalent to --author=$(...'%aN <%aE>') -m $(...'%B'), but allows editing
set -- git commit -c "$squashed_commit" --date="$committer_date" "$@"
echo "$@"
"$@"
14 changes: 7 additions & 7 deletions malloc_size_of/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "malloc_size_of"
version = "0.0.1"
name = "stylo_malloc_size_of"
version = "0.5.0"
authors = ["The Servo Project Developers"]
license = "MIT OR Apache-2.0"
repository = "https://github.com/servo/stylo"
Expand All @@ -15,12 +15,12 @@ servo = ["string_cache"]

[dependencies]
app_units = "0.7"
cssparser = "0.34"
cssparser = "0.35"
euclid = "0.22"
selectors = { path = "../selectors" }
servo_arc = { path = "../servo_arc" }
selectors = { version = "0.30", path = "../selectors" }
servo_arc = { version = "0.4", path = "../servo_arc" }
smallbitvec = "2.3.0"
smallvec = "1.0"
smallvec = "1.13"
string_cache = { version = "0.8", optional = true }
thin-vec = { version = "0.2.1" }
thin-vec = { version = "0.2.13" }
void = "1.0.2"
6 changes: 3 additions & 3 deletions selectors/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "selectors"
version = "0.26.0"
version = "0.30.0"
authors = ["The Servo Project Developers"]
documentation = "https://docs.rs/selectors/"
description = "CSS Selectors matching for Rust"
Expand All @@ -21,15 +21,15 @@ to_shmem = ["dep:to_shmem", "dep:to_shmem_derive"]

[dependencies]
bitflags = "2"
cssparser = "0.34"
cssparser = "0.35"
derive_more = { version = "2", features = ["add", "add_assign"] }
fxhash = "0.2"
log = "0.4"
phf = "0.11"
precomputed-hash = "0.1"
servo_arc = { version = "0.4", path = "../servo_arc" }
smallvec = "1.0"
to_shmem = { version = "0.1", path = "../to_shmem", features = ["servo_arc"], optional = true }
to_shmem = { version = "0.2", path = "../to_shmem", features = ["servo_arc"], optional = true }
to_shmem_derive = { version = "0.1", path = "../to_shmem_derive", optional = true }
new_debug_unreachable = "1"

Expand Down
2 changes: 1 addition & 1 deletion selectors/matching.rs
Original file line number Diff line number Diff line change
Expand Up @@ -249,7 +249,7 @@ impl From<SelectorMatchingResult> for KleeneValue {
/// partial selectors (indexed from the right). We use this API design, rather
/// than having the callers pass a SelectorIter, because creating a SelectorIter
/// requires dereferencing the selector to get the length, which adds an
/// unncessary cache miss for cases when we can fast-reject with AncestorHashes
/// unnecessary cache miss for cases when we can fast-reject with AncestorHashes
/// (which the caller can store inline with the selector pointer).
#[inline(always)]
pub fn matches_selector<E>(
Expand Down
Loading