Skip to main content

script/dom/bindings/
principals.rs

1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4
5use std::ptr::NonNull;
6
7use js::glue::{DestroyRustJSPrincipals, GetRustJSPrincipalsPrivate, JSPrincipalsCallbacks};
8use js::jsapi::{
9    JS_ReadUint32Pair, JSContext, JSPrincipals, JSStructuredCloneReader, JSStructuredCloneWriter,
10};
11use script_bindings::principals::{ServoJSPrincipals, ServoJSPrincipalsRef};
12use servo_url::MutableOrigin;
13
14use super::structuredclone::StructuredCloneTags;
15use crate::DomTypeHolder;
16
17#[expect(unused)]
18pub(crate) unsafe extern "C" fn destroy_servo_jsprincipal(principals: *mut JSPrincipals) {
19    unsafe {
20        Box::from_raw(GetRustJSPrincipalsPrivate(principals) as *mut MutableOrigin);
21        DestroyRustJSPrincipals(principals);
22    }
23}
24
25pub(crate) unsafe extern "C" fn write_jsprincipal(
26    principal: *mut JSPrincipals,
27    _cx: *mut JSContext,
28    writer: *mut JSStructuredCloneWriter,
29) -> bool {
30    let Some(principal) = NonNull::new(principal) else {
31        return false;
32    };
33    let obj = unsafe { ServoJSPrincipalsRef::from_raw_nonnull(principal) };
34    let origin = obj.origin();
35    let Ok(bytes_of_origin) = postcard::to_stdvec(&origin) else {
36        return false;
37    };
38    let Ok(len) = bytes_of_origin.len().try_into() else {
39        return false;
40    };
41
42    unsafe {
43        if !js::jsapi::JS_WriteUint32PairUnchecked(
44            writer,
45            StructuredCloneTags::Principals as u32,
46            len,
47        ) {
48            return false;
49        }
50        if !js::jsapi::JS_WriteBytes(writer, bytes_of_origin.as_ptr() as _, len as usize) {
51            return false;
52        }
53    }
54
55    true
56}
57
58pub(crate) unsafe extern "C" fn read_jsprincipal(
59    _cx: *mut JSContext,
60    reader: *mut JSStructuredCloneReader,
61    principals: *mut *mut JSPrincipals,
62) -> bool {
63    let mut tag: u32 = 0;
64    let mut len: u32 = 0;
65
66    unsafe {
67        if !JS_ReadUint32Pair(reader, &mut tag as *mut u32, &mut len as *mut u32) {
68            return false;
69        }
70    }
71
72    if tag != StructuredCloneTags::Principals as u32 {
73        return false;
74    }
75    let mut bytes = vec![0u8; len as usize];
76
77    unsafe {
78        if !js::jsapi::JS_ReadBytes(reader, bytes.as_mut_ptr() as _, len as usize) {
79            return false;
80        }
81    }
82
83    let Ok(origin) = postcard::from_bytes(&bytes[..]) else {
84        return false;
85    };
86    let principal = ServoJSPrincipals::new::<DomTypeHolder>(&origin);
87    unsafe { *principals = principal.as_raw() };
88    // we transferred ownership of principal to the caller
89    std::mem::forget(principal);
90    true
91}
92
93pub(crate) const PRINCIPALS_CALLBACKS: JSPrincipalsCallbacks = JSPrincipalsCallbacks {
94    write: Some(write_jsprincipal),
95    isSystemPrincipal: Some(principals_is_system_principal),
96    isAddonPrincipal: Some(principals_is_addon_principal),
97};
98
99unsafe extern "C" fn principals_is_system_principal(_: *mut JSPrincipals) -> bool {
100    false
101}
102
103unsafe extern "C" fn principals_is_addon_principal(_: *mut JSPrincipals) -> bool {
104    false
105}
106
107// TODO is same_origin_domain equivalent to subsumes for our purposes
108pub(crate) unsafe extern "C" fn subsumes(obj: *mut JSPrincipals, other: *mut JSPrincipals) -> bool {
109    match (NonNull::new(obj), NonNull::new(other)) {
110        (Some(obj), Some(other)) => {
111            let obj = unsafe { ServoJSPrincipalsRef::from_raw_nonnull(obj) };
112            let other = unsafe { ServoJSPrincipalsRef::from_raw_nonnull(other) };
113            let obj_origin = obj.origin();
114            let other_origin = other.origin();
115            obj_origin.same_origin_domain(&other_origin)
116        },
117        (None, Some(_)) => {
118            // See https://github.com/servo/servo/issues/32999#issuecomment-2542522289 for why
119            // it's safe to consider the null principal here subsumes all others.
120            true
121        },
122        _ => {
123            warn!("Received null JSPrincipal argument.");
124            false
125        },
126    }
127}