bdf7ed288a
- 将固定四切片与固定 drawImage 降为推荐路径 - 在导航前注入 Canvas/WebGL 平台素材运行时观测 - 对缺少核心素材渲染证据的 desktop/mobile 视口回灌同一 Codex thread 整改 - 补充直连验收、浏览器脚本与真实 Chrome fixture 测试 - 同步 AGC 直连 Runtime 实施计划
121 lines
4.3 KiB
Rust
121 lines
4.3 KiB
Rust
use std::fs;
|
|
use std::path::PathBuf;
|
|
use std::time::Duration;
|
|
|
|
use chromiumoxide::browser::{Browser, BrowserConfig};
|
|
use futures::StreamExt;
|
|
use tempfile::{Builder as TempDirBuilder, TempDir};
|
|
|
|
use super::cdp::run_browser_validation;
|
|
use super::discovery::discover_chrome_or_edge;
|
|
use super::evidence::{
|
|
browser_validation_result_for_report, prepare_evidence_root, unix_time_ms, write_json_report,
|
|
};
|
|
use super::model::{BrowserValidationInput, BrowserValidationResult, BROWSER_TIMEOUT};
|
|
use super::network_policy::{preview_proxy_bypass_list, validate_input};
|
|
|
|
fn browser_process_temp_root() -> PathBuf {
|
|
#[cfg(unix)]
|
|
{
|
|
PathBuf::from("/tmp")
|
|
}
|
|
#[cfg(not(unix))]
|
|
{
|
|
std::env::temp_dir()
|
|
}
|
|
}
|
|
|
|
pub(super) fn create_browser_process_temp_dir() -> Result<TempDir, String> {
|
|
TempDirBuilder::new()
|
|
.prefix("ga-browser-")
|
|
.tempdir_in(browser_process_temp_root())
|
|
.map_err(|error| format!("创建浏览器临时目录失败:{error}"))
|
|
}
|
|
|
|
pub async fn validate_local_preview_in_browser(
|
|
input: BrowserValidationInput,
|
|
) -> Result<BrowserValidationResult, String> {
|
|
validate_local_preview_in_browser_with_interaction(input, false).await
|
|
}
|
|
|
|
/// Runs the same restricted Chromium validation, optionally adding one bounded,
|
|
/// advisory click on a visible enabled control. The interaction is evidence for
|
|
/// the direct Codex reviewer; it is not a fixed gameplay contract and never
|
|
/// changes the deterministic browser safety checks.
|
|
pub async fn validate_local_preview_in_browser_with_interaction(
|
|
input: BrowserValidationInput,
|
|
advisory_interaction: bool,
|
|
) -> Result<BrowserValidationResult, String> {
|
|
let preview_url = validate_input(&input)?;
|
|
prepare_evidence_root(&input.evidence_root)?;
|
|
let browser_executable = discover_chrome_or_edge()?;
|
|
let browser_temp = create_browser_process_temp_dir()?;
|
|
let profile_path = browser_temp.path().join("profile");
|
|
fs::create_dir(&profile_path)
|
|
.map_err(|error| format!("创建浏览器临时 Profile 失败:{error}"))?;
|
|
let browser_temp_path = browser_temp.path().to_string_lossy().into_owned();
|
|
let proxy_bypass_list = preview_proxy_bypass_list(&preview_url);
|
|
|
|
let config = BrowserConfig::builder()
|
|
.chrome_executable(&browser_executable.executable_path)
|
|
.user_data_dir(profile_path)
|
|
.env("TMPDIR", browser_temp_path)
|
|
.new_headless_mode()
|
|
.enable_request_intercept()
|
|
.disable_cache()
|
|
.disable_https_first()
|
|
.request_timeout(BROWSER_TIMEOUT)
|
|
.launch_timeout(BROWSER_TIMEOUT)
|
|
.window_size(1280, 720)
|
|
.arg(("proxy-server", "http://127.0.0.1:9"))
|
|
.arg(("proxy-bypass-list", proxy_bypass_list.as_str()))
|
|
.arg("block-new-web-contents")
|
|
.arg("deny-permission-prompts")
|
|
.arg("disable-notifications")
|
|
.arg("disable-service-worker")
|
|
.build()
|
|
.map_err(|error| format!("构建浏览器配置失败:{error}"))?;
|
|
|
|
let (mut browser, mut handler) = tokio::time::timeout(BROWSER_TIMEOUT, Browser::launch(config))
|
|
.await
|
|
.map_err(|_| "启动浏览器超时".to_string())?
|
|
.map_err(|error| format!("启动浏览器失败:{error}"))?;
|
|
let handler_task = tokio::spawn(async move {
|
|
while let Some(message) = handler.next().await {
|
|
if message.is_err() {
|
|
break;
|
|
}
|
|
}
|
|
});
|
|
|
|
let validation = run_browser_validation(
|
|
&browser,
|
|
&browser_executable,
|
|
&preview_url,
|
|
&input,
|
|
advisory_interaction,
|
|
)
|
|
.await;
|
|
|
|
let close_result = browser
|
|
.close()
|
|
.await
|
|
.map_err(|error| format!("关闭浏览器失败:{error}"));
|
|
let wait_result = tokio::time::timeout(Duration::from_secs(5), browser.wait()).await;
|
|
handler_task.abort();
|
|
let _ = handler_task.await;
|
|
drop(browser_temp);
|
|
|
|
let mut result = validation?;
|
|
close_result?;
|
|
match wait_result {
|
|
Ok(Ok(_)) => {}
|
|
Ok(Err(error)) => return Err(format!("等待浏览器退出失败:{error}")),
|
|
Err(_) => return Err("等待浏览器退出超时".to_string()),
|
|
}
|
|
result.completed_at_unix_ms = unix_time_ms();
|
|
let persisted_result = browser_validation_result_for_report(&result)?;
|
|
write_json_report(&result.evidence.report_path, &persisted_result)?;
|
|
Ok(result)
|
|
}
|