1use std::cell::RefCell;
6use std::net::IpAddr;
7use std::rc::Rc;
8
9use malloc_size_of::malloc_size_of_is_0;
10use malloc_size_of_derive::MallocSizeOf;
11use serde::{Deserialize, Serialize};
12use url::{Host, Origin, Url};
13use uuid::Uuid;
14
15#[derive(Clone, Debug, Deserialize, Eq, Hash, MallocSizeOf, PartialEq, Serialize)]
17pub enum ImmutableOrigin {
18 Opaque(OpaqueOrigin),
20
21 Tuple(String, Host, u16),
23}
24
25pub trait DomainComparable {
26 fn has_domain(&self) -> bool;
27 fn immutable(&self) -> &ImmutableOrigin;
28}
29
30impl DomainComparable for OriginSnapshot {
31 fn has_domain(&self) -> bool {
32 self.1.is_some()
33 }
34 fn immutable(&self) -> &ImmutableOrigin {
35 &self.0
36 }
37}
38
39impl DomainComparable for MutableOrigin {
40 fn has_domain(&self) -> bool {
41 (self.0).1.borrow().is_some()
42 }
43 fn immutable(&self) -> &ImmutableOrigin {
44 &(self.0).0
45 }
46}
47
48impl ImmutableOrigin {
49 pub fn new(url: &Url) -> ImmutableOrigin {
50 if url.scheme() == "file" {
51 return Self::new_opaque_for_file();
52 }
53
54 match url.origin() {
55 Origin::Opaque(_) => ImmutableOrigin::new_opaque(),
56 Origin::Tuple(scheme, host, port) => ImmutableOrigin::Tuple(scheme, host, port),
57 }
58 }
59
60 pub fn same_origin(&self, other: &impl DomainComparable) -> bool {
61 self == other.immutable()
62 }
63
64 pub fn same_origin_domain(&self, other: &impl DomainComparable) -> bool {
65 !other.has_domain() && self == other.immutable()
66 }
67
68 pub fn new_opaque() -> ImmutableOrigin {
70 ImmutableOrigin::Opaque(OpaqueOrigin {
71 id: Uuid::new_v4(),
72 is_for_data_worker_from_secure_context: false,
73 is_file_origin: false,
74 })
75 }
76
77 pub fn new_opaque_data_url_worker() -> ImmutableOrigin {
79 ImmutableOrigin::Opaque(OpaqueOrigin {
80 id: Uuid::new_v4(),
81 is_for_data_worker_from_secure_context: true,
82 is_file_origin: false,
83 })
84 }
85
86 pub fn new_opaque_for_file() -> ImmutableOrigin {
87 ImmutableOrigin::Opaque(OpaqueOrigin {
88 id: Uuid::new_v4(),
89 is_for_data_worker_from_secure_context: false,
90 is_file_origin: true,
91 })
92 }
93
94 pub fn scheme(&self) -> Option<&str> {
95 match *self {
96 ImmutableOrigin::Opaque(_) => None,
97 ImmutableOrigin::Tuple(ref scheme, _, _) => Some(&**scheme),
98 }
99 }
100
101 pub fn host(&self) -> Option<&Host> {
102 match *self {
103 ImmutableOrigin::Opaque(_) => None,
104 ImmutableOrigin::Tuple(_, ref host, _) => Some(host),
105 }
106 }
107
108 pub fn port(&self) -> Option<u16> {
109 match *self {
110 ImmutableOrigin::Opaque(_) => None,
111 ImmutableOrigin::Tuple(_, _, port) => Some(port),
112 }
113 }
114
115 pub fn into_url_origin(self) -> Origin {
116 match self {
117 ImmutableOrigin::Opaque(_) => Origin::new_opaque(),
118 ImmutableOrigin::Tuple(scheme, host, port) => Origin::Tuple(scheme, host, port),
119 }
120 }
121
122 pub fn is_tuple(&self) -> bool {
125 matches!(self, ImmutableOrigin::Tuple(..))
126 }
127
128 pub fn is_file_origin(&self) -> bool {
129 matches!(
130 self,
131 ImmutableOrigin::Opaque(OpaqueOrigin {
132 is_file_origin: true,
133 ..
134 })
135 )
136 }
137
138 pub fn is_for_data_worker_from_secure_context(&self) -> bool {
139 matches!(
140 self,
141 ImmutableOrigin::Opaque(OpaqueOrigin {
142 is_for_data_worker_from_secure_context: true,
143 ..
144 })
145 )
146 }
147
148 pub fn is_potentially_trustworthy(&self) -> bool {
150 if let ImmutableOrigin::Opaque(opaque_origin) = self {
152 if opaque_origin.is_file_origin {
159 return true;
160 }
161 return false;
162 }
163
164 if let ImmutableOrigin::Tuple(scheme, host, _) = self {
165 if scheme == "https" || scheme == "wss" {
167 return true;
168 }
169
170 debug_assert_ne!(scheme, "file", "File URLs don't have a tuple origin");
173
174 if let Ok(ip_addr) = host.to_string().parse::<IpAddr>() {
177 return ip_addr.is_loopback();
178 }
179 if let Host::Domain(domain) = host {
185 if domain == "localhost" || domain.ends_with(".localhost") {
186 return true;
187 }
188 }
189 }
190
191 false
193 }
194
195 pub fn ascii_serialization(&self) -> String {
197 self.clone().into_url_origin().ascii_serialization()
198 }
199}
200
201#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
203pub struct OpaqueOrigin {
204 id: Uuid,
205 is_for_data_worker_from_secure_context: bool,
209 is_file_origin: bool,
214}
215
216malloc_size_of_is_0!(OpaqueOrigin);
217
218#[derive(Clone, Debug, Deserialize, Eq, Hash, MallocSizeOf, PartialEq, Serialize)]
220pub struct OriginSnapshot(ImmutableOrigin, Option<Host>);
221
222impl OriginSnapshot {
223 pub fn immutable(&self) -> &ImmutableOrigin {
224 &self.0
225 }
226
227 pub fn has_domain(&self) -> bool {
228 self.1.is_some()
229 }
230
231 pub fn same_origin(&self, other: &impl DomainComparable) -> bool {
232 self.immutable() == other.immutable()
233 }
234
235 pub fn same_origin_domain(&self, other: &OriginSnapshot) -> bool {
236 if let Some(ref self_domain) = self.1 {
237 if let Some(ref other_domain) = other.1 {
238 self_domain == other_domain && self.0.scheme() == other.0.scheme()
239 } else {
240 false
241 }
242 } else {
243 self.0.same_origin_domain(other)
244 }
245 }
246}
247
248#[derive(Clone, Debug, Deserialize, Serialize)]
250pub struct MutableOrigin(Rc<(ImmutableOrigin, RefCell<Option<Host>>)>);
251
252malloc_size_of_is_0!(MutableOrigin);
253
254impl MutableOrigin {
255 pub fn from_snapshot(snapshot: OriginSnapshot) -> MutableOrigin {
256 MutableOrigin(Rc::new((snapshot.0, RefCell::new(snapshot.1))))
257 }
258
259 pub fn snapshot(&self) -> OriginSnapshot {
260 OriginSnapshot(self.0.0.clone(), self.0.1.borrow().clone())
261 }
262
263 pub fn new(origin: ImmutableOrigin) -> MutableOrigin {
264 MutableOrigin(Rc::new((origin, RefCell::new(None))))
265 }
266
267 pub fn immutable(&self) -> &ImmutableOrigin {
268 &(self.0).0
269 }
270
271 pub fn is_tuple(&self) -> bool {
272 self.immutable().is_tuple()
273 }
274
275 pub fn scheme(&self) -> Option<&str> {
276 self.immutable().scheme()
277 }
278
279 pub fn host(&self) -> Option<&Host> {
280 self.immutable().host()
281 }
282
283 pub fn port(&self) -> Option<u16> {
284 self.immutable().port()
285 }
286
287 pub fn same_origin(&self, other: &MutableOrigin) -> bool {
288 self.immutable() == other.immutable()
289 }
290
291 pub fn same_origin_domain(&self, other: &MutableOrigin) -> bool {
292 if let Some(ref self_domain) = *(self.0).1.borrow() {
293 if let Some(ref other_domain) = *(other.0).1.borrow() {
294 self_domain == other_domain &&
295 self.immutable().scheme() == other.immutable().scheme()
296 } else {
297 false
298 }
299 } else {
300 self.immutable().same_origin_domain(other)
301 }
302 }
303
304 pub fn domain(&self) -> Option<Host> {
305 (self.0).1.borrow().clone()
306 }
307
308 pub fn set_domain(&self, domain: Host) {
309 *(self.0).1.borrow_mut() = Some(domain);
310 }
311
312 pub fn has_domain(&self) -> bool {
313 (self.0).1.borrow().is_some()
314 }
315
316 pub fn effective_domain(&self) -> Option<Host> {
318 if !self.is_tuple() {
320 return None;
321 }
322 self.immutable()
323 .host()
324 .map(|host| self.domain().unwrap_or_else(|| host.clone()))
327 }
328}