From 38ca621ea514fcf928d1bb30754bc130269b6aa3 Mon Sep 17 00:00:00 2001 From: rdlabo Date: Tue, 10 Nov 2026 15:07:02 +0900 Subject: [PATCH 1/8] perf: encode native snapshots with Rust and reuse prepared images --- .github/workflows/native-ui-engine.yml | 11 + .gitignore | 2 + .prettierignore | 4 + Package.swift | 3 +- WebViewNativeUIEngine.podspec | 3 +- android/build.gradle | 4 + android/consumer-rules.pro | 4 + .../engine/NativeUIEngineTest.kt | 38 + .../engine/NativeUIPngTest.kt | 72 + .../webview_native_ui/engine/NativeUIPng.kt | 41 + .../engine/NativeUISnapshots.kt | 50 +- docs/advanced/README.md | 2 + docs/advanced/native-png.md | 141 + docs/maintenance.md | 49 + docs/validation/README.md | 51 + docs/validation/image-pipeline-ios.json | 55 + docs/validation/review-rerun-android.json | 662 + .../review-rerun-android.metadata.json | 22 + docs/validation/review-rerun-ios.json | 662 + .../validation/review-rerun-ios.metadata.json | 25 + docs/validation/review.md | 52 + docs/validation/rust-png-android.json | 662 + docs/validation/rust-png-ios-p3.json | 662 + .../CoveredSnapshots.swift | 118 +- .../NativeUIPngEncoder.swift | 38 + .../NativeUIPngLicenses/Rust-Apache-2.0.txt | 73 + .../Rust-LLVM-exception.txt | 15 + .../NativeUIPngLicenses/Rust-MIT.txt | 9 + .../NativeUIPngLicenses/Rust-Unicode-3.0.txt | 39 + .../Rust-standard-library.html | 29357 ++++++++++++++++ .../adler2-2.0.1-LICENSE-0BSD | 12 + .../adler2-2.0.1-LICENSE-APACHE | 201 + .../adler2-2.0.1-LICENSE-MIT | 23 + .../bitflags-1.3.2-LICENSE-APACHE | 201 + .../bitflags-1.3.2-LICENSE-MIT | 25 + .../cfg-if-1.0.5-LICENSE-APACHE | 201 + .../cfg-if-1.0.5-LICENSE-MIT | 25 + .../crc32fast-1.5.4-LICENSE-APACHE | 202 + .../crc32fast-1.5.4-LICENSE-MIT | 21 + .../NativeUIPngLicenses/dependencies.json | 104 + .../fdeflate-0.3.7-LICENSE-APACHE | 177 + .../fdeflate-0.3.7-LICENSE-MIT | 25 + .../flate2-1.1.11-LICENSE-APACHE | 201 + .../flate2-1.1.11-LICENSE-MIT | 25 + .../miniz_oxide-0.8.9-LICENSE | 25 + .../miniz_oxide-0.8.9-LICENSE-APACHE.md | 177 + .../miniz_oxide-0.8.9-LICENSE-MIT.md | 24 + .../miniz_oxide-0.8.9-LICENSE-ZLIB.md | 14 + .../miniz_oxide-0.9.1-LICENSE | 25 + .../miniz_oxide-0.9.1-LICENSE-APACHE.md | 177 + .../miniz_oxide-0.9.1-LICENSE-MIT.md | 24 + .../miniz_oxide-0.9.1-LICENSE-ZLIB.md | 14 + .../png-0.17.16-LICENSE-APACHE | 201 + .../png-0.17.16-LICENSE-MIT | 25 + .../simd-adler32-0.3.10-LICENSE.md | 21 + .../NativeUIPngLicenses/zlib-rs-0.6.8-LICENSE | 19 + .../CoveredSnapshotsTests.swift | 122 + .../NativeUIPngEncoderTests.swift | 53 + native/android/png-jni.c | 19 + .../NativeUIPng.xcframework/Info.plist | 48 + .../ios-arm64/Headers/NativeUIPng.h | 12 + .../ios-arm64/Headers/module.modulemap | 1 + .../ios-arm64/libnative_ui_png.a | Bin 0 -> 2592504 bytes .../Headers/NativeUIPng.h | 12 + .../Headers/module.modulemap | 1 + .../libnative_ui_png.a | Bin 0 -> 5049960 bytes native/artifacts/all-build.json | 91 + .../android/arm64-v8a/libnative_ui_png.so | Bin 0 -> 1451424 bytes .../android/armeabi-v7a/libnative_ui_png.so | Bin 0 -> 1073880 bytes .../artifacts/android/x86/libnative_ui_png.so | Bin 0 -> 1482724 bytes .../android/x86_64/libnative_ui_png.so | Bin 0 -> 1487264 bytes native/include/NativeUIPng.h | 12 + native/include/module.modulemap | 1 + native/licenses/Android-NDK-NOTICE | 9874 ++++++ native/licenses/Android-NDK-NOTICE.toolchain | 15179 ++++++++ native/licenses/Rust-Apache-2.0.txt | 73 + native/licenses/Rust-LLVM-exception.txt | 15 + native/licenses/Rust-MIT.txt | 9 + native/licenses/Rust-Unicode-3.0.txt | 39 + native/licenses/Rust-standard-library.html | 29357 ++++++++++++++++ native/licenses/adler2-2.0.1-LICENSE-0BSD | 12 + native/licenses/adler2-2.0.1-LICENSE-APACHE | 201 + native/licenses/adler2-2.0.1-LICENSE-MIT | 23 + native/licenses/bitflags-1.3.2-LICENSE-APACHE | 201 + native/licenses/bitflags-1.3.2-LICENSE-MIT | 25 + native/licenses/cfg-if-1.0.5-LICENSE-APACHE | 201 + native/licenses/cfg-if-1.0.5-LICENSE-MIT | 25 + .../licenses/crc32fast-1.5.4-LICENSE-APACHE | 202 + native/licenses/crc32fast-1.5.4-LICENSE-MIT | 21 + native/licenses/dependencies.json | 104 + native/licenses/fdeflate-0.3.7-LICENSE-APACHE | 177 + native/licenses/fdeflate-0.3.7-LICENSE-MIT | 25 + native/licenses/flate2-1.1.11-LICENSE-APACHE | 201 + native/licenses/flate2-1.1.11-LICENSE-MIT | 25 + native/licenses/miniz_oxide-0.8.9-LICENSE | 25 + .../miniz_oxide-0.8.9-LICENSE-APACHE.md | 177 + .../licenses/miniz_oxide-0.8.9-LICENSE-MIT.md | 24 + .../miniz_oxide-0.8.9-LICENSE-ZLIB.md | 14 + native/licenses/miniz_oxide-0.9.1-LICENSE | 25 + .../miniz_oxide-0.9.1-LICENSE-APACHE.md | 177 + .../licenses/miniz_oxide-0.9.1-LICENSE-MIT.md | 24 + .../miniz_oxide-0.9.1-LICENSE-ZLIB.md | 14 + native/licenses/png-0.17.16-LICENSE-APACHE | 201 + native/licenses/png-0.17.16-LICENSE-MIT | 25 + .../licenses/simd-adler32-0.3.10-LICENSE.md | 21 + native/licenses/zlib-rs-0.6.8-LICENSE | 19 + native/png/Cargo.lock | 102 + native/png/Cargo.toml | 14 + native/png/src/lib.rs | 284 + package.json | 8 +- scripts/benchmark-native-png.py | 146 + scripts/build-native-png.py | 105 + scripts/native-ui-engine-release/verify.py | 4 + scripts/verify-image-pipeline.py | 62 + scripts/verify-native-png.py | 39 + scripts/verify-package.mjs | 3 + src/projection/web.ts | 15 +- src/types.ts | 2 + tests/performance/fresh/README.md | 116 + .../fresh/android/AndroidManifest.xml | 1 + .../performance/fresh/android/MainActivity.kt | 87 + tests/performance/fresh/android/build.gradle | 13 + .../performance/fresh/android/settings.gradle | 4 + tests/performance/fresh/ios.swift | 92 + tests/performance/ios/main.swift | 128 + tests/performance/ios/pipeline.ts | 97 + tests/projection/web.spec.ts | 59 + 127 files changed, 93578 insertions(+), 25 deletions(-) create mode 100644 android/consumer-rules.pro create mode 100644 android/src/androidTest/java/io/github/webview_native_ui/engine/NativeUIPngTest.kt create mode 100644 android/src/main/java/io/github/webview_native_ui/engine/NativeUIPng.kt create mode 100644 docs/advanced/native-png.md create mode 100644 docs/validation/README.md create mode 100644 docs/validation/image-pipeline-ios.json create mode 100644 docs/validation/review-rerun-android.json create mode 100644 docs/validation/review-rerun-android.metadata.json create mode 100644 docs/validation/review-rerun-ios.json create mode 100644 docs/validation/review-rerun-ios.metadata.json create mode 100644 docs/validation/review.md create mode 100644 docs/validation/rust-png-android.json create mode 100644 docs/validation/rust-png-ios-p3.json create mode 100644 ios/Sources/WebViewNativeUIEngine/NativeUIPngEncoder.swift create mode 100644 ios/Sources/WebViewNativeUIEngine/Resources/NativeUIPngLicenses/Rust-Apache-2.0.txt create mode 100644 ios/Sources/WebViewNativeUIEngine/Resources/NativeUIPngLicenses/Rust-LLVM-exception.txt create mode 100644 ios/Sources/WebViewNativeUIEngine/Resources/NativeUIPngLicenses/Rust-MIT.txt create mode 100644 ios/Sources/WebViewNativeUIEngine/Resources/NativeUIPngLicenses/Rust-Unicode-3.0.txt create mode 100644 ios/Sources/WebViewNativeUIEngine/Resources/NativeUIPngLicenses/Rust-standard-library.html create mode 100644 ios/Sources/WebViewNativeUIEngine/Resources/NativeUIPngLicenses/adler2-2.0.1-LICENSE-0BSD create mode 100644 ios/Sources/WebViewNativeUIEngine/Resources/NativeUIPngLicenses/adler2-2.0.1-LICENSE-APACHE create mode 100644 ios/Sources/WebViewNativeUIEngine/Resources/NativeUIPngLicenses/adler2-2.0.1-LICENSE-MIT create mode 100644 ios/Sources/WebViewNativeUIEngine/Resources/NativeUIPngLicenses/bitflags-1.3.2-LICENSE-APACHE create mode 100644 ios/Sources/WebViewNativeUIEngine/Resources/NativeUIPngLicenses/bitflags-1.3.2-LICENSE-MIT create mode 100644 ios/Sources/WebViewNativeUIEngine/Resources/NativeUIPngLicenses/cfg-if-1.0.5-LICENSE-APACHE create mode 100644 ios/Sources/WebViewNativeUIEngine/Resources/NativeUIPngLicenses/cfg-if-1.0.5-LICENSE-MIT create mode 100644 ios/Sources/WebViewNativeUIEngine/Resources/NativeUIPngLicenses/crc32fast-1.5.4-LICENSE-APACHE create mode 100644 ios/Sources/WebViewNativeUIEngine/Resources/NativeUIPngLicenses/crc32fast-1.5.4-LICENSE-MIT create mode 100644 ios/Sources/WebViewNativeUIEngine/Resources/NativeUIPngLicenses/dependencies.json create mode 100644 ios/Sources/WebViewNativeUIEngine/Resources/NativeUIPngLicenses/fdeflate-0.3.7-LICENSE-APACHE create mode 100644 ios/Sources/WebViewNativeUIEngine/Resources/NativeUIPngLicenses/fdeflate-0.3.7-LICENSE-MIT create mode 100644 ios/Sources/WebViewNativeUIEngine/Resources/NativeUIPngLicenses/flate2-1.1.11-LICENSE-APACHE create mode 100644 ios/Sources/WebViewNativeUIEngine/Resources/NativeUIPngLicenses/flate2-1.1.11-LICENSE-MIT create mode 100644 ios/Sources/WebViewNativeUIEngine/Resources/NativeUIPngLicenses/miniz_oxide-0.8.9-LICENSE create mode 100644 ios/Sources/WebViewNativeUIEngine/Resources/NativeUIPngLicenses/miniz_oxide-0.8.9-LICENSE-APACHE.md create mode 100644 ios/Sources/WebViewNativeUIEngine/Resources/NativeUIPngLicenses/miniz_oxide-0.8.9-LICENSE-MIT.md create mode 100644 ios/Sources/WebViewNativeUIEngine/Resources/NativeUIPngLicenses/miniz_oxide-0.8.9-LICENSE-ZLIB.md create mode 100644 ios/Sources/WebViewNativeUIEngine/Resources/NativeUIPngLicenses/miniz_oxide-0.9.1-LICENSE create mode 100644 ios/Sources/WebViewNativeUIEngine/Resources/NativeUIPngLicenses/miniz_oxide-0.9.1-LICENSE-APACHE.md create mode 100644 ios/Sources/WebViewNativeUIEngine/Resources/NativeUIPngLicenses/miniz_oxide-0.9.1-LICENSE-MIT.md create mode 100644 ios/Sources/WebViewNativeUIEngine/Resources/NativeUIPngLicenses/miniz_oxide-0.9.1-LICENSE-ZLIB.md create mode 100644 ios/Sources/WebViewNativeUIEngine/Resources/NativeUIPngLicenses/png-0.17.16-LICENSE-APACHE create mode 100644 ios/Sources/WebViewNativeUIEngine/Resources/NativeUIPngLicenses/png-0.17.16-LICENSE-MIT create mode 100644 ios/Sources/WebViewNativeUIEngine/Resources/NativeUIPngLicenses/simd-adler32-0.3.10-LICENSE.md create mode 100644 ios/Sources/WebViewNativeUIEngine/Resources/NativeUIPngLicenses/zlib-rs-0.6.8-LICENSE create mode 100644 ios/Tests/WebViewNativeUIEngineTests/NativeUIPngEncoderTests.swift create mode 100644 native/android/png-jni.c create mode 100644 native/artifacts/NativeUIPng.xcframework/Info.plist create mode 100644 native/artifacts/NativeUIPng.xcframework/ios-arm64/Headers/NativeUIPng.h create mode 100644 native/artifacts/NativeUIPng.xcframework/ios-arm64/Headers/module.modulemap create mode 100644 native/artifacts/NativeUIPng.xcframework/ios-arm64/libnative_ui_png.a create mode 100644 native/artifacts/NativeUIPng.xcframework/ios-arm64_x86_64-simulator/Headers/NativeUIPng.h create mode 100644 native/artifacts/NativeUIPng.xcframework/ios-arm64_x86_64-simulator/Headers/module.modulemap create mode 100644 native/artifacts/NativeUIPng.xcframework/ios-arm64_x86_64-simulator/libnative_ui_png.a create mode 100644 native/artifacts/all-build.json create mode 100755 native/artifacts/android/arm64-v8a/libnative_ui_png.so create mode 100755 native/artifacts/android/armeabi-v7a/libnative_ui_png.so create mode 100755 native/artifacts/android/x86/libnative_ui_png.so create mode 100755 native/artifacts/android/x86_64/libnative_ui_png.so create mode 100644 native/include/NativeUIPng.h create mode 100644 native/include/module.modulemap create mode 100644 native/licenses/Android-NDK-NOTICE create mode 100644 native/licenses/Android-NDK-NOTICE.toolchain create mode 100644 native/licenses/Rust-Apache-2.0.txt create mode 100644 native/licenses/Rust-LLVM-exception.txt create mode 100644 native/licenses/Rust-MIT.txt create mode 100644 native/licenses/Rust-Unicode-3.0.txt create mode 100644 native/licenses/Rust-standard-library.html create mode 100644 native/licenses/adler2-2.0.1-LICENSE-0BSD create mode 100644 native/licenses/adler2-2.0.1-LICENSE-APACHE create mode 100644 native/licenses/adler2-2.0.1-LICENSE-MIT create mode 100644 native/licenses/bitflags-1.3.2-LICENSE-APACHE create mode 100644 native/licenses/bitflags-1.3.2-LICENSE-MIT create mode 100644 native/licenses/cfg-if-1.0.5-LICENSE-APACHE create mode 100644 native/licenses/cfg-if-1.0.5-LICENSE-MIT create mode 100644 native/licenses/crc32fast-1.5.4-LICENSE-APACHE create mode 100644 native/licenses/crc32fast-1.5.4-LICENSE-MIT create mode 100644 native/licenses/dependencies.json create mode 100644 native/licenses/fdeflate-0.3.7-LICENSE-APACHE create mode 100644 native/licenses/fdeflate-0.3.7-LICENSE-MIT create mode 100644 native/licenses/flate2-1.1.11-LICENSE-APACHE create mode 100644 native/licenses/flate2-1.1.11-LICENSE-MIT create mode 100644 native/licenses/miniz_oxide-0.8.9-LICENSE create mode 100644 native/licenses/miniz_oxide-0.8.9-LICENSE-APACHE.md create mode 100644 native/licenses/miniz_oxide-0.8.9-LICENSE-MIT.md create mode 100644 native/licenses/miniz_oxide-0.8.9-LICENSE-ZLIB.md create mode 100644 native/licenses/miniz_oxide-0.9.1-LICENSE create mode 100644 native/licenses/miniz_oxide-0.9.1-LICENSE-APACHE.md create mode 100644 native/licenses/miniz_oxide-0.9.1-LICENSE-MIT.md create mode 100644 native/licenses/miniz_oxide-0.9.1-LICENSE-ZLIB.md create mode 100644 native/licenses/png-0.17.16-LICENSE-APACHE create mode 100644 native/licenses/png-0.17.16-LICENSE-MIT create mode 100644 native/licenses/simd-adler32-0.3.10-LICENSE.md create mode 100644 native/licenses/zlib-rs-0.6.8-LICENSE create mode 100644 native/png/Cargo.lock create mode 100644 native/png/Cargo.toml create mode 100644 native/png/src/lib.rs create mode 100644 scripts/benchmark-native-png.py create mode 100644 scripts/build-native-png.py create mode 100644 scripts/verify-image-pipeline.py create mode 100644 scripts/verify-native-png.py create mode 100644 tests/performance/fresh/README.md create mode 100644 tests/performance/fresh/android/AndroidManifest.xml create mode 100644 tests/performance/fresh/android/MainActivity.kt create mode 100644 tests/performance/fresh/android/build.gradle create mode 100644 tests/performance/fresh/android/settings.gradle create mode 100644 tests/performance/fresh/ios.swift create mode 100644 tests/performance/ios/main.swift create mode 100644 tests/performance/ios/pipeline.ts diff --git a/.github/workflows/native-ui-engine.yml b/.github/workflows/native-ui-engine.yml index 5cbae23..c5b6002 100644 --- a/.github/workflows/native-ui-engine.yml +++ b/.github/workflows/native-ui-engine.yml @@ -8,6 +8,15 @@ on: permissions: contents: read jobs: + rust: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v5 + - run: rustup toolchain install 1.99.1 --profile minimal --component rustfmt --component clippy + - run: cargo +1.99.1 fmt --manifest-path native/png/Cargo.toml --check + - run: cargo +1.99.1 test --locked --manifest-path native/png/Cargo.toml + - run: cargo +1.99.1 clippy --locked --manifest-path native/png/Cargo.toml --all-targets -- -D warnings + - run: python3 scripts/verify-native-png.py lint: runs-on: macos-15-intel steps: @@ -61,6 +70,8 @@ jobs: cd ios/Tests xcodegen generate xcodebuild -project NativeUIControlsHostedTests.xcodeproj -scheme NativeUIControlsHostedTests -destination "platform=iOS Simulator,id=$CORDOVA_SIMULATOR" CODE_SIGNING_ALLOWED=NO -only-testing:WebViewNativeUIControlsTests/NativeUISnapshotTests/testNativePopoverClosureNotifiesWebBeforeReopening -only-testing:WebViewNativeUIControlsTests/NativeUISnapshotTests/testPopoverReopensAfterPresentationIdentityKeepsReplacedSource test + - name: Verify capture-to-Web image reuse on a real simulator screen + run: python3 scripts/verify-image-pipeline.py --device "$CORDOVA_SIMULATOR" - name: Verify npm-distributed CocoaPod run: | set -euo pipefail diff --git a/.gitignore b/.gitignore index e96cca6..2cdce62 100644 --- a/.gitignore +++ b/.gitignore @@ -18,3 +18,5 @@ android/**/.kotlin/ ios/Tests/*.xcodeproj/ ios/Tests/TestHost/Info.plist + +native/png/target/ diff --git a/.prettierignore b/.prettierignore index b52a57d..e4dbc01 100644 --- a/.prettierignore +++ b/.prettierignore @@ -12,3 +12,7 @@ ios/Sources/WebViewNativeUIEngine/Resources/native-ui-engine.js runtime/native-ui-engine.js android/**/build/ + +native/licenses/ +native/artifacts/ +ios/Sources/WebViewNativeUIEngine/Resources/NativeUIPngLicenses/ diff --git a/Package.swift b/Package.swift index d6e3913..e0d0628 100644 --- a/Package.swift +++ b/Package.swift @@ -7,6 +7,7 @@ let package = Package( targets: [ .testTarget(name: "WebViewNativeUIControlsTests", dependencies: ["WebViewNativeUIControls"], path: "ios/Tests/WebViewNativeUIControlsTests"), .target(name: "WebViewNativeUIControls", dependencies: ["WebViewNativeUIEngine"], path: "ios/Sources/WebViewNativeUIControls"), - .target(name: "WebViewNativeUIEngine", path: "ios/Sources/WebViewNativeUIEngine", resources: [.process("Resources")]), + .binaryTarget(name: "NativeUIPng", path: "native/artifacts/NativeUIPng.xcframework"), + .target(name: "WebViewNativeUIEngine", dependencies: ["NativeUIPng"], path: "ios/Sources/WebViewNativeUIEngine", resources: [.process("Resources")]), .testTarget(name: "WebViewNativeUIEngineTests", dependencies: ["WebViewNativeUIEngine"], path: "ios/Tests/WebViewNativeUIEngineTests") ]) diff --git a/WebViewNativeUIEngine.podspec b/WebViewNativeUIEngine.podspec index 2738895..e83a8cc 100644 --- a/WebViewNativeUIEngine.podspec +++ b/WebViewNativeUIEngine.podspec @@ -9,7 +9,8 @@ Pod::Spec.new do |s| s.preserve_paths = 'LICENSE' s.author = { 'rdlabo' => 'sakakibara@rdlabo.jp' } s.source = { :git => 'https://github.com/webview-native-ui/engine.git', :tag => "v#{s.version}" } - s.resource_bundles = { 'WebViewNativeUIEngine' => ['ios/Sources/WebViewNativeUIEngine/Resources/*.js'] } + s.resource_bundles = { 'WebViewNativeUIEngine' => ['ios/Sources/WebViewNativeUIEngine/Resources/*.js', 'ios/Sources/WebViewNativeUIEngine/Resources/NativeUIPngLicenses/*'] } + s.vendored_frameworks = 'native/artifacts/NativeUIPng.xcframework' s.source_files = 'ios/Sources/WebViewNativeUIEngine/**/*.swift' s.ios.deployment_target = '15.0' s.swift_version = '5.9' diff --git a/android/build.gradle b/android/build.gradle index 8cb07cd..dc8d510 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -10,9 +10,11 @@ version = new groovy.json.JsonSlurper().parse(file('../package.json')).version android { namespace 'io.github.webview_native_ui.engine' compileSdk 36 + sourceSets.main.jniLibs.srcDir('../native/artifacts/android') lint { warningsAsErrors = true } defaultConfig { minSdk 23 + consumerProguardFiles 'consumer-rules.pro' testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner' } compileOptions { sourceCompatibility JavaVersion.VERSION_17; targetCompatibility JavaVersion.VERSION_17 } @@ -77,6 +79,7 @@ tasks.register('centralBundle', Zip) { // Keep the package license in binary and source distributions without duplicate source files. def licenseResources = tasks.register('prepareLicenseResources', Sync) { from('..') { include 'LICENSE' } + from('../native/licenses') { into 'native-ui-png' } into(layout.buildDirectory.dir('generated/license-resources/META-INF/webview-native-ui-engine')) } android.sourceSets.main.resources.srcDir(layout.buildDirectory.dir('generated/license-resources')) @@ -84,6 +87,7 @@ tasks.matching { it.name.startsWith('process') && it.name.endsWith('JavaRes') }. tasks.withType(Jar).configureEach { if (name in ['sourceReleaseJar', 'javaDocReleaseJar']) { from('..') { include 'LICENSE'; into 'META-INF/webview-native-ui-engine' } + from('../native/licenses') { into 'META-INF/webview-native-ui-engine/native-ui-png' } } } diff --git a/android/consumer-rules.pro b/android/consumer-rules.pro new file mode 100644 index 0000000..e817494 --- /dev/null +++ b/android/consumer-rules.pro @@ -0,0 +1,4 @@ +# JNI looks this class and its native entry point up by their JVM names. +-keep class io.github.webview_native_ui.engine.NativeUIPng { + native ; +} diff --git a/android/src/androidTest/java/io/github/webview_native_ui/engine/NativeUIEngineTest.kt b/android/src/androidTest/java/io/github/webview_native_ui/engine/NativeUIEngineTest.kt index 545df2a..e879b19 100644 --- a/android/src/androidTest/java/io/github/webview_native_ui/engine/NativeUIEngineTest.kt +++ b/android/src/androidTest/java/io/github/webview_native_ui/engine/NativeUIEngineTest.kt @@ -542,8 +542,23 @@ class NativeUIEngineTest { assertFalse(original.exists()) val changedFile = File(java.net.URI(changed.getJSONArray("parts").getJSONObject(0).getString("url"))) + main { native.alpha = 0.9f } + instrumentation.waitForIdleSync() + val appearanceChanged = prepareAgain(1) + val appearanceFile = + File( + java.net.URI( + appearanceChanged.getJSONArray("parts").getJSONObject(0).getString("url") + ) + ) + assertNotEquals( + "Native appearance changes must invalidate cached pixels even at the same content revision", + changedFile, + appearanceFile, + ) main { snapshots.reset() } assertFalse(changedFile.exists()) + assertFalse(appearanceFile.exists()) val newDocument = prepareAgain(1) assertNotEquals( changedFile.toURI().toString(), @@ -1457,18 +1472,24 @@ class NativeUIEngineTest { fun explicitPlacementIsNotOverwrittenByOlderImageFrame() = nativeInitialization(NativeUIProjection.CaptureMode.VIEW, authoritativePlacement = true) + @Test + fun viewImageReuseSkipsFileReadAndDecodeAcrossMovement() = + nativeInitialization(NativeUIProjection.CaptureMode.VIEW, auditReuse = true) + private fun nativeInitialization( mode: NativeUIProjection.CaptureMode, delayCover: Boolean = false, releaseWhileActive: Boolean = false, pressOnConnect: Boolean = false, authoritativePlacement: Boolean = false, + auditReuse: Boolean = false, ) { val activity = activity() lateinit var registration: NativeUIProjection lateinit var native: View val loaded = CountDownLatch(1) + val fileReads = java.util.concurrent.atomic.AtomicInteger() val pendingDraws = mutableListOf<() -> Unit>() var revealCount = 0 var pendingPlacement = false @@ -1540,6 +1561,7 @@ class NativeUIEngineTest { ) ) return null + fileReads.incrementAndGet() return runCatching { android.webkit.WebResourceResponse( "image/png", @@ -1640,6 +1662,8 @@ class NativeUIEngineTest { "https://localhost", """