WebClient: 3.44 webcodecs offline (#15722)

* feat(web): zero-readback WebCodecs video path

Decoded VideoFrames from js/src/webcodecs.js are handed to Flutter via
window.onVideoFrame and imported GPU-side with createImageFromTextureSource;
any failure unregisters the hook so the JS side falls back to RGBA readback.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix(web): load bundled terminal font when Google CDNs are unreachable

In air-gapped deployments GoogleFonts.robotoMono() cannot download the
terminal font; when index.html signals offline mode, load the copy bundled
with the web app under the family name google_fonts registers.

Part of the fix for rustdesk/rustdesk-server-pro#996.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* ci: bump windows arm64 to Flutter 3.44.8, add web build patch script

apply_flutter_3.44_web_patches.sh prepares a 3.44.x web build on top of the
shared source patches: qr_code_scanner's web impl needs dart:ui_web for the
removed platformViewRegistry, and flutter/web/fonts is refreshed to the font
paths the 3.44 engine requests. The disabled build-rustdesk-web job runs it
automatically once FLUTTER_VERSION moves to 3.44.x, and version-guarded
'Patch flutter' steps no longer fail when the guard does not match.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix(web): prevent stale WebCodecs frames across sessions

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(web): harden WebCodecs reconnect and Flutter 3.44 patches

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(ci): harden Flutter 3.44 patch input validation

Validate required files before checking patch state,
parameterize the theme-range validator, and prevent
missing inputs from satisfying NO_MATCHES checks.

Signed-off-by: fufesou <linlong1266@gmail.com>

* Remove unused code

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(web): retry font loading and dispose stale decoded images

Signed-off-by: fufesou <linlong1266@gmail.com>

* remove unused code

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(web): Bad state: RenderBox was not laid out

Signed-off-by: fufesou <linlong1266@gmail.com>

---------

Signed-off-by: fufesou <linlong1266@gmail.com>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Co-authored-by: fufesou <linlong1266@gmail.com>
This commit is contained in:
RustDesk
2026-08-07 15:20:57 +08:00
committed by GitHub
parent 6fd96dda6e
commit 4234b99029
12 changed files with 472 additions and 19 deletions

View File

@@ -17,6 +17,109 @@
# therefore CRLF-safe.
set -euo pipefail
readonly NO_MATCHES=0
readonly SINGLE_MATCH=1
readonly THEME_MATCHES=2
has_exact_count() {
local -r expected_count="$1"
local -r pattern="$2"
local -r file="$3"
local actual_count
[[ -r "$file" ]] || return 1
actual_count="$(grep -cF "$pattern" "$file" || true)"
[[ "$actual_count" -eq "$expected_count" ]]
}
# The target background-color line must directly follow DialogThemeData in the selected range.
has_dialog_background_in_theme_range() {
local -r start_pattern="$1"
local -r end_pattern="$2"
local -r target_pattern="$3"
local -r file="$4"
awk -v start_pattern="$start_pattern" \
-v end_pattern="$end_pattern" \
-v target_pattern="$target_pattern" '
index($0, start_pattern) {
in_theme = 1
next
}
in_theme && index($0, end_pattern) {
exit
}
in_theme && index($0, "dialogTheme: DialogThemeData(") {
if (getline > 0) {
line = $0
sub(/\r$/, "", line)
sub(/^[[:space:]]+/, "", line)
matched = line == target_pattern
}
exit
}
END {
exit matched ? 0 : 1
}
' "$file"
}
validate_patch_inputs() {
if [[ ! -f flutter/lib/common.dart || ! -r flutter/lib/common.dart ]]; then
echo "Flutter 3.44 source patch input is missing or unreadable: flutter/lib/common.dart" >&2
return 1
fi
if [[ ! -f flutter/pubspec.yaml || ! -r flutter/pubspec.yaml ]]; then
echo "Flutter 3.44 source patch input is missing or unreadable: flutter/pubspec.yaml" >&2
return 1
fi
}
is_complete_patch_state() {
has_exact_count "$THEME_MATCHES" 'dialogTheme: DialogThemeData(' flutter/lib/common.dart &&
has_exact_count "$THEME_MATCHES" 'tabBarTheme: const TabBarThemeData(' flutter/lib/common.dart &&
has_exact_count "$SINGLE_MATCH" 'backgroundColor: Colors.white,' flutter/lib/common.dart &&
has_exact_count "$SINGLE_MATCH" 'backgroundColor: Color(0xFF18191E),' flutter/lib/common.dart &&
has_exact_count "$SINGLE_MATCH" 'extended_text: 15.0.2' flutter/pubspec.yaml &&
has_exact_count "$SINGLE_MATCH" 'google_fonts: ^8.1.0' flutter/pubspec.yaml &&
has_exact_count "$NO_MATCHES" 'dialogTheme: DialogTheme(' flutter/lib/common.dart &&
has_exact_count "$NO_MATCHES" 'tabBarTheme: const TabBarTheme(' flutter/lib/common.dart &&
has_exact_count "$NO_MATCHES" 'extended_text: 14.0.0' flutter/pubspec.yaml &&
has_exact_count "$NO_MATCHES" 'google_fonts: ^6.2.1' flutter/pubspec.yaml &&
has_dialog_background_in_theme_range 'static ThemeData lightTheme = ThemeData(' \
'static ThemeData darkTheme = ThemeData(' 'backgroundColor: Colors.white,' \
flutter/lib/common.dart &&
has_dialog_background_in_theme_range 'static ThemeData darkTheme = ThemeData(' \
'scrollbarTheme: scrollbarThemeDark,' 'backgroundColor: Color(0xFF18191E),' \
flutter/lib/common.dart
}
is_unpatched_state() {
has_exact_count "$THEME_MATCHES" 'dialogTheme: DialogTheme(' flutter/lib/common.dart &&
has_exact_count "$THEME_MATCHES" 'tabBarTheme: const TabBarTheme(' flutter/lib/common.dart &&
has_exact_count "$SINGLE_MATCH" 'extended_text: 14.0.0' flutter/pubspec.yaml &&
has_exact_count "$SINGLE_MATCH" 'google_fonts: ^6.2.1' flutter/pubspec.yaml &&
has_exact_count "$NO_MATCHES" 'dialogTheme: DialogThemeData(' flutter/lib/common.dart &&
has_exact_count "$NO_MATCHES" 'tabBarTheme: const TabBarThemeData(' flutter/lib/common.dart &&
has_exact_count "$NO_MATCHES" 'backgroundColor: Colors.white,' flutter/lib/common.dart &&
has_exact_count "$NO_MATCHES" 'backgroundColor: Color(0xFF18191E),' flutter/lib/common.dart &&
has_exact_count "$NO_MATCHES" 'extended_text: 15.0.2' flutter/pubspec.yaml &&
has_exact_count "$NO_MATCHES" 'google_fonts: ^8.1.0' flutter/pubspec.yaml
}
if ! validate_patch_inputs; then
exit 1
fi
if is_complete_patch_state; then
echo "Flutter 3.44 source patches already applied."
git --no-pager diff -- flutter/lib/common.dart flutter/pubspec.yaml
exit 0
fi
if ! is_unpatched_state; then
echo "Flutter 3.44 source patches are partially applied or their anchors have drifted." >&2
exit 1
fi
# ThemeData API renames (Flutter 3.27+):
sed -i 's/dialogTheme: DialogTheme(/dialogTheme: DialogThemeData(/g' flutter/lib/common.dart
sed -i 's/tabBarTheme: const TabBarTheme(/tabBarTheme: const TabBarThemeData(/g' flutter/lib/common.dart
@@ -28,12 +131,10 @@ sed -i '/static ThemeData darkTheme = ThemeData(/,/scrollbarTheme: scrollbarThem
sed -i 's/extended_text: 14.0.0/extended_text: 15.0.2/' flutter/pubspec.yaml
sed -i 's/google_fonts: \^6.2.1/google_fonts: ^8.1.0/' flutter/pubspec.yaml
# Fail loudly if any expected string drifted, so we never silently build unpatched:
grep -qF 'dialogTheme: DialogThemeData(' flutter/lib/common.dart
grep -qF 'tabBarTheme: const TabBarThemeData(' flutter/lib/common.dart
grep -qF 'backgroundColor: Colors.white,' flutter/lib/common.dart
grep -qF 'backgroundColor: Color(0xFF18191E),' flutter/lib/common.dart
grep -qF 'extended_text: 15.0.2' flutter/pubspec.yaml
grep -qF 'google_fonts: ^8.1.0' flutter/pubspec.yaml
# Fail loudly if any expected substitution did not produce the complete state.
if ! is_complete_patch_state; then
echo "Flutter 3.44 source patches did not produce the expected state." >&2
exit 1
fi
git --no-pager diff -- flutter/lib/common.dart flutter/pubspec.yaml

View File

@@ -0,0 +1,51 @@
#!/usr/bin/env bash
# Prepares a web build on Flutter 3.44.x. Companion to
# apply_flutter_3.44_source_patches.sh (which it runs first): the web target
# additionally needs qr_code_scanner's web implementation patched for the
# dart:ui platformViewRegistry removal, and flutter/web/fonts refreshed with
# the font paths the 3.44 engine requests for offline/air-gapped support
# (rustdesk-server-pro#996; see flutter/web/fonts/sync_fonts.py).
#
# Run from the repository root with Flutter 3.44.x on PATH, then build:
# bash .github/patches/apply_flutter_3.44_web_patches.sh
# (cd flutter && flutter build web --release) # or ./web/js/flutter_build.py
#
# Idempotent. To undo the source changes locally:
# git checkout -- flutter/lib/common.dart flutter/pubspec.yaml flutter/pubspec.lock
set -euo pipefail
flutter --version | grep -q "Flutter 3\.44\." || {
echo "Flutter 3.44.x must be on PATH; found:" >&2
flutter --version | grep "^Flutter" >&2 || true
exit 1
}
# Shared 3.44 source/pubspec patches own their complete-state validation.
bash .github/patches/apply_flutter_3.44_source_patches.sh
# Populate the pub cache with the 3.44 dependency resolution.
(cd flutter && flutter pub get)
# qr_code_scanner 1.0.1 (unmaintained) reads platformViewRegistry from
# dart:ui, which Flutter 3.44 removed; point it at dart:ui_web instead. The
# patched file also compiles on Flutter 3.24 (dart:ui_web exists there), so
# mutating the shared pub cache is safe for other local builds.
QR_WEB="${PUB_CACHE:-$HOME/.pub-cache}/hosted/pub.dev/qr_code_scanner-1.0.1/lib/src/web/flutter_qr_web.dart"
if ! grep -qF "dart:ui_web" "$QR_WEB"; then
sed -i.bak "s|import 'dart:ui' as ui;|import 'dart:ui' as ui; import 'dart:ui_web' as ui_web;|" "$QR_WEB"
rm -f "$QR_WEB.bak"
fi
if grep -qF "ui.platformViewRegistry" "$QR_WEB"; then
sed -i.bak "s|ui\.platformViewRegistry|ui_web.platformViewRegistry|g" "$QR_WEB"
rm -f "$QR_WEB.bak"
fi
# Mirror the fonts this engine version requests into flutter/web/fonts.
python3 flutter/web/fonts/sync_fonts.py
# Fail loudly if any expected state is missing:
grep -qF "import 'dart:ui' as ui; import 'dart:ui_web' as ui_web;" "$QR_WEB"
grep -qF "ui_web.platformViewRegistry" "$QR_WEB"
grep -qF 'google_fonts: ^8.1.0' flutter/pubspec.yaml
echo "Flutter 3.44 web patches applied."