diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml new file mode 100644 index 0000000000..fa6c67068c --- /dev/null +++ b/.github/workflows/main.yml @@ -0,0 +1,75 @@ +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 + + 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 + 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..adf329ffa6 --- /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 main: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/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000000..5dbc3d8b2a --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,36 @@ +[workspace] +resolver = "2" +members = [ + "stylo_atoms", + "stylo_dom", + "malloc_size_of", + "rustfmt.toml", + "selectors", + "servo_arc", + "style", + "style_derive", + "stylo_static_prefs", + "style_traits", + "to_shmem", + "to_shmem_derive", +] +default-members = ["style"] + +[workspace.package] +version = "0.14.0" + +[workspace.dependencies] +# in-repo dependencies (separately versioned) +servo_arc = { version = "0.4.3", path = "./servo_arc" } +selectors = { version = "0.36.1", path = "./selectors" } +to_shmem = { version = "0.3.0", path = "./to_shmem", features = ["servo"] } +to_shmem_derive = { version = "0.1.0", path = "./to_shmem_derive" } + +# in-repo dependencies (main version) +malloc_size_of = { version = "0.14.0", path = "./malloc_size_of", package = "stylo_malloc_size_of", features = ["servo"] } +static_prefs = { version = "0.14.0", path = "./stylo_static_prefs", package = "stylo_static_prefs" } +stylo_atoms = { version = "0.14.0", path = "./stylo_atoms" } +dom = { version = "0.14.0", path = "./stylo_dom", package = "stylo_dom" } +style_traits = { version = "0.14.0", path = "./style_traits", features = ["servo"], package = "stylo_traits"} +style_derive = { version = "0.14.0", path = "./style_derive", package = "stylo_derive"} +stylo = { version = "0.14.0", path = "./style" } diff --git a/README.md b/README.md new file mode 100644 index 0000000000..bfc3eb5b28 --- /dev/null +++ b/README.md @@ -0,0 +1,105 @@ +Stylo +===== + +**High-Performance CSS Style Engine** + +[![Build Status](https://github.com/servo/stylo/actions/workflows/main.yml/badge.svg)](https://github.com/servo/stylo/actions) +[![Crates.io](https://img.shields.io/crates/v/stylo.svg)](https://crates.io/crates/stylo) +[![Docs](https://docs.rs/stylo/badge.svg)](https://docs.rs/stylo) +![Crates.io License](https://img.shields.io/crates/l/stylo) + +Stylo is a high-performance, browser-grade CSS style engine written in Rust that powers [Servo](https://servo.org) and [Firefox](https://firefox.com). This repo contains Servo’s downstream version of Stylo. The upstream version lives in mozilla-central with the rest of the Gecko/Firefox codebase. + +Coordination of Stylo development happens: + +- Here in Github Issues +- In the [#stylo](https://servo.zulipchat.com/#narrow/channel/417109-stylo) channel of the [Servo Zulip](https://servo.zulipchat.com/) +- In the [#layout](https://chat.mozilla.org/#/room/#layout:mozilla.org) room of the Mozilla Matrix instance (matrix.mozilla.org) + +## High-Level Documentation + +- This [Mozilla Hacks article](https://hacks.mozilla.org/2017/08/inside-a-super-fast-css-engine-quantum-css-aka-stylo) contains a high-level overview of the Stylo architecture. +- There is a [chapter](https://book.servo.org/architecture/style.html) in the Servo Book (although it is a little out of date). + +## Branches + +The branches are as follows: + +- [**upstream**](https://github.com/servo/style/tree/upstream) has upstream [mozilla-central](https://searchfox.org/mozilla-central/source/servo) filtered to the paths we care about ([style.paths](style.paths)), but is otherwise unmodified. +- [**main**](https://github.com/servo/style/tree/ci) adds our downstream patches, plus the scripts and workflows for syncing with mozilla-central on top of **upstream**. + +> [!WARNING] +> This repo syncs from upstream by creating a new branch and then rebasing our changes on top of it. This means that `git pull` will not work across syncs (you will need to use `git fetch`, `git reset` and `git rebase`). + +More information on the syncing process is available in [SYNCING.md](SYNCING.md) + +## Crates + +A guide to the crates contained within this repo + +### Stylo Crates + +These crates are largely implementation details of Stylo, although you may need to use some of them directly if you use Stylo. + +| Directory | Crate | Notes | +| --- | --- | --- | +| style | [![Crates.io](https://img.shields.io/crates/v/stylo.svg)](https://crates.io/crates/stylo) | The main Stylo crate containing the entire CSS engine | +| style_traits | [![Crates.io](https://img.shields.io/crates/v/stylo_traits.svg)](https://crates.io/crates/stylo_traits) | Types and traits which allow other code to interoperate with Stylo without depending on the main crate directly. | +| stylo_dom | [![Crates.io](https://img.shields.io/crates/v/stylo_dom.svg)](https://crates.io/crates/stylo_dom) | Similar to stylo_traits (but much smaller) | +| stylo_atoms | [![Crates.io](https://img.shields.io/crates/v/stylo_atoms.svg)](https://crates.io/crates/stylo_atoms) | [Atoms](https://docs.rs/string_cache/latest/string_cache/struct.Atom.html) for CSS and HTML event related strings | +| stylo_static_prefs | [![Crates.io](https://img.shields.io/crates/v/stylo_static_prefs.svg)](https://crates.io/crates/stylo_static_prefs) | Configuration for Stylo. Can be used to set runtime preferences (enabling/disabling properties, etc) | +| style_derive | [![Crates.io](https://img.shields.io/crates/v/stylo_derive.svg)](https://crates.io/crates/stylo_derive) | Internal derive macro for stylo crate | + +### Standalone Crates + +These crates form part of Stylo but are also be useful standalone. + +| Directory | Crate | Notes | +| --- | --- | --- | +| selectors | [![Crates.io](https://img.shields.io/crates/v/selectors.svg)](https://crates.io/crates/selectors) | CSS Selector matching | +| servo_arc | [![Crates.io](https://img.shields.io/crates/v/servo_arc.svg)](https://crates.io/crates/servo_arc) | A variant on `std::Arc` | + +You may also be interested in the `cssparser` crate which lives in the [servo/rust-cssparser](https://github.com/servo/rust-cssparser) repo. + +### Support Crates + +Low-level crates which could technically be used standalone but are unlikely to be generally useful in practice. + +| Directory | Crate | Notes | +| --- | --- | --- | +| malloc_size_of | [![Crates.io](https://img.shields.io/crates/v/stylo_malloc_size_of.svg)](https://crates.io/crates/stylo_malloc_size_of) | Heap size measurement for Stylo values | +| to_shmem | [![Crates.io](https://img.shields.io/crates/v/to_shmem.svg)](https://crates.io/crates/to_shmem) | Internal utility crate for sharing memory across processes. | +| to_shmem_derive | [![Crates.io](https://img.shields.io/crates/v/to_shmem_derive.svg)](https://crates.io/crates/to_shmem_derive) | Internal derive macro for to_shmem crate | + +## Building Servo Against a Local Copy of Stylo + +Assuming your local `servo` and `stylo` directories are siblings, you can build `servo` against `stylo` by adding the following to `servo/Cargo.toml`: + +```toml +[patch."https://github.com/servo/stylo"] +selectors = { path = "../stylo/selectors" } +servo_arc = { path = "../stylo/servo_arc" } +stylo = { path = "../stylo/style" } +stylo_atoms = { path = "../stylo/stylo_atoms" } +stylo_dom = { path = "../stylo/stylo_dom" } +stylo_malloc_size_of = { path = "../stylo/malloc_size_of" } +stylo_static_prefs = { path = "../stylo/stylo_static_prefs" } +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_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/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/malloc_size_of/Cargo.toml b/malloc_size_of/Cargo.toml index 4859a962c0..66288a8204 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.workspace = true authors = ["The Servo Project Developers"] license = "MIT OR Apache-2.0" repository = "https://github.com/servo/stylo" @@ -18,10 +18,10 @@ servo = ["string_cache"] app_units = "0.7" cssparser = "0.36" euclid = "0.22" -selectors = { path = "../selectors" } -servo_arc = { path = "../servo_arc" } +selectors = { workspace = true } +servo_arc = { workspace = true } smallbitvec = "2.3.0" -smallvec = "1.0" -string_cache = { version = "0.8", optional = true } -thin-vec = { version = "0.2.1" } +smallvec = "1.13" +string_cache = { version = "0.9", optional = true } +thin-vec = { version = "0.2.13" } void = "1.0.2" diff --git a/selectors/Cargo.toml b/selectors/Cargo.toml index de502baffd..f08bbaff2c 100644 --- a/selectors/Cargo.toml +++ b/selectors/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "selectors" -version = "0.26.0" +version = "0.36.1" authors = ["The Servo Project Developers"] documentation = "https://docs.rs/selectors/" description = "CSS Selectors matching for Rust" @@ -27,10 +27,10 @@ rustc-hash = "2.1.1" log = "0.4" phf = "0.13" precomputed-hash = "0.1" -servo_arc = { version = "0.4", path = "../servo_arc" } +servo_arc = { workspace = true } smallvec = "1.0" -to_shmem = { version = "0.1", path = "../to_shmem", features = ["servo_arc"], optional = true } -to_shmem_derive = { version = "0.1", path = "../to_shmem_derive", optional = true } +to_shmem = { workspace = true, optional = true } +to_shmem_derive = { workspace = true, optional = true } new_debug_unreachable = "1" [build-dependencies] diff --git a/selectors/README.md b/selectors/README.md index dac4a7ff91..3ce269fa3c 100644 --- a/selectors/README.md +++ b/selectors/README.md @@ -1,9 +1,8 @@ rust-selectors ============== -* [![Build Status](https://travis-ci.com/servo/rust-selectors.svg?branch=master)]( - https://travis-ci.com/servo/rust-selectors) -* [Documentation](https://docs.rs/selectors/) +* [![Build Status](https://github.com/servo/stylo/actions/workflows/main.yml/badge.svg)](https://github.com/servo/stylo/actions) +* [Documentation](https://docs.rs/selectors) * [crates.io](https://crates.io/crates/selectors) CSS Selectors library for Rust. diff --git a/servo_arc/Cargo.toml b/servo_arc/Cargo.toml index 8b0976b75d..f5ae244a50 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.3" 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/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/style/Cargo.toml b/style/Cargo.toml index 1c37aa62be..ab89b94872 100644 --- a/style/Cargo.toml +++ b/style/Cargo.toml @@ -1,11 +1,12 @@ [package] -name = "style" -version = "0.0.1" +name = "stylo" +version.workspace = true 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,14 @@ 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", "string_cache", "style_traits/servo", "url", @@ -48,6 +49,7 @@ servo = [ ] gecko_debug = [] gecko_refcount_logging = [] +nsstring = [] [dependencies] app_units = "0.7.8" @@ -57,20 +59,20 @@ bitflags = "2" byteorder = "1.0" cssparser = "0.36" derive_more = { version = "2", features = ["add", "add_assign", "deref", "deref_mut", "from"] } -dom = { path = "../../../dom/base/rust" } +dom = { workspace = true } new_debug_unreachable = "1.0" encoding_rs = {version = "0.8", optional = true} euclid = "0.22" rustc-hash = "2.1.1" -icu_segmenter = { version = "2.0", default-features = false, features = ["auto", "compiled_data"] } +icu_segmenter = { version = ">= 1.5, <= 2.*", default-features = false, features = ["auto", "compiled_data"] } indexmap = {version = "2", features = ["std"]} itertools = "0.14" itoa = "1.0" 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 } -nsstring = {path = "../../../xpcom/rust/nsstring/", optional = true} +malloc_size_of = { workspace = true } +malloc_size_of_derive = "0.1" +web_atoms = { version = "0.2.0", optional = true } +mime = { version = "0.3.13", optional = true } num_cpus = {version = "1.1.0"} num-integer = "0.1" num-traits = "0.2" @@ -79,26 +81,24 @@ parking_lot = "0.12" precomputed-hash = "0.1.1" rayon = "1" rayon-core = "1" -selectors = { path = "../selectors" } +selectors = { workspace = true } 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 = { workspace = true} +stylo_atoms = { workspace = true, optional = true} smallbitvec = "2.3.0" smallvec = "1.0" static_assertions = "1.1" -static_prefs = { path = "../../../modules/libpref/init/static_prefs" } -string_cache = { version = "0.8", optional = true } -strum = "0.27" -strum_macros = "0.27" -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"] } +static_prefs = { workspace = true} +string_cache = { version = "0.9", optional = true } +strum = "0.28" +strum_macros = "0.28" +style_derive = { workspace = true } +style_traits = { workspace = true } +to_shmem = { workspace = true} +to_shmem_derive = { workspace = true } +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 fb1f5e36ea..26176df9ad 100644 --- a/style/build.rs +++ b/style/build.rs @@ -19,7 +19,7 @@ mod build_gecko { pub static PYTHON: LazyLock = LazyLock::new(|| { env::var("PYTHON3").ok().unwrap_or_else(|| { let candidates = if cfg!(windows) { - ["python3.exe"] + ["python.exe"] } else { ["python3"] }; @@ -56,6 +56,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/color/mod.rs b/style/color/mod.rs index 93918b0a11..9d90b92b73 100644 --- a/style/color/mod.rs +++ b/style/color/mod.rs @@ -638,6 +638,30 @@ impl AbsoluteColor { self.alpha(), ) } + + /// Convert a color value to `nscolor`. + pub fn to_nscolor(&self) -> u32 { + let srgb = self.to_color_space(ColorSpace::Srgb); + u32::from_le_bytes([ + (srgb.components.0 * 255.0).round() as u8, + (srgb.components.1 * 255.0).round() as u8, + (srgb.components.2 * 255.0).round() as u8, + (srgb.alpha * 255.0).round() as u8, + ]) + } + + /// Convert a given `nscolor` to a Servo AbsoluteColor value. + pub fn from_nscolor(color: u32) -> Self { + let [r, g, b, a] = color.to_le_bytes(); + Self::srgb_legacy(r, g, b, a as f32 / 255.0) + } +} + +#[test] +fn from_nscolor_should_be_in_legacy_syntax() { + let result = AbsoluteColor::from_nscolor(0x336699CC); + assert!(result.flags.contains(ColorFlags::IS_LEGACY_SRGB)); + assert!(result.is_legacy_syntax()); } impl From for ColorSpace { diff --git a/style/context.rs b/style/context.rs index ec8eb204aa..d8c4ec8921 100644 --- a/style/context.rs +++ b/style/context.rs @@ -10,7 +10,7 @@ use crate::bloom::StyleBloom; use crate::computed_value_flags::ComputedValueFlags; use crate::data::{EagerPseudoStyles, ElementData}; use crate::derives::*; -use crate::dom::{SendElement, TElement}; +use crate::dom::{SendElement, TElement, TRestyleDamage}; #[cfg(feature = "gecko")] use crate::gecko_bindings::structs; use crate::parallel::{STACK_SAFETY_MARGIN_KB, STYLE_THREAD_STACK_SIZE_KB}; @@ -264,7 +264,7 @@ pub struct ElementCascadeInputs { impl ElementCascadeInputs { /// Construct inputs from previous cascade results, if any. #[inline] - pub fn new_from_element_data(data: &ElementData) -> Self { + pub fn new_from_element_data(data: &ElementData) -> Self { debug_assert!(data.has_styles()); ElementCascadeInputs { primary: CascadeInputs::new_from_style(data.styles.primary()), diff --git a/style/custom_properties.rs b/style/custom_properties.rs index 8a4a409cc0..18df89d77c 100644 --- a/style/custom_properties.rs +++ b/style/custom_properties.rs @@ -8,8 +8,8 @@ use crate::applicable_declarations::CascadePriority; use crate::custom_properties_map::CustomPropertiesMap; +use crate::device::Device; use crate::dom::AttributeTracker; -use crate::media_queries::Device; use crate::properties::{ CSSWideKeyword, CustomDeclaration, CustomDeclarationValue, LonghandId, LonghandIdSet, PropertyDeclaration, diff --git a/style/data.rs b/style/data.rs index 33afb8eae2..0ff41335e8 100644 --- a/style/data.rs +++ b/style/data.rs @@ -6,11 +6,11 @@ use crate::computed_value_flags::ComputedValueFlags; use crate::context::{SharedStyleContext, StackLimitChecker}; -use crate::dom::TElement; +use crate::dom::{TElement, TRestyleDamage}; use crate::invalidation::element::invalidator::InvalidationResult; use crate::invalidation::element::restyle_hints::RestyleHint; use crate::properties::ComputedValues; -use crate::selector_parser::{PseudoElement, RestyleDamage, EAGER_PSEUDO_COUNT}; +use crate::selector_parser::{PseudoElement, EAGER_PSEUDO_COUNT}; use crate::style_resolver::{PrimaryStyle, ResolvedElementStyles, ResolvedStyle}; #[cfg(feature = "gecko")] use malloc_size_of::MallocSizeOfOps; @@ -112,10 +112,7 @@ impl fmt::Debug for EagerPseudoArray { // Can't use [None; EAGER_PSEUDO_COUNT] here because it complains // about Copy not being implemented for our Arc type. -#[cfg(feature = "gecko")] const EMPTY_PSEUDO_ARRAY: &'static EagerPseudoArrayInner = &[None, None, None, None]; -#[cfg(feature = "servo")] -const EMPTY_PSEUDO_ARRAY: &'static EagerPseudoArrayInner = &[None, None, None]; impl EagerPseudoStyles { /// Returns whether there are any pseudo styles. @@ -262,7 +259,7 @@ impl fmt::Debug for ElementStyles { /// inside of layout data, which itself hangs directly off the Element. In /// both cases, it is wrapped inside an AtomicRefCell to ensure thread safety. #[derive(Debug, Default)] -pub struct ElementData { +pub struct ElementData { /// The styles for the element and its pseudo-elements. pub styles: ElementStyles, @@ -352,7 +349,14 @@ impl<'a> DerefMut for ElementDataMut<'a> { } // There's one of these per rendered elements so it better be small. -size_of_test!(ElementData, 24); +// mod element_data_test { +// use crate::dom::TRestyleDamage; +// use super::ElementData; +// #[derive(Debug, Copy, Clone, Default)] +// struct SizeOfRestyleDamage(u32); +// impl TRestyleDamage for SizeOfRestyleDamage {} +// size_of_test!(ElementData, 24); +// } /// The kind of restyle that a single element should do. #[derive(Debug)] @@ -368,11 +372,11 @@ pub enum RestyleKind { CascadeOnly, } -impl ElementData { +impl ElementData { /// Invalidates style for this element, its descendants, and later siblings, /// based on the snapshot of the element that we took when attributes or /// state changed. - pub fn invalidate_style_if_needed<'a, E: TElement>( + pub fn invalidate_style_if_needed<'a, E: TElement>( &mut self, element: E, shared_context: &SharedStyleContext, @@ -557,7 +561,7 @@ impl ElementData { /// Drops restyle flags and damage from the element. #[inline] pub fn clear_restyle_flags_and_damage(&mut self) { - self.damage = RestyleDamage::empty(); + self.damage.clear(); self.flags.remove(ElementDataFlags::WAS_RESTYLED); } diff --git a/style/gecko/media_queries.rs b/style/device/gecko.rs similarity index 61% rename from style/gecko/media_queries.rs rename to style/device/gecko.rs index 1066dd51dd..7ed8c2c7a1 100644 --- a/style/gecko/media_queries.rs +++ b/style/device/gecko.rs @@ -2,13 +2,14 @@ * 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/. */ -//! Gecko's media-query device and expression representation. +//! Gecko-specific logic for [`Device`]. use crate::color::AbsoluteColor; use crate::context::QuirksMode; use crate::custom_properties::CssEnvironment; +use crate::device::Device; use crate::font_metrics::FontMetrics; -use crate::gecko::values::{convert_absolute_color_to_nscolor, convert_nscolor_to_absolute_color}; +use crate::gecko::wrapper::GeckoElement; use crate::gecko_bindings::bindings; use crate::gecko_bindings::structs; use crate::logical_geometry::WritingMode; @@ -29,78 +30,14 @@ use euclid::default::Size2D; use euclid::{Scale, SideOffsets2D}; use parking_lot::RwLock; use servo_arc::Arc; -use std::sync::atomic::{AtomicBool, AtomicU32, AtomicUsize, Ordering}; -use std::{cmp, fmt, mem}; +use std::sync::atomic::{AtomicBool, AtomicU32, Ordering}; +use std::{cmp, fmt}; use style_traits::{CSSPixel, DevicePixel}; -/// The `Device` in Gecko wraps a pres context, has a default values computed, -/// and contains all the viewport rule state. -/// -/// This structure also contains atomics used for computing root font-relative -/// units. These atomics use relaxed ordering, since when computing the style -/// of the root element, there can't be any other style being computed at the -/// same time (given we need the style of the parent to compute everything else). -pub struct Device { +pub(super) struct ExtraDeviceData { /// NB: The document owns the styleset, who owns the stylist, and thus the /// `Device`, so having a raw document pointer here is fine. document: *const structs::Document, - default_values: Arc, - /// Current computed style of the root element, used for calculations of - /// root font-relative units. - root_style: RwLock>, - /// Font size of the root element, used for rem units in other elements. - root_font_size: AtomicU32, - /// Line height of the root element, used for rlh units in other elements. - root_line_height: AtomicU32, - /// X-height of the root element, used for rex units in other elements. - root_font_metrics_ex: AtomicU32, - /// Cap-height of the root element, used for rcap units in other elements. - root_font_metrics_cap: AtomicU32, - /// Advance measure (ch) of the root element, used for rch units in other elements. - root_font_metrics_ch: AtomicU32, - /// Ideographic advance measure of the root element, used for ric units in other elements. - root_font_metrics_ic: AtomicU32, - /// The body text color, stored as an `nscolor`, used for the "tables - /// inherit from body" quirk. - /// - /// - body_text_color: AtomicUsize, - /// Whether any styles computed in the document relied on the root font-size - /// by using rem units. - used_root_font_size: AtomicBool, - /// Whether any styles computed in the document relied on the root line-height - /// by using rlh units. - used_root_line_height: AtomicBool, - /// Whether any styles computed in the document relied on the root font metrics - /// by using rcap, rch, rex, or ric units. This is a lock instead of an atomic - /// in order to prevent concurrent writes to the root font metric values. - used_root_font_metrics: RwLock, - /// Whether any styles computed in the document relied on font metrics. - used_font_metrics: AtomicBool, - /// Whether any styles computed in the document relied on the viewport size - /// by using vw/vh/vmin/vmax units. - used_viewport_size: AtomicBool, - /// Whether any styles computed in the document relied on the viewport size - /// by using dvw/dvh/dvmin/dvmax units. - used_dynamic_viewport_size: AtomicBool, - /// The CssEnvironment object responsible of getting CSS environment - /// variables. - environment: CssEnvironment, -} - -impl fmt::Debug for Device { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - use nsstring::nsCString; - - let mut doc_uri = nsCString::new(); - unsafe { - bindings::Gecko_nsIURI_Debug((*self.document()).mDocumentURI.raw(), &mut doc_uri) - }; - - f.debug_struct("Device") - .field("document_url", &doc_uri) - .finish() - } } unsafe impl Sync for Device {} @@ -115,7 +52,6 @@ impl Device { let default_values = ComputedValues::default_values(doc); let root_style = RwLock::new(Arc::clone(&default_values)); Device { - document, default_values: default_values, root_style: root_style, root_font_size: AtomicU32::new(FONT_MEDIUM_PX.to_bits()), @@ -124,10 +60,6 @@ impl Device { root_font_metrics_cap: AtomicU32::new(FONT_MEDIUM_CAP_PX.to_bits()), root_font_metrics_ch: AtomicU32::new(FONT_MEDIUM_CH_PX.to_bits()), root_font_metrics_ic: AtomicU32::new(FONT_MEDIUM_IC_PX.to_bits()), - - // This gets updated when we see the , so it doesn't really - // matter which color-scheme we look at here. - body_text_color: AtomicUsize::new(prefs.mLightColors.mDefault as usize), used_root_font_size: AtomicBool::new(false), used_root_line_height: AtomicBool::new(false), used_root_font_metrics: RwLock::new(false), @@ -135,15 +67,13 @@ impl Device { used_viewport_size: AtomicBool::new(false), used_dynamic_viewport_size: AtomicBool::new(false), environment: CssEnvironment, + // This gets updated when we see the , so it doesn't really + // matter which color-scheme we look at here. + body_text_color: AtomicU32::new(prefs.mLightColors.mDefault), + extra: ExtraDeviceData { document }, } } - /// Get the relevant environment to resolve `env()` functions. - #[inline] - pub fn environment(&self) -> &CssEnvironment { - &self.environment - } - /// Returns the computed line-height for the font in a given computed values instance. /// /// If you pass down an element, then the used line-height is returned. @@ -151,7 +81,7 @@ impl Device { &self, font: &crate::properties::style_structs::Font, writing_mode: WritingMode, - element: Option, + element: Option, ) -> NonNegativeLength { let pres_context = self.pres_context(); let line_height = font.clone_line_height(); @@ -180,123 +110,11 @@ impl Device { } } - /// Returns the default computed values as a reference, in order to match - /// Servo. - pub fn default_computed_values(&self) -> &ComputedValues { - &self.default_values - } - - /// Returns the default computed values as an `Arc`. - pub fn default_computed_values_arc(&self) -> &Arc { - &self.default_values - } - - /// Store a pointer to the root element's computed style, for use in - /// calculation of root font-relative metrics. - pub fn set_root_style(&self, style: &Arc) { - *self.root_style.write() = style.clone(); - } - - /// Get the font size of the root element (for rem) - pub fn root_font_size(&self) -> Length { - self.used_root_font_size.store(true, Ordering::Relaxed); - Length::new(f32::from_bits(self.root_font_size.load(Ordering::Relaxed))) - } - - /// Set the font size of the root element (for rem), in zoom-independent CSS pixels. - pub fn set_root_font_size(&self, size: f32) { - self.root_font_size.store(size.to_bits(), Ordering::Relaxed) - } - - /// Get the line height of the root element (for rlh) - pub fn root_line_height(&self) -> Length { - self.used_root_line_height.store(true, Ordering::Relaxed); - Length::new(f32::from_bits( - self.root_line_height.load(Ordering::Relaxed), - )) - } - - /// Set the line height of the root element (for rlh), in zoom-independent CSS pixels. - pub fn set_root_line_height(&self, size: f32) { - self.root_line_height - .store(size.to_bits(), Ordering::Relaxed); - } - - /// Get the x-height of the root element (for rex) - pub fn root_font_metrics_ex(&self) -> Length { - self.ensure_root_font_metrics_updated(); - Length::new(f32::from_bits( - self.root_font_metrics_ex.load(Ordering::Relaxed), - )) - } - - /// Set the x-height of the root element (for rex), in zoom-independent CSS pixels. - pub fn set_root_font_metrics_ex(&self, size: f32) -> bool { - let size = size.to_bits(); - let previous = self.root_font_metrics_ex.swap(size, Ordering::Relaxed); - previous != size - } - - /// Get the cap-height of the root element (for rcap) - pub fn root_font_metrics_cap(&self) -> Length { - self.ensure_root_font_metrics_updated(); - Length::new(f32::from_bits( - self.root_font_metrics_cap.load(Ordering::Relaxed), - )) - } - - /// Set the cap-height of the root element (for rcap), in zoom-independent CSS pixels. - pub fn set_root_font_metrics_cap(&self, size: f32) -> bool { - let size = size.to_bits(); - let previous = self.root_font_metrics_cap.swap(size, Ordering::Relaxed); - previous != size - } - - /// Get the advance measure of the root element (for rch) - pub fn root_font_metrics_ch(&self) -> Length { - self.ensure_root_font_metrics_updated(); - Length::new(f32::from_bits( - self.root_font_metrics_ch.load(Ordering::Relaxed), - )) - } - - /// Set the advance measure of the root element (for rch), in zoom-independent CSS pixels. - pub fn set_root_font_metrics_ch(&self, size: f32) -> bool { - let size = size.to_bits(); - let previous = self.root_font_metrics_ch.swap(size, Ordering::Relaxed); - previous != size - } - - /// Get the ideographic advance measure of the root element (for ric) - pub fn root_font_metrics_ic(&self) -> Length { - self.ensure_root_font_metrics_updated(); - Length::new(f32::from_bits( - self.root_font_metrics_ic.load(Ordering::Relaxed), - )) - } - - /// Set the ideographic advance measure of the root element (for ric), in zoom-independent CSS pixels. - pub fn set_root_font_metrics_ic(&self, size: f32) -> bool { - let size = size.to_bits(); - let previous = self.root_font_metrics_ic.swap(size, Ordering::Relaxed); - previous != size - } - /// The quirks mode of the document. pub fn quirks_mode(&self) -> QuirksMode { self.document().mCompatMode.into() } - /// Sets the body text color for the "inherit color from body" quirk. - /// - /// - pub fn set_body_text_color(&self, color: AbsoluteColor) { - self.body_text_color.store( - convert_absolute_color_to_nscolor(&color) as usize, - Ordering::Relaxed, - ) - } - /// Gets the base size given a generic font family and a language. pub fn base_size_for_generic(&self, language: &Atom, generic: GenericFontFamily) -> Length { unsafe { bindings::Gecko_GetBaseSize(self.document(), language.as_ptr(), generic) } @@ -361,64 +179,10 @@ impl Device { } } - fn ensure_root_font_metrics_updated(&self) { - let mut guard = self.used_root_font_metrics.write(); - let previously_computed = mem::replace(&mut *guard, true); - if !previously_computed { - self.update_root_font_metrics(); - } - } - - /// Compute the root element's font metrics, and returns a bool indicating whether - /// the font metrics have changed since the previous restyle. - pub fn update_root_font_metrics(&self) -> bool { - let root_style = self.root_style.read(); - let root_effective_zoom = (*root_style).effective_zoom; - let root_font_size = (*root_style).get_font().clone_font_size().computed_size(); - - let root_font_metrics = self.query_font_metrics( - (*root_style).writing_mode.is_upright(), - &(*root_style).get_font(), - root_font_size, - QueryFontMetricsFlags::USE_USER_FONT_SET - | QueryFontMetricsFlags::NEEDS_CH - | QueryFontMetricsFlags::NEEDS_IC, - /* track_usage = */ false, - ); - - let mut root_font_metrics_changed = false; - root_font_metrics_changed |= self.set_root_font_metrics_ex( - root_effective_zoom.unzoom(root_font_metrics.x_height_or_default(root_font_size).px()), - ); - root_font_metrics_changed |= self.set_root_font_metrics_ch( - root_effective_zoom.unzoom( - root_font_metrics - .zero_advance_measure_or_default( - root_font_size, - (*root_style).writing_mode.is_upright(), - ) - .px(), - ), - ); - root_font_metrics_changed |= self.set_root_font_metrics_cap( - root_effective_zoom.unzoom(root_font_metrics.cap_height_or_default().px()), - ); - root_font_metrics_changed |= self.set_root_font_metrics_ic( - root_effective_zoom.unzoom(root_font_metrics.ic_width_or_default(root_font_size).px()), - ); - - root_font_metrics_changed - } - - /// Returns the body text color. - pub fn body_text_color(&self) -> AbsoluteColor { - convert_nscolor_to_absolute_color(self.body_text_color.load(Ordering::Relaxed) as u32) - } - /// Gets the document pointer. #[inline] pub fn document(&self) -> &structs::Document { - unsafe { &*self.document } + unsafe { &*self.extra.document } } /// Gets the pres context associated with this document. @@ -457,21 +221,6 @@ impl Device { .store(false, Ordering::Relaxed); } - /// Returns whether we ever looked up the root font size of the device. - pub fn used_root_font_size(&self) -> bool { - self.used_root_font_size.load(Ordering::Relaxed) - } - - /// Returns whether we ever looked up the root line-height of the device. - pub fn used_root_line_height(&self) -> bool { - self.used_root_line_height.load(Ordering::Relaxed) - } - - /// Returns whether we ever looked up the root font metrics of the device. - pub fn used_root_font_metrics(&self) -> bool { - *self.used_root_font_metrics.read() - } - /// Recreates all the temporary state that the `Device` stores. /// /// This includes the viewport override from `@viewport` rules, and also the @@ -590,21 +339,6 @@ impl Device { } } - /// Returns whether we ever looked up the viewport size of the Device. - pub fn used_viewport_size(&self) -> bool { - self.used_viewport_size.load(Ordering::Relaxed) - } - - /// Returns whether we ever looked up the dynamic viewport size of the Device. - pub fn used_dynamic_viewport_size(&self) -> bool { - self.used_dynamic_viewport_size.load(Ordering::Relaxed) - } - - /// Returns whether font metrics have been queried. - pub fn used_font_metrics(&self) -> bool { - self.used_font_metrics.load(Ordering::Relaxed) - } - /// Returns whether visited styles are enabled. pub fn visited_styles_enabled(&self) -> bool { unsafe { bindings::Gecko_VisitedStylesEnabled(self.document()) } @@ -674,7 +408,7 @@ impl Device { /// This is only for forced-colors/high-contrast, so looking at light colors /// is ok. pub fn default_background_color(&self) -> AbsoluteColor { - convert_nscolor_to_absolute_color( + AbsoluteColor::from_nscolor( self.system_nscolor(SystemColor::Canvas, ColorScheme::normal().bits), ) } @@ -683,7 +417,7 @@ impl Device { /// /// See above for looking at light colors only. pub fn default_color(&self) -> AbsoluteColor { - convert_nscolor_to_absolute_color( + AbsoluteColor::from_nscolor( self.system_nscolor(SystemColor::Canvastext, ColorScheme::normal().bits), ) } @@ -742,3 +476,18 @@ impl Device { self.document().mChromeRulesEnabled() } } + +impl fmt::Debug for Device { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + use nsstring::nsCString; + + let mut doc_uri = nsCString::new(); + unsafe { + bindings::Gecko_nsIURI_Debug((*self.document()).mDocumentURI.raw(), &mut doc_uri) + }; + + f.debug_struct("Device") + .field("document_url", &doc_uri) + .finish() + } +} diff --git a/style/device/mod.rs b/style/device/mod.rs new file mode 100644 index 0000000000..08a7502f8d --- /dev/null +++ b/style/device/mod.rs @@ -0,0 +1,303 @@ +/* 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/. */ + +//! Media-query device and expression representation. + +use crate::color::AbsoluteColor; +use crate::custom_properties::CssEnvironment; +#[cfg(feature = "servo")] +use crate::derives::*; +use crate::properties::ComputedValues; +use crate::values::computed::font::QueryFontMetricsFlags; +use crate::values::computed::Length; +use parking_lot::RwLock; +use servo_arc::Arc; +use std::mem; +use std::sync::atomic::{AtomicBool, AtomicU32, Ordering}; + +#[cfg(feature = "gecko")] +use crate::device::gecko::ExtraDeviceData; +#[cfg(feature = "servo")] +use crate::device::servo::ExtraDeviceData; + +#[cfg(feature = "gecko")] +pub mod gecko; +#[cfg(feature = "servo")] +pub mod servo; + +/// A device is a structure that represents the current media a given document +/// is displayed in. +/// +/// This is the struct against which media queries are evaluated, has a default +/// values computed, and contains all the viewport rule state. +/// +/// This structure also contains atomics used for computing root font-relative +/// units. These atomics use relaxed ordering, since when computing the style +/// of the root element, there can't be any other style being computed at the +/// same time (given we need the style of the parent to compute everything else). +/// +/// In Gecko, it wraps a pres context. +#[cfg_attr(feature = "servo", derive(Debug, MallocSizeOf))] +pub struct Device { + /// The default computed values for this Device. + #[cfg_attr(feature = "servo", ignore_malloc_size_of = "Arc is shared")] + default_values: Arc, + /// Current computed style of the root element, used for calculations of + /// root font-relative units. + #[cfg_attr(feature = "servo", ignore_malloc_size_of = "Arc")] + root_style: RwLock>, + /// Font size of the root element, used for rem units in other elements. + #[cfg_attr(feature = "servo", ignore_malloc_size_of = "Pure stack type")] + root_font_size: AtomicU32, + /// Line height of the root element, used for rlh units in other elements. + #[cfg_attr(feature = "servo", ignore_malloc_size_of = "Pure stack type")] + root_line_height: AtomicU32, + /// X-height of the root element, used for rex units in other elements. + #[cfg_attr(feature = "servo", ignore_malloc_size_of = "Pure stack type")] + root_font_metrics_ex: AtomicU32, + /// Cap-height of the root element, used for rcap units in other elements. + #[cfg_attr(feature = "servo", ignore_malloc_size_of = "Pure stack type")] + root_font_metrics_cap: AtomicU32, + /// Advance measure (ch) of the root element, used for rch units in other elements. + #[cfg_attr(feature = "servo", ignore_malloc_size_of = "Pure stack type")] + root_font_metrics_ch: AtomicU32, + /// Ideographic advance measure of the root element, used for ric units in other elements. + #[cfg_attr(feature = "servo", ignore_malloc_size_of = "Pure stack type")] + root_font_metrics_ic: AtomicU32, + /// Whether any styles computed in the document relied on the root font-size + /// by using rem units. + #[cfg_attr(feature = "servo", ignore_malloc_size_of = "Pure stack type")] + used_root_font_size: AtomicBool, + /// Whether any styles computed in the document relied on the root line-height + /// by using rlh units. + #[cfg_attr(feature = "servo", ignore_malloc_size_of = "Pure stack type")] + used_root_line_height: AtomicBool, + /// Whether any styles computed in the document relied on the root font metrics + /// by using rcap, rch, rex, or ric units. This is a lock instead of an atomic + /// in order to prevent concurrent writes to the root font metric values. + #[cfg_attr(feature = "servo", ignore_malloc_size_of = "Pure stack type")] + used_root_font_metrics: RwLock, + /// Whether any styles computed in the document relied on font metrics. + used_font_metrics: AtomicBool, + /// Whether any styles computed in the document relied on the viewport size + /// by using vw/vh/vmin/vmax units. + used_viewport_size: AtomicBool, + /// Whether any styles computed in the document relied on the viewport size + /// by using dvw/dvh/dvmin/dvmax units. + used_dynamic_viewport_size: AtomicBool, + /// The CssEnvironment object responsible of getting CSS environment + /// variables. + environment: CssEnvironment, + /// The body text color, stored as an `nscolor`, used for the "tables + /// inherit from body" quirk. + /// + /// + #[cfg_attr(feature = "servo", ignore_malloc_size_of = "Pure stack type")] + body_text_color: AtomicU32, + + /// Extra Gecko-specific or Servo-specific data. + extra: ExtraDeviceData, +} + +impl Device { + /// Get the relevant environment to resolve `env()` functions. + #[inline] + pub fn environment(&self) -> &CssEnvironment { + &self.environment + } + + /// Returns the default computed values as a reference, in order to match + /// Servo. + pub fn default_computed_values(&self) -> &ComputedValues { + &self.default_values + } + + /// Returns the default computed values as an `Arc`. + pub fn default_computed_values_arc(&self) -> &Arc { + &self.default_values + } + + /// Store a pointer to the root element's computed style, for use in + /// calculation of root font-relative metrics. + pub fn set_root_style(&self, style: &Arc) { + *self.root_style.write() = style.clone(); + } + + /// Get the font size of the root element (for rem) + pub fn root_font_size(&self) -> Length { + self.used_root_font_size.store(true, Ordering::Relaxed); + Length::new(f32::from_bits(self.root_font_size.load(Ordering::Relaxed))) + } + + /// Set the font size of the root element (for rem), in zoom-independent CSS pixels. + pub fn set_root_font_size(&self, size: f32) { + self.root_font_size.store(size.to_bits(), Ordering::Relaxed) + } + + /// Get the line height of the root element (for rlh) + pub fn root_line_height(&self) -> Length { + self.used_root_line_height.store(true, Ordering::Relaxed); + Length::new(f32::from_bits( + self.root_line_height.load(Ordering::Relaxed), + )) + } + + /// Set the line height of the root element (for rlh), in zoom-independent CSS pixels. + pub fn set_root_line_height(&self, size: f32) { + self.root_line_height + .store(size.to_bits(), Ordering::Relaxed); + } + + /// Get the x-height of the root element (for rex) + pub fn root_font_metrics_ex(&self) -> Length { + self.ensure_root_font_metrics_updated(); + Length::new(f32::from_bits( + self.root_font_metrics_ex.load(Ordering::Relaxed), + )) + } + + /// Set the x-height of the root element (for rex), in zoom-independent CSS pixels. + pub fn set_root_font_metrics_ex(&self, size: f32) -> bool { + let size = size.to_bits(); + let previous = self.root_font_metrics_ex.swap(size, Ordering::Relaxed); + previous != size + } + + /// Get the cap-height of the root element (for rcap) + pub fn root_font_metrics_cap(&self) -> Length { + self.ensure_root_font_metrics_updated(); + Length::new(f32::from_bits( + self.root_font_metrics_cap.load(Ordering::Relaxed), + )) + } + + /// Set the cap-height of the root element (for rcap), in zoom-independent CSS pixels. + pub fn set_root_font_metrics_cap(&self, size: f32) -> bool { + let size = size.to_bits(); + let previous = self.root_font_metrics_cap.swap(size, Ordering::Relaxed); + previous != size + } + + /// Get the advance measure of the root element (for rch) + pub fn root_font_metrics_ch(&self) -> Length { + self.ensure_root_font_metrics_updated(); + Length::new(f32::from_bits( + self.root_font_metrics_ch.load(Ordering::Relaxed), + )) + } + + /// Set the advance measure of the root element (for rch), in zoom-independent CSS pixels. + pub fn set_root_font_metrics_ch(&self, size: f32) -> bool { + let size = size.to_bits(); + let previous = self.root_font_metrics_ch.swap(size, Ordering::Relaxed); + previous != size + } + + /// Get the ideographic advance measure of the root element (for ric) + pub fn root_font_metrics_ic(&self) -> Length { + self.ensure_root_font_metrics_updated(); + Length::new(f32::from_bits( + self.root_font_metrics_ic.load(Ordering::Relaxed), + )) + } + + /// Set the ideographic advance measure of the root element (for ric), in zoom-independent CSS pixels. + pub fn set_root_font_metrics_ic(&self, size: f32) -> bool { + let size = size.to_bits(); + let previous = self.root_font_metrics_ic.swap(size, Ordering::Relaxed); + previous != size + } + + fn ensure_root_font_metrics_updated(&self) { + let mut guard = self.used_root_font_metrics.write(); + let previously_computed = mem::replace(&mut *guard, true); + if !previously_computed { + self.update_root_font_metrics(); + } + } + + /// Compute the root element's font metrics, and returns a bool indicating whether + /// the font metrics have changed since the previous restyle. + pub fn update_root_font_metrics(&self) -> bool { + let root_style = self.root_style.read(); + let root_effective_zoom = (*root_style).effective_zoom; + let root_font_size = (*root_style).get_font().clone_font_size().computed_size(); + + let root_font_metrics = self.query_font_metrics( + (*root_style).writing_mode.is_upright(), + &(*root_style).get_font(), + root_font_size, + QueryFontMetricsFlags::USE_USER_FONT_SET + | QueryFontMetricsFlags::NEEDS_CH + | QueryFontMetricsFlags::NEEDS_IC, + /* track_usage = */ false, + ); + + let mut root_font_metrics_changed = false; + root_font_metrics_changed |= self.set_root_font_metrics_ex( + root_effective_zoom.unzoom(root_font_metrics.x_height_or_default(root_font_size).px()), + ); + root_font_metrics_changed |= self.set_root_font_metrics_ch( + root_effective_zoom.unzoom( + root_font_metrics + .zero_advance_measure_or_default( + root_font_size, + (*root_style).writing_mode.is_upright(), + ) + .px(), + ), + ); + root_font_metrics_changed |= self.set_root_font_metrics_cap( + root_effective_zoom.unzoom(root_font_metrics.cap_height_or_default().px()), + ); + root_font_metrics_changed |= self.set_root_font_metrics_ic( + root_effective_zoom.unzoom(root_font_metrics.ic_width_or_default(root_font_size).px()), + ); + + root_font_metrics_changed + } + + /// Returns whether we ever looked up the root font size of the Device. + pub fn used_root_font_size(&self) -> bool { + self.used_root_font_size.load(Ordering::Relaxed) + } + + /// Returns whether we ever looked up the root line-height of the device. + pub fn used_root_line_height(&self) -> bool { + self.used_root_line_height.load(Ordering::Relaxed) + } + + /// Returns whether we ever looked up the root font metrics of the device. + pub fn used_root_font_metrics(&self) -> bool { + *self.used_root_font_metrics.read() + } + + /// Returns whether we ever looked up the viewport size of the Device. + pub fn used_viewport_size(&self) -> bool { + self.used_viewport_size.load(Ordering::Relaxed) + } + + /// Returns whether we ever looked up the dynamic viewport size of the Device. + pub fn used_dynamic_viewport_size(&self) -> bool { + self.used_dynamic_viewport_size.load(Ordering::Relaxed) + } + + /// Returns whether font metrics have been queried. + pub fn used_font_metrics(&self) -> bool { + self.used_font_metrics.load(Ordering::Relaxed) + } + + /// Returns the body text color. + pub fn body_text_color(&self) -> AbsoluteColor { + AbsoluteColor::from_nscolor(self.body_text_color.load(Ordering::Relaxed)) + } + + /// Sets the body text color for the "inherit color from body" quirk. + /// + /// + pub fn set_body_text_color(&self, color: AbsoluteColor) { + self.body_text_color + .store(color.to_nscolor(), Ordering::Relaxed) + } +} diff --git a/style/device/servo.rs b/style/device/servo.rs new file mode 100644 index 0000000000..2d14029f91 --- /dev/null +++ b/style/device/servo.rs @@ -0,0 +1,413 @@ +/* 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/. */ + +//! Servo-specific logic for [`Device`]. + +use crate::color::AbsoluteColor; +use crate::context::QuirksMode; +use crate::custom_properties::CssEnvironment; +use crate::font_metrics::FontMetrics; +use crate::logical_geometry::WritingMode; +use crate::media_queries::MediaType; +use crate::properties::style_structs::Font; +use crate::properties::ComputedValues; +use crate::queries::values::PrefersColorScheme; +use crate::values::computed::font::GenericFontFamily; +use crate::values::computed::{CSSPixelLength, Length, LineHeight, NonNegativeLength}; +use crate::values::specified::color::{ColorSchemeFlags, ForcedColors, SystemColor}; +use crate::values::specified::font::{ + QueryFontMetricsFlags, FONT_MEDIUM_CAP_PX, FONT_MEDIUM_CH_PX, FONT_MEDIUM_EX_PX, + FONT_MEDIUM_IC_PX, FONT_MEDIUM_LINE_HEIGHT_PX, FONT_MEDIUM_PX, +}; +use crate::values::specified::ViewportVariant; +use crate::values::KeyframesName; +use app_units::{Au, AU_PER_PX}; +use euclid::default::Size2D as UntypedSize2D; +use euclid::{Scale, SideOffsets2D, Size2D}; +use malloc_size_of_derive::MallocSizeOf; +use mime::Mime; +use parking_lot::RwLock; +use servo_arc::Arc; +use std::fmt::Debug; +use std::sync::atomic::{AtomicBool, AtomicU32, Ordering}; +use style_traits::{CSSPixel, DevicePixel}; + +use crate::device::Device; + +/// A trait used to query font metrics in clients of Stylo. This is used by Device to +/// query font metrics in a way that is specific to the client using Stylo. +pub trait FontMetricsProvider: Debug + Sync { + /// Query the font metrics for the given font and the given base font size. + fn query_font_metrics( + &self, + vertical: bool, + font: &Font, + base_size: CSSPixelLength, + flags: QueryFontMetricsFlags, + ) -> FontMetrics; + /// Gets the base size given a generic font family. + fn base_size_for_generic(&self, generic: GenericFontFamily) -> Length; +} + +#[derive(Debug, MallocSizeOf)] +pub(super) struct ExtraDeviceData { + /// The current media type used by de device. + media_type: MediaType, + /// The current viewport size, in CSS pixels. + viewport_size: Size2D, + /// The current device pixel ratio, from CSS pixels to device pixels. + device_pixel_ratio: Scale, + /// The current quirks mode. + #[ignore_malloc_size_of = "Pure stack type"] + quirks_mode: QuirksMode, + /// Whether the user prefers light mode or dark mode + #[ignore_malloc_size_of = "Pure stack type"] + prefers_color_scheme: PrefersColorScheme, + /// An implementation of a trait which implements support for querying font metrics. + #[ignore_malloc_size_of = "Owned by embedder"] + font_metrics_provider: Box, +} + +impl Device { + /// Trivially construct a new `Device`. + pub fn new( + media_type: MediaType, + quirks_mode: QuirksMode, + viewport_size: Size2D, + device_pixel_ratio: Scale, + font_metrics_provider: Box, + default_values: Arc, + prefers_color_scheme: PrefersColorScheme, + ) -> Device { + let root_style = RwLock::new(Arc::clone(&default_values)); + Device { + root_style, + root_font_size: AtomicU32::new(FONT_MEDIUM_PX.to_bits()), + root_line_height: AtomicU32::new(FONT_MEDIUM_LINE_HEIGHT_PX.to_bits()), + root_font_metrics_ex: AtomicU32::new(FONT_MEDIUM_EX_PX.to_bits()), + root_font_metrics_cap: AtomicU32::new(FONT_MEDIUM_CAP_PX.to_bits()), + root_font_metrics_ch: AtomicU32::new(FONT_MEDIUM_CH_PX.to_bits()), + root_font_metrics_ic: AtomicU32::new(FONT_MEDIUM_IC_PX.to_bits()), + used_root_font_size: AtomicBool::new(false), + used_root_line_height: AtomicBool::new(false), + used_root_font_metrics: RwLock::new(false), + used_font_metrics: AtomicBool::new(false), + used_viewport_size: AtomicBool::new(false), + used_dynamic_viewport_size: AtomicBool::new(false), + environment: CssEnvironment, + default_values, + body_text_color: AtomicU32::new(AbsoluteColor::BLACK.to_nscolor()), + extra: ExtraDeviceData { + media_type, + viewport_size, + device_pixel_ratio, + quirks_mode, + prefers_color_scheme, + font_metrics_provider, + }, + } + } + + /// Returns the computed line-height for the font in a given computed values instance. + /// + /// If you pass down an element, then the used line-height is returned. + pub fn calc_line_height( + &self, + font: &crate::properties::style_structs::Font, + _writing_mode: WritingMode, + _element: Option<()>, + ) -> NonNegativeLength { + (match font.line_height { + // TODO: compute `normal` from the font metrics + LineHeight::Normal => CSSPixelLength::new(0.), + LineHeight::Number(number) => font.font_size.computed_size() * number.0, + LineHeight::Length(length) => length.0, + }) + .into() + } + + /// Get the quirks mode of the current device. + pub fn quirks_mode(&self) -> QuirksMode { + self.extra.quirks_mode + } + + /// Gets the base size given a generic font family. + pub fn base_size_for_generic(&self, generic: GenericFontFamily) -> Length { + self.extra + .font_metrics_provider + .base_size_for_generic(generic) + } + + /// Whether a given animation name may be referenced from style. + pub fn animation_name_may_be_referenced(&self, _: &KeyframesName) -> bool { + // Assume it is, since we don't have any good way to prove it's not. + true + } + + /// Get the viewport size on this [`Device`]. + pub fn viewport_size(&self) -> Size2D { + self.extra.viewport_size + } + + /// Set the viewport size on this [`Device`]. + /// + /// Note that this does not update any associated `Stylist`. For this you must call + /// `Stylist::media_features_change_changed_style` and + /// `Stylist::force_stylesheet_origins_dirty`. + pub fn set_viewport_size(&mut self, viewport_size: Size2D) { + self.extra.viewport_size = viewport_size; + } + + /// Returns the viewport size of the current device in app units, needed, + /// among other things, to resolve viewport units. + #[inline] + pub fn au_viewport_size(&self) -> UntypedSize2D { + Size2D::new( + Au::from_f32_px(self.extra.viewport_size.width), + Au::from_f32_px(self.extra.viewport_size.height), + ) + } + + /// Like the above, but records that we've used viewport units. + pub fn au_viewport_size_for_viewport_unit_resolution( + &self, + _: ViewportVariant, + ) -> UntypedSize2D { + self.used_viewport_size.store(true, Ordering::Relaxed); + // Servo doesn't have dynamic UA interfaces that affect the viewport, + // so we can just ignore the ViewportVariant. + self.au_viewport_size() + } + + /// Returns the number of app units per device pixel we're using currently. + pub fn app_units_per_device_pixel(&self) -> i32 { + (AU_PER_PX as f32 / self.extra.device_pixel_ratio.0) as i32 + } + + /// Returns the device pixel ratio, ignoring the full zoom factor. + pub fn device_pixel_ratio_ignoring_full_zoom(&self) -> Scale { + self.extra.device_pixel_ratio + } + + /// Returns the device pixel ratio. + pub fn device_pixel_ratio(&self) -> Scale { + self.extra.device_pixel_ratio + } + + /// Set a new device pixel ratio on this [`Device`]. + /// + /// Note that this does not update any associated `Stylist`. For this you must call + /// `Stylist::media_features_change_changed_style` and + /// `Stylist::force_stylesheet_origins_dirty`. + pub fn set_device_pixel_ratio( + &mut self, + device_pixel_ratio: Scale, + ) { + self.extra.device_pixel_ratio = device_pixel_ratio; + } + + /// Gets the size of the scrollbar in CSS pixels. + pub fn scrollbar_inline_size(&self) -> CSSPixelLength { + // TODO: implement this. + CSSPixelLength::new(0.0) + } + + /// Queries font metrics using the [`FontMetricsProvider`] interface. + pub fn query_font_metrics( + &self, + vertical: bool, + font: &Font, + base_size: CSSPixelLength, + flags: QueryFontMetricsFlags, + track_usage: bool, + ) -> FontMetrics { + if track_usage { + self.used_font_metrics.store(true, Ordering::Relaxed); + } + self.extra + .font_metrics_provider + .query_font_metrics(vertical, font, base_size, flags) + } + + /// Return the media type of the current device. + pub fn media_type(&self) -> MediaType { + self.extra.media_type.clone() + } + + /// Returns whether document colors are enabled. + pub fn forced_colors(&self) -> ForcedColors { + ForcedColors::None + } + + /// Returns the default background color. + pub fn default_background_color(&self) -> AbsoluteColor { + AbsoluteColor::WHITE + } + + /// Returns the default foreground color. + pub fn default_color(&self) -> AbsoluteColor { + AbsoluteColor::BLACK + } + + /// Set the [`PrefersColorScheme`] value on this [`Device`]. + /// + /// Note that this does not update any associated `Stylist`. For this you must call + /// `Stylist::media_features_change_changed_style` and + /// `Stylist::force_stylesheet_origins_dirty`. + pub fn set_color_scheme(&mut self, new_color_scheme: PrefersColorScheme) { + self.extra.prefers_color_scheme = new_color_scheme; + } + + /// Returns the color scheme of this [`Device`]. + pub fn color_scheme(&self) -> PrefersColorScheme { + self.extra.prefers_color_scheme + } + + pub(crate) fn is_dark_color_scheme(&self, _: ColorSchemeFlags) -> bool { + false + } + + pub(crate) fn system_color( + &self, + system_color: SystemColor, + color_scheme_flags: ColorSchemeFlags, + ) -> AbsoluteColor { + fn srgb(r: u8, g: u8, b: u8) -> AbsoluteColor { + AbsoluteColor::srgb_legacy(r, g, b, 1f32) + } + + // Refer to spec + // + if self.is_dark_color_scheme(color_scheme_flags) { + // Note: is_dark_color_scheme always returns true, so this code is dead code. + match system_color { + SystemColor::Accentcolor => srgb(10, 132, 255), + SystemColor::Accentcolortext => srgb(255, 255, 255), + SystemColor::Activetext => srgb(255, 0, 0), + SystemColor::Linktext => srgb(158, 158, 255), + SystemColor::Visitedtext => srgb(208, 173, 240), + SystemColor::Buttonborder + // Deprecated system colors (CSS Color 4) mapped to Buttonborder. + | SystemColor::Activeborder + | SystemColor::Inactiveborder + | SystemColor::Threeddarkshadow + | SystemColor::Threedshadow + | SystemColor::Windowframe => srgb(255, 255, 255), + SystemColor::Buttonface + // Deprecated system colors (CSS Color 4) mapped to Buttonface. + | SystemColor::Buttonhighlight + | SystemColor::Buttonshadow + | SystemColor::Threedface + | SystemColor::Threedhighlight + | SystemColor::Threedlightshadow => srgb(107, 107, 107), + SystemColor::Buttontext => srgb(245, 245, 245), + SystemColor::Canvas + // Deprecated system colors (CSS Color 4) mapped to Canvas. + | SystemColor::Activecaption + | SystemColor::Appworkspace + | SystemColor::Background + | SystemColor::Inactivecaption + | SystemColor::Infobackground + | SystemColor::Menu + | SystemColor::Scrollbar + | SystemColor::Window => srgb(30, 30, 30), + SystemColor::Canvastext + // Deprecated system colors (CSS Color 4) mapped to Canvastext. + | SystemColor::Captiontext + | SystemColor::Infotext + | SystemColor::Menutext + | SystemColor::Windowtext => srgb(232, 232, 232), + SystemColor::Field => srgb(45, 45, 45), + SystemColor::Fieldtext => srgb(240, 240, 240), + SystemColor::Graytext + // Deprecated system colors (CSS Color 4) mapped to Graytext. + | SystemColor::Inactivecaptiontext => srgb(155, 155, 155), + SystemColor::Highlight => srgb(38, 79, 120), + SystemColor::Highlighttext => srgb(255, 255, 255), + SystemColor::Mark => srgb(102, 92, 0), + SystemColor::Marktext => srgb(255, 255, 255), + SystemColor::Selecteditem => srgb(153, 200, 255), + SystemColor::Selecteditemtext => srgb(59, 59, 59), + } + } else { + match system_color { + SystemColor::Accentcolor => srgb(0, 102, 204), + SystemColor::Accentcolortext => srgb(255, 255, 255), + SystemColor::Activetext => srgb(238, 0, 0), + SystemColor::Linktext => srgb(0, 0, 238), + SystemColor::Visitedtext => srgb(85, 26, 139), + SystemColor::Buttonborder + // Deprecated system colors (CSS Color 4) mapped to Buttonborder. + | SystemColor::Activeborder + | SystemColor::Inactiveborder + | SystemColor::Threeddarkshadow + | SystemColor::Threedshadow + | SystemColor::Windowframe => srgb(169, 169, 169), + SystemColor::Buttonface + // Deprecated system colors (CSS Color 4) mapped to Buttonface. + | SystemColor::Buttonhighlight + | SystemColor::Buttonshadow + | SystemColor::Threedface + | SystemColor::Threedhighlight + | SystemColor::Threedlightshadow => srgb(220, 220, 220), + SystemColor::Buttontext => srgb(0, 0, 0), + SystemColor::Canvas + // Deprecated system colors (CSS Color 4) mapped to Canvas. + | SystemColor::Activecaption + | SystemColor::Appworkspace + | SystemColor::Background + | SystemColor::Inactivecaption + | SystemColor::Infobackground + | SystemColor::Menu + | SystemColor::Scrollbar + | SystemColor::Window => srgb(255, 255, 255), + SystemColor::Canvastext + // Deprecated system colors (CSS Color 4) mapped to Canvastext. + | SystemColor::Captiontext + | SystemColor::Infotext + | SystemColor::Menutext + | SystemColor::Windowtext => srgb(0, 0, 0), + SystemColor::Field => srgb(255, 255, 255), + SystemColor::Fieldtext => srgb(0, 0, 0), + SystemColor::Graytext + // Deprecated system colors (CSS Color 4) mapped to Graytext. + | SystemColor::Inactivecaptiontext => srgb(109, 109, 109), + SystemColor::Highlight => srgb(0, 65, 198), + SystemColor::Highlighttext => srgb(0, 0, 0), + SystemColor::Mark => srgb(255, 235, 59), + SystemColor::Marktext => srgb(0, 0, 0), + SystemColor::Selecteditem => srgb(0, 102, 204), + SystemColor::Selecteditemtext => srgb(255, 255, 255), + } + } + } + + /// Returns safe area insets + pub fn safe_area_insets(&self) -> SideOffsets2D { + SideOffsets2D::zero() + } + + /// Returns true if the given MIME type is supported + pub fn is_supported_mime_type(&self, mime_type: &str) -> bool { + match mime_type.parse::() { + Ok(m) => { + // Keep this in sync with 'image_classifer' from + // components/net/mime_classifier.rs + m == mime::IMAGE_BMP + || m == mime::IMAGE_GIF + || m == mime::IMAGE_PNG + || m == mime::IMAGE_JPEG + || m == "image/x-icon" + || m == "image/webp" + }, + _ => false, + } + } + + /// Return whether the document is a chrome document. + #[inline] + pub fn chrome_rules_enabled_for_document(&self) -> bool { + false + } +} diff --git a/style/dom.rs b/style/dom.rs index b6cea79f6b..5dfccf6684 100644 --- a/style/dom.rs +++ b/style/dom.rs @@ -12,10 +12,10 @@ use crate::context::SharedStyleContext; #[cfg(feature = "gecko")] use crate::context::UpdateAnimationsTasks; use crate::data::{ElementData, ElementDataMut, ElementDataRef}; -use crate::media_queries::Device; +use crate::device::Device; use crate::properties::{AnimationDeclarations, ComputedValues, PropertyDeclarationBlock}; use crate::selector_map::PrecomputedHashMap; -use crate::selector_parser::{AttrValue, Lang, PseudoElement, RestyleDamage, SelectorImpl}; +use crate::selector_parser::{AttrValue, Lang, PseudoElement, SelectorImpl}; use crate::shared_lock::{Locked, SharedRwLock}; use crate::stylesheets::scope_rule::ImplicitScopeRoot; use crate::stylist::CascadeData; @@ -31,7 +31,7 @@ use smallvec::SmallVec; use std::fmt; use std::fmt::Debug; use std::hash::Hash; -use std::ops::Deref; +use std::ops::{BitOrAssign, Deref}; pub use style_traits::dom::OpaqueNode; @@ -405,6 +405,41 @@ pub trait TShadowRoot: Sized + Copy + Clone + Debug + PartialEq { } } +/// Represents restyle damage (whether to repaint, relayout, etc based on changed styles) +/// +/// Stylo is mostly agnostic to this type which is used by the embedder. This trait represents the +/// minimal interface that Stylo does need to the type. +pub trait TRestyleDamage: Copy + Clone + Default + Debug + BitOrAssign { + /// Clear/reset all damage flags + fn clear(&mut self); + /// Whether the damage is empty ("no styles changed") + fn is_empty(&self) -> bool; + /// Mark the element as needing it's (eager) pseudo-elements rebuilt + fn set_rebuild_pseudos(&mut self); +} + +/// Represents the result of comparing an element's old and new style. +#[derive(Debug)] +pub struct StyleDifference { + /// The resulting damage. + pub damage: RestyleDamage, + + /// Whether any styles changed. + pub change: StyleChange, +} + +/// Represents whether or not the style of an element has changed. +#[derive(Clone, Copy, Debug)] +pub enum StyleChange { + /// The style hasn't changed. + Unchanged, + /// The style has changed. + Changed { + /// Whether only reset structs changed. + reset_only: bool, + }, +} + /// The element trait, the main abstraction the style crate acts over. pub trait TElement: Eq @@ -420,6 +455,9 @@ pub trait TElement: /// The concrete node type. type ConcreteNode: TNode; + /// The type representing restyle damage flags + type RestyleDamage: TRestyleDamage; + /// A concrete children iterator type in order to iterate over the `Node`s. /// /// TODO(emilio): We should eventually replace this with the `impl Trait` @@ -648,7 +686,7 @@ pub trait TElement: /// Returns whether the element's styles are up-to-date after traversal /// (i.e. in post traversal). - fn has_current_styles(&self, data: &ElementData) -> bool { + fn has_current_styles(&self, data: &ElementData) -> bool { if self.has_snapshot() && !self.handled_snapshot() { return false; } @@ -736,6 +774,7 @@ pub trait TElement: /// Unsafe because it can race to allocate and leak if not used with /// exclusive access to the element. unsafe fn ensure_data(&self) -> ElementDataMut<'_>; + // unsafe fn ensure_data(&self) -> AtomicRefMut>; /// Clears the element data reference, if any. /// @@ -747,9 +786,11 @@ pub trait TElement: /// Immutably borrows the ElementData. fn borrow_data(&self) -> Option>; + // fn borrow_data(&self) -> Option>>; /// Mutably borrows the ElementData. fn mutate_data(&self) -> Option>; + // fn mutate_data(&self) -> Option>>; /// Whether we should skip any root- or item-based display property /// blockification on this element. (This function exists so that Gecko @@ -981,9 +1022,20 @@ pub trait TElement: None } - /// Compute the damage incurred by the change from the `_old` to `_new`. - fn compute_layout_damage(_old: &ComputedValues, _new: &ComputedValues) -> RestyleDamage { - Default::default() + /// Given the old and new style of this element, and whether it's a + /// pseudo-element, compute the restyle damage used to determine which + /// kind of layout or painting operations we'll need. + fn compute_style_difference( + &self, + _old: &ComputedValues, + _new: &ComputedValues, + pseudo: Option<&PseudoElement>, + ) -> StyleDifference { + debug_assert!(pseudo.map_or(true, |p| p.is_eager())); + StyleDifference { + damage: Default::default(), + change: StyleChange::Unchanged, + } } } diff --git a/style/gecko/mod.rs b/style/gecko/mod.rs index c32ded14f3..5428fdf6b4 100644 --- a/style/gecko/mod.rs +++ b/style/gecko/mod.rs @@ -11,7 +11,6 @@ pub mod arc_types; pub mod conversions; pub mod data; pub mod media_features; -pub mod media_queries; pub mod pseudo_element; pub mod restyle_damage; pub mod selector_parser; @@ -19,5 +18,4 @@ pub mod snapshot; pub mod snapshot_helpers; pub mod traversal; pub mod url; -pub mod values; pub mod wrapper; diff --git a/style/gecko/values.rs b/style/gecko/values.rs deleted file mode 100644 index 1b4f5e453a..0000000000 --- a/style/gecko/values.rs +++ /dev/null @@ -1,36 +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/. */ - -#![allow(unsafe_code)] - -//! Different kind of helpers to interact with Gecko values. - -use crate::color::{AbsoluteColor, ColorSpace}; - -/// Convert a color value to `nscolor`. -pub fn convert_absolute_color_to_nscolor(color: &AbsoluteColor) -> u32 { - let srgb = color.to_color_space(ColorSpace::Srgb); - u32::from_le_bytes([ - (srgb.components.0 * 255.0).round() as u8, - (srgb.components.1 * 255.0).round() as u8, - (srgb.components.2 * 255.0).round() as u8, - (srgb.alpha * 255.0).round() as u8, - ]) -} - -/// Convert a given `nscolor` to a Servo AbsoluteColor value. -pub fn convert_nscolor_to_absolute_color(color: u32) -> AbsoluteColor { - let [r, g, b, a] = color.to_le_bytes(); - AbsoluteColor::srgb_legacy(r, g, b, a as f32 / 255.0) -} - -#[test] -fn convert_ns_color_to_absolute_color_should_be_in_legacy_syntax() { - use crate::color::ColorFlags; - - let result = convert_nscolor_to_absolute_color(0x336699CC); - assert!(result.flags.contains(ColorFlags::IS_LEGACY_SRGB)); - - assert!(result.is_legacy_syntax()); -} diff --git a/style/gecko/wrapper.rs b/style/gecko/wrapper.rs index d76f1060b3..aabe596ac4 100644 --- a/style/gecko/wrapper.rs +++ b/style/gecko/wrapper.rs @@ -18,6 +18,7 @@ use crate::applicable_declarations::ApplicableDeclarationBlock; use crate::bloom::each_relevant_element_hash; use crate::context::{QuirksMode, SharedStyleContext, UpdateAnimationsTasks}; use crate::data::{ElementDataMut, ElementDataRef, ElementDataWrapper}; +use crate::device::Device; use crate::dom::{ AttributeProvider, LayoutIterator, NodeInfo, OpaqueNode, TDocument, TElement, TNode, TShadowRoot, @@ -54,7 +55,6 @@ use crate::gecko_bindings::structs::{nsAtom, nsIContent, nsINode_BooleanFlag}; use crate::gecko_bindings::structs::{nsINode as RawGeckoNode, Element as RawGeckoElement}; use crate::global_style_data::GLOBAL_STYLE_DATA; use crate::invalidation::element::restyle_hints::RestyleHint; -use crate::media_queries::Device; use crate::properties::{ animated_properties::{AnimationValue, AnimationValueMap}, ComputedValues, Importance, OwnedPropertyDeclarationId, PropertyDeclaration, diff --git a/style/global_style_data.rs b/style/global_style_data.rs index c1ab0f7c6e..3775f69ed5 100644 --- a/style/global_style_data.rs +++ b/style/global_style_data.rs @@ -156,7 +156,7 @@ impl StyleThreadPool { #[cfg(feature = "servo")] fn stylo_threads_pref() -> i32 { - style_config::get_i32("layout.threads") + static_prefs::pref!("layout.threads") } #[cfg(feature = "gecko")] diff --git a/style/invalidation/element/relative_selector.rs b/style/invalidation/element/relative_selector.rs index 92856553d0..e42b827c95 100644 --- a/style/invalidation/element/relative_selector.rs +++ b/style/invalidation/element/relative_selector.rs @@ -1053,7 +1053,7 @@ pub struct RelativeSelectorOuterInvalidationProcessor<'a, 'b, E: TElement> { /// The current shadow host, if any. pub host: Option, /// Data for the element being invalidated. - pub data: &'a mut ElementData, + pub data: &'a mut ElementData, /// Dependency to be processed. pub dependency: &'b Dependency, /// Matching context to use for invalidation. diff --git a/style/invalidation/element/state_and_attributes.rs b/style/invalidation/element/state_and_attributes.rs index 3b81330e0f..9b750047f9 100644 --- a/style/invalidation/element/state_and_attributes.rs +++ b/style/invalidation/element/state_and_attributes.rs @@ -7,7 +7,7 @@ use crate::context::SharedStyleContext; use crate::data::ElementData; -use crate::dom::{TElement, TNode}; +use crate::dom::{TElement, TNode, TRestyleDamage}; use crate::invalidation::element::element_wrapper::{ElementSnapshot, ElementWrapper}; use crate::invalidation::element::invalidation_map::*; use crate::invalidation::element::invalidator::{ @@ -58,7 +58,7 @@ where pub struct StateAndAttrInvalidationProcessor<'a, 'b: 'a, E: TElement> { shared_context: &'a SharedStyleContext<'b>, element: E, - data: &'a mut ElementData, + data: &'a mut ElementData, matching_context: MatchingContext<'a, E::Impl>, traversal_map: SiblingTraversalMap, } @@ -68,7 +68,7 @@ impl<'a, 'b: 'a, E: TElement + 'b> StateAndAttrInvalidationProcessor<'a, 'b, E> pub fn new( shared_context: &'a SharedStyleContext<'b>, element: E, - data: &'a mut ElementData, + data: &'a mut ElementData, selector_caches: &'a mut SelectorCaches, ) -> Self { let matching_context = MatchingContext::new_for_visited( @@ -138,7 +138,7 @@ where /// Whether we should process the descendants of a given element for style /// invalidation. -pub fn should_process_descendants(data: &ElementData) -> bool { +pub fn should_process_descendants(data: &ElementData) -> bool { !data.styles.is_display_none() && !data.hint.contains(RestyleHint::RESTYLE_DESCENDANTS) } diff --git a/style/invalidation/media_queries.rs b/style/invalidation/media_queries.rs index 1aabac4c68..1f9a61a886 100644 --- a/style/invalidation/media_queries.rs +++ b/style/invalidation/media_queries.rs @@ -6,7 +6,7 @@ use crate::context::QuirksMode; use crate::derives::*; -use crate::media_queries::Device; +use crate::device::Device; use crate::shared_lock::SharedRwLockReadGuard; use crate::stylesheets::{CustomMediaMap, DocumentRule, ImportRule, MediaRule}; use crate::stylesheets::{NestedRuleIterationCondition, StylesheetContents, SupportsRule}; diff --git a/style/invalidation/stylesheets.rs b/style/invalidation/stylesheets.rs index a6753f51c1..d043d613b8 100644 --- a/style/invalidation/stylesheets.rs +++ b/style/invalidation/stylesheets.rs @@ -10,10 +10,10 @@ use crate::context::QuirksMode; use crate::data::ElementData; use crate::derives::*; +use crate::device::Device; use crate::dom::{TDocument, TElement, TNode}; use crate::invalidation::element::element_wrapper::{ElementSnapshot, ElementWrapper}; use crate::invalidation::element::restyle_hints::RestyleHint; -use crate::media_queries::Device; use crate::selector_map::PrecomputedHashSet; use crate::selector_parser::{SelectorImpl, Snapshot, SnapshotMap}; use crate::shared_lock::SharedRwLockReadGuard; diff --git a/style/lib.rs b/style/lib.rs index 1f6a873073..ad12ef5157 100644 --- a/style/lib.rs +++ b/style/lib.rs @@ -17,6 +17,8 @@ //! //! [recalc_style_at]: traversal/fn.recalc_style_at.html //! +//! A list of supported style properties can be found as [docs::supported_properties] +//! //! Major dependencies are the [cssparser][cssparser] and [selectors][selectors] //! crates. //! @@ -37,6 +39,7 @@ extern crate gecko_profiler; pub mod gecko_string_cache; #[macro_use] extern crate log; +#[macro_use] extern crate serde; pub use servo_arc; #[cfg(feature = "servo")] @@ -71,6 +74,7 @@ pub mod counter_style; pub mod custom_properties; pub mod custom_properties_map; pub mod data; +pub mod device; pub mod dom; pub mod dom_apis; pub mod driver; @@ -117,6 +121,16 @@ pub mod use_counters; #[allow(non_camel_case_types)] pub mod values; +#[cfg(all(doc, feature = "servo"))] +/// Documentation +pub mod docs { + /// The CSS properties supported by the style system. + /// Generated from the `properties.mako.rs` template by `build.rs` + pub mod supported_properties { + #![doc = include_str!(concat!(env!("OUT_DIR"), "/css-properties.html"))] + } +} + #[cfg(feature = "gecko")] pub use crate::gecko_string_cache as string_cache; #[cfg(feature = "gecko")] diff --git a/style/matching.rs b/style/matching.rs index 5becfd6bba..1e59ff4f11 100644 --- a/style/matching.rs +++ b/style/matching.rs @@ -13,7 +13,7 @@ use crate::context::CascadeInputs; use crate::context::{ElementCascadeInputs, QuirksMode}; use crate::context::{SharedStyleContext, StyleContext}; use crate::data::{ElementData, ElementStyles}; -use crate::dom::TElement; +use crate::dom::{TElement, TRestyleDamage, StyleChange}; #[cfg(feature = "servo")] use crate::dom::TNode; use crate::invalidation::element::restyle_hints::RestyleHint; @@ -21,7 +21,7 @@ use crate::properties::longhands::display::computed_value::T as Display; use crate::properties::ComputedValues; use crate::properties::PropertyDeclarationBlock; use crate::rule_tree::{CascadeLevel, StrongRuleNode}; -use crate::selector_parser::{PseudoElement, RestyleDamage}; +use crate::selector_parser::{PseudoElement}; use crate::shared_lock::Locked; use crate::style_resolver::StyleResolverForElement; use crate::style_resolver::{PseudoElementResolution, ResolvedElementStyles}; @@ -30,28 +30,6 @@ use crate::stylist::RuleInclusion; use crate::traversal_flags::TraversalFlags; use servo_arc::{Arc, ArcBorrow}; -/// Represents the result of comparing an element's old and new style. -#[derive(Debug)] -pub struct StyleDifference { - /// The resulting damage. - pub damage: RestyleDamage, - - /// Whether any styles changed. - pub change: StyleChange, -} - -/// Represents whether or not the style of an element has changed. -#[derive(Clone, Copy, Debug)] -pub enum StyleChange { - /// The style hasn't changed. - Unchanged, - /// The style has changed. - Changed { - /// Whether only reset structs changed. - reset_only: bool, - }, -} - /// Whether or not newly computed values for an element need to be cascaded to /// children (or children might need to be re-matched, e.g., for container /// queries). @@ -778,7 +756,7 @@ trait PrivateMatchMethods: TElement { fn accumulate_damage_for( &self, shared_context: &SharedStyleContext, - damage: &mut RestyleDamage, + damage: &mut Self::RestyleDamage, old_values: &ComputedValues, new_values: &ComputedValues, pseudo: Option<&PseudoElement>, @@ -936,7 +914,7 @@ pub trait MatchMethods: TElement { fn finish_restyle( &self, context: &mut StyleContext, - data: &mut ElementData, + data: &mut ElementData, mut new_styles: ResolvedElementStyles, important_rules_changed: bool, ) -> ChildRestyleRequirement { @@ -1122,7 +1100,7 @@ pub trait MatchMethods: TElement { let old_pseudo_should_exist = old.as_ref().map_or(false, |s| pseudo.should_exist(s)); if new_pseudo_should_exist != old_pseudo_should_exist { - data.damage |= RestyleDamage::reconstruct(); + data.damage.set_rebuild_pseudos(); return restyle_requirement; } }, @@ -1157,26 +1135,6 @@ pub trait MatchMethods: TElement { ); result } - - /// Given the old and new style of this element, and whether it's a - /// pseudo-element, compute the restyle damage used to determine which - /// kind of layout or painting operations we'll need. - fn compute_style_difference( - &self, - old_values: &ComputedValues, - new_values: &ComputedValues, - pseudo: Option<&PseudoElement>, - ) -> StyleDifference { - debug_assert!(pseudo.map_or(true, |p| p.is_eager())); - #[cfg(feature = "gecko")] - { - RestyleDamage::compute_style_difference(old_values, new_values) - } - #[cfg(feature = "servo")] - { - RestyleDamage::compute_style_difference::(old_values, new_values) - } - } } impl MatchMethods for E {} diff --git a/style/media_queries/media_list.rs b/style/media_queries/media_list.rs index 63733c6f54..b644a4e3e6 100644 --- a/style/media_queries/media_list.rs +++ b/style/media_queries/media_list.rs @@ -6,9 +6,10 @@ //! //! https://drafts.csswg.org/mediaqueries/#typedef-media-query-list -use super::{Device, MediaQuery, Qualifier}; +use super::{MediaQuery, Qualifier}; use crate::context::QuirksMode; use crate::derives::*; +use crate::device::Device; use crate::error_reporting::ContextualParseError; use crate::parser::ParserContext; use crate::stylesheets::CustomMediaEvaluator; diff --git a/style/media_queries/mod.rs b/style/media_queries/mod.rs index 833f6f53cb..823ceca175 100644 --- a/style/media_queries/mod.rs +++ b/style/media_queries/mod.rs @@ -11,8 +11,3 @@ mod media_query; pub use self::media_list::MediaList; pub use self::media_query::{MediaQuery, MediaQueryType, MediaType, Qualifier}; - -#[cfg(feature = "gecko")] -pub use crate::gecko::media_queries::Device; -#[cfg(feature = "servo")] -pub use crate::servo::media_queries::Device; diff --git a/style/parallel.rs b/style/parallel.rs index d05524363b..40946a8cbe 100644 --- a/style/parallel.rs +++ b/style/parallel.rs @@ -34,8 +34,21 @@ pub const STYLE_THREAD_STACK_SIZE_KB: usize = 256; /// The minimum stack size for a thread in the styling pool, in kilobytes. /// Servo requires a bigger stack in debug builds. +/// We allow configuring the size, since running with ASAN requires an even larger +/// stack size. #[cfg(feature = "servo")] -pub const STYLE_THREAD_STACK_SIZE_KB: usize = 512; +pub const STYLE_THREAD_STACK_SIZE_KB: usize = const { + let default_stack_size = 512; + if let Some(user_def_size) = option_env!("SERVO_STYLE_THREAD_STACK_SIZE_KB") { + if let Ok(user_def_size) = usize::from_str_radix(user_def_size, 10) { + user_def_size + } else { + panic!("SERVO_STYLE_THREAD_STACK_SIZE_KB must be a valid integer") + } + } else { + default_stack_size + } +}; /// The stack margin. If we get this deep in the stack, we will skip recursive /// optimizations to ensure that there is sufficient room for non-recursive work. diff --git a/style/properties/build.py b/style/properties/build.py index e6b4bb63e1..5ad2bc0666 100644 --- a/style/properties/build.py +++ b/style/properties/build.py @@ -8,6 +8,9 @@ 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", "toml-0.10.2-py2.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 @@ -72,6 +75,7 @@ def main(): write(doc_servo, "css-properties.html", as_html) write(doc_servo, "css-properties.json", as_json) write(OUT_DIR, "css-properties.json", as_json) + write(OUT_DIR, "css-properties.html", as_html) def abort(message): diff --git a/style/properties/gecko.mako.rs b/style/properties/gecko.mako.rs index 379b956302..35d5ad36a4 100644 --- a/style/properties/gecko.mako.rs +++ b/style/properties/gecko.mako.rs @@ -11,6 +11,7 @@ use crate::Atom; use crate::logical_geometry::PhysicalSide; use crate::computed_value_flags::*; use crate::custom_properties::ComputedCustomProperties; +use crate::device::Device; use crate::gecko_bindings::bindings; % for style_struct in data.style_structs: use crate::gecko_bindings::bindings::Gecko_Construct_Default_${style_struct.gecko_ffi_name}; @@ -24,7 +25,6 @@ use crate::gecko_bindings::structs; use crate::gecko_bindings::structs::mozilla::PseudoStyleType; use crate::gecko::data::PerDocumentStyleData; use crate::logical_geometry::WritingMode; -use crate::media_queries::Device; use crate::properties::longhands; use crate::rule_tree::StrongRuleNode; use crate::selector_parser::PseudoElement; diff --git a/style/properties/properties.mako.rs b/style/properties/properties.mako.rs index 075cccd88b..0f2665b97e 100644 --- a/style/properties/properties.mako.rs +++ b/style/properties/properties.mako.rs @@ -20,7 +20,7 @@ use crate::logical_geometry::WritingMode; use malloc_size_of::{MallocSizeOf, MallocSizeOfOps}; use crate::computed_value_flags::*; use cssparser::Parser; -use crate::media_queries::Device; +use crate::device::Device; use crate::parser::ParserContext; use crate::selector_parser::PseudoElement; use crate::stylist::Stylist; @@ -391,7 +391,10 @@ impl NonCustomPropertyId { pref = getattr(property, "servo_pref") %> % if pref: - Some("${pref}"), + { + const_assert!(!static_prefs::default_value!("${pref}")); + Some("${pref}") + }, % else: None, % endif @@ -402,7 +405,8 @@ impl NonCustomPropertyId { Some(pref) => pref, }; - style_config::get_bool(pref) + // The assertions above guarantee that the pref defaults to false. + static_prefs::Preference::get(pref, false) % endif }; @@ -1380,6 +1384,7 @@ pub mod style_structs { self.${longhand.ident}.clone() } + /// Whether `self` and `other` have the same computed value for ${longhand.name}. #[allow(non_snake_case)] #[inline] pub fn ${longhand.ident}_equals(&self, other: &Self) -> bool { @@ -1948,6 +1953,14 @@ impl ComputedValues { } } } + + /// Calls the given function for each cached lazy pseudo-element style. + pub fn each_cached_lazy_pseudo(&self, mut _f: F) + where + F: FnMut(&Self), + { + // Servo doesn't currently cache lazy pseudo-element styles. + } } #[cfg(feature = "servo")] @@ -2863,7 +2876,8 @@ const_assert!(std::mem::size_of::( % if engine == "servo": % for effect_name in ["repaint", "recalculate_overflow", "rebuild_stacking_context", "rebuild_box"]: -pub(crate) fn restyle_damage_${effect_name} (old: &ComputedValues, new: &ComputedValues) -> bool { +/// Returns true if any of the properties marked as effecting "${effect_name}" damage have changed +pub fn restyle_damage_${effect_name} (old: &ComputedValues, new: &ComputedValues) -> bool { % for style_struct in data.active_style_structs(): <% longhands_affected = [effect_name in longhand.servo_restyle_damage.split() for longhand in style_struct.longhands if not longhand.logical] %> % if any(longhands_affected): diff --git a/style/properties/shorthands.rs b/style/properties/shorthands.rs index 141c8af862..9557655763 100644 --- a/style/properties/shorthands.rs +++ b/style/properties/shorthands.rs @@ -3141,6 +3141,7 @@ pub mod font_synthesis { } } +#[cfg(feature = "gecko")] pub mod text_box { pub use crate::properties::generated::shorthands::text_box::*; 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 0000000000..2f85cd7b57 Binary files /dev/null and b/style/properties/vendored_python/mako-1.3.10-py3-none-any.whl differ diff --git a/style/properties/vendored_python/markupsafe/LICENSE.txt b/style/properties/vendored_python/markupsafe/LICENSE.txt new file mode 100644 index 0000000000..e270514adb --- /dev/null +++ b/style/properties/vendored_python/markupsafe/LICENSE.txt @@ -0,0 +1,28 @@ +Copyright 2010 Pallets + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED +TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file diff --git a/style/properties/vendored_python/markupsafe/__init__.py b/style/properties/vendored_python/markupsafe/__init__.py new file mode 100644 index 0000000000..8bf187d55e --- /dev/null +++ b/style/properties/vendored_python/markupsafe/__init__.py @@ -0,0 +1,384 @@ +# Vendored from https://github.com/pallets/markupsafe/blob/1251593f6b0e3b45f2cc8aba662622bc22d6a5e2/src/markupsafe/__init__.py +# with patched section from https://github.com/pallets/markupsafe/blob/1251593f6b0e3b45f2cc8aba662622bc22d6a5e2/src/markupsafe/_native.py +from __future__ import annotations + +import collections.abc as cabc +import string +import typing as t + +# BEGIN PATCHED SECTION +def _escape_inner(s: str, /) -> 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/properties/vendored_python/toml-0.10.2-py2.py3-none-any.whl b/style/properties/vendored_python/toml-0.10.2-py2.py3-none-any.whl new file mode 100644 index 0000000000..2cb8dcbd80 Binary files /dev/null and b/style/properties/vendored_python/toml-0.10.2-py2.py3-none-any.whl differ diff --git a/style/queries/feature_expression.rs b/style/queries/feature_expression.rs index fb8801afd9..0298148348 100644 --- a/style/queries/feature_expression.rs +++ b/style/queries/feature_expression.rs @@ -39,7 +39,7 @@ impl FeatureType { #[cfg(feature = "gecko")] use crate::gecko::media_features::MEDIA_FEATURES; #[cfg(feature = "servo")] - use crate::servo::media_queries::MEDIA_FEATURES; + use crate::servo::media_features::MEDIA_FEATURES; use crate::stylesheets::container_rule::CONTAINER_FEATURES; @@ -941,7 +941,7 @@ impl QueryStyleRange { QueryExpressionValue::Custom(ident) => { // `ident` is the dashed ident, but we need the name // without "--" for custom-property lookup. - let name = Atom::from(&ident.0.as_slice()[2..]); + let name = ident.undashed(); let stylist = context .builder .stylist diff --git a/style/selector_parser.rs b/style/selector_parser.rs index a0761b3c45..fa18aed252 100644 --- a/style/selector_parser.rs +++ b/style/selector_parser.rs @@ -32,12 +32,6 @@ pub use crate::servo::selector_parser::ServoElementSnapshot as Snapshot; #[cfg(feature = "gecko")] pub use crate::gecko::snapshot::GeckoElementSnapshot as Snapshot; -#[cfg(feature = "servo")] -pub use crate::servo::restyle_damage::ServoRestyleDamage as RestyleDamage; - -#[cfg(feature = "gecko")] -pub use crate::gecko::restyle_damage::GeckoRestyleDamage as RestyleDamage; - /// Servo's selector parser. #[cfg_attr(feature = "servo", derive(MallocSizeOf))] pub struct SelectorParser<'a> { diff --git a/style/servo/media_features.rs b/style/servo/media_features.rs new file mode 100644 index 0000000000..a6a56039df --- /dev/null +++ b/style/servo/media_features.rs @@ -0,0 +1,87 @@ +/* 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/. */ + +//! Servo's media feature list and evaluator. + +use crate::derives::*; +use crate::queries::feature::{AllowsRanges, Evaluator, FeatureFlags, QueryFeatureDescription}; +use crate::queries::values::PrefersColorScheme; +use crate::values::computed::{CSSPixelLength, Context, Resolution}; +use std::fmt::Debug; + +/// https://drafts.csswg.org/mediaqueries-4/#width +fn eval_width(context: &Context) -> CSSPixelLength { + CSSPixelLength::new(context.device().au_viewport_size().width.to_f32_px()) +} + +#[derive(Clone, Copy, Debug, FromPrimitive, Parse, ToCss)] +#[repr(u8)] +enum Scan { + Progressive, + Interlace, +} + +/// https://drafts.csswg.org/mediaqueries-4/#scan +fn eval_scan(_: &Context, _: Option) -> bool { + // Since we doesn't support the 'tv' media type, the 'scan' feature never + // matches. + false +} + +/// https://drafts.csswg.org/mediaqueries-4/#resolution +fn eval_resolution(context: &Context) -> Resolution { + Resolution::from_dppx(context.device().device_pixel_ratio().0) +} + +/// https://compat.spec.whatwg.org/#css-media-queries-webkit-device-pixel-ratio +fn eval_device_pixel_ratio(context: &Context) -> f32 { + eval_resolution(context).dppx() +} + +fn eval_prefers_color_scheme(context: &Context, query_value: Option) -> bool { + match query_value { + Some(v) => context.device().color_scheme() == v, + None => true, + } +} + +/// A list with all the media features that Servo supports. +pub static MEDIA_FEATURES: [QueryFeatureDescription; 6] = [ + feature!( + atom!("width"), + AllowsRanges::Yes, + Evaluator::Length(eval_width), + FeatureFlags::empty(), + ), + feature!( + atom!("scan"), + AllowsRanges::No, + keyword_evaluator!(eval_scan, Scan), + FeatureFlags::empty(), + ), + feature!( + atom!("resolution"), + AllowsRanges::Yes, + Evaluator::Resolution(eval_resolution), + FeatureFlags::empty(), + ), + feature!( + atom!("device-pixel-ratio"), + AllowsRanges::Yes, + Evaluator::Float(eval_device_pixel_ratio), + FeatureFlags::WEBKIT_PREFIX, + ), + feature!( + atom!("-moz-device-pixel-ratio"), + AllowsRanges::Yes, + Evaluator::Float(eval_device_pixel_ratio), + FeatureFlags::empty(), + ), + feature!( + atom!("prefers-color-scheme"), + AllowsRanges::No, + keyword_evaluator!(eval_prefers_color_scheme, PrefersColorScheme), + FeatureFlags::empty(), + ), +]; diff --git a/style/servo/media_queries.rs b/style/servo/media_queries.rs deleted file mode 100644 index da9fa7c6ff..0000000000 --- a/style/servo/media_queries.rs +++ /dev/null @@ -1,611 +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/. */ - -//! Servo's media-query device and expression representation. - -use crate::color::AbsoluteColor; -use crate::context::QuirksMode; -use crate::custom_properties::CssEnvironment; -use crate::derives::*; -use crate::font_metrics::FontMetrics; -use crate::logical_geometry::WritingMode; -use crate::media_queries::MediaType; -use crate::properties::style_structs::Font; -use crate::properties::ComputedValues; -use crate::queries::feature::{AllowsRanges, Evaluator, FeatureFlags, QueryFeatureDescription}; -use crate::queries::values::PrefersColorScheme; -use crate::values::computed::font::GenericFontFamily; -use crate::values::computed::{ - CSSPixelLength, Context, Length, LineHeight, NonNegativeLength, Resolution, -}; -use crate::values::specified::color::{ColorSchemeFlags, ForcedColors}; -use crate::values::specified::font::{ - QueryFontMetricsFlags, FONT_MEDIUM_CAP_PX, FONT_MEDIUM_CH_PX, FONT_MEDIUM_EX_PX, - FONT_MEDIUM_IC_PX, FONT_MEDIUM_LINE_HEIGHT_PX, FONT_MEDIUM_PX, -}; -use crate::values::specified::ViewportVariant; -use crate::values::KeyframesName; -use app_units::{Au, AU_PER_PX}; -use euclid::default::Size2D as UntypedSize2D; -use euclid::{Scale, SideOffsets2D, Size2D}; -use mime::Mime; -use parking_lot::RwLock; -use servo_arc::Arc; -use std::fmt::Debug; -use std::mem; -use std::sync::atomic::{AtomicBool, AtomicU32, Ordering}; -use style_traits::{CSSPixel, DevicePixel}; - -/// A trait used to query font metrics in clients of Stylo. This is used by Device to -/// query font metrics in a way that is specific to the client using Stylo. -pub trait FontMetricsProvider: Debug + Sync { - /// Query the font metrics for the given font and the given base font size. - fn query_font_metrics( - &self, - vertical: bool, - font: &Font, - base_size: CSSPixelLength, - flags: QueryFontMetricsFlags, - ) -> FontMetrics; - /// Gets the base size given a generic font family. - fn base_size_for_generic(&self, generic: GenericFontFamily) -> Length; -} - -/// A device is a structure that represents the current media a given document -/// is displayed in. -/// -/// This is the struct against which media queries are evaluated. -/// and contains all the viewport rule state. -/// -/// This structure also contains atomics used for computing root font-relative -/// units. These atomics use relaxed ordering, since when computing the style -/// of the root element, there can't be any other style being computed at the -/// same time (given we need the style of the parent to compute everything else). -#[derive(Debug, MallocSizeOf)] -pub struct Device { - /// The current media type used by de device. - media_type: MediaType, - /// The current viewport size, in CSS pixels. - viewport_size: Size2D, - /// The current device pixel ratio, from CSS pixels to device pixels. - device_pixel_ratio: Scale, - /// The current quirks mode. - #[ignore_malloc_size_of = "Pure stack type"] - quirks_mode: QuirksMode, - - /// Current computed style of the root element, used for calculations of - /// root font-relative units. - #[ignore_malloc_size_of = "Arc"] - root_style: RwLock>, - /// Font size of the root element, used for rem units in other elements. - #[ignore_malloc_size_of = "Pure stack type"] - root_font_size: AtomicU32, - /// Line height of the root element, used for rlh units in other elements. - #[ignore_malloc_size_of = "Pure stack type"] - root_line_height: AtomicU32, - /// X-height of the root element, used for rex units in other elements. - #[ignore_malloc_size_of = "Pure stack type"] - root_font_metrics_ex: AtomicU32, - /// Cap-height of the root element, used for rcap units in other elements. - #[ignore_malloc_size_of = "Pure stack type"] - root_font_metrics_cap: AtomicU32, - /// Advance measure (ch) of the root element, used for rch units in other elements. - #[ignore_malloc_size_of = "Pure stack type"] - root_font_metrics_ch: AtomicU32, - /// Ideographic advance measure of the root element, used for ric units in other elements. - #[ignore_malloc_size_of = "Pure stack type"] - root_font_metrics_ic: AtomicU32, - /// Whether any styles computed in the document relied on the root font-size - /// by using rem units. - #[ignore_malloc_size_of = "Pure stack type"] - used_root_font_size: AtomicBool, - /// Whether any styles computed in the document relied on the root line-height - /// by using rlh units. - #[ignore_malloc_size_of = "Pure stack type"] - used_root_line_height: AtomicBool, - /// Whether any styles computed in the document relied on the root font metrics - /// by using rcap, rch, rex, or ric units. This is a lock instead of an atomic - /// in order to prevent concurrent writes to the root font metric values. - #[ignore_malloc_size_of = "Pure stack type"] - used_root_font_metrics: RwLock, - /// Whether any styles computed in the document relied on font metrics. - used_font_metrics: AtomicBool, - /// Whether any styles computed in the document relied on the viewport size. - #[ignore_malloc_size_of = "Pure stack type"] - used_viewport_units: AtomicBool, - /// Whether the user prefers light mode or dark mode - #[ignore_malloc_size_of = "Pure stack type"] - prefers_color_scheme: PrefersColorScheme, - /// The CssEnvironment object responsible of getting CSS environment - /// variables. - environment: CssEnvironment, - /// An implementation of a trait which implements support for querying font metrics. - #[ignore_malloc_size_of = "Owned by embedder"] - font_metrics_provider: Box, - /// The default computed values for this Device. - #[ignore_malloc_size_of = "Arc is shared"] - default_computed_values: Arc, -} - -impl Device { - /// Trivially construct a new `Device`. - pub fn new( - media_type: MediaType, - quirks_mode: QuirksMode, - viewport_size: Size2D, - device_pixel_ratio: Scale, - font_metrics_provider: Box, - default_computed_values: Arc, - prefers_color_scheme: PrefersColorScheme, - ) -> Device { - let default_values = - ComputedValues::initial_values_with_font_override(Font::initial_values()); - let root_style = RwLock::new(Arc::clone(&default_values)); - Device { - media_type, - viewport_size, - device_pixel_ratio, - quirks_mode, - root_style, - root_font_size: AtomicU32::new(FONT_MEDIUM_PX.to_bits()), - root_line_height: AtomicU32::new(FONT_MEDIUM_LINE_HEIGHT_PX.to_bits()), - root_font_metrics_ex: AtomicU32::new(FONT_MEDIUM_EX_PX.to_bits()), - root_font_metrics_cap: AtomicU32::new(FONT_MEDIUM_CAP_PX.to_bits()), - root_font_metrics_ch: AtomicU32::new(FONT_MEDIUM_CH_PX.to_bits()), - root_font_metrics_ic: AtomicU32::new(FONT_MEDIUM_IC_PX.to_bits()), - used_root_font_size: AtomicBool::new(false), - used_root_line_height: AtomicBool::new(false), - used_root_font_metrics: RwLock::new(false), - used_font_metrics: AtomicBool::new(false), - used_viewport_units: AtomicBool::new(false), - prefers_color_scheme, - environment: CssEnvironment, - font_metrics_provider, - default_computed_values, - } - } - - /// Get the relevant environment to resolve `env()` functions. - #[inline] - pub fn environment(&self) -> &CssEnvironment { - &self.environment - } - - /// Return the default computed values for this device. - pub fn default_computed_values(&self) -> &ComputedValues { - &self.default_computed_values - } - - /// Store a pointer to the root element's computed style, for use in - /// calculation of root font-relative metrics. - pub fn set_root_style(&self, style: &Arc) { - *self.root_style.write() = style.clone(); - } - - /// Get the font size of the root element (for rem) - pub fn root_font_size(&self) -> CSSPixelLength { - self.used_root_font_size.store(true, Ordering::Relaxed); - CSSPixelLength::new(f32::from_bits(self.root_font_size.load(Ordering::Relaxed))) - } - - /// Set the font size of the root element (for rem), in zoom-independent CSS pixels. - pub fn set_root_font_size(&self, size: f32) { - self.root_font_size.store(size.to_bits(), Ordering::Relaxed) - } - - /// Get the line height of the root element (for rlh) - pub fn root_line_height(&self) -> CSSPixelLength { - self.used_root_line_height.store(true, Ordering::Relaxed); - CSSPixelLength::new(f32::from_bits( - self.root_line_height.load(Ordering::Relaxed), - )) - } - - /// Set the line height of the root element (for rlh), in zoom-independent CSS pixels. - pub fn set_root_line_height(&self, size: f32) { - self.root_line_height - .store(size.to_bits(), Ordering::Relaxed); - } - - /// Get the x-height of the root element (for rex) - pub fn root_font_metrics_ex(&self) -> Length { - self.ensure_root_font_metrics_updated(); - Length::new(f32::from_bits( - self.root_font_metrics_ex.load(Ordering::Relaxed), - )) - } - - /// Set the x-height of the root element (for rex), in zoom-independent CSS pixels. - pub fn set_root_font_metrics_ex(&self, size: f32) -> bool { - let size = size.to_bits(); - let previous = self.root_font_metrics_ex.swap(size, Ordering::Relaxed); - previous != size - } - - /// Get the cap-height of the root element (for rcap) - pub fn root_font_metrics_cap(&self) -> Length { - self.ensure_root_font_metrics_updated(); - Length::new(f32::from_bits( - self.root_font_metrics_cap.load(Ordering::Relaxed), - )) - } - - /// Set the cap-height of the root element (for rcap), in zoom-independent CSS pixels. - pub fn set_root_font_metrics_cap(&self, size: f32) -> bool { - let size = size.to_bits(); - let previous = self.root_font_metrics_cap.swap(size, Ordering::Relaxed); - previous != size - } - - /// Get the advance measure of the root element (for rch) - pub fn root_font_metrics_ch(&self) -> Length { - self.ensure_root_font_metrics_updated(); - Length::new(f32::from_bits( - self.root_font_metrics_ch.load(Ordering::Relaxed), - )) - } - - /// Set the advance measure of the root element (for rch), in zoom-independent CSS pixels. - pub fn set_root_font_metrics_ch(&self, size: f32) -> bool { - let size = size.to_bits(); - let previous = self.root_font_metrics_ch.swap(size, Ordering::Relaxed); - previous != size - } - - /// Get the ideographic advance measure of the root element (for ric) - pub fn root_font_metrics_ic(&self) -> Length { - self.ensure_root_font_metrics_updated(); - Length::new(f32::from_bits( - self.root_font_metrics_ic.load(Ordering::Relaxed), - )) - } - - /// Set the ideographic advance measure of the root element (for ric), in zoom-independent CSS pixels. - pub fn set_root_font_metrics_ic(&self, size: f32) -> bool { - let size = size.to_bits(); - let previous = self.root_font_metrics_ic.swap(size, Ordering::Relaxed); - previous != size - } - - /// Returns the computed line-height for the font in a given computed values instance. - /// - /// If you pass down an element, then the used line-height is returned. - pub fn calc_line_height( - &self, - font: &crate::properties::style_structs::Font, - _writing_mode: WritingMode, - _element: Option<()>, - ) -> NonNegativeLength { - (match font.line_height { - // TODO: compute `normal` from the font metrics - LineHeight::Normal => CSSPixelLength::new(0.), - LineHeight::Number(number) => font.font_size.computed_size() * number.0, - LineHeight::Length(length) => length.0, - }) - .into() - } - - /// Get the quirks mode of the current device. - pub fn quirks_mode(&self) -> QuirksMode { - self.quirks_mode - } - - /// Sets the body text color for the "inherit color from body" quirk. - /// - /// - pub fn set_body_text_color(&self, _color: AbsoluteColor) { - // Servo doesn't implement this quirk (yet) - } - - /// Gets the base size given a generic font family. - pub fn base_size_for_generic(&self, generic: GenericFontFamily) -> Length { - self.font_metrics_provider.base_size_for_generic(generic) - } - - /// Whether a given animation name may be referenced from style. - pub fn animation_name_may_be_referenced(&self, _: &KeyframesName) -> bool { - // Assume it is, since we don't have any good way to prove it's not. - true - } - - /// Returns whether we ever looked up the root font size of the Device. - pub fn used_root_font_size(&self) -> bool { - self.used_root_font_size.load(Ordering::Relaxed) - } - - /// Returns whether we ever looked up the root line-height of the device. - pub fn used_root_line_height(&self) -> bool { - self.used_root_line_height.load(Ordering::Relaxed) - } - - /// Returns whether we ever looked up the root font metrics of the device. - pub fn used_root_font_metrics(&self) -> bool { - *self.used_root_font_metrics.read() - } - - /// Returns whether font metrics have been queried. - pub fn used_font_metrics(&self) -> bool { - self.used_font_metrics.load(Ordering::Relaxed) - } - - /// Get the viewport size on this [`Device`]. - pub fn viewport_size(&self) -> Size2D { - self.viewport_size - } - - /// Set the viewport size on this [`Device`]. - /// - /// Note that this does not update any associated `Stylist`. For this you must call - /// `Stylist::media_features_change_changed_style` and - /// `Stylist::force_stylesheet_origins_dirty`. - pub fn set_viewport_size(&mut self, viewport_size: Size2D) { - self.viewport_size = viewport_size; - } - - /// Returns the viewport size of the current device in app units, needed, - /// among other things, to resolve viewport units. - #[inline] - pub fn au_viewport_size(&self) -> UntypedSize2D { - Size2D::new( - Au::from_f32_px(self.viewport_size.width), - Au::from_f32_px(self.viewport_size.height), - ) - } - - /// Like the above, but records that we've used viewport units. - pub fn au_viewport_size_for_viewport_unit_resolution( - &self, - _: ViewportVariant, - ) -> UntypedSize2D { - self.used_viewport_units.store(true, Ordering::Relaxed); - // Servo doesn't have dynamic UA interfaces that affect the viewport, - // so we can just ignore the ViewportVariant. - self.au_viewport_size() - } - - /// Whether viewport units were used since the last device change. - pub fn used_viewport_units(&self) -> bool { - self.used_viewport_units.load(Ordering::Relaxed) - } - - /// Returns the number of app units per device pixel we're using currently. - pub fn app_units_per_device_pixel(&self) -> i32 { - (AU_PER_PX as f32 / self.device_pixel_ratio.0) as i32 - } - - /// Returns the device pixel ratio, ignoring the full zoom factor. - pub fn device_pixel_ratio_ignoring_full_zoom(&self) -> Scale { - self.device_pixel_ratio - } - - /// Returns the device pixel ratio. - pub fn device_pixel_ratio(&self) -> Scale { - self.device_pixel_ratio - } - - /// Set a new device pixel ratio on this [`Device`]. - /// - /// Note that this does not update any associated `Stylist`. For this you must call - /// `Stylist::media_features_change_changed_style` and - /// `Stylist::force_stylesheet_origins_dirty`. - pub fn set_device_pixel_ratio( - &mut self, - device_pixel_ratio: Scale, - ) { - self.device_pixel_ratio = device_pixel_ratio; - } - - /// Gets the size of the scrollbar in CSS pixels. - pub fn scrollbar_inline_size(&self) -> CSSPixelLength { - // TODO: implement this. - CSSPixelLength::new(0.0) - } - - /// Queries font metrics using the [`FontMetricsProvider`] interface. - pub fn query_font_metrics( - &self, - vertical: bool, - font: &Font, - base_size: CSSPixelLength, - flags: QueryFontMetricsFlags, - track_usage: bool, - ) -> FontMetrics { - if track_usage { - self.used_font_metrics.store(true, Ordering::Relaxed); - } - self.font_metrics_provider - .query_font_metrics(vertical, font, base_size, flags) - } - - fn ensure_root_font_metrics_updated(&self) { - let mut guard = self.used_root_font_metrics.write(); - let previously_computed = mem::replace(&mut *guard, true); - if !previously_computed { - self.update_root_font_metrics(); - } - } - - /// Compute the root element's font metrics, and returns a bool indicating whether - /// the font metrics have changed since the previous restyle. - pub fn update_root_font_metrics(&self) -> bool { - let root_style = self.root_style.read(); - let root_effective_zoom = (*root_style).effective_zoom; - let root_font_size = (*root_style).get_font().clone_font_size().computed_size(); - - let root_font_metrics = self.query_font_metrics( - (*root_style).writing_mode.is_upright(), - &(*root_style).get_font(), - root_font_size, - QueryFontMetricsFlags::USE_USER_FONT_SET - | QueryFontMetricsFlags::NEEDS_CH - | QueryFontMetricsFlags::NEEDS_IC, - /* track_usage = */ false, - ); - - let mut root_font_metrics_changed = false; - root_font_metrics_changed |= self.set_root_font_metrics_ex( - root_effective_zoom.unzoom(root_font_metrics.x_height_or_default(root_font_size).px()), - ); - root_font_metrics_changed |= self.set_root_font_metrics_ch( - root_effective_zoom.unzoom( - root_font_metrics - .zero_advance_measure_or_default( - root_font_size, - (*root_style).writing_mode.is_upright(), - ) - .px(), - ), - ); - root_font_metrics_changed |= self.set_root_font_metrics_cap( - root_effective_zoom.unzoom(root_font_metrics.cap_height_or_default().px()), - ); - root_font_metrics_changed |= self.set_root_font_metrics_ic( - root_effective_zoom.unzoom(root_font_metrics.ic_width_or_default(root_font_size).px()), - ); - - root_font_metrics_changed - } - - /// Return the media type of the current device. - pub fn media_type(&self) -> MediaType { - self.media_type.clone() - } - - /// Returns whether document colors are enabled. - pub fn forced_colors(&self) -> ForcedColors { - ForcedColors::None - } - - /// Returns the default background color. - pub fn default_background_color(&self) -> AbsoluteColor { - AbsoluteColor::WHITE - } - - /// Returns the default foreground color. - pub fn default_color(&self) -> AbsoluteColor { - AbsoluteColor::BLACK - } - - /// Set the [`PrefersColorScheme`] value on this [`Device`]. - /// - /// Note that this does not update any associated `Stylist`. For this you must call - /// `Stylist::media_features_change_changed_style` and - /// `Stylist::force_stylesheet_origins_dirty`. - pub fn set_color_scheme(&mut self, new_color_scheme: PrefersColorScheme) { - self.prefers_color_scheme = new_color_scheme; - } - - /// Returns the color scheme of this [`Device`]. - pub fn color_scheme(&self) -> PrefersColorScheme { - self.prefers_color_scheme - } - - pub(crate) fn is_dark_color_scheme(&self, _: ColorSchemeFlags) -> bool { - false - } - - /// Returns safe area insets - pub fn safe_area_insets(&self) -> SideOffsets2D { - SideOffsets2D::zero() - } - - /// Returns true if the given MIME type is supported - pub fn is_supported_mime_type(&self, mime_type: &str) -> bool { - match mime_type.parse::() { - Ok(m) => { - // Keep this in sync with 'image_classifer' from - // components/net/mime_classifier.rs - m == mime::IMAGE_BMP - || m == mime::IMAGE_GIF - || m == mime::IMAGE_PNG - || m == mime::IMAGE_JPEG - || m == "image/x-icon" - || m == "image/webp" - }, - _ => false, - } - } - - /// Return whether the document is a chrome document. - #[inline] - pub fn chrome_rules_enabled_for_document(&self) -> bool { - false - } -} - -/// https://drafts.csswg.org/mediaqueries-4/#width -fn eval_width(context: &Context) -> CSSPixelLength { - CSSPixelLength::new(context.device().au_viewport_size().width.to_f32_px()) -} - -#[derive(Clone, Copy, Debug, FromPrimitive, Parse, ToCss)] -#[repr(u8)] -enum Scan { - Progressive, - Interlace, -} - -/// https://drafts.csswg.org/mediaqueries-4/#scan -fn eval_scan(_: &Context, _: Option) -> bool { - // Since we doesn't support the 'tv' media type, the 'scan' feature never - // matches. - false -} - -/// https://drafts.csswg.org/mediaqueries-4/#resolution -fn eval_resolution(context: &Context) -> Resolution { - Resolution::from_dppx(context.device().device_pixel_ratio.0) -} - -/// https://compat.spec.whatwg.org/#css-media-queries-webkit-device-pixel-ratio -fn eval_device_pixel_ratio(context: &Context) -> f32 { - eval_resolution(context).dppx() -} - -fn eval_prefers_color_scheme(context: &Context, query_value: Option) -> bool { - match query_value { - Some(v) => context.device().prefers_color_scheme == v, - None => true, - } -} - -/// A list with all the media features that Servo supports. -pub static MEDIA_FEATURES: [QueryFeatureDescription; 6] = [ - feature!( - atom!("width"), - AllowsRanges::Yes, - Evaluator::Length(eval_width), - FeatureFlags::empty(), - ), - feature!( - atom!("scan"), - AllowsRanges::No, - keyword_evaluator!(eval_scan, Scan), - FeatureFlags::empty(), - ), - feature!( - atom!("resolution"), - AllowsRanges::Yes, - Evaluator::Resolution(eval_resolution), - FeatureFlags::empty(), - ), - feature!( - atom!("device-pixel-ratio"), - AllowsRanges::Yes, - Evaluator::Float(eval_device_pixel_ratio), - FeatureFlags::WEBKIT_PREFIX, - ), - feature!( - atom!("-moz-device-pixel-ratio"), - AllowsRanges::Yes, - Evaluator::Float(eval_device_pixel_ratio), - FeatureFlags::empty(), - ), - feature!( - atom!("prefers-color-scheme"), - AllowsRanges::No, - keyword_evaluator!(eval_prefers_color_scheme, PrefersColorScheme), - FeatureFlags::empty(), - ), -]; diff --git a/style/servo/mod.rs b/style/servo/mod.rs index 753e8cc6dd..b7bff0fc28 100644 --- a/style/servo/mod.rs +++ b/style/servo/mod.rs @@ -10,7 +10,7 @@ pub mod animation; #[allow(missing_docs)] // TODO. pub mod attr; mod encoding_support; -pub mod media_queries; +pub mod media_features; pub mod restyle_damage; pub mod selector_parser; mod shadow_parts; diff --git a/style/servo/restyle_damage.rs b/style/servo/restyle_damage.rs index 67790699ff..524405a583 100644 --- a/style/servo/restyle_damage.rs +++ b/style/servo/restyle_damage.rs @@ -2,138 +2,21 @@ * 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/. */ -//! The restyle damage is a hint that tells layout which kind of operations may -//! be needed in presence of incremental style changes. - -use bitflags::Flags; +//! Helpers for computing restyle damage use crate::computed_values::isolation::T as Isolation; use crate::computed_values::mix_blend_mode::T as MixBlendMode; use crate::computed_values::transform_style::T as TransformStyle; -use crate::dom::TElement; -use crate::matching::{StyleChange, StyleDifference}; use crate::properties::{ - restyle_damage_rebuild_box, restyle_damage_rebuild_stacking_context, - restyle_damage_recalculate_overflow, restyle_damage_repaint, style_structs, ComputedValues, + restyle_damage_rebuild_box, restyle_damage_rebuild_stacking_context, style_structs, + ComputedValues, }; use crate::values::computed::basic_shape::ClipPath; use crate::values::computed::Perspective; use crate::values::generics::transform::{GenericRotate, GenericScale, GenericTranslate}; -use std::fmt; - -bitflags! { - /// Major phases of layout that need to be run due to the damage to a node during restyling. In - /// addition to the 4 bytes used for that, the rest of the `u16` is exposed as an extension point - /// for users of the crate to add their own custom types of damage that correspond to the - /// layout system they are implementing. - #[derive(Clone, Copy, Debug, Eq, PartialEq)] - pub struct ServoRestyleDamage: u16 { - /// Repaint the node itself. - /// - /// Propagates both up and down the flow tree. - const REPAINT = 0b0001; - - /// Rebuilds the stacking contexts. - /// - /// Propagates both up and down the flow tree. - const REBUILD_STACKING_CONTEXT = 0b0011; - - /// Recalculates the scrollable overflow. - /// - /// Propagates both up and down the flow tree. - const RECALCULATE_OVERFLOW = 0b0111; - - /// Any other type of damage, which requires running layout again. - /// - /// Propagates both up and down the flow tree. - const RELAYOUT = 0b1111; - } -} - -malloc_size_of::malloc_size_of_is_0!(ServoRestyleDamage); - -impl ServoRestyleDamage { - /// Compute the `StyleDifference` (including the appropriate restyle damage) - /// for a given style change between `old` and `new`. - pub fn compute_style_difference( - old: &ComputedValues, - new: &ComputedValues, - ) -> StyleDifference { - let mut damage = if std::ptr::eq(old, new) { - ServoRestyleDamage::empty() - } else { - compute_damage(old, new) - }; - - if damage.is_empty() { - return StyleDifference { - damage, - change: StyleChange::Unchanged, - }; - } - - if damage.contains(ServoRestyleDamage::RELAYOUT) { - damage |= E::compute_layout_damage(old, new); - } - - // FIXME(emilio): Differentiate between reset and inherited - // properties here, and set `reset_only` appropriately so the - // optimization to skip the cascade in those cases applies. - let change = StyleChange::Changed { reset_only: false }; - - StyleDifference { damage, change } - } - - /// Returns a bitmask indicating that the frame needs to be reconstructed. - pub fn reconstruct() -> ServoRestyleDamage { - // There's no way of knowing what kind of damage system the embedder will use, but part of - // this interface is that a fully saturated restyle damage means to rebuild everything. - ServoRestyleDamage::from_bits_retain(::Bits::MAX) - } -} - -impl Default for ServoRestyleDamage { - fn default() -> Self { - Self::empty() - } -} - -impl fmt::Display for ServoRestyleDamage { - fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { - let mut first_elem = true; - - let to_iter = [ - (ServoRestyleDamage::REPAINT, "Repaint"), - ( - ServoRestyleDamage::REBUILD_STACKING_CONTEXT, - "Rebuild stacking context", - ), - ( - ServoRestyleDamage::RECALCULATE_OVERFLOW, - "Recalculate overflow", - ), - (ServoRestyleDamage::RELAYOUT, "Relayout"), - ]; - - for &(damage, damage_str) in &to_iter { - if self.contains(damage) { - if !first_elem { - write!(f, " | ")?; - } - write!(f, "{}", damage_str)?; - first_elem = false; - } - } - - if first_elem { - write!(f, "NoDamage")?; - } - Ok(()) - } -} - -fn augmented_restyle_damage_rebuild_box(old: &ComputedValues, new: &ComputedValues) -> bool { +/// Returns true if "relayout" damage should be applied +pub fn restyle_damage_layout(old: &ComputedValues, new: &ComputedValues) -> bool { let old_box = old.get_box(); let new_box = new.get_box(); restyle_damage_rebuild_box(old, new) @@ -142,34 +25,14 @@ fn augmented_restyle_damage_rebuild_box(old: &ComputedValues, new: &ComputedValu || old.get_effects().filter.0.is_empty() != new.get_effects().filter.0.is_empty() } -fn augmented_restyle_damage_rebuild_stacking_context( +/// Returns true if "rebuild_stacking_context" damage should be applied +pub fn augmented_restyle_damage_rebuild_stacking_context( old: &ComputedValues, new: &ComputedValues, ) -> bool { restyle_damage_rebuild_stacking_context(old, new) || old.guarantees_stacking_context() != new.guarantees_stacking_context() } -fn compute_damage(old: &ComputedValues, new: &ComputedValues) -> ServoRestyleDamage { - let mut damage = ServoRestyleDamage::empty(); - - // Damage flags higher up the if-else chain imply damage flags lower down the if-else chain, - // so we can skip the diffing process for later flags if an earlier flag is true - if augmented_restyle_damage_rebuild_box(old, new) { - damage.insert(ServoRestyleDamage::RELAYOUT) - } else if restyle_damage_recalculate_overflow(old, new) { - damage.insert(ServoRestyleDamage::RECALCULATE_OVERFLOW) - } else if augmented_restyle_damage_rebuild_stacking_context(old, new) { - damage.insert(ServoRestyleDamage::REBUILD_STACKING_CONTEXT); - } else if restyle_damage_repaint(old, new) { - damage.insert(ServoRestyleDamage::REPAINT); - } - // Paint worklets may depend on custom properties, so if they have changed we should repaint. - else if !old.custom_properties_equal(new) { - damage.insert(ServoRestyleDamage::REPAINT); - } - - damage -} impl ComputedValues { /// Some properties establish a stacking context when they are set to a non-initial value. diff --git a/style/servo/selector_parser.rs b/style/servo/selector_parser.rs index cdb4794efe..4e94dade56 100644 --- a/style/servo/selector_parser.rs +++ b/style/servo/selector_parser.rs @@ -49,10 +49,11 @@ pub enum PseudoElement { // If/when :first-line is added, update is_first_line accordingly. - // If/when ::first-letter, ::first-line, or ::placeholder are added, adjust - // our property_restriction implementation to do property filtering for - // them. Also, make sure the UA sheet has the !important rules some of the + // If/when ::first-letter or ::first-line are added, adjust our + // property_restriction implementation to do property filtering for them. + // Also, make sure the UA sheet has the !important rules some of the // APPLIES_TO_PLACEHOLDER properties expect! + FirstLetter, // Non-eager pseudos. Backdrop, @@ -92,6 +93,7 @@ impl ToCss for PseudoElement { After => "::after", Before => "::before", Selection => "::selection", + FirstLetter => "::first-letter", Backdrop => "::backdrop", DetailsSummary => "::-servo-details-summary", DetailsContent => "::details-content", @@ -115,7 +117,7 @@ impl ::selectors::parser::PseudoElement for PseudoElement { } /// The number of eager pseudo-elements. Keep this in sync with cascade_type. -pub const EAGER_PSEUDO_COUNT: usize = 3; +pub const EAGER_PSEUDO_COUNT: usize = 4; impl PseudoElement { /// Gets the canonical index of this eagerly-cascaded pseudo-element. @@ -184,7 +186,7 @@ impl PseudoElement { /// Whether the current pseudo element is :first-letter #[inline] pub fn is_first_letter(&self) -> bool { - false + *self == PseudoElement::FirstLetter } /// Whether the current pseudo element is :first-line @@ -240,9 +242,10 @@ impl PseudoElement { #[inline] pub fn cascade_type(&self) -> PseudoElementCascadeType { match *self { - PseudoElement::After | PseudoElement::Before | PseudoElement::Selection => { - PseudoElementCascadeType::Eager - }, + PseudoElement::After + | PseudoElement::Before + | PseudoElement::FirstLetter + | PseudoElement::Selection => PseudoElementCascadeType::Eager, PseudoElement::Backdrop | PseudoElement::ColorSwatch | PseudoElement::DetailsSummary @@ -274,7 +277,14 @@ impl PseudoElement { /// Property flag that properties must have to apply to this pseudo-element. #[inline] pub fn property_restriction(&self) -> Option { - None + Some(match self { + PseudoElement::FirstLetter => PropertyFlags::APPLIES_TO_FIRST_LETTER, + PseudoElement::Marker if static_prefs::pref!("layout.css.marker.restricted") => { + PropertyFlags::APPLIES_TO_MARKER + }, + PseudoElement::Placeholder => PropertyFlags::APPLIES_TO_PLACEHOLDER, + _ => return None, + }) } /// Whether this pseudo-element should actually exist if it has @@ -290,6 +300,25 @@ impl PseudoElement { true } + + /// Whether this pseudo-element is the ::highlight pseudo. + pub fn is_highlight(&self) -> bool { + false + } + + /// Whether this pseudo-element is the ::target-text pseudo. + #[inline] + pub fn is_target_text(&self) -> bool { + false + } + + /// Whether this is a highlight pseudo-element that is styled lazily during + /// painting rather than during the restyle traversal. These pseudos need + /// explicit repaint triggering when their styles change. + #[inline] + pub fn is_lazy_painted_highlight_pseudo(&self) -> bool { + self.is_selection() || self.is_highlight() || self.is_target_text() + } } /// The type used for storing `:lang` arguments. @@ -636,6 +665,7 @@ impl<'a, 'i> ::selectors::Parser<'i> for SelectorParser<'a> { "after" => After, "backdrop" => Backdrop, "selection" => Selection, + "first-letter" => FirstLetter, "marker" => Marker, "-servo-details-summary" => { if !self.in_user_agent_stylesheet() { @@ -645,12 +675,7 @@ impl<'a, 'i> ::selectors::Parser<'i> for SelectorParser<'a> { }, "details-content" => DetailsContent, "color-swatch" => ColorSwatch, - "placeholder" => { - if !self.in_user_agent_stylesheet() { - return Err(location.new_custom_error(SelectorParseErrorKind::UnexpectedIdent(name.clone()))) - } - Placeholder - }, + "placeholder" => Placeholder, "-servo-text-control-inner-container" => { if !self.in_user_agent_stylesheet() { return Err(location.new_custom_error(SelectorParseErrorKind::UnexpectedIdent(name.clone()))) diff --git a/style/servo/url.rs b/style/servo/url.rs index 95491dee1d..f2836ee52b 100644 --- a/style/servo/url.rs +++ b/style/servo/url.rs @@ -68,7 +68,12 @@ impl CssUrl { /// FIXME(emilio): Should honor CorsMode. pub fn parse_from_string(url: String, context: &ParserContext, _: CorsMode) -> Self { let serialization = Arc::new(url); - let resolved = context.url_data.0.join(&serialization).ok().map(Arc::new); + // Per https://drafts.csswg.org/css-values-4/#url-empty + // If the original url is empty, then the resolved url is considered invalid. + let resolved = (!serialization.is_empty()) + .then(|| context.url_data.0.join(&serialization)) + .and_then(Result::ok) + .map(Arc::new); CssUrl(Arc::new(CssUrlData { original: Some(serialization), resolved: resolved, @@ -120,7 +125,10 @@ impl CssUrl { pub fn new_for_testing(url: &str) -> Self { CssUrl(Arc::new(CssUrlData { original: Some(Arc::new(url.into())), - resolved: ::url::Url::parse(url).ok().map(Arc::new), + resolved: (!url.is_empty()) + .then(|| ::url::Url::parse(url)) + .and_then(Result::ok) + .map(Arc::new), })) } diff --git a/style/stylesheet_set.rs b/style/stylesheet_set.rs index 327048ef7c..14e465436f 100644 --- a/style/stylesheet_set.rs +++ b/style/stylesheet_set.rs @@ -5,8 +5,8 @@ //! A centralized set of stylesheets for a document. use crate::derives::*; +use crate::device::Device; use crate::invalidation::stylesheets::{RuleChangeKind, StylesheetInvalidationSet}; -use crate::media_queries::Device; use crate::shared_lock::SharedRwLockReadGuard; use crate::stylesheets::{ CssRule, CssRuleRef, CustomMediaMap, Origin, OriginSet, PerOrigin, StylesheetInDocument, diff --git a/style/stylesheets/document_rule.rs b/style/stylesheets/document_rule.rs index 4477aeedb3..a64cfdd9af 100644 --- a/style/stylesheets/document_rule.rs +++ b/style/stylesheets/document_rule.rs @@ -7,7 +7,7 @@ //! We implement the prefixed `@-moz-document`. use crate::derives::*; -use crate::media_queries::Device; +use crate::device::Device; use crate::parser::{Parse, ParserContext}; use crate::shared_lock::{DeepCloneWithLock, Locked}; use crate::shared_lock::{SharedRwLock, SharedRwLockReadGuard, ToCssWithGuard}; diff --git a/style/stylesheets/rules_iterator.rs b/style/stylesheets/rules_iterator.rs index 9db1759529..a60d36e960 100644 --- a/style/stylesheets/rules_iterator.rs +++ b/style/stylesheets/rules_iterator.rs @@ -5,7 +5,7 @@ //! An iterator over a list of rules. use crate::context::QuirksMode; -use crate::media_queries::Device; +use crate::device::Device; use crate::shared_lock::SharedRwLockReadGuard; use crate::stylesheets::{ CssRule, CssRuleRef, CustomMediaEvaluator, CustomMediaMap, DocumentRule, ImportRule, MediaRule, diff --git a/style/stylesheets/stylesheet.rs b/style/stylesheets/stylesheet.rs index 7caa7d2a06..8e23370634 100644 --- a/style/stylesheets/stylesheet.rs +++ b/style/stylesheets/stylesheet.rs @@ -4,8 +4,9 @@ use crate::context::QuirksMode; use crate::derives::*; +use crate::device::Device; use crate::error_reporting::{ContextualParseError, ParseErrorReporter}; -use crate::media_queries::{Device, MediaList}; +use crate::media_queries::MediaList; use crate::parser::ParserContext; use crate::shared_lock::{DeepCloneWithLock, Locked}; use crate::shared_lock::{SharedRwLock, SharedRwLockReadGuard}; diff --git a/style/stylist.rs b/style/stylist.rs index fa143b2a16..f4679f0597 100644 --- a/style/stylist.rs +++ b/style/stylist.rs @@ -10,7 +10,9 @@ use crate::applicable_declarations::{ use crate::computed_value_flags::ComputedValueFlags; use crate::context::{CascadeInputs, QuirksMode}; use crate::custom_properties::ComputedCustomProperties; +use crate::custom_properties::{parse_name, SpecifiedValue}; use crate::derives::*; +use crate::device::Device; use crate::dom::TElement; #[cfg(feature = "gecko")] use crate::gecko_bindings::structs::{ServoStyleSetSizes, StyleRuleInclusion}; @@ -22,7 +24,6 @@ use crate::invalidation::media_queries::{ EffectiveMediaQueryResults, MediaListKey, ToMediaListKey, }; use crate::invalidation::stylesheets::{RuleChangeKind, StylesheetInvalidationSet}; -use crate::media_queries::Device; #[cfg(feature = "gecko")] use crate::properties::StyleBuilder; use crate::properties::{ @@ -32,6 +33,8 @@ use crate::properties::{ use crate::properties_and_values::registry::{ PropertyRegistration, PropertyRegistrationData, ScriptRegistry as CustomPropertyScriptRegistry, }; +use crate::properties_and_values::rule::{Inherits, PropertyRegistrationError, PropertyRuleName}; +use crate::properties_and_values::syntax::Descriptor; use crate::rule_cache::{RuleCache, RuleCacheConditions}; use crate::rule_collector::RuleCollector; use crate::rule_tree::{CascadeLevel, RuleTree, StrongRuleNode, StyleSource}; @@ -49,6 +52,7 @@ use crate::stylesheets::scope_rule::{ collect_scope_roots, element_is_outside_of_scope, scope_selector_list_is_trivial, ImplicitScopeRoot, ScopeRootCandidate, ScopeSubjectMap, ScopeTarget, }; +use crate::stylesheets::UrlExtraData; use crate::stylesheets::{ CounterStyleRule, CssRule, CssRuleRef, EffectiveRulesIterator, FontFaceRule, FontFeatureValuesRule, FontPaletteValuesRule, Origin, OriginSet, PagePseudoClassFlags, @@ -58,9 +62,10 @@ use crate::stylesheets::{CustomMediaEvaluator, CustomMediaMap}; #[cfg(feature = "gecko")] use crate::values::specified::position::PositionTryFallbacksItem; use crate::values::specified::position::PositionTryFallbacksTryTactic; -use crate::values::{computed, AtomIdent}; +use crate::values::{computed, AtomIdent, Parser, SourceLocation}; use crate::AllocErr; use crate::{Atom, LocalName, Namespace, ShrinkIfNeeded, WeakAtom}; +use cssparser::ParserInput; use dom::{DocumentState, ElementState}; #[cfg(feature = "gecko")] use malloc_size_of::MallocUnconditionalShallowSizeOf; @@ -2020,6 +2025,97 @@ impl Stylist { } } +#[allow(missing_docs)] +#[repr(u8)] +pub enum RegisterCustomPropertyResult { + SuccessfullyRegistered, + InvalidName, + AlreadyRegistered, + InvalidSyntax, + NoInitialValue, + InvalidInitialValue, + InitialValueNotComputationallyIndependent, +} + +impl Stylist { + /// + pub fn register_custom_property( + &mut self, + url_data: &UrlExtraData, + name: &str, + syntax: &str, + inherits: bool, + initial_value: Option<&str>, + ) -> RegisterCustomPropertyResult { + use RegisterCustomPropertyResult::*; + + // If name is not a custom property name string, throw a SyntaxError and exit this algorithm. + let Ok(name) = parse_name(name).map(Atom::from) else { + return InvalidName; + }; + + // If property set already contains an entry with name as its property name (compared + // codepoint-wise), throw an InvalidModificationError and exit this algorithm. + if self.custom_property_script_registry().get(&name).is_some() { + return AlreadyRegistered; + } + // Attempt to consume a syntax definition from syntax. If it returns failure, throw a + // SyntaxError. Otherwise, let syntax definition be the returned syntax definition. + let Ok(syntax) = Descriptor::from_str(syntax, /* preserve_specified = */ false) else { + return InvalidSyntax; + }; + + let initial_value = match initial_value { + Some(value) => { + let mut input = ParserInput::new(value); + let parsed = Parser::new(&mut input) + .parse_entirely(|input| { + input.skip_whitespace(); + SpecifiedValue::parse(input, None, url_data).map(Arc::new) + }) + .ok(); + if parsed.is_none() { + return InvalidInitialValue; + } + parsed + }, + None => None, + }; + + if let Err(error) = + PropertyRegistration::validate_initial_value(&syntax, initial_value.as_deref()) + { + return match error { + PropertyRegistrationError::InitialValueNotComputationallyIndependent => { + InitialValueNotComputationallyIndependent + }, + PropertyRegistrationError::InvalidInitialValue => InvalidInitialValue, + PropertyRegistrationError::NoInitialValue => NoInitialValue, + }; + } + + let property_registration = PropertyRegistration { + name: PropertyRuleName(name), + data: PropertyRegistrationData { + syntax, + inherits: if inherits { + Inherits::True + } else { + Inherits::False + }, + initial_value, + }, + url_data: url_data.clone(), + source_location: SourceLocation { line: 0, column: 0 }, + }; + self.custom_property_script_registry_mut() + .register(property_registration); + self.rebuild_initial_values_for_custom_properties(); + + SuccessfullyRegistered + } +} + /// A vector that is sorted in layer order. #[derive(Clone, Debug, Deref, MallocSizeOf)] pub struct LayerOrderedVec(Vec<(T, LayerId)>); diff --git a/style/traversal.rs b/style/traversal.rs index b80fd1dc73..52696126fd 100644 --- a/style/traversal.rs +++ b/style/traversal.rs @@ -6,7 +6,7 @@ use crate::context::{ElementCascadeInputs, SharedStyleContext, StyleContext}; use crate::data::{ElementData, ElementStyles, RestyleKind}; -use crate::dom::{NodeInfo, OpaqueNode, TElement, TNode}; +use crate::dom::{NodeInfo, OpaqueNode, TElement, TNode, TRestyleDamage}; use crate::invalidation::element::restyle_hints::RestyleHint; use crate::matching::{ChildRestyleRequirement, MatchMethods}; use crate::selector_parser::PseudoElement; @@ -52,27 +52,6 @@ impl PreTraverseToken { } } -/// A global variable holding the state of -/// `is_servo_nonincremental_layout()`. -/// See [#22854](https://github.com/servo/servo/issues/22854). -#[cfg(feature = "servo")] -pub static IS_SERVO_NONINCREMENTAL_LAYOUT: std::sync::atomic::AtomicBool = - std::sync::atomic::AtomicBool::new(false); - -#[cfg(feature = "servo")] -#[inline] -fn is_servo_nonincremental_layout() -> bool { - use std::sync::atomic::Ordering; - - IS_SERVO_NONINCREMENTAL_LAYOUT.load(Ordering::Relaxed) -} - -#[cfg(not(feature = "servo"))] -#[inline] -fn is_servo_nonincremental_layout() -> bool { - false -} - /// A DOM Traversal trait, that is used to generically implement styling for /// Gecko and Servo. pub trait DomTraversal: Sync { @@ -203,7 +182,7 @@ pub trait DomTraversal: Sync { /// Returns true if traversal should visit a text node. The style system /// never processes text nodes, but Servo overrides this to visit them for /// flow construction when necessary. - fn text_node_needs_traversal(node: E::ConcreteNode, _parent_data: &ElementData) -> bool { + fn text_node_needs_traversal(node: E::ConcreteNode, _parent_data: &ElementData) -> bool { debug_assert!(node.is_text_node()); false } @@ -212,18 +191,13 @@ pub trait DomTraversal: Sync { fn element_needs_traversal( el: E, traversal_flags: TraversalFlags, - data: Option<&ElementData>, + data: Option<&ElementData>, ) -> bool { debug!( "element_needs_traversal({:?}, {:?}, {:?})", el, traversal_flags, data ); - // Non-incremental layout visits every node. - if is_servo_nonincremental_layout() { - return true; - } - // Unwrap the data. let data = match data { Some(d) if d.has_styles() => d, @@ -392,7 +366,7 @@ pub fn recalc_style_at( traversal_data: &PerLevelTraversalData, context: &mut StyleContext, element: E, - data: &mut ElementData, + data: &mut ElementData, note_child: F, ) where E: TElement, @@ -513,8 +487,7 @@ pub fn recalc_style_at( // We only do this if we're not a display: none root, since in that case // it's useless to style children. let mut traverse_children = has_dirty_descendants_for_this_restyle - || !propagated_hint.is_empty() - || is_servo_nonincremental_layout(); + || !propagated_hint.is_empty(); traverse_children = traverse_children && !data.styles.is_display_none(); @@ -539,7 +512,7 @@ pub fn recalc_style_at( clear_state_after_traversing(element, data, flags); } -fn clear_state_after_traversing(element: E, data: &mut ElementData, flags: TraversalFlags) +fn clear_state_after_traversing(element: E, data: &mut ElementData, flags: TraversalFlags) where E: TElement, { @@ -556,7 +529,7 @@ fn compute_style( traversal_data: &PerLevelTraversalData, context: &mut StyleContext, element: E, - data: &mut ElementData, + data: &mut ElementData, kind: RestyleKind, ) -> ChildRestyleRequirement where @@ -694,7 +667,7 @@ where } #[cfg(feature = "servo")] -fn notify_paint_worklet(context: &StyleContext, data: &ElementData) +fn notify_paint_worklet(context: &StyleContext, data: &ElementData) where E: TElement, { @@ -732,7 +705,7 @@ where } #[cfg(not(feature = "servo"))] -fn notify_paint_worklet(_context: &StyleContext, _data: &ElementData) +fn notify_paint_worklet(_context: &StyleContext, _data: &ElementData) where E: TElement, { @@ -742,7 +715,7 @@ where fn note_children( context: &mut StyleContext, element: E, - data: &ElementData, + data: &ElementData, propagated_hint: RestyleHint, is_initial_style: bool, mut note_child: F, @@ -759,9 +732,7 @@ fn note_children( let child = match child_node.as_element() { Some(el) => el, None => { - if is_servo_nonincremental_layout() - || D::text_node_needs_traversal(child_node, data) - { + if D::text_node_needs_traversal(child_node, data) { note_child(child_node); } continue; diff --git a/style/values/computed/mod.rs b/style/values/computed/mod.rs index 536bb81386..d2bca245ca 100644 --- a/style/values/computed/mod.rs +++ b/style/values/computed/mod.rs @@ -18,8 +18,8 @@ use crate::computed_value_flags::ComputedValueFlags; use crate::context::QuirksMode; use crate::custom_properties::ComputedCustomProperties; use crate::derives::*; +use crate::device::Device; use crate::font_metrics::{FontMetrics, FontMetricsOrientation}; -use crate::media_queries::Device; #[cfg(feature = "gecko")] use crate::properties; use crate::properties::{ComputedValues, StyleBuilder}; diff --git a/style/values/generics/counters.rs b/style/values/generics/counters.rs index e8ffbeb879..24a7360269 100644 --- a/style/values/generics/counters.rs +++ b/style/values/generics/counters.rs @@ -4,9 +4,6 @@ //! Generic types for counters-related CSS values. -#[cfg(feature = "servo")] -use crate::computed_values::list_style_type::T as ListStyleType; -#[cfg(feature = "gecko")] use crate::counter_style::CounterStyle; use crate::derives::*; use crate::values::specified::Attr; @@ -189,21 +186,9 @@ pub struct GenericCounters( ); pub use self::GenericCounters as Counters; -#[cfg(feature = "servo")] -type CounterStyleType = ListStyleType; -#[cfg(feature = "gecko")] -type CounterStyleType = CounterStyle; - -#[cfg(feature = "servo")] -#[inline] -fn is_decimal(counter_type: &CounterStyleType) -> bool { - *counter_type == ListStyleType::Decimal -} - -#[cfg(feature = "gecko")] #[inline] -fn is_decimal(counter_type: &CounterStyleType) -> bool { +fn is_decimal(counter_type: &CounterStyle) -> bool { *counter_type == CounterStyle::decimal() } @@ -302,13 +287,13 @@ pub enum GenericContentItem { String(crate::OwnedStr), /// `counter(name, style)`. #[css(comma, function)] - Counter(CustomIdent, #[css(skip_if = "is_decimal")] CounterStyleType), + Counter(CustomIdent, #[css(skip_if = "is_decimal")] CounterStyle), /// `counters(name, separator, style)`. #[css(comma, function)] Counters( CustomIdent, crate::OwnedStr, - #[css(skip_if = "is_decimal")] CounterStyleType, + #[css(skip_if = "is_decimal")] CounterStyle, ), /// `open-quote`. OpenQuote, diff --git a/style/values/generics/easing.rs b/style/values/generics/easing.rs index 1a8eaa229f..ee03c705b2 100644 --- a/style/values/generics/easing.rs +++ b/style/values/generics/easing.rs @@ -6,7 +6,6 @@ //! https://drafts.csswg.org/css-easing/#timing-functions use crate::derives::*; -use crate::parser::ParserContext; /// A generic easing function. #[derive( @@ -82,16 +81,6 @@ pub enum BeforeFlag { Set, } -#[cfg(feature = "gecko")] -fn step_position_jump_enabled(_context: &ParserContext) -> bool { - true -} - -#[cfg(feature = "servo")] -fn step_position_jump_enabled(_context: &ParserContext) -> bool { - false -} - #[allow(missing_docs)] #[derive( Clone, @@ -110,13 +99,9 @@ fn step_position_jump_enabled(_context: &ParserContext) -> bool { )] #[repr(u8)] pub enum StepPosition { - #[parse(condition = "step_position_jump_enabled")] JumpStart, - #[parse(condition = "step_position_jump_enabled")] JumpEnd, - #[parse(condition = "step_position_jump_enabled")] JumpNone, - #[parse(condition = "step_position_jump_enabled")] JumpBoth, Start, End, diff --git a/style/values/mod.rs b/style/values/mod.rs index 455d646a77..d511b01dc4 100644 --- a/style/values/mod.rs +++ b/style/values/mod.rs @@ -646,6 +646,20 @@ impl DashedIdent { pub fn is_empty(&self) -> bool { self.0 == atom!("") } + + /// Returns an atom with the same value, but without the starting "--". + /// + /// # Panics + /// + /// Panics when used on the special `DashedIdent::empty()`. + pub(crate) fn undashed(&self) -> Atom { + assert!(!self.is_empty(), "Can't undash the empty DashedIdent"); + #[cfg(feature = "gecko")] + let name = &self.0.as_slice()[2..]; + #[cfg(feature = "servo")] + let name = &self.0[2..]; + Atom::from(name) + } } impl Parse for DashedIdent { diff --git a/style/values/resolved/mod.rs b/style/values/resolved/mod.rs index 3261382c5a..86a7a8fe3e 100644 --- a/style/values/resolved/mod.rs +++ b/style/values/resolved/mod.rs @@ -6,7 +6,7 @@ //! there are used values. #[cfg(feature = "gecko")] -use crate::media_queries::Device; +use crate::device::Device; use crate::properties::{ComputedValues, LonghandId, NonCustomPropertyId}; use crate::ArcSlice; use app_units::Au; diff --git a/style/values/specified/box.rs b/style/values/specified/box.rs index c4332461fe..bf47e6a518 100644 --- a/style/values/specified/box.rs +++ b/style/values/specified/box.rs @@ -28,7 +28,7 @@ fn grid_enabled() -> bool { #[cfg(feature = "servo")] fn grid_enabled() -> bool { - style_config::get_bool("layout.grid.enabled") + static_prefs::pref!("layout.grid.enabled") } #[inline] diff --git a/style/values/specified/color.rs b/style/values/specified/color.rs index 7a7e308d54..dc46c5261f 100644 --- a/style/values/specified/color.rs +++ b/style/values/specified/color.rs @@ -8,7 +8,7 @@ use super::AllowQuirks; use crate::color::mix::ColorInterpolationMethod; use crate::color::{parsing, AbsoluteColor, ColorFunction, ColorMixItemList, ColorSpace}; use crate::derives::*; -use crate::media_queries::Device; +use crate::device::Device; use crate::parser::{Parse, ParserContext}; use crate::values::computed::{Color as ComputedColor, Context, ToComputedValue}; use crate::values::generics::color::{ @@ -167,7 +167,6 @@ pub enum Color { /// Right now this is only the case for relative colors with `currentColor` as the origin. ColorFunction(Box>), /// A system color. - #[cfg(feature = "gecko")] System(SystemColor), /// A color mix. ColorMix(Box), @@ -176,7 +175,6 @@ pub enum Color { /// The contrast-color function. ContrastColor(Box), /// Quirksmode-only rule for inheriting color from the body - #[cfg(feature = "gecko")] InheritFromBodyQuirk, } @@ -423,7 +421,6 @@ pub enum SystemColor { impl SystemColor { #[inline] fn compute(&self, cx: &Context) -> ComputedColor { - use crate::gecko::values::convert_nscolor_to_absolute_color; use crate::gecko_bindings::bindings; let color = cx.device().system_nscolor(*self, cx.builder.color_scheme); @@ -435,7 +432,80 @@ impl SystemColor { if color == bindings::NS_SAME_AS_FOREGROUND_COLOR { return ComputedColor::currentcolor(); } - ComputedColor::Absolute(convert_nscolor_to_absolute_color(color)) + ComputedColor::Absolute(AbsoluteColor::from_nscolor(color)) + } +} + +/// System colors. A bunch of these are ad-hoc, others come from Windows: +/// +/// https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getsyscolor +/// +/// Others are HTML/CSS specific. Spec is: +/// +/// https://drafts.csswg.org/css-color/#css-system-colors +/// https://drafts.csswg.org/css-color/#deprecated-system-colors +#[allow(missing_docs)] +#[cfg(feature = "servo")] +#[derive(Clone, Copy, Debug, MallocSizeOf, Parse, PartialEq, ToCss, ToShmem)] +#[repr(u8)] +pub enum SystemColor { + Accentcolor, + Accentcolortext, + Activetext, + Linktext, + Visitedtext, + Buttonborder, + Buttonface, + Buttontext, + Canvas, + Canvastext, + Field, + Fieldtext, + Graytext, + Highlight, + Highlighttext, + Mark, + Marktext, + Selecteditem, + Selecteditemtext, + + // Deprecated system colors. + Activeborder, + Inactiveborder, + Threeddarkshadow, + Threedhighlight, + Threedlightshadow, + Threedshadow, + Windowframe, + Buttonhighlight, + Buttonshadow, + Threedface, + Activecaption, + Appworkspace, + Background, + Inactivecaption, + Infobackground, + Menu, + Scrollbar, + Window, + Captiontext, + Infotext, + Menutext, + Windowtext, + Inactivecaptiontext, +} + +#[cfg(feature = "servo")] +impl SystemColor { + #[inline] + fn compute(&self, cx: &Context) -> ComputedColor { + if cx.for_non_inherited_property { + cx.rule_cache_conditions + .borrow_mut() + .set_color_scheme_dependency(cx.builder.color_scheme); + } + + ComputedColor::Absolute(cx.device().system_color(*self, cx.builder.color_scheme)) } } @@ -485,13 +555,16 @@ impl Color { Ok(color) }, Err(e) => { - #[cfg(feature = "gecko")] { + #[cfg(feature = "gecko")] if let Ok(system) = input.try_parse(|i| SystemColor::parse(context, i)) { return Ok(Color::System(system)); } + #[cfg(feature = "servo")] + if let Ok(system) = input.try_parse(SystemColor::parse) { + return Ok(Color::System(system)); + } } - if let Ok(mix) = input.try_parse(|i| ColorMix::parse(context, i, preserve_authored)) { return Ok(Color::ColorMix(Box::new(mix))); @@ -595,9 +668,7 @@ impl ToCss for Color { c.to_css(dest)?; dest.write_char(')') }, - #[cfg(feature = "gecko")] Color::System(system) => system.to_css(dest), - #[cfg(feature = "gecko")] Color::InheritFromBodyQuirk => dest.write_str("-moz-inherit-from-body-quirk"), } } @@ -607,10 +678,8 @@ impl Color { /// Returns whether this color is allowed in forced-colors mode. pub fn honored_in_forced_colors_mode(&self, allow_transparent: bool) -> bool { match *self { - #[cfg(feature = "gecko")] Self::InheritFromBodyQuirk => false, Self::CurrentColor => true, - #[cfg(feature = "gecko")] Self::System(..) => true, Self::Absolute(ref absolute) => allow_transparent && absolute.color.is_transparent(), Self::ColorFunction(ref color_function) => { @@ -841,9 +910,7 @@ impl Color { Color::ContrastColor(ref c) => { ComputedColor::ContrastColor(Box::new(c.to_computed_color(context)?)) }, - #[cfg(feature = "gecko")] Color::System(system) => system.compute(context?), - #[cfg(feature = "gecko")] Color::InheritFromBodyQuirk => { ComputedColor::Absolute(context?.device().body_text_color()) }, diff --git a/style/values/specified/counters.rs b/style/values/specified/counters.rs index b89d53358b..3d19b6a778 100644 --- a/style/values/specified/counters.rs +++ b/style/values/specified/counters.rs @@ -4,9 +4,6 @@ //! Specified types for counter properties. -#[cfg(feature = "servo")] -use crate::computed_values::list_style_type::T as ListStyleType; -#[cfg(feature = "gecko")] use crate::counter_style::CounterStyle; use crate::parser::{Parse, ParserContext}; use crate::values::generics::counters as generics; @@ -149,17 +146,6 @@ pub type Content = generics::GenericContent; pub type ContentItem = generics::GenericContentItem; impl Content { - #[cfg(feature = "servo")] - fn parse_counter_style(_: &ParserContext, input: &mut Parser) -> ListStyleType { - input - .try_parse(|input| { - input.expect_comma()?; - ListStyleType::parse(input) - }) - .unwrap_or(ListStyleType::Decimal) - } - - #[cfg(feature = "gecko")] fn parse_counter_style(context: &ParserContext, input: &mut Parser) -> CounterStyle { use crate::counter_style::CounterStyleParsingFlags; input diff --git a/style/values/specified/easing.rs b/style/values/specified/easing.rs index 8c4f29ef49..ac5c9cf2c8 100644 --- a/style/values/specified/easing.rs +++ b/style/values/specified/easing.rs @@ -78,7 +78,7 @@ impl TimingFunction { let position = input .try_parse(|i| { i.expect_comma()?; - StepPosition::parse(context, i) + StepPosition::parse(i) }) .unwrap_or(StepPosition::End); diff --git a/style/values/specified/font.rs b/style/values/specified/font.rs index fe91aa2470..610fb29517 100644 --- a/style/values/specified/font.rs +++ b/style/values/specified/font.rs @@ -1773,7 +1773,7 @@ impl XTextScale { ToShmem, ToTyped, )] -#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))] +#[cfg_attr(feature = "servo", derive(Deserialize, Eq, Hash, Serialize))] /// Internal property that reflects the lang attribute pub struct XLang(#[css(skip)] pub Atom); diff --git a/style/values/specified/image.rs b/style/values/specified/image.rs index 657e706bc6..7be7a1c87d 100644 --- a/style/values/specified/image.rs +++ b/style/values/specified/image.rs @@ -1291,7 +1291,12 @@ impl PaintWorklet { .try_parse(|input| { input.expect_comma()?; input.parse_comma_separated(|input| { - SpecifiedValue::parse(input, &context.url_data).map(Arc::new) + SpecifiedValue::parse( + input, + Some(&context.namespaces.prefixes), + &context.url_data, + ) + .map(Arc::new) }) }) .unwrap_or_default(); diff --git a/style/values/specified/list.rs b/style/values/specified/list.rs index f28211082d..6632a9db1d 100644 --- a/style/values/specified/list.rs +++ b/style/values/specified/list.rs @@ -4,7 +4,6 @@ //! `list` specified values. -#[cfg(feature = "gecko")] use crate::counter_style::{CounterStyle, CounterStyleParsingFlags}; use crate::derives::*; use crate::parser::{Parse, ParserContext}; @@ -12,7 +11,6 @@ use cssparser::{Parser, Token}; use style_traits::{ParseError, StyleParseErrorKind}; /// Specified and computed `list-style-type` property. -#[cfg(feature = "gecko")] #[derive( Clone, Debug, @@ -29,7 +27,6 @@ use style_traits::{ParseError, StyleParseErrorKind}; #[repr(transparent)] pub struct ListStyleType(pub CounterStyle); -#[cfg(feature = "gecko")] impl ListStyleType { /// Initial specified value for `list-style-type`. #[inline] @@ -47,6 +44,7 @@ impl ListStyleType { /// /// This should only be used for mapping type attribute to list-style-type, and thus only /// values possible in that attribute is considered here. + #[cfg(feature = "gecko")] pub fn from_gecko_keyword(value: u32) -> Self { use crate::gecko_bindings::structs; use crate::values::CustomIdent; @@ -75,7 +73,6 @@ impl ListStyleType { } } -#[cfg(feature = "gecko")] impl Parse for ListStyleType { fn parse<'i, 't>( context: &ParserContext, @@ -86,142 +83,6 @@ impl Parse for ListStyleType { } } -/// Specified and computed `list-style-type` property. -#[cfg(feature = "servo")] -#[derive( - Clone, - Debug, - Eq, - MallocSizeOf, - Parse, - PartialEq, - SpecifiedValueInfo, - ToCss, - ToComputedValue, - ToResolvedValue, - ToShmem, - ToTyped, -)] -pub enum ListStyleType { - /// A filled circle, similar to • U+2022 BULLET. - /// - Disc, - /// The element has no marker string. - /// - None, - /// A hollow circle, similar to ◦ U+25E6 WHITE BULLET. - /// - Circle, - /// A filled square, similar to ▪ U+25AA BLACK SMALL SQUARE. - /// - Square, - /// Symbol for indicating an open disclosure widget, such as the HTML `
` element. - /// - DisclosureOpen, - /// Symbol for indicating a closed disclosure widget, such as the HTML `
` element. - /// - DisclosureClosed, - /// Western decimal numbers (e.g., 1, 2, 3, ..., 98, 99, 100). - /// - Decimal, - /// Lowercase ASCII letters (e.g., a, b, c, ..., z, aa, ab). - /// - LowerAlpha, - /// Uppercase ASCII letters (e.g., A, B, C, ..., Z, AA, AB). - /// - UpperAlpha, - /// Arabic-indic numbering (e.g.، ١، ٢، ٣، ٤، ...، ٩٨، ٩٩، ١٠٠). - /// - ArabicIndic, - /// Bengali numbering (e.g., ১, ২, ৩, ..., ৯৮, ৯৯, ১০০). - /// - Bengali, - /// Cambodian/Khmer numbering (e.g., ១, ២, ៣, ..., ៩៨, ៩៩, ១០០). - /// - Cambodian, - /// Han decimal numbers (e.g., 一, 二, 三, ..., 九八, 九九, 一〇〇). - /// - CjkDecimal, - /// devanagari numbering (e.g., १, २, ३, ..., ९८, ९९, १००). - /// - Devanagari, - /// Gujarati numbering (e.g., ૧, ૨, ૩, ..., ૯૮, ૯૯, ૧૦૦). - /// - Gujarati, - /// Gurmukhi numbering (e.g., ੧, ੨, ੩, ..., ੯੮, ੯੯, ੧੦੦). - /// - Gurmukhi, - /// Kannada numbering (e.g., ೧, ೨, ೩, ..., ೯೮, ೯೯, ೧೦೦). - /// - Kannada, - /// Cambodian/Khmer numbering (e.g., ១, ២, ៣, ..., ៩៨, ៩៩, ១០០). - /// - Khmer, - /// Laotian numbering (e.g., ໑, ໒, ໓, ..., ໙໘, ໙໙, ໑໐໐). - /// - Malayalam, - /// Mongolian numbering (e.g., ᠑, ᠒, ᠓, ..., ᠙᠘, ᠙᠙, ᠑᠐᠐). - /// - Mongolian, - /// Myanmar (Burmese) numbering (e.g., ၁, ၂, ၃, ..., ၉၈, ၉၉, ၁၀၀). - /// - Myanmar, - /// Oriya numbering (e.g., ୧, ୨, ୩, ..., ୯୮, ୯୯, ୧୦୦). - /// - Oriya, - /// Persian numbering (e.g., ۱, ۲, ۳, ۴, ..., ۹۸, ۹۹, ۱۰۰). - /// - Persian, - /// Telugu numbering (e.g., ౧, ౨, ౩, ..., ౯౮, ౯౯, ౧౦౦). - /// - Telugu, - /// Thai (Siamese) numbering (e.g., ๑, ๒, ๓, ..., ๙๘, ๙๙, ๑๐๐). - /// - Thai, - /// Tibetan numbering (e.g., ༡, ༢, ༣, ..., ༩༨, ༩༩, ༡༠༠). - /// - Tibetan, - /// Han "Earthly Branch" ordinals (e.g., 子, 丑, 寅, ..., 亥). - /// - CjkEarthlyBranch, - /// Han "Heavenly Stem" ordinals (e.g., 甲, 乙, 丙, ..., 癸) - /// - CjkHeavenlyStem, - /// Lowercase classical Greek (e.g., α, β, γ, ..., ω, αα, αβ). - /// - LowerGreek, - /// Dictionary-order hiragana lettering (e.g., あ, い, う, ..., ん, ああ, あい). - /// - Hiragana, - /// Iroha-order hiragana lettering (e.g., い, ろ, は, ..., す, いい, いろ). - /// - HiraganaIroha, - /// Dictionary-order katakana lettering (e.g., ア, イ, ウ, ..., ン, アア, アイ). - /// - Katakana, - /// Iroha-order katakana lettering (e.g., イ, ロ, ハ, ..., ス, イイ, イロ) - /// - KatakanaIroha, -} - -#[cfg(feature = "servo")] -impl ListStyleType { - /// Initial specified value for `list-style-type`. - #[inline] - pub fn disc() -> Self { - Self::Disc - } - - /// none value. - #[inline] - pub fn none() -> Self { - Self::None - } -} - /// A quote pair. #[derive( Clone, diff --git a/style/values/specified/source_size_list.rs b/style/values/specified/source_size_list.rs index 08399af713..f3abe1f829 100644 --- a/style/values/specified/source_size_list.rs +++ b/style/values/specified/source_size_list.rs @@ -4,7 +4,7 @@ //! https://html.spec.whatwg.org/multipage/#source-size-list -use crate::media_queries::Device; +use crate::device::Device; use crate::parser::{Parse, ParserContext}; use crate::queries::{FeatureType, QueryCondition}; use crate::stylesheets::CustomMediaEvaluator; diff --git a/style_derive/Cargo.toml b/style_derive/Cargo.toml index 8d8a85f62d..b1c84ad073 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.workspace = true 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 9ba394fe91..323d6f0b85 100644 --- a/style_traits/Cargo.toml +++ b/style_traits/Cargo.toml @@ -1,33 +1,34 @@ [package] -name = "style_traits" -version = "0.0.1" +name = "stylo_traits" +version.workspace = true 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" path = "lib.rs" [features] +default = ["servo"] servo = ["stylo_atoms", "cssparser/serde", "url", "euclid/serde"] -gecko = ["nsstring"] +gecko = [] [dependencies] app_units = "0.7" bitflags = "2" cssparser = "0.36" 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 = { workspace = true} +malloc_size_of_derive = "0.1" +selectors = { workspace = true} serde = "1.0" -servo_arc = { path = "../servo_arc" } -stylo_atoms = { path = "../atoms", optional = true } +servo_arc = { workspace = true} +stylo_atoms = { workspace = true, optional = true } thin-vec = "0.2" -to_shmem = { path = "../to_shmem" } -to_shmem_derive = { path = "../to_shmem_derive" } +to_shmem = { workspace = true} +to_shmem_derive = { workspace = true} url = { version = "2.5", optional = true } diff --git a/stylo_atoms/Cargo.toml b/stylo_atoms/Cargo.toml new file mode 100644 index 0000000000..e39f28f9b1 --- /dev/null +++ b/stylo_atoms/Cargo.toml @@ -0,0 +1,20 @@ +[package] +name = "stylo_atoms" +version.workspace = true +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.1" 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..bff093bb35 --- /dev/null +++ b/stylo_atoms/static_atoms.txt @@ -0,0 +1,187 @@ +-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 +beforeinput +beforetoggle +beforeunload +block-size +button +canplay +canplaythrough +center +change +characteristicvaluechanged +checkbox +cancel +click +close +closing +color +command +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_dom/Cargo.toml b/stylo_dom/Cargo.toml new file mode 100644 index 0000000000..5d25f57c50 --- /dev/null +++ b/stylo_dom/Cargo.toml @@ -0,0 +1,18 @@ +[package] +name = "stylo_dom" +version.workspace = true +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 = { workspace = true } 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..3274bf47fe --- /dev/null +++ b/stylo_static_prefs/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "stylo_static_prefs" +version.workspace = true +authors = ["The Servo Project Developers"] +documentation = "https://docs.rs/stylo_static_prefs/" +description = "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..bf07907305 --- /dev/null +++ b/stylo_static_prefs/src/lib.rs @@ -0,0 +1,259 @@ +/* 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}; + +/// Returns the default value for a preference exposed to the style crate. +/// This is what will be used if the embedder has not set the preference. +#[macro_export] +macro_rules! default_value { + ("dom.select.customizable_select.enabled") => { + false + }; + ("layout.columns.enabled") => { + false + }; + ("layout.container-queries.enabled") => { + false + }; + ("layout.css.anchor-positioning.enabled") => { + false + }; + ("layout.css.at-scope.enabled") => { + false + }; + ("layout.css.attr.enabled") => { + false + }; + ("layout.css.basic-shape-shape.enabled") => { + false + }; + ("layout.css.color-mix-multi-color.enabled") => { + false + }; + ("layout.css.content.alt-text.enabled") => { + false + }; + ("layout.css.contrast-color.enabled") => { + true + }; + ("layout.css.custom-media.enabled") => { + false + }; + ("layout.css.fit-content-function.enabled") => { + true + }; + ("layout.css.font-palette.enabled") => { + false + }; + ("layout.css.font-tech.enabled") => { + false + }; + ("layout.css.font-variations.enabled") => { + true + }; + ("layout.css.gradient-color-interpolation-method.enabled") => { + true + }; + ("layout.css.light-dark.images.enabled") => { + false + }; + ("layout.css.margin-rules.enabled") => { + false + }; + ("layout.css.marker.restricted") => { + true + }; + ("layout.css.motion-path-url.enabled") => { + false + }; + ("layout.css.outline-offset.snapping") => { + 1 + }; + ("layout.css.properties-and-values.enabled") => { + true + }; + ("layout.css.relative-color-syntax.enabled") => { + true + }; + ("layout.css.scroll-driven-animations.enabled") => { + false + }; + ("layout.css.scroll-state.enabled") => { + false + }; + ("layout.css.starting-style-at-rules.enabled") => { + false + }; + ("layout.css.stretch-size-keyword.enabled") => { + true + }; + ("layout.css.style-queries.enabled") => { + false + }; + ("layout.css.stylo-local-work-queue.in-main-thread") => { + 32 + }; + ("layout.css.stylo-local-work-queue.in-worker") => { + 0 + }; + ("layout.css.stylo-work-unit-size") => { + 16 + }; + ("layout.css.system-ui.enabled") => { + true + }; + ("layout.css.webkit-fill-available.all-size-properties.enabled") => { + true + }; + ("layout.css.webkit-fill-available.enabled") => { + true + }; + ("layout.grid.enabled") => { + false + }; + ("layout.threads") => { + // Negative means auto, 0 disables the thread-pool (main-thread styling), + // other numbers override as specified. + -1 + }; + ("layout.unimplemented") => { + false + }; + ("layout.variable_fonts.enabled") => { + false + }; + ("layout.writing-mode.enabled") => { + false + }; +} + +/// Returns the value of a preference exposed to the style crate. If the embedder +/// has not set a value for it, this returns the default value of the preference. +#[macro_export] +macro_rules! pref { + ($string:tt) => { + $crate::Preference::get($string, $crate::default_value!($string)) + }; +} + +/// Sets a preference to the provided value. +#[macro_export] +macro_rules! set_pref { + ($string:tt, $($value:expr)+) => { + let value = $($value)+; + // This comparison ensures that the provided value belongs to the expected type. + let _ = $crate::default_value!($string) == value; + $crate::Preference::set($string, value) + }; +} + +static PREFS: LazyLock = LazyLock::new(Preferences::default); + +#[derive(Debug, Default)] +struct Preferences { + bool_prefs: RwLock>, + i32_prefs: RwLock>, +} + +impl Preferences { + pub fn get_bool(&self, key: &str, default: bool) -> bool { + let prefs = self.bool_prefs.read().expect("RwLock is poisoned"); + *prefs.get(key).unwrap_or(&default) + } + + pub fn get_i32(&self, key: &str, default: i32) -> i32 { + let prefs = self.i32_prefs.read().expect("RwLock is poisoned"); + *prefs.get(key).unwrap_or(&default) + } + + 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 trait Preference: Sized { + /// Gets the value of a preference, falling back to the provided default. + /// Prefer using [`pref!()`] instead, since it ensures that the preference + /// exists and uses the correct default automatically. + fn get(key: &str, default: Self) -> Self; + + /// Sets a preference to the provided value. Prefer using [`set_pref!()`] + /// instead, since it ensures that the preference exists and the value + /// belongs to the expected type. + fn set(key: &str, value: Self); +} + +impl Preference for bool { + fn get(key: &str, default: Self) -> Self { + PREFS.get_bool(key, default) + } + fn set(key: &str, value: Self) { + PREFS.set_bool(key, value) + } +} + +impl Preference for i32 { + fn get(key: &str, default: Self) -> Self { + PREFS.get_i32(key, default) + } + fn set(key: &str, value: Self) { + PREFS.set_i32(key, value) + } +} + +#[test] +fn test() { + let prefs = Preferences::default(); + + // We get the default value when the pref is not set. + assert_eq!(prefs.get_bool("foo", false), false); + assert_eq!(prefs.get_bool("foo", true), true); + assert_eq!(prefs.get_i32("bar", 0), 0); + assert_eq!(prefs.get_i32("bar", 1), 1); + assert_eq!(prefs.get_i32("bar", 2), 2); + + // Prefs can be set and retrieved. + prefs.set_bool("foo", true); + prefs.set_i32("bar", 1); + assert_eq!(prefs.get_bool("foo", false), true); + assert_eq!(prefs.get_bool("foo", true), true); + assert_eq!(prefs.get_i32("bar", 0), 1); + assert_eq!(prefs.get_i32("bar", 1), 1); + assert_eq!(prefs.get_i32("bar", 2), 1); + prefs.set_bool("foo", false); + prefs.set_i32("bar", 2); + assert_eq!(prefs.get_bool("foo", false), false); + assert_eq!(prefs.get_bool("foo", true), false); + assert_eq!(prefs.get_i32("bar", 0), 2); + assert_eq!(prefs.get_i32("bar", 1), 2); + assert_eq!(prefs.get_i32("bar", 2), 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", 0), 3); + assert_eq!(prefs.get_bool("foo", false), false); + assert_eq!(prefs.get_bool("bar", false), true); + assert_eq!(prefs.get_i32("bar", 0), 2); +} diff --git a/sync.sh b/sync.sh new file mode 100755 index 0000000000..68c8689c9c --- /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 --branch main --progress https://github.com/mozilla-firefox/firefox.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" diff --git a/to_shmem/Cargo.toml b/to_shmem/Cargo.toml index 60bcd524f6..81f7fed5d5 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.3.0" authors = ["The Servo Project Developers"] license = "MPL-2.0" repository = "https://github.com/servo/stylo" @@ -24,8 +24,8 @@ thin-vec = ["dep:thin-vec"] [dependencies] cssparser = { version = "0.36", optional = true } -servo_arc = { version = "0.4.0", path = "../servo_arc", optional = true } +servo_arc = { workspace = true, 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 }