build(win7): make the WebView2 loader build reproducible

This commit is contained in:
luo jiyin
2026-09-20 00:50:03 +08:00
committed by GitHub
parent 04376ef3fb
commit ead307dba0
29 changed files with 44534 additions and 111 deletions
+5
View File
@@ -14,6 +14,11 @@ src-tauri/windows/nsis/**/*.nsi text eol=lf
# Keep plugin dev-host formatting checks consistent on Windows checkouts.
plugins/sdk/dev-host/** text=auto eol=lf
# The vendored WebView2 loaders are hash-pinned build inputs. `-text` disables
# any eol conversion on checkout, because build.rs verifies their raw SHA256.
vendor/webview2-com-sys/**/*.lib -text -diff
vendor/webview2-com-sys/**/*.dll -text -diff
# Keep tests available for review and CI without counting them as shipped code
# in GitHub's repository language breakdown.
packages/app-tests/** linguist-vendored
@@ -46,7 +46,7 @@ $summary
A loader >= 1.0.1054.31 was linked instead of the prepared one and will fail on
Server 2012 R2 with ERROR_NOT_SUPPORTED. Do not ship this binary: rebuild the
win7 target and investigate how the registry loader patch was bypassed.
win7 target and investigate why Cargo did not select the vendored loader.
"@
}
+1
View File
@@ -89,6 +89,7 @@ test("DuckDB Windows builds persist Rust and C++ compiler results", () => {
});
test("standard Windows compatibility checks run separately with sccache", () => {
assert.ok(job("changes").includes("'vendor/webview2-com-sys/**'"));
const standard = job("windows-standard-check");
assert.ok(standard.includes("needs.changes.outputs.windows_win7_bundle == 'true'"));
assert.ok(standard.includes("RUSTC_WRAPPER: sccache"));
+31 -102
View File
@@ -4,114 +4,43 @@ param()
Set-StrictMode -Version Latest
$ErrorActionPreference = "Stop"
# WebView2 static loaders newer than SDK 1.0.1054 import EventSetInformation, which does
# not exist on Windows 7, while 1.0.1054.31 itself fails on Server 2012 R2 with
# ERROR_NOT_SUPPORTED (0x80070032, MicrosoftEdge/WebView2Feedback#2025). SDK 1.0.902.49 is
# the loader verified on both Windows 7 SP1 and Server 2012 R2 real machines, so the single
# win7 + server 2012 r2 offline bundle links it instead of the webview2-com-sys default.
# The Windows 7 / Server 2012 R2 bundle links the static loader from WebView2 SDK
# 1.0.902.49. Loaders >= 1.0.1054.31 import EventSetInformation, which Win7 and
# Server 2012 R2 do not provide, and fail with "Could not find the WebView2
# Runtime". The loader lives in vendor/webview2-com-sys/win7/x64/ and Cargo links
# it through the [patch.crates-io] entry. This script verifies the committed
# loader and stages the loader DLL for the runtime probe. It does not modify the
# Cargo registry, so compile caches stay correct.
$sdkVersion = "1.0.902.49"
$sdkPackageSha256 = "b483c906b03690267108f4304d456eac0e718131ef994e69aaa5a21532b512c6"
$loaderSha256 = "aa5c26670f1b18d0fa2a56ac3f1ae30110c332a8bfbd555a7be3e548d1b0da3d"
$loaderDllSha256 = "fdf978ba706578b05967d7f0181f462147864a5aa74f36016a62cb3d3dbe6909"
$legacy1054LoaderSha256 = "76314119685bbf4c2b2423a44e81b57beadc914c943d0e772fd6bc78c8e6b0e8"
$webView2ComSysVersion = "0.38.2"
$upstreamLoaderSha256 = "0659b741bde6348d4c4a6ec4ceb9af50e3d0048ed9cd3c8659bccbb61fde55ee"
$repositoryRoot = (Resolve-Path (Join-Path $PSScriptRoot "../..")).Path
$temporaryRoot = Join-Path ([System.IO.Path]::GetTempPath()) "dbx-win7-webview2-loader-$([Guid]::NewGuid())"
$packagePath = Join-Path $temporaryRoot "Microsoft.Web.WebView2.$sdkVersion.nupkg"
$extractedPath = Join-Path $temporaryRoot "extracted"
$loaderDirectory = Join-Path $repositoryRoot "vendor/webview2-com-sys/win7/x64"
$loaderPath = Join-Path $loaderDirectory "WebView2LoaderStatic.lib"
$loaderDllPath = Join-Path $loaderDirectory "WebView2Loader.dll"
try {
New-Item -ItemType Directory -Path $extractedPath -Force | Out-Null
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
$packageUrl = "https://www.nuget.org/api/v2/package/Microsoft.Web.WebView2/$sdkVersion"
Write-Host "Downloading WebView2 SDK $sdkVersion for the Windows 7 / Server 2012 R2 loader..."
Invoke-WebRequest -Uri $packageUrl -OutFile $packagePath -UseBasicParsing
$actualPackageSha256 = (Get-FileHash -LiteralPath $packagePath -Algorithm SHA256).Hash.ToLowerInvariant()
if ($actualPackageSha256 -ne $sdkPackageSha256) {
throw "Unexpected WebView2 SDK package SHA256: $actualPackageSha256"
function Assert-FileHash {
param(
[Parameter(Mandatory = $true)][string]$Path,
[Parameter(Mandatory = $true)][string]$Expected
)
if (!(Test-Path -LiteralPath $Path -PathType Leaf)) {
throw "Vendored Windows 7 WebView2 loader is missing: $Path"
}
Add-Type -AssemblyName System.IO.Compression.FileSystem
[System.IO.Compression.ZipFile]::ExtractToDirectory($packagePath, $extractedPath)
$legacyLoader = Join-Path $extractedPath "build/native/x64/WebView2LoaderStatic.lib"
if (!(Test-Path -LiteralPath $legacyLoader -PathType Leaf)) {
throw "WebView2 SDK $sdkVersion does not contain the x64 static loader."
}
$legacyLoaderDll = Join-Path $extractedPath "build/native/x64/WebView2Loader.dll"
if (!(Test-Path -LiteralPath $legacyLoaderDll -PathType Leaf)) {
throw "WebView2 SDK $sdkVersion does not contain the x64 loader DLL."
}
$actualLoaderSha256 = (Get-FileHash -LiteralPath $legacyLoader -Algorithm SHA256).Hash.ToLowerInvariant()
if ($actualLoaderSha256 -ne $loaderSha256) {
throw "Unexpected Windows 7 / Server 2012 R2 WebView2 loader SHA256: $actualLoaderSha256"
}
$actualLoaderDllSha256 = (Get-FileHash -LiteralPath $legacyLoaderDll -Algorithm SHA256).Hash.ToLowerInvariant()
if ($actualLoaderDllSha256 -ne $loaderDllSha256) {
throw "Unexpected Windows 7 / Server 2012 R2 WebView2 loader DLL SHA256: $actualLoaderDllSha256"
}
Push-Location $repositoryRoot
try {
& cargo fetch --locked --target x86_64-win7-windows-msvc
if ($LASTEXITCODE -ne 0) {
throw "cargo fetch failed while preparing the Windows 7 WebView2 loader."
}
$metadataJson = & cargo metadata --locked --format-version 1
if ($LASTEXITCODE -ne 0) {
throw "cargo metadata failed while locating webview2-com-sys."
}
}
finally {
Pop-Location
}
$metadata = $metadataJson | ConvertFrom-Json
$webView2Packages = @($metadata.packages | Where-Object {
$_.name -eq "webview2-com-sys" -and $_.version -eq $webView2ComSysVersion
})
if ($webView2Packages.Count -ne 1) {
throw "Expected exactly one webview2-com-sys $webView2ComSysVersion package, found $($webView2Packages.Count)."
}
$crateRoot = Split-Path -Parent $webView2Packages[0].manifest_path
$loaderDestination = Join-Path $crateRoot "x64/WebView2LoaderStatic.lib"
if (!(Test-Path -LiteralPath $loaderDestination -PathType Leaf)) {
throw "webview2-com-sys static loader does not exist: $loaderDestination"
}
$existingLoaderSha256 = (Get-FileHash -LiteralPath $loaderDestination -Algorithm SHA256).Hash.ToLowerInvariant()
$knownLoaderHashes = @($upstreamLoaderSha256, $legacy1054LoaderSha256, $loaderSha256)
if ($existingLoaderSha256 -notin $knownLoaderHashes) {
throw "Refusing to replace an unknown webview2-com-sys loader SHA256: $existingLoaderSha256"
}
Set-ItemProperty -LiteralPath $loaderDestination -Name IsReadOnly -Value $false
Copy-Item -LiteralPath $legacyLoader -Destination $loaderDestination -Force
$installedLoaderSha256 = (Get-FileHash -LiteralPath $loaderDestination -Algorithm SHA256).Hash.ToLowerInvariant()
if ($installedLoaderSha256 -ne $loaderSha256) {
throw "Windows 7 WebView2 loader replacement failed: $installedLoaderSha256"
}
$probeDirectory = Join-Path ([System.IO.Path]::GetTempPath()) "dbx-win7-webview2-loader-probe"
New-Item -ItemType Directory -Path $probeDirectory -Force | Out-Null
$probeLoader = Join-Path $probeDirectory "WebView2Loader.dll"
Copy-Item -LiteralPath $legacyLoaderDll -Destination $probeLoader -Force
Write-Host "Prepared WebView2 SDK $sdkVersion static loader for Windows 7 / Server 2012 R2: $loaderDestination"
Write-Host "Prepared WebView2 SDK $sdkVersion loader probe DLL: $probeLoader"
}
finally {
if (Test-Path -LiteralPath $temporaryRoot) {
Remove-Item -LiteralPath $temporaryRoot -Recurse -Force
$actual = (Get-FileHash -LiteralPath $Path -Algorithm SHA256).Hash.ToLowerInvariant()
if ($actual -ne $Expected) {
throw "Unexpected Windows 7 WebView2 loader SHA256 for ${Path}: $actual"
}
}
Assert-FileHash -Path $loaderPath -Expected $loaderSha256
Assert-FileHash -Path $loaderDllPath -Expected $loaderDllSha256
$probeDirectory = Join-Path ([System.IO.Path]::GetTempPath()) "dbx-win7-webview2-loader-probe"
New-Item -ItemType Directory -Path $probeDirectory -Force | Out-Null
$probeLoader = Join-Path $probeDirectory "WebView2Loader.dll"
Copy-Item -LiteralPath $loaderDllPath -Destination $probeLoader -Force
Write-Host "Verified vendored WebView2 SDK $sdkVersion static loader: $loaderPath"
Write-Host "Staged WebView2 SDK $sdkVersion loader probe DLL: $probeLoader"
@@ -0,0 +1,50 @@
import assert from "node:assert/strict";
import { createHash } from "node:crypto";
import { readFileSync } from "node:fs";
import test from "node:test";
const root = new URL("../../", import.meta.url);
const win7StaticPath = new URL("vendor/webview2-com-sys/win7/x64/WebView2LoaderStatic.lib", root);
const win7DllPath = new URL("vendor/webview2-com-sys/win7/x64/WebView2Loader.dll", root);
const win7StaticSha256 = "aa5c26670f1b18d0fa2a56ac3f1ae30110c332a8bfbd555a7be3e548d1b0da3d";
const win7DllSha256 = "fdf978ba706578b05967d7f0181f462147864a5aa74f36016a62cb3d3dbe6909";
function text(path) {
return readFileSync(new URL(path, root), "utf8");
}
function sha256(url) {
return createHash("sha256").update(readFileSync(url)).digest("hex");
}
// A replaced loader once shipped a binary that failed on Server 2012 R2. Pin the
// committed bytes so a PR cannot swap them without an explicit hash update.
test("the vendored Windows 7 WebView2 loader matches the pinned SHA256", () => {
assert.equal(sha256(win7StaticPath), win7StaticSha256);
assert.equal(sha256(win7DllPath), win7DllSha256);
});
test("build.rs pins the same Windows 7 loader SHA256", () => {
const build = text("vendor/webview2-com-sys/build.rs");
assert.ok(build.includes(win7StaticSha256));
assert.ok(build.includes(win7DllSha256));
assert.ok(build.includes('target == "x86_64-win7-windows-msvc"'));
assert.ok(build.includes("cargo:rerun-if-changed="));
});
test("upstream WebView2 loaders are vendored for standard Windows builds", () => {
for (const arch of ["x86", "x64", "arm64"]) {
const staticLib = readFileSync(new URL(`vendor/webview2-com-sys/${arch}/WebView2LoaderStatic.lib`, root));
assert.ok(staticLib.length > 0, arch);
}
});
test("Cargo uses the vendored webview2-com-sys crate", () => {
assert.ok(text("Cargo.toml").includes('webview2-com-sys = { path = "vendor/webview2-com-sys" }'));
});
test("the preparation script does not modify the Cargo registry", () => {
const script = text(".github/scripts/prepare-webview2-win7-loader.ps1");
assert.ok(script.includes("vendor/webview2-com-sys/win7/x64"));
assert.doesNotMatch(script, /cargo metadata|cargo fetch|registry\\src|Invoke-WebRequest/);
});
+1
View File
@@ -854,6 +854,7 @@ jobs:
- 'vendor/ctor/**'
- 'vendor/dirs-sys/**'
- 'vendor/pageant/**'
- 'vendor/webview2-com-sys/**'
- 'vendor/wry/**'
github_scripts:
- '.github/scripts/**'
Generated
+1 -2
View File
@@ -10653,9 +10653,8 @@ dependencies = [
[[package]]
name = "webview2-com-sys"
version = "0.38.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "381336cfffd772377d291702245447a5251a2ffa5bad679c99e61bc48bacbf9c"
dependencies = [
"sha2 0.10.9",
"thiserror 2.0.19",
"windows 0.61.3",
"windows-core 0.61.2",
+7 -1
View File
@@ -1,9 +1,15 @@
[workspace]
resolver = "2"
members = ["src-tauri", "crates/dbx-core", "crates/dbx-drivers", "crates/dbx-sql", "crates/dbx-plugin-runtime", "crates/dbx-formats", "crates/dbx-types", "crates/dbx-platform", "crates/dbx-ai-provider", "crates/dbx-web", "crates/dbx-mcp", "crates/dbx-cli", "crates/dbx-sqlite-worker"]
exclude = ["vendor/tauri-plugin-updater", "vendor/ctor", "vendor/dirs-sys", "vendor/pageant", "vendor/rumqttc", "vendor/tiberius", "vendor/wry"]
exclude = ["vendor/tauri-plugin-updater", "vendor/ctor", "vendor/dirs-sys", "vendor/pageant", "vendor/rumqttc", "vendor/tiberius", "vendor/webview2-com-sys", "vendor/wry"]
[patch.crates-io]
# The offline Win7 / Server 2012 R2 bundle links the WebView2 SDK 1.0.902.49
# loader. Loaders >= 1.0.1054.31 import EventSetInformation, which Win7 and
# Server 2012 R2 do not provide. Keep that loader in the source tree instead of
# swapping a file inside the cargo registry, because a registry swap bypasses
# compile caches and once shipped a binary linked against the wrong loader.
webview2-com-sys = { path = "vendor/webview2-com-sys" }
# Offline signed update restoration and checked Windows installer launch.
tauri-plugin-updater = { path = "vendor/tauri-plugin-updater" }
# Tauri 2.11 uses ctor 0.8, which excludes Rust's win7 vendor. This vendors
@@ -140,14 +140,12 @@ describe("Windows 7 fixed WebView2 runtime bundle", () => {
expect(releaseWorkflow).toContain("./.github/scripts/assert-webview2-win7-loader.ps1");
});
it("keeps compile caching away from the Win7 WebView2 loader patch", () => {
it("keeps the existing Win7 compile-cache policy unchanged", () => {
const releaseWin7Job = releaseWorkflow.slice(releaseWorkflow.indexOf(" build-windows-7-offline:"), releaseWorkflow.indexOf(" static-browser:"));
const ciWin7Job = ciWorkflow.slice(ciWorkflow.indexOf(" windows-win7-bundle:"), ciWorkflow.indexOf(" duckdb-windows-driver:"));
// The loader patch rewrites a file inside the cargo registry, which no
// compile cache can see. v0.6.0 linked a >= 1.0.1054.31 loader despite the
// patched 1.0.902.49 one being verified on disk, so neither Win7 job may
// route rustc through sccache until the loader is vendored into the tree.
// The vendored loader makes compile caching safe. This change does not alter
// cache policy. A separate CI change can enable caching for these jobs.
expect(releaseWin7Job).not.toContain("RUSTC_WRAPPER");
expect(releaseWin7Job).not.toContain("sccache-action");
expect(ciWin7Job).not.toContain("RUSTC_WRAPPER");
+20
View File
@@ -0,0 +1,20 @@
# Changelog
All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [Unreleased]
## [0.38.2](https://github.com/wravery/webview2-rs/compare/webview2-com-sys-v0.38.1...webview2-com-sys-v0.38.2) - 2026-01-07
### Other
- update WebView2 SDK to 1.0.3650.58
## [0.38.1](https://github.com/wravery/webview2-rs/compare/webview2-com-sys-v0.38.0...webview2-com-sys-v0.38.1) - 2026-01-07
### Added
- update bindings to 1.0.3351.48
+63
View File
@@ -0,0 +1,63 @@
# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO
#
# When uploading crates to the registry Cargo will automatically
# "normalize" Cargo.toml files for maximal compatibility
# with all versions of Cargo and also rewrite `path` dependencies
# to registry (e.g., crates.io) dependencies.
#
# If you are reading this file be aware that the original Cargo.toml
# will likely look very different (and much more reasonable).
# See Cargo.toml.orig for the original contents.
[package]
edition = "2021"
rust-version = "1.74"
name = "webview2-com-sys"
version = "0.38.2"
build = "build.rs"
autolib = false
autobins = false
autoexamples = false
autotests = false
autobenches = false
description = "Bindings generated with the windows crate for the WebView2 COM APIs"
readme = "README.md"
keywords = [
"win32",
"webview2",
]
categories = ["os::windows-apis"]
license = "MIT"
repository = "https://github.com/wravery/webview2-rs"
[package.metadata.docs.rs]
default-target = "x86_64-pc-windows-msvc"
targets = [
"i686-pc-windows-msvc",
"aarch64-pc-windows-msvc",
"x86_64-pc-windows-gnu",
"i686-pc-windows-gnu",
]
[lib]
name = "webview2_com_sys"
path = "src/lib.rs"
[dependencies.windows]
version = "0.61"
features = [
"Win32_Foundation",
"Win32_Globalization",
"Win32_System_Com",
"Win32_System_Ole",
"Win32_System_Variant",
"Win32_System_WinRT",
"Win32_UI_WindowsAndMessaging",
]
[dependencies.windows-core]
version = "0.61"
[build-dependencies]
sha2 = "0.10"
thiserror = "2"
+21
View File
@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2021 Bill Avery
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
+40
View File
@@ -0,0 +1,40 @@
# webview2-com-sys
This crate implements unsafe Rust bindings for the [WebView2](https://aka.ms/webview2) COM APIs using the [Windows](https://github.com/microsoft/windows-rs) crate.
## Getting Started
This crate has a friendlier wrapper in [webview2-com](https://crates.io/crates/webview2-com).
## DBX vendoring
DBX vendors this crate to make the Windows 7 WebView2 loader an explicit build input.
The crate source matches upstream `webview2-com-sys` 0.38.2.
`build.rs` selects the loader directory by target:
- The custom `x86_64-win7-windows-msvc` target uses `win7/x64/`.
- Every other Windows target uses the upstream `x86/`, `x64/`, or `arm64/` directory.
The old loader preparation scripts replaced a file inside the cargo registry.
That replacement was invisible to compile caches.
A release once shipped a binary linked against the wrong loader (see the comment in `.github/workflows/release.yml`).
The vendored copy removes that failure mode.
### Loader provenance
| Directory | Source | WebView2 SDK | SHA256 |
| --- | --- | --- | --- |
| `x86/` | crates.io `webview2-com-sys` 0.38.2 | 1.0.3650.58 | `6649ce9ca24e7a5693ee54178f42e0378004ce537d82d15354e6c9adb467bc16` (static) |
| `x64/` | crates.io `webview2-com-sys` 0.38.2 | 1.0.3650.58 | `0659b741bde6348d4c4a6ec4ceb9af50e3d0048ed9cd3c8659bccbb61fde55ee` (static) |
| `arm64/` | crates.io `webview2-com-sys` 0.38.2 | 1.0.3650.58 | `506ffde430bee7f91f2ce1a078effb5289b7cec3b0c7283647f0842def524ab4` (static) |
| `win7/x64/` | NuGet `Microsoft.Web.WebView2` 1.0.902.49 | 1.0.902.49 | `aa5c26670f1b18d0fa2a56ac3f1ae30110c332a8bfbd555a7be3e548d1b0da3d` (static), `fdf978ba706578b05967d7f0181f462147864a5aa74f36016a62cb3d3dbe6909` (`WebView2Loader.dll`) |
`build.rs` verifies the two pinned `win7/x64/` hashes at build time.
`.github/scripts/prepare-webview2-win7-loader.ps1` verifies the same hashes and stages the loader DLL for the runtime probe.
### License
The Rust source keeps the upstream MIT license.
The Microsoft WebView2 loader files keep the Microsoft WebView2 SDK license.
The repository commits them to build the offline Windows 7 / Server 2012 R2 installer.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+117
View File
@@ -0,0 +1,117 @@
use sha2::{Digest, Sha256};
use std::env;
use std::fs;
use std::path::{Path, PathBuf};
// WebView2 SDK 1.0.902.49 is the last static loader verified on Windows 7 SP1
// and Server 2012 R2. Loaders >= 1.0.1054.31 import EventSetInformation, which
// those systems do not provide. The win7/x64/ copy is the pinned loader. Verify
// its SHA256 before the link, because a wrong loader produces a binary that
// fails only at runtime. See .github/scripts/prepare-webview2-win7-loader.ps1.
const WIN7_LOADER_SHA256: &str = "aa5c26670f1b18d0fa2a56ac3f1ae30110c332a8bfbd555a7be3e548d1b0da3d";
const WIN7_LOADER_DLL_SHA256: &str = "fdf978ba706578b05967d7f0181f462147864a5aa74f36016a62cb3d3dbe6909";
fn main() -> Result<()> {
let arch = target_arch()?;
let (source, pinned) = loader_source(&arch)?;
let out_dir = PathBuf::from(env::var("OUT_DIR")?);
copy_loaders(&source, &out_dir, &arch, pinned)?;
update_rustc_flags(&out_dir, &arch)?;
println!("cargo:rustc-link-lib=advapi32");
Ok(())
}
#[derive(Debug, Error)]
pub enum Error {
#[error(transparent)]
Io(#[from] std::io::Error),
#[error(transparent)]
Var(#[from] std::env::VarError),
#[error("{0}")]
Message(String),
}
pub type Result<T> = std::result::Result<T, Error>;
#[macro_use]
extern crate thiserror;
fn target_arch() -> Result<String> {
Ok(match env::var("CARGO_CFG_TARGET_ARCH")?.as_str() {
"x86_64" => "x64".into(),
"x86" => "x86".into(),
"aarch64" => "arm64".into(),
other => return Err(Error::Message(format!("`{other}` is not supported by WebView2"))),
})
}
// The custom x86_64-win7-windows-msvc target links the pinned legacy loader.
// Every other Windows target keeps the upstream loader installed by cargo.
fn loader_source(arch: &str) -> Result<(PathBuf, bool)> {
let manifest_dir = PathBuf::from(env::var("CARGO_MANIFEST_DIR")?);
let target = env::var("TARGET")?;
if target == "x86_64-win7-windows-msvc" {
let source = manifest_dir.join("win7").join(arch);
if !source.is_dir() {
return Err(Error::Message(format!(
"the Windows 7 WebView2 loader is only vendored for x64, not `{arch}`"
)));
}
return Ok((source, true));
}
let source = manifest_dir.join(arch);
if !source.is_dir() {
return Err(Error::Message(format!("missing WebView2 loaders for `{arch}`: {source:?}")));
}
Ok((source, false))
}
fn copy_loaders(source: &Path, out_dir: &Path, arch: &str, pinned: bool) -> Result<()> {
const LOADER_LIBS: &[&str] = &["WebView2Loader.dll", "WebView2Loader.dll.lib", "WebView2LoaderStatic.lib"];
let destination = out_dir.join(arch);
fs::create_dir_all(&destination)?;
for lib in LOADER_LIBS {
let from = source.join(lib);
let to = destination.join(lib);
if pinned {
verify_pinned_loader(&from, lib)?;
}
fs::copy(&from, &to)?;
// Cargo rebuilds the crate when a loader changes, so a stale compile
// cache cannot serve a binary linked against a different loader.
println!("cargo:rerun-if-changed={}", from.display());
}
Ok(())
}
// Pin only the Windows 7 loader. The upstream loaders belong to the crate and
// change with the WebView2 SDK version.
fn verify_pinned_loader(path: &Path, lib: &str) -> Result<()> {
let expected = match lib {
"WebView2LoaderStatic.lib" => WIN7_LOADER_SHA256,
"WebView2Loader.dll" => WIN7_LOADER_DLL_SHA256,
_ => return Ok(()),
};
let mut hasher = Sha256::new();
hasher.update(fs::read(path)?);
let actual = format!("{:x}", hasher.finalize());
if actual != expected {
return Err(Error::Message(format!(
"Windows 7 WebView2 loader SHA256 mismatch for {path:?}: expected {expected}, got {actual}"
)));
}
Ok(())
}
fn update_rustc_flags(out_dir: &Path, arch: &str) -> Result<()> {
let lib_path = out_dir.join(arch);
if !lib_path.is_dir() {
return Err(Error::Message(format!("`{arch}` is not supported by WebView2")));
}
println!("cargo:rustc-link-search=native={}", lib_path.display());
Ok(())
}
File diff suppressed because it is too large Load Diff
+116
View File
@@ -0,0 +1,116 @@
use std::collections::BTreeSet;
/// Generate a list of all `ICoreWebView2...Handler` interfaces declared in `WebView2.h`. This is
/// for testing purposes to make sure they are all covered in
/// [callback.rs](../../webview2-com/src/callback.rs).
pub fn all_declared_callbacks() -> BTreeSet<&'static str> {
let mut interfaces = BTreeSet::new();
interfaces.insert("ICoreWebView2AcceleratorKeyPressedEventHandler");
interfaces.insert("ICoreWebView2AddScriptToExecuteOnDocumentCreatedCompletedHandler");
interfaces.insert("ICoreWebView2BasicAuthenticationRequestedEventHandler");
interfaces.insert("ICoreWebView2BrowserExtensionEnableCompletedHandler");
interfaces.insert("ICoreWebView2BrowserExtensionRemoveCompletedHandler");
interfaces.insert("ICoreWebView2BrowserProcessExitedEventHandler");
interfaces.insert("ICoreWebView2BytesReceivedChangedEventHandler");
interfaces.insert("ICoreWebView2CallDevToolsProtocolMethodCompletedHandler");
interfaces.insert("ICoreWebView2CapturePreviewCompletedHandler");
interfaces.insert("ICoreWebView2ClearBrowsingDataCompletedHandler");
interfaces.insert("ICoreWebView2ClearServerCertificateErrorActionsCompletedHandler");
interfaces.insert("ICoreWebView2ClientCertificateRequestedEventHandler");
interfaces.insert("ICoreWebView2ContainsFullScreenElementChangedEventHandler");
interfaces.insert("ICoreWebView2ContentLoadingEventHandler");
interfaces.insert("ICoreWebView2ContextMenuRequestedEventHandler");
interfaces.insert("ICoreWebView2CreateCoreWebView2CompositionControllerCompletedHandler");
interfaces.insert("ICoreWebView2CreateCoreWebView2ControllerCompletedHandler");
interfaces.insert("ICoreWebView2CreateCoreWebView2EnvironmentCompletedHandler");
interfaces.insert("ICoreWebView2CursorChangedEventHandler");
interfaces.insert("ICoreWebView2CustomItemSelectedEventHandler");
interfaces.insert("ICoreWebView2DOMContentLoadedEventHandler");
interfaces.insert("ICoreWebView2DevToolsProtocolEventReceivedEventHandler");
interfaces.insert("ICoreWebView2DocumentTitleChangedEventHandler");
interfaces.insert("ICoreWebView2DownloadStartingEventHandler");
interfaces.insert("ICoreWebView2EstimatedEndTimeChangedEventHandler");
interfaces.insert("ICoreWebView2ExecuteScriptCompletedHandler");
interfaces.insert("ICoreWebView2ExecuteScriptWithResultCompletedHandler");
interfaces.insert("ICoreWebView2FaviconChangedEventHandler");
interfaces.insert("ICoreWebView2FindActiveMatchIndexChangedEventHandler");
interfaces.insert("ICoreWebView2FindMatchCountChangedEventHandler");
interfaces.insert("ICoreWebView2FindStartCompletedHandler");
interfaces.insert("ICoreWebView2FocusChangedEventHandler");
interfaces.insert("ICoreWebView2FrameChildFrameCreatedEventHandler");
interfaces.insert("ICoreWebView2FrameContentLoadingEventHandler");
interfaces.insert("ICoreWebView2FrameCreatedEventHandler");
interfaces.insert("ICoreWebView2FrameDOMContentLoadedEventHandler");
interfaces.insert("ICoreWebView2FrameDestroyedEventHandler");
interfaces.insert("ICoreWebView2FrameNameChangedEventHandler");
interfaces.insert("ICoreWebView2FrameNavigationCompletedEventHandler");
interfaces.insert("ICoreWebView2FrameNavigationStartingEventHandler");
interfaces.insert("ICoreWebView2FramePermissionRequestedEventHandler");
interfaces.insert("ICoreWebView2FrameScreenCaptureStartingEventHandler");
interfaces.insert("ICoreWebView2FrameWebMessageReceivedEventHandler");
interfaces.insert("ICoreWebView2GetCookiesCompletedHandler");
interfaces.insert("ICoreWebView2GetFaviconCompletedHandler");
interfaces.insert("ICoreWebView2GetNonDefaultPermissionSettingsCompletedHandler");
interfaces.insert("ICoreWebView2GetProcessExtendedInfosCompletedHandler");
interfaces.insert("ICoreWebView2HistoryChangedEventHandler");
interfaces.insert("ICoreWebView2IsDefaultDownloadDialogOpenChangedEventHandler");
interfaces.insert("ICoreWebView2IsDocumentPlayingAudioChangedEventHandler");
interfaces.insert("ICoreWebView2IsMutedChangedEventHandler");
interfaces.insert("ICoreWebView2LaunchingExternalUriSchemeEventHandler");
interfaces.insert("ICoreWebView2MoveFocusRequestedEventHandler");
interfaces.insert("ICoreWebView2NavigationCompletedEventHandler");
interfaces.insert("ICoreWebView2NavigationStartingEventHandler");
interfaces.insert("ICoreWebView2NewBrowserVersionAvailableEventHandler");
interfaces.insert("ICoreWebView2NewWindowRequestedEventHandler");
interfaces.insert("ICoreWebView2NonClientRegionChangedEventHandler");
interfaces.insert("ICoreWebView2NotificationCloseRequestedEventHandler");
interfaces.insert("ICoreWebView2NotificationReceivedEventHandler");
interfaces.insert("ICoreWebView2PermissionRequestedEventHandler");
interfaces.insert("ICoreWebView2PrintCompletedHandler");
interfaces.insert("ICoreWebView2PrintToPdfCompletedHandler");
interfaces.insert("ICoreWebView2PrintToPdfStreamCompletedHandler");
interfaces.insert("ICoreWebView2ProcessFailedEventHandler");
interfaces.insert("ICoreWebView2ProcessInfosChangedEventHandler");
interfaces.insert("ICoreWebView2ProfileAddBrowserExtensionCompletedHandler");
interfaces.insert("ICoreWebView2ProfileDeletedEventHandler");
interfaces.insert("ICoreWebView2ProfileGetBrowserExtensionsCompletedHandler");
interfaces.insert("ICoreWebView2RasterizationScaleChangedEventHandler");
interfaces.insert("ICoreWebView2SaveAsUIShowingEventHandler");
interfaces.insert("ICoreWebView2SaveFileSecurityCheckStartingEventHandler");
interfaces.insert("ICoreWebView2ScreenCaptureStartingEventHandler");
interfaces.insert("ICoreWebView2ScriptDialogOpeningEventHandler");
interfaces.insert("ICoreWebView2ServerCertificateErrorDetectedEventHandler");
interfaces.insert("ICoreWebView2SetPermissionStateCompletedHandler");
interfaces.insert("ICoreWebView2ShowSaveAsUICompletedHandler");
interfaces.insert("ICoreWebView2SourceChangedEventHandler");
interfaces.insert("ICoreWebView2StateChangedEventHandler");
interfaces.insert("ICoreWebView2StatusBarTextChangedEventHandler");
interfaces.insert("ICoreWebView2TrySuspendCompletedHandler");
interfaces.insert("ICoreWebView2WebMessageReceivedEventHandler");
interfaces.insert("ICoreWebView2WebResourceRequestedEventHandler");
interfaces.insert("ICoreWebView2WebResourceResponseReceivedEventHandler");
interfaces.insert("ICoreWebView2WebResourceResponseViewGetContentCompletedHandler");
interfaces.insert("ICoreWebView2WindowCloseRequestedEventHandler");
interfaces.insert("ICoreWebView2ZoomFactorChangedEventHandler");
interfaces
}
/// Generate a list of all `ICoreWebView2EnvironmentOptions` interfaces declared in `WebView2.h`.
/// This is for testing purposes to make sure they are all covered in
/// [options.rs](../../webview2-com/src/options.rs).
pub fn all_declared_options() -> BTreeSet<&'static str> {
let mut interfaces = BTreeSet::new();
interfaces.insert("ICoreWebView2EnvironmentOptions");
interfaces.insert("ICoreWebView2EnvironmentOptions2");
interfaces.insert("ICoreWebView2EnvironmentOptions3");
interfaces.insert("ICoreWebView2EnvironmentOptions4");
interfaces.insert("ICoreWebView2EnvironmentOptions5");
interfaces.insert("ICoreWebView2EnvironmentOptions6");
interfaces.insert("ICoreWebView2EnvironmentOptions7");
interfaces.insert("ICoreWebView2EnvironmentOptions8");
interfaces
}
+66
View File
@@ -0,0 +1,66 @@
#[allow(
non_snake_case,
non_upper_case_globals,
non_camel_case_types,
dead_code,
clippy::all
)]
pub mod Microsoft {
pub mod Web {
pub mod WebView2 {
pub mod Win32 {
mod windows_link {
macro_rules! link_webview2 {
($library:literal $abi:literal fn $($function:tt)*) => (
#[cfg_attr(
target_env = "msvc",
link(name = "WebView2LoaderStatic", kind = "static")
)]
#[cfg_attr(
not(target_env = "msvc"),
link(name = "WebView2Loader.dll")
)]
extern $abi {
pub fn $($function)*;
}
)
}
pub(crate) use link_webview2 as link;
}
include!("bindings.rs");
}
}
}
}
pub mod declared_interfaces;
#[cfg(test)]
mod test {
use windows_core::w;
use crate::Microsoft::Web::WebView2::Win32::*;
#[test]
fn compare_eq() {
let mut result = 1;
unsafe { CompareBrowserVersions(w!("1.0.0"), w!("1.0.0"), &mut result) }.unwrap();
assert_eq!(0, result);
}
#[test]
fn compare_lt() {
let mut result = 0;
unsafe { CompareBrowserVersions(w!("1.0.0"), w!("1.0.1"), &mut result) }.unwrap();
assert_eq!(-1, result);
}
#[test]
fn compare_gt() {
let mut result = 0;
unsafe { CompareBrowserVersions(w!("2.0.0"), w!("1.0.1"), &mut result) }.unwrap();
assert_eq!(1, result);
}
}
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.