From 218809dd7a3a99e64a5adacff5033a6676e19722 Mon Sep 17 00:00:00 2001 From: Delan Azabani Date: Wed, 7 Feb 2024 17:52:11 +0800 Subject: [PATCH 1/8] Servo initial downstream commit Any ancestors of this commit are from upstream mozilla-central, with some filtering and renaming. Our patches and sync tooling start here. The sync tooling has all been squashed into this commit, based on: https://github.com/servo/stylo/commits/64731e10dc8ef87ef52aa2fb9f988c3b2530f3a7 --- .github/workflows/main.yml | 51 +++++++++++ .../workflows/mirror-to-release-branch.yml | 26 ++++++ .github/workflows/sync-upstream.yml | 23 +++++ .gitignore | 5 ++ README.md | 85 +++++++++++++++++++ commit-from-merge.sh | 15 ++++ commit-from-squashed.sh | 14 +++ shell.nix | 6 ++ start-rebase.sh | 10 +++ style.paths | 8 ++ sync.sh | 43 ++++++++++ 11 files changed, 286 insertions(+) create mode 100644 .github/workflows/main.yml create mode 100644 .github/workflows/mirror-to-release-branch.yml create mode 100644 .github/workflows/sync-upstream.yml create mode 100644 .gitignore create mode 100644 README.md create mode 100755 commit-from-merge.sh create mode 100755 commit-from-squashed.sh create mode 100644 shell.nix create mode 100755 start-rebase.sh create mode 100644 style.paths create mode 100755 sync.sh diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml new file mode 100644 index 0000000000..aafc8d0e84 --- /dev/null +++ b/.github/workflows/main.yml @@ -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 + diff --git a/.github/workflows/mirror-to-release-branch.yml b/.github/workflows/mirror-to-release-branch.yml new file mode 100644 index 0000000000..c8593195da --- /dev/null +++ b/.github/workflows/mirror-to-release-branch.yml @@ -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/mirror-branch-action@v1.0 + name: Mirror to ${{ steps.branch-name.outputs.BRANCH_NAME }} + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + source: main + dest: ${{ steps.branch-name.outputs.BRANCH_NAME }} diff --git a/.github/workflows/sync-upstream.yml b/.github/workflows/sync-upstream.yml new file mode 100644 index 0000000000..992fe6d2ce --- /dev/null +++ b/.github/workflows/sync-upstream.yml @@ -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 master:upstream + git push -fu --progress origin upstream diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000000..fc3c2f9b3c --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +/_cache/ +/_filtered/ +/target/ +/style/properties/__pycache__/ +Cargo.lock diff --git a/README.md b/README.md new file mode 100644 index 0000000000..b280c0c4a7 --- /dev/null +++ b/README.md @@ -0,0 +1,85 @@ +Stylo +===== + +This repo contains Servo’s downstream fork of [Stylo](https://searchfox.org/mozilla-central/source/servo). + +The branches are as follows: + +- [`upstream`](https://github.com/servo/style/tree/upstream) has upstream mozilla-central filtered to the paths we care about ([style.paths](style.paths)), but is otherwise unmodified +- [`main`](https://github.com/servo/style/tree/ci) has our downstream patches, plus the scripts and workflows for syncing with mozilla-central, to be rebased onto `upstream` + +## Building Servo against your local 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" } +style = { path = "../stylo/style" } +stylo_config = { path = "../stylo/stylo_config" } +stylo_dom = { path = "../stylo/stylo_dom" } +style_malloc_size_of = { path = "../stylo/malloc_size_of", package = "malloc_size_of" } +style_traits = { path = "../stylo/style_traits" } +``` + +## 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 master: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 +``` diff --git a/commit-from-merge.sh b/commit-from-merge.sh new file mode 100755 index 0000000000..94aa606f02 --- /dev/null +++ b/commit-from-merge.sh @@ -0,0 +1,15 @@ +#!/bin/sh +# Usage: commit-from-merge.sh [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 "$@" +"$@" diff --git a/commit-from-squashed.sh b/commit-from-squashed.sh new file mode 100755 index 0000000000..004e0f7840 --- /dev/null +++ b/commit-from-squashed.sh @@ -0,0 +1,14 @@ +#!/bin/sh +# Usage: commit-from-squashed.sh [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 "$@" +"$@" diff --git a/shell.nix b/shell.nix new file mode 100644 index 0000000000..2c96e200aa --- /dev/null +++ b/shell.nix @@ -0,0 +1,6 @@ +with import (builtins.fetchTarball { + url = "https://github.com/NixOS/nixpkgs/archive/46ae0210ce163b3cba6c7da08840c1d63de9c701.tar.gz"; +}) {}; +stdenv.mkDerivation rec { + name = "style-sync-shell"; +} diff --git a/start-rebase.sh b/start-rebase.sh new file mode 100755 index 0000000000..fe417f7f08 --- /dev/null +++ b/start-rebase.sh @@ -0,0 +1,10 @@ +#!/bin/sh +# Usage: start-rebase.sh [extra git-rebase(1) arguments ...] +# Equivalent to git rebase --onto . +set -eu + +new_base=$1; shift +first_commit=$(git log --pretty=\%H --grep='Servo initial downstream commit') +old_base=$first_commit~ + +git rebase --onto "$new_base" "$old_base" "$@" diff --git a/style.paths b/style.paths new file mode 100644 index 0000000000..d1d2d02638 --- /dev/null +++ b/style.paths @@ -0,0 +1,8 @@ +# Filters and renames use git-filter-repo(1) --paths-from-file: +# https://htmlpreview.github.io/?https://github.com/newren/git-filter-repo/blob/docs/html/git-filter-repo.html#_filtering_based_on_many_paths + +servo/components/ +servo/rustfmt.toml + +regex:servo/components/(.+)==>\1 +servo/rustfmt.toml==>rustfmt.toml diff --git a/sync.sh b/sync.sh new file mode 100755 index 0000000000..e92182c746 --- /dev/null +++ b/sync.sh @@ -0,0 +1,43 @@ +#!/bin/sh +# Usage: sync.sh +set -eu + +root=$(pwd) +mkdir -p "$1" +cd -- "$1" +filtered=$(pwd) +mkdir -p "$root/_cache" +cd "$root/_cache" +export PATH="$PWD:$PATH" + +step() { + if [ "${TERM-}" != '' ]; then + tput setaf 12 + fi + >&2 printf '* %s\n' "$*" + if [ "${TERM-}" != '' ]; then + tput sgr0 + fi +} + +step Downloading git-filter-repo if needed +if ! git filter-repo --version 2> /dev/null; then + curl -O https://raw.githubusercontent.com/newren/git-filter-repo/v2.38.0/git-filter-repo + chmod +x git-filter-repo + + git filter-repo --version +fi + +step Cloning upstream if needed +if ! [ -e upstream ]; then + git clone --bare --single-branch --progress https://github.com/mozilla/gecko-dev.git upstream +fi + +step Updating upstream +branch=$(git -C upstream rev-parse --abbrev-ref HEAD) +git -C upstream fetch origin $branch:$branch + +step Filtering upstream +# Cloning and filtering is much faster than git filter-repo --source --target. +git clone --bare upstream -- "$filtered" +git -C "$filtered" filter-repo --force --paths-from-file "$root/style.paths" From 30f8960428ee7bfc4d1443e136f77f79371ee500 Mon Sep 17 00:00:00 2001 From: Delan Azabani Date: Thu, 15 Feb 2024 18:30:14 +0800 Subject: [PATCH 2/8] Commit our changes on top of upstream Stylo This is a rebase of 35cd8025bb3d5c4cc50db80cb65f9b1840e5ac5a, but with 6a391617bc6ac596edb5ad9a5a3a3110c663ed59 reverted. Signed-off-by: Oriol Brufau --- .github/workflows/main.yml | 24 ++ .github/workflows/sync-upstream.yml | 2 +- Cargo.toml | 18 + README.md | 136 ++++--- SYNCING.md | 63 +++ malloc_size_of/Cargo.toml | 14 +- selectors/Cargo.toml | 6 +- servo_arc/Cargo.toml | 4 +- style/Cargo.toml | 46 ++- style/build.rs | 8 +- style/lib.rs | 1 - style/properties/build.py | 2 + .../mako-1.3.10-py3-none-any.whl | Bin 0 -> 78509 bytes .../vendored_python/markupsafe/LICENSE.txt | 28 ++ .../vendored_python/markupsafe/__init__.py | 384 ++++++++++++++++++ style/servo/selector_parser.rs | 14 +- style/stylesheets/stylesheet.rs | 14 +- style/values/specified/text.rs | 18 + style_derive/Cargo.toml | 5 +- style_traits/Cargo.toml | 24 +- stylo_atoms/Cargo.toml | 20 + stylo_atoms/build.rs | 31 ++ stylo_atoms/lib.rs | 5 + stylo_atoms/predefined_counter_styles.rs | 66 +++ stylo_atoms/static_atoms.txt | 185 +++++++++ stylo_config/Cargo.toml | 15 + stylo_config/lib.rs | 91 +++++ stylo_dom/Cargo.toml | 18 + stylo_dom/lib.rs | 181 +++++++++ stylo_static_prefs/Cargo.toml | 11 + stylo_static_prefs/src/lib.rs | 42 ++ sync.sh | 2 +- to_shmem/Cargo.toml | 4 +- 33 files changed, 1367 insertions(+), 115 deletions(-) create mode 100644 Cargo.toml create mode 100644 SYNCING.md create mode 100644 style/properties/vendored_python/mako-1.3.10-py3-none-any.whl create mode 100644 style/properties/vendored_python/markupsafe/LICENSE.txt create mode 100644 style/properties/vendored_python/markupsafe/__init__.py create mode 100644 stylo_atoms/Cargo.toml create mode 100644 stylo_atoms/build.rs create mode 100644 stylo_atoms/lib.rs create mode 100644 stylo_atoms/predefined_counter_styles.rs create mode 100644 stylo_atoms/static_atoms.txt create mode 100644 stylo_config/Cargo.toml create mode 100644 stylo_config/lib.rs create mode 100644 stylo_dom/Cargo.toml create mode 100644 stylo_dom/lib.rs create mode 100644 stylo_static_prefs/Cargo.toml create mode 100644 stylo_static_prefs/src/lib.rs diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index aafc8d0e84..fa6c67068c 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -34,6 +34,30 @@ jobs: env: RUST_BACKTRACE: 1 + macos-debug: + name: macOS (Debug) + runs-on: macos-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 + + windows-debug: + name: Windows (Debug) + runs-on: windows-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 + build-result: name: Result runs-on: ubuntu-latest diff --git a/.github/workflows/sync-upstream.yml b/.github/workflows/sync-upstream.yml index 992fe6d2ce..adf329ffa6 100644 --- a/.github/workflows/sync-upstream.yml +++ b/.github/workflows/sync-upstream.yml @@ -19,5 +19,5 @@ jobs: key: upstream - run: | ./sync.sh _filtered - git fetch -f --progress ./_filtered master:upstream + git fetch -f --progress ./_filtered main:upstream git push -fu --progress origin upstream diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000000..d2547c0d06 --- /dev/null +++ b/Cargo.toml @@ -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"] diff --git a/README.md b/README.md index b280c0c4a7..527db6b2f4 100644 --- a/README.md +++ b/README.md @@ -1,85 +1,107 @@ Stylo ===== -This repo contains Servo’s downstream fork of [Stylo](https://searchfox.org/mozilla-central/source/servo). +**High-Performance CSS Style Engine** -The branches are as follows: +[![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) -- [`upstream`](https://github.com/servo/style/tree/upstream) has upstream mozilla-central filtered to the paths we care about ([style.paths](style.paths)), but is otherwise unmodified -- [`main`](https://github.com/servo/style/tree/ci) has our downstream patches, plus the scripts and workflows for syncing with mozilla-central, to be rebased onto `upstream` +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. -## Building Servo against your local Stylo +Coordination of Stylo development happens: -Assuming your local `servo` and `stylo` directories are siblings, you can build `servo` against `stylo` by adding the following to `servo/Cargo.toml`: +- 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) -```toml -[patch."https://github.com/servo/stylo"] -selectors = { path = "../stylo/selectors" } -servo_arc = { path = "../stylo/servo_arc" } -stylo_atoms = { path = "../stylo/stylo_atoms" } -style = { path = "../stylo/style" } -stylo_config = { path = "../stylo/stylo_config" } -stylo_dom = { path = "../stylo/stylo_dom" } -style_malloc_size_of = { path = "../stylo/malloc_size_of", package = "malloc_size_of" } -style_traits = { path = "../stylo/style_traits" } -``` +## High-Level Documentation -## Syncing `upstream` with mozilla-central +- 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). -Start by generating a filtered copy of mozilla-central. This will cache the raw mozilla-central in `_cache/upstream`, storing the result in `_filtered`: +## Branches -```sh -$ ./sync.sh _filtered -``` +The branches are as follows: -If `_filtered` already exists, you will need to delete it and try again: +- [**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**. -```sh -$ rm -Rf _filtered -``` +> [!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`). -Now overwrite our `upstream` with those commits and push: +More information on the syncing process is available in [SYNCING.md](SYNCING.md) -```sh -$ git fetch -f --progress ./_filtered master:upstream -$ git push -fu --progress origin upstream -``` +## Crates -## Rebasing `main` onto `upstream` +A guide to the crates contained within this repo -Start by fetching `upstream` into your local repo: +### Stylo Crates -```sh -$ git fetch -f origin upstream:upstream -``` +These crates are largely implementation details of Stylo, although you may need to use some of them directly if you use Stylo. -In general, the filtering process is deterministic, yielding the same commit hashes each time, so we can rebase normally: +| 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 | -```sh -$ git rebase upstream -``` +### Standalone Crates -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 `~`): +These crates form part of Stylo but are also be useful standalone. -```sh -$ git log --pretty=\%H --grep='Servo initial downstream commit' -e62d7f0090941496e392e1dc91df103a38e3f488 +| 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` | -$ git rebase --onto upstream e62d7f0090941496e392e1dc91df103a38e3f488~ -Successfully rebased and updated refs/heads/main. -``` +You may also be interested in the `cssparser` crate which lives in the [servo/rust-cssparser](https://github.com/servo/rust-cssparser) repo. -`start-rebase.sh` takes care of this automatically, but you should still use `git rebase` for subsequent steps like `--continue` and `--abort`: +### Support Crates -```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 -``` +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 | -Or if we aren’t ready to rebase onto the tip of upstream: +## 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`: -```sh -$ ./start-rebase.sh upstream~10 -i +```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 diff --git a/SYNCING.md b/SYNCING.md new file mode 100644 index 0000000000..72a0a53d1d --- /dev/null +++ b/SYNCING.md @@ -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 +``` diff --git a/malloc_size_of/Cargo.toml b/malloc_size_of/Cargo.toml index a12c652662..fdb8d690a0 100644 --- a/malloc_size_of/Cargo.toml +++ b/malloc_size_of/Cargo.toml @@ -1,6 +1,6 @@ [package] -name = "malloc_size_of" -version = "0.0.1" +name = "stylo_malloc_size_of" +version = "0.6.0" authors = ["The Servo Project Developers"] license = "MIT OR Apache-2.0" repository = "https://github.com/servo/stylo" @@ -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.31", 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" diff --git a/selectors/Cargo.toml b/selectors/Cargo.toml index 8b3d39405e..c90cb73155 100644 --- a/selectors/Cargo.toml +++ b/selectors/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "selectors" -version = "0.26.0" +version = "0.31.0" authors = ["The Servo Project Developers"] documentation = "https://docs.rs/selectors/" description = "CSS Selectors matching for Rust" @@ -21,7 +21,7 @@ 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" @@ -29,7 +29,7 @@ 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" diff --git a/servo_arc/Cargo.toml b/servo_arc/Cargo.toml index 8b0976b75d..9f77b64b43 100644 --- a/servo_arc/Cargo.toml +++ b/servo_arc/Cargo.toml @@ -1,17 +1,19 @@ [package] name = "servo_arc" -version = "0.4.0" +version = "0.4.1" authors = ["The Servo Project Developers"] license = "MIT OR Apache-2.0" repository = "https://github.com/servo/stylo" description = "A fork of std::sync::Arc with some extra functionality and without weak references" edition = "2021" +readme = "../README.md" [lib] name = "servo_arc" path = "lib.rs" [features] +default = ["track_alloc_size"] gecko_refcount_logging = [] servo = ["serde", "track_alloc_size"] track_alloc_size = [] diff --git a/style/Cargo.toml b/style/Cargo.toml index ab039e5442..69f1df7246 100644 --- a/style/Cargo.toml +++ b/style/Cargo.toml @@ -1,11 +1,12 @@ [package] -name = "style" -version = "0.0.1" +name = "stylo" +version = "0.6.0" authors = ["The Servo Project Developers"] license = "MPL-2.0" repository = "https://github.com/servo/stylo" edition = "2021" description = "The Stylo CSS engine" +readme = "../README.md" build = "build.rs" @@ -18,6 +19,7 @@ path = "lib.rs" doctest = false [features] +default = ["servo"] gecko = [ "bindgen", "malloc_size_of/gecko", @@ -31,15 +33,15 @@ gecko = [ "to_shmem/gecko", ] servo = [ - "arrayvec/use_union", "cssparser/serde", "encoding_rs", "malloc_size_of/servo", "web_atoms", + "mime", "serde", "servo_arc/servo", "stylo_atoms", - "servo_config", + "style_config", "string_cache", "style_traits/servo", "url", @@ -48,6 +50,7 @@ servo = [ ] gecko_debug = [] gecko_refcount_logging = [] +nsstring = [] [dependencies] app_units = "0.7.8" @@ -55,24 +58,24 @@ arrayvec = "0.7" atomic_refcell = "0.1" bitflags = "2" byteorder = "1.0" -cssparser = "0.34" +cssparser = "0.35" derive_more = { version = "2", features = ["add", "add_assign", "deref", "deref_mut", "from"] } -dom = { path = "../../../dom/base/rust" } +dom = { version = "0.6", path = "../stylo_dom", package = "stylo_dom" } new_debug_unreachable = "1.0" encoding_rs = {version = "0.8", optional = true} euclid = "0.22" fxhash = "0.2" -icu_segmenter = { version = "2.0", default-features = false, features = ["auto", "compiled_data"] } +icu_segmenter = { version = "1.5", default-features = false, features = ["auto", "compiled_data"] } indexmap = {version = "2", features = ["std"]} itertools = "0.14" itoa = "1.0" lazy_static = "1" log = "0.4" -malloc_size_of = { path = "../malloc_size_of" } -malloc_size_of_derive = { path = "../../../xpcom/rust/malloc_size_of_derive" } -web_atoms = { version = "0.1", optional = true } +malloc_size_of = { version = "0.6", path = "../malloc_size_of", package = "stylo_malloc_size_of" } +malloc_size_of_derive = "0.1" +web_atoms = { version = "0.1.3", optional = true } matches = "0.1" -nsstring = {path = "../../../xpcom/rust/nsstring/", optional = true} +mime = { version = "0.3.13", optional = true } num_cpus = {version = "1.1.0"} num-integer = "0.1" num-traits = "0.2" @@ -81,24 +84,23 @@ parking_lot = "0.12" precomputed-hash = "0.1.1" rayon = "1" rayon-core = "1" -selectors = { path = "../selectors" } +selectors = { version = "0.31.0", path = "../selectors" } serde = {version = "1.0", optional = true, features = ["derive"]} -servo_arc = { path = "../servo_arc" } -stylo_atoms = {path = "../atoms", optional = true} -servo_config = {path = "../config", optional = true} +servo_arc = { version = "0.4.0", path = "../servo_arc" } +stylo_atoms = { version = "0.6", path = "../stylo_atoms", optional = true} smallbitvec = "2.3.0" smallvec = "1.0" static_assertions = "1.1" -static_prefs = { path = "../../../modules/libpref/init/static_prefs" } +static_prefs = { version = "0.6", path = "../stylo_static_prefs", package = "stylo_static_prefs" } string_cache = { version = "0.8", optional = true } -style_derive = {path = "../style_derive"} -style_traits = {path = "../style_traits"} -to_shmem = {path = "../to_shmem"} -to_shmem_derive = {path = "../to_shmem_derive"} -thin-vec = { version = "0.2.1", features = ["gecko-ffi"] } +style_config = { version = "0.6", path = "../stylo_config", package = "stylo_config", optional = true } +style_derive = { version = "0.6", path = "../style_derive", package = "stylo_derive" } +style_traits = { version = "0.6", path = "../style_traits", package = "stylo_traits" } +to_shmem = { version = "0.2", path = "../to_shmem" } +to_shmem_derive = { version = "0.1", path = "../to_shmem_derive" } +thin-vec = "0.2.1" uluru = "3.0" void = "1.0.2" -gecko-profiler = { path = "../../../tools/profiler/rust-api" } url = { version = "2.5", optional = true, features = ["serde"] } [build-dependencies] diff --git a/style/build.rs b/style/build.rs index 4b27edbe2c..2f12477133 100644 --- a/style/build.rs +++ b/style/build.rs @@ -21,7 +21,7 @@ mod build_gecko { lazy_static! { pub static ref PYTHON: String = env::var("PYTHON3").ok().unwrap_or_else(|| { let candidates = if cfg!(windows) { - ["python3.exe"] + ["python.exe"] } else { ["python3"] }; @@ -58,6 +58,12 @@ fn generate_properties(engine: &str) { .join("build.py"); let status = Command::new(&*PYTHON) + // `cargo publish` isn't happy with the `__pycache__` files that are created + // when we run the property generator. + // + // TODO(mrobinson): Is this happening because of how we run this script? It + // would be better to ensure are just placed in the output directory. + .env("PYTHONDONTWRITEBYTECODE", "1") .arg(&script) .arg(engine) .arg("style-crate") diff --git a/style/lib.rs b/style/lib.rs index aa1ece2e64..3974869aa0 100644 --- a/style/lib.rs +++ b/style/lib.rs @@ -48,7 +48,6 @@ extern crate malloc_size_of; #[macro_use] extern crate malloc_size_of_derive; #[cfg(feature = "servo")] -#[macro_use] extern crate web_atoms; #[allow(unused_extern_crates)] #[macro_use] diff --git a/style/properties/build.py b/style/properties/build.py index 3d0aed3b21..a2f79c9b3c 100644 --- a/style/properties/build.py +++ b/style/properties/build.py @@ -8,6 +8,8 @@ import sys BASE = os.path.dirname(__file__.replace("\\", "/")) +sys.path.insert(0, os.path.join(BASE, "vendored_python", "mako-1.3.10-py3-none-any.whl")) +sys.path.insert(0, os.path.join(BASE, "vendored_python")) # For importing markupsafe sys.path.insert(0, BASE) # For importing `data.py` from mako import exceptions diff --git a/style/properties/vendored_python/mako-1.3.10-py3-none-any.whl b/style/properties/vendored_python/mako-1.3.10-py3-none-any.whl new file mode 100644 index 0000000000000000000000000000000000000000..2f85cd7b576bf7998b55999cfe2f5519e3b75be3 GIT binary patch literal 78509 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
    X?O4`zuspQ{P-N3wH2jwQmX{zwD`%wDPR zKj;&WF$+qKV{ABBcYm@@`^RE-8~B*=$y;6u>M1&A+G>RxfP>!@UO+HVq0=10Mnruw4_)0vt88=en=d~>C2S2i^6-PG_6uApW<0AHtMB@y~KZP39ZS|KaovvNu!g^u~4f3R1s>pY}pGRADQ*V8~Zuob* z$mfu8?PYvYH7zZ?3cH~XTJ&x={96`1dZd~4EZUk9@YbBn-{vdZ>r4er20~Yih8QzL zTRa13;W9AT)T2)J?joB-X;gpK*sG3tEd`g}d9evvLI6|nWG$}rsuAu|x`iy1H&wOK zw=5G$BO{4CVLL?eTrQ$z;FX?fE2bC#WOq zq~C>N%&UR;6qLAA$X}=}dmifW za8XS|o;#0wzc``Lz+G+ZGzU3I63q$O-_3kl!hSXG_8=z!;B(74k{BaNwo*GOaPzPQ zbk|D^+o3^yMdh5st({(?gV4mdP7OmE;z>S0&>!eKh*w&6j9afDTFoxAEr7w9NNawV zW`pZXZElnkSYUh&A@)Fga9s&w=~ReU?S*eq3i1Ihl~wMiR8S5_v}i>x*&JAj#4DRg zRuO^8eTD55yF8=6oT(IeTbn>`Ezr22q4s3+GpHa#Si%kH!zq? z>J&WXHTeM&VwOQ3fFPsDlUE{proH-y&MUVGd`6gV?DDBm6;Y9xl|Hy{i)RRHTdDkl z(_w`*heMXdb9eKgWs@t z9MR(NYJU4iGkpZ7$6Aw+;B&Y1D)=+&REOi(jE8M_UH>Th@iN5n^q>@-r78Sae%((5 z?1n#Hi`zLu8uo%{I|;nXXL!#Q3@zU%XGYI6*%?o=9kbDdvD)zMUQZ7~07F)AaZnRU zS74Z{Qwj^K??x4-Q}I!sCx$doa5#x{K?Ftf$QG7e2tj(?iZBe;GIyxXURrh}R!VD1 zk98xQ{4YsA1Y27u)Fp1bE=n^cqzTQ^4Lxk2m}E@*vyQzD7SQ8DOim~;&|=Wsf!Z$e z2#U&}JCE<2D_HGyW41mD2Yr(G0n{3VE3cXkTM51GR{AC?g50EY8v$u3@_FxOrbOel z2GU8#g33}p(^w6r3E5S!l5JcdO91_MmMM|WkUYVNk9XUQ(?^d4A&4%OK@xO(W6}Zw zcMeHBSgelu(T4E964+Uw9{X`wc!^+y9Ac#~)0=g09Q6X!fMDg)Z{lU=t^z(4gjv$? zRW!2_(PCz>Vr936t&DhwUm%)Y=%A=7m8ckcyZFyKPc^*9!z|M1X#@YX28ZMxDNu#V zEkt^e=XQsIC7{6oA>YnNtxdqwWEu*}d=q*d%51f>&?;$vsF!5s30!)3^pSGzwo)s9 z8idD`=+oSULI`|f8<-|E>pn^q!FANL+!JW;*Uylje9?ifpX9gb9I6fi(l%_q(IF&%(=T*gxRu)^*4rVOS z#bB$F<4~rlAOP!i9LL4G!C?sT_M2*8T``p8z7O2y8 V0nvqmFhJJbC7)VJZ=kc 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

    hU}wVA9HjqE1mgo~JG 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 MOr})8 str: + return ( + s.replace("&", "&") + .replace(">", ">") + .replace("<", "<") + .replace("'", "'") + .replace('"', """) + ) +# BEGIN PATCHED SECTION + + +class _HasHTML(t.Protocol): + def __html__(self, /) -> str: ... + + +class _TPEscape(t.Protocol): + def __call__(self, s: t.Any, /) -> Markup: ... + + +def escape(s: t.Any, /) -> Markup: + """Replace the characters ``&``, ``<``, ``>``, ``'``, and ``"`` in + the string with HTML-safe sequences. Use this if you need to display + text that might contain such characters in HTML. + + If the object has an ``__html__`` method, it is called and the + return value is assumed to already be safe for HTML. + + :param s: An object to be converted to a string and escaped. + :return: A :class:`Markup` string with the escaped text. + """ + # If the object is already a plain string, skip __html__ check and string + # conversion. This is the most common use case. + # Use type(s) instead of s.__class__ because a proxy object may be reporting + # the __class__ of the proxied value. + if type(s) is str: + return Markup(_escape_inner(s)) + + if hasattr(s, "__html__"): + return Markup(s.__html__()) + + return Markup(_escape_inner(str(s))) + + +def escape_silent(s: t.Any | None, /) -> Markup: + """Like :func:`escape` but treats ``None`` as the empty string. + Useful with optional values, as otherwise you get the string + ``'None'`` when the value is ``None``. + + >>> escape(None) + Markup('None') + >>> escape_silent(None) + Markup('') + """ + if s is None: + return Markup() + + return escape(s) + + +def soft_str(s: t.Any, /) -> str: + """Convert an object to a string if it isn't already. This preserves + a :class:`Markup` string rather than converting it back to a basic + string, so it will still be marked as safe and won't be escaped + again. + + >>> value = escape("") + >>> value + Markup('<User 1>') + >>> escape(str(value)) + Markup('&lt;User 1&gt;') + >>> escape(soft_str(value)) + Markup('<User 1>') + """ + if not isinstance(s, str): + return str(s) + + return s + + +class Markup(str): + """A string that is ready to be safely inserted into an HTML or XML + document, either because it was escaped or because it was marked + safe. + + Passing an object to the constructor converts it to text and wraps + it to mark it safe without escaping. To escape the text, use the + :meth:`escape` class method instead. + + >>> Markup("Hello, World!") + Markup('Hello, World!') + >>> Markup(42) + Markup('42') + >>> Markup.escape("Hello, World!") + Markup('Hello <em>World</em>!') + + This implements the ``__html__()`` interface that some frameworks + use. Passing an object that implements ``__html__()`` will wrap the + output of that method, marking it safe. + + >>> class Foo: + ... def __html__(self): + ... return 'foo' + ... + >>> Markup(Foo()) + Markup('foo') + + This is a subclass of :class:`str`. It has the same methods, but + escapes their arguments and returns a ``Markup`` instance. + + >>> Markup("%s") % ("foo & bar",) + Markup('foo & bar') + >>> Markup("Hello ") + "" + Markup('Hello <foo>') + """ + + __slots__ = () + + def __new__( + cls, object: t.Any = "", encoding: str | None = None, errors: str = "strict" + ) -> te.Self: + if hasattr(object, "__html__"): + object = object.__html__() + + if encoding is None: + return super().__new__(cls, object) + + return super().__new__(cls, object, encoding, errors) + + def __html__(self, /) -> te.Self: + return self + + def __add__(self, value: str | _HasHTML, /) -> te.Self: + if isinstance(value, str) or hasattr(value, "__html__"): + return self.__class__(super().__add__(self.escape(value))) + + return NotImplemented + + def __radd__(self, value: str | _HasHTML, /) -> te.Self: + if isinstance(value, str) or hasattr(value, "__html__"): + return self.escape(value).__add__(self) + + return NotImplemented + + def __mul__(self, value: t.SupportsIndex, /) -> te.Self: + return self.__class__(super().__mul__(value)) + + def __rmul__(self, value: t.SupportsIndex, /) -> te.Self: + return self.__class__(super().__mul__(value)) + + def __mod__(self, value: t.Any, /) -> te.Self: + if isinstance(value, tuple): + # a tuple of arguments, each wrapped + value = tuple(_MarkupEscapeHelper(x, self.escape) for x in value) + elif hasattr(type(value), "__getitem__") and not isinstance(value, str): + # a mapping of arguments, wrapped + value = _MarkupEscapeHelper(value, self.escape) + else: + # a single argument, wrapped with the helper and a tuple + value = (_MarkupEscapeHelper(value, self.escape),) + + return self.__class__(super().__mod__(value)) + + def __repr__(self, /) -> str: + return f"{self.__class__.__name__}({super().__repr__()})" + + def join(self, iterable: cabc.Iterable[str | _HasHTML], /) -> te.Self: + return self.__class__(super().join(map(self.escape, iterable))) + + def split( # type: ignore[override] + self, /, sep: str | None = None, maxsplit: t.SupportsIndex = -1 + ) -> list[te.Self]: + return [self.__class__(v) for v in super().split(sep, maxsplit)] + + def rsplit( # type: ignore[override] + self, /, sep: str | None = None, maxsplit: t.SupportsIndex = -1 + ) -> list[te.Self]: + return [self.__class__(v) for v in super().rsplit(sep, maxsplit)] + + def splitlines( # type: ignore[override] + self, /, keepends: bool = False + ) -> list[te.Self]: + return [self.__class__(v) for v in super().splitlines(keepends)] + + def unescape(self, /) -> str: + """Convert escaped markup back into a text string. This replaces + HTML entities with the characters they represent. + + >>> Markup("Main » About").unescape() + 'Main » About' + """ + from html import unescape + + return unescape(str(self)) + + def striptags(self, /) -> str: + """:meth:`unescape` the markup, remove tags, and normalize + whitespace to single spaces. + + >>> Markup("Main »\tAbout").striptags() + 'Main » About' + """ + value = str(self) + + # Look for comments then tags separately. Otherwise, a comment that + # contains a tag would end early, leaving some of the comment behind. + + # keep finding comment start marks + while (start := value.find("", start)) == -1: + break + + value = f"{value[:start]}{value[end + 3 :]}" + + # remove tags using the same method + while (start := value.find("<")) != -1: + if (end := value.find(">", start)) == -1: + break + + value = f"{value[:start]}{value[end + 1 :]}" + + # collapse spaces + value = " ".join(value.split()) + return self.__class__(value).unescape() + + @classmethod + def escape(cls, s: t.Any, /) -> te.Self: + """Escape a string. Calls :func:`escape` and ensures that for + subclasses the correct type is returned. + """ + rv = escape(s) + + if rv.__class__ is not cls: + return cls(rv) + + return rv # type: ignore[return-value] + + def __getitem__(self, key: t.SupportsIndex | slice, /) -> te.Self: + return self.__class__(super().__getitem__(key)) + + def capitalize(self, /) -> te.Self: + return self.__class__(super().capitalize()) + + def title(self, /) -> te.Self: + return self.__class__(super().title()) + + def lower(self, /) -> te.Self: + return self.__class__(super().lower()) + + def upper(self, /) -> te.Self: + return self.__class__(super().upper()) + + def replace(self, old: str, new: str, count: t.SupportsIndex = -1, /) -> te.Self: + return self.__class__(super().replace(old, self.escape(new), count)) + + def ljust(self, width: t.SupportsIndex, fillchar: str = " ", /) -> te.Self: + return self.__class__(super().ljust(width, self.escape(fillchar))) + + def rjust(self, width: t.SupportsIndex, fillchar: str = " ", /) -> te.Self: + return self.__class__(super().rjust(width, self.escape(fillchar))) + + def lstrip(self, chars: str | None = None, /) -> te.Self: + return self.__class__(super().lstrip(chars)) + + def rstrip(self, chars: str | None = None, /) -> te.Self: + return self.__class__(super().rstrip(chars)) + + def center(self, width: t.SupportsIndex, fillchar: str = " ", /) -> te.Self: + return self.__class__(super().center(width, self.escape(fillchar))) + + def strip(self, chars: str | None = None, /) -> te.Self: + return self.__class__(super().strip(chars)) + + def translate( + self, + table: cabc.Mapping[int, str | int | None], # type: ignore[override] + /, + ) -> str: + return self.__class__(super().translate(table)) + + def expandtabs(self, /, tabsize: t.SupportsIndex = 8) -> te.Self: + return self.__class__(super().expandtabs(tabsize)) + + def swapcase(self, /) -> te.Self: + return self.__class__(super().swapcase()) + + def zfill(self, width: t.SupportsIndex, /) -> te.Self: + return self.__class__(super().zfill(width)) + + def casefold(self, /) -> te.Self: + return self.__class__(super().casefold()) + + def removeprefix(self, prefix: str, /) -> te.Self: + return self.__class__(super().removeprefix(prefix)) + + def removesuffix(self, suffix: str) -> te.Self: + return self.__class__(super().removesuffix(suffix)) + + def partition(self, sep: str, /) -> tuple[te.Self, te.Self, te.Self]: + left, sep, right = super().partition(sep) + cls = self.__class__ + return cls(left), cls(sep), cls(right) + + def rpartition(self, sep: str, /) -> tuple[te.Self, te.Self, te.Self]: + left, sep, right = super().rpartition(sep) + cls = self.__class__ + return cls(left), cls(sep), cls(right) + + def format(self, *args: t.Any, **kwargs: t.Any) -> te.Self: + formatter = EscapeFormatter(self.escape) + return self.__class__(formatter.vformat(self, args, kwargs)) + + def format_map( + self, + mapping: cabc.Mapping[str, t.Any], # type: ignore[override] + /, + ) -> te.Self: + formatter = EscapeFormatter(self.escape) + return self.__class__(formatter.vformat(self, (), mapping)) + + def __html_format__(self, format_spec: str, /) -> te.Self: + if format_spec: + raise ValueError("Unsupported format specification for Markup.") + + return self + + +class EscapeFormatter(string.Formatter): + __slots__ = ("escape",) + + def __init__(self, escape: _TPEscape) -> None: + self.escape: _TPEscape = escape + super().__init__() + + def format_field(self, value: t.Any, format_spec: str) -> str: + if hasattr(value, "__html_format__"): + rv = value.__html_format__(format_spec) + elif hasattr(value, "__html__"): + if format_spec: + raise ValueError( + f"Format specifier {format_spec} given, but {type(value)} does not" + " define __html_format__. A class that defines __html__ must define" + " __html_format__ to work with format specifiers." + ) + rv = value.__html__() + else: + # We need to make sure the format spec is str here as + # otherwise the wrong callback methods are invoked. + rv = super().format_field(value, str(format_spec)) + return str(self.escape(rv)) + + +class _MarkupEscapeHelper: + """Helper for :meth:`Markup.__mod__`.""" + + __slots__ = ("obj", "escape") + + def __init__(self, obj: t.Any, escape: _TPEscape) -> None: + self.obj: t.Any = obj + self.escape: _TPEscape = escape + + def __getitem__(self, key: t.Any, /) -> te.Self: + return self.__class__(self.obj[key], self.escape) + + def __str__(self, /) -> str: + return str(self.escape(self.obj)) + + def __repr__(self, /) -> str: + return str(self.escape(repr(self.obj))) + + def __int__(self, /) -> int: + return int(self.obj) + + def __float__(self, /) -> float: + return float(self.obj) diff --git a/style/servo/selector_parser.rs b/style/servo/selector_parser.rs index b0c2fbebea..9df236520e 100644 --- a/style/servo/selector_parser.rs +++ b/style/servo/selector_parser.rs @@ -410,7 +410,12 @@ impl ToCss for NonTSPseudoClass { Self::UserValid => ":user-valid", Self::Valid => ":valid", Self::Visited => ":visited", - Self::Lang(_) | Self::CustomState(_) => unreachable!(), + NonTSPseudoClass::CustomState(ref state) => { + dest.write_str(":state(")?; + state.0.to_css(dest)?; + return dest.write_char(')'); + }, + Self::Lang(_) => unreachable!(), }) } } @@ -597,10 +602,13 @@ impl<'a, 'i> ::selectors::Parser<'i> for SelectorParser<'a> { parser: &mut CssParser<'i, 't>, after_part: bool, ) -> Result> { - use self::NonTSPseudoClass::*; let pseudo_class = match_ignore_ascii_case! { &name, "lang" if !after_part => { - Lang(parser.expect_ident_or_string()?.as_ref().into()) + NonTSPseudoClass::Lang(parser.expect_ident_or_string()?.as_ref().into()) + }, + "state" => { + let result = AtomIdent::from(parser.expect_ident()?.as_ref()); + NonTSPseudoClass::CustomState(CustomState(result)) }, _ => return Err(parser.new_custom_error(SelectorParseErrorKind::UnexpectedIdent(name.clone()))), }; diff --git a/style/stylesheets/stylesheet.rs b/style/stylesheets/stylesheet.rs index 0e39286a5d..2a05167089 100644 --- a/style/stylesheets/stylesheet.rs +++ b/style/stylesheets/stylesheet.rs @@ -125,13 +125,12 @@ impl StylesheetContents { /// An empty namespace map should be fine, as it is only used for parsing, /// not serialization of existing selectors. Since UA sheets are read only, /// we should never need the namespace map. - pub fn from_shared_data( + pub fn from_data( rules: Arc>, origin: Origin, url_data: UrlExtraData, quirks_mode: QuirksMode, ) -> Arc { - debug_assert!(rules.is_static()); Arc::new(Self { rules, origin, @@ -145,6 +144,17 @@ impl StylesheetContents { }) } + /// Same as above, but ensuring that the rules are static. + pub fn from_shared_data( + rules: Arc>, + origin: Origin, + url_data: UrlExtraData, + quirks_mode: QuirksMode, + ) -> Arc { + debug_assert!(rules.is_static()); + Self::from_data(rules, origin, url_data, quirks_mode) + } + /// Returns a reference to the list of rules. #[inline] pub fn rules<'a, 'b: 'a>(&'a self, guard: &'b SharedRwLockReadGuard) -> &'a [CssRule] { diff --git a/style/values/specified/text.rs b/style/values/specified/text.rs index 8afcf8d18a..0be29f4a23 100644 --- a/style/values/specified/text.rs +++ b/style/values/specified/text.rs @@ -415,10 +415,15 @@ bitflags! { /// Capitalize each word. const CAPITALIZE = 1 << 2; /// Automatic italicization of math variables. + #[cfg(feature = "gecko")] const MATH_AUTO = 1 << 3; /// All the case transforms, which are exclusive with each other. + #[cfg(feature = "gecko")] const CASE_TRANSFORMS = Self::UPPERCASE.0 | Self::LOWERCASE.0 | Self::CAPITALIZE.0 | Self::MATH_AUTO.0; + /// All the case transforms, which are exclusive with each other. + #[cfg(feature = "servo")] + const CASE_TRANSFORMS = Self::UPPERCASE.0 | Self::LOWERCASE.0 | Self::CAPITALIZE.0; /// full-width const FULL_WIDTH = 1 << 4; @@ -445,6 +450,19 @@ impl TextTransform { // Case bits are exclusive with each other. case.is_empty() || case.bits().is_power_of_two() } + + /// Returns the corresponding TextTransformCase. + pub fn case(&self) -> TextTransformCase { + match *self & Self::CASE_TRANSFORMS { + Self::NONE => TextTransformCase::None, + Self::UPPERCASE => TextTransformCase::Uppercase, + Self::LOWERCASE => TextTransformCase::Lowercase, + Self::CAPITALIZE => TextTransformCase::Capitalize, + #[cfg(feature = "gecko")] + Self::MATH_AUTO => TextTransformCase::MathAuto, + _ => unreachable!("Case bits are exclusive with each other"), + } + } } /// Specified and computed value of text-align-last. diff --git a/style_derive/Cargo.toml b/style_derive/Cargo.toml index 8d8a85f62d..e5c6b63698 100644 --- a/style_derive/Cargo.toml +++ b/style_derive/Cargo.toml @@ -1,11 +1,12 @@ [package] -name = "style_derive" -version = "0.0.1" +name = "stylo_derive" +version = "0.6.0" authors = ["The Servo Project Developers"] license = "MPL-2.0" repository = "https://github.com/servo/stylo" edition = "2021" description = "Derive crate for Stylo CSS engine" +readme = "../README.md" [lib] path = "lib.rs" diff --git a/style_traits/Cargo.toml b/style_traits/Cargo.toml index 4d827aaffa..fe6458d50a 100644 --- a/style_traits/Cargo.toml +++ b/style_traits/Cargo.toml @@ -1,11 +1,12 @@ [package] -name = "style_traits" -version = "0.0.1" +name = "stylo_traits" +version = "0.6.0" authors = ["The Servo Project Developers"] license = "MPL-2.0" repository = "https://github.com/servo/stylo" edition = "2021" description = "Types used by the Stylo CSS engine" +readme = "../README.md" [lib] name = "style_traits" @@ -13,21 +14,20 @@ path = "lib.rs" [features] servo = ["stylo_atoms", "cssparser/serde", "url", "euclid/serde"] -gecko = ["nsstring"] +gecko = [] [dependencies] app_units = "0.7" bitflags = "2" -cssparser = "0.34" +cssparser = "0.35" euclid = "0.22" -malloc_size_of = { path = "../malloc_size_of" } -malloc_size_of_derive = { path = "../../../xpcom/rust/malloc_size_of_derive" } -nsstring = {path = "../../../xpcom/rust/nsstring/", optional = true} -selectors = { path = "../selectors" } +malloc_size_of = { version = "0.6", path = "../malloc_size_of", package = "stylo_malloc_size_of" } +malloc_size_of_derive = "0.1" +selectors = { version = "0.31", path = "../selectors" } serde = "1.0" -servo_arc = { path = "../servo_arc" } -stylo_atoms = { path = "../atoms", optional = true } +servo_arc = { version = "0.4", path = "../servo_arc" } +stylo_atoms = { version = "0.6", path = "../stylo_atoms", optional = true } thin-vec = "0.2" -to_shmem = { path = "../to_shmem" } -to_shmem_derive = { path = "../to_shmem_derive" } +to_shmem = { version = "0.2", path = "../to_shmem" } +to_shmem_derive = { version = "0.1", path = "../to_shmem_derive" } url = { version = "2.5", optional = true } diff --git a/stylo_atoms/Cargo.toml b/stylo_atoms/Cargo.toml new file mode 100644 index 0000000000..d80ff756e2 --- /dev/null +++ b/stylo_atoms/Cargo.toml @@ -0,0 +1,20 @@ +[package] +name = "stylo_atoms" +version = "0.6.0" +authors = ["The Servo Project Developers"] +documentation = "https://docs.rs/stylo_atoms/" +description = "Interned string type for the Servo and Stylo projects" +repository = "https://github.com/servo/stylo" +license = "MPL-2.0" +edition = "2018" +build = "build.rs" +readme = "../README.md" + +[lib] +path = "lib.rs" + +[dependencies] +string_cache = "0.8" + +[build-dependencies] +string_cache_codegen = "0.5" diff --git a/stylo_atoms/build.rs b/stylo_atoms/build.rs new file mode 100644 index 0000000000..b5f6775724 --- /dev/null +++ b/stylo_atoms/build.rs @@ -0,0 +1,31 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ + +use std::env; +use std::fs::File; +use std::io::{BufRead, BufReader}; +use std::path::Path; + +fn main() { + let static_atoms = + Path::new(&env::var_os("CARGO_MANIFEST_DIR").unwrap()).join("static_atoms.txt"); + let static_atoms = BufReader::new(File::open(&static_atoms).unwrap()); + let mut atom_type = string_cache_codegen::AtomType::new("Atom", "atom!"); + + macro_rules! predefined { + ($($name: expr,)+) => { + { + $( + atom_type.atom($name); + )+ + } + } + } + include!("./predefined_counter_styles.rs"); + + atom_type + .atoms(static_atoms.lines().map(Result::unwrap)) + .write_to_file(&Path::new(&env::var_os("OUT_DIR").unwrap()).join("atom.rs")) + .unwrap(); +} diff --git a/stylo_atoms/lib.rs b/stylo_atoms/lib.rs new file mode 100644 index 0000000000..03560a40c0 --- /dev/null +++ b/stylo_atoms/lib.rs @@ -0,0 +1,5 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ + +include!(concat!(env!("OUT_DIR"), "/atom.rs")); diff --git a/stylo_atoms/predefined_counter_styles.rs b/stylo_atoms/predefined_counter_styles.rs new file mode 100644 index 0000000000..f376981e32 --- /dev/null +++ b/stylo_atoms/predefined_counter_styles.rs @@ -0,0 +1,66 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ + + // THIS FILE IS DUPLICATED FROM style/counter_style/predefined.rs. + // TO UPDATE IT: + // - Run `python style/counter_style/updated_predefined.py` + // - Re-copy style/counter_style/predefined.rs to this location + +predefined! { + "decimal", + "decimal-leading-zero", + "arabic-indic", + "armenian", + "upper-armenian", + "lower-armenian", + "bengali", + "cambodian", + "khmer", + "cjk-decimal", + "devanagari", + "georgian", + "gujarati", + "gurmukhi", + "hebrew", + "kannada", + "lao", + "malayalam", + "mongolian", + "myanmar", + "oriya", + "persian", + "lower-roman", + "upper-roman", + "tamil", + "telugu", + "thai", + "tibetan", + "lower-alpha", + "lower-latin", + "upper-alpha", + "upper-latin", + "cjk-earthly-branch", + "cjk-heavenly-stem", + "lower-greek", + "hiragana", + "hiragana-iroha", + "katakana", + "katakana-iroha", + "disc", + "circle", + "square", + "disclosure-open", + "disclosure-closed", + "japanese-informal", + "japanese-formal", + "korean-hangul-formal", + "korean-hanja-informal", + "korean-hanja-formal", + "simp-chinese-informal", + "simp-chinese-formal", + "trad-chinese-informal", + "trad-chinese-formal", + "cjk-ideographic", + "ethiopic-numeric", +} diff --git a/stylo_atoms/static_atoms.txt b/stylo_atoms/static_atoms.txt new file mode 100644 index 0000000000..8e18e69021 --- /dev/null +++ b/stylo_atoms/static_atoms.txt @@ -0,0 +1,185 @@ +-moz-content-preferred-color-scheme +-moz-device-pixel-ratio +-moz-fixed-pos-containing-block +-moz-gtk-csd-close-button-position +-moz-gtk-csd-maximize-button-position +-moz-gtk-csd-menu-radius +-moz-gtk-csd-minimize-button-position +-moz-gtk-csd-titlebar-button-spacing +-moz-gtk-csd-titlebar-radius +-moz-gtk-csd-tooltip-radius +-moz-gtk-menu-radius +-moz-mac-titlebar-height +-moz-overlay-scrollbar-fade-duration +DOMContentLoaded +abort +activate +addtrack +animationcancel +animationend +animationiteration +animationstart +aspect-ratio +beforetoggle +beforeunload +block-size +button +canplay +canplaythrough +center +change +characteristicvaluechanged +checkbox +cancel +click +close +closing +color +complete +compositionend +compositionstart +compositionupdate +controllerchange +cursive +dark +datachannel +date +datetime-local +dir +device-pixel-ratio +durationchange +email +emptied +end +ended +error +fantasy +fetch +file +fill +fill-opacity +formdata +fullscreenchange +fullscreenerror +gattserverdisconnected +hairline +hashchange +height +hidden +icecandidate +iceconnectionstatechange +icegatheringstatechange +image +inline-size +input +inputsourceschange +invalid +keydown +keypress +kind +left +light +ltr +load +loadeddata +loadedmetadata +loadend +loadstart +message +message +messageerror +monospace +month +mousedown +mousemove +mouseover +mouseup +negotiationneeded +none +normal +number +onchange +open +orientation +pagehide +pageshow +password +pause +play +playing +popstate +postershown +prefers-color-scheme +print +progress +radio +range +ratechange +readystatechange +referrer +reftest-wait +rejectionhandled +removetrack +reset +resize +resolution +resourcetimingbufferfull +right +rtl +sans-serif +safe-area-inset-top +safe-area-inset-bottom +safe-area-inset-left +safe-area-inset-right +scan +screen +scroll-position +scrollbar-inline-size +search +seeked +seeking +select +selectend +selectionchange +selectstart +serif +sessionavailable +show +signalingstatechange +slotchange +squeeze +squeezeend +squeezestart +srclang +statechange +stroke +stroke-opacity +storage +submit +suspend +system-ui +tel +text +time +timeupdate +toggle +track +transitioncancel +transitionend +transitionrun +transitionstart +uncapturederror +unhandledrejection +unload +url +visibilitychange +volumechange +waiting +webglcontextcreationerror +webkitAnimationEnd +webkitAnimationIteration +webkitAnimationStart +webkitTransitionEnd +webkitTransitionRun +week +width diff --git a/stylo_config/Cargo.toml b/stylo_config/Cargo.toml new file mode 100644 index 0000000000..1692c21634 --- /dev/null +++ b/stylo_config/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "stylo_config" +version = "0.6.0" +authors = ["The Servo Project Developers"] +documentation = "https://docs.rs/stylo_config/" +description = "Runtime configuration for Stylo" +repository = "https://github.com/servo/stylo" +keywords = ["css", "style"] +license = "MPL-2.0" +edition = "2021" +readme = "../README.md" + +[lib] +name = "stylo_config" +path = "lib.rs" diff --git a/stylo_config/lib.rs b/stylo_config/lib.rs new file mode 100644 index 0000000000..ba504f625d --- /dev/null +++ b/stylo_config/lib.rs @@ -0,0 +1,91 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ + +use std::collections::HashMap; +use std::sync::{LazyLock, RwLock}; + +static PREFS: LazyLock = LazyLock::new(Preferences::default); + +#[derive(Debug, Default)] +pub struct Preferences { + bool_prefs: RwLock>, + i32_prefs: RwLock>, +} + +impl Preferences { + pub fn get_bool(&self, key: &str) -> bool { + let prefs = self.bool_prefs.read().expect("RwLock is poisoned"); + *prefs.get(key).unwrap_or(&false) + } + + pub fn get_i32(&self, key: &str) -> i32 { + let prefs = self.i32_prefs.read().expect("RwLock is poisoned"); + *prefs.get(key).unwrap_or(&0) + } + + pub fn set_bool(&self, key: &str, value: bool) { + let mut prefs = self.bool_prefs.write().expect("RwLock is poisoned"); + + // Avoid cloning the key if it exists. + if let Some(pref) = prefs.get_mut(key) { + *pref = value; + } else { + prefs.insert(key.to_owned(), value); + } + } + + pub fn set_i32(&self, key: &str, value: i32) { + let mut prefs = self.i32_prefs.write().expect("RwLock is poisoned"); + + // Avoid cloning the key if it exists. + if let Some(pref) = prefs.get_mut(key) { + *pref = value; + } else { + prefs.insert(key.to_owned(), value); + } + } +} + +pub fn get_bool(key: &str) -> bool { + PREFS.get_bool(key) +} + +pub fn get_i32(key: &str) -> i32 { + PREFS.get_i32(key) +} + +pub fn set_bool(key: &str, value: bool) { + PREFS.set_bool(key, value) +} + +pub fn set_i32(key: &str, value: i32) { + PREFS.set_i32(key, value) +} + +#[test] +fn test() { + let prefs = Preferences::default(); + + // Prefs have default values when unset. + assert_eq!(prefs.get_bool("foo"), false); + assert_eq!(prefs.get_i32("bar"), 0); + + // Prefs can be set and retrieved. + prefs.set_bool("foo", true); + prefs.set_i32("bar", 1); + assert_eq!(prefs.get_bool("foo"), true); + assert_eq!(prefs.get_i32("bar"), 1); + prefs.set_bool("foo", false); + prefs.set_i32("bar", 2); + assert_eq!(prefs.get_bool("foo"), false); + assert_eq!(prefs.get_i32("bar"), 2); + + // Each value type currently has an independent namespace. + prefs.set_i32("foo", 3); + prefs.set_bool("bar", true); + assert_eq!(prefs.get_i32("foo"), 3); + assert_eq!(prefs.get_bool("foo"), false); + assert_eq!(prefs.get_bool("bar"), true); + assert_eq!(prefs.get_i32("bar"), 2); +} diff --git a/stylo_dom/Cargo.toml b/stylo_dom/Cargo.toml new file mode 100644 index 0000000000..6283320d12 --- /dev/null +++ b/stylo_dom/Cargo.toml @@ -0,0 +1,18 @@ +[package] +name = "stylo_dom" +version = "0.6.0" +authors = ["The Servo Project Developers"] +documentation = "https://docs.rs/stylo_dom/" +description = "DOM state types for Stylo" +repository = "https://github.com/servo/stylo" +keywords = ["css", "style"] +license = "MPL-2.0" +edition = "2021" +readme = "../README.md" + +[lib] +path = "lib.rs" + +[dependencies] +bitflags = "2" +malloc_size_of = { version = "0.6", path = "../malloc_size_of", package = "stylo_malloc_size_of" } diff --git a/stylo_dom/lib.rs b/stylo_dom/lib.rs new file mode 100644 index 0000000000..98a0330cf4 --- /dev/null +++ b/stylo_dom/lib.rs @@ -0,0 +1,181 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ + +use bitflags::bitflags; +use malloc_size_of::malloc_size_of_is_0; + +pub const HEADING_LEVEL_OFFSET: usize = 52; + +// DOM types to be shared between Rust and C++. +bitflags! { + /// Event-based element states. + #[repr(C)] + #[derive(Clone, Copy, Debug, Eq, PartialEq)] + pub struct ElementState: u64 { + /// The mouse is down on this element. + /// + /// FIXME(#7333): set/unset this when appropriate + const ACTIVE = 1 << 0; + /// This element has focus. + /// + const FOCUS = 1 << 1; + /// The mouse is hovering over this element. + /// + const HOVER = 1 << 2; + /// Content is enabled (and can be disabled). + /// + const ENABLED = 1 << 3; + /// Content is disabled. + /// + const DISABLED = 1 << 4; + /// Content is checked. + /// + const CHECKED = 1 << 5; + /// + const INDETERMINATE = 1 << 6; + /// + const PLACEHOLDER_SHOWN = 1 << 7; + /// + const URLTARGET = 1 << 8; + /// + const FULLSCREEN = 1 << 9; + /// + const VALID = 1 << 10; + /// + const INVALID = 1 << 11; + /// + const USER_VALID = 1 << 12; + /// + const USER_INVALID = 1 << 13; + /// All the validity bits at once. + const VALIDITY_STATES = Self::VALID.bits() | Self::INVALID.bits() | Self::USER_VALID.bits() | Self::USER_INVALID.bits(); + /// Non-standard: https://developer.mozilla.org/en-US/docs/Web/CSS/:-moz-broken + const BROKEN = 1 << 14; + /// + const REQUIRED = 1 << 15; + /// + /// We use an underscore to workaround a silly windows.h define. + const OPTIONAL_ = 1 << 16; + /// + const DEFINED = 1 << 17; + /// + const VISITED = 1 << 18; + /// + const UNVISITED = 1 << 19; + /// + const VISITED_OR_UNVISITED = Self::VISITED.bits() | Self::UNVISITED.bits(); + /// Non-standard: https://developer.mozilla.org/en-US/docs/Web/CSS/:-moz-drag-over + const DRAGOVER = 1 << 20; + /// + const INRANGE = 1 << 21; + /// + const OUTOFRANGE = 1 << 22; + /// + const READONLY = 1 << 23; + /// + const READWRITE = 1 << 24; + /// + const DEFAULT = 1 << 25; + /// Non-standard & undocumented. + const OPTIMUM = 1 << 26; + /// Non-standard & undocumented. + const SUB_OPTIMUM = 1 << 27; + /// Non-standard & undocumented. + const SUB_SUB_OPTIMUM = 1 << 28; + /// All the above bits in one place. + const METER_OPTIMUM_STATES = Self::OPTIMUM.bits() | Self::SUB_OPTIMUM.bits() | Self::SUB_SUB_OPTIMUM.bits(); + /// Non-standard & undocumented. + const INCREMENT_SCRIPT_LEVEL = 1 << 29; + /// + const FOCUSRING = 1 << 30; + /// + const FOCUS_WITHIN = 1u64 << 31; + /// :dir matching; the states are used for dynamic change detection. + /// State that elements that match :dir(ltr) are in. + const LTR = 1u64 << 32; + /// State that elements that match :dir(rtl) are in. + const RTL = 1u64 << 33; + /// State that HTML elements that have a "dir" attr are in. + const HAS_DIR_ATTR = 1u64 << 34; + /// State that HTML elements with dir="ltr" (or something + /// case-insensitively equal to "ltr") are in. + const HAS_DIR_ATTR_LTR = 1u64 << 35; + /// State that HTML elements with dir="rtl" (or something + /// case-insensitively equal to "rtl") are in. + const HAS_DIR_ATTR_RTL = 1u64 << 36; + /// State that HTML elements without a valid-valued "dir" attr or + /// any HTML elements (including ) with dir="auto" (or something + /// case-insensitively equal to "auto") are in. + const HAS_DIR_ATTR_LIKE_AUTO = 1u64 << 37; + /// Non-standard & undocumented. + const AUTOFILL = 1u64 << 38; + /// Non-standard & undocumented. + const AUTOFILL_PREVIEW = 1u64 << 39; + /// State for modal elements: + /// + const MODAL = 1u64 << 40; + /// + const INERT = 1u64 << 41; + /// State for the topmost modal element in top layer + const TOPMOST_MODAL = 1u64 << 42; + /// Initially used for the devtools highlighter, but now somehow only + /// used for the devtools accessibility inspector. + const DEVTOOLS_HIGHLIGHTED = 1u64 << 43; + /// Used for the devtools style editor. Probably should go away. + const STYLEEDITOR_TRANSITIONING = 1u64 << 44; + /// For :-moz-value-empty (to show widgets like the reveal password + /// button or the clear button). + const VALUE_EMPTY = 1u64 << 45; + /// For :-moz-revealed. + const REVEALED = 1u64 << 46; + /// https://html.spec.whatwg.org/#selector-popover-open + /// Match element's popover visibility state of showing + const POPOVER_OPEN = 1u64 << 47; + /// https://drafts.csswg.org/css-scoping-1/#the-has-slotted-pseudo + /// Match whether a slot element has assigned nodes + const HAS_SLOTTED = 1u64 << 48; + /// https://drafts.csswg.org/selectors-4/#open-state + /// Match whether an openable element is currently open + const OPEN = 1u64 << 49; + /// For :active-view-transition. + /// + const ACTIVE_VIEW_TRANSITION = 1u64 << 50; + /// For :-moz-suppress-for-print-selection. + const SUPPRESS_FOR_PRINT_SELECTION = 1u64 << 51; + /// https://drafts.csswg.org/selectors-5/#headings + /// These 4 bits are used to pack the elements heading level into the element state + /// Heading levels can be from 1-9 so 4 bits allows us to express the full range. + const HEADING_LEVEL_BITS = 0b1111u64 << HEADING_LEVEL_OFFSET; + + /// Some convenience unions. + const DIR_STATES = Self::LTR.bits() | Self::RTL.bits(); + + const DIR_ATTR_STATES = Self::HAS_DIR_ATTR.bits() | + Self::HAS_DIR_ATTR_LTR.bits() | + Self::HAS_DIR_ATTR_RTL.bits() | + Self::HAS_DIR_ATTR_LIKE_AUTO.bits(); + + const DISABLED_STATES = Self::DISABLED.bits() | Self::ENABLED.bits(); + + const REQUIRED_STATES = Self::REQUIRED.bits() | Self::OPTIONAL_.bits(); + } +} + +bitflags! { + /// Event-based document states. + #[repr(C)] + #[derive(Clone, Copy, Debug, Eq, PartialEq)] + pub struct DocumentState: u64 { + /// Window activation status + const WINDOW_INACTIVE = 1 << 0; + /// RTL locale: specific to the XUL localedir attribute + const RTL_LOCALE = 1 << 1; + /// LTR locale: specific to the XUL localedir attribute + const LTR_LOCALE = 1 << 2; + + const ALL_LOCALEDIR_BITS = Self::LTR_LOCALE.bits() | Self::RTL_LOCALE.bits(); + } +} + +malloc_size_of_is_0!(ElementState, DocumentState); diff --git a/stylo_static_prefs/Cargo.toml b/stylo_static_prefs/Cargo.toml new file mode 100644 index 0000000000..54fa0950b1 --- /dev/null +++ b/stylo_static_prefs/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "stylo_static_prefs" +version = "0.6.0" +authors = ["The Servo Project Developers"] +documentation = "https://docs.rs/stylo_static_prefs/" +description = "Static configuration for Stylo" +repository = "https://github.com/servo/stylo" +keywords = ["css", "style"] +license = "MPL-2.0" +edition = "2021" +readme = "../README.md" diff --git a/stylo_static_prefs/src/lib.rs b/stylo_static_prefs/src/lib.rs new file mode 100644 index 0000000000..61a6bc2cdb --- /dev/null +++ b/stylo_static_prefs/src/lib.rs @@ -0,0 +1,42 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ + +//! A list of static preferences exposed to the style crate. These should +//! be kept sync with the preferences used by the style. +#[macro_export] +macro_rules! pref { + ("layout.css.stylo-local-work-queue.in-main-thread") => { + 32 + }; + ("layout.css.stylo-work-unit-size") => { + 16 + }; + ("layout.css.stylo-local-work-queue.in-worker") => { + 0 + }; + ("layout.css.system-ui.enabled") => { + true + }; + ("layout.css.basic-shape-rect.enabled") => { + true + }; + ("layout.css.basic-shape-xywh.enabled") => { + true + }; + ("layout.css.fit-content-function.enabled") => { + true + }; + ("layout.css.relative-color-syntax.enabled") => { + true + }; + ("layout.css.stretch-size-keyword.enabled") => { + true + }; + ("layout.css.marker.restricted") => { + true + }; + ($string:literal) => { + false + }; +} diff --git a/sync.sh b/sync.sh index e92182c746..68c8689c9c 100755 --- a/sync.sh +++ b/sync.sh @@ -30,7 +30,7 @@ fi step Cloning upstream if needed if ! [ -e upstream ]; then - git clone --bare --single-branch --progress https://github.com/mozilla/gecko-dev.git upstream + git clone --bare --single-branch --branch main --progress https://github.com/mozilla-firefox/firefox.git upstream fi step Updating upstream diff --git a/to_shmem/Cargo.toml b/to_shmem/Cargo.toml index fadd1e6e3a..58147a5e1d 100644 --- a/to_shmem/Cargo.toml +++ b/to_shmem/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "to_shmem" -version = "0.1.0" +version = "0.2.0" authors = ["The Servo Project Developers"] license = "MPL-2.0" repository = "https://github.com/servo/stylo" @@ -23,7 +23,7 @@ string_cache = ["dep:string_cache"] thin-vec = ["dep:thin-vec"] [dependencies] -cssparser = { version = "0.34", optional = true } +cssparser = { version = "0.35", optional = true } servo_arc = { version = "0.4.0", path = "../servo_arc", optional = true } smallbitvec = { version = "2.3.0", optional = true } smallvec = { version = "1.13", optional = true } From 00742b84aa81c0a34e39c7092f61b1ec1db19a19 Mon Sep 17 00:00:00 2001 From: Oriol Brufau Date: Wed, 3 Sep 2025 00:40:37 +0200 Subject: [PATCH 3/8] Fixup for https://phabricator.services.mozilla.com/D259803 Signed-off-by: Oriol Brufau --- style/properties/properties.mako.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/style/properties/properties.mako.rs b/style/properties/properties.mako.rs index e65e040a65..f5b63883c8 100644 --- a/style/properties/properties.mako.rs +++ b/style/properties/properties.mako.rs @@ -2004,6 +2004,7 @@ impl ComputedValues { PropertyDeclarationId::Longhand(id) => { let context = resolved::Context { style: self, + for_property: id.into(), }; let mut s = String::new(); self.computed_or_resolved_value( From c111ac0678db7fd8a55240635ecd18bf4777407b Mon Sep 17 00:00:00 2001 From: Oriol Brufau Date: Wed, 3 Sep 2025 18:44:03 +0200 Subject: [PATCH 4/8] Fixup for https://phabricator.services.mozilla.com/D261581 Signed-off-by: Oriol Brufau --- style/matching.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/style/matching.rs b/style/matching.rs index ac22bb6092..e1bf7d277f 100644 --- a/style/matching.rs +++ b/style/matching.rs @@ -691,7 +691,7 @@ trait PrivateMatchMethods: TElement { new_values: &Arc, pseudo_element: Option, ) -> bool { - use crate::animation::{self, AnimationSetKey, AnimationState}; + use crate::animation::{AnimationSetKey, AnimationState}; // We need to call this before accessing the `ElementAnimationSet` from the // map because this call will do a RwLock::read(). From 64d8521f1b8509a0160a89e24d0ace2883fa3269 Mon Sep 17 00:00:00 2001 From: Oriol Brufau Date: Wed, 3 Sep 2025 18:52:46 +0200 Subject: [PATCH 5/8] Bump stylo_* version to 0.7.0 Signed-off-by: Oriol Brufau --- malloc_size_of/Cargo.toml | 2 +- style/Cargo.toml | 16 ++++++++-------- style_derive/Cargo.toml | 2 +- style_traits/Cargo.toml | 6 +++--- stylo_atoms/Cargo.toml | 2 +- stylo_config/Cargo.toml | 2 +- stylo_dom/Cargo.toml | 4 ++-- stylo_static_prefs/Cargo.toml | 2 +- 8 files changed, 18 insertions(+), 18 deletions(-) diff --git a/malloc_size_of/Cargo.toml b/malloc_size_of/Cargo.toml index fdb8d690a0..e9c795b03a 100644 --- a/malloc_size_of/Cargo.toml +++ b/malloc_size_of/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "stylo_malloc_size_of" -version = "0.6.0" +version = "0.7.0" authors = ["The Servo Project Developers"] license = "MIT OR Apache-2.0" repository = "https://github.com/servo/stylo" diff --git a/style/Cargo.toml b/style/Cargo.toml index 69f1df7246..ce0e1e5a54 100644 --- a/style/Cargo.toml +++ b/style/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "stylo" -version = "0.6.0" +version = "0.7.0" authors = ["The Servo Project Developers"] license = "MPL-2.0" repository = "https://github.com/servo/stylo" @@ -60,7 +60,7 @@ bitflags = "2" byteorder = "1.0" cssparser = "0.35" derive_more = { version = "2", features = ["add", "add_assign", "deref", "deref_mut", "from"] } -dom = { version = "0.6", path = "../stylo_dom", package = "stylo_dom" } +dom = { version = "0.7", path = "../stylo_dom", package = "stylo_dom" } new_debug_unreachable = "1.0" encoding_rs = {version = "0.8", optional = true} euclid = "0.22" @@ -71,7 +71,7 @@ itertools = "0.14" itoa = "1.0" lazy_static = "1" log = "0.4" -malloc_size_of = { version = "0.6", path = "../malloc_size_of", package = "stylo_malloc_size_of" } +malloc_size_of = { version = "0.7", path = "../malloc_size_of", package = "stylo_malloc_size_of" } malloc_size_of_derive = "0.1" web_atoms = { version = "0.1.3", optional = true } matches = "0.1" @@ -87,15 +87,15 @@ rayon-core = "1" selectors = { version = "0.31.0", path = "../selectors" } serde = {version = "1.0", optional = true, features = ["derive"]} servo_arc = { version = "0.4.0", path = "../servo_arc" } -stylo_atoms = { version = "0.6", path = "../stylo_atoms", optional = true} +stylo_atoms = { version = "0.7", path = "../stylo_atoms", optional = true} smallbitvec = "2.3.0" smallvec = "1.0" static_assertions = "1.1" -static_prefs = { version = "0.6", path = "../stylo_static_prefs", package = "stylo_static_prefs" } +static_prefs = { version = "0.7", path = "../stylo_static_prefs", package = "stylo_static_prefs" } string_cache = { version = "0.8", optional = true } -style_config = { version = "0.6", path = "../stylo_config", package = "stylo_config", optional = true } -style_derive = { version = "0.6", path = "../style_derive", package = "stylo_derive" } -style_traits = { version = "0.6", path = "../style_traits", package = "stylo_traits" } +style_config = { version = "0.7", path = "../stylo_config", package = "stylo_config", optional = true } +style_derive = { version = "0.7", path = "../style_derive", package = "stylo_derive" } +style_traits = { version = "0.7", path = "../style_traits", package = "stylo_traits" } to_shmem = { version = "0.2", path = "../to_shmem" } to_shmem_derive = { version = "0.1", path = "../to_shmem_derive" } thin-vec = "0.2.1" diff --git a/style_derive/Cargo.toml b/style_derive/Cargo.toml index e5c6b63698..896d2575ed 100644 --- a/style_derive/Cargo.toml +++ b/style_derive/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "stylo_derive" -version = "0.6.0" +version = "0.7.0" authors = ["The Servo Project Developers"] license = "MPL-2.0" repository = "https://github.com/servo/stylo" diff --git a/style_traits/Cargo.toml b/style_traits/Cargo.toml index fe6458d50a..299d47e050 100644 --- a/style_traits/Cargo.toml +++ b/style_traits/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "stylo_traits" -version = "0.6.0" +version = "0.7.0" authors = ["The Servo Project Developers"] license = "MPL-2.0" repository = "https://github.com/servo/stylo" @@ -21,12 +21,12 @@ app_units = "0.7" bitflags = "2" cssparser = "0.35" euclid = "0.22" -malloc_size_of = { version = "0.6", path = "../malloc_size_of", package = "stylo_malloc_size_of" } +malloc_size_of = { version = "0.7", path = "../malloc_size_of", package = "stylo_malloc_size_of" } malloc_size_of_derive = "0.1" selectors = { version = "0.31", path = "../selectors" } serde = "1.0" servo_arc = { version = "0.4", path = "../servo_arc" } -stylo_atoms = { version = "0.6", path = "../stylo_atoms", optional = true } +stylo_atoms = { version = "0.7", path = "../stylo_atoms", optional = true } thin-vec = "0.2" to_shmem = { version = "0.2", path = "../to_shmem" } to_shmem_derive = { version = "0.1", path = "../to_shmem_derive" } diff --git a/stylo_atoms/Cargo.toml b/stylo_atoms/Cargo.toml index d80ff756e2..5cbf3fd6ff 100644 --- a/stylo_atoms/Cargo.toml +++ b/stylo_atoms/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "stylo_atoms" -version = "0.6.0" +version = "0.7.0" authors = ["The Servo Project Developers"] documentation = "https://docs.rs/stylo_atoms/" description = "Interned string type for the Servo and Stylo projects" diff --git a/stylo_config/Cargo.toml b/stylo_config/Cargo.toml index 1692c21634..f436e4d960 100644 --- a/stylo_config/Cargo.toml +++ b/stylo_config/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "stylo_config" -version = "0.6.0" +version = "0.7.0" authors = ["The Servo Project Developers"] documentation = "https://docs.rs/stylo_config/" description = "Runtime configuration for Stylo" diff --git a/stylo_dom/Cargo.toml b/stylo_dom/Cargo.toml index 6283320d12..2434583be5 100644 --- a/stylo_dom/Cargo.toml +++ b/stylo_dom/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "stylo_dom" -version = "0.6.0" +version = "0.7.0" authors = ["The Servo Project Developers"] documentation = "https://docs.rs/stylo_dom/" description = "DOM state types for Stylo" @@ -15,4 +15,4 @@ path = "lib.rs" [dependencies] bitflags = "2" -malloc_size_of = { version = "0.6", path = "../malloc_size_of", package = "stylo_malloc_size_of" } +malloc_size_of = { version = "0.7", path = "../malloc_size_of", package = "stylo_malloc_size_of" } diff --git a/stylo_static_prefs/Cargo.toml b/stylo_static_prefs/Cargo.toml index 54fa0950b1..088c7eb417 100644 --- a/stylo_static_prefs/Cargo.toml +++ b/stylo_static_prefs/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "stylo_static_prefs" -version = "0.6.0" +version = "0.7.0" authors = ["The Servo Project Developers"] documentation = "https://docs.rs/stylo_static_prefs/" description = "Static configuration for Stylo" From 508714f598310aa6c6faa61c274108c2ceea83f3 Mon Sep 17 00:00:00 2001 From: Nico Burns Date: Mon, 8 Sep 2025 14:56:35 +0100 Subject: [PATCH 6/8] Use `web_atoms::StyleAtom` instead of `stylo_atoms::Atom` --- malloc_size_of/Cargo.toml | 2 +- style/Cargo.toml | 6 ++---- style/context.rs | 4 ++-- style/dom_apis.rs | 2 +- style/lib.rs | 13 +++++-------- style/macros.rs | 7 +++++++ style/stylist.rs | 4 ++-- style/values/mod.rs | 2 +- style_traits/Cargo.toml | 4 ++-- style_traits/lib.rs | 2 +- style_traits/specified_value_info.rs | 2 +- stylo_atoms/Cargo.toml | 4 ++-- to_shmem/Cargo.toml | 2 +- 13 files changed, 28 insertions(+), 26 deletions(-) diff --git a/malloc_size_of/Cargo.toml b/malloc_size_of/Cargo.toml index e9c795b03a..e271ac3a26 100644 --- a/malloc_size_of/Cargo.toml +++ b/malloc_size_of/Cargo.toml @@ -21,6 +21,6 @@ selectors = { version = "0.31", path = "../selectors" } servo_arc = { version = "0.4", path = "../servo_arc" } smallbitvec = "2.3.0" smallvec = "1.13" -string_cache = { version = "0.8", optional = true } +string_cache = { version = "0.9", optional = true } thin-vec = { version = "0.2.13" } void = "1.0.2" diff --git a/style/Cargo.toml b/style/Cargo.toml index ce0e1e5a54..7973c6d39a 100644 --- a/style/Cargo.toml +++ b/style/Cargo.toml @@ -40,7 +40,6 @@ servo = [ "mime", "serde", "servo_arc/servo", - "stylo_atoms", "style_config", "string_cache", "style_traits/servo", @@ -73,7 +72,7 @@ lazy_static = "1" log = "0.4" malloc_size_of = { version = "0.7", path = "../malloc_size_of", package = "stylo_malloc_size_of" } malloc_size_of_derive = "0.1" -web_atoms = { version = "0.1.3", optional = true } +web_atoms = { git = "https://github.com/servo/html5ever", rev = "7abd5dbfbc11cca14f71d330fa8f2f0c5d3f55bb", optional = true } matches = "0.1" mime = { version = "0.3.13", optional = true } num_cpus = {version = "1.1.0"} @@ -87,12 +86,11 @@ rayon-core = "1" selectors = { version = "0.31.0", path = "../selectors" } serde = {version = "1.0", optional = true, features = ["derive"]} servo_arc = { version = "0.4.0", path = "../servo_arc" } -stylo_atoms = { version = "0.7", path = "../stylo_atoms", optional = true} smallbitvec = "2.3.0" smallvec = "1.0" static_assertions = "1.1" static_prefs = { version = "0.7", path = "../stylo_static_prefs", package = "stylo_static_prefs" } -string_cache = { version = "0.8", optional = true } +string_cache = { version = "0.9", optional = true } style_config = { version = "0.7", path = "../stylo_config", package = "stylo_config", optional = true } style_derive = { version = "0.7", path = "../style_derive", package = "stylo_derive" } style_traits = { version = "0.7", path = "../style_traits", package = "stylo_traits" } diff --git a/style/context.rs b/style/context.rs index d676791a9e..383b6e254d 100644 --- a/style/context.rs +++ b/style/context.rs @@ -25,6 +25,8 @@ use crate::stylist::Stylist; use crate::thread_state::{self, ThreadState}; use crate::traversal::DomTraversal; use crate::traversal_flags::TraversalFlags; +#[cfg(feature = "servo")] +use crate::Atom; use app_units::Au; use euclid::default::Size2D; use euclid::Scale; @@ -40,8 +42,6 @@ use style_traits::CSSPixel; use style_traits::DevicePixel; #[cfg(feature = "servo")] use style_traits::SpeculativePainter; -#[cfg(feature = "servo")] -use stylo_atoms::Atom; pub use selectors::matching::QuirksMode; diff --git a/style/dom_apis.rs b/style/dom_apis.rs index b49ac4d34d..2aad948fe6 100644 --- a/style/dom_apis.rs +++ b/style/dom_apis.rs @@ -162,7 +162,7 @@ where true } - fn check_outer_dependency(&mut self, _: &Dependency, _: E, _:Option) -> bool { + fn check_outer_dependency(&mut self, _: &Dependency, _: E, _: Option) -> bool { debug_assert!( false, "How? We should only have parent-less dependencies here!" diff --git a/style/lib.rs b/style/lib.rs index 3974869aa0..30c4f58b1c 100644 --- a/style/lib.rs +++ b/style/lib.rs @@ -61,9 +61,6 @@ extern crate num_derive; #[macro_use] extern crate serde; pub use servo_arc; -#[cfg(feature = "servo")] -#[macro_use] -extern crate stylo_atoms; #[macro_use] extern crate static_assertions; #[macro_use] @@ -138,6 +135,8 @@ pub mod values; pub use crate::gecko_string_cache as string_cache; #[cfg(feature = "gecko")] pub use crate::gecko_string_cache::Atom; +#[cfg(feature = "gecko")] +use crate::gecko_string_cache::WeakAtom; /// The namespace prefix type for Gecko, which is just an atom. #[cfg(feature = "gecko")] pub type Prefix = crate::values::AtomIdent; @@ -148,7 +147,9 @@ pub type LocalName = crate::values::AtomIdent; pub use crate::gecko_string_cache::Namespace; #[cfg(feature = "servo")] -pub use stylo_atoms::Atom; +pub use web_atoms::StyleAtom as Atom; +#[cfg(feature = "servo")] +use web_atoms::StyleAtom as WeakAtom; #[cfg(feature = "servo")] #[allow(missing_docs)] @@ -195,10 +196,6 @@ macro_rules! reexport_computed_values { } } longhand_properties_idents!(reexport_computed_values); -#[cfg(feature = "gecko")] -use crate::gecko_string_cache::WeakAtom; -#[cfg(feature = "servo")] -use stylo_atoms::Atom as WeakAtom; /// Extension methods for selectors::attr::CaseSensitivity pub trait CaseSensitivityExt { diff --git a/style/macros.rs b/style/macros.rs index fdf3c30290..211516e912 100644 --- a/style/macros.rs +++ b/style/macros.rs @@ -38,6 +38,13 @@ macro_rules! try_match_ident_ignore_ascii_case { }} } +#[cfg(feature = "servo")] +macro_rules! atom { + ($s:tt) => { + web_atoms::style_atom!($s) + }; +} + #[cfg(feature = "servo")] macro_rules! local_name { ($s:tt) => { diff --git a/style/stylist.rs b/style/stylist.rs index 4c1107651b..28f5c46098 100644 --- a/style/stylist.rs +++ b/style/stylist.rs @@ -3441,13 +3441,13 @@ impl CascadeData { if let Some(cond) = cur_scope.condition.as_ref() { let mut dependency_vector: Vec = Vec::new(); - if cond.start.is_none(){ + if cond.start.is_none() { dependency_vector.push(Dependency::new( IMPLICIT_SCOPE.slice()[0].clone(), 0, inner_scope_dependencies.clone(), DependencyInvalidationKind::Scope( - ScopeDependencyInvalidationKind::ImplicitScope + ScopeDependencyInvalidationKind::ImplicitScope, ), )); } diff --git a/style/values/mod.rs b/style/values/mod.rs index 323cdb347b..6159e95100 100644 --- a/style/values/mod.rs +++ b/style/values/mod.rs @@ -227,7 +227,7 @@ where /// A generic CSS `` stored as an `Atom`, for the default atom set. #[cfg(feature = "servo")] -pub type AtomIdent = GenericAtomIdent; +pub type AtomIdent = GenericAtomIdent; #[cfg(feature = "servo")] impl style_traits::SpecifiedValueInfo for GenericAtomIdent {} diff --git a/style_traits/Cargo.toml b/style_traits/Cargo.toml index 299d47e050..31a66a2d39 100644 --- a/style_traits/Cargo.toml +++ b/style_traits/Cargo.toml @@ -13,7 +13,7 @@ name = "style_traits" path = "lib.rs" [features] -servo = ["stylo_atoms", "cssparser/serde", "url", "euclid/serde"] +servo = ["web_atoms", "cssparser/serde", "url", "euclid/serde"] gecko = [] [dependencies] @@ -26,7 +26,7 @@ malloc_size_of_derive = "0.1" selectors = { version = "0.31", path = "../selectors" } serde = "1.0" servo_arc = { version = "0.4", path = "../servo_arc" } -stylo_atoms = { version = "0.7", path = "../stylo_atoms", optional = true } +web_atoms = { git = "https://github.com/servo/html5ever", rev = "7abd5dbfbc11cca14f71d330fa8f2f0c5d3f55bb", optional = true } thin-vec = "0.2" to_shmem = { version = "0.2", path = "../to_shmem" } to_shmem_derive = { version = "0.1", path = "../to_shmem_derive" } diff --git a/style_traits/lib.rs b/style_traits/lib.rs index 8c336c2d3f..59633c4acc 100644 --- a/style_traits/lib.rs +++ b/style_traits/lib.rs @@ -23,7 +23,7 @@ use bitflags::bitflags; use cssparser::{CowRcStr, Token}; use selectors::parser::SelectorParseErrorKind; #[cfg(feature = "servo")] -use stylo_atoms::Atom; +use web_atoms::StyleAtom as Atom; /// One hardware pixel. /// diff --git a/style_traits/specified_value_info.rs b/style_traits/specified_value_info.rs index 4cbcf82759..72628aed9f 100644 --- a/style_traits/specified_value_info.rs +++ b/style_traits/specified_value_info.rs @@ -89,7 +89,7 @@ impl SpecifiedValueInfo for String {} impl SpecifiedValueInfo for crate::owned_str::OwnedStr {} #[cfg(feature = "servo")] -impl SpecifiedValueInfo for ::stylo_atoms::Atom {} +impl SpecifiedValueInfo for crate::Atom {} #[cfg(feature = "servo")] impl SpecifiedValueInfo for ::url::Url {} diff --git a/stylo_atoms/Cargo.toml b/stylo_atoms/Cargo.toml index 5cbf3fd6ff..2a11182743 100644 --- a/stylo_atoms/Cargo.toml +++ b/stylo_atoms/Cargo.toml @@ -14,7 +14,7 @@ readme = "../README.md" path = "lib.rs" [dependencies] -string_cache = "0.8" +string_cache = "0.9" [build-dependencies] -string_cache_codegen = "0.5" +string_cache_codegen = "0.6" diff --git a/to_shmem/Cargo.toml b/to_shmem/Cargo.toml index 58147a5e1d..da10377f56 100644 --- a/to_shmem/Cargo.toml +++ b/to_shmem/Cargo.toml @@ -27,5 +27,5 @@ cssparser = { version = "0.35", optional = true } servo_arc = { version = "0.4.0", path = "../servo_arc", optional = true } smallbitvec = { version = "2.3.0", optional = true } smallvec = { version = "1.13", optional = true } -string_cache = { version = "0.8", optional = true } +string_cache = { version = "0.9", optional = true } thin-vec = { version = "0.2.1", optional = true } From 2a663bf4e9c11272d608c35f185fbe73f4283694 Mon Sep 17 00:00:00 2001 From: Nico Burns Date: Mon, 8 Sep 2025 14:58:10 +0100 Subject: [PATCH 7/8] Delete stylo_atoms crate Signed-off-by: Nico Burns --- Cargo.toml | 1 - README.md | 3 - stylo_atoms/Cargo.toml | 20 --- stylo_atoms/build.rs | 31 ---- stylo_atoms/lib.rs | 5 - stylo_atoms/predefined_counter_styles.rs | 66 -------- stylo_atoms/static_atoms.txt | 185 ----------------------- 7 files changed, 311 deletions(-) delete mode 100644 stylo_atoms/Cargo.toml delete mode 100644 stylo_atoms/build.rs delete mode 100644 stylo_atoms/lib.rs delete mode 100644 stylo_atoms/predefined_counter_styles.rs delete mode 100644 stylo_atoms/static_atoms.txt diff --git a/Cargo.toml b/Cargo.toml index d2547c0d06..5cfd1de449 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,6 @@ [workspace] resolver = "2" members = [ - "stylo_atoms", "stylo_dom", "malloc_size_of", "rustfmt.toml", diff --git a/README.md b/README.md index 527db6b2f4..31615ea244 100644 --- a/README.md +++ b/README.md @@ -46,7 +46,6 @@ These crates are largely implementation details of Stylo, although you may need | 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 | @@ -80,7 +79,6 @@ Assuming your local `servo` and `stylo` directories are siblings, you can build [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" } @@ -95,7 +93,6 @@ Releases are made every time this repository rebases its changes on top of the l - selectors - stylo_static_prefs - stylo_config -- stylo_atoms - stylo_malloc_size_of - stylo_dom - stylo_derive diff --git a/stylo_atoms/Cargo.toml b/stylo_atoms/Cargo.toml deleted file mode 100644 index 2a11182743..0000000000 --- a/stylo_atoms/Cargo.toml +++ /dev/null @@ -1,20 +0,0 @@ -[package] -name = "stylo_atoms" -version = "0.7.0" -authors = ["The Servo Project Developers"] -documentation = "https://docs.rs/stylo_atoms/" -description = "Interned string type for the Servo and Stylo projects" -repository = "https://github.com/servo/stylo" -license = "MPL-2.0" -edition = "2018" -build = "build.rs" -readme = "../README.md" - -[lib] -path = "lib.rs" - -[dependencies] -string_cache = "0.9" - -[build-dependencies] -string_cache_codegen = "0.6" diff --git a/stylo_atoms/build.rs b/stylo_atoms/build.rs deleted file mode 100644 index b5f6775724..0000000000 --- a/stylo_atoms/build.rs +++ /dev/null @@ -1,31 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ - -use std::env; -use std::fs::File; -use std::io::{BufRead, BufReader}; -use std::path::Path; - -fn main() { - let static_atoms = - Path::new(&env::var_os("CARGO_MANIFEST_DIR").unwrap()).join("static_atoms.txt"); - let static_atoms = BufReader::new(File::open(&static_atoms).unwrap()); - let mut atom_type = string_cache_codegen::AtomType::new("Atom", "atom!"); - - macro_rules! predefined { - ($($name: expr,)+) => { - { - $( - atom_type.atom($name); - )+ - } - } - } - include!("./predefined_counter_styles.rs"); - - atom_type - .atoms(static_atoms.lines().map(Result::unwrap)) - .write_to_file(&Path::new(&env::var_os("OUT_DIR").unwrap()).join("atom.rs")) - .unwrap(); -} diff --git a/stylo_atoms/lib.rs b/stylo_atoms/lib.rs deleted file mode 100644 index 03560a40c0..0000000000 --- a/stylo_atoms/lib.rs +++ /dev/null @@ -1,5 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ - -include!(concat!(env!("OUT_DIR"), "/atom.rs")); diff --git a/stylo_atoms/predefined_counter_styles.rs b/stylo_atoms/predefined_counter_styles.rs deleted file mode 100644 index f376981e32..0000000000 --- a/stylo_atoms/predefined_counter_styles.rs +++ /dev/null @@ -1,66 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ - - // THIS FILE IS DUPLICATED FROM style/counter_style/predefined.rs. - // TO UPDATE IT: - // - Run `python style/counter_style/updated_predefined.py` - // - Re-copy style/counter_style/predefined.rs to this location - -predefined! { - "decimal", - "decimal-leading-zero", - "arabic-indic", - "armenian", - "upper-armenian", - "lower-armenian", - "bengali", - "cambodian", - "khmer", - "cjk-decimal", - "devanagari", - "georgian", - "gujarati", - "gurmukhi", - "hebrew", - "kannada", - "lao", - "malayalam", - "mongolian", - "myanmar", - "oriya", - "persian", - "lower-roman", - "upper-roman", - "tamil", - "telugu", - "thai", - "tibetan", - "lower-alpha", - "lower-latin", - "upper-alpha", - "upper-latin", - "cjk-earthly-branch", - "cjk-heavenly-stem", - "lower-greek", - "hiragana", - "hiragana-iroha", - "katakana", - "katakana-iroha", - "disc", - "circle", - "square", - "disclosure-open", - "disclosure-closed", - "japanese-informal", - "japanese-formal", - "korean-hangul-formal", - "korean-hanja-informal", - "korean-hanja-formal", - "simp-chinese-informal", - "simp-chinese-formal", - "trad-chinese-informal", - "trad-chinese-formal", - "cjk-ideographic", - "ethiopic-numeric", -} diff --git a/stylo_atoms/static_atoms.txt b/stylo_atoms/static_atoms.txt deleted file mode 100644 index 8e18e69021..0000000000 --- a/stylo_atoms/static_atoms.txt +++ /dev/null @@ -1,185 +0,0 @@ --moz-content-preferred-color-scheme --moz-device-pixel-ratio --moz-fixed-pos-containing-block --moz-gtk-csd-close-button-position --moz-gtk-csd-maximize-button-position --moz-gtk-csd-menu-radius --moz-gtk-csd-minimize-button-position --moz-gtk-csd-titlebar-button-spacing --moz-gtk-csd-titlebar-radius --moz-gtk-csd-tooltip-radius --moz-gtk-menu-radius --moz-mac-titlebar-height --moz-overlay-scrollbar-fade-duration -DOMContentLoaded -abort -activate -addtrack -animationcancel -animationend -animationiteration -animationstart -aspect-ratio -beforetoggle -beforeunload -block-size -button -canplay -canplaythrough -center -change -characteristicvaluechanged -checkbox -cancel -click -close -closing -color -complete -compositionend -compositionstart -compositionupdate -controllerchange -cursive -dark -datachannel -date -datetime-local -dir -device-pixel-ratio -durationchange -email -emptied -end -ended -error -fantasy -fetch -file -fill -fill-opacity -formdata -fullscreenchange -fullscreenerror -gattserverdisconnected -hairline -hashchange -height -hidden -icecandidate -iceconnectionstatechange -icegatheringstatechange -image -inline-size -input -inputsourceschange -invalid -keydown -keypress -kind -left -light -ltr -load -loadeddata -loadedmetadata -loadend -loadstart -message -message -messageerror -monospace -month -mousedown -mousemove -mouseover -mouseup -negotiationneeded -none -normal -number -onchange -open -orientation -pagehide -pageshow -password -pause -play -playing -popstate -postershown -prefers-color-scheme -print -progress -radio -range -ratechange -readystatechange -referrer -reftest-wait -rejectionhandled -removetrack -reset -resize -resolution -resourcetimingbufferfull -right -rtl -sans-serif -safe-area-inset-top -safe-area-inset-bottom -safe-area-inset-left -safe-area-inset-right -scan -screen -scroll-position -scrollbar-inline-size -search -seeked -seeking -select -selectend -selectionchange -selectstart -serif -sessionavailable -show -signalingstatechange -slotchange -squeeze -squeezeend -squeezestart -srclang -statechange -stroke -stroke-opacity -storage -submit -suspend -system-ui -tel -text -time -timeupdate -toggle -track -transitioncancel -transitionend -transitionrun -transitionstart -uncapturederror -unhandledrejection -unload -url -visibilitychange -volumechange -waiting -webglcontextcreationerror -webkitAnimationEnd -webkitAnimationIteration -webkitAnimationStart -webkitTransitionEnd -webkitTransitionRun -week -width From 115420f86b1f7dffb8a23f1791f4e892aa8d72d7 Mon Sep 17 00:00:00 2001 From: Nico Burns Date: Mon, 8 Sep 2025 15:09:50 +0100 Subject: [PATCH 8/8] Bump phf dependency of selectors Signed-off-by: Nico Burns --- selectors/Cargo.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/selectors/Cargo.toml b/selectors/Cargo.toml index c90cb73155..2e5ead64fc 100644 --- a/selectors/Cargo.toml +++ b/selectors/Cargo.toml @@ -25,7 +25,7 @@ cssparser = "0.35" derive_more = { version = "2", features = ["add", "add_assign"] } fxhash = "0.2" log = "0.4" -phf = "0.11" +phf = "0.13" precomputed-hash = "0.1" servo_arc = { version = "0.4", path = "../servo_arc" } smallvec = "1.0" @@ -34,4 +34,4 @@ to_shmem_derive = { version = "0.1", path = "../to_shmem_derive", optional = tru new_debug_unreachable = "1" [build-dependencies] -phf_codegen = "0.11" +phf_codegen = "0.13"