1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
//! Global context of each worker type.

use wasm_bindgen::JsCast;
use web_sys::{DedicatedWorkerGlobalScope, SharedWorkerGlobalScope, Window, WorkerGlobalScope};

use super::js::{GlobalExt, WindowOrWorkerExt};

thread_local! {
	static GLOBAL: Global = {
		let global: GlobalExt = js_sys::global().unchecked_into();

		if !global.window().is_undefined() {
			Global::Window(global.unchecked_into())
		} else if !global.dedicated_worker_global_scope().is_undefined() {
			Global::Dedicated(global.unchecked_into())
		} else if !global.shared_worker_global_scope().is_undefined() {
			Global::Shared(global.unchecked_into())
		} else if !global.service_worker_global_scope().is_undefined() {
			Global::Service(global.unchecked_into())
		} else if !global.worklet_global_scope().is_undefined() {
			Global::Worklet
		} else if !global.worker_global_scope().is_undefined() {
			Global::Worker(global.unchecked_into())
		} else {
			Global::Unknown
		}
	};
}

/// Global context.
pub(super) enum Global {
	/// [`Window`].
	Window(Window),
	/// [`DedicatedWorkerGlobalScope`].
	Dedicated(DedicatedWorkerGlobalScope),
	/// [`SharedWorkerGlobalScope`].
	Shared(SharedWorkerGlobalScope),
	/// Service worker.
	Service(WorkerGlobalScope),
	/// Unknown worker type.
	Worker(WorkerGlobalScope),
	/// Worklet.
	Worklet,
	/// Unknown.
	Unknown,
}

impl Global {
	/// Executes the given `task` with [`Global`].
	pub(super) fn with<R>(task: impl FnOnce(&Self) -> R) -> R {
		GLOBAL.with(task)
	}

	/// Converts the global type to [`WindowOrWorkerExt`] when appropriate and
	/// executes the given `task` with it.
	pub(super) fn with_window_or_worker<R>(
		task: impl FnOnce(&WindowOrWorkerExt) -> R,
	) -> Option<R> {
		GLOBAL.with(|global| {
			let global: &WindowOrWorkerExt = match global {
				Self::Window(window) => window.unchecked_ref(),
				Self::Dedicated(worker) => worker.unchecked_ref(),
				Self::Service(worker) | Self::Worker(worker) => worker.unchecked_ref(),
				Self::Shared(worker) => worker.unchecked_ref(),
				Self::Worklet | Self::Unknown => return None,
			};

			Some(task(global))
		})
	}
}