use crate::host_bridge::protocol::{HOST_BRIDGE_PROTOCOL, HOST_BRIDGE_VERSION}; use serde_json::{json, Value}; const HOST_BRIDGE_EVENTS: [&str; 3] = [ "app.lifecycle", "navigation.canGoBack", "file.imageDropped", ]; fn is_host_bridge_event_name(event: &str) -> bool { HOST_BRIDGE_EVENTS.contains(&event) } pub(crate) fn host_bridge_event_script( event: &str, payload: Value, ) -> Result { if !is_host_bridge_event_name(event) { return Err(serde_json::Error::io(std::io::Error::new( std::io::ErrorKind::InvalidInput, format!("unknown HostBridge event: {}", event), ))); } let message = json!({ "bridge": HOST_BRIDGE_PROTOCOL, "version": HOST_BRIDGE_VERSION, "event": event, "payload": payload, }); let data = serde_json::to_string(&message)?; let data_literal = serde_json::to_string(&data)?; Ok(format!( "window.dispatchEvent(new MessageEvent('message', {{ data: {}, origin: window.location.origin, source: window }})); true;", data_literal )) } #[cfg(test)] mod tests { use super::*; use serde_json::json; #[test] fn host_bridge_event_script_dispatches_lifecycle_message() { let script = host_bridge_event_script( "app.lifecycle", json!({ "state": "active", "focused": true, "nativeState": "focused", }), ) .expect("event script"); assert!(script.contains("MessageEvent('message'")); assert!(script.contains("origin: window.location.origin")); assert!(script.contains("source: window")); assert!(script.contains("GenarrativeHostBridge")); assert!(script.contains("app.lifecycle")); assert!(script.contains("\\\"state\\\":\\\"active\\\"")); assert!(script.contains("\\\"focused\\\":true")); } #[test] fn host_bridge_event_script_rejects_unknown_events() { let error = host_bridge_event_script("unknown.event", json!({})).expect_err("unknown event"); assert!(error.to_string().contains("unknown HostBridge event")); } }