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