Skip to content

Comments

feat: 将webview2安装修改为解压并使用#75

Open
Rbqwow wants to merge 2 commits intoMistEO:mainfrom
Rbqwow:refactor/fixed-webview2
Open

feat: 将webview2安装修改为解压并使用#75
Rbqwow wants to merge 2 commits intoMistEO:mainfrom
Rbqwow:refactor/fixed-webview2

Conversation

@Rbqwow
Copy link

@Rbqwow Rbqwow commented Feb 19, 2026

怎么七点了写不动 description 了找 LLM 写了


WebView2 Fixed Version Runtime 支持

将 WebView2 从系统安装(Evergreen Bootstrapper)改为本地固定版本运行时(Fixed Version Runtime),下载后解压到 exe 同目录使用,不影响系统环境,且在 MXU 自更新后仍然生效。

主要变更

核心功能:本地 WebView2 运行时

  • 从微软 CDN 下载 WebView2 Fixed Version Runtime .cab 文件,使用 expand.exe 解压到 exe 同目录下的 webview2_runtime/ 文件夹
  • 通过设置 WEBVIEW2_BROWSER_EXECUTABLE_FOLDER 环境变量让 Tauri 使用本地运行时,不依赖系统安装的 WebView2
  • 自更新安全:webview2_runtime/ 目录不会被更新流程覆盖

触发逻辑

  • 系统 WebView2 可用且未被禁用 → 直接使用系统版本
  • 系统 WebView2 被禁用 → 弹窗提示禁用原因及修复方法,点击确定后下载独立运行时
  • 系统 WebView2 未安装 → 直接下载独立运行时

本地 cab 文件支持

  • 启动时检测 exe 同目录下是否已存在 .cab 文件,存在则跳过下载直接解压
  • 校验 cab 文件架构(x64/arm64)是否与当前系统匹配,不匹配则弹窗警告并继续下载
  • 多个 cab 共存时优先匹配当前架构
  • 方便网络环境不佳的用户手动放置 cab 文件使用

下载体验优化

  • 流式下载,使用 256KB 缓冲区持续写入,避免分段下载造成的卡顿
  • 进度条 UI 更新节流(≥200ms 间隔),避免 SendMessageW 跨线程同步调用阻塞下载
  • 下载失败时提示手动下载 cab 文件的方法及预期文件名

UI 修复

  • 进度对话框点击关闭按钮(×)时正确退出进程,不再后台残留
  • 使用 AdjustWindowRect 计算窗口尺寸,确保对话框按钮不会被非客户区挤出可视范围

Summary by Sourcery

将 WebView2 集成方式从系统范围的 Evergreen 安装器切换为在可执行文件旁解压的本地固定版本运行时,并改进下载处理和对话框体验。

New Features:

  • 支持使用打包的 WebView2 固定版本运行时(Fixed Version Runtime),解压到与可执行文件同级的 webview2_runtime 目录中运行。
  • 允许用户在可执行文件目录中提供预先下载好的 WebView2 固定版本运行时 .cab 文件,以便在离线或低网络环境中完成安装。

Bug Fixes:

  • 修复进度对话框的关闭按钮会留下后台进程的问题:当用户在下载进行中关闭对话框时,立即退出应用程序。

Enhancements:

  • 在系统 WebView2 可用且未被禁用时优先使用系统 WebView2,否则通过环境变量自动下载并配置本地固定版本运行时。
  • 简化 WebView2 运行时的下载流程:使用缓冲流式下载、节流的 UI 进度更新,以及更清晰的错误信息(包括手动下载指引)。
  • 在应用启动时检查本地运行时目录是否完整,以持久化并复用先前解压的本地 WebView2 运行时,跨应用多次运行及自更新保持可用。
  • 改进进度对话框行为,确保关闭对话框时进程可以干净退出,并通过 AdjustWindowRect 调整窗口大小以保证控件可见。
Original summary in English

Summary by Sourcery

Switch WebView2 integration from system-wide Evergreen installer to a local fixed-version runtime unpacked alongside the executable, with improved download handling and dialogs.

New Features:

  • Support running against a bundled WebView2 Fixed Version Runtime extracted to a webview2_runtime directory next to the executable.
  • Allow users to supply pre-downloaded WebView2 Fixed Version Runtime .cab files in the executable directory for offline or low-connectivity installation.

Bug Fixes:

  • Fix progress dialog close button leaving a background process by exiting the app when the user closes an in-progress dialog.

Enhancements:

  • Prefer system WebView2 when available and not disabled, otherwise automatically download and configure the local fixed runtime via environment variables.
  • Streamline WebView2 runtime downloading with buffered streaming, throttled UI progress updates, and clearer error messaging including manual download guidance.
  • Persist and reuse a previously extracted local WebView2 runtime across application runs and self-updates by checking for a complete runtime directory at startup.
  • Improve progress dialog behavior so closing it exits the process cleanly and ensure window sizing via AdjustWindowRect keeps controls visible.
Original summary in English

Summary by Sourcery

将 WebView2 集成方式从系统范围的 Evergreen 安装器切换为在可执行文件旁解压的本地固定版本运行时,并改进下载处理和对话框体验。

New Features:

  • 支持使用打包的 WebView2 固定版本运行时(Fixed Version Runtime),解压到与可执行文件同级的 webview2_runtime 目录中运行。
  • 允许用户在可执行文件目录中提供预先下载好的 WebView2 固定版本运行时 .cab 文件,以便在离线或低网络环境中完成安装。

Bug Fixes:

  • 修复进度对话框的关闭按钮会留下后台进程的问题:当用户在下载进行中关闭对话框时,立即退出应用程序。

Enhancements:

  • 在系统 WebView2 可用且未被禁用时优先使用系统 WebView2,否则通过环境变量自动下载并配置本地固定版本运行时。
  • 简化 WebView2 运行时的下载流程:使用缓冲流式下载、节流的 UI 进度更新,以及更清晰的错误信息(包括手动下载指引)。
  • 在应用启动时检查本地运行时目录是否完整,以持久化并复用先前解压的本地 WebView2 运行时,跨应用多次运行及自更新保持可用。
  • 改进进度对话框行为,确保关闭对话框时进程可以干净退出,并通过 AdjustWindowRect 调整窗口大小以保证控件可见。
Original summary in English

Summary by Sourcery

Switch WebView2 integration from system-wide Evergreen installer to a local fixed-version runtime unpacked alongside the executable, with improved download handling and dialogs.

New Features:

  • Support running against a bundled WebView2 Fixed Version Runtime extracted to a webview2_runtime directory next to the executable.
  • Allow users to supply pre-downloaded WebView2 Fixed Version Runtime .cab files in the executable directory for offline or low-connectivity installation.

Bug Fixes:

  • Fix progress dialog close button leaving a background process by exiting the app when the user closes an in-progress dialog.

Enhancements:

  • Prefer system WebView2 when available and not disabled, otherwise automatically download and configure the local fixed runtime via environment variables.
  • Streamline WebView2 runtime downloading with buffered streaming, throttled UI progress updates, and clearer error messaging including manual download guidance.
  • Persist and reuse a previously extracted local WebView2 runtime across application runs and self-updates by checking for a complete runtime directory at startup.
  • Improve progress dialog behavior so closing it exits the process cleanly and ensure window sizing via AdjustWindowRect keeps controls visible.

Copy link
Contributor

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - 我发现了两个问题,并给出了一些整体性的反馈:

  • 临时解压目录 temp_dir.join("mxu_webview2_extract") 是固定路径;建议改为每次运行唯一的目录(例如包含 PID 或随机后缀),以避免在多个实例并发运行时或之前的运行遗留目录时出现冲突或竞争条件。
给 AI Agent 的提示词
请根据本次代码审查中的评论进行修改:

## 总体说明
- 临时解压目录 `temp_dir.join("mxu_webview2_extract")` 是固定路径;建议改为每次运行唯一的目录(例如包含 PID 或随机后缀),以避免在多个实例并发运行时或之前的运行遗留目录时出现冲突或竞争条件。

## 单条评论

### Comment 1
<location> `src-tauri/src/webview2/install.rs:97-99` </location>
<code_context>
+}
+
+/// 解压 cab 文件到 WebView2 运行时目录
+fn extract_cab_to_runtime(cab_path: &std::path::Path, runtime_dir: &std::path::Path) -> Result<(), String> {
+    let temp_dir = std::env::temp_dir();
+    let extract_temp = temp_dir.join("mxu_webview2_extract");
+
+    let _ = std::fs::remove_dir_all(&extract_temp);
</code_context>

<issue_to_address>
**issue (bug_risk):** 使用固定的临时目录进行解压,在多实例情况下可能产生冲突。

解压路径始终是 `%TEMP%/mxu_webview2_extract`,并且在每次运行前都会被删除。如果多个实例并发运行,或者其他进程也在复用这个 helper,它们可能会互相删除或覆盖对方的临时目录,导致偶发失败或清理不完全。应为每次运行使用唯一的临时目录(例如基于 PID/时间戳/随机后缀,或使用类似 `tempfile` 的 API)以避免这些竞争问题。
</issue_to_address>

### Comment 2
<location> `src-tauri/src/webview2/install.rs:184-193` </location>
<code_context>
+    }
+
+    // 优先使用架构匹配的 cab
+    if let Some(cab_path) = matched {
+        let progress_dialog = CustomDialog::new_progress(
+            "正在解压 WebView2",
+            "检测到本地 WebView2 运行时 cab 文件,正在解压...",
+        );
+
+        let result = extract_cab_to_runtime(&cab_path, runtime_dir);
+
+        if let Some(pw) = progress_dialog {
+            pw.close();
+        }
+
+        if result.is_ok() {
+            let _ = std::fs::remove_file(&cab_path);
+        }
+        return Some(result);
+    }
+
</code_context>

<issue_to_address>
**issue:** 本地 cab 解压失败时,并不会像注释所说那样回退到在线下载。

`try_extract_local_cab` 的文档说明在本地 cab 不可用时应该“返回 None 继续下载”,但如果存在匹配的 cab 且 `extract_cab_to_runtime` 失败(例如 cab 损坏/不完整),当前分支会返回 `Some(Err(_))`。此时 `download_and_extract` 会直接退出,而不会尝试在线下载,因此一次错误的本地 cab 可能会长期阻塞后续恢复。建议在解压失败时:(1) 删除该 cab 文件;(2) 返回 `None` 以触发在线下载;(3) 仅在在线下载也失败时再向上抛出错误。
</issue_to_address>

Sourcery 对开源项目免费——如果你觉得我们的代码审查有帮助,欢迎分享 ✨
帮我变得更有用!请对每条评论点 👍 或 👎,我会根据你的反馈改进后续的代码审查。
Original comment in English

Hey - I've found 2 issues, and left some high level feedback:

  • The temporary extraction directory temp_dir.join("mxu_webview2_extract") is a fixed path; consider using a per-run unique directory (e.g., including PID or a random suffix) to avoid clashes or race conditions when multiple instances run concurrently or when a previous run left the directory behind.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- The temporary extraction directory `temp_dir.join("mxu_webview2_extract")` is a fixed path; consider using a per-run unique directory (e.g., including PID or a random suffix) to avoid clashes or race conditions when multiple instances run concurrently or when a previous run left the directory behind.

## Individual Comments

### Comment 1
<location> `src-tauri/src/webview2/install.rs:97-99` </location>
<code_context>
+}
+
+/// 解压 cab 文件到 WebView2 运行时目录
+fn extract_cab_to_runtime(cab_path: &std::path::Path, runtime_dir: &std::path::Path) -> Result<(), String> {
+    let temp_dir = std::env::temp_dir();
+    let extract_temp = temp_dir.join("mxu_webview2_extract");
+
+    let _ = std::fs::remove_dir_all(&extract_temp);
</code_context>

<issue_to_address>
**issue (bug_risk):** Using a fixed temp directory for extraction can cause conflicts between multiple instances.

The extraction path is always `%TEMP%/mxu_webview2_extract`, which is deleted before each run. If multiple instances run concurrently or another process shares this helper, they could delete or overwrite each other’s temp directory, leading to intermittent failures or incomplete cleanup. Use a per-run unique temp directory (e.g., PID/timestamp/random suffix or a `tempfile`-style API) to avoid these races.
</issue_to_address>

### Comment 2
<location> `src-tauri/src/webview2/install.rs:184-193` </location>
<code_context>
+    }
+
+    // 优先使用架构匹配的 cab
+    if let Some(cab_path) = matched {
+        let progress_dialog = CustomDialog::new_progress(
+            "正在解压 WebView2",
+            "检测到本地 WebView2 运行时 cab 文件,正在解压...",
+        );
+
+        let result = extract_cab_to_runtime(&cab_path, runtime_dir);
+
+        if let Some(pw) = progress_dialog {
+            pw.close();
+        }
+
+        if result.is_ok() {
+            let _ = std::fs::remove_file(&cab_path);
+        }
+        return Some(result);
+    }
+
</code_context>

<issue_to_address>
**issue:** Local cab extraction failures do not fall back to online download as the comment suggests.

The doc for `try_extract_local_cab` says it should “返回 None 继续下载” when the local cab is unusable, but if a matching cab exists and `extract_cab_to_runtime` fails (e.g. corrupted/partial cab), this branch returns `Some(Err(_))`. `download_and_extract` then exits without trying the online download, so a single bad local cab can permanently block recovery. Consider, on extraction failure: (1) delete the cab, (2) return `None` to trigger the online download, and (3) only bubble up an error if the online download also fails.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This pull request migrates WebView2 deployment from system-wide installation (Evergreen Bootstrapper) to a local fixed-version runtime approach. The new implementation downloads WebView2 Fixed Version Runtime as a CAB file from Microsoft's CDN, extracts it to a local webview2_runtime/ directory alongside the executable, and uses the WEBVIEW2_BROWSER_EXECUTABLE_FOLDER environment variable to direct Tauri to use this isolated runtime instead of system-wide installation.

Changes:

  • Replaces system installation workflow with local CAB download and extraction using Windows' native expand.exe
  • Implements local CAB file detection for offline/pre-downloaded runtimes with architecture validation
  • Adds UI improvements including proper window sizing with AdjustWindowRect, throttled progress updates (≥200ms) to prevent blocking, and correct process exit behavior when users close the progress dialog

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 11 comments.

File Description
src-tauri/src/webview2/install.rs Core migration: replaced Evergreen Bootstrapper download/install with Fixed Version CAB download/extraction logic, local CAB detection, streaming download with throttled UI updates, and architecture-aware GUID selection
src-tauri/src/webview2/dialog.rs Fixed window sizing calculation using AdjustWindowRect, added dialog_type tracking for correct WM_CLOSE handling (exit process for progress dialogs)
src-tauri/src/webview2/detection.rs Formatting only: added blank line before function
src-tauri/src/main.rs Added early detection and environment variable setup for existing webview2_runtime directory, skip ensure_webview2() call when local runtime already configured

@Rbqwow
Copy link
Author

Rbqwow commented Feb 19, 2026

我草 这Copilot怎么找了这么多自己的毛病
睡醒再说 我虚拟机测了半天是没什么问题

Rbqwow added a commit to Rbqwow/MXU that referenced this pull request Feb 20, 2026
- 改进架构不支持的错误信息,注明仅支持 x64/ARM64
- 递归复制时检查并跳过符号链接
- 添加 TOCTOU 竞态条件注释说明
- 删除/创建 runtime_dir 时改进文件锁错误提示
- 下载完成后 flush 文件缓冲
- HTTP 客户端显式设置 TLS 证书验证
- 添加 HTTP 整体超时 (600s) 防止无限挂起
- SendMessageW 安全性注释说明必须同步调用
- 验证 webview2_runtime 目录包含 msedgewebview2.exe

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@Rbqwow
Copy link
Author

Rbqwow commented Feb 20, 2026

@sourcery-ai review

Copy link
Contributor

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - 我发现了 1 个问题,并且给出了一些总体反馈:

  • extract_cab_to_runtime 辅助函数假设 expand.exe 已经在 PATH 中,并且除了一个通用错误之外,不会检查或细化失败原因;建议显式地从 System32 解析该可执行文件,并在其缺失时给出更清晰的错误信息,以避免在环境配置有问题时出现令人困惑的失败。
  • download_and_extract 中,临时 cab 路径只是 temp_dir.join(cab_name),因此应用的并发实例可能会争用同一个文件;在临时文件名中加入进程 ID 或随机后缀会让这一点更加健壮。
给 AI 代理的提示
Please address the comments from this code review:

## Overall Comments
- `extract_cab_to_runtime` 辅助函数假设 `expand.exe` 已经在 PATH 中,并且除了一个通用错误之外,不会检查或细化失败原因;建议显式地从 `System32` 解析该可执行文件,并在其缺失时给出更清晰的错误信息,以避免在环境配置有问题时出现令人困惑的失败。
-`download_and_extract` 中,临时 cab 路径只是 `temp_dir.join(cab_name)`,因此应用的并发实例可能会争用同一个文件;在临时文件名中加入进程 ID 或随机后缀会让这一点更加健壮。

## Individual Comments

### Comment 1
<location> `src-tauri/src/webview2/install.rs:106-102` </location>
<code_context>
+fn extract_cab_to_runtime(cab_path: &std::path::Path, runtime_dir: &std::path::Path) -> Result<(), String> {
</code_context>

<issue_to_address>
**suggestion:** 考虑增强对 `expand.exe` 调用的健壮性,并处理 `expand.exe` 缺失或不在 PATH 中的情况。

当前实现依赖 `expand.exe` 在 PATH 中;在锁定或精简的 Windows 环境中它可能不存在,从而只会产生一个通用的“运行 expand.exe 失败”错误信息。

建议:
- 通过 `%SystemRoot%\System32\expand.exe`(例如使用 `GetSystemDirectoryW`)解析可执行文件,而不是依赖 PATH。
- 区分“找不到可执行文件”和“非零退出码”两种情况,这样错误信息可以清楚地表明何时是 `expand.exe` 本身不可用。

这将使 cab 解压失败对用户来说更容易诊断。
</issue_to_address>

Sourcery 对开源项目是免费的 —— 如果你觉得我们的代码评审有帮助,欢迎分享 ✨
帮我变得更有用!请在每条评论上点击 👍 或 👎,我会根据反馈改进后续的评审。
Original comment in English

Hey - I've found 1 issue, and left some high level feedback:

  • The extract_cab_to_runtime helper assumes expand.exe is available on PATH and doesn’t exist or resolve failures beyond a generic error; consider resolving it explicitly from System32 and surfacing a clearer message when it’s missing to avoid confusing failures on misconfigured environments.
  • In download_and_extract, the temporary cab path is just temp_dir.join(cab_name), so concurrent instances of the app could contend for the same file; including the process ID or a random suffix in the temp filename would make this more robust.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- The `extract_cab_to_runtime` helper assumes `expand.exe` is available on PATH and doesn’t exist or resolve failures beyond a generic error; consider resolving it explicitly from `System32` and surfacing a clearer message when it’s missing to avoid confusing failures on misconfigured environments.
- In `download_and_extract`, the temporary cab path is just `temp_dir.join(cab_name)`, so concurrent instances of the app could contend for the same file; including the process ID or a random suffix in the temp filename would make this more robust.

## Individual Comments

### Comment 1
<location> `src-tauri/src/webview2/install.rs:106-102` </location>
<code_context>
+fn extract_cab_to_runtime(cab_path: &std::path::Path, runtime_dir: &std::path::Path) -> Result<(), String> {
</code_context>

<issue_to_address>
**suggestion:** Consider hardening the `expand.exe` invocation and handling the case where `expand.exe` is missing or not on PATH.

This relies on `expand.exe` being on PATH; in locked-down or minimal Windows setups it may be missing, resulting in a generic “运行 expand.exe 失败” message.

I’d suggest:
- Resolving `%SystemRoot%\System32\expand.exe` (e.g., via `GetSystemDirectoryW`) instead of depending on PATH.
- Distinguishing between “executable not found” and a non‑zero exit code so the error clearly indicates when `expand.exe` itself is unavailable.

That will make cab extraction failures more diagnosable for users.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated 5 comments.

Comment on lines +392 to +413
CustomDialog::show_error(
"系统 WebView2 已被禁用",
&format!(
"检测到系统 WebView2 已被禁用:\r\n{}\r\n\r\n\
【什么是 WebView2?】\r\n\
WebView2 是微软提供的网页渲染组件,本程序依赖它来\r\n\
显示界面。如果 WebView2 被禁用,程序将无法正常运行。\r\n\r\n\
【如何解决?】\r\n\
方法一:如果使用了 Edge Blocker 等工具\r\n\
- 打开 Edge Blocker,点击\"Unblock\"解除禁用\r\n\
- 或删除注册表中的 IFEO 拦截项\r\n\r\n\
方法二:修改组策略(需要管理员权限)\r\n\
1. 按 Win + R,输入 gpedit.msc\r\n\
2. 导航到:计算机配置 > 管理模板 > Microsoft Edge WebView2\r\n\
3. 将相关策略设置为\"未配置\"或\"已启用\"\r\n\r\n\
方法三:加入我们的 QQ 群,获取帮助和支持\r\n\
- 群号可在我们的官网或文档底部找到\r\n\r\n\
点击确定后将尝试下载独立 WebView2 运行时以继续运行。\r\n\
若想恢复使用系统 WebView2,请手动删除 exe 目录下的 webview2_runtime 文件夹",
reason
),
);
Copy link

Copilot AI Feb 20, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

此处新增/扩展了大量面向用户的提示文案(标题/正文均为硬编码字符串)。仓库规范要求“所有面向用户的文本必须定义在 src/i18n/locales/ 中”(见 AGENTS.md 3.2)。建议将这些提示文案迁移到统一的本地化资源(至少提供 zh-CN/en-US),并在 Rust 侧通过一套可复用的字符串表/桥接方式读取,避免后续无法维护多语言一致性。

Copilot uses AI. Check for mistakes.
- 改进架构不支持的错误信息,注明仅支持 x64/ARM64
- 递归复制时检查并跳过符号链接
- 添加 TOCTOU 竞态条件注释说明
- 删除/创建 runtime_dir 时改进文件锁错误提示
- 下载完成后 flush 文件缓冲
- HTTP 客户端显式设置 TLS 证书验证
- 添加 HTTP 整体超时 (600s) 防止无限挂起
- SendMessageW 安全性注释说明必须同步调用
- 验证 webview2_runtime 目录包含 msedgewebview2.exe

- expand.exe 从 System32 解析并为下载临时文件添加 PID 前缀
- 从 %SystemRoot%\System32 解析 expand.exe 完整路径,不依赖 PATH
- expand.exe 不存在时给出明确路径提示
- 下载的临时 cab 文件名添加 PID 前缀,避免并发实例冲突

- show_download_failed_dialog 不再默认回退 x64,架构不支持时展示专门提示
- 删除 runtime_dir 前通过 symlink_metadata 检查符号链接/重解析点,拒绝操作以防任意目录删除
- 新增 validate_runtime_dir 在设置环境变量前校验 msedgewebview2.exe 存在
- 下载临时 cab 路径已在上次提交中添加 PID 前缀

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@Rbqwow Rbqwow force-pushed the refactor/fixed-webview2 branch from c4408b0 to 4c60ca1 Compare February 20, 2026 12:26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant