script/dom/bindings/
refcounted.rs1use std::cell::RefCell;
10use std::collections::hash_map::Entry::{Occupied, Vacant};
11use std::rc::Rc;
12
13use js::context::JSContext;
14use js::conversions::ToJSValConvertible;
15use js::jsapi::JSTracer;
16use rustc_hash::FxHashMap;
17use script_bindings::error::Error;
18pub(crate) use script_bindings::refcounted::Trusted;
19use script_bindings::reflector::DomObject;
20use script_bindings::trace::trace_reflector;
21
22use crate::dom::promise::{Promise, RootedPromise};
23use crate::tasks::task::TaskOnce;
24
25thread_local!(pub(super) static LIVE_PROMISE_REFERENCES: LivePromiseReferences =
26 LivePromiseReferences {
27 promise_table: RefCell::new(FxHashMap::default()),
28 }
29);
30
31pub(crate) struct LivePromiseReferences {
34 promise_table: RefCell<FxHashMap<*const Promise, Vec<Rc<Promise>>>>,
36}
37
38impl LivePromiseReferences {
39 pub(crate) fn destruct() {
40 LIVE_PROMISE_REFERENCES.with(|live_references| {
41 let _ = live_references.promise_table.take();
42 });
43 }
44
45 fn addref_promise(&self, promise: Rc<Promise>) {
46 let mut table = self.promise_table.borrow_mut();
47 table.entry(&*promise).or_default().push(promise)
48 }
49}
50
51pub struct TrustedPromise {
56 dom_object: *const Promise,
57 owner_thread: *const libc::c_void,
58}
59
60unsafe impl Send for TrustedPromise {}
61
62impl TrustedPromise {
63 pub(crate) fn new(promise: Rc<Promise>) -> TrustedPromise {
67 LIVE_PROMISE_REFERENCES.with(|live_references| {
68 let ptr = &raw const *promise;
69 live_references.addref_promise(promise);
70 TrustedPromise {
71 dom_object: ptr,
72 owner_thread: (live_references) as *const _ as *const libc::c_void,
73 }
74 })
75 }
76
77 pub(crate) fn root(self, cx: &JSContext) -> RootedPromise {
81 LIVE_PROMISE_REFERENCES.with(|live_references| {
82 assert_eq!(
83 self.owner_thread,
84 live_references as *const _ as *const libc::c_void
85 );
86 match live_references
87 .promise_table
88 .borrow_mut()
89 .entry(self.dom_object)
90 {
91 Occupied(mut entry) => {
92 let promise = {
93 let promises = entry.get_mut();
94 promises
95 .pop()
96 .expect("rooted promise list unexpectedly empty")
97 .duplicate(cx)
98 };
99 if entry.get().is_empty() {
100 entry.remove();
101 }
102 promise
103 },
104 Vacant(_) => unreachable!(),
105 }
106 })
107 }
108
109 pub(crate) fn reject_task(self, error: Error) -> impl TaskOnce {
111 let this = self;
112 task!(reject_promise: move |cx| {
113 debug!("Rejecting promise.");
114 this.root(cx).reject_error(cx, error);
115 })
116 }
117
118 pub(crate) fn resolve_task<T>(self, value: T) -> impl TaskOnce
120 where
121 T: ToJSValConvertible + Send,
122 {
123 let this = self;
124 task!(resolve_promise: move |cx| {
125 debug!("Resolving promise.");
126 this.root(cx).resolve_native(cx, &value);
127 })
128 }
129}
130
131pub(crate) unsafe fn trace_refcounted_objects(tracer: *mut JSTracer) {
133 trace!("tracing live refcounted promise references");
134 LIVE_PROMISE_REFERENCES.with(|live_references| {
135 let table = live_references.promise_table.borrow_mut();
136 for promise in table.keys() {
137 unsafe {
138 trace_reflector(tracer, "refcounted", (**promise).reflector());
139 }
140 }
141 });
142 trace!("tracing live refcounted references");
143 unsafe {
144 script_bindings::refcounted::trace_live_domreferences(tracer);
145 }
146}