Skip to main content

script/dom/
servointernals.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::rc::Rc;
6
7use dom_struct::dom_struct;
8use js::gc::MutableHandleValue;
9use js::jsapi::Heap;
10use js::jsval::UndefinedValue;
11use js::realm::CurrentRealm;
12use js::rust::HandleObject;
13use profile_traits::mem::MemoryReportResult;
14use script_bindings::conversions::SafeToJSValConvertible;
15use script_bindings::error::{Error, Fallible};
16use script_bindings::interfaces::ServoInternalsHelpers;
17use script_bindings::reflector::{Reflector, reflect_dom_object};
18use script_bindings::str::USVString;
19use servo_config::prefs::{self, PrefValue, Preferences};
20use servo_constellation_traits::ScriptToConstellationMessage;
21
22use crate::dom::bindings::codegen::Bindings::ServoInternalsBinding::ServoInternalsMethods;
23use crate::dom::bindings::reflector::DomGlobal;
24use crate::dom::bindings::root::DomRoot;
25use crate::dom::globalscope::GlobalScope;
26use crate::dom::promise::Promise;
27use crate::routed_promise::{RoutedPromiseListener, callback_promise};
28use crate::script_runtime::CanGc;
29use crate::script_thread::ScriptThread;
30
31fn pref_to_jsval(cx: &mut js::context::JSContext, pref: &PrefValue, rval: MutableHandleValue) {
32    match pref {
33        PrefValue::Bool(b) => b.safe_to_jsval(cx, rval),
34        PrefValue::Int(i) => i.safe_to_jsval(cx, rval),
35        PrefValue::UInt(u) => u.safe_to_jsval(cx, rval),
36        PrefValue::Str(s) => s.safe_to_jsval(cx, rval),
37        PrefValue::Float(f) => f.safe_to_jsval(cx, rval),
38        PrefValue::Array(arr) => {
39            rooted_vec!(let mut js_arr);
40            for item in arr {
41                rooted!(&in(cx) let mut js_val = UndefinedValue());
42                pref_to_jsval(cx, item, js_val.handle_mut());
43                js_arr.push(Heap::boxed(js_val.get()));
44            }
45            js_arr.safe_to_jsval(cx, rval);
46        },
47    }
48}
49
50#[dom_struct]
51pub(crate) struct ServoInternals {
52    reflector_: Reflector,
53}
54
55impl ServoInternals {
56    pub fn new_inherited() -> ServoInternals {
57        ServoInternals {
58            reflector_: Reflector::new(),
59        }
60    }
61
62    pub(crate) fn new(global: &GlobalScope, can_gc: CanGc) -> DomRoot<ServoInternals> {
63        reflect_dom_object(Box::new(ServoInternals::new_inherited()), global, can_gc)
64    }
65}
66
67impl ServoInternalsMethods<crate::DomTypeHolder> for ServoInternals {
68    /// <https://servo.org/internal-no-spec>
69    fn ReportMemory(&self, cx: &mut CurrentRealm) -> Rc<Promise> {
70        let promise = Promise::new_in_realm(cx);
71        let global = self.global();
72        let task_manager = global.task_manager();
73        let task_source = task_manager.dom_manipulation_task_source();
74        let callback = callback_promise(&promise, self, task_source);
75
76        let script_to_constellation_chan = global.script_to_constellation_chan();
77        if script_to_constellation_chan
78            .send(ScriptToConstellationMessage::ReportMemory(callback))
79            .is_err()
80        {
81            promise.reject_error(cx, Error::Operation(None));
82        }
83        promise
84    }
85
86    /// <https://servo.org/internal-no-spec>
87    fn GarbageCollectAllContexts(&self) {
88        let global = &self.global();
89
90        let script_to_constellation_chan = global.script_to_constellation_chan();
91        let _ = script_to_constellation_chan
92            .send(ScriptToConstellationMessage::TriggerGarbageCollection);
93    }
94
95    /// <https://servo.org/internal-no-spec>
96    fn PreferenceList(&self) -> Vec<USVString> {
97        Preferences::all_fields()
98            .into_iter()
99            .map(|s| USVString::from(s.to_string()))
100            .collect()
101    }
102
103    /// <https://servo.org/internal-no-spec>
104    fn PreferenceType(&self, name: USVString) -> Fallible<USVString> {
105        if !Preferences::exists(&name) {
106            return Err(Error::NotFound(None));
107        }
108        let type_name = Preferences::type_of(&name).split("::").last().unwrap();
109        Ok(USVString::from(type_name.to_string()))
110    }
111
112    /// <https://servo.org/internal-no-spec>
113    fn DefaultPreferenceValue(
114        &self,
115        cx: &mut js::context::JSContext,
116        name: USVString,
117        rval: MutableHandleValue,
118    ) -> Fallible<()> {
119        if !Preferences::exists(&name) {
120            return Err(Error::NotFound(None));
121        }
122        let pref = Preferences::default().get_value(&name);
123        pref_to_jsval(cx, &pref, rval);
124        Ok(())
125    }
126
127    /// <https://servo.org/internal-no-spec>
128    fn GetPreference(
129        &self,
130        cx: &mut js::context::JSContext,
131        name: USVString,
132        rval: MutableHandleValue,
133    ) -> Fallible<()> {
134        if !Preferences::exists(&name) {
135            return Err(Error::NotFound(None));
136        }
137        let pref = prefs::get().get_value(&name);
138        pref_to_jsval(cx, &pref, rval);
139        Ok(())
140    }
141
142    /// <https://servo.org/internal-no-spec>
143    fn GetBoolPreference(&self, name: USVString) -> Fallible<bool> {
144        if !Preferences::exists(&name) {
145            return Err(Error::NotFound(None));
146        }
147        if let PrefValue::Bool(b) = prefs::get().get_value(&name) {
148            return Ok(b);
149        }
150        Err(Error::TypeMismatch(None))
151    }
152
153    /// <https://servo.org/internal-no-spec>
154    fn GetIntPreference(&self, name: USVString) -> Fallible<i64> {
155        if !Preferences::exists(&name) {
156            return Err(Error::NotFound(None));
157        }
158        if let PrefValue::Int(i) = prefs::get().get_value(&name) {
159            return Ok(i);
160        }
161        Err(Error::TypeMismatch(None))
162    }
163
164    /// <https://servo.org/internal-no-spec>
165    fn GetStringPreference(&self, name: USVString) -> Fallible<USVString> {
166        if !Preferences::exists(&name) {
167            return Err(Error::NotFound(None));
168        }
169        if let PrefValue::Str(s) = prefs::get().get_value(&name) {
170            return Ok(s.into());
171        }
172        Err(Error::TypeMismatch(None))
173    }
174
175    /// <https://servo.org/internal-no-spec>
176    fn SetBoolPreference(&self, name: USVString, value: bool) {
177        let mut current_prefs = prefs::get().clone();
178        current_prefs.set_value(&name, value.into());
179        prefs::set(current_prefs);
180    }
181
182    /// <https://servo.org/internal-no-spec>
183    fn SetIntPreference(&self, name: USVString, value: i64) {
184        let mut current_prefs = prefs::get().clone();
185        current_prefs.set_value(&name, value.into());
186        prefs::set(current_prefs);
187    }
188
189    /// <https://servo.org/internal-no-spec>
190    fn SetStringPreference(&self, name: USVString, value: USVString) {
191        let mut current_prefs = prefs::get().clone();
192        current_prefs.set_value(&name, value.0.into());
193        prefs::set(current_prefs);
194    }
195}
196
197impl RoutedPromiseListener<MemoryReportResult> for ServoInternals {
198    fn handle_response(
199        &self,
200        cx: &mut js::context::JSContext,
201        response: MemoryReportResult,
202        promise: &Rc<Promise>,
203    ) {
204        let stringified = serde_json::to_string(&response.results)
205            .unwrap_or_else(|_| "{ error: \"failed to create memory report\"}".to_owned());
206        promise.resolve_native(cx, &stringified);
207    }
208}
209
210impl ServoInternalsHelpers for ServoInternals {
211    /// The navigator.servo api is exposed to about: pages except about:blank, as
212    /// well as any URLs provided by embedders that register new protocol handlers.
213    fn is_servo_internal(cx: &mut js::context::JSContext, _global: HandleObject) -> bool {
214        let realm = CurrentRealm::assert(cx);
215        let global_scope = GlobalScope::from_current_realm(&realm);
216        let url = global_scope.get_url();
217        (url.scheme() == "about" && url.as_str() != "about:blank") ||
218            ScriptThread::is_servo_privileged(url) ||
219            prefs::get().expose_servointernals_globally
220    }
221}