Mountain/Environment/
WebViewProvider.rs

1// File: Mountain/Source/Environment/WebViewProvider.rs
2// Role: Implements the `WebViewProvider` trait for the `MountainEnvironment`.
3// Responsibilities:
4//   - Core logic for creating and managing WebView instances.
5//   - Uses Tauri's multi-window capabilities to host WebView content.
6//   - Manages WebView state in `ApplicationState` and pushes updates to the
7//     frontend.
8
9//! # WebViewProvider Implementation
10//!
11//! Implements the `WebViewProvider` trait for the `MountainEnvironment`. This
12//! provider contains the core logic for creating and managing WebView
13//! instances using Tauri's multi-window capabilities.
14
15#![allow(non_snake_case, non_camel_case_types)]
16
17use Common::{Error::CommonError::CommonError, WebView::WebViewProvider::WebViewProvider};
18use async_trait::async_trait;
19use log::info;
20use serde_json::{Value, json};
21use tauri::{Emitter, Manager, WebviewWindowBuilder};
22
23use super::{MountainEnvironment::MountainEnvironment, Utility};
24use crate::ApplicationState::DTO::WebViewStateDTO::WebViewStateDTO;
25
26#[async_trait]
27impl WebViewProvider for MountainEnvironment {
28	async fn CreateWebViewPanel(
29		&self,
30
31		ExtensionDataValue:Value,
32
33		ViewType:String,
34
35		Title:String,
36
37		_ShowOptionsValue:Value,
38
39		PanelOptionsValue:Value,
40
41		ContentOptionsValue:Value,
42	) -> Result<String, CommonError> {
43		let Handle = uuid::Uuid::new_v4().to_string();
44
45		info!("[WebViewProvider] Creating WebViewPanel with handle: {}", Handle);
46
47		let State = WebViewStateDTO {
48			Handle:Handle.clone(),
49
50			ViewType,
51
52			Title:Title.clone(),
53
54			ContentOptions:serde_json::from_value(ContentOptionsValue)?,
55
56			PanelOptions:PanelOptionsValue,
57
58			// TODO: This should come from request context
59			SideCarIdentifier:"cocoon-main".to_string(),
60
61			ExtensionIdentifier:ExtensionDataValue
62				.get("id")
63				.and_then(|v| v.as_str())
64				.unwrap_or_default()
65				.to_string(),
66
67			IsActive:true,
68
69			IsVisible:true,
70		};
71
72		// Store the initial state.
73		self.ApplicationState
74			.ActiveWebViews
75			.lock()
76			.map_err(Utility::MapApplicationStateLockErrorToCommonError)?
77			.insert(Handle.clone(), State.clone());
78
79		// Create a new Tauri window for this webview.
80		WebviewWindowBuilder::new(
81			&self.ApplicationHandle,
82			&Handle,
83			tauri::WebviewUrl::App("WebviewHost.html".into()),
84		)
85		.title(Title)
86		.initialization_script(&format!("window.__WEBVIEW_INITIAL_STATE__ = {}", json!(State)))
87		.build()
88		.map_err(|Error| CommonError::UserInterfaceInteraction { Reason:Error.to_string() })?;
89
90		Ok(Handle)
91	}
92
93	async fn DisposeWebViewPanel(&self, Handle:String) -> Result<(), CommonError> {
94		info!("[WebViewProvider] Disposing WebViewPanel: {}", Handle);
95
96		if let Some(WebviewWindow) = self.ApplicationHandle.get_webview_window(&Handle) {
97			WebviewWindow
98				.close()
99				.map_err(|Error| CommonError::UserInterfaceInteraction { Reason:Error.to_string() })?;
100		}
101
102		self.ApplicationState
103			.ActiveWebViews
104			.lock()
105			.map_err(Utility::MapApplicationStateLockErrorToCommonError)?
106			.remove(&Handle);
107
108		Ok(())
109	}
110
111	async fn RevealWebViewPanel(&self, Handle:String, _ShowOptionsValue:Value) -> Result<(), CommonError> {
112		if let Some(WebviewWindow) = self.ApplicationHandle.get_webview_window(&Handle) {
113			WebviewWindow
114				.set_focus()
115				.map_err(|Error| CommonError::UserInterfaceInteraction { Reason:Error.to_string() })?;
116		}
117
118		Ok(())
119	}
120
121	async fn SetWebViewOptions(&self, Handle:String, OptionsValue:Value) -> Result<(), CommonError> {
122		if let Some(WebviewWindow) = self.ApplicationHandle.get_webview_window(&Handle) {
123			if let Some(Title) = OptionsValue.get("title").and_then(|v| v.as_str()) {
124				WebviewWindow
125					.set_title(Title)
126					.map_err(|Error| CommonError::UserInterfaceInteraction { Reason:Error.to_string() })?;
127			}
128
129			// TODO: Implement icon path setting.
130		}
131
132		Ok(())
133	}
134
135	async fn SetWebViewHTML(&self, Handle:String, HTML:String) -> Result<(), CommonError> {
136		if let Some(WebviewWindow) = self.ApplicationHandle.get_webview_window(&Handle) {
137			WebviewWindow
138				.emit("sky://webview/set-html", HTML)
139				.map_err(|Error| CommonError::IPCError { Description:Error.to_string() })?;
140		}
141
142		Ok(())
143	}
144
145	async fn PostMessageToWebView(&self, Handle:String, Message:Value) -> Result<bool, CommonError> {
146		if let Some(WebviewWindow) = self.ApplicationHandle.get_webview_window(&Handle) {
147			WebviewWindow
148				.emit("sky://webview/post-message", Message)
149				.map_err(|Error| CommonError::IPCError { Description:Error.to_string() })?;
150
151			return Ok(true);
152		}
153
154		Ok(false)
155	}
156}