mirror of
https://github.com/servo/servo.git
synced 2025-06-06 16:45:39 +00:00
Auto merge of #29772 - Loirooriol:sync, r=mrobinson
Backport several style changes from Gecko (2) <!-- Please describe your changes on the following line: --> This continues #29748. --- <!-- 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: --> - [X] There are tests for these changes OR - [ ] These changes do not require tests because ___ <!-- Also, please make sure that "Allow edits from maintainers" checkbox is checked, so that we can help you if you get stuck somewhere along the way.--> <!-- Pull requests that do not address these steps are welcome, but they will require additional verification as part of the review process. -->
This commit is contained in:
commit
867326c46a
146 changed files with 2524 additions and 1683 deletions
|
@ -127,6 +127,7 @@ stroke-opacity
|
|||
storage
|
||||
submit
|
||||
suspend
|
||||
system-ui
|
||||
tel
|
||||
text
|
||||
time
|
||||
|
|
|
@ -1387,6 +1387,8 @@ fn to_font_kit_family(font_family: &font::SingleFontFamily) -> FamilyName {
|
|||
font::GenericFontFamily::Monospace => FamilyName::Monospace,
|
||||
font::GenericFontFamily::Fantasy => FamilyName::Fantasy,
|
||||
font::GenericFontFamily::Cursive => FamilyName::Cursive,
|
||||
// TODO: There is no FontFamily::SystemUi.
|
||||
font::GenericFontFamily::SystemUi => unreachable!("system-ui should be disabled"),
|
||||
font::GenericFontFamily::None => unreachable!("Shouldn't appear in computed values"),
|
||||
},
|
||||
}
|
||||
|
|
|
@ -569,6 +569,7 @@ impl<'a> From<&'a SingleFontFamily> for FontFamilyName {
|
|||
GenericFontFamily::Monospace => atom!("monospace"),
|
||||
GenericFontFamily::Cursive => atom!("cursive"),
|
||||
GenericFontFamily::Fantasy => atom!("fantasy"),
|
||||
GenericFontFamily::SystemUi => atom!("system-ui"),
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -120,7 +120,9 @@ fn font_family(names: Vec<&str>) -> FontFamily {
|
|||
.collect();
|
||||
|
||||
FontFamily {
|
||||
families: FontFamilyList::new(names.into_boxed_slice()),
|
||||
families: FontFamilyList {
|
||||
list: names.into_boxed_slice(),
|
||||
},
|
||||
is_system_font: false,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -733,9 +733,9 @@ impl<'dom> LayoutElementHelpers<'dom> for LayoutDom<'dom, Element> {
|
|||
hints.push(from_declaration(
|
||||
shared_lock,
|
||||
PropertyDeclaration::FontFamily(font_family::SpecifiedValue::Values(
|
||||
computed::font::FontFamilyList::new(Box::new([
|
||||
computed::font::SingleFontFamily::from_atom(font_family),
|
||||
])),
|
||||
computed::font::FontFamilyList {
|
||||
list: Box::new([computed::font::SingleFontFamily::from_atom(font_family)]),
|
||||
},
|
||||
)),
|
||||
));
|
||||
}
|
||||
|
|
|
@ -21,7 +21,6 @@ use crate::dom::node::{
|
|||
use crate::dom::virtualmethods::VirtualMethods;
|
||||
use dom_struct::dom_struct;
|
||||
use html5ever::{LocalName, Prefix};
|
||||
use parking_lot::RwLock;
|
||||
use servo_arc::Arc;
|
||||
use servo_config::pref;
|
||||
use std::sync::atomic::AtomicBool;
|
||||
|
@ -112,15 +111,12 @@ impl HTMLMetaElement {
|
|||
let shared_lock = document.style_shared_lock();
|
||||
let rule = CssRule::Viewport(Arc::new(shared_lock.wrap(translated_rule)));
|
||||
let sheet = Arc::new(Stylesheet {
|
||||
contents: StylesheetContents {
|
||||
rules: CssRules::new(vec![rule], shared_lock),
|
||||
origin: Origin::Author,
|
||||
namespaces: Default::default(),
|
||||
quirks_mode: document.quirks_mode(),
|
||||
url_data: RwLock::new(window_from_node(self).get_url()),
|
||||
source_map_url: RwLock::new(None),
|
||||
source_url: RwLock::new(None),
|
||||
},
|
||||
contents: StylesheetContents::from_shared_data(
|
||||
CssRules::new(vec![rule], shared_lock),
|
||||
Origin::Author,
|
||||
window_from_node(self).get_url(),
|
||||
document.quirks_mode(),
|
||||
),
|
||||
media: Arc::new(shared_lock.wrap(MediaList::empty())),
|
||||
shared_lock: shared_lock.clone(),
|
||||
disabled: AtomicBool::new(false),
|
||||
|
|
|
@ -29,7 +29,6 @@ use net_traits::{
|
|||
FetchMetadata, FetchResponseListener, FilteredMetadata, Metadata, NetworkError, ReferrerPolicy,
|
||||
};
|
||||
use net_traits::{ResourceFetchTiming, ResourceTimingType};
|
||||
use parking_lot::RwLock;
|
||||
use servo_arc::Arc;
|
||||
use servo_url::ImmutableOrigin;
|
||||
use servo_url::ServoUrl;
|
||||
|
@ -41,9 +40,7 @@ use style::parser::ParserContext;
|
|||
use style::shared_lock::{Locked, SharedRwLock};
|
||||
use style::stylesheets::import_rule::ImportSheet;
|
||||
use style::stylesheets::StylesheetLoader as StyleStylesheetLoader;
|
||||
use style::stylesheets::{
|
||||
CssRules, ImportRule, Namespaces, Origin, Stylesheet, StylesheetContents,
|
||||
};
|
||||
use style::stylesheets::{CssRules, ImportRule, Origin, Stylesheet, StylesheetContents};
|
||||
use style::values::CssUrl;
|
||||
|
||||
pub trait StylesheetOwner {
|
||||
|
@ -363,15 +360,12 @@ impl<'a> StyleStylesheetLoader for StylesheetLoader<'a> {
|
|||
media: Arc<Locked<MediaList>>,
|
||||
) -> Arc<Locked<ImportRule>> {
|
||||
let sheet = Arc::new(Stylesheet {
|
||||
contents: StylesheetContents {
|
||||
rules: CssRules::new(Vec::new(), lock),
|
||||
origin: context.stylesheet_origin,
|
||||
url_data: RwLock::new(context.url_data.clone()),
|
||||
quirks_mode: context.quirks_mode,
|
||||
namespaces: RwLock::new(Namespaces::default()),
|
||||
source_map_url: RwLock::new(None),
|
||||
source_url: RwLock::new(None),
|
||||
},
|
||||
contents: StylesheetContents::from_shared_data(
|
||||
CssRules::new(Vec::new(), lock),
|
||||
context.stylesheet_origin,
|
||||
context.url_data.clone(),
|
||||
context.quirks_mode,
|
||||
),
|
||||
media: media,
|
||||
shared_lock: lock.clone(),
|
||||
disabled: AtomicBool::new(false),
|
||||
|
|
|
@ -256,11 +256,6 @@ pub trait Parser<'i> {
|
|||
false
|
||||
}
|
||||
|
||||
/// The error recovery that selector lists inside :is() and :where() have.
|
||||
fn is_and_where_error_recovery(&self) -> ParseErrorRecovery {
|
||||
ParseErrorRecovery::IgnoreInvalidSelector
|
||||
}
|
||||
|
||||
/// Whether the given function name is an alias for the `:is()` function.
|
||||
fn is_is_alias(&self, _name: &str) -> bool {
|
||||
false
|
||||
|
@ -344,7 +339,7 @@ pub struct SelectorList<Impl: SelectorImpl>(
|
|||
);
|
||||
|
||||
/// How to treat invalid selectors in a selector list.
|
||||
pub enum ParseErrorRecovery {
|
||||
enum ParseErrorRecovery {
|
||||
/// Discard the entire selector list, this is the default behavior for
|
||||
/// almost all of CSS.
|
||||
DiscardList,
|
||||
|
@ -2277,7 +2272,7 @@ where
|
|||
state |
|
||||
SelectorParsingState::SKIP_DEFAULT_NAMESPACE |
|
||||
SelectorParsingState::DISALLOW_PSEUDOS,
|
||||
parser.is_and_where_error_recovery(),
|
||||
ParseErrorRecovery::IgnoreInvalidSelector,
|
||||
)?;
|
||||
Ok(component(inner.0.into_vec().into_boxed_slice()))
|
||||
}
|
||||
|
@ -2686,10 +2681,6 @@ pub mod tests {
|
|||
true
|
||||
}
|
||||
|
||||
fn is_and_where_error_recovery(&self) -> ParseErrorRecovery {
|
||||
ParseErrorRecovery::DiscardList
|
||||
}
|
||||
|
||||
fn parse_part(&self) -> bool {
|
||||
true
|
||||
}
|
||||
|
@ -3294,9 +3285,9 @@ pub mod tests {
|
|||
assert!(parse("::slotted(div)::before").is_ok());
|
||||
assert!(parse("slot::slotted(div,foo)").is_err());
|
||||
|
||||
assert!(parse("foo:where()").is_err());
|
||||
assert!(parse("foo:where()").is_ok());
|
||||
assert!(parse("foo:where(div, foo, .bar baz)").is_ok());
|
||||
assert!(parse("foo:where(::before)").is_err());
|
||||
assert!(parse_expected("foo:where(::before)", Some("foo:where()")).is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
|
@ -248,6 +248,14 @@ impl<T> Arc<T> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Like from_raw, but returns an addrefed arc instead.
|
||||
#[inline]
|
||||
pub unsafe fn from_raw_addrefed(ptr: *const T) -> Self {
|
||||
let arc = Self::from_raw(ptr);
|
||||
mem::forget(arc.clone());
|
||||
arc
|
||||
}
|
||||
|
||||
/// Create a new static Arc<T> (one that won't reference count the object)
|
||||
/// and place it in the allocation provided by the specified `alloc`
|
||||
/// function.
|
||||
|
|
|
@ -27,7 +27,6 @@ servo-layout-2013 = []
|
|||
servo-layout-2020 = []
|
||||
gecko_debug = []
|
||||
gecko_refcount_logging = []
|
||||
gecko_profiler = []
|
||||
|
||||
[dependencies]
|
||||
app_units = "0.7"
|
||||
|
|
|
@ -46,11 +46,15 @@ lazy_static! {
|
|||
.join("layout/style/ServoBindings.toml");
|
||||
read_config(&path)
|
||||
};
|
||||
static ref BUILD_CONFIG: Table = {
|
||||
static ref BINDGEN_FLAGS: Vec<String> = {
|
||||
// Load build-specific config overrides.
|
||||
let path = PathBuf::from(env::var_os("MOZ_TOPOBJDIR").unwrap())
|
||||
.join("layout/style/bindgen.toml");
|
||||
read_config(&path)
|
||||
.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()
|
||||
.map(std::borrow::ToOwned::to_owned)
|
||||
.collect()
|
||||
};
|
||||
static ref INCLUDE_RE: Regex = Regex::new(r#"#include\s*"(.+?)""#).unwrap();
|
||||
static ref DISTDIR_PATH: PathBuf = {
|
||||
|
@ -159,12 +163,8 @@ impl BuilderExt for Builder {
|
|||
builder = builder.clang_arg("-DDEBUG=1").clang_arg("-DJS_DEBUG=1");
|
||||
}
|
||||
|
||||
let build_config = BUILD_CONFIG["build"]
|
||||
.as_table()
|
||||
.expect("Malformed config file");
|
||||
let extra_bindgen_flags = build_config["args"].as_array().unwrap().as_slice();
|
||||
for item in extra_bindgen_flags.iter() {
|
||||
builder = builder.clang_arg(item.as_str().expect("Expect string in list"));
|
||||
for item in &*BINDGEN_FLAGS {
|
||||
builder = builder.clang_arg(item);
|
||||
}
|
||||
|
||||
builder
|
||||
|
|
|
@ -55,7 +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!("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
|
||||
|
|
|
@ -88,7 +88,7 @@ pub type Name = Atom;
|
|||
///
|
||||
/// <https://drafts.csswg.org/css-variables/#typedef-custom-property-name>
|
||||
pub fn parse_name(s: &str) -> Result<&str, ()> {
|
||||
if s.starts_with("--") {
|
||||
if s.starts_with("--") && s.len() > 2 {
|
||||
Ok(&s[2..])
|
||||
} else {
|
||||
Err(())
|
||||
|
@ -318,11 +318,6 @@ fn parse_declaration_value<'i, 't>(
|
|||
missing_closing_characters: &mut String,
|
||||
) -> Result<(TokenSerializationType, TokenSerializationType), ParseError<'i>> {
|
||||
input.parse_until_before(Delimiter::Bang | Delimiter::Semicolon, |input| {
|
||||
// Need at least one token
|
||||
let start = input.state();
|
||||
input.next_including_whitespace()?;
|
||||
input.reset(&start);
|
||||
|
||||
parse_declaration_value_block(input, references, missing_closing_characters)
|
||||
})
|
||||
}
|
||||
|
@ -334,6 +329,7 @@ fn parse_declaration_value_block<'i, 't>(
|
|||
mut references: Option<&mut VarOrEnvReferences>,
|
||||
missing_closing_characters: &mut String,
|
||||
) -> Result<(TokenSerializationType, TokenSerializationType), ParseError<'i>> {
|
||||
input.skip_whitespace();
|
||||
let mut token_start = input.position();
|
||||
let mut token = match input.next_including_whitespace_and_comments() {
|
||||
Ok(token) => token,
|
||||
|
@ -477,10 +473,8 @@ fn parse_fallback<'i, 't>(input: &mut Parser<'i, 't>) -> Result<(), ParseError<'
|
|||
// Exclude `!` and `;` at the top level
|
||||
// https://drafts.csswg.org/css-syntax/#typedef-declaration-value
|
||||
input.parse_until_before(Delimiter::Bang | Delimiter::Semicolon, |input| {
|
||||
// At least one non-comment token.
|
||||
input.next_including_whitespace()?;
|
||||
// Skip until the end.
|
||||
while let Ok(_) = input.next_including_whitespace_and_comments() {}
|
||||
while input.next_including_whitespace_and_comments().is_ok() {}
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
|
@ -996,12 +990,12 @@ fn substitute_block<'i>(
|
|||
while input.next().is_ok() {}
|
||||
} else {
|
||||
input.expect_comma()?;
|
||||
input.skip_whitespace();
|
||||
let after_comma = input.state();
|
||||
let first_token_type = input
|
||||
.next_including_whitespace_and_comments()
|
||||
// parse_var_function() ensures that .unwrap() will not fail.
|
||||
.unwrap()
|
||||
.serialization_type();
|
||||
.ok()
|
||||
.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(
|
||||
|
|
|
@ -175,6 +175,25 @@ impl ElementStyles {
|
|||
self.primary().get_box().clone_display().is_none()
|
||||
}
|
||||
|
||||
/// Whether this element uses viewport units.
|
||||
pub fn uses_viewport_units(&self) -> bool {
|
||||
use crate::computed_value_flags::ComputedValueFlags;
|
||||
|
||||
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) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
#[cfg(feature = "gecko")]
|
||||
fn size_of_excluding_cvs(&self, _ops: &mut MallocSizeOfOps) -> usize {
|
||||
// As the method name suggests, we don't measures the ComputedValues
|
||||
|
|
|
@ -151,7 +151,7 @@ pub trait TNode: Sized + Copy + Clone + Debug + NodeInfo + PartialEq {
|
|||
/// Get this node's first child.
|
||||
fn first_child(&self) -> Option<Self>;
|
||||
|
||||
/// Get this node's first child.
|
||||
/// Get this node's last child.
|
||||
fn last_child(&self) -> Option<Self>;
|
||||
|
||||
/// Get this node's previous sibling.
|
||||
|
@ -881,7 +881,7 @@ pub trait TElement:
|
|||
}
|
||||
}
|
||||
// TODO: Could be more granular.
|
||||
if !shadow.host().exports_any_part() {
|
||||
if !inner_shadow_host.exports_any_part() {
|
||||
break;
|
||||
}
|
||||
inner_shadow = shadow;
|
||||
|
|
|
@ -140,7 +140,8 @@ where
|
|||
// 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| {
|
||||
profiler_label!(Style);
|
||||
#[cfg(feature = "gecko")]
|
||||
gecko_profiler_label!(Layout, StyleComputation);
|
||||
parallel::traverse_nodes(
|
||||
drain,
|
||||
DispatchMode::TailCall,
|
||||
|
|
|
@ -56,10 +56,17 @@ impl GeckoStyleSheet {
|
|||
/// already holds a strong reference.
|
||||
#[inline]
|
||||
pub unsafe fn from_addrefed(s: *const DomStyleSheet) -> Self {
|
||||
debug_assert!(!s.is_null());
|
||||
assert!(!s.is_null());
|
||||
GeckoStyleSheet(s)
|
||||
}
|
||||
|
||||
/// HACK(emilio): This is so that we can avoid crashing release due to
|
||||
/// bug 1719963 and can hopefully get a useful report from fuzzers.
|
||||
#[inline]
|
||||
pub fn hack_is_null(&self) -> bool {
|
||||
self.0.is_null()
|
||||
}
|
||||
|
||||
/// Get the raw `StyleSheet` that we're wrapping.
|
||||
pub fn raw(&self) -> &DomStyleSheet {
|
||||
unsafe { &*self.0 }
|
||||
|
|
|
@ -590,6 +590,23 @@ fn eval_moz_os_version(
|
|||
query_value.as_ptr() == os_version
|
||||
}
|
||||
|
||||
fn eval_moz_windows_non_native_menus(
|
||||
device: &Device,
|
||||
query_value: Option<bool>,
|
||||
_: Option<RangeOrOperator>,
|
||||
) -> bool {
|
||||
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) &&
|
||||
get_lnf_int_as_bool(bindings::LookAndFeel_IntID::WindowsDefaultTheme as i32)
|
||||
},
|
||||
};
|
||||
|
||||
query_value.map_or(use_non_native_menus, |v| v == use_non_native_menus)
|
||||
}
|
||||
|
||||
fn get_lnf_int(int_id: i32) -> i32 {
|
||||
unsafe { bindings::Gecko_GetLookAndFeelInt(int_id) }
|
||||
}
|
||||
|
@ -663,7 +680,7 @@ macro_rules! bool_pref_feature {
|
|||
/// to support new types in these entries and (2) ensuring that either
|
||||
/// nsPresContext::MediaFeatureValuesChanged is called when the value that
|
||||
/// would be returned by the evaluator function could change.
|
||||
pub static MEDIA_FEATURES: [MediaFeatureDescription; 62] = [
|
||||
pub static MEDIA_FEATURES: [MediaFeatureDescription; 60] = [
|
||||
feature!(
|
||||
atom!("width"),
|
||||
AllowsRanges::Yes,
|
||||
|
@ -879,6 +896,12 @@ pub static MEDIA_FEATURES: [MediaFeatureDescription; 62] = [
|
|||
keyword_evaluator!(eval_toolbar_prefers_color_scheme, ToolbarPrefersColorScheme),
|
||||
ParsingRequirements::CHROME_AND_UA_ONLY,
|
||||
),
|
||||
feature!(
|
||||
atom!("-moz-windows-non-native-menus"),
|
||||
AllowsRanges::No,
|
||||
Evaluator::BoolInteger(eval_moz_windows_non_native_menus),
|
||||
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),
|
||||
|
@ -903,10 +926,6 @@ pub static MEDIA_FEATURES: [MediaFeatureDescription; 62] = [
|
|||
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-system-dark-theme"), SystemUsesDarkTheme),
|
||||
|
||||
bool_pref_feature!(atom!("-moz-proton"), "browser.proton.enabled"),
|
||||
bool_pref_feature!(atom!("-moz-proton-modals"), "browser.proton.modals.enabled"),
|
||||
bool_pref_feature!(atom!("-moz-proton-contextmenus"), "browser.proton.contextmenus.enabled"),
|
||||
bool_pref_feature!(atom!("-moz-proton-doorhangers"), "browser.proton.doorhangers.enabled"),
|
||||
bool_pref_feature!(atom!("-moz-proton-places-tooltip"), "browser.proton.places-tooltip.enabled"),
|
||||
];
|
||||
|
|
|
@ -93,7 +93,7 @@ impl Device {
|
|||
document,
|
||||
default_values: ComputedValues::default_values(doc),
|
||||
root_font_size: AtomicU32::new(FONT_MEDIUM_PX.to_bits()),
|
||||
body_text_color: AtomicUsize::new(prefs.mDefaultColor as usize),
|
||||
body_text_color: AtomicUsize::new(prefs.mColors.mDefault as usize),
|
||||
used_root_font_size: AtomicBool::new(false),
|
||||
used_font_metrics: AtomicBool::new(false),
|
||||
used_viewport_size: AtomicBool::new(false),
|
||||
|
@ -387,12 +387,12 @@ impl Device {
|
|||
|
||||
/// Returns the default background color.
|
||||
pub fn default_background_color(&self) -> RGBA {
|
||||
convert_nscolor_to_rgba(self.pref_sheet_prefs().mDefaultBackgroundColor)
|
||||
convert_nscolor_to_rgba(self.pref_sheet_prefs().mColors.mDefaultBackground)
|
||||
}
|
||||
|
||||
/// Returns the default foreground color.
|
||||
pub fn default_color(&self) -> RGBA {
|
||||
convert_nscolor_to_rgba(self.pref_sheet_prefs().mDefaultColor)
|
||||
convert_nscolor_to_rgba(self.pref_sheet_prefs().mColors.mDefault)
|
||||
}
|
||||
|
||||
/// Returns the current effective text zoom.
|
||||
|
|
|
@ -13,8 +13,6 @@ pub mod conversions;
|
|||
pub mod data;
|
||||
pub mod media_features;
|
||||
pub mod media_queries;
|
||||
#[cfg(feature = "gecko_profiler")]
|
||||
pub mod profiler;
|
||||
pub mod pseudo_element;
|
||||
pub mod restyle_damage;
|
||||
pub mod selector_parser;
|
||||
|
|
|
@ -1,75 +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/. */
|
||||
|
||||
//! Gecko profiler support.
|
||||
//!
|
||||
//! Use the `profiler_label!` macro from macros.rs.
|
||||
|
||||
use crate::gecko_bindings::structs;
|
||||
|
||||
/// A label describing a category of work that style threads can perform.
|
||||
pub enum ProfilerLabel {
|
||||
/// Style computation.
|
||||
Style,
|
||||
/// Style sheet parsing.
|
||||
Parse,
|
||||
}
|
||||
|
||||
/// RAII object that constructs and destroys a C++ AutoProfilerLabel object
|
||||
/// pointed to be the specified reference.
|
||||
#[cfg(feature = "gecko_profiler")]
|
||||
pub struct AutoProfilerLabel<'a>(&'a mut structs::AutoProfilerLabel);
|
||||
|
||||
#[cfg(feature = "gecko_profiler")]
|
||||
impl<'a> AutoProfilerLabel<'a> {
|
||||
/// Creates a new AutoProfilerLabel with the specified label type.
|
||||
///
|
||||
/// unsafe since the caller must ensure that `label` is allocated on the
|
||||
/// stack.
|
||||
#[inline]
|
||||
pub unsafe fn new(
|
||||
label: &mut std::mem::MaybeUninit<structs::AutoProfilerLabel>,
|
||||
label_type: ProfilerLabel,
|
||||
) -> AutoProfilerLabel {
|
||||
let category_pair = match label_type {
|
||||
ProfilerLabel::Style => structs::JS::ProfilingCategoryPair_LAYOUT_StyleComputation,
|
||||
ProfilerLabel::Parse => structs::JS::ProfilingCategoryPair_LAYOUT_CSSParsing,
|
||||
};
|
||||
structs::Gecko_Construct_AutoProfilerLabel(label.as_mut_ptr(), category_pair);
|
||||
AutoProfilerLabel(&mut *label.as_mut_ptr())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "gecko_profiler")]
|
||||
impl<'a> Drop for AutoProfilerLabel<'a> {
|
||||
#[inline]
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
structs::Gecko_Destroy_AutoProfilerLabel(self.0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Whether the Gecko profiler is currently active.
|
||||
///
|
||||
/// This implementation must be kept in sync with
|
||||
/// `mozilla::profiler::detail::RacyFeatures::IsActive`.
|
||||
#[cfg(feature = "gecko_profiler")]
|
||||
#[inline]
|
||||
pub fn profiler_is_active() -> bool {
|
||||
use self::structs::profiler::detail;
|
||||
use std::mem;
|
||||
use std::sync::atomic::{AtomicU32, Ordering};
|
||||
|
||||
let active_and_features: &AtomicU32 =
|
||||
unsafe { mem::transmute(&detail::RacyFeatures_sActiveAndFeatures) };
|
||||
(active_and_features.load(Ordering::Relaxed) & detail::RacyFeatures_Active) != 0
|
||||
}
|
||||
|
||||
/// Always false when the Gecko profiler is disabled.
|
||||
#[cfg(not(feature = "gecko_profiler"))]
|
||||
#[inline]
|
||||
pub fn profiler_is_active() -> bool {
|
||||
false
|
||||
}
|
|
@ -14,7 +14,7 @@ use crate::string_cache::{Atom, Namespace, WeakAtom, WeakNamespace};
|
|||
use crate::values::{AtomIdent, AtomString};
|
||||
use cssparser::{BasicParseError, BasicParseErrorKind, Parser};
|
||||
use cssparser::{CowRcStr, SourceLocation, ToCss, Token};
|
||||
use selectors::parser::{ParseErrorRecovery, SelectorParseErrorKind};
|
||||
use selectors::parser::SelectorParseErrorKind;
|
||||
use selectors::SelectorList;
|
||||
use std::fmt;
|
||||
use style_traits::{CssWriter, ParseError, StyleParseErrorKind, ToCss as ToCss_};
|
||||
|
@ -311,15 +311,6 @@ impl<'a, 'i> ::selectors::Parser<'i> for SelectorParser<'a> {
|
|||
true
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn is_and_where_error_recovery(&self) -> ParseErrorRecovery {
|
||||
if static_prefs::pref!("layout.css.is-and-where-better-error-recovery.enabled") {
|
||||
ParseErrorRecovery::IgnoreInvalidSelector
|
||||
} else {
|
||||
ParseErrorRecovery::DiscardList
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn parse_part(&self) -> bool {
|
||||
true
|
||||
|
|
|
@ -409,9 +409,11 @@ impl<'ln> TNode for GeckoNode<'ln> {
|
|||
#[inline]
|
||||
fn prev_sibling(&self) -> Option<Self> {
|
||||
unsafe {
|
||||
bindings::Gecko_GetPreviousSibling(self.0)
|
||||
.as_ref()
|
||||
.map(GeckoNode)
|
||||
let prev_or_last = GeckoNode::from_content(self.0.mPreviousOrLastSibling.as_ref()?);
|
||||
if prev_or_last.0.mNextSibling.raw::<nsIContent>().is_null() {
|
||||
return None;
|
||||
}
|
||||
Some(prev_or_last)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -308,11 +308,6 @@ impl_threadsafe_refcount!(
|
|||
bindings::Gecko_AddRefnsIURIArbitraryThread,
|
||||
bindings::Gecko_ReleasensIURIArbitraryThread
|
||||
);
|
||||
impl_threadsafe_refcount!(
|
||||
structs::SharedFontList,
|
||||
bindings::Gecko_AddRefSharedFontListArbitraryThread,
|
||||
bindings::Gecko_ReleaseSharedFontListArbitraryThread
|
||||
);
|
||||
impl_threadsafe_refcount!(
|
||||
structs::SheetLoadDataHolder,
|
||||
bindings::Gecko_AddRefSheetLoadDataHolderArbitraryThread,
|
||||
|
|
|
@ -10,6 +10,8 @@ use crate::gecko_bindings::bindings;
|
|||
use crate::parallel::STYLE_THREAD_STACK_SIZE_KB;
|
||||
use crate::shared_lock::SharedRwLock;
|
||||
use crate::thread_state;
|
||||
#[cfg(feature = "gecko")]
|
||||
use gecko_profiler;
|
||||
use parking_lot::{RwLock, RwLockReadGuard};
|
||||
use rayon;
|
||||
use std::env;
|
||||
|
@ -49,20 +51,16 @@ fn thread_startup(_index: usize) {
|
|||
thread_state::initialize_layout_worker_thread();
|
||||
#[cfg(feature = "gecko")]
|
||||
unsafe {
|
||||
use std::ffi::CString;
|
||||
|
||||
bindings::Gecko_SetJemallocThreadLocalArena(true);
|
||||
let name = thread_name(_index);
|
||||
let name = CString::new(name).unwrap();
|
||||
// Gecko_RegisterProfilerThread copies the passed name here.
|
||||
bindings::Gecko_RegisterProfilerThread(name.as_ptr());
|
||||
gecko_profiler::register_thread(&name);
|
||||
}
|
||||
}
|
||||
|
||||
fn thread_shutdown(_: usize) {
|
||||
#[cfg(feature = "gecko")]
|
||||
unsafe {
|
||||
bindings::Gecko_UnregisterProfilerThread();
|
||||
gecko_profiler::unregister_thread();
|
||||
bindings::Gecko_SetJemallocThreadLocalArena(false);
|
||||
}
|
||||
ALIVE_WORKER_THREADS.fetch_sub(1, Ordering::Relaxed);
|
||||
|
|
|
@ -60,7 +60,12 @@ impl RestyleHint {
|
|||
/// Returns whether this hint invalidates the element and all its
|
||||
/// descendants.
|
||||
pub fn contains_subtree(&self) -> bool {
|
||||
self.contains(RestyleHint::RESTYLE_SELF | RestyleHint::RESTYLE_DESCENDANTS)
|
||||
self.contains(Self::restyle_subtree())
|
||||
}
|
||||
|
||||
/// Returns whether we'll recascade all of the descendants.
|
||||
pub fn will_recascade_subtree(&self) -> bool {
|
||||
self.contains_subtree() || self.contains(Self::recascade_subtree())
|
||||
}
|
||||
|
||||
/// Returns whether we need to restyle this element.
|
||||
|
|
|
@ -7,3 +7,4 @@
|
|||
pub mod element;
|
||||
pub mod media_queries;
|
||||
pub mod stylesheets;
|
||||
pub mod viewport_units;
|
||||
|
|
57
components/style/invalidation/viewport_units.rs
Normal file
57
components/style/invalidation/viewport_units.rs
Normal file
|
@ -0,0 +1,57 @@
|
|||
/* 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/. */
|
||||
|
||||
//! Invalidates style of all elements that depend on viewport units.
|
||||
|
||||
use crate::dom::{TElement, TNode};
|
||||
use crate::invalidation::element::restyle_hints::RestyleHint;
|
||||
|
||||
/// Invalidates style of all elements that depend on viewport units.
|
||||
///
|
||||
/// Returns whether any element was invalidated.
|
||||
pub fn invalidate<E>(root: E) -> bool
|
||||
where
|
||||
E: TElement,
|
||||
{
|
||||
debug!("invalidation::viewport_units::invalidate({:?})", root);
|
||||
invalidate_recursively(root)
|
||||
}
|
||||
|
||||
fn invalidate_recursively<E>(element: E) -> bool
|
||||
where
|
||||
E: TElement,
|
||||
{
|
||||
let mut data = match element.mutate_data() {
|
||||
Some(data) => data,
|
||||
None => return false,
|
||||
};
|
||||
|
||||
if data.hint.will_recascade_subtree() {
|
||||
debug!("invalidate_recursively: {:?} was already invalid", element);
|
||||
return false;
|
||||
}
|
||||
|
||||
let uses_viewport_units = data.styles.uses_viewport_units();
|
||||
if uses_viewport_units {
|
||||
debug!("invalidate_recursively: {:?} uses viewport units", element);
|
||||
data.hint.insert(RestyleHint::RECASCADE_SELF);
|
||||
}
|
||||
|
||||
let mut any_children_invalid = false;
|
||||
for child in element.traversal_children() {
|
||||
if let Some(child) = child.as_element() {
|
||||
any_children_invalid |= invalidate_recursively(child);
|
||||
}
|
||||
}
|
||||
|
||||
if any_children_invalid {
|
||||
debug!(
|
||||
"invalidate_recursively: Children of {:?} changed, setting dirty descendants",
|
||||
element
|
||||
);
|
||||
unsafe { element.set_dirty_descendants() }
|
||||
}
|
||||
|
||||
uses_viewport_units || any_children_invalid
|
||||
}
|
|
@ -33,6 +33,9 @@ extern crate cssparser;
|
|||
extern crate debug_unreachable;
|
||||
#[macro_use]
|
||||
extern crate derive_more;
|
||||
#[macro_use]
|
||||
#[cfg(feature = "gecko")]
|
||||
extern crate gecko_profiler;
|
||||
#[cfg(feature = "gecko")]
|
||||
#[macro_use]
|
||||
pub mod gecko_string_cache;
|
||||
|
|
|
@ -105,35 +105,6 @@ macro_rules! define_keyword_type {
|
|||
};
|
||||
}
|
||||
|
||||
/// Place a Gecko profiler label on the stack.
|
||||
///
|
||||
/// The `label_type` argument must be the name of a variant of `ProfilerLabel`.
|
||||
#[cfg(feature = "gecko_profiler")]
|
||||
#[macro_export]
|
||||
macro_rules! profiler_label {
|
||||
($label_type:ident) => {
|
||||
let mut _profiler_label =
|
||||
::std::mem::MaybeUninit::<$crate::gecko_bindings::structs::AutoProfilerLabel>::uninit();
|
||||
let _profiler_label = if $crate::gecko::profiler::profiler_is_active() {
|
||||
unsafe {
|
||||
Some($crate::gecko::profiler::AutoProfilerLabel::new(
|
||||
&mut _profiler_label,
|
||||
$crate::gecko::profiler::ProfilerLabel::$label_type,
|
||||
))
|
||||
}
|
||||
} else {
|
||||
None
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
/// No-op when the Gecko profiler is not available.
|
||||
#[cfg(not(feature = "gecko_profiler"))]
|
||||
#[macro_export]
|
||||
macro_rules! profiler_label {
|
||||
($label_type:ident) => {};
|
||||
}
|
||||
|
||||
#[cfg(feature = "servo")]
|
||||
macro_rules! local_name {
|
||||
($s:tt) => {
|
||||
|
|
|
@ -274,7 +274,8 @@ pub fn traverse_nodes<'a, 'scope, E, D, I>(
|
|||
top_down_dom(&work, root, traversal_data, scope, pool, traversal, tls);
|
||||
} else {
|
||||
scope.spawn_fifo(move |scope| {
|
||||
profiler_label!(Style);
|
||||
#[cfg(feature = "gecko")]
|
||||
gecko_profiler_label!(Layout, StyleComputation);
|
||||
let work = work;
|
||||
top_down_dom(&work, root, traversal_data, scope, pool, traversal, tls);
|
||||
});
|
||||
|
@ -284,7 +285,8 @@ pub fn traverse_nodes<'a, 'scope, E, D, I>(
|
|||
let nodes: WorkUnit<E::ConcreteNode> = chunk.collect();
|
||||
let traversal_data_copy = traversal_data.clone();
|
||||
scope.spawn_fifo(move |scope| {
|
||||
profiler_label!(Style);
|
||||
#[cfg(feature = "gecko")]
|
||||
gecko_profiler_label!(Layout, StyleComputation);
|
||||
let n = nodes;
|
||||
top_down_dom(&*n, root, traversal_data_copy, scope, pool, traversal, tls)
|
||||
});
|
||||
|
|
|
@ -464,7 +464,22 @@ fn tweak_when_ignoring_colors(
|
|||
}
|
||||
}
|
||||
},
|
||||
_ => {},
|
||||
_ => {
|
||||
// We honor transparent and 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.
|
||||
if let Some(color) = declaration.color_value() {
|
||||
if color.is_system() || alpha_channel(color, context) == 0 {
|
||||
return;
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
*declaration.to_mut() =
|
||||
|
@ -786,6 +801,35 @@ impl<'a, 'b: 'a> Cascade<'a, 'b> {
|
|||
builder.add_flags(ComputedValueFlags::HAS_AUTHOR_SPECIFIED_PADDING);
|
||||
}
|
||||
|
||||
if self
|
||||
.author_specified
|
||||
.contains(LonghandId::FontFamily)
|
||||
{
|
||||
builder.add_flags(ComputedValueFlags::HAS_AUTHOR_SPECIFIED_FONT_FAMILY);
|
||||
}
|
||||
|
||||
if self
|
||||
.author_specified
|
||||
.contains(LonghandId::LetterSpacing)
|
||||
{
|
||||
builder.add_flags(ComputedValueFlags::HAS_AUTHOR_SPECIFIED_LETTER_SPACING);
|
||||
}
|
||||
|
||||
if self
|
||||
.author_specified
|
||||
.contains(LonghandId::WordSpacing)
|
||||
{
|
||||
builder.add_flags(ComputedValueFlags::HAS_AUTHOR_SPECIFIED_WORD_SPACING);
|
||||
}
|
||||
|
||||
#[cfg(feature = "gecko")]
|
||||
if self
|
||||
.author_specified
|
||||
.contains(LonghandId::FontSynthesis)
|
||||
{
|
||||
builder.add_flags(ComputedValueFlags::HAS_AUTHOR_SPECIFIED_FONT_SYNTHESIS);
|
||||
}
|
||||
|
||||
#[cfg(feature = "servo")]
|
||||
{
|
||||
if let Some(font) = builder.get_font_if_mutated() {
|
||||
|
@ -849,18 +893,19 @@ impl<'a, 'b: 'a> Cascade<'a, 'b> {
|
|||
|
||||
// System fonts are all right, and should have the default font type
|
||||
// set to none already, so bail out early.
|
||||
if font.mFont.systemFont {
|
||||
if font.mFont.family.is_system_font {
|
||||
debug_assert_eq!(
|
||||
font.mFont.fontlist.mDefaultFontType,
|
||||
font.mFont.family.families.fallback,
|
||||
GenericFontFamily::None
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
let generic = font.mFont.family.families.single_generic().unwrap_or(GenericFontFamily::None);
|
||||
let default_font_type = unsafe {
|
||||
bindings::Gecko_nsStyleFont_ComputeDefaultFontType(
|
||||
builder.device.document(),
|
||||
font.mGenericID,
|
||||
generic,
|
||||
font.mLanguage.mRawPtr,
|
||||
)
|
||||
};
|
||||
|
@ -870,15 +915,15 @@ impl<'a, 'b: 'a> Cascade<'a, 'b> {
|
|||
// 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 &&
|
||||
matches!(
|
||||
font.mGenericID,
|
||||
generic,
|
||||
GenericFontFamily::None |
|
||||
GenericFontFamily::Fantasy |
|
||||
GenericFontFamily::Cursive
|
||||
) &&
|
||||
default_font_type != GenericFontFamily::None;
|
||||
GenericFontFamily::Fantasy |
|
||||
GenericFontFamily::Cursive
|
||||
);
|
||||
|
||||
if !prioritize_user_fonts && default_font_type == font.mFont.fontlist.mDefaultFontType {
|
||||
if !prioritize_user_fonts && default_font_type == font.mFont.family.families.fallback {
|
||||
// Nothing to do.
|
||||
return;
|
||||
}
|
||||
|
@ -886,9 +931,9 @@ impl<'a, 'b: 'a> Cascade<'a, 'b> {
|
|||
};
|
||||
|
||||
let font = builder.mutate_font().gecko_mut();
|
||||
font.mFont.fontlist.mDefaultFontType = default_font_type;
|
||||
font.mFont.family.families.fallback = default_font_type;
|
||||
if prioritize_user_fonts {
|
||||
unsafe { bindings::Gecko_nsStyleFont_PrioritizeUserFonts(font, default_font_type) }
|
||||
font.mFont.family.families.prioritize_first_generic_or_prepend(default_font_type);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -12,7 +12,7 @@ bitflags! {
|
|||
/// If we ever want to add some flags that shouldn't inherit for them,
|
||||
/// we might want to add a function to handle this.
|
||||
#[repr(C)]
|
||||
pub struct ComputedValueFlags: u16 {
|
||||
pub struct ComputedValueFlags: u32 {
|
||||
/// Whether the style or any of the ancestors has a text-decoration-line
|
||||
/// property that should get propagated to descendants.
|
||||
///
|
||||
|
@ -89,6 +89,21 @@ bitflags! {
|
|||
/// FIXME(emilio): Try to merge this with BORDER_BACKGROUND, see
|
||||
/// https://github.com/w3c/csswg-drafts/issues/4777
|
||||
const HAS_AUTHOR_SPECIFIED_PADDING = 1 << 15;
|
||||
|
||||
/// Whether there are author-specified rules for `font-family`.
|
||||
const HAS_AUTHOR_SPECIFIED_FONT_FAMILY = 1 << 16;
|
||||
|
||||
/// Whether there are author-specified rules for `font-synthesis`.
|
||||
const HAS_AUTHOR_SPECIFIED_FONT_SYNTHESIS = 1 << 17;
|
||||
|
||||
/// Whether there are author-specified rules for `letter-spacing`.
|
||||
const HAS_AUTHOR_SPECIFIED_LETTER_SPACING = 1 << 18;
|
||||
|
||||
/// Whether there are author-specified rules for `word-spacing`.
|
||||
const HAS_AUTHOR_SPECIFIED_WORD_SPACING = 1 << 19;
|
||||
|
||||
/// Whether the style depends on viewport units.
|
||||
const USES_VIEWPORT_UNITS = 1 << 20;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -25,7 +25,6 @@ COUNTED_UNKNOWN_PROPERTIES = [
|
|||
"-webkit-user-modify",
|
||||
"-webkit-margin-before",
|
||||
"-webkit-margin-after",
|
||||
"tab-size",
|
||||
"-webkit-margin-start",
|
||||
"-webkit-column-break-inside",
|
||||
"-webkit-padding-start",
|
||||
|
@ -63,7 +62,6 @@ COUNTED_UNKNOWN_PROPERTIES = [
|
|||
"-webkit-text-combine",
|
||||
"-webkit-text-emphasis-style",
|
||||
"-webkit-text-emphasis",
|
||||
"d",
|
||||
"-webkit-mask-box-image-width",
|
||||
"-webkit-mask-box-image-source",
|
||||
"-webkit-mask-box-image-outset",
|
||||
|
@ -123,4 +121,5 @@ COUNTED_UNKNOWN_PROPERTIES = [
|
|||
"-webkit-columns",
|
||||
"-webkit-column-rule-color",
|
||||
"-webkit-shape-margin",
|
||||
"content-visibility",
|
||||
]
|
||||
|
|
|
@ -340,13 +340,14 @@ class Longhand(Property):
|
|||
assert (
|
||||
has_effect_on_gecko_scrollbars in [None, False, True]
|
||||
and not style_struct.inherited
|
||||
or (gecko_pref is None) == (has_effect_on_gecko_scrollbars is None)
|
||||
or (gecko_pref is None and enabled_in != "")
|
||||
== (has_effect_on_gecko_scrollbars is None)
|
||||
), (
|
||||
"Property "
|
||||
+ name
|
||||
+ ": has_effect_on_gecko_scrollbars must be "
|
||||
+ "specified, and must have a value of True or False, iff a "
|
||||
+ "property is inherited and is behind a Gecko pref"
|
||||
+ "property is inherited and is behind a Gecko pref or internal"
|
||||
)
|
||||
self.need_index = need_index
|
||||
self.gecko_ffi_name = gecko_ffi_name or "m" + self.camel_case
|
||||
|
@ -759,7 +760,7 @@ def _add_logical_props(data, props):
|
|||
# These are probably Gecko bugs and should be supported per spec.
|
||||
def _remove_common_first_line_and_first_letter_properties(props, engine):
|
||||
if engine == "gecko":
|
||||
props.remove("-moz-tab-size")
|
||||
props.remove("tab-size")
|
||||
props.remove("hyphens")
|
||||
props.remove("line-break")
|
||||
props.remove("text-align-last")
|
||||
|
|
|
@ -385,7 +385,7 @@ impl PropertyDeclarationBlock {
|
|||
// Step 1.2.3
|
||||
// We don't print !important when serializing individual properties,
|
||||
// so we treat this as a normal-importance property
|
||||
match shorthand.get_shorthand_appendable_value(list.iter().cloned()) {
|
||||
match shorthand.get_shorthand_appendable_value(&list) {
|
||||
Some(appendable_value) => append_declaration_value(dest, appendable_value),
|
||||
None => return Ok(()),
|
||||
}
|
||||
|
@ -911,9 +911,6 @@ impl PropertyDeclarationBlock {
|
|||
///
|
||||
/// https://drafts.csswg.org/cssom/#serialize-a-css-declaration-block
|
||||
pub fn to_css(&self, dest: &mut CssStringWriter) -> fmt::Result {
|
||||
use std::iter::Cloned;
|
||||
use std::slice;
|
||||
|
||||
let mut is_first_serialization = true; // trailing serializations should have a prepended space
|
||||
|
||||
// Step 1 -> dest = result list
|
||||
|
@ -938,7 +935,7 @@ impl PropertyDeclarationBlock {
|
|||
// properties in a declaration block, and that custom
|
||||
// properties can't be part of a shorthand, we can just care
|
||||
// about them here.
|
||||
append_serialization::<Cloned<slice::Iter<_>>, _>(
|
||||
append_serialization(
|
||||
dest,
|
||||
&property,
|
||||
AppendableValue::Declaration(declaration),
|
||||
|
@ -992,7 +989,7 @@ impl PropertyDeclarationBlock {
|
|||
|
||||
// Step 3.4.3:
|
||||
// Let current longhands be an empty array.
|
||||
let mut current_longhands = SmallVec::<[_; 10]>::new();
|
||||
let mut current_longhands = SmallVec::<[&_; 10]>::new();
|
||||
let mut logical_groups = LogicalGroupSet::new();
|
||||
let mut saw_one = false;
|
||||
let mut logical_mismatch = false;
|
||||
|
@ -1066,7 +1063,7 @@ impl PropertyDeclarationBlock {
|
|||
// Let value be the result of invoking serialize a CSS value
|
||||
// of current longhands.
|
||||
let appendable_value = match shorthand
|
||||
.get_shorthand_appendable_value(current_longhands.iter().cloned())
|
||||
.get_shorthand_appendable_value(¤t_longhands)
|
||||
{
|
||||
None => continue,
|
||||
Some(appendable_value) => appendable_value,
|
||||
|
@ -1076,15 +1073,9 @@ impl PropertyDeclarationBlock {
|
|||
// AppendableValue::Css.
|
||||
let mut v = CssString::new();
|
||||
let value = match appendable_value {
|
||||
AppendableValue::Css {
|
||||
css,
|
||||
with_variables,
|
||||
} => {
|
||||
AppendableValue::Css(css) => {
|
||||
debug_assert!(!css.is_empty());
|
||||
AppendableValue::Css {
|
||||
css,
|
||||
with_variables,
|
||||
}
|
||||
appendable_value
|
||||
},
|
||||
other => {
|
||||
append_declaration_value(&mut v, other)?;
|
||||
|
@ -1096,14 +1087,13 @@ impl PropertyDeclarationBlock {
|
|||
continue;
|
||||
}
|
||||
|
||||
AppendableValue::Css {
|
||||
AppendableValue::Css({
|
||||
// Safety: serialization only generates valid utf-8.
|
||||
#[cfg(feature = "gecko")]
|
||||
css: unsafe { v.as_str_unchecked() },
|
||||
unsafe { v.as_str_unchecked() }
|
||||
#[cfg(feature = "servo")]
|
||||
css: &v,
|
||||
with_variables: false,
|
||||
}
|
||||
&v
|
||||
})
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -1116,7 +1106,7 @@ impl PropertyDeclarationBlock {
|
|||
//
|
||||
// 3.4.10:
|
||||
// Append serialized declaration to list.
|
||||
append_serialization::<Cloned<slice::Iter<_>>, _>(
|
||||
append_serialization(
|
||||
dest,
|
||||
&shorthand,
|
||||
value,
|
||||
|
@ -1152,7 +1142,7 @@ impl PropertyDeclarationBlock {
|
|||
// its important flag set.
|
||||
//
|
||||
// Append serialized declaration to list.
|
||||
append_serialization::<Cloned<slice::Iter<_>>, _>(
|
||||
append_serialization(
|
||||
dest,
|
||||
&property,
|
||||
AppendableValue::Declaration(declaration),
|
||||
|
@ -1172,25 +1162,17 @@ impl PropertyDeclarationBlock {
|
|||
|
||||
/// A convenient enum to represent different kinds of stuff that can represent a
|
||||
/// _value_ in the serialization of a property declaration.
|
||||
pub enum AppendableValue<'a, I>
|
||||
where
|
||||
I: Iterator<Item = &'a PropertyDeclaration>,
|
||||
{
|
||||
pub enum AppendableValue<'a, 'b: 'a> {
|
||||
/// A given declaration, of which we'll serialize just the value.
|
||||
Declaration(&'a PropertyDeclaration),
|
||||
/// A set of declarations for a given shorthand.
|
||||
///
|
||||
/// FIXME: This needs more docs, where are the shorthands expanded? We print
|
||||
/// the property name before-hand, don't we?
|
||||
DeclarationsForShorthand(ShorthandId, I),
|
||||
DeclarationsForShorthand(ShorthandId, &'a [&'b PropertyDeclaration]),
|
||||
/// A raw CSS string, coming for example from a property with CSS variables,
|
||||
/// or when storing a serialized shorthand value before appending directly.
|
||||
Css {
|
||||
/// The raw CSS string.
|
||||
css: &'a str,
|
||||
/// Whether the original serialization contained variables or not.
|
||||
with_variables: bool,
|
||||
},
|
||||
Css(&'a str),
|
||||
}
|
||||
|
||||
/// Potentially appends whitespace after the first (property: value;) pair.
|
||||
|
@ -1207,56 +1189,34 @@ where
|
|||
}
|
||||
|
||||
/// Append a given kind of appendable value to a serialization.
|
||||
pub fn append_declaration_value<'a, I>(
|
||||
pub fn append_declaration_value<'a, 'b: 'a>(
|
||||
dest: &mut CssStringWriter,
|
||||
appendable_value: AppendableValue<'a, I>,
|
||||
) -> fmt::Result
|
||||
where
|
||||
I: Iterator<Item = &'a PropertyDeclaration>,
|
||||
{
|
||||
appendable_value: AppendableValue<'a, 'b>,
|
||||
) -> fmt::Result {
|
||||
match appendable_value {
|
||||
AppendableValue::Css { css, .. } => dest.write_str(css),
|
||||
AppendableValue::Css(css) => dest.write_str(css),
|
||||
AppendableValue::Declaration(decl) => decl.to_css(dest),
|
||||
AppendableValue::DeclarationsForShorthand(shorthand, decls) => {
|
||||
shorthand.longhands_to_css(decls, &mut CssWriter::new(dest))
|
||||
shorthand.longhands_to_css(decls, dest)
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
/// Append a given property and value pair to a serialization.
|
||||
pub fn append_serialization<'a, I, N>(
|
||||
pub fn append_serialization<'a, 'b: 'a, N>(
|
||||
dest: &mut CssStringWriter,
|
||||
property_name: &N,
|
||||
appendable_value: AppendableValue<'a, I>,
|
||||
appendable_value: AppendableValue<'a, 'b>,
|
||||
importance: Importance,
|
||||
is_first_serialization: &mut bool,
|
||||
) -> fmt::Result
|
||||
where
|
||||
I: Iterator<Item = &'a PropertyDeclaration>,
|
||||
N: ToCss,
|
||||
{
|
||||
handle_first_serialization(dest, is_first_serialization)?;
|
||||
|
||||
property_name.to_css(&mut CssWriter::new(dest))?;
|
||||
dest.write_char(':')?;
|
||||
|
||||
// for normal parsed values, add a space between key: and value
|
||||
match appendable_value {
|
||||
AppendableValue::Declaration(decl) => {
|
||||
if !decl.value_is_unparsed() {
|
||||
// For normal parsed values, add a space between key: and value.
|
||||
dest.write_str(" ")?
|
||||
}
|
||||
},
|
||||
AppendableValue::Css { with_variables, .. } => {
|
||||
if !with_variables {
|
||||
dest.write_str(" ")?
|
||||
}
|
||||
},
|
||||
// Currently append_serialization is only called with a Css or
|
||||
// a Declaration AppendableValue.
|
||||
AppendableValue::DeclarationsForShorthand(..) => unreachable!(),
|
||||
}
|
||||
dest.write_str(": ")?;
|
||||
|
||||
append_declaration_value(dest, appendable_value)?;
|
||||
|
||||
|
|
|
@ -22,7 +22,6 @@ use crate::gecko_bindings::bindings::Gecko_CopyConstruct_${style_struct.gecko_ff
|
|||
use crate::gecko_bindings::bindings::Gecko_Destroy_${style_struct.gecko_ffi_name};
|
||||
% endfor
|
||||
use crate::gecko_bindings::bindings::Gecko_CopyCounterStyle;
|
||||
use crate::gecko_bindings::bindings::Gecko_CopyFontFamilyFrom;
|
||||
use crate::gecko_bindings::bindings::Gecko_EnsureImageLayersLength;
|
||||
use crate::gecko_bindings::bindings::Gecko_nsStyleFont_SetLang;
|
||||
use crate::gecko_bindings::bindings::Gecko_nsStyleFont_CopyLangFrom;
|
||||
|
@ -799,11 +798,8 @@ fn static_assert() {
|
|||
${impl_simple_type_with_conversion("masonry_auto_flow", "mMasonryAutoFlow")}
|
||||
</%self:impl_trait>
|
||||
|
||||
<% skip_outline_longhands = " ".join("outline-style outline-width".split() +
|
||||
["-moz-outline-radius-{0}".format(x.replace("_", ""))
|
||||
for x in CORNERS]) %>
|
||||
<%self:impl_trait style_struct_name="Outline"
|
||||
skip_longhands="${skip_outline_longhands}">
|
||||
skip_longhands="outline-style outline-width">
|
||||
|
||||
pub fn set_outline_style(&mut self, v: longhands::outline_style::computed_value::T) {
|
||||
self.gecko.mOutlineStyle = v;
|
||||
|
@ -831,12 +827,6 @@ fn static_assert() {
|
|||
inherit_from="mOutlineWidth",
|
||||
round_to_pixels=True) %>
|
||||
|
||||
% for corner in CORNERS:
|
||||
<% impl_corner_style_coord("_moz_outline_radius_%s" % corner.replace("_", ""),
|
||||
"mOutlineRadius",
|
||||
corner) %>
|
||||
% endfor
|
||||
|
||||
pub fn outline_has_nonzero_width(&self) -> bool {
|
||||
self.gecko.mActualOutlineWidth != 0
|
||||
}
|
||||
|
@ -857,52 +847,6 @@ fn static_assert() {
|
|||
<% impl_font_settings("font_feature_settings", "gfxFontFeature", "FeatureTagValue", "i32", "u32") %>
|
||||
<% impl_font_settings("font_variation_settings", "gfxFontVariation", "VariationValue", "f32", "f32") %>
|
||||
|
||||
pub fn set_font_family(&mut self, v: longhands::font_family::computed_value::T) {
|
||||
use crate::values::computed::font::GenericFontFamily;
|
||||
|
||||
let is_system_font = v.is_system_font;
|
||||
self.gecko.mFont.systemFont = is_system_font;
|
||||
self.gecko.mGenericID = if is_system_font {
|
||||
GenericFontFamily::None
|
||||
} else {
|
||||
v.families.single_generic().unwrap_or(GenericFontFamily::None)
|
||||
};
|
||||
self.gecko.mFont.fontlist.mFontlist.mBasePtr.set_move(
|
||||
v.families.shared_font_list().clone()
|
||||
);
|
||||
// Fixed-up if needed in Cascade::fixup_font_stuff.
|
||||
self.gecko.mFont.fontlist.mDefaultFontType = GenericFontFamily::None;
|
||||
}
|
||||
|
||||
pub fn copy_font_family_from(&mut self, other: &Self) {
|
||||
unsafe { Gecko_CopyFontFamilyFrom(&mut self.gecko.mFont, &other.gecko.mFont); }
|
||||
self.gecko.mGenericID = other.gecko.mGenericID;
|
||||
self.gecko.mFont.systemFont = other.gecko.mFont.systemFont;
|
||||
}
|
||||
|
||||
pub fn reset_font_family(&mut self, other: &Self) {
|
||||
self.copy_font_family_from(other)
|
||||
}
|
||||
|
||||
pub fn clone_font_family(&self) -> longhands::font_family::computed_value::T {
|
||||
use crate::values::computed::font::{FontFamily, SingleFontFamily, FontFamilyList};
|
||||
|
||||
let fontlist = &self.gecko.mFont.fontlist;
|
||||
let shared_fontlist = unsafe { fontlist.mFontlist.mBasePtr.to_safe() };
|
||||
|
||||
let families = if shared_fontlist.mNames.is_empty() {
|
||||
let default = SingleFontFamily::Generic(fontlist.mDefaultFontType);
|
||||
FontFamilyList::new(Box::new([default]))
|
||||
} else {
|
||||
FontFamilyList::SharedFontList(shared_fontlist)
|
||||
};
|
||||
|
||||
FontFamily {
|
||||
families,
|
||||
is_system_font: self.gecko.mFont.systemFont,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn unzoom_fonts(&mut self, device: &Device) {
|
||||
use crate::values::generics::NonNegative;
|
||||
self.gecko.mSize = NonNegative(device.unzoom_text(self.gecko.mSize.0));
|
||||
|
@ -1008,26 +952,9 @@ fn static_assert() {
|
|||
|
||||
${impl_simple("font_variant_alternates", "mFont.variantAlternates")}
|
||||
|
||||
pub fn set_font_size_adjust(&mut self, v: longhands::font_size_adjust::computed_value::T) {
|
||||
use crate::properties::longhands::font_size_adjust::computed_value::T;
|
||||
match v {
|
||||
T::None => self.gecko.mFont.sizeAdjust = -1.0 as f32,
|
||||
T::Number(n) => self.gecko.mFont.sizeAdjust = n,
|
||||
}
|
||||
}
|
||||
${impl_simple("font_size_adjust", "mFont.sizeAdjust")}
|
||||
|
||||
pub fn copy_font_size_adjust_from(&mut self, other: &Self) {
|
||||
self.gecko.mFont.sizeAdjust = other.gecko.mFont.sizeAdjust;
|
||||
}
|
||||
|
||||
pub fn reset_font_size_adjust(&mut self, other: &Self) {
|
||||
self.copy_font_size_adjust_from(other)
|
||||
}
|
||||
|
||||
pub fn clone_font_size_adjust(&self) -> longhands::font_size_adjust::computed_value::T {
|
||||
use crate::properties::longhands::font_size_adjust::computed_value::T;
|
||||
T::from_gecko_adjust(self.gecko.mFont.sizeAdjust)
|
||||
}
|
||||
${impl_simple("font_family", "mFont.family")}
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
pub fn set__x_lang(&mut self, v: longhands::_x_lang::computed_value::T) {
|
||||
|
|
|
@ -845,10 +845,7 @@
|
|||
impl<'a> LonghandsToSerialize<'a> {
|
||||
/// Tries to get a serializable set of longhands given a set of
|
||||
/// property declarations.
|
||||
pub fn from_iter<I>(iter: I) -> Result<Self, ()>
|
||||
where
|
||||
I: Iterator<Item=&'a PropertyDeclaration>,
|
||||
{
|
||||
pub fn from_iter(iter: impl Iterator<Item = &'a PropertyDeclaration>) -> Result<Self, ()> {
|
||||
// Define all of the expected variables that correspond to the shorthand
|
||||
% for sub_property in shorthand.sub_properties:
|
||||
let mut ${sub_property.ident} =
|
||||
|
@ -856,8 +853,8 @@
|
|||
% endfor
|
||||
|
||||
// Attempt to assign the incoming declarations to the expected variables
|
||||
for longhand in iter {
|
||||
match *longhand {
|
||||
for declaration in iter {
|
||||
match *declaration {
|
||||
% for sub_property in shorthand.sub_properties:
|
||||
PropertyDeclaration::${sub_property.camel_case}(ref value) => {
|
||||
${sub_property.ident} = Some(value)
|
||||
|
@ -918,6 +915,14 @@
|
|||
})
|
||||
}
|
||||
|
||||
/// Try to serialize a given shorthand to a string.
|
||||
pub fn to_css(declarations: &[&PropertyDeclaration], dest: &mut crate::str::CssStringWriter) -> fmt::Result {
|
||||
match LonghandsToSerialize::from_iter(declarations.iter().cloned()) {
|
||||
Ok(longhands) => longhands.to_css(&mut CssWriter::new(dest)),
|
||||
Err(_) => Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
${caller.body()}
|
||||
}
|
||||
% endif
|
||||
|
|
|
@ -503,7 +503,6 @@ ${helpers.predefined_type(
|
|||
"BreakWithin",
|
||||
"computed::BreakWithin::Auto",
|
||||
engines="gecko",
|
||||
aliases="page-break-inside",
|
||||
spec="https://drafts.csswg.org/css-break/#propdef-break-inside",
|
||||
animation_value_type="discrete",
|
||||
)}
|
||||
|
|
|
@ -13,7 +13,6 @@ ${helpers.predefined_type(
|
|||
engines="gecko servo-2013 servo-2020",
|
||||
servo_2020_pref="layout.2020.unimplemented",
|
||||
initial_specified_value="specified::length::NonNegativeLengthOrAuto::auto()",
|
||||
extra_prefixes="moz:layout.css.prefixes.columns",
|
||||
animation_value_type="NonNegativeLengthOrAuto",
|
||||
servo_2013_pref="layout.columns.enabled",
|
||||
spec="https://drafts.csswg.org/css-multicol/#propdef-column-width",
|
||||
|
@ -29,7 +28,6 @@ ${helpers.predefined_type(
|
|||
initial_specified_value="specified::ColumnCount::auto()",
|
||||
servo_2013_pref="layout.columns.enabled",
|
||||
animation_value_type="AnimatedColumnCount",
|
||||
extra_prefixes="moz:layout.css.prefixes.columns",
|
||||
spec="https://drafts.csswg.org/css-multicol/#propdef-column-count",
|
||||
servo_restyle_damage="rebuild_and_reflow",
|
||||
)}
|
||||
|
@ -38,7 +36,6 @@ ${helpers.single_keyword(
|
|||
"column-fill",
|
||||
"balance auto",
|
||||
engines="gecko",
|
||||
extra_prefixes="moz:layout.css.prefixes.columns",
|
||||
animation_value_type="discrete",
|
||||
gecko_enum_prefix="StyleColumnFill",
|
||||
spec="https://drafts.csswg.org/css-multicol/#propdef-column-fill",
|
||||
|
@ -53,7 +50,6 @@ ${helpers.predefined_type(
|
|||
computed_type="crate::values::computed::NonNegativeLength",
|
||||
spec="https://drafts.csswg.org/css-multicol/#propdef-column-rule-width",
|
||||
animation_value_type="NonNegativeLength",
|
||||
extra_prefixes="moz:layout.css.prefixes.columns",
|
||||
)}
|
||||
|
||||
// https://drafts.csswg.org/css-multicol-1/#crc
|
||||
|
@ -64,7 +60,6 @@ ${helpers.predefined_type(
|
|||
engines="gecko",
|
||||
initial_specified_value="specified::Color::currentcolor()",
|
||||
animation_value_type="AnimatedColor",
|
||||
extra_prefixes="moz:layout.css.prefixes.columns",
|
||||
ignored_when_colors_disabled=True,
|
||||
spec="https://drafts.csswg.org/css-multicol/#propdef-column-rule-color",
|
||||
)}
|
||||
|
@ -76,7 +71,6 @@ ${helpers.single_keyword(
|
|||
animation_value_type="discrete",
|
||||
gecko_enum_prefix="StyleColumnSpan",
|
||||
spec="https://drafts.csswg.org/css-multicol/#propdef-column-span",
|
||||
extra_prefixes="moz:layout.css.prefixes.columns",
|
||||
)}
|
||||
|
||||
${helpers.predefined_type(
|
||||
|
@ -85,7 +79,6 @@ ${helpers.predefined_type(
|
|||
"computed::BorderStyle::None",
|
||||
engines="gecko",
|
||||
initial_specified_value="specified::BorderStyle::None",
|
||||
extra_prefixes="moz:layout.css.prefixes.columns",
|
||||
animation_value_type="discrete",
|
||||
spec="https://drafts.csswg.org/css-multicol/#propdef-column-rule-style",
|
||||
)}
|
||||
|
|
|
@ -213,6 +213,7 @@ ${helpers.predefined_type(
|
|||
initial_value="computed::XLang::get_initial_value()",
|
||||
animation_value_type="none",
|
||||
enabled_in="",
|
||||
has_effect_on_gecko_scrollbars=False,
|
||||
spec="Internal (not web-exposed)",
|
||||
)}
|
||||
|
||||
|
@ -224,6 +225,7 @@ ${helpers.predefined_type(
|
|||
animation_value_type="none",
|
||||
gecko_ffi_name="mScriptSizeMultiplier",
|
||||
enabled_in="",
|
||||
has_effect_on_gecko_scrollbars=False,
|
||||
spec="Internal (not web-exposed)",
|
||||
)}
|
||||
|
||||
|
@ -263,6 +265,7 @@ ${helpers.single_keyword(
|
|||
spec="Internal (not web-exposed)",
|
||||
animation_value_type="none",
|
||||
enabled_in="",
|
||||
has_effect_on_gecko_scrollbars=False,
|
||||
needs_conversion=True,
|
||||
)}
|
||||
|
||||
|
@ -273,6 +276,7 @@ ${helpers.predefined_type(
|
|||
engines="gecko",
|
||||
animation_value_type="none",
|
||||
enabled_in="",
|
||||
has_effect_on_gecko_scrollbars=False,
|
||||
gecko_ffi_name="mScriptMinSize",
|
||||
spec="Internal (not web-exposed)",
|
||||
)}
|
||||
|
@ -284,6 +288,7 @@ ${helpers.predefined_type(
|
|||
engines="gecko",
|
||||
animation_value_type="none",
|
||||
enabled_in="",
|
||||
has_effect_on_gecko_scrollbars=False,
|
||||
spec="Internal (not web-exposed)",
|
||||
)}
|
||||
|
||||
|
@ -309,7 +314,6 @@ pub mod system_font {
|
|||
//! variable reference. We may want to improve this behavior at some
|
||||
//! point. See also https://github.com/w3c/csswg-drafts/issues/1586.
|
||||
|
||||
use crate::values::computed::font::GenericFontFamily;
|
||||
use crate::properties::longhands;
|
||||
use std::hash::{Hash, Hasher};
|
||||
use crate::values::computed::{ToComputedValue, Context};
|
||||
|
@ -351,7 +355,7 @@ pub mod system_font {
|
|||
use std::mem;
|
||||
use crate::values::computed::Percentage;
|
||||
use crate::values::specified::font::KeywordInfo;
|
||||
use crate::values::computed::font::{FontFamily, FontSize, FontStretch, FontStyle, FontFamilyList};
|
||||
use crate::values::computed::font::{FontSize, FontStretch, FontStyle};
|
||||
use crate::values::generics::NonNegative;
|
||||
|
||||
let mut system = mem::MaybeUninit::<nsFont>::uninit();
|
||||
|
@ -370,12 +374,7 @@ pub mod system_font {
|
|||
})));
|
||||
let font_style = FontStyle::from_gecko(system.style);
|
||||
let ret = ComputedSystemFont {
|
||||
font_family: FontFamily {
|
||||
families: FontFamilyList::SharedFontList(
|
||||
unsafe { system.fontlist.mFontlist.mBasePtr.to_safe() }
|
||||
),
|
||||
is_system_font: true,
|
||||
},
|
||||
font_family: system.family.clone(),
|
||||
font_size: FontSize {
|
||||
size: NonNegative(cx.maybe_zoom_text(system.size.0)),
|
||||
keyword_info: KeywordInfo::none()
|
||||
|
@ -383,8 +382,7 @@ pub mod system_font {
|
|||
font_weight,
|
||||
font_stretch,
|
||||
font_style,
|
||||
font_size_adjust: longhands::font_size_adjust::computed_value
|
||||
::T::from_gecko_adjust(system.sizeAdjust),
|
||||
font_size_adjust: system.sizeAdjust,
|
||||
% for kwprop in kw_font_props:
|
||||
${kwprop}: longhands::${kwprop}::computed_value::T::from_gecko_keyword(
|
||||
system.${to_camel_case_lower(kwprop.replace('font_', ''))}
|
||||
|
@ -399,7 +397,6 @@ pub mod system_font {
|
|||
font_variation_settings: longhands::font_variation_settings::get_initial_value(),
|
||||
font_variant_alternates: longhands::font_variant_alternates::get_initial_value(),
|
||||
system_font: *self,
|
||||
default_font_type: system.fontlist.mDefaultFontType,
|
||||
};
|
||||
unsafe { bindings::Gecko_nsFont_Destroy(system); }
|
||||
ret
|
||||
|
@ -431,7 +428,6 @@ pub mod system_font {
|
|||
pub ${name}: longhands::${name}::computed_value::T,
|
||||
% endfor
|
||||
pub system_font: SystemFont,
|
||||
pub default_font_type: GenericFontFamily,
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -209,6 +209,9 @@ ${helpers.predefined_type(
|
|||
"MozContextProperties",
|
||||
"computed::MozContextProperties::default()",
|
||||
engines="gecko",
|
||||
enabled_in="chrome",
|
||||
gecko_pref="svg.context-properties.content.enabled",
|
||||
has_effect_on_gecko_scrollbars=False,
|
||||
animation_value_type="none",
|
||||
spec="Nonstandard (https://developer.mozilla.org/en-US/docs/Web/CSS/-moz-context-properties)",
|
||||
)}
|
||||
|
|
|
@ -238,12 +238,13 @@ ${helpers.predefined_type(
|
|||
)}
|
||||
|
||||
${helpers.predefined_type(
|
||||
"-moz-tab-size",
|
||||
"tab-size",
|
||||
"NonNegativeLengthOrNumber",
|
||||
"generics::length::LengthOrNumber::Number(From::from(8.0))",
|
||||
engines="gecko",
|
||||
animation_value_type="LengthOrNumber",
|
||||
spec="https://drafts.csswg.org/css-text-3/#tab-size-property",
|
||||
aliases="-moz-tab-size",
|
||||
)}
|
||||
|
||||
${helpers.predefined_type(
|
||||
|
@ -336,6 +337,9 @@ ${helpers.predefined_type(
|
|||
"text::MozControlCharacterVisibility",
|
||||
"Default::default()",
|
||||
engines="gecko",
|
||||
enabled_in="chrome",
|
||||
gecko_pref="layout.css.moz-control-character-visibility.enabled",
|
||||
has_effect_on_gecko_scrollbars=False,
|
||||
animation_value_type="none",
|
||||
spec="Nonstandard"
|
||||
)}
|
||||
|
|
|
@ -95,6 +95,17 @@ ${helpers.predefined_type(
|
|||
has_effect_on_gecko_scrollbars=False,
|
||||
)}
|
||||
|
||||
${helpers.predefined_type(
|
||||
"color-scheme",
|
||||
"ColorScheme",
|
||||
"specified::color::ColorScheme::normal()",
|
||||
engines="gecko",
|
||||
spec="https://drafts.csswg.org/css-color-adjust/#color-scheme-prop",
|
||||
gecko_pref="layout.css.color-scheme.enabled",
|
||||
animation_value_type="discrete",
|
||||
has_effect_on_gecko_scrollbars=False,
|
||||
)}
|
||||
|
||||
${helpers.predefined_type(
|
||||
"scrollbar-color",
|
||||
"ui::ScrollbarColor",
|
||||
|
|
|
@ -42,20 +42,6 @@ ${helpers.predefined_type(
|
|||
spec="https://drafts.csswg.org/css-ui/#propdef-outline-width",
|
||||
)}
|
||||
|
||||
// The -moz-outline-radius-* properties are non-standard and not on a standards track.
|
||||
% for corner in ["topleft", "topright", "bottomright", "bottomleft"]:
|
||||
${helpers.predefined_type(
|
||||
"-moz-outline-radius-" + corner,
|
||||
"BorderCornerRadius",
|
||||
"computed::BorderCornerRadius::zero()",
|
||||
engines="gecko",
|
||||
boxed=True,
|
||||
animation_value_type="BorderCornerRadius",
|
||||
gecko_pref="layout.css.moz-outline-radius.enabled",
|
||||
spec="Nonstandard (https://developer.mozilla.org/en-US/docs/Web/CSS/-moz-outline-radius)",
|
||||
)}
|
||||
% endfor
|
||||
|
||||
${helpers.predefined_type(
|
||||
"outline-offset",
|
||||
"Length",
|
||||
|
|
|
@ -434,7 +434,6 @@ ${helpers.predefined_type(
|
|||
"computed::length::NonNegativeLengthPercentageOrNormal::normal()",
|
||||
engines="gecko servo-2013",
|
||||
aliases="grid-column-gap" if engine == "gecko" else "",
|
||||
extra_prefixes="moz:layout.css.prefixes.columns",
|
||||
servo_2013_pref="layout.columns.enabled",
|
||||
spec="https://drafts.csswg.org/css-align-3/#propdef-column-gap",
|
||||
animation_value_type="NonNegativeLengthPercentageOrNormal",
|
||||
|
|
|
@ -246,3 +246,13 @@ ${helpers.predefined_type(
|
|||
animation_value_type="LengthPercentage",
|
||||
spec="https://svgwg.org/svg2-draft/geometry.html#R",
|
||||
)}
|
||||
|
||||
${helpers.predefined_type(
|
||||
"d",
|
||||
"DProperty",
|
||||
"specified::DProperty::none()",
|
||||
engines="gecko",
|
||||
animation_value_type="ComputedValue",
|
||||
gecko_pref="layout.css.d-property.enabled",
|
||||
spec="https://svgwg.org/svg2-draft/paths.html#TheDProperty",
|
||||
)}
|
||||
|
|
|
@ -232,7 +232,7 @@ pub mod shorthands {
|
|||
// which don't exist in `LonghandId`.
|
||||
|
||||
<%
|
||||
extra = [
|
||||
extra_variants = [
|
||||
{
|
||||
"name": "CSSWideKeyword",
|
||||
"type": "WideKeywordDeclaration",
|
||||
|
@ -252,7 +252,7 @@ pub mod shorthands {
|
|||
"copy": False,
|
||||
},
|
||||
]
|
||||
for v in extra:
|
||||
for v in extra_variants:
|
||||
variants.append(v)
|
||||
groups[v["type"]] = [v]
|
||||
%>
|
||||
|
@ -394,6 +394,17 @@ impl MallocSizeOf for PropertyDeclaration {
|
|||
|
||||
|
||||
impl PropertyDeclaration {
|
||||
/// Returns whether this is a variant of the Longhand(Value) type, rather
|
||||
/// than one of the special variants in extra_variants.
|
||||
fn is_longhand_value(&self) -> bool {
|
||||
match *self {
|
||||
% for v in extra_variants:
|
||||
PropertyDeclaration::${v["name"]}(..) => false,
|
||||
% endfor
|
||||
_ => true,
|
||||
}
|
||||
}
|
||||
|
||||
/// Like the method on ToCss, but without the type parameter to avoid
|
||||
/// accidentally monomorphizing this large function multiple times for
|
||||
/// different writers.
|
||||
|
@ -409,6 +420,24 @@ impl PropertyDeclaration {
|
|||
% endfor
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the color value of a given property, for high-contrast-mode
|
||||
/// tweaks.
|
||||
pub(crate) fn color_value(&self) -> Option<<&crate::values::specified::Color> {
|
||||
${static_longhand_id_set("COLOR_PROPERTIES", lambda p: p.predefined_type == "Color")}
|
||||
<%
|
||||
# sanity check
|
||||
assert data.longhands_by_name["background-color"].predefined_type == "Color"
|
||||
|
||||
color_specified_type = data.longhands_by_name["background-color"].specified_type()
|
||||
%>
|
||||
let id = self.id().as_longhand()?;
|
||||
if !COLOR_PROPERTIES.contains(id) || !self.is_longhand_value() {
|
||||
return None;
|
||||
}
|
||||
let repr = self as *const _ as *const PropertyDeclarationVariantRepr<${color_specified_type}>;
|
||||
Some(unsafe { &(*repr).value })
|
||||
}
|
||||
}
|
||||
|
||||
/// A module with all the code related to animated properties.
|
||||
|
@ -1366,6 +1395,9 @@ impl LonghandId {
|
|||
|
||||
// font-size depends on math-depth's computed value.
|
||||
LonghandId::MathDepth |
|
||||
|
||||
// color-scheme affects how system colors resolve.
|
||||
LonghandId::ColorScheme |
|
||||
% endif
|
||||
|
||||
// Needed to compute the first available font, in order to
|
||||
|
@ -1471,74 +1503,66 @@ impl ShorthandId {
|
|||
///
|
||||
/// Returns an error if writing to the stream fails, or if the declarations
|
||||
/// do not map to a shorthand.
|
||||
pub fn longhands_to_css<'a, W, I>(
|
||||
pub fn longhands_to_css(
|
||||
&self,
|
||||
declarations: I,
|
||||
dest: &mut CssWriter<W>,
|
||||
) -> fmt::Result
|
||||
where
|
||||
W: Write,
|
||||
I: Iterator<Item=&'a PropertyDeclaration>,
|
||||
{
|
||||
match *self {
|
||||
ShorthandId::All => {
|
||||
// No need to try to serialize the declarations as the 'all'
|
||||
// shorthand, since it only accepts CSS-wide keywords (and
|
||||
// variable references), which will be handled in
|
||||
// get_shorthand_appendable_value.
|
||||
Err(fmt::Error)
|
||||
}
|
||||
% for property in data.shorthands_except_all():
|
||||
ShorthandId::${property.camel_case} => {
|
||||
match shorthands::${property.ident}::LonghandsToSerialize::from_iter(declarations) {
|
||||
Ok(longhands) => longhands.to_css(dest),
|
||||
Err(_) => Err(fmt::Error)
|
||||
}
|
||||
},
|
||||
% endfor
|
||||
declarations: &[&PropertyDeclaration],
|
||||
dest: &mut CssStringWriter,
|
||||
) -> fmt::Result {
|
||||
type LonghandsToCssFn = for<'a, 'b> fn(&'a [&'b PropertyDeclaration], &mut CssStringWriter) -> fmt::Result;
|
||||
fn all_to_css(_: &[&PropertyDeclaration], _: &mut CssStringWriter) -> fmt::Result {
|
||||
// No need to try to serialize the declarations as the 'all'
|
||||
// shorthand, since it only accepts CSS-wide keywords (and variable
|
||||
// references), which will be handled in
|
||||
// get_shorthand_appendable_value.
|
||||
Ok(())
|
||||
}
|
||||
|
||||
static LONGHANDS_TO_CSS: [LonghandsToCssFn; ${len(data.shorthands)}] = [
|
||||
% for shorthand in data.shorthands:
|
||||
% if shorthand.ident == "all":
|
||||
all_to_css,
|
||||
% else:
|
||||
shorthands::${shorthand.ident}::to_css,
|
||||
% endif
|
||||
% endfor
|
||||
];
|
||||
|
||||
LONGHANDS_TO_CSS[*self as usize](declarations, dest)
|
||||
}
|
||||
|
||||
/// Finds and returns an appendable value for the given declarations.
|
||||
///
|
||||
/// Returns the optional appendable value.
|
||||
pub fn get_shorthand_appendable_value<'a, I>(
|
||||
pub fn get_shorthand_appendable_value<'a, 'b: 'a>(
|
||||
self,
|
||||
declarations: I,
|
||||
) -> Option<AppendableValue<'a, I::IntoIter>>
|
||||
where
|
||||
I: IntoIterator<Item=&'a PropertyDeclaration>,
|
||||
I::IntoIter: Clone,
|
||||
{
|
||||
let declarations = declarations.into_iter();
|
||||
|
||||
// Only cloning iterators (a few pointers each) not declarations.
|
||||
let mut declarations2 = declarations.clone();
|
||||
let mut declarations3 = declarations.clone();
|
||||
|
||||
let first_declaration = declarations2.next()?;
|
||||
declarations: &'a [&'b PropertyDeclaration],
|
||||
) -> Option<AppendableValue<'a, 'b>> {
|
||||
let first_declaration = declarations.get(0)?;
|
||||
let rest = || declarations.iter().skip(1);
|
||||
|
||||
// https://drafts.csswg.org/css-variables/#variables-in-shorthands
|
||||
if let Some(css) = first_declaration.with_variables_from_shorthand(self) {
|
||||
if declarations2.all(|d| d.with_variables_from_shorthand(self) == Some(css)) {
|
||||
return Some(AppendableValue::Css { css, with_variables: true });
|
||||
if rest().all(|d| d.with_variables_from_shorthand(self) == Some(css)) {
|
||||
return Some(AppendableValue::Css(css));
|
||||
}
|
||||
return None;
|
||||
}
|
||||
|
||||
// Check whether they are all the same CSS-wide keyword.
|
||||
if let Some(keyword) = first_declaration.get_css_wide_keyword() {
|
||||
if declarations2.all(|d| d.get_css_wide_keyword() == Some(keyword)) {
|
||||
return Some(AppendableValue::Css {
|
||||
css: keyword.to_str(),
|
||||
with_variables: false,
|
||||
});
|
||||
if rest().all(|d| d.get_css_wide_keyword() == Some(keyword)) {
|
||||
return Some(AppendableValue::Css(keyword.to_str()))
|
||||
}
|
||||
return None;
|
||||
}
|
||||
|
||||
if self == ShorthandId::All {
|
||||
// 'all' only supports variables and CSS wide keywords.
|
||||
return None;
|
||||
}
|
||||
|
||||
// Check whether all declarations can be serialized as part of shorthand.
|
||||
if declarations3.all(|d| d.may_serialize_as_part_of_shorthand()) {
|
||||
if declarations.iter().all(|d| d.may_serialize_as_part_of_shorthand()) {
|
||||
return Some(AppendableValue::DeclarationsForShorthand(self, declarations));
|
||||
}
|
||||
|
||||
|
@ -1596,23 +1620,20 @@ impl ShorthandId {
|
|||
input: &mut Parser<'i, 't>,
|
||||
) -> Result<(), ParseError<'i>>;
|
||||
|
||||
fn unreachable<'i, 't>(
|
||||
fn parse_all<'i, 't>(
|
||||
_: &mut SourcePropertyDeclaration,
|
||||
_: &ParserContext,
|
||||
_: &mut Parser<'i, 't>
|
||||
input: &mut Parser<'i, 't>
|
||||
) -> Result<(), ParseError<'i>> {
|
||||
unreachable!()
|
||||
// 'all' accepts no value other than CSS-wide keywords
|
||||
Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
|
||||
}
|
||||
|
||||
// 'all' accepts no value other than CSS-wide keywords
|
||||
if *self == ShorthandId::All {
|
||||
return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
|
||||
}
|
||||
|
||||
static PARSE_INTO: [ParseIntoFn; ${len(data.shorthands)}] = [
|
||||
% for shorthand in data.shorthands:
|
||||
% if shorthand.ident == "all":
|
||||
unreachable,
|
||||
parse_all,
|
||||
% else:
|
||||
shorthands::${shorthand.ident}::parse_into,
|
||||
% endif
|
||||
|
@ -1720,7 +1741,8 @@ impl UnparsedValue {
|
|||
|
||||
let mut input = ParserInput::new(&css);
|
||||
let mut input = Parser::new(&mut input);
|
||||
input.skip_whitespace(); // Unnecessary for correctness, but may help try() rewind less.
|
||||
input.skip_whitespace();
|
||||
|
||||
if let Ok(keyword) = input.try_parse(CSSWideKeyword::parse) {
|
||||
return Cow::Owned(PropertyDeclaration::css_wide_keyword(longhand_id, keyword));
|
||||
}
|
||||
|
@ -2436,12 +2458,11 @@ impl PropertyDeclaration {
|
|||
debug_assert!(id.allowed_in(context), "{:?}", id);
|
||||
|
||||
let non_custom_id = id.non_custom_id();
|
||||
input.skip_whitespace();
|
||||
|
||||
let start = input.state();
|
||||
match id {
|
||||
PropertyId::Custom(property_name) => {
|
||||
// FIXME: fully implement https://github.com/w3c/csswg-drafts/issues/774
|
||||
// before adding skip_whitespace here.
|
||||
// This probably affects some test results.
|
||||
let value = match input.try_parse(CSSWideKeyword::parse) {
|
||||
Ok(keyword) => CustomDeclarationValue::CSSWideKeyword(keyword),
|
||||
Err(()) => CustomDeclarationValue::Value(
|
||||
|
@ -2456,7 +2477,6 @@ impl PropertyDeclaration {
|
|||
}
|
||||
PropertyId::LonghandAlias(id, _) |
|
||||
PropertyId::Longhand(id) => {
|
||||
input.skip_whitespace(); // Unnecessary for correctness, but may help try() rewind less.
|
||||
input.try_parse(CSSWideKeyword::parse).map(|keyword| {
|
||||
PropertyDeclaration::css_wide_keyword(id, keyword)
|
||||
}).or_else(|()| {
|
||||
|
@ -2486,7 +2506,6 @@ impl PropertyDeclaration {
|
|||
}
|
||||
PropertyId::ShorthandAlias(id, _) |
|
||||
PropertyId::Shorthand(id) => {
|
||||
input.skip_whitespace(); // Unnecessary for correctness, but may help try() rewind less.
|
||||
if let Ok(keyword) = input.try_parse(CSSWideKeyword::parse) {
|
||||
if id == ShorthandId::All {
|
||||
declarations.all_shorthand = AllShorthand::CSSWideKeyword(keyword)
|
||||
|
|
|
@ -316,15 +316,15 @@ ${helpers.two_properties_shorthand(
|
|||
name="page-break-before"
|
||||
flags="SHORTHAND_IN_GETCS IS_LEGACY_SHORTHAND"
|
||||
sub_properties="break-before"
|
||||
spec="https://drafts.csswg.org/css2/page.html#propdef-page-break-before"
|
||||
spec="https://drafts.csswg.org/css-break-3/#page-break-properties"
|
||||
>
|
||||
pub fn parse_value<'i>(
|
||||
_: &ParserContext,
|
||||
context: &ParserContext,
|
||||
input: &mut Parser<'i, '_>,
|
||||
) -> Result<Longhands, ParseError<'i>> {
|
||||
use crate::values::specified::box_::BreakBetween;
|
||||
Ok(expanded! {
|
||||
break_before: BreakBetween::parse_legacy(input)?,
|
||||
break_before: BreakBetween::parse_legacy(context, input)?,
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -340,15 +340,15 @@ ${helpers.two_properties_shorthand(
|
|||
name="page-break-after"
|
||||
flags="SHORTHAND_IN_GETCS IS_LEGACY_SHORTHAND"
|
||||
sub_properties="break-after"
|
||||
spec="https://drafts.csswg.org/css2/page.html#propdef-page-break-after"
|
||||
spec="https://drafts.csswg.org/css-break-3/#page-break-properties"
|
||||
>
|
||||
pub fn parse_value<'i>(
|
||||
_: &ParserContext,
|
||||
context: &ParserContext,
|
||||
input: &mut Parser<'i, '_>,
|
||||
) -> Result<Longhands, ParseError<'i>> {
|
||||
use crate::values::specified::box_::BreakBetween;
|
||||
Ok(expanded! {
|
||||
break_after: BreakBetween::parse_legacy(input)?,
|
||||
break_after: BreakBetween::parse_legacy(context, input)?,
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -359,6 +359,30 @@ ${helpers.two_properties_shorthand(
|
|||
}
|
||||
</%helpers:shorthand>
|
||||
|
||||
<%helpers:shorthand
|
||||
engines="gecko"
|
||||
name="page-break-inside"
|
||||
flags="SHORTHAND_IN_GETCS IS_LEGACY_SHORTHAND"
|
||||
sub_properties="break-inside"
|
||||
spec="https://drafts.csswg.org/css-break-3/#page-break-properties"
|
||||
>
|
||||
pub fn parse_value<'i>(
|
||||
context: &ParserContext,
|
||||
input: &mut Parser<'i, '_>,
|
||||
) -> Result<Longhands, ParseError<'i>> {
|
||||
use crate::values::specified::box_::BreakWithin;
|
||||
Ok(expanded! {
|
||||
break_inside: BreakWithin::parse_legacy(context, input)?,
|
||||
})
|
||||
}
|
||||
|
||||
impl<'a> ToCss for LonghandsToSerialize<'a> {
|
||||
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result where W: fmt::Write {
|
||||
self.break_inside.to_css_legacy(dest)
|
||||
}
|
||||
}
|
||||
</%helpers:shorthand>
|
||||
|
||||
<%helpers:shorthand name="offset"
|
||||
engines="gecko"
|
||||
sub_properties="offset-path offset-distance offset-rotate offset-anchor"
|
||||
|
|
|
@ -9,7 +9,6 @@
|
|||
sub_properties="column-width column-count"
|
||||
servo_2013_pref="layout.columns.enabled",
|
||||
derive_serialize="True"
|
||||
extra_prefixes="moz:layout.css.prefixes.columns"
|
||||
spec="https://drafts.csswg.org/css-multicol/#propdef-columns">
|
||||
use crate::properties::longhands::{column_count, column_width};
|
||||
|
||||
|
@ -60,7 +59,6 @@
|
|||
<%helpers:shorthand
|
||||
name="column-rule"
|
||||
engines="gecko"
|
||||
extra_prefixes="moz:layout.css.prefixes.columns"
|
||||
sub_properties="column-rule-width column-rule-style column-rule-color"
|
||||
derive_serialize="True"
|
||||
spec="https://drafts.csswg.org/css-multicol/#propdef-column-rule"
|
||||
|
|
|
@ -137,7 +137,7 @@
|
|||
return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
|
||||
}
|
||||
|
||||
let family = FontFamily::parse_specified(input)?;
|
||||
let family = FontFamily::parse(context, input)?;
|
||||
Ok(expanded! {
|
||||
% for name in "style weight stretch variant_caps".split():
|
||||
font_${name}: unwrap_or_initial!(font_${name}, ${name}),
|
||||
|
|
|
@ -78,50 +78,3 @@
|
|||
}
|
||||
}
|
||||
</%helpers:shorthand>
|
||||
|
||||
// The -moz-outline-radius shorthand is non-standard and not on a standards track.
|
||||
<%helpers:shorthand
|
||||
name="-moz-outline-radius"
|
||||
engines="gecko"
|
||||
gecko_pref="layout.css.moz-outline-radius.enabled"
|
||||
sub_properties="${' '.join(
|
||||
'-moz-outline-radius-%s' % corner
|
||||
for corner in ['topleft', 'topright', 'bottomright', 'bottomleft']
|
||||
)}"
|
||||
spec="Nonstandard (https://developer.mozilla.org/en-US/docs/Web/CSS/-moz-outline-radius)"
|
||||
>
|
||||
use crate::values::generics::rect::Rect;
|
||||
use crate::values::specified::border::BorderRadius;
|
||||
use crate::parser::Parse;
|
||||
|
||||
pub fn parse_value<'i, 't>(
|
||||
context: &ParserContext,
|
||||
input: &mut Parser<'i, 't>,
|
||||
) -> Result<Longhands, ParseError<'i>> {
|
||||
let radii = BorderRadius::parse(context, input)?;
|
||||
Ok(expanded! {
|
||||
_moz_outline_radius_topleft: radii.top_left,
|
||||
_moz_outline_radius_topright: radii.top_right,
|
||||
_moz_outline_radius_bottomright: radii.bottom_right,
|
||||
_moz_outline_radius_bottomleft: radii.bottom_left,
|
||||
})
|
||||
}
|
||||
|
||||
impl<'a> ToCss for LonghandsToSerialize<'a> {
|
||||
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result where W: fmt::Write {
|
||||
use crate::values::generics::border::BorderCornerRadius;
|
||||
|
||||
let LonghandsToSerialize {
|
||||
_moz_outline_radius_topleft: &BorderCornerRadius(ref tl),
|
||||
_moz_outline_radius_topright: &BorderCornerRadius(ref tr),
|
||||
_moz_outline_radius_bottomright: &BorderCornerRadius(ref br),
|
||||
_moz_outline_radius_bottomleft: &BorderCornerRadius(ref bl),
|
||||
} = *self;
|
||||
|
||||
let widths = Rect::new(tl.width(), tr.width(), br.width(), bl.width());
|
||||
let heights = Rect::new(tl.height(), tr.height(), br.height(), bl.height());
|
||||
|
||||
BorderRadius::serialize_rects(widths, heights, dest)
|
||||
}
|
||||
}
|
||||
</%helpers:shorthand>
|
||||
|
|
|
@ -232,9 +232,6 @@ impl<'a, 'b: 'a> StyleAdjuster<'a, 'b> {
|
|||
where
|
||||
E: TElement,
|
||||
{
|
||||
#[cfg(any(feature = "servo-layout-2013", feature = "gecko"))]
|
||||
use crate::computed_values::list_style_position::T as ListStylePosition;
|
||||
|
||||
let mut blockify = false;
|
||||
macro_rules! blockify_if {
|
||||
($if_what:expr) => {
|
||||
|
@ -254,16 +251,6 @@ impl<'a, 'b: 'a> StyleAdjuster<'a, 'b> {
|
|||
|
||||
blockify_if!(self.style.is_floating());
|
||||
blockify_if!(self.style.is_absolutely_positioned());
|
||||
#[cfg(any(feature = "servo-layout-2013", feature = "gecko"))]
|
||||
blockify_if!(
|
||||
self.style.pseudo.map_or(false, |p| p.is_marker()) &&
|
||||
self.style.get_parent_list().clone_list_style_position() ==
|
||||
ListStylePosition::Outside &&
|
||||
!layout_parent_style
|
||||
.get_box()
|
||||
.clone_display()
|
||||
.is_inline_flow()
|
||||
);
|
||||
|
||||
if !blockify {
|
||||
return;
|
||||
|
@ -818,6 +805,46 @@ impl<'a, 'b: 'a> StyleAdjuster<'a, 'b> {
|
|||
}
|
||||
}
|
||||
|
||||
/// A legacy ::marker (i.e. no 'content') without an author-specified 'font-family'
|
||||
/// and 'list-style-type:disc|circle|square|disclosure-closed|disclosure-open'
|
||||
/// is assigned 'font-family:-moz-bullet-font'. (This is for <ul><li> etc.)
|
||||
/// We don't want synthesized italic/bold for this font, so turn that off too.
|
||||
/// Likewise for 'letter/word-spacing' -- unless the author specified it then reset
|
||||
/// them to their initial value because traditionally we never added such spacing
|
||||
/// between a legacy bullet and the list item's content, so we keep that behavior
|
||||
/// for web-compat reasons.
|
||||
/// We intentionally don't check 'list-style-image' below since we want it to use
|
||||
/// 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::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() &&
|
||||
self.style.get_counters().clone_content() == Content::Normal;
|
||||
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());
|
||||
|
||||
// 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_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());
|
||||
}
|
||||
}
|
||||
|
||||
/// Adjusts the style to account for various fixups that don't fit naturally
|
||||
/// into the cascade.
|
||||
///
|
||||
|
@ -883,6 +910,7 @@ impl<'a, 'b: 'a> StyleAdjuster<'a, 'b> {
|
|||
{
|
||||
self.adjust_for_appearance(element);
|
||||
self.adjust_for_inert();
|
||||
self.adjust_for_marker_pseudo();
|
||||
}
|
||||
self.set_bits();
|
||||
}
|
||||
|
|
|
@ -56,7 +56,13 @@ impl ImportSheet {
|
|||
/// exists.
|
||||
pub fn as_sheet(&self) -> Option<&crate::gecko::data::GeckoStyleSheet> {
|
||||
match *self {
|
||||
ImportSheet::Sheet(ref s) => Some(s),
|
||||
ImportSheet::Sheet(ref s) => {
|
||||
debug_assert!(!s.hack_is_null());
|
||||
if s.hack_is_null() {
|
||||
return None;
|
||||
}
|
||||
Some(s)
|
||||
},
|
||||
ImportSheet::Pending(_) => None,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -65,6 +65,10 @@ pub struct StylesheetContents {
|
|||
pub source_map_url: RwLock<Option<String>>,
|
||||
/// This stylesheet's source URL.
|
||||
pub source_url: RwLock<Option<String>>,
|
||||
|
||||
/// We don't want to allow construction outside of this file, to guarantee
|
||||
/// that all contents are created with Arc<>.
|
||||
_forbid_construction: (),
|
||||
}
|
||||
|
||||
impl StylesheetContents {
|
||||
|
@ -82,7 +86,7 @@ impl StylesheetContents {
|
|||
use_counters: Option<&UseCounters>,
|
||||
allow_import_rules: AllowImportRules,
|
||||
sanitization_data: Option<&mut SanitizationData>,
|
||||
) -> Self {
|
||||
) -> Arc<Self> {
|
||||
let namespaces = RwLock::new(Namespaces::default());
|
||||
let (rules, source_map_url, source_url) = Stylesheet::parse_rules(
|
||||
css,
|
||||
|
@ -99,7 +103,7 @@ impl StylesheetContents {
|
|||
sanitization_data,
|
||||
);
|
||||
|
||||
Self {
|
||||
Arc::new(Self {
|
||||
rules: CssRules::new(rules, &shared_lock),
|
||||
origin,
|
||||
url_data: RwLock::new(url_data),
|
||||
|
@ -107,7 +111,8 @@ impl StylesheetContents {
|
|||
quirks_mode,
|
||||
source_map_url: RwLock::new(source_map_url),
|
||||
source_url: RwLock::new(source_url),
|
||||
}
|
||||
_forbid_construction: (),
|
||||
})
|
||||
}
|
||||
|
||||
/// Creates a new StylesheetContents with the specified pre-parsed rules,
|
||||
|
@ -126,9 +131,9 @@ impl StylesheetContents {
|
|||
origin: Origin,
|
||||
url_data: UrlExtraData,
|
||||
quirks_mode: QuirksMode,
|
||||
) -> Self {
|
||||
) -> Arc<Self> {
|
||||
debug_assert!(rules.is_static());
|
||||
Self {
|
||||
Arc::new(Self {
|
||||
rules,
|
||||
origin,
|
||||
url_data: RwLock::new(url_data),
|
||||
|
@ -136,7 +141,8 @@ impl StylesheetContents {
|
|||
quirks_mode,
|
||||
source_map_url: RwLock::new(None),
|
||||
source_url: RwLock::new(None),
|
||||
}
|
||||
_forbid_construction: (),
|
||||
})
|
||||
}
|
||||
|
||||
/// Returns a reference to the list of rules.
|
||||
|
@ -178,6 +184,7 @@ impl DeepCloneWithLock for StylesheetContents {
|
|||
namespaces: RwLock::new((*self.namespaces.read()).clone()),
|
||||
source_map_url: RwLock::new((*self.source_map_url.read()).clone()),
|
||||
source_url: RwLock::new((*self.source_url.read()).clone()),
|
||||
_forbid_construction: (),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -186,7 +193,7 @@ impl DeepCloneWithLock for StylesheetContents {
|
|||
#[derive(Debug)]
|
||||
pub struct Stylesheet {
|
||||
/// The contents of this stylesheet.
|
||||
pub contents: StylesheetContents,
|
||||
pub contents: Arc<StylesheetContents>,
|
||||
/// The lock used for objects inside this stylesheet
|
||||
pub shared_lock: SharedRwLock,
|
||||
/// List of media associated with the Stylesheet.
|
||||
|
@ -587,9 +594,9 @@ 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 = self
|
||||
let contents = Arc::new(self
|
||||
.contents
|
||||
.deep_clone_with_lock(&lock, &guard, &DeepCloneParams);
|
||||
.deep_clone_with_lock(&lock, &guard, &DeepCloneParams));
|
||||
|
||||
Stylesheet {
|
||||
contents,
|
||||
|
|
|
@ -11,7 +11,7 @@ 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;
|
||||
use crate::invalidation::media_queries::{EffectiveMediaQueryResults, MediaListKey, ToMediaListKey};
|
||||
use crate::invalidation::stylesheets::RuleChangeKind;
|
||||
use crate::media_queries::Device;
|
||||
use crate::properties::{self, CascadeMode, ComputedValues};
|
||||
|
@ -26,8 +26,7 @@ use crate::stylesheet_set::{DataValidity, DocumentStylesheetSet, SheetRebuildKin
|
|||
use crate::stylesheet_set::{DocumentStylesheetFlusher, SheetCollectionFlusher};
|
||||
use crate::stylesheets::keyframes_rule::KeyframesAnimation;
|
||||
use crate::stylesheets::viewport_rule::{self, MaybeNew, ViewportRule};
|
||||
use crate::stylesheets::StyleRule;
|
||||
use crate::stylesheets::StylesheetInDocument;
|
||||
use crate::stylesheets::{StyleRule, StylesheetInDocument, StylesheetContents};
|
||||
#[cfg(feature = "gecko")]
|
||||
use crate::stylesheets::{CounterStyleRule, FontFaceRule, FontFeatureValuesRule, PageRule};
|
||||
use crate::stylesheets::{CssRule, Origin, OriginSet, PerOrigin, PerOriginIter};
|
||||
|
@ -50,7 +49,9 @@ use smallbitvec::SmallBitVec;
|
|||
use smallvec::SmallVec;
|
||||
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")]
|
||||
|
@ -60,6 +61,37 @@ pub type StylistSheet = crate::stylesheets::DocumentStyleSheet;
|
|||
#[cfg(feature = "gecko")]
|
||||
pub type StylistSheet = crate::gecko::data::GeckoStyleSheet;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
struct StylesheetContentsPtr(Arc<StylesheetContents>);
|
||||
|
||||
impl PartialEq for StylesheetContentsPtr {
|
||||
#[inline]
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
Arc::ptr_eq(&self.0, &other.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl Eq for StylesheetContentsPtr {}
|
||||
|
||||
impl Hash for StylesheetContentsPtr {
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
let contents: &StylesheetContents = &*self.0;
|
||||
(contents as *const StylesheetContents).hash(state)
|
||||
}
|
||||
}
|
||||
|
||||
type StyleSheetContentList = Vec<StylesheetContentsPtr>;
|
||||
|
||||
/// A key in the cascade data cache.
|
||||
#[derive(Debug, Hash, Default, PartialEq, Eq)]
|
||||
struct CascadeDataCacheKey {
|
||||
media_query_results: Vec<MediaListKey>,
|
||||
contents: StyleSheetContentList,
|
||||
}
|
||||
|
||||
unsafe impl Send for CascadeDataCacheKey {}
|
||||
unsafe impl Sync for CascadeDataCacheKey {}
|
||||
|
||||
trait CascadeDataCacheEntry : Sized {
|
||||
/// Returns a reference to the cascade data.
|
||||
fn cascade_data(&self) -> &CascadeData;
|
||||
|
@ -80,7 +112,7 @@ trait CascadeDataCacheEntry : Sized {
|
|||
}
|
||||
|
||||
struct CascadeDataCache<Entry> {
|
||||
entries: Vec<Arc<Entry>>,
|
||||
entries: FxHashMap<CascadeDataCacheKey, Arc<Entry>>,
|
||||
}
|
||||
|
||||
impl<Entry> CascadeDataCache<Entry>
|
||||
|
@ -88,7 +120,7 @@ where
|
|||
Entry: CascadeDataCacheEntry,
|
||||
{
|
||||
fn new() -> Self {
|
||||
Self { entries: vec![] }
|
||||
Self { entries: Default::default() }
|
||||
}
|
||||
|
||||
fn len(&self) -> usize {
|
||||
|
@ -110,51 +142,66 @@ where
|
|||
where
|
||||
S: StylesheetInDocument + PartialEq + 'static,
|
||||
{
|
||||
use std::collections::hash_map::Entry as HashMapEntry;
|
||||
debug!("StyleSheetCache::lookup({})", self.len());
|
||||
|
||||
if !collection.dirty() {
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
let mut key = EffectiveMediaQueryResults::new();
|
||||
let mut key = CascadeDataCacheKey::default();
|
||||
for sheet in collection.sheets() {
|
||||
CascadeData::collect_applicable_media_query_results_into(device, sheet, guard, &mut key)
|
||||
CascadeData::collect_applicable_media_query_results_into(
|
||||
device,
|
||||
sheet,
|
||||
guard,
|
||||
&mut key.media_query_results,
|
||||
&mut key.contents,
|
||||
)
|
||||
}
|
||||
|
||||
for entry in &self.entries {
|
||||
if std::ptr::eq(&**entry, old_entry) {
|
||||
let new_entry;
|
||||
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,
|
||||
)?;
|
||||
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
|
||||
// contents were already unique, for example). This old entry
|
||||
// will be pruned from the cache with take_unused() afterwards.
|
||||
continue;
|
||||
}
|
||||
if entry.cascade_data().effective_media_query_results != key {
|
||||
continue;
|
||||
}
|
||||
if log_enabled!(log::Level::Debug) {
|
||||
debug!("cache hit for:");
|
||||
for sheet in collection.sheets() {
|
||||
debug!(" > {:?}", sheet);
|
||||
// contents were already unique, for example).
|
||||
if !std::ptr::eq(&**e.get(), old_entry) {
|
||||
if log_enabled!(log::Level::Debug) {
|
||||
debug!("cache hit for:");
|
||||
for sheet in collection.sheets() {
|
||||
debug!(" > {:?}", sheet);
|
||||
}
|
||||
}
|
||||
// The line below ensures the "committed" bit is updated
|
||||
// properly.
|
||||
collection.each(|_, _| true);
|
||||
return Ok(Some(e.get().clone()));
|
||||
}
|
||||
|
||||
debug!("> Picking the slow path due to same entry as old");
|
||||
new_entry = Entry::rebuild(
|
||||
device,
|
||||
quirks_mode,
|
||||
collection,
|
||||
guard,
|
||||
old_entry,
|
||||
)?;
|
||||
e.insert(new_entry.clone());
|
||||
}
|
||||
// The line below ensures the "committed" bit is updated properly
|
||||
// below.
|
||||
collection.each(|_, _| true);
|
||||
return Ok(Some(entry.clone()));
|
||||
}
|
||||
|
||||
debug!("> Picking the slow path");
|
||||
|
||||
let new_entry = Entry::rebuild(
|
||||
device,
|
||||
quirks_mode,
|
||||
collection,
|
||||
guard,
|
||||
old_entry,
|
||||
)?;
|
||||
|
||||
self.entries.push(new_entry.clone());
|
||||
Ok(Some(new_entry))
|
||||
}
|
||||
|
||||
|
@ -167,25 +214,27 @@ where
|
|||
/// cache to not deadlock.
|
||||
fn take_unused(&mut self) -> SmallVec<[Arc<Entry>; 3]> {
|
||||
let mut unused = SmallVec::new();
|
||||
for i in (0..self.entries.len()).rev() {
|
||||
self.entries.retain(|_key, value| {
|
||||
// is_unique() returns false for static references, but we never
|
||||
// have static references to UserAgentCascadeDatas. If we did, it
|
||||
// may not make sense to put them in the cache in the first place.
|
||||
if self.entries[i].is_unique() {
|
||||
unused.push(self.entries.remove(i));
|
||||
if !value.is_unique() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
unused.push(value.clone());
|
||||
false
|
||||
});
|
||||
unused
|
||||
}
|
||||
|
||||
fn take_all(&mut self) -> Vec<Arc<Entry>> {
|
||||
mem::replace(&mut self.entries, Vec::new())
|
||||
fn take_all(&mut self) -> FxHashMap<CascadeDataCacheKey, Arc<Entry>> {
|
||||
mem::take(&mut self.entries)
|
||||
}
|
||||
|
||||
#[cfg(feature = "gecko")]
|
||||
fn add_size_of(&self, ops: &mut MallocSizeOfOps, sizes: &mut ServoStyleSetSizes) {
|
||||
sizes.mOther += self.entries.shallow_size_of(ops);
|
||||
for arc in self.entries.iter() {
|
||||
for (_key, arc) in self.entries.iter() {
|
||||
// These are primary Arc references that can be measured
|
||||
// unconditionally.
|
||||
sizes.mOther += arc.unconditional_shallow_size_of(ops);
|
||||
|
@ -2033,7 +2082,8 @@ impl CascadeData {
|
|||
device: &Device,
|
||||
stylesheet: &S,
|
||||
guard: &SharedRwLockReadGuard,
|
||||
results: &mut EffectiveMediaQueryResults,
|
||||
results: &mut Vec<MediaListKey>,
|
||||
contents_list: &mut StyleSheetContentList,
|
||||
) where
|
||||
S: StylesheetInDocument + 'static,
|
||||
{
|
||||
|
@ -2042,19 +2092,25 @@ impl CascadeData {
|
|||
}
|
||||
|
||||
debug!(" + {:?}", stylesheet);
|
||||
results.saw_effective(stylesheet.contents());
|
||||
let contents = stylesheet.contents();
|
||||
results.push(contents.to_media_list_key());
|
||||
|
||||
// Safety: StyleSheetContents are reference-counted with Arc.
|
||||
contents_list.push(StylesheetContentsPtr(unsafe {
|
||||
Arc::from_raw_addrefed(contents)
|
||||
}));
|
||||
|
||||
for rule in stylesheet.effective_rules(device, guard) {
|
||||
match *rule {
|
||||
CssRule::Import(ref lock) => {
|
||||
let import_rule = lock.read_with(guard);
|
||||
debug!(" + {:?}", import_rule.stylesheet.media(guard));
|
||||
results.saw_effective(import_rule);
|
||||
results.push(import_rule.to_media_list_key());
|
||||
},
|
||||
CssRule::Media(ref lock) => {
|
||||
let media_rule = lock.read_with(guard);
|
||||
debug!(" + {:?}", media_rule.media_queries.read_with(guard));
|
||||
results.saw_effective(media_rule);
|
||||
results.push(media_rule.to_media_list_key());
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
|
|
|
@ -7,6 +7,8 @@
|
|||
use crate::values::animated::{Animate, Procedure, ToAnimatedZero};
|
||||
use crate::values::distance::{ComputeSquaredDistance, SquaredDistance};
|
||||
use crate::values::generics::color::{Color as GenericColor, ComplexColorRatios};
|
||||
use crate::values::specified::color::{ColorSpaceKind, HueAdjuster};
|
||||
use euclid::default::{Transform3D, Vector3D};
|
||||
|
||||
/// An animated RGBA color.
|
||||
///
|
||||
|
@ -41,6 +43,26 @@ impl RGBA {
|
|||
alpha,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns whether or not the colour is in gamut for sRGB.
|
||||
pub fn in_gamut(&self) -> bool {
|
||||
0. <= self.red &&
|
||||
self.red <= 1. &&
|
||||
0. <= self.green &&
|
||||
self.green <= 1. &&
|
||||
0. <= self.blue &&
|
||||
self.blue <= 1.
|
||||
}
|
||||
|
||||
/// Returns the colour with coordinates clamped to the sRGB range.
|
||||
pub fn clamp(&self) -> Self {
|
||||
Self {
|
||||
red: self.red.max(0.).min(1.),
|
||||
green: self.green.max(0.).min(1.),
|
||||
blue: self.blue.max(0.).min(1.),
|
||||
alpha: self.alpha,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Animate for RGBA {
|
||||
|
@ -106,43 +128,69 @@ impl Color {
|
|||
|
||||
/// Mix two colors into one.
|
||||
pub fn mix(
|
||||
color_space: ColorSpaceKind,
|
||||
left_color: &Color,
|
||||
left_weight: f32,
|
||||
right_color: &Color,
|
||||
right_weight: f32,
|
||||
hue_adjuster: HueAdjuster,
|
||||
) -> Self {
|
||||
let left_bg = left_color.scaled_rgba();
|
||||
let right_bg = right_color.scaled_rgba();
|
||||
let alpha = (left_bg.alpha * left_weight +
|
||||
right_bg.alpha * right_weight)
|
||||
.min(1.);
|
||||
|
||||
let mut fg = 0.;
|
||||
let mut red = 0.;
|
||||
let mut green = 0.;
|
||||
let mut blue = 0.;
|
||||
|
||||
let colors = [
|
||||
(left_color, &left_bg, left_weight),
|
||||
(right_color, &right_bg, right_weight),
|
||||
];
|
||||
|
||||
for &(color, bg, weight) in &colors {
|
||||
fg += color.ratios.fg * weight;
|
||||
|
||||
red += bg.red * bg.alpha * weight;
|
||||
green += bg.green * bg.alpha * weight;
|
||||
blue += bg.blue * bg.alpha * weight;
|
||||
match color_space {
|
||||
ColorSpaceKind::Srgb => Self::mix_in::<RGBA>(
|
||||
left_color,
|
||||
left_weight,
|
||||
right_color,
|
||||
right_weight,
|
||||
hue_adjuster,
|
||||
),
|
||||
ColorSpaceKind::Xyz => Self::mix_in::<XYZA>(
|
||||
left_color,
|
||||
left_weight,
|
||||
right_color,
|
||||
right_weight,
|
||||
hue_adjuster,
|
||||
),
|
||||
ColorSpaceKind::Lab => Self::mix_in::<LABA>(
|
||||
left_color,
|
||||
left_weight,
|
||||
right_color,
|
||||
right_weight,
|
||||
hue_adjuster,
|
||||
),
|
||||
ColorSpaceKind::Lch => Self::mix_in::<LCHA>(
|
||||
left_color,
|
||||
left_weight,
|
||||
right_color,
|
||||
right_weight,
|
||||
hue_adjuster,
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
let color = if alpha <= 0. {
|
||||
RGBA::transparent()
|
||||
fn mix_in<S>(
|
||||
left_color: &Color,
|
||||
left_weight: f32,
|
||||
right_color: &Color,
|
||||
right_weight: f32,
|
||||
hue_adjuster: HueAdjuster,
|
||||
) -> Self
|
||||
where
|
||||
S: ModelledColor,
|
||||
{
|
||||
let left_bg = S::from(left_color.scaled_rgba());
|
||||
let right_bg = S::from(right_color.scaled_rgba());
|
||||
|
||||
let color = S::lerp(left_bg, left_weight, right_bg, right_weight, hue_adjuster);
|
||||
let rgba: RGBA = color.into();
|
||||
let rgba = if !rgba.in_gamut() {
|
||||
// TODO: Better gamut mapping.
|
||||
rgba.clamp()
|
||||
} else {
|
||||
let inv = 1. / alpha;
|
||||
RGBA::new(red * inv, green * inv, blue * inv, alpha)
|
||||
rgba
|
||||
};
|
||||
|
||||
Self::new(color, ComplexColorRatios { bg: 1., fg })
|
||||
let fg = left_color.ratios.fg * left_weight + right_color.ratios.fg * right_weight;
|
||||
Self::new(rgba, ComplexColorRatios { bg: 1., fg })
|
||||
}
|
||||
|
||||
fn scaled_rgba(&self) -> RGBA {
|
||||
|
@ -309,3 +357,573 @@ impl ToAnimatedZero for Color {
|
|||
Ok(RGBA::transparent().into())
|
||||
}
|
||||
}
|
||||
|
||||
/// A color modelled in a specific color space (such as sRGB or CIE XYZ).
|
||||
///
|
||||
/// For now, colors modelled in other spaces need to be convertible to and from
|
||||
/// `RGBA` because we use sRGB for displaying colors.
|
||||
trait ModelledColor: Clone + Copy + From<RGBA> + Into<RGBA> {
|
||||
/// Linearly interpolate between the left and right colors.
|
||||
///
|
||||
/// The HueAdjuster parameter is only for color spaces where the hue is
|
||||
/// represented as an angle (e.g., CIE LCH).
|
||||
fn lerp(
|
||||
left_bg: Self,
|
||||
left_weight: f32,
|
||||
right_bg: Self,
|
||||
right_weight: f32,
|
||||
hue_adjuster: HueAdjuster,
|
||||
) -> Self;
|
||||
}
|
||||
|
||||
impl ModelledColor for RGBA {
|
||||
fn lerp(
|
||||
left_bg: Self,
|
||||
left_weight: f32,
|
||||
right_bg: Self,
|
||||
right_weight: f32,
|
||||
_: HueAdjuster,
|
||||
) -> Self {
|
||||
// Interpolation with alpha, as per
|
||||
// https://drafts.csswg.org/css-color/#interpolation-alpha.
|
||||
let mut red = 0.;
|
||||
let mut green = 0.;
|
||||
let mut blue = 0.;
|
||||
|
||||
// sRGB is a rectangular othogonal color space, so all component values
|
||||
// are multiplied by the alpha value.
|
||||
for &(bg, weight) in &[(left_bg, left_weight), (right_bg, right_weight)] {
|
||||
red += bg.red * bg.alpha * weight;
|
||||
green += bg.green * bg.alpha * weight;
|
||||
blue += bg.blue * bg.alpha * weight;
|
||||
}
|
||||
|
||||
let alpha = (left_bg.alpha * left_weight + right_bg.alpha * right_weight).min(1.);
|
||||
if alpha <= 0. {
|
||||
RGBA::transparent()
|
||||
} else {
|
||||
let inv = 1. / alpha;
|
||||
RGBA::new(red * inv, green * inv, blue * inv, alpha)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// An animated XYZA colour.
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct XYZA {
|
||||
/// The x component.
|
||||
pub x: f32,
|
||||
/// The y component.
|
||||
pub y: f32,
|
||||
/// The z component.
|
||||
pub z: f32,
|
||||
/// The alpha component.
|
||||
pub alpha: f32,
|
||||
}
|
||||
|
||||
impl XYZA {
|
||||
/// Returns a transparent color.
|
||||
#[inline]
|
||||
pub fn transparent() -> Self {
|
||||
Self {
|
||||
x: 0.,
|
||||
y: 0.,
|
||||
z: 0.,
|
||||
alpha: 0.,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ModelledColor for XYZA {
|
||||
fn lerp(
|
||||
left_bg: Self,
|
||||
left_weight: f32,
|
||||
right_bg: Self,
|
||||
right_weight: f32,
|
||||
_: HueAdjuster,
|
||||
) -> Self {
|
||||
// Interpolation with alpha, as per
|
||||
// https://drafts.csswg.org/css-color/#interpolation-alpha.
|
||||
let mut x = 0.;
|
||||
let mut y = 0.;
|
||||
let mut z = 0.;
|
||||
|
||||
// CIE XYZ is a rectangular othogonal color space, so all component
|
||||
// values are multiplied by the alpha value.
|
||||
for &(bg, weight) in &[(left_bg, left_weight), (right_bg, right_weight)] {
|
||||
x += bg.x * bg.alpha * weight;
|
||||
y += bg.y * bg.alpha * weight;
|
||||
z += bg.z * bg.alpha * weight;
|
||||
}
|
||||
|
||||
let alpha = (left_bg.alpha * left_weight + right_bg.alpha * right_weight).min(1.);
|
||||
if alpha <= 0. {
|
||||
Self::transparent()
|
||||
} else {
|
||||
let inv = 1. / alpha;
|
||||
Self {
|
||||
x: x * inv,
|
||||
y: y * inv,
|
||||
z: z * inv,
|
||||
alpha,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// An animated LABA colour.
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct LABA {
|
||||
/// The lightness component.
|
||||
pub lightness: f32,
|
||||
/// The a component.
|
||||
pub a: f32,
|
||||
/// The b component.
|
||||
pub b: f32,
|
||||
/// The alpha component.
|
||||
pub alpha: f32,
|
||||
}
|
||||
|
||||
impl LABA {
|
||||
/// Returns a transparent color.
|
||||
#[inline]
|
||||
pub fn transparent() -> Self {
|
||||
Self {
|
||||
lightness: 0.,
|
||||
a: 0.,
|
||||
b: 0.,
|
||||
alpha: 0.,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ModelledColor for LABA {
|
||||
fn lerp(
|
||||
left_bg: Self,
|
||||
left_weight: f32,
|
||||
right_bg: Self,
|
||||
right_weight: f32,
|
||||
_: HueAdjuster,
|
||||
) -> Self {
|
||||
// Interpolation with alpha, as per
|
||||
// https://drafts.csswg.org/css-color/#interpolation-alpha.
|
||||
let mut lightness = 0.;
|
||||
let mut a = 0.;
|
||||
let mut b = 0.;
|
||||
|
||||
// CIE LAB is a rectangular othogonal color space, so all component
|
||||
// values are multiplied by the alpha value.
|
||||
for &(bg, weight) in &[(left_bg, left_weight), (right_bg, right_weight)] {
|
||||
lightness += bg.lightness * bg.alpha * weight;
|
||||
a += bg.a * bg.alpha * weight;
|
||||
b += bg.b * bg.alpha * weight;
|
||||
}
|
||||
|
||||
let alpha = (left_bg.alpha * left_weight + right_bg.alpha * right_weight).min(1.);
|
||||
if alpha <= 0. {
|
||||
Self::transparent()
|
||||
} else {
|
||||
let inv = 1. / alpha;
|
||||
Self {
|
||||
lightness: lightness * inv,
|
||||
a: a * inv,
|
||||
b: b * inv,
|
||||
alpha,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// An animated LCHA colour.
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct LCHA {
|
||||
/// The lightness component.
|
||||
pub lightness: f32,
|
||||
/// The chroma component.
|
||||
pub chroma: f32,
|
||||
/// The hua component.
|
||||
pub hue: f32,
|
||||
/// The alpha component.
|
||||
pub alpha: f32,
|
||||
}
|
||||
|
||||
impl LCHA {
|
||||
/// Returns a transparent color.
|
||||
#[inline]
|
||||
pub fn transparent() -> Self {
|
||||
Self {
|
||||
lightness: 0.,
|
||||
chroma: 0.,
|
||||
hue: 0.,
|
||||
alpha: 0.,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl LCHA {
|
||||
fn adjust(left_bg: Self, right_bg: Self, hue_adjuster: HueAdjuster) -> (Self, Self) {
|
||||
use std::f32::consts::{PI, TAU};
|
||||
|
||||
let mut left_bg = left_bg;
|
||||
let mut right_bg = right_bg;
|
||||
|
||||
// Adjust the hue angle as per
|
||||
// https://drafts.csswg.org/css-color/#hue-interpolation.
|
||||
//
|
||||
// If both hue angles are NAN, they should be set to 0. Otherwise, if a
|
||||
// single hue angle is NAN, it should use the other hue angle.
|
||||
if left_bg.hue.is_nan() || right_bg.hue.is_nan() {
|
||||
if left_bg.hue.is_nan() && right_bg.hue.is_nan() {
|
||||
left_bg.hue = 0.;
|
||||
right_bg.hue = 0.;
|
||||
} else if left_bg.hue.is_nan() {
|
||||
left_bg.hue = right_bg.hue;
|
||||
} else if right_bg.hue.is_nan() {
|
||||
right_bg.hue = left_bg.hue;
|
||||
}
|
||||
}
|
||||
|
||||
if hue_adjuster != HueAdjuster::Specified {
|
||||
// Normalize hue into [0, 2 * PI)
|
||||
while left_bg.hue < 0. {
|
||||
left_bg.hue += TAU;
|
||||
}
|
||||
while left_bg.hue > TAU {
|
||||
left_bg.hue -= TAU;
|
||||
}
|
||||
|
||||
while right_bg.hue < 0. {
|
||||
right_bg.hue += TAU;
|
||||
}
|
||||
while right_bg.hue >= TAU {
|
||||
right_bg.hue -= TAU;
|
||||
}
|
||||
}
|
||||
|
||||
match hue_adjuster {
|
||||
HueAdjuster::Shorter => {
|
||||
let delta = right_bg.hue - left_bg.hue;
|
||||
|
||||
if delta > PI {
|
||||
left_bg.hue += PI;
|
||||
} else if delta < -1. * PI {
|
||||
right_bg.hue += PI;
|
||||
}
|
||||
},
|
||||
|
||||
HueAdjuster::Longer => {
|
||||
let delta = right_bg.hue - left_bg.hue;
|
||||
if 0. < delta && delta < PI {
|
||||
left_bg.hue += TAU;
|
||||
} else if -1. * PI < delta && delta < 0. {
|
||||
right_bg.hue += TAU;
|
||||
}
|
||||
},
|
||||
|
||||
HueAdjuster::Increasing => {
|
||||
if right_bg.hue < left_bg.hue {
|
||||
right_bg.hue += TAU;
|
||||
}
|
||||
},
|
||||
|
||||
HueAdjuster::Decreasing => {
|
||||
if left_bg.hue < right_bg.hue {
|
||||
left_bg.hue += TAU;
|
||||
}
|
||||
},
|
||||
|
||||
//Angles are not adjusted. They are interpolated like any other
|
||||
//component.
|
||||
HueAdjuster::Specified => {},
|
||||
}
|
||||
|
||||
(left_bg, right_bg)
|
||||
}
|
||||
}
|
||||
|
||||
impl ModelledColor for LCHA {
|
||||
fn lerp(
|
||||
left_bg: Self,
|
||||
left_weight: f32,
|
||||
right_bg: Self,
|
||||
right_weight: f32,
|
||||
hue_adjuster: HueAdjuster,
|
||||
) -> Self {
|
||||
// Interpolation with alpha, as per
|
||||
// https://drafts.csswg.org/css-color/#interpolation-alpha.
|
||||
let (left_bg, right_bg) = Self::adjust(left_bg, right_bg, hue_adjuster);
|
||||
|
||||
let mut lightness = 0.;
|
||||
let mut chroma = 0.;
|
||||
let mut hue = 0.;
|
||||
|
||||
// CIE LCH is a cylindical polar color space, so all component values
|
||||
// are multiplied by the alpha value.
|
||||
for &(bg, weight) in &[(left_bg, left_weight), (right_bg, right_weight)] {
|
||||
lightness += bg.lightness * bg.alpha * weight;
|
||||
chroma += bg.chroma * bg.alpha * weight;
|
||||
// LCHA is a cylindrical color space so the hue coordinate is not
|
||||
// pre-multipled by the alpha component when interpolating.
|
||||
hue += bg.hue * weight;
|
||||
}
|
||||
|
||||
let alpha = (left_bg.alpha * left_weight + right_bg.alpha * right_weight).min(1.);
|
||||
if alpha <= 0. {
|
||||
Self::transparent()
|
||||
} else {
|
||||
let inv = 1. / alpha;
|
||||
Self {
|
||||
lightness: lightness * inv,
|
||||
chroma: chroma * inv,
|
||||
hue,
|
||||
alpha,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<RGBA> for XYZA {
|
||||
/// Convert an RGBA colour to XYZ as specified in [1].
|
||||
///
|
||||
/// [1]: https://drafts.csswg.org/css-color/#rgb-to-lab
|
||||
fn from(rgba: RGBA) -> Self {
|
||||
fn linearize(value: f32) -> f32 {
|
||||
let sign = if value < 0. { -1. } else { 1. };
|
||||
let abs = value.abs();
|
||||
if abs < 0.04045 {
|
||||
return value / 12.92;
|
||||
}
|
||||
|
||||
sign * ((abs + 0.055) / 1.055).powf(2.4)
|
||||
}
|
||||
|
||||
#[cfg_attr(rustfmt, rustfmt_skip)]
|
||||
const SRGB_TO_XYZ: Transform3D<f32> = Transform3D::new(
|
||||
0.41239079926595934, 0.21263900587151027, 0.01933081871559182, 0.,
|
||||
0.357584339383878, 0.715168678767756, 0.11919477979462598, 0.,
|
||||
0.1804807884018343, 0.07219231536073371, 0.9505321522496607, 0.,
|
||||
0., 0., 0., 1.,
|
||||
);
|
||||
|
||||
#[cfg_attr(rustfmt, rustfmt_skip)]
|
||||
const BRADFORD: Transform3D<f32> = Transform3D::new(
|
||||
1.0479298208405488, 0.029627815688159344, -0.009243058152591178, 0.,
|
||||
0.022946793341019088, 0.990434484573249, 0.015055144896577895, 0.,
|
||||
-0.05019222954313557, -0.01707382502938514, 0.7518742899580008, 0.,
|
||||
0., 0., 0., 1.,
|
||||
);
|
||||
|
||||
// 1. Convert from sRGB to linear-light sRGB (undo gamma encoding).
|
||||
let rgb = Vector3D::new(
|
||||
linearize(rgba.red),
|
||||
linearize(rgba.green),
|
||||
linearize(rgba.blue),
|
||||
);
|
||||
|
||||
// 2. Convert from linear sRGB to CIE XYZ.
|
||||
// 3. Convert from a D65 whitepoint (used by sRGB) to the D50 whitepoint used in XYZ
|
||||
// with the Bradford transform.
|
||||
let xyz = SRGB_TO_XYZ.then(&BRADFORD).transform_vector3d(rgb);
|
||||
|
||||
XYZA {
|
||||
x: xyz.x,
|
||||
y: xyz.y,
|
||||
z: xyz.z,
|
||||
alpha: rgba.alpha,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<XYZA> for LABA {
|
||||
/// Convert an XYZ colour to LAB as specified in [1] and [2].
|
||||
///
|
||||
/// [1]: https://drafts.csswg.org/css-color/#rgb-to-lab
|
||||
/// [2]: https://drafts.csswg.org/css-color/#color-conversion-code
|
||||
fn from(xyza: XYZA) -> Self {
|
||||
const WHITE: [f32; 3] = [0.96422, 1., 0.82521];
|
||||
|
||||
fn compute_f(value: f32) -> f32 {
|
||||
const EPSILON: f32 = 216. / 24389.;
|
||||
const KAPPA: f32 = 24389. / 27.;
|
||||
|
||||
if value > EPSILON {
|
||||
value.cbrt()
|
||||
} else {
|
||||
(KAPPA * value + 16.) / 116.
|
||||
}
|
||||
}
|
||||
|
||||
// 4. Convert D50-adapted XYZ to Lab.
|
||||
let f = [
|
||||
compute_f(xyza.x / WHITE[0]),
|
||||
compute_f(xyza.y / WHITE[1]),
|
||||
compute_f(xyza.z / WHITE[2]),
|
||||
];
|
||||
|
||||
let lightness = 116. * f[1] - 16.;
|
||||
let a = 500. * (f[0] - f[1]);
|
||||
let b = 200. * (f[1] - f[2]);
|
||||
|
||||
LABA {
|
||||
lightness,
|
||||
a,
|
||||
b,
|
||||
alpha: xyza.alpha,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<LABA> for LCHA {
|
||||
/// Convert a LAB color to LCH as specified in [1].
|
||||
///
|
||||
/// [1]: https://drafts.csswg.org/css-color/#color-conversion-code
|
||||
fn from(laba: LABA) -> Self {
|
||||
let hue = laba.b.atan2(laba.a);
|
||||
let chroma = (laba.a * laba.a + laba.b * laba.b).sqrt();
|
||||
LCHA {
|
||||
lightness: laba.lightness,
|
||||
chroma,
|
||||
hue,
|
||||
alpha: laba.alpha,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<LCHA> for LABA {
|
||||
/// Convert a LCH color to LAB as specified in [1].
|
||||
///
|
||||
/// [1]: https://drafts.csswg.org/css-color/#color-conversion-code
|
||||
fn from(lcha: LCHA) -> Self {
|
||||
let a = lcha.chroma * lcha.hue.cos();
|
||||
let b = lcha.chroma * lcha.hue.sin();
|
||||
LABA {
|
||||
lightness: lcha.lightness,
|
||||
a,
|
||||
b,
|
||||
alpha: lcha.alpha,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<LABA> for XYZA {
|
||||
/// Convert a CIELAB color to XYZ as specified in [1] and [2].
|
||||
///
|
||||
/// [1]: https://drafts.csswg.org/css-color/#lab-to-predefined
|
||||
/// [2]: https://drafts.csswg.org/css-color/#color-conversion-code
|
||||
fn from(laba: LABA) -> Self {
|
||||
// 1. Convert LAB to (D50-adapated) XYZ.
|
||||
const KAPPA: f32 = 24389. / 27.;
|
||||
const EPSILON: f32 = 216. / 24389.;
|
||||
const WHITE: [f32; 3] = [0.96422, 1., 0.82521];
|
||||
|
||||
let f1 = (laba.lightness + 16f32) / 116f32;
|
||||
let f0 = (laba.a / 500.) + f1;
|
||||
let f2 = f1 - laba.b / 200.;
|
||||
|
||||
let x = if f0.powf(3.) > EPSILON {
|
||||
f0.powf(3.)
|
||||
} else {
|
||||
(116. * f0 - 16.) / KAPPA
|
||||
};
|
||||
let y = if laba.lightness > KAPPA * EPSILON {
|
||||
((laba.lightness + 16.) / 116.).powf(3.)
|
||||
} else {
|
||||
laba.lightness / KAPPA
|
||||
};
|
||||
let z = if f2.powf(3.) > EPSILON {
|
||||
f2.powf(3.)
|
||||
} else {
|
||||
(116. * f2 - 16.) / KAPPA
|
||||
};
|
||||
|
||||
XYZA {
|
||||
x: x * WHITE[0],
|
||||
y: y * WHITE[1],
|
||||
z: z * WHITE[2],
|
||||
alpha: laba.alpha,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<XYZA> for RGBA {
|
||||
/// Convert an XYZ color to sRGB as specified in [1] and [2].
|
||||
///
|
||||
/// [1]: https://www.w3.org/TR/css-color-4/#lab-to-predefined
|
||||
/// [2]: https://www.w3.org/TR/css-color-4/#color-conversion-code
|
||||
fn from(xyza: XYZA) -> Self {
|
||||
#[cfg_attr(rustfmt, rustfmt_skip)]
|
||||
const BRADFORD_INVERSE: Transform3D<f32> = Transform3D::new(
|
||||
0.9554734527042182, -0.028369706963208136, 0.012314001688319899, 0.,
|
||||
-0.023098536874261423, 1.0099954580058226, -0.020507696433477912, 0.,
|
||||
0.0632593086610217, 0.021041398966943008, 1.3303659366080753, 0.,
|
||||
0., 0., 0., 1.,
|
||||
);
|
||||
|
||||
#[cfg_attr(rustfmt, rustfmt_skip)]
|
||||
const XYZ_TO_SRGB: Transform3D<f32> = Transform3D::new(
|
||||
3.2409699419045226, -0.9692436362808796, 0.05563007969699366, 0.,
|
||||
-1.537383177570094, 1.8759675015077202, -0.20397695888897652, 0.,
|
||||
-0.4986107602930034, 0.04155505740717559, 1.0569715142428786, 0.,
|
||||
0., 0., 0., 1.,
|
||||
);
|
||||
|
||||
// 2. Convert from a D50 whitepoint (used by Lab) to the D65 whitepoint
|
||||
// used in sRGB, with the Bradford transform.
|
||||
// 3. Convert from (D65-adapted) CIE XYZ to linear-light srgb
|
||||
let xyz = Vector3D::new(xyza.x, xyza.y, xyza.z);
|
||||
let linear_rgb = BRADFORD_INVERSE.then(&XYZ_TO_SRGB).transform_vector3d(xyz);
|
||||
|
||||
// 4. Convert from linear-light srgb to srgb (do gamma encoding).
|
||||
fn delinearize(value: f32) -> f32 {
|
||||
let sign = if value < 0. { -1. } else { 1. };
|
||||
let abs = value.abs();
|
||||
|
||||
if abs > 0.0031308 {
|
||||
sign * (1.055 * abs.powf(1. / 2.4) - 0.055)
|
||||
} else {
|
||||
12.92 * value
|
||||
}
|
||||
}
|
||||
|
||||
let red = delinearize(linear_rgb.x);
|
||||
let green = delinearize(linear_rgb.y);
|
||||
let blue = delinearize(linear_rgb.z);
|
||||
|
||||
RGBA {
|
||||
red,
|
||||
green,
|
||||
blue,
|
||||
alpha: xyza.alpha,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<RGBA> for LABA {
|
||||
fn from(rgba: RGBA) -> Self {
|
||||
let xyza: XYZA = rgba.into();
|
||||
xyza.into()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<LABA> for RGBA {
|
||||
fn from(laba: LABA) -> Self {
|
||||
let xyza: XYZA = laba.into();
|
||||
xyza.into()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<RGBA> for LCHA {
|
||||
fn from(rgba: RGBA) -> Self {
|
||||
let xyza: XYZA = rgba.into();
|
||||
let laba: LABA = xyza.into();
|
||||
laba.into()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<LCHA> for RGBA {
|
||||
fn from(lcha: LCHA) -> Self {
|
||||
let laba: LABA = lcha.into();
|
||||
let xyza: XYZA = laba.into();
|
||||
xyza.into()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1105,21 +1105,27 @@ impl Animate for ComputedTransformOperation {
|
|||
// interpolated as defined in section Interpolation of
|
||||
// Matrices afterwards.
|
||||
//
|
||||
let from = create_perspective_matrix(fd.px());
|
||||
let to = create_perspective_matrix(td.px());
|
||||
let from = create_perspective_matrix(fd.infinity_or(|l| l.px()));
|
||||
let to = create_perspective_matrix(td.infinity_or(|l| l.px()));
|
||||
|
||||
let interpolated = Matrix3D::from(from).animate(&Matrix3D::from(to), procedure)?;
|
||||
|
||||
let decomposed = decompose_3d_matrix(interpolated)?;
|
||||
let perspective_z = decomposed.perspective.2;
|
||||
let used_value = if perspective_z == 0. {
|
||||
0.
|
||||
// Clamp results outside of the -1 to 0 range so that we get perspective
|
||||
// function values between 1 and infinity.
|
||||
let used_value = if perspective_z >= 0. {
|
||||
transform::PerspectiveFunction::None
|
||||
} else {
|
||||
-1. / perspective_z
|
||||
transform::PerspectiveFunction::Length(CSSPixelLength::new(
|
||||
if perspective_z <= -1. {
|
||||
1.
|
||||
} else {
|
||||
-1. / perspective_z
|
||||
}
|
||||
))
|
||||
};
|
||||
Ok(TransformOperation::Perspective(CSSPixelLength::new(
|
||||
used_value,
|
||||
)))
|
||||
Ok(TransformOperation::Perspective(used_value))
|
||||
},
|
||||
_ if self.is_translate() && other.is_translate() => self
|
||||
.to_translate_3d()
|
||||
|
@ -1198,14 +1204,18 @@ impl ComputeSquaredDistance for ComputedTransformOperation {
|
|||
(
|
||||
&TransformOperation::Perspective(ref fd),
|
||||
&TransformOperation::Perspective(ref td),
|
||||
) => fd.compute_squared_distance(td),
|
||||
) => {
|
||||
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
|
||||
// Perspective but not with anything else?
|
||||
let mut p_matrix = Matrix3D::identity();
|
||||
if p.px() > 0. {
|
||||
p_matrix.m34 = -1. / p.px();
|
||||
let p = p.infinity_or(|p| p.px());
|
||||
if p >= 0. {
|
||||
p_matrix.m34 = -1. / p.max(1.);
|
||||
}
|
||||
p_matrix.compute_squared_distance(&m)
|
||||
},
|
||||
|
|
|
@ -11,6 +11,8 @@ use cssparser::{Color as CSSParserColor, RGBA};
|
|||
use std::fmt;
|
||||
use style_traits::{CssWriter, ToCss};
|
||||
|
||||
pub use crate::values::specified::color::ColorScheme;
|
||||
|
||||
/// The computed value of the `color` property.
|
||||
pub type ColorPropertyValue = RGBA;
|
||||
|
||||
|
|
|
@ -4,13 +4,12 @@
|
|||
|
||||
//! Computed values for font properties
|
||||
|
||||
#[cfg(feature = "gecko")]
|
||||
use crate::gecko_bindings::sugar::refptr::RefPtr;
|
||||
#[cfg(feature = "gecko")]
|
||||
use crate::gecko_bindings::{bindings, structs};
|
||||
use crate::values::animated::{ToAnimatedValue, ToAnimatedZero};
|
||||
use crate::parser::{Parse, ParserContext};
|
||||
use crate::values::animated::ToAnimatedValue;
|
||||
use crate::values::computed::{
|
||||
Angle, Context, Integer, Length, NonNegativeLength, NonNegativePercentage,
|
||||
Angle, Context, Integer, Length, NonNegativeLength, NonNegativeNumber, NonNegativePercentage,
|
||||
};
|
||||
use crate::values::computed::{Number, Percentage, ToComputedValue};
|
||||
use crate::values::generics::font::{FeatureTagValue, FontSettings, VariationValue};
|
||||
|
@ -26,13 +25,7 @@ use cssparser::{serialize_identifier, CssStringWriter, Parser};
|
|||
use malloc_size_of::{MallocSizeOf, MallocSizeOfOps};
|
||||
use std::fmt::{self, Write};
|
||||
use std::hash::{Hash, Hasher};
|
||||
#[cfg(feature = "gecko")]
|
||||
use std::mem::{self, ManuallyDrop};
|
||||
#[cfg(feature = "servo")]
|
||||
use std::slice;
|
||||
use style_traits::{CssWriter, ParseError, ToCss};
|
||||
#[cfg(feature = "gecko")]
|
||||
use to_shmem::{self, SharedMemoryBuilder, ToShmem};
|
||||
|
||||
pub use crate::values::computed::Length as MozScriptMinSize;
|
||||
pub use crate::values::specified::font::{FontSynthesis, MozScriptSizeMultiplier};
|
||||
|
@ -183,6 +176,7 @@ impl ToAnimatedValue for FontSize {
|
|||
#[derive(Clone, Debug, Eq, PartialEq, ToComputedValue, ToResolvedValue)]
|
||||
#[cfg_attr(feature = "servo", derive(Hash, MallocSizeOf, Serialize, Deserialize))]
|
||||
/// Specifies a prioritized list of font family names or generic family names.
|
||||
#[repr(C)]
|
||||
pub struct FontFamily {
|
||||
/// The actual list of family names.
|
||||
pub families: FontFamilyList,
|
||||
|
@ -190,27 +184,105 @@ pub struct FontFamily {
|
|||
pub is_system_font: bool,
|
||||
}
|
||||
|
||||
macro_rules! static_font_family {
|
||||
($ident:ident, $family:expr) => {
|
||||
lazy_static! {
|
||||
static ref $ident: FontFamily = FontFamily {
|
||||
families: FontFamilyList {
|
||||
#[cfg(feature = "gecko")]
|
||||
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,
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
impl FontFamily {
|
||||
#[inline]
|
||||
/// Get default font family as `serif` which is a generic font-family
|
||||
pub fn serif() -> Self {
|
||||
FontFamily {
|
||||
families: FontFamilyList::new(Box::new([SingleFontFamily::Generic(
|
||||
GenericFontFamily::Serif,
|
||||
)])),
|
||||
is_system_font: false,
|
||||
Self::generic(GenericFontFamily::Serif).clone()
|
||||
}
|
||||
|
||||
/// 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,
|
||||
}));
|
||||
|
||||
&*MOZ_BULLET
|
||||
}
|
||||
|
||||
/// Returns a font family for a single system font.
|
||||
#[cfg(feature = "gecko")]
|
||||
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,
|
||||
},
|
||||
is_system_font: true,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a generic font family.
|
||||
pub fn generic(generic: GenericFontFamily) -> &'static Self {
|
||||
macro_rules! generic_font_family {
|
||||
($ident:ident, $family:ident) => {
|
||||
static_font_family!($ident, SingleFontFamily::Generic(GenericFontFamily::$family))
|
||||
}
|
||||
}
|
||||
|
||||
generic_font_family!(SERIF, Serif);
|
||||
generic_font_family!(SANS_SERIF, SansSerif);
|
||||
generic_font_family!(MONOSPACE, Monospace);
|
||||
generic_font_family!(CURSIVE, Cursive);
|
||||
generic_font_family!(FANTASY, Fantasy);
|
||||
#[cfg(feature = "gecko")]
|
||||
generic_font_family!(MOZ_EMOJI, MozEmoji);
|
||||
generic_font_family!(SYSTEM_UI, SystemUi);
|
||||
|
||||
match generic {
|
||||
GenericFontFamily::None => {
|
||||
debug_assert!(false, "Bogus caller!");
|
||||
&*SERIF
|
||||
}
|
||||
GenericFontFamily::Serif => &*SERIF,
|
||||
GenericFontFamily::SansSerif => &*SANS_SERIF,
|
||||
GenericFontFamily::Monospace => &*MONOSPACE,
|
||||
GenericFontFamily::Cursive => &*CURSIVE,
|
||||
GenericFontFamily::Fantasy => &*FANTASY,
|
||||
#[cfg(feature = "gecko")]
|
||||
GenericFontFamily::MozEmoji => &*MOZ_EMOJI,
|
||||
GenericFontFamily::SystemUi => &*SYSTEM_UI,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "gecko")]
|
||||
impl MallocSizeOf for FontFamily {
|
||||
fn size_of(&self, _ops: &mut MallocSizeOfOps) -> usize {
|
||||
// SharedFontList objects are generally shared from the pointer
|
||||
// stored in the specified value. So only count this if the
|
||||
// SharedFontList is unshared.
|
||||
let shared_font_list = self.families.shared_font_list().get();
|
||||
unsafe { bindings::Gecko_SharedFontList_SizeOfIncludingThisIfUnshared(shared_font_list) }
|
||||
fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize {
|
||||
use malloc_size_of::MallocUnconditionalSizeOf;
|
||||
// SharedFontList objects are generally measured from the pointer stored
|
||||
// in the specified value. So only count this if the SharedFontList is
|
||||
// unshared.
|
||||
let shared_font_list = &self.families.list;
|
||||
if shared_font_list.is_unique() {
|
||||
shared_font_list.unconditional_size_of(ops)
|
||||
} else {
|
||||
0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -220,7 +292,15 @@ impl ToCss for FontFamily {
|
|||
W: fmt::Write,
|
||||
{
|
||||
let mut iter = self.families.iter();
|
||||
iter.next().unwrap().to_css(dest)?;
|
||||
match iter.next() {
|
||||
Some(f) => f.to_css(dest)?,
|
||||
None => {
|
||||
#[cfg(feature = "gecko")]
|
||||
return self.families.fallback.to_css(dest);
|
||||
#[cfg(feature = "servo")]
|
||||
unreachable!();
|
||||
},
|
||||
}
|
||||
for family in iter {
|
||||
dest.write_str(", ")?;
|
||||
family.to_css(dest)?;
|
||||
|
@ -229,15 +309,16 @@ impl ToCss for FontFamily {
|
|||
}
|
||||
}
|
||||
|
||||
/// The name of a font family of choice.
|
||||
#[derive(
|
||||
Clone, Debug, Eq, Hash, MallocSizeOf, PartialEq, ToComputedValue, ToResolvedValue, ToShmem,
|
||||
)]
|
||||
#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
|
||||
/// The name of a font family of choice
|
||||
#[repr(C)]
|
||||
pub struct FamilyName {
|
||||
/// Name of the font family
|
||||
/// Name of the font family.
|
||||
pub name: Atom,
|
||||
/// Syntax of the font family
|
||||
/// Syntax of the font family.
|
||||
pub syntax: FontFamilyNameSyntax,
|
||||
}
|
||||
|
||||
|
@ -291,11 +372,13 @@ pub enum FontFamilyNameSyntax {
|
|||
Identifiers,
|
||||
}
|
||||
|
||||
/// A set of faces that vary in weight, width or slope.
|
||||
/// cbindgen:derive-mut-casts=true
|
||||
#[derive(
|
||||
Clone, Debug, Eq, MallocSizeOf, PartialEq, ToCss, ToComputedValue, ToResolvedValue, ToShmem,
|
||||
)]
|
||||
#[cfg_attr(feature = "servo", derive(Deserialize, Serialize, Hash))]
|
||||
/// A set of faces that vary in weight, width or slope.
|
||||
#[repr(u8)]
|
||||
pub enum SingleFontFamily {
|
||||
/// The name of a font family of choice.
|
||||
FamilyName(FamilyName),
|
||||
|
@ -303,11 +386,22 @@ pub enum SingleFontFamily {
|
|||
Generic(GenericFontFamily),
|
||||
}
|
||||
|
||||
fn system_ui_enabled(_: &ParserContext) -> bool {
|
||||
#[cfg(feature = "gecko")]
|
||||
return static_prefs::pref!("layout.css.system-ui.enabled");
|
||||
#[cfg(feature = "servo")]
|
||||
return false;
|
||||
}
|
||||
|
||||
/// A generic font-family name.
|
||||
///
|
||||
/// The order here is important, if you change it make sure that
|
||||
/// `gfxPlatformFontList.h`s ranged array and `gfxFontFamilyList`'s
|
||||
/// sSingleGenerics are updated as well.
|
||||
///
|
||||
/// NOTE(emilio): Should be u8, but it's a u32 because of ABI issues between GCC
|
||||
/// and LLVM see https://bugs.llvm.org/show_bug.cgi?id=44228 / bug 1600735 /
|
||||
/// bug 1726515.
|
||||
#[derive(
|
||||
Clone,
|
||||
Copy,
|
||||
|
@ -323,7 +417,7 @@ pub enum SingleFontFamily {
|
|||
ToShmem,
|
||||
)]
|
||||
#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
|
||||
#[repr(u8)]
|
||||
#[repr(u32)]
|
||||
#[allow(missing_docs)]
|
||||
pub enum GenericFontFamily {
|
||||
/// No generic family specified, only for internal usage.
|
||||
|
@ -337,15 +431,17 @@ pub enum GenericFontFamily {
|
|||
Monospace,
|
||||
Cursive,
|
||||
Fantasy,
|
||||
#[parse(condition = "system_ui_enabled")]
|
||||
SystemUi,
|
||||
/// An internal value for emoji font selection.
|
||||
#[css(skip)]
|
||||
#[cfg(feature = "gecko")]
|
||||
MozEmoji,
|
||||
}
|
||||
|
||||
impl SingleFontFamily {
|
||||
impl Parse for SingleFontFamily {
|
||||
/// Parse a font-family value.
|
||||
pub fn parse<'i, 't>(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),
|
||||
|
@ -353,11 +449,11 @@ impl SingleFontFamily {
|
|||
}));
|
||||
}
|
||||
|
||||
let first_ident = input.expect_ident_cloned()?;
|
||||
if let Ok(generic) = GenericFontFamily::from_ident(&first_ident) {
|
||||
if let Ok(generic) = input.try_parse(|i| GenericFontFamily::parse(context, i)) {
|
||||
return Ok(SingleFontFamily::Generic(generic));
|
||||
}
|
||||
|
||||
let first_ident = input.expect_ident_cloned()?;
|
||||
let reserved = match_ignore_ascii_case! { &first_ident,
|
||||
// https://drafts.csswg.org/css-fonts/#propdef-font-family
|
||||
// "Font family names that happen to be the same as a keyword value
|
||||
|
@ -400,8 +496,10 @@ impl SingleFontFamily {
|
|||
syntax,
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "servo")]
|
||||
#[cfg(feature = "servo")]
|
||||
impl SingleFontFamily {
|
||||
/// Get the corresponding font-family with Atom
|
||||
pub fn from_atom(input: Atom) -> SingleFontFamily {
|
||||
match input {
|
||||
|
@ -410,6 +508,7 @@ impl SingleFontFamily {
|
|||
atom!("cursive") => return SingleFontFamily::Generic(GenericFontFamily::Cursive),
|
||||
atom!("fantasy") => return SingleFontFamily::Generic(GenericFontFamily::Fantasy),
|
||||
atom!("monospace") => return SingleFontFamily::Generic(GenericFontFamily::Monospace),
|
||||
atom!("system-ui") => return SingleFontFamily::Generic(GenericFontFamily::SystemUi),
|
||||
_ => {},
|
||||
}
|
||||
|
||||
|
@ -419,6 +518,7 @@ impl SingleFontFamily {
|
|||
"cursive" => return SingleFontFamily::Generic(GenericFontFamily::Cursive),
|
||||
"fantasy" => return SingleFontFamily::Generic(GenericFontFamily::Fantasy),
|
||||
"monospace" => return SingleFontFamily::Generic(GenericFontFamily::Monospace),
|
||||
"system-ui" => return SingleFontFamily::Generic(GenericFontFamily::SystemUi),
|
||||
_ => {}
|
||||
}
|
||||
|
||||
|
@ -429,22 +529,20 @@ impl SingleFontFamily {
|
|||
syntax: FontFamilyNameSyntax::Quoted,
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(feature = "gecko")]
|
||||
/// Get the corresponding font-family with family name
|
||||
fn from_font_family_name(family: &structs::FontFamilyName) -> SingleFontFamily {
|
||||
if family.mName.mRawPtr.is_null() {
|
||||
debug_assert_ne!(family.mGeneric, GenericFontFamily::None);
|
||||
return SingleFontFamily::Generic(family.mGeneric);
|
||||
}
|
||||
let name = unsafe { Atom::from_raw(family.mName.mRawPtr) };
|
||||
SingleFontFamily::FamilyName(FamilyName {
|
||||
name,
|
||||
syntax: family.mSyntax,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// A list of font families.
|
||||
#[cfg(feature = "gecko")]
|
||||
#[derive(Clone, Debug, ToComputedValue, ToResolvedValue, ToShmem, PartialEq, Eq)]
|
||||
#[repr(C)]
|
||||
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.
|
||||
#[cfg(feature = "servo")]
|
||||
#[derive(
|
||||
Clone,
|
||||
|
@ -459,115 +557,52 @@ impl SingleFontFamily {
|
|||
ToResolvedValue,
|
||||
ToShmem,
|
||||
)]
|
||||
/// A list of SingleFontFamily
|
||||
pub struct FontFamilyList(Box<[SingleFontFamily]>);
|
||||
|
||||
#[cfg(feature = "gecko")]
|
||||
#[derive(Clone, Debug, ToComputedValue, ToResolvedValue)]
|
||||
/// A list of SingleFontFamily
|
||||
pub enum FontFamilyList {
|
||||
/// A strong reference to a Gecko SharedFontList object.
|
||||
SharedFontList(
|
||||
#[compute(no_field_bound)]
|
||||
#[resolve(no_field_bound)]
|
||||
RefPtr<structs::SharedFontList>,
|
||||
),
|
||||
/// A font-family generic ID.
|
||||
Generic(GenericFontFamily),
|
||||
pub struct FontFamilyList {
|
||||
/// The actual list of font families specified.
|
||||
pub list: Box<[SingleFontFamily]>,
|
||||
}
|
||||
|
||||
#[cfg(feature = "gecko")]
|
||||
impl ToShmem for FontFamilyList {
|
||||
fn to_shmem(&self, _builder: &mut SharedMemoryBuilder) -> to_shmem::Result<Self> {
|
||||
// In practice, the only SharedFontList objects we create from shared
|
||||
// style sheets are ones with a single generic entry.
|
||||
Ok(ManuallyDrop::new(match *self {
|
||||
FontFamilyList::SharedFontList(ref r) => {
|
||||
if !(r.mNames.len() == 1 && r.mNames[0].mName.mRawPtr.is_null()) {
|
||||
return Err(String::from(
|
||||
"ToShmem failed for FontFamilyList: cannot handle non-generic families",
|
||||
));
|
||||
}
|
||||
FontFamilyList::Generic(r.mNames[0].mGeneric)
|
||||
},
|
||||
FontFamilyList::Generic(t) => FontFamilyList::Generic(t),
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "gecko")]
|
||||
impl PartialEq for FontFamilyList {
|
||||
fn eq(&self, other: &FontFamilyList) -> bool {
|
||||
let self_list = self.shared_font_list();
|
||||
let other_list = other.shared_font_list();
|
||||
|
||||
if self_list.mNames.len() != other_list.mNames.len() {
|
||||
return false;
|
||||
}
|
||||
for (a, b) in self_list.mNames.iter().zip(other_list.mNames.iter()) {
|
||||
if a.mSyntax != b.mSyntax ||
|
||||
a.mName.mRawPtr != b.mName.mRawPtr ||
|
||||
a.mGeneric != b.mGeneric
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "gecko")]
|
||||
impl Eq for FontFamilyList {}
|
||||
|
||||
impl FontFamilyList {
|
||||
/// Return FontFamilyList with a vector of SingleFontFamily
|
||||
#[cfg(feature = "servo")]
|
||||
pub fn new(families: Box<[SingleFontFamily]>) -> FontFamilyList {
|
||||
FontFamilyList(families)
|
||||
/// Return iterator of SingleFontFamily
|
||||
pub fn iter(&self) -> impl Iterator<Item = &SingleFontFamily> {
|
||||
self.list.iter()
|
||||
}
|
||||
|
||||
/// Return FontFamilyList with a vector of SingleFontFamily
|
||||
/// Puts the fallback in the list if needed.
|
||||
#[cfg(feature = "gecko")]
|
||||
pub fn new(families: Box<[SingleFontFamily]>) -> FontFamilyList {
|
||||
let fontlist;
|
||||
let names;
|
||||
unsafe {
|
||||
fontlist = bindings::Gecko_SharedFontList_Create();
|
||||
names = &mut (*fontlist).mNames;
|
||||
names.ensure_capacity(families.len());
|
||||
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 isn't cursive or
|
||||
/// fantasy), 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 != GenericFontFamily::Cursive && f != GenericFontFamily::Fantasy,
|
||||
_ => false,
|
||||
}
|
||||
});
|
||||
|
||||
if let Some(0) = index_of_first_generic {
|
||||
return; // Already first
|
||||
}
|
||||
|
||||
let mut new_list = self.list.iter().cloned().collect::<Vec<_>>();
|
||||
let element_to_prepend = match index_of_first_generic {
|
||||
Some(i) => new_list.remove(i),
|
||||
None => SingleFontFamily::Generic(generic),
|
||||
};
|
||||
|
||||
for family in families.iter() {
|
||||
match *family {
|
||||
SingleFontFamily::FamilyName(ref f) => unsafe {
|
||||
bindings::Gecko_nsTArray_FontFamilyName_AppendNamed(
|
||||
names,
|
||||
f.name.as_ptr(),
|
||||
f.syntax,
|
||||
);
|
||||
},
|
||||
SingleFontFamily::Generic(family) => unsafe {
|
||||
bindings::Gecko_nsTArray_FontFamilyName_AppendGeneric(names, family);
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
FontFamilyList::SharedFontList(unsafe { RefPtr::from_addrefed(fontlist) })
|
||||
}
|
||||
|
||||
/// Return iterator of SingleFontFamily
|
||||
#[cfg(feature = "servo")]
|
||||
pub fn iter(&self) -> slice::Iter<SingleFontFamily> {
|
||||
self.0.iter()
|
||||
}
|
||||
|
||||
/// Return iterator of SingleFontFamily
|
||||
#[cfg(feature = "gecko")]
|
||||
pub fn iter(&self) -> FontFamilyNameIter {
|
||||
FontFamilyNameIter {
|
||||
names: &self.shared_font_list().mNames,
|
||||
cur: 0,
|
||||
}
|
||||
new_list.insert(0, element_to_prepend);
|
||||
self.list = crate::ArcSlice::from_iter(new_list.into_iter());
|
||||
}
|
||||
|
||||
/// Return the generic ID if it is a single generic font
|
||||
|
@ -580,67 +615,10 @@ impl FontFamilyList {
|
|||
}
|
||||
None
|
||||
}
|
||||
|
||||
/// Return a reference to the Gecko SharedFontList.
|
||||
#[cfg(feature = "gecko")]
|
||||
pub fn shared_font_list(&self) -> &RefPtr<structs::SharedFontList> {
|
||||
match *self {
|
||||
FontFamilyList::SharedFontList(ref r) => r,
|
||||
FontFamilyList::Generic(t) => {
|
||||
unsafe {
|
||||
// TODO(heycam): Should really add StaticRefPtr sugar.
|
||||
let index = t as usize;
|
||||
mem::transmute::<
|
||||
&structs::StaticRefPtr<structs::SharedFontList>,
|
||||
&RefPtr<structs::SharedFontList>,
|
||||
>(&structs::SharedFontList_sSingleGenerics[index])
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Iterator of FontFamily
|
||||
#[cfg(feature = "gecko")]
|
||||
pub struct FontFamilyNameIter<'a> {
|
||||
names: &'a structs::nsTArray<structs::FontFamilyName>,
|
||||
cur: usize,
|
||||
}
|
||||
|
||||
#[cfg(feature = "gecko")]
|
||||
impl<'a> Iterator for FontFamilyNameIter<'a> {
|
||||
type Item = SingleFontFamily;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
if self.cur < self.names.len() {
|
||||
let item = SingleFontFamily::from_font_family_name(&self.names[self.cur]);
|
||||
self.cur += 1;
|
||||
Some(item)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Preserve the readability of text when font fallback occurs
|
||||
#[derive(
|
||||
Animate,
|
||||
Clone,
|
||||
ComputeSquaredDistance,
|
||||
Copy,
|
||||
Debug,
|
||||
MallocSizeOf,
|
||||
PartialEq,
|
||||
ToCss,
|
||||
ToResolvedValue,
|
||||
)]
|
||||
pub enum FontSizeAdjust {
|
||||
#[animation(error)]
|
||||
/// None variant
|
||||
None,
|
||||
/// Number variant
|
||||
Number(CSSFloat),
|
||||
}
|
||||
pub type FontSizeAdjust = generics::GenericFontSizeAdjust<NonNegativeNumber>;
|
||||
|
||||
impl FontSizeAdjust {
|
||||
#[inline]
|
||||
|
@ -648,40 +626,6 @@ impl FontSizeAdjust {
|
|||
pub fn none() -> Self {
|
||||
FontSizeAdjust::None
|
||||
}
|
||||
|
||||
/// Get font-size-adjust with float number
|
||||
pub fn from_gecko_adjust(gecko: f32) -> Self {
|
||||
if gecko == -1.0 {
|
||||
FontSizeAdjust::None
|
||||
} else {
|
||||
FontSizeAdjust::Number(gecko)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ToAnimatedZero for FontSizeAdjust {
|
||||
#[inline]
|
||||
// FIXME(emilio): why?
|
||||
fn to_animated_zero(&self) -> Result<Self, ()> {
|
||||
Err(())
|
||||
}
|
||||
}
|
||||
|
||||
impl ToAnimatedValue for FontSizeAdjust {
|
||||
type AnimatedValue = Self;
|
||||
|
||||
#[inline]
|
||||
fn to_animated_value(self) -> Self {
|
||||
self
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn from_animated_value(animated: Self::AnimatedValue) -> Self {
|
||||
match animated {
|
||||
FontSizeAdjust::Number(number) => FontSizeAdjust::Number(number.max(0.)),
|
||||
_ => animated,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Use VariantAlternatesList as computed type of FontVariantAlternates
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
//! `<length>` computed values, and related ones.
|
||||
|
||||
use super::{Context, Number, ToComputedValue};
|
||||
use crate::computed_value_flags::ComputedValueFlags;
|
||||
use crate::values::animated::ToAnimatedValue;
|
||||
use crate::values::computed::NonNegativeNumber;
|
||||
use crate::values::generics::length as generics;
|
||||
|
@ -36,6 +37,7 @@ impl ToComputedValue for specified::NoCalcLength {
|
|||
length.to_computed_value(context, FontBaseSize::CurrentStyle)
|
||||
},
|
||||
specified::NoCalcLength::ViewportPercentage(length) => {
|
||||
context.builder.add_flags(ComputedValueFlags::USES_VIEWPORT_UNITS);
|
||||
length.to_computed_value(context.viewport_size_for_viewport_unit_resolution())
|
||||
},
|
||||
specified::NoCalcLength::ServoCharacterWidth(length) => {
|
||||
|
@ -188,7 +190,8 @@ impl Size {
|
|||
GenericSize::MinContent |
|
||||
GenericSize::MaxContent |
|
||||
GenericSize::MozFitContent |
|
||||
GenericSize::MozAvailable => false
|
||||
GenericSize::MozAvailable |
|
||||
GenericSize::FitContentFunction(_) => false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -607,22 +607,11 @@ impl CalcLengthPercentageLeaf {
|
|||
impl PartialOrd for CalcLengthPercentageLeaf {
|
||||
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
|
||||
use self::CalcLengthPercentageLeaf::*;
|
||||
|
||||
if std::mem::discriminant(self) != std::mem::discriminant(other) {
|
||||
return None;
|
||||
}
|
||||
|
||||
// NOTE: Percentages can't be compared reasonably here because the
|
||||
// percentage basis might be negative, see bug 1709018.
|
||||
match (self, other) {
|
||||
(&Length(ref one), &Length(ref other)) => one.partial_cmp(other),
|
||||
(&Percentage(ref one), &Percentage(ref other)) => one.partial_cmp(other),
|
||||
_ => {
|
||||
match *self {
|
||||
Length(..) | Percentage(..) => {},
|
||||
}
|
||||
unsafe {
|
||||
debug_unreachable!("Forgot a branch?");
|
||||
}
|
||||
},
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -50,7 +50,7 @@ pub use self::box_::{Display, Overflow, OverflowAnchor, TransitionProperty};
|
|||
pub use self::box_::{OverflowClipBox, OverscrollBehavior, Perspective, Resize};
|
||||
pub use self::box_::{ScrollSnapAlign, ScrollSnapAxis, ScrollSnapStrictness, ScrollSnapType};
|
||||
pub use self::box_::{TouchAction, VerticalAlign, WillChange};
|
||||
pub use self::color::{Color, ColorOrAuto, ColorPropertyValue};
|
||||
pub use self::color::{Color, ColorOrAuto, ColorPropertyValue, ColorScheme};
|
||||
pub use self::column::ColumnCount;
|
||||
pub use self::counters::{Content, ContentItem, CounterIncrement, CounterSetOrReset};
|
||||
pub use self::easing::TimingFunction;
|
||||
|
@ -82,7 +82,7 @@ pub use self::position::{
|
|||
pub use self::ratio::Ratio;
|
||||
pub use self::rect::NonNegativeLengthOrNumberRect;
|
||||
pub use self::resolution::Resolution;
|
||||
pub use self::svg::MozContextProperties;
|
||||
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::TextUnderlinePosition;
|
||||
|
|
|
@ -5,10 +5,69 @@
|
|||
//! Computed @page at-rule properties
|
||||
|
||||
use crate::values::computed::length::NonNegativeLength;
|
||||
use crate::values::computed::{Context, ToComputedValue};
|
||||
use crate::values::generics;
|
||||
use crate::values::generics::size::Size2D;
|
||||
|
||||
use crate::values::specified::page as specified;
|
||||
pub use generics::page::GenericPageSize;
|
||||
pub use generics::page::Orientation;
|
||||
pub use generics::page::PaperSize;
|
||||
|
||||
/// Computed value of the @page size descriptor
|
||||
pub type PageSize = generics::page::GenericPageSize<Size2D<NonNegativeLength>>;
|
||||
///
|
||||
/// The spec says that the computed value should be the same as the specified
|
||||
/// value but with all absolute units, but it's not currently possibly observe
|
||||
/// the computed value of page-size.
|
||||
#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, ToCss, ToResolvedValue, ToShmem)]
|
||||
#[repr(C, u8)]
|
||||
pub enum PageSize {
|
||||
/// Specified size, paper size, or paper size and orientation.
|
||||
Size(Size2D<NonNegativeLength>),
|
||||
/// `landscape` or `portrait` value, no specified size.
|
||||
Orientation(Orientation),
|
||||
/// `auto` value
|
||||
Auto,
|
||||
}
|
||||
|
||||
impl ToComputedValue for specified::PageSize {
|
||||
type ComputedValue = PageSize;
|
||||
|
||||
fn to_computed_value(&self, ctx: &Context) -> Self::ComputedValue {
|
||||
match &*self {
|
||||
Self::Size(s) => PageSize::Size(s.to_computed_value(ctx)),
|
||||
Self::PaperSize(p, Orientation::Landscape) => PageSize::Size(Size2D {
|
||||
width: p.long_edge().to_computed_value(ctx),
|
||||
height: p.short_edge().to_computed_value(ctx),
|
||||
}),
|
||||
Self::PaperSize(p, Orientation::Portrait) => PageSize::Size(Size2D {
|
||||
width: p.short_edge().to_computed_value(ctx),
|
||||
height: p.long_edge().to_computed_value(ctx),
|
||||
}),
|
||||
Self::Orientation(o) => PageSize::Orientation(*o),
|
||||
Self::Auto => PageSize::Auto,
|
||||
}
|
||||
}
|
||||
|
||||
fn from_computed_value(computed: &Self::ComputedValue) -> Self {
|
||||
match *computed {
|
||||
PageSize::Size(s) => Self::Size(ToComputedValue::from_computed_value(&s)),
|
||||
PageSize::Orientation(o) => Self::Orientation(o),
|
||||
PageSize::Auto => Self::Auto,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PageSize {
|
||||
/// `auto` value.
|
||||
#[inline]
|
||||
pub fn auto() -> Self {
|
||||
PageSize::Auto
|
||||
}
|
||||
|
||||
/// Whether this is the `auto` value.
|
||||
#[inline]
|
||||
pub fn is_auto(&self) -> bool {
|
||||
matches!(*self, PageSize::Auto)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,7 +11,7 @@ use crate::values::generics::svg as generic;
|
|||
use crate::values::RGBA;
|
||||
use crate::Zero;
|
||||
|
||||
pub use crate::values::specified::{MozContextProperties, SVGPaintOrder};
|
||||
pub use crate::values::specified::{DProperty, MozContextProperties, SVGPaintOrder};
|
||||
|
||||
/// Computed SVG Paint value
|
||||
pub type SVGPaint = generic::GenericSVGPaint<Color, ComputedUrl>;
|
||||
|
|
|
@ -110,6 +110,14 @@ impl ToComputedValue for specified::WordSpacing {
|
|||
/// A computed value for the `line-height` property.
|
||||
pub type LineHeight = GenericLineHeight<NonNegativeNumber, NonNegativeLength>;
|
||||
|
||||
impl WordSpacing {
|
||||
/// Return the `normal` computed value, which is just zero.
|
||||
#[inline]
|
||||
pub fn normal() -> Self {
|
||||
LengthPercentage::zero()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, MallocSizeOf, PartialEq, ToResolvedValue)]
|
||||
#[repr(C)]
|
||||
/// text-overflow.
|
||||
|
|
|
@ -24,6 +24,9 @@ pub type Transform = generic::GenericTransform<TransformOperation>;
|
|||
pub type TransformOrigin =
|
||||
generic::GenericTransformOrigin<LengthPercentage, LengthPercentage, Length>;
|
||||
|
||||
/// The computed value of the `perspective()` transform function.
|
||||
pub type PerspectiveFunction = generic::PerspectiveFunction<Length>;
|
||||
|
||||
/// A vector to represent the direction vector (rotate axis) for Rotate3D.
|
||||
pub type DirectionVector = Vector3D<CSSFloat>;
|
||||
|
||||
|
@ -516,8 +519,8 @@ impl ToAnimatedZero for TransformOperation {
|
|||
generic::TransformOperation::Rotate(_) => {
|
||||
Ok(generic::TransformOperation::Rotate(Angle::zero()))
|
||||
},
|
||||
generic::TransformOperation::Perspective(ref l) => Ok(
|
||||
generic::TransformOperation::Perspective(l.to_animated_zero()?),
|
||||
generic::TransformOperation::Perspective(_) => Ok(
|
||||
generic::TransformOperation::Perspective(generic::PerspectiveFunction::None)
|
||||
),
|
||||
generic::TransformOperation::AccumulateMatrix { .. } |
|
||||
generic::TransformOperation::InterpolateMatrix { .. } => {
|
||||
|
|
|
@ -147,7 +147,7 @@ impl<L> BorderSpacing<L> {
|
|||
}
|
||||
}
|
||||
|
||||
/// A generic value for `border-radius`, `outline-radius` and `inset()`.
|
||||
/// A generic value for `border-radius` and `inset()`.
|
||||
///
|
||||
/// <https://drafts.csswg.org/css-backgrounds-3/#border-radius>
|
||||
#[derive(
|
||||
|
|
|
@ -202,3 +202,60 @@ pub enum FontStyle<Angle> {
|
|||
#[value_info(starts_with_keyword)]
|
||||
Oblique(Angle),
|
||||
}
|
||||
|
||||
/// A generic value for the `font-size-adjust` property.
|
||||
///
|
||||
/// https://www.w3.org/TR/css-fonts-4/#font-size-adjust-prop
|
||||
/// https://github.com/w3c/csswg-drafts/issues/6160
|
||||
/// https://github.com/w3c/csswg-drafts/issues/6288
|
||||
#[allow(missing_docs)]
|
||||
#[repr(u8)]
|
||||
#[derive(
|
||||
Animate,
|
||||
Clone,
|
||||
ComputeSquaredDistance,
|
||||
Copy,
|
||||
Debug,
|
||||
Hash,
|
||||
MallocSizeOf,
|
||||
PartialEq,
|
||||
SpecifiedValueInfo,
|
||||
ToAnimatedValue,
|
||||
ToAnimatedZero,
|
||||
ToComputedValue,
|
||||
ToResolvedValue,
|
||||
ToShmem,
|
||||
)]
|
||||
pub enum GenericFontSizeAdjust<Number> {
|
||||
#[animation(error)]
|
||||
None,
|
||||
// 'ex-height' is the implied basis, so the keyword can be omitted
|
||||
ExHeight(Number),
|
||||
#[value_info(starts_with_keyword)]
|
||||
CapHeight(Number),
|
||||
#[value_info(starts_with_keyword)]
|
||||
ChWidth(Number),
|
||||
#[value_info(starts_with_keyword)]
|
||||
IcWidth(Number),
|
||||
#[value_info(starts_with_keyword)]
|
||||
IcHeight(Number),
|
||||
}
|
||||
|
||||
impl<Number: ToCss> ToCss for GenericFontSizeAdjust<Number> {
|
||||
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
|
||||
where
|
||||
W: Write,
|
||||
{
|
||||
let (prefix, value) = match self {
|
||||
Self::None => return dest.write_str("none"),
|
||||
Self::ExHeight(v) => ("", v),
|
||||
Self::CapHeight(v) => ("cap-height ", v),
|
||||
Self::ChWidth(v) => ("ch-width ", v),
|
||||
Self::IcWidth(v) => ("ic-width ", v),
|
||||
Self::IcHeight(v) => ("ic-height ", v),
|
||||
};
|
||||
|
||||
dest.write_str(prefix)?;
|
||||
value.to_css(dest)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -161,6 +161,10 @@ pub enum GenericSize<LengthPercent> {
|
|||
#[cfg(feature = "gecko")]
|
||||
#[animation(error)]
|
||||
MozAvailable,
|
||||
#[cfg(feature = "gecko")]
|
||||
#[animation(error)]
|
||||
#[css(function = "fit-content")]
|
||||
FitContentFunction(LengthPercent)
|
||||
}
|
||||
|
||||
pub use self::GenericSize as Size;
|
||||
|
@ -215,6 +219,10 @@ pub enum GenericMaxSize<LengthPercent> {
|
|||
#[cfg(feature = "gecko")]
|
||||
#[animation(error)]
|
||||
MozAvailable,
|
||||
#[cfg(feature = "gecko")]
|
||||
#[animation(error)]
|
||||
#[css(function = "fit-content")]
|
||||
FitContentFunction(LengthPercent),
|
||||
}
|
||||
|
||||
pub use self::GenericMaxSize as MaxSize;
|
||||
|
@ -227,7 +235,7 @@ impl<LengthPercentage> MaxSize<LengthPercentage> {
|
|||
}
|
||||
}
|
||||
|
||||
/// A generic `<length>` | `<number>` value for the `-moz-tab-size` property.
|
||||
/// A generic `<length>` | `<number>` value for the `tab-size` property.
|
||||
#[derive(
|
||||
Animate,
|
||||
Clone,
|
||||
|
|
|
@ -95,6 +95,21 @@ impl CounterStyle {
|
|||
pub fn decimal() -> Self {
|
||||
CounterStyle::Name(CustomIdent(atom!("decimal")))
|
||||
}
|
||||
|
||||
/// Is this a bullet? (i.e. `list-style-type: disc|circle|square|disclosure-closed|disclosure-open`)
|
||||
#[inline]
|
||||
pub fn is_bullet(&self) -> bool {
|
||||
match self {
|
||||
CounterStyle::Name(CustomIdent(ref name)) => {
|
||||
name == &atom!("disc") ||
|
||||
name == &atom!("circle") ||
|
||||
name == &atom!("square") ||
|
||||
name == &atom!("disclosure-closed") ||
|
||||
name == &atom!("disclosure-open")
|
||||
}
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Parse for CounterStyle {
|
||||
|
|
|
@ -4,22 +4,14 @@
|
|||
|
||||
//! @page at-rule properties
|
||||
|
||||
use crate::values::generics::NonNegative;
|
||||
use crate::values::specified::length::AbsoluteLength;
|
||||
|
||||
/// Page size names.
|
||||
///
|
||||
/// https://drafts.csswg.org/css-page-3/#typedef-page-size-page-size
|
||||
#[derive(
|
||||
Clone,
|
||||
Copy,
|
||||
Debug,
|
||||
Eq,
|
||||
MallocSizeOf,
|
||||
Parse,
|
||||
PartialEq,
|
||||
SpecifiedValueInfo,
|
||||
ToComputedValue,
|
||||
ToCss,
|
||||
ToResolvedValue,
|
||||
ToShmem,
|
||||
Clone, Copy, Debug, Eq, MallocSizeOf, Parse, PartialEq, SpecifiedValueInfo, ToCss, ToShmem,
|
||||
)]
|
||||
#[repr(u8)]
|
||||
pub enum PaperSize {
|
||||
|
@ -45,6 +37,39 @@ pub enum PaperSize {
|
|||
Ledger,
|
||||
}
|
||||
|
||||
impl PaperSize {
|
||||
/// Gets the long edge length of the paper size
|
||||
pub fn long_edge(&self) -> NonNegative<AbsoluteLength> {
|
||||
NonNegative(match *self {
|
||||
PaperSize::A5 => AbsoluteLength::Mm(210.0),
|
||||
PaperSize::A4 => AbsoluteLength::Mm(297.0),
|
||||
PaperSize::A3 => AbsoluteLength::Mm(420.0),
|
||||
PaperSize::B5 => AbsoluteLength::Mm(250.0),
|
||||
PaperSize::B4 => AbsoluteLength::Mm(353.0),
|
||||
PaperSize::JisB5 => AbsoluteLength::Mm(257.0),
|
||||
PaperSize::JisB4 => AbsoluteLength::Mm(364.0),
|
||||
PaperSize::Letter => AbsoluteLength::In(11.0),
|
||||
PaperSize::Legal => AbsoluteLength::In(14.0),
|
||||
PaperSize::Ledger => AbsoluteLength::In(17.0),
|
||||
})
|
||||
}
|
||||
/// Gets the short edge length of the paper size
|
||||
pub fn short_edge(&self) -> NonNegative<AbsoluteLength> {
|
||||
NonNegative(match *self {
|
||||
PaperSize::A5 => AbsoluteLength::Mm(148.0),
|
||||
PaperSize::A4 => AbsoluteLength::Mm(210.0),
|
||||
PaperSize::A3 => AbsoluteLength::Mm(297.0),
|
||||
PaperSize::B5 => AbsoluteLength::Mm(176.0),
|
||||
PaperSize::B4 => AbsoluteLength::Mm(250.0),
|
||||
PaperSize::JisB5 => AbsoluteLength::Mm(182.0),
|
||||
PaperSize::JisB4 => AbsoluteLength::Mm(257.0),
|
||||
PaperSize::Letter => AbsoluteLength::In(8.5),
|
||||
PaperSize::Legal => AbsoluteLength::In(8.5),
|
||||
PaperSize::Ledger => AbsoluteLength::In(11.0),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Paper orientation
|
||||
///
|
||||
/// https://drafts.csswg.org/css-page-3/#page-size-prop
|
||||
|
@ -57,7 +82,6 @@ pub enum PaperSize {
|
|||
Parse,
|
||||
PartialEq,
|
||||
SpecifiedValueInfo,
|
||||
ToComputedValue,
|
||||
ToCss,
|
||||
ToResolvedValue,
|
||||
ToShmem,
|
||||
|
@ -70,33 +94,25 @@ pub enum Orientation {
|
|||
Landscape,
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn is_portrait(orientation: &Orientation) -> bool {
|
||||
*orientation == Orientation::Portrait
|
||||
}
|
||||
|
||||
/// Page size property
|
||||
///
|
||||
/// https://drafts.csswg.org/css-page-3/#page-size-prop
|
||||
#[derive(
|
||||
Clone,
|
||||
Copy,
|
||||
Debug,
|
||||
MallocSizeOf,
|
||||
PartialEq,
|
||||
SpecifiedValueInfo,
|
||||
ToComputedValue,
|
||||
ToCss,
|
||||
ToResolvedValue,
|
||||
ToShmem,
|
||||
)]
|
||||
#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToCss, ToShmem)]
|
||||
#[repr(C, u8)]
|
||||
pub enum GenericPageSize<S> {
|
||||
/// Page dimensions.
|
||||
Size(S),
|
||||
/// Paper size with no orientation.
|
||||
PaperSize(PaperSize),
|
||||
/// An orientation with no size.
|
||||
Orientation(Orientation),
|
||||
/// Paper size by name, with an orientation.
|
||||
PaperSizeAndOrientation(PaperSize, Orientation),
|
||||
/// `auto` value.
|
||||
Auto,
|
||||
/// Page dimensions.
|
||||
Size(S),
|
||||
/// An orientation with no size.
|
||||
Orientation(Orientation),
|
||||
/// Paper size by name
|
||||
PaperSize(PaperSize, #[css(skip_if = "is_portrait")] Orientation),
|
||||
}
|
||||
|
||||
pub use self::GenericPageSize as PageSize;
|
||||
|
|
|
@ -141,6 +141,41 @@ fn is_same<N: PartialEq>(x: &N, y: &N) -> bool {
|
|||
x == y
|
||||
}
|
||||
|
||||
/// A value for the `perspective()` transform function, which is either a
|
||||
/// non-negative `<length>` or `none`.
|
||||
#[derive(
|
||||
Clone,
|
||||
Debug,
|
||||
Deserialize,
|
||||
MallocSizeOf,
|
||||
PartialEq,
|
||||
Serialize,
|
||||
SpecifiedValueInfo,
|
||||
ToComputedValue,
|
||||
ToCss,
|
||||
ToResolvedValue,
|
||||
ToShmem,
|
||||
)]
|
||||
#[repr(C, u8)]
|
||||
pub enum GenericPerspectiveFunction<L> {
|
||||
/// `none`
|
||||
None,
|
||||
/// A `<length>`.
|
||||
Length(L),
|
||||
}
|
||||
|
||||
impl<L> GenericPerspectiveFunction<L> {
|
||||
/// Returns `f32::INFINITY` or the result of a function on the length value.
|
||||
pub fn infinity_or(&self, f: impl FnOnce(&L) -> f32) -> f32 {
|
||||
match *self {
|
||||
Self::None => std::f32::INFINITY,
|
||||
Self::Length(ref l) => f(l),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub use self::GenericPerspectiveFunction as PerspectiveFunction;
|
||||
|
||||
#[derive(
|
||||
Clone,
|
||||
Debug,
|
||||
|
@ -240,7 +275,7 @@ where
|
|||
///
|
||||
/// The value must be greater than or equal to zero.
|
||||
#[css(function)]
|
||||
Perspective(Length),
|
||||
Perspective(GenericPerspectiveFunction<Length>),
|
||||
/// A intermediate type for interpolation of mismatched transform lists.
|
||||
#[allow(missing_docs)]
|
||||
#[css(comma, function = "interpolatematrix")]
|
||||
|
@ -443,36 +478,38 @@ where
|
|||
use self::TransformOperation::*;
|
||||
use std::f64;
|
||||
|
||||
const TWO_PI: f64 = 2.0f64 * f64::consts::PI;
|
||||
let reference_width = reference_box.map(|v| v.size.width);
|
||||
let reference_height = reference_box.map(|v| v.size.height);
|
||||
let matrix = match *self {
|
||||
Rotate3D(ax, ay, az, theta) => {
|
||||
let theta = TWO_PI - theta.radians64();
|
||||
let theta = theta.radians64();
|
||||
let (ax, ay, az, theta) =
|
||||
get_normalized_vector_and_angle(ax.into(), ay.into(), az.into(), theta);
|
||||
Transform3D::rotation(
|
||||
ax as f64,
|
||||
ay as f64,
|
||||
az as f64,
|
||||
-euclid::Angle::radians(theta),
|
||||
euclid::Angle::radians(theta),
|
||||
)
|
||||
},
|
||||
RotateX(theta) => {
|
||||
let theta = euclid::Angle::radians(TWO_PI - theta.radians64());
|
||||
Transform3D::rotation(1., 0., 0., -theta)
|
||||
let theta = euclid::Angle::radians(theta.radians64());
|
||||
Transform3D::rotation(1., 0., 0., theta)
|
||||
},
|
||||
RotateY(theta) => {
|
||||
let theta = euclid::Angle::radians(TWO_PI - theta.radians64());
|
||||
Transform3D::rotation(0., 1., 0., -theta)
|
||||
let theta = euclid::Angle::radians(theta.radians64());
|
||||
Transform3D::rotation(0., 1., 0., theta)
|
||||
},
|
||||
RotateZ(theta) | Rotate(theta) => {
|
||||
let theta = euclid::Angle::radians(TWO_PI - theta.radians64());
|
||||
Transform3D::rotation(0., 0., 1., -theta)
|
||||
let theta = euclid::Angle::radians(theta.radians64());
|
||||
Transform3D::rotation(0., 0., 1., theta)
|
||||
},
|
||||
Perspective(ref d) => {
|
||||
let m = create_perspective_matrix(d.to_pixel_length(None)?);
|
||||
m.cast()
|
||||
Perspective(ref p) => {
|
||||
let px = match p {
|
||||
PerspectiveFunction::None => std::f32::INFINITY,
|
||||
PerspectiveFunction::Length(ref p) => p.to_pixel_length(None)?,
|
||||
};
|
||||
create_perspective_matrix(px).cast()
|
||||
},
|
||||
Scale3D(sx, sy, sz) => Transform3D::scale(sx.into(), sy.into(), sz.into()),
|
||||
Scale(sx, sy) => Transform3D::scale(sx.into(), sy.into(), 1.),
|
||||
|
@ -583,17 +620,10 @@ impl<T: ToMatrix> Transform<T> {
|
|||
/// Return the transform matrix from a perspective length.
|
||||
#[inline]
|
||||
pub fn create_perspective_matrix(d: CSSFloat) -> Transform3D<CSSFloat> {
|
||||
// TODO(gw): The transforms spec says that perspective length must
|
||||
// be positive. However, there is some confusion between the spec
|
||||
// and browser implementations as to handling the case of 0 for the
|
||||
// perspective value. Until the spec bug is resolved, at least ensure
|
||||
// that a provided perspective value of <= 0.0 doesn't cause panics
|
||||
// and behaves as it does in other browsers.
|
||||
// See https://lists.w3.org/Archives/Public/www-style/2016Jan/0020.html for more details.
|
||||
if d <= 0.0 {
|
||||
Transform3D::identity()
|
||||
if d.is_finite() {
|
||||
Transform3D::perspective(d.max(1.))
|
||||
} else {
|
||||
Transform3D::perspective(d)
|
||||
Transform3D::identity()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -7,6 +7,14 @@
|
|||
use super::{Context, ToResolvedValue};
|
||||
use crate::values::computed;
|
||||
|
||||
#[inline]
|
||||
fn allow_element_content_none() -> bool {
|
||||
#[cfg(feature = "gecko")]
|
||||
return static_prefs::pref!("layout.css.element-content-none.enabled");
|
||||
#[cfg(feature = "servo")]
|
||||
return false;
|
||||
}
|
||||
|
||||
/// https://drafts.csswg.org/css-content/#content-property
|
||||
///
|
||||
/// We implement this at resolved value time because otherwise it causes us to
|
||||
|
@ -23,19 +31,19 @@ impl ToResolvedValue for computed::Content {
|
|||
|
||||
#[inline]
|
||||
fn to_resolved_value(self, context: &Context) -> Self {
|
||||
let is_before_or_after = context
|
||||
.style
|
||||
.pseudo()
|
||||
.map_or(false, |p| p.is_before_or_after());
|
||||
|
||||
let (is_pseudo, is_before_or_after, is_marker) =
|
||||
match context.style.pseudo() {
|
||||
Some(ref pseudo) => (true, pseudo.is_before_or_after(), pseudo.is_marker()),
|
||||
None => (false, false, false)
|
||||
};
|
||||
match self {
|
||||
Self::Normal if is_before_or_after => Self::None,
|
||||
// For now, make `content: none` compute to `normal` on other
|
||||
// elements, as we don't respect it.
|
||||
//
|
||||
// FIXME(emilio, bug 1605473): for marker this should be preserved
|
||||
// and respected, probably.
|
||||
Self::None if !is_before_or_after => Self::Normal,
|
||||
// For now, make `content: none` compute to `normal` for pseudos
|
||||
// other than ::before, ::after and ::marker, as we don't respect it.
|
||||
// https://github.com/w3c/csswg-drafts/issues/6124
|
||||
// Ditto for non-pseudo elements if the pref is disabled.
|
||||
Self::None if (is_pseudo && !is_before_or_after && !is_marker) ||
|
||||
(!is_pseudo && !allow_element_content_none()) => Self::Normal,
|
||||
other => other,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -55,15 +55,6 @@ pub type ShapeRadius = generic::ShapeRadius<NonNegativeLengthPercentage>;
|
|||
/// The specified value of `Polygon`
|
||||
pub type Polygon = generic::GenericPolygon<LengthPercentage>;
|
||||
|
||||
#[cfg(feature = "gecko")]
|
||||
fn is_clip_path_path_enabled(context: &ParserContext) -> bool {
|
||||
context.chrome_rules_enabled() || static_prefs::pref!("layout.css.clip-path-path.enabled")
|
||||
}
|
||||
#[cfg(feature = "servo")]
|
||||
fn is_clip_path_path_enabled(_: &ParserContext) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
/// A helper for both clip-path and shape-outside parsing of shapes.
|
||||
fn parse_shape_or_box<'i, 't, R, ReferenceBox>(
|
||||
context: &ParserContext,
|
||||
|
@ -116,10 +107,8 @@ impl Parse for ClipPath {
|
|||
return Ok(ClipPath::None);
|
||||
}
|
||||
|
||||
if is_clip_path_path_enabled(context) {
|
||||
if let Ok(p) = input.try_parse(|i| Path::parse(context, i)) {
|
||||
return Ok(ClipPath::Path(p));
|
||||
}
|
||||
if let Ok(p) = input.try_parse(|i| Path::parse(context, i)) {
|
||||
return Ok(ClipPath::Path(p));
|
||||
}
|
||||
|
||||
if let Ok(url) = input.try_parse(|i| SpecifiedUrl::parse(context, i)) {
|
||||
|
|
|
@ -1838,8 +1838,6 @@ pub enum Appearance {
|
|||
#[parse(condition = "ParserContext::in_ua_or_chrome_sheet")]
|
||||
MozWindowTitlebarMaximized,
|
||||
|
||||
#[parse(condition = "ParserContext::in_ua_or_chrome_sheet")]
|
||||
MozGtkInfoBar,
|
||||
#[parse(condition = "ParserContext::in_ua_or_chrome_sheet")]
|
||||
MozMacActiveSourceListSelection,
|
||||
#[parse(condition = "ParserContext::in_ua_or_chrome_sheet")]
|
||||
|
@ -1896,28 +1894,20 @@ pub enum BreakBetween {
|
|||
}
|
||||
|
||||
impl BreakBetween {
|
||||
/// Parse a legacy break-between value for `page-break-*`.
|
||||
/// Parse a legacy break-between value for `page-break-{before,after}`.
|
||||
///
|
||||
/// See https://drafts.csswg.org/css-break/#page-break-properties.
|
||||
#[cfg(feature = "gecko")]
|
||||
#[inline]
|
||||
pub fn parse_legacy<'i>(input: &mut Parser<'i, '_>) -> Result<Self, ParseError<'i>> {
|
||||
let location = input.current_source_location();
|
||||
let ident = input.expect_ident()?;
|
||||
let break_value = match BreakBetween::from_ident(ident) {
|
||||
Ok(v) => v,
|
||||
Err(()) => {
|
||||
return Err(location
|
||||
.new_custom_error(SelectorParseErrorKind::UnexpectedIdent(ident.clone())));
|
||||
},
|
||||
};
|
||||
pub(crate) fn parse_legacy<'i>(_: &ParserContext, input: &mut Parser<'i, '_>) -> Result<Self, ParseError<'i>> {
|
||||
let break_value = BreakBetween::parse(input)?;
|
||||
match break_value {
|
||||
BreakBetween::Always => Ok(BreakBetween::Page),
|
||||
BreakBetween::Auto | BreakBetween::Avoid | BreakBetween::Left | BreakBetween::Right => {
|
||||
Ok(break_value)
|
||||
},
|
||||
BreakBetween::Page => {
|
||||
Err(location
|
||||
.new_custom_error(SelectorParseErrorKind::UnexpectedIdent(ident.clone())))
|
||||
Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
|
||||
},
|
||||
}
|
||||
}
|
||||
|
@ -1925,7 +1915,8 @@ impl BreakBetween {
|
|||
/// Serialize a legacy break-between value for `page-break-*`.
|
||||
///
|
||||
/// See https://drafts.csswg.org/css-break/#page-break-properties.
|
||||
pub fn to_css_legacy<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
|
||||
#[cfg(feature = "gecko")]
|
||||
pub(crate) fn to_css_legacy<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
|
||||
where
|
||||
W: Write,
|
||||
{
|
||||
|
@ -1962,6 +1953,39 @@ impl BreakBetween {
|
|||
pub enum BreakWithin {
|
||||
Auto,
|
||||
Avoid,
|
||||
AvoidPage,
|
||||
AvoidColumn,
|
||||
}
|
||||
|
||||
impl BreakWithin {
|
||||
/// Parse a legacy break-between value for `page-break-inside`.
|
||||
///
|
||||
/// See https://drafts.csswg.org/css-break/#page-break-properties.
|
||||
#[cfg(feature = "gecko")]
|
||||
#[inline]
|
||||
pub(crate) fn parse_legacy<'i>(_: &ParserContext, input: &mut Parser<'i, '_>) -> Result<Self, ParseError<'i>> {
|
||||
let break_value = BreakWithin::parse(input)?;
|
||||
match break_value {
|
||||
BreakWithin::Auto | BreakWithin::Avoid => Ok(break_value),
|
||||
BreakWithin::AvoidPage | BreakWithin::AvoidColumn => {
|
||||
Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
/// Serialize a legacy break-between value for `page-break-inside`.
|
||||
///
|
||||
/// See https://drafts.csswg.org/css-break/#page-break-properties.
|
||||
#[cfg(feature = "gecko")]
|
||||
pub(crate) fn to_css_legacy<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
|
||||
where
|
||||
W: Write,
|
||||
{
|
||||
match *self {
|
||||
BreakWithin::Auto | BreakWithin::Avoid => self.to_css(dest),
|
||||
BreakWithin::AvoidPage | BreakWithin::AvoidColumn => Ok(()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// The value for the `overflow-x` / `overflow-y` properties.
|
||||
|
|
|
@ -107,8 +107,12 @@ impl PartialOrd for Leaf {
|
|||
}
|
||||
|
||||
match (self, other) {
|
||||
// NOTE: Percentages can't be compared reasonably here because the
|
||||
// percentage basis might be negative, see bug 1709018.
|
||||
// Conveniently, we only use this for <length-percentage> (for raw
|
||||
// percentages, we go through resolve()).
|
||||
(&Percentage(..), &Percentage(..)) => None,
|
||||
(&Length(ref one), &Length(ref other)) => one.partial_cmp(other),
|
||||
(&Percentage(ref one), &Percentage(ref other)) => one.partial_cmp(other),
|
||||
(&Angle(ref one), &Angle(ref other)) => one.degrees().partial_cmp(&other.degrees()),
|
||||
(&Time(ref one), &Time(ref other)) => one.seconds().partial_cmp(&other.seconds()),
|
||||
(&Number(ref one), &Number(ref other)) => one.partial_cmp(other),
|
||||
|
|
|
@ -9,9 +9,10 @@ use super::AllowQuirks;
|
|||
use crate::gecko_bindings::structs::nscolor;
|
||||
use crate::parser::{Parse, ParserContext};
|
||||
use crate::values::computed::{Color as ComputedColor, Context, ToComputedValue};
|
||||
use crate::values::generics::color::{GenericColorOrAuto, GenericCaretColor};
|
||||
use crate::values::generics::color::{GenericCaretColor, GenericColorOrAuto};
|
||||
use crate::values::specified::calc::CalcNode;
|
||||
use crate::values::specified::Percentage;
|
||||
use crate::values::CustomIdent;
|
||||
use cssparser::{AngleOrNumber, Color as CSSParserColor, Parser, Token, RGBA};
|
||||
use cssparser::{BasicParseErrorKind, NumberOrPercentage, ParseErrorKind};
|
||||
use itoa;
|
||||
|
@ -20,29 +21,67 @@ use std::io::Write as IoWrite;
|
|||
use style_traits::{CssType, CssWriter, KeywordsCollectFn, ParseError, StyleParseErrorKind};
|
||||
use style_traits::{SpecifiedValueInfo, ToCss, ValueParseErrorKind};
|
||||
|
||||
/// A color space as defined in [1].
|
||||
///
|
||||
/// [1]: https://drafts.csswg.org/css-color-5/#typedef-colorspace
|
||||
#[derive(Clone, Copy, Debug, Eq, MallocSizeOf, Parse, PartialEq, ToCss, ToShmem)]
|
||||
pub enum ColorSpaceKind {
|
||||
/// The sRGB color space.
|
||||
Srgb,
|
||||
/// The CIEXYZ color space.
|
||||
Xyz,
|
||||
/// The CIELAB color space.
|
||||
Lab,
|
||||
/// The CIELAB color space, expressed in cylindrical coordinates.
|
||||
Lch,
|
||||
}
|
||||
|
||||
/// A hue adjuster as defined in [1].
|
||||
///
|
||||
/// [1]: https://drafts.csswg.org/css-color-5/#typedef-hue-adjuster
|
||||
#[derive(Clone, Copy, Debug, Eq, MallocSizeOf, Parse, PartialEq, ToCss, ToShmem)]
|
||||
pub enum HueAdjuster {
|
||||
/// The "shorter" angle adjustment.
|
||||
Shorter,
|
||||
/// The "longer" angle adjustment.
|
||||
Longer,
|
||||
/// The "increasing" angle adjustment.
|
||||
Increasing,
|
||||
/// The "decreasing" angle adjustment.
|
||||
Decreasing,
|
||||
/// The "specified" angle adjustment.
|
||||
Specified,
|
||||
}
|
||||
|
||||
/// A restricted version of the css `color-mix()` function, which only supports
|
||||
/// percentages and sRGB color-space interpolation.
|
||||
/// percentages.
|
||||
///
|
||||
/// https://drafts.csswg.org/css-color-5/#color-mix
|
||||
#[derive(Clone, Debug, MallocSizeOf, PartialEq, ToShmem)]
|
||||
#[allow(missing_docs)]
|
||||
pub struct ColorMix {
|
||||
pub color_space: ColorSpaceKind,
|
||||
pub left: Color,
|
||||
pub left_percentage: Percentage,
|
||||
pub right: Color,
|
||||
pub right_percentage: Percentage,
|
||||
pub hue_adjuster: HueAdjuster,
|
||||
}
|
||||
|
||||
#[cfg(feature = "gecko")]
|
||||
#[inline]
|
||||
fn allow_color_mix() -> bool {
|
||||
static_prefs::pref!("layout.css.color-mix.enabled")
|
||||
#[cfg(feature = "gecko")]
|
||||
return static_prefs::pref!("layout.css.color-mix.enabled");
|
||||
#[cfg(feature = "servo")]
|
||||
return false;
|
||||
}
|
||||
|
||||
#[cfg(feature = "servo")]
|
||||
#[inline]
|
||||
fn allow_color_mix() -> bool {
|
||||
false
|
||||
fn allow_color_mix_color_spaces() -> bool {
|
||||
#[cfg(feature = "gecko")]
|
||||
return static_prefs::pref!("layout.css.color-mix.color-spaces.enabled");
|
||||
#[cfg(feature = "servo")]
|
||||
return false;
|
||||
}
|
||||
|
||||
// NOTE(emilio): Syntax is still a bit in-flux, since [1] doesn't seem
|
||||
|
@ -61,6 +100,9 @@ impl Parse for ColorMix {
|
|||
return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
|
||||
}
|
||||
|
||||
let color_spaces_enabled = context.chrome_rules_enabled() ||
|
||||
allow_color_mix_color_spaces();
|
||||
|
||||
input.expect_function_matching("color-mix")?;
|
||||
|
||||
// NOTE(emilio): This implements the syntax described here for now,
|
||||
|
@ -69,12 +111,18 @@ impl Parse for ColorMix {
|
|||
// https://github.com/w3c/csswg-drafts/issues/6066#issuecomment-789836765
|
||||
input.parse_nested_block(|input| {
|
||||
input.expect_ident_matching("in")?;
|
||||
// TODO: support multiple interpolation spaces.
|
||||
input.expect_ident_matching("srgb")?;
|
||||
let color_space = if color_spaces_enabled {
|
||||
ColorSpaceKind::parse(input)?
|
||||
} else {
|
||||
input.expect_ident_matching("srgb")?;
|
||||
ColorSpaceKind::Srgb
|
||||
};
|
||||
input.expect_comma()?;
|
||||
|
||||
let left = Color::parse(context, input)?;
|
||||
let left_percentage = input.try_parse(|input| Percentage::parse(context, input)).ok();
|
||||
let left_percentage = input
|
||||
.try_parse(|input| Percentage::parse(context, input))
|
||||
.ok();
|
||||
|
||||
input.expect_comma()?;
|
||||
|
||||
|
@ -87,11 +135,18 @@ impl Parse for ColorMix {
|
|||
|
||||
let left_percentage =
|
||||
left_percentage.unwrap_or_else(|| Percentage::new(1.0 - right_percentage.get()));
|
||||
|
||||
let hue_adjuster = input
|
||||
.try_parse(|input| HueAdjuster::parse(input))
|
||||
.unwrap_or(HueAdjuster::Shorter);
|
||||
|
||||
Ok(ColorMix {
|
||||
color_space,
|
||||
left,
|
||||
left_percentage,
|
||||
right,
|
||||
right_percentage,
|
||||
hue_adjuster,
|
||||
})
|
||||
})
|
||||
}
|
||||
|
@ -115,7 +170,9 @@ impl ToCss for ColorMix {
|
|||
(1.0 - percent.get() - other.get()).abs() <= f32::EPSILON
|
||||
}
|
||||
|
||||
dest.write_str("color-mix(in srgb, ")?;
|
||||
dest.write_str("color-mix(in ")?;
|
||||
self.color_space.to_css(dest)?;
|
||||
dest.write_str(", ")?;
|
||||
self.left.to_css(dest)?;
|
||||
if !can_omit(&self.left_percentage, &self.right_percentage, true) {
|
||||
dest.write_str(" ")?;
|
||||
|
@ -127,10 +184,30 @@ impl ToCss for ColorMix {
|
|||
dest.write_str(" ")?;
|
||||
self.right_percentage.to_css(dest)?;
|
||||
}
|
||||
|
||||
if self.hue_adjuster != HueAdjuster::Shorter {
|
||||
dest.write_str(" ")?;
|
||||
self.hue_adjuster.to_css(dest)?;
|
||||
}
|
||||
|
||||
dest.write_str(")")
|
||||
}
|
||||
}
|
||||
|
||||
/// The color scheme for a specific system color.
|
||||
#[cfg(feature = "gecko")]
|
||||
#[derive(Clone, Copy, Debug, MallocSizeOf, Parse, PartialEq, ToCss, ToShmem)]
|
||||
#[repr(u8)]
|
||||
pub enum SystemColorScheme {
|
||||
/// The default color-scheme for the document.
|
||||
#[css(skip)]
|
||||
Default,
|
||||
/// A light color scheme.
|
||||
Light,
|
||||
/// A dark color scheme.
|
||||
Dark,
|
||||
}
|
||||
|
||||
/// Specified color value
|
||||
#[derive(Clone, Debug, MallocSizeOf, PartialEq, ToShmem)]
|
||||
pub enum Color {
|
||||
|
@ -145,9 +222,10 @@ pub enum Color {
|
|||
},
|
||||
/// A complex color value from computed value
|
||||
Complex(ComputedColor),
|
||||
/// A system color
|
||||
/// Either a system color, or a `-moz-system-color(<system-color>, light|dark)`
|
||||
/// function which allows chrome code to choose between color schemes.
|
||||
#[cfg(feature = "gecko")]
|
||||
System(SystemColor),
|
||||
System(SystemColor, SystemColorScheme),
|
||||
/// A color mix.
|
||||
ColorMix(Box<ColorMix>),
|
||||
/// Quirksmode-only rule for inheriting color from the body
|
||||
|
@ -181,13 +259,13 @@ pub enum SystemColor {
|
|||
TextBackground,
|
||||
#[css(skip)]
|
||||
TextForeground,
|
||||
#[css(skip)]
|
||||
#[parse(condition = "ParserContext::in_ua_or_chrome_sheet")]
|
||||
TextSelectBackground,
|
||||
#[css(skip)]
|
||||
#[parse(condition = "ParserContext::in_ua_or_chrome_sheet")]
|
||||
TextSelectForeground,
|
||||
#[css(skip)]
|
||||
#[parse(condition = "ParserContext::in_ua_or_chrome_sheet")]
|
||||
TextSelectBackgroundDisabled,
|
||||
#[css(skip)]
|
||||
#[parse(condition = "ParserContext::in_ua_or_chrome_sheet")]
|
||||
TextSelectBackgroundAttention,
|
||||
#[css(skip)]
|
||||
TextHighlightBackground,
|
||||
|
@ -358,6 +436,10 @@ pub enum SystemColor {
|
|||
/// colors.
|
||||
MozNativehyperlinktext,
|
||||
|
||||
/// As above, but visited link color.
|
||||
#[css(skip)]
|
||||
MozNativevisitedhyperlinktext,
|
||||
|
||||
#[parse(aliases = "-moz-hyperlinktext")]
|
||||
Linktext,
|
||||
#[parse(aliases = "-moz-activehyperlinktext")]
|
||||
|
@ -369,14 +451,20 @@ pub enum SystemColor {
|
|||
MozComboboxtext,
|
||||
MozCombobox,
|
||||
|
||||
MozGtkInfoBarText,
|
||||
|
||||
/// Color of tree column headers
|
||||
#[parse(condition = "ParserContext::in_ua_or_chrome_sheet")]
|
||||
MozColheadertext,
|
||||
#[parse(condition = "ParserContext::in_ua_or_chrome_sheet")]
|
||||
MozColheaderhovertext,
|
||||
|
||||
/// Color of text in the (active) titlebar.
|
||||
#[parse(condition = "ParserContext::in_ua_or_chrome_sheet")]
|
||||
MozGtkTitlebarText,
|
||||
|
||||
/// Color of text in the (inactive) titlebar.
|
||||
#[parse(condition = "ParserContext::in_ua_or_chrome_sheet")]
|
||||
MozGtkTitlebarInactiveText,
|
||||
|
||||
#[css(skip)]
|
||||
End, // Just for array-indexing purposes.
|
||||
}
|
||||
|
@ -384,20 +472,29 @@ pub enum SystemColor {
|
|||
#[cfg(feature = "gecko")]
|
||||
impl SystemColor {
|
||||
#[inline]
|
||||
fn compute(&self, cx: &Context) -> ComputedColor {
|
||||
fn compute(&self, cx: &Context, scheme: SystemColorScheme) -> ComputedColor {
|
||||
use crate::gecko_bindings::bindings;
|
||||
|
||||
let prefs = cx.device().pref_sheet_prefs();
|
||||
let colors = &cx.device().pref_sheet_prefs().mColors;
|
||||
let style_color_scheme = cx.style().get_inherited_ui().clone_color_scheme();
|
||||
|
||||
// TODO: At least Canvas / CanvasText should be color-scheme aware
|
||||
// (probably the link colors too).
|
||||
convert_nscolor_to_computedcolor(match *self {
|
||||
SystemColor::Canvastext => prefs.mDefaultColor,
|
||||
SystemColor::Canvas => prefs.mDefaultBackgroundColor,
|
||||
SystemColor::Linktext => prefs.mLinkColor,
|
||||
SystemColor::Activetext => prefs.mActiveLinkColor,
|
||||
SystemColor::Visitedtext => prefs.mVisitedLinkColor,
|
||||
SystemColor::Canvastext => colors.mDefault,
|
||||
SystemColor::Canvas => colors.mDefaultBackground,
|
||||
SystemColor::Linktext => colors.mLink,
|
||||
SystemColor::Activetext => colors.mActiveLink,
|
||||
SystemColor::Visitedtext => colors.mVisitedLink,
|
||||
|
||||
_ => unsafe {
|
||||
bindings::Gecko_GetLookAndFeelSystemColor(*self as i32, cx.device().document())
|
||||
_ => {
|
||||
let color = unsafe {
|
||||
bindings::Gecko_GetLookAndFeelSystemColor(*self as i32, cx.device().document(), scheme, &style_color_scheme)
|
||||
};
|
||||
if color == bindings::NS_SAME_AS_FOREGROUND_COLOR {
|
||||
return ComputedColor::currentcolor();
|
||||
}
|
||||
color
|
||||
},
|
||||
})
|
||||
}
|
||||
|
@ -473,6 +570,21 @@ impl<'a, 'b: 'a, 'i: 'a> ::cssparser::ColorComponentParser<'i> for ColorComponen
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "gecko")]
|
||||
fn parse_moz_system_color<'i, 't>(
|
||||
context: &ParserContext,
|
||||
input: &mut Parser<'i, 't>,
|
||||
) -> Result<(SystemColor, SystemColorScheme), ParseError<'i>> {
|
||||
debug_assert!(context.chrome_rules_enabled());
|
||||
input.expect_function_matching("-moz-system-color")?;
|
||||
input.parse_nested_block(|input| {
|
||||
let color = SystemColor::parse(context, input)?;
|
||||
input.expect_comma()?;
|
||||
let scheme = SystemColorScheme::parse(input)?;
|
||||
Ok((color, scheme))
|
||||
})
|
||||
}
|
||||
|
||||
impl Parse for Color {
|
||||
fn parse<'i, 't>(
|
||||
context: &ParserContext,
|
||||
|
@ -498,7 +610,15 @@ impl Parse for Color {
|
|||
#[cfg(feature = "gecko")]
|
||||
{
|
||||
if let Ok(system) = input.try_parse(|i| SystemColor::parse(context, i)) {
|
||||
return Ok(Color::System(system));
|
||||
return Ok(Color::System(system, SystemColorScheme::Default));
|
||||
}
|
||||
|
||||
if context.chrome_rules_enabled() {
|
||||
if let Ok((color, scheme)) =
|
||||
input.try_parse(|i| parse_moz_system_color(context, i))
|
||||
{
|
||||
return Ok(Color::System(color, scheme));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -537,7 +657,17 @@ impl ToCss for Color {
|
|||
Color::Complex(_) => Ok(()),
|
||||
Color::ColorMix(ref mix) => mix.to_css(dest),
|
||||
#[cfg(feature = "gecko")]
|
||||
Color::System(system) => system.to_css(dest),
|
||||
Color::System(system, scheme) => {
|
||||
if scheme == SystemColorScheme::Default {
|
||||
system.to_css(dest)
|
||||
} else {
|
||||
dest.write_str("-moz-system-color(")?;
|
||||
system.to_css(dest)?;
|
||||
dest.write_str(", ")?;
|
||||
scheme.to_css(dest)?;
|
||||
dest.write_char(')')
|
||||
}
|
||||
},
|
||||
#[cfg(feature = "gecko")]
|
||||
Color::InheritFromBodyQuirk => Ok(()),
|
||||
}
|
||||
|
@ -696,14 +826,16 @@ impl Color {
|
|||
let left = mix.left.to_computed_color(context)?.to_animated_value();
|
||||
let right = mix.right.to_computed_color(context)?.to_animated_value();
|
||||
ToAnimatedValue::from_animated_value(AnimatedColor::mix(
|
||||
mix.color_space,
|
||||
&left,
|
||||
mix.left_percentage.get(),
|
||||
&right,
|
||||
mix.right_percentage.get(),
|
||||
mix.hue_adjuster,
|
||||
))
|
||||
},
|
||||
#[cfg(feature = "gecko")]
|
||||
Color::System(system) => system.compute(context?),
|
||||
Color::System(system, scheme) => system.compute(context?, scheme),
|
||||
#[cfg(feature = "gecko")]
|
||||
Color::InheritFromBodyQuirk => ComputedColor::rgba(context?.device().body_text_color()),
|
||||
})
|
||||
|
@ -818,3 +950,123 @@ impl Parse for CaretColor {
|
|||
ColorOrAuto::parse(context, input).map(GenericCaretColor)
|
||||
}
|
||||
}
|
||||
|
||||
bitflags! {
|
||||
/// Various flags to represent the color-scheme property in an efficient
|
||||
/// way.
|
||||
#[derive(Default, MallocSizeOf, SpecifiedValueInfo, ToComputedValue, ToResolvedValue, ToShmem)]
|
||||
#[repr(C)]
|
||||
#[value_info(other_values = "light,dark,only")]
|
||||
pub struct ColorSchemeFlags: u8 {
|
||||
/// Whether the author specified `light`.
|
||||
const LIGHT = 1 << 0;
|
||||
/// Whether the author specified `dark`.
|
||||
const DARK = 1 << 1;
|
||||
/// Whether the author specified `only`.
|
||||
const ONLY = 1 << 2;
|
||||
}
|
||||
}
|
||||
|
||||
/// <https://drafts.csswg.org/css-color-adjust/#color-scheme-prop>
|
||||
#[derive(
|
||||
Clone,
|
||||
Debug,
|
||||
Default,
|
||||
MallocSizeOf,
|
||||
PartialEq,
|
||||
SpecifiedValueInfo,
|
||||
ToComputedValue,
|
||||
ToResolvedValue,
|
||||
ToShmem,
|
||||
)]
|
||||
#[repr(C)]
|
||||
#[value_info(other_values = "normal")]
|
||||
pub struct ColorScheme {
|
||||
#[ignore_malloc_size_of = "Arc"]
|
||||
idents: crate::ArcSlice<CustomIdent>,
|
||||
bits: ColorSchemeFlags,
|
||||
}
|
||||
|
||||
impl ColorScheme {
|
||||
/// Returns the `normal` value.
|
||||
pub fn normal() -> Self {
|
||||
Self {
|
||||
idents: Default::default(),
|
||||
bits: ColorSchemeFlags::empty(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Parse for ColorScheme {
|
||||
fn parse<'i, 't>(_: &ParserContext, input: &mut Parser<'i, 't>) -> Result<Self, ParseError<'i>> {
|
||||
let mut idents = vec![];
|
||||
let mut bits = ColorSchemeFlags::empty();
|
||||
|
||||
let mut location = input.current_source_location();
|
||||
while let Ok(ident) = input.try_parse(|i| i.expect_ident_cloned()) {
|
||||
let mut is_only = false;
|
||||
match_ignore_ascii_case! { &ident,
|
||||
"normal" => {
|
||||
if idents.is_empty() && bits.is_empty() {
|
||||
return Ok(Self::normal());
|
||||
}
|
||||
return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
|
||||
},
|
||||
"light" => bits.insert(ColorSchemeFlags::LIGHT),
|
||||
"dark" => bits.insert(ColorSchemeFlags::DARK),
|
||||
"only" => {
|
||||
if bits.intersects(ColorSchemeFlags::ONLY) {
|
||||
return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
|
||||
}
|
||||
bits.insert(ColorSchemeFlags::ONLY);
|
||||
is_only = true;
|
||||
},
|
||||
_ => {},
|
||||
};
|
||||
|
||||
if is_only {
|
||||
if !idents.is_empty() {
|
||||
// Only is allowed either at the beginning or at the end,
|
||||
// but not in the middle.
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
idents.push(CustomIdent::from_ident(location, &ident, &[])?);
|
||||
}
|
||||
location = input.current_source_location();
|
||||
}
|
||||
|
||||
if idents.is_empty() {
|
||||
return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
|
||||
}
|
||||
|
||||
Ok(Self {
|
||||
idents: crate::ArcSlice::from_iter(idents.into_iter()),
|
||||
bits,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl ToCss for ColorScheme {
|
||||
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
|
||||
where
|
||||
W: Write,
|
||||
{
|
||||
if self.idents.is_empty() {
|
||||
debug_assert!(self.bits.is_empty());
|
||||
return dest.write_str("normal");
|
||||
}
|
||||
let mut first = true;
|
||||
for ident in self.idents.iter() {
|
||||
if !first {
|
||||
dest.write_char(' ')?;
|
||||
}
|
||||
first = false;
|
||||
ident.to_css(dest)?;
|
||||
}
|
||||
if self.bits.intersects(ColorSchemeFlags::ONLY) {
|
||||
dest.write_str(" only")?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,15 +6,14 @@
|
|||
|
||||
#[cfg(feature = "gecko")]
|
||||
use crate::context::QuirksMode;
|
||||
#[cfg(feature = "gecko")]
|
||||
use crate::gecko_bindings::bindings;
|
||||
use crate::parser::{Parse, ParserContext};
|
||||
use crate::values::computed::font::{FamilyName, FontFamilyList, FontStyleAngle, SingleFontFamily};
|
||||
use crate::values::computed::{font as computed, Length, NonNegativeLength};
|
||||
use crate::values::computed::{Angle as ComputedAngle, Percentage as ComputedPercentage};
|
||||
use crate::values::computed::{CSSPixelLength, Context, ToComputedValue};
|
||||
use crate::values::computed::FontSizeAdjust as ComputedFontSizeAdjust;
|
||||
use crate::values::generics::font::VariationValue;
|
||||
use crate::values::generics::font::{self as generics, FeatureTagValue, FontSettings, FontTag};
|
||||
use crate::values::generics::font::{self as generics, FeatureTagValue, FontSettings, FontTag, GenericFontSizeAdjust};
|
||||
use crate::values::generics::NonNegative;
|
||||
use crate::values::specified::length::{FontBaseSize, PX_PER_PT};
|
||||
use crate::values::specified::{AllowQuirks, Angle, Integer, LengthPercentage};
|
||||
|
@ -23,7 +22,7 @@ use crate::values::CustomIdent;
|
|||
use crate::Atom;
|
||||
use cssparser::{Parser, Token};
|
||||
#[cfg(feature = "gecko")]
|
||||
use malloc_size_of::{MallocSizeOf, MallocSizeOfOps};
|
||||
use malloc_size_of::{MallocSizeOf, MallocSizeOfOps, MallocUnconditionalSizeOf};
|
||||
use std::fmt::{self, Write};
|
||||
use style_traits::values::SequenceWriter;
|
||||
use style_traits::{CssWriter, KeywordsCollectFn, ParseError};
|
||||
|
@ -682,14 +681,6 @@ pub enum FontFamily {
|
|||
|
||||
impl FontFamily {
|
||||
system_font_methods!(FontFamily, font_family);
|
||||
|
||||
/// Parse a specified font-family value
|
||||
pub fn parse_specified<'i, 't>(input: &mut Parser<'i, 't>) -> Result<Self, ParseError<'i>> {
|
||||
let values = input.parse_comma_separated(SingleFontFamily::parse)?;
|
||||
Ok(FontFamily::Values(FontFamilyList::new(
|
||||
values.into_boxed_slice(),
|
||||
)))
|
||||
}
|
||||
}
|
||||
|
||||
impl ToComputedValue for FontFamily {
|
||||
|
@ -697,8 +688,8 @@ impl ToComputedValue for FontFamily {
|
|||
|
||||
fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
|
||||
match *self {
|
||||
FontFamily::Values(ref v) => computed::FontFamily {
|
||||
families: v.clone(),
|
||||
FontFamily::Values(ref list) => computed::FontFamily {
|
||||
families: list.clone(),
|
||||
is_system_font: false,
|
||||
},
|
||||
FontFamily::System(_) => self.compute_system(context),
|
||||
|
@ -712,18 +703,12 @@ impl ToComputedValue for FontFamily {
|
|||
|
||||
#[cfg(feature = "gecko")]
|
||||
impl MallocSizeOf for FontFamily {
|
||||
fn size_of(&self, _ops: &mut MallocSizeOfOps) -> usize {
|
||||
fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize {
|
||||
match *self {
|
||||
FontFamily::Values(ref v) => {
|
||||
// Although a SharedFontList object is refcounted, we always
|
||||
// attribute its size to the specified value, as long as it's
|
||||
// not a value in SharedFontList::sSingleGenerics.
|
||||
if matches!(v, FontFamilyList::SharedFontList(_)) {
|
||||
let ptr = v.shared_font_list().get();
|
||||
unsafe { bindings::Gecko_SharedFontList_SizeOfIncludingThis(ptr) }
|
||||
} else {
|
||||
0
|
||||
}
|
||||
// Although the family list is refcounted, we always attribute
|
||||
// its size to the specified value.
|
||||
v.list.unconditional_size_of(ops)
|
||||
},
|
||||
FontFamily::System(_) => 0,
|
||||
}
|
||||
|
@ -735,23 +720,31 @@ impl Parse for FontFamily {
|
|||
/// <family-name> = <string> | [ <ident>+ ]
|
||||
/// TODO: <generic-family>
|
||||
fn parse<'i, 't>(
|
||||
_: &ParserContext,
|
||||
context: &ParserContext,
|
||||
input: &mut Parser<'i, 't>,
|
||||
) -> Result<FontFamily, ParseError<'i>> {
|
||||
FontFamily::parse_specified(input)
|
||||
let values = input.parse_comma_separated(|input| SingleFontFamily::parse(context, input))?;
|
||||
Ok(FontFamily::Values(FontFamilyList {
|
||||
#[cfg(feature = "gecko")]
|
||||
list: crate::ArcSlice::from_iter(values.into_iter()),
|
||||
#[cfg(feature = "servo")]
|
||||
list: values.into_boxed_slice(),
|
||||
#[cfg(feature = "gecko")]
|
||||
fallback: computed::GenericFontFamily::None,
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
impl SpecifiedValueInfo for FontFamily {}
|
||||
|
||||
/// `FamilyName::parse` is based on `SingleFontFamily::parse` and not the other way around
|
||||
/// because we want the former to exclude generic family keywords.
|
||||
/// `FamilyName::parse` is based on `SingleFontFamily::parse` and not the other
|
||||
/// way around because we want the former to exclude generic family keywords.
|
||||
impl Parse for FamilyName {
|
||||
fn parse<'i, 't>(
|
||||
_: &ParserContext,
|
||||
context: &ParserContext,
|
||||
input: &mut Parser<'i, 't>,
|
||||
) -> Result<Self, ParseError<'i>> {
|
||||
match SingleFontFamily::parse(input) {
|
||||
match SingleFontFamily::parse(context, input) {
|
||||
Ok(SingleFontFamily::FamilyName(name)) => Ok(name),
|
||||
Ok(SingleFontFamily::Generic(_)) => {
|
||||
Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
|
||||
|
@ -761,16 +754,13 @@ impl Parse for FamilyName {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(
|
||||
Clone, Copy, Debug, MallocSizeOf, Parse, PartialEq, SpecifiedValueInfo, ToCss, ToShmem,
|
||||
)]
|
||||
/// Preserve the readability of text when font fallback occurs
|
||||
#[derive(
|
||||
Clone, Copy, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToCss, ToShmem,
|
||||
)]
|
||||
#[allow(missing_docs)]
|
||||
pub enum FontSizeAdjust {
|
||||
/// None variant
|
||||
None,
|
||||
/// Number variant
|
||||
Number(NonNegativeNumber),
|
||||
/// system font
|
||||
Value(GenericFontSizeAdjust<NonNegativeNumber>),
|
||||
#[css(skip)]
|
||||
System(SystemFont),
|
||||
}
|
||||
|
@ -779,34 +769,57 @@ impl FontSizeAdjust {
|
|||
#[inline]
|
||||
/// Default value of font-size-adjust
|
||||
pub fn none() -> Self {
|
||||
FontSizeAdjust::None
|
||||
FontSizeAdjust::Value(GenericFontSizeAdjust::None)
|
||||
}
|
||||
|
||||
system_font_methods!(FontSizeAdjust, font_size_adjust);
|
||||
}
|
||||
|
||||
impl Parse for FontSizeAdjust {
|
||||
fn parse<'i, 't>(
|
||||
context: &ParserContext,
|
||||
input: &mut Parser<'i, 't>,
|
||||
) -> Result<Self, ParseError<'i>> {
|
||||
let location = input.current_source_location();
|
||||
if let Ok(ident) = input.try_parse(|i| i.expect_ident_cloned()) {
|
||||
#[cfg(feature = "gecko")]
|
||||
let basis_enabled = static_prefs::pref!("layout.css.font-size-adjust.basis.enabled");
|
||||
#[cfg(feature = "servo")]
|
||||
let basis_enabled = false;
|
||||
let basis = match_ignore_ascii_case! { &ident,
|
||||
"none" => return Ok(FontSizeAdjust::none()),
|
||||
// Check for size adjustment basis keywords if enabled.
|
||||
"ex-height" if basis_enabled => GenericFontSizeAdjust::ExHeight,
|
||||
"cap-height" if basis_enabled => GenericFontSizeAdjust::CapHeight,
|
||||
"ch-width" if basis_enabled => GenericFontSizeAdjust::ChWidth,
|
||||
"ic-width" if basis_enabled => GenericFontSizeAdjust::IcWidth,
|
||||
"ic-height" if basis_enabled => GenericFontSizeAdjust::IcHeight,
|
||||
// Unknown (or disabled) keyword.
|
||||
_ => return Err(location.new_custom_error(
|
||||
::selectors::parser::SelectorParseErrorKind::UnexpectedIdent(ident)
|
||||
)),
|
||||
};
|
||||
let value = NonNegativeNumber::parse(context, input)?;
|
||||
return Ok(FontSizeAdjust::Value(basis(value)));
|
||||
}
|
||||
// Without a basis keyword, the number refers to the 'ex-height' metric.
|
||||
let value = NonNegativeNumber::parse(context, input)?;
|
||||
Ok(FontSizeAdjust::Value(GenericFontSizeAdjust::ExHeight(value)))
|
||||
}
|
||||
}
|
||||
|
||||
impl ToComputedValue for FontSizeAdjust {
|
||||
type ComputedValue = computed::FontSizeAdjust;
|
||||
type ComputedValue = ComputedFontSizeAdjust;
|
||||
|
||||
fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
|
||||
match *self {
|
||||
FontSizeAdjust::None => computed::FontSizeAdjust::None,
|
||||
FontSizeAdjust::Number(ref n) => {
|
||||
// The computed version handles clamping of animated values
|
||||
// itself.
|
||||
computed::FontSizeAdjust::Number(n.to_computed_value(context).0)
|
||||
},
|
||||
FontSizeAdjust::Value(v) => v.to_computed_value(context),
|
||||
FontSizeAdjust::System(_) => self.compute_system(context),
|
||||
}
|
||||
}
|
||||
|
||||
fn from_computed_value(computed: &computed::FontSizeAdjust) -> Self {
|
||||
match *computed {
|
||||
computed::FontSizeAdjust::None => FontSizeAdjust::None,
|
||||
computed::FontSizeAdjust::Number(v) => {
|
||||
FontSizeAdjust::Number(NonNegativeNumber::from_computed_value(&v.into()))
|
||||
},
|
||||
}
|
||||
fn from_computed_value(computed: &ComputedFontSizeAdjust) -> Self {
|
||||
Self::Value(ToComputedValue::from_computed_value(computed))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1977,23 +1990,24 @@ impl Parse for FontFeatureSettings {
|
|||
Debug,
|
||||
MallocSizeOf,
|
||||
PartialEq,
|
||||
SpecifiedValueInfo,
|
||||
ToComputedValue,
|
||||
ToResolvedValue,
|
||||
ToShmem,
|
||||
)]
|
||||
/// Whether user agents are allowed to synthesize bold or oblique font faces
|
||||
/// when a font family lacks bold or italic faces
|
||||
/// when a font family lacks those faces, or a small-caps variant when this is
|
||||
/// not supported by the face.
|
||||
pub struct FontSynthesis {
|
||||
/// If a `font-weight` is requested that the font family does not contain,
|
||||
/// the user agent may synthesize the requested weight from the weights
|
||||
/// that do exist in the font family.
|
||||
#[css(represents_keyword)]
|
||||
pub weight: bool,
|
||||
/// If a font-style is requested that the font family does not contain,
|
||||
/// the user agent may synthesize the requested style from the normal face in the font family.
|
||||
#[css(represents_keyword)]
|
||||
pub style: bool,
|
||||
/// This bit controls whether the user agent is allowed to synthesize small caps variant
|
||||
/// when a font face lacks it.
|
||||
pub small_caps: bool,
|
||||
}
|
||||
|
||||
impl FontSynthesis {
|
||||
|
@ -2003,8 +2017,31 @@ impl FontSynthesis {
|
|||
FontSynthesis {
|
||||
weight: true,
|
||||
style: true,
|
||||
small_caps: true,
|
||||
}
|
||||
}
|
||||
#[inline]
|
||||
/// Get the 'none' value of font-synthesis
|
||||
pub fn none() -> Self {
|
||||
FontSynthesis {
|
||||
weight: false,
|
||||
style: false,
|
||||
small_caps: false,
|
||||
}
|
||||
}
|
||||
#[inline]
|
||||
/// Return true if this is the 'none' value
|
||||
pub fn is_none(&self) -> bool {
|
||||
*self == Self::none()
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn allow_font_synthesis_small_caps() -> bool {
|
||||
#[cfg(feature = "gecko")]
|
||||
return static_prefs::pref!("layout.css.font-synthesis-small-caps.enabled");
|
||||
#[cfg(feature = "servo")]
|
||||
return false;
|
||||
}
|
||||
|
||||
impl Parse for FontSynthesis {
|
||||
|
@ -2012,26 +2049,22 @@ impl Parse for FontSynthesis {
|
|||
_: &ParserContext,
|
||||
input: &mut Parser<'i, 't>,
|
||||
) -> Result<FontSynthesis, ParseError<'i>> {
|
||||
let mut result = FontSynthesis {
|
||||
weight: false,
|
||||
style: false,
|
||||
};
|
||||
try_match_ident_ignore_ascii_case! { input,
|
||||
"none" => Ok(result),
|
||||
"weight" => {
|
||||
result.weight = true;
|
||||
if input.try_parse(|input| input.expect_ident_matching("style")).is_ok() {
|
||||
result.style = true;
|
||||
}
|
||||
Ok(result)
|
||||
},
|
||||
"style" => {
|
||||
result.style = true;
|
||||
if input.try_parse(|input| input.expect_ident_matching("weight")).is_ok() {
|
||||
result.weight = true;
|
||||
}
|
||||
Ok(result)
|
||||
},
|
||||
use crate::values::SelectorParseErrorKind;
|
||||
let mut result = Self::none();
|
||||
while let Ok(ident) = input.try_parse(|i| i.expect_ident_cloned()) {
|
||||
match_ignore_ascii_case! { &ident,
|
||||
"none" if result.is_none() => return Ok(result),
|
||||
"weight" if !result.weight => result.weight = true,
|
||||
"style" if !result.style => result.style = true,
|
||||
"small-caps" if !result.small_caps && allow_font_synthesis_small_caps()
|
||||
=> result.small_caps = true,
|
||||
_ => return Err(input.new_custom_error(SelectorParseErrorKind::UnexpectedIdent(ident))),
|
||||
}
|
||||
}
|
||||
if !result.is_none() {
|
||||
Ok(result)
|
||||
} else {
|
||||
Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2041,14 +2074,41 @@ impl ToCss for FontSynthesis {
|
|||
where
|
||||
W: Write,
|
||||
{
|
||||
if self.weight && self.style {
|
||||
dest.write_str("weight style")
|
||||
} else if self.style {
|
||||
dest.write_str("style")
|
||||
} else if self.weight {
|
||||
dest.write_str("weight")
|
||||
} else {
|
||||
dest.write_str("none")
|
||||
if self.is_none() {
|
||||
return dest.write_str("none");
|
||||
}
|
||||
|
||||
let mut need_space = false;
|
||||
if self.weight {
|
||||
dest.write_str("weight")?;
|
||||
need_space = true;
|
||||
}
|
||||
if self.style {
|
||||
if need_space {
|
||||
dest.write_str(" ")?;
|
||||
}
|
||||
dest.write_str("style")?;
|
||||
need_space = true;
|
||||
}
|
||||
if self.small_caps {
|
||||
if need_space {
|
||||
dest.write_str(" ")?;
|
||||
}
|
||||
dest.write_str("small-caps")?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl SpecifiedValueInfo for FontSynthesis {
|
||||
fn collect_completion_keywords(f: KeywordsCollectFn) {
|
||||
f(&[
|
||||
"none",
|
||||
"weight",
|
||||
"style",
|
||||
]);
|
||||
if allow_font_synthesis_small_caps() {
|
||||
f(&["small-caps"]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2061,6 +2121,7 @@ impl From<u8> for FontSynthesis {
|
|||
FontSynthesis {
|
||||
weight: bits & structs::NS_FONT_SYNTHESIS_WEIGHT as u8 != 0,
|
||||
style: bits & structs::NS_FONT_SYNTHESIS_STYLE as u8 != 0,
|
||||
small_caps: bits & structs::NS_FONT_SYNTHESIS_SMALL_CAPS as u8 != 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2077,6 +2138,9 @@ impl From<FontSynthesis> for u8 {
|
|||
if v.style {
|
||||
bits |= structs::NS_FONT_SYNTHESIS_STYLE as u8;
|
||||
}
|
||||
if v.small_caps {
|
||||
bits |= structs::NS_FONT_SYNTHESIS_SMALL_CAPS as u8;
|
||||
}
|
||||
bits
|
||||
}
|
||||
}
|
||||
|
|
|
@ -83,7 +83,7 @@ fn cross_fade_enabled() -> bool {
|
|||
|
||||
#[cfg(feature = "gecko")]
|
||||
fn image_set_enabled() -> bool {
|
||||
static_prefs::pref!("layout.css.image-set.enabled")
|
||||
true
|
||||
}
|
||||
|
||||
#[cfg(feature = "servo")]
|
||||
|
@ -365,7 +365,14 @@ impl ImageSet {
|
|||
cors_mode: CorsMode,
|
||||
only_url: bool,
|
||||
) -> Result<Self, ParseError<'i>> {
|
||||
input.expect_function_matching("image-set")?;
|
||||
let function = input.expect_function()?;
|
||||
match_ignore_ascii_case! { &function,
|
||||
"-webkit-image-set" | "image-set" => {},
|
||||
_ => {
|
||||
let func = function.clone();
|
||||
return Err(input.new_custom_error(StyleParseErrorKind::UnexpectedFunction(func)));
|
||||
}
|
||||
}
|
||||
let items = input.parse_nested_block(|input| {
|
||||
input.parse_comma_separated(|input| ImageSetItem::parse(context, input, cors_mode, only_url))
|
||||
})?;
|
||||
|
|
|
@ -1247,6 +1247,27 @@ macro_rules! parse_size_non_length {
|
|||
}};
|
||||
}
|
||||
|
||||
#[cfg(feature = "gecko")]
|
||||
fn is_fit_content_function_enabled() -> bool {
|
||||
static_prefs::pref!("layout.css.fit-content-function.enabled")
|
||||
}
|
||||
|
||||
#[cfg(feature = "gecko")]
|
||||
macro_rules! parse_fit_content_function {
|
||||
($size:ident, $input:expr, $context:expr, $allow_quirks:expr) => {
|
||||
if is_fit_content_function_enabled() {
|
||||
if let Ok(length) = $input.try_parse(|input| {
|
||||
input.expect_function_matching("fit-content")?;
|
||||
input.parse_nested_block(|i| {
|
||||
NonNegativeLengthPercentage::parse_quirky($context, i, $allow_quirks)
|
||||
})
|
||||
}) {
|
||||
return Ok($size::FitContentFunction(length));
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
impl Size {
|
||||
/// Parses, with quirks.
|
||||
pub fn parse_quirky<'i, 't>(
|
||||
|
@ -1255,6 +1276,8 @@ impl Size {
|
|||
allow_quirks: AllowQuirks,
|
||||
) -> Result<Self, ParseError<'i>> {
|
||||
parse_size_non_length!(Size, input, "auto" => Auto);
|
||||
#[cfg(feature = "gecko")]
|
||||
parse_fit_content_function!(Size, input, context, allow_quirks);
|
||||
|
||||
let length = NonNegativeLengthPercentage::parse_quirky(context, input, allow_quirks)?;
|
||||
Ok(GenericSize::LengthPercentage(length))
|
||||
|
@ -1287,6 +1310,8 @@ impl MaxSize {
|
|||
allow_quirks: AllowQuirks,
|
||||
) -> Result<Self, ParseError<'i>> {
|
||||
parse_size_non_length!(MaxSize, input, "none" => None);
|
||||
#[cfg(feature = "gecko")]
|
||||
parse_fit_content_function!(MaxSize, input, context, allow_quirks);
|
||||
|
||||
let length = NonNegativeLengthPercentage::parse_quirky(context, input, allow_quirks)?;
|
||||
Ok(GenericMaxSize::LengthPercentage(length))
|
||||
|
|
|
@ -67,6 +67,15 @@ impl ListStyleType {
|
|||
_ => unreachable!("Unknown counter style keyword value"),
|
||||
})))
|
||||
}
|
||||
|
||||
/// Is this a bullet? (i.e. `list-style-type: disc|circle|square|disclosure-closed|disclosure-open`)
|
||||
#[inline]
|
||||
pub fn is_bullet(&self) -> bool {
|
||||
match self {
|
||||
ListStyleType::CounterStyle(ref style) => style.is_bullet(),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "gecko")]
|
||||
|
|
|
@ -42,7 +42,7 @@ pub use self::box_::{Clear, Float, Overflow, OverflowAnchor};
|
|||
pub use self::box_::{OverflowClipBox, OverscrollBehavior, Perspective, Resize};
|
||||
pub use self::box_::{ScrollSnapAlign, ScrollSnapAxis, ScrollSnapStrictness, ScrollSnapType};
|
||||
pub use self::box_::{TouchAction, TransitionProperty, VerticalAlign, WillChange};
|
||||
pub use self::color::{Color, ColorOrAuto, ColorPropertyValue};
|
||||
pub use self::color::{Color, ColorOrAuto, ColorPropertyValue, ColorScheme};
|
||||
pub use self::column::ColumnCount;
|
||||
pub use self::counters::{Content, ContentItem, CounterIncrement, CounterSetOrReset};
|
||||
pub use self::easing::TimingFunction;
|
||||
|
@ -80,7 +80,7 @@ pub use self::position::{PositionComponent, ZIndex};
|
|||
pub use self::ratio::Ratio;
|
||||
pub use self::rect::NonNegativeLengthOrNumberRect;
|
||||
pub use self::resolution::Resolution;
|
||||
pub use self::svg::MozContextProperties;
|
||||
pub use self::svg::{DProperty, MozContextProperties};
|
||||
pub use self::svg::{SVGLength, SVGOpacity, SVGPaint};
|
||||
pub use self::svg::{SVGPaintOrder, SVGStrokeDashArray, SVGWidth};
|
||||
pub use self::svg_path::SVGPathData;
|
||||
|
|
|
@ -23,15 +23,15 @@ impl Parse for PageSize {
|
|||
) -> Result<Self, ParseError<'i>> {
|
||||
// Try to parse as <page-size> [ <orientation> ]
|
||||
if let Ok(paper_size) = input.try_parse(PaperSize::parse) {
|
||||
if let Ok(orientation) = input.try_parse(Orientation::parse) {
|
||||
return Ok(PageSize::PaperSizeAndOrientation(paper_size, orientation));
|
||||
}
|
||||
return Ok(PageSize::PaperSize(paper_size));
|
||||
let orientation = input
|
||||
.try_parse(Orientation::parse)
|
||||
.unwrap_or(Orientation::Portrait);
|
||||
return Ok(PageSize::PaperSize(paper_size, orientation));
|
||||
}
|
||||
// Try to parse as <orientation> [ <page-size> ]
|
||||
if let Ok(orientation) = input.try_parse(Orientation::parse) {
|
||||
if let Ok(paper_size) = input.try_parse(PaperSize::parse) {
|
||||
return Ok(PageSize::PaperSizeAndOrientation(paper_size, orientation));
|
||||
return Ok(PageSize::PaperSize(paper_size, orientation));
|
||||
}
|
||||
return Ok(PageSize::Orientation(orientation));
|
||||
}
|
||||
|
|
|
@ -10,6 +10,7 @@ use crate::values::specified::color::Color;
|
|||
use crate::values::specified::url::SpecifiedUrl;
|
||||
use crate::values::specified::AllowQuirks;
|
||||
use crate::values::specified::LengthPercentage;
|
||||
use crate::values::specified::SVGPathData;
|
||||
use crate::values::specified::{NonNegativeLengthPercentage, Opacity};
|
||||
use crate::values::CustomIdent;
|
||||
use cssparser::{Parser, Token};
|
||||
|
@ -334,3 +335,57 @@ impl Parse for MozContextProperties {
|
|||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// The svg d property type.
|
||||
///
|
||||
/// https://svgwg.org/svg2-draft/paths.html#TheDProperty
|
||||
#[derive(
|
||||
Animate,
|
||||
Clone,
|
||||
ComputeSquaredDistance,
|
||||
Debug,
|
||||
Deserialize,
|
||||
MallocSizeOf,
|
||||
PartialEq,
|
||||
Serialize,
|
||||
SpecifiedValueInfo,
|
||||
ToAnimatedZero,
|
||||
ToComputedValue,
|
||||
ToCss,
|
||||
ToResolvedValue,
|
||||
ToShmem,
|
||||
)]
|
||||
#[repr(C, u8)]
|
||||
pub enum DProperty {
|
||||
/// Path value for path(<string>) or just a <string>.
|
||||
#[css(function)]
|
||||
Path(SVGPathData),
|
||||
/// None value.
|
||||
#[animation(error)]
|
||||
None,
|
||||
}
|
||||
|
||||
impl DProperty {
|
||||
/// return none.
|
||||
#[inline]
|
||||
pub fn none() -> Self {
|
||||
DProperty::None
|
||||
}
|
||||
}
|
||||
|
||||
impl Parse for DProperty {
|
||||
fn parse<'i, 't>(
|
||||
context: &ParserContext,
|
||||
input: &mut Parser<'i, 't>,
|
||||
) -> Result<Self, ParseError<'i>> {
|
||||
// Parse none.
|
||||
if input.try_parse(|i| i.expect_ident_matching("none")).is_ok() {
|
||||
return Ok(DProperty::none());
|
||||
}
|
||||
|
||||
// Parse possible functions.
|
||||
input.expect_function_matching("path")?;
|
||||
let path_data = input.parse_nested_block(|i| SVGPathData::parse(context, i))?;
|
||||
Ok(DProperty::Path(path_data))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -60,6 +60,111 @@ impl SVGPathData {
|
|||
|
||||
SVGPathData(crate::ArcSlice::from_iter(result.into_iter()))
|
||||
}
|
||||
|
||||
// FIXME: Bug 1714238, we may drop this once we use the same data structure for both SVG and
|
||||
// CSS.
|
||||
/// Decode the svg path raw data from Gecko.
|
||||
#[cfg(feature = "gecko")]
|
||||
pub fn decode_from_f32_array(path: &[f32]) -> Result<Self, ()> {
|
||||
use crate::gecko_bindings::structs::dom::SVGPathSeg_Binding::*;
|
||||
|
||||
let mut result: Vec<PathCommand> = Vec::new();
|
||||
let mut i: usize = 0;
|
||||
while i < path.len() {
|
||||
// See EncodeType() and DecodeType() in SVGPathSegUtils.h.
|
||||
// We are using reinterpret_cast<> to encode and decode between u32 and f32, so here we
|
||||
// use to_bits() to decode the type.
|
||||
let seg_type = path[i].to_bits() as u16;
|
||||
i = i + 1;
|
||||
match seg_type {
|
||||
PATHSEG_CLOSEPATH => result.push(PathCommand::ClosePath),
|
||||
PATHSEG_MOVETO_ABS | PATHSEG_MOVETO_REL => {
|
||||
debug_assert!(i + 1 < path.len());
|
||||
result.push(PathCommand::MoveTo {
|
||||
point: CoordPair::new(path[i], path[i + 1]),
|
||||
absolute: IsAbsolute::new(seg_type == PATHSEG_MOVETO_ABS),
|
||||
});
|
||||
i = i + 2;
|
||||
}
|
||||
PATHSEG_LINETO_ABS | PATHSEG_LINETO_REL => {
|
||||
debug_assert!(i + 1 < path.len());
|
||||
result.push(PathCommand::LineTo {
|
||||
point: CoordPair::new(path[i], path[i + 1]),
|
||||
absolute: IsAbsolute::new(seg_type == PATHSEG_LINETO_ABS),
|
||||
});
|
||||
i = i + 2;
|
||||
}
|
||||
PATHSEG_CURVETO_CUBIC_ABS | PATHSEG_CURVETO_CUBIC_REL => {
|
||||
debug_assert!(i + 5 < path.len());
|
||||
result.push(PathCommand::CurveTo {
|
||||
control1: CoordPair::new(path[i], path[i + 1]),
|
||||
control2: CoordPair::new(path[i + 2], path[i + 3]),
|
||||
point: CoordPair::new(path[i + 4], path[i + 5]),
|
||||
absolute: IsAbsolute::new(seg_type == PATHSEG_CURVETO_CUBIC_ABS),
|
||||
});
|
||||
i = i + 6;
|
||||
}
|
||||
PATHSEG_CURVETO_QUADRATIC_ABS | PATHSEG_CURVETO_QUADRATIC_REL => {
|
||||
debug_assert!(i + 3 < path.len());
|
||||
result.push(PathCommand::QuadBezierCurveTo {
|
||||
control1: CoordPair::new(path[i], path[i + 1]),
|
||||
point: CoordPair::new(path[i + 2], path[i + 3]),
|
||||
absolute: IsAbsolute::new(seg_type == PATHSEG_CURVETO_QUADRATIC_ABS),
|
||||
});
|
||||
i = i + 4;
|
||||
}
|
||||
PATHSEG_ARC_ABS | PATHSEG_ARC_REL => {
|
||||
debug_assert!(i + 6 < path.len());
|
||||
result.push(PathCommand::EllipticalArc {
|
||||
rx: path[i],
|
||||
ry: path[i + 1],
|
||||
angle: path[i + 2],
|
||||
large_arc_flag: ArcFlag(path[i + 3] != 0.0f32),
|
||||
sweep_flag: ArcFlag(path[i + 4] != 0.0f32),
|
||||
point: CoordPair::new(path[i + 5], path[i + 6]),
|
||||
absolute: IsAbsolute::new(seg_type == PATHSEG_ARC_ABS),
|
||||
});
|
||||
i = i + 7;
|
||||
}
|
||||
PATHSEG_LINETO_HORIZONTAL_ABS | PATHSEG_LINETO_HORIZONTAL_REL => {
|
||||
debug_assert!(i < path.len());
|
||||
result.push(PathCommand::HorizontalLineTo {
|
||||
x: path[i],
|
||||
absolute: IsAbsolute::new(seg_type == PATHSEG_LINETO_HORIZONTAL_ABS),
|
||||
});
|
||||
i = i + 1;
|
||||
}
|
||||
PATHSEG_LINETO_VERTICAL_ABS | PATHSEG_LINETO_VERTICAL_REL => {
|
||||
debug_assert!(i < path.len());
|
||||
result.push(PathCommand::VerticalLineTo {
|
||||
y: path[i],
|
||||
absolute: IsAbsolute::new(seg_type == PATHSEG_LINETO_VERTICAL_ABS),
|
||||
});
|
||||
i = i + 1;
|
||||
}
|
||||
PATHSEG_CURVETO_CUBIC_SMOOTH_ABS | PATHSEG_CURVETO_CUBIC_SMOOTH_REL => {
|
||||
debug_assert!(i + 3 < path.len());
|
||||
result.push(PathCommand::SmoothCurveTo {
|
||||
control2: CoordPair::new(path[i], path[i + 1]),
|
||||
point: CoordPair::new(path[i + 2], path[i + 3]),
|
||||
absolute: IsAbsolute::new(seg_type == PATHSEG_CURVETO_CUBIC_SMOOTH_ABS),
|
||||
});
|
||||
i = i + 4;
|
||||
}
|
||||
PATHSEG_CURVETO_QUADRATIC_SMOOTH_ABS | PATHSEG_CURVETO_QUADRATIC_SMOOTH_REL => {
|
||||
debug_assert!(i + 1 < path.len());
|
||||
result.push(PathCommand::SmoothQuadBezierCurveTo {
|
||||
point: CoordPair::new(path[i], path[i + 1]),
|
||||
absolute: IsAbsolute::new(seg_type == PATHSEG_CURVETO_QUADRATIC_SMOOTH_ABS),
|
||||
});
|
||||
i = i + 2;
|
||||
}
|
||||
PATHSEG_UNKNOWN | _ => return Err(()),
|
||||
}
|
||||
}
|
||||
|
||||
Ok(SVGPathData(crate::ArcSlice::from_iter(result.into_iter())))
|
||||
}
|
||||
}
|
||||
|
||||
impl ToCss for SVGPathData {
|
||||
|
@ -506,6 +611,17 @@ impl IsAbsolute {
|
|||
pub fn is_yes(&self) -> bool {
|
||||
*self == IsAbsolute::Yes
|
||||
}
|
||||
|
||||
/// Return Yes if value is true. Otherwise, return No.
|
||||
#[inline]
|
||||
#[cfg(feature = "gecko")]
|
||||
fn new(value: bool) -> Self {
|
||||
if value {
|
||||
IsAbsolute::Yes
|
||||
} else {
|
||||
IsAbsolute::No
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// The path coord type.
|
||||
|
|
|
@ -238,8 +238,14 @@ impl Transform {
|
|||
Ok(generic::TransformOperation::SkewY(theta))
|
||||
},
|
||||
"perspective" => {
|
||||
let d = specified::Length::parse_non_negative(context, input)?;
|
||||
Ok(generic::TransformOperation::Perspective(d))
|
||||
let p = match input.try_parse(|input| specified::Length::parse_non_negative(context, input)) {
|
||||
Ok(p) => generic::PerspectiveFunction::Length(p),
|
||||
Err(..) => {
|
||||
input.expect_ident_matching("none")?;
|
||||
generic::PerspectiveFunction::None
|
||||
}
|
||||
};
|
||||
Ok(generic::TransformOperation::Perspective(p))
|
||||
},
|
||||
_ => Err(()),
|
||||
};
|
||||
|
|
|
@ -13,6 +13,8 @@ use std::ptr::NonNull;
|
|||
use std::{iter, mem};
|
||||
use to_shmem_derive::ToShmem;
|
||||
|
||||
use malloc_size_of::{MallocSizeOf, MallocSizeOfOps, MallocUnconditionalSizeOf};
|
||||
|
||||
/// A canary that we stash in ArcSlices.
|
||||
///
|
||||
/// Given we cannot use a zero-sized-type for the header, since well, C++
|
||||
|
@ -137,6 +139,22 @@ impl<T> ArcSlice<T> {
|
|||
std::mem::forget(empty);
|
||||
ptr as *mut _
|
||||
}
|
||||
|
||||
/// Returns whether there's only one reference to this ArcSlice.
|
||||
pub fn is_unique(&self) -> bool {
|
||||
self.0.with_arc(|arc| arc.is_unique())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: MallocSizeOf> MallocUnconditionalSizeOf for ArcSlice<T> {
|
||||
#[allow(unsafe_code)]
|
||||
fn unconditional_size_of(&self, ops: &mut MallocSizeOfOps) -> usize {
|
||||
let mut size = unsafe { ops.malloc_size_of(self.0.heap_ptr()) };
|
||||
for el in self.iter() {
|
||||
size += el.size_of(ops);
|
||||
}
|
||||
size
|
||||
}
|
||||
}
|
||||
|
||||
/// The inner pointer of an ArcSlice<T>, to be sent via FFI.
|
||||
|
|
|
@ -81,10 +81,14 @@ packages = [
|
|||
files = [
|
||||
"./components/net/tests/parsable_mime/text",
|
||||
# These are ignored to avoid diverging from Gecko
|
||||
"./components/style/counter_style/mod.rs",
|
||||
"./components/style/properties/helpers.mako.rs",
|
||||
"./components/style/stylesheets/rule_parser.rs",
|
||||
"./components/style/stylist.rs",
|
||||
"./components/style/values/computed/font.rs",
|
||||
"./components/style/values/computed/image.rs",
|
||||
"./components/style/values/specified/color.rs",
|
||||
"./components/style/values/specified/transform.rs",
|
||||
# Mako does not lend itself easily to splitting long lines
|
||||
"./components/style/properties/helpers/animated_properties.mako.rs",
|
||||
"./components/style/properties/shorthands/text.mako.rs",
|
||||
|
|
|
@ -1,2 +0,0 @@
|
|||
[background-position-calc-minmax-001.html]
|
||||
expected: FAIL
|
|
@ -1,2 +0,0 @@
|
|||
[background-position-negative-percentage-comparison.html]
|
||||
expected: FAIL
|
|
@ -926,51 +926,6 @@
|
|||
[Web Animations: property <transform> from [scaleZ(2)\] to [scaleZ(2) perspective(500px)\] at (2) should be [scaleZ(2) perspective(250px)\]]
|
||||
expected: FAIL
|
||||
|
||||
[CSS Transitions: property <transform> from [perspective(none)\] to [perspective(500px)\] at (-1) should be [perspective(none)\]]
|
||||
expected: FAIL
|
||||
|
||||
[CSS Transitions: property <transform> from [perspective(none)\] to [perspective(500px)\] at (0) should be [perspective(none)\]]
|
||||
expected: FAIL
|
||||
|
||||
[CSS Transitions: property <transform> from [perspective(none)\] to [perspective(500px)\] at (0.5) should be [perspective(1000px)\]]
|
||||
expected: FAIL
|
||||
|
||||
[CSS Transitions: property <transform> from [perspective(none)\] to [perspective(500px)\] at (1) should be [perspective(500px)\]]
|
||||
expected: FAIL
|
||||
|
||||
[CSS Transitions: property <transform> from [perspective(none)\] to [perspective(500px)\] at (2) should be [perspective(250px)\]]
|
||||
expected: FAIL
|
||||
|
||||
[CSS Transitions with transition: all: property <transform> from [perspective(none)\] to [perspective(500px)\] at (-1) should be [perspective(none)\]]
|
||||
expected: FAIL
|
||||
|
||||
[CSS Transitions with transition: all: property <transform> from [perspective(none)\] to [perspective(500px)\] at (0) should be [perspective(none)\]]
|
||||
expected: FAIL
|
||||
|
||||
[CSS Transitions with transition: all: property <transform> from [perspective(none)\] to [perspective(500px)\] at (0.5) should be [perspective(1000px)\]]
|
||||
expected: FAIL
|
||||
|
||||
[CSS Transitions with transition: all: property <transform> from [perspective(none)\] to [perspective(500px)\] at (1) should be [perspective(500px)\]]
|
||||
expected: FAIL
|
||||
|
||||
[CSS Transitions with transition: all: property <transform> from [perspective(none)\] to [perspective(500px)\] at (2) should be [perspective(250px)\]]
|
||||
expected: FAIL
|
||||
|
||||
[CSS Animations: property <transform> from [perspective(none)\] to [perspective(500px)\] at (-1) should be [perspective(none)\]]
|
||||
expected: FAIL
|
||||
|
||||
[CSS Animations: property <transform> from [perspective(none)\] to [perspective(500px)\] at (0) should be [perspective(none)\]]
|
||||
expected: FAIL
|
||||
|
||||
[CSS Animations: property <transform> from [perspective(none)\] to [perspective(500px)\] at (0.5) should be [perspective(1000px)\]]
|
||||
expected: FAIL
|
||||
|
||||
[CSS Animations: property <transform> from [perspective(none)\] to [perspective(500px)\] at (1) should be [perspective(500px)\]]
|
||||
expected: FAIL
|
||||
|
||||
[CSS Animations: property <transform> from [perspective(none)\] to [perspective(500px)\] at (2) should be [perspective(250px)\]]
|
||||
expected: FAIL
|
||||
|
||||
[Web Animations: property <transform> from [perspective(none)\] to [perspective(500px)\] at (-1) should be [perspective(none)\]]
|
||||
expected: FAIL
|
||||
|
||||
|
|
|
@ -203,39 +203,12 @@
|
|||
[Web Animations: property <transform> from [matrix(1,0,0,1,0,0) rotate(0deg)\] to [matrix(2,0,0,2,0,0) rotate(360deg)\] at (0.5) should be [matrix(1.5,0,0,1.5,0,0) rotate(180deg)\]]
|
||||
expected: FAIL
|
||||
|
||||
[CSS Transitions: property <transform> from [perspective(none) translateZ(15px)\] to [perspective(none) translateZ(15px)\] at (0.5) should be [perspective(none) translateZ(15px)\]]
|
||||
expected: FAIL
|
||||
|
||||
[CSS Transitions with transition: all: property <transform> from [perspective(none) translateZ(15px)\] to [perspective(none) translateZ(15px)\] at (0.5) should be [perspective(none) translateZ(15px)\]]
|
||||
expected: FAIL
|
||||
|
||||
[CSS Animations: property <transform> from [perspective(none) translateZ(15px)\] to [perspective(none) translateZ(15px)\] at (0.5) should be [perspective(none) translateZ(15px)\]]
|
||||
expected: FAIL
|
||||
|
||||
[Web Animations: property <transform> from [perspective(none) translateZ(15px)\] to [perspective(none) translateZ(15px)\] at (0.5) should be [perspective(none) translateZ(15px)\]]
|
||||
expected: FAIL
|
||||
|
||||
[CSS Transitions: property <transform> from [perspective(100px) translateZ(50px)\] to [perspective(none) translateZ(50px)\] at (0.5) should be [perspective(200px) translateZ(50px)\]]
|
||||
expected: FAIL
|
||||
|
||||
[CSS Transitions with transition: all: property <transform> from [perspective(100px) translateZ(50px)\] to [perspective(none) translateZ(50px)\] at (0.5) should be [perspective(200px) translateZ(50px)\]]
|
||||
expected: FAIL
|
||||
|
||||
[CSS Animations: property <transform> from [perspective(100px) translateZ(50px)\] to [perspective(none) translateZ(50px)\] at (0.5) should be [perspective(200px) translateZ(50px)\]]
|
||||
expected: FAIL
|
||||
|
||||
[Web Animations: property <transform> from [perspective(100px) translateZ(50px)\] to [perspective(none) translateZ(50px)\] at (0.5) should be [perspective(200px) translateZ(50px)\]]
|
||||
expected: FAIL
|
||||
|
||||
[CSS Transitions: property <transform> from [perspective(none) translateZ(15px)\] to [perspective(25px) translateZ(15px)\] at (0.5) should be [perspective(50px) translateZ(15px)\]]
|
||||
expected: FAIL
|
||||
|
||||
[CSS Transitions with transition: all: property <transform> from [perspective(none) translateZ(15px)\] to [perspective(25px) translateZ(15px)\] at (0.5) should be [perspective(50px) translateZ(15px)\]]
|
||||
expected: FAIL
|
||||
|
||||
[CSS Animations: property <transform> from [perspective(none) translateZ(15px)\] to [perspective(25px) translateZ(15px)\] at (0.5) should be [perspective(50px) translateZ(15px)\]]
|
||||
expected: FAIL
|
||||
|
||||
[Web Animations: property <transform> from [perspective(none) translateZ(15px)\] to [perspective(25px) translateZ(15px)\] at (0.5) should be [perspective(50px) translateZ(15px)\]]
|
||||
expected: FAIL
|
||||
|
||||
|
@ -248,9 +221,6 @@
|
|||
[CSS Transitions with transition: all: property <transform> from [perspective(0.1px) translateZ(0.25px)\] to [perspective(0.1px) translateZ(0.25px)\] at (0.5) should be [perspective(1px) translateZ(0.25px)\]]
|
||||
expected: FAIL
|
||||
|
||||
[CSS Animations: property <transform> from [perspective(0.1px) translateZ(0.25px)\] to [perspective(0.1px) translateZ(0.25px)\] at (0.5) should be [perspective(1px) translateZ(0.25px)\]]
|
||||
expected: FAIL
|
||||
|
||||
[Web Animations: property <transform> from [perspective(0.1px) translateZ(0.25px)\] to [perspective(0.1px) translateZ(0.25px)\] at (0.5) should be [perspective(1px) translateZ(0.25px)\]]
|
||||
expected: FAIL
|
||||
|
||||
|
@ -260,21 +230,9 @@
|
|||
[CSS Transitions with transition: all: property <transform> from [perspective(0px) translateZ(0.25px)\] to [perspective(0px) translateZ(0.25px)\] at (0.5) should be [perspective(1px) translateZ(0.25px)\]]
|
||||
expected: FAIL
|
||||
|
||||
[CSS Animations: property <transform> from [perspective(0px) translateZ(0.25px)\] to [perspective(0px) translateZ(0.25px)\] at (0.5) should be [perspective(1px) translateZ(0.25px)\]]
|
||||
expected: FAIL
|
||||
|
||||
[Web Animations: property <transform> from [perspective(0px) translateZ(0.25px)\] to [perspective(0px) translateZ(0.25px)\] at (0.5) should be [perspective(1px) translateZ(0.25px)\]]
|
||||
expected: FAIL
|
||||
|
||||
[CSS Transitions: property <transform> from [perspective(0px) translateZ(0.5px)\] to [perspective(3px) translateZ(0.5px)\] at (0.5) should be [perspective(1.5px) translateZ(0.5px)\]]
|
||||
expected: FAIL
|
||||
|
||||
[CSS Transitions with transition: all: property <transform> from [perspective(0px) translateZ(0.5px)\] to [perspective(3px) translateZ(0.5px)\] at (0.5) should be [perspective(1.5px) translateZ(0.5px)\]]
|
||||
expected: FAIL
|
||||
|
||||
[CSS Animations: property <transform> from [perspective(0px) translateZ(0.5px)\] to [perspective(3px) translateZ(0.5px)\] at (0.5) should be [perspective(1.5px) translateZ(0.5px)\]]
|
||||
expected: FAIL
|
||||
|
||||
[Web Animations: property <transform> from [perspective(0px) translateZ(0.5px)\] to [perspective(3px) translateZ(0.5px)\] at (0.5) should be [perspective(1.5px) translateZ(0.5px)\]]
|
||||
expected: FAIL
|
||||
|
||||
|
@ -290,14 +248,5 @@
|
|||
[Web Animations: property <transform> from [perspective(10px) translateZ(0.5px)\] to [perspective(1px) translateZ(0.5px)\] at (-1) should be [translateZ(0.5px)\]]
|
||||
expected: FAIL
|
||||
|
||||
[CSS Transitions: property <transform> from [perspective(1px) translateZ(0.5px)\] to [perspective(10px) translateZ(0.5px)\] at (-1) should be [perspective(1px) translateZ(0.5px)\]]
|
||||
expected: FAIL
|
||||
|
||||
[CSS Transitions with transition: all: property <transform> from [perspective(1px) translateZ(0.5px)\] to [perspective(10px) translateZ(0.5px)\] at (-1) should be [perspective(1px) translateZ(0.5px)\]]
|
||||
expected: FAIL
|
||||
|
||||
[CSS Animations: property <transform> from [perspective(1px) translateZ(0.5px)\] to [perspective(10px) translateZ(0.5px)\] at (-1) should be [perspective(1px) translateZ(0.5px)\]]
|
||||
expected: FAIL
|
||||
|
||||
[Web Animations: property <transform> from [perspective(1px) translateZ(0.5px)\] to [perspective(10px) translateZ(0.5px)\] at (-1) should be [perspective(1px) translateZ(0.5px)\]]
|
||||
expected: FAIL
|
||||
|
|
|
@ -1,6 +1,3 @@
|
|||
[transform-valid.html]
|
||||
[e.style['transform'\] = "perspective(none)" should set the property value]
|
||||
expected: FAIL
|
||||
|
||||
[e.style['transform'\] = "translate(1px, 0%)" should set the property value]
|
||||
expected: FAIL
|
||||
|
|
|
@ -1,2 +0,0 @@
|
|||
[perspective-zero-2.html]
|
||||
expected: FAIL
|
|
@ -1,2 +0,0 @@
|
|||
[perspective-zero-3.html]
|
||||
expected: FAIL
|
|
@ -1,2 +0,0 @@
|
|||
[perspective-zero.html]
|
||||
expected: FAIL
|
|
@ -1,2 +0,0 @@
|
|||
[transform3d-perspective-005.html]
|
||||
expected: FAIL
|
|
@ -1,36 +1,3 @@
|
|||
[minmax-percentage-serialize.html]
|
||||
['min(1%, 2%, 3%)' as a specified value should serialize as 'min(1%, 2%, 3%)'.]
|
||||
expected: FAIL
|
||||
|
||||
['min(1%, 2%, 3%)' as a computed value should serialize as 'min(1%, 2%, 3%)'.]
|
||||
expected: FAIL
|
||||
|
||||
['min(3%, 2%, 1%)' as a specified value should serialize as 'min(3%, 2%, 1%)'.]
|
||||
expected: FAIL
|
||||
|
||||
['min(3%, 2%, 1%)' as a computed value should serialize as 'min(3%, 2%, 1%)'.]
|
||||
expected: FAIL
|
||||
|
||||
['max(1%, 2%, 3%)' as a specified value should serialize as 'max(1%, 2%, 3%)'.]
|
||||
expected: FAIL
|
||||
|
||||
['max(1%, 2%, 3%)' as a computed value should serialize as 'max(1%, 2%, 3%)'.]
|
||||
expected: FAIL
|
||||
|
||||
['max(3%, 2%, 1%)' as a specified value should serialize as 'max(3%, 2%, 1%)'.]
|
||||
expected: FAIL
|
||||
|
||||
['max(3%, 2%, 1%)' as a computed value should serialize as 'max(3%, 2%, 1%)'.]
|
||||
expected: FAIL
|
||||
|
||||
['min(1%, 2%, 3%) 0px' as a specified value should serialize as 'min(1%, 2%, 3%) 0px'.]
|
||||
expected: FAIL
|
||||
|
||||
['min(1%, 2%, 3%) 0px' as a computed value should serialize as 'min(1%, 2%, 3%) 0px'.]
|
||||
expected: FAIL
|
||||
|
||||
['calc(min(1%, 2%) + max(3%, 4%) + 10%)' as a specified value should serialize as 'calc(10% + min(1%, 2%) + max(3%, 4%))'.]
|
||||
expected: FAIL
|
||||
|
||||
['calc(min(1%, 2%) + max(3%, 4%) + 10%)' as a computed value should serialize as 'calc(10% + min(1%, 2%) + max(3%, 4%))'.]
|
||||
expected: FAIL
|
||||
|
|
|
@ -7,9 +7,3 @@
|
|||
|
||||
[target6]
|
||||
expected: FAIL
|
||||
|
||||
[target9]
|
||||
expected: FAIL
|
||||
|
||||
[target11]
|
||||
expected: FAIL
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue