firebase_rs_sdk/messaging/
support.rs

1//! Environment capability checks for Firebase Messaging.
2//!
3//! The JavaScript SDK exposes `isSupported()` so consumers can guard calls to
4//! messaging APIs on browsers that implement the Notification and Push APIs.
5//! This module mirrors the behaviour for WebAssembly builds and falls back to
6//! `false` for native targets.
7
8#[cfg(all(feature = "wasm-web", target_arch = "wasm32"))]
9use wasm_bindgen::{JsCast, JsValue};
10
11/// Returns `true` when the current environment exposes the browser APIs that
12/// Firebase Cloud Messaging requires.
13///
14/// Port of `packages/messaging/src/api/isSupported.ts` in the Firebase JS SDK.
15///
16/// # Examples
17///
18/// ```
19/// use firebase_rs_sdk::messaging;
20///
21/// if messaging::is_supported() {
22///     // Safe to call messaging APIs that rely on browser push features.
23/// }
24/// ```
25#[cfg(all(feature = "wasm-web", target_arch = "wasm32"))]
26pub fn is_supported() -> bool {
27    let window = match web_sys::window() {
28        Some(window) => window,
29        None => return false,
30    };
31    let navigator = window.navigator();
32
33    if !navigator.cookie_enabled() {
34        return false;
35    }
36
37    if navigator.service_worker().is_none() {
38        return false;
39    }
40
41    // Ensure indexedDB is available. We only check that the factory exists; the
42    // JavaScript SDK further verifies openability, which we can add once async
43    // event handling is wired up.
44    match window.indexed_db() {
45        Ok(Some(_)) => {}
46        _ => return false,
47    }
48
49    let window_js = JsValue::from(window.clone());
50    if !property_in(&window_js, "PushManager")
51        || !property_in(&window_js, "Notification")
52        || !property_in(&window_js, "fetch")
53    {
54        return false;
55    }
56
57    if !prototype_has_property(&window_js, "ServiceWorkerRegistration", "showNotification") {
58        return false;
59    }
60
61    if !prototype_has_property(&window_js, "PushSubscription", "getKey") {
62        return false;
63    }
64
65    true
66}
67
68#[cfg(all(feature = "wasm-web", target_arch = "wasm32"))]
69fn property_in(target: &JsValue, property: &str) -> bool {
70    js_sys::Reflect::has(target, &JsValue::from_str(property)).unwrap_or(false)
71}
72
73#[cfg(all(feature = "wasm-web", target_arch = "wasm32"))]
74fn prototype_has_property(target: &JsValue, constructor: &str, property: &str) -> bool {
75    let ctor = match js_sys::Reflect::get(target, &JsValue::from_str(constructor)) {
76        Ok(value) => value,
77        Err(_) => return false,
78    };
79
80    let prototype = match js_sys::Reflect::get(&ctor, &JsValue::from_str("prototype")) {
81        Ok(value) => value,
82        Err(_) => return false,
83    };
84
85    prototype
86        .dyn_ref::<js_sys::Object>()
87        .map(|obj| obj.has_own_property(&JsValue::from_str(property)))
88        .unwrap_or(false)
89}
90
91/// Returns `false` outside a web environment, where the required browser APIs
92/// are unavailable.
93#[cfg(not(all(feature = "wasm-web", target_arch = "wasm32")))]
94pub fn is_supported() -> bool {
95    false
96}
97
98#[cfg(all(test, not(all(feature = "wasm-web", target_arch = "wasm32"))))]
99mod tests {
100    #[test]
101    fn non_wasm_targets_are_not_supported() {
102        assert!(!super::is_supported());
103    }
104}