diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 00:47:55 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 00:47:55 +0000 |
commit | 26a029d407be480d791972afb5975cf62c9360a6 (patch) | |
tree | f435a8308119effd964b339f76abb83a57c29483 /servo/components/style/servo/url.rs | |
parent | Initial commit. (diff) | |
download | firefox-26a029d407be480d791972afb5975cf62c9360a6.tar.xz firefox-26a029d407be480d791972afb5975cf62c9360a6.zip |
Adding upstream version 124.0.1.upstream/124.0.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'servo/components/style/servo/url.rs')
-rw-r--r-- | servo/components/style/servo/url.rs | 238 |
1 files changed, 238 insertions, 0 deletions
diff --git a/servo/components/style/servo/url.rs b/servo/components/style/servo/url.rs new file mode 100644 index 0000000000..2186be7aab --- /dev/null +++ b/servo/components/style/servo/url.rs @@ -0,0 +1,238 @@ +/* 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/. */ + +//! Common handling for the specified value CSS url() values. + +use crate::parser::{Parse, ParserContext}; +use crate::stylesheets::CorsMode; +use crate::values::computed::{Context, ToComputedValue}; +use cssparser::Parser; +use servo_arc::Arc; +use servo_url::ServoUrl; +use std::fmt::{self, Write}; +use style_traits::{CssWriter, ParseError, ToCss}; + +/// A CSS url() value for servo. +/// +/// Servo eagerly resolves SpecifiedUrls, which it can then take advantage of +/// when computing values. In contrast, Gecko uses a different URL backend, so +/// eagerly resolving with rust-url would be duplicated work. +/// +/// However, this approach is still not necessarily optimal: See +/// <https://bugzilla.mozilla.org/show_bug.cgi?id=1347435#c6> +/// +/// TODO(emilio): This should be shrunk by making CssUrl a wrapper type of an +/// arc, and keep the serialization in that Arc. See gecko/url.rs for example. +#[derive(Clone, Debug, Deserialize, MallocSizeOf, Serialize, SpecifiedValueInfo, ToShmem)] +pub struct CssUrl { + /// The original URI. This might be optional since we may insert computed + /// values of images into the cascade directly, and we don't bother to + /// convert their serialization. + /// + /// Refcounted since cloning this should be cheap and data: uris can be + /// really large. + #[ignore_malloc_size_of = "Arc"] + original: Option<Arc<String>>, + + /// The resolved value for the url, if valid. + resolved: Option<ServoUrl>, +} + +impl CssUrl { + /// Try to parse a URL from a string value that is a valid CSS token for a + /// URL. + /// + /// FIXME(emilio): Should honor CorsMode. + pub fn parse_from_string(url: String, context: &ParserContext, _: CorsMode) -> Self { + let serialization = Arc::new(url); + let resolved = context.url_data.join(&serialization).ok(); + CssUrl { + original: Some(serialization), + resolved: resolved, + } + } + + /// Returns true if the URL is definitely invalid. For Servo URLs, we can + /// use its |resolved| status. + pub fn is_invalid(&self) -> bool { + self.resolved.is_none() + } + + /// Returns true if this URL looks like a fragment. + /// See https://drafts.csswg.org/css-values/#local-urls + /// + /// Since Servo currently stores resolved URLs, this is hard to implement. We + /// either need to change servo to lazily resolve (like Gecko), or note this + /// information in the tokenizer. + pub fn is_fragment(&self) -> bool { + error!("Can't determine whether the url is a fragment."); + false + } + + /// Returns the resolved url if it was valid. + pub fn url(&self) -> Option<&ServoUrl> { + self.resolved.as_ref() + } + + /// Return the resolved url as string, or the empty string if it's invalid. + /// + /// TODO(emilio): Should we return the original one if needed? + pub fn as_str(&self) -> &str { + match self.resolved { + Some(ref url) => url.as_str(), + None => "", + } + } + + /// Creates an already specified url value from an already resolved URL + /// for insertion in the cascade. + pub fn for_cascade(url: ServoUrl) -> Self { + CssUrl { + original: None, + resolved: Some(url), + } + } + + /// Gets a new url from a string for unit tests. + pub fn new_for_testing(url: &str) -> Self { + CssUrl { + original: Some(Arc::new(url.into())), + resolved: ServoUrl::parse(url).ok(), + } + } + + /// Parses a URL request and records that the corresponding request needs to + /// be CORS-enabled. + /// + /// This is only for shape images and masks in Gecko, thus unimplemented for + /// now so somebody notices when trying to do so. + pub fn parse_with_cors_mode<'i, 't>( + context: &ParserContext, + input: &mut Parser<'i, 't>, + cors_mode: CorsMode, + ) -> Result<Self, ParseError<'i>> { + let url = input.expect_url()?; + Ok(Self::parse_from_string( + url.as_ref().to_owned(), + context, + cors_mode, + )) + } +} + +impl Parse for CssUrl { + fn parse<'i, 't>( + context: &ParserContext, + input: &mut Parser<'i, 't>, + ) -> Result<Self, ParseError<'i>> { + Self::parse_with_cors_mode(context, input, CorsMode::None) + } +} + +impl PartialEq for CssUrl { + fn eq(&self, other: &Self) -> bool { + // TODO(emilio): maybe we care about equality of the specified values if + // present? Seems not. + self.resolved == other.resolved + } +} + +impl Eq for CssUrl {} + +impl ToCss for CssUrl { + fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result + where + W: Write, + { + let string = match self.original { + Some(ref original) => &**original, + None => match self.resolved { + Some(ref url) => url.as_str(), + // This can only happen if the url wasn't specified by the + // user *and* it's an invalid url that has been transformed + // back to specified value via the "uncompute" functionality. + None => "about:invalid", + }, + }; + + dest.write_str("url(")?; + string.to_css(dest)?; + dest.write_char(')') + } +} + +/// A specified url() value for servo. +pub type SpecifiedUrl = CssUrl; + +impl ToComputedValue for SpecifiedUrl { + type ComputedValue = ComputedUrl; + + // If we can't resolve the URL from the specified one, we fall back to the original + // but still return it as a ComputedUrl::Invalid + fn to_computed_value(&self, _: &Context) -> Self::ComputedValue { + match self.resolved { + Some(ref url) => ComputedUrl::Valid(url.clone()), + None => match self.original { + Some(ref url) => ComputedUrl::Invalid(url.clone()), + None => { + unreachable!("Found specified url with neither resolved or original URI!"); + }, + }, + } + } + + fn from_computed_value(computed: &ComputedUrl) -> Self { + match *computed { + ComputedUrl::Valid(ref url) => SpecifiedUrl { + original: None, + resolved: Some(url.clone()), + }, + ComputedUrl::Invalid(ref url) => SpecifiedUrl { + original: Some(url.clone()), + resolved: None, + }, + } + } +} + +/// A specified image url() value for servo. +pub type SpecifiedImageUrl = CssUrl; + +/// The computed value of a CSS `url()`, resolved relative to the stylesheet URL. +#[derive(Clone, Debug, Deserialize, MallocSizeOf, PartialEq, Serialize)] +pub enum ComputedUrl { + /// The `url()` was invalid or it wasn't specified by the user. + Invalid(#[ignore_malloc_size_of = "Arc"] Arc<String>), + /// The resolved `url()` relative to the stylesheet URL. + Valid(ServoUrl), +} + +impl ComputedUrl { + /// Returns the resolved url if it was valid. + pub fn url(&self) -> Option<&ServoUrl> { + match *self { + ComputedUrl::Valid(ref url) => Some(url), + _ => None, + } + } +} + +impl ToCss for ComputedUrl { + fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result + where + W: Write, + { + let string = match *self { + ComputedUrl::Valid(ref url) => url.as_str(), + ComputedUrl::Invalid(ref invalid_string) => invalid_string, + }; + + dest.write_str("url(")?; + string.to_css(dest)?; + dest.write_char(')') + } +} + +/// The computed value of a CSS `url()` for image. +pub type ComputedImageUrl = ComputedUrl; |