Skip to main content

Mountain/Environment/WebviewProvider/
Messaging.rs

1//! # WebviewProvider - Messaging Operations
2//!
3//! Implementation of webview message passing for
4//! [`MountainEnvironment`]
5//!
6//! Handles secure bidirectional communication between host and webview.
7
8use std::collections::HashMap;
9
10use CommonLibrary::{Error::CommonError::CommonError, IPC::SkyEvent::SkyEvent};
11use serde::{Deserialize, Serialize};
12use serde_json::Value;
13use tauri::{Emitter, Listener, Manager};
14use uuid::Uuid;
15
16use super::super::MountainEnvironment::MountainEnvironment;
17use crate::dev_log;
18
19/// Represents a Webview message
20#[derive(Debug, Clone, Serialize, Deserialize)]
21pub struct WebviewMessage {
22	pub MessageIdentifier:String,
23
24	pub MessageType:String,
25
26	pub Payload:Value,
27
28	pub Source:Option<String>,
29}
30
31/// Webview message handler context
32#[allow(dead_code)]
33struct WebviewMessageContext {
34	Handle:String,
35
36	SideCarIdentifier:Option<String>,
37
38	PendingResponses:HashMap<String, tokio::sync::oneshot::Sender<Value>>,
39}
40
41/// Messaging operations implementation for MountainEnvironment
42pub(super) async fn post_message_to_webview_impl(
43	env:&MountainEnvironment,
44
45	handle:String,
46
47	message:Value,
48) -> Result<bool, CommonError> {
49	dev_log!("extensions", "[WebviewProvider] Posting message to Webview: {}", handle);
50
51	if let Some(webview_window) = env.ApplicationHandle.get_webview_window(&handle) {
52		let webview_message = WebviewMessage {
53			MessageIdentifier:Uuid::new_v4().to_string(),
54
55			MessageType:"request".to_string(),
56
57			Payload:message,
58
59			Source:Some("host".to_string()),
60		};
61
62		webview_window
63			.emit::<WebviewMessage>(SkyEvent::WebviewPostMessage.AsStr(), webview_message)
64			.map_err(|error| {
65				CommonError::IPCError { Description:format!("Failed to post message to Webview: {}", error) }
66			})?;
67
68		dev_log!(
69			"extensions",
70			"[WebviewProvider] Message sent successfully to Webview: {}",
71			handle
72		);
73
74		Ok(true)
75	} else {
76		dev_log!(
77			"extensions",
78			"warn: [WebviewProvider] Webview not found for message: {}",
79			handle
80		);
81
82		Ok(false)
83	}
84}
85
86/// Sets up a message listener for a specific Webview.
87///
88/// When an extension iframe calls `acquireVsCodeApi().postMessage(data)`,
89/// the iframe's `pre/index.html` shim fires a `webview-message` Tauri event
90/// on the webview window. We forward it to Cocoon via
91/// `SendNotificationToSideCar("cocoon-main", "webview.message", {handle,
92/// message})` so the extension host's `onDidReceiveMessage` subscriber fires.
93pub(super) async fn setup_webview_message_listener_impl(
94	env:&MountainEnvironment,
95
96	handle:String,
97) -> Result<(), CommonError> {
98	dev_log!(
99		"extensions",
100		"[WebviewProvider] Setting up message listener for Webview: {}",
101		handle
102	);
103
104	if let Some(WebviewWin) = env.ApplicationHandle.get_webview_window(&handle) {
105		let H = handle.clone();
106
107		WebviewWin.listen("webview-message", move |Event| {
108			let H2 = H.clone();
109
110			let RawPayload = Event.payload();
111
112			let Parsed:Value = serde_json::from_str(RawPayload).unwrap_or_else(|_| {
113				// If it's not valid JSON, wrap as a string value so Cocoon
114				// still receives something meaningful.
115				Value::String(RawPayload.to_string())
116			});
117
118			tokio::spawn(async move {
119				let Notification = serde_json::json!({
120					"handle": H2,
121					"message": Parsed,
122				});
123
124				if let Err(E) = crate::Vine::Client::SendNotification::Fn(
125					"cocoon-main".to_string(),
126					"webview.message".to_string(),
127					Notification,
128				)
129				.await
130				{
131					dev_log!(
132						"extensions",
133						"warn: [WebviewProvider] webview.message notify failed handle={}: {}",
134						H2,
135						E
136					);
137				}
138			});
139		});
140
141		dev_log!(
142			"extensions",
143			"[WebviewProvider] Message listener installed for handle={}",
144			handle
145		);
146	} else {
147		dev_log!(
148			"extensions",
149			"warn: [WebviewProvider] Webview window not found for handle={}, listener skipped",
150			handle
151		);
152	}
153
154	Ok(())
155}
156
157/// Removes a message listener for a specific Webview.
158/// Tauri's `listen` returns an unlisten closure; for simplicity we rely
159/// on the webview window being destroyed (which drops all its listeners)
160/// rather than storing the handle. Future work: store in a global map.
161pub(super) async fn remove_webview_message_listener_impl(_env:&MountainEnvironment, handle:&str) {
162	dev_log!(
163		"extensions",
164		"[WebviewProvider] Message listener unregistered for handle={}",
165		handle
166	);
167}