Skip to main content

Mountain/Environment/WebviewProvider/
Lifecycle.rs

1//! # WebviewProvider - Lifecycle Operations
2//!
3//! Implementation of webview panel lifecycle methods for
4//! [`MountainEnvironment`]
5//!
6//! Handles creation, disposal, and visibility management of webview panels.
7
8use CommonLibrary::{
9	Error::CommonError::CommonError,
10	Webview::DTO::WebviewContentOptionsDTO::WebviewContentOptionsDTO,
11};
12use serde_json::{Value, json};
13use tauri::{Emitter, Manager, WebviewWindowBuilder};
14use uuid::Uuid;
15
16use super::super::{MountainEnvironment::MountainEnvironment, Utility};
17use crate::{ApplicationState::DTO::WebviewStateDTO::WebviewStateDTO, dev_log};
18
19/// Lifecycle operations implementation for MountainEnvironment
20pub(super) async fn create_webview_panel_impl(
21	env:&MountainEnvironment,
22	extension_data_value:Value,
23	view_type:String,
24	title:String,
25	_show_options_value:Value,
26	panel_options_value:Value,
27	content_options_value:Value,
28) -> Result<String, CommonError> {
29	let handle = Uuid::new_v4().to_string();
30
31	dev_log!(
32		"extensions",
33		"[WebviewProvider] Creating WebviewPanel with handle: {}, viewType: {}",
34		handle,
35		view_type
36	);
37
38	// Parse content options to ensure security settings
39	let content_options:WebviewContentOptionsDTO =
40		serde_json::from_value(content_options_value.clone()).map_err(|error| {
41			CommonError::InvalidArgument { ArgumentName:"ContentOptions".into(), Reason:error.to_string() }
42		})?;
43
44	let state = WebviewStateDTO {
45		Handle:handle.clone(),
46		ViewType:view_type.clone(),
47		Title:title.clone(),
48		ContentOptions:content_options,
49		PanelOptions:panel_options_value.clone(),
50		SideCarIdentifier:"cocoon-main".to_string(),
51		ExtensionIdentifier:extension_data_value
52			.get("id")
53			.and_then(|v| v.as_str())
54			.unwrap_or_default()
55			.to_string(),
56		IsActive:true,
57		IsVisible:true,
58	};
59
60	// Store the initial state with lifecycle state
61	{
62		let mut webview_guard = env
63			.ApplicationState
64			.Feature
65			.Webviews
66			.ActiveWebviews
67			.lock()
68			.map_err(Utility::MapApplicationStateLockErrorToCommonError)?;
69
70		webview_guard.insert(handle.clone(), state);
71	}
72
73	// Create a new Tauri window for this webview with security settings
74	let title_clone = title.clone();
75	let _webview_window = WebviewWindowBuilder::new(
76		&env.ApplicationHandle,
77		&handle,
78		tauri::WebviewUrl::App("WebviewHost.html".into()),
79	)
80	.title(title)
81	.initialization_script(&format!(
82		"window.__WEBVIEW_INITIAL_STATE__ = {};",
83		json!({
84			"Handle": handle,
85			"ViewType": view_type,
86			"Title": title_clone
87		})
88	))
89	.build()
90	.map_err(|error| {
91		dev_log!(
92			"extensions",
93			"error: [WebviewProvider] Failed to create Webview window: {}",
94			error
95		);
96		CommonError::UserInterfaceInteraction { Reason:error.to_string() }
97	})?;
98
99	// Setup message listener for this Webview
100	crate::Environment::WebviewProvider::Messaging::setup_webview_message_listener_impl(env, handle.clone()).await?;
101
102	// Notify frontend about Webview creation
103	env.ApplicationHandle
104		.emit::<Value>(
105			"sky://webview/created",
106			json!({ "Handle": handle.clone(), "ViewType": view_type.clone(), "Title": title_clone }),
107		)
108		.map_err(|error| {
109			CommonError::IPCError { Description:format!("Failed to emit Webview creation event: {}", error) }
110		})?;
111
112	Ok(handle)
113}
114
115/// Disposes a Webview panel and cleans up all associated resources.
116pub(super) async fn dispose_webview_panel_impl(env:&MountainEnvironment, handle:String) -> Result<(), CommonError> {
117	dev_log!("extensions", "[WebviewProvider] Disposing WebviewPanel: {}", handle);
118
119	// Remove message listener
120	let _ = crate::Environment::WebviewProvider::Messaging::remove_webview_message_listener_impl(env, &handle).await;
121
122	// Close the window
123	if let Some(webview_window) = env.ApplicationHandle.get_webview_window(&handle) {
124		if let Err(error) = webview_window.close() {
125			dev_log!(
126				"extensions",
127				"warn: [WebviewProvider] Failed to close Webview window: {}",
128				error
129			);
130		}
131	}
132
133	// Remove state
134	env.ApplicationState
135		.Feature
136		.Webviews
137		.ActiveWebviews
138		.lock()
139		.map_err(Utility::MapApplicationStateLockErrorToCommonError)?
140		.remove(&handle);
141
142	// Notify frontend about Webview disposal
143	env.ApplicationHandle
144		.emit::<Value>("sky://webview/disposed", json!({ "Handle": handle }))
145		.map_err(|error| {
146			CommonError::IPCError { Description:format!("Failed to emit Webview disposal event: {}", error) }
147		})?;
148
149	Ok(())
150}
151
152/// Reveals (shows and focuses) a Webview panel.
153pub(super) async fn reveal_webview_panel_impl(
154	env:&MountainEnvironment,
155	handle:String,
156	_show_options_value:Value,
157) -> Result<(), CommonError> {
158	dev_log!("extensions", "[WebviewProvider] Revealing WebviewPanel: {}", handle);
159
160	if let Some(webview_window) = env.ApplicationHandle.get_webview_window(&handle) {
161		webview_window.show().map_err(|error| {
162			CommonError::UserInterfaceInteraction { Reason:format!("Failed to show Webview window: {}", error) }
163		})?;
164
165		webview_window.set_focus().map_err(|error| {
166			CommonError::UserInterfaceInteraction { Reason:format!("Failed to focus Webview window: {}", error) }
167		})?;
168
169		// Update visibility state
170		{
171			let mut webview_guard = env
172				.ApplicationState
173				.Feature
174				.Webviews
175				.ActiveWebviews
176				.lock()
177				.map_err(Utility::MapApplicationStateLockErrorToCommonError)?;
178
179			if let Some(state) = webview_guard.get_mut(&handle) {
180				state.IsVisible = true;
181			}
182		}
183
184		// Emit visibility event
185		env.ApplicationHandle
186			.emit::<Value>("sky://webview/revealed", json!({ "Handle": handle }))
187			.map_err(|error| {
188				CommonError::IPCError { Description:format!("Failed to emit Webview revealed event: {}", error) }
189			})?;
190	}
191
192	Ok(())
193}