script/dom/geolocation/
geolocation.rs1use std::cell::{Cell, RefCell};
5use std::rc::Rc;
6
7use dom_struct::dom_struct;
8use js::context::JSContext;
9use js::gc::HandleValue;
10use js::jsapi::IsCallable;
11use rustc_hash::FxHashSet;
12use script_bindings::callback::{ExceptionHandling, OwnerWindow};
13use script_bindings::codegen::GenericBindings::GeolocationBinding::Geolocation_Binding::GeolocationMethods;
14use script_bindings::codegen::GenericBindings::GeolocationBinding::{
15 PositionCallback, PositionErrorCallback, PositionOptions,
16};
17use script_bindings::codegen::GenericBindings::PermissionStatusBinding::PermissionName;
18use script_bindings::codegen::GenericBindings::WindowBinding::WindowMethods;
19use script_bindings::domstring::DOMString;
20use script_bindings::error::{Error, Fallible};
21use script_bindings::reflector::{Reflector, reflect_dom_object_with_cx};
22use script_bindings::root::DomRoot;
23
24use crate::dom::bindings::codegen::DomTypeHolder::DomTypeHolder;
25use crate::dom::bindings::reflector::DomGlobal;
26use crate::dom::geolocationpositionerror::GeolocationPositionError;
27use crate::dom::globalscope::GlobalScope;
28
29fn cast_error_callback(
30 cx: &mut JSContext,
31 error_callback: HandleValue,
32) -> Fallible<Option<Rc<PositionErrorCallback<DomTypeHolder>>>> {
33 if error_callback.get().is_object() {
34 let error_callback = error_callback.to_object();
35 #[expect(unsafe_code)]
36 unsafe {
37 if IsCallable(error_callback) {
38 Ok(Some(PositionErrorCallback::new(cx, error_callback)))
39 } else {
40 Err(Error::Type(c"Value is not callable.".to_owned()))
41 }
42 }
43 } else if error_callback.get().is_null_or_undefined() {
44 Ok(None)
45 } else {
46 Err(Error::Type(c"Value is not an object.".to_owned()))
47 }
48}
49
50#[dom_struct]
51pub struct Geolocation {
52 reflector_: Reflector,
53 watch_ids: RefCell<FxHashSet<u32>>,
55 next_watch_id: Cell<u32>,
56}
57
58impl Geolocation {
59 fn new_inherited() -> Self {
60 Geolocation {
61 reflector_: Reflector::new(),
62 watch_ids: RefCell::new(FxHashSet::default()),
63 next_watch_id: Cell::new(1),
64 }
65 }
66
67 pub(crate) fn new(cx: &mut JSContext, global: &GlobalScope) -> DomRoot<Self> {
68 reflect_dom_object_with_cx(Box::new(Self::new_inherited()), global, cx)
69 }
70
71 fn request_position(
73 &self,
74 cx: &mut JSContext,
75 _success_callback: Rc<PositionCallback<DomTypeHolder>>,
76 error_callback: Option<Rc<PositionErrorCallback<DomTypeHolder>>>,
77 _options: &PositionOptions,
78 watch_id: Option<u32>,
79 ) -> Fallible<()> {
80 let document = self.global().as_window().Document();
83 if !document.allowed_to_use_feature(PermissionName::Geolocation) {
85 if let Some(id) = watch_id {
86 self.watch_ids.borrow_mut().remove(&id);
88 }
89 if let Some(error_callback) = error_callback {
91 let position_error = GeolocationPositionError::permission_denied(
92 cx,
93 &self.global(),
94 DOMString::from("User denied Geolocation".to_string()),
95 );
96 error_callback.Call_(cx, self, &position_error, ExceptionHandling::Report)?;
97 }
98 return Ok(());
100 }
101 if !self.global().is_secure_context() {
103 if let Some(id) = watch_id {
104 self.watch_ids.borrow_mut().remove(&id);
106 }
107 if let Some(error_callback) = error_callback {
109 let position_error = GeolocationPositionError::permission_denied(
110 cx,
111 &self.global(),
112 DOMString::from("Insecure context for Geolocation".to_string()),
113 );
114 error_callback.Call_(cx, self, &position_error, ExceptionHandling::Report)?;
115 }
116 return Ok(());
118 }
119 Ok(())
123 }
124}
125
126impl GeolocationMethods<DomTypeHolder> for Geolocation {
127 fn GetCurrentPosition(
129 &self,
130 cx: &mut JSContext,
131 success_callback: Rc<PositionCallback<DomTypeHolder>>,
132 error_callback: HandleValue,
133 options: &PositionOptions,
134 ) -> Fallible<()> {
135 let error_callback = cast_error_callback(cx, error_callback)?;
136 if !self.global().as_window().Document().is_active() {
138 if let Some(error_callback) = error_callback {
140 let position_error = GeolocationPositionError::position_unavailable(
141 cx,
142 &self.global(),
143 DOMString::from("Document is not fully active".to_string()),
144 );
145 error_callback.Call_(cx, self, &position_error, ExceptionHandling::Report)?;
146 }
147 return Ok(());
149 }
150 self.request_position(cx, success_callback, error_callback, options, None)
152 }
153
154 fn WatchPosition(
156 &self,
157 cx: &mut JSContext,
158 success_callback: Rc<PositionCallback<DomTypeHolder>>,
159 error_callback: HandleValue,
160 options: &PositionOptions,
161 ) -> Fallible<i32> {
162 let error_callback = cast_error_callback(cx, error_callback)?;
163 if !self.global().as_window().Document().is_active() {
165 if let Some(error_callback) = error_callback {
167 let position_error = GeolocationPositionError::position_unavailable(
168 cx,
169 &self.global(),
170 DOMString::from("Document is not fully active".to_string()),
171 );
172 error_callback.Call_(cx, self, &position_error, ExceptionHandling::Report)?;
173 }
174 return Ok(0);
176 }
177 let watch_id = self.next_watch_id.get();
179 self.next_watch_id.set(watch_id + 1);
180 self.watch_ids.borrow_mut().insert(watch_id);
182 self.request_position(
184 cx,
185 success_callback,
186 error_callback,
187 options,
188 Some(watch_id),
189 )?;
190 Ok(watch_id as i32)
192 }
193
194 fn ClearWatch(&self, watch_id: i32) {
196 let watch_id = u32::try_from(watch_id).ok();
197 if let Some(id) = watch_id {
198 self.watch_ids.borrow_mut().remove(&id);
199 }
200 }
201}
202
203impl OwnerWindow<crate::DomTypeHolder> for Geolocation {}