diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml new file mode 100644 index 0000000000..aafc8d0e84 --- /dev/null +++ b/.github/workflows/main.yml @@ -0,0 +1,51 @@ +name: CI + +on: + push: + branches: ["main"] + pull_request: + workflow_dispatch: + merge_group: + types: [checks_requested] + + +jobs: + linux-debug: + name: Linux (Debug) + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Install Rust + uses: dtolnay/rust-toolchain@stable + - name: Run Tests + run: cargo build --features servo + env: + RUST_BACKTRACE: 1 + + linux-release: + name: Linux (Release) + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Install Rust + uses: dtolnay/rust-toolchain@stable + - name: Run Tests + run: cargo build --release --features servo + env: + RUST_BACKTRACE: 1 + + build-result: + name: Result + runs-on: ubuntu-latest + if: ${{ always() }} + needs: + - linux-debug + - linux-release + steps: + - name: Success + if: ${{ !contains(needs.*.result, 'failure') && !contains(needs.*.result, 'cancelled') }} + run: exit 0 + - name: Failure + if: ${{ contains(needs.*.result, 'failure') || contains(needs.*.result, 'cancelled') }} + run: exit 1 + diff --git a/.github/workflows/mirror-to-release-branch.yml b/.github/workflows/mirror-to-release-branch.yml new file mode 100644 index 0000000000..c8593195da --- /dev/null +++ b/.github/workflows/mirror-to-release-branch.yml @@ -0,0 +1,26 @@ +name: 🪞 Mirror `main` +on: + push: + branches: + - main + +jobs: + mirror: + name: Mirror + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + - name: Get branch name + id: branch-name + run: | + first_commit=$(git log --pretty=\%H --grep='Servo initial downstream commit') + upstream_base="$first_commit~" + echo BRANCH_NAME=$(git log -n1 --pretty='%as' $upstream_base) >> $GITHUB_OUTPUT + - uses: google/mirror-branch-action@v1.0 + name: Mirror to ${{ steps.branch-name.outputs.BRANCH_NAME }} + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + source: main + dest: ${{ steps.branch-name.outputs.BRANCH_NAME }} diff --git a/.github/workflows/sync-upstream.yml b/.github/workflows/sync-upstream.yml new file mode 100644 index 0000000000..992fe6d2ce --- /dev/null +++ b/.github/workflows/sync-upstream.yml @@ -0,0 +1,23 @@ +name: Sync upstream with mozilla-central + +on: + schedule: + - cron: '0 13 * * *' + workflow_dispatch: + +jobs: + sync: + name: Sync + runs-on: ubuntu-22.04 + steps: + - uses: actions/checkout@v3 + with: + fetch-depth: 1 + - uses: actions/cache@v3 + with: + path: _cache/upstream + key: upstream + - run: | + ./sync.sh _filtered + git fetch -f --progress ./_filtered master:upstream + git push -fu --progress origin upstream diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000000..fc3c2f9b3c --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +/_cache/ +/_filtered/ +/target/ +/style/properties/__pycache__/ +Cargo.lock diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000000..72d5501272 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,16 @@ +[workspace] +resolver = "2" +members = [ + "dom", + "malloc_size_of", + "rustfmt.toml", + "selectors", + "servo_arc", + "style", + "style_derive", + "style_static_prefs", + "style_traits", + "to_shmem", + "to_shmem_derive", +] +default-members = ["style"] diff --git a/README.md b/README.md new file mode 100644 index 0000000000..198c0db215 --- /dev/null +++ b/README.md @@ -0,0 +1,88 @@ +Stylo +===== + +This repo contains Servo’s downstream fork of [Stylo](https://searchfox.org/mozilla-central/source/servo). + +The branches are as follows: + +- [`upstream`](https://github.com/servo/style/tree/upstream) has upstream mozilla-central filtered to the paths we care about ([style.paths](style.paths)), but is otherwise unmodified +- [`main`](https://github.com/servo/style/tree/ci) has our downstream patches, plus the scripts and workflows for syncing with mozilla-central, to be rebased onto `upstream` + +## Building Servo against your local Stylo + +Assuming your local `servo` and `stylo` directories are siblings, you can build `servo` against `stylo` by adding the following to `servo/Cargo.toml`: + +```toml +[patch."https://github.com/servo/stylo.git"] +derive_common = { path = "../stylo/derive_common" } +malloc_size_of = { path = "../stylo/malloc_size_of" } +selectors = { path = "../stylo/selectors" } +servo_arc = { path = "../stylo/servo_arc" } +servo_atoms = { path = "../stylo/atoms" } +size_of_test = { path = "../stylo/size_of_test" } +static_prefs = { path = "../stylo/style_static_prefs" } +style_config = { path = "../stylo/style_config" } +style_derive = { path = "../stylo/style_derive" } +style = { path = "../stylo/style" } +style_traits = { path = "../stylo/style_traits" } +``` + +## Syncing `upstream` with mozilla-central + +Start by generating a filtered copy of mozilla-central. This will cache the raw mozilla-central in `_cache/upstream`, storing the result in `_filtered`: + +```sh +$ ./sync.sh _filtered +``` + +If `_filtered` already exists, you will need to delete it and try again: + +```sh +$ rm -Rf _filtered +``` + +Now overwrite our `upstream` with those commits and push: + +```sh +$ git fetch -f --progress ./_filtered master:upstream +$ git push -fu --progress origin upstream +``` + +## Rebasing `main` onto `upstream` + +Start by fetching `upstream` into your local repo: + +```sh +$ git fetch -f origin upstream:upstream +``` + +In general, the filtering process is deterministic, yielding the same commit hashes each time, so we can rebase normally: + +```sh +$ git rebase upstream +``` + +But if the filtering config changes or Mozilla moves to GitHub, the commit hashes on `upstream` may change. In this case, we need to tell git where the old upstream ends and our own commits start (notice the `~`): + +```sh +$ git log --pretty=\%H --grep='Servo initial downstream commit' +e62d7f0090941496e392e1dc91df103a38e3f488 + +$ git rebase --onto upstream e62d7f0090941496e392e1dc91df103a38e3f488~ +Successfully rebased and updated refs/heads/main. +``` + +`start-rebase.sh` takes care of this automatically, but you should still use `git rebase` for subsequent steps like `--continue` and `--abort`: + +```sh +$ ./start-rebase.sh upstream +$ ./start-rebase.sh upstream -i # interactive +$ git rebase --continue # not ./start-rebase.sh --continue +$ git rebase --abort # not ./start-rebase.sh --abort +``` + +Or if we aren’t ready to rebase onto the tip of upstream: + +```sh +$ ./start-rebase.sh upstream~10 -i +``` diff --git a/atoms/Cargo.toml b/atoms/Cargo.toml new file mode 100644 index 0000000000..aa232e86e5 --- /dev/null +++ b/atoms/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "servo_atoms" +version = "0.0.1" +authors = ["The Servo Project Developers"] +license = "MPL-2.0" +edition = "2018" +publish = false +build = "build.rs" + +[lib] +path = "lib.rs" + +[dependencies] +string_cache = "0.8" + +[build-dependencies] +string_cache_codegen = "0.5" diff --git a/atoms/build.rs b/atoms/build.rs new file mode 100644 index 0000000000..b5f6775724 --- /dev/null +++ b/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/atoms/lib.rs b/atoms/lib.rs new file mode 100644 index 0000000000..03560a40c0 --- /dev/null +++ b/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/atoms/predefined_counter_styles.rs b/atoms/predefined_counter_styles.rs new file mode 100644 index 0000000000..f376981e32 --- /dev/null +++ b/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/atoms/static_atoms.txt b/atoms/static_atoms.txt new file mode 100644 index 0000000000..d658fcf1c6 --- /dev/null +++ b/atoms/static_atoms.txt @@ -0,0 +1,179 @@ +-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-menu-radius +-moz-mac-titlebar-height +-moz-overlay-scrollbar-fade-duration +DOMContentLoaded +abort +activate +addtrack +animationcancel +animationend +animationiteration +animationstart +aspect-ratio +beforeunload +block-size +button +canplay +canplaythrough +center +change +characteristicvaluechanged +checkbox +click +close +closing +color +complete +compositionend +compositionstart +compositionupdate +controllerchange +cursive +dark +datachannel +date +datetime-local +dir +device-pixel-ratio +durationchange +email +emptied +end +ended +error +fantasy +fetch +file +fill +fill-opacity +formdata +fullscreenchange +fullscreenerror +gattserverdisconnected +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 +signalingstatechange +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/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/dom/Cargo.toml b/dom/Cargo.toml new file mode 100644 index 0000000000..9d678fa5df --- /dev/null +++ b/dom/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "dom" +version = "0.0.1" +authors = ["The Servo Project Developers"] +license = "MPL-2.0" +edition = "2021" +publish = false + +[lib] +path = "lib.rs" + +[dependencies] +bitflags = "2" +malloc_size_of = { path = "../malloc_size_of" } diff --git a/dom/lib.rs b/dom/lib.rs new file mode 100644 index 0000000000..9137763491 --- /dev/null +++ b/dom/lib.rs @@ -0,0 +1,164 @@ +/* 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; + +// 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; + + /// 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/malloc_size_of/Cargo.toml b/malloc_size_of/Cargo.toml index eefd6cd89a..68defa0d61 100644 --- a/malloc_size_of/Cargo.toml +++ b/malloc_size_of/Cargo.toml @@ -20,7 +20,7 @@ euclid = "0.22" selectors = { path = "../selectors" } servo_arc = { path = "../servo_arc" } smallbitvec = "2.3.0" -smallvec = "1.0" +smallvec = "1.13" string_cache = { version = "0.8", optional = true } -thin-vec = { version = "0.2.1" } +thin-vec = { version = "0.2.13" } void = "1.0.2" diff --git a/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 58de5ba26a..d37a742e7e 100644 --- a/style/Cargo.toml +++ b/style/Cargo.toml @@ -31,15 +31,15 @@ gecko = [ "to_shmem/gecko", ] servo = [ - "arrayvec/use_union", "cssparser/serde", "encoding_rs", "malloc_size_of/servo", "markup5ever", + "mime", "serde", "servo_arc/servo", "servo_atoms", - "servo_config", + "style_config", "string_cache", "style_traits/servo", "url", @@ -48,6 +48,7 @@ servo = [ ] gecko_debug = [] gecko_refcount_logging = [] +nsstring = [] [dependencies] app_units = "0.7" @@ -57,7 +58,7 @@ bitflags = "2" byteorder = "1.0" cssparser = "0.34" derive_more = { version = "0.99", default-features = false, features = ["add", "add_assign", "deref", "deref_mut", "from"] } -dom = { path = "../../../dom/base/rust" } +dom = { path = "../dom" } new_debug_unreachable = "1.0" encoding_rs = {version = "0.8", optional = true} euclid = "0.22" @@ -69,10 +70,10 @@ itoa = "1.0" lazy_static = "1" log = "0.4" malloc_size_of = { path = "../malloc_size_of" } -malloc_size_of_derive = { path = "../../../xpcom/rust/malloc_size_of_derive" } -markup5ever = { version = "0.12", optional = true } +malloc_size_of_derive = "0.1" +markup5ever = { version = "0.14", optional = true } matches = "0.1" -nsstring = {path = "../../../xpcom/rust/nsstring/", optional = true} +mime = { version = "0.3.13", optional = true } num_cpus = {version = "1.1.0"} num-integer = "0.1" num-traits = "0.2" @@ -85,21 +86,20 @@ selectors = { path = "../selectors" } serde = {version = "1.0", optional = true, features = ["derive"]} servo_arc = { path = "../servo_arc" } servo_atoms = {path = "../atoms", optional = true} -servo_config = {path = "../config", optional = true} smallbitvec = "2.3.0" smallvec = "1.0" static_assertions = "1.1" -static_prefs = { path = "../../../modules/libpref/init/static_prefs" } +static_prefs = { path = "../style_static_prefs" } string_cache = { version = "0.8", optional = true } +style_config = { path = "../style_config", optional = true } style_derive = {path = "../style_derive"} style_traits = {path = "../style_traits"} to_shmem = {path = "../to_shmem"} to_shmem_derive = {path = "../to_shmem_derive"} -thin-vec = { version = "0.2.1", features = ["gecko-ffi"] } +thin-vec = "0.2.1" uluru = "3.0" unicode-bidi = { version = "0.3", default-features = false } 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/README.md b/style/README.md index 96457e1b30..bdbe36e44c 100644 --- a/style/README.md +++ b/style/README.md @@ -3,4 +3,4 @@ servo-style Style system for Servo, using [rust-cssparser](https://github.com/servo/rust-cssparser) for parsing. - * [Documentation](https://github.com/servo/servo/blob/master/docs/components/style.md). + * [Documentation](https://github.com/servo/servo/blob/main/docs/components/style.md). diff --git a/style/animation.rs b/style/animation.rs index 29c98b0c7e..f90b9df81f 100644 --- a/style/animation.rs +++ b/style/animation.rs @@ -27,6 +27,7 @@ use crate::stylesheets::layer_rule::LayerOrder; use crate::values::animated::{Animate, Procedure}; use crate::values::computed::{Time, TimingFunction}; use crate::values::generics::easing::BeforeFlag; +use crate::values::specified::TransitionBehavior; use crate::Atom; use fxhash::FxHashMap; use parking_lot::RwLock; @@ -1030,6 +1031,15 @@ impl ElementAnimationSet { new_style: &Arc, ) { let style = new_style.get_ui(); + + #[cfg(feature = "servo")] + if !property_declaration_id.is_animatable() || + (style.transition_behavior_mod(index) != TransitionBehavior::AllowDiscrete && + property_declaration_id.is_discrete_animatable()) + { + return; + } + let timing_function = style.transition_timing_function_mod(index); let duration = style.transition_duration_mod(index); let delay = style.transition_delay_mod(index).seconds() as f64; diff --git a/style/build.rs b/style/build.rs index 4b27edbe2c..eacb9b3fc9 100644 --- a/style/build.rs +++ b/style/build.rs @@ -21,7 +21,7 @@ mod build_gecko { lazy_static! { pub static ref PYTHON: String = env::var("PYTHON3").ok().unwrap_or_else(|| { let candidates = if cfg!(windows) { - ["python3.exe"] + ["python.exe"] } else { ["python3"] }; diff --git a/style/context.rs b/style/context.rs index 1e4fcca15f..5337617689 100644 --- a/style/context.rs +++ b/style/context.rs @@ -475,7 +475,7 @@ impl SequentialTask { /// Executes this task. pub fn execute(self) { use self::SequentialTask::*; - debug_assert_eq!(thread_state::get(), ThreadState::LAYOUT); + debug_assert!(thread_state::get().contains(ThreadState::LAYOUT)); match self { Unused(_) => unreachable!(), #[cfg(feature = "gecko")] @@ -551,7 +551,7 @@ where E: TElement, { fn drop(&mut self) { - debug_assert_eq!(thread_state::get(), ThreadState::LAYOUT); + debug_assert!(thread_state::get().contains(ThreadState::LAYOUT)); for task in self.0.drain(..) { task.execute() } diff --git a/style/encoding_support.rs b/style/encoding_support.rs index c144ad0b3b..b07fccfd30 100644 --- a/style/encoding_support.rs +++ b/style/encoding_support.rs @@ -75,7 +75,6 @@ impl Stylesheet { stylesheet_loader, error_reporter, quirks_mode, - 0, AllowImportRules::Yes, ) } @@ -98,7 +97,6 @@ impl Stylesheet { url_data, stylesheet_loader, error_reporter, - 0, AllowImportRules::Yes, ) } diff --git a/style/font_face.rs b/style/font_face.rs index fd52874da8..9c117604b2 100644 --- a/style/font_face.rs +++ b/style/font_face.rs @@ -485,18 +485,6 @@ pub fn parse_font_face_block( #[cfg(feature = "servo")] pub struct FontFace<'a>(&'a FontFaceRuleData); -#[cfg(feature = "servo")] -impl Iterator for EffectiveSources { - type Item = Source; - fn next(&mut self) -> Option { - self.0.pop() - } - - fn size_hint(&self) -> (usize, Option) { - (self.0.len(), Some(self.0.len())) - } -} - struct FontFaceRuleParser<'a, 'b: 'a> { context: &'a ParserContext<'b>, rule: &'a mut FontFaceRuleData, diff --git a/style/gecko/media_features.rs b/style/gecko/media_features.rs index d0f7dad876..743697d8c5 100644 --- a/style/gecko/media_features.rs +++ b/style/gecko/media_features.rs @@ -8,7 +8,7 @@ use crate::gecko_bindings::bindings; use crate::gecko_bindings::structs; use crate::media_queries::{Device, MediaType}; use crate::queries::feature::{AllowsRanges, Evaluator, FeatureFlags, QueryFeatureDescription}; -use crate::queries::values::Orientation; +use crate::queries::values::{Orientation, PrefersColorScheme}; use crate::values::computed::{CSSPixelLength, Context, Ratio, Resolution}; use crate::values::specified::color::ForcedColors; use crate::values::AtomString; @@ -191,15 +191,6 @@ enum PrefersReducedTransparency { Reduce, } -/// Values for the prefers-color-scheme media feature. -#[derive(Clone, Copy, Debug, FromPrimitive, Parse, PartialEq, ToCss)] -#[repr(u8)] -#[allow(missing_docs)] -pub enum PrefersColorScheme { - Light, - Dark, -} - /// Values for the dynamic-range and video-dynamic-range media features. /// https://drafts.csswg.org/mediaqueries-5/#dynamic-range /// This implements PartialOrd so that lower values will correctly match diff --git a/style/global_style_data.rs b/style/global_style_data.rs index d6e49e7da6..943e53d8fe 100644 --- a/style/global_style_data.rs +++ b/style/global_style_data.rs @@ -139,7 +139,7 @@ impl StyleThreadPool { #[cfg(feature = "servo")] fn stylo_threads_pref() -> i32 { - pref!(layout.threads) + style_config::get_i32("layout.threads") } #[cfg(feature = "gecko")] diff --git a/style/invalidation/element/invalidation_map.rs b/style/invalidation/element/invalidation_map.rs index 80e85bfc7b..f5b940f666 100644 --- a/style/invalidation/element/invalidation_map.rs +++ b/style/invalidation/element/invalidation_map.rs @@ -515,7 +515,7 @@ trait Collector { fn class_map(&mut self) -> &mut IdOrClassDependencyMap; fn state_map(&mut self) -> &mut StateDependencyMap; fn attribute_map(&mut self) -> &mut LocalNameDependencyMap; - fn custom_state_map(&mut self) -> &mut LocalNameDependencyMap; + fn custom_state_map(&mut self) -> &mut CustomStateDependencyMap; fn update_states(&mut self, element_state: ElementState, document_state: DocumentState); // In normal invalidations, type-based dependencies don't need to be explicitly tracked; diff --git a/style/invalidation/element/relative_selector.rs b/style/invalidation/element/relative_selector.rs index e633df468d..e951c2598d 100644 --- a/style/invalidation/element/relative_selector.rs +++ b/style/invalidation/element/relative_selector.rs @@ -22,6 +22,8 @@ use crate::invalidation::element::state_and_attributes::{ check_dependency, dependency_may_be_relevant, invalidated_descendants, invalidated_self, invalidated_sibling, push_invalidation, should_process_descendants, }; +#[cfg(feature = "servo")] +use crate::selector_parser::SnapshotMap as ServoElementSnapshotTable; use crate::stylist::{CascadeData, Stylist}; use dom::ElementState; use fxhash::FxHashMap; diff --git a/style/lib.rs b/style/lib.rs index 578ac5baba..bb69c986e8 100644 --- a/style/lib.rs +++ b/style/lib.rs @@ -173,6 +173,7 @@ pub use style_traits::owned_str::OwnedStr; use std::hash::{BuildHasher, Hash}; +#[macro_use] pub mod properties; #[cfg(feature = "gecko")] diff --git a/style/matching.rs b/style/matching.rs index ddb806e4f8..c1007a3f9d 100644 --- a/style/matching.rs +++ b/style/matching.rs @@ -8,6 +8,8 @@ #![deny(missing_docs)] use crate::computed_value_flags::ComputedValueFlags; +#[cfg(feature = "servo")] +use crate::context::CascadeInputs; use crate::context::{ElementCascadeInputs, QuirksMode}; use crate::context::{SharedStyleContext, StyleContext}; use crate::data::{ElementData, ElementStyles}; @@ -585,7 +587,8 @@ trait PrivateMatchMethods: TElement { // If we have modified animation or transitions, we recascade style for this node. if style_changed { - let mut rule_node = new_resolved_styles.primary_style().rules().clone(); + let primary_style = new_resolved_styles.primary_style(); + let mut rule_node = primary_style.rules().clone(); let declarations = context.shared.animations.get_all_declarations( &AnimationSetKey::new_for_non_pseudo(self.as_node().opaque()), context.shared.current_time_for_animations, @@ -594,20 +597,23 @@ trait PrivateMatchMethods: TElement { Self::replace_single_rule_node( &context.shared, CascadeLevel::Transitions, + LayerOrder::root(), declarations.transitions.as_ref().map(|a| a.borrow_arc()), &mut rule_node, ); Self::replace_single_rule_node( &context.shared, CascadeLevel::Animations, + LayerOrder::root(), declarations.animations.as_ref().map(|a| a.borrow_arc()), &mut rule_node, ); - if rule_node != *new_resolved_styles.primary_style().rules() { + if rule_node != *primary_style.rules() { let inputs = CascadeInputs { rules: Some(rule_node), - visited_rules: new_resolved_styles.primary_style().visited_rules().cloned(), + visited_rules: primary_style.visited_rules().cloned(), + flags: primary_style.flags.for_cascade_inputs(), }; new_resolved_styles.primary.style = StyleResolverForElement::new( @@ -696,6 +702,7 @@ trait PrivateMatchMethods: TElement { let inputs = CascadeInputs { rules: Some(rule_node), visited_rules: style.visited_rules().cloned(), + flags: style.flags.for_cascade_inputs(), }; let new_style = StyleResolverForElement::new( diff --git a/style/properties/Mako-1.1.2-py2.py3-none-any.whl b/style/properties/Mako-1.1.2-py2.py3-none-any.whl new file mode 100644 index 0000000000..9593025a47 Binary files /dev/null and b/style/properties/Mako-1.1.2-py2.py3-none-any.whl differ diff --git a/style/properties/build.py b/style/properties/build.py index d1f76d4a25..1a92544bfd 100644 --- a/style/properties/build.py +++ b/style/properties/build.py @@ -8,6 +8,7 @@ import sys BASE = os.path.dirname(__file__.replace("\\", "/")) +sys.path.insert(0, os.path.join(BASE, "Mako-1.1.2-py2.py3-none-any.whl")) sys.path.insert(0, BASE) # For importing `data.py` from mako import exceptions @@ -66,7 +67,12 @@ def main(): os.path.join(BASE, "properties.html.mako"), properties=properties_dict ) as_json = json.dumps(properties_dict, indent=4, sort_keys=True) - doc_servo = os.path.join(BASE, "..", "..", "..", "target", "doc", "servo") + + # Four dotdots: /path/to/target(4)/debug(3)/build(2)/style-*(1)/out + # Do not ascend above the target dir, because it may not be called target + # or even have a parent (see CARGO_TARGET_DIR). + doc_servo = os.path.join(OUT_DIR, "..", "..", "..", "..", "doc", "stylo") + write(doc_servo, "css-properties.html", as_html) write(doc_servo, "css-properties.json", as_json) write(OUT_DIR, "css-properties.json", as_json) diff --git a/style/properties/cascade.rs b/style/properties/cascade.rs index e38259dd39..56f0a13522 100644 --- a/style/properties/cascade.rs +++ b/style/properties/cascade.rs @@ -11,8 +11,6 @@ use crate::custom_properties::{ CustomPropertiesBuilder, DeferFontRelativeCustomPropertyResolution, }; use crate::dom::TElement; -#[cfg(feature = "gecko")] -use crate::font_metrics::FontMetricsOrientation; use crate::logical_geometry::WritingMode; use crate::properties::{ property_counts, CSSWideKeyword, ComputedValues, DeclarationImportanceIterator, Importance, @@ -761,7 +759,9 @@ impl<'b> Cascade<'b> { return; } - let has_writing_mode = apply!(WritingMode) | apply!(Direction) | apply!(TextOrientation); + let has_writing_mode = apply!(WritingMode) | apply!(Direction); + #[cfg(feature = "gecko")] + let has_writing_mode = has_writing_mode | apply!(TextOrientation); if has_writing_mode { context.builder.writing_mode = WritingMode::new(context.builder.get_inherited_box()) } @@ -781,29 +781,41 @@ impl<'b> Cascade<'b> { // Compute font-family. let has_font_family = apply!(FontFamily); let has_lang = apply!(XLang); - if has_lang { - self.recompute_initial_font_family_if_needed(&mut context.builder); - } - if has_font_family { - self.prioritize_user_fonts_if_needed(&mut context.builder); + + #[cfg(feature = "gecko")] { + if has_lang { + self.recompute_initial_font_family_if_needed(&mut context.builder); + } + if has_font_family { + self.prioritize_user_fonts_if_needed(&mut context.builder); + } } // Compute font-size. - if apply!(XTextScale) { - self.unzoom_fonts_if_needed(&mut context.builder); - } - let has_font_size = apply!(FontSize); - let has_math_depth = apply!(MathDepth); - let has_min_font_size_ratio = apply!(MozMinFontSizeRatio); + #[cfg(feature = "gecko")] { + if apply!(XTextScale) { + self.unzoom_fonts_if_needed(&mut context.builder); + } + let has_font_size = apply!(FontSize); + let has_math_depth = apply!(MathDepth); + let has_min_font_size_ratio = apply!(MozMinFontSizeRatio); - if has_math_depth && has_font_size { - self.recompute_math_font_size_if_needed(context); - } - if has_lang || has_font_family { - self.recompute_keyword_font_size_if_needed(context); + if has_math_depth && has_font_size { + self.recompute_math_font_size_if_needed(context); + } + if has_lang || has_font_family { + self.recompute_keyword_font_size_if_needed(context); + } + if has_font_size || has_min_font_size_ratio || has_lang || has_font_family { + self.constrain_font_size_if_needed(&mut context.builder); + } } - if has_font_size || has_min_font_size_ratio || has_lang || has_font_family { - self.constrain_font_size_if_needed(&mut context.builder); + #[cfg(feature = "servo")] + { + apply!(FontSize); + if has_lang || has_font_family { + self.recompute_keyword_font_size_if_needed(context); + } } // Compute the rest of the first-available-font-affecting properties. @@ -1035,6 +1047,7 @@ impl<'b> Cascade<'b> { builder.add_flags(ComputedValueFlags::HAS_AUTHOR_SPECIFIED_WORD_SPACING); } + #[cfg(feature = "gecko")] if self .author_specified .contains(LonghandId::FontSynthesisWeight) @@ -1042,6 +1055,7 @@ impl<'b> Cascade<'b> { builder.add_flags(ComputedValueFlags::HAS_AUTHOR_SPECIFIED_FONT_SYNTHESIS_WEIGHT); } + #[cfg(feature = "gecko")] if self .author_specified .contains(LonghandId::FontSynthesisStyle) @@ -1177,7 +1191,6 @@ impl<'b> Cascade<'b> { } /// Some keyword sizes depend on the font family and language. - #[cfg(feature = "gecko")] fn recompute_keyword_font_size_if_needed(&self, context: &mut computed::Context) { use crate::values::computed::ToComputedValue; @@ -1196,6 +1209,7 @@ impl<'b> Cascade<'b> { }, }; + #[cfg(feature = "gecko")] if font.mScriptUnconstrainedSize == new_size.computed_size { return; } diff --git a/style/properties/declaration_block.rs b/style/properties/declaration_block.rs index 49b1d020f7..78a3bdbfbb 100644 --- a/style/properties/declaration_block.rs +++ b/style/properties/declaration_block.rs @@ -940,7 +940,7 @@ impl PropertyDeclarationBlock { ); if let Some(cv) = computed_values { - context.builder.custom_properties = cv.custom_properties.clone(); + context.builder.custom_properties = cv.custom_properties().clone(); }; match (declaration, computed_values) { diff --git a/style/properties/helpers.mako.rs b/style/properties/helpers.mako.rs index 764ae9288a..3523516416 100644 --- a/style/properties/helpers.mako.rs +++ b/style/properties/helpers.mako.rs @@ -598,7 +598,7 @@ <%def name="inner_body(keyword, needs_conversion=False)"> pub use self::computed_value::T as SpecifiedValue; pub mod computed_value { - #[cfg_attr(feature = "servo", derive(Deserialize, Serialize))] + #[cfg_attr(feature = "servo", derive(Deserialize, Hash, Serialize))] #[derive(Clone, Copy, Debug, Eq, FromPrimitive, MallocSizeOf, Parse, PartialEq, SpecifiedValueInfo, ToAnimatedValue, ToComputedValue, ToCss, ToResolvedValue, ToShmem)] pub enum T { % for variant in keyword.values_for(engine): diff --git a/style/properties/helpers/animated_properties.mako.rs b/style/properties/helpers/animated_properties.mako.rs index 513a198a6a..cf5665782b 100644 --- a/style/properties/helpers/animated_properties.mako.rs +++ b/style/properties/helpers/animated_properties.mako.rs @@ -396,7 +396,7 @@ impl AnimationValue { Some(animatable) } - /// Get an AnimationValue for an declaration id from a given computed values. + /// Get an AnimationValue for an AnimatableLonghand from a given computed values. pub fn from_computed_values( property: PropertyDeclarationId, style: &ComputedValues, diff --git a/style/properties/longhands/counters.mako.rs b/style/properties/longhands/counters.mako.rs index 80f5c50847..ae871d1d31 100644 --- a/style/properties/longhands/counters.mako.rs +++ b/style/properties/longhands/counters.mako.rs @@ -20,6 +20,7 @@ ${helpers.predefined_type( "counter-increment", "CounterIncrement", engines="gecko servo", + servo_pref="layout.legacy_layout", initial_value="Default::default()", animation_type="discrete", spec="https://drafts.csswg.org/css-lists/#propdef-counter-increment", @@ -31,6 +32,7 @@ ${helpers.predefined_type( "counter-reset", "CounterReset", engines="gecko servo", + servo_pref="layout.legacy_layout", initial_value="Default::default()", animation_type="discrete", spec="https://drafts.csswg.org/css-lists-3/#propdef-counter-reset", diff --git a/style/properties/longhands/inherited_text.mako.rs b/style/properties/longhands/inherited_text.mako.rs index 537cf2f486..49fb0dab1c 100644 --- a/style/properties/longhands/inherited_text.mako.rs +++ b/style/properties/longhands/inherited_text.mako.rs @@ -147,6 +147,7 @@ ${helpers.single_keyword( gecko_aliases="-moz-pre-space=preserve-spaces", engines="gecko servo", gecko_enum_prefix="StyleWhiteSpaceCollapse", + needs_conversion="True", animation_type="discrete", spec="https://drafts.csswg.org/css-text-4/#propdef-white-space-collapse", servo_restyle_damage="rebuild_and_reflow", @@ -381,6 +382,7 @@ ${helpers.single_keyword( "wrap nowrap", engines="gecko servo", gecko_enum_prefix="StyleTextWrapMode", + needs_conversion="True", animation_type="discrete", spec="https://drafts.csswg.org/css-text-4/#propdef-text-wrap-mode", servo_restyle_damage="rebuild_and_reflow", diff --git a/style/properties/longhands/list.mako.rs b/style/properties/longhands/list.mako.rs index 603b3b09d5..83b30b189b 100644 --- a/style/properties/longhands/list.mako.rs +++ b/style/properties/longhands/list.mako.rs @@ -69,6 +69,7 @@ ${helpers.predefined_type( "Quotes", "computed::Quotes::get_initial_value()", engines="gecko servo", + servo_pref="layout.legacy_layout", animation_type="discrete", spec="https://drafts.csswg.org/css-content/#propdef-quotes", servo_restyle_damage="rebuild_and_reflow", diff --git a/style/properties/longhands/text.mako.rs b/style/properties/longhands/text.mako.rs index 55b0928ba4..9cb725048b 100644 --- a/style/properties/longhands/text.mako.rs +++ b/style/properties/longhands/text.mako.rs @@ -9,6 +9,7 @@ ${helpers.predefined_type( "TextOverflow", "computed::TextOverflow::get_initial_value()", engines="gecko servo", + servo_pref="layout.legacy_layout", animation_type="discrete", boxed=True, spec="https://drafts.csswg.org/css-ui/#propdef-text-overflow", diff --git a/style/properties/mod.rs b/style/properties/mod.rs index a6a853abb1..d2299d6760 100644 --- a/style/properties/mod.rs +++ b/style/properties/mod.rs @@ -360,7 +360,10 @@ impl PropertyId { pub fn is_animatable(&self) -> bool { match self { Self::NonCustom(id) => id.is_animatable(), - Self::Custom(..) => true, + #[cfg(feature = "gecko")] + Self::Custom(_) => true, + #[cfg(feature = "servo")] + Self::Custom(_) => false, } } @@ -1086,7 +1089,10 @@ impl<'a> PropertyDeclarationId<'a> { pub fn is_animatable(&self) -> bool { match self { Self::Longhand(id) => id.is_animatable(), - Self::Custom(_) => true, + #[cfg(feature = "gecko")] + PropertyDeclarationId::Custom(_) => true, + #[cfg(feature = "servo")] + PropertyDeclarationId::Custom(_) => false, } } @@ -1096,7 +1102,10 @@ impl<'a> PropertyDeclarationId<'a> { match self { Self::Longhand(longhand) => longhand.is_discrete_animatable(), // TODO(bug 1885995): Refine this. + #[cfg(feature = "gecko")] Self::Custom(_) => true, + #[cfg(feature = "servo")] + Self::Custom(_) => false, } } @@ -1301,7 +1310,7 @@ pub struct SourcePropertyDeclaration { // This is huge, but we allocate it on the stack and then never move it, // we only pass `&mut SourcePropertyDeclaration` references around. -size_of_test!(SourcePropertyDeclaration, 632); +size_of_test!(SourcePropertyDeclaration, 568); impl SourcePropertyDeclaration { /// Create one with a single PropertyDeclaration. diff --git a/style/properties/properties.mako.rs b/style/properties/properties.mako.rs index 22b7758c85..84b15c0284 100644 --- a/style/properties/properties.mako.rs +++ b/style/properties/properties.mako.rs @@ -27,7 +27,6 @@ use crate::media_queries::Device; use crate::parser::ParserContext; use crate::selector_parser::PseudoElement; use crate::stylist::Stylist; -#[cfg(feature = "servo")] use servo_config::prefs; use style_traits::{CssWriter, KeywordsCollectFn, ParseError, SpecifiedValueInfo, StyleParseErrorKind, ToCss}; use crate::stylesheets::{CssRuleType, CssRuleTypes, Origin}; use crate::logical_geometry::{LogicalAxis, LogicalCorner, LogicalSide}; @@ -455,7 +454,7 @@ pub mod property_counts { /// The number of non-custom properties. pub const NON_CUSTOM: usize = LONGHANDS_AND_SHORTHANDS + ALIASES; /// The number of prioritary properties that we have. - pub const PRIORITARY: usize = ${len(PRIORITARY_PROPERTIES)}; + pub const PRIORITARY: usize = ${len(PRIORITARY_PROPERTIES.intersection(set(list(map(lambda p: p.name, data.longhands)))))}; /// The max number of longhands that a shorthand other than "all" expands to. pub const MAX_SHORTHAND_EXPANDED: usize = ${max(len(s.sub_properties) for s in data.shorthands_except_all())}; @@ -529,7 +528,7 @@ impl NonCustomPropertyId { Some(pref) => pref, }; - prefs::pref_map().get(pref).as_bool().unwrap_or(false) + style_config::get_bool(pref) % endif }; @@ -1375,9 +1374,6 @@ pub mod style_structs { use fxhash::FxHasher; use super::longhands; use std::hash::{Hash, Hasher}; - use crate::logical_geometry::WritingMode; - use crate::media_queries::Device; - use crate::values::computed::NonNegativeLength; use crate::values::specified::color::ColorSchemeFlags; % for style_struct in data.active_style_structs(): @@ -1507,8 +1503,6 @@ pub mod style_structs { /// Computes a font hash in order to be able to cache fonts /// effectively in GFX and layout. pub fn compute_font_hash(&mut self) { - // Corresponds to the fields in - // `gfx::font_template::FontTemplateDescriptor`. let mut hasher: FxHasher = Default::default(); self.font_weight.hash(&mut hasher); self.font_stretch.hash(&mut hasher); @@ -1516,25 +1510,17 @@ pub mod style_structs { self.font_family.hash(&mut hasher); self.hash = hasher.finish() } - - /// (Servo does not handle MathML, so this just calls copy_font_size_from) - pub fn inherit_font_size_from(&mut self, parent: &Self, - _: Option, - _: &Device) { - self.copy_font_size_from(parent); - } - /// (Servo does not handle MathML, so this just calls set_font_size) - pub fn apply_font_size(&mut self, - v: longhands::font_size::computed_value::T, - _: &Self, - _: &Device) -> Option { - self.set_font_size(v); - None - } - /// (Servo does not handle MathML, so this does nothing) - pub fn apply_unconstrained_font_size(&mut self, _: NonNegativeLength) { + /// Create a new Font with the initial values of all members. + pub fn initial_values() -> Self { + Self { + % for longhand in style_struct.longhands: + % if not longhand.logical: + ${longhand.ident}: longhands::${longhand.ident}::get_initial_value(), + % endif + % endfor + hash: 0, + } } - % elif style_struct.name == "InheritedUI": /// Returns the ColorSchemeFlags corresponding to the value of `color-scheme`. #[inline] @@ -1708,7 +1694,7 @@ pub struct ComputedValuesInner { pub writing_mode: WritingMode, /// The effective zoom value. - pub effective_zoom: Zoom, + pub effective_zoom: computed::Zoom, /// A set of flags we use to store misc information regarding this style. pub flags: ComputedValueFlags, @@ -1950,16 +1936,67 @@ impl ComputedValues { } /// Get the initial computed values. - pub fn initial_values() -> &'static Self { &*INITIAL_SERVO_VALUES } + pub fn initial_values_with_font_override(default_font: super::style_structs::Font) -> Arc { + use crate::logical_geometry::WritingMode; + use crate::computed_value_flags::ComputedValueFlags; + use servo_arc::Arc; + use super::{ComputedValues, ComputedValuesInner, longhands, style_structs}; + + Arc::new(ComputedValues { + inner: ComputedValuesInner { + % for style_struct in data.active_style_structs(): + % if style_struct.name == "Font": + font: Arc::new(default_font), + <% continue %> + % endif % + + ${style_struct.ident}: Arc::new(style_structs::${style_struct.name} { + % for longhand in style_struct.longhands: + % if not longhand.logical: + ${longhand.ident}: longhands::${longhand.ident}::get_initial_value(), + % endif + % endfor + % if style_struct.name == "InheritedText": + text_decorations_in_effect: + crate::values::computed::text::TextDecorationsInEffect::default(), + % endif + % if style_struct.name == "Box": + original_display: longhands::display::get_initial_value(), + % endif + }), + % endfor + custom_properties: crate::custom_properties::ComputedCustomProperties::default(), + writing_mode: WritingMode::empty(), + rules: None, + visited_style: None, + effective_zoom: crate::values::computed::Zoom::ONE, + flags: ComputedValueFlags::empty(), + }, + pseudo: None, + }) + } + + /// Converts the computed values to an Arc<> from a reference. + pub fn to_arc(&self) -> Arc { + // SAFETY: We're guaranteed to be allocated as an Arc<> since the + // functions above are the only ones that create ComputedValues + // instances in Servo (and that must be the case since ComputedValues' + // member is private). + unsafe { Arc::from_raw_addrefed(self) } + } /// Serializes the computed value of this property as a string. pub fn computed_value_to_string(&self, property: PropertyDeclarationId) -> String { match property { PropertyDeclarationId::Longhand(id) => { + let context = resolved::Context { + style: self, + }; let mut s = String::new(); - self.get_longhand_property_value( + self.computed_or_resolved_value( id, - &mut CssWriter::new(&mut s) + Some(&context), + &mut s ).unwrap(); s } @@ -1970,9 +2007,8 @@ impl ComputedValues { let p = &self.custom_properties; let value = p .inherited - .as_ref() - .and_then(|map| map.get(name)) - .or_else(|| p.non_inherited.as_ref().and_then(|map| map.get(name))); + .get(name) + .or_else(|| p.non_inherited.get(name)); value.map_or(String::new(), |value| value.to_css_string()) } } @@ -2034,7 +2070,7 @@ impl ComputedValuesInner { use crate::values::generics::counters::Content; match self.get_counters().content { Content::Normal | Content::None => true, - Content::Items(ref items) => items.is_empty(), + Content::Items(ref items) => items.items.is_empty(), } } @@ -2128,7 +2164,7 @@ impl ComputedValuesInner { /// Gets the logical computed margin from this style. #[inline] - pub fn logical_margin(&self) -> LogicalMargin<<&computed::LengthPercentageOrAuto> { + pub fn logical_margin(&self) -> LogicalMargin<<&computed::Margin> { let margin_style = self.get_margin(); LogicalMargin::from_physical(self.writing_mode, SideOffsets2D::new( &margin_style.margin_top, @@ -2140,7 +2176,7 @@ impl ComputedValuesInner { /// Gets the logical position from this style. #[inline] - pub fn logical_position(&self) -> LogicalMargin<<&computed::LengthPercentageOrAuto> { + pub fn logical_position(&self) -> LogicalMargin<<&computed::Inset> { // FIXME(SimonSapin): should be the writing mode of the containing block, maybe? let position_style = self.get_position(); LogicalMargin::from_physical(self.writing_mode, SideOffsets2D::new( @@ -2803,52 +2839,6 @@ impl<'a> StyleBuilder<'a> { % endfor } -#[cfg(feature = "servo")] -pub use self::lazy_static_module::INITIAL_SERVO_VALUES; - -// Use a module to work around #[cfg] on lazy_static! not being applied to every generated item. -#[cfg(feature = "servo")] -#[allow(missing_docs)] -mod lazy_static_module { - use crate::logical_geometry::WritingMode; - use crate::computed_value_flags::ComputedValueFlags; - use servo_arc::Arc; - use super::{ComputedValues, ComputedValuesInner, longhands, style_structs}; - - lazy_static! { - /// The initial values for all style structs as defined by the specification. - pub static ref INITIAL_SERVO_VALUES: ComputedValues = ComputedValues { - inner: ComputedValuesInner { - % for style_struct in data.active_style_structs(): - ${style_struct.ident}: Arc::new(style_structs::${style_struct.name} { - % for longhand in style_struct.longhands: - % if not longhand.logical: - ${longhand.ident}: longhands::${longhand.ident}::get_initial_value(), - % endif - % endfor - % if style_struct.name == "InheritedText": - text_decorations_in_effect: - crate::values::computed::text::TextDecorationsInEffect::default(), - % endif - % if style_struct.name == "Font": - hash: 0, - % endif - % if style_struct.name == "Box": - original_display: longhands::display::get_initial_value(), - % endif - }), - % endfor - custom_properties, - writing_mode: WritingMode::empty(), - rules: None, - visited_style: None, - flags: ComputedValueFlags::empty(), - }, - pseudo: None, - }; - } -} - /// A per-longhand function that performs the CSS cascade for that longhand. pub type CascadePropertyFn = unsafe extern "Rust" fn( @@ -2928,10 +2918,10 @@ macro_rules! css_properties_accessors { % for prop in [property] + property.aliases: % if '-' in prop.name: [${prop.ident.capitalize()}, Set${prop.ident.capitalize()}, - PropertyId::${kind}(${kind}Id::${property.camel_case})], + PropertyId::NonCustom(${kind}Id::${property.camel_case}.into())], % endif [${prop.camel_case}, Set${prop.camel_case}, - PropertyId::${kind}(${kind}Id::${property.camel_case})], + PropertyId::NonCustom(${kind}Id::${property.camel_case}.into())], % endfor % endif % endfor @@ -2945,6 +2935,9 @@ macro_rules! css_properties_accessors { /// ``` /// { snake_case_ident } /// ``` +/// +/// … where the boolean indicates whether the property value type +/// is wrapped in a `Box<_>` in the corresponding `PropertyDeclaration` variant. #[macro_export] macro_rules! longhand_properties_idents { ($macro_name: ident) => { @@ -2957,7 +2950,7 @@ macro_rules! longhand_properties_idents { } // Large pages generate tens of thousands of ComputedValues. -size_of_test!(ComputedValues, 240); +size_of_test!(ComputedValues, 208); // FFI relies on this. size_of_test!(Option>, 8); @@ -2987,6 +2980,9 @@ const_assert!(std::mem::size_of::( % for effect_name in ["repaint", "reflow_out_of_flow", "reflow", "rebuild_and_reflow_inline", "rebuild_and_reflow"]: macro_rules! restyle_damage_${effect_name} { ($old: ident, $new: ident, $damage: ident, [ $($effect:expr),* ]) => ({ + restyle_damage_${effect_name}!($old, $new, $damage, [$($effect),*], false) + }); + ($old: ident, $new: ident, $damage: ident, [ $($effect:expr),* ], $extra:expr) => ({ if % for style_struct in data.active_style_structs(): % for longhand in style_struct.longhands: @@ -2997,13 +2993,13 @@ const_assert!(std::mem::size_of::( % endfor % endfor - false { + $extra || false { $damage.insert($($effect)|*); true } else { false } - }) + }); } % endfor % endif diff --git a/style/properties/shorthands/font.mako.rs b/style/properties/shorthands/font.mako.rs index 198e862871..d708a737ea 100644 --- a/style/properties/shorthands/font.mako.rs +++ b/style/properties/shorthands/font.mako.rs @@ -34,7 +34,9 @@ > use crate::computed_values::font_variant_caps::T::SmallCaps; use crate::parser::Parse; - use crate::properties::longhands::{font_family, font_style, font_size, font_weight, font_stretch}; + use crate::properties::longhands::{font_family, font_style, font_weight, font_stretch}; + #[cfg(feature = "gecko")] + use crate::properties::longhands::font_size; use crate::properties::longhands::font_variant_caps; use crate::values::specified::font::LineHeight; use crate::values::specified::{FontSize, FontWeight}; @@ -315,7 +317,9 @@ % for p in subprops_for_value_info: ${p}::collect_completion_keywords(f); % endfor + % if engine == "gecko": ::collect_completion_keywords(f); + % endif } } diff --git a/style/properties_and_values/registry.rs b/style/properties_and_values/registry.rs index e3cd552c9c..5734aa1b2c 100644 --- a/style/properties_and_values/registry.rs +++ b/style/properties_and_values/registry.rs @@ -69,6 +69,7 @@ impl PropertyRegistration { /// The script registry of custom properties. /// #[derive(Default)] +#[cfg_attr(feature = "servo", derive(MallocSizeOf))] pub struct ScriptRegistry { properties: PrecomputedHashMap, } diff --git a/style/queries/values.rs b/style/queries/values.rs index f4934408c4..077f8f7a08 100644 --- a/style/queries/values.rs +++ b/style/queries/values.rs @@ -34,3 +34,12 @@ impl Orientation { } } } + +/// Values for the prefers-color-scheme media feature. +#[derive(Clone, Copy, Debug, FromPrimitive, Parse, PartialEq, ToCss, MallocSizeOf)] +#[repr(u8)] +#[allow(missing_docs)] +pub enum PrefersColorScheme { + Light, + Dark, +} diff --git a/style/servo/media_queries.rs b/style/servo/media_queries.rs index 4f87c2c49e..fc926c2b24 100644 --- a/style/servo/media_queries.rs +++ b/style/servo/media_queries.rs @@ -7,21 +7,44 @@ use crate::color::AbsoluteColor; use crate::context::QuirksMode; use crate::custom_properties::CssEnvironment; -use crate::media_queries::media_feature::{AllowsRanges, ParsingRequirements}; -use crate::media_queries::media_feature::{Evaluator, MediaFeatureDescription}; -use crate::media_queries::media_feature_expression::RangeOrOperator; +use crate::font_metrics::FontMetrics; +use crate::queries::feature::{AllowsRanges, Evaluator, FeatureFlags, QueryFeatureDescription}; +use crate::queries::values::PrefersColorScheme; +use crate::logical_geometry::WritingMode; use crate::media_queries::MediaType; +use crate::properties::style_structs::Font; use crate::properties::ComputedValues; -use crate::values::computed::CSSPixelLength; +use crate::values::computed::{CSSPixelLength, Context, Length, LineHeight, NonNegativeLength, Resolution}; +use crate::values::computed::font::GenericFontFamily; use crate::values::specified::color::{ColorSchemeFlags, ForcedColors}; -use crate::values::specified::font::FONT_MEDIUM_PX; +use crate::values::specified::font::{FONT_MEDIUM_LINE_HEIGHT_PX, FONT_MEDIUM_PX}; +use crate::values::specified::ViewportVariant; use crate::values::KeyframesName; -use app_units::Au; +use app_units::{Au, AU_PER_PX}; use euclid::default::Size2D as UntypedSize2D; use euclid::{Scale, SideOffsets2D, Size2D}; +use mime::Mime; +use servo_arc::Arc; +use std::fmt::Debug; 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, + in_media_query: bool, + retrieve_math_scales: bool, + ) -> 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. /// @@ -48,16 +71,34 @@ pub struct Device { /// a relaxed atomic here. #[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, /// 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 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 { @@ -67,17 +108,25 @@ impl Device { quirks_mode: QuirksMode, viewport_size: Size2D, device_pixel_ratio: Scale, + font_metrics_provider: Box, + default_computed_values: Arc, + prefers_color_scheme: PrefersColorScheme, ) -> Device { Device { media_type, viewport_size, device_pixel_ratio, quirks_mode, - // FIXME(bz): Seems dubious? root_font_size: AtomicU32::new(FONT_MEDIUM_PX.to_bits()), + root_line_height: AtomicU32::new(FONT_MEDIUM_LINE_HEIGHT_PX.to_bits()), used_root_font_size: AtomicBool::new(false), + used_root_line_height: AtomicBool::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, } } @@ -89,10 +138,7 @@ impl Device { /// Return the default computed values for this device. pub fn default_computed_values(&self) -> &ComputedValues { - // FIXME(bz): This isn't really right, but it's no more wrong - // than what we used to do. See - // https://github.com/servo/servo/issues/14773 for fixing it properly. - ComputedValues::initial_values() + &self.default_computed_values } /// Get the font size of the root element (for rem) @@ -101,10 +147,39 @@ impl Device { CSSPixelLength::new(f32::from_bits(self.root_font_size.load(Ordering::Relaxed))) } - /// Set the font size of the root element (for rem) - pub fn set_root_font_size(&self, size: CSSPixelLength) { - self.root_font_size - .store(size.px().to_bits(), 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); + } + + /// 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. @@ -119,6 +194,11 @@ impl Device { // 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. @@ -130,6 +210,16 @@ impl Device { 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 font metrics have been queried. + pub fn used_font_metrics(&self) -> bool { + self.used_font_metrics.load(Ordering::Relaxed) + } + /// Returns the viewport size of the current device in app units, needed, /// among other things, to resolve viewport units. #[inline] @@ -141,8 +231,13 @@ impl Device { } /// Like the above, but records that we've used viewport units. - pub fn au_viewport_size_for_viewport_unit_resolution(&self) -> UntypedSize2D { + 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() } @@ -151,11 +246,41 @@ impl Device { 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. pub fn device_pixel_ratio(&self) -> Scale { self.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, + in_media_query: bool, + retrieve_math_scales: bool, + ) -> FontMetrics { + self.used_font_metrics.store(true, Ordering::Relaxed); + self.font_metrics_provider.query_font_metrics( + vertical, + font, + base_size, + in_media_query, + retrieve_math_scales, + ) + } + /// Return the media type of the current device. pub fn media_type(&self) -> MediaType { self.media_type.clone() @@ -184,19 +309,34 @@ impl Device { 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( - device: &Device, - value: Option, - range_or_operator: Option, -) -> bool { - RangeOrOperator::evaluate( - range_or_operator, - value.map(Au::from), - device.au_viewport_size().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)] @@ -207,26 +347,65 @@ enum Scan { } /// https://drafts.csswg.org/mediaqueries-4/#scan -fn eval_scan(_: &Device, _: Option) -> bool { +fn eval_scan(_: &Context, _: Option) -> bool { // Since we doesn't support the 'tv' media type, the 'scan' feature never // matches. false } -lazy_static! { - /// A list with all the media features that Servo supports. - pub static ref MEDIA_FEATURES: [MediaFeatureDescription; 2] = [ - feature!( - atom!("width"), - AllowsRanges::Yes, - Evaluator::Length(eval_width), - ParsingRequirements::empty(), - ), - feature!( - atom!("scan"), - AllowsRanges::No, - keyword_evaluator!(eval_scan, Scan), - ParsingRequirements::empty(), - ), - ]; +/// 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/restyle_damage.rs b/style/servo/restyle_damage.rs index fe17fa6198..6355959805 100644 --- a/style/servo/restyle_damage.rs +++ b/style/servo/restyle_damage.rs @@ -12,6 +12,7 @@ use std::fmt; bitflags! { /// Individual layout actions that may be necessary after restyling. + #[derive(Clone, Copy, Debug, Eq, PartialEq)] pub struct ServoRestyleDamage: u8 { /// Repaint the node itself. /// @@ -210,7 +211,8 @@ fn compute_damage(old: &ComputedValues, new: &ComputedValues) -> ServoRestyleDam ServoRestyleDamage::REFLOW_OUT_OF_FLOW, ServoRestyleDamage::REFLOW, ServoRestyleDamage::RECONSTRUCT_FLOW - ] + ], + old.get_box().original_display != new.get_box().original_display ) || (new.get_box().display == Display::Inline && restyle_damage_rebuild_and_reflow_inline!( old, diff --git a/style/servo/selector_parser.rs b/style/servo/selector_parser.rs index b20f1754a0..f04c265875 100644 --- a/style/servo/selector_parser.rs +++ b/style/servo/selector_parser.rs @@ -7,6 +7,7 @@ //! Servo's selector parser. use crate::attr::{AttrIdentifier, AttrValue}; +use crate::computed_value_flags::ComputedValueFlags; use crate::dom::{OpaqueNode, TElement, TNode}; use crate::invalidation::element::document_state::InvalidationMatchingData; use crate::invalidation::element::element_wrapper::ElementSnapshot; @@ -52,24 +53,24 @@ pub enum PseudoElement { // Non-eager pseudos. DetailsSummary, DetailsContent, - ServoText, - ServoInputText, - ServoTableWrapper, - ServoAnonymousTableWrapper, + ServoAnonymousBox, ServoAnonymousTable, - ServoAnonymousTableRow, ServoAnonymousTableCell, - ServoAnonymousBlock, - ServoInlineBlockWrapper, - ServoInlineAbsolute, + ServoAnonymousTableRow, + ServoLegacyText, + ServoLegacyInputText, + ServoLegacyTableWrapper, + ServoLegacyAnonymousTableWrapper, + ServoLegacyAnonymousTable, + ServoLegacyAnonymousBlock, + ServoLegacyInlineBlockWrapper, + ServoLegacyInlineAbsolute, + ServoTableGrid, + ServoTableWrapper, } /// The count of all pseudo-elements. -pub const PSEUDO_COUNT: usize = PseudoElement::ServoInlineAbsolute as usize + 1; - -impl ::selectors::parser::PseudoElement for PseudoElement { - type Impl = SelectorImpl; -} +pub const PSEUDO_COUNT: usize = PseudoElement::ServoTableWrapper as usize + 1; impl ToCss for PseudoElement { fn to_css(&self, dest: &mut W) -> fmt::Result @@ -83,20 +84,28 @@ impl ToCss for PseudoElement { Selection => "::selection", DetailsSummary => "::-servo-details-summary", DetailsContent => "::-servo-details-content", - ServoText => "::-servo-text", - ServoInputText => "::-servo-input-text", - ServoTableWrapper => "::-servo-table-wrapper", - ServoAnonymousTableWrapper => "::-servo-anonymous-table-wrapper", + ServoAnonymousBox => "::-servo-anonymous-box", ServoAnonymousTable => "::-servo-anonymous-table", - ServoAnonymousTableRow => "::-servo-anonymous-table-row", ServoAnonymousTableCell => "::-servo-anonymous-table-cell", - ServoAnonymousBlock => "::-servo-anonymous-block", - ServoInlineBlockWrapper => "::-servo-inline-block-wrapper", - ServoInlineAbsolute => "::-servo-inline-absolute", + ServoAnonymousTableRow => "::-servo-anonymous-table-row", + ServoLegacyText => "::-servo-legacy-text", + ServoLegacyInputText => "::-servo-legacy-input-text", + ServoLegacyTableWrapper => "::-servo-legacy-table-wrapper", + ServoLegacyAnonymousTableWrapper => "::-servo-legacy-anonymous-table-wrapper", + ServoLegacyAnonymousTable => "::-servo-legacy-anonymous-table", + ServoLegacyAnonymousBlock => "::-servo-legacy-anonymous-block", + ServoLegacyInlineBlockWrapper => "::-servo-legacy-inline-block-wrapper", + ServoLegacyInlineAbsolute => "::-servo-legacy-inline-absolute", + ServoTableGrid => "::-servo-table-grid", + ServoTableWrapper => "::-servo-table-wrapper", }) } } +impl ::selectors::parser::PseudoElement for PseudoElement { + type Impl = SelectorImpl; +} + /// The number of eager pseudo-elements. Keep this in sync with cascade_type. pub const EAGER_PSEUDO_COUNT: usize = 3; @@ -225,16 +234,20 @@ impl PseudoElement { }, PseudoElement::DetailsSummary => PseudoElementCascadeType::Lazy, PseudoElement::DetailsContent | - PseudoElement::ServoText | - PseudoElement::ServoInputText | - PseudoElement::ServoTableWrapper | - PseudoElement::ServoAnonymousTableWrapper | + PseudoElement::ServoAnonymousBox | PseudoElement::ServoAnonymousTable | - PseudoElement::ServoAnonymousTableRow | PseudoElement::ServoAnonymousTableCell | - PseudoElement::ServoAnonymousBlock | - PseudoElement::ServoInlineBlockWrapper | - PseudoElement::ServoInlineAbsolute => PseudoElementCascadeType::Precomputed, + PseudoElement::ServoAnonymousTableRow | + PseudoElement::ServoLegacyText | + PseudoElement::ServoLegacyInputText | + PseudoElement::ServoLegacyTableWrapper | + PseudoElement::ServoLegacyAnonymousTableWrapper | + PseudoElement::ServoLegacyAnonymousTable | + PseudoElement::ServoLegacyAnonymousBlock | + PseudoElement::ServoLegacyInlineBlockWrapper | + PseudoElement::ServoLegacyInlineAbsolute | + PseudoElement::ServoTableGrid | + PseudoElement::ServoTableWrapper => PseudoElementCascadeType::Precomputed, } } @@ -273,6 +286,10 @@ impl PseudoElement { /// The type used for storing `:lang` arguments. pub type Lang = Box; +/// The type used to store the state argument to the `:state` pseudo-class. +#[derive(Clone, Debug, Eq, Hash, MallocSizeOf, PartialEq, ToCss, ToShmem)] +pub struct CustomState(pub AtomIdent); + /// A non tree-structural pseudo-class. /// See https://drafts.csswg.org/selectors-4/#structural-pseudos #[derive(Clone, Debug, Eq, Hash, MallocSizeOf, PartialEq, ToShmem)] @@ -280,21 +297,37 @@ pub type Lang = Box; pub enum NonTSPseudoClass { Active, AnyLink, + Autofill, Checked, + /// The :state` pseudo-class. + CustomState(CustomState), + Default, Defined, Disabled, Enabled, Focus, + FocusWithin, + FocusVisible, Fullscreen, Hover, + InRange, Indeterminate, + Invalid, Lang(Lang), Link, + Modal, + Optional, + OutOfRange, PlaceholderShown, - ReadWrite, + PopoverOpen, ReadOnly, + ReadWrite, + Required, ServoNonZeroBorder, Target, + UserInvalid, + UserValid, + Valid, Visited, } @@ -335,24 +368,38 @@ impl ToCss for NonTSPseudoClass { } dest.write_str(match *self { - Active => ":active", - AnyLink => ":any-link", - Checked => ":checked", - Defined => ":defined", - Disabled => ":disabled", - Enabled => ":enabled", - Focus => ":focus", - Fullscreen => ":fullscreen", - Hover => ":hover", - Indeterminate => ":indeterminate", - Link => ":link", - PlaceholderShown => ":placeholder-shown", - ReadWrite => ":read-write", - ReadOnly => ":read-only", - ServoNonZeroBorder => ":-servo-nonzero-border", - Target => ":target", - Visited => ":visited", - Lang(_) => unreachable!(), + Self::Active => ":active", + Self::AnyLink => ":any-link", + Self::Autofill => ":autofill", + Self::Checked => ":checked", + Self::Default => ":default", + Self::Defined => ":defined", + Self::Disabled => ":disabled", + Self::Enabled => ":enabled", + Self::Focus => ":focus", + Self::FocusVisible => ":focus-visible", + Self::FocusWithin => ":focus-within", + Self::Fullscreen => ":fullscreen", + Self::Hover => ":hover", + Self::InRange => ":in-range", + Self::Indeterminate => ":indeterminate", + Self::Invalid => ":invalid", + Self::Link => ":link", + Self::Modal => ":modal", + Self::Optional => ":optional", + Self::OutOfRange => ":out-of-range", + Self::PlaceholderShown => ":placeholder-shown", + Self::PopoverOpen => ":popover-open", + Self::ReadOnly => ":read-only", + Self::ReadWrite => ":read-write", + Self::Required => ":required", + Self::ServoNonZeroBorder => ":-servo-nonzero-border", + Self::Target => ":target", + Self::UserInvalid => ":user-invalid", + Self::UserValid => ":user-valid", + Self::Valid => ":valid", + Self::Visited => ":visited", + Self::Lang(_) | Self::CustomState(_) => unreachable!(), }) } } @@ -361,22 +408,38 @@ impl NonTSPseudoClass { /// Gets a given state flag for this pseudo-class. This is used to do /// selector matching, and it's set from the DOM. pub fn state_flag(&self) -> ElementState { - use self::NonTSPseudoClass::*; match *self { - Active => ElementState::IN_ACTIVE_STATE, - Focus => ElementState::IN_FOCUS_STATE, - Fullscreen => ElementState::IN_FULLSCREEN_STATE, - Hover => ElementState::IN_HOVER_STATE, - Defined => ElementState::IN_DEFINED_STATE, - Enabled => ElementState::IN_ENABLED_STATE, - Disabled => ElementState::IN_DISABLED_STATE, - Checked => ElementState::IN_CHECKED_STATE, - Indeterminate => ElementState::IN_INDETERMINATE_STATE, - ReadOnly | ReadWrite => ElementState::IN_READWRITE_STATE, - PlaceholderShown => ElementState::IN_PLACEHOLDER_SHOWN_STATE, - Target => ElementState::IN_TARGET_STATE, - - AnyLink | Lang(_) | Link | Visited | ServoNonZeroBorder => ElementState::empty(), + Self::Active => ElementState::ACTIVE, + Self::AnyLink => ElementState::VISITED_OR_UNVISITED, + Self::Autofill => ElementState::AUTOFILL, + Self::Checked => ElementState::CHECKED, + Self::Default => ElementState::DEFAULT, + Self::Defined => ElementState::DEFINED, + Self::Disabled => ElementState::DISABLED, + Self::Enabled => ElementState::ENABLED, + Self::Focus => ElementState::FOCUS, + Self::FocusVisible => ElementState::FOCUSRING, + Self::FocusWithin => ElementState::FOCUS_WITHIN, + Self::Fullscreen => ElementState::FULLSCREEN, + Self::Hover => ElementState::HOVER, + Self::InRange => ElementState::INRANGE, + Self::Indeterminate => ElementState::INDETERMINATE, + Self::Invalid => ElementState::INVALID, + Self::Link => ElementState::UNVISITED, + Self::Modal => ElementState::MODAL, + Self::Optional => ElementState::OPTIONAL_, + Self::OutOfRange => ElementState::OUTOFRANGE, + Self::PlaceholderShown => ElementState::PLACEHOLDER_SHOWN, + Self::PopoverOpen => ElementState::POPOVER_OPEN, + Self::ReadOnly => ElementState::READONLY, + Self::ReadWrite => ElementState::READWRITE, + Self::Required => ElementState::REQUIRED, + Self::Target => ElementState::URLTARGET, + Self::UserInvalid => ElementState::USER_INVALID, + Self::UserValid => ElementState::USER_VALID, + Self::Valid => ElementState::VALID, + Self::Visited => ElementState::VISITED, + Self::CustomState(_) | Self::Lang(_) | Self::ServoNonZeroBorder => ElementState::empty(), } } @@ -397,57 +460,106 @@ impl NonTSPseudoClass { #[cfg_attr(feature = "servo", derive(MallocSizeOf))] pub struct SelectorImpl; +/// A set of extra data to carry along with the matching context, either for +/// selector-matching or invalidation. +#[derive(Debug, Default)] +pub struct ExtraMatchingData<'a> { + /// The invalidation data to invalidate doc-state pseudo-classes correctly. + pub invalidation_data: InvalidationMatchingData, + + /// The invalidation bits from matching container queries. These are here + /// just for convenience mostly. + pub cascade_input_flags: ComputedValueFlags, + + /// The style of the originating element in order to evaluate @container + /// size queries affecting pseudo-elements. + pub originating_element_style: Option<&'a ComputedValues>, +} + impl ::selectors::SelectorImpl for SelectorImpl { type PseudoElement = PseudoElement; type NonTSPseudoClass = NonTSPseudoClass; - type ExtraMatchingData = InvalidationMatchingData; - type AttrValue = String; - type Identifier = Atom; - type ClassName = Atom; - type PartName = Atom; + type ExtraMatchingData<'a> = ExtraMatchingData<'a>; + type AttrValue = AtomString; + type Identifier = AtomIdent; type LocalName = LocalName; type NamespacePrefix = Prefix; type NamespaceUrl = Namespace; - type BorrowedLocalName = LocalName; - type BorrowedNamespaceUrl = Namespace; + type BorrowedLocalName = markup5ever::LocalName; + type BorrowedNamespaceUrl = markup5ever::Namespace; } impl<'a, 'i> ::selectors::Parser<'i> for SelectorParser<'a> { type Impl = SelectorImpl; type Error = StyleParseErrorKind<'i>; + #[inline] + fn parse_nth_child_of(&self) -> bool { + false + } + + #[inline] + fn parse_is_and_where(&self) -> bool { + true + } + + #[inline] + fn parse_has(&self) -> bool { + false + } + + #[inline] + fn parse_parent_selector(&self) -> bool { + false + } + + #[inline] + fn allow_forgiving_selectors(&self) -> bool { + !self.for_supports_rule + } + fn parse_non_ts_pseudo_class( &self, location: SourceLocation, name: CowRcStr<'i>, ) -> Result> { - use self::NonTSPseudoClass::*; let pseudo_class = match_ignore_ascii_case! { &name, - "active" => Active, - "any-link" => AnyLink, - "checked" => Checked, - "defined" => Defined, - "disabled" => Disabled, - "enabled" => Enabled, - "focus" => Focus, - "fullscreen" => Fullscreen, - "hover" => Hover, - "indeterminate" => Indeterminate, - "-moz-inert" => MozInert, - "link" => Link, - "placeholder-shown" => PlaceholderShown, - "read-write" => ReadWrite, - "read-only" => ReadOnly, - "target" => Target, - "visited" => Visited, + "active" => NonTSPseudoClass::Active, + "any-link" => NonTSPseudoClass::AnyLink, + "autofill" => NonTSPseudoClass::Autofill, + "checked" => NonTSPseudoClass::Checked, + "default" => NonTSPseudoClass::Default, + "defined" => NonTSPseudoClass::Defined, + "disabled" => NonTSPseudoClass::Disabled, + "enabled" => NonTSPseudoClass::Enabled, + "focus" => NonTSPseudoClass::Focus, + "focus-visible" => NonTSPseudoClass::FocusVisible, + "focus-within" => NonTSPseudoClass::FocusWithin, + "fullscreen" => NonTSPseudoClass::Fullscreen, + "hover" => NonTSPseudoClass::Hover, + "indeterminate" => NonTSPseudoClass::Indeterminate, + "invalid" => NonTSPseudoClass::Invalid, + "link" => NonTSPseudoClass::Link, + "optional" => NonTSPseudoClass::Optional, + "out-of-range" => NonTSPseudoClass::OutOfRange, + "placeholder-shown" => NonTSPseudoClass::PlaceholderShown, + "popover-open" => NonTSPseudoClass::PopoverOpen, + "read-only" => NonTSPseudoClass::ReadOnly, + "read-write" => NonTSPseudoClass::ReadWrite, + "required" => NonTSPseudoClass::Required, + "target" => NonTSPseudoClass::Target, + "user-invalid" => NonTSPseudoClass::UserInvalid, + "user-valid" => NonTSPseudoClass::UserValid, + "valid" => NonTSPseudoClass::Valid, + "visited" => NonTSPseudoClass::Visited, "-servo-nonzero-border" => { if !self.in_user_agent_stylesheet() { return Err(location.new_custom_error( SelectorParseErrorKind::UnexpectedIdent("-servo-nonzero-border".into()) )) } - ServoNonZeroBorder + NonTSPseudoClass::ServoNonZeroBorder }, _ => return Err(location.new_custom_error(SelectorParseErrorKind::UnexpectedIdent(name.clone()))), }; @@ -459,10 +571,11 @@ impl<'a, 'i> ::selectors::Parser<'i> for SelectorParser<'a> { &self, name: CowRcStr<'i>, parser: &mut CssParser<'i, 't>, + after_part: bool, ) -> Result> { use self::NonTSPseudoClass::*; let pseudo_class = match_ignore_ascii_case! { &name, - "lang" => { + "lang" if !after_part => { Lang(parser.expect_ident_or_string()?.as_ref().into()) }, _ => return Err(parser.new_custom_error(SelectorParseErrorKind::UnexpectedIdent(name.clone()))), @@ -493,29 +606,41 @@ impl<'a, 'i> ::selectors::Parser<'i> for SelectorParser<'a> { } DetailsContent }, - "-servo-text" => { + "-servo-anonymous-box" => { if !self.in_user_agent_stylesheet() { return Err(location.new_custom_error(SelectorParseErrorKind::UnexpectedIdent(name.clone()))) } - ServoText + ServoAnonymousBox }, - "-servo-input-text" => { + "-servo-legacy-text" => { if !self.in_user_agent_stylesheet() { return Err(location.new_custom_error(SelectorParseErrorKind::UnexpectedIdent(name.clone()))) } - ServoInputText + ServoLegacyText }, - "-servo-table-wrapper" => { + "-servo-legacy-input-text" => { if !self.in_user_agent_stylesheet() { return Err(location.new_custom_error(SelectorParseErrorKind::UnexpectedIdent(name.clone()))) } - ServoTableWrapper + ServoLegacyInputText + }, + "-servo-legacy-table-wrapper" => { + if !self.in_user_agent_stylesheet() { + return Err(location.new_custom_error(SelectorParseErrorKind::UnexpectedIdent(name.clone()))) + } + ServoLegacyTableWrapper + }, + "-servo-legacy-anonymous-table-wrapper" => { + if !self.in_user_agent_stylesheet() { + return Err(location.new_custom_error(SelectorParseErrorKind::UnexpectedIdent(name.clone()))) + } + ServoLegacyAnonymousTableWrapper }, - "-servo-anonymous-table-wrapper" => { + "-servo-legacy-anonymous-table" => { if !self.in_user_agent_stylesheet() { return Err(location.new_custom_error(SelectorParseErrorKind::UnexpectedIdent(name.clone()))) } - ServoAnonymousTableWrapper + ServoLegacyAnonymousTable }, "-servo-anonymous-table" => { if !self.in_user_agent_stylesheet() { @@ -535,23 +660,35 @@ impl<'a, 'i> ::selectors::Parser<'i> for SelectorParser<'a> { } ServoAnonymousTableCell }, - "-servo-anonymous-block" => { + "-servo-legacy-anonymous-block" => { + if !self.in_user_agent_stylesheet() { + return Err(location.new_custom_error(SelectorParseErrorKind::UnexpectedIdent(name.clone()))) + } + ServoLegacyAnonymousBlock + }, + "-servo-legacy-inline-block-wrapper" => { if !self.in_user_agent_stylesheet() { return Err(location.new_custom_error(SelectorParseErrorKind::UnexpectedIdent(name.clone()))) } - ServoAnonymousBlock + ServoLegacyInlineBlockWrapper }, - "-servo-inline-block-wrapper" => { + "-servo-legacy-inline-absolute" => { if !self.in_user_agent_stylesheet() { return Err(location.new_custom_error(SelectorParseErrorKind::UnexpectedIdent(name.clone()))) } - ServoInlineBlockWrapper + ServoLegacyInlineAbsolute }, - "-servo-inline-absolute" => { + "-servo-table-grid" => { if !self.in_user_agent_stylesheet() { return Err(location.new_custom_error(SelectorParseErrorKind::UnexpectedIdent(name.clone()))) } - ServoInlineAbsolute + ServoTableGrid + }, + "-servo-table-wrapper" => { + if !self.in_user_agent_stylesheet() { + return Err(location.new_custom_error(SelectorParseErrorKind::UnexpectedIdent(name.clone()))) + } + ServoTableWrapper }, _ => return Err(location.new_custom_error(SelectorParseErrorKind::UnexpectedIdent(name.clone()))) @@ -730,6 +867,25 @@ impl ElementSnapshot for ServoElementSnapshot { .or_else(|| self.get_attr(&ns!(), &local_name!("lang"))) .map(|v| SelectorAttrValue::from(v as &str)) } + + /// Returns true if the snapshot has stored state for custom states + #[inline] + fn has_custom_states(&self) -> bool { + false + } + + /// Returns true if the snapshot has a given CustomState + #[inline] + fn has_custom_state(&self, _state: &AtomIdent) -> bool { + false + } + + #[inline] + fn each_custom_state(&self, mut _callback: F) + where + F: FnMut(&AtomIdent), + { + } } impl ServoElementSnapshot { diff --git a/style/str.rs b/style/str.rs index fe598f546e..ad706746fe 100644 --- a/style/str.rs +++ b/style/str.rs @@ -178,3 +178,11 @@ pub type CssStringWriter = ::nsstring::nsACString; /// needs to allocate a temporary string. #[cfg(feature = "gecko")] pub type CssString = ::nsstring::nsCString; + +/// String. The comments for the Gecko types explain the need for this abstraction. +#[cfg(feature = "servo")] +pub type CssStringWriter = String; + +/// String. The comments for the Gecko types explain the need for this abstraction. +#[cfg(feature = "servo")] +pub type CssString = String; diff --git a/style/stylesheets/import_rule.rs b/style/stylesheets/import_rule.rs index d0e5528c18..477942ca0c 100644 --- a/style/stylesheets/import_rule.rs +++ b/style/stylesheets/import_rule.rs @@ -108,18 +108,45 @@ impl DeepCloneWithLock for ImportSheet { /// A sheet that is held from an import rule. #[cfg(feature = "servo")] #[derive(Debug)] -pub struct ImportSheet(pub ::servo_arc::Arc); +pub enum ImportSheet { + /// A bonafide stylesheet. + Sheet(::servo_arc::Arc), + + /// An @import created with a false , so will never be fetched. + Refused, +} #[cfg(feature = "servo")] impl ImportSheet { + /// Creates a new ImportSheet from a stylesheet. + pub fn new(sheet: ::servo_arc::Arc) -> Self { + ImportSheet::Sheet(sheet) + } + + /// Creates a refused ImportSheet for a load that will not happen. + pub fn new_refused() -> Self { + ImportSheet::Refused + } + + /// Returns a reference to the stylesheet in this ImportSheet, if it exists. + pub fn as_sheet(&self) -> Option<&::servo_arc::Arc> { + match *self { + ImportSheet::Sheet(ref s) => Some(s), + ImportSheet::Refused => None, + } + } + /// Returns the media list for this import rule. pub fn media<'a>(&'a self, guard: &'a SharedRwLockReadGuard) -> Option<&'a MediaList> { - self.0.media(guard) + self.as_sheet().and_then(|s| s.media(guard)) } /// Returns the rules for this import rule. pub fn rules<'a>(&'a self, guard: &'a SharedRwLockReadGuard) -> &'a [CssRule] { - self.0.rules() + match self.as_sheet() { + Some(s) => s.rules(guard), + None => &[], + } } } @@ -130,8 +157,13 @@ impl DeepCloneWithLock for ImportSheet { _lock: &SharedRwLock, _guard: &SharedRwLockReadGuard, ) -> Self { - use servo_arc::Arc; - ImportSheet(Arc::new((&*self.0).clone())) + match *self { + ImportSheet::Sheet(ref s) => { + use servo_arc::Arc; + ImportSheet::Sheet(Arc::new((&**s).clone())) + }, + ImportSheet::Refused => ImportSheet::Refused, + } } } diff --git a/style/stylesheets/mod.rs b/style/stylesheets/mod.rs index 8af13ef15c..1871f1037d 100644 --- a/style/stylesheets/mod.rs +++ b/style/stylesheets/mod.rs @@ -115,8 +115,8 @@ pub struct UrlExtraData(usize); /// Extra data that the backend may need to resolve url values. #[cfg(feature = "servo")] -#[derive(Clone, Debug, Eq, PartialEq)] -pub struct UrlExtraData(pub Arc<::url::Url>); +#[derive(Clone, Debug, Eq, MallocSizeOf, PartialEq)] +pub struct UrlExtraData(#[ignore_malloc_size_of = "Arc"] pub Arc<::url::Url>); #[cfg(feature = "servo")] impl UrlExtraData { diff --git a/style/stylesheets/position_try_rule.rs b/style/stylesheets/position_try_rule.rs index 3efab5ee5a..584d4122c4 100644 --- a/style/stylesheets/position_try_rule.rs +++ b/style/stylesheets/position_try_rule.rs @@ -15,6 +15,7 @@ use crate::shared_lock::{ use crate::str::CssStringWriter; use crate::values::DashedIdent; use cssparser::SourceLocation; +#[cfg(feature = "gecko")] use malloc_size_of::{MallocSizeOf, MallocSizeOfOps, MallocUnconditionalShallowSizeOf}; use servo_arc::Arc; use style_traits::{CssWriter, ToCss}; diff --git a/style/stylesheets/stylesheet.rs b/style/stylesheets/stylesheet.rs index 64d78fae46..e34e8d8e93 100644 --- a/style/stylesheets/stylesheet.rs +++ b/style/stylesheets/stylesheet.rs @@ -122,13 +122,12 @@ impl StylesheetContents { /// An empty namespace map should be fine, as it is only used for parsing, /// not serialization of existing selectors. Since UA sheets are read only, /// we should never need the namespace map. - pub fn from_shared_data( + pub fn from_data( rules: Arc>, origin: Origin, url_data: UrlExtraData, quirks_mode: QuirksMode, ) -> Arc { - debug_assert!(rules.is_static()); Arc::new(Self { rules, origin, @@ -141,6 +140,17 @@ impl StylesheetContents { }) } + /// Same as above, but ensuring that the rules are static. + pub fn from_shared_data( + rules: Arc>, + origin: Origin, + url_data: UrlExtraData, + quirks_mode: QuirksMode, + ) -> Arc { + debug_assert!(rules.is_static()); + Self::from_data(rules, origin, url_data, quirks_mode) + } + /// Returns a reference to the list of rules. #[inline] pub fn rules<'a, 'b: 'a>(&'a self, guard: &'b SharedRwLockReadGuard) -> &'a [CssRule] { diff --git a/style/stylist.rs b/style/stylist.rs index 9801b53965..d4950c5e22 100644 --- a/style/stylist.rs +++ b/style/stylist.rs @@ -47,9 +47,10 @@ use crate::stylesheets::scope_rule::{ #[cfg(feature = "gecko")] use crate::stylesheets::{ CounterStyleRule, FontFaceRule, FontFeatureValuesRule, FontPaletteValuesRule, + PagePseudoClassFlags, }; use crate::stylesheets::{ - CssRule, EffectiveRulesIterator, Origin, OriginSet, PagePseudoClassFlags, PageRule, PerOrigin, + CssRule, EffectiveRulesIterator, Origin, OriginSet, PageRule, PerOrigin, PerOriginIter, StylesheetContents, StylesheetInDocument, }; use crate::values::{computed, AtomIdent}; @@ -57,9 +58,9 @@ use crate::AllocErr; use crate::{Atom, LocalName, Namespace, ShrinkIfNeeded, WeakAtom}; use dom::{DocumentState, ElementState}; use fxhash::FxHashMap; -use malloc_size_of::MallocSizeOf; +use malloc_size_of::{MallocSizeOf, MallocShallowSizeOf, MallocSizeOfOps}; #[cfg(feature = "gecko")] -use malloc_size_of::{MallocShallowSizeOf, MallocSizeOfOps, MallocUnconditionalShallowSizeOf}; +use malloc_size_of::MallocUnconditionalShallowSizeOf; use selectors::attr::{CaseSensitivity, NamespaceConstraint}; use selectors::bloom::BloomFilter; use selectors::matching::{ @@ -542,6 +543,7 @@ pub struct Stylist { stylesheets: StylistStylesheetSet, /// A cache of CascadeDatas for AuthorStylesheetSets (i.e., shadow DOM). + #[cfg_attr(feature = "servo", ignore_malloc_size_of = "XXX: how to handle this?")] author_data_cache: CascadeDataCache, /// If true, the quirks-mode stylesheet is applied. @@ -564,6 +566,7 @@ pub struct Stylist { script_custom_properties: CustomPropertyScriptRegistry, /// Initial values for registered custom properties. + #[ignore_malloc_size_of = "Arc"] initial_values_for_custom_properties: ComputedCustomProperties, /// Flags set from computing registered custom property initial values. @@ -3188,7 +3191,9 @@ impl CascadeData { } } #[cfg(feature = "gecko")] - self.extra_data.sort_by_layer(&self.layers); + { + self.extra_data.sort_by_layer(&self.layers); + } self.animations .sort_with(&self.layers, compare_keyframes_in_same_layer); self.custom_property_registrations.sort(&self.layers) diff --git a/style/values/animated/effects.rs b/style/values/animated/effects.rs index 67557e54b7..e2b37cecbe 100644 --- a/style/values/animated/effects.rs +++ b/style/values/animated/effects.rs @@ -24,4 +24,4 @@ pub type AnimatedFilter = /// An animated value for a single `filter`. #[cfg(not(feature = "gecko"))] -pub type AnimatedFilter = GenericFilter; +pub type AnimatedFilter = GenericFilter; diff --git a/style/values/computed/effects.rs b/style/values/computed/effects.rs index b0a92024ca..ce3498d141 100644 --- a/style/values/computed/effects.rs +++ b/style/values/computed/effects.rs @@ -36,7 +36,7 @@ pub type Filter = GenericFilter< NonNegativeNumber, ZeroToOneNumber, NonNegativeLength, - Impossible, + SimpleShadow, Impossible, >; diff --git a/style/values/computed/font.rs b/style/values/computed/font.rs index 3219fb5815..0e5c128607 100644 --- a/style/values/computed/font.rs +++ b/style/values/computed/font.rs @@ -1205,7 +1205,7 @@ pub type FontStretchFixedPoint = FixedPoint; #[derive( Clone, ComputeSquaredDistance, Copy, Debug, MallocSizeOf, PartialEq, PartialOrd, ToResolvedValue, )] -#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))] +#[cfg_attr(feature = "servo", derive(Deserialize, Hash, Serialize))] #[repr(C)] pub struct FontStretch(pub FontStretchFixedPoint); diff --git a/style/values/computed/image.rs b/style/values/computed/image.rs index 8a91d95313..4a317b4912 100644 --- a/style/values/computed/image.rs +++ b/style/values/computed/image.rs @@ -29,7 +29,7 @@ pub use specified::ImageRendering; pub type Image = generic::GenericImage; // Images should remain small, see https://github.com/servo/servo/pull/18430 -size_of_test!(Image, 16); +size_of_test!(Image, 40); /// Computed values for a CSS gradient. /// diff --git a/style/values/computed/length.rs b/style/values/computed/length.rs index bed8d37b74..b7e78183e7 100644 --- a/style/values/computed/length.rs +++ b/style/values/computed/length.rs @@ -19,7 +19,7 @@ use crate::values::{specified, CSSFloat}; use crate::Zero; use app_units::Au; use std::fmt::{self, Write}; -use std::ops::{Add, AddAssign, Div, Mul, MulAssign, Neg, Sub}; +use std::ops::{Add, AddAssign, Div, Mul, MulAssign, Neg, Sub, SubAssign}; use style_traits::{CSSPixel, CssWriter, ToCss}; pub use super::image::Image; @@ -182,8 +182,23 @@ impl MaxSize { #[inline] pub fn to_used_value(&self, percentage_basis: Au) -> Option { match *self { - GenericMaxSize::None => None, - GenericMaxSize::LengthPercentage(ref lp) => Some(lp.to_used_value(percentage_basis)), + Self::None | Self::MinContent | Self::MaxContent | Self::FitContent | Self::Stretch => { + None + }, + Self::LengthPercentage(ref lp) => Some(lp.to_used_value(percentage_basis)), + Self::AnchorSizeFunction(_) => unreachable!("anchor-size() should be disabled"), + } + } + + /// Convert the computed value into used value if there is enough information. + #[inline] + pub fn maybe_to_used_value(&self, percentage_basis: Option) -> Option { + match *self { + Self::None | Self::MinContent | Self::MaxContent | Self::FitContent | Self::Stretch => { + None + }, + Self::LengthPercentage(ref lp) => lp.maybe_to_used_value(percentage_basis), + Self::AnchorSizeFunction(_) => unreachable!("anchor-size() should be disabled"), } } } @@ -194,8 +209,24 @@ impl Size { #[cfg(feature = "servo")] pub fn to_used_value(&self, percentage_basis: Au) -> Option { match *self { - GenericSize::Auto => None, - GenericSize::LengthPercentage(ref lp) => Some(lp.to_used_value(percentage_basis)), + Self::Auto | Self::MinContent | Self::MaxContent | Self::FitContent | Self::Stretch => { + None + }, + Self::LengthPercentage(ref lp) => Some(lp.to_used_value(percentage_basis)), + Self::AnchorSizeFunction(_) => unreachable!("anchor-size() should be disabled"), + } + } + + /// Convert the computed value into used value if there is enough information. + #[inline] + #[cfg(feature = "servo")] + pub fn maybe_to_used_value(&self, percentage_basis: Option) -> Option { + match *self { + Self::Auto | Self::MinContent | Self::MaxContent | Self::FitContent | Self::Stretch => { + None + }, + Self::LengthPercentage(ref lp) => lp.maybe_to_used_value(percentage_basis), + Self::AnchorSizeFunction(_) => unreachable!("anchor-size() should be disabled"), } } @@ -205,15 +236,13 @@ impl Size { match *self { Self::Auto => false, Self::LengthPercentage(ref lp) => lp.is_definitely_zero(), - #[cfg(feature = "gecko")] Self::MinContent | Self::MaxContent | Self::FitContent | - Self::MozAvailable | - Self::WebkitFillAvailable | Self::Stretch | - Self::FitContentFunction(_) | Self::AnchorSizeFunction(_) => false, + #[cfg(feature = "gecko")] + Self::MozAvailable | Self::WebkitFillAvailable | Self::FitContentFunction(_) => false, } } } @@ -457,6 +486,13 @@ impl Sub for CSSPixelLength { } } +impl SubAssign for CSSPixelLength { + #[inline] + fn sub_assign(&mut self, other: Self) { + self.0 -= other.0; + } +} + impl From for Au { #[inline] fn from(len: CSSPixelLength) -> Self { diff --git a/style/values/generics/image.rs b/style/values/generics/image.rs index ca1c716052..7865b4fc55 100644 --- a/style/values/generics/image.rs +++ b/style/values/generics/image.rs @@ -13,7 +13,6 @@ use crate::values::generics::Optional; use crate::values::serialize_atom_identifier; use crate::Atom; use crate::Zero; -use servo_arc::Arc; use std::fmt::{self, Write}; use style_traits::{CssWriter, ToCss}; @@ -368,10 +367,9 @@ pub struct PaintWorklet { pub name: Atom, /// The arguments for the worklet. /// TODO: store a parsed representation of the arguments. - #[cfg_attr(feature = "servo", ignore_malloc_size_of = "Arc")] #[compute(no_field_bound)] #[resolve(no_field_bound)] - pub arguments: Vec>, + pub arguments: Vec, } impl ::style_traits::SpecifiedValueInfo for PaintWorklet {} diff --git a/style/values/generics/length.rs b/style/values/generics/length.rs index 6512d7dd7f..73e804c62c 100644 --- a/style/values/generics/length.rs +++ b/style/values/generics/length.rs @@ -7,7 +7,6 @@ use crate::parser::{Parse, ParserContext}; use crate::values::generics::Optional; use crate::values::DashedIdent; -#[cfg(feature = "gecko")] use crate::Zero; use cssparser::Parser; use std::fmt::Write; @@ -75,13 +74,13 @@ impl LengthPercentageOrAuto { } } -impl LengthPercentageOrAuto +impl LengthPercentageOrAuto where - LengthPercentage: Clone, + T: Clone, { /// Resolves `auto` values by calling `f`. #[inline] - pub fn auto_is(&self, f: impl FnOnce() -> LengthPercentage) -> LengthPercentage { + pub fn auto_is(&self, f: impl FnOnce() -> T) -> T { match self { LengthPercentageOrAuto::LengthPercentage(length) => length.clone(), LengthPercentageOrAuto::Auto => f(), @@ -90,7 +89,7 @@ where /// Returns the non-`auto` value, if any. #[inline] - pub fn non_auto(&self) -> Option { + pub fn non_auto(&self) -> Option { match self { LengthPercentageOrAuto::LengthPercentage(length) => Some(length.clone()), LengthPercentageOrAuto::Auto => None, @@ -98,7 +97,7 @@ where } /// Maps the length of this value. - pub fn map(&self, f: impl FnOnce(LengthPercentage) -> T) -> LengthPercentageOrAuto { + pub fn map(&self, f: impl FnOnce(T) -> U) -> LengthPercentageOrAuto { match self { LengthPercentageOrAuto::LengthPercentage(l) => { LengthPercentageOrAuto::LengthPercentage(f(l.clone())) @@ -155,13 +154,10 @@ impl Parse for LengthPercentageOrAuto pub enum GenericSize { LengthPercentage(LengthPercent), Auto, - #[cfg(feature = "gecko")] #[animation(error)] MaxContent, - #[cfg(feature = "gecko")] #[animation(error)] MinContent, - #[cfg(feature = "gecko")] #[animation(error)] FitContent, #[cfg(feature = "gecko")] @@ -172,6 +168,7 @@ pub enum GenericSize { WebkitFillAvailable, #[animation(error)] Stretch, + #[cfg(feature = "gecko")] #[animation(error)] #[css(function = "fit-content")] FitContentFunction(LengthPercent), @@ -234,13 +231,10 @@ impl Size { pub enum GenericMaxSize { LengthPercentage(LengthPercent), None, - #[cfg(feature = "gecko")] #[animation(error)] MaxContent, - #[cfg(feature = "gecko")] #[animation(error)] MinContent, - #[cfg(feature = "gecko")] #[animation(error)] FitContent, #[cfg(feature = "gecko")] @@ -251,6 +245,7 @@ pub enum GenericMaxSize { WebkitFillAvailable, #[animation(error)] Stretch, + #[cfg(feature = "gecko")] #[animation(error)] #[css(function = "fit-content")] FitContentFunction(LengthPercent), @@ -560,6 +555,27 @@ pub enum GenericMargin { ), } +#[cfg(feature = "servo")] +impl GenericMargin { + /// Return true if it is 'auto'. + #[inline] + pub fn is_auto(&self) -> bool { + matches!(self, Self::Auto) + } +} + +#[cfg(feature = "servo")] +impl GenericMargin { + /// Returns true if the computed value is absolute 0 or 0%. + #[inline] + pub fn is_definitely_zero(&self) -> bool { + match self { + Self::LengthPercentage(lp) => lp.is_definitely_zero(), + _ => false, + } + } +} + impl SpecifiedValueInfo for GenericMargin where LP: SpecifiedValueInfo, diff --git a/style/values/generics/position.rs b/style/values/generics/position.rs index c895fc09b3..ab75d9ad9a 100644 --- a/style/values/generics/position.rs +++ b/style/values/generics/position.rs @@ -314,6 +314,13 @@ impl GenericInset { pub fn auto() -> Self { Self::Auto } + + /// Return true if it is 'auto'. + #[inline] + #[cfg(feature = "servo")] + pub fn is_auto(&self) -> bool { + matches!(self, Self::Auto) + } } pub use self::GenericInset as Inset; diff --git a/style/values/generics/transform.rs b/style/values/generics/transform.rs index 0d26dc2c9e..70dba82a05 100644 --- a/style/values/generics/transform.rs +++ b/style/values/generics/transform.rs @@ -604,6 +604,13 @@ impl Transform { } /// Same as Transform::to_transform_3d_matrix but a f64 version. + pub fn to_transform_3d_matrix_f64( + &self, + reference_box: Option<&Rect> + ) -> Result<(Transform3D, bool), ()> { + Self::components_to_transform_3d_matrix_f64(&self.0, reference_box) + } + fn components_to_transform_3d_matrix_f64( ops: &[T], reference_box: Option<&Rect>, diff --git a/style/values/mod.rs b/style/values/mod.rs index ef0ed1fb0c..ad416b09d8 100644 --- a/style/values/mod.rs +++ b/style/values/mod.rs @@ -297,6 +297,16 @@ impl cssparser::ToCss for GenericAtomIdent style_traits::ToCss for GenericAtomIdent { + fn to_css(&self, dest: &mut CssWriter) -> fmt::Result + where + W: Write, + { + serialize_atom_identifier(&self.0, dest) + } +} + #[cfg(feature = "servo")] impl PrecomputedHash for GenericAtomIdent { #[inline] @@ -718,7 +728,7 @@ impl ToCss for KeyframesName { return dest.write_str("none"); } - let serialize = |string: &_| { + let mut serialize = |string: &_| { if CustomIdent::is_valid(string, &["none"]) { serialize_identifier(string, dest) } else { diff --git a/style/values/specified/animation.rs b/style/values/specified/animation.rs index fd12e43c14..a711661609 100644 --- a/style/values/specified/animation.rs +++ b/style/values/specified/animation.rs @@ -311,7 +311,7 @@ impl AnimationPlayState { #[cfg(feature = "gecko")] return name.with_str(|n| Self::from_ident(n).is_ok()); #[cfg(feature = "servo")] - return Self::from_ident(atom).is_ok(); + return Self::from_ident(name).is_ok(); } false } @@ -346,7 +346,10 @@ impl AnimationFillMode { #[inline] pub fn match_keywords(name: &AnimationName) -> bool { if let Some(atom) = name.as_atom() { + #[cfg(feature = "gecko")] return !name.is_none() && atom.with_str(|n| Self::from_ident(n).is_ok()); + #[cfg(feature = "servo")] + return !name.is_none() && Self::from_ident(atom).is_ok(); } false } diff --git a/style/values/specified/box.rs b/style/values/specified/box.rs index 93f26b6325..f227ad61d4 100644 --- a/style/values/specified/box.rs +++ b/style/values/specified/box.rs @@ -5,7 +5,6 @@ //! Specified types for box properties. use crate::parser::{Parse, ParserContext}; -#[cfg(feature = "gecko")] use crate::properties::{LonghandId, PropertyDeclarationId, PropertyId}; use crate::values::generics::box_::{ GenericContainIntrinsicSize, GenericLineClamp, GenericPerspective, GenericVerticalAlign, @@ -16,7 +15,7 @@ use crate::values::specified::{AllowQuirks, Integer, NonNegativeNumberOrPercenta use crate::values::CustomIdent; use cssparser::Parser; use num_traits::FromPrimitive; -use std::fmt::{self, Write}; +use std::fmt::{self, Debug, Write}; use style_traits::{CssWriter, KeywordsCollectFn, ParseError}; use style_traits::{SpecifiedValueInfo, StyleParseErrorKind, ToCss}; @@ -31,10 +30,7 @@ fn grid_enabled() -> bool { #[cfg(feature = "servo")] fn flexbox_enabled() -> bool { - servo_config::prefs::pref_map() - .get("layout.flexbox.enabled") - .as_bool() - .unwrap_or(false) + style_config::get_bool("layout.flexbox.enabled") } #[cfg(feature = "servo")] fn grid_enabled() -> bool { @@ -1025,7 +1021,7 @@ bitflags! { } #[cfg(feature="servo")] -fn change_bits_for_longhand(longhand: LonghandId) -> WillChangeBits { WillChangeBits::empty() } +fn change_bits_for_longhand(_longhand: LonghandId) -> WillChangeBits { WillChangeBits::empty() } #[cfg(feature = "gecko")] fn change_bits_for_longhand(longhand: LonghandId) -> WillChangeBits { diff --git a/style/values/specified/effects.rs b/style/values/specified/effects.rs index 64e7ed290c..8ed38b739c 100644 --- a/style/values/specified/effects.rs +++ b/style/values/specified/effects.rs @@ -53,7 +53,7 @@ pub type SpecifiedFilter = GenericFilter< NonNegativeFactor, ZeroToOneFactor, NonNegativeLength, - Impossible, + SimpleShadow, Impossible, >; diff --git a/style/values/specified/font.rs b/style/values/specified/font.rs index ee01506248..0457ee8f4d 100644 --- a/style/values/specified/font.rs +++ b/style/values/specified/font.rs @@ -771,48 +771,41 @@ pub const FONT_MEDIUM_PX: f32 = 16.0; pub const FONT_MEDIUM_LINE_HEIGHT_PX: f32 = FONT_MEDIUM_PX * 1.2; impl FontSizeKeyword { - #[inline] - #[cfg(feature = "servo")] - fn to_length(&self, _: &Context) -> NonNegativeLength { - let medium = Length::new(FONT_MEDIUM_PX); - // https://drafts.csswg.org/css-fonts-3/#font-size-prop - NonNegative(match *self { - FontSizeKeyword::XXSmall => medium * 3.0 / 5.0, - FontSizeKeyword::XSmall => medium * 3.0 / 4.0, - FontSizeKeyword::Small => medium * 8.0 / 9.0, - FontSizeKeyword::Medium => medium, - FontSizeKeyword::Large => medium * 6.0 / 5.0, - FontSizeKeyword::XLarge => medium * 3.0 / 2.0, - FontSizeKeyword::XXLarge => medium * 2.0, - FontSizeKeyword::XXXLarge => medium * 3.0, - FontSizeKeyword::Math | FontSizeKeyword::None => unreachable!(), - }) - } - - #[cfg(feature = "gecko")] #[inline] fn to_length(&self, cx: &Context) -> NonNegativeLength { let font = cx.style().get_font(); + + #[cfg(feature = "servo")] + let family = &font.font_family.families; + + #[cfg(feature = "gecko")] let family = &font.mFont.family.families; + let generic = family .single_generic() .unwrap_or(computed::GenericFontFamily::None); + + #[cfg(feature = "gecko")] let base_size = unsafe { Atom::with(font.mLanguage.mRawPtr, |language| { cx.device().base_size_for_generic(language, generic) }) }; + + #[cfg(feature = "servo")] + let base_size = cx.device().base_size_for_generic(generic); + self.to_length_without_context(cx.quirks_mode, base_size) } /// Resolve a keyword length without any context, with explicit arguments. - #[cfg(feature = "gecko")] #[inline] pub fn to_length_without_context( &self, quirks_mode: QuirksMode, base_size: Length, ) -> NonNegativeLength { + #[cfg(feature = "gecko")] debug_assert_ne!(*self, FontSizeKeyword::Math); // The tables in this function are originally from // nsRuleNode::CalcFontPointSize in Gecko: @@ -932,6 +925,7 @@ impl FontSize { calc.resolve(base_size.resolve(context).computed_size()) }, FontSize::Keyword(i) => { + #[cfg(feature="gecko")] if i.kw == FontSizeKeyword::Math { // Scaling is done in recompute_math_font_size_if_needed(). info = compose_keyword(1.); @@ -946,6 +940,11 @@ impl FontSize { info = i; i.to_computed_value(context).clamp_to_non_negative() } + #[cfg(feature="servo")] { + // As a specified keyword, this is keyword derived + info = i; + i.to_computed_value(context).clamp_to_non_negative() + } }, FontSize::Smaller => { info = compose_keyword(1. / LARGER_FONT_SIZE_RATIO); diff --git a/style/values/specified/image.rs b/style/values/specified/image.rs index 5d494a44c7..8ce05399f6 100644 --- a/style/values/specified/image.rs +++ b/style/values/specified/image.rs @@ -43,7 +43,7 @@ fn gradient_color_interpolation_method_enabled() -> bool { pub type Image = generic::Image; // Images should remain small, see https://github.com/servo/servo/pull/18430 -size_of_test!(Image, 16); +size_of_test!(Image, 40); /// Specified values for a CSS gradient. /// @@ -1274,13 +1274,13 @@ impl generic::ColorStop { impl PaintWorklet { #[cfg(feature = "servo")] - fn parse_args<'i>(input: &mut Parser<'i, '_>) -> Result> { + fn parse_args<'i>(context: &ParserContext, input: &mut Parser<'i, '_>) -> Result> { use crate::custom_properties::SpecifiedValue; let name = Atom::from(&**input.expect_ident()?); let arguments = input .try_parse(|input| { input.expect_comma()?; - input.parse_comma_separated(SpecifiedValue::parse) + input.parse_comma_separated(|input| SpecifiedValue::parse(input, &context.url_data)) }) .unwrap_or_default(); Ok(Self { name, arguments }) diff --git a/style/values/specified/length.rs b/style/values/specified/length.rs index 352c14314c..95951ec5ce 100644 --- a/style/values/specified/length.rs +++ b/style/values/specified/length.rs @@ -1231,6 +1231,7 @@ impl NoCalcLength { /// absolute or (if a font metrics getter is provided) font-relative units. #[cfg(feature = "gecko")] #[inline] + #[cfg(feature = "gecko")] pub fn to_computed_pixel_length_with_font_metrics( &self, get_font_metrics: Option GeckoFontMetrics>, @@ -2008,17 +2009,13 @@ macro_rules! parse_size_non_length { ($size:ident, $input:expr, $auto_or_none:expr => $auto_or_none_ident:ident) => {{ let size = $input.try_parse(|input| { Ok(try_match_ident_ignore_ascii_case! { input, - #[cfg(feature = "gecko")] "min-content" | "-moz-min-content" => $size::MinContent, - #[cfg(feature = "gecko")] "max-content" | "-moz-max-content" => $size::MaxContent, - #[cfg(feature = "gecko")] "fit-content" | "-moz-fit-content" => $size::FitContent, #[cfg(feature = "gecko")] "-moz-available" => $size::MozAvailable, #[cfg(feature = "gecko")] "-webkit-fill-available" if is_webkit_fill_available_keyword_enabled() => $size::WebkitFillAvailable, - #[cfg(feature = "gecko")] "stretch" if is_stretch_enabled() => $size::Stretch, $auto_or_none => $size::$auto_or_none_ident, }) @@ -2033,7 +2030,6 @@ macro_rules! parse_size_non_length { fn is_webkit_fill_available_keyword_enabled() -> bool { static_prefs::pref!("layout.css.webkit-fill-available.enabled") } -#[cfg(feature = "gecko")] fn is_stretch_enabled() -> bool { static_prefs::pref!("layout.css.stretch-size-keyword.enabled") } @@ -2042,11 +2038,8 @@ fn is_stretch_enabled() -> bool { fn is_fit_content_function_enabled() -> bool { static_prefs::pref!("layout.css.fit-content-function.enabled") } -#[cfg(feature = "servo")] -fn is_fit_content_function_enabled() -> bool { - false -} +#[cfg(feature = "gecko")] macro_rules! parse_fit_content_function { ($size:ident, $input:expr, $context:expr, $allow_quirks:expr) => { if is_fit_content_function_enabled() { @@ -2070,6 +2063,7 @@ impl Size { allow_quirks: AllowQuirks, ) -> Result> { parse_size_non_length!(Size, input, "auto" => Auto); + #[cfg(feature = "gecko")] parse_fit_content_function!(Size, input, context, allow_quirks); if let Ok(length) = @@ -2109,6 +2103,7 @@ impl MaxSize { allow_quirks: AllowQuirks, ) -> Result> { parse_size_non_length!(MaxSize, input, "none" => None); + #[cfg(feature = "gecko")] parse_fit_content_function!(MaxSize, input, context, allow_quirks); if let Ok(length) = diff --git a/style/values/specified/list.rs b/style/values/specified/list.rs index d085bcf189..d10aaa96d8 100644 --- a/style/values/specified/list.rs +++ b/style/values/specified/list.rs @@ -7,6 +7,8 @@ #[cfg(feature = "gecko")] use crate::counter_style::{CounterStyle, CounterStyleParsingFlags}; use crate::parser::{Parse, ParserContext}; +#[cfg(feature = "servo")] +use crate::properties::longhands::list_style_type::SpecifiedValue as ListStyleType; use cssparser::{Parser, Token}; use style_traits::{ParseError, StyleParseErrorKind}; diff --git a/style/values/specified/text.rs b/style/values/specified/text.rs index 1e9c7a6819..b99d8fd1c2 100644 --- a/style/values/specified/text.rs +++ b/style/values/specified/text.rs @@ -365,10 +365,15 @@ bitflags! { /// Capitalize each word. const CAPITALIZE = 1 << 2; /// Automatic italicization of math variables. + #[cfg(feature = "gecko")] const MATH_AUTO = 1 << 3; /// All the case transforms, which are exclusive with each other. + #[cfg(feature = "gecko")] const CASE_TRANSFORMS = Self::UPPERCASE.0 | Self::LOWERCASE.0 | Self::CAPITALIZE.0 | Self::MATH_AUTO.0; + /// All the case transforms, which are exclusive with each other. + #[cfg(feature = "servo")] + const CASE_TRANSFORMS = Self::UPPERCASE.0 | Self::LOWERCASE.0 | Self::CAPITALIZE.0; /// full-width const FULL_WIDTH = 1 << 4; @@ -395,6 +400,19 @@ impl TextTransform { // Case bits are exclusive with each other. case.is_empty() || case.bits().is_power_of_two() } + + /// Returns the corresponding TextTransformCase. + pub fn case(&self) -> TextTransformCase { + match *self & Self::CASE_TRANSFORMS { + Self::NONE => TextTransformCase::None, + Self::UPPERCASE => TextTransformCase::Uppercase, + Self::LOWERCASE => TextTransformCase::Lowercase, + Self::CAPITALIZE => TextTransformCase::Capitalize, + #[cfg(feature = "gecko")] + Self::MATH_AUTO => TextTransformCase::MathAuto, + _ => unreachable!("Case bits are exclusive with each other"), + } + } } /// Specified and computed value of text-align-last. @@ -483,7 +501,6 @@ pub enum TextAlign { /// Since selectors can't depend on the ancestor styles, we implement it with a /// magic value that computes to the right thing. Since this is an /// implementation detail, it shouldn't be exposed to web content. - #[cfg(feature = "gecko")] #[parse(condition = "ParserContext::chrome_rules_enabled")] MozCenterOrInherit, } @@ -519,7 +536,6 @@ impl ToComputedValue for TextAlign { _ => parent, } }, - #[cfg(feature = "gecko")] TextAlign::MozCenterOrInherit => { let parent = _context .builder diff --git a/style_config/Cargo.toml b/style_config/Cargo.toml new file mode 100644 index 0000000000..0076dbf6e8 --- /dev/null +++ b/style_config/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "style_config" +version = "0.0.1" +authors = ["The Servo Project Developers"] +license = "MPL-2.0" +edition = "2021" +publish = false + +[lib] +name = "style_config" +path = "lib.rs" + +[dependencies] +lazy_static = "1.4" diff --git a/style_config/lib.rs b/style_config/lib.rs new file mode 100644 index 0000000000..4881ea6df6 --- /dev/null +++ b/style_config/lib.rs @@ -0,0 +1,95 @@ +/* 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::RwLock; + +use lazy_static::lazy_static; + +lazy_static! { + static ref PREFS: Preferences = Preferences::default(); +} + +#[derive(Debug, Default)] +pub struct Preferences { + bool_prefs: RwLock>, + i32_prefs: RwLock>, +} + +impl Preferences { + pub fn get_bool(&self, key: &str) -> bool { + let prefs = self.bool_prefs.read().expect("RwLock is poisoned"); + *prefs.get(key).unwrap_or(&false) + } + + pub fn get_i32(&self, key: &str) -> i32 { + let prefs = self.i32_prefs.read().expect("RwLock is poisoned"); + *prefs.get(key).unwrap_or(&0) + } + + pub fn set_bool(&self, key: &str, value: bool) { + let mut prefs = self.bool_prefs.write().expect("RwLock is poisoned"); + + // Avoid cloning the key if it exists. + if let Some(pref) = prefs.get_mut(key) { + *pref = value; + } else { + prefs.insert(key.to_owned(), value); + } + } + + pub fn set_i32(&self, key: &str, value: i32) { + let mut prefs = self.i32_prefs.write().expect("RwLock is poisoned"); + + // Avoid cloning the key if it exists. + if let Some(pref) = prefs.get_mut(key) { + *pref = value; + } else { + prefs.insert(key.to_owned(), value); + } + } +} + +pub fn get_bool(key: &str) -> bool { + PREFS.get_bool(key) +} + +pub fn get_i32(key: &str) -> i32 { + PREFS.get_i32(key) +} + +pub fn set_bool(key: &str, value: bool) { + PREFS.set_bool(key, value) +} + +pub fn set_i32(key: &str, value: i32) { + PREFS.set_i32(key, value) +} + +#[test] +fn test() { + let prefs = Preferences::default(); + + // Prefs have default values when unset. + assert_eq!(prefs.get_bool("foo"), false); + assert_eq!(prefs.get_i32("bar"), 0); + + // Prefs can be set and retrieved. + prefs.set_bool("foo", true); + prefs.set_i32("bar", 1); + assert_eq!(prefs.get_bool("foo"), true); + assert_eq!(prefs.get_i32("bar"), 1); + prefs.set_bool("foo", false); + prefs.set_i32("bar", 2); + assert_eq!(prefs.get_bool("foo"), false); + assert_eq!(prefs.get_i32("bar"), 2); + + // Each value type currently has an independent namespace. + prefs.set_i32("foo", 3); + prefs.set_bool("bar", true); + assert_eq!(prefs.get_i32("foo"), 3); + assert_eq!(prefs.get_bool("foo"), false); + assert_eq!(prefs.get_bool("bar"), true); + assert_eq!(prefs.get_i32("bar"), 2); +} diff --git a/style_static_prefs/Cargo.toml b/style_static_prefs/Cargo.toml new file mode 100644 index 0000000000..7a42225d8e --- /dev/null +++ b/style_static_prefs/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "static_prefs" +version = "0.1.0" +edition = "2021" +authors = ["The Servo Project Developers"] +license = "MPL-2.0" +publish = false diff --git a/style_static_prefs/src/lib.rs b/style_static_prefs/src/lib.rs new file mode 100644 index 0000000000..1a36fc9149 --- /dev/null +++ b/style_static_prefs/src/lib.rs @@ -0,0 +1,36 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ + +//! A list of static preferences exposed to the style crate. These should +//! be kept sync with the preferences used by the style. +#[macro_export] +macro_rules! pref { + ("layout.css.stylo-local-work-queue.in-main-thread") => { + 32 + }; + ("layout.css.stylo-work-unit-size") => { + 16 + }; + ("layout.css.stylo-local-work-queue.in-worker") => { + 0 + }; + ("layout.css.system-ui.enabled") => { + true + }; + ("layout.css.basic-shape-rect.enabled") => { + true + }; + ("layout.css.basic-shape-xywh.enabled") => { + true + }; + ("layout.css.stretch-size-keyword.enabled") => { + true + }; + ("layout.css.transition-behavior.enabled") => { + true + }; + ($string:literal) => { + false + }; +} diff --git a/style_traits/Cargo.toml b/style_traits/Cargo.toml index 6e456c80e4..f558c4cef2 100644 --- a/style_traits/Cargo.toml +++ b/style_traits/Cargo.toml @@ -12,7 +12,7 @@ path = "lib.rs" [features] servo = ["servo_atoms", "cssparser/serde", "url", "euclid/serde"] -gecko = ["nsstring"] +gecko = [] [dependencies] app_units = "0.7" @@ -21,8 +21,7 @@ cssparser = "0.34" euclid = "0.22" lazy_static = "1" 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} +malloc_size_of_derive = "0.1" selectors = { path = "../selectors" } serde = "1.0" servo_arc = { path = "../servo_arc" } diff --git a/style_traits/lib.rs b/style_traits/lib.rs index 7e155538fb..66a07884d2 100644 --- a/style_traits/lib.rs +++ b/style_traits/lib.rs @@ -28,12 +28,12 @@ extern crate serde; extern crate servo_arc; #[cfg(feature = "servo")] extern crate servo_atoms; -#[cfg(feature = "servo")] -extern crate servo_url; extern crate thin_vec; extern crate to_shmem; #[macro_use] extern crate to_shmem_derive; +#[cfg(feature = "servo")] +extern crate url; use cssparser::{CowRcStr, Token}; use selectors::parser::SelectorParseErrorKind; diff --git a/style_traits/values.rs b/style_traits/values.rs index e406bb7b1c..e2b87b0229 100644 --- a/style_traits/values.rs +++ b/style_traits/values.rs @@ -100,6 +100,7 @@ pub trait ToCss { /// /// (This is a convenience wrapper for `to_css` and probably should not be overridden.) #[inline] + #[cfg(feature = "gecko")] fn to_css_nscstring(&self) -> nsCString { let mut s = nsCString::new(); self.to_css(&mut CssWriter::new(&mut s)).unwrap(); diff --git a/sync.sh b/sync.sh new file mode 100755 index 0000000000..e92182c746 --- /dev/null +++ b/sync.sh @@ -0,0 +1,43 @@ +#!/bin/sh +# Usage: sync.sh +set -eu + +root=$(pwd) +mkdir -p "$1" +cd -- "$1" +filtered=$(pwd) +mkdir -p "$root/_cache" +cd "$root/_cache" +export PATH="$PWD:$PATH" + +step() { + if [ "${TERM-}" != '' ]; then + tput setaf 12 + fi + >&2 printf '* %s\n' "$*" + if [ "${TERM-}" != '' ]; then + tput sgr0 + fi +} + +step Downloading git-filter-repo if needed +if ! git filter-repo --version 2> /dev/null; then + curl -O https://raw.githubusercontent.com/newren/git-filter-repo/v2.38.0/git-filter-repo + chmod +x git-filter-repo + + git filter-repo --version +fi + +step Cloning upstream if needed +if ! [ -e upstream ]; then + git clone --bare --single-branch --progress https://github.com/mozilla/gecko-dev.git upstream +fi + +step Updating upstream +branch=$(git -C upstream rev-parse --abbrev-ref HEAD) +git -C upstream fetch origin $branch:$branch + +step Filtering upstream +# Cloning and filtering is much faster than git filter-repo --source --target. +git clone --bare upstream -- "$filtered" +git -C "$filtered" filter-repo --force --paths-from-file "$root/style.paths"