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:
bors-servo 2023-06-12 12:11:30 +02:00 committed by GitHub
commit 9260683b5f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
143 changed files with 2648 additions and 8840 deletions

51
Cargo.lock generated
View file

@ -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]]

View file

@ -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"

View file

@ -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

View file

@ -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 = []

View file

@ -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(())
}

View file

@ -124,6 +124,7 @@ fn font_family(names: Vec<&str>) -> FontFamily {
list: names.into_boxed_slice(),
},
is_system_font: false,
is_initial: false,
}
}

View file

@ -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 }

View file

@ -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.

View file

@ -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.

View file

@ -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.

View file

@ -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
}
}

View file

@ -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

View file

@ -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),
}
}
}

View file

@ -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

View file

@ -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 }

View file

@ -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

View file

@ -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
}
}

View file

@ -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);

View file

@ -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,

View file

@ -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.

View file

@ -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"

View file

@ -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,

View file

@ -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)
}
}

View file

@ -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,
{

View file

@ -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"),

View file

@ -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

View file

@ -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(

View file

@ -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;
}
}

View file

@ -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());

View file

@ -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;
}
}

View file

@ -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)
}
},
}
}
}

View file

@ -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),
}
}

View file

@ -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]);

View file

@ -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"
),
];

View file

@ -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 {

View file

@ -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, _, _),
]
}

View file

@ -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)
}

View file

@ -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

View file

@ -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

View file

@ -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()),
}
}

View file

@ -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>;

View file

@ -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.
//

View file

@ -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 {

View file

@ -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."

View file

@ -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.

View file

@ -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,
);

View file

@ -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));

View file

@ -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()

View file

@ -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",
]

View file

@ -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")

View file

@ -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()));
}
}

View file

@ -476,6 +476,7 @@
context.builder.inherit_${property.ident}();
% endif
}
CSSWideKeyword::RevertLayer |
CSSWideKeyword::Revert => unreachable!("Should never get here"),
}
return;

View file

@ -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 => {

View file

@ -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",
)}

View file

@ -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",
)}

View file

@ -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",

View file

@ -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",

View file

@ -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",

View file

@ -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`

View file

@ -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",
)}

View file

@ -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",
)}

View file

@ -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",
)}

View file

@ -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)

View file

@ -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(),
));
}
}

View file

@ -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

View file

@ -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;
}
}
}

View file

@ -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,

View file

@ -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

View file

@ -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

View file

@ -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 {

View file

@ -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())
}
}

View file

@ -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());
}
}

View file

@ -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

View file

@ -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,
},
)

View file

@ -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()
}
}

View file

@ -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;

View file

@ -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),
}
}

View file

@ -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(");")
}
}

View file

@ -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>

View file

@ -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(),
}

View file

@ -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() {

View file

@ -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()))
}
}

View file

@ -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)?;

View file

@ -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,

View file

@ -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)]

View file

@ -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.

View file

@ -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

View file

@ -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.

View file

@ -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;

View file

@ -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>;

View file

@ -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

View file

@ -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;

View file

@ -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,
}
}
}

View file

@ -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 {

View file

@ -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};

View file

@ -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
///

View file

@ -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