mirror of
https://github.com/servo/servo.git
synced 2025-06-06 16:45:39 +00:00
Auto merge of #29848 - Loirooriol:sync, r=mrobinson
Backport several style changes from Gecko (4) <!-- Please describe your changes on the following line: --> This continues https://github.com/servo/servo/pull/29816. --- <!-- Thank you for contributing to Servo! Please replace each `[ ]` by `[X]` when the step is complete, and replace `___` with appropriate data: --> - [X] `./mach build -d` does not report any errors - [X] `./mach test-tidy` does not report any errors - [ ] These changes fix #___ (GitHub issue number if applicable) <!-- Either: --> - [ ] There are tests for these changes OR - [ ] These changes do not require tests because ___ <!-- Also, please make sure that "Allow edits from maintainers" checkbox is checked, so that we can help you if you get stuck somewhere along the way.--> <!-- Pull requests that do not address these steps are welcome, but they will require additional verification as part of the review process. -->
This commit is contained in:
commit
9260683b5f
143 changed files with 2648 additions and 8840 deletions
51
Cargo.lock
generated
51
Cargo.lock
generated
|
@ -1204,9 +1204,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "darling"
|
||||
version = "0.10.2"
|
||||
version = "0.13.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0d706e75d87e35569db781a9b5e2416cff1236a47ed380831f959382ccd5f858"
|
||||
checksum = "a01d95850c592940db9b8194bc39f4bc0e89dee5c4265e4b1807c34a9aba453c"
|
||||
dependencies = [
|
||||
"darling_core",
|
||||
"darling_macro",
|
||||
|
@ -1214,9 +1214,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "darling_core"
|
||||
version = "0.10.2"
|
||||
version = "0.13.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f0c960ae2da4de88a91b2d920c2a7233b400bc33cb28453a2987822d8392519b"
|
||||
checksum = "859d65a907b6852c9361e3185c862aae7fafd2887876799fa55f5f99dc40d610"
|
||||
dependencies = [
|
||||
"fnv",
|
||||
"ident_case",
|
||||
|
@ -1227,9 +1227,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "darling_macro"
|
||||
version = "0.10.2"
|
||||
version = "0.13.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d9b5a2f4ac4969822c62224815d069952656cadc7084fdca9751e6d959189b72"
|
||||
checksum = "9c972679f83bdf9c42bd905396b6c3588a843a17f0f16dfcfa3e2c5d57441835"
|
||||
dependencies = [
|
||||
"darling_core",
|
||||
"quote",
|
||||
|
@ -1601,14 +1601,6 @@ version = "0.1.2"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed"
|
||||
|
||||
[[package]]
|
||||
name = "fallible"
|
||||
version = "0.0.1"
|
||||
dependencies = [
|
||||
"hashglobe",
|
||||
"smallvec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fastrand"
|
||||
version = "1.7.0"
|
||||
|
@ -2598,14 +2590,6 @@ version = "0.9.1"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d7afe4a420e3fe79967a00898cc1f4db7c8a49a9333a29f8a4bd76a253d5cd04"
|
||||
|
||||
[[package]]
|
||||
name = "hashglobe"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"rand 0.7.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "headers"
|
||||
version = "0.3.8"
|
||||
|
@ -2775,9 +2759,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "ident_case"
|
||||
version = "1.0.0"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3c9826188e666f2ed92071d2dadef6edc430b11b158b5b2b3f4babbcc891eaaa"
|
||||
checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39"
|
||||
|
||||
[[package]]
|
||||
name = "idna"
|
||||
|
@ -3461,7 +3445,6 @@ dependencies = [
|
|||
"crossbeam-channel 0.4.4",
|
||||
"cssparser",
|
||||
"euclid",
|
||||
"hashglobe",
|
||||
"http",
|
||||
"hyper_serde",
|
||||
"keyboard-types",
|
||||
|
@ -3472,7 +3455,6 @@ dependencies = [
|
|||
"smallbitvec",
|
||||
"smallvec",
|
||||
"string_cache",
|
||||
"thin-slice",
|
||||
"time 0.1.45",
|
||||
"tokio",
|
||||
"url",
|
||||
|
@ -3749,6 +3731,12 @@ dependencies = [
|
|||
"walkdir",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "mozbuild"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "903970ae2f248d7275214cf8f387f8ba0c4ea7e3d87a320e85493db60ce28616"
|
||||
|
||||
[[package]]
|
||||
name = "mozjs"
|
||||
version = "0.14.1"
|
||||
|
@ -5970,9 +5958,7 @@ dependencies = [
|
|||
"derive_more",
|
||||
"encoding_rs",
|
||||
"euclid",
|
||||
"fallible",
|
||||
"fxhash",
|
||||
"hashglobe",
|
||||
"html5ever",
|
||||
"indexmap",
|
||||
"itertools",
|
||||
|
@ -5982,6 +5968,7 @@ dependencies = [
|
|||
"malloc_size_of",
|
||||
"malloc_size_of_derive",
|
||||
"mime",
|
||||
"mozbuild",
|
||||
"new_debug_unreachable",
|
||||
"num-derive",
|
||||
"num-integer",
|
||||
|
@ -6003,7 +5990,6 @@ dependencies = [
|
|||
"string_cache",
|
||||
"style_derive",
|
||||
"style_traits",
|
||||
"thin-slice",
|
||||
"time 0.1.45",
|
||||
"to_shmem",
|
||||
"to_shmem_derive",
|
||||
|
@ -6216,12 +6202,6 @@ dependencies = [
|
|||
"unicode-width",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thin-slice"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8eaa81235c7058867fa8c0e7314f33dcce9c215f535d1913822a2b3f5e289f3c"
|
||||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "1.0.38"
|
||||
|
@ -6371,7 +6351,6 @@ dependencies = [
|
|||
"smallbitvec",
|
||||
"smallvec",
|
||||
"string_cache",
|
||||
"thin-slice",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
|
@ -24,7 +24,7 @@ cookie = "0.12"
|
|||
content-security-policy = { version = "0.5", features = ["serde"]}
|
||||
crossbeam-channel = "0.4"
|
||||
cssparser = "0.29"
|
||||
darling = { version = "0.10", default-features = false }
|
||||
darling = { version = "0.13", default-features = false }
|
||||
data-url = "0.1.0"
|
||||
env_logger = "0.8"
|
||||
fnv = "1.0"
|
||||
|
|
|
@ -1,3 +1,7 @@
|
|||
-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-radius
|
||||
-moz-gtk-menu-radius
|
||||
DOMContentLoaded
|
||||
|
|
|
@ -1,26 +0,0 @@
|
|||
[package]
|
||||
name = "fallible"
|
||||
version = "0.0.1"
|
||||
authors = ["The Servo Project Developers"]
|
||||
license = "MPL-2.0"
|
||||
edition = "2018"
|
||||
publish = false
|
||||
|
||||
[lib]
|
||||
name = "fallible"
|
||||
path = "lib.rs"
|
||||
|
||||
[dependencies]
|
||||
hashglobe = { path = "../hashglobe" }
|
||||
smallvec = { workspace = true }
|
||||
|
||||
# This crate effectively does nothing except if the `known_system_malloc`
|
||||
# feature is specified.
|
||||
#
|
||||
# In that case, we actually call the system malloc functions to reserve space,
|
||||
# otherwise we just let Rust do its thing (aborting on OOM).
|
||||
#
|
||||
# This is effectively a stop-gap measure until we can do this properly in
|
||||
# stable Rust.
|
||||
[features]
|
||||
known_system_malloc = []
|
|
@ -1,160 +0,0 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#[cfg(feature = "known_system_malloc")]
|
||||
use hashglobe::alloc;
|
||||
use hashglobe::FailedAllocationError;
|
||||
use smallvec::Array;
|
||||
use smallvec::SmallVec;
|
||||
use std::vec::Vec;
|
||||
|
||||
pub trait FallibleVec<T> {
|
||||
/// Append |val| to the end of |vec|. Returns Ok(()) on success,
|
||||
/// Err(reason) if it fails, with |reason| describing the failure.
|
||||
fn try_push(&mut self, value: T) -> Result<(), FailedAllocationError>;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////
|
||||
// Vec
|
||||
|
||||
impl<T> FallibleVec<T> for Vec<T> {
|
||||
#[inline(always)]
|
||||
fn try_push(&mut self, val: T) -> Result<(), FailedAllocationError> {
|
||||
#[cfg(feature = "known_system_malloc")]
|
||||
{
|
||||
if self.capacity() == self.len() {
|
||||
try_double_vec(self)?;
|
||||
debug_assert!(self.capacity() > self.len());
|
||||
}
|
||||
}
|
||||
self.push(val);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
// Double the capacity of |vec|, or fail to do so due to lack of memory.
|
||||
// Returns Ok(()) on success, Err(..) on failure.
|
||||
#[cfg(feature = "known_system_malloc")]
|
||||
#[inline(never)]
|
||||
#[cold]
|
||||
fn try_double_vec<T>(vec: &mut Vec<T>) -> Result<(), FailedAllocationError> {
|
||||
use std::mem;
|
||||
|
||||
let old_ptr = vec.as_mut_ptr();
|
||||
let old_len = vec.len();
|
||||
|
||||
let old_cap: usize = vec.capacity();
|
||||
let new_cap: usize = if old_cap == 0 {
|
||||
4
|
||||
} else {
|
||||
old_cap
|
||||
.checked_mul(2)
|
||||
.ok_or(FailedAllocationError::new("capacity overflow for Vec"))?
|
||||
};
|
||||
|
||||
let new_size_bytes = new_cap
|
||||
.checked_mul(mem::size_of::<T>())
|
||||
.ok_or(FailedAllocationError::new("capacity overflow for Vec"))?;
|
||||
|
||||
let new_ptr = unsafe {
|
||||
if old_cap == 0 {
|
||||
alloc::alloc(new_size_bytes, 0)
|
||||
} else {
|
||||
alloc::realloc(old_ptr as *mut u8, new_size_bytes)
|
||||
}
|
||||
};
|
||||
|
||||
if new_ptr.is_null() {
|
||||
return Err(FailedAllocationError::new(
|
||||
"out of memory when allocating Vec",
|
||||
));
|
||||
}
|
||||
|
||||
let new_vec = unsafe { Vec::from_raw_parts(new_ptr as *mut T, old_len, new_cap) };
|
||||
|
||||
mem::forget(mem::replace(vec, new_vec));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////
|
||||
// SmallVec
|
||||
|
||||
impl<T: Array> FallibleVec<T::Item> for SmallVec<T> {
|
||||
#[inline(always)]
|
||||
fn try_push(&mut self, val: T::Item) -> Result<(), FailedAllocationError> {
|
||||
#[cfg(feature = "known_system_malloc")]
|
||||
{
|
||||
if self.capacity() == self.len() {
|
||||
try_double_small_vec(self)?;
|
||||
debug_assert!(self.capacity() > self.len());
|
||||
}
|
||||
}
|
||||
self.push(val);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
// Double the capacity of |svec|, or fail to do so due to lack of memory.
|
||||
// Returns Ok(()) on success, Err(..) on failure.
|
||||
#[cfg(feature = "known_system_malloc")]
|
||||
#[inline(never)]
|
||||
#[cold]
|
||||
fn try_double_small_vec<T>(svec: &mut SmallVec<T>) -> Result<(), FailedAllocationError>
|
||||
where
|
||||
T: Array,
|
||||
{
|
||||
use std::mem;
|
||||
use std::ptr::copy_nonoverlapping;
|
||||
|
||||
let old_ptr = svec.as_mut_ptr();
|
||||
let old_len = svec.len();
|
||||
|
||||
let old_cap: usize = svec.capacity();
|
||||
let new_cap: usize = if old_cap == 0 {
|
||||
4
|
||||
} else {
|
||||
old_cap
|
||||
.checked_mul(2)
|
||||
.ok_or(FailedAllocationError::new("capacity overflow for SmallVec"))?
|
||||
};
|
||||
|
||||
// This surely shouldn't fail, if |old_cap| was previously accepted as a
|
||||
// valid value. But err on the side of caution.
|
||||
let old_size_bytes = old_cap
|
||||
.checked_mul(mem::size_of::<T>())
|
||||
.ok_or(FailedAllocationError::new("capacity overflow for SmallVec"))?;
|
||||
|
||||
let new_size_bytes = new_cap
|
||||
.checked_mul(mem::size_of::<T>())
|
||||
.ok_or(FailedAllocationError::new("capacity overflow for SmallVec"))?;
|
||||
|
||||
let new_ptr;
|
||||
if svec.spilled() {
|
||||
// There's an old block to free, and, presumably, old contents to
|
||||
// copy. realloc takes care of both aspects.
|
||||
unsafe {
|
||||
new_ptr = alloc::realloc(old_ptr as *mut u8, new_size_bytes);
|
||||
}
|
||||
} else {
|
||||
// There's no old block to free. There may be old contents to copy.
|
||||
unsafe {
|
||||
new_ptr = alloc::alloc(new_size_bytes, 0);
|
||||
if !new_ptr.is_null() && old_size_bytes > 0 {
|
||||
copy_nonoverlapping(old_ptr as *const u8, new_ptr as *mut u8, old_size_bytes);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if new_ptr.is_null() {
|
||||
return Err(FailedAllocationError::new(
|
||||
"out of memory when allocating SmallVec",
|
||||
));
|
||||
}
|
||||
|
||||
let new_vec = unsafe { Vec::from_raw_parts(new_ptr as *mut T::Item, old_len, new_cap) };
|
||||
|
||||
let new_svec = SmallVec::from_vec(new_vec);
|
||||
mem::forget(mem::replace(svec, new_svec));
|
||||
Ok(())
|
||||
}
|
|
@ -124,6 +124,7 @@ fn font_family(names: Vec<&str>) -> FontFamily {
|
|||
list: names.into_boxed_slice(),
|
||||
},
|
||||
is_system_font: false,
|
||||
is_initial: false,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,16 +0,0 @@
|
|||
[package]
|
||||
name = "hashglobe"
|
||||
version = "0.1.0"
|
||||
authors = ["The Rust Project Developers", "Manish Goregaokar <manishsmail@gmail.com>"]
|
||||
license = "MIT OR Apache-2.0"
|
||||
description = "Fork of std::HashMap with stable fallible allocation."
|
||||
documentation = "https://docs.rs/hashglobe"
|
||||
repository = "https://github.com/Manishearth/hashglobe"
|
||||
readme = "README.md"
|
||||
edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
libc = { workspace = true }
|
||||
|
||||
[dev-dependencies]
|
||||
rand = { workspace = true }
|
|
@ -1,201 +0,0 @@
|
|||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
|
@ -1,23 +0,0 @@
|
|||
Permission is hereby granted, free of charge, to any
|
||||
person obtaining a copy of this software and associated
|
||||
documentation files (the "Software"), to deal in the
|
||||
Software without restriction, including without
|
||||
limitation the rights to use, copy, modify, merge,
|
||||
publish, distribute, sublicense, and/or sell copies of
|
||||
the Software, and to permit persons to whom the Software
|
||||
is furnished to do so, subject to the following
|
||||
conditions:
|
||||
|
||||
The above copyright notice and this permission notice
|
||||
shall be included in all copies or substantial portions
|
||||
of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
|
||||
ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
|
||||
TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
|
||||
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
|
||||
SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
|
||||
IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
|
@ -1,17 +0,0 @@
|
|||
hashglobe
|
||||
========
|
||||
|
||||
|
||||
This is a fork of Rust's `std::HashMap`. It works on stable out of the stdlib and has fallible APIs.
|
||||
|
||||
We intend to diverge as little as possible from the original hashmap.
|
||||
|
||||
|
||||
Dual licensed Apache/MIT, the same as the stdlib.
|
||||
|
||||
|
||||
## Should I use this?
|
||||
|
||||
No.
|
||||
|
||||
Wait for https://github.com/rust-lang/rfcs/pull/2116 instead.
|
|
@ -1,160 +0,0 @@
|
|||
// FORK NOTE: Copied from liballoc_system, removed unnecessary APIs,
|
||||
// APIs take size/align directly instead of Layout
|
||||
|
||||
// The minimum alignment guaranteed by the architecture. This value is used to
|
||||
// add fast paths for low alignment values. In practice, the alignment is a
|
||||
// constant at the call site and the branch will be optimized out.
|
||||
#[cfg(all(any(
|
||||
target_arch = "x86",
|
||||
target_arch = "arm",
|
||||
target_arch = "mips",
|
||||
target_arch = "powerpc",
|
||||
target_arch = "powerpc64",
|
||||
target_arch = "asmjs",
|
||||
target_arch = "wasm32"
|
||||
)))]
|
||||
const MIN_ALIGN: usize = 8;
|
||||
#[cfg(all(any(
|
||||
target_arch = "x86_64",
|
||||
target_arch = "aarch64",
|
||||
target_arch = "mips64",
|
||||
target_arch = "s390x",
|
||||
target_arch = "sparc64"
|
||||
)))]
|
||||
const MIN_ALIGN: usize = 16;
|
||||
|
||||
pub use self::platform::{alloc, dealloc, realloc};
|
||||
|
||||
#[cfg(any(unix, target_os = "redox"))]
|
||||
mod platform {
|
||||
use libc;
|
||||
|
||||
#[cfg(not(any(target_os = "android")))]
|
||||
use std::ptr;
|
||||
|
||||
use super::MIN_ALIGN;
|
||||
|
||||
#[inline]
|
||||
pub unsafe fn alloc(size: usize, align: usize) -> *mut u8 {
|
||||
if align <= MIN_ALIGN {
|
||||
libc::malloc(size) as *mut u8
|
||||
} else {
|
||||
aligned_malloc(size, align)
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub unsafe fn dealloc(ptr: *mut u8, _align: usize) {
|
||||
libc::free(ptr as *mut libc::c_void)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub unsafe fn realloc(ptr: *mut u8, new_size: usize) -> *mut u8 {
|
||||
libc::realloc(ptr as *mut libc::c_void, new_size) as *mut u8
|
||||
}
|
||||
|
||||
#[cfg(any(target_os = "android", target_os = "redox"))]
|
||||
#[inline]
|
||||
unsafe fn aligned_malloc(size: usize, align: usize) -> *mut u8 {
|
||||
// On android we currently target API level 9 which unfortunately
|
||||
// doesn't have the `posix_memalign` API used below. Instead we use
|
||||
// `memalign`, but this unfortunately has the property on some systems
|
||||
// where the memory returned cannot be deallocated by `free`!
|
||||
//
|
||||
// Upon closer inspection, however, this appears to work just fine with
|
||||
// Android, so for this platform we should be fine to call `memalign`
|
||||
// (which is present in API level 9). Some helpful references could
|
||||
// possibly be chromium using memalign [1], attempts at documenting that
|
||||
// memalign + free is ok [2] [3], or the current source of chromium
|
||||
// which still uses memalign on android [4].
|
||||
//
|
||||
// [1]: https://codereview.chromium.org/10796020/
|
||||
// [2]: https://code.google.com/p/android/issues/detail?id=35391
|
||||
// [3]: https://bugs.chromium.org/p/chromium/issues/detail?id=138579
|
||||
// [4]: https://chromium.googlesource.com/chromium/src/base/+/master/
|
||||
// /memory/aligned_memory.cc
|
||||
libc::memalign(align, size) as *mut u8
|
||||
}
|
||||
|
||||
#[cfg(not(any(target_os = "android", target_os = "redox")))]
|
||||
#[inline]
|
||||
unsafe fn aligned_malloc(size: usize, align: usize) -> *mut u8 {
|
||||
let mut out = ptr::null_mut();
|
||||
let ret = libc::posix_memalign(&mut out, align, size);
|
||||
if ret != 0 {
|
||||
ptr::null_mut()
|
||||
} else {
|
||||
out as *mut u8
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
#[allow(bad_style)]
|
||||
mod platform {
|
||||
|
||||
use super::MIN_ALIGN;
|
||||
type LPVOID = *mut u8;
|
||||
type HANDLE = LPVOID;
|
||||
type SIZE_T = usize;
|
||||
type DWORD = u32;
|
||||
type BOOL = i32;
|
||||
|
||||
extern "system" {
|
||||
fn GetProcessHeap() -> HANDLE;
|
||||
fn HeapAlloc(hHeap: HANDLE, dwFlags: DWORD, dwBytes: SIZE_T) -> LPVOID;
|
||||
fn HeapReAlloc(hHeap: HANDLE, dwFlags: DWORD, lpMem: LPVOID, dwBytes: SIZE_T) -> LPVOID;
|
||||
fn HeapFree(hHeap: HANDLE, dwFlags: DWORD, lpMem: LPVOID) -> BOOL;
|
||||
fn GetLastError() -> DWORD;
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
struct Header(*mut u8);
|
||||
|
||||
unsafe fn get_header<'a>(ptr: *mut u8) -> &'a mut Header {
|
||||
&mut *(ptr as *mut Header).offset(-1)
|
||||
}
|
||||
|
||||
unsafe fn align_ptr(ptr: *mut u8, align: usize) -> *mut u8 {
|
||||
let aligned = ptr.offset((align - (ptr as usize & (align - 1))) as isize);
|
||||
*get_header(aligned) = Header(ptr);
|
||||
aligned
|
||||
}
|
||||
|
||||
#[inline]
|
||||
unsafe fn allocate_with_flags(size: usize, align: usize, flags: DWORD) -> *mut u8 {
|
||||
if align <= MIN_ALIGN {
|
||||
HeapAlloc(GetProcessHeap(), flags, size)
|
||||
} else {
|
||||
let size = size + align;
|
||||
let ptr = HeapAlloc(GetProcessHeap(), flags, size);
|
||||
if ptr.is_null() {
|
||||
ptr
|
||||
} else {
|
||||
align_ptr(ptr, align)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub unsafe fn alloc(size: usize, align: usize) -> *mut u8 {
|
||||
allocate_with_flags(size, align, 0)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub unsafe fn dealloc(ptr: *mut u8, align: usize) {
|
||||
if align <= MIN_ALIGN {
|
||||
let err = HeapFree(GetProcessHeap(), 0, ptr as LPVOID);
|
||||
debug_assert!(err != 0, "Failed to free heap memory: {}", GetLastError());
|
||||
} else {
|
||||
let header = get_header(ptr);
|
||||
let err = HeapFree(GetProcessHeap(), 0, header.0 as LPVOID);
|
||||
debug_assert!(err != 0, "Failed to free heap memory: {}", GetLastError());
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub unsafe fn realloc(ptr: *mut u8, new_size: usize) -> *mut u8 {
|
||||
HeapReAlloc(GetProcessHeap(), 0, ptr as LPVOID, new_size) as *mut u8
|
||||
}
|
||||
}
|
|
@ -1,269 +0,0 @@
|
|||
// Copyright 2014-2015 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
//! This module contains shims around the stdlib HashMap
|
||||
//! that add fallible methods
|
||||
//!
|
||||
//! These methods are a lie. They are not actually fallible. This is just to make
|
||||
//! it smooth to switch between hashmap impls in a codebase.
|
||||
|
||||
use std::collections::HashMap as StdMap;
|
||||
use std::collections::HashSet as StdSet;
|
||||
use std::fmt;
|
||||
use std::hash::{BuildHasher, Hash};
|
||||
use std::ops::{Deref, DerefMut};
|
||||
|
||||
pub use std::collections::hash_map::{Entry, Iter as MapIter, IterMut as MapIterMut, RandomState};
|
||||
pub use std::collections::hash_set::{IntoIter as SetIntoIter, Iter as SetIter};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct HashMap<K, V, S = RandomState>(StdMap<K, V, S>);
|
||||
|
||||
use crate::FailedAllocationError;
|
||||
|
||||
impl<K, V, S> Deref for HashMap<K, V, S> {
|
||||
type Target = StdMap<K, V, S>;
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl<K, V, S> DerefMut for HashMap<K, V, S> {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl<K, V, S> HashMap<K, V, S>
|
||||
where
|
||||
K: Eq + Hash,
|
||||
S: BuildHasher,
|
||||
{
|
||||
#[inline]
|
||||
pub fn try_with_hasher(hash_builder: S) -> Result<HashMap<K, V, S>, FailedAllocationError> {
|
||||
Ok(HashMap(StdMap::with_hasher(hash_builder)))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn try_with_capacity_and_hasher(
|
||||
capacity: usize,
|
||||
hash_builder: S,
|
||||
) -> Result<HashMap<K, V, S>, FailedAllocationError> {
|
||||
Ok(HashMap(StdMap::with_capacity_and_hasher(
|
||||
capacity,
|
||||
hash_builder,
|
||||
)))
|
||||
}
|
||||
|
||||
pub fn with_capacity_and_hasher(capacity: usize, hash_builder: S) -> HashMap<K, V, S> {
|
||||
HashMap(StdMap::with_capacity_and_hasher(capacity, hash_builder))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn try_reserve(&mut self, additional: usize) -> Result<(), FailedAllocationError> {
|
||||
Ok(self.reserve(additional))
|
||||
}
|
||||
|
||||
pub fn try_shrink_to_fit(&mut self) -> Result<(), FailedAllocationError> {
|
||||
Ok(self.shrink_to_fit())
|
||||
}
|
||||
|
||||
pub fn try_entry(&mut self, key: K) -> Result<Entry<'_, K, V>, FailedAllocationError> {
|
||||
Ok(self.entry(key))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn try_insert(&mut self, k: K, v: V) -> Result<Option<V>, FailedAllocationError> {
|
||||
Ok(self.insert(k, v))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct HashSet<T, S = RandomState>(StdSet<T, S>);
|
||||
|
||||
impl<T, S> Deref for HashSet<T, S> {
|
||||
type Target = StdSet<T, S>;
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, S> DerefMut for HashSet<T, S> {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Hash + Eq> HashSet<T, RandomState> {
|
||||
#[inline]
|
||||
pub fn new() -> HashSet<T, RandomState> {
|
||||
HashSet(StdSet::new())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn with_capacity(capacity: usize) -> HashSet<T, RandomState> {
|
||||
HashSet(StdSet::with_capacity(capacity))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, S> HashSet<T, S>
|
||||
where
|
||||
T: Eq + Hash,
|
||||
S: BuildHasher,
|
||||
{
|
||||
#[inline]
|
||||
pub fn with_hasher(hasher: S) -> HashSet<T, S> {
|
||||
HashSet(StdSet::with_hasher(hasher))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn with_capacity_and_hasher(capacity: usize, hasher: S) -> HashSet<T, S> {
|
||||
HashSet(StdSet::with_capacity_and_hasher(capacity, hasher))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn try_reserve(&mut self, additional: usize) -> Result<(), FailedAllocationError> {
|
||||
Ok(self.reserve(additional))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn try_shrink_to_fit(&mut self) -> Result<(), FailedAllocationError> {
|
||||
Ok(self.shrink_to_fit())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn try_insert(&mut self, value: T) -> Result<bool, FailedAllocationError> {
|
||||
Ok(self.insert(value))
|
||||
}
|
||||
}
|
||||
|
||||
// Pass through trait impls
|
||||
// We can't derive these since the bounds are not obvious to the derive macro
|
||||
|
||||
impl<K: Hash + Eq, V, S: BuildHasher + Default> Default for HashMap<K, V, S> {
|
||||
fn default() -> Self {
|
||||
HashMap(Default::default())
|
||||
}
|
||||
}
|
||||
|
||||
impl<K, V, S> fmt::Debug for HashMap<K, V, S>
|
||||
where
|
||||
K: Eq + Hash + fmt::Debug,
|
||||
V: fmt::Debug,
|
||||
S: BuildHasher,
|
||||
{
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
self.0.fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
impl<K, V, S> PartialEq for HashMap<K, V, S>
|
||||
where
|
||||
K: Eq + Hash,
|
||||
V: PartialEq,
|
||||
S: BuildHasher,
|
||||
{
|
||||
fn eq(&self, other: &HashMap<K, V, S>) -> bool {
|
||||
self.0.eq(&other.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl<K, V, S> Eq for HashMap<K, V, S>
|
||||
where
|
||||
K: Eq + Hash,
|
||||
V: Eq,
|
||||
S: BuildHasher,
|
||||
{
|
||||
}
|
||||
|
||||
impl<'a, K, V, S> IntoIterator for &'a HashMap<K, V, S>
|
||||
where
|
||||
K: Eq + Hash,
|
||||
S: BuildHasher,
|
||||
{
|
||||
type Item = (&'a K, &'a V);
|
||||
type IntoIter = MapIter<'a, K, V>;
|
||||
|
||||
fn into_iter(self) -> MapIter<'a, K, V> {
|
||||
self.0.iter()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, K, V, S> IntoIterator for &'a mut HashMap<K, V, S>
|
||||
where
|
||||
K: Eq + Hash,
|
||||
S: BuildHasher,
|
||||
{
|
||||
type Item = (&'a K, &'a mut V);
|
||||
type IntoIter = MapIterMut<'a, K, V>;
|
||||
|
||||
fn into_iter(self) -> MapIterMut<'a, K, V> {
|
||||
self.0.iter_mut()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Eq + Hash, S: BuildHasher + Default> Default for HashSet<T, S> {
|
||||
fn default() -> Self {
|
||||
HashSet(Default::default())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, S> fmt::Debug for HashSet<T, S>
|
||||
where
|
||||
T: Eq + Hash + fmt::Debug,
|
||||
S: BuildHasher,
|
||||
{
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
self.0.fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, S> PartialEq for HashSet<T, S>
|
||||
where
|
||||
T: Eq + Hash,
|
||||
S: BuildHasher,
|
||||
{
|
||||
fn eq(&self, other: &HashSet<T, S>) -> bool {
|
||||
self.0.eq(&other.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, S> Eq for HashSet<T, S>
|
||||
where
|
||||
T: Eq + Hash,
|
||||
S: BuildHasher,
|
||||
{
|
||||
}
|
||||
|
||||
impl<'a, T, S> IntoIterator for &'a HashSet<T, S>
|
||||
where
|
||||
T: Eq + Hash,
|
||||
S: BuildHasher,
|
||||
{
|
||||
type Item = &'a T;
|
||||
type IntoIter = SetIter<'a, T>;
|
||||
|
||||
fn into_iter(self) -> SetIter<'a, T> {
|
||||
self.0.iter()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, S> IntoIterator for HashSet<T, S>
|
||||
where
|
||||
T: Eq + Hash,
|
||||
S: BuildHasher,
|
||||
{
|
||||
type Item = T;
|
||||
type IntoIter = SetIntoIter<T>;
|
||||
|
||||
fn into_iter(self) -> SetIntoIter<T> {
|
||||
self.0.into_iter()
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
@ -1,71 +0,0 @@
|
|||
// Copyright 2014-2015 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
pub mod alloc;
|
||||
pub mod hash_map;
|
||||
pub mod hash_set;
|
||||
mod shim;
|
||||
mod table;
|
||||
|
||||
pub mod fake;
|
||||
|
||||
use std::{error, fmt};
|
||||
|
||||
trait Recover<Q: ?Sized> {
|
||||
type Key;
|
||||
|
||||
fn get(&self, key: &Q) -> Option<&Self::Key>;
|
||||
fn take(&mut self, key: &Q) -> Option<Self::Key>;
|
||||
fn replace(&mut self, key: Self::Key) -> Option<Self::Key>;
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct AllocationInfo {
|
||||
/// The size we are requesting.
|
||||
size: usize,
|
||||
/// The alignment we are requesting.
|
||||
alignment: usize,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct FailedAllocationError {
|
||||
reason: &'static str,
|
||||
/// The allocation info we are requesting, if needed.
|
||||
allocation_info: Option<AllocationInfo>,
|
||||
}
|
||||
|
||||
impl FailedAllocationError {
|
||||
#[inline]
|
||||
pub fn new(reason: &'static str) -> Self {
|
||||
Self {
|
||||
reason,
|
||||
allocation_info: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl error::Error for FailedAllocationError {
|
||||
fn description(&self) -> &str {
|
||||
self.reason
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for FailedAllocationError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self.allocation_info {
|
||||
Some(ref info) => write!(
|
||||
f,
|
||||
"{}, allocation: (size: {}, alignment: {})",
|
||||
self.reason, info.size, info.alignment
|
||||
),
|
||||
None => self.reason.fmt(f),
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,62 +0,0 @@
|
|||
use std::marker::PhantomData;
|
||||
|
||||
// FIXME: remove this and use std::ptr::NonNull when Firefox requires Rust 1.25+
|
||||
pub struct NonZeroPtr<T: 'static>(&'static T);
|
||||
|
||||
impl<T: 'static> NonZeroPtr<T> {
|
||||
pub unsafe fn new_unchecked(ptr: *mut T) -> Self {
|
||||
NonZeroPtr(&*ptr)
|
||||
}
|
||||
pub fn as_ptr(&self) -> *mut T {
|
||||
self.0 as *const T as *mut T
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Unique<T: 'static> {
|
||||
ptr: NonZeroPtr<T>,
|
||||
_marker: PhantomData<T>,
|
||||
}
|
||||
|
||||
impl<T: 'static> Unique<T> {
|
||||
pub unsafe fn new_unchecked(ptr: *mut T) -> Self {
|
||||
Unique {
|
||||
ptr: NonZeroPtr::new_unchecked(ptr),
|
||||
_marker: PhantomData,
|
||||
}
|
||||
}
|
||||
pub fn as_ptr(&self) -> *mut T {
|
||||
self.ptr.as_ptr()
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl<T: Send + 'static> Send for Unique<T> {}
|
||||
|
||||
unsafe impl<T: Sync + 'static> Sync for Unique<T> {}
|
||||
|
||||
pub struct Shared<T: 'static> {
|
||||
ptr: NonZeroPtr<T>,
|
||||
_marker: PhantomData<T>,
|
||||
// force it to be !Send/!Sync
|
||||
_marker2: PhantomData<*const u8>,
|
||||
}
|
||||
|
||||
impl<T: 'static> Shared<T> {
|
||||
pub unsafe fn new_unchecked(ptr: *mut T) -> Self {
|
||||
Shared {
|
||||
ptr: NonZeroPtr::new_unchecked(ptr),
|
||||
_marker: PhantomData,
|
||||
_marker2: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::mut_from_ref)]
|
||||
pub unsafe fn as_mut(&self) -> &mut T {
|
||||
&mut *self.ptr.as_ptr()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T> From<&'a mut T> for Shared<T> {
|
||||
fn from(reference: &'a mut T) -> Self {
|
||||
unsafe { Shared::new_unchecked(reference) }
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load diff
|
@ -34,7 +34,6 @@ content-security-policy = { workspace = true, optional = true }
|
|||
crossbeam-channel = { workspace = true, optional = true }
|
||||
cssparser = { workspace = true }
|
||||
euclid = { workspace = true }
|
||||
hashglobe = { path = "../hashglobe" }
|
||||
http = { workspace = true, optional = true }
|
||||
hyper_serde = { workspace = true, optional = true }
|
||||
keyboard-types = { workspace = true, optional = true }
|
||||
|
@ -45,7 +44,6 @@ servo_arc = { path = "../servo_arc" }
|
|||
smallbitvec = { workspace = true }
|
||||
smallvec = { workspace = true }
|
||||
string_cache = { workspace = true, optional = true }
|
||||
thin-slice = { workspace = true }
|
||||
time = { workspace = true, optional = true }
|
||||
tokio = { workspace = true }
|
||||
url = { workspace = true, optional = true }
|
||||
|
|
|
@ -213,24 +213,6 @@ impl<T: MallocSizeOf + ?Sized> MallocSizeOf for Box<T> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<T> MallocShallowSizeOf for thin_slice::ThinBoxedSlice<T> {
|
||||
fn shallow_size_of(&self, ops: &mut MallocSizeOfOps) -> usize {
|
||||
let mut n = 0;
|
||||
unsafe {
|
||||
n += thin_slice::ThinBoxedSlice::spilled_storage(self)
|
||||
.map_or(0, |ptr| ops.malloc_size_of(ptr));
|
||||
n += ops.malloc_size_of(&**self);
|
||||
}
|
||||
n
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: MallocSizeOf> MallocSizeOf for thin_slice::ThinBoxedSlice<T> {
|
||||
fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize {
|
||||
self.shallow_size_of(ops) + (**self).size_of(ops)
|
||||
}
|
||||
}
|
||||
|
||||
impl MallocSizeOf for () {
|
||||
fn size_of(&self, _ops: &mut MallocSizeOfOps) -> usize {
|
||||
0
|
||||
|
@ -448,8 +430,6 @@ macro_rules! malloc_size_of_hash_set {
|
|||
}
|
||||
|
||||
malloc_size_of_hash_set!(std::collections::HashSet<T, S>);
|
||||
malloc_size_of_hash_set!(hashglobe::hash_set::HashSet<T, S>);
|
||||
malloc_size_of_hash_set!(hashglobe::fake::HashSet<T, S>);
|
||||
|
||||
macro_rules! malloc_size_of_hash_map {
|
||||
($ty:ty) => {
|
||||
|
@ -489,8 +469,6 @@ macro_rules! malloc_size_of_hash_map {
|
|||
}
|
||||
|
||||
malloc_size_of_hash_map!(std::collections::HashMap<K, V, S>);
|
||||
malloc_size_of_hash_map!(hashglobe::hash_map::HashMap<K, V, S>);
|
||||
malloc_size_of_hash_map!(hashglobe::fake::HashMap<K, V, S>);
|
||||
|
||||
impl<K, V> MallocShallowSizeOf for std::collections::BTreeMap<K, V>
|
||||
where
|
||||
|
|
|
@ -105,7 +105,8 @@ impl CSSRule {
|
|||
},
|
||||
StyleCssRule::Page(_) => unreachable!(),
|
||||
StyleCssRule::Document(_) => unimplemented!(), // TODO
|
||||
StyleCssRule::Layer(_) => unimplemented!(), // TODO
|
||||
StyleCssRule::LayerBlock(_) => unimplemented!(), // TODO
|
||||
StyleCssRule::LayerStatement(_) => unimplemented!(), // TODO
|
||||
StyleCssRule::ScrollTimeline(_) => unimplemented!(), // TODO
|
||||
}
|
||||
}
|
||||
|
|
|
@ -96,19 +96,15 @@ impl CSSStyleRuleMethods for CSSStyleRule {
|
|||
|
||||
// https://drafts.csswg.org/cssom/#dom-cssstylerule-selectortext
|
||||
fn SetSelectorText(&self, value: DOMString) {
|
||||
let contents = &self.cssrule.parent_stylesheet().style_stylesheet().contents;
|
||||
// It's not clear from the spec if we should use the stylesheet's namespaces.
|
||||
// https://github.com/w3c/csswg-drafts/issues/1511
|
||||
let namespaces = self
|
||||
.cssrule
|
||||
.parent_stylesheet()
|
||||
.style_stylesheet()
|
||||
.contents
|
||||
.namespaces
|
||||
.read();
|
||||
let namespaces = contents.namespaces.read();
|
||||
let url_data = contents.url_data.read();
|
||||
let parser = SelectorParser {
|
||||
stylesheet_origin: Origin::Author,
|
||||
namespaces: &namespaces,
|
||||
url_data: None,
|
||||
url_data: &url_data,
|
||||
};
|
||||
let mut css_parser = CssParserInput::new(&*value);
|
||||
let mut css_parser = CssParser::new(&mut css_parser);
|
||||
|
|
|
@ -130,6 +130,7 @@ use style::selector_parser::{
|
|||
NonTSPseudoClass, PseudoElement, RestyleDamage, SelectorImpl, SelectorParser,
|
||||
};
|
||||
use style::shared_lock::{Locked, SharedRwLock};
|
||||
use style::stylesheets::layer_rule::LayerOrder;
|
||||
use style::stylesheets::CssRuleType;
|
||||
use style::thread_state;
|
||||
use style::values::generics::NonNegative;
|
||||
|
@ -665,6 +666,7 @@ impl<'dom> LayoutElementHelpers<'dom> for LayoutDom<'dom, Element> {
|
|||
Importance::Normal,
|
||||
))),
|
||||
CascadeLevel::PresHints,
|
||||
LayerOrder::root(),
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -2700,12 +2702,14 @@ impl ElementMethods for Element {
|
|||
|
||||
// https://dom.spec.whatwg.org/#dom-element-matches
|
||||
fn Matches(&self, selectors: DOMString) -> Fallible<bool> {
|
||||
let selectors = match SelectorParser::parse_author_origin_no_namespace(&selectors) {
|
||||
let doc = document_from_node(self);
|
||||
let url = doc.url();
|
||||
let selectors = match SelectorParser::parse_author_origin_no_namespace(&selectors, &url) {
|
||||
Err(_) => return Err(Error::Syntax),
|
||||
Ok(selectors) => selectors,
|
||||
};
|
||||
|
||||
let quirks_mode = document_from_node(self).quirks_mode();
|
||||
let quirks_mode = doc.quirks_mode();
|
||||
let element = DomRoot::from_ref(self);
|
||||
|
||||
Ok(dom_apis::element_matches(&element, &selectors, quirks_mode))
|
||||
|
@ -2718,12 +2722,14 @@ impl ElementMethods for Element {
|
|||
|
||||
// https://dom.spec.whatwg.org/#dom-element-closest
|
||||
fn Closest(&self, selectors: DOMString) -> Fallible<Option<DomRoot<Element>>> {
|
||||
let selectors = match SelectorParser::parse_author_origin_no_namespace(&selectors) {
|
||||
let doc = document_from_node(self);
|
||||
let url = doc.url();
|
||||
let selectors = match SelectorParser::parse_author_origin_no_namespace(&selectors, &url) {
|
||||
Err(_) => return Err(Error::Syntax),
|
||||
Ok(selectors) => selectors,
|
||||
};
|
||||
|
||||
let quirks_mode = document_from_node(self).quirks_mode();
|
||||
let quirks_mode = doc.quirks_mode();
|
||||
Ok(dom_apis::element_closest(
|
||||
DomRoot::from_ref(self),
|
||||
&selectors,
|
||||
|
|
|
@ -949,18 +949,15 @@ impl Node {
|
|||
// https://dom.spec.whatwg.org/#dom-parentnode-queryselector
|
||||
pub fn query_selector(&self, selectors: DOMString) -> Fallible<Option<DomRoot<Element>>> {
|
||||
// Step 1.
|
||||
match SelectorParser::parse_author_origin_no_namespace(&selectors) {
|
||||
let doc = self.owner_doc();
|
||||
match SelectorParser::parse_author_origin_no_namespace(&selectors, &doc.url()) {
|
||||
// Step 2.
|
||||
Err(_) => Err(Error::Syntax),
|
||||
// Step 3.
|
||||
Ok(selectors) => {
|
||||
// FIXME(bholley): Consider an nth-index cache here.
|
||||
let mut ctx = MatchingContext::new(
|
||||
MatchingMode::Normal,
|
||||
None,
|
||||
None,
|
||||
self.owner_doc().quirks_mode(),
|
||||
);
|
||||
let mut ctx =
|
||||
MatchingContext::new(MatchingMode::Normal, None, None, doc.quirks_mode());
|
||||
Ok(self
|
||||
.traverse_preorder(ShadowIncluding::No)
|
||||
.filter_map(DomRoot::downcast)
|
||||
|
@ -975,7 +972,8 @@ impl Node {
|
|||
/// whilst iterating, otherwise the iterator may be invalidated.
|
||||
pub fn query_selector_iter(&self, selectors: DOMString) -> Fallible<QuerySelectorIterator> {
|
||||
// Step 1.
|
||||
match SelectorParser::parse_author_origin_no_namespace(&selectors) {
|
||||
let url = self.owner_doc().url();
|
||||
match SelectorParser::parse_author_origin_no_namespace(&selectors, &url) {
|
||||
// Step 2.
|
||||
Err(_) => Err(Error::Syntax),
|
||||
// Step 3.
|
||||
|
|
|
@ -17,8 +17,7 @@ path = "lib.rs"
|
|||
doctest = false
|
||||
|
||||
[features]
|
||||
gecko = ["style_traits/gecko", "fallible/known_system_malloc", "bindgen", "regex", "toml",
|
||||
"num_cpus", "thin-slice"]
|
||||
gecko = ["style_traits/gecko", "bindgen", "regex", "toml", "num_cpus", "mozbuild"]
|
||||
servo = ["serde", "style_traits/servo", "servo_atoms", "servo_config", "html5ever",
|
||||
"cssparser/serde", "encoding_rs", "malloc_size_of/servo", "servo_url",
|
||||
"string_cache", "to_shmem/servo",
|
||||
|
@ -38,9 +37,7 @@ cssparser = "0.29"
|
|||
derive_more = "0.99"
|
||||
encoding_rs = { version = "0.8", optional = true }
|
||||
euclid = "0.22"
|
||||
fallible = { path = "../fallible" }
|
||||
fxhash = "0.2"
|
||||
hashglobe = { path = "../hashglobe" }
|
||||
html5ever = { version = "0.26", optional = true }
|
||||
indexmap = "1.0"
|
||||
itertools = "0.8"
|
||||
|
@ -70,7 +67,6 @@ smallvec = "1.0"
|
|||
string_cache = { version = "0.8", optional = true }
|
||||
style_derive = { path = "../style_derive" }
|
||||
style_traits = { path = "../style_traits" }
|
||||
thin-slice = { version = "0.1.0", optional = true }
|
||||
time = "0.1"
|
||||
to_shmem = { path = "../to_shmem" }
|
||||
to_shmem_derive = { path = "../to_shmem_derive" }
|
||||
|
@ -83,6 +79,7 @@ void = "1.0.2"
|
|||
bindgen = { version = "0.62", optional = true, default-features = false }
|
||||
lazy_static = "1"
|
||||
log = "0.4"
|
||||
mozbuild = {version = "0.1", optional = true}
|
||||
regex = { version = "1.1", optional = true }
|
||||
toml = { version = "0.5", optional = true, default-features = false }
|
||||
walkdir = "2.1.4"
|
||||
|
|
|
@ -24,6 +24,7 @@ use crate::selector_parser::PseudoElement;
|
|||
use crate::shared_lock::{Locked, SharedRwLock};
|
||||
use crate::style_resolver::StyleResolverForElement;
|
||||
use crate::stylesheets::keyframes_rule::{KeyframesAnimation, KeyframesStep, KeyframesStepValue};
|
||||
use crate::stylesheets::layer_rule::LayerOrder;
|
||||
use crate::values::animated::{Animate, Procedure};
|
||||
use crate::values::computed::{Time, TimingFunction};
|
||||
use crate::values::generics::box_::AnimationIterationCount;
|
||||
|
@ -290,6 +291,7 @@ impl IntermediateComputedKeyframe {
|
|||
let rule_node = base_style.rules().clone();
|
||||
let new_node = context.stylist.rule_tree().update_rule_at_level(
|
||||
CascadeLevel::Animations,
|
||||
LayerOrder::root(),
|
||||
Some(locked_block.borrow_arc()),
|
||||
&rule_node,
|
||||
&context.guards,
|
||||
|
|
|
@ -6,8 +6,8 @@
|
|||
|
||||
use crate::properties::PropertyDeclarationBlock;
|
||||
use crate::rule_tree::{CascadeLevel, StyleSource};
|
||||
use crate::stylesheets::layer_rule::LayerOrder;
|
||||
use crate::shared_lock::Locked;
|
||||
use crate::stylesheets::layer_rule::LayerOrder;
|
||||
use servo_arc::Arc;
|
||||
use smallvec::SmallVec;
|
||||
|
||||
|
@ -24,36 +24,109 @@ pub type ApplicableDeclarationList = SmallVec<[ApplicableDeclarationBlock; 16]>;
|
|||
/// That's a limit that could be reached in realistic webpages, so we use
|
||||
/// 24 bits and enforce defined behavior in the overflow case.
|
||||
///
|
||||
/// Note that right now this restriction could be lifted if wanted (because we
|
||||
/// no longer stash the cascade level in the remaining bits), but we keep it in
|
||||
/// place in case we come up with a use-case for them, lacking reports of the
|
||||
/// current limit being too small.
|
||||
///
|
||||
/// [1] https://cs.chromium.org/chromium/src/third_party/WebKit/Source/core/css/
|
||||
/// RuleSet.h?l=128&rcl=90140ab80b84d0f889abc253410f44ed54ae04f3
|
||||
const SOURCE_ORDER_SHIFT: usize = 0;
|
||||
const SOURCE_ORDER_BITS: usize = 24;
|
||||
const SOURCE_ORDER_MAX: u32 = (1 << SOURCE_ORDER_BITS) - 1;
|
||||
const SOURCE_ORDER_MASK: u32 = SOURCE_ORDER_MAX << SOURCE_ORDER_SHIFT;
|
||||
const SOURCE_ORDER_MASK: u32 = SOURCE_ORDER_MAX;
|
||||
|
||||
/// We pack the cascade level in a single byte, see CascadeLevel::to_byte_lossy
|
||||
/// for the different trade-offs there.
|
||||
const CASCADE_LEVEL_SHIFT: usize = SOURCE_ORDER_BITS;
|
||||
/// The cascade-level+layer order of this declaration.
|
||||
#[derive(Clone, Copy, Debug, Eq, Hash, MallocSizeOf, PartialEq)]
|
||||
pub struct CascadePriority {
|
||||
cascade_level: CascadeLevel,
|
||||
layer_order: LayerOrder,
|
||||
}
|
||||
|
||||
/// Stores the source order of a block, the cascade level it belongs to, and the
|
||||
/// counter needed to handle Shadow DOM cascade order properly.
|
||||
#[derive(Clone, Copy, Debug, Eq, MallocSizeOf, PartialEq)]
|
||||
struct ApplicableDeclarationBits(u32);
|
||||
#[allow(dead_code)]
|
||||
fn size_assert() {
|
||||
#[allow(unsafe_code)]
|
||||
unsafe {
|
||||
std::mem::transmute::<u32, CascadePriority>(0u32)
|
||||
};
|
||||
}
|
||||
|
||||
impl ApplicableDeclarationBits {
|
||||
fn new(source_order: u32, cascade_level: CascadeLevel) -> Self {
|
||||
Self(
|
||||
(source_order & SOURCE_ORDER_MASK) |
|
||||
((cascade_level.to_byte_lossy() as u32) << CASCADE_LEVEL_SHIFT),
|
||||
)
|
||||
impl PartialOrd for CascadePriority {
|
||||
#[inline]
|
||||
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
|
||||
Some(self.cmp(other))
|
||||
}
|
||||
}
|
||||
|
||||
impl Ord for CascadePriority {
|
||||
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
|
||||
self.cascade_level.cmp(&other.cascade_level).then_with(|| {
|
||||
let ordering = self.layer_order.cmp(&other.layer_order);
|
||||
if ordering == std::cmp::Ordering::Equal {
|
||||
return ordering;
|
||||
}
|
||||
// https://drafts.csswg.org/css-cascade-5/#cascade-layering
|
||||
//
|
||||
// Cascade layers (like declarations) are ordered by order
|
||||
// of appearance. When comparing declarations that belong to
|
||||
// different layers, then for normal rules the declaration
|
||||
// whose cascade layer is last wins, and for important rules
|
||||
// the declaration whose cascade layer is first wins.
|
||||
//
|
||||
// But the style attribute layer for some reason is special.
|
||||
if self.cascade_level.is_important() &&
|
||||
!self.layer_order.is_style_attribute_layer() &&
|
||||
!other.layer_order.is_style_attribute_layer()
|
||||
{
|
||||
ordering.reverse()
|
||||
} else {
|
||||
ordering
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl CascadePriority {
|
||||
/// Construct a new CascadePriority for a given (level, order) pair.
|
||||
pub fn new(cascade_level: CascadeLevel, layer_order: LayerOrder) -> Self {
|
||||
Self {
|
||||
cascade_level,
|
||||
layer_order,
|
||||
}
|
||||
}
|
||||
|
||||
fn source_order(&self) -> u32 {
|
||||
self.0 & SOURCE_ORDER_MASK
|
||||
/// Returns the layer order.
|
||||
#[inline]
|
||||
pub fn layer_order(&self) -> LayerOrder {
|
||||
self.layer_order
|
||||
}
|
||||
|
||||
fn level(&self) -> CascadeLevel {
|
||||
CascadeLevel::from_byte((self.0 >> CASCADE_LEVEL_SHIFT) as u8)
|
||||
/// Returns the cascade level.
|
||||
#[inline]
|
||||
pub fn cascade_level(&self) -> CascadeLevel {
|
||||
self.cascade_level
|
||||
}
|
||||
|
||||
/// Whether this declaration should be allowed if `revert` or `revert-layer`
|
||||
/// have been specified on a given origin.
|
||||
///
|
||||
/// `self` is the priority at which the `revert` or `revert-layer` keyword
|
||||
/// have been specified.
|
||||
pub fn allows_when_reverted(&self, other: &Self, origin_revert: bool) -> bool {
|
||||
if origin_revert {
|
||||
other.cascade_level.origin() < self.cascade_level.origin()
|
||||
} else {
|
||||
other.unimportant() < self.unimportant()
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert this priority from "important" to "non-important", if needed.
|
||||
pub fn unimportant(&self) -> Self {
|
||||
Self::new(self.cascade_level().unimportant(), self.layer_order())
|
||||
}
|
||||
|
||||
/// Convert this priority from "non-important" to "important", if needed.
|
||||
pub fn important(&self) -> Self {
|
||||
Self::new(self.cascade_level().important(), self.layer_order())
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -69,11 +142,11 @@ pub struct ApplicableDeclarationBlock {
|
|||
pub source: StyleSource,
|
||||
/// The bits containing the source order, cascade level, and shadow cascade
|
||||
/// order.
|
||||
bits: ApplicableDeclarationBits,
|
||||
source_order: u32,
|
||||
/// The specificity of the selector.
|
||||
pub specificity: u32,
|
||||
/// The layer order of the selector.
|
||||
pub layer_order: LayerOrder,
|
||||
/// The cascade priority of the rule.
|
||||
pub cascade_priority: CascadePriority,
|
||||
}
|
||||
|
||||
impl ApplicableDeclarationBlock {
|
||||
|
@ -83,12 +156,13 @@ impl ApplicableDeclarationBlock {
|
|||
pub fn from_declarations(
|
||||
declarations: Arc<Locked<PropertyDeclarationBlock>>,
|
||||
level: CascadeLevel,
|
||||
layer_order: LayerOrder,
|
||||
) -> Self {
|
||||
ApplicableDeclarationBlock {
|
||||
source: StyleSource::from_declarations(declarations),
|
||||
bits: ApplicableDeclarationBits::new(0, level),
|
||||
source_order: 0,
|
||||
specificity: 0,
|
||||
layer_order: LayerOrder::root(),
|
||||
cascade_priority: CascadePriority::new(level, layer_order),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -103,29 +177,34 @@ impl ApplicableDeclarationBlock {
|
|||
) -> Self {
|
||||
ApplicableDeclarationBlock {
|
||||
source,
|
||||
bits: ApplicableDeclarationBits::new(source_order, level),
|
||||
source_order: source_order & SOURCE_ORDER_MASK,
|
||||
specificity,
|
||||
layer_order,
|
||||
cascade_priority: CascadePriority::new(level, layer_order),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the source order of the block.
|
||||
#[inline]
|
||||
pub fn source_order(&self) -> u32 {
|
||||
self.bits.source_order()
|
||||
self.source_order
|
||||
}
|
||||
|
||||
/// Returns the cascade level of the block.
|
||||
#[inline]
|
||||
pub fn level(&self) -> CascadeLevel {
|
||||
self.bits.level()
|
||||
self.cascade_priority.cascade_level()
|
||||
}
|
||||
|
||||
/// Returns the cascade level of the block.
|
||||
#[inline]
|
||||
pub fn layer_order(&self) -> LayerOrder {
|
||||
self.cascade_priority.layer_order()
|
||||
}
|
||||
|
||||
/// Convenience method to consume self and return the right thing for the
|
||||
/// rule tree to iterate over.
|
||||
#[inline]
|
||||
pub fn for_rule_tree(self) -> (StyleSource, CascadeLevel) {
|
||||
let level = self.level();
|
||||
(self.source, level)
|
||||
pub fn for_rule_tree(self) -> (StyleSource, CascadePriority) {
|
||||
(self.source, self.cascade_priority)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,10 +10,10 @@ use crate::dom::TElement;
|
|||
use crate::gecko_bindings::sugar::ownership::{HasBoxFFI, HasFFI, HasSimpleFFI};
|
||||
use crate::invalidation::media_queries::ToMediaListKey;
|
||||
use crate::shared_lock::SharedRwLockReadGuard;
|
||||
use crate::stylist::Stylist;
|
||||
use crate::stylesheet_set::AuthorStylesheetSet;
|
||||
use crate::stylesheets::StylesheetInDocument;
|
||||
use crate::stylist::CascadeData;
|
||||
use crate::stylist::Stylist;
|
||||
use servo_arc::Arc;
|
||||
|
||||
/// A set of author stylesheets and their computed representation, such as the
|
||||
|
@ -32,9 +32,7 @@ where
|
|||
}
|
||||
|
||||
lazy_static! {
|
||||
static ref EMPTY_CASCADE_DATA: Arc<CascadeData> = {
|
||||
Arc::new_leaked(CascadeData::new())
|
||||
};
|
||||
static ref EMPTY_CASCADE_DATA: Arc<CascadeData> = Arc::new_leaked(CascadeData::new());
|
||||
}
|
||||
|
||||
impl<S> AuthorStyles<S>
|
||||
|
@ -55,11 +53,8 @@ where
|
|||
/// TODO(emilio): Need a host element and a snapshot map to do invalidation
|
||||
/// properly.
|
||||
#[inline]
|
||||
pub fn flush<E>(
|
||||
&mut self,
|
||||
stylist: &mut Stylist,
|
||||
guard: &SharedRwLockReadGuard,
|
||||
) where
|
||||
pub fn flush<E>(&mut self, stylist: &mut Stylist, guard: &SharedRwLockReadGuard)
|
||||
where
|
||||
E: TElement,
|
||||
S: ToMediaListKey,
|
||||
{
|
||||
|
|
|
@ -42,14 +42,12 @@ fn read_config(path: &PathBuf) -> Table {
|
|||
lazy_static! {
|
||||
static ref CONFIG: Table = {
|
||||
// Load Gecko's binding generator config from the source tree.
|
||||
let path = PathBuf::from(env::var_os("MOZ_SRC").unwrap())
|
||||
.join("layout/style/ServoBindings.toml");
|
||||
let path = mozbuild::TOPSRCDIR.join("layout/style/ServoBindings.toml");
|
||||
read_config(&path)
|
||||
};
|
||||
static ref BINDGEN_FLAGS: Vec<String> = {
|
||||
// Load build-specific config overrides.
|
||||
let path = PathBuf::from(env::var_os("MOZ_TOPOBJDIR").unwrap())
|
||||
.join("layout/style/extra-bindgen-flags");
|
||||
let path = mozbuild::TOPOBJDIR.join("layout/style/extra-bindgen-flags");
|
||||
println!("cargo:rerun-if-changed={}", path.to_str().unwrap());
|
||||
fs::read_to_string(path).expect("Failed to read extra-bindgen-flags file")
|
||||
.split_whitespace()
|
||||
|
@ -57,13 +55,7 @@ lazy_static! {
|
|||
.collect()
|
||||
};
|
||||
static ref INCLUDE_RE: Regex = Regex::new(r#"#include\s*"(.+?)""#).unwrap();
|
||||
static ref DISTDIR_PATH: PathBuf = {
|
||||
let path = PathBuf::from(env::var_os("MOZ_DIST").unwrap());
|
||||
if !path.is_absolute() || !path.is_dir() {
|
||||
panic!("MOZ_DIST must be an absolute directory, was: {}", path.display());
|
||||
}
|
||||
path
|
||||
};
|
||||
static ref DISTDIR_PATH: PathBuf = mozbuild::TOPOBJDIR.join("dist");
|
||||
static ref SEARCH_PATHS: Vec<PathBuf> = vec![
|
||||
DISTDIR_PATH.join("include"),
|
||||
DISTDIR_PATH.join("include/nspr"),
|
||||
|
|
|
@ -55,12 +55,12 @@ pub fn parse_counter_style_name<'i, 't>(
|
|||
}
|
||||
|
||||
fn is_valid_name_definition(ident: &CustomIdent) -> bool {
|
||||
ident.0 != atom!("decimal")
|
||||
&& ident.0 != atom!("disc")
|
||||
&& ident.0 != atom!("circle")
|
||||
&& ident.0 != atom!("square")
|
||||
&& ident.0 != atom!("disclosure-closed")
|
||||
&& ident.0 != atom!("disclosure-open")
|
||||
ident.0 != atom!("decimal") &&
|
||||
ident.0 != atom!("disc") &&
|
||||
ident.0 != atom!("circle") &&
|
||||
ident.0 != atom!("square") &&
|
||||
ident.0 != atom!("disclosure-closed") &&
|
||||
ident.0 != atom!("disclosure-open")
|
||||
}
|
||||
|
||||
/// Parse the prelude of an @counter-style rule
|
||||
|
|
|
@ -6,11 +6,10 @@
|
|||
//!
|
||||
//! [custom]: https://drafts.csswg.org/css-variables/
|
||||
|
||||
use crate::hash::map::Entry;
|
||||
use crate::applicable_declarations::CascadePriority;
|
||||
use crate::media_queries::Device;
|
||||
use crate::properties::{CSSWideKeyword, CustomDeclaration, CustomDeclarationValue};
|
||||
use crate::selector_map::{PrecomputedHashMap, PrecomputedHashSet, PrecomputedHasher};
|
||||
use crate::stylesheets::{Origin, PerOrigin};
|
||||
use crate::Atom;
|
||||
use cssparser::{
|
||||
CowRcStr, Delimiter, Parser, ParserInput, SourcePosition, Token, TokenSerializationType,
|
||||
|
@ -21,6 +20,7 @@ use servo_arc::Arc;
|
|||
use smallvec::SmallVec;
|
||||
use std::borrow::Cow;
|
||||
use std::cmp;
|
||||
use std::collections::hash_map::Entry;
|
||||
use std::fmt::{self, Write};
|
||||
use std::hash::BuildHasherDefault;
|
||||
use style_traits::{CssWriter, ParseError, StyleParseErrorKind, ToCss};
|
||||
|
@ -49,19 +49,19 @@ macro_rules! make_variable {
|
|||
}
|
||||
|
||||
fn get_safearea_inset_top(device: &Device) -> VariableValue {
|
||||
VariableValue::pixel(device.safe_area_insets().top)
|
||||
VariableValue::pixels(device.safe_area_insets().top)
|
||||
}
|
||||
|
||||
fn get_safearea_inset_bottom(device: &Device) -> VariableValue {
|
||||
VariableValue::pixel(device.safe_area_insets().bottom)
|
||||
VariableValue::pixels(device.safe_area_insets().bottom)
|
||||
}
|
||||
|
||||
fn get_safearea_inset_left(device: &Device) -> VariableValue {
|
||||
VariableValue::pixel(device.safe_area_insets().left)
|
||||
VariableValue::pixels(device.safe_area_insets().left)
|
||||
}
|
||||
|
||||
fn get_safearea_inset_right(device: &Device) -> VariableValue {
|
||||
VariableValue::pixel(device.safe_area_insets().right)
|
||||
VariableValue::pixels(device.safe_area_insets().right)
|
||||
}
|
||||
|
||||
static ENVIRONMENT_VARIABLES: [EnvironmentVariable; 4] = [
|
||||
|
@ -71,17 +71,56 @@ static ENVIRONMENT_VARIABLES: [EnvironmentVariable; 4] = [
|
|||
make_variable!(atom!("safe-area-inset-right"), get_safearea_inset_right),
|
||||
];
|
||||
|
||||
fn get_titlebar_radius(device: &Device) -> VariableValue {
|
||||
VariableValue::pixel(device.titlebar_radius())
|
||||
#[cfg(feature = "gecko")]
|
||||
macro_rules! lnf_int {
|
||||
($id:ident) => {
|
||||
unsafe {
|
||||
crate::gecko_bindings::bindings::Gecko_GetLookAndFeelInt(
|
||||
crate::gecko_bindings::bindings::LookAndFeel_IntID::$id as i32,
|
||||
)
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
fn get_menu_radius(device: &Device) -> VariableValue {
|
||||
VariableValue::pixel(device.menu_radius())
|
||||
#[cfg(feature = "servo")]
|
||||
macro_rules! lnf_int {
|
||||
($id:ident) => {
|
||||
// TODO: implement this.
|
||||
0
|
||||
};
|
||||
}
|
||||
|
||||
static CHROME_ENVIRONMENT_VARIABLES: [EnvironmentVariable; 2] = [
|
||||
make_variable!(atom!("-moz-gtk-csd-titlebar-radius"), get_titlebar_radius),
|
||||
make_variable!(atom!("-moz-gtk-menu-radius"), get_menu_radius),
|
||||
macro_rules! lnf_int_variable {
|
||||
($atom:expr, $id:ident, $ctor:ident) => {{
|
||||
fn __eval(_: &Device) -> VariableValue {
|
||||
VariableValue::$ctor(lnf_int!($id))
|
||||
}
|
||||
make_variable!($atom, __eval)
|
||||
}};
|
||||
}
|
||||
|
||||
static CHROME_ENVIRONMENT_VARIABLES: [EnvironmentVariable; 5] = [
|
||||
lnf_int_variable!(
|
||||
atom!("-moz-gtk-csd-titlebar-radius"),
|
||||
TitlebarRadius,
|
||||
int_pixels
|
||||
),
|
||||
lnf_int_variable!(atom!("-moz-gtk-csd-menu-radius"), GtkMenuRadius, int_pixels),
|
||||
lnf_int_variable!(
|
||||
atom!("-moz-gtk-csd-close-button-position"),
|
||||
GTKCSDCloseButtonPosition,
|
||||
integer
|
||||
),
|
||||
lnf_int_variable!(
|
||||
atom!("-moz-gtk-csd-minimize-button-position"),
|
||||
GTKCSDMinimizeButtonPosition,
|
||||
integer
|
||||
),
|
||||
lnf_int_variable!(
|
||||
atom!("-moz-gtk-csd-maximize-button-position"),
|
||||
GTKCSDMaximizeButtonPosition,
|
||||
integer
|
||||
),
|
||||
];
|
||||
|
||||
impl CssEnvironment {
|
||||
|
@ -93,7 +132,9 @@ impl CssEnvironment {
|
|||
if !device.is_chrome_document() {
|
||||
return None;
|
||||
}
|
||||
let var = CHROME_ENVIRONMENT_VARIABLES.iter().find(|var| var.name == *name)?;
|
||||
let var = CHROME_ENVIRONMENT_VARIABLES
|
||||
.iter()
|
||||
.find(|var| var.name == *name)?;
|
||||
Some((var.evaluator)(device))
|
||||
}
|
||||
}
|
||||
|
@ -278,17 +319,39 @@ impl VariableValue {
|
|||
}))
|
||||
}
|
||||
|
||||
/// Create VariableValue from css pixel value
|
||||
pub fn pixel(number: f32) -> Self {
|
||||
/// Create VariableValue from an int.
|
||||
fn integer(number: i32) -> Self {
|
||||
Self::from_token(Token::Number {
|
||||
has_sign: false,
|
||||
value: number as f32,
|
||||
int_value: Some(number),
|
||||
})
|
||||
}
|
||||
|
||||
/// Create VariableValue from a float amount of CSS pixels.
|
||||
fn pixels(number: f32) -> Self {
|
||||
// FIXME (https://github.com/servo/rust-cssparser/issues/266):
|
||||
// No way to get TokenSerializationType::Dimension without creating
|
||||
// Token object.
|
||||
let token = Token::Dimension {
|
||||
Self::from_token(Token::Dimension {
|
||||
has_sign: false,
|
||||
value: number,
|
||||
int_value: None,
|
||||
unit: CowRcStr::from("px"),
|
||||
};
|
||||
})
|
||||
}
|
||||
|
||||
/// Create VariableValue from an integer amount of CSS pixels.
|
||||
fn int_pixels(number: i32) -> Self {
|
||||
Self::from_token(Token::Dimension {
|
||||
has_sign: false,
|
||||
value: number as f32,
|
||||
int_value: Some(number),
|
||||
unit: CowRcStr::from("px"),
|
||||
})
|
||||
}
|
||||
|
||||
fn from_token(token: Token) -> Self {
|
||||
let token_type = token.serialization_type();
|
||||
let mut css = token.to_css_string();
|
||||
css.shrink_to_fit();
|
||||
|
@ -536,10 +599,10 @@ fn parse_env_function<'i, 't>(
|
|||
/// properties.
|
||||
pub struct CustomPropertiesBuilder<'a> {
|
||||
seen: PrecomputedHashSet<&'a Name>,
|
||||
reverted: PerOrigin<PrecomputedHashSet<&'a Name>>,
|
||||
may_have_cycles: bool,
|
||||
custom_properties: Option<CustomPropertiesMap>,
|
||||
inherited: Option<&'a Arc<CustomPropertiesMap>>,
|
||||
reverted: PrecomputedHashMap<&'a Name, (CascadePriority, bool)>,
|
||||
device: &'a Device,
|
||||
}
|
||||
|
||||
|
@ -557,14 +620,16 @@ impl<'a> CustomPropertiesBuilder<'a> {
|
|||
}
|
||||
|
||||
/// Cascade a given custom property declaration.
|
||||
pub fn cascade(&mut self, declaration: &'a CustomDeclaration, origin: Origin) {
|
||||
pub fn cascade(&mut self, declaration: &'a CustomDeclaration, priority: CascadePriority) {
|
||||
let CustomDeclaration {
|
||||
ref name,
|
||||
ref value,
|
||||
} = *declaration;
|
||||
|
||||
if self.reverted.borrow_for_origin(&origin).contains(&name) {
|
||||
return;
|
||||
if let Some(&(reverted_priority, is_origin_revert)) = self.reverted.get(&name) {
|
||||
if !reverted_priority.allows_when_reverted(&priority, is_origin_revert) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
let was_already_present = !self.seen.insert(name);
|
||||
|
@ -597,8 +662,7 @@ impl<'a> CustomPropertiesBuilder<'a> {
|
|||
match result {
|
||||
Ok(new_value) => new_value,
|
||||
Err(..) => {
|
||||
// Don't touch the map, this has the same effect as
|
||||
// making it compute to the inherited one.
|
||||
map.remove(name);
|
||||
return;
|
||||
},
|
||||
}
|
||||
|
@ -608,11 +672,10 @@ impl<'a> CustomPropertiesBuilder<'a> {
|
|||
map.insert(name.clone(), value);
|
||||
},
|
||||
CustomDeclarationValue::CSSWideKeyword(keyword) => match keyword {
|
||||
CSSWideKeyword::Revert => {
|
||||
CSSWideKeyword::RevertLayer | CSSWideKeyword::Revert => {
|
||||
let origin_revert = keyword == CSSWideKeyword::Revert;
|
||||
self.seen.remove(name);
|
||||
for origin in origin.following_including() {
|
||||
self.reverted.borrow_mut_for_origin(&origin).insert(name);
|
||||
}
|
||||
self.reverted.insert(name, (priority, origin_revert));
|
||||
},
|
||||
CSSWideKeyword::Initial => {
|
||||
map.remove(name);
|
||||
|
@ -660,6 +723,22 @@ impl<'a> CustomPropertiesBuilder<'a> {
|
|||
true
|
||||
}
|
||||
|
||||
fn inherited_properties_match(&self, map: &CustomPropertiesMap) -> bool {
|
||||
let inherited = match self.inherited {
|
||||
Some(inherited) => inherited,
|
||||
None => return false,
|
||||
};
|
||||
if inherited.len() != map.len() {
|
||||
return false;
|
||||
}
|
||||
for name in self.seen.iter() {
|
||||
if inherited.get(*name) != map.get(*name) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
true
|
||||
}
|
||||
|
||||
/// Returns the final map of applicable custom properties.
|
||||
///
|
||||
/// If there was any specified property, we've created a new map and now we
|
||||
|
@ -671,10 +750,19 @@ impl<'a> CustomPropertiesBuilder<'a> {
|
|||
Some(m) => m,
|
||||
None => return self.inherited.cloned(),
|
||||
};
|
||||
|
||||
if self.may_have_cycles {
|
||||
let inherited = self.inherited.as_ref().map(|m| &***m);
|
||||
substitute_all(&mut map, inherited, self.device);
|
||||
substitute_all(&mut map, &self.seen, self.device);
|
||||
}
|
||||
|
||||
// Some pages apply a lot of redundant custom properties, see e.g.
|
||||
// bug 1758974 comment 5. Try to detect the case where the values
|
||||
// haven't really changed, and save some memory by reusing the inherited
|
||||
// map in that case.
|
||||
if self.inherited_properties_match(&map) {
|
||||
return self.inherited.cloned();
|
||||
}
|
||||
|
||||
map.shrink_to_fit();
|
||||
Some(Arc::new(map))
|
||||
}
|
||||
|
@ -684,11 +772,7 @@ impl<'a> CustomPropertiesBuilder<'a> {
|
|||
/// (meaning we should use the inherited value).
|
||||
///
|
||||
/// It does cycle dependencies removal at the same time as substitution.
|
||||
fn substitute_all(
|
||||
custom_properties_map: &mut CustomPropertiesMap,
|
||||
inherited: Option<&CustomPropertiesMap>,
|
||||
device: &Device,
|
||||
) {
|
||||
fn substitute_all(custom_properties_map: &mut CustomPropertiesMap, seen: &PrecomputedHashSet<&Name>, device: &Device) {
|
||||
// The cycle dependencies removal in this function is a variant
|
||||
// of Tarjan's algorithm. It is mostly based on the pseudo-code
|
||||
// listed in
|
||||
|
@ -724,10 +808,7 @@ fn substitute_all(
|
|||
/// all unfinished strong connected components.
|
||||
stack: SmallVec<[usize; 5]>,
|
||||
map: &'a mut CustomPropertiesMap,
|
||||
/// The inherited variables. We may need to restore some if we fail
|
||||
/// substitution.
|
||||
inherited: Option<&'a CustomPropertiesMap>,
|
||||
/// to resolve the environment to substitute `env()` variables.
|
||||
/// To resolve the environment to substitute `env()` variables.
|
||||
device: &'a Device,
|
||||
}
|
||||
|
||||
|
@ -749,10 +830,10 @@ fn substitute_all(
|
|||
/// doesn't have reference at all in specified value, or it has
|
||||
/// been completely resolved.
|
||||
/// * There is no such variable at all.
|
||||
fn traverse<'a>(name: Name, context: &mut Context<'a>) -> Option<usize> {
|
||||
fn traverse<'a>(name: &Name, context: &mut Context<'a>) -> Option<usize> {
|
||||
// Some shortcut checks.
|
||||
let (name, value) = {
|
||||
let value = context.map.get(&name)?;
|
||||
let value = context.map.get(name)?;
|
||||
|
||||
// Nothing to resolve.
|
||||
if value.references.is_empty() {
|
||||
|
@ -765,7 +846,7 @@ fn substitute_all(
|
|||
|
||||
// Whether this variable has been visited in this traversal.
|
||||
let key;
|
||||
match context.index_map.entry(name) {
|
||||
match context.index_map.entry(name.clone()) {
|
||||
Entry::Occupied(entry) => {
|
||||
return Some(*entry.get());
|
||||
},
|
||||
|
@ -793,7 +874,7 @@ fn substitute_all(
|
|||
let mut self_ref = false;
|
||||
let mut lowlink = index;
|
||||
for next in value.references.iter() {
|
||||
let next_index = match traverse(next.clone(), context) {
|
||||
let next_index = match traverse(next, context) {
|
||||
Some(index) => index,
|
||||
// There is nothing to do if the next variable has been
|
||||
// fully resolved at this point.
|
||||
|
@ -869,16 +950,8 @@ fn substitute_all(
|
|||
context.map.insert(name, computed_value);
|
||||
},
|
||||
Err(..) => {
|
||||
// This is invalid, reset it to the unset (inherited) value.
|
||||
let inherited = context.inherited.and_then(|m| m.get(&name)).cloned();
|
||||
match inherited {
|
||||
Some(computed_value) => {
|
||||
context.map.insert(name, computed_value);
|
||||
},
|
||||
None => {
|
||||
context.map.remove(&name);
|
||||
},
|
||||
};
|
||||
// This is invalid, reset it to the guaranteed-invalid value.
|
||||
context.map.remove(&name);
|
||||
},
|
||||
}
|
||||
|
||||
|
@ -886,17 +959,16 @@ fn substitute_all(
|
|||
None
|
||||
}
|
||||
|
||||
// We have to clone the names so that we can mutably borrow the map
|
||||
// in the context we create for traversal.
|
||||
let names: Vec<_> = custom_properties_map.keys().cloned().collect();
|
||||
for name in names.into_iter() {
|
||||
// Note that `seen` doesn't contain names inherited from our parent, but
|
||||
// those can't have variable references (since we inherit the computed
|
||||
// variables) so we don't want to spend cycles traversing them anyway.
|
||||
for name in seen {
|
||||
let mut context = Context {
|
||||
count: 0,
|
||||
index_map: PrecomputedHashMap::default(),
|
||||
stack: SmallVec::new(),
|
||||
var_info: SmallVec::new(),
|
||||
map: custom_properties_map,
|
||||
inherited,
|
||||
device,
|
||||
};
|
||||
traverse(name, &mut context);
|
||||
|
@ -1014,7 +1086,9 @@ fn substitute_block<'i>(
|
|||
let first_token_type = input
|
||||
.next_including_whitespace_and_comments()
|
||||
.ok()
|
||||
.map_or_else(TokenSerializationType::nothing, |t| t.serialization_type());
|
||||
.map_or_else(TokenSerializationType::nothing, |t| {
|
||||
t.serialization_type()
|
||||
});
|
||||
input.reset(&after_comma);
|
||||
let mut position = (after_comma.position(), first_token_type);
|
||||
last_token_type = substitute_block(
|
||||
|
|
|
@ -179,13 +179,20 @@ impl ElementStyles {
|
|||
pub fn uses_viewport_units(&self) -> bool {
|
||||
use crate::computed_value_flags::ComputedValueFlags;
|
||||
|
||||
if self.primary().flags.intersects(ComputedValueFlags::USES_VIEWPORT_UNITS) {
|
||||
if self
|
||||
.primary()
|
||||
.flags
|
||||
.intersects(ComputedValueFlags::USES_VIEWPORT_UNITS)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
for pseudo_style in self.pseudos.as_array() {
|
||||
if let Some(ref pseudo_style) = pseudo_style {
|
||||
if pseudo_style.flags.intersects(ComputedValueFlags::USES_VIEWPORT_UNITS) {
|
||||
if pseudo_style
|
||||
.flags
|
||||
.intersects(ComputedValueFlags::USES_VIEWPORT_UNITS)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -133,29 +133,27 @@ where
|
|||
let tls = ScopedTLS::<ThreadLocalStyleContext<E>>::new(pool);
|
||||
let root_opaque = root.as_node().opaque();
|
||||
let drain = discovered.drain(..);
|
||||
pool.install(|| {
|
||||
pool.scope_fifo(|scope| {
|
||||
// Enable a breadth-first rayon traversal. This causes the work
|
||||
// queue to be always FIFO, rather than FIFO for stealers and
|
||||
// FILO for the owner (which is what rayon does by default). This
|
||||
// ensures that we process all the elements at a given depth before
|
||||
// proceeding to the next depth, which is important for style sharing.
|
||||
rayon::scope_fifo(|scope| {
|
||||
#[cfg(feature = "gecko")]
|
||||
gecko_profiler_label!(Layout, StyleComputation);
|
||||
parallel::traverse_nodes(
|
||||
drain,
|
||||
DispatchMode::TailCall,
|
||||
/* recursion_ok = */ true,
|
||||
root_opaque,
|
||||
PerLevelTraversalData {
|
||||
current_dom_depth: depth,
|
||||
},
|
||||
scope,
|
||||
pool,
|
||||
traversal,
|
||||
&tls,
|
||||
);
|
||||
});
|
||||
#[cfg(feature = "gecko")]
|
||||
gecko_profiler_label!(Layout, StyleComputation);
|
||||
parallel::traverse_nodes(
|
||||
drain,
|
||||
DispatchMode::TailCall,
|
||||
/* recursion_ok = */ true,
|
||||
root_opaque,
|
||||
PerLevelTraversalData {
|
||||
current_dom_depth: depth,
|
||||
},
|
||||
scope,
|
||||
pool,
|
||||
traversal,
|
||||
&tls,
|
||||
);
|
||||
});
|
||||
|
||||
tls_slots = Some(tls.into_slots());
|
||||
|
|
|
@ -80,8 +80,6 @@ bitflags! {
|
|||
const IN_READWRITE_STATE = 1 << 25;
|
||||
/// <https://html.spec.whatwg.org/multipage/#selector-default>
|
||||
const IN_DEFAULT_STATE = 1 << 26;
|
||||
/// Non-standard: https://developer.mozilla.org/en-US/docs/Web/CSS/:-moz-submit-invalid
|
||||
const IN_MOZ_SUBMITINVALID_STATE = 1 << 27;
|
||||
/// Non-standard & undocumented.
|
||||
const IN_OPTIMUM_STATE = 1 << 28;
|
||||
/// Non-standard & undocumented.
|
||||
|
@ -127,6 +125,11 @@ bitflags! {
|
|||
const IN_DEVTOOLS_HIGHLIGHTED_STATE = 1 << 45;
|
||||
/// Used for the devtools style editor. Probably should go away.
|
||||
const IN_STYLEEDITOR_TRANSITIONING_STATE = 1 << 46;
|
||||
/// For :-moz-value-empty (to show widgets like the reveal password
|
||||
/// button or the clear button).
|
||||
const IN_VALUE_EMPTY_STATE = 1 << 47;
|
||||
/// For :-moz-revealed.
|
||||
const IN_REVEALED_STATE = 1 << 48;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -137,9 +140,17 @@ bitflags! {
|
|||
/// dom/base/Document.h.
|
||||
#[derive(MallocSizeOf)]
|
||||
pub struct DocumentState: u64 {
|
||||
/// RTL locale: specific to the XUL localedir attribute
|
||||
const NS_DOCUMENT_STATE_RTL_LOCALE = 1 << 0;
|
||||
/// Window activation status
|
||||
const NS_DOCUMENT_STATE_WINDOW_INACTIVE = 1 << 1;
|
||||
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;
|
||||
/// LWTheme status
|
||||
const LWTHEME = 1 << 3;
|
||||
/// LWTheme status
|
||||
const LWTHEME_BRIGHTTEXT = 1 << 4;
|
||||
/// LWTheme status
|
||||
const LWTHEME_DARKTEXT = 1 << 5;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -214,7 +214,7 @@ impl<'a> fmt::Display for ContextualParseError<'a> {
|
|||
ContextualParseError::UnsupportedValue(_value, ref err) => parse_error_to_str(err, f),
|
||||
ContextualParseError::NeverMatchingHostSelector(ref selector) => {
|
||||
write!(f, ":host selector is not featureless: {}", selector)
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,6 +18,8 @@ pub struct FontMetrics {
|
|||
pub zero_advance_measure: Option<Length>,
|
||||
/// The cap-height of the font.
|
||||
pub cap_height: Option<Length>,
|
||||
/// The ideographic-width of the font.
|
||||
pub ic_width: Option<Length>,
|
||||
/// The ascent of the font (a value is always available for this).
|
||||
pub ascent: Length,
|
||||
}
|
||||
|
@ -28,6 +30,7 @@ impl Default for FontMetrics {
|
|||
x_height: None,
|
||||
zero_advance_measure: None,
|
||||
cap_height: None,
|
||||
ic_width: None,
|
||||
ascent: Length::new(0.0),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,9 +12,10 @@ use crate::gecko::url::CssUrlData;
|
|||
use crate::gecko_bindings::structs::{
|
||||
RawServoAnimationValue, RawServoCounterStyleRule, RawServoCssUrlData, RawServoDeclarationBlock,
|
||||
RawServoFontFaceRule, RawServoFontFeatureValuesRule, RawServoImportRule, RawServoKeyframe,
|
||||
RawServoKeyframesRule, RawServoLayerRule, RawServoMediaList, RawServoMediaRule,
|
||||
RawServoMozDocumentRule, RawServoNamespaceRule, RawServoPageRule, RawServoScrollTimelineRule,
|
||||
RawServoStyleRule, RawServoStyleSheetContents, RawServoSupportsRule, ServoCssRules,
|
||||
RawServoKeyframesRule, RawServoLayerBlockRule, RawServoLayerStatementRule, RawServoMediaList,
|
||||
RawServoMediaRule, RawServoMozDocumentRule, RawServoNamespaceRule, RawServoPageRule,
|
||||
RawServoScrollTimelineRule, RawServoStyleRule, RawServoStyleSheetContents,
|
||||
RawServoSupportsRule, ServoCssRules,
|
||||
};
|
||||
use crate::gecko_bindings::sugar::ownership::{HasArcFFI, HasFFI, Strong};
|
||||
use crate::media_queries::MediaList;
|
||||
|
@ -24,8 +25,8 @@ use crate::shared_lock::Locked;
|
|||
use crate::stylesheets::keyframes_rule::Keyframe;
|
||||
use crate::stylesheets::{
|
||||
CounterStyleRule, CssRules, DocumentRule, FontFaceRule, FontFeatureValuesRule, ImportRule,
|
||||
KeyframesRule, LayerRule, MediaRule, NamespaceRule, PageRule, ScrollTimelineRule, StyleRule,
|
||||
StylesheetContents, SupportsRule,
|
||||
KeyframesRule, LayerBlockRule, LayerStatementRule, MediaRule, NamespaceRule, PageRule,
|
||||
ScrollTimelineRule, StyleRule, StylesheetContents, SupportsRule,
|
||||
};
|
||||
use servo_arc::{Arc, ArcBorrow};
|
||||
use std::{mem, ptr};
|
||||
|
@ -73,8 +74,11 @@ impl_arc_ffi!(Locked<Keyframe> => RawServoKeyframe
|
|||
impl_arc_ffi!(Locked<KeyframesRule> => RawServoKeyframesRule
|
||||
[Servo_KeyframesRule_AddRef, Servo_KeyframesRule_Release]);
|
||||
|
||||
impl_arc_ffi!(Locked<LayerRule> => RawServoLayerRule
|
||||
[Servo_LayerRule_AddRef, Servo_LayerRule_Release]);
|
||||
impl_arc_ffi!(Locked<LayerBlockRule> => RawServoLayerBlockRule
|
||||
[Servo_LayerBlockRule_AddRef, Servo_LayerBlockRule_Release]);
|
||||
|
||||
impl_arc_ffi!(Locked<LayerStatementRule> => RawServoLayerStatementRule
|
||||
[Servo_LayerStatementRule_AddRef, Servo_LayerStatementRule_Release]);
|
||||
|
||||
impl_arc_ffi!(Locked<MediaList> => RawServoMediaList
|
||||
[Servo_MediaList_AddRef, Servo_MediaList_Release]);
|
||||
|
|
|
@ -13,7 +13,6 @@ use crate::media_queries::{Device, MediaType};
|
|||
use crate::values::computed::CSSPixelLength;
|
||||
use crate::values::computed::Ratio;
|
||||
use crate::values::computed::Resolution;
|
||||
use crate::Atom;
|
||||
use app_units::Au;
|
||||
use euclid::default::Size2D;
|
||||
|
||||
|
@ -396,16 +395,31 @@ fn eval_overflow_inline(device: &Device, query_value: Option<OverflowInline>) ->
|
|||
}
|
||||
}
|
||||
|
||||
/// https://drafts.csswg.org/mediaqueries-5/#prefers-color-scheme
|
||||
fn eval_prefers_color_scheme(device: &Device, query_value: Option<PrefersColorScheme>) -> bool {
|
||||
fn do_eval_prefers_color_scheme(
|
||||
device: &Device,
|
||||
use_content: bool,
|
||||
query_value: Option<PrefersColorScheme>,
|
||||
) -> bool {
|
||||
let prefers_color_scheme =
|
||||
unsafe { bindings::Gecko_MediaFeatures_PrefersColorScheme(device.document()) };
|
||||
unsafe { bindings::Gecko_MediaFeatures_PrefersColorScheme(device.document(), use_content) };
|
||||
match query_value {
|
||||
Some(v) => prefers_color_scheme == v,
|
||||
None => true,
|
||||
}
|
||||
}
|
||||
|
||||
/// https://drafts.csswg.org/mediaqueries-5/#prefers-color-scheme
|
||||
fn eval_prefers_color_scheme(device: &Device, query_value: Option<PrefersColorScheme>) -> bool {
|
||||
do_eval_prefers_color_scheme(device, /* use_content = */ false, query_value)
|
||||
}
|
||||
|
||||
fn eval_content_prefers_color_scheme(
|
||||
device: &Device,
|
||||
query_value: Option<PrefersColorScheme>,
|
||||
) -> bool {
|
||||
do_eval_prefers_color_scheme(device, /* use_content = */ true, query_value)
|
||||
}
|
||||
|
||||
bitflags! {
|
||||
/// https://drafts.csswg.org/mediaqueries-4/#mf-interaction
|
||||
struct PointerCapabilities: u8 {
|
||||
|
@ -535,20 +549,36 @@ fn eval_moz_is_resource_document(
|
|||
query_value.map_or(is_resource_doc, |v| v == is_resource_doc)
|
||||
}
|
||||
|
||||
fn eval_moz_os_version(
|
||||
device: &Device,
|
||||
query_value: Option<Atom>,
|
||||
_: Option<RangeOrOperator>,
|
||||
) -> bool {
|
||||
/// Allows front-end CSS to discern platform via media queries.
|
||||
#[derive(Clone, Copy, Debug, FromPrimitive, Parse, ToCss)]
|
||||
#[repr(u8)]
|
||||
pub enum Platform {
|
||||
/// Matches any Android version.
|
||||
Android,
|
||||
/// For our purposes here, "linux" is just "gtk" (so unix-but-not-mac).
|
||||
/// There's no need for our front-end code to differentiate between those
|
||||
/// platforms and they already use the "linux" string elsewhere (e.g.,
|
||||
/// toolkit/themes/linux).
|
||||
Linux,
|
||||
/// Matches any macOS version.
|
||||
Macos,
|
||||
/// Matches any Windows version.
|
||||
Windows,
|
||||
/// Matches only Windows 7.
|
||||
WindowsWin7,
|
||||
/// Matches only Windows 8.
|
||||
WindowsWin8,
|
||||
/// Matches windows 10 and actually matches windows 11 too, as of right now.
|
||||
WindowsWin10,
|
||||
}
|
||||
|
||||
fn eval_moz_platform(_: &Device, query_value: Option<Platform>) -> bool {
|
||||
let query_value = match query_value {
|
||||
Some(v) => v,
|
||||
None => return false,
|
||||
};
|
||||
|
||||
let os_version =
|
||||
unsafe { bindings::Gecko_MediaFeatures_GetOperatingSystemVersion(device.document()) };
|
||||
|
||||
query_value.as_ptr() == os_version
|
||||
unsafe { bindings::Gecko_MediaFeatures_MatchesPlatform(query_value) }
|
||||
}
|
||||
|
||||
fn eval_moz_windows_non_native_menus(
|
||||
|
@ -556,11 +586,12 @@ fn eval_moz_windows_non_native_menus(
|
|||
query_value: Option<bool>,
|
||||
_: Option<RangeOrOperator>,
|
||||
) -> bool {
|
||||
let use_non_native_menus = match static_prefs::pref!("browser.display.windows.non_native_menus") {
|
||||
let use_non_native_menus = match static_prefs::pref!("browser.display.windows.non_native_menus")
|
||||
{
|
||||
0 => false,
|
||||
1 => true,
|
||||
_ => {
|
||||
eval_moz_os_version(device, Some(atom!("windows-win10")), None) &&
|
||||
eval_moz_platform(device, Some(Platform::WindowsWin10)) &&
|
||||
get_lnf_int_as_bool(bindings::LookAndFeel_IntID::WindowsDefaultTheme as i32)
|
||||
},
|
||||
};
|
||||
|
@ -804,6 +835,15 @@ pub static MEDIA_FEATURES: [MediaFeatureDescription; 58] = [
|
|||
keyword_evaluator!(eval_prefers_color_scheme, PrefersColorScheme),
|
||||
ParsingRequirements::empty(),
|
||||
),
|
||||
// Evaluates to the preferred color scheme for content. Only useful in
|
||||
// chrome context, where the chrome color-scheme and the content
|
||||
// color-scheme might differ.
|
||||
feature!(
|
||||
atom!("-moz-content-prefers-color-scheme"),
|
||||
AllowsRanges::No,
|
||||
keyword_evaluator!(eval_content_prefers_color_scheme, PrefersColorScheme),
|
||||
ParsingRequirements::CHROME_AND_UA_ONLY,
|
||||
),
|
||||
feature!(
|
||||
atom!("pointer"),
|
||||
AllowsRanges::No,
|
||||
|
@ -844,9 +884,9 @@ pub static MEDIA_FEATURES: [MediaFeatureDescription; 58] = [
|
|||
ParsingRequirements::CHROME_AND_UA_ONLY,
|
||||
),
|
||||
feature!(
|
||||
atom!("-moz-os-version"),
|
||||
atom!("-moz-platform"),
|
||||
AllowsRanges::No,
|
||||
Evaluator::Ident(eval_moz_os_version),
|
||||
keyword_evaluator!(eval_moz_platform, Platform),
|
||||
ParsingRequirements::CHROME_AND_UA_ONLY,
|
||||
),
|
||||
feature!(
|
||||
|
@ -873,18 +913,39 @@ pub static MEDIA_FEATURES: [MediaFeatureDescription; 58] = [
|
|||
Evaluator::BoolInteger(eval_moz_overlay_scrollbars),
|
||||
ParsingRequirements::CHROME_AND_UA_ONLY,
|
||||
),
|
||||
|
||||
lnf_int_feature!(atom!("-moz-scrollbar-start-backward"), ScrollArrowStyle, get_scrollbar_start_backward),
|
||||
lnf_int_feature!(atom!("-moz-scrollbar-start-forward"), ScrollArrowStyle, get_scrollbar_start_forward),
|
||||
lnf_int_feature!(atom!("-moz-scrollbar-end-backward"), ScrollArrowStyle, get_scrollbar_end_backward),
|
||||
lnf_int_feature!(atom!("-moz-scrollbar-end-forward"), ScrollArrowStyle, get_scrollbar_end_forward),
|
||||
lnf_int_feature!(atom!("-moz-scrollbar-thumb-proportional"), ScrollSliderStyle),
|
||||
lnf_int_feature!(
|
||||
atom!("-moz-scrollbar-start-backward"),
|
||||
ScrollArrowStyle,
|
||||
get_scrollbar_start_backward
|
||||
),
|
||||
lnf_int_feature!(
|
||||
atom!("-moz-scrollbar-start-forward"),
|
||||
ScrollArrowStyle,
|
||||
get_scrollbar_start_forward
|
||||
),
|
||||
lnf_int_feature!(
|
||||
atom!("-moz-scrollbar-end-backward"),
|
||||
ScrollArrowStyle,
|
||||
get_scrollbar_end_backward
|
||||
),
|
||||
lnf_int_feature!(
|
||||
atom!("-moz-scrollbar-end-forward"),
|
||||
ScrollArrowStyle,
|
||||
get_scrollbar_end_forward
|
||||
),
|
||||
lnf_int_feature!(
|
||||
atom!("-moz-scrollbar-thumb-proportional"),
|
||||
ScrollSliderStyle
|
||||
),
|
||||
lnf_int_feature!(atom!("-moz-menubar-drag"), MenuBarDrag),
|
||||
lnf_int_feature!(atom!("-moz-windows-default-theme"), WindowsDefaultTheme),
|
||||
lnf_int_feature!(atom!("-moz-mac-graphite-theme"), MacGraphiteTheme),
|
||||
lnf_int_feature!(atom!("-moz-mac-big-sur-theme"), MacBigSurTheme),
|
||||
lnf_int_feature!(atom!("-moz-mac-rtl"), MacRTL),
|
||||
lnf_int_feature!(atom!("-moz-windows-accent-color-in-titlebar"), WindowsAccentColorInTitlebar),
|
||||
lnf_int_feature!(
|
||||
atom!("-moz-windows-accent-color-in-titlebar"),
|
||||
WindowsAccentColorInTitlebar
|
||||
),
|
||||
lnf_int_feature!(atom!("-moz-windows-compositor"), DWMCompositor),
|
||||
lnf_int_feature!(atom!("-moz-windows-classic"), WindowsClassic),
|
||||
lnf_int_feature!(atom!("-moz-windows-glass"), WindowsGlass),
|
||||
|
@ -893,8 +954,13 @@ pub static MEDIA_FEATURES: [MediaFeatureDescription; 58] = [
|
|||
lnf_int_feature!(atom!("-moz-gtk-csd-minimize-button"), GTKCSDMinimizeButton),
|
||||
lnf_int_feature!(atom!("-moz-gtk-csd-maximize-button"), GTKCSDMaximizeButton),
|
||||
lnf_int_feature!(atom!("-moz-gtk-csd-close-button"), GTKCSDCloseButton),
|
||||
lnf_int_feature!(atom!("-moz-gtk-csd-reversed-placement"), GTKCSDReversedPlacement),
|
||||
lnf_int_feature!(
|
||||
atom!("-moz-gtk-csd-reversed-placement"),
|
||||
GTKCSDReversedPlacement
|
||||
),
|
||||
lnf_int_feature!(atom!("-moz-system-dark-theme"), SystemUsesDarkTheme),
|
||||
bool_pref_feature!(atom!("-moz-proton"), "browser.proton.enabled"),
|
||||
bool_pref_feature!(atom!("-moz-proton-places-tooltip"), "browser.proton.places-tooltip.enabled"),
|
||||
bool_pref_feature!(
|
||||
atom!("-moz-proton-places-tooltip"),
|
||||
"browser.proton.places-tooltip.enabled"
|
||||
),
|
||||
];
|
||||
|
|
|
@ -14,7 +14,8 @@ use crate::media_queries::MediaType;
|
|||
use crate::properties::ComputedValues;
|
||||
use crate::string_cache::Atom;
|
||||
use crate::values::computed::font::GenericFontFamily;
|
||||
use crate::values::computed::Length;
|
||||
use crate::values::computed::{ColorScheme, Length};
|
||||
use crate::values::specified::color::SystemColor;
|
||||
use crate::values::specified::font::FONT_MEDIUM_PX;
|
||||
use crate::values::{CustomIdent, KeyframesName};
|
||||
use app_units::{Au, AU_PER_PX};
|
||||
|
@ -387,19 +388,30 @@ impl Device {
|
|||
self.pref_sheet_prefs().mUseDocumentColors
|
||||
}
|
||||
|
||||
/// Computes a system color and returns it as an nscolor.
|
||||
pub(crate) fn system_nscolor(
|
||||
&self,
|
||||
system_color: SystemColor,
|
||||
color_scheme: &ColorScheme,
|
||||
) -> u32 {
|
||||
unsafe { bindings::Gecko_ComputeSystemColor(system_color, self.document(), color_scheme) }
|
||||
}
|
||||
|
||||
/// Returns the default background color.
|
||||
///
|
||||
/// This is only for forced-colors/high-contrast, so looking at light colors
|
||||
/// is ok.
|
||||
pub fn default_background_color_for_forced_colors(&self) -> RGBA {
|
||||
convert_nscolor_to_rgba(self.pref_sheet_prefs().mLightColors.mDefaultBackground)
|
||||
pub fn default_background_color(&self) -> RGBA {
|
||||
let normal = ColorScheme::normal();
|
||||
convert_nscolor_to_rgba(self.system_nscolor(SystemColor::Canvas, &normal))
|
||||
}
|
||||
|
||||
/// Returns the default foreground color.
|
||||
///
|
||||
/// See above for looking at light colors only.
|
||||
pub fn default_color_for_forced_colors(&self) -> RGBA {
|
||||
convert_nscolor_to_rgba(self.pref_sheet_prefs().mLightColors.mDefault)
|
||||
pub fn default_color(&self) -> RGBA {
|
||||
let normal = ColorScheme::normal();
|
||||
convert_nscolor_to_rgba(self.system_nscolor(SystemColor::Canvastext, &normal))
|
||||
}
|
||||
|
||||
/// Returns the current effective text zoom.
|
||||
|
@ -447,20 +459,6 @@ impl Device {
|
|||
}
|
||||
}
|
||||
|
||||
/// Returns the gtk titlebar radius in CSS pixels.
|
||||
pub fn titlebar_radius(&self) -> f32 {
|
||||
unsafe {
|
||||
bindings::Gecko_GetLookAndFeelInt(bindings::LookAndFeel_IntID::TitlebarRadius as i32) as f32
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the gtk menu radius in CSS pixels.
|
||||
pub fn menu_radius(&self) -> f32 {
|
||||
unsafe {
|
||||
bindings::Gecko_GetLookAndFeelInt(bindings::LookAndFeel_IntID::GtkMenuRadius as i32) as f32
|
||||
}
|
||||
}
|
||||
|
||||
/// Return whether the document is a chrome document.
|
||||
#[inline]
|
||||
pub fn is_chrome_document(&self) -> bool {
|
||||
|
|
|
@ -37,7 +37,7 @@ macro_rules! apply_non_ts_list {
|
|||
("any-link", AnyLink, IN_VISITED_OR_UNVISITED_STATE, _),
|
||||
("visited", Visited, IN_VISITED_STATE, _),
|
||||
("active", Active, IN_ACTIVE_STATE, _),
|
||||
("autofill", Autofill, IN_AUTOFILL_STATE, PSEUDO_CLASS_ENABLED_IN_UA_SHEETS_AND_CHROME),
|
||||
("autofill", Autofill, IN_AUTOFILL_STATE, _),
|
||||
("checked", Checked, IN_CHECKED_STATE, _),
|
||||
("defined", Defined, IN_DEFINED_STATE, _),
|
||||
("disabled", Disabled, IN_DISABLED_STATE, _),
|
||||
|
@ -63,6 +63,8 @@ macro_rules! apply_non_ts_list {
|
|||
("-moz-dir-attr-like-auto", MozDirAttrLikeAuto, IN_HAS_DIR_ATTR_LIKE_AUTO_STATE, PSEUDO_CLASS_ENABLED_IN_UA_SHEETS),
|
||||
|
||||
("-moz-autofill-preview", MozAutofillPreview, IN_AUTOFILL_PREVIEW_STATE, PSEUDO_CLASS_ENABLED_IN_UA_SHEETS_AND_CHROME),
|
||||
("-moz-value-empty", MozValueEmpty, IN_VALUE_EMPTY_STATE, PSEUDO_CLASS_ENABLED_IN_UA_SHEETS),
|
||||
("-moz-revealed", MozRevealed, IN_REVEALED_STATE, PSEUDO_CLASS_ENABLED_IN_UA_SHEETS),
|
||||
|
||||
("-moz-math-increment-script-level", MozMathIncrementScriptLevel, IN_INCREMENT_SCRIPT_LEVEL_STATE, _),
|
||||
|
||||
|
@ -76,7 +78,6 @@ macro_rules! apply_non_ts_list {
|
|||
("placeholder-shown", PlaceholderShown, IN_PLACEHOLDER_SHOWN_STATE, _),
|
||||
("read-only", ReadOnly, IN_READONLY_STATE, _),
|
||||
("read-write", ReadWrite, IN_READWRITE_STATE, _),
|
||||
("-moz-submit-invalid", MozSubmitInvalid, IN_MOZ_SUBMITINVALID_STATE, _),
|
||||
("user-valid", UserValid, IN_MOZ_UI_VALID_STATE, _),
|
||||
("user-invalid", UserInvalid, IN_MOZ_UI_INVALID_STATE, _),
|
||||
("-moz-meter-optimum", MozMeterOptimum, IN_OPTIMUM_STATE, _),
|
||||
|
@ -90,9 +91,9 @@ macro_rules! apply_non_ts_list {
|
|||
("-moz-use-shadow-tree-root", MozUseShadowTreeRoot, _, PSEUDO_CLASS_ENABLED_IN_UA_SHEETS),
|
||||
("-moz-is-html", MozIsHTML, _, _),
|
||||
("-moz-placeholder", MozPlaceholder, _, _),
|
||||
("-moz-lwtheme", MozLWTheme, _, _),
|
||||
("-moz-lwtheme-brighttext", MozLWThemeBrightText, _, _),
|
||||
("-moz-lwtheme-darktext", MozLWThemeDarkText, _, _),
|
||||
("-moz-lwtheme", MozLWTheme, _, PSEUDO_CLASS_ENABLED_IN_UA_SHEETS_AND_CHROME),
|
||||
("-moz-lwtheme-brighttext", MozLWThemeBrightText, _, PSEUDO_CLASS_ENABLED_IN_UA_SHEETS_AND_CHROME),
|
||||
("-moz-lwtheme-darktext", MozLWThemeDarkText, _, PSEUDO_CLASS_ENABLED_IN_UA_SHEETS_AND_CHROME),
|
||||
("-moz-window-inactive", MozWindowInactive, _, _),
|
||||
]
|
||||
}
|
||||
|
|
|
@ -17,7 +17,6 @@ use crate::string_cache::Atom;
|
|||
use crate::values::serialize_atom_identifier;
|
||||
use cssparser::ToCss;
|
||||
use std::fmt;
|
||||
use thin_slice::ThinBoxedSlice;
|
||||
|
||||
include!(concat!(
|
||||
env!("OUT_DIR"),
|
||||
|
@ -93,9 +92,10 @@ impl PseudoElement {
|
|||
EAGER_PSEUDOS[i].clone()
|
||||
}
|
||||
|
||||
/// Whether the current pseudo element is animatable.
|
||||
/// Whether animations for the current pseudo element are stored in the
|
||||
/// parent element.
|
||||
#[inline]
|
||||
pub fn is_animatable(&self) -> bool {
|
||||
pub fn animations_stored_in_parent(&self) -> bool {
|
||||
matches!(*self, Self::Before | Self::After | Self::Marker)
|
||||
}
|
||||
|
||||
|
|
|
@ -3,12 +3,15 @@
|
|||
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||
|
||||
/// Gecko's pseudo-element definition.
|
||||
///
|
||||
/// We intentionally double-box legacy ::-moz-tree pseudo-elements to keep the
|
||||
/// size of PseudoElement (and thus selector components) small.
|
||||
#[derive(Clone, Debug, Eq, Hash, MallocSizeOf, PartialEq, ToShmem)]
|
||||
pub enum PseudoElement {
|
||||
% for pseudo in PSEUDOS:
|
||||
/// ${pseudo.value}
|
||||
% if pseudo.is_tree_pseudo_element():
|
||||
${pseudo.capitalized_pseudo()}(ThinBoxedSlice<Atom>),
|
||||
${pseudo.capitalized_pseudo()}(Box<Box<[Atom]>>),
|
||||
% else:
|
||||
${pseudo.capitalized_pseudo()},
|
||||
% endif
|
||||
|
|
|
@ -8,7 +8,7 @@ use crate::element_state::{DocumentState, ElementState};
|
|||
use crate::gecko_bindings::structs::RawServoSelectorList;
|
||||
use crate::gecko_bindings::sugar::ownership::{HasBoxFFI, HasFFI, HasSimpleFFI};
|
||||
use crate::invalidation::element::document_state::InvalidationMatchingData;
|
||||
use crate::selector_parser::{Direction, SelectorParser};
|
||||
use crate::selector_parser::{Direction, HorizontalDirection, SelectorParser};
|
||||
use crate::str::starts_with_ignore_ascii_case;
|
||||
use crate::string_cache::{Atom, Namespace, WeakAtom, WeakNamespace};
|
||||
use crate::values::{AtomIdent, AtomString};
|
||||
|
@ -69,7 +69,7 @@ impl ToCss for NonTSPseudoClass {
|
|||
$(NonTSPseudoClass::$name => concat!(":", $css),)*
|
||||
NonTSPseudoClass::Lang(ref s) => {
|
||||
dest.write_str(":lang(")?;
|
||||
s.to_css(dest)?;
|
||||
cssparser::ToCss::to_css(s, dest)?;
|
||||
return dest.write_char(')');
|
||||
},
|
||||
NonTSPseudoClass::MozLocaleDir(ref dir) => {
|
||||
|
@ -127,7 +127,7 @@ impl NonTSPseudoClass {
|
|||
([$(($css:expr, $name:ident, $state:tt, $flags:tt),)*]) => {
|
||||
match *self {
|
||||
$(NonTSPseudoClass::$name => check_flag!($flags),)*
|
||||
NonTSPseudoClass::MozLocaleDir(_) |
|
||||
NonTSPseudoClass::MozLocaleDir(_) => check_flag!(PSEUDO_CLASS_ENABLED_IN_UA_SHEETS_AND_CHROME),
|
||||
NonTSPseudoClass::Lang(_) |
|
||||
NonTSPseudoClass::Dir(_) => false,
|
||||
}
|
||||
|
@ -139,11 +139,14 @@ impl NonTSPseudoClass {
|
|||
/// Returns whether the pseudo-class is enabled in content sheets.
|
||||
#[inline]
|
||||
fn is_enabled_in_content(&self) -> bool {
|
||||
if let NonTSPseudoClass::Autofill = *self {
|
||||
return static_prefs::pref!("layout.css.autofill.enabled");
|
||||
if matches!(
|
||||
*self,
|
||||
Self::MozLWTheme | Self::MozLWThemeBrightText | Self::MozLWThemeDarkText
|
||||
) {
|
||||
return static_prefs::pref!("layout.css.moz-lwtheme.content.enabled");
|
||||
}
|
||||
if let NonTSPseudoClass::MozSubmitInvalid = *self {
|
||||
return static_prefs::pref!("layout.css.moz-submit-invalid.enabled");
|
||||
if let NonTSPseudoClass::MozLocaleDir(..) = *self {
|
||||
return static_prefs::pref!("layout.css.moz-locale-dir.content.enabled");
|
||||
}
|
||||
!self.has_any_flag(NonTSPseudoClassFlag::PSEUDO_CLASS_ENABLED_IN_UA_SHEETS_AND_CHROME)
|
||||
}
|
||||
|
@ -162,7 +165,7 @@ impl NonTSPseudoClass {
|
|||
([$(($css:expr, $name:ident, $state:tt, $flags:tt),)*]) => {
|
||||
match *self {
|
||||
$(NonTSPseudoClass::$name => flag!($state),)*
|
||||
NonTSPseudoClass::Dir(..) |
|
||||
NonTSPseudoClass::Dir(ref dir) => dir.element_state(),
|
||||
NonTSPseudoClass::MozLocaleDir(..) |
|
||||
NonTSPseudoClass::Lang(..) => ElementState::empty(),
|
||||
}
|
||||
|
@ -174,8 +177,15 @@ impl NonTSPseudoClass {
|
|||
/// Get the document state flag associated with a pseudo-class, if any.
|
||||
pub fn document_state_flag(&self) -> DocumentState {
|
||||
match *self {
|
||||
NonTSPseudoClass::MozLocaleDir(..) => DocumentState::NS_DOCUMENT_STATE_RTL_LOCALE,
|
||||
NonTSPseudoClass::MozWindowInactive => DocumentState::NS_DOCUMENT_STATE_WINDOW_INACTIVE,
|
||||
NonTSPseudoClass::MozLocaleDir(ref dir) => match dir.as_horizontal_direction() {
|
||||
Some(HorizontalDirection::Ltr) => DocumentState::LTR_LOCALE,
|
||||
Some(HorizontalDirection::Rtl) => DocumentState::RTL_LOCALE,
|
||||
None => DocumentState::empty(),
|
||||
},
|
||||
NonTSPseudoClass::MozWindowInactive => DocumentState::WINDOW_INACTIVE,
|
||||
NonTSPseudoClass::MozLWTheme => DocumentState::LWTHEME,
|
||||
NonTSPseudoClass::MozLWThemeBrightText => DocumentState::LWTHEME_BRIGHTTEXT,
|
||||
NonTSPseudoClass::MozLWThemeDarkText => DocumentState::LWTHEME_DARKTEXT,
|
||||
_ => DocumentState::empty(),
|
||||
}
|
||||
}
|
||||
|
@ -186,10 +196,8 @@ impl NonTSPseudoClass {
|
|||
self.state_flag().is_empty() &&
|
||||
!matches!(
|
||||
*self,
|
||||
// :dir() depends on state only, but doesn't use state_flag
|
||||
// because its semantics don't quite match. Nevertheless, it
|
||||
// doesn't need cache revalidation, because we already compare
|
||||
// states for elements and candidates.
|
||||
// :dir() depends on state only, but may have an empty
|
||||
// state_flag for invalid arguments.
|
||||
NonTSPseudoClass::Dir(_) |
|
||||
// :-moz-is-html only depends on the state of the document and
|
||||
// the namespace of the element; the former is invariant
|
||||
|
|
|
@ -27,6 +27,7 @@ use crate::gecko_bindings::bindings;
|
|||
use crate::gecko_bindings::bindings::Gecko_ElementHasAnimations;
|
||||
use crate::gecko_bindings::bindings::Gecko_ElementHasCSSAnimations;
|
||||
use crate::gecko_bindings::bindings::Gecko_ElementHasCSSTransitions;
|
||||
use crate::gecko_bindings::bindings::Gecko_ElementState;
|
||||
use crate::gecko_bindings::bindings::Gecko_GetActiveLinkAttrDeclarationBlock;
|
||||
use crate::gecko_bindings::bindings::Gecko_GetAnimationEffectCount;
|
||||
use crate::gecko_bindings::bindings::Gecko_GetAnimationRule;
|
||||
|
@ -39,11 +40,8 @@ use crate::gecko_bindings::bindings::Gecko_IsSignificantChild;
|
|||
use crate::gecko_bindings::bindings::Gecko_MatchLang;
|
||||
use crate::gecko_bindings::bindings::Gecko_UnsetDirtyStyleAttr;
|
||||
use crate::gecko_bindings::bindings::Gecko_UpdateAnimations;
|
||||
use crate::gecko_bindings::bindings::{Gecko_ElementState, Gecko_GetDocumentLWTheme};
|
||||
use crate::gecko_bindings::bindings::{Gecko_SetNodeFlags, Gecko_UnsetNodeFlags};
|
||||
use crate::gecko_bindings::structs;
|
||||
use crate::gecko_bindings::structs::nsChangeHint;
|
||||
use crate::gecko_bindings::structs::Document_DocumentTheme as DocumentTheme;
|
||||
use crate::gecko_bindings::structs::EffectCompositor_CascadeLevel as CascadeLevel;
|
||||
use crate::gecko_bindings::structs::ELEMENT_HANDLED_SNAPSHOT;
|
||||
use crate::gecko_bindings::structs::ELEMENT_HAS_ANIMATION_ONLY_DIRTY_DESCENDANTS_FOR_SERVO;
|
||||
|
@ -55,14 +53,13 @@ use crate::gecko_bindings::structs::{nsAtom, nsIContent, nsINode_BooleanFlag};
|
|||
use crate::gecko_bindings::structs::{nsINode as RawGeckoNode, Element as RawGeckoElement};
|
||||
use crate::gecko_bindings::sugar::ownership::{HasArcFFI, HasSimpleFFI};
|
||||
use crate::global_style_data::GLOBAL_STYLE_DATA;
|
||||
use crate::hash::FxHashMap;
|
||||
use crate::invalidation::element::restyle_hints::RestyleHint;
|
||||
use crate::media_queries::Device;
|
||||
use crate::properties::animated_properties::{AnimationValue, AnimationValueMap};
|
||||
use crate::properties::{ComputedValues, LonghandId};
|
||||
use crate::properties::{Importance, PropertyDeclaration, PropertyDeclarationBlock};
|
||||
use crate::rule_tree::CascadeLevel as ServoCascadeLevel;
|
||||
use crate::selector_parser::{AttrValue, HorizontalDirection, Lang};
|
||||
use crate::selector_parser::{AttrValue, Lang};
|
||||
use crate::shared_lock::{Locked, SharedRwLock};
|
||||
use crate::string_cache::{Atom, Namespace, WeakAtom, WeakNamespace};
|
||||
use crate::stylist::CascadeData;
|
||||
|
@ -70,6 +67,7 @@ use crate::values::{AtomIdent, AtomString};
|
|||
use crate::CaseSensitivityExt;
|
||||
use crate::LocalName;
|
||||
use atomic_refcell::{AtomicRef, AtomicRefCell, AtomicRefMut};
|
||||
use fxhash::FxHashMap;
|
||||
use selectors::attr::{AttrSelectorOperation, AttrSelectorOperator};
|
||||
use selectors::attr::{CaseSensitivity, NamespaceConstraint};
|
||||
use selectors::matching::VisitedHandlingMode;
|
||||
|
@ -77,6 +75,7 @@ use selectors::matching::{ElementSelectorFlags, MatchingContext};
|
|||
use selectors::sink::Push;
|
||||
use selectors::{Element, OpaqueElement};
|
||||
use servo_arc::{Arc, ArcBorrow, RawOffsetArc};
|
||||
use std::sync::atomic::{AtomicU32, Ordering};
|
||||
use std::fmt;
|
||||
use std::hash::{Hash, Hasher};
|
||||
use std::mem;
|
||||
|
@ -266,9 +265,29 @@ impl<'ln> GeckoNode<'ln> {
|
|||
GeckoNode(&content._base)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn flags_atomic(&self) -> &AtomicU32 {
|
||||
use std::cell::Cell;
|
||||
let flags: &Cell<u32> = &(self.0)._base._base_1.mFlags;
|
||||
|
||||
#[allow(dead_code)]
|
||||
fn static_assert() {
|
||||
let _: [u8; std::mem::size_of::<Cell<u32>>()] = [0u8; std::mem::size_of::<AtomicU32>()];
|
||||
let _: [u8; std::mem::align_of::<Cell<u32>>()] = [0u8; std::mem::align_of::<AtomicU32>()];
|
||||
}
|
||||
|
||||
// Rust doesn't provide standalone atomic functions like GCC/clang do
|
||||
// (via the atomic intrinsics) or via std::atomic_ref, but it guarantees
|
||||
// that the memory representation of u32 and AtomicU32 matches:
|
||||
// https://doc.rust-lang.org/std/sync/atomic/struct.AtomicU32.html
|
||||
unsafe {
|
||||
std::mem::transmute::<&Cell<u32>, &AtomicU32>(flags)
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn flags(&self) -> u32 {
|
||||
(self.0)._base._base_1.mFlags
|
||||
self.flags_atomic().load(Ordering::Relaxed)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
|
@ -649,18 +668,14 @@ impl<'le> GeckoElement<'le> {
|
|||
self.as_node().flags()
|
||||
}
|
||||
|
||||
// FIXME: We can implement this without OOL calls, but we can't easily given
|
||||
// GeckoNode is a raw reference.
|
||||
//
|
||||
// We can use a Cell<T>, but that's a bit of a pain.
|
||||
#[inline]
|
||||
fn set_flags(&self, flags: u32) {
|
||||
unsafe { Gecko_SetNodeFlags(self.as_node().0, flags) }
|
||||
self.as_node().flags_atomic().fetch_or(flags, Ordering::Relaxed);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
unsafe fn unset_flags(&self, flags: u32) {
|
||||
Gecko_UnsetNodeFlags(self.as_node().0, flags)
|
||||
self.as_node().flags_atomic().fetch_and(!flags, Ordering::Relaxed);
|
||||
}
|
||||
|
||||
/// Returns true if this element has descendants for lazy frame construction.
|
||||
|
@ -752,12 +767,6 @@ impl<'le> GeckoElement<'le> {
|
|||
.get_bool_flag(nsINode_BooleanFlag::ElementMayHaveStyle)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn document_theme(&self) -> DocumentTheme {
|
||||
let node = self.as_node();
|
||||
unsafe { Gecko_GetDocumentLWTheme(node.owner_doc().0) }
|
||||
}
|
||||
|
||||
/// Only safe to call on the main thread, with exclusive access to the
|
||||
/// element and its ancestors.
|
||||
///
|
||||
|
@ -1203,7 +1212,11 @@ impl<'le> TElement for GeckoElement<'le> {
|
|||
where
|
||||
F: FnMut(&AtomIdent),
|
||||
{
|
||||
for attr in self.non_mapped_attrs().iter().chain(self.mapped_attrs().iter()) {
|
||||
for attr in self
|
||||
.non_mapped_attrs()
|
||||
.iter()
|
||||
.chain(self.mapped_attrs().iter())
|
||||
{
|
||||
let is_nodeinfo = attr.mName.mBits & 1 != 0;
|
||||
unsafe {
|
||||
let atom = if is_nodeinfo {
|
||||
|
@ -1393,15 +1406,14 @@ impl<'le> TElement for GeckoElement<'le> {
|
|||
#[inline]
|
||||
fn may_have_animations(&self) -> bool {
|
||||
if let Some(pseudo) = self.implemented_pseudo_element() {
|
||||
if !pseudo.is_animatable() {
|
||||
return false;
|
||||
if pseudo.animations_stored_in_parent() {
|
||||
// FIXME(emilio): When would the parent of a ::before / ::after
|
||||
// pseudo-element be null?
|
||||
return self.parent_element().map_or(false, |p| {
|
||||
p.as_node()
|
||||
.get_bool_flag(nsINode_BooleanFlag::ElementHasAnimations)
|
||||
});
|
||||
}
|
||||
// FIXME(emilio): When would the parent of a ::before / ::after
|
||||
// pseudo-element be null?
|
||||
return self.parent_element().map_or(false, |p| {
|
||||
p.as_node()
|
||||
.get_bool_flag(nsINode_BooleanFlag::ElementHasAnimations)
|
||||
});
|
||||
}
|
||||
self.as_node()
|
||||
.get_bool_flag(nsINode_BooleanFlag::ElementHasAnimations)
|
||||
|
@ -1582,6 +1594,7 @@ impl<'le> TElement for GeckoElement<'le> {
|
|||
use crate::properties::longhands::_x_text_zoom::SpecifiedValue as SpecifiedZoom;
|
||||
use crate::properties::longhands::color::SpecifiedValue as SpecifiedColor;
|
||||
use crate::properties::longhands::text_align::SpecifiedValue as SpecifiedTextAlign;
|
||||
use crate::stylesheets::layer_rule::LayerOrder;
|
||||
use crate::values::specified::color::Color;
|
||||
lazy_static! {
|
||||
static ref TH_RULE: ApplicableDeclarationBlock = {
|
||||
|
@ -1591,7 +1604,11 @@ impl<'le> TElement for GeckoElement<'le> {
|
|||
Importance::Normal,
|
||||
);
|
||||
let arc = Arc::new_leaked(global_style_data.shared_lock.wrap(pdb));
|
||||
ApplicableDeclarationBlock::from_declarations(arc, ServoCascadeLevel::PresHints)
|
||||
ApplicableDeclarationBlock::from_declarations(
|
||||
arc,
|
||||
ServoCascadeLevel::PresHints,
|
||||
LayerOrder::root(),
|
||||
)
|
||||
};
|
||||
static ref TABLE_COLOR_RULE: ApplicableDeclarationBlock = {
|
||||
let global_style_data = &*GLOBAL_STYLE_DATA;
|
||||
|
@ -1600,7 +1617,11 @@ impl<'le> TElement for GeckoElement<'le> {
|
|||
Importance::Normal,
|
||||
);
|
||||
let arc = Arc::new_leaked(global_style_data.shared_lock.wrap(pdb));
|
||||
ApplicableDeclarationBlock::from_declarations(arc, ServoCascadeLevel::PresHints)
|
||||
ApplicableDeclarationBlock::from_declarations(
|
||||
arc,
|
||||
ServoCascadeLevel::PresHints,
|
||||
LayerOrder::root(),
|
||||
)
|
||||
};
|
||||
static ref MATHML_LANG_RULE: ApplicableDeclarationBlock = {
|
||||
let global_style_data = &*GLOBAL_STYLE_DATA;
|
||||
|
@ -1609,7 +1630,11 @@ impl<'le> TElement for GeckoElement<'le> {
|
|||
Importance::Normal,
|
||||
);
|
||||
let arc = Arc::new_leaked(global_style_data.shared_lock.wrap(pdb));
|
||||
ApplicableDeclarationBlock::from_declarations(arc, ServoCascadeLevel::PresHints)
|
||||
ApplicableDeclarationBlock::from_declarations(
|
||||
arc,
|
||||
ServoCascadeLevel::PresHints,
|
||||
LayerOrder::root(),
|
||||
)
|
||||
};
|
||||
static ref SVG_TEXT_DISABLE_ZOOM_RULE: ApplicableDeclarationBlock = {
|
||||
let global_style_data = &*GLOBAL_STYLE_DATA;
|
||||
|
@ -1618,7 +1643,11 @@ impl<'le> TElement for GeckoElement<'le> {
|
|||
Importance::Normal,
|
||||
);
|
||||
let arc = Arc::new_leaked(global_style_data.shared_lock.wrap(pdb));
|
||||
ApplicableDeclarationBlock::from_declarations(arc, ServoCascadeLevel::PresHints)
|
||||
ApplicableDeclarationBlock::from_declarations(
|
||||
arc,
|
||||
ServoCascadeLevel::PresHints,
|
||||
LayerOrder::root(),
|
||||
)
|
||||
};
|
||||
};
|
||||
|
||||
|
@ -1646,6 +1675,7 @@ impl<'le> TElement for GeckoElement<'le> {
|
|||
hints.push(ApplicableDeclarationBlock::from_declarations(
|
||||
decl.clone_arc(),
|
||||
ServoCascadeLevel::PresHints,
|
||||
LayerOrder::root(),
|
||||
));
|
||||
}
|
||||
let declarations = unsafe { Gecko_GetExtraContentStyleDeclarations(self.0).as_ref() };
|
||||
|
@ -1655,6 +1685,7 @@ impl<'le> TElement for GeckoElement<'le> {
|
|||
hints.push(ApplicableDeclarationBlock::from_declarations(
|
||||
decl.clone_arc(),
|
||||
ServoCascadeLevel::PresHints,
|
||||
LayerOrder::root(),
|
||||
));
|
||||
}
|
||||
|
||||
|
@ -1682,6 +1713,7 @@ impl<'le> TElement for GeckoElement<'le> {
|
|||
hints.push(ApplicableDeclarationBlock::from_declarations(
|
||||
decl.clone_arc(),
|
||||
ServoCascadeLevel::PresHints,
|
||||
LayerOrder::root(),
|
||||
));
|
||||
}
|
||||
|
||||
|
@ -1697,6 +1729,7 @@ impl<'le> TElement for GeckoElement<'le> {
|
|||
hints.push(ApplicableDeclarationBlock::from_declarations(
|
||||
decl.clone_arc(),
|
||||
ServoCascadeLevel::PresHints,
|
||||
LayerOrder::root(),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
@ -1718,6 +1751,7 @@ impl<'le> TElement for GeckoElement<'le> {
|
|||
hints.push(ApplicableDeclarationBlock::from_declarations(
|
||||
arc,
|
||||
ServoCascadeLevel::PresHints,
|
||||
LayerOrder::root(),
|
||||
))
|
||||
}
|
||||
// MathML's default lang has precedence over both `lang` and `xml:lang`
|
||||
|
@ -1965,7 +1999,6 @@ impl<'le> ::selectors::Element for GeckoElement<'le> {
|
|||
NonTSPseudoClass::InRange |
|
||||
NonTSPseudoClass::OutOfRange |
|
||||
NonTSPseudoClass::Default |
|
||||
NonTSPseudoClass::MozSubmitInvalid |
|
||||
NonTSPseudoClass::UserValid |
|
||||
NonTSPseudoClass::UserInvalid |
|
||||
NonTSPseudoClass::MozMeterOptimum |
|
||||
|
@ -1979,9 +2012,10 @@ impl<'le> ::selectors::Element for GeckoElement<'le> {
|
|||
NonTSPseudoClass::MozTopmostModalDialog |
|
||||
NonTSPseudoClass::Active |
|
||||
NonTSPseudoClass::Hover |
|
||||
NonTSPseudoClass::MozAutofillPreview => {
|
||||
self.state().intersects(pseudo_class.state_flag())
|
||||
},
|
||||
NonTSPseudoClass::MozAutofillPreview |
|
||||
NonTSPseudoClass::MozRevealed |
|
||||
NonTSPseudoClass::MozValueEmpty |
|
||||
NonTSPseudoClass::Dir(..) => self.state().intersects(pseudo_class.state_flag()),
|
||||
NonTSPseudoClass::AnyLink => self.is_link(),
|
||||
NonTSPseudoClass::Link => {
|
||||
self.is_link() && context.visited_handling().matches_unvisited()
|
||||
|
@ -2032,40 +2066,27 @@ impl<'le> ::selectors::Element for GeckoElement<'le> {
|
|||
bindings::Gecko_IsSelectListBox(self.0)
|
||||
},
|
||||
NonTSPseudoClass::MozIsHTML => self.is_html_element_in_html_document(),
|
||||
NonTSPseudoClass::MozLWTheme => self.document_theme() != DocumentTheme::Doc_Theme_None,
|
||||
NonTSPseudoClass::MozLWThemeBrightText => {
|
||||
self.document_theme() == DocumentTheme::Doc_Theme_Bright
|
||||
},
|
||||
NonTSPseudoClass::MozLWThemeDarkText => {
|
||||
self.document_theme() == DocumentTheme::Doc_Theme_Dark
|
||||
},
|
||||
|
||||
NonTSPseudoClass::MozLWTheme |
|
||||
NonTSPseudoClass::MozLWThemeBrightText |
|
||||
NonTSPseudoClass::MozLWThemeDarkText |
|
||||
NonTSPseudoClass::MozLocaleDir(..) |
|
||||
NonTSPseudoClass::MozWindowInactive => {
|
||||
let state_bit = DocumentState::NS_DOCUMENT_STATE_WINDOW_INACTIVE;
|
||||
let state_bit = pseudo_class.document_state_flag();
|
||||
if state_bit.is_empty() {
|
||||
debug_assert!(
|
||||
matches!(pseudo_class, NonTSPseudoClass::MozLocaleDir(..)),
|
||||
"Only moz-locale-dir should ever return an empty state"
|
||||
);
|
||||
return false;
|
||||
}
|
||||
if context.extra_data.document_state.intersects(state_bit) {
|
||||
return !context.in_negation();
|
||||
}
|
||||
|
||||
self.document_state().contains(state_bit)
|
||||
},
|
||||
NonTSPseudoClass::MozPlaceholder => false,
|
||||
NonTSPseudoClass::Lang(ref lang_arg) => self.match_element_lang(None, lang_arg),
|
||||
NonTSPseudoClass::MozLocaleDir(ref dir) => {
|
||||
let state_bit = DocumentState::NS_DOCUMENT_STATE_RTL_LOCALE;
|
||||
if context.extra_data.document_state.intersects(state_bit) {
|
||||
// NOTE(emilio): We could still return false for values
|
||||
// other than "ltr" and "rtl", but we don't bother.
|
||||
return !context.in_negation();
|
||||
}
|
||||
|
||||
let doc_is_rtl = self.document_state().contains(state_bit);
|
||||
|
||||
match dir.as_horizontal_direction() {
|
||||
Some(HorizontalDirection::Ltr) => !doc_is_rtl,
|
||||
Some(HorizontalDirection::Rtl) => doc_is_rtl,
|
||||
None => false,
|
||||
}
|
||||
},
|
||||
NonTSPseudoClass::Dir(ref dir) => self.state().intersects(dir.element_state()),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,31 +0,0 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||
|
||||
//! Reexports of hashglobe types in Gecko mode, and stdlib hashmap shims in Servo mode
|
||||
//!
|
||||
//! Can go away when the stdlib gets fallible collections
|
||||
//! https://github.com/rust-lang/rfcs/pull/2116
|
||||
|
||||
use fxhash;
|
||||
|
||||
#[cfg(feature = "gecko")]
|
||||
pub use hashglobe::hash_map::HashMap;
|
||||
#[cfg(feature = "gecko")]
|
||||
pub use hashglobe::hash_set::HashSet;
|
||||
|
||||
#[cfg(feature = "servo")]
|
||||
pub use hashglobe::fake::{HashMap, HashSet};
|
||||
|
||||
/// Appropriate reexports of hash_map types
|
||||
pub mod map {
|
||||
#[cfg(feature = "gecko")]
|
||||
pub use hashglobe::hash_map::{Entry, Iter};
|
||||
#[cfg(feature = "servo")]
|
||||
pub use std::collections::hash_map::{Entry, Iter};
|
||||
}
|
||||
|
||||
/// Hash map that uses the Fx hasher
|
||||
pub type FxHashMap<K, V> = HashMap<K, V, fxhash::FxBuildHasher>;
|
||||
/// Hash set that uses the Fx hasher
|
||||
pub type FxHashSet<T> = HashSet<T, fxhash::FxBuildHasher>;
|
|
@ -178,28 +178,6 @@ where
|
|||
// Some pseudo-classes need special handling to evaluate them against
|
||||
// the snapshot.
|
||||
match *pseudo_class {
|
||||
// :dir is implemented in terms of state flags, but which state flag
|
||||
// it maps to depends on the argument to :dir. That means we can't
|
||||
// just add its state flags to the NonTSPseudoClass, because if we
|
||||
// added all of them there, and tested via intersects() here, we'd
|
||||
// get incorrect behavior for :not(:dir()) cases.
|
||||
//
|
||||
// FIXME(bz): How can I set this up so once Servo adds :dir()
|
||||
// support we don't forget to update this code?
|
||||
#[cfg(feature = "gecko")]
|
||||
NonTSPseudoClass::Dir(ref dir) => {
|
||||
let selector_flag = dir.element_state();
|
||||
if selector_flag.is_empty() {
|
||||
// :dir() with some random argument; does not match.
|
||||
return false;
|
||||
}
|
||||
let state = match self.snapshot().and_then(|s| s.state()) {
|
||||
Some(snapshot_state) => snapshot_state,
|
||||
None => self.element.state(),
|
||||
};
|
||||
return state.contains(selector_flag);
|
||||
},
|
||||
|
||||
// For :link and :visited, we don't actually want to test the
|
||||
// element state directly.
|
||||
//
|
||||
|
|
|
@ -10,9 +10,8 @@ use crate::selector_map::{
|
|||
MaybeCaseInsensitiveHashMap, PrecomputedHashMap, SelectorMap, SelectorMapEntry,
|
||||
};
|
||||
use crate::selector_parser::SelectorImpl;
|
||||
use crate::{Atom, LocalName, Namespace};
|
||||
use fallible::FallibleVec;
|
||||
use hashglobe::FailedAllocationError;
|
||||
use crate::AllocErr;
|
||||
use crate::{Atom, LocalName, Namespace, ShrinkIfNeeded};
|
||||
use selectors::attr::NamespaceConstraint;
|
||||
use selectors::parser::{Combinator, Component};
|
||||
use selectors::parser::{Selector, SelectorIter};
|
||||
|
@ -238,13 +237,21 @@ impl InvalidationMap {
|
|||
self.other_attribute_affecting_selectors.clear();
|
||||
}
|
||||
|
||||
/// Shrink the capacity of hash maps if needed.
|
||||
pub fn shrink_if_needed(&mut self) {
|
||||
self.class_to_selector.shrink_if_needed();
|
||||
self.id_to_selector.shrink_if_needed();
|
||||
self.state_affecting_selectors.shrink_if_needed();
|
||||
self.other_attribute_affecting_selectors.shrink_if_needed();
|
||||
}
|
||||
|
||||
/// Adds a selector to this `InvalidationMap`. Returns Err(..) to
|
||||
/// signify OOM.
|
||||
pub fn note_selector(
|
||||
&mut self,
|
||||
selector: &Selector<SelectorImpl>,
|
||||
quirks_mode: QuirksMode,
|
||||
) -> Result<(), FailedAllocationError> {
|
||||
) -> Result<(), AllocErr> {
|
||||
debug!("InvalidationMap::note_selector({:?})", selector);
|
||||
|
||||
let mut document_state = DocumentState::empty();
|
||||
|
@ -274,7 +281,8 @@ impl InvalidationMap {
|
|||
state: document_state,
|
||||
dependency: Dependency::for_full_selector_invalidation(selector.clone()),
|
||||
};
|
||||
self.document_state_selectors.try_push(dep)?;
|
||||
self.document_state_selectors.try_reserve(1)?;
|
||||
self.document_state_selectors.push(dep);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
@ -325,7 +333,7 @@ struct SelectorDependencyCollector<'a> {
|
|||
compound_state: PerCompoundState,
|
||||
|
||||
/// The allocation error, if we OOM.
|
||||
alloc_error: &'a mut Option<FailedAllocationError>,
|
||||
alloc_error: &'a mut Option<AllocErr>,
|
||||
}
|
||||
|
||||
impl<'a> SelectorDependencyCollector<'a> {
|
||||
|
@ -361,7 +369,7 @@ impl<'a> SelectorDependencyCollector<'a> {
|
|||
self.quirks_mode,
|
||||
);
|
||||
if let Err(alloc_error) = result {
|
||||
*self.alloc_error = Some(alloc_error);
|
||||
*self.alloc_error = Some(alloc_error.into());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -378,21 +386,17 @@ impl<'a> SelectorDependencyCollector<'a> {
|
|||
let dependency = self.dependency();
|
||||
|
||||
let map = &mut self.map.other_attribute_affecting_selectors;
|
||||
let entry = match map.try_entry(name) {
|
||||
Ok(entry) => entry,
|
||||
Err(err) => {
|
||||
*self.alloc_error = Some(err);
|
||||
return false;
|
||||
},
|
||||
};
|
||||
|
||||
match entry.or_insert_with(SmallVec::new).try_push(dependency) {
|
||||
Ok(..) => true,
|
||||
Err(err) => {
|
||||
*self.alloc_error = Some(err);
|
||||
return false;
|
||||
},
|
||||
if let Err(err) = map.try_reserve(1) {
|
||||
*self.alloc_error = Some(err.into());
|
||||
return false;
|
||||
}
|
||||
let vec = map.entry(name).or_default();
|
||||
if let Err(err) = vec.try_reserve(1) {
|
||||
*self.alloc_error = Some(err.into());
|
||||
return false;
|
||||
}
|
||||
vec.push(dependency);
|
||||
true
|
||||
}
|
||||
|
||||
fn dependency(&self) -> Dependency {
|
||||
|
@ -481,24 +485,20 @@ impl<'a> SelectorVisitor for SelectorDependencyCollector<'a> {
|
|||
let entry = match map.try_entry(atom.0.clone(), self.quirks_mode) {
|
||||
Ok(entry) => entry,
|
||||
Err(err) => {
|
||||
*self.alloc_error = Some(err);
|
||||
*self.alloc_error = Some(err.into());
|
||||
return false;
|
||||
},
|
||||
};
|
||||
match entry.or_insert_with(SmallVec::new).try_push(dependency) {
|
||||
Ok(..) => true,
|
||||
Err(err) => {
|
||||
*self.alloc_error = Some(err);
|
||||
return false;
|
||||
},
|
||||
let vec = entry.or_insert_with(SmallVec::new);
|
||||
if let Err(err) = vec.try_reserve(1) {
|
||||
*self.alloc_error = Some(err.into());
|
||||
return false;
|
||||
}
|
||||
vec.push(dependency);
|
||||
true
|
||||
},
|
||||
Component::NonTSPseudoClass(ref pc) => {
|
||||
self.compound_state.element_state |= match *pc {
|
||||
#[cfg(feature = "gecko")]
|
||||
NonTSPseudoClass::Dir(ref dir) => dir.element_state(),
|
||||
_ => pc.state_flag(),
|
||||
};
|
||||
self.compound_state.element_state |= pc.state_flag();
|
||||
*self.document_state |= pc.document_state_flag();
|
||||
|
||||
let attr_name = match *pc {
|
||||
|
|
|
@ -18,7 +18,7 @@ use crate::shared_lock::SharedRwLockReadGuard;
|
|||
use crate::stylesheets::{CssRule, StylesheetInDocument};
|
||||
use crate::stylesheets::{EffectiveRules, EffectiveRulesIterator};
|
||||
use crate::values::AtomIdent;
|
||||
use crate::Atom;
|
||||
use crate::{Atom, ShrinkIfNeeded};
|
||||
use crate::LocalName as SelectorLocalName;
|
||||
use selectors::parser::{Component, LocalName, Selector};
|
||||
|
||||
|
@ -119,6 +119,15 @@ impl StylesheetInvalidationSet {
|
|||
self.fully_invalid = true;
|
||||
}
|
||||
|
||||
fn shrink_if_needed(&mut self) {
|
||||
if self.fully_invalid {
|
||||
return;
|
||||
}
|
||||
self.classes.shrink_if_needed();
|
||||
self.ids.shrink_if_needed();
|
||||
self.local_names.shrink_if_needed();
|
||||
}
|
||||
|
||||
/// Analyze the given stylesheet, and collect invalidations from their
|
||||
/// rules, in order to avoid doing a full restyle when we style the document
|
||||
/// next time.
|
||||
|
@ -149,6 +158,8 @@ impl StylesheetInvalidationSet {
|
|||
}
|
||||
}
|
||||
|
||||
self.shrink_if_needed();
|
||||
|
||||
debug!(" > resulting class invalidations: {:?}", self.classes);
|
||||
debug!(" > resulting id invalidations: {:?}", self.ids);
|
||||
debug!(
|
||||
|
@ -484,16 +495,16 @@ impl StylesheetInvalidationSet {
|
|||
},
|
||||
Invalidation::LocalName { name, lower_name } => {
|
||||
let insert_lower = name != lower_name;
|
||||
let entry = match self.local_names.try_entry(name) {
|
||||
Ok(e) => e,
|
||||
Err(..) => return false,
|
||||
};
|
||||
if self.local_names.try_reserve(1).is_err() {
|
||||
return false;
|
||||
}
|
||||
let entry = self.local_names.entry(name);
|
||||
*entry.or_insert(InvalidationKind::None) |= kind;
|
||||
if insert_lower {
|
||||
let entry = match self.local_names.try_entry(lower_name) {
|
||||
Ok(e) => e,
|
||||
Err(..) => return false,
|
||||
};
|
||||
if self.local_names.try_reserve(1).is_err() {
|
||||
return false;
|
||||
}
|
||||
let entry = self.local_names.entry(lower_name);
|
||||
*entry.or_insert(InvalidationKind::None) |= kind;
|
||||
}
|
||||
},
|
||||
|
@ -541,6 +552,7 @@ impl StylesheetInvalidationSet {
|
|||
Page(..) |
|
||||
Viewport(..) |
|
||||
FontFeatureValues(..) |
|
||||
LayerStatement(..) |
|
||||
FontFace(..) |
|
||||
Keyframes(..) |
|
||||
ScrollTimeline(..) |
|
||||
|
@ -556,7 +568,7 @@ impl StylesheetInvalidationSet {
|
|||
|
||||
self.collect_invalidations_for_rule(rule, guard, device, quirks_mode)
|
||||
},
|
||||
Document(..) | Import(..) | Media(..) | Supports(..) | Layer(..) => {
|
||||
Document(..) | Import(..) | Media(..) | Supports(..) | LayerBlock(..) => {
|
||||
if !is_generic_change &&
|
||||
!EffectiveRules::is_effective(guard, device, quirks_mode, rule)
|
||||
{
|
||||
|
@ -597,7 +609,8 @@ impl StylesheetInvalidationSet {
|
|||
}
|
||||
}
|
||||
},
|
||||
Document(..) | Namespace(..) | Import(..) | Media(..) | Supports(..) | Layer(..) => {
|
||||
Document(..) | Namespace(..) | Import(..) | Media(..) | Supports(..) |
|
||||
LayerStatement(..) | LayerBlock(..) => {
|
||||
// Do nothing, relevant nested rules are visited as part of the
|
||||
// iteration.
|
||||
},
|
||||
|
@ -619,11 +632,12 @@ impl StylesheetInvalidationSet {
|
|||
// existing elements.
|
||||
}
|
||||
},
|
||||
ScrollTimeline(..) => {
|
||||
// TODO: Bug 1676784: check if animation-timeline name is referenced.
|
||||
// Now we do nothing.
|
||||
},
|
||||
CounterStyle(..) | Page(..) | Viewport(..) | FontFeatureValues(..) => {
|
||||
// TODO: Check if timeline name is referenced, though this might go away in bug 1737918.
|
||||
ScrollTimeline(..) |
|
||||
CounterStyle(..) |
|
||||
Page(..) |
|
||||
Viewport(..) |
|
||||
FontFeatureValues(..) => {
|
||||
debug!(
|
||||
" > Found unsupported rule, marking the whole subtree \
|
||||
invalid."
|
||||
|
|
|
@ -97,7 +97,6 @@ pub mod font_metrics;
|
|||
#[allow(unsafe_code)]
|
||||
pub mod gecko_bindings;
|
||||
pub mod global_style_data;
|
||||
pub mod hash;
|
||||
pub mod invalidation;
|
||||
#[allow(missing_docs)] // TODO.
|
||||
pub mod logical_geometry;
|
||||
|
@ -158,6 +157,8 @@ pub use style_traits::arc_slice::ArcSlice;
|
|||
pub use style_traits::owned_slice::OwnedSlice;
|
||||
pub use style_traits::owned_str::OwnedStr;
|
||||
|
||||
use std::hash::{Hash, BuildHasher};
|
||||
|
||||
/// The CSS properties supported by the style system.
|
||||
/// Generated from the properties.mako.rs template by build.rs
|
||||
#[macro_use]
|
||||
|
@ -263,3 +264,68 @@ where
|
|||
*self == One::one()
|
||||
}
|
||||
}
|
||||
|
||||
/// An allocation error.
|
||||
///
|
||||
/// TODO(emilio): Would be nice to have more information here, or for SmallVec
|
||||
/// to return the standard error type (and then we can just return that).
|
||||
///
|
||||
/// But given we use these mostly to bail out and ignore them, it's not a big
|
||||
/// deal.
|
||||
#[derive(Debug)]
|
||||
pub struct AllocErr;
|
||||
|
||||
impl From<smallvec::CollectionAllocErr> for AllocErr {
|
||||
#[inline]
|
||||
fn from(_: smallvec::CollectionAllocErr) -> Self {
|
||||
Self
|
||||
}
|
||||
}
|
||||
|
||||
impl From<std::collections::TryReserveError> for AllocErr {
|
||||
#[inline]
|
||||
fn from(_: std::collections::TryReserveError) -> Self {
|
||||
Self
|
||||
}
|
||||
}
|
||||
|
||||
/// Shrink the capacity of the collection if needed.
|
||||
pub (crate) trait ShrinkIfNeeded {
|
||||
fn shrink_if_needed(&mut self);
|
||||
}
|
||||
|
||||
/// We shrink the capacity of a collection if we're wasting more than a 25% of
|
||||
/// its capacity, and if the collection is arbitrarily big enough
|
||||
/// (>= CAPACITY_THRESHOLD entries).
|
||||
#[inline]
|
||||
fn should_shrink(len: usize, capacity: usize) -> bool {
|
||||
const CAPACITY_THRESHOLD: usize = 64;
|
||||
capacity >= CAPACITY_THRESHOLD && len + capacity / 4 < capacity
|
||||
}
|
||||
|
||||
impl<K, V, H> ShrinkIfNeeded for std::collections::HashMap<K, V, H>
|
||||
where
|
||||
K: Eq + Hash,
|
||||
H: BuildHasher,
|
||||
{
|
||||
fn shrink_if_needed(&mut self) {
|
||||
if should_shrink(self.len(), self.capacity()) {
|
||||
self.shrink_to_fit();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, H> ShrinkIfNeeded for std::collections::HashSet<T, H>
|
||||
where
|
||||
T: Eq + Hash,
|
||||
H: BuildHasher,
|
||||
{
|
||||
fn shrink_if_needed(&mut self) {
|
||||
if should_shrink(self.len(), self.capacity()) {
|
||||
self.shrink_to_fit();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO(emilio): Measure and see if we're wasting a lot of memory on Vec /
|
||||
// SmallVec, and if so consider shrinking those as well.
|
||||
|
|
|
@ -23,6 +23,7 @@ use crate::selector_parser::{PseudoElement, RestyleDamage};
|
|||
use crate::shared_lock::Locked;
|
||||
use crate::style_resolver::ResolvedElementStyles;
|
||||
use crate::style_resolver::{PseudoElementResolution, StyleResolverForElement};
|
||||
use crate::stylesheets::layer_rule::LayerOrder;
|
||||
use crate::stylist::RuleInclusion;
|
||||
use crate::traversal_flags::TraversalFlags;
|
||||
use selectors::matching::ElementSelectorFlags;
|
||||
|
@ -92,6 +93,7 @@ trait PrivateMatchMethods: TElement {
|
|||
fn replace_single_rule_node(
|
||||
context: &SharedStyleContext,
|
||||
level: CascadeLevel,
|
||||
layer_order: LayerOrder,
|
||||
pdb: Option<ArcBorrow<Locked<PropertyDeclarationBlock>>>,
|
||||
path: &mut StrongRuleNode,
|
||||
) -> bool {
|
||||
|
@ -101,6 +103,7 @@ trait PrivateMatchMethods: TElement {
|
|||
let mut important_rules_changed = false;
|
||||
let new_node = stylist.rule_tree().update_rule_at_level(
|
||||
level,
|
||||
layer_order,
|
||||
pdb,
|
||||
path,
|
||||
guards,
|
||||
|
@ -145,12 +148,14 @@ trait PrivateMatchMethods: TElement {
|
|||
result |= Self::replace_single_rule_node(
|
||||
context.shared,
|
||||
CascadeLevel::same_tree_author_normal(),
|
||||
LayerOrder::root(),
|
||||
style_attribute,
|
||||
primary_rules,
|
||||
);
|
||||
result |= Self::replace_single_rule_node(
|
||||
context.shared,
|
||||
CascadeLevel::same_tree_author_important(),
|
||||
LayerOrder::root(),
|
||||
style_attribute,
|
||||
primary_rules,
|
||||
);
|
||||
|
@ -172,6 +177,7 @@ trait PrivateMatchMethods: TElement {
|
|||
Self::replace_single_rule_node(
|
||||
context.shared,
|
||||
CascadeLevel::SMILOverride,
|
||||
LayerOrder::root(),
|
||||
self.smil_override(),
|
||||
primary_rules,
|
||||
);
|
||||
|
@ -181,6 +187,7 @@ trait PrivateMatchMethods: TElement {
|
|||
Self::replace_single_rule_node(
|
||||
context.shared,
|
||||
CascadeLevel::Transitions,
|
||||
LayerOrder::root(),
|
||||
self.transition_rule(&context.shared)
|
||||
.as_ref()
|
||||
.map(|a| a.borrow_arc()),
|
||||
|
@ -192,6 +199,7 @@ trait PrivateMatchMethods: TElement {
|
|||
Self::replace_single_rule_node(
|
||||
context.shared,
|
||||
CascadeLevel::Animations,
|
||||
LayerOrder::root(),
|
||||
self.animation_rule(&context.shared)
|
||||
.as_ref()
|
||||
.map(|a| a.borrow_arc()),
|
||||
|
@ -277,7 +285,7 @@ trait PrivateMatchMethods: TElement {
|
|||
|
||||
let old_box_style = old_style.get_box();
|
||||
|
||||
let keyframes_could_have_changed = context
|
||||
let keyframes_or_timeline_could_have_changed = context
|
||||
.shared
|
||||
.traversal_flags
|
||||
.contains(TraversalFlags::ForCSSRuleChanges);
|
||||
|
@ -287,9 +295,9 @@ trait PrivateMatchMethods: TElement {
|
|||
// element has or will have CSS animation style regardless of whether
|
||||
// the animation is running or not.
|
||||
//
|
||||
// TODO: We should check which @keyframes were added/changed/deleted and
|
||||
// update only animations corresponding to those @keyframes.
|
||||
if keyframes_could_have_changed {
|
||||
// TODO: We should check which @keyframes/@scroll-timeline were added/changed/deleted and
|
||||
// update only animations corresponding to those @keyframes/@scroll-timeline.
|
||||
if keyframes_or_timeline_could_have_changed {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -505,12 +513,14 @@ 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,
|
||||
);
|
||||
|
@ -589,12 +599,14 @@ 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,
|
||||
);
|
||||
|
|
|
@ -211,13 +211,15 @@ fn consume_operation_or_colon(input: &mut Parser) -> Result<Option<Operator>, ()
|
|||
//
|
||||
// TODO(emilio): Maybe we should ignore comments as well?
|
||||
// https://github.com/w3c/csswg-drafts/issues/6248
|
||||
let parsed_equal = input.try_parse(|i| {
|
||||
let t = i.next_including_whitespace().map_err(|_| ())?;
|
||||
if !matches!(t, Token::Delim('=')) {
|
||||
return Err(())
|
||||
}
|
||||
Ok(())
|
||||
}).is_ok();
|
||||
let parsed_equal = input
|
||||
.try_parse(|i| {
|
||||
let t = i.next_including_whitespace().map_err(|_| ())?;
|
||||
if !matches!(t, Token::Delim('=')) {
|
||||
return Err(());
|
||||
}
|
||||
Ok(())
|
||||
})
|
||||
.is_ok();
|
||||
|
||||
if !parsed_equal {
|
||||
return Ok(Some(operator));
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
|
||||
//! The main cascading algorithm of the style system.
|
||||
|
||||
use crate::applicable_declarations::CascadePriority;
|
||||
use crate::context::QuirksMode;
|
||||
use crate::custom_properties::CustomPropertiesBuilder;
|
||||
use crate::dom::TElement;
|
||||
|
@ -15,12 +16,13 @@ use crate::properties::{
|
|||
ShorthandsWithPropertyReferencesCache, StyleBuilder, CASCADE_PROPERTY,
|
||||
};
|
||||
use crate::rule_cache::{RuleCache, RuleCacheConditions};
|
||||
use crate::rule_tree::StrongRuleNode;
|
||||
use crate::rule_tree::{StrongRuleNode, CascadeLevel};
|
||||
use crate::selector_parser::PseudoElement;
|
||||
use crate::shared_lock::StylesheetGuards;
|
||||
use crate::style_adjuster::StyleAdjuster;
|
||||
use crate::stylesheets::{Origin, PerOrigin};
|
||||
use crate::stylesheets::{Origin, layer_rule::LayerOrder};
|
||||
use crate::values::{computed, specified};
|
||||
use fxhash::FxHashMap;
|
||||
use servo_arc::Arc;
|
||||
use smallvec::SmallVec;
|
||||
use std::borrow::Cow;
|
||||
|
@ -115,6 +117,7 @@ struct DeclarationIterator<'a> {
|
|||
declarations: DeclarationImportanceIterator<'a>,
|
||||
origin: Origin,
|
||||
importance: Importance,
|
||||
priority: CascadePriority,
|
||||
}
|
||||
|
||||
impl<'a> DeclarationIterator<'a> {
|
||||
|
@ -128,8 +131,9 @@ impl<'a> DeclarationIterator<'a> {
|
|||
let mut iter = Self {
|
||||
guards,
|
||||
current_rule_node: Some(rule_node),
|
||||
origin: Origin::Author,
|
||||
origin: Origin::UserAgent,
|
||||
importance: Importance::Normal,
|
||||
priority: CascadePriority::new(CascadeLevel::UANormal, LayerOrder::root()),
|
||||
declarations: DeclarationImportanceIterator::default(),
|
||||
restriction,
|
||||
};
|
||||
|
@ -138,10 +142,11 @@ impl<'a> DeclarationIterator<'a> {
|
|||
}
|
||||
|
||||
fn update_for_node(&mut self, node: &'a StrongRuleNode) {
|
||||
let origin = node.cascade_level().origin();
|
||||
self.origin = origin;
|
||||
self.importance = node.importance();
|
||||
let guard = match origin {
|
||||
self.priority = node.cascade_priority();
|
||||
let level = self.priority.cascade_level();
|
||||
self.origin = level.origin();
|
||||
self.importance = level.importance();
|
||||
let guard = match self.origin {
|
||||
Origin::Author => self.guards.author,
|
||||
Origin::User | Origin::UserAgent => self.guards.ua_or_user,
|
||||
};
|
||||
|
@ -153,7 +158,7 @@ impl<'a> DeclarationIterator<'a> {
|
|||
}
|
||||
|
||||
impl<'a> Iterator for DeclarationIterator<'a> {
|
||||
type Item = (&'a PropertyDeclaration, Origin);
|
||||
type Item = (&'a PropertyDeclaration, CascadePriority);
|
||||
|
||||
#[inline]
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
|
@ -163,20 +168,19 @@ impl<'a> Iterator for DeclarationIterator<'a> {
|
|||
continue;
|
||||
}
|
||||
|
||||
let origin = self.origin;
|
||||
if let Some(restriction) = self.restriction {
|
||||
// decl.id() is either a longhand or a custom
|
||||
// property. Custom properties are always allowed, but
|
||||
// longhands are only allowed if they have our
|
||||
// restriction flag set.
|
||||
if let PropertyDeclarationId::Longhand(id) = decl.id() {
|
||||
if !id.flags().contains(restriction) && origin != Origin::UserAgent {
|
||||
if !id.flags().contains(restriction) && self.origin != Origin::UserAgent {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return Some((decl, origin));
|
||||
return Some((decl, self.priority));
|
||||
}
|
||||
|
||||
let next_node = self.current_rule_node.take()?.parent()?;
|
||||
|
@ -259,7 +263,7 @@ pub fn apply_declarations<'a, E, I>(
|
|||
) -> Arc<ComputedValues>
|
||||
where
|
||||
E: TElement,
|
||||
I: Iterator<Item = (&'a PropertyDeclaration, Origin)>,
|
||||
I: Iterator<Item = (&'a PropertyDeclaration, CascadePriority)>,
|
||||
{
|
||||
debug_assert!(layout_parent_style.is_none() || parent_style.is_some());
|
||||
debug_assert_eq!(
|
||||
|
@ -278,14 +282,14 @@ where
|
|||
|
||||
let inherited_style = parent_style.unwrap_or(device.default_computed_values());
|
||||
|
||||
let mut declarations = SmallVec::<[(&_, Origin); 32]>::new();
|
||||
let mut declarations = SmallVec::<[(&_, CascadePriority); 32]>::new();
|
||||
let custom_properties = {
|
||||
let mut builder = CustomPropertiesBuilder::new(inherited_style.custom_properties(), device);
|
||||
|
||||
for (declaration, origin) in iter {
|
||||
declarations.push((declaration, origin));
|
||||
for (declaration, priority) in iter {
|
||||
declarations.push((declaration, priority));
|
||||
if let PropertyDeclaration::Custom(ref declaration) = *declaration {
|
||||
builder.cascade(declaration, origin);
|
||||
builder.cascade(declaration, priority);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -424,17 +428,18 @@ fn tweak_when_ignoring_colors(
|
|||
// otherwise, this is needed to preserve semi-transparent
|
||||
// backgrounds.
|
||||
//
|
||||
// FIXME(emilio, bug 1666059): We revert for alpha == 0, but maybe
|
||||
// should consider not doing that even if it causes some issues like
|
||||
// bug 1625036, or finding a performant way to preserve the original
|
||||
// widget background color's rgb channels but not alpha...
|
||||
// NOTE(emilio): We revert even for alpha == 0. Not doing so would
|
||||
// be a bit special casey, even though it causes issues like
|
||||
// bug 1625036. The reasoning is that the conditions that trigger
|
||||
// that (having mismatched widget and default backgrounds) are both
|
||||
// uncommon, and broken in other applications as well, and not
|
||||
// honoring transparent makes stuff uglier or break unconditionally
|
||||
// (bug 1666059, bug 1755713).
|
||||
let alpha = alpha_channel(color, context);
|
||||
if alpha != 0 {
|
||||
let mut color = context.builder.device.default_background_color_for_forced_colors();
|
||||
color.alpha = alpha;
|
||||
declarations_to_apply_unless_overriden
|
||||
.push(PropertyDeclaration::BackgroundColor(color.into()))
|
||||
}
|
||||
let mut color = context.builder.device.default_background_color();
|
||||
color.alpha = alpha;
|
||||
declarations_to_apply_unless_overriden
|
||||
.push(PropertyDeclaration::BackgroundColor(color.into()))
|
||||
},
|
||||
PropertyDeclaration::Color(ref color) => {
|
||||
// We honor color: transparent and system colors.
|
||||
|
@ -448,7 +453,7 @@ fn tweak_when_ignoring_colors(
|
|||
// override this with a non-transparent color, then override it with
|
||||
// the default color. Otherwise just let it inherit through.
|
||||
if context.builder.get_parent_inherited_text().clone_color().alpha == 0 {
|
||||
let color = context.builder.device.default_color_for_forced_colors();
|
||||
let color = context.builder.device.default_color();
|
||||
declarations_to_apply_unless_overriden.push(PropertyDeclaration::Color(
|
||||
specified::ColorPropertyValue(color.into()),
|
||||
))
|
||||
|
@ -465,17 +470,20 @@ fn tweak_when_ignoring_colors(
|
|||
}
|
||||
},
|
||||
_ => {
|
||||
// We honor transparent and system colors more generally for all
|
||||
// colors.
|
||||
// We honor system colors more generally for all colors.
|
||||
//
|
||||
// NOTE(emilio): This doesn't handle caret-color and
|
||||
// accent-color because those use a slightly different syntax
|
||||
// (<color> | auto for example). That's probably fine though, as
|
||||
// using a system color for caret-color doesn't make sense (using
|
||||
// currentColor is fine), and we ignore accent-color in
|
||||
// high-contrast-mode anyways.
|
||||
// We used to honor transparent but that causes accessibility
|
||||
// regressions like bug 1740924.
|
||||
//
|
||||
// NOTE(emilio): This doesn't handle caret-color and accent-color
|
||||
// because those use a slightly different syntax (<color> | auto for
|
||||
// example).
|
||||
//
|
||||
// That's probably fine though, as using a system color for
|
||||
// caret-color doesn't make sense (using currentColor is fine), and
|
||||
// we ignore accent-color in high-contrast-mode anyways.
|
||||
if let Some(color) = declaration.color_value() {
|
||||
if color.is_system() || alpha_channel(color, context) == 0 {
|
||||
if color.is_system() {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -491,7 +499,8 @@ struct Cascade<'a, 'b: 'a> {
|
|||
cascade_mode: CascadeMode<'a>,
|
||||
seen: LonghandIdSet,
|
||||
author_specified: LonghandIdSet,
|
||||
reverted: PerOrigin<LonghandIdSet>,
|
||||
reverted_set: LonghandIdSet,
|
||||
reverted: FxHashMap<LonghandId, (CascadePriority, bool)>,
|
||||
}
|
||||
|
||||
impl<'a, 'b: 'a> Cascade<'a, 'b> {
|
||||
|
@ -501,6 +510,7 @@ impl<'a, 'b: 'a> Cascade<'a, 'b> {
|
|||
cascade_mode,
|
||||
seen: LonghandIdSet::default(),
|
||||
author_specified: LonghandIdSet::default(),
|
||||
reverted_set: Default::default(),
|
||||
reverted: Default::default(),
|
||||
}
|
||||
}
|
||||
|
@ -572,7 +582,7 @@ impl<'a, 'b: 'a> Cascade<'a, 'b> {
|
|||
mut shorthand_cache: &mut ShorthandsWithPropertyReferencesCache,
|
||||
) where
|
||||
Phase: CascadePhase,
|
||||
I: Iterator<Item = (&'decls PropertyDeclaration, Origin)>,
|
||||
I: Iterator<Item = (&'decls PropertyDeclaration, CascadePriority)>,
|
||||
{
|
||||
let apply_reset = apply_reset == ApplyResetProperties::Yes;
|
||||
|
||||
|
@ -586,7 +596,9 @@ impl<'a, 'b: 'a> Cascade<'a, 'b> {
|
|||
let ignore_colors = !self.context.builder.device.use_document_colors();
|
||||
let mut declarations_to_apply_unless_overriden = DeclarationsToApplyUnlessOverriden::new();
|
||||
|
||||
for (declaration, origin) in declarations {
|
||||
for (declaration, priority) in declarations {
|
||||
let origin = priority.cascade_level().origin();
|
||||
|
||||
let declaration_id = declaration.id();
|
||||
let longhand_id = match declaration_id {
|
||||
PropertyDeclarationId::Longhand(id) => id,
|
||||
|
@ -613,12 +625,12 @@ impl<'a, 'b: 'a> Cascade<'a, 'b> {
|
|||
continue;
|
||||
}
|
||||
|
||||
if self
|
||||
.reverted
|
||||
.borrow_for_origin(&origin)
|
||||
.contains(physical_longhand_id)
|
||||
{
|
||||
continue;
|
||||
if self.reverted_set.contains(physical_longhand_id) {
|
||||
if let Some(&(reverted_priority, is_origin_revert)) = self.reverted.get(&physical_longhand_id) {
|
||||
if !reverted_priority.allows_when_reverted(&priority, is_origin_revert) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Only a few properties are allowed to depend on the visited state
|
||||
|
@ -650,32 +662,31 @@ impl<'a, 'b: 'a> Cascade<'a, 'b> {
|
|||
);
|
||||
}
|
||||
|
||||
let css_wide_keyword = declaration.get_css_wide_keyword();
|
||||
if let Some(CSSWideKeyword::Revert) = css_wide_keyword {
|
||||
// We intentionally don't want to insert it into `self.seen`,
|
||||
// `reverted` takes care of rejecting other declarations as
|
||||
// needed.
|
||||
for origin in origin.following_including() {
|
||||
self.reverted
|
||||
.borrow_mut_for_origin(&origin)
|
||||
.insert(physical_longhand_id);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
let is_unset = match declaration.get_css_wide_keyword() {
|
||||
Some(keyword) => match keyword {
|
||||
CSSWideKeyword::RevertLayer |
|
||||
CSSWideKeyword::Revert => {
|
||||
let origin_revert = keyword == CSSWideKeyword::Revert;
|
||||
// We intentionally don't want to insert it into
|
||||
// `self.seen`, `reverted` takes care of rejecting other
|
||||
// declarations as needed.
|
||||
self.reverted_set.insert(physical_longhand_id);
|
||||
self.reverted.insert(physical_longhand_id, (priority, origin_revert));
|
||||
continue;
|
||||
},
|
||||
CSSWideKeyword::Unset => true,
|
||||
CSSWideKeyword::Inherit => inherited,
|
||||
CSSWideKeyword::Initial => !inherited,
|
||||
},
|
||||
None => false,
|
||||
};
|
||||
|
||||
self.seen.insert(physical_longhand_id);
|
||||
if origin == Origin::Author {
|
||||
self.author_specified.insert(physical_longhand_id);
|
||||
}
|
||||
|
||||
let unset = css_wide_keyword.map_or(false, |css_wide_keyword| match css_wide_keyword {
|
||||
CSSWideKeyword::Unset => true,
|
||||
CSSWideKeyword::Inherit => inherited,
|
||||
CSSWideKeyword::Initial => !inherited,
|
||||
CSSWideKeyword::Revert => unreachable!(),
|
||||
});
|
||||
|
||||
if unset {
|
||||
if is_unset {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -871,66 +882,83 @@ impl<'a, 'b: 'a> Cascade<'a, 'b> {
|
|||
true
|
||||
}
|
||||
|
||||
/// The default font type (which is stored in FontFamilyList's
|
||||
/// `mDefaultFontType`) depends on the current lang group and generic font
|
||||
/// family, so we may need to recompute it if or the family changed.
|
||||
///
|
||||
/// Also, we prioritize non-document fonts here if we need to (see the pref
|
||||
/// `browser.display.use_document_fonts`).
|
||||
/// The initial font depends on the current lang group so we may need to
|
||||
/// recompute it if the language changed.
|
||||
#[inline]
|
||||
#[cfg(feature = "gecko")]
|
||||
fn recompute_default_font_family_type_if_needed(&mut self) {
|
||||
fn recompute_initial_font_family_if_needed(&mut self) {
|
||||
use crate::gecko_bindings::bindings;
|
||||
use crate::values::computed::font::GenericFontFamily;
|
||||
use crate::values::computed::font::FontFamily;
|
||||
|
||||
if !self.seen.contains(LonghandId::XLang) && !self.seen.contains(LonghandId::FontFamily) {
|
||||
if !self.seen.contains(LonghandId::XLang) {
|
||||
return;
|
||||
}
|
||||
|
||||
let use_document_fonts = static_prefs::pref!("browser.display.use_document_fonts") != 0;
|
||||
let builder = &mut self.context.builder;
|
||||
let (default_font_type, prioritize_user_fonts) = {
|
||||
let default_font_type = {
|
||||
let font = builder.get_font().gecko();
|
||||
|
||||
// System fonts are all right, and should have the default font type
|
||||
// set to none already, so bail out early.
|
||||
if font.mFont.family.is_system_font {
|
||||
debug_assert_eq!(
|
||||
font.mFont.family.families.fallback,
|
||||
GenericFontFamily::None
|
||||
);
|
||||
if !font.mFont.family.is_initial {
|
||||
return;
|
||||
}
|
||||
|
||||
let generic = font.mFont.family.families.single_generic().unwrap_or(GenericFontFamily::None);
|
||||
let default_font_type = unsafe {
|
||||
bindings::Gecko_nsStyleFont_ComputeDefaultFontType(
|
||||
bindings::Gecko_nsStyleFont_ComputeFallbackFontTypeForLanguage(
|
||||
builder.device.document(),
|
||||
generic,
|
||||
font.mLanguage.mRawPtr,
|
||||
)
|
||||
};
|
||||
|
||||
// We prioritize user fonts over document fonts if the pref is set,
|
||||
// and we don't have a generic family already (or we're using
|
||||
// cursive or fantasy, since they're ignored, see bug 789788), and
|
||||
// we have a generic family to actually replace it with.
|
||||
let prioritize_user_fonts = !use_document_fonts &&
|
||||
default_font_type != GenericFontFamily::None &&
|
||||
!generic.valid_for_user_font_prioritization();
|
||||
|
||||
if !prioritize_user_fonts && default_font_type == font.mFont.family.families.fallback {
|
||||
// Nothing to do.
|
||||
let initial_generic = font.mFont.family.families.single_generic();
|
||||
debug_assert!(initial_generic.is_some(), "Initial font should be just one generic font");
|
||||
if initial_generic == Some(default_font_type) {
|
||||
return;
|
||||
}
|
||||
(default_font_type, prioritize_user_fonts)
|
||||
|
||||
default_font_type
|
||||
};
|
||||
|
||||
let font = builder.mutate_font().gecko_mut();
|
||||
font.mFont.family.families.fallback = default_font_type;
|
||||
if prioritize_user_fonts {
|
||||
font.mFont.family.families.prioritize_first_generic_or_prepend(default_font_type);
|
||||
// NOTE: Leaves is_initial untouched.
|
||||
font.mFont.family.families = FontFamily::generic(default_font_type).families.clone();
|
||||
}
|
||||
|
||||
/// Prioritize user fonts if needed by pref.
|
||||
#[inline]
|
||||
#[cfg(feature = "gecko")]
|
||||
fn prioritize_user_fonts_if_needed(&mut self) {
|
||||
use crate::gecko_bindings::bindings;
|
||||
|
||||
if !self.seen.contains(LonghandId::FontFamily) {
|
||||
return;
|
||||
}
|
||||
|
||||
if static_prefs::pref!("browser.display.use_document_fonts") != 0 {
|
||||
return;
|
||||
}
|
||||
|
||||
let builder = &mut self.context.builder;
|
||||
let default_font_type = {
|
||||
let font = builder.get_font().gecko();
|
||||
|
||||
if font.mFont.family.is_system_font {
|
||||
return;
|
||||
}
|
||||
|
||||
if !font.mFont.family.families.needs_user_font_prioritization() {
|
||||
return;
|
||||
}
|
||||
|
||||
unsafe {
|
||||
bindings::Gecko_nsStyleFont_ComputeFallbackFontTypeForLanguage(
|
||||
builder.device.document(),
|
||||
font.mLanguage.mRawPtr,
|
||||
)
|
||||
}
|
||||
};
|
||||
|
||||
let font = builder.mutate_font().gecko_mut();
|
||||
font.mFont.family.families.prioritize_first_generic_or_prepend(default_font_type);
|
||||
}
|
||||
|
||||
/// Some keyword sizes depend on the font family and language.
|
||||
|
@ -1104,7 +1132,8 @@ impl<'a, 'b: 'a> Cascade<'a, 'b> {
|
|||
#[cfg(feature = "gecko")]
|
||||
{
|
||||
self.unzoom_fonts_if_needed();
|
||||
self.recompute_default_font_family_type_if_needed();
|
||||
self.recompute_initial_font_family_if_needed();
|
||||
self.prioritize_user_fonts_if_needed();
|
||||
self.recompute_keyword_font_size_if_needed();
|
||||
self.handle_mathml_scriptlevel_if_needed();
|
||||
self.constrain_font_size_if_needed()
|
||||
|
|
|
@ -38,7 +38,6 @@ COUNTED_UNKNOWN_PROPERTIES = [
|
|||
"-webkit-writing-mode",
|
||||
"baseline-shift",
|
||||
"-webkit-hyphenate-character",
|
||||
"page",
|
||||
"-webkit-highlight",
|
||||
"background-repeat-x",
|
||||
"-webkit-padding-end",
|
||||
|
@ -121,5 +120,4 @@ COUNTED_UNKNOWN_PROPERTIES = [
|
|||
"-webkit-columns",
|
||||
"-webkit-column-rule-color",
|
||||
"-webkit-shape-margin",
|
||||
"content-visibility",
|
||||
]
|
||||
|
|
|
@ -462,6 +462,7 @@ class Longhand(Property):
|
|||
"Clear",
|
||||
"ColumnCount",
|
||||
"Contain",
|
||||
"ContentVisibility",
|
||||
"Display",
|
||||
"FillRule",
|
||||
"Float",
|
||||
|
@ -486,7 +487,6 @@ class Longhand(Property):
|
|||
"MasonryAutoFlow",
|
||||
"MozForceBrokenImageIcon",
|
||||
"text::MozControlCharacterVisibility",
|
||||
"MozListReversed",
|
||||
"MathDepth",
|
||||
"MozScriptMinSize",
|
||||
"MozScriptSizeMultiplier",
|
||||
|
@ -502,10 +502,12 @@ class Longhand(Property):
|
|||
"OverscrollBehavior",
|
||||
"Percentage",
|
||||
"PositiveIntegerOrNone",
|
||||
"PrintColorAdjust",
|
||||
"Resize",
|
||||
"RubyPosition",
|
||||
"SVGOpacity",
|
||||
"SVGPaintOrder",
|
||||
"ScrollbarGutter",
|
||||
"ScrollSnapAlign",
|
||||
"ScrollSnapAxis",
|
||||
"ScrollSnapStrictness",
|
||||
|
@ -888,6 +890,7 @@ class PropertyRestrictions:
|
|||
"unicode-bidi",
|
||||
"direction",
|
||||
"content",
|
||||
"line-height",
|
||||
"-moz-osx-font-smoothing",
|
||||
]
|
||||
+ PropertyRestrictions.spec(data, "css-fonts")
|
||||
|
|
|
@ -7,15 +7,17 @@
|
|||
#![deny(missing_docs)]
|
||||
|
||||
use super::*;
|
||||
use crate::applicable_declarations::CascadePriority;
|
||||
use crate::context::QuirksMode;
|
||||
use crate::custom_properties::CustomPropertiesBuilder;
|
||||
use crate::error_reporting::{ContextualParseError, ParseErrorReporter};
|
||||
use crate::parser::ParserContext;
|
||||
use crate::properties::animated_properties::{AnimationValue, AnimationValueMap};
|
||||
use crate::rule_tree::CascadeLevel;
|
||||
use crate::selector_parser::SelectorImpl;
|
||||
use crate::shared_lock::Locked;
|
||||
use crate::str::{CssString, CssStringWriter};
|
||||
use crate::stylesheets::{CssRuleType, Origin, UrlExtraData};
|
||||
use crate::stylesheets::{CssRuleType, Origin, UrlExtraData, layer_rule::LayerOrder};
|
||||
use crate::values::computed::Context;
|
||||
use cssparser::{parse_important, CowRcStr, DeclarationListParser, ParserInput};
|
||||
use cssparser::{AtRuleParser, DeclarationParser, Delimiter, ParseErrorKind, Parser};
|
||||
|
@ -898,7 +900,7 @@ impl PropertyDeclarationBlock {
|
|||
|
||||
for declaration in self.normal_declaration_iter() {
|
||||
if let PropertyDeclaration::Custom(ref declaration) = *declaration {
|
||||
builder.cascade(declaration, Origin::Author);
|
||||
builder.cascade(declaration, CascadePriority::new(CascadeLevel::same_tree_author_normal(), LayerOrder::root()));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -476,6 +476,7 @@
|
|||
context.builder.inherit_${property.ident}();
|
||||
% endif
|
||||
}
|
||||
CSSWideKeyword::RevertLayer |
|
||||
CSSWideKeyword::Revert => unreachable!("Should never get here"),
|
||||
}
|
||||
return;
|
||||
|
|
|
@ -19,7 +19,7 @@ use servo_arc::Arc;
|
|||
use smallvec::SmallVec;
|
||||
use std::ptr;
|
||||
use std::mem;
|
||||
use crate::hash::FxHashMap;
|
||||
use fxhash::FxHashMap;
|
||||
use super::ComputedValues;
|
||||
use crate::values::animated::{Animate, Procedure, ToAnimatedValue, ToAnimatedZero};
|
||||
use crate::values::animated::effects::AnimatedFilter;
|
||||
|
@ -311,9 +311,9 @@ impl AnimationValue {
|
|||
% for prop in data.longhands:
|
||||
% if prop.animatable:
|
||||
LonghandId::${prop.camel_case} => {
|
||||
// FIXME(emilio, bug 1533327): I think
|
||||
// CSSWideKeyword::Revert handling is not fine here, but
|
||||
// what to do instead?
|
||||
// FIXME(emilio, bug 1533327): I think revert (and
|
||||
// revert-layer) handling is not fine here, but what to
|
||||
// do instead?
|
||||
//
|
||||
// Seems we'd need the computed value as if it was
|
||||
// revert, somehow. Treating it as `unset` seems fine
|
||||
|
@ -321,6 +321,7 @@ impl AnimationValue {
|
|||
let style_struct = match declaration.keyword {
|
||||
% if not prop.style_struct.inherited:
|
||||
CSSWideKeyword::Revert |
|
||||
CSSWideKeyword::RevertLayer |
|
||||
CSSWideKeyword::Unset |
|
||||
% endif
|
||||
CSSWideKeyword::Initial => {
|
||||
|
@ -328,6 +329,7 @@ impl AnimationValue {
|
|||
},
|
||||
% if prop.style_struct.inherited:
|
||||
CSSWideKeyword::Revert |
|
||||
CSSWideKeyword::RevertLayer |
|
||||
CSSWideKeyword::Unset |
|
||||
% endif
|
||||
CSSWideKeyword::Inherit => {
|
||||
|
|
|
@ -111,5 +111,6 @@ ${helpers.single_keyword(
|
|||
vector=True,
|
||||
engines="gecko",
|
||||
animation_value_type="discrete",
|
||||
gecko_inexhaustive=True,
|
||||
spec="https://drafts.fxtf.org/compositing/#background-blend-mode",
|
||||
)}
|
||||
|
|
|
@ -613,6 +613,16 @@ ${helpers.predefined_type(
|
|||
spec="https://drafts.csswg.org/css-contain/#contain-property",
|
||||
)}
|
||||
|
||||
${helpers.predefined_type(
|
||||
"content-visibility",
|
||||
"ContentVisibility",
|
||||
"computed::ContentVisibility::Visible",
|
||||
engines="gecko",
|
||||
spec="https://drafts.csswg.org/css-contain/#content-visibility",
|
||||
gecko_pref="layout.css.content-visibility.enabled",
|
||||
animation_value_type="none",
|
||||
)}
|
||||
|
||||
${helpers.predefined_type(
|
||||
"appearance",
|
||||
"Appearance",
|
||||
|
@ -689,7 +699,6 @@ ${helpers.predefined_type(
|
|||
"TouchAction",
|
||||
"computed::TouchAction::auto()",
|
||||
engines="gecko",
|
||||
gecko_pref="layout.css.touch_action.enabled",
|
||||
animation_value_type="discrete",
|
||||
spec="https://compat.spec.whatwg.org/#touch-action",
|
||||
)}
|
||||
|
@ -706,3 +715,13 @@ ${helpers.predefined_type(
|
|||
animation_value_type="Integer",
|
||||
spec="https://drafts.csswg.org/css-overflow-3/#line-clamp",
|
||||
)}
|
||||
|
||||
${helpers.predefined_type(
|
||||
"scrollbar-gutter",
|
||||
"ScrollbarGutter",
|
||||
"computed::ScrollbarGutter::AUTO",
|
||||
engines="gecko",
|
||||
gecko_pref="layout.css.scrollbar-gutter.enabled",
|
||||
animation_value_type="discrete",
|
||||
spec="https://drafts.csswg.org/css-overflow-3/#scrollbar-gutter-property",
|
||||
)}
|
||||
|
|
|
@ -29,7 +29,7 @@ ${helpers.predefined_type(
|
|||
|
||||
${helpers.predefined_type(
|
||||
"counter-reset",
|
||||
"CounterSetOrReset",
|
||||
"CounterReset",
|
||||
engines="gecko servo-2013",
|
||||
initial_value="Default::default()",
|
||||
animation_value_type="discrete",
|
||||
|
@ -39,7 +39,7 @@ ${helpers.predefined_type(
|
|||
|
||||
${helpers.predefined_type(
|
||||
"counter-set",
|
||||
"CounterSetOrReset",
|
||||
"CounterSet",
|
||||
engines="gecko",
|
||||
initial_value="Default::default()",
|
||||
animation_value_type="discrete",
|
||||
|
|
|
@ -78,7 +78,7 @@ ${helpers.single_keyword(
|
|||
"mix-blend-mode",
|
||||
"""normal multiply screen overlay darken lighten color-dodge
|
||||
color-burn hard-light soft-light difference exclusion hue
|
||||
saturation color luminosity""",
|
||||
saturation color luminosity""" + ("plus-lighter" if engine == 'gecko' else ""),
|
||||
engines="gecko servo-2013 servo-2020",
|
||||
gecko_enum_prefix="StyleBlend",
|
||||
animation_value_type="discrete",
|
||||
|
|
|
@ -260,7 +260,7 @@ ${helpers.single_keyword(
|
|||
bold-sans-serif sans-serif-italic sans-serif-bold-italic
|
||||
monospace initial tailed looped stretched""",
|
||||
engines="gecko",
|
||||
gecko_constant_prefix="NS_MATHML_MATHVARIANT",
|
||||
gecko_enum_prefix="StyleMathVariant",
|
||||
gecko_ffi_name="mMathVariant",
|
||||
spec="Internal (not web-exposed)",
|
||||
animation_value_type="none",
|
||||
|
|
|
@ -56,15 +56,14 @@ ${helpers.single_keyword(
|
|||
spec="https://drafts.csswg.org/css-writing-modes/#propdef-text-orientation",
|
||||
)}
|
||||
|
||||
// CSS Color Module Level 4
|
||||
// https://drafts.csswg.org/css-color/
|
||||
${helpers.single_keyword(
|
||||
"color-adjust",
|
||||
"economy exact",
|
||||
${helpers.predefined_type(
|
||||
"print-color-adjust",
|
||||
"PrintColorAdjust",
|
||||
"computed::PrintColorAdjust::Economy",
|
||||
engines="gecko",
|
||||
gecko_enum_prefix="StyleColorAdjust",
|
||||
aliases="color-adjust",
|
||||
spec="https://drafts.csswg.org/css-color-adjust/#print-color-adjust",
|
||||
animation_value_type="discrete",
|
||||
spec="https://drafts.csswg.org/css-color/#propdef-color-adjust",
|
||||
)}
|
||||
|
||||
// According to to CSS-IMAGES-3, `optimizespeed` and `optimizequality` are synonyms for `auto`
|
||||
|
|
|
@ -373,3 +373,15 @@ ${helpers.predefined_type(
|
|||
animation_value_type="discrete",
|
||||
spec="https://drafts.csswg.org/css-text-decor-4/#text-decoration-skip-ink-property",
|
||||
)}
|
||||
|
||||
// hyphenation character
|
||||
${helpers.predefined_type(
|
||||
"hyphenate-character",
|
||||
"HyphenateCharacter",
|
||||
"computed::HyphenateCharacter::Auto",
|
||||
engines="gecko",
|
||||
gecko_pref="layout.css.hyphenate-character.enabled",
|
||||
has_effect_on_gecko_scrollbars=False,
|
||||
animation_value_type="discrete",
|
||||
spec="https://www.w3.org/TR/css-text-4/#hyphenate-character",
|
||||
)}
|
||||
|
|
|
@ -84,14 +84,3 @@ ${helpers.predefined_type(
|
|||
boxed=True,
|
||||
spec="Nonstandard (https://developer.mozilla.org/en-US/docs/Web/CSS/-moz-image-region)",
|
||||
)}
|
||||
|
||||
${helpers.predefined_type(
|
||||
"-moz-list-reversed",
|
||||
"MozListReversed",
|
||||
"computed::MozListReversed::False",
|
||||
engines="gecko",
|
||||
animation_value_type="discrete",
|
||||
enabled_in="ua",
|
||||
spec="Internal implementation detail for <ol reversed>",
|
||||
servo_restyle_damage="rebuild_and_reflow",
|
||||
)}
|
||||
|
|
|
@ -19,3 +19,13 @@ ${helpers.predefined_type(
|
|||
animation_value_type="none",
|
||||
rule_types_allowed=PAGE_RULE,
|
||||
)}
|
||||
|
||||
${helpers.predefined_type(
|
||||
"page",
|
||||
"PageName",
|
||||
"computed::PageName::auto()",
|
||||
engines="gecko",
|
||||
gecko_pref="layout.css.named-pages.enabled",
|
||||
spec="https://drafts.csswg.org/css-page-3/#using-named-pages",
|
||||
animation_value_type="discrete",
|
||||
)}
|
||||
|
|
|
@ -29,7 +29,7 @@ use crate::context::QuirksMode;
|
|||
use crate::logical_geometry::WritingMode;
|
||||
use malloc_size_of::{MallocSizeOf, MallocSizeOfOps};
|
||||
use crate::computed_value_flags::*;
|
||||
use crate::hash::FxHashMap;
|
||||
use fxhash::FxHashMap;
|
||||
use crate::media_queries::Device;
|
||||
use crate::parser::ParserContext;
|
||||
use crate::selector_parser::PseudoElement;
|
||||
|
@ -1051,6 +1051,8 @@ pub enum CSSWideKeyword {
|
|||
Unset,
|
||||
/// The `revert` keyword.
|
||||
Revert,
|
||||
/// The `revert-layer` keyword.
|
||||
RevertLayer,
|
||||
}
|
||||
|
||||
impl CSSWideKeyword {
|
||||
|
@ -1060,22 +1062,36 @@ impl CSSWideKeyword {
|
|||
CSSWideKeyword::Inherit => "inherit",
|
||||
CSSWideKeyword::Unset => "unset",
|
||||
CSSWideKeyword::Revert => "revert",
|
||||
CSSWideKeyword::RevertLayer => "revert-layer",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn cascade_layes_enabled() -> bool {
|
||||
#[cfg(feature = "gecko")]
|
||||
return static_prefs::pref!("layout.css.cascade-layers.enabled");
|
||||
#[cfg(feature = "servo")]
|
||||
return false;
|
||||
}
|
||||
|
||||
impl CSSWideKeyword {
|
||||
/// Parses a CSS wide keyword from a CSS identifier.
|
||||
pub fn from_ident(ident: &str) -> Result<Self, ()> {
|
||||
Ok(match_ignore_ascii_case! { ident,
|
||||
"initial" => CSSWideKeyword::Initial,
|
||||
"inherit" => CSSWideKeyword::Inherit,
|
||||
"unset" => CSSWideKeyword::Unset,
|
||||
"revert" => CSSWideKeyword::Revert,
|
||||
"revert-layer" if cascade_layes_enabled() => CSSWideKeyword::RevertLayer,
|
||||
_ => return Err(()),
|
||||
})
|
||||
}
|
||||
|
||||
fn parse(input: &mut Parser) -> Result<Self, ()> {
|
||||
let keyword = {
|
||||
let ident = input.expect_ident().map_err(|_| ())?;
|
||||
match_ignore_ascii_case! { ident,
|
||||
// If modifying this set of keyword, also update values::CustomIdent::from_ident
|
||||
"initial" => CSSWideKeyword::Initial,
|
||||
"inherit" => CSSWideKeyword::Inherit,
|
||||
"unset" => CSSWideKeyword::Unset,
|
||||
"revert" => CSSWideKeyword::Revert,
|
||||
_ => return Err(()),
|
||||
}
|
||||
Self::from_ident(ident)?
|
||||
};
|
||||
input.expect_exhausted().map_err(|_| ())?;
|
||||
Ok(keyword)
|
||||
|
|
|
@ -11,7 +11,7 @@ use crate::rule_tree::{CascadeLevel, ShadowCascadeOrder};
|
|||
use crate::selector_map::SelectorMap;
|
||||
use crate::selector_parser::PseudoElement;
|
||||
use crate::shared_lock::Locked;
|
||||
use crate::stylesheets::Origin;
|
||||
use crate::stylesheets::{layer_rule::LayerOrder, Origin};
|
||||
use crate::stylist::{AuthorStylesEnabled, CascadeData, Rule, RuleInclusion, Stylist};
|
||||
use selectors::matching::{ElementSelectorFlags, MatchingContext, MatchingMode};
|
||||
use servo_arc::ArcBorrow;
|
||||
|
@ -147,8 +147,9 @@ where
|
|||
self.context.current_host = host.map(|e| e.opaque());
|
||||
f(self);
|
||||
if start != self.rules.len() {
|
||||
self.rules[start..]
|
||||
.sort_unstable_by_key(|block| (block.layer_order, block.specificity, block.source_order()));
|
||||
self.rules[start..].sort_unstable_by_key(|block| {
|
||||
(block.layer_order(), block.specificity, block.source_order())
|
||||
});
|
||||
}
|
||||
self.context.current_host = old_host;
|
||||
self.in_sort_scope = false;
|
||||
|
@ -214,7 +215,12 @@ where
|
|||
}
|
||||
|
||||
#[inline]
|
||||
fn collect_rules_in_list(&mut self, part_rules: &[Rule], cascade_level: CascadeLevel, cascade_data: &CascadeData) {
|
||||
fn collect_rules_in_list(
|
||||
&mut self,
|
||||
part_rules: &[Rule],
|
||||
cascade_level: CascadeLevel,
|
||||
cascade_data: &CascadeData,
|
||||
) {
|
||||
debug_assert!(self.in_sort_scope, "Rules gotta be sorted");
|
||||
SelectorMap::get_matching_rules(
|
||||
self.element,
|
||||
|
@ -228,7 +234,12 @@ where
|
|||
}
|
||||
|
||||
#[inline]
|
||||
fn collect_rules_in_map(&mut self, map: &SelectorMap<Rule>, cascade_level: CascadeLevel, cascade_data: &CascadeData) {
|
||||
fn collect_rules_in_map(
|
||||
&mut self,
|
||||
map: &SelectorMap<Rule>,
|
||||
cascade_level: CascadeLevel,
|
||||
cascade_data: &CascadeData,
|
||||
) {
|
||||
debug_assert!(self.in_sort_scope, "Rules gotta be sorted");
|
||||
map.get_all_matching_rules(
|
||||
self.element,
|
||||
|
@ -390,10 +401,10 @@ where
|
|||
let outer_shadow = inner_shadow_host.containing_shadow();
|
||||
let cascade_data = match outer_shadow {
|
||||
Some(shadow) => shadow.style_data(),
|
||||
None => Some(self
|
||||
.stylist
|
||||
.cascade_data()
|
||||
.borrow_for_origin(Origin::Author)
|
||||
None => Some(
|
||||
self.stylist
|
||||
.cascade_data()
|
||||
.borrow_for_origin(Origin::Author),
|
||||
),
|
||||
};
|
||||
|
||||
|
@ -406,7 +417,11 @@ where
|
|||
self.in_tree(containing_host, |collector| {
|
||||
for p in &parts {
|
||||
if let Some(part_rules) = part_rules.get(&p.0) {
|
||||
collector.collect_rules_in_list(part_rules, cascade_level, cascade_data);
|
||||
collector.collect_rules_in_list(
|
||||
part_rules,
|
||||
cascade_level,
|
||||
cascade_data,
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@ -435,6 +450,7 @@ where
|
|||
.push(ApplicableDeclarationBlock::from_declarations(
|
||||
sa.clone_arc(),
|
||||
CascadeLevel::same_tree_author_normal(),
|
||||
LayerOrder::style_attribute(),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
@ -445,6 +461,7 @@ where
|
|||
.push(ApplicableDeclarationBlock::from_declarations(
|
||||
so.clone_arc(),
|
||||
CascadeLevel::SMILOverride,
|
||||
LayerOrder::root(),
|
||||
));
|
||||
}
|
||||
|
||||
|
@ -456,6 +473,7 @@ where
|
|||
.push(ApplicableDeclarationBlock::from_declarations(
|
||||
anim,
|
||||
CascadeLevel::Animations,
|
||||
LayerOrder::root(),
|
||||
));
|
||||
}
|
||||
|
||||
|
@ -466,6 +484,7 @@ where
|
|||
.push(ApplicableDeclarationBlock::from_declarations(
|
||||
anim,
|
||||
CascadeLevel::Transitions,
|
||||
LayerOrder::root(),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,8 +4,9 @@
|
|||
|
||||
#![allow(unsafe_code)]
|
||||
|
||||
use crate::properties::Importance;
|
||||
use crate::applicable_declarations::CascadePriority;
|
||||
use crate::shared_lock::StylesheetGuards;
|
||||
use crate::stylesheets::layer_rule::LayerOrder;
|
||||
use malloc_size_of::{MallocShallowSizeOf, MallocSizeOf, MallocSizeOfOps};
|
||||
use parking_lot::RwLock;
|
||||
use smallvec::SmallVec;
|
||||
|
@ -66,7 +67,7 @@ impl MallocSizeOf for RuleTree {
|
|||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
|
||||
struct ChildKey(CascadeLevel, ptr::NonNull<()>);
|
||||
struct ChildKey(CascadePriority, ptr::NonNull<()>);
|
||||
unsafe impl Send for ChildKey {}
|
||||
unsafe impl Sync for ChildKey {}
|
||||
|
||||
|
@ -127,7 +128,7 @@ impl RuleTree {
|
|||
return;
|
||||
}
|
||||
|
||||
let mut children_count = crate::hash::FxHashMap::default();
|
||||
let mut children_count = fxhash::FxHashMap::default();
|
||||
|
||||
let mut stack = SmallVec::<[_; 32]>::new();
|
||||
stack.push(self.root.clone());
|
||||
|
@ -219,8 +220,8 @@ struct RuleNode {
|
|||
/// None for the root node.
|
||||
source: Option<StyleSource>,
|
||||
|
||||
/// The cascade level this rule is positioned at.
|
||||
level: CascadeLevel,
|
||||
/// The cascade level + layer order this rule is positioned at.
|
||||
cascade_priority: CascadePriority,
|
||||
|
||||
/// The refcount of this node.
|
||||
///
|
||||
|
@ -316,14 +317,14 @@ impl RuleNode {
|
|||
root: WeakRuleNode,
|
||||
parent: StrongRuleNode,
|
||||
source: StyleSource,
|
||||
level: CascadeLevel,
|
||||
cascade_priority: CascadePriority,
|
||||
) -> Self {
|
||||
debug_assert!(root.p.parent.is_none());
|
||||
RuleNode {
|
||||
root: Some(root),
|
||||
parent: Some(parent),
|
||||
source: Some(source),
|
||||
level: level,
|
||||
cascade_priority,
|
||||
refcount: AtomicUsize::new(1),
|
||||
children: Default::default(),
|
||||
approximate_free_count: AtomicUsize::new(0),
|
||||
|
@ -336,7 +337,7 @@ impl RuleNode {
|
|||
root: None,
|
||||
parent: None,
|
||||
source: None,
|
||||
level: CascadeLevel::UANormal,
|
||||
cascade_priority: CascadePriority::new(CascadeLevel::UANormal, LayerOrder::root()),
|
||||
refcount: AtomicUsize::new(1),
|
||||
approximate_free_count: AtomicUsize::new(0),
|
||||
children: Default::default(),
|
||||
|
@ -346,7 +347,7 @@ impl RuleNode {
|
|||
|
||||
fn key(&self) -> ChildKey {
|
||||
ChildKey(
|
||||
self.level,
|
||||
self.cascade_priority,
|
||||
self.source
|
||||
.as_ref()
|
||||
.expect("Called key() on the root node")
|
||||
|
@ -554,20 +555,20 @@ impl StrongRuleNode {
|
|||
&self,
|
||||
root: &StrongRuleNode,
|
||||
source: StyleSource,
|
||||
level: CascadeLevel,
|
||||
cascade_priority: CascadePriority,
|
||||
) -> StrongRuleNode {
|
||||
use parking_lot::RwLockUpgradableReadGuard;
|
||||
|
||||
debug_assert!(
|
||||
self.p.level <= level,
|
||||
self.p.cascade_priority <= cascade_priority,
|
||||
"Should be ordered (instead {:?} > {:?}), from {:?} and {:?}",
|
||||
self.p.level,
|
||||
level,
|
||||
self.p.cascade_priority,
|
||||
cascade_priority,
|
||||
self.p.source,
|
||||
source,
|
||||
);
|
||||
|
||||
let key = ChildKey(level, source.key());
|
||||
let key = ChildKey(cascade_priority, source.key());
|
||||
let children = self.p.children.upgradable_read();
|
||||
if let Some(child) = children.get(&key, |node| node.p.key()) {
|
||||
// Sound to call because we read-locked the parent's children.
|
||||
|
@ -584,7 +585,7 @@ impl StrongRuleNode {
|
|||
root.downgrade(),
|
||||
self.clone(),
|
||||
source,
|
||||
level,
|
||||
cascade_priority,
|
||||
)));
|
||||
// Sound to call because we still own a strong reference to
|
||||
// this node, through the `node` variable itself that we are
|
||||
|
@ -602,14 +603,22 @@ impl StrongRuleNode {
|
|||
self.p.source.as_ref()
|
||||
}
|
||||
|
||||
/// The cascade level for this node
|
||||
pub fn cascade_level(&self) -> CascadeLevel {
|
||||
self.p.level
|
||||
/// The cascade priority.
|
||||
#[inline]
|
||||
pub fn cascade_priority(&self) -> CascadePriority {
|
||||
self.p.cascade_priority
|
||||
}
|
||||
|
||||
/// Get the importance that this rule node represents.
|
||||
pub fn importance(&self) -> Importance {
|
||||
self.p.level.importance()
|
||||
/// The cascade level.
|
||||
#[inline]
|
||||
pub fn cascade_level(&self) -> CascadeLevel {
|
||||
self.cascade_priority().cascade_level()
|
||||
}
|
||||
|
||||
/// The importance.
|
||||
#[inline]
|
||||
pub fn importance(&self) -> crate::properties::Importance {
|
||||
self.cascade_level().importance()
|
||||
}
|
||||
|
||||
/// Returns whether this node has any child, only intended for testing
|
||||
|
|
|
@ -29,7 +29,7 @@ use crate::stylesheets::Origin;
|
|||
/// [3]: https://html.spec.whatwg.org/multipage/#presentational-hints
|
||||
/// [4]: https://drafts.csswg.org/css-scoping/#shadow-cascading
|
||||
#[repr(u8)]
|
||||
#[derive(Clone, Copy, Debug, Eq, Hash, MallocSizeOf, PartialEq, PartialOrd)]
|
||||
#[derive(Clone, Copy, Debug, Eq, Hash, MallocSizeOf, Ord, PartialEq, PartialOrd)]
|
||||
pub enum CascadeLevel {
|
||||
/// Normal User-Agent rules.
|
||||
UANormal,
|
||||
|
@ -69,82 +69,44 @@ pub enum CascadeLevel {
|
|||
}
|
||||
|
||||
impl CascadeLevel {
|
||||
/// Pack this cascade level in a single byte.
|
||||
///
|
||||
/// We have 10 levels, which we can represent with 4 bits, and then a
|
||||
/// cascade order optionally, which we can clamp to three bits max, and
|
||||
/// represent with a fourth bit for the sign.
|
||||
///
|
||||
/// So this creates: SOOODDDD
|
||||
///
|
||||
/// Where `S` is the sign of the order (one if negative, 0 otherwise), `O`
|
||||
/// is the absolute value of the order, and `D`s are the discriminant.
|
||||
#[inline]
|
||||
pub fn to_byte_lossy(&self) -> u8 {
|
||||
let (discriminant, order) = match *self {
|
||||
Self::UANormal => (0, 0),
|
||||
Self::UserNormal => (1, 0),
|
||||
Self::PresHints => (2, 0),
|
||||
/// Convert this level from "unimportant" to "important".
|
||||
pub fn important(&self) -> Self {
|
||||
match *self {
|
||||
Self::UANormal => Self::UAImportant,
|
||||
Self::UserNormal => Self::UserImportant,
|
||||
Self::AuthorNormal {
|
||||
shadow_cascade_order,
|
||||
} => (3, shadow_cascade_order.0),
|
||||
Self::SMILOverride => (4, 0),
|
||||
Self::Animations => (5, 0),
|
||||
Self::AuthorImportant {
|
||||
shadow_cascade_order,
|
||||
} => (6, shadow_cascade_order.0),
|
||||
Self::UserImportant => (7, 0),
|
||||
Self::UAImportant => (8, 0),
|
||||
Self::Transitions => (9, 0),
|
||||
};
|
||||
|
||||
debug_assert_eq!(discriminant & 0xf, discriminant);
|
||||
if order == 0 {
|
||||
return discriminant;
|
||||
} => Self::AuthorImportant {
|
||||
shadow_cascade_order: -shadow_cascade_order,
|
||||
},
|
||||
Self::PresHints |
|
||||
Self::SMILOverride |
|
||||
Self::Animations |
|
||||
Self::AuthorImportant { .. } |
|
||||
Self::UserImportant |
|
||||
Self::UAImportant |
|
||||
Self::Transitions => *self,
|
||||
}
|
||||
|
||||
let negative = order < 0;
|
||||
let value = std::cmp::min(order.abs() as u8, 0b111);
|
||||
(negative as u8) << 7 | value << 4 | discriminant
|
||||
}
|
||||
|
||||
/// Convert back from the single-byte representation of the cascade level
|
||||
/// explained above.
|
||||
#[inline]
|
||||
pub fn from_byte(b: u8) -> Self {
|
||||
let order = {
|
||||
let abs = ((b & 0b01110000) >> 4) as i8;
|
||||
let negative = b & 0b10000000 != 0;
|
||||
if negative {
|
||||
-abs
|
||||
} else {
|
||||
abs
|
||||
}
|
||||
};
|
||||
let discriminant = b & 0xf;
|
||||
let level = match discriminant {
|
||||
0 => Self::UANormal,
|
||||
1 => Self::UserNormal,
|
||||
2 => Self::PresHints,
|
||||
3 => {
|
||||
return Self::AuthorNormal {
|
||||
shadow_cascade_order: ShadowCascadeOrder(order),
|
||||
}
|
||||
/// Convert this level from "important" to "non-important".
|
||||
pub fn unimportant(&self) -> Self {
|
||||
match *self {
|
||||
Self::UAImportant => Self::UANormal,
|
||||
Self::UserImportant => Self::UserNormal,
|
||||
Self::AuthorImportant {
|
||||
shadow_cascade_order,
|
||||
} => Self::AuthorNormal {
|
||||
shadow_cascade_order: -shadow_cascade_order,
|
||||
},
|
||||
4 => Self::SMILOverride,
|
||||
5 => Self::Animations,
|
||||
6 => {
|
||||
return Self::AuthorImportant {
|
||||
shadow_cascade_order: ShadowCascadeOrder(order),
|
||||
}
|
||||
},
|
||||
7 => Self::UserImportant,
|
||||
8 => Self::UAImportant,
|
||||
9 => Self::Transitions,
|
||||
_ => unreachable!("Didn't expect {} as a discriminant", discriminant),
|
||||
};
|
||||
debug_assert_eq!(order, 0, "Didn't expect an order value for {:?}", level);
|
||||
level
|
||||
Self::PresHints |
|
||||
Self::SMILOverride |
|
||||
Self::Animations |
|
||||
Self::AuthorNormal { .. } |
|
||||
Self::UserNormal |
|
||||
Self::UANormal |
|
||||
Self::Transitions => *self,
|
||||
}
|
||||
}
|
||||
|
||||
/// Select a lock guard for this level
|
||||
|
@ -231,6 +193,12 @@ impl CascadeLevel {
|
|||
pub struct ShadowCascadeOrder(i8);
|
||||
|
||||
impl ShadowCascadeOrder {
|
||||
/// We keep a maximum of 3 bits of order as a limit so that we can pack
|
||||
/// CascadeLevel in one byte by using half of it for the order, if that ends
|
||||
/// up being necessary.
|
||||
const MAX: i8 = 0b111;
|
||||
const MIN: i8 = -Self::MAX;
|
||||
|
||||
/// A level for the outermost shadow tree (the shadow tree we own, and the
|
||||
/// ones from the slots we're slotted in).
|
||||
#[inline]
|
||||
|
@ -256,7 +224,9 @@ impl ShadowCascadeOrder {
|
|||
#[inline]
|
||||
pub fn dec(&mut self) {
|
||||
debug_assert!(self.0 < 0);
|
||||
self.0 = self.0.saturating_sub(1);
|
||||
if self.0 != Self::MIN {
|
||||
self.0 -= 1;
|
||||
}
|
||||
}
|
||||
|
||||
/// The level, moving inwards. We should only move inwards if we're
|
||||
|
@ -264,7 +234,9 @@ impl ShadowCascadeOrder {
|
|||
#[inline]
|
||||
pub fn inc(&mut self) {
|
||||
debug_assert_ne!(self.0, -1);
|
||||
self.0 = self.0.saturating_add(1);
|
||||
if self.0 != Self::MAX {
|
||||
self.0 += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -6,9 +6,10 @@
|
|||
|
||||
//! The rule tree.
|
||||
|
||||
use crate::applicable_declarations::ApplicableDeclarationList;
|
||||
use crate::applicable_declarations::{ApplicableDeclarationList, CascadePriority};
|
||||
use crate::properties::{LonghandIdSet, PropertyDeclarationBlock};
|
||||
use crate::shared_lock::{Locked, StylesheetGuards};
|
||||
use crate::stylesheets::layer_rule::LayerOrder;
|
||||
use servo_arc::{Arc, ArcBorrow};
|
||||
use smallvec::SmallVec;
|
||||
use std::io::{self, Write};
|
||||
|
@ -47,21 +48,22 @@ impl RuleTree {
|
|||
guards: &StylesheetGuards,
|
||||
) -> StrongRuleNode
|
||||
where
|
||||
I: Iterator<Item = (StyleSource, CascadeLevel)>,
|
||||
I: Iterator<Item = (StyleSource, CascadePriority)>,
|
||||
{
|
||||
use self::CascadeLevel::*;
|
||||
let mut current = self.root().clone();
|
||||
|
||||
let mut found_important = false;
|
||||
|
||||
let mut important_author = SmallVec::<[(StyleSource, ShadowCascadeOrder); 4]>::new();
|
||||
|
||||
let mut important_user = SmallVec::<[StyleSource; 4]>::new();
|
||||
let mut important_ua = SmallVec::<[StyleSource; 4]>::new();
|
||||
let mut important_author = SmallVec::<[(StyleSource, CascadePriority); 4]>::new();
|
||||
let mut important_user = SmallVec::<[(StyleSource, CascadePriority); 4]>::new();
|
||||
let mut important_ua = SmallVec::<[(StyleSource, CascadePriority); 4]>::new();
|
||||
let mut transition = None;
|
||||
|
||||
for (source, level) in iter {
|
||||
for (source, priority) in iter {
|
||||
let level = priority.cascade_level();
|
||||
debug_assert!(!level.is_important(), "Important levels handled internally");
|
||||
|
||||
let any_important = {
|
||||
let pdb = source.read(level.guard(guards));
|
||||
pdb.any_important()
|
||||
|
@ -70,13 +72,11 @@ impl RuleTree {
|
|||
if any_important {
|
||||
found_important = true;
|
||||
match level {
|
||||
AuthorNormal {
|
||||
shadow_cascade_order,
|
||||
} => {
|
||||
important_author.push((source.clone(), shadow_cascade_order));
|
||||
AuthorNormal { .. } => {
|
||||
important_author.push((source.clone(), priority.important()))
|
||||
},
|
||||
UANormal => important_ua.push(source.clone()),
|
||||
UserNormal => important_user.push(source.clone()),
|
||||
UANormal => important_ua.push((source.clone(), priority.important())),
|
||||
UserNormal => important_user.push((source.clone(), priority.important())),
|
||||
_ => {},
|
||||
};
|
||||
}
|
||||
|
@ -98,7 +98,7 @@ impl RuleTree {
|
|||
debug_assert!(transition.is_none());
|
||||
transition = Some(source);
|
||||
} else {
|
||||
current = current.ensure_child(self.root(), source, level);
|
||||
current = current.ensure_child(self.root(), source, priority);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -110,10 +110,8 @@ impl RuleTree {
|
|||
// Insert important declarations, in order of increasing importance,
|
||||
// followed by any transition rule.
|
||||
//
|
||||
// Inner shadow wins over same-tree, which wins over outer-shadow.
|
||||
//
|
||||
// We negate the shadow cascade order to preserve the right PartialOrd
|
||||
// behavior.
|
||||
// Important rules are sorted differently from unimportant ones by
|
||||
// shadow order and cascade order.
|
||||
if !important_author.is_empty() &&
|
||||
important_author.first().unwrap().1 != important_author.last().unwrap().1
|
||||
{
|
||||
|
@ -129,29 +127,27 @@ impl RuleTree {
|
|||
// inside the same chunk already sorted. Seems like we could try to
|
||||
// keep a SmallVec-of-SmallVecs with the chunks and just iterate the
|
||||
// outer in reverse.
|
||||
important_author.sort_by_key(|&(_, order)| -order);
|
||||
important_author.sort_by_key(|&(_, priority)| priority);
|
||||
}
|
||||
|
||||
for (source, shadow_cascade_order) in important_author.drain(..) {
|
||||
current = current.ensure_child(
|
||||
self.root(),
|
||||
source,
|
||||
AuthorImportant {
|
||||
shadow_cascade_order: -shadow_cascade_order,
|
||||
},
|
||||
);
|
||||
for (source, priority) in important_author.drain(..) {
|
||||
current = current.ensure_child(self.root(), source, priority);
|
||||
}
|
||||
|
||||
for source in important_user.drain(..) {
|
||||
current = current.ensure_child(self.root(), source, UserImportant);
|
||||
for (source, priority) in important_user.drain(..) {
|
||||
current = current.ensure_child(self.root(), source, priority);
|
||||
}
|
||||
|
||||
for source in important_ua.drain(..) {
|
||||
current = current.ensure_child(self.root(), source, UAImportant);
|
||||
for (source, priority) in important_ua.drain(..) {
|
||||
current = current.ensure_child(self.root(), source, priority);
|
||||
}
|
||||
|
||||
if let Some(source) = transition {
|
||||
current = current.ensure_child(self.root(), source, Transitions);
|
||||
current = current.ensure_child(
|
||||
self.root(),
|
||||
source,
|
||||
CascadePriority::new(Transitions, LayerOrder::root()),
|
||||
);
|
||||
}
|
||||
|
||||
current
|
||||
|
@ -174,18 +170,18 @@ impl RuleTree {
|
|||
/// return the corresponding rule node representing the last inserted one.
|
||||
pub fn insert_ordered_rules<'a, I>(&self, iter: I) -> StrongRuleNode
|
||||
where
|
||||
I: Iterator<Item = (StyleSource, CascadeLevel)>,
|
||||
I: Iterator<Item = (StyleSource, CascadePriority)>,
|
||||
{
|
||||
self.insert_ordered_rules_from(self.root().clone(), iter)
|
||||
}
|
||||
|
||||
fn insert_ordered_rules_from<'a, I>(&self, from: StrongRuleNode, iter: I) -> StrongRuleNode
|
||||
where
|
||||
I: Iterator<Item = (StyleSource, CascadeLevel)>,
|
||||
I: Iterator<Item = (StyleSource, CascadePriority)>,
|
||||
{
|
||||
let mut current = from;
|
||||
for (source, level) in iter {
|
||||
current = current.ensure_child(self.root(), source, level);
|
||||
for (source, priority) in iter {
|
||||
current = current.ensure_child(self.root(), source, priority);
|
||||
}
|
||||
current
|
||||
}
|
||||
|
@ -197,6 +193,7 @@ impl RuleTree {
|
|||
pub fn update_rule_at_level(
|
||||
&self,
|
||||
level: CascadeLevel,
|
||||
layer_order: LayerOrder,
|
||||
pdb: Option<ArcBorrow<Locked<PropertyDeclarationBlock>>>,
|
||||
path: &StrongRuleNode,
|
||||
guards: &StylesheetGuards,
|
||||
|
@ -209,10 +206,10 @@ impl RuleTree {
|
|||
|
||||
// First walk up until the first less-or-equally specific rule.
|
||||
let mut children = SmallVec::<[_; 10]>::new();
|
||||
while current.cascade_level() > level {
|
||||
while current.cascade_priority().cascade_level() > level {
|
||||
children.push((
|
||||
current.style_source().unwrap().clone(),
|
||||
current.cascade_level(),
|
||||
current.cascade_priority(),
|
||||
));
|
||||
current = current.parent().unwrap().clone();
|
||||
}
|
||||
|
@ -227,7 +224,7 @@ impl RuleTree {
|
|||
// to special-case (isn't hard, it's just about removing the `if` and
|
||||
// special cases, and replacing them for a `while` loop, avoiding the
|
||||
// optimizations).
|
||||
if current.cascade_level() == level {
|
||||
if current.cascade_priority().cascade_level() == level {
|
||||
*important_rules_changed |= level.is_important();
|
||||
|
||||
let current_decls = current.style_source().unwrap().as_declarations();
|
||||
|
@ -267,7 +264,7 @@ impl RuleTree {
|
|||
current = current.ensure_child(
|
||||
self.root(),
|
||||
StyleSource::from_declarations(pdb.clone_arc()),
|
||||
level,
|
||||
CascadePriority::new(level, layer_order),
|
||||
);
|
||||
*important_rules_changed = true;
|
||||
}
|
||||
|
@ -276,7 +273,7 @@ impl RuleTree {
|
|||
current = current.ensure_child(
|
||||
self.root(),
|
||||
StyleSource::from_declarations(pdb.clone_arc()),
|
||||
level,
|
||||
CascadePriority::new(level, layer_order),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -312,7 +309,10 @@ impl RuleTree {
|
|||
let mut children = SmallVec::<[_; 10]>::new();
|
||||
for node in iter {
|
||||
if !node.cascade_level().is_animation() {
|
||||
children.push((node.style_source().unwrap().clone(), node.cascade_level()));
|
||||
children.push((
|
||||
node.style_source().unwrap().clone(),
|
||||
node.cascade_priority(),
|
||||
));
|
||||
}
|
||||
last = node;
|
||||
}
|
||||
|
@ -336,6 +336,7 @@ impl RuleTree {
|
|||
let mut dummy = false;
|
||||
self.update_rule_at_level(
|
||||
CascadeLevel::Transitions,
|
||||
LayerOrder::root(),
|
||||
Some(pdb.borrow_arc()),
|
||||
path,
|
||||
guards,
|
||||
|
|
|
@ -8,18 +8,17 @@
|
|||
use crate::applicable_declarations::ApplicableDeclarationList;
|
||||
use crate::context::QuirksMode;
|
||||
use crate::dom::TElement;
|
||||
use crate::hash::map as hash_map;
|
||||
use crate::hash::{HashMap, HashSet};
|
||||
use crate::rule_tree::CascadeLevel;
|
||||
use crate::selector_parser::SelectorImpl;
|
||||
use crate::stylist::{Rule, CascadeData};
|
||||
use crate::{Atom, LocalName, Namespace, WeakAtom};
|
||||
use fallible::FallibleVec;
|
||||
use hashglobe::FailedAllocationError;
|
||||
use crate::stylist::{CascadeData, Rule};
|
||||
use crate::AllocErr;
|
||||
use crate::{Atom, LocalName, Namespace, ShrinkIfNeeded, WeakAtom};
|
||||
use precomputed_hash::PrecomputedHash;
|
||||
use selectors::matching::{matches_selector, ElementSelectorFlags, MatchingContext};
|
||||
use selectors::parser::{Combinator, Component, SelectorIter};
|
||||
use smallvec::SmallVec;
|
||||
use std::collections::hash_map;
|
||||
use std::collections::{HashMap, HashSet};
|
||||
use std::hash::{BuildHasherDefault, Hash, Hasher};
|
||||
|
||||
/// A hasher implementation that doesn't hash anything, because it expects its
|
||||
|
@ -123,10 +122,7 @@ impl<T: 'static> Default for SelectorMap<T> {
|
|||
}
|
||||
}
|
||||
|
||||
// FIXME(Manishearth) the 'static bound can be removed when
|
||||
// our HashMap fork (hashglobe) is able to use NonZero,
|
||||
// or when stdlib gets fallible collections
|
||||
impl<T: 'static> SelectorMap<T> {
|
||||
impl<T> SelectorMap<T> {
|
||||
/// Trivially constructs an empty `SelectorMap`.
|
||||
pub fn new() -> Self {
|
||||
SelectorMap {
|
||||
|
@ -153,6 +149,15 @@ impl<T: 'static> SelectorMap<T> {
|
|||
ret
|
||||
}
|
||||
|
||||
/// Shrink the capacity of the map if needed.
|
||||
pub fn shrink_if_needed(&mut self) {
|
||||
self.id_hash.shrink_if_needed();
|
||||
self.class_hash.shrink_if_needed();
|
||||
self.attribute_hash.shrink_if_needed();
|
||||
self.local_name_hash.shrink_if_needed();
|
||||
self.namespace_hash.shrink_if_needed();
|
||||
}
|
||||
|
||||
/// Clears the hashmap retaining storage.
|
||||
pub fn clear(&mut self) {
|
||||
self.root.clear();
|
||||
|
@ -313,7 +318,8 @@ impl SelectorMap<Rule> {
|
|||
context,
|
||||
flags_setter,
|
||||
) {
|
||||
matching_rules.push(rule.to_applicable_declaration_block(cascade_level, cascade_data));
|
||||
matching_rules
|
||||
.push(rule.to_applicable_declaration_block(cascade_level, cascade_data));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -321,11 +327,7 @@ impl SelectorMap<Rule> {
|
|||
|
||||
impl<T: SelectorMapEntry> SelectorMap<T> {
|
||||
/// Inserts an entry into the correct bucket(s).
|
||||
pub fn insert(
|
||||
&mut self,
|
||||
entry: T,
|
||||
quirks_mode: QuirksMode,
|
||||
) -> Result<(), FailedAllocationError> {
|
||||
pub fn insert(&mut self, entry: T, quirks_mode: QuirksMode) -> Result<(), AllocErr> {
|
||||
self.count += 1;
|
||||
|
||||
// NOTE(emilio): It'd be nice for this to be a separate function, but
|
||||
|
@ -334,16 +336,16 @@ impl<T: SelectorMapEntry> SelectorMap<T> {
|
|||
// common path.
|
||||
macro_rules! insert_into_bucket {
|
||||
($entry:ident, $bucket:expr) => {{
|
||||
match $bucket {
|
||||
let vec = match $bucket {
|
||||
Bucket::Root => &mut self.root,
|
||||
Bucket::ID(id) => self
|
||||
.id_hash
|
||||
.try_entry(id.clone(), quirks_mode)?
|
||||
.or_insert_with(SmallVec::new),
|
||||
.or_default(),
|
||||
Bucket::Class(class) => self
|
||||
.class_hash
|
||||
.try_entry(class.clone(), quirks_mode)?
|
||||
.or_insert_with(SmallVec::new),
|
||||
.or_default(),
|
||||
Bucket::Attribute { name, lower_name } |
|
||||
Bucket::LocalName { name, lower_name } => {
|
||||
// If the local name in the selector isn't lowercase,
|
||||
|
@ -366,28 +368,32 @@ impl<T: SelectorMapEntry> SelectorMap<T> {
|
|||
&mut self.local_name_hash
|
||||
};
|
||||
if name != lower_name {
|
||||
hash
|
||||
.try_entry(lower_name.clone())?
|
||||
.or_insert_with(SmallVec::new)
|
||||
.try_push($entry.clone())?;
|
||||
hash.try_reserve(1)?;
|
||||
let vec = hash.entry(lower_name.clone()).or_default();
|
||||
vec.try_reserve(1)?;
|
||||
vec.push($entry.clone());
|
||||
}
|
||||
hash
|
||||
.try_entry(name.clone())?
|
||||
.or_insert_with(SmallVec::new)
|
||||
hash.try_reserve(1)?;
|
||||
hash.entry(name.clone()).or_default()
|
||||
},
|
||||
Bucket::Namespace(url) => {
|
||||
self.namespace_hash.try_reserve(1)?;
|
||||
self.namespace_hash.entry(url.clone()).or_default()
|
||||
},
|
||||
Bucket::Namespace(url) => self
|
||||
.namespace_hash
|
||||
.try_entry(url.clone())?
|
||||
.or_insert_with(SmallVec::new),
|
||||
Bucket::Universal => &mut self.other,
|
||||
}
|
||||
.try_push($entry)?;
|
||||
};
|
||||
vec.try_reserve(1)?;
|
||||
vec.push($entry);
|
||||
}};
|
||||
}
|
||||
|
||||
let bucket = {
|
||||
let mut disjoint_buckets = SmallVec::new();
|
||||
let bucket = find_bucket(entry.selector(), &mut disjoint_buckets, self.bucket_attributes);
|
||||
let bucket = find_bucket(
|
||||
entry.selector(),
|
||||
&mut disjoint_buckets,
|
||||
self.bucket_attributes,
|
||||
);
|
||||
|
||||
// See if inserting this selector in multiple entries in the
|
||||
// selector map would be worth it. Consider a case like:
|
||||
|
@ -619,11 +625,16 @@ fn specific_bucket_for<'a>(
|
|||
Component::Root => Bucket::Root,
|
||||
Component::ID(ref id) => Bucket::ID(id),
|
||||
Component::Class(ref class) => Bucket::Class(class),
|
||||
Component::AttributeInNoNamespace { ref local_name, .. } if bucket_attributes => Bucket::Attribute {
|
||||
name: local_name,
|
||||
lower_name: local_name,
|
||||
Component::AttributeInNoNamespace { ref local_name, .. } if bucket_attributes => {
|
||||
Bucket::Attribute {
|
||||
name: local_name,
|
||||
lower_name: local_name,
|
||||
}
|
||||
},
|
||||
Component::AttributeInNoNamespaceExists { ref local_name, ref local_name_lower } if bucket_attributes => Bucket::Attribute {
|
||||
Component::AttributeInNoNamespaceExists {
|
||||
ref local_name,
|
||||
ref local_name_lower,
|
||||
} if bucket_attributes => Bucket::Attribute {
|
||||
name: local_name,
|
||||
lower_name: local_name_lower,
|
||||
},
|
||||
|
@ -656,8 +667,12 @@ fn specific_bucket_for<'a>(
|
|||
//
|
||||
// So inserting `span` in the rule hash makes sense since we want to
|
||||
// match the slotted <span>.
|
||||
Component::Slotted(ref selector) => find_bucket(selector.iter(), disjoint_buckets, bucket_attributes),
|
||||
Component::Host(Some(ref selector)) => find_bucket(selector.iter(), disjoint_buckets, bucket_attributes),
|
||||
Component::Slotted(ref selector) => {
|
||||
find_bucket(selector.iter(), disjoint_buckets, bucket_attributes)
|
||||
},
|
||||
Component::Host(Some(ref selector)) => {
|
||||
find_bucket(selector.iter(), disjoint_buckets, bucket_attributes)
|
||||
},
|
||||
Component::Is(ref list) | Component::Where(ref list) => {
|
||||
if list.len() == 1 {
|
||||
find_bucket(list[0].iter(), disjoint_buckets, bucket_attributes)
|
||||
|
@ -706,36 +721,39 @@ fn find_bucket<'a>(
|
|||
|
||||
/// Wrapper for PrecomputedHashMap that does ASCII-case-insensitive lookup in quirks mode.
|
||||
#[derive(Clone, Debug, MallocSizeOf)]
|
||||
pub struct MaybeCaseInsensitiveHashMap<K: PrecomputedHash + Hash + Eq, V: 'static>(
|
||||
pub struct MaybeCaseInsensitiveHashMap<K: PrecomputedHash + Hash + Eq, V>(
|
||||
PrecomputedHashMap<K, V>,
|
||||
);
|
||||
|
||||
impl<V: 'static> Default for MaybeCaseInsensitiveHashMap<Atom, V> {
|
||||
impl<V> Default for MaybeCaseInsensitiveHashMap<Atom, V> {
|
||||
#[inline]
|
||||
fn default() -> Self {
|
||||
MaybeCaseInsensitiveHashMap(PrecomputedHashMap::default())
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME(Manishearth) the 'static bound can be removed when
|
||||
// our HashMap fork (hashglobe) is able to use NonZero,
|
||||
// or when stdlib gets fallible collections
|
||||
impl<V: 'static> MaybeCaseInsensitiveHashMap<Atom, V> {
|
||||
impl<V> MaybeCaseInsensitiveHashMap<Atom, V> {
|
||||
/// Empty map
|
||||
pub fn new() -> Self {
|
||||
Self::default()
|
||||
}
|
||||
|
||||
/// Shrink the capacity of the map if needed.
|
||||
pub fn shrink_if_needed(&mut self) {
|
||||
self.0.shrink_if_needed()
|
||||
}
|
||||
|
||||
/// HashMap::try_entry
|
||||
pub fn try_entry(
|
||||
&mut self,
|
||||
mut key: Atom,
|
||||
quirks_mode: QuirksMode,
|
||||
) -> Result<hash_map::Entry<Atom, V>, FailedAllocationError> {
|
||||
) -> Result<hash_map::Entry<Atom, V>, AllocErr> {
|
||||
if quirks_mode == QuirksMode::Quirks {
|
||||
key = key.to_ascii_lowercase()
|
||||
}
|
||||
self.0.try_entry(key)
|
||||
self.0.try_reserve(1)?;
|
||||
Ok(self.0.entry(key))
|
||||
}
|
||||
|
||||
/// HashMap::is_empty
|
||||
|
|
|
@ -46,7 +46,7 @@ pub struct SelectorParser<'a> {
|
|||
pub namespaces: &'a Namespaces,
|
||||
/// The extra URL data of the stylesheet, which is used to look up
|
||||
/// whether we are parsing a chrome:// URL style sheet.
|
||||
pub url_data: Option<&'a UrlExtraData>,
|
||||
pub url_data: &'a UrlExtraData,
|
||||
}
|
||||
|
||||
impl<'a> SelectorParser<'a> {
|
||||
|
@ -54,14 +54,15 @@ impl<'a> SelectorParser<'a> {
|
|||
/// account namespaces.
|
||||
///
|
||||
/// This is used for some DOM APIs like `querySelector`.
|
||||
pub fn parse_author_origin_no_namespace(
|
||||
input: &str,
|
||||
) -> Result<SelectorList<SelectorImpl>, ParseError> {
|
||||
pub fn parse_author_origin_no_namespace<'i>(
|
||||
input: &'i str,
|
||||
url_data: &UrlExtraData,
|
||||
) -> Result<SelectorList<SelectorImpl>, ParseError<'i>> {
|
||||
let namespaces = Namespaces::default();
|
||||
let parser = SelectorParser {
|
||||
stylesheet_origin: Origin::Author,
|
||||
namespaces: &namespaces,
|
||||
url_data: None,
|
||||
url_data,
|
||||
};
|
||||
let mut input = ParserInput::new(input);
|
||||
SelectorList::parse(&parser, &mut CssParser::new(&mut input))
|
||||
|
@ -75,8 +76,7 @@ impl<'a> SelectorParser<'a> {
|
|||
/// Whether we're parsing selectors in a stylesheet that has chrome
|
||||
/// privilege.
|
||||
pub fn chrome_rules_enabled(&self) -> bool {
|
||||
self.url_data.map_or(false, |d| d.chrome_rules_enabled()) ||
|
||||
self.stylesheet_origin == Origin::User
|
||||
self.url_data.chrome_rules_enabled() || self.stylesheet_origin == Origin::User
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -170,9 +170,14 @@ impl<T> PerPseudoElementMap<T> {
|
|||
}
|
||||
|
||||
/// Get an iterator for the entries.
|
||||
pub fn iter(&self) -> ::std::slice::Iter<Option<T>> {
|
||||
pub fn iter(&self) -> std::slice::Iter<Option<T>> {
|
||||
self.entries.iter()
|
||||
}
|
||||
|
||||
/// Get a mutable iterator for the entries.
|
||||
pub fn iter_mut(&mut self) -> std::slice::IterMut<Option<T>> {
|
||||
self.entries.iter_mut()
|
||||
}
|
||||
}
|
||||
|
||||
/// Values for the :dir() pseudo class
|
||||
|
|
|
@ -190,12 +190,12 @@ impl Device {
|
|||
}
|
||||
|
||||
/// Returns the default background color.
|
||||
pub fn default_background_color_for_forced_colors(&self) -> RGBA {
|
||||
pub fn default_background_color(&self) -> RGBA {
|
||||
RGBA::new(255, 255, 255, 255)
|
||||
}
|
||||
|
||||
/// Returns the default foreground color.
|
||||
pub fn default_color_for_forced_colors(&self) -> RGBA {
|
||||
pub fn default_color(&self) -> RGBA {
|
||||
RGBA::new(0, 0, 0, 255)
|
||||
}
|
||||
|
||||
|
@ -221,18 +221,6 @@ impl Device {
|
|||
}
|
||||
}
|
||||
|
||||
/// Returns the gtk titlebar radius in CSS pixels.
|
||||
/// TODO: implement this method.
|
||||
pub fn titlebar_radius(&self) -> f32 {
|
||||
0.0
|
||||
}
|
||||
|
||||
/// Returns the gtk menu radius in CSS pixels.
|
||||
/// TODO: implement this method.
|
||||
pub fn menu_radius(&self) -> f32 {
|
||||
0.0
|
||||
}
|
||||
|
||||
/// Return whether the document is a chrome document.
|
||||
#[inline]
|
||||
pub fn is_chrome_document(&self) -> bool {
|
||||
|
|
|
@ -97,7 +97,10 @@ impl SharedRwLock {
|
|||
#[cfg(feature = "gecko")]
|
||||
#[inline]
|
||||
fn ptr(&self) -> *const SomethingZeroSizedButTyped {
|
||||
self.cell.as_ref().map(|cell| cell.as_ptr() as *const _).unwrap_or(ptr::null())
|
||||
self.cell
|
||||
.as_ref()
|
||||
.map(|cell| cell.as_ptr() as *const _)
|
||||
.unwrap_or(ptr::null())
|
||||
}
|
||||
|
||||
/// Wrap the given data to make its access protected by this lock.
|
||||
|
@ -154,7 +157,10 @@ impl<'a> SharedRwLockReadGuard<'a> {
|
|||
#[inline]
|
||||
#[cfg(feature = "gecko")]
|
||||
fn ptr(&self) -> *const SomethingZeroSizedButTyped {
|
||||
self.0.as_ref().map(|r| &**r as *const _).unwrap_or(ptr::null())
|
||||
self.0
|
||||
.as_ref()
|
||||
.map(|r| &**r as *const _)
|
||||
.unwrap_or(ptr::null())
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -136,9 +136,15 @@ impl<'a, 'b: 'a> StyleAdjuster<'a, 'b> {
|
|||
/// computed to 'absolute' if the element is in a top layer.
|
||||
///
|
||||
fn adjust_for_top_layer(&mut self) {
|
||||
if !self.style.is_absolutely_positioned() && self.style.in_top_layer() {
|
||||
if !self.style.in_top_layer() {
|
||||
return;
|
||||
}
|
||||
if !self.style.is_absolutely_positioned() {
|
||||
self.style.mutate_box().set_position(Position::Absolute);
|
||||
}
|
||||
if self.style.get_box().clone_display().is_contents() {
|
||||
self.style.mutate_box().set_display(Display::Block);
|
||||
}
|
||||
}
|
||||
|
||||
/// CSS 2.1 section 9.7:
|
||||
|
@ -760,9 +766,9 @@ impl<'a, 'b: 'a> StyleAdjuster<'a, 'b> {
|
|||
/// the same font as its fallback ('list-style-type') in case it fails to load.
|
||||
#[cfg(feature = "gecko")]
|
||||
fn adjust_for_marker_pseudo(&mut self) {
|
||||
use crate::values::computed::counters::Content;
|
||||
use crate::values::computed::font::{FontFamily, FontSynthesis};
|
||||
use crate::values::computed::text::{LetterSpacing, WordSpacing};
|
||||
use crate::values::computed::counters::{Content};
|
||||
|
||||
let is_legacy_marker = self.style.pseudo.map_or(false, |p| p.is_marker()) &&
|
||||
self.style.get_list().clone_list_style_type().is_bullet() &&
|
||||
|
@ -770,21 +776,49 @@ impl<'a, 'b: 'a> StyleAdjuster<'a, 'b> {
|
|||
if !is_legacy_marker {
|
||||
return;
|
||||
}
|
||||
if !self.style.flags.get().contains(ComputedValueFlags::HAS_AUTHOR_SPECIFIED_FONT_FAMILY) {
|
||||
self.style.mutate_font().set_font_family(FontFamily::moz_bullet().clone());
|
||||
if !self
|
||||
.style
|
||||
.flags
|
||||
.get()
|
||||
.contains(ComputedValueFlags::HAS_AUTHOR_SPECIFIED_FONT_FAMILY)
|
||||
{
|
||||
self.style
|
||||
.mutate_font()
|
||||
.set_font_family(FontFamily::moz_bullet().clone());
|
||||
|
||||
// FIXME(mats): We can remove this if support for font-synthesis is added to @font-face rules.
|
||||
// Then we can add it to the @font-face rule in html.css instead.
|
||||
// https://github.com/w3c/csswg-drafts/issues/6081
|
||||
if !self.style.flags.get().contains(ComputedValueFlags::HAS_AUTHOR_SPECIFIED_FONT_SYNTHESIS) {
|
||||
self.style.mutate_font().set_font_synthesis(FontSynthesis::none());
|
||||
if !self
|
||||
.style
|
||||
.flags
|
||||
.get()
|
||||
.contains(ComputedValueFlags::HAS_AUTHOR_SPECIFIED_FONT_SYNTHESIS)
|
||||
{
|
||||
self.style
|
||||
.mutate_font()
|
||||
.set_font_synthesis(FontSynthesis::none());
|
||||
}
|
||||
}
|
||||
if !self.style.flags.get().contains(ComputedValueFlags::HAS_AUTHOR_SPECIFIED_LETTER_SPACING) {
|
||||
self.style.mutate_inherited_text().set_letter_spacing(LetterSpacing::normal());
|
||||
if !self
|
||||
.style
|
||||
.flags
|
||||
.get()
|
||||
.contains(ComputedValueFlags::HAS_AUTHOR_SPECIFIED_LETTER_SPACING)
|
||||
{
|
||||
self.style
|
||||
.mutate_inherited_text()
|
||||
.set_letter_spacing(LetterSpacing::normal());
|
||||
}
|
||||
if !self.style.flags.get().contains(ComputedValueFlags::HAS_AUTHOR_SPECIFIED_WORD_SPACING) {
|
||||
self.style.mutate_inherited_text().set_word_spacing(WordSpacing::normal());
|
||||
if !self
|
||||
.style
|
||||
.flags
|
||||
.get()
|
||||
.contains(ComputedValueFlags::HAS_AUTHOR_SPECIFIED_WORD_SPACING)
|
||||
{
|
||||
self.style
|
||||
.mutate_inherited_text()
|
||||
.set_word_spacing(WordSpacing::normal());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -10,8 +10,8 @@ use crate::media_queries::MediaList;
|
|||
use crate::shared_lock::{DeepCloneParams, DeepCloneWithLock};
|
||||
use crate::shared_lock::{SharedRwLock, SharedRwLockReadGuard, ToCssWithGuard};
|
||||
use crate::str::CssStringWriter;
|
||||
use crate::stylesheets::{CssRule, StylesheetInDocument};
|
||||
use crate::stylesheets::layer_rule::LayerName;
|
||||
use crate::stylesheets::{CssRule, StylesheetInDocument};
|
||||
use crate::values::CssUrl;
|
||||
use cssparser::SourceLocation;
|
||||
use std::fmt::{self, Write};
|
||||
|
@ -131,7 +131,6 @@ pub struct ImportLayer {
|
|||
pub name: Option<LayerName>,
|
||||
}
|
||||
|
||||
|
||||
impl ToCss for ImportLayer {
|
||||
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
|
||||
where
|
||||
|
|
|
@ -14,7 +14,6 @@ use crate::properties::{PropertyDeclarationId, SourcePropertyDeclaration};
|
|||
use crate::shared_lock::{DeepCloneParams, DeepCloneWithLock, SharedRwLock, SharedRwLockReadGuard};
|
||||
use crate::shared_lock::{Locked, ToCssWithGuard};
|
||||
use crate::str::CssStringWriter;
|
||||
use crate::stylesheets::layer_rule::LayerId;
|
||||
use crate::stylesheets::rule_parser::VendorPrefix;
|
||||
use crate::stylesheets::{CssRuleType, StylesheetContents};
|
||||
use crate::values::{serialize_percentage, KeyframesName};
|
||||
|
@ -358,8 +357,6 @@ pub struct KeyframesAnimation {
|
|||
pub properties_changed: LonghandIdSet,
|
||||
/// Vendor prefix type the @keyframes has.
|
||||
pub vendor_prefix: Option<VendorPrefix>,
|
||||
/// The id of the cascade layer the keyframe rule was in.
|
||||
pub layer_id: LayerId,
|
||||
}
|
||||
|
||||
/// Get all the animated properties in a keyframes animation.
|
||||
|
@ -412,14 +409,12 @@ impl KeyframesAnimation {
|
|||
pub fn from_keyframes(
|
||||
keyframes: &[Arc<Locked<Keyframe>>],
|
||||
vendor_prefix: Option<VendorPrefix>,
|
||||
layer_id: LayerId,
|
||||
guard: &SharedRwLockReadGuard,
|
||||
) -> Self {
|
||||
let mut result = KeyframesAnimation {
|
||||
steps: vec![],
|
||||
properties_changed: LonghandIdSet::new(),
|
||||
vendor_prefix,
|
||||
layer_id,
|
||||
};
|
||||
|
||||
if keyframes.is_empty() {
|
||||
|
@ -500,8 +495,8 @@ pub fn parse_keyframe_list(
|
|||
RuleListParser::new_for_nested_rule(
|
||||
input,
|
||||
KeyframeListParser {
|
||||
context: context,
|
||||
shared_lock: shared_lock,
|
||||
context,
|
||||
shared_lock,
|
||||
declarations: &mut declarations,
|
||||
},
|
||||
)
|
||||
|
|
|
@ -13,20 +13,38 @@ use crate::values::AtomIdent;
|
|||
|
||||
use super::CssRules;
|
||||
|
||||
use cssparser::{Parser, SourceLocation, ToCss as CssParserToCss, Token};
|
||||
use cssparser::{Parser, SourceLocation, Token};
|
||||
use servo_arc::Arc;
|
||||
use smallvec::SmallVec;
|
||||
use std::fmt::{self, Write};
|
||||
use style_traits::{CssWriter, ParseError, ToCss};
|
||||
|
||||
/// The order of a given layer.
|
||||
#[derive(Clone, Copy, Debug, Eq, MallocSizeOf, PartialEq, PartialOrd, Ord)]
|
||||
pub struct LayerOrder(u32);
|
||||
/// The order of a given layer. We use 16 bits so that we can pack LayerOrder
|
||||
/// and CascadeLevel in a single 32-bit struct. If we need more bits we can go
|
||||
/// back to packing CascadeLevel in a single byte as we did before.
|
||||
#[derive(Clone, Copy, Debug, Eq, Hash, MallocSizeOf, PartialEq, PartialOrd, Ord)]
|
||||
pub struct LayerOrder(u16);
|
||||
|
||||
impl LayerOrder {
|
||||
/// The order of the root layer.
|
||||
pub const fn root() -> Self {
|
||||
Self(std::u32::MAX)
|
||||
Self(std::u16::MAX - 1)
|
||||
}
|
||||
|
||||
/// The order of the style attribute layer.
|
||||
pub const fn style_attribute() -> Self {
|
||||
Self(std::u16::MAX)
|
||||
}
|
||||
|
||||
/// Returns whether this layer is for the style attribute, which behaves
|
||||
/// differently in terms of !important, see
|
||||
/// https://github.com/w3c/csswg-drafts/issues/6872
|
||||
///
|
||||
/// (This is a bit silly, mind-you, but it's needed so that revert-layer
|
||||
/// behaves correctly).
|
||||
#[inline]
|
||||
pub fn is_style_attribute_layer(&self) -> bool {
|
||||
*self == Self::style_attribute()
|
||||
}
|
||||
|
||||
/// The first cascade layer order.
|
||||
|
@ -37,7 +55,9 @@ impl LayerOrder {
|
|||
/// Increment the cascade layer order.
|
||||
#[inline]
|
||||
pub fn inc(&mut self) {
|
||||
self.0 += 1;
|
||||
if self.0 != std::u16::MAX - 1 {
|
||||
self.0 += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -139,71 +159,34 @@ impl ToCss for LayerName {
|
|||
}
|
||||
}
|
||||
|
||||
/// The kind of layer rule this is.
|
||||
#[derive(Debug, ToShmem)]
|
||||
pub enum LayerRuleKind {
|
||||
/// A block `@layer <name>? { ... }`
|
||||
Block {
|
||||
/// The layer name, or `None` if anonymous.
|
||||
name: Option<LayerName>,
|
||||
/// The nested rules.
|
||||
rules: Arc<Locked<CssRules>>,
|
||||
},
|
||||
/// A statement `@layer <name>, <name>, <name>;`
|
||||
Statement {
|
||||
/// The list of layers to sort.
|
||||
names: Vec<LayerName>,
|
||||
},
|
||||
}
|
||||
|
||||
/// A [`@layer`][layer] rule.
|
||||
///
|
||||
/// [layer]: https://drafts.csswg.org/css-cascade-5/#layering
|
||||
#[derive(Debug, ToShmem)]
|
||||
pub struct LayerRule {
|
||||
/// The kind of layer rule we are.
|
||||
pub kind: LayerRuleKind,
|
||||
/// The source position where this media rule was found.
|
||||
/// A block `@layer <name>? { ... }`
|
||||
/// https://drafts.csswg.org/css-cascade-5/#layer-block
|
||||
pub struct LayerBlockRule {
|
||||
/// The layer name, or `None` if anonymous.
|
||||
pub name: Option<LayerName>,
|
||||
/// The nested rules.
|
||||
pub rules: Arc<Locked<CssRules>>,
|
||||
/// The source position where this rule was found.
|
||||
pub source_location: SourceLocation,
|
||||
}
|
||||
|
||||
impl ToCssWithGuard for LayerRule {
|
||||
impl ToCssWithGuard for LayerBlockRule {
|
||||
fn to_css(
|
||||
&self,
|
||||
guard: &SharedRwLockReadGuard,
|
||||
dest: &mut crate::str::CssStringWriter,
|
||||
) -> fmt::Result {
|
||||
dest.write_str("@layer")?;
|
||||
match self.kind {
|
||||
LayerRuleKind::Block {
|
||||
ref name,
|
||||
ref rules,
|
||||
} => {
|
||||
if let Some(ref name) = *name {
|
||||
dest.write_char(' ')?;
|
||||
name.to_css(&mut CssWriter::new(dest))?;
|
||||
}
|
||||
rules.read_with(guard).to_css_block(guard, dest)
|
||||
},
|
||||
LayerRuleKind::Statement { ref names } => {
|
||||
let mut writer = CssWriter::new(dest);
|
||||
let mut first = true;
|
||||
for name in &**names {
|
||||
if first {
|
||||
writer.write_char(' ')?;
|
||||
} else {
|
||||
writer.write_str(", ")?;
|
||||
}
|
||||
first = false;
|
||||
name.to_css(&mut writer)?;
|
||||
}
|
||||
dest.write_char(';')
|
||||
},
|
||||
if let Some(ref name) = self.name {
|
||||
dest.write_char(' ')?;
|
||||
name.to_css(&mut CssWriter::new(dest))?;
|
||||
}
|
||||
self.rules.read_with(guard).to_css_block(guard, dest)
|
||||
}
|
||||
}
|
||||
|
||||
impl DeepCloneWithLock for LayerRule {
|
||||
impl DeepCloneWithLock for LayerBlockRule {
|
||||
fn deep_clone_with_lock(
|
||||
&self,
|
||||
lock: &SharedRwLock,
|
||||
|
@ -211,25 +194,57 @@ impl DeepCloneWithLock for LayerRule {
|
|||
params: &DeepCloneParams,
|
||||
) -> Self {
|
||||
Self {
|
||||
kind: match self.kind {
|
||||
LayerRuleKind::Block {
|
||||
ref name,
|
||||
ref rules,
|
||||
} => LayerRuleKind::Block {
|
||||
name: name.clone(),
|
||||
rules: Arc::new(
|
||||
lock.wrap(
|
||||
rules
|
||||
.read_with(guard)
|
||||
.deep_clone_with_lock(lock, guard, params),
|
||||
),
|
||||
),
|
||||
},
|
||||
LayerRuleKind::Statement { ref names } => LayerRuleKind::Statement {
|
||||
names: names.clone(),
|
||||
},
|
||||
},
|
||||
name: self.name.clone(),
|
||||
rules: Arc::new(
|
||||
lock.wrap(
|
||||
self.rules
|
||||
.read_with(guard)
|
||||
.deep_clone_with_lock(lock, guard, params),
|
||||
),
|
||||
),
|
||||
source_location: self.source_location.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A statement `@layer <name>, <name>, <name>;`
|
||||
///
|
||||
/// https://drafts.csswg.org/css-cascade-5/#layer-empty
|
||||
#[derive(Clone, Debug, ToShmem)]
|
||||
pub struct LayerStatementRule {
|
||||
/// The list of layers to sort.
|
||||
pub names: Vec<LayerName>,
|
||||
/// The source position where this rule was found.
|
||||
pub source_location: SourceLocation,
|
||||
}
|
||||
|
||||
impl ToCssWithGuard for LayerStatementRule {
|
||||
fn to_css(
|
||||
&self,
|
||||
_: &SharedRwLockReadGuard,
|
||||
dest: &mut crate::str::CssStringWriter,
|
||||
) -> fmt::Result {
|
||||
let mut writer = CssWriter::new(dest);
|
||||
writer.write_str("@layer ")?;
|
||||
let mut first = true;
|
||||
for name in &*self.names {
|
||||
if !first {
|
||||
writer.write_str(", ")?;
|
||||
}
|
||||
first = false;
|
||||
name.to_css(&mut writer)?;
|
||||
}
|
||||
writer.write_char(';')
|
||||
}
|
||||
}
|
||||
|
||||
impl DeepCloneWithLock for LayerStatementRule {
|
||||
fn deep_clone_with_lock(
|
||||
&self,
|
||||
_: &SharedRwLock,
|
||||
_: &SharedRwLockReadGuard,
|
||||
_: &DeepCloneParams,
|
||||
) -> Self {
|
||||
self.clone()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
use crate::media_queries::MediaList;
|
||||
use crate::parser::ParserContext;
|
||||
use crate::shared_lock::{Locked, SharedRwLock};
|
||||
use crate::stylesheets::import_rule::{ImportRule, ImportLayer};
|
||||
use crate::stylesheets::import_rule::{ImportLayer, ImportRule};
|
||||
use crate::values::CssUrl;
|
||||
use cssparser::SourceLocation;
|
||||
use servo_arc::Arc;
|
||||
|
|
|
@ -51,12 +51,12 @@ pub use self::font_face_rule::FontFaceRule;
|
|||
pub use self::font_feature_values_rule::FontFeatureValuesRule;
|
||||
pub use self::import_rule::ImportRule;
|
||||
pub use self::keyframes_rule::KeyframesRule;
|
||||
pub use self::layer_rule::LayerRule;
|
||||
pub use self::layer_rule::{LayerBlockRule, LayerStatementRule};
|
||||
pub use self::loader::StylesheetLoader;
|
||||
pub use self::media_rule::MediaRule;
|
||||
pub use self::namespace_rule::NamespaceRule;
|
||||
pub use self::origin::{Origin, OriginSet, OriginSetIterator, PerOrigin, PerOriginIter};
|
||||
pub use self::page_rule::PageRule;
|
||||
pub use self::page_rule::{PageRule, PageSelector, PageSelectors};
|
||||
pub use self::rule_list::{CssRules, CssRulesHelpers};
|
||||
pub use self::rule_parser::{InsertRuleContext, State, TopLevelRuleParser};
|
||||
pub use self::rules_iterator::{AllRules, EffectiveRules};
|
||||
|
@ -261,7 +261,8 @@ pub enum CssRule {
|
|||
Supports(Arc<Locked<SupportsRule>>),
|
||||
Page(Arc<Locked<PageRule>>),
|
||||
Document(Arc<Locked<DocumentRule>>),
|
||||
Layer(Arc<Locked<LayerRule>>),
|
||||
LayerBlock(Arc<Locked<LayerBlockRule>>),
|
||||
LayerStatement(Arc<Locked<LayerStatementRule>>),
|
||||
ScrollTimeline(Arc<Locked<ScrollTimelineRule>>),
|
||||
}
|
||||
|
||||
|
@ -304,9 +305,8 @@ impl CssRule {
|
|||
lock.unconditional_shallow_size_of(ops) + lock.read_with(guard).size_of(guard, ops)
|
||||
},
|
||||
|
||||
// TODO(emilio): Add memory reporting for @layer rules.
|
||||
CssRule::Layer(_) => 0,
|
||||
CssRule::ScrollTimeline(_) => 0,
|
||||
// TODO(emilio): Add memory reporting for these rules.
|
||||
CssRule::LayerBlock(_) | CssRule::LayerStatement(_) | CssRule::ScrollTimeline(_) => 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -341,8 +341,9 @@ pub enum CssRuleType {
|
|||
Viewport = 15,
|
||||
// After viewport, all rules should return 0 from the API, but we still need
|
||||
// a constant somewhere.
|
||||
Layer = 16,
|
||||
ScrollTimeline = 17,
|
||||
LayerBlock = 16,
|
||||
LayerStatement = 17,
|
||||
ScrollTimeline = 18,
|
||||
}
|
||||
|
||||
#[allow(missing_docs)]
|
||||
|
@ -369,7 +370,8 @@ impl CssRule {
|
|||
CssRule::Supports(_) => CssRuleType::Supports,
|
||||
CssRule::Page(_) => CssRuleType::Page,
|
||||
CssRule::Document(_) => CssRuleType::Document,
|
||||
CssRule::Layer(_) => CssRuleType::Layer,
|
||||
CssRule::LayerBlock(_) => CssRuleType::LayerBlock,
|
||||
CssRule::LayerStatement(_) => CssRuleType::LayerStatement,
|
||||
CssRule::ScrollTimeline(_) => CssRuleType::ScrollTimeline,
|
||||
}
|
||||
}
|
||||
|
@ -504,16 +506,22 @@ impl DeepCloneWithLock for CssRule {
|
|||
lock.wrap(rule.deep_clone_with_lock(lock, guard, params)),
|
||||
))
|
||||
},
|
||||
CssRule::Layer(ref arc) => {
|
||||
CssRule::LayerStatement(ref arc) => {
|
||||
let rule = arc.read_with(guard);
|
||||
CssRule::Layer(Arc::new(
|
||||
CssRule::LayerStatement(Arc::new(
|
||||
lock.wrap(rule.deep_clone_with_lock(lock, guard, params)),
|
||||
))
|
||||
}
|
||||
},
|
||||
CssRule::LayerBlock(ref arc) => {
|
||||
let rule = arc.read_with(guard);
|
||||
CssRule::LayerBlock(Arc::new(
|
||||
lock.wrap(rule.deep_clone_with_lock(lock, guard, params)),
|
||||
))
|
||||
},
|
||||
CssRule::ScrollTimeline(ref arc) => {
|
||||
let rule = arc.read_with(guard);
|
||||
CssRule::ScrollTimeline(Arc::new(lock.wrap(rule.clone())))
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -534,7 +542,8 @@ impl ToCssWithGuard for CssRule {
|
|||
CssRule::Supports(ref lock) => lock.read_with(guard).to_css(guard, dest),
|
||||
CssRule::Page(ref lock) => lock.read_with(guard).to_css(guard, dest),
|
||||
CssRule::Document(ref lock) => lock.read_with(guard).to_css(guard, dest),
|
||||
CssRule::Layer(ref lock) => lock.read_with(guard).to_css(guard, dest),
|
||||
CssRule::LayerBlock(ref lock) => lock.read_with(guard).to_css(guard, dest),
|
||||
CssRule::LayerStatement(ref lock) => lock.read_with(guard).to_css(guard, dest),
|
||||
CssRule::ScrollTimeline(ref lock) => lock.read_with(guard).to_css(guard, dest),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
use crate::shared_lock::{SharedRwLockReadGuard, ToCssWithGuard};
|
||||
use crate::str::CssStringWriter;
|
||||
use crate::{Namespace, Prefix};
|
||||
use cssparser::{self, SourceLocation};
|
||||
use cssparser::SourceLocation;
|
||||
use std::fmt::{self, Write};
|
||||
use style_traits::{CssWriter, ToCss};
|
||||
|
||||
|
@ -25,15 +25,15 @@ pub struct NamespaceRule {
|
|||
|
||||
impl ToCssWithGuard for NamespaceRule {
|
||||
// https://drafts.csswg.org/cssom/#serialize-a-css-rule CSSNamespaceRule
|
||||
fn to_css(&self, _guard: &SharedRwLockReadGuard, dest: &mut CssStringWriter) -> fmt::Result {
|
||||
fn to_css(&self, _guard: &SharedRwLockReadGuard, dest_str: &mut CssStringWriter) -> fmt::Result {
|
||||
let mut dest = CssWriter::new(dest_str);
|
||||
dest.write_str("@namespace ")?;
|
||||
if let Some(ref prefix) = self.prefix {
|
||||
let prefix = prefix.to_string();
|
||||
cssparser::serialize_identifier(&prefix, dest)?;
|
||||
dest.write_str(" ")?;
|
||||
prefix.to_css(&mut dest)?;
|
||||
dest.write_char(' ')?;
|
||||
}
|
||||
dest.write_str("url(")?;
|
||||
self.url.to_string().to_css(&mut CssWriter::new(dest))?;
|
||||
self.url.to_string().to_css(&mut dest)?;
|
||||
dest.write_str(");")
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,7 +10,7 @@ use std::ops::BitOrAssign;
|
|||
/// Each style rule has an origin, which determines where it enters the cascade.
|
||||
///
|
||||
/// <https://drafts.csswg.org/css-cascade/#cascading-origins>
|
||||
#[derive(Clone, Copy, Debug, Eq, MallocSizeOf, PartialEq, ToShmem)]
|
||||
#[derive(Clone, Copy, Debug, Eq, MallocSizeOf, PartialEq, ToShmem, PartialOrd, Ord)]
|
||||
#[repr(u8)]
|
||||
pub enum Origin {
|
||||
/// <https://drafts.csswg.org/css-cascade/#cascade-origin-user-agent>
|
||||
|
|
|
@ -6,27 +6,91 @@
|
|||
//!
|
||||
//! [page]: https://drafts.csswg.org/css2/page.html#page-box
|
||||
|
||||
use crate::parser::{Parse, ParserContext};
|
||||
use crate::properties::PropertyDeclarationBlock;
|
||||
use crate::shared_lock::{DeepCloneParams, DeepCloneWithLock, Locked};
|
||||
use crate::shared_lock::{SharedRwLock, SharedRwLockReadGuard, ToCssWithGuard};
|
||||
use crate::str::CssStringWriter;
|
||||
use cssparser::SourceLocation;
|
||||
use crate::values::{AtomIdent, CustomIdent};
|
||||
use style_traits::{CssWriter, ParseError, ToCss};
|
||||
use cssparser::{Parser, SourceLocation};
|
||||
#[cfg(feature = "gecko")]
|
||||
use malloc_size_of::{MallocSizeOf, MallocSizeOfOps, MallocUnconditionalShallowSizeOf};
|
||||
use servo_arc::Arc;
|
||||
use std::fmt::{self, Write};
|
||||
|
||||
/// Type of a single [`@page`][page selector]
|
||||
///
|
||||
/// We do not support pseudo selectors yet.
|
||||
/// [page-selectors]: https://drafts.csswg.org/css2/page.html#page-selectors
|
||||
#[derive(Clone, Debug, Eq, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToCss, ToShmem)]
|
||||
pub struct PageSelector(pub AtomIdent);
|
||||
|
||||
impl PageSelector {
|
||||
/// Checks if the ident matches a page-name's ident.
|
||||
///
|
||||
/// This does not currently take pseudo selectors into account.
|
||||
#[inline]
|
||||
pub fn ident_matches(&self, other: &CustomIdent) -> bool {
|
||||
self.0.0 == other.0
|
||||
}
|
||||
}
|
||||
|
||||
impl Parse for PageSelector {
|
||||
fn parse<'i, 't>(
|
||||
_context: &ParserContext,
|
||||
input: &mut Parser<'i, 't>,
|
||||
) -> Result<Self, ParseError<'i>> {
|
||||
let s = input.expect_ident()?;
|
||||
Ok(PageSelector(AtomIdent::from(&**s)))
|
||||
}
|
||||
}
|
||||
|
||||
/// A list of [`@page`][page selectors]
|
||||
///
|
||||
/// [page-selectors]: https://drafts.csswg.org/css2/page.html#page-selectors
|
||||
#[derive(Clone, Debug, Default, MallocSizeOf, ToCss, ToShmem)]
|
||||
#[css(comma)]
|
||||
pub struct PageSelectors(#[css(iterable)] pub Box<[PageSelector]>);
|
||||
|
||||
impl PageSelectors {
|
||||
/// Creates a new PageSelectors from a Vec, as from parse_comma_separated
|
||||
#[inline]
|
||||
pub fn new(s: Vec<PageSelector>) -> Self {
|
||||
PageSelectors(s.into())
|
||||
}
|
||||
/// Returns true iff there are any page selectors
|
||||
#[inline]
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.as_slice().is_empty()
|
||||
}
|
||||
/// Get the underlying PageSelector data as a slice
|
||||
#[inline]
|
||||
pub fn as_slice(&self) -> &[PageSelector] {
|
||||
&*self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl Parse for PageSelectors {
|
||||
fn parse<'i, 't>(
|
||||
context: &ParserContext,
|
||||
input: &mut Parser<'i, 't>,
|
||||
) -> Result<Self, ParseError<'i>> {
|
||||
Ok(PageSelectors::new(input.parse_comma_separated(|i| PageSelector::parse(context, i))?))
|
||||
}
|
||||
}
|
||||
|
||||
/// A [`@page`][page] rule.
|
||||
///
|
||||
/// This implements only a limited subset of the CSS
|
||||
/// 2.2 syntax.
|
||||
///
|
||||
/// In this subset, [page selectors][page-selectors] are not implemented.
|
||||
///
|
||||
/// [page]: https://drafts.csswg.org/css2/page.html#page-box
|
||||
/// [page-selectors]: https://drafts.csswg.org/css2/page.html#page-selectors
|
||||
#[derive(Debug, ToShmem)]
|
||||
#[derive(Clone, Debug, ToShmem)]
|
||||
pub struct PageRule {
|
||||
/// Selectors of the page-rule
|
||||
pub selectors: PageSelectors,
|
||||
/// The declaration block this page rule contains.
|
||||
pub block: Arc<Locked<PropertyDeclarationBlock>>,
|
||||
/// The source position this rule was found at.
|
||||
|
@ -38,7 +102,7 @@ impl PageRule {
|
|||
#[cfg(feature = "gecko")]
|
||||
pub fn size_of(&self, guard: &SharedRwLockReadGuard, ops: &mut MallocSizeOfOps) -> usize {
|
||||
// Measurement of other fields may be added later.
|
||||
self.block.unconditional_shallow_size_of(ops) + self.block.read_with(guard).size_of(ops)
|
||||
self.block.unconditional_shallow_size_of(ops) + self.block.read_with(guard).size_of(ops) + self.selectors.size_of(ops)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -46,13 +110,18 @@ impl ToCssWithGuard for PageRule {
|
|||
/// Serialization of PageRule is not specced, adapted from steps for
|
||||
/// StyleRule.
|
||||
fn to_css(&self, guard: &SharedRwLockReadGuard, dest: &mut CssStringWriter) -> fmt::Result {
|
||||
dest.write_str("@page { ")?;
|
||||
dest.write_str("@page ")?;
|
||||
if !self.selectors.is_empty() {
|
||||
self.selectors.to_css(&mut CssWriter::new(dest))?;
|
||||
dest.write_char(' ')?;
|
||||
}
|
||||
dest.write_str("{ ")?;
|
||||
let declaration_block = self.block.read_with(guard);
|
||||
declaration_block.to_css(dest)?;
|
||||
if !declaration_block.declarations().is_empty() {
|
||||
dest.write_str(" ")?;
|
||||
dest.write_char(' ')?;
|
||||
}
|
||||
dest.write_str("}")
|
||||
dest.write_char('}')
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -64,6 +133,7 @@ impl DeepCloneWithLock for PageRule {
|
|||
_params: &DeepCloneParams,
|
||||
) -> Self {
|
||||
PageRule {
|
||||
selectors: self.selectors.clone(),
|
||||
block: Arc::new(lock.wrap(self.block.read_with(&guard).clone())),
|
||||
source_location: self.source_location.clone(),
|
||||
}
|
||||
|
|
|
@ -17,13 +17,13 @@ use crate::stylesheets::document_rule::DocumentCondition;
|
|||
use crate::stylesheets::font_feature_values_rule::parse_family_name_list;
|
||||
use crate::stylesheets::import_rule::ImportLayer;
|
||||
use crate::stylesheets::keyframes_rule::parse_keyframe_list;
|
||||
use crate::stylesheets::layer_rule::{LayerName, LayerRuleKind};
|
||||
use crate::stylesheets::layer_rule::{LayerBlockRule, LayerName, LayerStatementRule};
|
||||
use crate::stylesheets::scroll_timeline_rule::ScrollTimelineDescriptors;
|
||||
use crate::stylesheets::stylesheet::Namespaces;
|
||||
use crate::stylesheets::supports_rule::SupportsCondition;
|
||||
use crate::stylesheets::{
|
||||
viewport_rule, AllowImportRules, CorsMode, CssRule, CssRuleType, CssRules, DocumentRule,
|
||||
FontFeatureValuesRule, KeyframesRule, LayerRule, MediaRule, NamespaceRule, PageRule,
|
||||
FontFeatureValuesRule, KeyframesRule, MediaRule, NamespaceRule, PageRule, PageSelectors,
|
||||
RulesMutateError, ScrollTimelineRule, StyleRule, StylesheetLoader, SupportsRule, ViewportRule,
|
||||
};
|
||||
use crate::values::computed::font::FamilyName;
|
||||
|
@ -168,8 +168,8 @@ pub enum AtRulePrelude {
|
|||
Viewport,
|
||||
/// A @keyframes rule, with its animation name and vendor prefix if exists.
|
||||
Keyframes(KeyframesName, Option<VendorPrefix>),
|
||||
/// A @page rule prelude.
|
||||
Page,
|
||||
/// A @page rule prelude, with its page name if it exists.
|
||||
Page(PageSelectors),
|
||||
/// A @document rule, with its conditional.
|
||||
Document(DocumentCondition),
|
||||
/// A @import rule prelude.
|
||||
|
@ -289,7 +289,7 @@ impl<'a, 'i> AtRuleParser<'i> for TopLevelRuleParser<'a> {
|
|||
&mut self,
|
||||
prelude: AtRulePrelude,
|
||||
start: &ParserState,
|
||||
) -> Result<Self::AtRule, ()> {
|
||||
) -> Result<Self::AtRule, ()> {
|
||||
let rule = match prelude {
|
||||
AtRulePrelude::Import(url, media, layer) => {
|
||||
let loader = self
|
||||
|
@ -468,8 +468,13 @@ impl<'a, 'b, 'i> AtRuleParser<'i> for NestedRuleParser<'a, 'b> {
|
|||
let name = KeyframesName::parse(self.context, input)?;
|
||||
AtRulePrelude::Keyframes(name, prefix)
|
||||
},
|
||||
"page" if cfg!(feature = "gecko") => {
|
||||
AtRulePrelude::Page
|
||||
#[cfg(feature = "gecko")]
|
||||
"page" => {
|
||||
AtRulePrelude::Page(if static_prefs::pref!("layout.css.named-pages.enabled") {
|
||||
input.try_parse(|i| PageSelectors::parse(self.context, i)).unwrap_or_default()
|
||||
} else {
|
||||
PageSelectors::default()
|
||||
})
|
||||
},
|
||||
"-moz-document" if cfg!(feature = "gecko") => {
|
||||
let cond = DocumentCondition::parse(self.context, input)?;
|
||||
|
@ -583,7 +588,7 @@ impl<'a, 'b, 'i> AtRuleParser<'i> for NestedRuleParser<'a, 'b> {
|
|||
},
|
||||
))))
|
||||
},
|
||||
AtRulePrelude::Page => {
|
||||
AtRulePrelude::Page(selectors) => {
|
||||
let context = ParserContext::new_with_rule_type(
|
||||
self.context,
|
||||
CssRuleType::Page,
|
||||
|
@ -592,6 +597,7 @@ impl<'a, 'b, 'i> AtRuleParser<'i> for NestedRuleParser<'a, 'b> {
|
|||
|
||||
let declarations = parse_property_declaration_list(&context, input, None);
|
||||
Ok(CssRule::Page(Arc::new(self.shared_lock.wrap(PageRule {
|
||||
selectors,
|
||||
block: Arc::new(self.shared_lock.wrap(declarations)),
|
||||
source_location: start.source_location(),
|
||||
}))))
|
||||
|
@ -613,12 +619,10 @@ impl<'a, 'b, 'i> AtRuleParser<'i> for NestedRuleParser<'a, 'b> {
|
|||
0 | 1 => names.into_iter().next(),
|
||||
_ => return Err(input.new_error(BasicParseErrorKind::AtRuleBodyInvalid)),
|
||||
};
|
||||
Ok(CssRule::Layer(Arc::new(self.shared_lock.wrap(
|
||||
LayerRule {
|
||||
kind: LayerRuleKind::Block {
|
||||
name,
|
||||
rules: self.parse_nested_rules(input, CssRuleType::Layer),
|
||||
},
|
||||
Ok(CssRule::LayerBlock(Arc::new(self.shared_lock.wrap(
|
||||
LayerBlockRule {
|
||||
name,
|
||||
rules: self.parse_nested_rules(input, CssRuleType::LayerBlock),
|
||||
source_location: start.source_location(),
|
||||
},
|
||||
))))
|
||||
|
@ -650,14 +654,14 @@ impl<'a, 'b, 'i> AtRuleParser<'i> for NestedRuleParser<'a, 'b> {
|
|||
&mut self,
|
||||
prelude: AtRulePrelude,
|
||||
start: &ParserState,
|
||||
) -> Result<Self::AtRule, ()> {
|
||||
) -> Result<Self::AtRule, ()> {
|
||||
Ok(match prelude {
|
||||
AtRulePrelude::Layer(names) => {
|
||||
if names.is_empty() {
|
||||
return Err(());
|
||||
}
|
||||
CssRule::Layer(Arc::new(self.shared_lock.wrap(LayerRule {
|
||||
kind: LayerRuleKind::Statement { names },
|
||||
CssRule::LayerStatement(Arc::new(self.shared_lock.wrap(LayerStatementRule {
|
||||
names,
|
||||
source_location: start.source_location(),
|
||||
})))
|
||||
},
|
||||
|
@ -687,7 +691,10 @@ fn check_for_useless_selector(
|
|||
}
|
||||
if found_host && found_non_host {
|
||||
let location = input.current_source_location();
|
||||
context.log_css_error(location, ContextualParseError::NeverMatchingHostSelector(selector.to_css_string()));
|
||||
context.log_css_error(
|
||||
location,
|
||||
ContextualParseError::NeverMatchingHostSelector(selector.to_css_string()),
|
||||
);
|
||||
continue 'selector_loop;
|
||||
}
|
||||
}
|
||||
|
@ -710,7 +717,7 @@ impl<'a, 'b, 'i> QualifiedRuleParser<'i> for NestedRuleParser<'a, 'b> {
|
|||
let selector_parser = SelectorParser {
|
||||
stylesheet_origin: self.context.stylesheet_origin,
|
||||
namespaces: self.namespaces,
|
||||
url_data: Some(self.context.url_data),
|
||||
url_data: self.context.url_data,
|
||||
};
|
||||
let selectors = SelectorList::parse(&selector_parser, input)?;
|
||||
if self.context.error_reporting_enabled() {
|
||||
|
|
|
@ -70,6 +70,7 @@ where
|
|||
CssRule::Keyframes(_) |
|
||||
CssRule::ScrollTimeline(_) |
|
||||
CssRule::Page(_) |
|
||||
CssRule::LayerStatement(_) |
|
||||
CssRule::FontFeatureValues(_) => None,
|
||||
CssRule::Import(ref import_rule) => {
|
||||
let import_rule = import_rule.read_with(guard);
|
||||
|
@ -103,15 +104,10 @@ where
|
|||
}
|
||||
Some(supports_rule.rules.read_with(guard).0.iter())
|
||||
},
|
||||
CssRule::Layer(ref lock) => {
|
||||
use crate::stylesheets::layer_rule::LayerRuleKind;
|
||||
|
||||
CssRule::LayerBlock(ref lock) => {
|
||||
let layer_rule = lock.read_with(guard);
|
||||
match layer_rule.kind {
|
||||
LayerRuleKind::Block { ref rules, .. } => Some(rules.read_with(guard).0.iter()),
|
||||
LayerRuleKind::Statement { .. } => None,
|
||||
}
|
||||
}
|
||||
Some(layer_rule.rules.read_with(guard).0.iter())
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -323,7 +319,8 @@ impl<'a, 'b> EffectiveRulesIterator<'a, 'b> {
|
|||
guard: &'a SharedRwLockReadGuard<'b>,
|
||||
rule: &'a CssRule,
|
||||
) -> Self {
|
||||
let children = RulesIterator::<AllRules>::children(rule, device, quirks_mode, guard, &mut false);
|
||||
let children =
|
||||
RulesIterator::<AllRules>::children(rule, device, quirks_mode, guard, &mut false);
|
||||
EffectiveRulesIterator::new(device, quirks_mode, guard, children.unwrap_or([].iter()))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -157,15 +157,19 @@ impl<'a, 'b, 'i> DeclarationParser<'i> for ScrollTimelineDescriptorsParser<'a, '
|
|||
/// The scroll-timeline source.
|
||||
///
|
||||
/// https://drafts.csswg.org/scroll-animations/#descdef-scroll-timeline-source
|
||||
// FIXME: Bug 1733260 may drop the entire @scroll-timeline, and now we don't support source other
|
||||
// than the default value (so use #[css(skip)]).
|
||||
#[derive(Clone, Debug, Parse, PartialEq, ToCss, ToShmem)]
|
||||
pub enum Source {
|
||||
/// The scroll container.
|
||||
#[css(skip)]
|
||||
Selector(ScrollTimelineSelector),
|
||||
/// The initial value. The scrollingElement of the Document associated with the Window that is
|
||||
/// the current global object.
|
||||
Auto,
|
||||
/// Null. However, it's not clear what is the expected behavior of this. See the spec issue:
|
||||
/// https://drafts.csswg.org/scroll-animations/#issue-0d1e73bd
|
||||
#[css(skip)]
|
||||
None,
|
||||
}
|
||||
|
||||
|
@ -182,7 +186,8 @@ impl Default for Source {
|
|||
/// definition of ScrollTimelineOptions (WebIDL API).
|
||||
/// https://drafts.csswg.org/scroll-animations/#dom-scrolltimelineoptions-orientation
|
||||
#[derive(Clone, Copy, Debug, MallocSizeOf, Eq, Parse, PartialEq, PartialOrd, ToCss, ToShmem)]
|
||||
pub enum Orientation {
|
||||
#[repr(u8)]
|
||||
pub enum ScrollDirection {
|
||||
/// The initial value.
|
||||
Auto,
|
||||
/// The direction along the block axis. This is the default value.
|
||||
|
@ -195,12 +200,15 @@ pub enum Orientation {
|
|||
Vertical,
|
||||
}
|
||||
|
||||
impl Default for Orientation {
|
||||
impl Default for ScrollDirection {
|
||||
fn default() -> Self {
|
||||
Orientation::Auto
|
||||
ScrollDirection::Auto
|
||||
}
|
||||
}
|
||||
|
||||
// Avoid name collision in cbindgen with StyleOrientation.
|
||||
pub use self::ScrollDirection as Orientation;
|
||||
|
||||
/// Scroll-timeline offsets. We treat None as an empty vector.
|
||||
/// value: none | <scroll-timeline-offset>#
|
||||
///
|
||||
|
@ -211,7 +219,7 @@ pub struct ScrollOffsets(#[css(if_empty = "none", iterable)] Box<[ScrollTimeline
|
|||
|
||||
impl Parse for ScrollOffsets {
|
||||
fn parse<'i, 't>(
|
||||
context: &ParserContext,
|
||||
_context: &ParserContext,
|
||||
input: &mut Parser<'i, 't>,
|
||||
) -> Result<Self, ParseError<'i>> {
|
||||
if input.try_parse(|i| i.expect_ident_matching("none")).is_ok() {
|
||||
|
@ -220,7 +228,7 @@ impl Parse for ScrollOffsets {
|
|||
|
||||
Ok(ScrollOffsets(
|
||||
input
|
||||
.parse_comma_separated(|i| ScrollTimelineOffset::parse(context, i))?
|
||||
.parse_comma_separated(|i| ScrollTimelineOffset::parse(i))?
|
||||
.into_boxed_slice(),
|
||||
))
|
||||
}
|
||||
|
@ -230,14 +238,18 @@ impl Parse for ScrollOffsets {
|
|||
/// value: auto | <length-percentage> | <element-offset>
|
||||
///
|
||||
/// https://drafts.csswg.org/scroll-animations/#typedef-scroll-timeline-offset
|
||||
// FIXME: Bug 1733260 may drop the entire @scroll-timeline, and now we don't support
|
||||
// <scroll-timeline-offset> other than the default value (so use #[css(skip)]).
|
||||
#[derive(Clone, Debug, Parse, PartialEq, ToCss, ToShmem)]
|
||||
pub enum ScrollTimelineOffset {
|
||||
/// The initial value. A container-based offset.
|
||||
Auto,
|
||||
/// A container-based offset with the distance indicated by the value along source's scroll
|
||||
/// range in orientation.
|
||||
#[css(skip)]
|
||||
LengthPercentage(LengthPercentage),
|
||||
/// An element-based offset.
|
||||
#[css(skip)]
|
||||
ElementOffset(ElementOffset),
|
||||
}
|
||||
|
||||
|
@ -312,7 +324,6 @@ impl ToCss for ScrollTimelineSelector {
|
|||
where
|
||||
W: Write,
|
||||
{
|
||||
use crate::cssparser::ToCss as CssparserToCss;
|
||||
dest.write_str("selector(")?;
|
||||
dest.write_char('#')?;
|
||||
self.0.to_css(dest)?;
|
||||
|
|
|
@ -16,7 +16,6 @@ use crate::stylesheets::{CssRule, CssRules, Origin, UrlExtraData};
|
|||
use crate::use_counters::UseCounters;
|
||||
use crate::{Namespace, Prefix};
|
||||
use cssparser::{Parser, ParserInput, RuleListParser};
|
||||
use fallible::FallibleVec;
|
||||
use fxhash::FxHashMap;
|
||||
#[cfg(feature = "gecko")]
|
||||
use malloc_size_of::{MallocSizeOfOps, MallocUnconditionalShallowSizeOf};
|
||||
|
@ -363,7 +362,8 @@ impl SanitizationKind {
|
|||
CssRule::Import(..) |
|
||||
// TODO(emilio): Perhaps Layer should not be always sanitized? But
|
||||
// we sanitize @media and co, so this seems safer for now.
|
||||
CssRule::Layer(..) => false,
|
||||
CssRule::LayerStatement(..) |
|
||||
CssRule::LayerBlock(..) => false,
|
||||
|
||||
CssRule::FontFace(..) | CssRule::Namespace(..) | CssRule::Style(..) => true,
|
||||
|
||||
|
@ -505,9 +505,10 @@ impl Stylesheet {
|
|||
// Use a fallible push here, and if it fails, just fall
|
||||
// out of the loop. This will cause the page to be
|
||||
// shown incorrectly, but it's better than OOMing.
|
||||
if rules.try_push(rule).is_err() {
|
||||
if rules.try_reserve(1).is_err() {
|
||||
break;
|
||||
}
|
||||
rules.push(rule);
|
||||
},
|
||||
Err((error, slice)) => {
|
||||
let location = error.location;
|
||||
|
@ -591,9 +592,11 @@ impl Clone for Stylesheet {
|
|||
// Make a deep clone of the media, using the new lock.
|
||||
let media = self.media.read_with(&guard).clone();
|
||||
let media = Arc::new(lock.wrap(media));
|
||||
let contents = Arc::new(self
|
||||
.contents
|
||||
.deep_clone_with_lock(&lock, &guard, &DeepCloneParams));
|
||||
let contents = Arc::new(self.contents.deep_clone_with_lock(
|
||||
&lock,
|
||||
&guard,
|
||||
&DeepCloneParams,
|
||||
));
|
||||
|
||||
Stylesheet {
|
||||
contents,
|
||||
|
|
|
@ -335,7 +335,7 @@ impl RawSelector {
|
|||
let parser = SelectorParser {
|
||||
namespaces,
|
||||
stylesheet_origin: context.stylesheet_origin,
|
||||
url_data: Some(context.url_data),
|
||||
url_data: context.url_data,
|
||||
};
|
||||
|
||||
#[allow(unused_variables)]
|
||||
|
|
|
@ -4,14 +4,18 @@
|
|||
|
||||
//! Selector matching.
|
||||
|
||||
use crate::applicable_declarations::{ApplicableDeclarationBlock, ApplicableDeclarationList};
|
||||
use crate::applicable_declarations::{
|
||||
ApplicableDeclarationBlock, ApplicableDeclarationList, CascadePriority,
|
||||
};
|
||||
use crate::context::{CascadeInputs, QuirksMode};
|
||||
use crate::dom::{TElement, TShadowRoot};
|
||||
use crate::element_state::{DocumentState, ElementState};
|
||||
#[cfg(feature = "gecko")]
|
||||
use crate::gecko_bindings::structs::{ServoStyleSetSizes, StyleRuleInclusion};
|
||||
use crate::invalidation::element::invalidation_map::InvalidationMap;
|
||||
use crate::invalidation::media_queries::{EffectiveMediaQueryResults, MediaListKey, ToMediaListKey};
|
||||
use crate::invalidation::media_queries::{
|
||||
EffectiveMediaQueryResults, MediaListKey, ToMediaListKey,
|
||||
};
|
||||
use crate::invalidation::stylesheets::RuleChangeKind;
|
||||
use crate::media_queries::Device;
|
||||
use crate::properties::{self, CascadeMode, ComputedValues};
|
||||
|
@ -25,19 +29,23 @@ use crate::shared_lock::{Locked, SharedRwLockReadGuard, StylesheetGuards};
|
|||
use crate::stylesheet_set::{DataValidity, DocumentStylesheetSet, SheetRebuildKind};
|
||||
use crate::stylesheet_set::{DocumentStylesheetFlusher, SheetCollectionFlusher};
|
||||
use crate::stylesheets::keyframes_rule::KeyframesAnimation;
|
||||
use crate::stylesheets::layer_rule::{LayerName, LayerId, LayerOrder};
|
||||
use crate::stylesheets::layer_rule::{LayerId, LayerName, LayerOrder};
|
||||
use crate::stylesheets::viewport_rule::{self, MaybeNew, ViewportRule};
|
||||
use crate::stylesheets::{StyleRule, StylesheetInDocument, StylesheetContents};
|
||||
#[cfg(feature = "gecko")]
|
||||
use crate::stylesheets::{CounterStyleRule, FontFaceRule, FontFeatureValuesRule, PageRule};
|
||||
use crate::stylesheets::{CssRule, Origin, OriginSet, PerOrigin, PerOriginIter, EffectiveRulesIterator};
|
||||
use crate::stylesheets::{
|
||||
CounterStyleRule, FontFaceRule, FontFeatureValuesRule, ScrollTimelineRule,
|
||||
};
|
||||
use crate::stylesheets::{
|
||||
CssRule, EffectiveRulesIterator, Origin, OriginSet, PageRule, PerOrigin, PerOriginIter,
|
||||
};
|
||||
use crate::stylesheets::{StyleRule, StylesheetContents, StylesheetInDocument};
|
||||
use crate::thread_state::{self, ThreadState};
|
||||
use crate::{Atom, LocalName, Namespace, WeakAtom};
|
||||
use fallible::FallibleVec;
|
||||
use hashglobe::FailedAllocationError;
|
||||
use malloc_size_of::MallocSizeOf;
|
||||
use crate::AllocErr;
|
||||
use crate::{Atom, LocalName, Namespace, ShrinkIfNeeded, WeakAtom};
|
||||
use fxhash::FxHashMap;
|
||||
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::VisitedHandlingMode;
|
||||
|
@ -48,11 +56,11 @@ use selectors::NthIndexCache;
|
|||
use servo_arc::{Arc, ArcBorrow};
|
||||
use smallbitvec::SmallBitVec;
|
||||
use smallvec::SmallVec;
|
||||
use std::cmp::Ordering;
|
||||
use std::hash::{Hash, Hasher};
|
||||
use std::sync::Mutex;
|
||||
use std::{mem, ops};
|
||||
use std::hash::{Hash, Hasher};
|
||||
use style_traits::viewport::ViewportConstraints;
|
||||
use fxhash::FxHashMap;
|
||||
|
||||
/// The type of the stylesheets that the stylist contains.
|
||||
#[cfg(feature = "servo")]
|
||||
|
@ -93,7 +101,7 @@ struct CascadeDataCacheKey {
|
|||
unsafe impl Send for CascadeDataCacheKey {}
|
||||
unsafe impl Sync for CascadeDataCacheKey {}
|
||||
|
||||
trait CascadeDataCacheEntry : Sized {
|
||||
trait CascadeDataCacheEntry: Sized {
|
||||
/// Returns a reference to the cascade data.
|
||||
fn cascade_data(&self) -> &CascadeData;
|
||||
/// Rebuilds the cascade data for the new stylesheet collection. The
|
||||
|
@ -104,7 +112,7 @@ trait CascadeDataCacheEntry : Sized {
|
|||
collection: SheetCollectionFlusher<S>,
|
||||
guard: &SharedRwLockReadGuard,
|
||||
old_entry: &Self,
|
||||
) -> Result<Arc<Self>, FailedAllocationError>
|
||||
) -> Result<Arc<Self>, AllocErr>
|
||||
where
|
||||
S: StylesheetInDocument + PartialEq + 'static;
|
||||
/// Measures heap memory usage.
|
||||
|
@ -121,7 +129,9 @@ where
|
|||
Entry: CascadeDataCacheEntry,
|
||||
{
|
||||
fn new() -> Self {
|
||||
Self { entries: Default::default() }
|
||||
Self {
|
||||
entries: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
fn len(&self) -> usize {
|
||||
|
@ -139,7 +149,7 @@ where
|
|||
collection: SheetCollectionFlusher<S>,
|
||||
guard: &SharedRwLockReadGuard,
|
||||
old_entry: &Entry,
|
||||
) -> Result<Option<Arc<Entry>>, FailedAllocationError>
|
||||
) -> Result<Option<Arc<Entry>>, AllocErr>
|
||||
where
|
||||
S: StylesheetInDocument + PartialEq + 'static,
|
||||
{
|
||||
|
@ -165,15 +175,9 @@ where
|
|||
match self.entries.entry(key) {
|
||||
HashMapEntry::Vacant(e) => {
|
||||
debug!("> Picking the slow path (not in the cache)");
|
||||
new_entry = Entry::rebuild(
|
||||
device,
|
||||
quirks_mode,
|
||||
collection,
|
||||
guard,
|
||||
old_entry,
|
||||
)?;
|
||||
new_entry = Entry::rebuild(device, quirks_mode, collection, guard, old_entry)?;
|
||||
e.insert(new_entry.clone());
|
||||
}
|
||||
},
|
||||
HashMapEntry::Occupied(mut e) => {
|
||||
// Avoid reusing our old entry (this can happen if we get
|
||||
// invalidated due to CSSOM mutations and our old stylesheet
|
||||
|
@ -192,15 +196,9 @@ where
|
|||
}
|
||||
|
||||
debug!("> Picking the slow path due to same entry as old");
|
||||
new_entry = Entry::rebuild(
|
||||
device,
|
||||
quirks_mode,
|
||||
collection,
|
||||
guard,
|
||||
old_entry,
|
||||
)?;
|
||||
new_entry = Entry::rebuild(device, quirks_mode, collection, guard, old_entry)?;
|
||||
e.insert(new_entry.clone());
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
Ok(Some(new_entry))
|
||||
|
@ -270,9 +268,9 @@ impl CascadeDataCacheEntry for UserAgentCascadeData {
|
|||
collection: SheetCollectionFlusher<S>,
|
||||
guard: &SharedRwLockReadGuard,
|
||||
_old: &Self,
|
||||
) -> Result<Arc<Self>, FailedAllocationError>
|
||||
) -> Result<Arc<Self>, AllocErr>
|
||||
where
|
||||
S: StylesheetInDocument + PartialEq + 'static
|
||||
S: StylesheetInDocument + PartialEq + 'static,
|
||||
{
|
||||
// TODO: Maybe we should support incremental rebuilds, though they seem
|
||||
// uncommon and rebuild() doesn't deal with
|
||||
|
@ -293,7 +291,7 @@ impl CascadeDataCacheEntry for UserAgentCascadeData {
|
|||
)?;
|
||||
}
|
||||
|
||||
new_data.cascade_data.compute_layer_order();
|
||||
new_data.cascade_data.did_finish_rebuild();
|
||||
|
||||
Ok(Arc::new(new_data))
|
||||
}
|
||||
|
@ -386,7 +384,7 @@ impl DocumentCascadeData {
|
|||
quirks_mode: QuirksMode,
|
||||
mut flusher: DocumentStylesheetFlusher<'a, S>,
|
||||
guards: &StylesheetGuards,
|
||||
) -> Result<(), FailedAllocationError>
|
||||
) -> Result<(), AllocErr>
|
||||
where
|
||||
S: StylesheetInDocument + PartialEq + 'static,
|
||||
{
|
||||
|
@ -600,17 +598,12 @@ impl Stylist {
|
|||
old_data: &CascadeData,
|
||||
collection: SheetCollectionFlusher<S>,
|
||||
guard: &SharedRwLockReadGuard,
|
||||
) -> Result<Option<Arc<CascadeData>>, FailedAllocationError>
|
||||
) -> Result<Option<Arc<CascadeData>>, AllocErr>
|
||||
where
|
||||
S: StylesheetInDocument + PartialEq + 'static,
|
||||
{
|
||||
self.author_data_cache.lookup(
|
||||
&self.device,
|
||||
self.quirks_mode,
|
||||
collection,
|
||||
guard,
|
||||
old_data,
|
||||
)
|
||||
self.author_data_cache
|
||||
.lookup(&self.device, self.quirks_mode, collection, guard, old_data)
|
||||
}
|
||||
|
||||
/// Iterate over the extra data in origin order.
|
||||
|
@ -1483,9 +1476,15 @@ impl Stylist {
|
|||
/* pseudo = */ None,
|
||||
self.rule_tree.root(),
|
||||
guards,
|
||||
block
|
||||
.declaration_importance_iter()
|
||||
.map(|(declaration, _)| (declaration, Origin::Author)),
|
||||
block.declaration_importance_iter().map(|(declaration, _)| {
|
||||
(
|
||||
declaration,
|
||||
CascadePriority::new(
|
||||
CascadeLevel::same_tree_author_normal(),
|
||||
LayerOrder::root(),
|
||||
),
|
||||
)
|
||||
}),
|
||||
Some(parent_style),
|
||||
Some(parent_style),
|
||||
Some(parent_style),
|
||||
|
@ -1533,6 +1532,133 @@ impl Stylist {
|
|||
}
|
||||
}
|
||||
|
||||
/// A vector that is sorted in layer order.
|
||||
#[derive(Clone, Debug, Deref, MallocSizeOf)]
|
||||
pub struct LayerOrderedVec<T>(Vec<(T, LayerId)>);
|
||||
impl<T> Default for LayerOrderedVec<T> {
|
||||
fn default() -> Self {
|
||||
Self(Default::default())
|
||||
}
|
||||
}
|
||||
|
||||
/// A map that is sorted in layer order.
|
||||
#[derive(Clone, Debug, Deref, MallocSizeOf)]
|
||||
pub struct LayerOrderedMap<T>(PrecomputedHashMap<Atom, SmallVec<[(T, LayerId); 1]>>);
|
||||
impl<T> Default for LayerOrderedMap<T> {
|
||||
fn default() -> Self {
|
||||
Self(Default::default())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "gecko")]
|
||||
impl<T: 'static> LayerOrderedVec<T> {
|
||||
fn clear(&mut self) {
|
||||
self.0.clear();
|
||||
}
|
||||
fn push(&mut self, v: T, id: LayerId) {
|
||||
self.0.push((v, id));
|
||||
}
|
||||
fn sort(&mut self, layers: &[CascadeLayer]) {
|
||||
self.0
|
||||
.sort_by_key(|&(_, ref id)| layers[id.0 as usize].order)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: 'static> LayerOrderedMap<T> {
|
||||
fn clear(&mut self) {
|
||||
self.0.clear();
|
||||
}
|
||||
#[cfg(feature = "gecko")]
|
||||
fn try_insert(&mut self, name: Atom, v: T, id: LayerId) -> Result<(), AllocErr> {
|
||||
self.try_insert_with(name, v, id, |_, _| Ordering::Equal)
|
||||
}
|
||||
fn try_insert_with(
|
||||
&mut self,
|
||||
name: Atom,
|
||||
v: T,
|
||||
id: LayerId,
|
||||
cmp: impl Fn(&T, &T) -> Ordering,
|
||||
) -> Result<(), AllocErr> {
|
||||
self.0.try_reserve(1)?;
|
||||
let vec = self.0.entry(name).or_default();
|
||||
if let Some(&mut (ref mut val, ref last_id)) = vec.last_mut() {
|
||||
if *last_id == id {
|
||||
if cmp(&val, &v) != Ordering::Greater {
|
||||
*val = v;
|
||||
}
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
vec.push((v, id));
|
||||
Ok(())
|
||||
}
|
||||
#[cfg(feature = "gecko")]
|
||||
fn sort(&mut self, layers: &[CascadeLayer]) {
|
||||
self.sort_with(layers, |_, _| Ordering::Equal)
|
||||
}
|
||||
fn sort_with(&mut self, layers: &[CascadeLayer], cmp: impl Fn(&T, &T) -> Ordering) {
|
||||
for (_, v) in self.0.iter_mut() {
|
||||
v.sort_by(|&(ref v1, ref id1), &(ref v2, ref id2)| {
|
||||
let order1 = layers[id1.0 as usize].order;
|
||||
let order2 = layers[id2.0 as usize].order;
|
||||
order1.cmp(&order2).then_with(|| cmp(v1, v2))
|
||||
})
|
||||
}
|
||||
}
|
||||
/// Get an entry on the LayerOrderedMap by name.
|
||||
pub fn get(&self, name: &Atom) -> Option<&T> {
|
||||
let vec = self.0.get(name)?;
|
||||
Some(&vec.last()?.0)
|
||||
}
|
||||
}
|
||||
|
||||
/// Wrapper to allow better tracking of memory usage by page rule lists.
|
||||
///
|
||||
/// This includes the layer ID for use with the named page table.
|
||||
#[derive(Clone, Debug, MallocSizeOf)]
|
||||
pub struct PageRuleData {
|
||||
/// Layer ID for sorting page rules after matching.
|
||||
pub layer: LayerId,
|
||||
/// Page rule
|
||||
#[ignore_malloc_size_of = "Arc, stylesheet measures as primary ref"]
|
||||
pub rule: Arc<Locked<PageRule>>,
|
||||
}
|
||||
|
||||
/// Wrapper to allow better tracking of memory usage by page rule lists.
|
||||
///
|
||||
/// This is meant to be used by the global page rule list which are already
|
||||
/// sorted by layer ID, since all global page rules are less specific than all
|
||||
/// named page rules that match a certain page.
|
||||
#[derive(Clone, Debug, Deref, MallocSizeOf)]
|
||||
pub struct PageRuleDataNoLayer(
|
||||
#[ignore_malloc_size_of = "Arc, stylesheet measures as primary ref"]
|
||||
pub Arc<Locked<PageRule>>,
|
||||
);
|
||||
|
||||
/// Stores page rules indexed by page names.
|
||||
#[derive(Clone, Debug, Default, MallocSizeOf)]
|
||||
pub struct PageRuleMap {
|
||||
/// Global, unnamed page rules.
|
||||
pub global: LayerOrderedVec<PageRuleDataNoLayer>,
|
||||
/// Named page rules
|
||||
pub named: PrecomputedHashMap<Atom, SmallVec<[PageRuleData; 1]>>,
|
||||
}
|
||||
|
||||
#[cfg(feature = "gecko")]
|
||||
impl PageRuleMap {
|
||||
#[inline]
|
||||
fn clear(&mut self) {
|
||||
self.global.clear();
|
||||
self.named.clear();
|
||||
}
|
||||
}
|
||||
|
||||
impl MallocShallowSizeOf for PageRuleMap {
|
||||
fn shallow_size_of(&self, ops: &mut MallocSizeOfOps) -> usize {
|
||||
self.global.size_of(ops) + self.named.shallow_size_of(ops)
|
||||
}
|
||||
}
|
||||
|
||||
/// This struct holds data which users of Stylist may want to extract
|
||||
/// from stylesheets which can be done at the same time as updating.
|
||||
#[derive(Clone, Debug, Default)]
|
||||
|
@ -1540,31 +1666,39 @@ impl Stylist {
|
|||
pub struct ExtraStyleData {
|
||||
/// A list of effective font-face rules and their origin.
|
||||
#[cfg(feature = "gecko")]
|
||||
pub font_faces: Vec<Arc<Locked<FontFaceRule>>>,
|
||||
pub font_faces: LayerOrderedVec<Arc<Locked<FontFaceRule>>>,
|
||||
|
||||
/// A list of effective font-feature-values rules.
|
||||
#[cfg(feature = "gecko")]
|
||||
pub font_feature_values: Vec<Arc<Locked<FontFeatureValuesRule>>>,
|
||||
pub font_feature_values: LayerOrderedVec<Arc<Locked<FontFeatureValuesRule>>>,
|
||||
|
||||
/// A map of effective counter-style rules.
|
||||
#[cfg(feature = "gecko")]
|
||||
pub counter_styles: PrecomputedHashMap<Atom, Arc<Locked<CounterStyleRule>>>,
|
||||
pub counter_styles: LayerOrderedMap<Arc<Locked<CounterStyleRule>>>,
|
||||
|
||||
/// A map of effective page rules.
|
||||
#[cfg(feature = "gecko")]
|
||||
pub pages: Vec<Arc<Locked<PageRule>>>,
|
||||
pub pages: PageRuleMap,
|
||||
|
||||
/// A map of effective scroll-timeline rules.
|
||||
#[cfg(feature = "gecko")]
|
||||
pub scroll_timelines: LayerOrderedMap<Arc<Locked<ScrollTimelineRule>>>,
|
||||
}
|
||||
|
||||
#[cfg(feature = "gecko")]
|
||||
impl ExtraStyleData {
|
||||
/// Add the given @font-face rule.
|
||||
fn add_font_face(&mut self, rule: &Arc<Locked<FontFaceRule>>) {
|
||||
self.font_faces.push(rule.clone());
|
||||
fn add_font_face(&mut self, rule: &Arc<Locked<FontFaceRule>>, layer: LayerId) {
|
||||
self.font_faces.push(rule.clone(), layer);
|
||||
}
|
||||
|
||||
/// Add the given @font-feature-values rule.
|
||||
fn add_font_feature_values(&mut self, rule: &Arc<Locked<FontFeatureValuesRule>>) {
|
||||
self.font_feature_values.push(rule.clone());
|
||||
fn add_font_feature_values(
|
||||
&mut self,
|
||||
rule: &Arc<Locked<FontFeatureValuesRule>>,
|
||||
layer: LayerId,
|
||||
) {
|
||||
self.font_feature_values.push(rule.clone(), layer);
|
||||
}
|
||||
|
||||
/// Add the given @counter-style rule.
|
||||
|
@ -1572,18 +1706,53 @@ impl ExtraStyleData {
|
|||
&mut self,
|
||||
guard: &SharedRwLockReadGuard,
|
||||
rule: &Arc<Locked<CounterStyleRule>>,
|
||||
) {
|
||||
layer: LayerId,
|
||||
) -> Result<(), AllocErr> {
|
||||
let name = rule.read_with(guard).name().0.clone();
|
||||
self.counter_styles.insert(name, rule.clone());
|
||||
self.counter_styles.try_insert(name, rule.clone(), layer)
|
||||
}
|
||||
|
||||
/// Add the given @page rule.
|
||||
fn add_page(&mut self, rule: &Arc<Locked<PageRule>>) {
|
||||
self.pages.push(rule.clone());
|
||||
fn add_page(
|
||||
&mut self,
|
||||
guard: &SharedRwLockReadGuard,
|
||||
rule: &Arc<Locked<PageRule>>,
|
||||
layer: LayerId,
|
||||
) -> Result<(), AllocErr> {
|
||||
let page_rule = rule.read_with(guard);
|
||||
if page_rule.selectors.0.is_empty() {
|
||||
self.pages.global.push(PageRuleDataNoLayer(rule.clone()), layer);
|
||||
} else {
|
||||
// TODO: Handle pseudo-classes
|
||||
self.pages.named.try_reserve(page_rule.selectors.0.len())?;
|
||||
for name in page_rule.selectors.as_slice() {
|
||||
let vec = self.pages.named.entry(name.0.0.clone()).or_default();
|
||||
vec.try_reserve(1)?;
|
||||
vec.push(PageRuleData{layer, rule: rule.clone()});
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Add the given @scroll-timeline rule.
|
||||
fn add_scroll_timeline(
|
||||
&mut self,
|
||||
guard: &SharedRwLockReadGuard,
|
||||
rule: &Arc<Locked<ScrollTimelineRule>>,
|
||||
layer: LayerId,
|
||||
) -> Result<(), AllocErr> {
|
||||
let name = rule.read_with(guard).name.as_atom().clone();
|
||||
self.scroll_timelines.try_insert(name, rule.clone(), layer)
|
||||
}
|
||||
|
||||
fn sort_by_layer(&mut self, layers: &[CascadeLayer]) {
|
||||
self.font_faces.sort(layers);
|
||||
self.font_feature_values.sort(layers);
|
||||
self.counter_styles.sort(layers);
|
||||
self.pages.global.sort(layers);
|
||||
self.scroll_timelines.sort(layers);
|
||||
}
|
||||
}
|
||||
|
||||
impl ExtraStyleData {
|
||||
fn clear(&mut self) {
|
||||
#[cfg(feature = "gecko")]
|
||||
{
|
||||
|
@ -1591,10 +1760,23 @@ impl ExtraStyleData {
|
|||
self.font_feature_values.clear();
|
||||
self.counter_styles.clear();
|
||||
self.pages.clear();
|
||||
self.scroll_timelines.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Don't let a prefixed keyframes animation override
|
||||
// a non-prefixed one.
|
||||
fn compare_keyframes_in_same_layer(v1: &KeyframesAnimation, v2: &KeyframesAnimation) -> Ordering {
|
||||
if v1.vendor_prefix.is_some() == v2.vendor_prefix.is_some() {
|
||||
Ordering::Equal
|
||||
} else if v2.vendor_prefix.is_some() {
|
||||
Ordering::Greater
|
||||
} else {
|
||||
Ordering::Less
|
||||
}
|
||||
}
|
||||
|
||||
/// An iterator over the different ExtraStyleData.
|
||||
pub struct ExtraStyleDataIterator<'a>(DocumentCascadeDataIter<'a>);
|
||||
|
||||
|
@ -1615,6 +1797,7 @@ impl MallocSizeOf for ExtraStyleData {
|
|||
n += self.font_feature_values.shallow_size_of(ops);
|
||||
n += self.counter_styles.shallow_size_of(ops);
|
||||
n += self.pages.shallow_size_of(ops);
|
||||
n += self.scroll_timelines.shallow_size_of(ops);
|
||||
n
|
||||
}
|
||||
}
|
||||
|
@ -1859,6 +2042,15 @@ impl ElementAndPseudoRules {
|
|||
self.element_map.clear();
|
||||
self.pseudos_map.clear();
|
||||
}
|
||||
|
||||
fn shrink_if_needed(&mut self) {
|
||||
self.element_map.shrink_if_needed();
|
||||
for pseudo in self.pseudos_map.iter_mut() {
|
||||
if let Some(ref mut pseudo) = pseudo {
|
||||
pseudo.shrink_if_needed();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PartElementAndPseudoRules {
|
||||
|
@ -1949,7 +2141,7 @@ pub struct CascadeData {
|
|||
|
||||
/// A map with all the animations at this `CascadeData`'s origin, indexed
|
||||
/// by name.
|
||||
animations: PrecomputedHashMap<Atom, KeyframesAnimation>,
|
||||
animations: LayerOrderedMap<KeyframesAnimation>,
|
||||
|
||||
/// A map from cascade layer name to layer order.
|
||||
layer_id: FxHashMap<LayerName, LayerId>,
|
||||
|
@ -2015,7 +2207,7 @@ impl CascadeData {
|
|||
quirks_mode: QuirksMode,
|
||||
collection: SheetCollectionFlusher<S>,
|
||||
guard: &SharedRwLockReadGuard,
|
||||
) -> Result<(), FailedAllocationError>
|
||||
) -> Result<(), AllocErr>
|
||||
where
|
||||
S: StylesheetInDocument + PartialEq + 'static,
|
||||
{
|
||||
|
@ -2045,7 +2237,7 @@ impl CascadeData {
|
|||
result.is_ok()
|
||||
});
|
||||
|
||||
self.compute_layer_order();
|
||||
self.did_finish_rebuild();
|
||||
|
||||
result
|
||||
}
|
||||
|
@ -2113,8 +2305,33 @@ impl CascadeData {
|
|||
self.layers[id.0 as usize].order
|
||||
}
|
||||
|
||||
fn did_finish_rebuild(&mut self) {
|
||||
self.shrink_maps_if_needed();
|
||||
self.compute_layer_order();
|
||||
}
|
||||
|
||||
fn shrink_maps_if_needed(&mut self) {
|
||||
self.normal_rules.shrink_if_needed();
|
||||
if let Some(ref mut host_rules) = self.host_rules {
|
||||
host_rules.shrink_if_needed();
|
||||
}
|
||||
if let Some(ref mut slotted_rules) = self.slotted_rules {
|
||||
slotted_rules.shrink_if_needed();
|
||||
}
|
||||
self.invalidation_map.shrink_if_needed();
|
||||
self.attribute_dependencies.shrink_if_needed();
|
||||
self.mapped_ids.shrink_if_needed();
|
||||
self.layer_id.shrink_if_needed();
|
||||
self.selectors_for_cache_revalidation.shrink_if_needed();
|
||||
|
||||
}
|
||||
|
||||
fn compute_layer_order(&mut self) {
|
||||
debug_assert_ne!(self.layers.len(), 0, "There should be at least the root layer!");
|
||||
debug_assert_ne!(
|
||||
self.layers.len(),
|
||||
0,
|
||||
"There should be at least the root layer!"
|
||||
);
|
||||
if self.layers.len() == 1 {
|
||||
return; // Nothing to do
|
||||
}
|
||||
|
@ -2131,7 +2348,10 @@ impl CascadeData {
|
|||
order: &mut LayerOrder,
|
||||
) {
|
||||
for child in parent.children.iter() {
|
||||
debug_assert!(parent.id < *child, "Children are always registered after parents");
|
||||
debug_assert!(
|
||||
parent.id < *child,
|
||||
"Children are always registered after parents"
|
||||
);
|
||||
let child_index = (child.0 - parent.id.0 - 1) as usize;
|
||||
let (first, remaining) = remaining_layers.split_at_mut(child_index + 1);
|
||||
let child = &mut first[child_index];
|
||||
|
@ -2143,6 +2363,12 @@ impl CascadeData {
|
|||
order.inc();
|
||||
}
|
||||
}
|
||||
#[cfg(feature = "gecko")]
|
||||
{
|
||||
self.extra_data.sort_by_layer(&self.layers);
|
||||
}
|
||||
self.animations
|
||||
.sort_with(&self.layers, compare_keyframes_in_same_layer);
|
||||
}
|
||||
|
||||
/// Collects all the applicable media query results into `results`.
|
||||
|
@ -2203,7 +2429,7 @@ impl CascadeData {
|
|||
mut current_layer: &mut LayerName,
|
||||
current_layer_id: LayerId,
|
||||
mut precomputed_pseudo_element_decls: Option<&mut PrecomputedPseudoElementDeclarations>,
|
||||
) -> Result<(), FailedAllocationError>
|
||||
) -> Result<(), AllocErr>
|
||||
where
|
||||
S: StylesheetInDocument + 'static,
|
||||
{
|
||||
|
@ -2289,12 +2515,14 @@ impl CascadeData {
|
|||
// We choose the last one quite arbitrarily,
|
||||
// expecting it's slightly more likely to be more
|
||||
// specific.
|
||||
self.part_rules
|
||||
let map = self
|
||||
.part_rules
|
||||
.get_or_insert_with(|| Box::new(Default::default()))
|
||||
.for_insertion(pseudo_element)
|
||||
.try_entry(parts.last().unwrap().clone().0)?
|
||||
.or_insert_with(SmallVec::new)
|
||||
.try_push(rule)?;
|
||||
.for_insertion(pseudo_element);
|
||||
map.try_reserve(1)?;
|
||||
let vec = map.entry(parts.last().unwrap().clone().0).or_default();
|
||||
vec.try_reserve(1)?;
|
||||
vec.push(rule);
|
||||
} else {
|
||||
// NOTE(emilio): It's fine to look at :host and then at
|
||||
// ::slotted(..), since :host::slotted(..) could never
|
||||
|
@ -2316,65 +2544,45 @@ impl CascadeData {
|
|||
self.rules_source_order += 1;
|
||||
},
|
||||
CssRule::Keyframes(ref keyframes_rule) => {
|
||||
#[cfg(feature = "gecko")]
|
||||
use hashglobe::hash_map::Entry;
|
||||
#[cfg(feature = "servo")]
|
||||
use hashglobe::fake::Entry;
|
||||
|
||||
let keyframes_rule = keyframes_rule.read_with(guard);
|
||||
debug!("Found valid keyframes rule: {:?}", *keyframes_rule);
|
||||
match self.animations.try_entry(keyframes_rule.name.as_atom().clone())? {
|
||||
Entry::Vacant(e) => {
|
||||
e.insert(KeyframesAnimation::from_keyframes(
|
||||
&keyframes_rule.keyframes,
|
||||
keyframes_rule.vendor_prefix.clone(),
|
||||
current_layer_id,
|
||||
guard,
|
||||
));
|
||||
},
|
||||
Entry::Occupied(mut e) => {
|
||||
// Don't let a prefixed keyframes animation override
|
||||
// a non-prefixed one.
|
||||
//
|
||||
// TODO(emilio): This will need to be harder for
|
||||
// layers.
|
||||
let needs_insert =
|
||||
keyframes_rule.vendor_prefix.is_none() ||
|
||||
e.get().vendor_prefix.is_some();
|
||||
if needs_insert {
|
||||
e.insert(KeyframesAnimation::from_keyframes(
|
||||
&keyframes_rule.keyframes,
|
||||
keyframes_rule.vendor_prefix.clone(),
|
||||
current_layer_id,
|
||||
guard,
|
||||
));
|
||||
}
|
||||
},
|
||||
}
|
||||
let keyframes_rule = keyframes_rule.read_with(guard);
|
||||
let name = keyframes_rule.name.as_atom().clone();
|
||||
let animation = KeyframesAnimation::from_keyframes(
|
||||
&keyframes_rule.keyframes,
|
||||
keyframes_rule.vendor_prefix.clone(),
|
||||
guard,
|
||||
);
|
||||
self.animations.try_insert_with(
|
||||
name,
|
||||
animation,
|
||||
current_layer_id,
|
||||
compare_keyframes_in_same_layer,
|
||||
)?;
|
||||
},
|
||||
#[cfg(feature = "gecko")]
|
||||
CssRule::ScrollTimeline(..) => {
|
||||
// TODO: Bug 1676791: set the timeline into animation.
|
||||
// https://phabricator.services.mozilla.com/D126452
|
||||
//
|
||||
CssRule::ScrollTimeline(ref rule) => {
|
||||
// Note: Bug 1733260: we may drop @scroll-timeline rule once this spec issue
|
||||
// https://github.com/w3c/csswg-drafts/issues/6674 gets landed.
|
||||
self.extra_data
|
||||
.add_scroll_timeline(guard, rule, current_layer_id)?;
|
||||
},
|
||||
#[cfg(feature = "gecko")]
|
||||
CssRule::FontFace(ref rule) => {
|
||||
self.extra_data.add_font_face(rule);
|
||||
self.extra_data.add_font_face(rule, current_layer_id);
|
||||
},
|
||||
#[cfg(feature = "gecko")]
|
||||
CssRule::FontFeatureValues(ref rule) => {
|
||||
self.extra_data.add_font_feature_values(rule);
|
||||
self.extra_data
|
||||
.add_font_feature_values(rule, current_layer_id);
|
||||
},
|
||||
#[cfg(feature = "gecko")]
|
||||
CssRule::CounterStyle(ref rule) => {
|
||||
self.extra_data.add_counter_style(guard, rule);
|
||||
self.extra_data
|
||||
.add_counter_style(guard, rule, current_layer_id)?;
|
||||
},
|
||||
#[cfg(feature = "gecko")]
|
||||
CssRule::Page(ref rule) => {
|
||||
self.extra_data.add_page(rule);
|
||||
self.extra_data.add_page(guard, rule, current_layer_id)?;
|
||||
},
|
||||
CssRule::Viewport(..) => {},
|
||||
_ => {
|
||||
|
@ -2401,13 +2609,8 @@ impl CascadeData {
|
|||
}
|
||||
|
||||
let mut effective = false;
|
||||
let children = EffectiveRulesIterator::children(
|
||||
rule,
|
||||
device,
|
||||
quirks_mode,
|
||||
guard,
|
||||
&mut effective,
|
||||
);
|
||||
let children =
|
||||
EffectiveRulesIterator::children(rule, device, quirks_mode, guard, &mut effective);
|
||||
|
||||
if !effective {
|
||||
continue;
|
||||
|
@ -2426,7 +2629,8 @@ impl CascadeData {
|
|||
let mut parent = layer.clone();
|
||||
parent.0.pop();
|
||||
|
||||
*data.layer_id
|
||||
*data
|
||||
.layer_id
|
||||
.get_mut(&parent)
|
||||
.expect("Parent layers should be registered before child layers")
|
||||
} else {
|
||||
|
@ -2489,7 +2693,6 @@ impl CascadeData {
|
|||
&mut layer_names_to_pop,
|
||||
);
|
||||
}
|
||||
|
||||
},
|
||||
CssRule::Media(ref lock) => {
|
||||
if rebuild_kind.should_rebuild_invalidation() {
|
||||
|
@ -2497,34 +2700,24 @@ impl CascadeData {
|
|||
self.effective_media_query_results.saw_effective(media_rule);
|
||||
}
|
||||
},
|
||||
CssRule::Layer(ref lock) => {
|
||||
use crate::stylesheets::layer_rule::LayerRuleKind;
|
||||
|
||||
CssRule::LayerBlock(ref lock) => {
|
||||
let layer_rule = lock.read_with(guard);
|
||||
match layer_rule.kind {
|
||||
LayerRuleKind::Block { ref name, .. } => {
|
||||
children_layer_id = maybe_register_layers(
|
||||
self,
|
||||
name.as_ref(),
|
||||
&mut current_layer,
|
||||
&mut layer_names_to_pop,
|
||||
);
|
||||
}
|
||||
LayerRuleKind::Statement { ref names } => {
|
||||
for name in &**names {
|
||||
let mut pushed = 0;
|
||||
// There are no children, so we can ignore the
|
||||
// return value.
|
||||
maybe_register_layers(
|
||||
self,
|
||||
Some(name),
|
||||
&mut current_layer,
|
||||
&mut pushed,
|
||||
);
|
||||
for _ in 0..pushed {
|
||||
current_layer.0.pop();
|
||||
}
|
||||
}
|
||||
children_layer_id = maybe_register_layers(
|
||||
self,
|
||||
layer_rule.name.as_ref(),
|
||||
&mut current_layer,
|
||||
&mut layer_names_to_pop,
|
||||
);
|
||||
},
|
||||
CssRule::LayerStatement(ref lock) => {
|
||||
let layer_rule = lock.read_with(guard);
|
||||
for name in &*layer_rule.names {
|
||||
let mut pushed = 0;
|
||||
// There are no children, so we can ignore the
|
||||
// return value.
|
||||
maybe_register_layers(self, Some(name), &mut current_layer, &mut pushed);
|
||||
for _ in 0..pushed {
|
||||
current_layer.0.pop();
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -2563,7 +2756,7 @@ impl CascadeData {
|
|||
guard: &SharedRwLockReadGuard,
|
||||
rebuild_kind: SheetRebuildKind,
|
||||
mut precomputed_pseudo_element_decls: Option<&mut PrecomputedPseudoElementDeclarations>,
|
||||
) -> Result<(), FailedAllocationError>
|
||||
) -> Result<(), AllocErr>
|
||||
where
|
||||
S: StylesheetInDocument + 'static,
|
||||
{
|
||||
|
@ -2609,7 +2802,9 @@ impl CascadeData {
|
|||
|
||||
let effective_now = stylesheet.is_effective_for_device(device, guard);
|
||||
|
||||
let effective_then = self.effective_media_query_results.was_effective(stylesheet.contents());
|
||||
let effective_then = self
|
||||
.effective_media_query_results
|
||||
.was_effective(stylesheet.contents());
|
||||
|
||||
if effective_now != effective_then {
|
||||
debug!(
|
||||
|
@ -2639,7 +2834,8 @@ impl CascadeData {
|
|||
CssRule::Page(..) |
|
||||
CssRule::Viewport(..) |
|
||||
CssRule::Document(..) |
|
||||
CssRule::Layer(..) |
|
||||
CssRule::LayerBlock(..) |
|
||||
CssRule::LayerStatement(..) |
|
||||
CssRule::FontFeatureValues(..) => {
|
||||
// Not affected by device changes.
|
||||
continue;
|
||||
|
@ -2708,7 +2904,10 @@ impl CascadeData {
|
|||
self.layer_id.clear();
|
||||
self.layers.clear();
|
||||
self.layers.push(CascadeLayer::root());
|
||||
self.extra_data.clear();
|
||||
#[cfg(feature = "gecko")]
|
||||
{
|
||||
self.extra_data.clear();
|
||||
}
|
||||
self.rules_source_order = 0;
|
||||
self.num_selectors = 0;
|
||||
self.num_declarations = 0;
|
||||
|
@ -2737,9 +2936,9 @@ impl CascadeDataCacheEntry for CascadeData {
|
|||
collection: SheetCollectionFlusher<S>,
|
||||
guard: &SharedRwLockReadGuard,
|
||||
old: &Self,
|
||||
) -> Result<Arc<Self>, FailedAllocationError>
|
||||
) -> Result<Arc<Self>, AllocErr>
|
||||
where
|
||||
S: StylesheetInDocument + PartialEq + 'static
|
||||
S: StylesheetInDocument + PartialEq + 'static,
|
||||
{
|
||||
debug_assert!(collection.dirty(), "We surely need to do something?");
|
||||
// If we're doing a full rebuild anyways, don't bother cloning the data.
|
||||
|
|
|
@ -891,25 +891,8 @@ impl Animate for ComputedTransform {
|
|||
match (this_remainder, other_remainder) {
|
||||
// If there is a remainder from *both* lists we must have had mismatched functions.
|
||||
// => Add the remainders to a suitable ___Matrix function.
|
||||
(Some(this_remainder), Some(other_remainder)) => match procedure {
|
||||
Procedure::Add => {
|
||||
debug_assert!(false, "Should have already dealt with add by the point");
|
||||
return Err(());
|
||||
},
|
||||
Procedure::Interpolate { progress } => {
|
||||
result.push(TransformOperation::InterpolateMatrix {
|
||||
from_list: Transform(this_remainder.to_vec().into()),
|
||||
to_list: Transform(other_remainder.to_vec().into()),
|
||||
progress: Percentage(progress as f32),
|
||||
});
|
||||
},
|
||||
Procedure::Accumulate { count } => {
|
||||
result.push(TransformOperation::AccumulateMatrix {
|
||||
from_list: Transform(this_remainder.to_vec().into()),
|
||||
to_list: Transform(other_remainder.to_vec().into()),
|
||||
count: cmp::min(count, i32::max_value() as u64) as i32,
|
||||
});
|
||||
},
|
||||
(Some(this_remainder), Some(other_remainder)) => {
|
||||
result.push(TransformOperation::animate_mismatched_transforms(this_remainder, other_remainder, procedure)?);
|
||||
},
|
||||
// If there is a remainder from just one list, then one list must be shorter but
|
||||
// completely match the type of the corresponding functions in the longer list.
|
||||
|
@ -923,36 +906,19 @@ impl Animate for ComputedTransform {
|
|||
let identity = transform.to_animated_zero().unwrap();
|
||||
|
||||
match transform {
|
||||
// We can't interpolate/accumulate ___Matrix types directly with a
|
||||
// matrix. Instead we need to wrap it in another ___Matrix type.
|
||||
TransformOperation::AccumulateMatrix { .. } |
|
||||
TransformOperation::InterpolateMatrix { .. } => {
|
||||
let transform_list = Transform(vec![transform.clone()].into());
|
||||
let identity_list = Transform(vec![identity].into());
|
||||
let (from_list, to_list) = if fill_right {
|
||||
(transform_list, identity_list)
|
||||
let (from, to) = if fill_right {
|
||||
(transform, &identity)
|
||||
} else {
|
||||
(identity_list, transform_list)
|
||||
(&identity, transform)
|
||||
};
|
||||
|
||||
match procedure {
|
||||
Procedure::Add => Err(()),
|
||||
Procedure::Interpolate { progress } => {
|
||||
Ok(TransformOperation::InterpolateMatrix {
|
||||
from_list,
|
||||
to_list,
|
||||
progress: Percentage(progress as f32),
|
||||
})
|
||||
},
|
||||
Procedure::Accumulate { count } => {
|
||||
Ok(TransformOperation::AccumulateMatrix {
|
||||
from_list,
|
||||
to_list,
|
||||
count: cmp::min(count, i32::max_value() as u64)
|
||||
as i32,
|
||||
})
|
||||
},
|
||||
}
|
||||
TransformOperation::animate_mismatched_transforms(
|
||||
&[from.clone()],
|
||||
&[to.clone()],
|
||||
procedure,
|
||||
)
|
||||
},
|
||||
_ => {
|
||||
let (lhs, rhs) = if fill_right {
|
||||
|
@ -981,9 +947,13 @@ impl ComputeSquaredDistance for ComputedTransform {
|
|||
|
||||
// Roll back to matrix interpolation if there is any Err(()) in the
|
||||
// transform lists, such as mismatched transform functions.
|
||||
//
|
||||
// FIXME: Using a zero size here seems a bit sketchy but matches the
|
||||
// previous behavior.
|
||||
if squared_dist.is_err() {
|
||||
let matrix1: Matrix3D = self.to_transform_3d_matrix(None)?.0.into();
|
||||
let matrix2: Matrix3D = other.to_transform_3d_matrix(None)?.0.into();
|
||||
let rect = euclid::Rect::zero();
|
||||
let matrix1: Matrix3D = self.to_transform_3d_matrix(Some(&rect))?.0.into();
|
||||
let matrix2: Matrix3D = other.to_transform_3d_matrix(Some(&rect))?.0.into();
|
||||
return matrix1.compute_squared_distance(&matrix2);
|
||||
}
|
||||
|
||||
|
@ -1122,7 +1092,7 @@ impl Animate for ComputedTransformOperation {
|
|||
1.
|
||||
} else {
|
||||
-1. / perspective_z
|
||||
}
|
||||
},
|
||||
))
|
||||
};
|
||||
Ok(TransformOperation::Perspective(used_value))
|
||||
|
@ -1141,6 +1111,52 @@ impl Animate for ComputedTransformOperation {
|
|||
}
|
||||
}
|
||||
|
||||
impl ComputedTransformOperation {
|
||||
/// If there are no size dependencies, we try to animate in-place, to avoid
|
||||
/// creating deeply nested Interpolate* operations.
|
||||
fn try_animate_mismatched_transforms_in_place(
|
||||
left: &[Self],
|
||||
right: &[Self],
|
||||
procedure: Procedure,
|
||||
) -> Result<Self, ()> {
|
||||
let (left, _left_3d) = Transform::components_to_transform_3d_matrix(left, None)?;
|
||||
let (right, _right_3d) = Transform::components_to_transform_3d_matrix(right, None)?;
|
||||
ComputedTransformOperation::Matrix3D(left.into()).animate(&ComputedTransformOperation::Matrix3D(right.into()), procedure)
|
||||
}
|
||||
|
||||
fn animate_mismatched_transforms(
|
||||
left: &[Self],
|
||||
right: &[Self],
|
||||
procedure: Procedure,
|
||||
) -> Result<Self, ()> {
|
||||
if let Ok(op) = Self::try_animate_mismatched_transforms_in_place(left, right, procedure) {
|
||||
return Ok(op);
|
||||
}
|
||||
let from_list = Transform(left.to_vec().into());
|
||||
let to_list = Transform(right.to_vec().into());
|
||||
Ok(match procedure {
|
||||
Procedure::Add => {
|
||||
debug_assert!(false, "Addition should've been handled earlier");
|
||||
return Err(())
|
||||
},
|
||||
Procedure::Interpolate { progress } => {
|
||||
Self::InterpolateMatrix {
|
||||
from_list,
|
||||
to_list,
|
||||
progress: Percentage(progress as f32),
|
||||
}
|
||||
}
|
||||
Procedure::Accumulate { count } => {
|
||||
Self::AccumulateMatrix {
|
||||
from_list,
|
||||
to_list,
|
||||
count: cmp::min(count, i32::max_value() as u64) as i32,
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// This might not be the most useful definition of distance. It might be better, for example,
|
||||
// to trace the distance travelled by a point as its transform is interpolated between the two
|
||||
// lists. That, however, proves to be quite complicated so we take a simple approach for now.
|
||||
|
@ -1204,10 +1220,9 @@ impl ComputeSquaredDistance for ComputedTransformOperation {
|
|||
(
|
||||
&TransformOperation::Perspective(ref fd),
|
||||
&TransformOperation::Perspective(ref td),
|
||||
) => {
|
||||
fd.infinity_or(|l| l.px())
|
||||
.compute_squared_distance(&td.infinity_or(|l| l.px()))
|
||||
},
|
||||
) => fd
|
||||
.infinity_or(|l| l.px())
|
||||
.compute_squared_distance(&td.infinity_or(|l| l.px())),
|
||||
(&TransformOperation::Perspective(ref p), &TransformOperation::Matrix3D(ref m)) |
|
||||
(&TransformOperation::Matrix3D(ref m), &TransformOperation::Perspective(ref p)) => {
|
||||
// FIXME(emilio): Is this right? Why interpolating this with
|
||||
|
|
|
@ -13,9 +13,9 @@ use crate::values::specified::box_ as specified;
|
|||
|
||||
pub use crate::values::specified::box_::{
|
||||
AnimationName, AnimationTimeline, Appearance, BreakBetween, BreakWithin,
|
||||
Clear as SpecifiedClear, Contain, Display, Float as SpecifiedFloat, Overflow, OverflowAnchor,
|
||||
OverflowClipBox, OverscrollBehavior, ScrollSnapAlign, ScrollSnapAxis, ScrollSnapStrictness,
|
||||
ScrollSnapType, TouchAction, TransitionProperty, WillChange,
|
||||
Clear as SpecifiedClear, Contain, ContentVisibility, Display, Float as SpecifiedFloat, Overflow,
|
||||
OverflowAnchor, OverflowClipBox, OverscrollBehavior, ScrollSnapAlign, ScrollSnapAxis,
|
||||
ScrollSnapStrictness, ScrollSnapType, ScrollbarGutter, TouchAction, TransitionProperty, WillChange,
|
||||
};
|
||||
|
||||
/// A computed value for the `vertical-align` property.
|
||||
|
|
|
@ -6,12 +6,12 @@
|
|||
|
||||
use crate::values::animated::color::RGBA as AnimatedRGBA;
|
||||
use crate::values::animated::ToAnimatedValue;
|
||||
use crate::values::generics::color::{GenericColor, GenericColorOrAuto, GenericCaretColor};
|
||||
use crate::values::generics::color::{GenericCaretColor, GenericColor, GenericColorOrAuto};
|
||||
use cssparser::{Color as CSSParserColor, RGBA};
|
||||
use std::fmt;
|
||||
use style_traits::{CssWriter, ToCss};
|
||||
|
||||
pub use crate::values::specified::color::ColorScheme;
|
||||
pub use crate::values::specified::color::{ColorScheme, PrintColorAdjust};
|
||||
|
||||
/// The computed value of the `color` property.
|
||||
pub type ColorPropertyValue = RGBA;
|
||||
|
|
|
@ -7,13 +7,17 @@
|
|||
use crate::values::computed::image::Image;
|
||||
use crate::values::generics::counters as generics;
|
||||
use crate::values::generics::counters::CounterIncrement as GenericCounterIncrement;
|
||||
use crate::values::generics::counters::CounterSetOrReset as GenericCounterSetOrReset;
|
||||
use crate::values::generics::counters::CounterReset as GenericCounterReset;
|
||||
use crate::values::generics::counters::CounterSet as GenericCounterSet;
|
||||
|
||||
/// A computed value for the `counter-increment` property.
|
||||
pub type CounterIncrement = GenericCounterIncrement<i32>;
|
||||
|
||||
/// A computed value for the `counter-set` and `counter-reset` properties.
|
||||
pub type CounterSetOrReset = GenericCounterSetOrReset<i32>;
|
||||
/// A computed value for the `counter-reset` property.
|
||||
pub type CounterReset = GenericCounterReset<i32>;
|
||||
|
||||
/// A computed value for the `counter-set` property.
|
||||
pub type CounterSet = GenericCounterSet<i32>;
|
||||
|
||||
/// A computed value for the `content` property.
|
||||
pub type Content = generics::GenericContent<Image>;
|
||||
|
|
|
@ -182,6 +182,9 @@ pub struct FontFamily {
|
|||
pub families: FontFamilyList,
|
||||
/// Whether this font-family came from a specified system-font.
|
||||
pub is_system_font: bool,
|
||||
/// Whether this is the initial font-family that might react to language
|
||||
/// changes.
|
||||
pub is_initial: bool,
|
||||
}
|
||||
|
||||
macro_rules! static_font_family {
|
||||
|
@ -193,16 +196,14 @@ macro_rules! static_font_family {
|
|||
list: crate::ArcSlice::from_iter_leaked(std::iter::once($family)),
|
||||
#[cfg(feature = "servo")]
|
||||
list: Box::new([$family]),
|
||||
#[cfg(feature = "gecko")]
|
||||
fallback: GenericFontFamily::None,
|
||||
},
|
||||
is_system_font: false,
|
||||
is_initial: false,
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
impl FontFamily {
|
||||
#[inline]
|
||||
/// Get default font family as `serif` which is a generic font-family
|
||||
|
@ -213,10 +214,13 @@ impl FontFamily {
|
|||
/// Returns the font family for `-moz-bullet-font`.
|
||||
#[cfg(feature = "gecko")]
|
||||
pub(crate) fn moz_bullet() -> &'static Self {
|
||||
static_font_family!(MOZ_BULLET, SingleFontFamily::FamilyName(FamilyName {
|
||||
name: atom!("-moz-bullet-font"),
|
||||
syntax: FontFamilyNameSyntax::Identifiers,
|
||||
}));
|
||||
static_font_family!(
|
||||
MOZ_BULLET,
|
||||
SingleFontFamily::FamilyName(FamilyName {
|
||||
name: atom!("-moz-bullet-font"),
|
||||
syntax: FontFamilyNameSyntax::Identifiers,
|
||||
})
|
||||
);
|
||||
|
||||
&*MOZ_BULLET
|
||||
}
|
||||
|
@ -226,13 +230,15 @@ impl FontFamily {
|
|||
pub fn for_system_font(name: &str) -> Self {
|
||||
Self {
|
||||
families: FontFamilyList {
|
||||
list: crate::ArcSlice::from_iter(std::iter::once(SingleFontFamily::FamilyName(FamilyName {
|
||||
name: Atom::from(name),
|
||||
syntax: FontFamilyNameSyntax::Identifiers,
|
||||
}))),
|
||||
fallback: GenericFontFamily::None,
|
||||
list: crate::ArcSlice::from_iter(std::iter::once(SingleFontFamily::FamilyName(
|
||||
FamilyName {
|
||||
name: Atom::from(name),
|
||||
syntax: FontFamilyNameSyntax::Identifiers,
|
||||
},
|
||||
))),
|
||||
},
|
||||
is_system_font: true,
|
||||
is_initial: false,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -240,8 +246,11 @@ impl FontFamily {
|
|||
pub fn generic(generic: GenericFontFamily) -> &'static Self {
|
||||
macro_rules! generic_font_family {
|
||||
($ident:ident, $family:ident) => {
|
||||
static_font_family!($ident, SingleFontFamily::Generic(GenericFontFamily::$family))
|
||||
}
|
||||
static_font_family!(
|
||||
$ident,
|
||||
SingleFontFamily::Generic(GenericFontFamily::$family)
|
||||
)
|
||||
};
|
||||
}
|
||||
|
||||
generic_font_family!(SERIF, Serif);
|
||||
|
@ -257,7 +266,7 @@ impl FontFamily {
|
|||
GenericFontFamily::None => {
|
||||
debug_assert!(false, "Bogus caller!");
|
||||
&*SERIF
|
||||
}
|
||||
},
|
||||
GenericFontFamily::Serif => &*SERIF,
|
||||
GenericFontFamily::SansSerif => &*SANS_SERIF,
|
||||
GenericFontFamily::Monospace => &*MONOSPACE,
|
||||
|
@ -296,7 +305,7 @@ impl ToCss for FontFamily {
|
|||
Some(f) => f.to_css(dest)?,
|
||||
None => {
|
||||
#[cfg(feature = "gecko")]
|
||||
return self.families.fallback.to_css(dest);
|
||||
return return Ok(());
|
||||
#[cfg(feature = "servo")]
|
||||
unreachable!();
|
||||
},
|
||||
|
@ -444,24 +453,21 @@ impl GenericFontFamily {
|
|||
/// families that the website might specify, since they're not configured by
|
||||
/// the user. See bug 789788 and bug 1730098.
|
||||
#[cfg(feature = "gecko")]
|
||||
pub (crate) fn valid_for_user_font_prioritization(self) -> bool {
|
||||
pub(crate) fn valid_for_user_font_prioritization(self) -> bool {
|
||||
match self {
|
||||
Self::None |
|
||||
Self::Fantasy |
|
||||
Self::Cursive |
|
||||
Self::SystemUi |
|
||||
Self::MozEmoji => false,
|
||||
Self::None | Self::Fantasy | Self::Cursive | Self::SystemUi | Self::MozEmoji => false,
|
||||
|
||||
Self::Serif |
|
||||
Self::SansSerif |
|
||||
Self::Monospace => true,
|
||||
Self::Serif | Self::SansSerif | Self::Monospace => true,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Parse for SingleFontFamily {
|
||||
/// Parse a font-family value.
|
||||
fn parse<'i, 't>(context: &ParserContext, input: &mut Parser<'i, 't>) -> Result<Self, ParseError<'i>> {
|
||||
fn parse<'i, 't>(
|
||||
context: &ParserContext,
|
||||
input: &mut Parser<'i, 't>,
|
||||
) -> Result<Self, ParseError<'i>> {
|
||||
if let Ok(value) = input.try_parse(|i| i.expect_string_cloned()) {
|
||||
return Ok(SingleFontFamily::FamilyName(FamilyName {
|
||||
name: Atom::from(&*value),
|
||||
|
@ -558,8 +564,6 @@ impl SingleFontFamily {
|
|||
pub struct FontFamilyList {
|
||||
/// The actual list of font families specified.
|
||||
pub list: crate::ArcSlice<SingleFontFamily>,
|
||||
/// A fallback font type (none, serif, or sans-serif, generally).
|
||||
pub fallback: GenericFontFamily,
|
||||
}
|
||||
|
||||
/// A list of font families.
|
||||
|
@ -588,28 +592,15 @@ impl FontFamilyList {
|
|||
self.list.iter()
|
||||
}
|
||||
|
||||
/// Puts the fallback in the list if needed.
|
||||
#[cfg(feature = "gecko")]
|
||||
pub fn normalize(&mut self) {
|
||||
if self.fallback == GenericFontFamily::None {
|
||||
return;
|
||||
}
|
||||
let mut new_list = self.list.iter().cloned().collect::<Vec<_>>();
|
||||
new_list.push(SingleFontFamily::Generic(self.fallback));
|
||||
self.list = crate::ArcSlice::from_iter(new_list.into_iter());
|
||||
}
|
||||
|
||||
/// If there's a generic font family on the list which is suitable for user
|
||||
/// font prioritization, then move it to the front of the list. Otherwise,
|
||||
/// prepend the default generic.
|
||||
#[cfg(feature = "gecko")]
|
||||
pub (crate) fn prioritize_first_generic_or_prepend(&mut self, generic: GenericFontFamily) {
|
||||
let index_of_first_generic = self.iter().position(|f| {
|
||||
match *f {
|
||||
SingleFontFamily::Generic(f) => f.valid_for_user_font_prioritization(),
|
||||
_ => false,
|
||||
}
|
||||
});
|
||||
pub(crate) fn prioritize_first_generic_or_prepend(&mut self, generic: GenericFontFamily) {
|
||||
let index_of_first_generic = self.iter().position(|f| match *f {
|
||||
SingleFontFamily::Generic(f) => f.valid_for_user_font_prioritization(),
|
||||
_ => false,
|
||||
});
|
||||
|
||||
if let Some(0) = index_of_first_generic {
|
||||
return; // Already first
|
||||
|
@ -625,12 +616,21 @@ impl FontFamilyList {
|
|||
self.list = crate::ArcSlice::from_iter(new_list.into_iter());
|
||||
}
|
||||
|
||||
/// Returns whether we need to prioritize user fonts.
|
||||
#[cfg(feature = "gecko")]
|
||||
pub(crate) fn needs_user_font_prioritization(&self) -> bool {
|
||||
self.iter().next().map_or(true, |f| match f {
|
||||
SingleFontFamily::Generic(f) => !f.valid_for_user_font_prioritization(),
|
||||
_ => true,
|
||||
})
|
||||
}
|
||||
|
||||
/// Return the generic ID if it is a single generic font
|
||||
pub fn single_generic(&self) -> Option<GenericFontFamily> {
|
||||
let mut iter = self.iter();
|
||||
if let Some(SingleFontFamily::Generic(f)) = iter.next() {
|
||||
if iter.next().is_none() {
|
||||
return Some(f.clone());
|
||||
return Some(*f);
|
||||
}
|
||||
}
|
||||
None
|
||||
|
|
|
@ -84,7 +84,6 @@ impl ToComputedValue for specified::ImageSet {
|
|||
let mut selected_resolution = items[0].resolution.dppx();
|
||||
|
||||
for (i, item) in items.iter().enumerate() {
|
||||
|
||||
// If the MIME type is not supported, we discard the ImageSetItem
|
||||
if item.has_mime_type && !context.device().is_supported_mime_type(&item.mime_type) {
|
||||
continue;
|
||||
|
|
|
@ -37,7 +37,9 @@ impl ToComputedValue for specified::NoCalcLength {
|
|||
length.to_computed_value(context, FontBaseSize::CurrentStyle)
|
||||
},
|
||||
specified::NoCalcLength::ViewportPercentage(length) => {
|
||||
context.builder.add_flags(ComputedValueFlags::USES_VIEWPORT_UNITS);
|
||||
context
|
||||
.builder
|
||||
.add_flags(ComputedValueFlags::USES_VIEWPORT_UNITS);
|
||||
length.to_computed_value(context.viewport_size_for_viewport_unit_resolution())
|
||||
},
|
||||
specified::NoCalcLength::ServoCharacterWidth(length) => {
|
||||
|
@ -191,7 +193,7 @@ impl Size {
|
|||
GenericSize::MaxContent |
|
||||
GenericSize::FitContent |
|
||||
GenericSize::MozAvailable |
|
||||
GenericSize::FitContentFunction(_) => false
|
||||
GenericSize::FitContentFunction(_) => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,7 +6,6 @@
|
|||
|
||||
#[cfg(feature = "gecko")]
|
||||
pub use crate::values::specified::list::ListStyleType;
|
||||
pub use crate::values::specified::list::MozListReversed;
|
||||
pub use crate::values::specified::list::Quotes;
|
||||
|
||||
impl Quotes {
|
||||
|
|
|
@ -45,14 +45,14 @@ pub use self::border::{BorderCornerRadius, BorderRadius, BorderSpacing};
|
|||
pub use self::border::{BorderImageRepeat, BorderImageSideWidth};
|
||||
pub use self::border::{BorderImageSlice, BorderImageWidth};
|
||||
pub use self::box_::{AnimationIterationCount, AnimationName, AnimationTimeline, Contain};
|
||||
pub use self::box_::{Appearance, BreakBetween, BreakWithin, Clear, Float};
|
||||
pub use self::box_::{Appearance, BreakBetween, BreakWithin, Clear, ContentVisibility, Float};
|
||||
pub use self::box_::{Display, Overflow, OverflowAnchor, TransitionProperty};
|
||||
pub use self::box_::{OverflowClipBox, OverscrollBehavior, Perspective, Resize};
|
||||
pub use self::box_::{OverflowClipBox, OverscrollBehavior, Perspective, Resize, ScrollbarGutter};
|
||||
pub use self::box_::{ScrollSnapAlign, ScrollSnapAxis, ScrollSnapStrictness, ScrollSnapType};
|
||||
pub use self::box_::{TouchAction, VerticalAlign, WillChange};
|
||||
pub use self::color::{Color, ColorOrAuto, ColorPropertyValue, ColorScheme};
|
||||
pub use self::color::{Color, ColorOrAuto, ColorPropertyValue, ColorScheme, PrintColorAdjust};
|
||||
pub use self::column::ColumnCount;
|
||||
pub use self::counters::{Content, ContentItem, CounterIncrement, CounterSetOrReset};
|
||||
pub use self::counters::{Content, ContentItem, CounterIncrement, CounterReset, CounterSet};
|
||||
pub use self::easing::TimingFunction;
|
||||
pub use self::effects::{BoxShadow, Filter, SimpleShadow};
|
||||
pub use self::flex::FlexBasis;
|
||||
|
@ -62,18 +62,17 @@ pub use self::font::{FontSize, FontSizeAdjust, FontStretch, FontSynthesis};
|
|||
pub use self::font::{FontVariantAlternates, FontWeight};
|
||||
pub use self::font::{FontVariantEastAsian, FontVariationSettings};
|
||||
pub use self::font::{MathDepth, MozScriptMinSize, MozScriptSizeMultiplier, XLang, XTextZoom};
|
||||
pub use self::image::{Gradient, Image, LineDirection, MozImageRect, ImageRendering};
|
||||
pub use self::image::{Gradient, Image, ImageRendering, LineDirection, MozImageRect};
|
||||
pub use self::length::{CSSPixelLength, NonNegativeLength};
|
||||
pub use self::length::{Length, LengthOrNumber, LengthPercentage, NonNegativeLengthOrNumber};
|
||||
pub use self::length::{LengthOrAuto, LengthPercentageOrAuto, MaxSize, Size};
|
||||
pub use self::length::{NonNegativeLengthPercentage, NonNegativeLengthPercentageOrAuto};
|
||||
#[cfg(feature = "gecko")]
|
||||
pub use self::list::ListStyleType;
|
||||
pub use self::list::MozListReversed;
|
||||
pub use self::list::Quotes;
|
||||
pub use self::motion::{OffsetPath, OffsetRotate};
|
||||
pub use self::outline::OutlineStyle;
|
||||
pub use self::page::{Orientation, PageSize, PaperSize};
|
||||
pub use self::page::{Orientation, PageName, PageSize, PaperSize};
|
||||
pub use self::percentage::{NonNegativePercentage, Percentage};
|
||||
pub use self::position::AspectRatio;
|
||||
pub use self::position::{
|
||||
|
@ -85,6 +84,7 @@ pub use self::resolution::Resolution;
|
|||
pub use self::svg::{DProperty, MozContextProperties};
|
||||
pub use self::svg::{SVGLength, SVGOpacity, SVGPaint, SVGPaintKind};
|
||||
pub use self::svg::{SVGPaintOrder, SVGStrokeDashArray, SVGWidth};
|
||||
pub use self::text::HyphenateCharacter;
|
||||
pub use self::text::TextUnderlinePosition;
|
||||
pub use self::text::{InitialLetter, LetterSpacing, LineBreak, LineHeight};
|
||||
pub use self::text::{OverflowWrap, RubyPosition, TextOverflow, WordBreak, WordSpacing};
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
* 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/. */
|
||||
|
||||
//! Computed @page at-rule properties
|
||||
//! Computed @page at-rule properties and named-page style properties
|
||||
|
||||
use crate::values::computed::length::NonNegativeLength;
|
||||
use crate::values::computed::{Context, ToComputedValue};
|
||||
|
@ -13,6 +13,7 @@ use crate::values::specified::page as specified;
|
|||
pub use generics::page::GenericPageSize;
|
||||
pub use generics::page::Orientation;
|
||||
pub use generics::page::PaperSize;
|
||||
pub use specified::PageName;
|
||||
|
||||
/// Computed value of the @page size descriptor
|
||||
///
|
||||
|
|
|
@ -18,7 +18,10 @@ use crate::Zero;
|
|||
use std::fmt::{self, Write};
|
||||
use style_traits::{CssWriter, ToCss};
|
||||
|
||||
pub use crate::values::specified::text::{TextAlignLast, TextUnderlinePosition, MozControlCharacterVisibility};
|
||||
pub use crate::values::specified::text::{
|
||||
MozControlCharacterVisibility, TextAlignLast, TextUnderlinePosition,
|
||||
};
|
||||
pub use crate::values::specified::HyphenateCharacter;
|
||||
pub use crate::values::specified::{LineBreak, OverflowWrap, RubyPosition, WordBreak};
|
||||
pub use crate::values::specified::{TextDecorationLine, TextEmphasisPosition};
|
||||
pub use crate::values::specified::{TextDecorationSkipInk, TextJustify, TextTransform};
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue