      1
      2
      3
      4
      5
      6
      7
      8
      9
     10
     11
     12
     13
     14
     15
     16
     17
     18
     19
     20
     21
     22
     23
     24
     25
     26
     27
     28
     29
     30
     31
     32
     33
     34
     35
     36
     37
     38
     39
     40
     41
     42
     43
     44
     45
     46
     47
     48
     49
     50
     51
     52
     53
     54
     55
     56
     57
     58
     59
     60
     61
     62
     63
     64
     65
     66
     67
     68
     69
     70
     71
     72
     73
     74
     75
     76
     77
     78
     79
     80
     81
     82
     83
     84
     85
     86
     87
     88
     89
     90
     91
     92
     93
     94
     95
     96
     97
     98
     99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113

    // Copyright 2016 The rust-url developers.
    //
    // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
    // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
    // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
    // option. This file may not be copied, modified, or distributed
    // except according to those terms.

    use crate::host::Host;
    use crate::parser::default_port;
    use crate::Url;
    use idna::domain_to_unicode;
    use std::sync::atomic::{AtomicUsize, Ordering};

    pub fn url_origin(url: &Url) -> Origin {
        let scheme = url.scheme();
        match scheme {
            "blob" => {
                let result = Url::parse(url.path());
                match result {
                    Ok(ref url) => url_origin(url),
                    Err(_) => Origin::new_opaque(),
                }
            }
            "ftp" | "http" | "https" | "ws" | "wss" => Origin::Tuple(
                scheme.to_owned(),
                url.host().unwrap().to_owned(),
                url.port_or_known_default().unwrap(),
            ),
            // TODO: Figure out what to do if the scheme is a file
            "file" => Origin::new_opaque(),
            _ => Origin::new_opaque(),
        }
    }

    /// The origin of an URL
    ///
    /// Two URLs with the same origin are considered
    /// to originate from the same entity and can therefore trust
    /// each other.
    ///
    /// The origin is determined based on the scheme as follows:
    ///
    /// - If the scheme is "blob" the origin is the origin of the
    ///   URL contained in the path component. If parsing fails,
    ///   it is an opaque origin.
    /// - If the scheme is "ftp", "http", "https", "ws", or "wss",
    ///   then the origin is a tuple of the scheme, host, and port.
    /// - If the scheme is anything else, the origin is opaque, meaning
    ///   the URL does not have the same origin as any other URL.
    ///
    /// For more information see <https://url.spec.whatwg.org/#origin>
    #[derive(PartialEq, Eq, Hash, Clone, Debug)]
    pub enum Origin {
        /// A globally unique identifier
        Opaque(OpaqueOrigin),

        /// Consists of the URL's scheme, host and port
        Tuple(String, Host<String>, u16),
    }

    impl Origin {
        /// Creates a new opaque origin that is only equal to itself.
        pub fn new_opaque() -> Origin {
            static COUNTER: AtomicUsize = AtomicUsize::new(0);
            Origin::Opaque(OpaqueOrigin(COUNTER.fetch_add(1, Ordering::SeqCst)))
        }

        /// Return whether this origin is a (scheme, host, port) tuple
        /// (as opposed to an opaque origin).
        pub fn is_tuple(&self) -> bool {
            matches!(*self, Origin::Tuple(..))
        }

        /// <https://html.spec.whatwg.org/multipage/#ascii-serialisation-of-an-origin>
        pub fn ascii_serialization(&self) -> String {
            match *self {
                Origin::Opaque(_) => "null".to_owned(),
                Origin::Tuple(ref scheme, ref host, port) => {
                    if default_port(scheme) == Some(port) {
                        format!("{}://{}", scheme, host)
                    } else {
                        format!("{}://{}:{}", scheme, host, port)
                    }
                }
            }
        }

        /// <https://html.spec.whatwg.org/multipage/#unicode-serialisation-of-an-origin>
        pub fn unicode_serialization(&self) -> String {
            match *self {
                Origin::Opaque(_) => "null".to_owned(),
                Origin::Tuple(ref scheme, ref host, port) => {
                    let host = match *host {
                        Host::Domain(ref domain) => {
                            let (domain, _errors) = domain_to_unicode(domain);
                            Host::Domain(domain)
                        }
                        _ => host.clone(),
                    };
                    if default_port(scheme) == Some(port) {
                        format!("{}://{}", scheme, host)
                    } else {
                        format!("{}://{}:{}", scheme, host, port)
                    }
                }
            }
        }
    }

    /// Opaque identifier for URLs that have file or other schemes
    #[derive(Eq, PartialEq, Hash, Clone, Debug)]
    pub struct OpaqueOrigin(usize);