diff --git a/.changes/android-plugin-manager-no-activity.md b/.changes/android-plugin-manager-no-activity.md new file mode 100644 index 000000000000..90bc3bda7da1 --- /dev/null +++ b/.changes/android-plugin-manager-no-activity.md @@ -0,0 +1,5 @@ +--- +tauri: patch:bug +--- + +On Android, fixed requesting permission in a plugin fails after the first activity closed. diff --git a/.changes/appimage-respect-gdk-backend.md b/.changes/appimage-respect-gdk-backend.md new file mode 100644 index 000000000000..44644221ea03 --- /dev/null +++ b/.changes/appimage-respect-gdk-backend.md @@ -0,0 +1,5 @@ +--- +"tauri-bundler": "patch:bug" +--- + +Respect an explicitly configured `GDK_BACKEND` in AppImage GTK hooks while retaining `x11` as the default. diff --git a/.changes/build-config-path.md b/.changes/build-config-path.md new file mode 100644 index 000000000000..a86e359da37b --- /dev/null +++ b/.changes/build-config-path.md @@ -0,0 +1,5 @@ +--- +"tauri-build": minor:feat +--- + +Added `Attributes::config_path` to customize config path, deprecated `CodegenContext::config_path` in favor of this diff --git a/.changes/clarify-init-frontend-command-prompts.md b/.changes/clarify-init-frontend-command-prompts.md new file mode 100644 index 000000000000..91d0dfc93cce --- /dev/null +++ b/.changes/clarify-init-frontend-command-prompts.md @@ -0,0 +1,6 @@ +--- +"tauri-cli": "patch:enhance" +"@tauri-apps/cli": "patch:enhance" +--- + +Clarify that the `tauri init` frontend commands run before `tauri dev` and `tauri build`, and can be left empty when they are not needed. diff --git a/.changes/disable-binary-patching.md b/.changes/disable-binary-patching.md new file mode 100644 index 000000000000..8a911bcbaa1d --- /dev/null +++ b/.changes/disable-binary-patching.md @@ -0,0 +1,7 @@ +--- +"tauri-bundler": "minor:feat" +"tauri-cli": "minor:feat" +"@tauri-apps/cli": "minor:feat" +--- + +Add a `--no-binary-patching` flag to `tauri build` and `tauri bundle`. When set, the bundler skips patching the main executable with bundle type information (and the subsequent re-signing), leaving an already-signed binary untouched. Patching is only required when shipping multiple bundle types per platform that should each update with their own installer format. diff --git a/.changes/event-listener-cleanup-on-destroy.md b/.changes/event-listener-cleanup-on-destroy.md new file mode 100644 index 000000000000..e096256132da --- /dev/null +++ b/.changes/event-listener-cleanup-on-destroy.md @@ -0,0 +1,5 @@ +--- +"tauri": "patch:bug" +--- + +Remove Rust-side event listeners bound to a window or webview when that target is destroyed, so `listen`/`once` handlers registered on a `Window`, `Webview` or `WebviewWindow` no longer leak after it is closed. diff --git a/.changes/fix-appimage-xdg-utils.md b/.changes/fix-appimage-xdg-utils.md new file mode 100644 index 000000000000..94aa65a612f1 --- /dev/null +++ b/.changes/fix-appimage-xdg-utils.md @@ -0,0 +1,7 @@ +--- +tauri-bundler: patch:bug +tauri-cli: patch:bug +"@tauri-apps/cli": patch:bug +--- + +On Linux, do not bundle xdg-open and xdg-utils in the AppImage anymore. This rarely worked and usually requires host system support anyway. diff --git a/.changes/fix-copy-new-resource-files.md b/.changes/fix-copy-new-resource-files.md index 69b7a059e091..757b5aeec942 100644 --- a/.changes/fix-copy-new-resource-files.md +++ b/.changes/fix-copy-new-resource-files.md @@ -3,4 +3,4 @@ 'tauri-utils': 'patch:enhance' --- -Emit a `cargo:rerun-if-changed` for each resource directory and glob base directory, so adding or removing files inside configured resource directories re-runs the build script and copies the changed resources. +Emit a `cargo:rerun-if-changed` for each resource directory (and glob base directory), so that adding or removing a file inside a resource directory re-runs the build script and copies the new files. Previously only the individual files present at build time were watched, so newly added files were silently ignored until an unrelated rebuild. diff --git a/.changes/icon-non-square-fit.md b/.changes/icon-non-square-fit.md new file mode 100644 index 000000000000..826afbe57342 --- /dev/null +++ b/.changes/icon-non-square-fit.md @@ -0,0 +1,6 @@ +--- +"tauri-cli": "minor:feat" +"@tauri-apps/cli": "minor:feat" +--- + +Add a `--fit` option to `tauri icon` to accept non-square source images. `--fit cover` center-crops the source to a square (clipping the longer side) and `--fit contain` pads the shorter side with transparency. Non-square sources without `--fit` keep erroring, now with a hint pointing to the flag. diff --git a/.changes/mobile-resumed-suspended-event.md b/.changes/mobile-resumed-suspended-event.md new file mode 100644 index 000000000000..a7e8d89c6a45 --- /dev/null +++ b/.changes/mobile-resumed-suspended-event.md @@ -0,0 +1,6 @@ +--- +"tauri": minor:changes +"tauri-runtime-wry": minor:changes +--- + +`WindowEvent::Resumed` and `WindowEvent::Suspended` are now only fired for the matching activity window instead of every window. diff --git a/.changes/objc2-0.3.2.md b/.changes/objc2-0.3.2.md new file mode 100644 index 000000000000..83c6cffcb400 --- /dev/null +++ b/.changes/objc2-0.3.2.md @@ -0,0 +1,7 @@ +--- +"tauri": patch:deps +"tauri-runtime": patch:deps +"tauri-runtime-wry": patch:deps +--- + +On macOS, updated `objc2-*` dependencies to 0.3.2 diff --git a/.changes/permission-handler.md b/.changes/permission-handler.md new file mode 100644 index 000000000000..00da137d5370 --- /dev/null +++ b/.changes/permission-handler.md @@ -0,0 +1,9 @@ +--- +"tauri": "minor:feat" +"tauri-runtime": "minor:feat" +"tauri-runtime-wry": "minor:feat" +--- + +Expose the `wry` permission handler API through Tauri. +This includes support for permission types such as `DisplayCapture`, `Midi`, `Sensors`, `MediaKeySystemAccess`, `LocalFonts`, `WindowManagement`, `PointerLock`, `AutomaticDownloads`, `FileSystemAccess`, and `Autoplay`. +Added `PermissionResponse::{Allow, Deny, Default}` for runtime permission decisions. diff --git a/.changes/signing-env-vars.md b/.changes/signing-env-vars.md new file mode 100644 index 000000000000..d9cff76ac7c2 --- /dev/null +++ b/.changes/signing-env-vars.md @@ -0,0 +1,6 @@ +--- +"tauri-cli": patch:enhance +"@tauri-apps/cli": patch:enhance +--- + +Document the `TAURI_SIGNING_PRIVATE_KEY_PATH` environment variable and clarify that `TAURI_SIGNING_PRIVATE_KEY` accepts a string or a path for the `build` and `bundle` command but must be the literal key string for the `signer sign` command, both in `ENVIRONMENT_VARIABLES.md` and in the `signer generate` command output. diff --git a/.changes/unstable-webview-focus.md b/.changes/unstable-webview-focus.md new file mode 100644 index 000000000000..6854f716e884 --- /dev/null +++ b/.changes/unstable-webview-focus.md @@ -0,0 +1,6 @@ +--- +tauri: minor:bug +tauri-runtime-wry: minor:bug +--- + +Fix webview don't get focus when Alt-Tab back to the window if `unstable` feature is enabled on Windows diff --git a/ports/wry/.changes/update-dom-query.md b/.changes/update-dom-query.md similarity index 65% rename from ports/wry/.changes/update-dom-query.md rename to .changes/update-dom-query.md index 0a5653415d66..edfe93528c1f 100644 --- a/ports/wry/.changes/update-dom-query.md +++ b/.changes/update-dom-query.md @@ -1,5 +1,5 @@ --- -'wry': minor +"tauri-utils": minor:deps --- Updated `dom_query` dependency to 0.28.0 diff --git a/.changes/webkitgtk-resize-cursor.md b/.changes/webkitgtk-resize-cursor.md new file mode 100644 index 000000000000..89aee8d840d4 --- /dev/null +++ b/.changes/webkitgtk-resize-cursor.md @@ -0,0 +1,6 @@ +--- +'tauri-runtime-wry': 'patch:bug' +'tauri': 'patch:bug' +--- + +On Linux, fix resize cursor for undecorated Window not being set correctly when mouse is over a resize edge. diff --git a/.changes/webview-focus-on-move.md b/.changes/webview-focus-on-move.md new file mode 100644 index 000000000000..fb5ccc5be258 --- /dev/null +++ b/.changes/webview-focus-on-move.md @@ -0,0 +1,6 @@ +--- +tauri: minor:bug +tauri-runtime-wry: minor:bug +--- + +Fix `WindowEvent::Focused` events emitted when dragging the window on Windows diff --git a/.changes/wry-focused-webview.md b/.changes/wry-focused-webview.md new file mode 100644 index 000000000000..45a16428cec1 --- /dev/null +++ b/.changes/wry-focused-webview.md @@ -0,0 +1,5 @@ +--- +tauri-runtime-wry: minor:breaking +--- + +`CreateWebviewOptions::focused_webview` now takes `Arc>` instead of `Arc>>` diff --git a/.github/workflows/publish-cli-js.yml b/.github/workflows/publish-cli-js.yml index 429d47002985..417ee74e827f 100644 --- a/.github/workflows/publish-cli-js.yml +++ b/.github/workflows/publish-cli-js.yml @@ -91,7 +91,8 @@ jobs: - name: Install Rust uses: dtolnay/rust-toolchain@stable with: - targets: ${{ matrix.settings.target }} + # CEF helper needs both archs + targets: ${{ contains(matrix.settings.target, 'apple-darwin') && 'aarch64-apple-darwin,x86_64-apple-darwin' || matrix.settings.target }} - uses: Swatinem/rust-cache@v2 with: key: ${{ matrix.settings.target }} @@ -148,8 +149,8 @@ jobs: target: x86_64-apple-darwin architecture: x64 node: - - '20' - '22' + - '24' runs-on: ${{ matrix.settings.host }} steps: - uses: actions/checkout@v7 @@ -188,8 +189,8 @@ jobs: - aarch64-unknown-linux-musl - armv7-unknown-linux-gnueabihf node: - - '20' - '22' + - '24' runs-on: ${{ contains(matrix.target, 'aarch64') && 'ubuntu-24.04-arm' || 'ubuntu-latest' }} steps: - uses: actions/checkout@v7 @@ -244,7 +245,7 @@ jobs: if ('${{ matrix.target }}'.endsWith('-musl')) { console.log('IMAGE=node:${{ matrix.node }}-alpine') } else { - console.log('IMAGE=node:${{ matrix.node }}-slim') + console.log('IMAGE=node:22-slim') } " >> $GITHUB_OUTPUT diff --git a/.github/workflows/test-core.yml b/.github/workflows/test-core.yml index 4e9b87d8cd9f..c3554875c457 100644 --- a/.github/workflows/test-core.yml +++ b/.github/workflows/test-core.yml @@ -108,6 +108,10 @@ jobs: # Using --lib --bins --tests to skip doc tests run: ${{ matrix.platform.cargo }} ${{ matrix.platform.command }} --target ${{ matrix.platform.target }} ${{ matrix.features.args }} --lib --bins --tests --manifest-path crates/tauri-utils/Cargo.toml + - name: run acl-tests + if: ${{ matrix.features.key != 'no-default' && matrix.platform.command == 'test' }} + run: ${{ matrix.platform.cargo }} test --target ${{ matrix.platform.target }} ${{ matrix.features.args }} --manifest-path crates/tests/acl/Cargo.toml + - name: test tauri run: ${{ matrix.platform.cargo }} ${{ matrix.features.key == 'no-default' && 'check' || matrix.platform.command }} --target ${{ matrix.platform.target }} ${{ matrix.features.args }} --manifest-path crates/tauri/Cargo.toml diff --git a/Cargo.lock b/Cargo.lock index 4a63c03852bb..229c7067af8a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1060,9 +1060,9 @@ checksum = "a3e368af43e418a04d52505cf3dbc23dda4e3407ae2fa99fd0e4f308ce546acc" [[package]] name = "bytesize" -version = "2.4.2" +version = "2.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d7c8918969267b2932ffd5655509bbbea0833823058c378876953217f5fc50e" +checksum = "7354288c522e7e980fafd2075d63d1285794c3a6a16cdd492f189ea406e5f18b" [[package]] name = "bzip2" @@ -1246,13 +1246,13 @@ checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c" [[package]] name = "cfb" -version = "0.7.3" +version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d38f2da7a0a2c4ccf0065be06397cc26a81f4e528be095826eee9d4adbb8c60f" +checksum = "a347dcabdae9c31b0825fd6a8bed285ec9c2acb89c47827126d52fa4f59cece3" dependencies = [ - "byteorder", "fnv", "uuid", + "web-time", ] [[package]] @@ -1416,7 +1416,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "117725a109d387c937a1533ce01b450cbde6b88abceea8473c4d7a85853cda3c" dependencies = [ "lazy_static", - "windows-sys 0.48.0", + "windows-sys 0.59.0", ] [[package]] @@ -1795,19 +1795,6 @@ dependencies = [ "syn 1.0.109", ] -[[package]] -name = "cssparser" -version = "0.36.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dae61cf9c0abb83bd659dab65b7e4e38d8236824c85f0f804f173567bda257d2" -dependencies = [ - "cssparser-macros 0.6.1", - "dtoa-short", - "itoa", - "phf 0.13.1", - "smallvec", -] - [[package]] name = "cssparser" version = "0.37.0" @@ -2309,21 +2296,6 @@ dependencies = [ "syn 2.0.117", ] -[[package]] -name = "dom_query" -version = "0.27.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "521e380c0c8afb8d9a1e83a1822ee03556fc3e3e7dbc1fd30be14e37f9cb3f89" -dependencies = [ - "bit-set", - "cssparser 0.36.0", - "foldhash 0.2.0", - "html5ever 0.38.0", - "precomputed-hash", - "selectors 0.36.1", - "tendril 0.5.0", -] - [[package]] name = "dom_query" version = "0.28.0" @@ -2679,11 +2651,10 @@ dependencies = [ [[package]] name = "event-listener" -version = "5.4.1" +version = "5.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e13b66accf52311f30a0db42147dadea9850cb48cd070028831ae5f5d4b856ab" +checksum = "5a23add41df1562121a9393cb065eab5146a1242410f23a644851e90cfd669d2" dependencies = [ - "concurrent-queue", "parking", "pin-project-lite", ] @@ -3675,16 +3646,6 @@ dependencies = [ "match_token", ] -[[package]] -name = "html5ever" -version = "0.38.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1054432bae2f14e0061e33d23402fbaa67a921d319d56adc6bcf887ddad1cbc2" -dependencies = [ - "log", - "markup5ever 0.38.0", -] - [[package]] name = "html5ever" version = "0.39.0" @@ -4100,9 +4061,9 @@ dependencies = [ [[package]] name = "infer" -version = "0.19.0" +version = "0.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a588916bfdfd92e71cacef98a63d9b1f0d74d6599980d11894290e7ddefffcf7" +checksum = "f4200d433cbd5178df7797c9c2e75b348b728e39631cf14520d1e2fc424201f4" dependencies = [ "cfb", ] @@ -4458,9 +4419,9 @@ dependencies = [ [[package]] name = "jsonschema" -version = "0.47.0" +version = "0.49.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "281c43ff06dcb331e9356d30e38853d559ce3d0a3f693e0b0e102667dec14fb1" +checksum = "508004a5500f2e1f68af048f70feea2de86d35ab115d85716530860822aef397" dependencies = [ "ahash 0.8.11", "bytecount", @@ -4472,6 +4433,7 @@ dependencies = [ "idna", "itoa", "jsonschema-regex", + "jsonschema-value", "num-cmp", "num-traits", "percent-encoding", @@ -4479,19 +4441,34 @@ dependencies = [ "regex", "serde", "serde_json", + "strum 0.28.0", "unicode-general-category", "uuid-simd", ] [[package]] name = "jsonschema-regex" -version = "0.47.0" +version = "0.49.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee0b351864e7ffbc5db9273daf7fa1b4d5177b0946713d667ca571b83c0b4045" +checksum = "5a8b30cafa78358ae6cd1494a7d6410b89530e28bf567f862c869c667e900d9f" dependencies = [ "regex-syntax", ] +[[package]] +name = "jsonschema-value" +version = "0.49.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5526bd381d230af94908d07e6835a33fd82a465e12f5f1e9c81f5c2aa23b3c21" +dependencies = [ + "ahash 0.8.11", + "bytecount", + "fraction", + "num-cmp", + "num-traits", + "serde_json", +] + [[package]] name = "k256" version = "0.13.4" @@ -4808,17 +4785,6 @@ dependencies = [ "tendril 0.4.3", ] -[[package]] -name = "markup5ever" -version = "0.38.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8983d30f2915feeaaab2d6babdd6bc7e9ed1a00b66b5e6d74df19aa9c0e91862" -dependencies = [ - "log", - "tendril 0.5.0", - "web_atoms", -] - [[package]] name = "markup5ever" version = "0.39.0" @@ -5100,6 +5066,12 @@ dependencies = [ "thiserror 1.0.69", ] +[[package]] +name = "ndk-context" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "27b02d87554356db9e9a873add8782d4ea6e3e58ea071a9adb9a2e8ddb884a8b" + [[package]] name = "ndk-sys" version = "0.6.0+11769913" @@ -5706,14 +5678,15 @@ checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" [[package]] name = "openssl" -version = "0.10.80" +version = "0.10.78" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a45fa2aa886c42762255da344f0a0d313e254066c46aad76f300c3d3da62d967" +checksum = "f38c4372413cdaaf3cc79dd92d29d7d9f5ab09b51b10dded508fb90bb70b9222" dependencies = [ "bitflags 2.13.0", "cfg-if", "foreign-types 0.3.2", "libc", + "once_cell", "openssl-macros", "openssl-sys", ] @@ -5752,9 +5725,9 @@ dependencies = [ [[package]] name = "openssl-sys" -version = "0.9.117" +version = "0.9.114" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b47e7e6bb2c38cd930d25a23b40fa52e068c10e85f3e03a7f5ba5aaca5713695" +checksum = "13ce1245cd07fcc4cfdb438f7507b0c7e4f3849a69fd84d52374c66d83741bb6" dependencies = [ "cc", "libc", @@ -7265,9 +7238,9 @@ dependencies = [ [[package]] name = "referencing" -version = "0.47.0" +version = "0.49.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "348e860aeb0b7bd035778fd11dd9cd5290d32e4aed3b8f2274a00287a9fd362b" +checksum = "7af3eb523cce0df0af3c30d624b829b2dabd233172b5bc2615fcd03ceae8f746" dependencies = [ "ahash 0.8.11", "fluent-uri", @@ -7876,9 +7849,9 @@ dependencies = [ [[package]] name = "schemars" -version = "1.2.1" +version = "1.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2b42f36aa1cd011945615b92222f6bf73c599a102a300334cd7f8dbeec726cc" +checksum = "687274d293b6cdc6e73e0fee520bf2049650090d7164f87672d212a3c530cf4a" dependencies = [ "dyn-clone", "ref-cast", @@ -8014,25 +7987,6 @@ dependencies = [ "smallvec", ] -[[package]] -name = "selectors" -version = "0.36.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5d9c0c92a92d33f08817311cf3f2c29a3538a8240e94a6a3c622ce652d7e00c" -dependencies = [ - "bitflags 2.13.0", - "cssparser 0.36.0", - "derive_more 2.0.1", - "log", - "new_debug_unreachable", - "phf 0.13.1", - "phf_codegen 0.13.1", - "precomputed-hash", - "rustc-hash", - "servo_arc 0.4.3", - "smallvec", -] - [[package]] name = "selectors" version = "0.38.0" @@ -8240,7 +8194,7 @@ dependencies = [ "indexmap 1.9.3", "indexmap 2.11.4", "schemars 0.9.0", - "schemars 1.2.1", + "schemars 1.2.2", "serde_core", "serde_json", "serde_with_macros", @@ -8852,7 +8806,16 @@ version = "0.27.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "af23d6f6c1a224baef9d3f61e287d2761385a5b88fdab4eb4c6f11aeb54c4bcf" dependencies = [ - "strum_macros", + "strum_macros 0.27.2", +] + +[[package]] +name = "strum" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9628de9b8791db39ceda2b119bbe13134770b56c138ec1d3af810d045c04f9bd" +dependencies = [ + "strum_macros 0.28.0", ] [[package]] @@ -8867,6 +8830,18 @@ dependencies = [ "syn 2.0.117", ] +[[package]] +name = "strum_macros" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab85eea0270ee17587ed4156089e10b9e6880ee688791d45a905f5b1ca36f664" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn 2.0.117", +] + [[package]] name = "sublime_fuzzy" version = "0.7.0" @@ -9048,7 +9023,7 @@ dependencies = [ [[package]] name = "tao" -version = "0.35.3" +version = "0.36.0" dependencies = [ "async-channel", "bitflags 2.13.0", @@ -9067,6 +9042,7 @@ dependencies = [ "libc", "log", "ndk", + "ndk-context", "ndk-sys", "objc2 0.6.4", "objc2-app-kit", @@ -9076,7 +9052,7 @@ dependencies = [ "parking_lot", "percent-encoding", "raw-window-handle", - "tao-macros 0.1.3", + "tao-macros 0.1.4", "unicode-segmentation", "url", "windows 0.61.1", @@ -9087,7 +9063,7 @@ dependencies = [ [[package]] name = "tao-macros" -version = "0.1.3" +version = "0.1.4" dependencies = [ "proc-macro2", "quote", @@ -9096,9 +9072,9 @@ dependencies = [ [[package]] name = "tao-macros" -version = "0.1.3" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4e16beb8b2ac17db28eab8bca40e62dbfbb34c0fcdc6d9826b11b7b5d047dfd" +checksum = "5f7eeb6d99155545da6150a1795945f16ac9c178deb2a5f2e74d776107bd5849" dependencies = [ "proc-macro2", "quote", @@ -9217,7 +9193,7 @@ dependencies = [ "anyhow", "ar", "bitness", - "bytesize 2.4.2", + "bytesize 2.7.0", "dirs 6.0.0", "dunce", "flate2", @@ -9577,7 +9553,7 @@ dependencies = [ "brotli", "cargo_metadata", "ctor", - "dom_query 0.27.0", + "dom_query", "dunce", "getrandom 0.3.4", "glob", @@ -10731,9 +10707,9 @@ dependencies = [ [[package]] name = "wayland-backend" -version = "0.3.15" +version = "0.3.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2857dd20b54e916ec7253b3d6b4d5c4d7d4ca2c33c2e11c6c76a99bd8744755d" +checksum = "016ccf01d1c58b6f8999612813e17c9b2390f7d70671428869913310f83f54b8" dependencies = [ "cc", "downcast-rs", @@ -10745,9 +10721,9 @@ dependencies = [ [[package]] name = "wayland-client" -version = "0.31.14" +version = "0.31.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "645c7c96bb74690c3189b5c9cb4ca1627062bb23693a4fad9d8c3de958260144" +checksum = "e3c36a0f861ad76d0901f2800b46321410d9f73f2ea88aac0650d86c32688073" dependencies = [ "bitflags 2.13.0", "rustix 1.0.7", @@ -10970,7 +10946,7 @@ version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" dependencies = [ - "windows-sys 0.48.0", + "windows-sys 0.59.0", ] [[package]] @@ -11515,7 +11491,7 @@ dependencies = [ "serde-wasm-bindgen", "serde_json", "serde_urlencoded", - "strum", + "strum 0.27.2", "tokio", "url", "wasm-bindgen", @@ -11535,7 +11511,7 @@ dependencies = [ "async-trait", "proc-macro2", "quote", - "strum", + "strum 0.27.2", "syn 2.0.117", "wasm-bindgen", "wasm-bindgen-macro-support", @@ -11562,14 +11538,14 @@ checksum = "1ffae5123b2d3fc086436f8834ae3ab053a283cfac8fe0a0b8eaae044768a4c4" [[package]] name = "wry" -version = "0.55.1" +version = "0.56.0" dependencies = [ "base64 0.22.1", "block2 0.6.2", "cookie", "crossbeam-channel", "dirs 6.0.0", - "dom_query 0.28.0", + "dom_query", "dpi", "dunce", "gtk4", @@ -11589,7 +11565,7 @@ dependencies = [ "sha2 0.10.8", "soup3", "tao", - "tao-macros 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", + "tao-macros 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", "thiserror 2.0.12", "tracing", "url", diff --git a/bench/src/run_benchmark.rs b/bench/src/run_benchmark.rs index aea0aa518710..0f7275f2dd1c 100644 --- a/bench/src/run_benchmark.rs +++ b/bench/src/run_benchmark.rs @@ -134,24 +134,34 @@ fn run_max_mem_benchmark(target: &str) -> Result> { Ok(results) } -fn rlib_size(target_dir: &Path, prefix: &str) -> Result { +fn rlib_size(target_dir: &std::path::Path, library: &str) -> Result { + let prefix = format!("lib{library}"); + let mut size = 0; let mut seen = HashSet::new(); - let deps_dir = target_dir.join("deps"); - for entry in std::fs::read_dir(&deps_dir).with_context(|| { + let build_dir = target_dir.join("build").join(library); + for entry in std::fs::read_dir(&build_dir).with_context(|| { format!( - "failed to read target deps directory: {}", - deps_dir.display() + "failed to read target build directory: {}", + build_dir.display() ) })? { let entry = entry.context("failed to read directory entry")?; - let name = entry.file_name().to_string_lossy().to_string(); - - if name.starts_with(prefix) && name.ends_with(".rlib") { - if let Some(start) = name.split('-').next() { + let out_path = entry.path().join("out"); + + for file in std::fs::read_dir(&out_path).with_context(|| { + format!( + "failed to read target build output directory: {}", + out_path.display() + ) + })? { + let file = file.context("failed to read build output directory entry")?; + let name = file.file_name().to_string_lossy().to_string(); + if name.starts_with(&prefix) && name.ends_with(".rlib") { + let start = name.split('-').next().unwrap(); if seen.insert(start.to_string()) { - size += entry + size += file .metadata() .context("failed to read file metadata")? .len(); @@ -163,7 +173,7 @@ fn rlib_size(target_dir: &Path, prefix: &str) -> Result { if size == 0 { anyhow::bail!( "no rlib files found for prefix {prefix} in {}", - deps_dir.display() + build_dir.display() ); } @@ -173,7 +183,7 @@ fn rlib_size(target_dir: &Path, prefix: &str) -> Result { fn get_binary_sizes(target_dir: &Path, target: &str) -> Result> { let mut sizes = HashMap::::new(); - let wry_size = rlib_size(target_dir, "libwry")?; + let wry_size = rlib_size(target_dir, "wry")?; sizes.insert("wry_rlib".to_string(), wry_size); for (name, example_exe) in get_all_benchmarks(target) { diff --git a/crates/tauri-build/src/codegen/context.rs b/crates/tauri-build/src/codegen/context.rs index d130899cd017..c44105a8d0d5 100644 --- a/crates/tauri-build/src/codegen/context.rs +++ b/crates/tauri-build/src/codegen/context.rs @@ -17,7 +17,7 @@ use tauri_utils::config::FrontendDist; #[cfg_attr(docsrs, doc(cfg(feature = "codegen")))] #[derive(Debug)] pub struct CodegenContext { - config_path: PathBuf, + pub(crate) config_path: Option, out_file: PathBuf, capabilities: Option>, } @@ -25,7 +25,7 @@ pub struct CodegenContext { impl Default for CodegenContext { fn default() -> Self { Self { - config_path: PathBuf::from("tauri.conf.json"), + config_path: None, out_file: PathBuf::from("tauri-build-context.rs"), capabilities: None, } @@ -38,14 +38,15 @@ impl CodegenContext { Self::default() } - /// Set the path to the `tauri.conf.json` (relative to the package's directory). + /// Set the path to the `tauri.conf.json` (relative to the crate's directory). /// /// This defaults to a file called `tauri.conf.json` inside of the current working directory of - /// the package compiling; does not need to be set manually if that config file is in the same + /// the crate compiling; does not need to be set manually if that config file is in the same /// directory as your `Cargo.toml`. #[must_use] + #[deprecated(since = "2.12.0", note = "Use `Attributes::config_path()` instead")] pub fn config_path(mut self, config_path: impl Into) -> Self { - self.config_path = config_path.into(); + self.config_path.replace(config_path.into()); self } @@ -81,7 +82,11 @@ impl CodegenContext { /// Unless you are doing something special with this builder, you don't need to do anything with /// the returned output path. pub(crate) fn try_build(self) -> Result { - let (config, config_parent) = tauri_codegen::get_config(&self.config_path)?; + let (config, config_parent) = tauri_codegen::get_config( + &self + .config_path + .unwrap_or_else(|| PathBuf::from("tauri.conf.json")), + )?; // rerun if changed match &config.build.frontend_dist { diff --git a/crates/tauri-build/src/lib.rs b/crates/tauri-build/src/lib.rs index b59a31fc118d..9c3a8d29172c 100644 --- a/crates/tauri-build/src/lib.rs +++ b/crates/tauri-build/src/lib.rs @@ -91,8 +91,6 @@ fn copy_resources(resources: ResourcePaths<'_>, path: &Path) -> Result<()> { for resource in resources.by_ref() { let resource = resource?; - println!("cargo:rerun-if-changed={}", resource.path().display()); - // avoid copying the resource if target is the same as source let src = resource.path().canonicalize()?; let target = path.join(resource.target()); @@ -101,10 +99,8 @@ fn copy_resources(resources: ResourcePaths<'_>, path: &Path) -> Result<()> { } } - // Watch each resource directory so adding or removing files inside it - // re-runs the build script. Individual file watches miss newly added files. - for dir in resources.rerun_if_changed() { - println!("cargo:rerun-if-changed={}", dir.display()); + for path in resources.rerun_if_changed() { + println!("cargo:rerun-if-changed={}", path.display()); } Ok(()) @@ -375,6 +371,7 @@ pub struct Attributes { #[allow(dead_code)] windows_attributes: WindowsAttributes, capabilities_path_pattern: Option<&'static str>, + config_path: Option, #[cfg(feature = "codegen")] codegen: Option, inlined_plugins: HashMap<&'static str, InlinedPlugin>, @@ -426,6 +423,16 @@ impl Attributes { self } + /// Set the path to the `tauri.conf.json` (relative to the crate's directory). + /// + /// This defaults to a file called `tauri.conf.json` inside of the current working directory of + /// the crate compiling; does not need to be set manually if that config file is in the same + /// directory as your `Cargo.toml`. + pub fn config_path(mut self, config_path: impl Into) -> Self { + self.config_path = Some(config_path.into()); + self + } + /// Sets the application manifest for the Access Control List. /// /// See [`AppManifest`] for more information. @@ -500,8 +507,19 @@ pub fn try_build(attributes: Attributes) -> Result<()> { let target_triple = env::var("TARGET").unwrap(); let target = tauri_utils::platform::Target::from_triple(&target_triple); - let (mut config, config_paths) = - tauri_utils::config::parse::read_from(target, &env::current_dir().unwrap())?; + let config_root = if let Some(config_path) = &attributes.config_path { + config_path.parent().with_context(|| { + format!( + "`config_path` '{}' doesn't have a parent directory", + config_path.display() + ) + })? + } else { + &env::current_dir().unwrap() + }; + + let (mut config, config_paths) = tauri_utils::config::parse::read_from(target, config_root)?; + for config_file_path in config_paths { println!("cargo:rerun-if-changed={}", config_file_path.display()); } @@ -573,7 +591,6 @@ pub fn try_build(attributes: Attributes) -> Result<()> { )?; } - #[allow(unused_mut, clippy::redundant_clone)] let mut resources = config .bundle .resources @@ -735,7 +752,10 @@ pub fn try_build(attributes: Attributes) -> Result<()> { } #[cfg(feature = "codegen")] - if let Some(codegen) = attributes.codegen { + if let Some(mut codegen) = attributes.codegen { + if codegen.config_path.is_none() { + codegen.config_path = attributes.config_path; + } codegen.try_build()?; } diff --git a/crates/tauri-bundler/src/bundle.rs b/crates/tauri-bundler/src/bundle.rs index 60685a421ef5..7a47935b4a07 100644 --- a/crates/tauri-bundler/src/bundle.rs +++ b/crates/tauri-bundler/src/bundle.rs @@ -147,8 +147,15 @@ pub fn bundle_project(settings: &Settings) -> crate::Result> { continue; } - if let Err(e) = patch_binary(&main_binary_path, package_type) { - log::warn!("Failed to add bundler type to the binary: {e}. Updater plugin may not be able to update this package. This shouldn't normally happen, please report it to https://github.com/tauri-apps/tauri/issues"); + if settings.binary_patching() { + if let Err(e) = patch_binary(&main_binary_path, package_type) { + log::warn!("Failed to add bundler type to the binary: {e}. Updater plugin may not be able to update this package. This shouldn't normally happen, please report it to https://github.com/tauri-apps/tauri/issues"); + } + } else { + log::warn!( + "Skipping binary patching for {} due to --no-binary-patching flag.", + main_binary_path.display() + ); } // sign main binary for every package type after patch diff --git a/crates/tauri-bundler/src/bundle/linux/appimage/linuxdeploy-plugin-gtk.sh b/crates/tauri-bundler/src/bundle/linux/appimage/linuxdeploy-plugin-gtk.sh index eec65b5932f6..6ca00ac22f1c 100644 --- a/crates/tauri-bundler/src/bundle/linux/appimage/linuxdeploy-plugin-gtk.sh +++ b/crates/tauri-bundler/src/bundle/linux/appimage/linuxdeploy-plugin-gtk.sh @@ -184,7 +184,7 @@ APPIMAGE_GTK_THEME="${APPIMAGE_GTK_THEME:-"Adwaita:$GTK_THEME_VARIANT"}" # Allow export APPDIR="${APPDIR:-"$(dirname "$(realpath "$0")")"}" # Workaround to run extracted AppImage export GTK_DATA_PREFIX="$APPDIR" export GTK_THEME="$APPIMAGE_GTK_THEME" # Custom themes are broken -export GDK_BACKEND=x11 # Crash with Wayland backend on Wayland - We tested it without it and ended up with this: https://github.com/tauri-apps/tauri/issues/8541 +export GDK_BACKEND="${GDK_BACKEND:-x11}" # Crash with Wayland backend on Wayland - We tested it without it and ended up with this: https://github.com/tauri-apps/tauri/issues/8541 export XDG_DATA_DIRS="$APPDIR/usr/share:/usr/share:$XDG_DATA_DIRS" # g_get_system_data_dirs() from GLib EOF diff --git a/crates/tauri-bundler/src/bundle/linux/appimage/linuxdeploy.rs b/crates/tauri-bundler/src/bundle/linux/appimage/linuxdeploy.rs index a5e0f59a0653..1b1a2a5076c5 100644 --- a/crates/tauri-bundler/src/bundle/linux/appimage/linuxdeploy.rs +++ b/crates/tauri-bundler/src/bundle/linux/appimage/linuxdeploy.rs @@ -6,7 +6,7 @@ use super::{super::debian, write_and_make_executable}; use crate::{ bundle::settings::Arch, - error::{Context, ErrorExt}, + error::Context, utils::{fs_utils, http_utils::download, CommandExt}, Settings, }; @@ -118,21 +118,6 @@ pub fn bundle_project(settings: &Settings) -> crate::Result> { fs::create_dir_all(&app_dir_usr_bin)?; fs::create_dir_all(app_dir_usr_lib)?; - // Copy bins and libs that linuxdeploy doesn't know about - - // we also check if the user may have provided their own copy already - // xdg-open will be handled by the `files` config instead - if settings.deep_link_protocols().is_some() && !app_dir_usr_bin.join("xdg-open").exists() { - fs::copy("/usr/bin/xdg-mime", app_dir_usr_bin.join("xdg-mime")) - .fs_context("xdg-mime binary not found", "/usr/bin/xdg-mime".to_string())?; - } - - // we also check if the user may have provided their own copy already - if settings.appimage().bundle_xdg_open && !app_dir_usr_bin.join("xdg-open").exists() { - fs::copy("/usr/bin/xdg-open", app_dir_usr_bin.join("xdg-open")) - .fs_context("xdg-open binary not found", "/usr/bin/xdg-open".to_string())?; - } - let search_dirs = [ match settings.binary_arch() { Arch::X86_64 => "/usr/lib/x86_64-linux-gnu/", diff --git a/crates/tauri-bundler/src/bundle/settings.rs b/crates/tauri-bundler/src/bundle/settings.rs index 62e6557863a1..113abb3ac97e 100644 --- a/crates/tauri-bundler/src/bundle/settings.rs +++ b/crates/tauri-bundler/src/bundle/settings.rs @@ -223,6 +223,10 @@ pub struct AppImageSettings { /// Whether to include gstreamer plugins for audio/media support. pub bundle_media_framework: bool, /// Whether to include the `xdg-open` binary. + #[deprecated( + since = "2.12.0", + note = "Bundling xdg-open in an AppImage does not work and therefore was disabled." + )] pub bundle_xdg_open: bool, } @@ -830,10 +834,11 @@ pub struct Settings { target: String, /// Whether to disable code signing during the bundling process. no_sign: bool, + /// Whether to patch the main binary with bundle type information. + binary_patching: bool, } /// A builder for [`Settings`]. -#[derive(Default)] pub struct SettingsBuilder { log_level: Option, project_out_directory: Option, @@ -844,12 +849,31 @@ pub struct SettingsBuilder { target: Option, local_tools_directory: Option, no_sign: bool, + binary_patching: bool, +} + +impl Default for SettingsBuilder { + fn default() -> Self { + Self { + log_level: None, + project_out_directory: None, + package_types: None, + package_settings: None, + bundle_settings: BundleSettings::default(), + binaries: Vec::new(), + target: None, + local_tools_directory: None, + no_sign: false, + // Binary patching is on by default; disabled via `--no-binary-patching`. + binary_patching: true, + } + } } impl SettingsBuilder { /// Creates the default settings builder. pub fn new() -> Self { - Default::default() + Self::default() } /// Sets the project output directory. It's used as current working directory. @@ -920,6 +944,13 @@ impl SettingsBuilder { self } + /// Sets whether to patch the main binary with bundle type information. Defaults to `true`. + #[must_use] + pub fn binary_patching(mut self, binary_patching: bool) -> Self { + self.binary_patching = binary_patching; + self + } + /// Builds a Settings from the CLI args. /// /// Package settings will be read from Cargo.toml. @@ -964,6 +995,7 @@ impl SettingsBuilder { target_platform, target, no_sign: self.no_sign, + binary_patching: self.binary_patching, }) } } @@ -1324,4 +1356,14 @@ impl Settings { pub fn set_no_sign(&mut self, no_sign: bool) { self.no_sign = no_sign; } + + /// Whether the main binary is patched with bundle type information. + pub fn binary_patching(&self) -> bool { + self.binary_patching + } + + /// Set whether to patch the main binary with bundle type information. + pub fn set_binary_patching(&mut self, binary_patching: bool) { + self.binary_patching = binary_patching; + } } diff --git a/crates/tauri-cli/Cargo.toml b/crates/tauri-cli/Cargo.toml index d1caa9581bf0..c29140d4b643 100644 --- a/crates/tauri-cli/Cargo.toml +++ b/crates/tauri-cli/Cargo.toml @@ -67,7 +67,7 @@ tauri-utils = { version = "2.9.3", path = "../tauri-utils", features = [ "html-manipulation-2", ] } toml = "1" -jsonschema = { version = "0.47", default-features = false } +jsonschema = { version = "0.49", default-features = false } handlebars = "6" include_dir = "0.7" dirs = "6" diff --git a/crates/tauri-cli/ENVIRONMENT_VARIABLES.md b/crates/tauri-cli/ENVIRONMENT_VARIABLES.md index f6f400ff7678..7be779de9710 100644 --- a/crates/tauri-cli/ENVIRONMENT_VARIABLES.md +++ b/crates/tauri-cli/ENVIRONMENT_VARIABLES.md @@ -19,7 +19,8 @@ These environment variables are inputs to the CLI which may have an equivalent C - `TAURI_BUNDLER_TOOLS_GITHUB_MIRROR_TEMPLATE` - Specify a GitHub mirror template to download files and tools used by tauri bundler, for example: `https://mirror.example.com///releases/download//`. - `TAURI_BUNDLER_DMG_IGNORE_CI` - Disable the check for `CI: true` in the `.dmg` bundler. - `TAURI_SKIP_SIDECAR_SIGNATURE_CHECK` - Skip signing sidecars. -- `TAURI_SIGNING_PRIVATE_KEY` — Private key used to sign your app bundles, can be either a string or a path to the file. +- `TAURI_SIGNING_PRIVATE_KEY` — Private key used to sign your app bundles. For the `build` and `bundle` command, can be either a string or a path to the file. For the `signer sign` command, this must be the literal key string. +- `TAURI_SIGNING_PRIVATE_KEY_PATH` — Path to the private key file for the `signer sign` command. Mutually exclusive with `TAURI_SIGNING_PRIVATE_KEY` when using the signer command. - `TAURI_SIGNING_PRIVATE_KEY_PASSWORD` — The signing private key password, see `TAURI_SIGNING_PRIVATE_KEY`. - `TAURI_SIGNING_RPM_KEY` — The private GPG key used to sign the RPM bundle, exported to its ASCII-armored format. - `TAURI_SIGNING_RPM_KEY_PASSPHRASE` — The GPG key passphrase for `TAURI_SIGNING_RPM_KEY`, if needed. diff --git a/crates/tauri-cli/config.schema.json b/crates/tauri-cli/config.schema.json index 74560b92b988..f2f53d910255 100644 --- a/crates/tauri-cli/config.schema.json +++ b/crates/tauri-cli/config.schema.json @@ -483,7 +483,7 @@ ] }, "additionalBrowserArgs": { - "description": "Defines additional browser arguments on Windows. By default wry passes `--disable-features=msWebOOUI,msPdfOOUI,msSmartScreenProtection`\n so if you use this method, you also need to disable these components by yourself if you want.", + "description": "Defines additional browser arguments on Windows.\n\n ## Warning\n\n Webview instances with different browser arguments must also have different [data directories](Self::data_directory).\n\n By default wry passes `--disable-features=msWebOOUI,msPdfOOUI,msSmartScreenProtection`\n so if you set this, you also need to disable these components by yourself if you want.", "type": [ "string", "null" diff --git a/crates/tauri-cli/src/acl/permission/rm.rs b/crates/tauri-cli/src/acl/permission/rm.rs index 52f770f859d0..20643038bdbe 100644 --- a/crates/tauri-cli/src/acl/permission/rm.rs +++ b/crates/tauri-cli/src/acl/permission/rm.rs @@ -108,65 +108,65 @@ fn rm_permission_from_capabilities(identifier: &str, dir: &Path) -> Result<()> { let file_type = entry .file_type() .fs_context("failed to get capability file type", entry.path())?; - if file_type.is_file() { - let path = entry.path(); - match path.extension().and_then(|o| o.to_str()) { - Some("toml") => { - let content = std::fs::read_to_string(&path) - .fs_context("failed to read capability file", path.clone())?; - if let Ok(mut value) = content.parse::() { - if let Some(permissions) = value.get_mut("permissions").and_then(|p| p.as_array_mut()) { - let prev_len = permissions.len(); - permissions.retain(|p| match p { - toml_edit::Value::String(s) => !identifier_match(identifier, s.value()), - toml_edit::Value::InlineTable(o) => { - if let Some(toml_edit::Value::String(permission_name)) = o.get("identifier") { - return !identifier_match(identifier, permission_name.value()); - } - - true + if !file_type.is_file() { + continue; + } + let path = entry.path(); + match path.extension().and_then(|o| o.to_str()) { + Some("toml") => { + let content = std::fs::read_to_string(&path) + .fs_context("failed to read capability file", path.clone())?; + if let Ok(mut value) = content.parse::() { + if let Some(permissions) = value.get_mut("permissions").and_then(|p| p.as_array_mut()) { + let prev_len = permissions.len(); + permissions.retain(|p| match p { + toml_edit::Value::String(s) => !identifier_match(identifier, s.value()), + toml_edit::Value::InlineTable(o) => { + if let Some(toml_edit::Value::String(permission_name)) = o.get("identifier") { + return !identifier_match(identifier, permission_name.value()); } - _ => false, - }); - if prev_len != permissions.len() { - std::fs::write(&path, value.to_string()) - .fs_context("failed to write capability file", path.clone())?; - log::info!(action = "Removed"; "permission from capability at {}", dunce::simplified(&path).display()); + + true } + _ => false, + }); + if prev_len != permissions.len() { + std::fs::write(&path, value.to_string()) + .fs_context("failed to write capability file", path.clone())?; + log::info!(action = "Removed"; "permission from capability at {}", dunce::simplified(&path).display()); } } } - Some("json") => { - let content = - std::fs::read(&path).fs_context("failed to read capability file", path.clone())?; - if let Ok(mut value) = serde_json::from_slice::(&content) { - if let Some(permissions) = value.get_mut("permissions").and_then(|p| p.as_array_mut()) { - let prev_len = permissions.len(); - permissions.retain(|p| match p { - serde_json::Value::String(s) => !identifier_match(identifier, s), - serde_json::Value::Object(o) => { - if let Some(serde_json::Value::String(permission_name)) = o.get("identifier") { - return !identifier_match(identifier, permission_name); - } - - true + } + Some("json") => { + let content = + std::fs::read(&path).fs_context("failed to read capability file", path.clone())?; + if let Ok(mut value) = serde_json::from_slice::(&content) { + if let Some(permissions) = value.get_mut("permissions").and_then(|p| p.as_array_mut()) { + let prev_len = permissions.len(); + permissions.retain(|p| match p { + serde_json::Value::String(s) => !identifier_match(identifier, s), + serde_json::Value::Object(o) => { + if let Some(serde_json::Value::String(permission_name)) = o.get("identifier") { + return !identifier_match(identifier, permission_name); } - _ => false, - }); - if prev_len != permissions.len() { - std::fs::write( - &path, - serde_json::to_vec_pretty(&value) - .context("failed to serialize capability JSON")?, - ) - .fs_context("failed to write capability file", path.clone())?; - log::info!(action = "Removed"; "permission from capability at {}", dunce::simplified(&path).display()); + + true } + _ => false, + }); + if prev_len != permissions.len() { + std::fs::write( + &path, + serde_json::to_vec_pretty(&value).context("failed to serialize capability JSON")?, + ) + .fs_context("failed to write capability file", path.clone())?; + log::info!(action = "Removed"; "permission from capability at {}", dunce::simplified(&path).display()); } } } - _ => {} } + _ => {} } } diff --git a/crates/tauri-cli/src/build.rs b/crates/tauri-cli/src/build.rs index 2819bc3fcd6d..557433e5c744 100644 --- a/crates/tauri-cli/src/build.rs +++ b/crates/tauri-cli/src/build.rs @@ -79,6 +79,14 @@ pub struct Options { /// Skip code signing when bundling the app #[clap(long)] pub no_sign: bool, + /// Skip patching the main executable with bundle type information. + /// + /// The patching rewrites the binary in place, invalidating an existing code + /// signature. Skipping it preserves an already-signed binary at the cost of + /// per-bundle-type updater support (only relevant when shipping multiple + /// bundle types per platform). + #[clap(long)] + pub no_binary_patching: bool, } pub fn command(mut options: Options, verbosity: u8) -> Result<()> { diff --git a/crates/tauri-cli/src/bundle.rs b/crates/tauri-cli/src/bundle.rs index 6e23539a2f1f..a822a531c51a 100644 --- a/crates/tauri-cli/src/bundle.rs +++ b/crates/tauri-cli/src/bundle.rs @@ -100,6 +100,15 @@ pub struct Options { /// are not available or not needed. #[clap(long)] pub no_sign: bool, + + /// Skip patching the main executable with bundle type information. + /// + /// The patching rewrites the binary in place, invalidating an existing code + /// signature. Skipping it preserves an already-signed binary at the cost of + /// per-bundle-type updater support (only relevant when shipping multiple + /// bundle types per platform). + #[clap(long)] + pub no_binary_patching: bool, } impl From for Options { @@ -113,6 +122,7 @@ impl From for Options { config: value.config, skip_stapling: value.skip_stapling, no_sign: value.no_sign, + no_binary_patching: value.no_binary_patching, } } } @@ -209,6 +219,7 @@ pub fn bundle( ) .context("failed to build bundler settings")?; settings.set_no_sign(options.no_sign); + settings.set_binary_patching(!options.no_binary_patching); settings.set_log_level(match verbosity { 0 => log::Level::Error, diff --git a/crates/tauri-cli/src/icon.rs b/crates/tauri-cli/src/icon.rs index 515b68e08a15..3407f343ebb5 100644 --- a/crates/tauri-cli/src/icon.rs +++ b/crates/tauri-cli/src/icon.rs @@ -16,7 +16,7 @@ use std::{ sync::Arc, }; -use clap::Parser; +use clap::{Parser, ValueEnum}; use icns::{IconFamily, IconType}; use image::{ codecs::{ @@ -99,48 +99,38 @@ pub struct Options { /// The background color of the iOS icon - string as defined in the W3C's CSS Color Module Level 4 . #[clap(long, default_value = "#fff")] ios_color: String, + + /// How to fit a non-square source image into the square icon canvas, similar to the CSS `object-fit` property. + /// + /// When not set, a non-square source is rejected. + #[clap(long, value_enum)] + fit: Option, +} + +/// How to fit a non-square source image into the square icon canvas. +#[derive(Debug, Clone, Copy, PartialEq, Eq, ValueEnum)] +enum Fit { + /// Scale the source to fill the square, center-cropping the longer side (may clip content). + Cover, + /// Scale the source to fit inside the square, padding the shorter side with transparency. + Contain, } #[derive(Clone)] #[allow(clippy::large_enum_variant)] enum Source { - Svg(resvg::usvg::Tree), + Svg { + tree: resvg::usvg::Tree, + /// Records how a non-square source should be squared + fit: Option, + }, DynamicImage(DynamicImage), } impl Source { - fn width(&self) -> u32 { - match self { - Self::Svg(svg) => svg.size().width() as u32, - Self::DynamicImage(i) => i.width(), - } - } - - fn height(&self) -> u32 { - match self { - Self::Svg(svg) => svg.size().height() as u32, - Self::DynamicImage(i) => i.height(), - } - } - fn resize_exact(&self, size: u32) -> DynamicImage { match self { - Self::Svg(svg) => { - let mut pixmap = tiny_skia::Pixmap::new(size, size).unwrap(); - let scale = size as f32 / svg.size().height(); - resvg::render( - svg, - tiny_skia::Transform::from_scale(scale, scale), - &mut pixmap.as_mut(), - ); - // Switch to use `Pixmap::take_demultiplied` in the future when it's published - // https://github.com/linebender/tiny-skia/blob/624257c0feb394bf6c4d0d688f8ea8030aae320f/src/pixmap.rs#L266 - let img_buffer = ImageBuffer::from_par_fn(size, size, |x, y| { - let pixel = pixmap.pixel(x, y).unwrap().demultiply(); - Rgba([pixel.red(), pixel.green(), pixel.blue(), pixel.alpha()]) - }); - DynamicImage::ImageRgba8(img_buffer) - } + Self::Svg { tree, fit } => rasterize_svg(tree, *fit, size), Self::DynamicImage(image) => { // image.resize_exact(size, size, FilterType::Lanczos3) resize_image(image, size, size) @@ -196,7 +186,10 @@ fn read_source(path: PathBuf) -> Result { usvg::Tree::from_data(&svg_data, &opt).unwrap() }; - Ok(Source::Svg(rtree)) + Ok(Source::Svg { + tree: rtree, + fit: None, + }) } else { Ok(Source::DynamicImage(DynamicImage::ImageRgba8( open(&path) @@ -232,6 +225,84 @@ fn parse_bg_color(bg_color_string: &String) -> Result> { Ok(bg_color) } +// Rasterize an SVG tree to a square `size`x`size` `DynamicImage`, applying the +// `fit` mode as a render transform. Because the vector tree is rendered at the +// final resolution, there is no intermediate bitmap and no quality loss — a +// source declaring a tiny intrinsic size (e.g. `width="16"`) is just as crisp +// as a large one. +fn rasterize_svg(tree: &usvg::Tree, fit: Option, size: u32) -> DynamicImage { + let native = tree.size(); + let (nw, nh) = (native.width(), native.height()); + + // `side` is the square edge (in user units) we crop/pad the source to; the + // offsets recenter the content within that square. + let (side, off_x, off_y) = match fit { + // Crop to the shorter side, centered. + Some(Fit::Cover) => { + let side = f32::min(nw, nh); + (side, (nw - side) / 2.0, (nh - side) / 2.0) + } + // Pad the shorter side, centered (offsets are negative → transparent bands). + Some(Fit::Contain) => { + let side = f32::max(nw, nh); + (side, (nw - side) / 2.0, (nh - side) / 2.0) + } + // Already square (or squaring not requested): scale to fit as-is. + None => (nh, 0.0, 0.0), + }; + + let scale = size as f32 / side; + let transform = + tiny_skia::Transform::from_scale(scale, scale).post_translate(-off_x * scale, -off_y * scale); + + let mut pixmap = tiny_skia::Pixmap::new(size, size).unwrap(); + resvg::render(tree, transform, &mut pixmap.as_mut()); + + // Switch to use `Pixmap::take_demultiplied` in the future when it's published + // https://github.com/linebender/tiny-skia/blob/624257c0feb394bf6c4d0d688f8ea8030aae320f/src/pixmap.rs#L266 + let img_buffer = ImageBuffer::from_par_fn(size, size, |x, y| { + let pixel = pixmap.pixel(x, y).unwrap().demultiply(); + Rgba([pixel.red(), pixel.green(), pixel.blue(), pixel.alpha()]) + }); + DynamicImage::ImageRgba8(img_buffer) +} + +// Convert a non-square source into a square one according to the `fit` mode. +// SVG sources stay vector — the fit is recorded and applied at render time; only +// raster sources are cropped/padded here. +fn fit_to_square(source: Source, fit: Fit) -> Source { + let image = match source { + Source::DynamicImage(image) => image, + Source::Svg { tree, .. } => { + return Source::Svg { + tree, + fit: Some(fit), + } + } + }; + + let (width, height) = image.dimensions(); + let squared = match fit { + Fit::Cover => { + let side = u32::min(width, height); + image.crop_imm((width - side) / 2, (height - side) / 2, side, side) + } + Fit::Contain => { + let side = u32::max(width, height); + let mut canvas = ImageBuffer::from_pixel(side, side, Rgba([0, 0, 0, 0])); + image::imageops::overlay( + &mut canvas, + &image, + ((side - width) / 2) as i64, + ((side - height) / 2) as i64, + ); + DynamicImage::ImageRgba8(canvas) + } + }; + + Source::DynamicImage(squared) +} + pub fn command(options: Options) -> Result<()> { let input = options.input; let out_dir = options.output.unwrap_or_else(|| { @@ -263,10 +334,21 @@ pub fn command(options: Options) -> Result<()> { None => input.clone(), }; - let source = read_source(default_icon)?; + let mut source = read_source(default_icon)?; - if source.height() != source.width() { - crate::error::bail!("Source image must be square"); + let (width, height) = match &source { + Source::Svg { tree, .. } => (tree.size().width() as u32, tree.size().height() as u32), + Source::DynamicImage(i) => (i.width(), i.height()), + }; + if width != height { + if let Some(fit) = options.fit { + log::info!(action = "Fit"; "Squaring {width}x{height} source with `{fit:?}`"); + source = fit_to_square(source, fit); + } else { + crate::error::bail!( + "Source image must be square; pass `--fit cover` or `--fit contain` to convert a non-square source" + ) + } } if png_icon_sizes.is_empty() { @@ -982,3 +1064,72 @@ fn apply_round_mask( DynamicImage::ImageRgba8(out) } + +#[cfg(test)] +mod tests { + use super::*; + + fn landscape(width: u32, height: u32) -> Source { + // opaque red rectangle + Source::DynamicImage(DynamicImage::ImageRgba8(ImageBuffer::from_pixel( + width, + height, + Rgba([255, 0, 0, 255]), + ))) + } + + fn svg_landscape() -> usvg::Tree { + // 40x20 opaque red rectangle + let data = br#""#; + usvg::Tree::from_data(data, &usvg::Options::default()).unwrap() + } + + #[test] + fn cover_center_crops_to_square() { + let out = fit_to_square(landscape(40, 20), Fit::Cover); + let Source::DynamicImage(image) = out else { + panic!("expected a raster source"); + }; + assert_eq!((image.width(), image.height()), (20, 20)); + // the cropped region keeps the original content (fully opaque) + assert_eq!(image.get_pixel(10, 10)[3], 255); + } + + #[test] + fn contain_pads_shorter_side_with_transparency() { + let out = fit_to_square(landscape(40, 20), Fit::Contain); + let Source::DynamicImage(image) = out else { + panic!("expected a raster source"); + }; + assert_eq!((image.width(), image.height()), (40, 40)); + // top padding band is transparent, the centered content is opaque + assert_eq!(image.get_pixel(20, 0)[3], 0); + assert_eq!(image.get_pixel(20, 20)[3], 255); + } + + #[test] + fn svg_cover_renders_square_from_vector() { + let source = Source::Svg { + tree: svg_landscape(), + fit: Some(Fit::Cover), + }; + // rendered directly from the vector tree at the requested size + let image = source.resize_exact(64); + assert_eq!((image.width(), image.height()), (64, 64)); + // center of the cropped region stays opaque + assert_eq!(image.get_pixel(32, 32)[3], 255); + } + + #[test] + fn svg_contain_pads_shorter_side_with_transparency() { + let source = Source::Svg { + tree: svg_landscape(), + fit: Some(Fit::Contain), + }; + let image = source.resize_exact(64); + assert_eq!((image.width(), image.height()), (64, 64)); + // top band is transparent padding, the centered content is opaque + assert_eq!(image.get_pixel(32, 2)[3], 0); + assert_eq!(image.get_pixel(32, 32)[3], 255); + } +} diff --git a/crates/tauri-cli/src/init.rs b/crates/tauri-cli/src/init.rs index 268f5fb9ca07..910432301caf 100644 --- a/crates/tauri-cli/src/init.rs +++ b/crates/tauri-cli/src/init.rs @@ -142,7 +142,7 @@ impl Options { .map(|s| Ok(Some(s))) .unwrap_or_else(|| { prompts::input( - "What is your frontend dev command?", + "What command should Tauri run before `tauri dev` to start your frontend? (leave empty if not needed)", Some(default_dev_command(detected_package_manager).into()), self.ci, true, @@ -154,7 +154,7 @@ impl Options { .map(|s| Ok(Some(s))) .unwrap_or_else(|| { prompts::input( - "What is your frontend build command?", + "What command should Tauri run before `tauri build` to build your frontend? (leave empty if not needed)", Some(default_build_command(detected_package_manager).into()), self.ci, true, diff --git a/crates/tauri-cli/src/interface/rust.rs b/crates/tauri-cli/src/interface/rust.rs index 669d839e97fa..367dd7c3bd8d 100644 --- a/crates/tauri-cli/src/interface/rust.rs +++ b/crates/tauri-cli/src/interface/rust.rs @@ -920,26 +920,6 @@ impl AppSettings for RustAppSettings { }); } - if let Some(open) = config.plugins.0.get("shell").and_then(|v| v.get("open")) { - if open.as_bool().is_some_and(|x| x) || open.is_string() { - settings.appimage.bundle_xdg_open = true; - } - } - - if let Some(deps) = self - .manifest - .lock() - .unwrap() - .inner - .as_table() - .get("dependencies") - .and_then(|f| f.as_table()) - { - if deps.contains_key("tauri-plugin-opener") { - settings.appimage.bundle_xdg_open = true; - }; - } - Ok(settings) } diff --git a/crates/tauri-cli/src/mobile/android/build.rs b/crates/tauri-cli/src/mobile/android/build.rs index 7d5f39c8d076..38617443c99a 100644 --- a/crates/tauri-cli/src/mobile/android/build.rs +++ b/crates/tauri-cli/src/mobile/android/build.rs @@ -106,6 +106,7 @@ impl From for BuildOptions { skip_stapling: false, ignore_version_mismatches: options.ignore_version_mismatches, no_sign: false, + no_binary_patching: false, } } } diff --git a/crates/tauri-cli/src/mobile/ios/build.rs b/crates/tauri-cli/src/mobile/ios/build.rs index 2ecbf7b0a265..4ef9f677da4f 100644 --- a/crates/tauri-cli/src/mobile/ios/build.rs +++ b/crates/tauri-cli/src/mobile/ios/build.rs @@ -161,6 +161,7 @@ impl From for BuildOptions { skip_stapling: false, ignore_version_mismatches: options.ignore_version_mismatches, no_sign: options.no_sign, + no_binary_patching: false, } } } diff --git a/crates/tauri-cli/src/signer/generate.rs b/crates/tauri-cli/src/signer/generate.rs index 6ca4244c74be..7cf22d46b75c 100644 --- a/crates/tauri-cli/src/signer/generate.rs +++ b/crates/tauri-cli/src/signer/generate.rs @@ -57,8 +57,8 @@ pub fn command(mut options: Options) -> Result<()> { println!(); println!("Environment variables used to sign:"); - println!("- `TAURI_SIGNING_PRIVATE_KEY`: String of your private key"); - println!("- `TAURI_SIGNING_PRIVATE_KEY_PATH`: Path to your private key file"); + println!("- `TAURI_SIGNING_PRIVATE_KEY`: Your private key. For the `build` and `bundle` command it can be either a string or a path to the file, for the `signer sign` command it must be the literal key string"); + println!("- `TAURI_SIGNING_PRIVATE_KEY_PATH`: Path to your private key file, used by the `signer sign` command"); println!("- `TAURI_SIGNING_PRIVATE_KEY_PASSWORD`: Your private key password (optional if key has no password)"); println!(); println!("ATTENTION: If you lose your private key OR password, you'll not be able to sign your update package and updates will not work"); diff --git a/crates/tauri-runtime-wry/Cargo.toml b/crates/tauri-runtime-wry/Cargo.toml index b4600a86f4cd..e5aeb13a0fe7 100644 --- a/crates/tauri-runtime-wry/Cargo.toml +++ b/crates/tauri-runtime-wry/Cargo.toml @@ -13,12 +13,11 @@ edition.workspace = true rust-version.workspace = true [dependencies] -wry = { version = "0.55.0", default-features = false, features = [ - "protocol", +wry = { version = "0.56.0", default-features = false, features = [ "os-webview", "linux-body", ] } -tao = { version = "0.35.0", default-features = false, features = ["rwh_06"] } +tao = { version = "0.36.0", default-features = false, features = ["rwh_06"] } tauri-runtime = { version = "2.11.3", path = "../tauri-runtime" } tauri-utils = { version = "2.9.3", path = "../tauri-utils" } raw-window-handle = "0.6" @@ -45,7 +44,7 @@ percent-encoding = "2" objc2 = "0.6" [target.'cfg(target_os = "macos")'.dependencies] -objc2-app-kit = { version = "0.3", default-features = false, features = [ +objc2-app-kit = { version = "0.3.2", default-features = false, features = [ "block2", "NSApplication", "NSResponder", @@ -62,11 +61,7 @@ jni = "0.21" default = ["x11", "dbus"] devtools = ["wry/devtools", "tauri-runtime/devtools"] x11 = ["tao/x11", "wry/x11"] -macos-private-api = [ - "wry/fullscreen", - "wry/transparent", - "tauri-runtime/macos-private-api", -] +macos-private-api = ["tauri-runtime/macos-private-api"] # TODO: Remove in v3 - wry does not have this feature anymore objc-exception = [] tracing = ["dep:tracing", "wry/tracing"] diff --git a/crates/tauri-runtime-wry/src/lib.rs b/crates/tauri-runtime-wry/src/lib.rs index 9ad759075f4f..17680937f420 100644 --- a/crates/tauri-runtime-wry/src/lib.rs +++ b/crates/tauri-runtime-wry/src/lib.rs @@ -58,7 +58,10 @@ use tao::platform::unix::{WindowBuilderExtUnix, WindowExtUnix}; #[cfg(windows)] use tao::platform::windows::{WindowBuilderExtWindows, WindowExtWindows}; #[cfg(windows)] -use webview2_com::{ContainsFullScreenElementChangedEventHandler, FocusChangedEventHandler}; +use webview2_com::{ + ContainsFullScreenElementChangedEventHandler, FocusChangedEventHandler, + Microsoft::Web::WebView2::Win32::ICoreWebView2Controller, +}; #[cfg(windows)] use windows::Win32::Foundation::HWND; #[cfg(target_os = "ios")] @@ -162,6 +165,7 @@ mod monitor; mod undecorated_resizing; mod util; mod webview; +mod webview_permissions; mod window; pub use webview::Webview; @@ -500,10 +504,7 @@ impl TryFrom> for TaoIcon { pub struct WindowEventWrapper(pub Option); impl WindowEventWrapper { - fn map_from_tao( - event: &TaoWindowEvent<'_>, - #[allow(unused_variables)] window: &WindowWrapper, - ) -> Self { + fn map_from_tao(event: &TaoWindowEvent<'_>, #[cfg(windows)] window: &WindowWrapper) -> Self { let event = match event { TaoWindowEvent::Resized(size) => WindowEvent::Resized(*size), TaoWindowEvent::Moved(position) => WindowEvent::Moved(*position), @@ -522,35 +523,50 @@ impl WindowEventWrapper { // (without receiving a webview focus, such as when clicking the taskbar app icon or using Alt + Tab) // in this case we must send the focus change event here #[cfg(windows)] - #[allow(clippy::collapsible_match)] if window.has_children.load(Ordering::Relaxed) { - const FOCUSED_WEBVIEW_MARKER: &str = "__tauriWindow?"; - let mut focused_webview = window.focused_webview.lock().unwrap(); - // when we focus a webview and the window was previously focused, we get a blur event here - // so on blur we should only send events if the current focus is owned by the window - if !*focused - && focused_webview - .as_deref() - .is_some_and(|w| w != FOCUSED_WEBVIEW_MARKER) - { + if !*focused { + // Blur events are handled in the webview side (add_LostFocus) return Self(None); } - // reset focused_webview on blur, or set to a dummy value on focus - // (to prevent double focus event when we click a webview after focusing a window) - *focused_webview = if *focused { - Some(FOCUSED_WEBVIEW_MARKER.to_owned()) + let mut focused_webview = window.focused_webview.lock().unwrap(); + if let FocusState::Blured { + last_focused_webview_label, + } = &*focused_webview + { + let should_focus_webview = + last_focused_webview_label + .as_deref() + .and_then(|last_focused_webview_label| { + window + .webviews + .iter() + .find(|w| w.label == last_focused_webview_label) + }); + *focused_webview = FocusState::WindowFocused; + if let Some(should_focus_webview) = should_focus_webview { + drop(focused_webview); + let _ = should_focus_webview.focus(); + } + WindowEvent::Focused(true) } else { - None - }; - + // Already focused + return Self(None); + } + } else if window.webviews.is_empty() { + // Raw tao window without webviews, forward the event WindowEvent::Focused(*focused) } else { - // when not on multiwebview mode, we handle focus change events on the webview (add_GotFocus and add_LostFocus) + // when not on multiwebview mode, wry will set focus to the webview, + // and we will handle focus change events on the webview (add_GotFocus and add_LostFocus) return Self(None); } } TaoWindowEvent::ThemeChanged(theme) => WindowEvent::ThemeChanged(map_theme(theme)), + #[cfg(mobile)] + TaoWindowEvent::Suspended => WindowEvent::Suspended, + #[cfg(mobile)] + TaoWindowEvent::Resumed => WindowEvent::Resumed, _ => return Self(None), }; Self(Some(event)) @@ -572,7 +588,11 @@ impl WindowEventWrapper { Self(None) } } - e => Self::map_from_tao(e, window), + e => Self::map_from_tao( + e, + #[cfg(windows)] + window, + ), } } } @@ -1483,7 +1503,7 @@ pub type CreateWebviewClosure = Box Result + Send>; pub struct CreateWebviewOptions { - pub focused_webview: Arc>>, + pub focused_webview: Arc>, } pub enum Message { @@ -2519,6 +2539,25 @@ impl Drop for WebviewWrapper { } } +#[derive(Debug)] +pub enum FocusState { + WindowFocused, + WebviewFocused { + webview_label: String, + }, + Blured { + last_focused_webview_label: Option, + }, +} + +impl Default for FocusState { + fn default() -> Self { + Self::Blured { + last_focused_webview_label: None, + } + } +} + pub struct WindowWrapper { label: String, inner: Option>, @@ -2533,7 +2572,7 @@ pub struct WindowWrapper { is_window_transparent: bool, #[cfg(windows)] surface: Option, Arc>>, - focused_webview: Arc>>, + focused_webview: Arc>, } impl WindowWrapper { @@ -4317,39 +4356,6 @@ fn handle_event_loop( Event::SceneRequested { scene, options } => { callback(RunEvent::SceneRequested { scene, options }); } - #[cfg(mobile)] - e @ Event::Resumed | e @ Event::Suspended => { - let event = match e { - Event::Resumed => WindowEvent::Resumed, - Event::Suspended => WindowEvent::Suspended, - _ => unreachable!(), - }; - - // Collect the per-window listener handles and release the `windows` - // borrow before dispatching: handlers and the `RunEvent` callback may - // create or close windows (`windows.0.borrow_mut()`), which would panic - // the `RefCell` if we held the borrow across them. The desktop - // `WindowEvent` branches drop the borrow before dispatching for the same - // reason; this mobile `Resumed`/`Suspended` branch was the exception. - let targets = windows - .0 - .borrow() - .values() - .map(|w| (w.label.clone(), w.window_event_listeners.clone())) - .collect::>(); - - for (label, window_event_listeners) in targets { - let listeners = window_event_listeners.lock().unwrap(); - for handler in listeners.values() { - handler(&event); - } - - callback(RunEvent::WindowEvent { - label, - event: event.clone(), - }); - } - } _ => (), } } @@ -4596,7 +4602,12 @@ fn create_window( let mut webviews = Vec::new(); - let focused_webview = Arc::new(Mutex::new(None)); + let focused_webview = Arc::new(Mutex::new(FocusState::default())); + + #[cfg(feature = "unstable")] + let has_children = webview.is_some(); + #[cfg(not(feature = "unstable"))] + let has_children = false; if let Some(webview) = webview { webviews.push(create_webview( @@ -4633,7 +4644,7 @@ fn create_window( Ok(WindowWrapper { label, - has_children: AtomicBool::new(false), + has_children: AtomicBool::new(has_children), inner: Some(window), webviews, window_event_listeners, @@ -4671,7 +4682,7 @@ fn create_webview( id: WebviewId, context: &Context, pending: PendingWebview>, - #[allow(unused_variables)] focused_webview: Arc>>, + #[allow(unused_variables)] focused_webview: Arc>, ) -> Result { if !context.webview_runtime_installed { #[cfg(all(not(debug_assertions), windows))] @@ -4886,6 +4897,14 @@ You may have it installed on another user account, but it is not available for t webview_builder.with_document_title_changed_handler(document_title_changed_handler) } + if let Some(permission_request_handler) = pending.permission_request_handler { + webview_builder = webview_builder.with_permission_handler(move |kind| { + let kind = webview_permissions::from_wry_permission_kind(kind); + let response = permission_request_handler(kind); + webview_permissions::to_wry_permission_response(response) + }); + } + let webview_bounds = if let Some(bounds) = webview_attributes.bounds { let bounds: RectWrapper = bounds.into(); let bounds = bounds.0; @@ -5207,64 +5226,17 @@ You may have it installed on another user account, but it is not available for t #[cfg(windows)] { let controller = webview.controller(); - let proxy_clone = context.proxy.clone(); - let window_id_ = window_id.clone(); let mut token = 0; - unsafe { - let label_ = label.clone(); - let focused_webview_ = focused_webview.clone(); - controller.add_GotFocus( - &FocusChangedEventHandler::create(Box::new(move |_, _| { - let mut focused_webview = focused_webview_.lock().unwrap(); - // when using multiwebview mode, we should check if the focus change is actually a "webview focus change" - // instead of a window focus change (here we're patching window events, so we only care about the actual window changing focus) - let already_focused = focused_webview.is_some(); - focused_webview.replace(label_.clone()); - - if !already_focused { - let _ = proxy_clone.send_event(Message::Webview( - *window_id_.lock().unwrap(), - id, - WebviewMessage::SynthesizedWindowEvent(SynthesizedWindowEvent::Focused(true)), - )); - } - Ok(()) - })), - &mut token, - ) - } - .unwrap(); - unsafe { - let label_ = label.clone(); - let window_id_ = window_id.clone(); - let proxy_clone = context.proxy.clone(); - controller.add_LostFocus( - &FocusChangedEventHandler::create(Box::new(move |_, _| { - let mut focused_webview = focused_webview.lock().unwrap(); - // when using multiwebview mode, we should handle webview focus changes - // so we check is the currently focused webview matches this webview's - // (in this case, it means we lost the window focus) - // - // on multiwebview mode if we change focus to a different webview - // we get the gotFocus event of the other webview before the lostFocus - // so this check makes sense - let lost_window_focus = focused_webview.as_ref().is_none_or(|t| t == &label_); - if lost_window_focus { - // only reset when we lost window focus - otherwise some other webview is focused - *focused_webview = None; - let _ = proxy_clone.send_event(Message::Webview( - *window_id_.lock().unwrap(), - id, - WebviewMessage::SynthesizedWindowEvent(SynthesizedWindowEvent::Focused(false)), - )); - } - Ok(()) - })), - &mut token, - ) - } - .unwrap(); + add_focus_change_listeners( + window_id.clone(), + id, + context.proxy.clone(), + focused_webview, + label.clone(), + &controller, + &mut token, + ); if let Ok(webview) = unsafe { controller.CoreWebView2() } { let proxy_clone = context.proxy.clone(); @@ -5362,3 +5334,84 @@ fn to_tao_theme(theme: Option) -> Option { _ => None, } } + +/// Used to prevent duplicated [`WindowEvent::Focused`] events, +/// and to track last focused webview in multi-webview mode for us to restore webview focuses +#[cfg(windows)] +fn add_focus_change_listeners( + window_id: Arc>, + id: u32, + proxy: TaoEventLoopProxy>, + focused_webview: Arc>, + label: String, + controller: &ICoreWebView2Controller, + token: &mut i64, +) { + let label_ = label.clone(); + let window_id_ = window_id.clone(); + let proxy_clone = proxy.clone(); + let focused_webview_ = focused_webview.clone(); + if let Err(error) = unsafe { + controller.add_GotFocus( + &FocusChangedEventHandler::create(Box::new(move |_, _| { + let mut focused_webview = focused_webview_.lock().unwrap(); + // when using multiwebview mode, we should check if the focus change is actually a "webview focus change" + // instead of a window focus change (here we're patching window events, so we only care about the actual window changing focus) + let already_focused = matches!( + *focused_webview, + FocusState::WindowFocused | FocusState::WebviewFocused { .. } + ); + *focused_webview = FocusState::WebviewFocused { + webview_label: label_.clone(), + }; + + if !already_focused { + let _ = proxy_clone.send_event(Message::Webview( + *window_id_.lock().unwrap(), + id, + WebviewMessage::SynthesizedWindowEvent(SynthesizedWindowEvent::Focused(true)), + )); + } + Ok(()) + })), + token, + ) + } { + log::error!("Failed to attach WebView2 `add_GotFocus` handler, `WindowEvent::Focused` will not be sent: {error}"); + return; + } + + if let Err(error) = unsafe { + controller.add_LostFocus( + &FocusChangedEventHandler::create(Box::new(move |_, _| { + let mut focused_webview = focused_webview.lock().unwrap(); + // when using multiwebview mode, we should handle webview focus changes + // so we check is the currently focused webview matches this webview's + // (in this case, it means we lost the window focus) + // + // on multiwebview mode if we change focus to a different webview + // we get the gotFocus event of the other webview before the lostFocus + // so this check makes sense + if let FocusState::WebviewFocused { ref webview_label } = *focused_webview { + let lost_window_focus = webview_label == &label; + if lost_window_focus { + // only reset when we lost window focus - otherwise some other webview is focused + *focused_webview = FocusState::Blured { + last_focused_webview_label: Some(label.clone()), + }; + let _ = proxy.send_event(Message::Webview( + *window_id.lock().unwrap(), + id, + WebviewMessage::SynthesizedWindowEvent(SynthesizedWindowEvent::Focused(false)), + )); + } + } + + Ok(()) + })), + token, + ) + } { + log::error!("Failed to attach WebView2 `add_LostFocus` handler, `WindowEvent::Focused` will not be sent: {error}"); + } +} diff --git a/crates/tauri-runtime-wry/src/webview_permissions.rs b/crates/tauri-runtime-wry/src/webview_permissions.rs new file mode 100644 index 000000000000..98e1f57b57db --- /dev/null +++ b/crates/tauri-runtime-wry/src/webview_permissions.rs @@ -0,0 +1,35 @@ +// Copyright 2019-2024 Tauri Programme within The Commons Conservancy +// SPDX-License-Identifier: Apache-2.0 +// SPDX-License-Identifier: MIT + +use tauri_runtime::webview::{PermissionKind, PermissionResponse}; + +pub(super) fn from_wry_permission_kind(kind: wry::PermissionKind) -> PermissionKind { + match kind { + wry::PermissionKind::Microphone => PermissionKind::Microphone, + wry::PermissionKind::Camera => PermissionKind::Camera, + wry::PermissionKind::Geolocation => PermissionKind::Geolocation, + wry::PermissionKind::Notifications => PermissionKind::Notifications, + wry::PermissionKind::ClipboardRead => PermissionKind::ClipboardRead, + wry::PermissionKind::DisplayCapture => PermissionKind::DisplayCapture, + wry::PermissionKind::Midi => PermissionKind::Midi, + wry::PermissionKind::Sensors => PermissionKind::Sensors, + wry::PermissionKind::MediaKeySystemAccess => PermissionKind::MediaKeySystemAccess, + wry::PermissionKind::LocalFonts => PermissionKind::LocalFonts, + wry::PermissionKind::WindowManagement => PermissionKind::WindowManagement, + wry::PermissionKind::PointerLock => PermissionKind::PointerLock, + wry::PermissionKind::AutomaticDownloads => PermissionKind::AutomaticDownloads, + wry::PermissionKind::FileSystemAccess => PermissionKind::FileSystemAccess, + wry::PermissionKind::Autoplay => PermissionKind::Autoplay, + wry::PermissionKind::Other => PermissionKind::Other, + _ => PermissionKind::Other, + } +} + +pub(super) fn to_wry_permission_response(response: PermissionResponse) -> wry::PermissionResponse { + match response { + PermissionResponse::Allow => wry::PermissionResponse::Allow, + PermissionResponse::Deny => wry::PermissionResponse::Deny, + PermissionResponse::Default => wry::PermissionResponse::Default, + } +} diff --git a/crates/tauri-runtime/Cargo.toml b/crates/tauri-runtime/Cargo.toml index e034a688aed3..83fef2f3b07b 100644 --- a/crates/tauri-runtime/Cargo.toml +++ b/crates/tauri-runtime/Cargo.toml @@ -51,7 +51,7 @@ jni = "0.21" [target.'cfg(all(target_vendor = "apple", not(target_os = "macos")))'.dependencies] objc2 = "0.6" -objc2-ui-kit = { version = "0.3.0", default-features = false, features = [ +objc2-ui-kit = { version = "0.3.2", default-features = false, features = [ "UIView", "UIResponder", "UIScene", @@ -61,7 +61,7 @@ objc2-ui-kit = { version = "0.3.0", default-features = false, features = [ [target."cfg(target_os = \"macos\")".dependencies] url = "2" objc2 = "0.6" -objc2-web-kit = { version = "0.3", default-features = false, features = [ +objc2-web-kit = { version = "0.3.2", default-features = false, features = [ "objc2-app-kit", "WKWebView", "WKWebViewConfiguration", diff --git a/crates/tauri-runtime/src/lib.rs b/crates/tauri-runtime/src/lib.rs index eac06fbe95ef..af811c20e7f0 100644 --- a/crates/tauri-runtime/src/lib.rs +++ b/crates/tauri-runtime/src/lib.rs @@ -26,6 +26,7 @@ pub mod dpi; /// Types useful for interacting with a user's monitors. pub mod monitor; pub mod webview; +mod webview_permissions; pub mod window; use dpi::{PhysicalPosition, PhysicalSize, Position, Rect, Size}; diff --git a/crates/tauri-runtime/src/webview.rs b/crates/tauri-runtime/src/webview.rs index 36206e1d6541..e10fd356984b 100644 --- a/crates/tauri-runtime/src/webview.rs +++ b/crates/tauri-runtime/src/webview.rs @@ -4,6 +4,7 @@ //! A layer between raw [`Runtime`] webviews and Tauri. //! +pub use crate::webview_permissions::{PermissionKind, PermissionResponse}; #[cfg(not(any(target_os = "android", target_os = "ios")))] use crate::window::WindowId; use crate::{window::is_label_valid, Rect, Runtime, UserEvent}; @@ -41,6 +42,8 @@ type DocumentTitleChangedHandler = dyn Fn(String) + Send + Sync + 'static; type DownloadHandler = dyn Fn(DownloadEvent) -> bool + Send + Sync; +type PermissionRequestHandler = dyn Fn(PermissionKind) -> PermissionResponse + Send + Sync; + #[cfg(any(target_os = "macos", target_os = "ios"))] type OnWebContentProcessTerminateHandler = dyn Fn() + Send; @@ -229,6 +232,8 @@ pub struct PendingWebview> { pub download_handler: Option>, + pub permission_request_handler: Option>, + #[cfg(any(target_os = "macos", target_os = "ios"))] pub on_web_content_process_terminate_handler: Option>, } @@ -257,6 +262,7 @@ impl> PendingWebview { web_resource_request_handler: None, on_page_load_handler: None, download_handler: None, + permission_request_handler: None, #[cfg(any(target_os = "macos", target_os = "ios"))] on_web_content_process_terminate_handler: None, }) diff --git a/crates/tauri-runtime/src/webview_permissions.rs b/crates/tauri-runtime/src/webview_permissions.rs new file mode 100644 index 000000000000..7221c7ecbe19 --- /dev/null +++ b/crates/tauri-runtime/src/webview_permissions.rs @@ -0,0 +1,161 @@ +// Copyright 2019-2024 Tauri Programme within The Commons Conservancy +// SPDX-License-Identifier: Apache-2.0 +// SPDX-License-Identifier: MIT + +/// Permission types that can be requested by the webview. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +#[non_exhaustive] +pub enum PermissionKind { + /// Microphone access permission. + Microphone, + /// Camera access permission. + Camera, + /// Geolocation access permission. + /// + /// ## Platform-specific + /// + /// - **Windows**: Supported via `COREWEBVIEW2_PERMISSION_KIND_GEOLOCATION`. + /// - **Linux**: Supported via `GeolocationPermissionRequest`. + /// - **Android**: Supported via `WebChromeClient.onGeolocationPermissionsShowPrompt`. + /// - **macOS / iOS**: Not yet supported by platform backends. + Geolocation, + /// Notifications permission. + /// + /// ## Platform-specific + /// + /// - **Windows**: Supported via `COREWEBVIEW2_PERMISSION_KIND_NOTIFICATIONS`. + /// - **Linux**: Supported via `NotificationPermissionRequest`. + /// - **macOS / Android / iOS**: Not yet supported by platform backends. + Notifications, + /// Clipboard read permission. + /// + /// ## Platform-specific + /// + /// - **Windows**: Supported via `COREWEBVIEW2_PERMISSION_KIND_CLIPBOARD_READ`. + /// - **macOS / Linux / Android / iOS**: Not yet supported by platform backends. + ClipboardRead, + /// Display capture permission (for getDisplayMedia). + DisplayCapture, + /// Midi access permission. + /// + /// ## Platform-specific + /// + /// - **Windows**: Supported via `COREWEBVIEW2_PERMISSION_KIND_MIDI_SYSTEM_EXCLUSIVE_MESSAGES`. + /// - **Android**: Supported via `android.webkit.resource.MIDI_SYSEX`. + /// - **macOS / Linux / iOS**: Not yet supported by platform backends. + Midi, + /// Sensors (accelerometer, gyroscope, etc.) access permission. + /// + /// ## Platform-specific + /// + /// - **Windows**: Supported via `COREWEBVIEW2_PERMISSION_KIND_OTHER_SENSORS`. + /// - **macOS / Linux / Android / iOS**: Not yet supported by platform backends. + Sensors, + /// Media key system access permission. + /// + /// ## Platform-specific + /// + /// - **Android**: Supported via `android.webkit.resource.PROTECTED_MEDIA_ID`. + /// - **Windows / macOS / Linux / iOS**: Not yet supported by platform backends. + MediaKeySystemAccess, + /// Local fonts access permission. + /// + /// ## Platform-specific + /// + /// - **Windows**: Supported via `COREWEBVIEW2_PERMISSION_KIND_LOCAL_FONTS`. + /// - **macOS / Linux / Android / iOS**: Not yet supported by platform backends. + LocalFonts, + /// Window management permission. + /// + /// ## Platform-specific + /// + /// - **Windows**: Supported via `COREWEBVIEW2_PERMISSION_KIND_WINDOW_MANAGEMENT`. + /// - **macOS / Linux / Android / iOS**: Not yet supported by platform backends. + WindowManagement, + /// Pointer lock permission. + /// + /// ## Platform-specific + /// + /// - **Linux**: Supported via `PointerLockPermissionRequest`. + /// - **Windows / macOS / Android / iOS**: Not yet supported by platform backends. + PointerLock, + /// Automatic downloads permission (multiple downloads without user interaction). + /// + /// ## Platform-specific + /// + /// - **Windows**: Supported via `COREWEBVIEW2_PERMISSION_KIND_MULTIPLE_AUTOMATIC_DOWNLOADS`. + /// - **macOS / Linux / Android / iOS**: Not yet supported by platform backends. + AutomaticDownloads, + /// File system access permission (read/write via File System Access API). + /// + /// ## Platform-specific + /// + /// - **Windows**: Supported via `COREWEBVIEW2_PERMISSION_KIND_FILE_READ_WRITE`. + /// - **macOS / Linux / Android / iOS**: Not yet supported by platform backends. + FileSystemAccess, + /// Media autoplay permission. + /// + /// ## Platform-specific + /// + /// - **Windows**: Supported via `COREWEBVIEW2_PERMISSION_KIND_AUTOPLAY`. + /// - **macOS / Linux / Android / iOS**: Not yet supported by platform backends. + Autoplay, + /// Other unrecognized permission type. + Other, +} + +impl std::fmt::Display for PermissionKind { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::Microphone => write!(f, "microphone"), + Self::Camera => write!(f, "camera"), + Self::Geolocation => write!(f, "geolocation"), + Self::Notifications => write!(f, "notifications"), + Self::ClipboardRead => write!(f, "clipboard-read"), + Self::DisplayCapture => write!(f, "display-capture"), + Self::Midi => write!(f, "midi"), + Self::Sensors => write!(f, "sensors"), + Self::MediaKeySystemAccess => write!(f, "media-key-system-access"), + Self::LocalFonts => write!(f, "local-fonts"), + Self::WindowManagement => write!(f, "window-management"), + Self::PointerLock => write!(f, "pointer-lock"), + Self::AutomaticDownloads => write!(f, "automatic-downloads"), + Self::FileSystemAccess => write!(f, "file-system-access"), + Self::Autoplay => write!(f, "autoplay"), + Self::Other => write!(f, "other"), + } + } +} + +/// Response for permission requests. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)] +pub enum PermissionResponse { + /// Grant the permission. + /// + /// ## Platform-specific + /// + /// - **Android**: Not supported for runtime permissions; the normal Android + /// permission flow is used instead. + Allow, + /// Deny the permission. + Deny, + /// Use the platform or browser default behavior. + /// + /// ## Platform-specific + /// + /// - **Windows / macOS / Android**: The default behavior is to continue the + /// platform or browser permission flow. + /// - **Linux**: The default behavior is [`Self::Deny`] + #[default] + Default, +} + +impl std::fmt::Display for PermissionResponse { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::Allow => write!(f, "allow"), + Self::Deny => write!(f, "deny"), + Self::Default => write!(f, "default"), + } + } +} diff --git a/crates/tauri-schema-generator/schemas/config.schema.json b/crates/tauri-schema-generator/schemas/config.schema.json index 74560b92b988..f2f53d910255 100644 --- a/crates/tauri-schema-generator/schemas/config.schema.json +++ b/crates/tauri-schema-generator/schemas/config.schema.json @@ -483,7 +483,7 @@ ] }, "additionalBrowserArgs": { - "description": "Defines additional browser arguments on Windows. By default wry passes `--disable-features=msWebOOUI,msPdfOOUI,msSmartScreenProtection`\n so if you use this method, you also need to disable these components by yourself if you want.", + "description": "Defines additional browser arguments on Windows.\n\n ## Warning\n\n Webview instances with different browser arguments must also have different [data directories](Self::data_directory).\n\n By default wry passes `--disable-features=msWebOOUI,msPdfOOUI,msSmartScreenProtection`\n so if you set this, you also need to disable these components by yourself if you want.", "type": [ "string", "null" diff --git a/crates/tauri-schema-worker/package.json b/crates/tauri-schema-worker/package.json index 83cdd41d819b..a3f06ab2b0bf 100644 --- a/crates/tauri-schema-worker/package.json +++ b/crates/tauri-schema-worker/package.json @@ -8,6 +8,6 @@ "dev": "wrangler dev" }, "devDependencies": { - "wrangler": "^4.103.0" + "wrangler": "^4.114.0" } } diff --git a/crates/tauri-utils/Cargo.toml b/crates/tauri-utils/Cargo.toml index 5863f44bcdaf..1b7425c67807 100644 --- a/crates/tauri-utils/Cargo.toml +++ b/crates/tauri-utils/Cargo.toml @@ -24,7 +24,7 @@ brotli = { version = "8", optional = true, default-features = false, features = url = { version = "2", features = ["serde"] } html5ever = { version = "0.29", optional = true } kuchiki = { package = "kuchikiki", version = "0.8.8-speedreader", optional = true } -dom_query = { version = "0.27", optional = true, default-features = false } +dom_query = { version = "0.28", optional = true, default-features = false } proc-macro2 = { version = "1", optional = true } quote = { version = "1", optional = true } # Our code requires at least 0.8.21 so don't change this to 0.8 @@ -45,7 +45,7 @@ regex = "1" walkdir = { version = "2", optional = true } memchr = "2" semver = "1" -infer = "0.19" +infer = "0.22" dunce = "1" log = "0.4.21" cargo_metadata = { version = "0.19", optional = true } diff --git a/crates/tauri-utils/src/acl/resolved.rs b/crates/tauri-utils/src/acl/resolved.rs index 101725e95874..a0f30c272563 100644 --- a/crates/tauri-utils/src/acl/resolved.rs +++ b/crates/tauri-utils/src/acl/resolved.rs @@ -152,7 +152,7 @@ impl Resolved { }| { if commands.allow.is_empty() && commands.deny.is_empty() { // global scope - global_scope.entry(key.to_string()).or_default().push(scope); + global_scope.entry(key).or_default().push(scope); } else { let scope_id = if scope.allow.is_some() || scope.deny.is_some() { current_scope_id += 1; @@ -181,7 +181,7 @@ impl Resolved { capability, scope_id, #[cfg(debug_assertions)] - permission_name.to_string(), + permission_name, )?; } @@ -198,7 +198,7 @@ impl Resolved { capability, scope_id, #[cfg(debug_assertions)] - permission_name.to_string(), + permission_name, )?; } } @@ -255,7 +255,7 @@ fn resolve_command( command: String, capability: &Capability, scope_id: Option, - #[cfg(debug_assertions)] referenced_by_permission_identifier: String, + #[cfg(debug_assertions)] referenced_by_permission_identifier: &str, ) -> Result<(), Error> { let mut contexts = Vec::new(); if capability.local { @@ -279,7 +279,7 @@ fn resolve_command( #[cfg(debug_assertions)] referenced_by: ResolvedCommandReference { capability: capability.identifier.clone(), - permission: referenced_by_permission_identifier.clone(), + permission: referenced_by_permission_identifier.to_owned(), }, windows: parse_glob_patterns(capability.windows.clone())?, webviews: parse_glob_patterns(capability.webviews.clone())?, @@ -291,7 +291,7 @@ fn resolve_command( } struct ResolvedPermission<'a> { - key: &'a str, + key: String, permission_name: &'a str, commands: Commands, scope: Scopes, @@ -357,7 +357,7 @@ fn with_resolved_permissions) -> Result<(), Erro commands.deny.extend(permission.commands.deny.clone()); f(ResolvedPermission { - key: &key, + key, permission_name: &permission_name, commands, scope: resolved_scope, diff --git a/crates/tauri-utils/src/config.rs b/crates/tauri-utils/src/config.rs index da0c5369760c..3e5332ed70fe 100644 --- a/crates/tauri-utils/src/config.rs +++ b/crates/tauri-utils/src/config.rs @@ -2104,8 +2104,14 @@ pub struct WindowConfig { /// [tabbing identifier]: #[serde(default, alias = "tabbing-identifier")] pub tabbing_identifier: Option, - /// Defines additional browser arguments on Windows. By default wry passes `--disable-features=msWebOOUI,msPdfOOUI,msSmartScreenProtection` - /// so if you use this method, you also need to disable these components by yourself if you want. + /// Defines additional browser arguments on Windows. + /// + /// ## Warning + /// + /// Webview instances with different browser arguments must also have different [data directories](Self::data_directory). + /// + /// By default wry passes `--disable-features=msWebOOUI,msPdfOOUI,msSmartScreenProtection` + /// so if you set this, you also need to disable these components by yourself if you want. #[serde(default, alias = "additional-browser-args")] pub additional_browser_args: Option, /// Whether or not the window has shadow. diff --git a/crates/tauri-utils/src/resources.rs b/crates/tauri-utils/src/resources.rs index 14681bd14ebd..6b2c410cddb8 100644 --- a/crates/tauri-utils/src/resources.rs +++ b/crates/tauri-utils/src/resources.rs @@ -138,8 +138,9 @@ pub struct ResourcePathsIter<'a> { /// The iter for the current pattern. The cycle goes like this: /// [`ResourcePaths::next`] -> [`Self::next`] -> [`Self::pattern_iter::next`] -> [`Self::current_iter::next`] current_iter: Option, - /// Directories that were walked or globbed while iterating. Build scripts - /// should watch these so new or removed files re-trigger resource copying. + /// Paths that were walked or globbed while iterating. Build scripts + /// should emit a `rerun-if-changed` for each so that adding or removing a + /// file inside a resource directory re-triggers the resource copy. rerun_if_changed: Vec, } @@ -173,11 +174,13 @@ impl Iterator for ResourcePathsInnerIter { } impl ResourcePathsIter<'_> { - /// Directories that were walked or globbed while iterating. + /// Paths that were walked or globbed while iterating. /// - /// Build scripts should emit `cargo:rerun-if-changed` for each directory - /// after exhausting the iterator, so newly added or removed resource files - /// are noticed by cargo. + /// A build script should emit a `cargo:rerun-if-changed` for each of these + /// after iterating, so that adding or removing a file inside a resource + /// directory re-runs the script and copies the new files. Only meaningful + /// once iteration has produced the entries (i.e. after the iterator is + /// exhausted). pub fn rerun_if_changed(&self) -> &[PathBuf] { &self.rerun_if_changed } @@ -261,6 +264,8 @@ impl ResourcePathsIter<'_> { }; if pattern.contains('*') { + // Watch the fixed directory prefix of the glob (everything before the + // first wildcard component) so new files matching the glob are noticed. let mut base = PathBuf::new(); for component in Path::new(pattern).components() { if component.as_os_str().to_string_lossy().contains('*') { @@ -286,11 +291,11 @@ impl ResourcePathsIter<'_> { } } else { let path = normalize(Path::new(pattern)); + self.rerun_if_changed.push(path.clone()); if path.is_dir() { if !self.allow_walk { return Some(Err(crate::Error::NotAllowedToWalkDir(path))); } - self.rerun_if_changed.push(path.clone()); self.current_iter = Some(ResourcePathsInnerIter::Walk { iter: WalkDir::new(&path).into_iter(), current_pattern: if matches!(self.pattern_iter, PatternIter::Map(_)) { @@ -482,35 +487,32 @@ mod tests { #[test] #[serial_test::serial(resources)] - fn resource_paths_iter_tracks_rerun_dirs() { + fn resource_paths_iter_rerun_if_changed() { setup_test_dirs(); let dir = std::env::current_dir().unwrap().join("src-tauri"); let _ = std::env::set_current_dir(dir); let patterns = [ - "../src/script.js".to_string(), - "../src/assets".to_string(), - "../src/textures/**/*".to_string(), + "../src/script.js".into(), + "../src/assets".into(), + "../src/textures/**/*".into(), + "*.toml".into(), ]; - let mut iter = ResourcePaths::new(&patterns, true).iter(); + let mut resources = ResourcePaths::new(&patterns, true).iter(); - for resource in iter.by_ref() { + for resource in resources.by_ref() { resource.unwrap(); } - let rerun = iter.rerun_if_changed(); - assert!( - rerun.contains(&normalize(Path::new("../src/assets"))), - "expected resource directory to be watched, got {rerun:?}" - ); - assert!( - rerun.contains(&PathBuf::from("../src/textures")), - "expected glob base directory to be watched, got {rerun:?}" - ); - assert!( - !rerun.contains(&normalize(Path::new("../src/script.js"))), - "single-file pattern should not be watched as a directory, got {rerun:?}" + assert_eq!( + resources.rerun_if_changed(), + &[ + normalize(Path::new("../src/script.js")), + normalize(Path::new("../src/assets")), + normalize(Path::new("../src/textures")), + PathBuf::from("."), + ] ); } diff --git a/crates/tauri/Cargo.toml b/crates/tauri/Cargo.toml index 8b9d862db938..61f295cf58fa 100644 --- a/crates/tauri/Cargo.toml +++ b/crates/tauri/Cargo.toml @@ -111,12 +111,12 @@ objc2 = "0.6" [target.'cfg(target_os = "macos")'.dependencies] embed_plist = "1.2" plist = "1" -objc2-foundation = { version = "0.3", default-features = false, features = [ +objc2-foundation = { version = "0.3.2", default-features = false, features = [ "std", "NSData", "NSThread", ] } -objc2-app-kit = { version = "0.3", default-features = false, features = [ +objc2-app-kit = { version = "0.3.2", default-features = false, features = [ "std", "NSApplication", "NSColor", @@ -125,7 +125,7 @@ objc2-app-kit = { version = "0.3", default-features = false, features = [ "NSWindow", "NSImage", ] } -objc2-web-kit = { version = "0.3", default-features = false, features = [ +objc2-web-kit = { version = "0.3.2", default-features = false, features = [ "objc2-app-kit", "WKWebView", "WKWebViewConfiguration", @@ -162,7 +162,7 @@ jni = "0.21" [target.'cfg(all(target_vendor = "apple", not(target_os = "macos")))'.dependencies] libc = "0.2" swift-rs = "1" -objc2-ui-kit = { version = "0.3.0", default-features = false, features = [ +objc2-ui-kit = { version = "0.3.2", default-features = false, features = [ "UIApplication", "UIResponder", "UIView", diff --git a/crates/tauri/mobile/android-codegen/TauriActivity.kt b/crates/tauri/mobile/android-codegen/TauriActivity.kt index 251c41e3598e..0cfbe3f51be5 100644 --- a/crates/tauri/mobile/android-codegen/TauriActivity.kt +++ b/crates/tauri/mobile/android-codegen/TauriActivity.kt @@ -10,33 +10,13 @@ import android.content.Intent import android.content.res.Configuration import android.os.Bundle import app.tauri.plugin.PluginManager -import androidx.lifecycle.DefaultLifecycleObserver -import androidx.lifecycle.LifecycleOwner -import androidx.lifecycle.ProcessLifecycleOwner - -object TauriLifecycleObserver : DefaultLifecycleObserver { - override fun onResume(owner: LifecycleOwner) { - super.onResume(owner) - PluginManager.onResume() - } - - override fun onPause(owner: LifecycleOwner) { - super.onPause(owner) - PluginManager.onPause() - } - - override fun onStop(owner: LifecycleOwner) { - super.onStop(owner) - PluginManager.onStop() - } -} abstract class TauriActivity : WryActivity() { override val handleBackNavigation: Boolean = false override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - PluginManager.onActivityCreate(this) + PluginManager.onCreate(this) } fun getPluginManager(): PluginManager { @@ -53,6 +33,21 @@ abstract class TauriActivity : WryActivity() { PluginManager.onRestart(this) } + override fun onResume() { + super.onResume() + PluginManager.onResume(this) + } + + override fun onPause() { + super.onPause() + PluginManager.onPause(this) + } + + override fun onStop() { + super.onStop() + PluginManager.onStop(this) + } + override fun onDestroy() { super.onDestroy() PluginManager.onDestroy(this) diff --git a/crates/tauri/mobile/android/src/main/java/app/tauri/PermissionHelper.kt b/crates/tauri/mobile/android/src/main/java/app/tauri/PermissionHelper.kt index 37468d20d1dc..d9a5cd489d7b 100644 --- a/crates/tauri/mobile/android/src/main/java/app/tauri/PermissionHelper.kt +++ b/crates/tauri/mobile/android/src/main/java/app/tauri/PermissionHelper.kt @@ -39,16 +39,15 @@ object PermissionHelper { * @return True if the permission has been defined in the Manifest, false if not. */ fun hasDefinedPermission(context: Context, permission: String): Boolean { - var hasPermission = false val requestedPermissions = getManifestPermissions(context) - if (requestedPermissions != null && requestedPermissions.isNotEmpty()) { + if (!requestedPermissions.isNullOrEmpty()) { val requestedPermissionsList = listOf(*requestedPermissions) val requestedPermissionsArrayList = ArrayList(requestedPermissionsList) if (requestedPermissionsArrayList.contains(permission)) { - hasPermission = true + return true } } - return hasPermission + return false } /** @@ -58,12 +57,7 @@ object PermissionHelper { * @return true only if all permissions are defined in the AndroidManifest.xml */ fun hasDefinedPermissions(context: Context, permissions: Array): Boolean { - for (permission in permissions) { - if (!hasDefinedPermission(context, permission)) { - return false - } - } - return true + return permissions.all { hasDefinedPermission(context, it) } } /** @@ -96,9 +90,9 @@ object PermissionHelper { * @return The permissions not present in AndroidManifest.xml */ fun getUndefinedPermissions(context: Context, neededPermissions: Array): Array { - val undefinedPermissions = ArrayList() val requestedPermissions = getManifestPermissions(context) if (!requestedPermissions.isNullOrEmpty()) { + val undefinedPermissions = ArrayList() val requestedPermissionsList = listOf(*requestedPermissions) val requestedPermissionsArrayList = ArrayList(requestedPermissionsList) for (permission in neededPermissions) { diff --git a/crates/tauri/mobile/android/src/main/java/app/tauri/plugin/Plugin.kt b/crates/tauri/mobile/android/src/main/java/app/tauri/plugin/Plugin.kt index fda20dd03f07..589db2fd0a8e 100644 --- a/crates/tauri/mobile/android/src/main/java/app/tauri/plugin/Plugin.kt +++ b/crates/tauri/mobile/android/src/main/java/app/tauri/plugin/Plugin.kt @@ -65,13 +65,24 @@ abstract class Plugin(private val activity: Activity) { /** * This event is called just before another activity comes into the foreground. */ + open fun onPause(activity: AppCompatActivity) {} + + /** + * This event is called just before another activity comes into the foreground. + */ + @Deprecated("use onPause(activity: AppCompatActivity) instead") open fun onPause() {} /** * This event is called when the user returns to the activity. It is also called on cold starts. */ - open fun onResume() {} + open fun onResume(activity: AppCompatActivity) {} + /** + * This event is called when the user returns to the activity. It is also called on cold starts. + */ + @Deprecated("use onResume(activity: AppCompatActivity) instead") + open fun onResume() {} /** * This event is called after onStop() when the current activity is being re-displayed to the user (the user has navigated back to it). @@ -90,6 +101,13 @@ abstract class Plugin(private val activity: Activity) { * This event is called when the app is no longer visible to the user. * You will next receive either onRestart(), onDestroy(), or nothing, depending on later user activity. */ + open fun onStop(activity: AppCompatActivity) {} + + /** + * This event is called when the app is no longer visible to the user. + * You will next receive either onRestart(), onDestroy(), or nothing, depending on later user activity. + */ + @Deprecated("use onStop(activity: AppCompatActivity) instead") open fun onStop() {} /** @@ -112,6 +130,21 @@ abstract class Plugin(private val activity: Activity) { onRestart() } + internal fun triggerOnPause(activity: AppCompatActivity) { + onPause(activity) + onPause() + } + + internal fun triggerOnResume(activity: AppCompatActivity) { + onResume(activity) + onResume() + } + + internal fun triggerOnStop(activity: AppCompatActivity) { + onStop(activity) + onStop() + } + /** * This event is called when a configuration change occurs but the app does not recreate the activity. */ @@ -324,16 +357,13 @@ abstract class Plugin(private val activity: Activity) { * @return true only if all permissions associated with the given alias are declared in the manifest */ fun isPermissionDeclared(alias: String): Boolean { - val annotation = handle?.annotation - if (annotation != null) { - for (perm in annotation.permissions) { - if (alias.equals(perm.alias, ignoreCase = true)) { - var result = true - for (permString in perm.strings) { - result = result && PermissionHelper.hasDefinedPermission(activity, permString) - } - return result + for (perm in handle?.annotation?.permissions.orEmpty()) { + if (alias.equals(perm.alias, ignoreCase = true)) { + var result = true + for (permString in perm.strings) { + result = result && PermissionHelper.hasDefinedPermission(activity, permString) } + return result } } Logger.error( @@ -430,13 +460,10 @@ abstract class Plugin(private val activity: Activity) { * @return Android permission strings associated with the provided aliases, if exists */ private fun getPermissionStringsForAliases(aliases: Array): Array { - val annotation = handle?.annotation val perms: HashSet = HashSet() - if (annotation != null) { - for (perm in annotation.permissions) { - if (aliases.contains(perm.alias)) { - perms.addAll(perm.strings) - } + for (perm in handle?.annotation?.permissions.orEmpty()) { + if (aliases.contains(perm.alias)) { + perms.addAll(perm.strings) } } return perms.toArray(arrayOfNulls(0)) @@ -459,50 +486,45 @@ abstract class Plugin(private val activity: Activity) { */ open fun getPermissionStates(): Map { val permissionsResults: MutableMap = HashMap() - val annotation = handle?.annotation - if (annotation != null) { - for (perm in annotation.permissions) { - // If a permission is defined with no permission constants, return GRANTED for it. - // Otherwise, get its true state. - if (perm.strings.isEmpty() || perm.strings.size == 1 && perm.strings[0] - .isEmpty() - ) { - val key = perm.alias - if (key.isNotEmpty()) { - val existingResult = permissionsResults[key] - - // auto set permission state to GRANTED if the alias is empty. - if (existingResult == null) { - permissionsResults[key] = PermissionState.GRANTED - } + for (perm in handle?.annotation?.permissions.orEmpty()) { + // If a permission is defined with no permission constants, return GRANTED for it. + // Otherwise, get its true state. + if (perm.strings.isEmpty() || perm.strings.size == 1 && perm.strings[0].isEmpty()) { + val key = perm.alias + if (key.isNotEmpty()) { + val existingResult = permissionsResults[key] + + // auto set permission state to GRANTED if the alias is empty. + if (existingResult == null) { + permissionsResults[key] = PermissionState.GRANTED } - } else { - for (permString in perm.strings) { - val key = perm.alias.ifEmpty { permString } - var permissionStatus: PermissionState - if (ActivityCompat.checkSelfPermission( - activity, - permString - ) == PackageManager.PERMISSION_GRANTED - ) { - permissionStatus = PermissionState.GRANTED - } else { - permissionStatus = PermissionState.PROMPT - - // Check if there is a cached permission state for the "Never ask again" state - val prefs = - activity.getSharedPreferences("PluginPermStates", Activity.MODE_PRIVATE) - val state = prefs.getString(permString, null) - if (state != null) { - permissionStatus = PermissionState.byState(state) - } + } + } else { + for (permString in perm.strings) { + val key = perm.alias.ifEmpty { permString } + var permissionStatus: PermissionState + if (ActivityCompat.checkSelfPermission( + activity, + permString + ) == PackageManager.PERMISSION_GRANTED + ) { + permissionStatus = PermissionState.GRANTED + } else { + permissionStatus = PermissionState.PROMPT + + // Check if there is a cached permission state for the "Never ask again" state + val prefs = + activity.getSharedPreferences("PluginPermStates", Activity.MODE_PRIVATE) + val state = prefs.getString(permString, null) + if (state != null) { + permissionStatus = PermissionState.byState(state) } - val existingResult = permissionsResults[key] + } + val existingResult = permissionsResults[key] - // multiple permissions with the same alias must all be true, otherwise all false. - if (existingResult == null || existingResult === PermissionState.GRANTED) { - permissionsResults[key] = permissionStatus - } + // multiple permissions with the same alias must all be true, otherwise all false. + if (existingResult == null || existingResult === PermissionState.GRANTED) { + permissionsResults[key] = permissionStatus } } } diff --git a/crates/tauri/mobile/android/src/main/java/app/tauri/plugin/PluginHandle.kt b/crates/tauri/mobile/android/src/main/java/app/tauri/plugin/PluginHandle.kt index 8296c815b8e6..594a96f4b7e0 100644 --- a/crates/tauri/mobile/android/src/main/java/app/tauri/plugin/PluginHandle.kt +++ b/crates/tauri/mobile/android/src/main/java/app/tauri/plugin/PluginHandle.kt @@ -6,7 +6,6 @@ package app.tauri.plugin import android.app.Activity import android.content.Intent -import android.content.SharedPreferences import android.webkit.WebView import androidx.activity.result.IntentSenderRequest import androidx.core.app.ActivityCompat @@ -18,8 +17,15 @@ import app.tauri.annotation.PermissionCallback import app.tauri.annotation.TauriPlugin import com.fasterxml.jackson.databind.ObjectMapper import java.lang.reflect.Method +import androidx.core.content.edit -class PluginHandle(private val manager: PluginManager, val name: String, val instance: Plugin, val config: String, val jsonMapper: ObjectMapper) { +class PluginHandle( + private val manager: PluginManager, + val name: String, + val instance: Plugin, + val config: String, + val jsonMapper: ObjectMapper +) { private val commands: HashMap = HashMap() private val permissionCallbackMethods: HashMap = HashMap() private val startActivityCallbackMethods: HashMap = HashMap() @@ -47,7 +53,11 @@ class PluginHandle(private val manager: PluginManager, val name: String, val ins } } - fun startIntentSenderForResult(invoke: Invoke, intentSender: IntentSenderRequest, callbackName: String) { + fun startIntentSenderForResult( + invoke: Invoke, + intentSender: IntentSenderRequest, + callbackName: String + ) { manager.startIntentSenderForResult(intentSender) { result -> val method = startActivityCallbackMethods[callbackName] if (method != null) { @@ -84,7 +94,7 @@ class PluginHandle(private val manager: PluginManager, val name: String, val ins invoke: Invoke, permissions: Map ): Boolean { - val activity = manager.activity + val activity = manager.activity!! val prefs = activity.getSharedPreferences("PluginPermStates", Activity.MODE_PRIVATE) for ((permString, isGranted) in permissions) { @@ -92,24 +102,24 @@ class PluginHandle(private val manager: PluginManager, val name: String, val ins // Permission granted. If previously denied, remove cached state val state = prefs.getString(permString, null) if (state != null) { - val editor: SharedPreferences.Editor = prefs.edit() - editor.remove(permString) - editor.apply() + prefs.edit { + remove(permString) + } } } else { - val editor: SharedPreferences.Editor = prefs.edit() - if (ActivityCompat.shouldShowRequestPermissionRationale( - activity, - permString - ) - ) { - // Permission denied, can prompt again with rationale - editor.putString(permString, PermissionState.PROMPT_WITH_RATIONALE.toString()) - } else { - // Permission denied permanently, store this state for future reference - editor.putString(permString, PermissionState.DENIED.toString()) + prefs.edit { + if (ActivityCompat.shouldShowRequestPermissionRationale( + activity, + permString + ) + ) { + // Permission denied, can prompt again with rationale + putString(permString, PermissionState.PROMPT_WITH_RATIONALE.toString()) + } else { + // Permission denied permanently, store this state for future reference + putString(permString, PermissionState.DENIED.toString()) + } } - editor.apply() } } val permStrings = permissions.keys.toTypedArray() diff --git a/crates/tauri/mobile/android/src/main/java/app/tauri/plugin/PluginManager.kt b/crates/tauri/mobile/android/src/main/java/app/tauri/plugin/PluginManager.kt index 0c64bb5f3411..32a1cc519d23 100644 --- a/crates/tauri/mobile/android/src/main/java/app/tauri/plugin/PluginManager.kt +++ b/crates/tauri/mobile/android/src/main/java/app/tauri/plugin/PluginManager.kt @@ -34,11 +34,12 @@ object PluginManager { fun onResult(result: ActivityResult) } - lateinit var activity: AppCompatActivity + private val activities: HashSet = HashSet() + var activity: AppCompatActivity? = null private val plugins: HashMap = HashMap() - private lateinit var startActivityForResultLauncher: ActivityResultLauncher - private lateinit var startIntentSenderForResultLauncher: ActivityResultLauncher - private lateinit var requestPermissionsLauncher: ActivityResultLauncher> + private var startActivityForResultLauncher: ActivityResultLauncher? = null + private var startIntentSenderForResultLauncher: ActivityResultLauncher? = null + private var requestPermissionsLauncher: ActivityResultLauncher>? = null private var requestPermissionsCallback: RequestPermissionsCallback? = null private var startActivityForResultCallback: ActivityResultCallback? = null private var startIntentSenderForResultCallback: ActivityResultCallback? = null @@ -55,12 +56,16 @@ object PluginManager { .registerModule(SimpleModule().addDeserializer(Channel::class.java, channelDeserializer)) } - fun onActivityCreate(activity: AppCompatActivity) { - // TODO: on destroy, we should change to a different activity - if (::activity.isInitialized) { - return + fun onCreate(activity: AppCompatActivity) { + // Record the activity, and if that's the only activity we got, register result launchers + activities.add(activity) + if (this.activity == null) { + this.activity = activity + registerResultLaunchers(activity) } - this.activity = activity + } + + private fun registerResultLaunchers(activity: AppCompatActivity) { startActivityForResultLauncher = activity.registerForActivityResult(ActivityResultContracts.StartActivityForResult() ) { result -> @@ -92,15 +97,15 @@ object PluginManager { } } - fun onPause() { + fun onPause(activity: AppCompatActivity) { for (plugin in plugins.values) { - plugin.instance.onPause() + plugin.instance.triggerOnPause(activity) } } - fun onResume() { + fun onResume(activity: AppCompatActivity) { for (plugin in plugins.values) { - plugin.instance.onResume() + plugin.instance.triggerOnResume(activity) } } @@ -110,9 +115,9 @@ object PluginManager { } } - fun onStop() { + fun onStop(activity: AppCompatActivity) { for (plugin in plugins.values) { - plugin.instance.onStop() + plugin.instance.triggerOnStop(activity) } } @@ -120,6 +125,20 @@ object PluginManager { for (plugin in plugins.values) { plugin.instance.triggerOnDestroy(activity) } + + activities.remove(activity) + val nextActivity = activities.firstOrNull() + if (nextActivity != null) { + if (this.activity == activity) { + this.activity = nextActivity + registerResultLaunchers(nextActivity) + } + } else { + this.activity = null + this.startActivityForResultLauncher = null + this.startIntentSenderForResultLauncher = null + this.requestPermissionsLauncher = null + } } fun onConfigurationChanged(newConfig: Configuration) { @@ -130,12 +149,12 @@ object PluginManager { fun startActivityForResult(intent: Intent, callback: ActivityResultCallback) { startActivityForResultCallback = callback - startActivityForResultLauncher.launch(intent) + startActivityForResultLauncher!!.launch(intent) } fun startIntentSenderForResult(intent: IntentSenderRequest, callback: ActivityResultCallback) { startIntentSenderForResultCallback = callback - startIntentSenderForResultLauncher.launch(intent) + startIntentSenderForResultLauncher!!.launch(intent) } fun requestPermissions( @@ -143,7 +162,7 @@ object PluginManager { callback: RequestPermissionsCallback ) { requestPermissionsCallback = callback - requestPermissionsLauncher.launch(permissionStrings) + requestPermissionsLauncher!!.launch(permissionStrings) } @JniMethod diff --git a/crates/tauri/src/app.rs b/crates/tauri/src/app.rs index 1b47f1b380de..2b24287ef15b 100644 --- a/crates/tauri/src/app.rs +++ b/crates/tauri/src/app.rs @@ -1419,28 +1419,25 @@ impl App { mut self, mut callback: F, ) -> impl FnMut(RuntimeRunEvent) { - let app_handle = self.handle().clone(); - let manager = self.manager.clone(); - move |event| match event { RuntimeRunEvent::Ready => { if let Err(e) = setup(&mut self) { panic!("Failed to setup app: {e}"); } - let event = on_event_loop_event(&app_handle, RuntimeRunEvent::Ready, &manager); - callback(&app_handle, event); + let event = on_event_loop_event(self.handle(), RuntimeRunEvent::Ready, self.manager()); + callback(self.handle(), event); } RuntimeRunEvent::Exit => { - let event = on_event_loop_event(&app_handle, RuntimeRunEvent::Exit, &manager); - callback(&app_handle, event); - app_handle.cleanup_before_exit(); + let event = on_event_loop_event(self.handle(), RuntimeRunEvent::Exit, self.manager()); + callback(self.handle(), event); + self.cleanup_before_exit(); if self.manager.restart_on_exit.load(atomic::Ordering::Relaxed) { crate::process::restart(&self.env()); } } _ => { - let event = on_event_loop_event(&app_handle, event, &manager); - callback(&app_handle, event); + let event = on_event_loop_event(self.handle(), event, self.manager()); + callback(self.handle(), event); } } } @@ -1472,19 +1469,18 @@ impl App { note = "When called in a loop (as suggested by the name), this function will busy-loop. To re-gain control of control flow after the app has exited, use `App::run_return` instead." )] pub fn run_iteration, RunEvent) + 'static>(&mut self, mut callback: F) { - let manager = self.manager.clone(); - let app_handle = self.handle().clone(); - if !self.ran_setup { if let Err(e) = setup(self) { panic!("Failed to setup app: {e}"); } } + let app_handle = self.handle().clone(); + app_handle.event_loop.lock().unwrap().main_thread_id = std::thread::current().id(); self.runtime.as_mut().unwrap().run_iteration(move |event| { - let event = on_event_loop_event(&app_handle, event, &manager); + let event = on_event_loop_event(&app_handle, event, app_handle.manager()); callback(&app_handle, event); }) } @@ -1519,6 +1515,9 @@ pub struct Builder { /// Page load hook. on_page_load: Option>>, + /// Permission request hook. + on_permission_request: Option>>, + /// Web content process termination hook. #[cfg(any(target_os = "macos", target_os = "ios"))] on_web_content_process_terminate: Option>>, @@ -1609,6 +1608,7 @@ impl Builder { .into_string(), channel_interceptor: None, on_page_load: None, + on_permission_request: None, #[cfg(any(target_os = "macos", target_os = "ios"))] on_web_content_process_terminate: None, plugins: PluginStore::default(), @@ -1791,6 +1791,53 @@ tauri::Builder::default() self } + /// Defines a closure to be executed when a permission is requested. + /// + /// The handler receives the [`crate::webview::PermissionKind`] and should return + /// the desired [`crate::webview::PermissionResponse`]. + /// + /// This is not called if a [`crate::webview::WebviewBuilder::on_permission_request`] + /// is set on the webview and returned a response other than [`crate::webview::PermissionResponse::Default`]. + /// + /// > [!NOTE] + /// > This handler only triggers for new permission requests. If the user has already + /// > allowed or denied a permission persistently within the webview, the browser + /// > will use the saved preference instead of calling this handler. + /// + /// ## Platform-specific: + /// + /// - **Windows**: Fully supported via WebView2's PermissionRequested event. + /// - **macOS / iOS**: Fully supported via WKUIDelegate's requestMediaCapturePermission. + /// - **Linux**: Fully supported via WebKitGTK's permission-request signal. + /// - **Android**: Supported via JNI bridge for geolocation, microphone, camera, + /// protected media, and MIDI requests. Android runtime permissions may still + /// trigger native OS prompts before access is granted. + /// + /// # Examples + /// + /// ```rust,no_run + /// use tauri::webview::{PermissionKind, PermissionResponse}; + /// tauri::Builder::default() + /// .on_permission_request(|_, kind| match kind { + /// PermissionKind::Geolocation => PermissionResponse::Allow, + /// PermissionKind::Notifications => PermissionResponse::Allow, + /// _ => PermissionResponse::Default, + /// }); + /// ``` + #[must_use] + pub fn on_permission_request(mut self, on_permission_request: F) -> Self + where + F: Fn(Webview, crate::webview::PermissionKind) -> crate::webview::PermissionResponse + + Send + + Sync + + 'static, + { + self + .on_permission_request + .replace(Arc::new(on_permission_request)); + self + } + /// Defines the web content process termination hook. /// /// ## Platform-specific @@ -2256,6 +2303,7 @@ tauri::Builder::default() self.plugins, self.invoke_handler, self.on_page_load, + self.on_permission_request, #[cfg(any(target_os = "macos", target_os = "ios"))] self.on_web_content_process_terminate, self.uri_scheme_protocols, @@ -2368,8 +2416,7 @@ tauri::Builder::default() let runtime_handle = runtime.handle(); - #[allow(unused_mut)] - let mut app = App { + let app = App { runtime: Some(runtime), setup: Some(self.setup), manager: manager.clone(), diff --git a/crates/tauri/src/event/listener.rs b/crates/tauri/src/event/listener.rs index bed30b715225..afcc3ca5bb27 100644 --- a/crates/tauri/src/event/listener.rs +++ b/crates/tauri/src/event/listener.rs @@ -25,6 +25,7 @@ enum Pending { handler: Handler, }, Emit(EmitArgs), + RemoveForTarget(EventTarget), } /// Stored in [`Listeners`] to be called upon, when the event that stored it, is triggered. @@ -128,6 +129,7 @@ impl Listeners { Pending::Unlisten(id) => self.unlisten(id), Pending::Listen { id, event, handler } => self.listen_with_id(id, event, handler), Pending::Emit(args) => self.emit(args)?, + Pending::RemoveForTarget(target) => self.remove_listeners_for_target(target), } } @@ -186,6 +188,22 @@ impl Listeners { } } + /// Removes every Rust-side event listener registered for the exact given target. + /// + /// Called when a window or webview is destroyed so its listeners don't leak. + /// Global targets such as [`EventTarget::App`], [`EventTarget::Any`] and [`EventTarget::AnyLabel`] are never matched here. + fn remove_listeners_for_target(&self, target: EventTarget) { + match self.inner.handlers.try_lock() { + Err(_) => self.insert_pending(Pending::RemoveForTarget(target)), + Ok(mut lock) => { + for handlers in lock.values_mut() { + handlers.retain(|_, handler| handler.target != target); + } + lock.retain(|_, handlers| !handlers.is_empty()); + } + } + } + /// Emits the given event with its payload based on a filter. pub(crate) fn emit_filter(&self, emit_args: EmitArgs, filter: Option) -> crate::Result<()> where @@ -252,11 +270,7 @@ impl Listeners { } /// Removes all JS event listeners registered from the given webview. - /// - /// Called when a webview is destroyed: its JavaScript runtime no longer - /// exists, so the listeners keyed by its label can never be delivered and - /// would otherwise leak in the map until the app exits. - pub(crate) fn remove_webview_js_listeners(&self, webview_label: &str) { + fn remove_webview_js_listeners(&self, webview_label: &str) { self .inner .js_event_listeners @@ -265,6 +279,18 @@ impl Listeners { .remove(webview_label); } + /// Removes all event listeners associated with a destroyed webview. + pub(crate) fn remove_webview_listeners(&self, label: &str) { + self.remove_webview_js_listeners(label); + self.remove_listeners_for_target(EventTarget::webview(label)); + } + + /// Removes all event listeners associated with a destroyed window. + pub(crate) fn remove_window_listeners(&self, label: &str) { + self.remove_listeners_for_target(EventTarget::window(label)); + self.remove_listeners_for_target(EventTarget::webview_window(label)); + } + pub(crate) fn has_js_listener bool>( &self, event: crate::EventName<&str>, @@ -414,24 +440,49 @@ mod test { } #[test] - fn js_listeners_removed_on_webview_close() { + fn listeners_removed_on_webview_close() { let listeners = Listeners::default(); - let event = crate::EventName::new("some-event".to_owned()).unwrap(); + let event = crate::EventName::new("test-event".to_owned()).unwrap(); let webview_label = "main"; + let window = listeners.listen(event.clone(), EventTarget::window(webview_label), event_fn); + // Same label as the window, but a different target kind (multi-webview mode). + let webview = listeners.listen(event.clone(), EventTarget::webview(webview_label), event_fn); + let webview_window = listeners.listen( + event.clone(), + EventTarget::webview_window(webview_label), + event_fn, + ); + let other = listeners.listen(event.clone(), EventTarget::webview("other"), event_fn); + let app = listeners.listen(event.clone(), EventTarget::App, event_fn); + let any = listeners.listen(event.clone(), EventTarget::Any, event_fn); + let labeled = listeners.listen(event.clone(), EventTarget::labeled(webview_label), event_fn); + let id = listeners.next_event_id(); listeners.listen_js( event.as_str_event(), webview_label, - EventTarget::Webview { - label: webview_label.to_owned(), - }, + EventTarget::webview(webview_label), id, ); assert!(listeners.has_js_listener(event.as_str_event(), |_| true)); - // dropping the source webview must drop its JS listeners. - listeners.remove_webview_js_listeners(webview_label); + listeners.remove_webview_listeners(webview_label); + assert!(!listeners.has_js_listener(event.as_str_event(), |_| true)); + + let lock = listeners.inner.handlers.lock().unwrap(); + let remaining: HashSet = lock.get(&event).unwrap().keys().copied().collect(); + + // only the exact `Webview { label: "main" }` listener is gone + assert!(!remaining.contains(&webview)); + // same label but different kind is kept + assert!(remaining.contains(&window)); + assert!(remaining.contains(&webview_window)); + // unrelated and global listeners are kept + assert!(remaining.contains(&other)); + assert!(remaining.contains(&app)); + assert!(remaining.contains(&any)); + assert!(remaining.contains(&labeled)); } } diff --git a/crates/tauri/src/ipc/authority.rs b/crates/tauri/src/ipc/authority.rs index f3eef9f07b62..80953901f399 100644 --- a/crates/tauri/src/ipc/authority.rs +++ b/crates/tauri/src/ipc/authority.rs @@ -609,13 +609,11 @@ impl CommandScope { impl<'a, R: Runtime, T: ScopeObject> CommandArg<'a, R> for CommandScope { /// Grabs the [`ResolvedScope`] from the [`CommandItem`] and returns the associated [`CommandScope`]. fn from_command(command: CommandItem<'a, R>) -> Result { - let scope_ids = command.acl.as_ref().map(|resolved| { - resolved + if let Some(resolved) = &command.acl { + let scope_ids = resolved .iter() .filter_map(|cmd| cmd.scope_id) - .collect::>() - }); - if let Some(scope_ids) = scope_ids { + .collect::>(); CommandScope::resolve(&command.message.webview, scope_ids).map_err(Into::into) } else { Ok(CommandScope { diff --git a/crates/tauri/src/ipc/protocol.rs b/crates/tauri/src/ipc/protocol.rs index 66f9abe1a804..f0422e8c7426 100644 --- a/crates/tauri/src/ipc/protocol.rs +++ b/crates/tauri/src/ipc/protocol.rs @@ -569,6 +569,7 @@ mod tests { #[cfg(any(target_os = "macos", target_os = "ios"))] None, Default::default(), + Default::default(), StateManager::new(), Default::default(), #[cfg(all(desktop, feature = "tray-icon"))] @@ -687,6 +688,7 @@ mod tests { #[cfg(any(target_os = "macos", target_os = "ios"))] None, Default::default(), + Default::default(), StateManager::new(), Default::default(), #[cfg(all(desktop, feature = "tray-icon"))] diff --git a/crates/tauri/src/manager/mod.rs b/crates/tauri/src/manager/mod.rs index 9a61be604713..a60ac048f911 100644 --- a/crates/tauri/src/manager/mod.rs +++ b/crates/tauri/src/manager/mod.rs @@ -254,6 +254,7 @@ impl AppManager { plugins: PluginStore, invoke_handler: Box>, on_page_load: Option>>, + on_permission_request: Option>>, #[cfg(any(target_os = "macos", target_os = "ios"))] on_web_content_process_terminate: Option< Arc>, >, @@ -290,6 +291,7 @@ impl AppManager { webviews: Mutex::default(), invoke_handler, on_page_load, + on_permission_request, #[cfg(any(target_os = "macos", target_os = "ios"))] on_web_content_process_terminate, uri_scheme_protocols: Mutex::new(uri_scheme_protocols), @@ -658,17 +660,16 @@ impl AppManager { if let Some(window) = window { for webview in window.webviews() { self.webview.webviews_lock().remove(webview.label()); - self - .listeners() - .remove_webview_js_listeners(webview.label()); + self.listeners().remove_webview_listeners(webview.label()); } } + self.listeners().remove_window_listeners(label); } #[cfg(desktop)] pub(crate) fn on_webview_close(&self, label: &str) { self.webview.webviews_lock().remove(label); - self.listeners().remove_webview_js_listeners(label); + self.listeners().remove_webview_listeners(label); } pub fn windows(&self) -> HashMap> { @@ -774,6 +775,7 @@ mod test { #[cfg(any(target_os = "macos", target_os = "ios"))] None, Default::default(), + Default::default(), StateManager::new(), Default::default(), #[cfg(all(desktop, feature = "tray-icon"))] diff --git a/crates/tauri/src/manager/webview.rs b/crates/tauri/src/manager/webview.rs index 8c99afb9ea47..9a871990740e 100644 --- a/crates/tauri/src/manager/webview.rs +++ b/crates/tauri/src/manager/webview.rs @@ -73,6 +73,8 @@ pub struct WebviewManager { pub invoke_handler: Box>, /// The page load hook, invoked when the webview performs a navigation. pub on_page_load: Option>>, + /// The permission request hook, invoked when the webview requests a permission. + pub on_permission_request: Option>>, /// The web content process termination hook. #[cfg(any(target_os = "macos", target_os = "ios"))] pub on_web_content_process_terminate: Option>>, @@ -310,6 +312,30 @@ impl WebviewManager { } })); + let permission_request_handler = pending.permission_request_handler.take(); + if permission_request_handler.is_some() || self.on_permission_request.is_some() { + let label_ = pending.label.clone(); + let app_manager_ = manager.manager_owned(); + pending + .permission_request_handler + .replace(Box::new(move |kind| { + if let Some(handler) = &permission_request_handler { + let response = handler(kind); + if response != crate::webview::PermissionResponse::Default { + return response; + } + } + + if let Some(w) = app_manager_.get_webview(&label_) { + if let Some(on_permission_request) = &app_manager_.webview.on_permission_request { + return on_permission_request(w, kind); + } + } + + crate::webview::PermissionResponse::Default + })); + } + #[cfg(any(target_os = "macos", target_os = "ios"))] if pending.on_web_content_process_terminate_handler.is_none() { let app_manager_ = manager.manager_owned(); diff --git a/crates/tauri/src/webview/mod.rs b/crates/tauri/src/webview/mod.rs index 9b4d9175eca6..c32bc2068434 100644 --- a/crates/tauri/src/webview/mod.rs +++ b/crates/tauri/src/webview/mod.rs @@ -18,7 +18,9 @@ pub use cookie; use http::HeaderMap; use serde::Serialize; use tauri_macros::default_runtime; -pub use tauri_runtime::webview::{NewWindowFeatures, PageLoadEvent, ScrollBarStyle}; +pub use tauri_runtime::webview::{ + NewWindowFeatures, PageLoadEvent, PermissionKind, PermissionResponse, ScrollBarStyle, +}; // Remove this re-export in v3 pub use tauri_runtime::Cookie; #[cfg(desktop)] @@ -64,6 +66,8 @@ pub(crate) type UriSchemeProtocolHandler = pub(crate) type OnPageLoad = dyn Fn(Webview, PageLoadPayload<'_>) + Send + Sync + 'static; pub(crate) type OnDocumentTitleChanged = dyn Fn(Webview, String) + Send + Sync + 'static; pub(crate) type DownloadHandler = dyn Fn(Webview, DownloadEvent<'_>) -> bool + Send + Sync; +pub(crate) type PermissionRequestHandler = + dyn Fn(Webview, PermissionKind) -> PermissionResponse + Send + Sync + 'static; #[derive(Clone, Serialize)] pub(crate) struct CreatedEvent { @@ -277,6 +281,7 @@ unstable_struct!( pub(crate) on_page_load_handler: Option>>, pub(crate) document_title_changed_handler: Option>>, pub(crate) download_handler: Option>>, + pub(crate) permission_request_handler: Option>>, } ); @@ -355,6 +360,7 @@ async fn create_window(app: tauri::AppHandle) { on_page_load_handler: None, document_title_changed_handler: None, download_handler: None, + permission_request_handler: None, } } @@ -434,6 +440,7 @@ async fn create_window(app: tauri::AppHandle) { on_page_load_handler: None, document_title_changed_handler: None, download_handler: None, + permission_request_handler: None, } } @@ -693,6 +700,57 @@ tauri::Builder::default() self } + /// Defines a closure to be executed when a permission is requested. + /// + /// The handler receives the [`PermissionKind`] and should return + /// the desired [`PermissionResponse`]. + /// + /// This is called before [`crate::Builder::on_permission_request`], + /// and skips it if the returned response is not [`crate::webview::PermissionResponse::Default`]. + /// + /// > [!NOTE] + /// > This handler only triggers for new permission requests. If the user has already + /// > allowed or denied a permission persistently within the webview, the browser + /// > will use the saved preference instead of calling this handler. + /// + /// ## Platform-specific: + /// + /// - **Windows**: Fully supported via WebView2's PermissionRequested event. + /// - **macOS / iOS**: Fully supported via WKUIDelegate's requestMediaCapturePermission. + /// - **Linux**: Fully supported via WebKitGTK's permission-request signal. + /// - **Android**: Supported via JNI bridge for geolocation, microphone, camera, + /// protected media, and MIDI requests. Android runtime permissions may still + /// trigger native OS prompts before access is granted. + /// + /// # Examples + /// + /// ```rust,no_run + /// use tauri::webview::{WebviewBuilder, PermissionKind, PermissionResponse}; + /// tauri::Builder::default() + /// .setup(|app| { + /// let window = tauri::window::WindowBuilder::new(app, "label").build()?; + /// let webview_builder = WebviewBuilder::new("core", tauri::WebviewUrl::App("index.html".into())) + /// .on_permission_request(|webview, kind| { + /// match kind { + /// PermissionKind::Geolocation => PermissionResponse::Allow, + /// PermissionKind::Notifications => PermissionResponse::Allow, + /// _ => PermissionResponse::Default, + /// } + /// }); + /// let webview = window.add_child(webview_builder, tauri::LogicalPosition::new(0, 0), window.inner_size().unwrap())?; + /// Ok(()) + /// }); + /// ``` + pub fn on_permission_request< + F: Fn(Webview, PermissionKind) -> PermissionResponse + Send + Sync + 'static, + >( + mut self, + f: F, + ) -> Self { + self.permission_request_handler.replace(Box::new(f)); + self + } + pub(crate) fn into_pending_webview>( mut self, manager: &M, @@ -771,6 +829,18 @@ tauri::Builder::default() } })); + let label_ = pending.label.clone(); + let manager_ = manager.manager_owned(); + if let Some(handler) = self.permission_request_handler { + pending.permission_request_handler = Some(Box::new(move |kind| { + if let Some(w) = manager_.get_webview(&label_) { + handler(w, kind) + } else { + PermissionResponse::Default + } + })); + } + manager .manager() .webview @@ -950,6 +1020,8 @@ fn main() { /// /// ## Warning /// + /// Webview instances with different browser arguments must also have different [data directories](Self::data_directory). + /// /// By default wry passes `--disable-features=msWebOOUI,msPdfOOUI,msSmartScreenProtection` /// so if you use this method, you also need to disable these components by yourself if you want. #[must_use] @@ -1912,6 +1984,7 @@ tauri::Builder::default() if let Some((plugin, command_name)) = plugin_command { invoke.message.command = command_name; + #[cfg(desktop)] let command = invoke.message.command.clone(); #[cfg(mobile)] @@ -1920,48 +1993,46 @@ tauri::Builder::default() #[allow(unused_mut)] let mut handled = manager.run_plugin_invoke_handler(plugin, invoke); + if handled { + return; + } + #[cfg(mobile)] { - if !handled { - handled = true; - - fn load_channels(payload: &serde_json::Value, webview: &Webview) { - use std::str::FromStr; - - if let serde_json::Value::Object(map) = payload { - for v in map.values() { - if let serde_json::Value::String(s) = v { - let _ = crate::ipc::JavaScriptChannelId::from_str(s) - .map(|id| id.channel_on::(webview.clone())); - } + fn load_channels(payload: &serde_json::Value, webview: &Webview) { + use std::str::FromStr; + + if let serde_json::Value::Object(map) = payload { + for v in map.values() { + if let serde_json::Value::String(s) = v { + let _ = crate::ipc::JavaScriptChannelId::from_str(s) + .map(|id| id.channel_on::(webview.clone())); } } } + } - let payload = message.payload.into_json(); - // initialize channels - load_channels(&payload, &message.webview); - - let resolver_ = resolver.clone(); - if let Err(e) = crate::plugin::mobile::run_command( - plugin, - &app_handle, - heck::AsLowerCamelCase(message.command).to_string(), - payload, - move |response| match response { - Ok(r) => resolver_.resolve(r), - Err(e) => resolver_.reject(e), - }, - ) { - resolver.reject(e.to_string()); - return; - } + let payload = message.payload.into_json(); + // initialize channels + load_channels(&payload, &message.webview); + + let resolver_ = resolver.clone(); + if let Err(e) = crate::plugin::mobile::run_command( + plugin, + &app_handle, + heck::AsLowerCamelCase(message.command).to_string(), + payload, + move |response| match response { + Ok(r) => resolver_.resolve(r), + Err(e) => resolver_.reject(e), + }, + ) { + resolver.reject(e.to_string()); } } - if !handled { - resolver.reject(format!("Command {command} not found")); - } + #[cfg(desktop)] + resolver.reject(format!("Command {command} not found")); } else { let command = invoke.message.command.clone(); let handled = manager.run_invoke_handler(invoke); @@ -2414,6 +2485,18 @@ impl ResolvedScope { mod tests { use url::Url; + #[test] + fn no_permission_handler_preserves_runtime_default() { + use crate::test::{mock_builder, mock_context, noop_assets}; + + let app = mock_builder().build(mock_context(noop_assets())).unwrap(); + let pending = super::WebviewBuilder::new("test", crate::WebviewUrl::default()) + .into_pending_webview(&app, "test") + .unwrap(); + + assert!(pending.permission_request_handler.is_none()); + } + fn test_webview_window() -> crate::WebviewWindow { use crate::test::{mock_builder, mock_context, noop_assets}; diff --git a/crates/tauri/src/webview/webview_window.rs b/crates/tauri/src/webview/webview_window.rs index d841d954d887..2e7770041415 100644 --- a/crates/tauri/src/webview/webview_window.rs +++ b/crates/tauri/src/webview/webview_window.rs @@ -434,6 +434,57 @@ tauri::Builder::default() self } + /// Defines a closure to be executed when a permission is requested. + /// + /// The handler receives the [`crate::webview::PermissionKind`] and should return + /// the desired [`crate::webview::PermissionResponse`]. + /// + /// > [!NOTE] + /// > This handler only triggers for new permission requests. If the user has already + /// > allowed or denied a permission persistently within the webview, the browser + /// > will use the saved preference instead of calling this handler. + /// + /// ## Platform-specific: + /// + /// - **Windows**: Fully supported via WebView2's PermissionRequested event. + /// - **macOS / iOS**: Fully supported via WKUIDelegate's requestMediaCapturePermission. + /// - **Linux**: Fully supported via WebKitGTK's permission-request signal. + /// - **Android**: Supported via JNI bridge for geolocation, microphone, camera, + /// protected media, and MIDI requests. Android runtime permissions may still + /// trigger native OS prompts before access is granted. + /// + /// # Examples + /// + /// ```rust,no_run + /// use tauri::{ + /// webview::{PermissionKind, PermissionResponse, WebviewWindowBuilder}, + /// WebviewUrl, + /// }; + /// tauri::Builder::default() + /// .setup(|app| { + /// WebviewWindowBuilder::new(app, "core", WebviewUrl::App("index.html".into())) + /// .on_permission_request(|_, kind| match kind { + /// PermissionKind::Geolocation => PermissionResponse::Allow, + /// PermissionKind::Notifications => PermissionResponse::Allow, + /// _ => PermissionResponse::Default, + /// }) + /// .build()?; + /// Ok(()) + /// }); + /// ``` + pub fn on_permission_request< + F: Fn(Webview, crate::webview::PermissionKind) -> crate::webview::PermissionResponse + + Send + + Sync + + 'static, + >( + mut self, + f: F, + ) -> Self { + self.webview_builder = self.webview_builder.on_permission_request(f); + self + } + /// Creates a new window. pub fn build(self) -> crate::Result> { let (window, webview) = self.window_builder.with_webview(self.webview_builder)?; @@ -1021,6 +1072,8 @@ impl> WebviewWindowBuilder<'_, R, M> { /// /// ## Warning /// + /// Webview instances with different browser arguments must also have different [data directories](Self::data_directory). + /// /// By default wry passes `--disable-features=msWebOOUI,msPdfOOUI,msSmartScreenProtection` /// so if you use this method, you also need to disable these components by yourself if you want. #[must_use] diff --git a/crates/tests/acl/fixtures/snapshots/acl_tests__tests__file-explorer-remote.snap b/crates/tests/acl/fixtures/snapshots/acl_tests__tests__file-explorer-remote.snap index 50029e7bd55c..9eba3e347416 100644 --- a/crates/tests/acl/fixtures/snapshots/acl_tests__tests__file-explorer-remote.snap +++ b/crates/tests/acl/fixtures/snapshots/acl_tests__tests__file-explorer-remote.snap @@ -14,7 +14,7 @@ Resolved { pattern_string: "https", regexp: Ok( Regex( - "(?u)^https$", + "^https$", ), ), group_name_list: [], @@ -32,7 +32,7 @@ Resolved { pattern_string: "*", regexp: Ok( Regex( - "(?u)^(.*)$", + "^(.*)$", ), ), group_name_list: [ @@ -53,7 +53,7 @@ Resolved { pattern_string: "*", regexp: Ok( Regex( - "(?u)^(.*)$", + "^(.*)$", ), ), group_name_list: [ @@ -74,7 +74,7 @@ Resolved { pattern_string: "tauri.app", regexp: Ok( Regex( - "(?u)^tauri\\.app$", + "^tauri\\.app$", ), ), group_name_list: [], @@ -92,7 +92,7 @@ Resolved { pattern_string: "", regexp: Ok( Regex( - "(?u)^$", + "^$", ), ), group_name_list: [], @@ -110,7 +110,7 @@ Resolved { pattern_string: "*", regexp: Ok( Regex( - "(?u)^(.*)$", + "^(.*)$", ), ), group_name_list: [ @@ -131,7 +131,7 @@ Resolved { pattern_string: "*", regexp: Ok( Regex( - "(?u)^(.*)$", + "^(.*)$", ), ), group_name_list: [ @@ -152,7 +152,7 @@ Resolved { pattern_string: "*", regexp: Ok( Regex( - "(?u)^(.*)$", + "^(.*)$", ), ), group_name_list: [ @@ -206,7 +206,7 @@ Resolved { pattern_string: "https", regexp: Ok( Regex( - "(?u)^https$", + "^https$", ), ), group_name_list: [], @@ -224,7 +224,7 @@ Resolved { pattern_string: "*", regexp: Ok( Regex( - "(?u)^(.*)$", + "^(.*)$", ), ), group_name_list: [ @@ -245,7 +245,7 @@ Resolved { pattern_string: "*", regexp: Ok( Regex( - "(?u)^(.*)$", + "^(.*)$", ), ), group_name_list: [ @@ -266,7 +266,7 @@ Resolved { pattern_string: "tauri.app", regexp: Ok( Regex( - "(?u)^tauri\\.app$", + "^tauri\\.app$", ), ), group_name_list: [], @@ -284,7 +284,7 @@ Resolved { pattern_string: "", regexp: Ok( Regex( - "(?u)^$", + "^$", ), ), group_name_list: [], @@ -302,7 +302,7 @@ Resolved { pattern_string: "*", regexp: Ok( Regex( - "(?u)^(.*)$", + "^(.*)$", ), ), group_name_list: [ @@ -323,7 +323,7 @@ Resolved { pattern_string: "*", regexp: Ok( Regex( - "(?u)^(.*)$", + "^(.*)$", ), ), group_name_list: [ @@ -344,7 +344,7 @@ Resolved { pattern_string: "*", regexp: Ok( Regex( - "(?u)^(.*)$", + "^(.*)$", ), ), group_name_list: [ diff --git a/packages/api/package.json b/packages/api/package.json index b15baefd788c..52a0e0aca870 100644 --- a/packages/api/package.json +++ b/packages/api/package.json @@ -57,7 +57,7 @@ "globals": "^17.4.0", "rollup": "4.62.2", "tslib": "^2.8.1", - "typescript": "^6.0.0", + "typescript": "^6.0.2", "typescript-eslint": "^8.58.2" } } diff --git a/packages/cli/package.json b/packages/cli/package.json index c54837a13f6e..a78611cb2783 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -44,7 +44,7 @@ "@napi-rs/cli": "3.7.2", "@types/node": "^24.11.0", "cross-env": "10.1.0", - "vitest": "^4.0.18" + "vitest": "^4.1.0" }, "engines": { "node": ">= 10" diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 4cb006f84968..9eacb4bdd177 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -10,6 +10,7 @@ overrides: js-yaml@>=4.0.0 <4.3.0: 4.3.0 postcss@>=8 <8.5.23: 8.5.23 sharp@<0.35.0: 0.35.3 + undici@>=7.0.0 <7.29.0: ^7.29.0 ws@>=8.0.0 <8.20.1: '>=8.20.1' importers: @@ -23,8 +24,8 @@ importers: crates/tauri-schema-worker: devDependencies: wrangler: - specifier: ^4.103.0 - version: 4.103.0 + specifier: ^4.114.0 + version: 4.114.0 examples/api: dependencies: @@ -103,7 +104,7 @@ importers: specifier: ^2.8.1 version: 2.8.1 typescript: - specifier: ^6.0.0 + specifier: ^6.0.2 version: 6.0.2 typescript-eslint: specifier: ^8.58.2 @@ -121,7 +122,7 @@ importers: specifier: 10.1.0 version: 10.1.0 vitest: - specifier: ^4.0.18 + specifier: ^4.1.0 version: 4.1.0(@types/node@24.11.0)(vite@8.1.4(@types/node@24.11.0)(esbuild@0.28.1)(jiti@2.6.1)(terser@5.46.0)) packages: @@ -142,32 +143,32 @@ packages: workerd: optional: true - '@cloudflare/workerd-darwin-64@1.20260617.1': - resolution: {integrity: sha512-jWwmgEVVWbsHNrLSNXzwjJaH90VzRxq1cWkQFUidxyeUPnMxemeNE8I9qFAfrpzGgE11e9sKDcE3ettJW08swQ==} + '@cloudflare/workerd-darwin-64@1.20260722.1': + resolution: {integrity: sha512-vZOP8vIS3NwnuaO+gz0FZ7kIGeiO3bZmxV35Ph9zOXKSREhDFlH7wQ7mkCdhW3O4jnXsew+XT7b+DNEI2CcJGQ==} engines: {node: '>=16'} cpu: [x64] os: [darwin] - '@cloudflare/workerd-darwin-arm64@1.20260617.1': - resolution: {integrity: sha512-LHH7b565g9znfCUOkwbec6FG2rmRbsgCy6aJiU9KN662mNheWl5sw/iKleiFSiljPKQQP3HkjnC/NSkdgi/aSA==} + '@cloudflare/workerd-darwin-arm64@1.20260722.1': + resolution: {integrity: sha512-EmIQymihDq6WNdER4+LF8Qn80yqayBUpJ+tkOO7wmY8pmgfyXjIUFNXotl21AHovTeu2seR7HdVUgeN/BilCWw==} engines: {node: '>=16'} cpu: [arm64] os: [darwin] - '@cloudflare/workerd-linux-64@1.20260617.1': - resolution: {integrity: sha512-FMnaAKXe4Cfd8TQurCVd9fs2XQVBFRCsP+Id/SRdUv89MlwYu9zXfoyx6BxM+brPTIUK38SHbo8iaxiwzLi9JQ==} + '@cloudflare/workerd-linux-64@1.20260722.1': + resolution: {integrity: sha512-jvZ3k9fxcnEn04s80CgIYxQfpOyAiz/8qC42DP8EBa9tR27qWyg9wmm31zIobVlrgBZn/+8NfdP73avRGcQOjQ==} engines: {node: '>=16'} cpu: [x64] os: [linux] - '@cloudflare/workerd-linux-arm64@1.20260617.1': - resolution: {integrity: sha512-MRoifFYcqbxxIIQy7PqO5tFY/qPFSnjXzakWl0sO93l+HLyG35jRAgOi6jfqa4kBxc7gKKtH861DcewjxUfkjA==} + '@cloudflare/workerd-linux-arm64@1.20260722.1': + resolution: {integrity: sha512-BOSB55SMNdy+DA5uj2WirgiNanpHGis5PVvXH1wSfvjRKr4JGgWK+EZzxz0RFUo6QjjQQC/NimEzNZ7va7jmKg==} engines: {node: '>=16'} cpu: [arm64] os: [linux] - '@cloudflare/workerd-windows-64@1.20260617.1': - resolution: {integrity: sha512-rgBV9wQrv0OSKgCTTbhFUFY3sLGNANZ88aqaLvtmEn2gmbFVb1J4PDGochVUdB7NSEp4D/ghHva6/8SZmbONpw==} + '@cloudflare/workerd-windows-64@1.20260722.1': + resolution: {integrity: sha512-sYM8YgUpKnRz2xjvdJLX1Ojzoi4MlA4gk8WTTExhGydjYB2UTs5NIbv0ZmpKgMoK9io3ixgmiW56ZnTbcWOdiA==} engines: {node: '>=16'} cpu: [x64] os: [win32] @@ -424,160 +425,160 @@ packages: resolution: {integrity: sha512-Td76q7j57o/tLVdgS746cYARfSyxk8iEfRxewL9h4OMzYhbW4TAcppl0mT4eyqXddh6L/jwoM75mo7ixa/pCeQ==} engines: {node: '>=18'} - '@img/sharp-darwin-arm64@0.35.3': - resolution: {integrity: sha512-RMnFX7YQsMoh7lWfcM4NEHHymBX/rLuKNPVM84XE9ONPcaSCDgE7CHIHpSgPcO2xcRthgBy1HfNO319mwhIAkg==} + '@img/sharp-darwin-arm64@0.35.2': + resolution: {integrity: sha512-eEieHsMksAW4IiO5NzauESRl2D2qz3J/kwUxUrSfV06A93eEaRfMpHXyUb1mAqrR7i8U9A0GRqE9pjn6u1Jjpg==} engines: {node: '>=20.9.0'} cpu: [arm64] os: [darwin] - '@img/sharp-darwin-x64@0.35.3': - resolution: {integrity: sha512-Xo+5uFBtLN0BKqieTxiFzFPQAUlBbbH5iBKyRX/z1JrbnYsHTfKJnUfL8+p2TPXr1pXqao4eeL4Rl144uDpK9w==} + '@img/sharp-darwin-x64@0.35.2': + resolution: {integrity: sha512-BaktuGPCeHJMARpodR8jK4uKiZrPAy9WrfQW0sdI37clracq8Bp01AYS3SZgi5FS/y5twa9t4+LIuuxQjqRrWw==} engines: {node: '>=20.9.0'} cpu: [x64] os: [darwin] - '@img/sharp-freebsd-wasm32@0.35.3': - resolution: {integrity: sha512-lUxcqWIj2wMQ9BrwNjngcr1gWUr5xgaGThBRqPPalIC2n67Cqj1uPh8NnA/ZhAg8hUbKl+kVHKwgUIwe6ZYPrg==} + '@img/sharp-freebsd-wasm32@0.35.2': + resolution: {integrity: sha512-YoAxdnd8hPUkvLHd3bWY+YA8nw3xM/RyRopYucNsWHVSan8NLVM3X2volsfoRDcXdUJPg6tXahSd7HXPK7lRnw==} engines: {node: '>=20.9.0'} os: [freebsd] - '@img/sharp-libvips-darwin-arm64@1.3.2': - resolution: {integrity: sha512-9J6ypZFpQBj4YnePGoq/S38w6nz+vqg5WZLrLGY4YuSemdMq47GMLBPO42MzwdGwpg/agZ7xzZcFHa48xlywfg==} + '@img/sharp-libvips-darwin-arm64@1.3.1': + resolution: {integrity: sha512-4V/M3roRMTYjiwZY9IOVQOE8OyeCxFAkYmyZDrZl51uOKjibm3oeEJ4WAmLxutAfzFbC9jqUiPs2gbnGflH+7g==} cpu: [arm64] os: [darwin] - '@img/sharp-libvips-darwin-x64@1.3.2': - resolution: {integrity: sha512-m2pW1n6cns9VaubNwsZ+c3CRYjxNQWgJ5gPlnL1nbBcpkBvFm6SCFN5o0psFHI8w9n11NKhFkeEDns98tiqbEw==} + '@img/sharp-libvips-darwin-x64@1.3.1': + resolution: {integrity: sha512-c0/DxItpJv2+dGhgycJBBgotdqruGYDvA79drdh0MD1dFpy7JzJ/PlXwi1H4rFf0eTy8tgbI91aHDnZIceY3jQ==} cpu: [x64] os: [darwin] - '@img/sharp-libvips-linux-arm64@1.3.2': - resolution: {integrity: sha512-dqVSFynCox4C/J8kT16V7SIFAns0IjgLwkvYT7p8LQVmJ5OS5b6tI9IGflxTeuBS//zXeFIUbwt5dwxyZ17cnA==} + '@img/sharp-libvips-linux-arm64@1.3.1': + resolution: {integrity: sha512-JznefmcK9j1JKPz8AkQDh89kjojubyfOasWBPKfzMIhPwsgDy9evpE/naJTXXXmghS1iFwR8u/kTwh/I2/+GCw==} cpu: [arm64] os: [linux] libc: [glibc] - '@img/sharp-libvips-linux-arm@1.3.2': - resolution: {integrity: sha512-1eMLzy92I4J6rmi4mAT8yC3HxOtniyGELlzGbNMLLeqe052ahFQ0h6LFq+lh5DsDIdYViIDst08abvSbcEdLXQ==} + '@img/sharp-libvips-linux-arm@1.3.1': + resolution: {integrity: sha512-aGGy9aWzXgHBG7HNyQPWorZthlp7+x6fDRoPAQbGO3ThcttuTyKIx3NuSHb6zb4gBNq6/yNn9f1cy9nFKS/Vmg==} cpu: [arm] os: [linux] libc: [glibc] - '@img/sharp-libvips-linux-ppc64@1.3.2': - resolution: {integrity: sha512-3z0NHDxD6n5I9gc05U1eW1AyRm+Gznzq3naMrthPNqE6oYykcogW0l/jfpJdjYnuNl8R7yI9pNbE1XiUeyq0Aw==} + '@img/sharp-libvips-linux-ppc64@1.3.1': + resolution: {integrity: sha512-1EkwGNCZk6iWNCMWqrvdJ+r1j0PT1zIz60CNPhYnJlK/zyeWqlsPZIe+ocBVqPF8k/Ssee/NCk+tE9Ryrko6ng==} cpu: [ppc64] os: [linux] libc: [glibc] - '@img/sharp-libvips-linux-riscv64@1.3.2': - resolution: {integrity: sha512-bsb4rI+NldGOsXuej2r8OdSS8+zXDVaCWxyWrcv6kneTOlgAHtZABRzBBCwdsPiD90J4myNJuHpg6kA20ImW/w==} + '@img/sharp-libvips-linux-riscv64@1.3.1': + resolution: {integrity: sha512-Ilays+w2bXdnxzxtQdmXR62u8o8GYa3eL4+Gr+1KiE4xperMZUslRaVPJwwPkzlHEjGfXAfRVAa/7CYCtSqsBw==} cpu: [riscv64] os: [linux] libc: [glibc] - '@img/sharp-libvips-linux-s390x@1.3.2': - resolution: {integrity: sha512-/ABshyj8gCpyIrNXnHn4LorDJ0HHm1VhXPBlxZ8zAtfVPAaSafXPGn+sUSIRiwaSBy0mmFjSjiXI5mkcwdChKQ==} + '@img/sharp-libvips-linux-s390x@1.3.1': + resolution: {integrity: sha512-VfBwVHQTbRoj4XlpA/KLZ7ltgMpz+4WSejFzQ+GnoImjo1PtEJ59QB2qR1xQEeRPYIkNrPIm2L4cICMvz4C2ew==} cpu: [s390x] os: [linux] libc: [glibc] - '@img/sharp-libvips-linux-x64@1.3.2': - resolution: {integrity: sha512-ITPEtgffGJ0S6G9dRyw/366tJQqFRcHWPHhC+Stpg3Z8AEMrDrTr2lhdz4f/Y/HMbRh//7Z5mBzEpVdi62Oc3w==} + '@img/sharp-libvips-linux-x64@1.3.1': + resolution: {integrity: sha512-+c8ukgwU62DS54nCAjw7keOfHUkmr0B5QHEdcOqRnodF/MNXJbVI8Eopoj4B/0H8Asr65I+A4Amrn7a85/md6A==} cpu: [x64] os: [linux] libc: [glibc] - '@img/sharp-libvips-linuxmusl-arm64@1.3.2': - resolution: {integrity: sha512-zE9EdiUzUmg5mDT5a1rk5fYJ6GWPloTwWBYDS14naqHsL+EaMpDj1AWnpLgh3u0YCORv2Tt50wrcrpYqkP97Kw==} + '@img/sharp-libvips-linuxmusl-arm64@1.3.1': + resolution: {integrity: sha512-qlKb/pwbkAi1WMsJrYHk7CuDrd12s27U2QnRhFYUoJNrRCmkosMTttuRFat/DDB3IlDm5qE1TJgZ4JDnHX8Ldw==} cpu: [arm64] os: [linux] libc: [musl] - '@img/sharp-libvips-linuxmusl-x64@1.3.2': - resolution: {integrity: sha512-m0lrLiUt+lBYnCFr8qV/65yMR4E/c7/wf78I5eKTdkEakFAlZ9QlzEM3QIhhAwVeUhLAHLcCq7a7Vszq/oFNZQ==} + '@img/sharp-libvips-linuxmusl-x64@1.3.1': + resolution: {integrity: sha512-yO21HwoUVLN8Qa+/SBjQLMYwBWAVJjeGPNe+hc0OUeMeifEtJqu5a1c4HayE1nNpDih9y3/KkoltfkDodmKAlg==} cpu: [x64] os: [linux] libc: [musl] - '@img/sharp-linux-arm64@0.35.3': - resolution: {integrity: sha512-QgKDspHPnrU+GQ55XPhGwyhC8acLVOOSyAvo1oVfFmrIXLkDNmGWzAfDZ4xK8oSA1qBQrALcHX0G5UZni/SuFQ==} + '@img/sharp-linux-arm64@0.35.2': + resolution: {integrity: sha512-af12Pnd0ZGu2HfP8NayB0kk6eC/lrfbQE6HlR4jD+34wdJ1Vw9TF6TMn6ZvffT+WgqVsl0hRbmNvz2u/23VmwA==} engines: {node: '>=20.9.0'} cpu: [arm64] os: [linux] libc: [glibc] - '@img/sharp-linux-arm@0.35.3': - resolution: {integrity: sha512-affVWCTLooy8TSxbDx2qkzuDeaWLNVBA+P//FNBirHsXpP2fuBhk5AuboYUnrDnzoXes8GFjpTx0SBFOCRg+FA==} + '@img/sharp-linux-arm@0.35.2': + resolution: {integrity: sha512-SE4kzF2mepn6z+6E7L6lsV8FzuLL6IPQdyX8ZiwROAG/G8td+hP/m7FsFPwidtrF19gvajuC9l6TxAVcsA4S7A==} engines: {node: '>=20.9.0'} cpu: [arm] os: [linux] libc: [glibc] - '@img/sharp-linux-ppc64@0.35.3': - resolution: {integrity: sha512-sMd8rDxmpLOwv/7N44klFjOD5DUO7FLdjiXDI0hoxYaf7Ar262dQIEkosE98bps+5HPLtp/EvNqeqQtOycP/IA==} + '@img/sharp-linux-ppc64@0.35.2': + resolution: {integrity: sha512-hYSBm7zcNtDCozCxQHYZJiu63b/bXsgRZuOxCIBZsStMM9Vap47iFHdbX4kCvQsblPB/k+clhELpdQJHQLSHvg==} engines: {node: '>=20.9.0'} cpu: [ppc64] os: [linux] libc: [glibc] - '@img/sharp-linux-riscv64@0.35.3': - resolution: {integrity: sha512-0Eob78yjlYPfL5vMNWAW55l3R9Y6BQS/gOfe0ZcP9mEz9ohhKSt4im1hayiknXgf8AWrFqMvJcKIdmLmEe7yeQ==} + '@img/sharp-linux-riscv64@0.35.2': + resolution: {integrity: sha512-qQt0Kc13+Hoan/Awq/qMSQw3L+RI1NCRPgD5cUJ/1WSSmIoysLOc72jlRM3E0OHN9Yr313jgeQ2T+zW+F03QFA==} engines: {node: '>=20.9.0'} cpu: [riscv64] os: [linux] libc: [glibc] - '@img/sharp-linux-s390x@0.35.3': - resolution: {integrity: sha512-KgAxQ0DxpNOq1rG2t5cgTgShJFGSuU7XO45cqC+1NVOuZnP6tlgZRuSYOfNupGkHID0o3cJOsw4DVeJpMovcGw==} + '@img/sharp-linux-s390x@0.35.2': + resolution: {integrity: sha512-E4fLLfRPzDLlEeDaTzI98OFLcv++WL5ChLLMwPoVd0CIoZQqupBSNbOisPL5am9XsbQ9T84+iiMpUvbFtkunbA==} engines: {node: '>=20.9.0'} cpu: [s390x] os: [linux] libc: [glibc] - '@img/sharp-linux-x64@0.35.3': - resolution: {integrity: sha512-8pqvxubL2PGdhlPy6GLqzDYMUjyRmKAwKHYKixpdJYBUK7PJ0C029XdsnpFIdgRZG68fZiGdHVWcKPvtiPB4cA==} + '@img/sharp-linux-x64@0.35.2': + resolution: {integrity: sha512-gi0zFJJRLswfCZmHtJdikXPOc5u7qamSOS3NHedLqLd4W8Q0NqjdBr6TTRIgsfFjqfTsHFgdfvJ9LwqSgcHiAA==} engines: {node: '>=20.9.0'} cpu: [x64] os: [linux] libc: [glibc] - '@img/sharp-linuxmusl-arm64@0.35.3': - resolution: {integrity: sha512-Vz0iQjzzcSX3HCbfwFfCSG/9SCIqyO0mH2sXyiHaAYfBk0cRsCWXRyQYX0ovCK/PAQBbTzQ0dsPQHh5MAFL59w==} + '@img/sharp-linuxmusl-arm64@0.35.2': + resolution: {integrity: sha512-siWbOW1u6HFnFLrp0waKyW7VEf7jYvcDWdrXEFa8AkdAQgEvuu5Fz8/Y70w9EeqAdwDtfU012BhEHHaDqvQNzg==} engines: {node: '>=20.9.0'} cpu: [arm64] os: [linux] libc: [musl] - '@img/sharp-linuxmusl-x64@0.35.3': - resolution: {integrity: sha512-6O1NPKcDVj9QEdg7Hx549EX8U0rp6yXQERqru6yRN7fGBn32UvIRJUlWnk+8xDCiG76hXVBbX82NZ/ZKr0euIg==} + '@img/sharp-linuxmusl-x64@0.35.2': + resolution: {integrity: sha512-YBqMMcjDi4QGYiSn4vNOYBhmlC4z5AXqkOUUqI2e0AFA4urNv4ESgOgwNl3K+4etQhha0twXlzeF20bbULm9Yg==} engines: {node: '>=20.9.0'} cpu: [x64] os: [linux] libc: [musl] - '@img/sharp-wasm32@0.35.3': - resolution: {integrity: sha512-cZ0XkcYGpHZkqW6iCkqTcmUC0CD9DhD5d/qeZlZkfRBn6GnHniZXLUo5+9xw8Iv76YE6LQFN9YNBlKREcCG76w==} + '@img/sharp-wasm32@0.35.2': + resolution: {integrity: sha512-Mrv4JQNYVQ94xH+jzZ9r+gowleN8mv2FTgKT+PI6bx5C0G8TdNYndu161pg2i7uoBwxy2ImPMHrJOM2LZef7Bw==} engines: {node: '>=20.9.0'} - '@img/sharp-webcontainers-wasm32@0.35.3': - resolution: {integrity: sha512-2rnq7bX3NzeR2T4YWgz8qiG4h3TSdMe+vN1iQXpJleSJ3SM5zQ8Fy2SyyXAWlbxpEZ2Y+Z4u1BePgJEYbSy80Q==} + '@img/sharp-webcontainers-wasm32@0.35.2': + resolution: {integrity: sha512-QNV27pxs9wpApEiCfvHM1RDoP1w1+2KrUWWDPEhEwg+latvOrfuhWrHWZKwdSFwU6jh3myjw/yOCRsUIuOft3g==} engines: {node: '>=20.9.0'} cpu: [wasm32] - '@img/sharp-win32-arm64@0.35.3': - resolution: {integrity: sha512-4bPwFdMbeC4JQ8L8LOyWp6nsHcboP5fxkp6iPOXz2Vg49R42TuMs2whkJ5OAP4/Ul035qOzy0AecOF9VOscn4w==} + '@img/sharp-win32-arm64@0.35.2': + resolution: {integrity: sha512-BiVRYc/t6/Vl3e1hBx0hugG4oN9Pydf4fgMSpxTQJmwGUg/YoXTWHiFeRymHfCZzifxu4F4rpk/I67D0LQ20wQ==} engines: {node: '>=20.9.0'} cpu: [arm64] os: [win32] - '@img/sharp-win32-ia32@0.35.3': - resolution: {integrity: sha512-r53mXsBN6lFUDiST764SvgwUdHAqM4rPAiDzAmf4fLoB6X/rkfyTrLCg6+g17wJJiCmB3JYgHuUldCWUIRFSXw==} + '@img/sharp-win32-ia32@0.35.2': + resolution: {integrity: sha512-YYEhx9PImCC7T0tI8JDMi4DB9LwLCXCU5OWNYEXAxh5Q1ShKkyC6byxzoBJ3gEFDnH2lQckWuDe70G7mB2XJog==} engines: {node: ^20.9.0} cpu: [ia32] os: [win32] - '@img/sharp-win32-x64@0.35.3': - resolution: {integrity: sha512-D4y1vNeZrIIJCN+uHaWVtH86B+aCrdMYYjicy9pXHvbGZeGYLLSd3wdVuC37FxVXlU1ARsk84eKWfWMXGYEqvA==} + '@img/sharp-win32-x64@0.35.2': + resolution: {integrity: sha512-imoOyBcoM/iiUr4J6VPpCNjPnjvP/Gks95898yB8YqoGGYmHYbOyCuNv9FMhFgtaiHFGbHW8bxKqRV6VjtXThQ==} engines: {node: '>=20.9.0'} cpu: [x64] os: [win32] @@ -2283,8 +2284,8 @@ packages: resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==} engines: {node: '>=8.6'} - miniflare@4.20260617.1: - resolution: {integrity: sha512-Go3/gzStm99QHptsSgU+q1S+xDfLoRgwjJNY80kaTVi0ENhTyqKq+sc4xZiWBSbM7uUcJwmzm8+QFKtcYLJ9nw==} + miniflare@4.20260722.0: + resolution: {integrity: sha512-LW6ABMhCx/yIEFBLC/DO4yAhdm2T/G7jp7pr5T2kj895+CCIaHZqpMXdW9O6YE48LcYcCJChwWc8aEs1vpbTXw==} engines: {node: '>=22.0.0'} hasBin: true @@ -2454,14 +2455,9 @@ packages: resolution: {integrity: sha512-F4LcB0UqUl1zErq+1nYEEzSHJnIwb3AF2XWB94b+afhrekOUijwooAYqFyRbjYkm2PAKBabx6oYv/xDxNi8IBw==} engines: {node: '>=20.0.0'} - sharp@0.35.3: - resolution: {integrity: sha512-ej0zVHuZGHCiABXcNxeYhpRnPNPAcvbG8RMdBAhDAxLKkCRVSpK3Iyu7qbqw3JMzoj0REeM6f3tJLtVwl0023Q==} + sharp@0.35.2: + resolution: {integrity: sha512-FVtFjtBCMiJS6yb5CX7Sop45WFMpeGw6oRKuJnXYgf/f1ms/D7LE/ZUSNxnW7rZ/dbslQWYkoqFHGPaDBtaK4w==} engines: {node: '>=20.9.0'} - peerDependencies: - '@types/node': '*' - peerDependenciesMeta: - '@types/node': - optional: true shebang-command@2.0.0: resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} @@ -2589,8 +2585,8 @@ packages: undici-types@7.16.0: resolution: {integrity: sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==} - undici@7.28.0: - resolution: {integrity: sha512-cRZYrTDwWznlnRiPjggAGxZXanty6M8RV1ff8Wm4LWXBp7/IG8v5DnOm74DtUBp9OONpK75YlPnIjQqX0dBDtA==} + undici@7.29.0: + resolution: {integrity: sha512-IDxfleLmmbSskfWSUATiN1nfn2rDuvnMOqb5CWR92iIfojA0Ud+ulOAAEQ57LPr9rWmsreUyf5lwyao+7GNNVw==} engines: {node: '>=20.18.1'} unenv@2.0.0-rc.24: @@ -2727,17 +2723,17 @@ packages: resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} engines: {node: '>=0.10.0'} - workerd@1.20260617.1: - resolution: {integrity: sha512-Re5pl6pdowt3ZmWUzGlOuB7jbRIIPetgKalmo4cYmucQnVhpo7/3e4MfpekbhLi2EhZZz5EY9NWRu8zFzuEZew==} + workerd@1.20260722.1: + resolution: {integrity: sha512-NycKuc1x2onvsRfGGpM093vRlLFU2zHDAM0+APpccfg4+gZxDGCH27RmdDvkeBuoZyYqgLo3oAfF6re4mvC3vQ==} engines: {node: '>=16'} hasBin: true - wrangler@4.103.0: - resolution: {integrity: sha512-3Lv1P5t2xcSEkSTKtG+Lz+3JFryuU7YPLkaCUj7gNe+CJsjZJLtUwqsh1x595QBxkIbCE0GAvDx2DCJUU4+oqw==} + wrangler@4.114.0: + resolution: {integrity: sha512-M65P25t5UHA1TIJfgZXDcj+YzVobgKdRguM2QPz0xnxLFuOcuE3ErgllDht0iaho7MS4o0g/Bb4YK2+GT+bibg==} engines: {node: '>=22.0.0'} hasBin: true peerDependencies: - '@cloudflare/workers-types': ^4.20260617.1 + '@cloudflare/workers-types': ^5.20260722.1 peerDependenciesMeta: '@cloudflare/workers-types': optional: true @@ -2776,25 +2772,25 @@ snapshots: '@cloudflare/kv-asset-handler@0.5.0': {} - '@cloudflare/unenv-preset@2.16.1(unenv@2.0.0-rc.24)(workerd@1.20260617.1)': + '@cloudflare/unenv-preset@2.16.1(unenv@2.0.0-rc.24)(workerd@1.20260722.1)': dependencies: unenv: 2.0.0-rc.24 optionalDependencies: - workerd: 1.20260617.1 + workerd: 1.20260722.1 - '@cloudflare/workerd-darwin-64@1.20260617.1': + '@cloudflare/workerd-darwin-64@1.20260722.1': optional: true - '@cloudflare/workerd-darwin-arm64@1.20260617.1': + '@cloudflare/workerd-darwin-arm64@1.20260722.1': optional: true - '@cloudflare/workerd-linux-64@1.20260617.1': + '@cloudflare/workerd-linux-64@1.20260722.1': optional: true - '@cloudflare/workerd-linux-arm64@1.20260617.1': + '@cloudflare/workerd-linux-arm64@1.20260722.1': optional: true - '@cloudflare/workerd-windows-64@1.20260617.1': + '@cloudflare/workerd-windows-64@1.20260722.1': optional: true '@cspotcode/source-map-support@0.8.1': @@ -2976,108 +2972,108 @@ snapshots: '@img/colour@1.1.0': {} - '@img/sharp-darwin-arm64@0.35.3': + '@img/sharp-darwin-arm64@0.35.2': optionalDependencies: - '@img/sharp-libvips-darwin-arm64': 1.3.2 + '@img/sharp-libvips-darwin-arm64': 1.3.1 optional: true - '@img/sharp-darwin-x64@0.35.3': + '@img/sharp-darwin-x64@0.35.2': optionalDependencies: - '@img/sharp-libvips-darwin-x64': 1.3.2 + '@img/sharp-libvips-darwin-x64': 1.3.1 optional: true - '@img/sharp-freebsd-wasm32@0.35.3': + '@img/sharp-freebsd-wasm32@0.35.2': dependencies: - '@img/sharp-wasm32': 0.35.3 + '@img/sharp-wasm32': 0.35.2 optional: true - '@img/sharp-libvips-darwin-arm64@1.3.2': + '@img/sharp-libvips-darwin-arm64@1.3.1': optional: true - '@img/sharp-libvips-darwin-x64@1.3.2': + '@img/sharp-libvips-darwin-x64@1.3.1': optional: true - '@img/sharp-libvips-linux-arm64@1.3.2': + '@img/sharp-libvips-linux-arm64@1.3.1': optional: true - '@img/sharp-libvips-linux-arm@1.3.2': + '@img/sharp-libvips-linux-arm@1.3.1': optional: true - '@img/sharp-libvips-linux-ppc64@1.3.2': + '@img/sharp-libvips-linux-ppc64@1.3.1': optional: true - '@img/sharp-libvips-linux-riscv64@1.3.2': + '@img/sharp-libvips-linux-riscv64@1.3.1': optional: true - '@img/sharp-libvips-linux-s390x@1.3.2': + '@img/sharp-libvips-linux-s390x@1.3.1': optional: true - '@img/sharp-libvips-linux-x64@1.3.2': + '@img/sharp-libvips-linux-x64@1.3.1': optional: true - '@img/sharp-libvips-linuxmusl-arm64@1.3.2': + '@img/sharp-libvips-linuxmusl-arm64@1.3.1': optional: true - '@img/sharp-libvips-linuxmusl-x64@1.3.2': + '@img/sharp-libvips-linuxmusl-x64@1.3.1': optional: true - '@img/sharp-linux-arm64@0.35.3': + '@img/sharp-linux-arm64@0.35.2': optionalDependencies: - '@img/sharp-libvips-linux-arm64': 1.3.2 + '@img/sharp-libvips-linux-arm64': 1.3.1 optional: true - '@img/sharp-linux-arm@0.35.3': + '@img/sharp-linux-arm@0.35.2': optionalDependencies: - '@img/sharp-libvips-linux-arm': 1.3.2 + '@img/sharp-libvips-linux-arm': 1.3.1 optional: true - '@img/sharp-linux-ppc64@0.35.3': + '@img/sharp-linux-ppc64@0.35.2': optionalDependencies: - '@img/sharp-libvips-linux-ppc64': 1.3.2 + '@img/sharp-libvips-linux-ppc64': 1.3.1 optional: true - '@img/sharp-linux-riscv64@0.35.3': + '@img/sharp-linux-riscv64@0.35.2': optionalDependencies: - '@img/sharp-libvips-linux-riscv64': 1.3.2 + '@img/sharp-libvips-linux-riscv64': 1.3.1 optional: true - '@img/sharp-linux-s390x@0.35.3': + '@img/sharp-linux-s390x@0.35.2': optionalDependencies: - '@img/sharp-libvips-linux-s390x': 1.3.2 + '@img/sharp-libvips-linux-s390x': 1.3.1 optional: true - '@img/sharp-linux-x64@0.35.3': + '@img/sharp-linux-x64@0.35.2': optionalDependencies: - '@img/sharp-libvips-linux-x64': 1.3.2 + '@img/sharp-libvips-linux-x64': 1.3.1 optional: true - '@img/sharp-linuxmusl-arm64@0.35.3': + '@img/sharp-linuxmusl-arm64@0.35.2': optionalDependencies: - '@img/sharp-libvips-linuxmusl-arm64': 1.3.2 + '@img/sharp-libvips-linuxmusl-arm64': 1.3.1 optional: true - '@img/sharp-linuxmusl-x64@0.35.3': + '@img/sharp-linuxmusl-x64@0.35.2': optionalDependencies: - '@img/sharp-libvips-linuxmusl-x64': 1.3.2 + '@img/sharp-libvips-linuxmusl-x64': 1.3.1 optional: true - '@img/sharp-wasm32@0.35.3': + '@img/sharp-wasm32@0.35.2': dependencies: '@emnapi/runtime': 1.11.1 optional: true - '@img/sharp-webcontainers-wasm32@0.35.3': + '@img/sharp-webcontainers-wasm32@0.35.2': dependencies: - '@img/sharp-wasm32': 0.35.3 + '@img/sharp-wasm32': 0.35.2 optional: true - '@img/sharp-win32-arm64@0.35.3': + '@img/sharp-win32-arm64@0.35.2': optional: true - '@img/sharp-win32-ia32@0.35.3': + '@img/sharp-win32-ia32@0.35.2': optional: true - '@img/sharp-win32-x64@0.35.3': + '@img/sharp-win32-x64@0.35.2': optional: true '@inquirer/ansi@2.0.7': {} @@ -4578,16 +4574,15 @@ snapshots: braces: 3.0.3 picomatch: 2.3.2 - miniflare@4.20260617.1: + miniflare@4.20260722.0: dependencies: '@cspotcode/source-map-support': 0.8.1 - sharp: 0.35.3 - undici: 7.28.0 - workerd: 1.20260617.1 + sharp: 0.35.2 + undici: 7.29.0 + workerd: 1.20260722.1 ws: 8.21.0 youch: 4.1.0-beta.10 transitivePeerDependencies: - - '@types/node' - bufferutil - utf-8-validate @@ -4791,37 +4786,37 @@ snapshots: serialize-javascript@7.0.5: {} - sharp@0.35.3: + sharp@0.35.2: dependencies: '@img/colour': 1.1.0 detect-libc: 2.1.2 semver: 7.8.5 optionalDependencies: - '@img/sharp-darwin-arm64': 0.35.3 - '@img/sharp-darwin-x64': 0.35.3 - '@img/sharp-freebsd-wasm32': 0.35.3 - '@img/sharp-libvips-darwin-arm64': 1.3.2 - '@img/sharp-libvips-darwin-x64': 1.3.2 - '@img/sharp-libvips-linux-arm': 1.3.2 - '@img/sharp-libvips-linux-arm64': 1.3.2 - '@img/sharp-libvips-linux-ppc64': 1.3.2 - '@img/sharp-libvips-linux-riscv64': 1.3.2 - '@img/sharp-libvips-linux-s390x': 1.3.2 - '@img/sharp-libvips-linux-x64': 1.3.2 - '@img/sharp-libvips-linuxmusl-arm64': 1.3.2 - '@img/sharp-libvips-linuxmusl-x64': 1.3.2 - '@img/sharp-linux-arm': 0.35.3 - '@img/sharp-linux-arm64': 0.35.3 - '@img/sharp-linux-ppc64': 0.35.3 - '@img/sharp-linux-riscv64': 0.35.3 - '@img/sharp-linux-s390x': 0.35.3 - '@img/sharp-linux-x64': 0.35.3 - '@img/sharp-linuxmusl-arm64': 0.35.3 - '@img/sharp-linuxmusl-x64': 0.35.3 - '@img/sharp-webcontainers-wasm32': 0.35.3 - '@img/sharp-win32-arm64': 0.35.3 - '@img/sharp-win32-ia32': 0.35.3 - '@img/sharp-win32-x64': 0.35.3 + '@img/sharp-darwin-arm64': 0.35.2 + '@img/sharp-darwin-x64': 0.35.2 + '@img/sharp-freebsd-wasm32': 0.35.2 + '@img/sharp-libvips-darwin-arm64': 1.3.1 + '@img/sharp-libvips-darwin-x64': 1.3.1 + '@img/sharp-libvips-linux-arm': 1.3.1 + '@img/sharp-libvips-linux-arm64': 1.3.1 + '@img/sharp-libvips-linux-ppc64': 1.3.1 + '@img/sharp-libvips-linux-riscv64': 1.3.1 + '@img/sharp-libvips-linux-s390x': 1.3.1 + '@img/sharp-libvips-linux-x64': 1.3.1 + '@img/sharp-libvips-linuxmusl-arm64': 1.3.1 + '@img/sharp-libvips-linuxmusl-x64': 1.3.1 + '@img/sharp-linux-arm': 0.35.2 + '@img/sharp-linux-arm64': 0.35.2 + '@img/sharp-linux-ppc64': 0.35.2 + '@img/sharp-linux-riscv64': 0.35.2 + '@img/sharp-linux-s390x': 0.35.2 + '@img/sharp-linux-x64': 0.35.2 + '@img/sharp-linuxmusl-arm64': 0.35.2 + '@img/sharp-linuxmusl-x64': 0.35.2 + '@img/sharp-webcontainers-wasm32': 0.35.2 + '@img/sharp-win32-arm64': 0.35.2 + '@img/sharp-win32-ia32': 0.35.2 + '@img/sharp-win32-x64': 0.35.2 shebang-command@2.0.0: dependencies: @@ -4949,7 +4944,7 @@ snapshots: undici-types@7.16.0: {} - undici@7.28.0: {} + undici@7.29.0: {} unenv@2.0.0-rc.24: dependencies: @@ -5053,28 +5048,27 @@ snapshots: word-wrap@1.2.5: {} - workerd@1.20260617.1: + workerd@1.20260722.1: optionalDependencies: - '@cloudflare/workerd-darwin-64': 1.20260617.1 - '@cloudflare/workerd-darwin-arm64': 1.20260617.1 - '@cloudflare/workerd-linux-64': 1.20260617.1 - '@cloudflare/workerd-linux-arm64': 1.20260617.1 - '@cloudflare/workerd-windows-64': 1.20260617.1 + '@cloudflare/workerd-darwin-64': 1.20260722.1 + '@cloudflare/workerd-darwin-arm64': 1.20260722.1 + '@cloudflare/workerd-linux-64': 1.20260722.1 + '@cloudflare/workerd-linux-arm64': 1.20260722.1 + '@cloudflare/workerd-windows-64': 1.20260722.1 - wrangler@4.103.0: + wrangler@4.114.0: dependencies: '@cloudflare/kv-asset-handler': 0.5.0 - '@cloudflare/unenv-preset': 2.16.1(unenv@2.0.0-rc.24)(workerd@1.20260617.1) + '@cloudflare/unenv-preset': 2.16.1(unenv@2.0.0-rc.24)(workerd@1.20260722.1) blake3-wasm: 2.1.5 esbuild: 0.28.1 - miniflare: 4.20260617.1 + miniflare: 4.20260722.0 path-to-regexp: 6.3.0 unenv: 2.0.0-rc.24 - workerd: 1.20260617.1 + workerd: 1.20260722.1 optionalDependencies: fsevents: 2.3.3 transitivePeerDependencies: - - '@types/node' - bufferutil - utf-8-validate diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 8f7754271075..909cea70b637 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -17,4 +17,5 @@ overrides: js-yaml@>=4.0.0 <4.3.0: 4.3.0 postcss@>=8 <8.5.23: 8.5.23 sharp@<0.35.0: 0.35.3 + undici@>=7.0.0 <7.29.0: ^7.29.0 ws@>=8.0.0 <8.20.1: '>=8.20.1' diff --git a/ports/tao/.cargo/audit.toml b/ports/tao/.cargo/audit.toml new file mode 100644 index 000000000000..d9b19e526b86 --- /dev/null +++ b/ports/tao/.cargo/audit.toml @@ -0,0 +1,7 @@ +[advisories] +ignore = [ + # quick-xml, `softbuffer > wayland-scanner > quick-xml`users are unaffected + "RUSTSEC-2026-0194", + # quick-xml, `softbuffer > wayland-scanner > quick-xml`users are unaffected + "RUSTSEC-2026-0195", +] diff --git a/ports/tao/.changes/change-pr-1173.md b/ports/tao/.changes/change-pr-1173.md deleted file mode 100644 index 10be46a8d5d3..000000000000 --- a/ports/tao/.changes/change-pr-1173.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -'tao': patch ---- - -`fn set_min_inner_size`, `fn set_max_inner_size`, `fn set_inner_size_constraints`, -`fn set_fullscreen` and `fn set_theme` on `Window` were not properly thread-safe -on Linux. diff --git a/ports/tao/.changes/dpi-leak-hdc-handle.md b/ports/tao/.changes/dpi-leak-hdc-handle.md deleted file mode 100644 index cc80da5e36c1..000000000000 --- a/ports/tao/.changes/dpi-leak-hdc-handle.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -tao: patch ---- - -Fix getting the DPI internally leaks `HDC` handles on Windows. Also only call `GetDC` when on < Windows 8.1 which improves its performance. diff --git a/ports/tao/.changes/fix-jis-key-mapping.md b/ports/tao/.changes/fix-jis-key-mapping.md deleted file mode 100644 index d96420c705bc..000000000000 --- a/ports/tao/.changes/fix-jis-key-mapping.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'tao': patch ---- - -fix(linux): map JIS keyboard-specific keys (`Zenkaku_Hankaku`, `Hiragana_Katakana`, `Henkan`, `Muhenkan`) in `raw_key_to_key` to prevent them from becoming `Key::Unidentified`. diff --git a/ports/tao/.changes/fix-wayland-csd.md b/ports/tao/.changes/fix-wayland-csd.md deleted file mode 100644 index a26310f7f341..000000000000 --- a/ports/tao/.changes/fix-wayland-csd.md +++ /dev/null @@ -1,10 +0,0 @@ ---- -'tao': patch ---- - -On Linux, multiple issues regarding window decoration handling for Wayland have been fixed -(#899, #1046, tauri-apps/tauri#6562, tauri-apps/tauri#13440, tauri-apps/tauri#13749, tauri-apps/tauri#14251, tauri-apps/tauri#14748). -Title bar buttons and changing of the title should now work as expected. -Furthermore, client-side decorations are no longer applied, when server-side decorations are supported. -SSD are no longer applied when decorations are disabled for a window during creation. -Toggling of SSD rendering for existing windows is however not supported at this time. diff --git a/ports/tao/.changes/ios-multiwindow-crash.md b/ports/tao/.changes/ios-multiwindow-crash.md deleted file mode 100644 index a77ff6e7d3c9..000000000000 --- a/ports/tao/.changes/ios-multiwindow-crash.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'tao': patch ---- - -Prevent use-after-free in iOS `configurationForConnectingSceneSession` that crashes release builds on launch. diff --git a/ports/tao/.changes/ios-windowing-control-style.md b/ports/tao/.changes/ios-windowing-control-style.md deleted file mode 100644 index d4552c986f4d..000000000000 --- a/ports/tao/.changes/ios-windowing-control-style.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'tao': patch ---- - -Fix iPadOS 26 system window controls overlapping `WKWebView` content by implementing `preferredWindowingControlStyleForScene:` on the scene delegate and returning the `minimal` style. The optional protocol method is a no-op on iOS versions earlier than 26. diff --git a/ports/tao/.changes/refactor-windows-keyboard.md b/ports/tao/.changes/refactor-windows-keyboard.md deleted file mode 100644 index 9f19eb83d27c..000000000000 --- a/ports/tao/.changes/refactor-windows-keyboard.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'tao': minor ---- - -Refactored some Windows keyboard internal implementations to catch up with winit, there should not be any behavior changes, please report if you find one diff --git a/ports/tao/.changes/taskbar-created-lock.md b/ports/tao/.changes/taskbar-created-lock.md deleted file mode 100644 index 688af74ed77b..000000000000 --- a/ports/tao/.changes/taskbar-created-lock.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'tao': patch ---- - -Release the window state lock before updating taskbar visibility on Windows to avoid a reentrant `TaskbarCreated` deadlock. diff --git a/ports/tao/.changes/windows-ime-event.md b/ports/tao/.changes/windows-ime-event.md deleted file mode 100644 index 25d533784c5a..000000000000 --- a/ports/tao/.changes/windows-ime-event.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'tao': minor ---- - -`WindowEvent::ReceivedImeText` event's text is now coming from `ImmGetCompositionStringW` instead of a recording `WM_CHAR` and `WM_SYSCHAR` messages diff --git a/ports/tao/.changes/windows-initial-background-color.md b/ports/tao/.changes/windows-initial-background-color.md deleted file mode 100644 index 2552a5674db3..000000000000 --- a/ports/tao/.changes/windows-initial-background-color.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'tao': patch ---- - -Fixed `with_background_color` doesn't work on initial load on Windows diff --git a/ports/tao/.changes/windows-input-deadlock.md b/ports/tao/.changes/windows-input-deadlock.md deleted file mode 100644 index 67cda0f11a56..000000000000 --- a/ports/tao/.changes/windows-input-deadlock.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'tao': patch ---- - -Avoid Windows keyboard and IME deadlocks caused by re-entrant message processing while input state locks are held. diff --git a/ports/tao/.changes/windows-remove-subclassing.md b/ports/tao/.changes/windows-remove-subclassing.md deleted file mode 100644 index 4ba56a3b9d60..000000000000 --- a/ports/tao/.changes/windows-remove-subclassing.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'tao': minor ---- - -Removed window subclassing on Windows diff --git a/ports/tao/.github/workflows/audit.yml b/ports/tao/.github/workflows/audit.yml index d977db33a147..5992a3e79903 100644 --- a/ports/tao/.github/workflows/audit.yml +++ b/ports/tao/.github/workflows/audit.yml @@ -17,7 +17,7 @@ jobs: audit-rust: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 - - uses: rustsec/audit-check@v1 + - uses: actions/checkout@v7 + - uses: rustsec/audit-check@v2 with: token: ${{ secrets.GITHUB_TOKEN }} diff --git a/ports/tao/.github/workflows/ci.yml b/ports/tao/.github/workflows/ci.yml index 1a6eb6853158..cf339b1feaa5 100644 --- a/ports/tao/.github/workflows/ci.yml +++ b/ports/tao/.github/workflows/ci.yml @@ -46,7 +46,7 @@ jobs: runs-on: ${{ matrix.platform.os }} steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@v7 - uses: dtolnay/rust-toolchain@stable @@ -123,7 +123,7 @@ jobs: test_tao_macros: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@v7 - uses: dtolnay/rust-toolchain@stable @@ -137,6 +137,6 @@ jobs: name: fmt check runs-on: ubuntu-latest steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@v7 - uses: dtolnay/rust-toolchain@stable - run: cargo fmt --all -- --check diff --git a/ports/tao/.github/workflows/covector-status.yml b/ports/tao/.github/workflows/covector-status.yml index 8f7060cbef10..ae439f05e93c 100644 --- a/ports/tao/.github/workflows/covector-status.yml +++ b/ports/tao/.github/workflows/covector-status.yml @@ -10,7 +10,7 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v7 with: fetch-depth: 0 - name: covector status diff --git a/ports/tao/.github/workflows/covector-version-or-publish.yml b/ports/tao/.github/workflows/covector-version-or-publish.yml index ad8f1eec73d3..b59830efe5d2 100644 --- a/ports/tao/.github/workflows/covector-version-or-publish.yml +++ b/ports/tao/.github/workflows/covector-version-or-publish.yml @@ -20,7 +20,7 @@ jobs: successfulPublish: ${{ steps.covector.outputs.successfulPublish }} steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v7 with: fetch-depth: 0 - name: cargo login @@ -47,7 +47,7 @@ jobs: - name: Create Pull Request With Versions Bumped id: cpr - uses: peter-evans/create-pull-request@dd2324fc52d5d43c699a5636bcf19fceaa70c284 # 7.0.7 + uses: peter-evans/create-pull-request@v8.1.1 if: steps.covector.outputs.commandRan == 'version' with: token: ${{ secrets.GITHUB_TOKEN }} diff --git a/ports/tao/CHANGELOG.md b/ports/tao/CHANGELOG.md index 407d76788f25..72a578a63f4a 100644 --- a/ports/tao/CHANGELOG.md +++ b/ports/tao/CHANGELOG.md @@ -1,5 +1,54 @@ # Changelog +## \[0.36.0] + +- [`2b818c4e`](https://github.com/tauri-apps/tao/commit/2b818c4e84288c42365f8c790f4e7a519f6376ec) ([#1288](https://github.com/tauri-apps/tao/pull/1288) by [@Legend-Master](https://github.com/tauri-apps/tao/../../Legend-Master)) **Breaking Change:** + + Changed the names of the Android JNI lifecycle functions exposed by `android_binding!` as follows: + + - `create` to `onFirstActivityCreate` + - `onActivityCreate` to `onCreate` + - `start` to `onStart` + - `resume` to `onResume` + - `pause` to `onPause` + - `stop` to `onStop` + - Removed `onActivitySaveInstanceState` + - `onActivityDestroy` to `onDestroy` + - `onActivityLowMemory` to `onLowMemory` + + `onLowMemory` no longer takes any parameters. + `onFirstActivityCreate` no longer takes any parameters. +- [`4d5005ff`](https://github.com/tauri-apps/tao/commit/4d5005ff484ec11a2909de68251bb4477121c66d) ([#1173](https://github.com/tauri-apps/tao/pull/1173) by [@sftse](https://github.com/tauri-apps/tao/../../sftse)) `fn set_min_inner_size`, `fn set_max_inner_size`, `fn set_inner_size_constraints`, + `fn set_fullscreen` and `fn set_theme` on `Window` were not properly thread-safe + on Linux. +- [`2ff9e3e9`](https://github.com/tauri-apps/tao/commit/2ff9e3e968d7c4ade5d3c4fb64439ce3893a8bdb) ([#1271](https://github.com/tauri-apps/tao/pull/1271) by [@Legend-Master](https://github.com/tauri-apps/tao/../../Legend-Master)) Fix `EventLoopProxy::send_event` sometimes doesn't deliver the event until the next `EventLoopProxy::send_event` call +- [`14a6dec2`](https://github.com/tauri-apps/tao/commit/14a6dec20897904c9041c52cb92111b81db20e13) ([#1252](https://github.com/tauri-apps/tao/pull/1252) by [@Legend-Master](https://github.com/tauri-apps/tao/../../Legend-Master)) Fix getting the DPI internally leaks `HDC` handles on Windows. Also only call `GetDC` when on < Windows 8.1 which improves its performance. +- [`47d38f36`](https://github.com/tauri-apps/tao/commit/47d38f369f7fb6778df90744faa39d25ab0e3bcc) ([#1228](https://github.com/tauri-apps/tao/pull/1228) by [@ushinohama966](https://github.com/tauri-apps/tao/../../ushinohama966)) fix(linux): map JIS keyboard specific keys (`Zenkaku_Hankaku`, `Hiragana_Katakana`, `Henkan`, `Muhenkan`) in `raw_key_to_key` to prevent them from becoming `Key::Unidentified`. +- [`2ada91bf`](https://github.com/tauri-apps/tao/commit/2ada91bf6372cd9fd3071377a1da90edee601f39) ([#1254](https://github.com/tauri-apps/tao/pull/1254) by [@tenderdeve](https://github.com/tauri-apps/tao/../../tenderdeve)) On macOS, re-apply the custom traffic light inset after a title change and + after leaving fullscreen, so the buttons no longer jump back to their default + position on those events. +- [`07f3742b`](https://github.com/tauri-apps/tao/commit/07f3742b1833b64be27b1ef991e38d557d4276c9) ([#1218](https://github.com/tauri-apps/tao/pull/1218) by [@dgerhardt](https://github.com/tauri-apps/tao/../../dgerhardt)) On Linux, multiple issues regarding window decoration handling for Wayland have been fixed + (#899, #1046, tauri-apps/tauri#6562, tauri-apps/tauri#13440, tauri-apps/tauri#13749, tauri-apps/tauri#14251, tauri-apps/tauri#14748). + Title bar buttons and changing of the title should now work as expected. + Furthermore, client-side decorations are no longer applied, when server-side decorations are supported. + SSD are no longer applied when decorations are disabled for a window during creation. + Toggling of SSD rendering for existing windows is however not supported at this time. +- [`dad6b990`](https://github.com/tauri-apps/tao/commit/dad6b990d0bc0aca382de3631a86870928ef78f6) ([#1266](https://github.com/tauri-apps/tao/pull/1266) by [@FabianLars](https://github.com/tauri-apps/tao/../../FabianLars)) Tao now initializes [`ndk-context`](https://docs.rs/ndk-context) again. This happens in the first onActivityCreate call and uses an Application context instead of an Activity context. +- [`729bbcad`](https://github.com/tauri-apps/tao/commit/729bbcad70a1c912ceb9b3c03cfc250c63e8a368) ([#1270](https://github.com/tauri-apps/tao/pull/1270) by [@ahirner](https://github.com/tauri-apps/tao/../../ahirner)) Prevent iOS apps from aborting when UIKit lays out a Tao view while it is temporarily detached from its window. +- [`f2163508`](https://github.com/tauri-apps/tao/commit/f2163508104413ef0609178dcebdd5803b496211) ([#1245](https://github.com/tauri-apps/tao/pull/1245) by [@velocitysystems](https://github.com/tauri-apps/tao/../../velocitysystems)) Prevent use-after-free in iOS `configurationForConnectingSceneSession` that crashes release builds on launch. +- [`3f70d07c`](https://github.com/tauri-apps/tao/commit/3f70d07c39c3062cf31bb9c83ae853fae8bcffa6) ([#1250](https://github.com/tauri-apps/tao/pull/1250) by [@velocitysystems](https://github.com/tauri-apps/tao/../../velocitysystems)) Fix iPadOS 26 system window controls overlapping `WKWebView` content by implementing `preferredWindowingControlStyleForScene:` on the scene delegate and returning the `minimal` style. The optional protocol method is a no-op on iOS versions earlier than 26. +- [`87c46b52`](https://github.com/tauri-apps/tao/commit/87c46b52b4fc580cd79183f325cb6572ab689945) ([#1238](https://github.com/tauri-apps/tao/pull/1238) by [@Legend-Master](https://github.com/tauri-apps/tao/../../Legend-Master)) Refactored some Windows keyboard internal implementations to catch up with winit, there should not be any behavior changes, please report if you find one +- [`f7f8173d`](https://github.com/tauri-apps/tao/commit/f7f8173dc85287b2a18aceb9c47533097b8c123f) ([#1264](https://github.com/tauri-apps/tao/pull/1264) by [@seemoer](https://github.com/tauri-apps/tao/../../seemoer)) Release the window state lock before updating taskbar visibility on Windows to avoid a reentrant `TaskbarCreated` deadlock. +- [`71b59efe`](https://github.com/tauri-apps/tao/commit/71b59efea161e7270024046686504250571e542a) ([#1209](https://github.com/tauri-apps/tao/pull/1209) by [@lucasfernog](https://github.com/tauri-apps/tao/../../lucasfernog)) Moved Android and iOS lifecycle events to `WindowEvent`, including `Started`, `Stopped`, `Suspended`, and `Resumed`, so they are emitted for the specific window whose activity or scene changed lifecycle state. +- [`87c46b52`](https://github.com/tauri-apps/tao/commit/87c46b52b4fc580cd79183f325cb6572ab689945) ([#1238](https://github.com/tauri-apps/tao/pull/1238) by [@Legend-Master](https://github.com/tauri-apps/tao/../../Legend-Master)) `WindowEvent::ReceivedImeText` event's text is now coming from `ImmGetCompositionStringW` instead of a recording `WM_CHAR` and `WM_SYSCHAR` messages +- [`5f209bd8`](https://github.com/tauri-apps/tao/commit/5f209bd8f6d087ecc1aad6610ca798e378a179cd) ([#1231](https://github.com/tauri-apps/tao/pull/1231) by [@Legend-Master](https://github.com/tauri-apps/tao/../../Legend-Master)) Fixed `with_background_color` doesn't work on initial load on Windows +- [`c704261c`](https://github.com/tauri-apps/tao/commit/c704261c519c58cfdd0bc2d58ba24e06a0b71c92) ([#1215](https://github.com/tauri-apps/tao/pull/1215) by [@chuwik](https://github.com/tauri-apps/tao/../../chuwik)) Avoid Windows keyboard and IME deadlocks caused by re-entrant message processing while input state locks are held. +- [`5f209bd8`](https://github.com/tauri-apps/tao/commit/5f209bd8f6d087ecc1aad6610ca798e378a179cd) ([#1231](https://github.com/tauri-apps/tao/pull/1231) by [@Legend-Master](https://github.com/tauri-apps/tao/../../Legend-Master)) Removed window subclassing on Windows + +### Dependencies + +- Upgraded to `tao-macros@0.1.4` + ## \[0.35.3] - [`1bcd5165`](https://github.com/tauri-apps/tao/commit/1bcd51652763fa6d9512370af6adaea140053891) ([#1224](https://github.com/tauri-apps/tao/pull/1224) by [@brtinney](https://github.com/tauri-apps/tao/../../brtinney)) fix(android): don't panic on `onNewIntent` when `intent.getType()` returns null diff --git a/ports/tao/Cargo.lock b/ports/tao/Cargo.lock index c9a8ddbc2215..a401a290cb79 100644 --- a/ports/tao/Cargo.lock +++ b/ports/tao/Cargo.lock @@ -74,9 +74,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.95" +version = "1.0.104" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34ac096ce696dc2fcabef30516bb13c0a68a11d30131d3df6f04711467681b04" +checksum = "330a5ed07fa54e4702c9d6c4174f74427fc0ef6e214bbd677ae50a5099946470" [[package]] name = "arbitrary" @@ -1555,9 +1555,9 @@ checksum = "88904434abc2901f197fe8cc55f0445e7ded921dba5911dad2e2b39b48e663c4" [[package]] name = "memmap2" -version = "0.9.5" +version = "0.9.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd3f7eed9d3848f8b98834af67102b720745c4ec028fcd0aa0239277e7de374f" +checksum = "d1219ed1b7f229ee7104d281dd01d6802fe28bb6e95d292942c4daacdeb798c0" dependencies = [ "libc", ] @@ -1602,6 +1602,12 @@ dependencies = [ "thiserror", ] +[[package]] +name = "ndk-context" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "27b02d87554356db9e9a873add8782d4ea6e3e58ea071a9adb9a2e8ddb884a8b" + [[package]] name = "ndk-sys" version = "0.6.0+11769913" @@ -2511,7 +2517,7 @@ dependencies = [ [[package]] name = "tao" -version = "0.35.3" +version = "0.36.0" dependencies = [ "async-channel", "bitflags 2.13.0", @@ -2533,6 +2539,7 @@ dependencies = [ "libc", "log", "ndk", + "ndk-context", "ndk-sys", "objc2 0.6.4", "objc2-app-kit", @@ -2557,7 +2564,7 @@ dependencies = [ [[package]] name = "tao-macros" -version = "0.1.3" +version = "0.1.4" dependencies = [ "proc-macro2", "quote", diff --git a/ports/tao/Cargo.toml b/ports/tao/Cargo.toml index 4ebf655ed5fd..860ec7f05548 100644 --- a/ports/tao/Cargo.toml +++ b/ports/tao/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "tao" -version = "0.35.3" +version = "0.36.0" description = "Cross-platform window manager library." authors = [ "Tauri Programme within The Commons Conservancy", @@ -107,8 +107,9 @@ features = [ jni = "0.21" ndk = "0.9" ndk-sys = "0.6" +ndk-context = "0.1" percent-encoding = "2.3" -tao-macros = { version = "0.1.0", path = "./tao-macros" } +tao-macros = { version = "0.1.4", path = "./tao-macros" } [target."cfg(any(target_os = \"ios\", target_os = \"macos\"))".dependencies] objc2 = "0.6" diff --git a/ports/tao/src/event.rs b/ports/tao/src/event.rs index de6c05061e03..7c02d70ddc59 100644 --- a/ports/tao/src/event.rs +++ b/ports/tao/src/event.rs @@ -81,24 +81,6 @@ pub enum Event<'a, T: 'static> { /// Emitted when an event is sent from [`EventLoopProxy::send_event`](crate::event_loop::EventLoopProxy::send_event) UserEvent(T), - /// Emitted when the application has been suspended. - /// - /// ## Platform-specific - /// - /// - **Android**: This is triggered by `onPause` method of the Activity. - /// - **iOS**: This is triggered by `applicationWillResignActive` method of the UIApplicationDelegate. - /// - **Linux / macOS / Windows**: Unsupported. - Suspended, - - /// Emitted when the application has been resumed. - /// - /// ## Platform-specific - /// - /// - **Android**: This is triggered by `onResume` method of the Activity. The first onResume() is ignored to match the iOS implementation, since that is called on activity creation. - /// - **iOS**: This is triggered by `applicationWillEnterForeground` method of the UIApplicationDelegate. - /// - **Linux / macOS / Windows**: Unsupported. - Resumed, - /// Emitted when all of the event loop's input events have been processed and redraw processing /// is about to begin. /// @@ -145,7 +127,7 @@ pub enum Event<'a, T: 'static> { /// Emitted when the event loop is being shut down. /// - /// This is irreversable - if this event is emitted, it is guaranteed to be the last event that + /// This is irreversible - if this event is emitted, it is guaranteed to be the last event that /// gets emitted. You generally want to treat this as an "do on quit" event. LoopDestroyed, @@ -193,8 +175,6 @@ impl Clone for Event<'static, T> { RedrawRequested(wid) => RedrawRequested(*wid), RedrawEventsCleared => RedrawEventsCleared, LoopDestroyed => LoopDestroyed, - Suspended => Suspended, - Resumed => Resumed, Opened { urls } => Opened { urls: urls.clone() }, Reopen { has_visible_windows, @@ -222,8 +202,6 @@ impl<'a, T> Event<'a, T> { RedrawRequested(wid) => Ok(RedrawRequested(wid)), RedrawEventsCleared => Ok(RedrawEventsCleared), LoopDestroyed => Ok(LoopDestroyed), - Suspended => Ok(Suspended), - Resumed => Ok(Resumed), Opened { urls } => Ok(Opened { urls }), Reopen { has_visible_windows, @@ -250,8 +228,6 @@ impl<'a, T> Event<'a, T> { RedrawRequested(wid) => Some(RedrawRequested(wid)), RedrawEventsCleared => Some(RedrawEventsCleared), LoopDestroyed => Some(LoopDestroyed), - Suspended => Some(Suspended), - Resumed => Some(Resumed), Opened { urls } => Some(Opened { urls }), Reopen { has_visible_windows, @@ -304,7 +280,7 @@ pub enum WindowEvent<'a> { /// /// ## Platform-specific /// - /// - **Linux(Wayland)**: will always be (0, 0) since Wayland doesn't support a global cordinate system. + /// - **Linux(Wayland)**: will always be (0, 0) since Wayland doesn't support a global coordinate system. Moved(PhysicalPosition), /// The window has been requested to close. @@ -318,6 +294,40 @@ pub enum WindowEvent<'a> { /// - **macOS:** Fired if the [`crate::window::Window`] is dropped or the dock `Quit` item is clicked. Destroyed, + /// The window has been started. + /// + /// ## Platform-specific + /// + /// - **Android**: This is triggered by `onStart` method of the Activity. + /// - **Linux / macOS / iOS / Windows**: Unsupported. + Started, + + /// The window has been suspended. + /// + /// ## Platform-specific + /// + /// - **Android**: This is triggered by `onPause` method of the Activity. + /// - **iOS**: This is triggered by `applicationWillResignActive` method of the UIApplicationDelegate. + /// - **Linux / macOS / Windows**: Unsupported. + Suspended, + + /// The window has been resumed. + /// + /// ## Platform-specific + /// + /// - **Android**: This is triggered by `onResume` method of the Activity. The first onResume() is ignored to match the iOS implementation, since that is called on activity creation. + /// - **iOS**: This is triggered by `applicationWillEnterForeground` method of the UIApplicationDelegate. + /// - **Linux / macOS / Windows**: Unsupported. + Resumed, + + /// The window has been stopped. + /// + /// ## Platform-specific + /// + /// - **Android**: This is triggered by `onStop` method of the Activity. + /// - **Linux / macOS / iOS / Windows**: Unsupported. + Stopped, + /// A file has been dropped into the window. /// /// When the user drops multiple files at once, this event will be emitted for each file @@ -471,6 +481,10 @@ impl Clone for WindowEvent<'static> { Moved(pos) => Moved(*pos), CloseRequested => CloseRequested, Destroyed => Destroyed, + Started => Started, + Suspended => Suspended, + Resumed => Resumed, + Stopped => Stopped, DroppedFile(file) => DroppedFile(file.clone()), HoveredFile(file) => HoveredFile(file.clone()), HoveredFileCancelled => HoveredFileCancelled, @@ -563,6 +577,10 @@ impl<'a> WindowEvent<'a> { Moved(position) => Some(Moved(position)), CloseRequested => Some(CloseRequested), Destroyed => Some(Destroyed), + Started => Some(Started), + Suspended => Some(Suspended), + Resumed => Some(Resumed), + Stopped => Some(Stopped), DroppedFile(file) => Some(DroppedFile(file)), HoveredFile(file) => Some(HoveredFile(file)), HoveredFileCancelled => Some(HoveredFileCancelled), diff --git a/ports/tao/src/event_loop.rs b/ports/tao/src/event_loop.rs index 5dedd4f4ada7..e3b954c563f8 100644 --- a/ports/tao/src/event_loop.rs +++ b/ports/tao/src/event_loop.rs @@ -407,7 +407,7 @@ impl fmt::Display for EventLoopClosed { impl error::Error for EventLoopClosed {} -/// Fiter controlling the propagation of device events. +/// Filter controlling the propagation of device events. #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Default)] pub enum DeviceEventFilter { /// Always filter out device events. diff --git a/ports/tao/src/keyboard.rs b/ports/tao/src/keyboard.rs index 60cb4be04861..444fd336e40e 100644 --- a/ports/tao/src/keyboard.rs +++ b/ports/tao/src/keyboard.rs @@ -206,7 +206,7 @@ pub enum NativeKeyCode { /// - The keys that the specification calls "MetaLeft" and "MetaRight" are named "SuperLeft" and /// "SuperRight" here. /// - The key that the specification calls "Super" is reported as `Unidentified` here. -/// - The `Unidentified` variant here, can still identifiy a key through it's `NativeKeyCode`. +/// - The `Unidentified` variant here, can still identify a key through it's `NativeKeyCode`. /// /// [`KeyboardEvent.code`]: https://w3c.github.io/uievents-code/#code-value-tables #[non_exhaustive] @@ -841,8 +841,8 @@ impl fmt::Display for KeyCode { /// - The `Super` variant here, is named `Meta` in the aforementioned specification. (There's /// another key which the specification calls `Super`. That does not exist here.) /// - The `Space` variant here, can be identified by the character it generates in the -/// specificaiton. -/// - The `Unidentified` variant here, can still identifiy a key through it's `NativeKeyCode`. +/// specification. +/// - The `Unidentified` variant here, can still identify a key through it's `NativeKeyCode`. /// - The `Dead` variant here, can specify the character which is inserted when pressing the /// dead-key twice. /// diff --git a/ports/tao/src/platform/unix.rs b/ports/tao/src/platform/unix.rs index 4108753f3ab3..66677dde5cf3 100644 --- a/ports/tao/src/platform/unix.rs +++ b/ports/tao/src/platform/unix.rs @@ -127,7 +127,7 @@ pub trait WindowBuilderExtUnix { /// Whether to enable or disable the internal draw for transparent window. /// - /// When tranparent attribute is enabled, we will call `connect_draw` and draw a transparent background. + /// When transparent attribute is enabled, we will call `connect_draw` and draw a transparent background. /// For anyone who wants to draw the background themselves, set this to `false`. /// Default is `true`. fn with_transparent_draw(self, draw: bool) -> WindowBuilder; @@ -142,7 +142,7 @@ pub trait WindowBuilderExtUnix { /// Default is `false` but is always `true` if [`WindowAttributes::transparent`](crate::window::WindowAttributes::transparent) is `true` fn with_rgba_visual(self, rgba_visual: bool) -> WindowBuilder; - /// Wether to set this window as app paintable + /// Whether to set this window as app paintable /// /// /// diff --git a/ports/tao/src/platform_impl/android/mod.rs b/ports/tao/src/platform_impl/android/mod.rs index 82f0a599dd8b..d29e073db614 100644 --- a/ports/tao/src/platform_impl/android/mod.rs +++ b/ports/tao/src/platform_impl/android/mod.rs @@ -21,7 +21,7 @@ use ndk::{ }; use once_cell::sync::Lazy; use std::{ - collections::VecDeque, + collections::{HashSet, VecDeque}, error::Error, fmt, sync::RwLock, @@ -174,7 +174,7 @@ pub struct EventLoop { first_event: Option, start_cause: event::StartCause, looper: ThreadLooper, - running: bool, + running: HashSet, } #[derive(Default, Debug, Copy, Clone, PartialEq, Eq, Hash)] @@ -196,7 +196,7 @@ impl EventLoop { first_event: None, start_cause: event::StartCause::Init, looper: ThreadLooper::for_thread().unwrap(), - running: false, + running: HashSet::new(), } } @@ -228,8 +228,15 @@ impl EventLoop { match self.first_event.take() { Some(EventSource::Callback) => match ndk_glue::poll_events().unwrap() { - Event::Resume => { - self.call_event_handler(event_handler, control_flow, event::Event::Resumed); + Event::Resume { id: window_id } => { + self.call_event_handler( + event_handler, + control_flow, + event::Event::WindowEvent { + window_id, + event: event::WindowEvent::Resumed, + }, + ); } Event::WindowEvent { id: window_id, @@ -237,6 +244,17 @@ impl EventLoop { } => match event { WindowEvent::Resized => resized_window_id = Some(window_id), WindowEvent::RedrawNeeded => redraw_window_id = Some(window_id), + WindowEvent::Started => { + self.running.insert(window_id); + self.call_event_handler( + event_handler, + control_flow, + event::Event::WindowEvent { + window_id, + event: event::WindowEvent::Started, + }, + ); + } WindowEvent::Focused(focused) => { self.call_event_handler( event_handler, @@ -247,7 +265,19 @@ impl EventLoop { }, ); } + WindowEvent::Stopped => { + self.running.remove(&window_id); + self.call_event_handler( + event_handler, + control_flow, + event::Event::WindowEvent { + window_id, + event: event::WindowEvent::Stopped, + }, + ); + } WindowEvent::Destroyed => { + self.running.remove(&window_id); self.call_event_handler( event_handler, control_flow, @@ -259,11 +289,16 @@ impl EventLoop { } _ => {} }, - Event::Pause => { - self.call_event_handler(event_handler, control_flow, event::Event::Suspended); + Event::Pause { id: window_id } => { + self.call_event_handler( + event_handler, + control_flow, + event::Event::WindowEvent { + window_id, + event: event::WindowEvent::Suspended, + }, + ); } - Event::Stop => self.running = false, - Event::Start => self.running = true, Event::Opened => { let urls = ndk_glue::take_intent_urls(); if !urls.is_empty() { @@ -404,7 +439,7 @@ impl EventLoop { self.call_event_handler(event_handler, control_flow, event::Event::MainEventsCleared); if let Some(window_id) = resized_window_id { - if self.running { + if self.running.contains(&window_id) { let size = MonitorHandle.size(); let event = event::Event::WindowEvent { window_id, @@ -415,7 +450,7 @@ impl EventLoop { } if let Some(window_id) = redraw_window_id { - if self.running { + if self.running.contains(&window_id) { let event = event::Event::RedrawRequested(window_id); self.call_event_handler(event_handler, control_flow, event); } @@ -512,7 +547,7 @@ pub struct EventLoopProxy { impl EventLoopProxy { pub fn send_event(&self, event: T) -> Result<(), event_loop::EventLoopClosed> { - _ = self.queue.try_send(event); + let _ = self.queue.send(event); self.looper.wake(); Ok(()) } diff --git a/ports/tao/src/platform_impl/android/ndk_glue.rs b/ports/tao/src/platform_impl/android/ndk_glue.rs index 91e75d94b4c4..4c5e3c8829ce 100644 --- a/ports/tao/src/platform_impl/android/ndk_glue.rs +++ b/ports/tao/src/platform_impl/android/ndk_glue.rs @@ -29,10 +29,7 @@ use std::{ fs::File, io::{BufRead, BufReader}, os::unix::prelude::*, - sync::{ - atomic::{AtomicBool, Ordering}, - Arc, Condvar, Mutex, RwLock, RwLockReadGuard, - }, + sync::{Arc, Condvar, Mutex, RwLock, RwLockReadGuard}, thread, time::Duration, }; @@ -68,17 +65,18 @@ const DATA_URL_ENCODING_SET: &AsciiSet = &CONTROLS /// 1. android app domain name in reverse snake_case as an ident (for ex: com_example) /// 2. android package anme (for ex: wryapp) /// 3. the android activity that has external linking for the following functions and calls them: -/// - `private external fun onActivityCreate(activity: WryActivity)`` -/// - `private external fun start()` -/// - `private external fun resume()` -/// - `private external fun pause()` -/// - `private external fun stop()` -/// - `private external fun onActivitySaveInstanceState()` -/// - `private external fun onActivityDestroy(activity: WryActivity)` -/// - `private external fun onActivityLowMemory()` +/// - `private external fun onFirstActivityCreate()` +/// - `private external fun onCreate(activity: WryActivity)` +/// - `private external fun onStart(activity: WryActivity)` +/// - `private external fun onResume(activity: WryActivity)` +/// - `private external fun onPause(activity: WryActivity)` +/// - `private external fun onStop(activity: WryActivity)` +/// - `private external fun onDestroy(activity: WryActivity)` /// - `private external fun onWindowFocusChanged(activity: WryActivity, focus: Boolean)` -/// 4. a on_activity_create function that will be ran once after the `onActivityCreate` function above. -/// 5. the main entry point of your android application. +/// - `private external fun onLowMemory()` +/// - `private external fun onNewIntent(intent: Intent)` +/// 4. an on_activity_create function that will be ran once after the `onCreate` function above. +/// 5. the main entry point of your android application that runs in `onFirstActivityCreate` after `onCreate` and `on_activity_create`. #[rustfmt::skip] #[macro_export] macro_rules! android_binding { @@ -96,8 +94,8 @@ macro_rules! android_binding { $domain, $package, $activity, - create, - [JObject], + onFirstActivityCreate, + [], __VOID__, [$main], ); @@ -105,20 +103,19 @@ macro_rules! android_binding { $domain, $package, $activity, - onActivityCreate, + onCreate, [JObject], __VOID__, [$on_activity_create], _____tao_store_package_name__, ); - android_fn!($domain, $package, $activity, start, [JObject]); - android_fn!($domain, $package, $activity, stop, [JObject]); - android_fn!($domain, $package, $activity, resume, [JObject]); - android_fn!($domain, $package, $activity, pause, [JObject]); - android_fn!($domain, $package, $activity, onActivitySaveInstanceState, [JObject]); - android_fn!($domain, $package, $activity, onActivityDestroy, [JObject]); - android_fn!($domain, $package, $activity, onActivityLowMemory, [JObject]); - android_fn!($domain, $package, $activity, onWindowFocusChanged, [JObject,i32]); + android_fn!($domain, $package, $activity, onStart, [JObject]); + android_fn!($domain, $package, $activity, onStop, [JObject]); + android_fn!($domain, $package, $activity, onResume, [JObject]); + android_fn!($domain, $package, $activity, onPause, [JObject]); + android_fn!($domain, $package, $activity, onDestroy, [JObject]); + android_fn!($domain, $package, $activity, onWindowFocusChanged, [JObject, i32]); + android_fn!($domain, $package, $activity, onLowMemory, []); android_fn!($domain, $package, $activity, onNewIntent, [JObject]); }}; } @@ -218,6 +215,8 @@ pub type ActivityId = i32; pub(crate) static CONTEXTS: Lazy>> = Lazy::new(Default::default); +// Keeping a reference so we can store it in ndk-context +static APPLICATION_CONTEXT: OnceCell = OnceCell::new(); static WINDOW_MANAGER: Lazy>> = Lazy::new(Default::default); pub(crate) static ACTIVITY_CREATED_SENDERS: Lazy>>> = Lazy::new(Default::default); @@ -225,7 +224,7 @@ static INTENT_URLS: Lazy>> = Lazy::new(Default::default); static INPUT_QUEUE: Lazy>> = Lazy::new(Default::default); static CONTENT_RECT: Lazy> = Lazy::new(Default::default); static LOOPER: Lazy>> = Lazy::new(Default::default); -static DID_RESUME: AtomicBool = AtomicBool::new(false); +static RESUMED_ACTIVITIES: Lazy>> = Lazy::new(Default::default); pub fn main_window_manager() -> Option { WINDOW_MANAGER.lock().unwrap().values().next().cloned() @@ -247,6 +246,14 @@ pub fn content_rect() -> Rect { CONTENT_RECT.read().unwrap().clone() } +fn activity_id(env: &mut JNIEnv<'_>, activity: &JObject<'_>) -> ActivityId { + env + .call_method(activity, "getId", "()I", &[]) + .unwrap() + .i() + .unwrap() +} + pub fn main_android_context() -> Option { CONTEXTS.lock().unwrap().values().next().cloned() } @@ -303,10 +310,8 @@ pub struct Rect { #[derive(Clone, Debug, Eq, PartialEq, Copy)] #[repr(u8)] pub enum Event { - Start, - Resume, - Pause, - Stop, + Resume { id: WindowId }, + Pause { id: WindowId }, LowMemory, WindowEvent { id: WindowId, event: WindowEvent }, ContentRectChanged, @@ -318,12 +323,15 @@ pub enum Event { pub enum WindowEvent { Focused(bool), Created, + Started, Resized, RedrawNeeded, + Stopped, Destroyed, } -pub unsafe fn create(_env: JNIEnv, _: JClass, _: JObject, main: fn()) { +#[allow(non_snake_case)] +pub unsafe fn onFirstActivityCreate(_env: JNIEnv, _: JClass, main: fn()) { let logpipe = { let mut logpipe: [RawFd; 2] = Default::default(); libc::pipe(logpipe.as_mut_ptr()); @@ -382,7 +390,7 @@ pub unsafe fn create(_env: JNIEnv, _: JClass, _: JObject, main: fn()) { } #[allow(non_snake_case)] -pub unsafe fn onActivityCreate( +pub unsafe fn onCreate( mut env: JNIEnv, _jclass: JClass, activity: JObject, @@ -391,6 +399,25 @@ pub unsafe fn onActivityCreate( let intent = jni_call_method!(env, &activity, "getIntent", "()Landroid/content/Intent;", l).unwrap(); + let vm = env.get_java_vm().unwrap(); + + APPLICATION_CONTEXT.get_or_init(|| { + let application = jni_call_method!( + env, + &activity, + "getApplicationContext", + "()Landroid/content/Context;", + l + ) + .unwrap(); + let application = env.new_global_ref(application).unwrap(); + ndk_context::initialize_android_context( + vm.get_java_vm_pointer() as *mut _, + application.as_obj().as_raw() as *mut _, + ); + application + }); + let activity_id = jni_call_method!(env, &activity, "getId", "()I", i).unwrap(); let activity_name: JString = jni_call_method!( @@ -423,7 +450,6 @@ pub unsafe fn onActivityCreate( .unwrap() .insert(activity_id, window_manager); let activity = env.new_global_ref(activity).unwrap(); - let vm = env.get_java_vm().unwrap(); let thread_env = vm.attach_current_thread_as_daemon().unwrap(); CONTEXTS.lock().unwrap().insert( @@ -449,17 +475,25 @@ pub unsafe fn onActivityCreate( handle_intent(env, intent); } -pub unsafe fn resume(_: JNIEnv, _: JClass, _: JObject) { - let did_resume = DID_RESUME.swap(true, Ordering::Relaxed); +#[allow(non_snake_case)] +pub unsafe fn onResume(mut env: JNIEnv, _: JClass, activity: JObject) { + let activity_id = activity_id(&mut env, &activity); + let did_resume = !RESUMED_ACTIVITIES.lock().unwrap().insert(activity_id); // first Activity onResume() is called even after onCreate() // to match the iOS implementation, we ignore the first resume event if did_resume { - wake(Event::Resume); + wake(Event::Resume { + id: WindowId(super::WindowId(activity_id)), + }); } } -pub unsafe fn pause(_: JNIEnv, _: JClass, _: JObject) { - wake(Event::Pause); +#[allow(non_snake_case)] +pub unsafe fn onPause(mut env: JNIEnv, _: JClass, activity: JObject) { + let activity_id = activity_id(&mut env, &activity); + wake(Event::Pause { + id: WindowId(super::WindowId(activity_id)), + }); } #[allow(non_snake_case)] @@ -469,7 +503,11 @@ pub unsafe fn onWindowFocusChanged( activity: JObject, has_focus: libc::c_int, ) { - let activity_id = jni_call_method!(env, &activity, "getId", "()I", i).unwrap(); + let activity_id = env + .call_method(&activity, "getId", "()I", &[]) + .unwrap() + .i() + .unwrap(); let event = Event::WindowEvent { id: WindowId(super::WindowId(activity_id)), event: WindowEvent::Focused(has_focus != 0), @@ -669,17 +707,31 @@ pub unsafe fn handle_intent(mut env: JNIEnv, intent: JObject) { } } -pub unsafe fn start(_: JNIEnv, _: JClass, _: JObject) { - wake(Event::Start); +#[allow(non_snake_case)] +pub unsafe fn onStart(mut env: JNIEnv, _: JClass, activity: JObject) { + let activity_id = activity_id(&mut env, &activity); + wake(Event::WindowEvent { + id: WindowId(super::WindowId(activity_id)), + event: WindowEvent::Started, + }); } -pub unsafe fn stop(_: JNIEnv, _: JClass, _: JObject) { - wake(Event::Stop); +#[allow(non_snake_case)] +pub unsafe fn onStop(mut env: JNIEnv, _: JClass, activity: JObject) { + let activity_id = activity_id(&mut env, &activity); + wake(Event::WindowEvent { + id: WindowId(super::WindowId(activity_id)), + event: WindowEvent::Stopped, + }); } #[allow(non_snake_case)] -pub unsafe fn onActivityDestroy(mut env: JNIEnv, _: JClass, activity: JObject) { - let activity_id = jni_call_method!(env, &activity, "getId", "()I", i).unwrap(); +pub unsafe fn onDestroy(mut env: JNIEnv, _: JClass, activity: JObject) { + let activity_id = env + .call_method(&activity, "getId", "()I", &[]) + .unwrap() + .i() + .unwrap(); let is_changing_configurations = jni_call_method!(env, &activity, "isChangingConfigurations", "()Z", z).unwrap(); @@ -693,6 +745,7 @@ pub unsafe fn onActivityDestroy(mut env: JNIEnv, _: JClass, activity: JObject) { }); CONTEXTS.lock().unwrap().remove(&activity_id); WINDOW_MANAGER.lock().unwrap().remove(&activity_id); + RESUMED_ACTIVITIES.lock().unwrap().remove(&activity_id); } } @@ -701,10 +754,7 @@ pub unsafe fn onActivityDestroy(mut env: JNIEnv, _: JClass, activity: JObject) { /////////////////////////////////////////////// #[allow(non_snake_case)] -pub unsafe fn onActivitySaveInstanceState(_: JNIEnv, _: JClass, _: JObject) {} - -#[allow(non_snake_case)] -pub unsafe fn onActivityLowMemory(_: JNIEnv, _: JClass, _: JObject) { +pub unsafe fn onLowMemory(_: JNIEnv, _: JClass) { wake(Event::LowMemory); } diff --git a/ports/tao/src/platform_impl/ios/mod.rs b/ports/tao/src/platform_impl/ios/mod.rs index 58515e6af1d5..270c2a5532c8 100644 --- a/ports/tao/src/platform_impl/ios/mod.rs +++ b/ports/tao/src/platform_impl/ios/mod.rs @@ -54,8 +54,10 @@ //! //! This is how those event are represented in tao: //! -//! - applicationWillEnterForeground is Resumed -//! - applicationWillResignActive is Suspended +//! - sceneWillEnterForeground is WindowEvent::Resumed for each window in the scene +//! - sceneWillResignActive is WindowEvent::Suspended for each window in the scene +//! - applicationWillEnterForeground is WindowEvent::Resumed for each window in non-scene apps +//! - applicationWillResignActive is WindowEvent::Suspended for each window in non-scene apps //! - applicationWillTerminate is LoopDestroyed //! //! Keep in mind that after LoopDestroyed event is received every attempt to draw with diff --git a/ports/tao/src/platform_impl/ios/scene.rs b/ports/tao/src/platform_impl/ios/scene.rs index fb293e718cad..f7f1a35cf891 100644 --- a/ports/tao/src/platform_impl/ios/scene.rs +++ b/ports/tao/src/platform_impl/ios/scene.rs @@ -51,6 +51,17 @@ pub unsafe fn multiple_scenes_enabled() -> bool { (*num).as_bool() } +unsafe fn handle_scene_window_events(scene: &UIScene, event: impl Fn() -> WindowEvent<'static>) { + if let Some(window_scene) = scene.downcast_ref::() { + for window in window_scene.windows() { + app_state::handle_nonuser_event(EventWrapper::StaticEvent(Event::WindowEvent { + window_id: RootWindowId(window.into()), + event: event(), + })); + } + } +} + define_class!( #[unsafe(super(NSObject))] #[name = "TaoSceneDelegate"] @@ -93,19 +104,17 @@ define_class!( #[unsafe(method(sceneWillResignActive:))] fn sceneWillResignActive(&self, scene: &UIScene) { unsafe { - if let Some(window_scene) = scene.downcast_ref::() { - for window in window_scene.windows() { - app_state::handle_nonuser_event(EventWrapper::StaticEvent(Event::WindowEvent { - window_id: RootWindowId(window.into()), - event: WindowEvent::Focused(false), - })); - } - } + handle_scene_window_events(scene, || WindowEvent::Focused(false)); + handle_scene_window_events(scene, || WindowEvent::Suspended); } } #[unsafe(method(sceneWillEnterForeground:))] - fn sceneWillEnterForeground(&self, _scene: &UIScene) {} + fn sceneWillEnterForeground(&self, scene: &UIScene) { + unsafe { + handle_scene_window_events(scene, || WindowEvent::Resumed); + } + } #[unsafe(method(sceneDidEnterBackground:))] fn sceneDidEnterBackground(&self, _scene: &UIScene) {} @@ -195,14 +204,12 @@ define_class!( #[allow(non_snake_case)] unsafe impl UIWindowSceneDelegate for TaoSceneDelegate { - #[unsafe(method(preferredWindowingControlStyleForScene:))] + #[unsafe(method_id(preferredWindowingControlStyleForScene:))] fn preferredWindowingControlStyleForScene( &self, _window_scene: &UIWindowScene, - ) -> Option> { - std::ptr::NonNull::new(Retained::autorelease_ptr( - UISceneWindowingControlStyle::minimalStyle(), - )) + ) -> Retained { + UISceneWindowingControlStyle::minimalStyle() } } ); diff --git a/ports/tao/src/platform_impl/ios/view.rs b/ports/tao/src/platform_impl/ios/view.rs index 2e91edc02a20..0e49e7fc1bcf 100644 --- a/ports/tao/src/platform_impl/ios/view.rs +++ b/ports/tao/src/platform_impl/ios/view.rs @@ -140,7 +140,11 @@ unsafe fn get_view_class(root_view_class: &'static Class) -> &'static Class { let () = msg_send![super(object, superclass), layoutSubviews]; let window: id = msg_send![object, window]; - assert!(!window.is_null()); + // UIKit can lay out a view while it is detached during window and view-controller + // transitions. There is no window ID or screen geometry to report until reattachment. + if window.is_null() { + return; + } let window_bounds: CGRect = msg_send![window, bounds]; let screen: id = msg_send![window, screen]; let screen_space: id = msg_send![screen, coordinateSpace]; @@ -669,6 +673,27 @@ pub fn create_delegate_class() { } } + unsafe fn handle_tao_window_events(event: impl Fn() -> WindowEvent<'static>) { + let app: id = msg_send![class!(UIApplication), sharedApplication]; + let windows: id = msg_send![app, windows]; + let windows_enum: id = msg_send![windows, objectEnumerator]; + let mut events = Vec::new(); + loop { + let window: id = msg_send![windows_enum, nextObject]; + if window == nil { + break; + } + let is_tao_window: bool = msg_send![window, isKindOfClass: class!(TaoUIWindow)]; + if is_tao_window { + events.push(EventWrapper::StaticEvent(Event::WindowEvent { + window_id: RootWindowId(window.into()), + event: event(), + })); + } + } + app_state::handle_nonuser_events(events); + } + // custom URL schemes // https://developer.apple.com/documentation/xcode/defining-a-custom-url-scheme-for-your-app extern "C" fn application_open_url( @@ -705,35 +730,16 @@ pub fn create_delegate_class() { } extern "C" fn will_resign_active(_: &Object, _: Sel, _: id) { - unsafe { app_state::handle_nonuser_event(EventWrapper::StaticEvent(Event::Suspended)) } + unsafe { handle_tao_window_events(|| WindowEvent::Suspended) } } extern "C" fn will_enter_foreground(_: &Object, _: Sel, _: id) { - unsafe { app_state::handle_nonuser_event(EventWrapper::StaticEvent(Event::Resumed)) } + unsafe { handle_tao_window_events(|| WindowEvent::Resumed) } } - extern "C" fn did_enter_background(_: &Object, _: Sel, _: id) {} - extern "C" fn will_terminate(_: &Object, _: Sel, _: id) { unsafe { - let app: id = msg_send![class!(UIApplication), sharedApplication]; - let windows: id = msg_send![app, windows]; - let windows_enum: id = msg_send![windows, objectEnumerator]; - let mut events = Vec::new(); - loop { - let window: id = msg_send![windows_enum, nextObject]; - if window == nil { - break; - } - let is_tao_window: bool = msg_send![window, isKindOfClass: class!(TaoUIWindow)]; - if is_tao_window { - events.push(EventWrapper::StaticEvent(Event::WindowEvent { - window_id: RootWindowId(window.into()), - event: WindowEvent::Destroyed, - })); - } - } - app_state::handle_nonuser_events(events); + handle_tao_window_events(|| WindowEvent::Destroyed); app_state::terminated(); } } @@ -746,12 +752,14 @@ pub fn create_delegate_class() { .expect("Failed to declare class `AppDelegate`"); unsafe { + let uses_scenes = multiple_scenes_enabled(); + decl.add_method( sel!(application:didFinishLaunchingWithOptions:), did_finish_launching as extern "C" fn(_, _, _, _) -> _, ); - if multiple_scenes_enabled() { + if uses_scenes { decl.add_method( sel!(application:configurationForConnectingSceneSession:options:), configuration_for_connecting_scene_session as extern "C" fn(_, _, _, _, _) -> _, @@ -768,18 +776,16 @@ pub fn create_delegate_class() { application_continue as extern "C" fn(_, _, _, _, _) -> _, ); - decl.add_method( - sel!(applicationWillResignActive:), - will_resign_active as extern "C" fn(_, _, _), - ); - decl.add_method( - sel!(applicationWillEnterForeground:), - will_enter_foreground as extern "C" fn(_, _, _), - ); - decl.add_method( - sel!(applicationDidEnterBackground:), - did_enter_background as extern "C" fn(_, _, _), - ); + if !uses_scenes { + decl.add_method( + sel!(applicationWillResignActive:), + will_resign_active as extern "C" fn(_, _, _), + ); + decl.add_method( + sel!(applicationWillEnterForeground:), + will_enter_foreground as extern "C" fn(_, _, _), + ); + } decl.add_method( sel!(applicationWillTerminate:), diff --git a/ports/tao/src/platform_impl/macos/util/async.rs b/ports/tao/src/platform_impl/macos/util/async.rs index 69880a8dd279..e7c5fd597b2e 100644 --- a/ports/tao/src/platform_impl/macos/util/async.rs +++ b/ports/tao/src/platform_impl/macos/util/async.rs @@ -21,6 +21,7 @@ use crate::{ dpi::LogicalSize, platform_impl::platform::{ ffi::{self, id, NO, YES}, + view::reapply_traffic_light_inset, window::SharedState, }, }; @@ -223,11 +224,16 @@ pub unsafe fn make_key_and_order_front_sync(ns_window: &NSWindow) { // `setTitle:` isn't thread-safe. Calling it from another thread invalidates the // window drag regions, which throws an exception when not done in the main // thread -pub unsafe fn set_title_async(ns_window: &NSWindow, title: String) { +pub unsafe fn set_title_async(ns_window: &NSWindow, ns_view: &NSView, title: String) { let ns_window = MainThreadSafe(ns_window.retain()); + let ns_view = MainThreadSafe(ns_view.retain()); DispatchQueue::main().exec_async(move || { let title = NSString::from_str(&title); ns_window.setTitle(&title); + // `setTitle:` moves the traffic light buttons back to their default spot. + // Re-apply the configured inset kept on the view so the custom position + // survives a title change (#13044). + reapply_traffic_light_inset(&ns_window, &ns_view); }); } diff --git a/ports/tao/src/platform_impl/macos/view.rs b/ports/tao/src/platform_impl/macos/view.rs index 9ce79867e1cb..6986f4d92a3e 100644 --- a/ports/tao/src/platform_impl/macos/view.rs +++ b/ports/tao/src/platform_impl/macos/view.rs @@ -1184,3 +1184,18 @@ pub unsafe fn inset_traffic_lights(window: &NSWindow, position: LogicalPosition< button.setFrameOrigin(rect.origin); } } + +// Re-apply the custom traffic light inset stored on the view, if one was set. +// AppKit resets the buttons to their default position on some events (title +// change, leaving fullscreen) without triggering a `drawRect:`, so callers use +// this to restore the configured position afterwards (#13044, #15451). +pub unsafe fn reapply_traffic_light_inset(ns_window: &NSWindow, ns_view: &NSView) { + let state_ptr: *mut c_void = *ns_view.get_ivar("taoState"); + if state_ptr.is_null() { + return; + } + let state = &*(state_ptr as *mut ViewState); + if let Some(position) = state.traffic_light_inset { + inset_traffic_lights(ns_window, position); + } +} diff --git a/ports/tao/src/platform_impl/macos/window.rs b/ports/tao/src/platform_impl/macos/window.rs index 7bf3289b73d6..6b6b98371e59 100644 --- a/ports/tao/src/platform_impl/macos/window.rs +++ b/ports/tao/src/platform_impl/macos/window.rs @@ -659,7 +659,7 @@ impl UnownedWindow { pub fn set_title(&self, title: &str) { unsafe { - util::set_title_async(&self.ns_window, title.to_string()); + util::set_title_async(&self.ns_window, &self.ns_view, title.to_string()); } } diff --git a/ports/tao/src/platform_impl/macos/window_delegate.rs b/ports/tao/src/platform_impl/macos/window_delegate.rs index 8a0431a92bfd..efddee52fa01 100644 --- a/ports/tao/src/platform_impl/macos/window_delegate.rs +++ b/ports/tao/src/platform_impl/macos/window_delegate.rs @@ -33,7 +33,7 @@ use crate::{ event::{EventProxy, EventWrapper}, ffi::{id, nil, BOOL, NO, YES}, util::{self, IdRef}, - view::ViewState, + view::{reapply_traffic_light_inset, ViewState}, window::{get_ns_theme, get_window_id, UnownedWindow}, }, window::{Fullscreen, WindowId}, @@ -609,6 +609,9 @@ extern "C" fn window_did_exit_fullscreen(this: &Object, _: Sel, _: id) { if let Some(target_fullscreen) = target_fullscreen { window.set_fullscreen(target_fullscreen); } + // Leaving fullscreen resets the traffic light buttons to their default + // position without a `drawRect:`, so re-apply the custom inset (#15451). + unsafe { reapply_traffic_light_inset(&window.ns_window, &window.ns_view) }; }); state.emit_resize_event(); state.emit_move_event(); diff --git a/ports/tao/src/platform_impl/windows/event_loop.rs b/ports/tao/src/platform_impl/windows/event_loop.rs index 3b02b1555ef9..8b04a0b5460e 100644 --- a/ports/tao/src/platform_impl/windows/event_loop.rs +++ b/ports/tao/src/platform_impl/windows/event_loop.rs @@ -37,8 +37,7 @@ use windows::{ Controls::{self as win32c, HOVER_DEFAULT}, Input::{KeyboardAndMouse::*, Pointer::*, Touch::*, *}, Shell::{ - DefSubclassProc, SHAppBarMessage, ABE_BOTTOM, ABE_LEFT, ABE_RIGHT, ABE_TOP, - ABM_GETAUTOHIDEBAR, APPBARDATA, + SHAppBarMessage, ABE_BOTTOM, ABE_LEFT, ABE_RIGHT, ABE_TOP, ABM_GETAUTOHIDEBAR, APPBARDATA, }, WindowsAndMessaging::{self as win32wm, *}, }, @@ -2287,7 +2286,7 @@ unsafe extern "system" fn thread_event_target_callback( } // Default WM_PAINT behaviour. This makes sure modals and popups are shown immediatly when opening them. - DefSubclassProc(window, msg, wparam, lparam) + DefWindowProcW(window, msg, wparam, lparam) } win32wm::WM_INPUT_DEVICE_CHANGE => { @@ -2312,7 +2311,7 @@ unsafe extern "system" fn thread_event_target_callback( let _ = RedrawWindow(Some(window), None, None, RDW_INTERNALPAINT); } - DefSubclassProc(window, msg, wparam, lparam) + DefWindowProcW(window, msg, wparam, lparam) } // We don't process `WM_QUERYENDSESSION` yet until we introduce the same mechanism as Tauri's `ExitRequested` event @@ -2375,7 +2374,7 @@ unsafe extern "system" fn thread_event_target_callback( let _ = RedrawWindow(Some(window), None, None, RDW_INTERNALPAINT); LRESULT(0) } - _ => DefSubclassProc(window, msg, wparam, lparam), + _ => DefWindowProcW(window, msg, wparam, lparam), }; let result = userdata diff --git a/ports/tao/src/window.rs b/ports/tao/src/window.rs index 5b7a6cf2f81b..c33572214a81 100644 --- a/ports/tao/src/window.rs +++ b/ports/tao/src/window.rs @@ -721,7 +721,7 @@ impl Window { /// - **iOS:** Can only be called on the main thread. Returns the top left coordinates of the /// window in the screen space coordinate system. /// - **Android:** Always returns [`NotSupportedError`]. - /// - **Linux**: Has no effect, since Wayland doesn't support a global cordinate system + /// - **Linux**: Has no effect, since GTK4 does not expose a global coordinate system #[inline] pub fn outer_position(&self) -> Result, NotSupportedError> { self.window.outer_position() @@ -831,7 +831,7 @@ impl Window { /// /// ## Platform-specific /// - /// - **iOS / Android:** Unsupported. Returns ane empty string. + /// - **iOS / Android:** Unsupported. Returns an empty string. #[inline] pub fn title(&self) -> String { self.window.title() @@ -1661,6 +1661,7 @@ pub enum ResizeDirection { West, } +#[cfg(any(target_os = "windows", target_os = "linux"))] pub(crate) fn hit_test( (left, top, right, bottom): (i32, i32, i32, i32), cx: i32, diff --git a/ports/tao/tao-macros/CHANGELOG.md b/ports/tao/tao-macros/CHANGELOG.md index 2fe0117a3060..99fffd69135f 100644 --- a/ports/tao/tao-macros/CHANGELOG.md +++ b/ports/tao/tao-macros/CHANGELOG.md @@ -1,5 +1,9 @@ # Changelog +## \[0.1.4] + +- [`2b818c4e`](https://github.com/tauri-apps/tao/commit/2b818c4e84288c42365f8c790f4e7a519f6376ec) ([#1288](https://github.com/tauri-apps/tao/pull/1288) by [@Legend-Master](https://github.com/tauri-apps/tao/../../Legend-Master)) Fix `android_fn!` with no jni args but additional args fail to compile + ## \[0.1.3] - [`4cd53415`](https://github.com/tauri-apps/tao/commit/4cd534151a2d7a14ade906f960ec02655a91feae) ([#964](https://github.com/tauri-apps/tao/pull/964) by [@lucasfernog](https://github.com/tauri-apps/tao/../../lucasfernog)) Allow Android domain names to include `_1` as escaped `_` characters - required because `_` is the separator for domain parts. diff --git a/ports/tao/tao-macros/Cargo.toml b/ports/tao/tao-macros/Cargo.toml index f70f3054ba34..593730b55a3c 100644 --- a/ports/tao/tao-macros/Cargo.toml +++ b/ports/tao/tao-macros/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "tao-macros" description = "Proc macros for tao" -version = "0.1.3" +version = "0.1.4" edition = "2021" authors = ["Tauri Programme within The Commons Conservancy"] rust-version = "1.74" diff --git a/ports/tao/tao-macros/src/lib.rs b/ports/tao/tao-macros/src/lib.rs index 5ecf49c48ced..cd1e5673d919 100644 --- a/ports/tao/tao-macros/src/lib.rs +++ b/ports/tao/tao-macros/src/lib.rs @@ -237,6 +237,14 @@ pub fn android_fn(tokens: TokenStream) -> TokenStream { syn::ReturnType::Default }; + let function_before = function_before.map(|function_before| quote!(#function_before();)); + + let comma_before_args = if args.is_empty() { + None + } else { + Some(syn::token::Comma(proc_macro2::Span::call_site())) + }; + let comma_before_non_jni_args = if non_jni_args.is_empty() { None } else { @@ -250,8 +258,8 @@ pub fn android_fn(tokens: TokenStream) -> TokenStream { class: JClass<'local>, #(#args),* ) #ret { - #function_before(); - #function(env, class, #(#args_),* #comma_before_non_jni_args #(#non_jni_args),*) + #function_before + #function(env, class #comma_before_args #(#args_),* #comma_before_non_jni_args #(#non_jni_args),*) } } diff --git a/ports/wry/.changes/change-pr-1736.md b/ports/wry/.changes/change-pr-1736.md deleted file mode 100644 index 93686a72fb32..000000000000 --- a/ports/wry/.changes/change-pr-1736.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'wry': minor ---- - -Remove Send, Sync bounds from NewWindowOpener. diff --git a/ports/wry/.changes/change-pr-1748.md b/ports/wry/.changes/change-pr-1748.md deleted file mode 100644 index e7fad51afc95..000000000000 --- a/ports/wry/.changes/change-pr-1748.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'wry': minor ---- - -Add back/forward navigation APIs (`go_back`, `go_forward`, `can_go_back`, and `can_go_forward`) to `WebView`. diff --git a/ports/wry/.changes/early-mutex-release.md b/ports/wry/.changes/early-mutex-release.md deleted file mode 100644 index c58313360567..000000000000 --- a/ports/wry/.changes/early-mutex-release.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'wry': minor ---- - -On Android, release REQUEST_HANDLER mutex sooner by cloning the underlying Send + Sync handler. diff --git a/ports/wry/.changes/forward-hwnd-handler.md b/ports/wry/.changes/forward-hwnd-handler.md deleted file mode 100644 index 496b400bbdfb..000000000000 --- a/ports/wry/.changes/forward-hwnd-handler.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'wry': minor ---- - -On Windows, Add `WebViewExtWindows::hwnd` getter to access the child HWND containing the webview. diff --git a/ports/wry/.changes/macos-protocol-body-nocopy.md b/ports/wry/.changes/macos-protocol-body-nocopy.md deleted file mode 100644 index 4cf9286fee4a..000000000000 --- a/ports/wry/.changes/macos-protocol-body-nocopy.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'wry': patch ---- - -On macOS, avoid an extra copy for owned custom protocol response bodies by transferring the body buffer into `NSData`. diff --git a/ports/wry/.changes/objc2-0.3.2.md b/ports/wry/.changes/objc2-0.3.2.md deleted file mode 100644 index 4d1ded2d190e..000000000000 --- a/ports/wry/.changes/objc2-0.3.2.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'wry': patch ---- - -On macOS, updated `objc2-*` dependencies to 0.3.2 diff --git a/ports/wry/.changes/permission-handler.md b/ports/wry/.changes/permission-handler.md deleted file mode 100644 index 42a5f99025f2..000000000000 --- a/ports/wry/.changes/permission-handler.md +++ /dev/null @@ -1,13 +0,0 @@ ---- -'wry': minor ---- - -Add an expanded permission handling API for WebView2, WKWebView, WebKitGTK, and Android. -This includes: - -- `PermissionKind` expansion: `DisplayCapture`, `Midi`, `Sensors`, `MediaKeySystemAccess`, `LocalFonts`, `WindowManagement`, `PointerLock`, `AutomaticDownloads`, `FileSystemAccess`, `Autoplay`. -- Support for `PermissionResponse::Prompt` to trigger native system dialogs. -- Android support via JNI bridge (`onPermissionRequest` in `RustWebChromeClient`). -- macOS: Split camera/microphone requests; `CameraAndMicrophone` resolved from individual responses. -- Linux: `DisplayCapture` detection for WebKitGTK < 2.42 (getDisplayMedia fix). -- Windows: Full coverage of all 12 `COREWEBVIEW2_PERMISSION_KIND` values. diff --git a/ports/wry/.changes/windows-with-profile-name.md b/ports/wry/.changes/windows-with-profile-name.md deleted file mode 100644 index 786844c5b28f..000000000000 --- a/ports/wry/.changes/windows-with-profile-name.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'wry': minor ---- - -On Windows, add `WebViewBuilderExtWindows::with_profile_name` to opt the webview into a named WebView2 profile. Webviews with different profile names within the same environment have isolated cookies, storage, IndexedDB, and cache while sharing the runtime — matching WebView2's documented multi-profile pattern. diff --git a/ports/wry/CHANGELOG.md b/ports/wry/CHANGELOG.md index 0812b6845dc2..32748167a8d2 100644 --- a/ports/wry/CHANGELOG.md +++ b/ports/wry/CHANGELOG.md @@ -1,5 +1,44 @@ # Changelog +## \[0.56.0] + +- [`affbb3c`](https://github.com/tauri-apps/wry/commit/affbb3cbccc618663737f9b629f2aa21d3890159) ([#1720](https://github.com/tauri-apps/wry/pull/1720)) Updated Android lifecycle JNI calls in `WryActivity` for Tao 0.36's renames: + + > - `create` to `onFirstActivityCreate` + > - `onActivityCreate` to `onCreate` + > - `start` to `onStart` + > - `resume` to `onResume` + > - `pause` to `onPause` + > - `stop` to `onStop` + > - Removed `onActivitySaveInstanceState` + > - `onActivityDestroy` to `onDestroy` + > - `onActivityLowMemory` to `onLowMemory` + > + > `onLowMemory` no longer takes any parameters. + > `onFirstActivityCreate` no longer takes any parameters. + + and also emitting them as window-specific events. +- [`145b36f`](https://github.com/tauri-apps/wry/commit/145b36fb5e3551851a9afbdf9c34154e2388f85d) ([#1736](https://github.com/tauri-apps/wry/pull/1736)) Remove Send, Sync bounds from NewWindowOpener. +- [`871497a`](https://github.com/tauri-apps/wry/commit/871497a726adaa2a29ab4a51b8bff23f23d22182) ([#1748](https://github.com/tauri-apps/wry/pull/1748)) Add back/forward navigation APIs (`go_back`, `go_forward`, `can_go_back`, and `can_go_forward`) to `WebView`. +- [`fe52ff9`](https://github.com/tauri-apps/wry/commit/fe52ff9374fb4d649eda7d7f6fccb8b047fb49ba) ([#1715](https://github.com/tauri-apps/wry/pull/1715)) On Android, release REQUEST_HANDLER mutex sooner by cloning the underlying Send + Sync handler. +- [`9d0a9fe`](https://github.com/tauri-apps/wry/commit/9d0a9fe31321797b2fb4dd83e01408df9d7fedcc) ([#1712](https://github.com/tauri-apps/wry/pull/1712)) On Windows, Add `WebViewExtWindows::hwnd` getter to access the child HWND containing the webview. +- [`4ee8c38`](https://github.com/tauri-apps/wry/commit/4ee8c38651683ca69530a837a830d0e4028a6c44) ([#1779](https://github.com/tauri-apps/wry/pull/1779)) Enable WebView element fullscreen support on macOS 12.3+ and iOS 15.4+ without using private APIs. +- [`5bdda32`](https://github.com/tauri-apps/wry/commit/5bdda323937c01e497a9735ce83a3636202a8a6a) ([#1719](https://github.com/tauri-apps/wry/pull/1719)) On macOS, avoid an extra copy for owned custom protocol response bodies by transferring the body buffer into `NSData`. +- [`6ef93b3`](https://github.com/tauri-apps/wry/commit/6ef93b3e99161446d8a829d8c985074c6f5d1c9c) ([#1737](https://github.com/tauri-apps/wry/pull/1737)) On macOS, updated `objc2-*` dependencies to 0.3.2 +- [`9f9a237`](https://github.com/tauri-apps/wry/commit/9f9a237d781ad972442ea7330ecce885de08a445) ([#1654](https://github.com/tauri-apps/wry/pull/1654)) Add an expanded permission handling API for WebView2, WKWebView, WebKitGTK, and Android. + This includes: + + - `PermissionKind` expansion: `DisplayCapture`, `Midi`, `Sensors`, `MediaKeySystemAccess`, `LocalFonts`, `WindowManagement`, `PointerLock`, `AutomaticDownloads`, `FileSystemAccess`, `Autoplay`. + - Support for `PermissionResponse::Prompt` to trigger native system dialogs. + - Android support via JNI bridge (`onPermissionRequest` in `RustWebChromeClient`). + - macOS: Split camera/microphone requests; `CameraAndMicrophone` resolved from individual responses. + - Linux: `DisplayCapture` detection for WebKitGTK < 2.42 (getDisplayMedia fix). + - Windows: Full coverage of all 12 `COREWEBVIEW2_PERMISSION_KIND` values. +- [`bd1decc`](https://github.com/tauri-apps/wry/commit/bd1decc497517cd5f035a65e75a965c1311dc62a) ([#1780](https://github.com/tauri-apps/wry/pull/1780)) **Breaking Change:** Removed the macOS/iOS specific `transparent` and `fullscreen` feature flags. The functionalities are now always enabled. Please reach out immediatly if you see issues in the App Store submission process. +- [`d29cd57`](https://github.com/tauri-apps/wry/commit/d29cd576761770debeac351dc6ab96b8300465e3) ([#1787](https://github.com/tauri-apps/wry/pull/1787)) Removed `protocol` feature, previously gated `WebViewBuilder::with_custom_protocol`, `WebViewBuilder::with_asynchronous_custom_protocol` and `WebViewBuilder::with_asset_loader` are now always available +- [`86ac6e8`](https://github.com/tauri-apps/wry/commit/86ac6e8891be99efa21756d751573bcbaa22554c) ([#1732](https://github.com/tauri-apps/wry/pull/1732)) Updated `dom_query` dependency to 0.28.0 +- [`10fdf73`](https://github.com/tauri-apps/wry/commit/10fdf7308013d40741990c60e960cf82b18d9372) ([#1738](https://github.com/tauri-apps/wry/pull/1738)) On Windows, add `WebViewBuilderExtWindows::with_profile_name` to opt the webview into a named WebView2 profile. Webviews with different profile names within the same environment have isolated cookies, storage, IndexedDB, and cache while sharing the runtime — matching WebView2's documented multi-profile pattern. + ## \[0.55.1] - [`3860359`](https://github.com/tauri-apps/wry/commit/3860359e3dc75260fb9865f7a8eae00a9ec4733d) ([#1721](https://github.com/tauri-apps/wry/pull/1721) by [@lucasfernog](https://github.com/tauri-apps/wry/../../lucasfernog)) Tweak ProGuard rules to keep the getId required activity method. diff --git a/ports/wry/Cargo.lock b/ports/wry/Cargo.lock index fea8a67c43d2..ddea43e72923 100644 --- a/ports/wry/Cargo.lock +++ b/ports/wry/Cargo.lock @@ -1,12 +1,12 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. -version = 4 +version = 3 [[package]] name = "ab_glyph" -version = "0.2.32" +version = "0.2.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01c0457472c38ea5bd1c3b5ada5e368271cb550be7a4ca4a0b4634e9913f6cc2" +checksum = "ec3672c180e71eeaaac3a541fbbc5f5ad4def8b747c595ad30d674e43049f7b0" dependencies = [ "ab_glyph_rasterizer", "owned_ttf_parser", @@ -14,18 +14,18 @@ dependencies = [ [[package]] name = "ab_glyph_rasterizer" -version = "0.1.10" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "366ffbaa4442f4684d91e2cd7c5ea7c4ed8add41959a31447066e279e432b618" +checksum = "c71b1793ee61086797f5c80b6efa2b8ffa6d5dd703f118545808a7f2e27f7046" [[package]] name = "ahash" -version = "0.8.12" +version = "0.8.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75" +checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" dependencies = [ "cfg-if", - "getrandom 0.3.4", + "getrandom 0.2.15", "once_cell", "version_check", "zerocopy", @@ -33,21 +33,23 @@ dependencies = [ [[package]] name = "android-activity" -version = "0.6.1" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f2a1bb052857d5dd49572219344a7332b31b76405648eabac5bc68978251bcd" +checksum = "ef6978589202a00cd7e118380c448a08b6ed394c3a8df3a430d0898e3a42d046" dependencies = [ "android-properties", - "bitflags 2.13.0", + "bitflags 2.8.0", "cc", - "jni 0.22.4", + "cesu8", + "jni", + "jni-sys", "libc", "log", "ndk", "ndk-context", "ndk-sys 0.6.0+11769913", "num_enum", - "thiserror 2.0.18", + "thiserror 1.0.69", ] [[package]] @@ -73,9 +75,9 @@ checksum = "76a2e8124351fda1ef8aaaa3bbd7ebbcb486bbcd4225aca0aa0d84bb2db8fecb" [[package]] name = "arrayvec" -version = "0.7.8" +version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3fb67a6e08acf24fdeccbac2cb6ac4305825bd1f117462e0e6f2f193345ad56" +checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" [[package]] name = "as-raw-xcb-connection" @@ -93,15 +95,26 @@ dependencies = [ ] [[package]] -name = "async-channel" -version = "2.5.0" +name = "atk" +version = "0.18.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "924ed96dd52d1b75e9c1a3e6275715fd320f5f9439fb5a4a11fa51f4221158d2" +checksum = "241b621213072e993be4f6f3a9e4b45f65b7e6faad43001be957184b7bb1824b" dependencies = [ - "concurrent-queue", - "event-listener-strategy", - "futures-core", - "pin-project-lite", + "atk-sys", + "glib", + "libc", +] + +[[package]] +name = "atk-sys" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c5e48b684b0ca77d2bbadeef17424c2ea3c897d44d566a1617e7e8f30614d086" +dependencies = [ + "glib-sys", + "gobject-sys", + "libc", + "system-deps", ] [[package]] @@ -112,9 +125,9 @@ checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" [[package]] name = "autocfg" -version = "1.5.1" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53" +checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" [[package]] name = "base64" @@ -145,9 +158,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.13.0" +version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4388bee8683e3d04af747c73422af53102d2bd24d9eadb6cbc100baef4b43f8" +checksum = "8f68f53c83ab957f72c32642f3868eec03eb974d1fb82e453128456482613d36" [[package]] name = "block" @@ -184,39 +197,41 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.20.3" +version = "3.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72f5acc6cb2ba439de613abc23857ec3d78374d8ed5ac84e9d11336e87da8649" +checksum = "1628fb46dfa0b37568d12e5edd512553eccf6a22a78e8bde00bb4aed84d5bdbf" [[package]] name = "bytemuck" -version = "1.25.0" +version = "1.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8efb64bd706a16a1bdde310ae86b351e4d21550d98d056f22f8a7f7a2183fec" +checksum = "ef657dfab802224e671f5818e9a4935f9b1957ed18e58292690cc39e7a4092a3" [[package]] name = "bytes" -version = "1.12.0" +version = "1.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ae3f5d315924270530207e2a68396c3cc547f6dca3fbdca317cfb1a51edb593" +checksum = "fc652a48c352aef3ea3aed32080501cf3ef6ed5da78602a020c991775b0aff04" [[package]] name = "cairo-rs" -version = "0.22.0" +version = "0.18.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5cc8d9aa793480744cd9a0524fef1a2e197d9eaa0f739cde19d16aba530dcb95" +checksum = "8ca26ef0159422fb77631dc9d17b102f253b876fe1586b03b803e63a309b4ee2" dependencies = [ - "bitflags 2.13.0", + "bitflags 2.8.0", "cairo-sys-rs", "glib", "libc", + "once_cell", + "thiserror 1.0.69", ] [[package]] name = "cairo-sys-rs" -version = "0.22.0" +version = "0.18.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8b4985713047f5faee02b8db6a6ef32bbb50269ff53c1aee716d1d195b76d54" +checksum = "685c9fa8e590b8b3d678873528d83411db17242a73fccaed827770ea0fedda51" dependencies = [ "glib-sys", "libc", @@ -229,10 +244,10 @@ version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b99da2f8558ca23c71f4fd15dc57c906239752dd27ff3c00a1d56b685b7cbfec" dependencies = [ - "bitflags 2.13.0", + "bitflags 2.8.0", "log", "polling", - "rustix 0.38.44", + "rustix", "slab", "thiserror 1.0.69", ] @@ -244,18 +259,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "95a66a987056935f7efce4ab5668920b5d0dac4a7c99991a67395f13702ddd20" dependencies = [ "calloop", - "rustix 0.38.44", + "rustix", "wayland-backend", "wayland-client", ] [[package]] name = "cc" -version = "1.2.66" +version = "1.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f5d6cac793997bd970000024b2934968efe83b382de4fdcf4fcb46b6ee4ad996" +checksum = "c736e259eea577f443d5c86c304f9f4ae0295c43f3ba05c21f1d66b5f06001af" dependencies = [ - "find-msvc-tools", "jobserver", "libc", "shlex", @@ -269,9 +283,9 @@ checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c" [[package]] name = "cfg-expr" -version = "0.20.8" +version = "0.15.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb693542bcafa528e198be0ebd9d3632ca5b7c93dbe7237460e199910835997c" +checksum = "d067ad48b8650848b989a59a86c6c36a995d02d2bf778d45c3c5d57bc2718f02" dependencies = [ "smallvec", "target-lexicon", @@ -279,9 +293,9 @@ dependencies = [ [[package]] name = "cfg-if" -version = "1.0.4" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "cfg_aliases" @@ -346,9 +360,9 @@ dependencies = [ [[package]] name = "core-foundation" -version = "0.10.1" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2a6cd9ae233e7f62ba4e9353e81a88df7fc8a5987b8d445b4d90c879bd156f6" +checksum = "b55271e5c8c478ad3f38ad24ef34923091e0548492a266d19b3c0b4d82574c63" dependencies = [ "core-foundation-sys", "libc", @@ -379,8 +393,8 @@ version = "0.25.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "064badf302c3194842cf2c5d61f56cc88e54a759313879cdf03abdd27d0c3b97" dependencies = [ - "bitflags 2.13.0", - "core-foundation 0.10.1", + "bitflags 2.8.0", + "core-foundation 0.10.0", "core-graphics-types 0.2.0", "foreign-types", "libc", @@ -403,8 +417,8 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3d44a101f213f6c4cdc1853d4b78aef6db6bdfa3468798cc1d9912f4735013eb" dependencies = [ - "bitflags 2.13.0", - "core-foundation 0.10.1", + "bitflags 2.8.0", + "core-foundation 0.10.0", "libc", ] @@ -419,24 +433,24 @@ dependencies = [ [[package]] name = "crossbeam-channel" -version = "0.5.16" +version = "0.5.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d85363c37faeca707aef026efa9f3b34d077bce547e48f770770625c6013679e" +checksum = "82b8f8f868b36967f9606790d1903570de9ceaf870a7bf9fbbd3016d636a2cb2" dependencies = [ "crossbeam-utils", ] [[package]] name = "crossbeam-utils" -version = "0.8.22" +version = "0.8.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61803da095bee82a81bb1a452ecc25d3b2f1416d1897eb86430c6159ef717c17" +checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" [[package]] name = "crypto-common" -version = "0.1.7" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" dependencies = [ "generic-array", "typenum", @@ -462,51 +476,53 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "10a2a99df6e410a8ff4245aa2006499ea662245f967cc7c0a38c83ef8eb44dbf" dependencies = [ "quote", - "syn", + "syn 2.0.98", ] [[package]] name = "cursor-icon" -version = "1.2.0" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f27ae1dd37df86211c42e150270f82743308803d90a6f6e6651cd730d5e1732f" +checksum = "96a6ac251f4a2aca6b3f91340350eab87ae57c3f127ffeb585e92bd336717991" [[package]] name = "dbus" -version = "0.9.12" +version = "0.9.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ab69f03cc8c4340c9c8e315114e1658e6775a9b16a04357973aa21cec22b32e" +checksum = "21b3aa68d7e7abee336255bd7248ea965cc393f3e70411135a6f6a4b651345d4" dependencies = [ "libc", "libdbus-sys", - "windows-sys 0.61.2", + "windows-sys 0.59.0", ] [[package]] name = "deranged" -version = "0.5.8" +version = "0.3.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7cd812cc2bc1d69d4764bd80df88b4317eaef9e773c75226407d9bc0876b211c" +checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4" +dependencies = [ + "powerfmt", +] [[package]] name = "derive_more" -version = "2.1.1" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d751e9e49156b02b44f9c1815bcb94b984cdcc4396ecc32521c739452808b134" +checksum = "093242cf7570c207c83073cf82f79706fe7b8317e98620a47d5be7c3d8497678" dependencies = [ "derive_more-impl", ] [[package]] name = "derive_more-impl" -version = "2.1.1" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "799a97264921d8623a957f6c3b9011f3b5492f557bbb7a5a19b7fa6d06ba8dcb" +checksum = "bda628edc44c4bb645fbe0f758797143e4e07926f7ebf4e9bdfbd3d2ce621df3" dependencies = [ "proc-macro2", "quote", - "rustc_version", - "syn", + "syn 2.0.98", ] [[package]] @@ -552,7 +568,7 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e0e367e4e7da84520dedcac1901e4da967309406d1e51017ae1abfb97adbd38" dependencies = [ - "bitflags 2.13.0", + "bitflags 2.8.0", "block2 0.6.2", "libc", "objc2 0.6.4", @@ -560,29 +576,29 @@ dependencies = [ [[package]] name = "displaydoc" -version = "0.2.6" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ac70aa55017e108007fbaf5aa0f54b021c98f92ff8af59d42eda9da96e3dd4f" +checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.98", ] [[package]] name = "dlib" -version = "0.5.3" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab8ecd87370524b461f8557c119c405552c396ed91fc0a8eec68679eab26f94a" +checksum = "330c60081dcc4c72131f8eb70510f1ac07223e5d4163db481a04a0befcffa412" dependencies = [ "libloading", ] [[package]] name = "dlopen2" -version = "0.8.2" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e2c5bd4158e66d1e215c49b837e11d62f3267b30c92f1d171c4d3105e3dc4d4" +checksum = "b54f373ccf864bf587a89e880fb7610f8d73f3045f13580948ccbcaff26febff" dependencies = [ "dlopen2_derive", "libc", @@ -592,20 +608,20 @@ dependencies = [ [[package]] name = "dlopen2_derive" -version = "0.4.3" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fbbb781877580993a8707ec48672673ec7b81eeba04cfd2310bd28c08e47c8f" +checksum = "f2b99bf03862d7f545ebc28ddd33a665b50865f4dfd84031a393823879bd4c54" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.98", ] [[package]] name = "document-features" -version = "0.2.12" +version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4b8a88685455ed29a21542a33abd9cb6510b6b129abadabdcef0f4c55bc8f61" +checksum = "95249b50c6c185bee49034bcb378a49dc2b5dff0be90ff6616d31d64febab05d" dependencies = [ "litrs", ] @@ -633,18 +649,18 @@ checksum = "75b325c5dbd37f80359721ad39aca5a29fb04c89279657cffdda8736d0c0b9d2" [[package]] name = "dpi" -version = "0.1.2" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8b14ccef22fc6f5a8f4d7d768562a182c04ce9a3b3157b91390b52ddfdf1a76" +checksum = "f25c0e292a7ca6d6498557ff1df68f32c99850012b6ea401cf8daf771f22ff53" dependencies = [ "serde", ] [[package]] name = "dtoa" -version = "1.0.11" +version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c3cf4824e2d5f025c7b531afcb2325364084a16806f6d47fbc1f5fbd9960590" +checksum = "dcbb2bf8e87535c23f7a8a321e364ce21462d0ff10cb6407820e8e96dfff6653" [[package]] name = "dtoa-short" @@ -669,40 +685,19 @@ checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" [[package]] name = "errno" -version = "0.3.14" +version = "0.3.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" +checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d" dependencies = [ "libc", "windows-sys 0.59.0", ] -[[package]] -name = "event-listener" -version = "5.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e13b66accf52311f30a0db42147dadea9850cb48cd070028831ae5f5d4b856ab" -dependencies = [ - "concurrent-queue", - "parking", - "pin-project-lite", -] - -[[package]] -name = "event-listener-strategy" -version = "0.5.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8be9f3dfaaffdae2972880079a491a1a8bb7cbed0b8dd7a347f668b4150a3b93" -dependencies = [ - "event-listener", - "pin-project-lite", -] - [[package]] name = "fastrand" -version = "2.4.1" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f1f227452a390804cdb637b74a86990f2a7d7ba4b7d5693aac9b4dd6defd8d6" +checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" [[package]] name = "field-offset" @@ -715,16 +710,16 @@ dependencies = [ ] [[package]] -name = "find-msvc-tools" -version = "0.1.9" +name = "fnv" +version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" [[package]] name = "foldhash" -version = "0.1.5" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" +checksum = "a0d2fde1f7b3d48b8395d5f2de76c18a528bd6a9cdde438df747bfcba3e05d6f" [[package]] name = "foldhash" @@ -750,7 +745,7 @@ checksum = "1a5c6c585bc94aaf2c7b51dd4c2ba22680844aba4c687be581871a6f518c5742" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.98", ] [[package]] @@ -761,33 +756,33 @@ checksum = "aa9a19cbb55df58761df49b23516a86d432839add4af60fc256da840f66ed35b" [[package]] name = "form_urlencoded" -version = "1.2.2" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb4cb245038516f5f85277875cdaa4f7d2c9a0fa0468de06ed190163b1581fcf" +checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" dependencies = [ "percent-encoding", ] [[package]] name = "futures-channel" -version = "0.3.32" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07bbe89c50d7a535e539b8c17bc0b49bdb77747034daa8087407d655f3f7cc1d" +checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" dependencies = [ "futures-core", ] [[package]] name = "futures-core" -version = "0.3.32" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d" +checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" [[package]] name = "futures-executor" -version = "0.3.32" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf29c38818342a3b26b5b923639e7b1f4a61fc5e76102d4b1981c6dc7a7579d" +checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f" dependencies = [ "futures-core", "futures-task", @@ -796,57 +791,74 @@ dependencies = [ [[package]] name = "futures-io" -version = "0.3.32" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cecba35d7ad927e23624b22ad55235f2239cfa44fd10428eecbeba6d6a717718" +checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" [[package]] name = "futures-macro" -version = "0.3.32" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e835b70203e41293343137df5c0664546da5745f82ec9b84d40be8336958447b" +checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.98", ] [[package]] name = "futures-task" -version = "0.3.32" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393" +checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" [[package]] name = "futures-util" -version = "0.3.32" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6" +checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" dependencies = [ "futures-core", "futures-macro", "futures-task", "pin-project-lite", + "pin-utils", "slab", ] +[[package]] +name = "gdk" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9f245958c627ac99d8e529166f9823fb3b838d1d41fd2b297af3075093c2691" +dependencies = [ + "cairo-rs", + "gdk-pixbuf", + "gdk-sys", + "gio", + "glib", + "libc", + "pango", +] + [[package]] name = "gdk-pixbuf" -version = "0.22.0" +version = "0.18.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25f420376dbee041b2db374ce4573892a36222bb3f6c0c43e24f0d67eae9b646" +checksum = "50e1f5f1b0bfb830d6ccc8066d18db35c487b1b2b1e8589b5dfe9f07e8defaec" dependencies = [ "gdk-pixbuf-sys", "gio", "glib", "libc", + "once_cell", ] [[package]] name = "gdk-pixbuf-sys" -version = "0.22.0" +version = "0.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48f31b37b1fc4b48b54f6b91b7ef04c18e00b4585d98359dd7b998774bbd91fb" +checksum = "3f9839ea644ed9c97a34d129ad56d38a25e6756f99f3a88e15cd39c20629caf7" dependencies = [ "gio-sys", "glib-sys", @@ -856,25 +868,10 @@ dependencies = [ ] [[package]] -name = "gdk4" -version = "0.11.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d81e2a6c6ecba2aab60633a98df1868b03fa0bfdce8105edc27c1bccf71f0e39" -dependencies = [ - "cairo-rs", - "gdk-pixbuf", - "gdk4-sys", - "gio", - "glib", - "libc", - "pango", -] - -[[package]] -name = "gdk4-sys" -version = "0.11.4" +name = "gdk-sys" +version = "0.18.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d8f608d8d7d229975c4d0d026f5d3071598c4ddab3c5262b0a31840fec78d13" +checksum = "5c2d13f38594ac1e66619e188c6d5a1adb98d11b2fcf7894fc416ad76aa2f3f7" dependencies = [ "cairo-sys-rs", "gdk-pixbuf-sys", @@ -888,55 +885,44 @@ dependencies = [ ] [[package]] -name = "gdk4-wayland" -version = "0.11.4" +name = "gdkwayland-sys" +version = "0.18.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32d95828dadc0357dee236cc6a5712808bd894d3217159982592ce24f2b8aaf7" -dependencies = [ - "gdk4", - "gdk4-wayland-sys", - "gio", - "glib", - "libc", - "wayland-backend", - "wayland-client", - "wayland-protocols", -] - -[[package]] -name = "gdk4-wayland-sys" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "653cafb8715f2ac1c56edaf8060d9360ae3acbe3c6fb61b676a2917d42b668cf" +checksum = "140071d506d223f7572b9f09b5e155afbd77428cd5cc7af8f2694c41d98dfe69" dependencies = [ + "gdk-sys", "glib-sys", + "gobject-sys", "libc", + "pkg-config", "system-deps", ] [[package]] -name = "gdk4-x11" -version = "0.11.4" +name = "gdkx11" +version = "0.18.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d892b4cf5a07e90e1390e93cd792975746c68b59f790a2de9e96ce77211ea9a" +checksum = "3caa00e14351bebbc8183b3c36690327eb77c49abc2268dd4bd36b856db3fbfe" dependencies = [ - "gdk4", - "gdk4-x11-sys", + "gdk", + "gdkx11-sys", "gio", "glib", "libc", + "x11", ] [[package]] -name = "gdk4-x11-sys" -version = "0.11.0" +name = "gdkx11-sys" +version = "0.18.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24d41f2e28f62378b081288914325d73bddbb6ccbbadb41aa6392afc71d27002" +checksum = "6e2e7445fe01ac26f11601db260dd8608fe172514eb63b3b5e261ea6b0f4428d" dependencies = [ - "gdk4-sys", + "gdk-sys", "glib-sys", "libc", "system-deps", + "x11", ] [[package]] @@ -951,53 +937,42 @@ dependencies = [ [[package]] name = "gethostname" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bd49230192a3797a9a4d6abe9b3eed6f7fa4c8a8a4947977c6f80025f92cbd8" -dependencies = [ - "rustix 1.1.4", - "windows-link 0.2.1", -] - -[[package]] -name = "getrandom" -version = "0.2.17" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0" +checksum = "0176e0459c2e4a1fe232f984bca6890e681076abb9934f6cea7c326f3fc47818" dependencies = [ - "cfg-if", "libc", - "wasi", + "windows-targets 0.48.5", ] [[package]] name = "getrandom" -version = "0.3.4" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" +checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" dependencies = [ "cfg-if", "libc", - "r-efi 5.3.0", - "wasip2", + "wasi 0.11.0+wasi-snapshot-preview1", ] [[package]] name = "getrandom" -version = "0.4.3" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "300e883d756b2e4ec94e02791f39b04b522276138852cfc41d9fb7e904106099" +checksum = "43a49c392881ce6d5c3b8cb70f98717b7c07aabbdff06687b9030dbfbe2725f8" dependencies = [ "cfg-if", "libc", - "r-efi 6.0.0", + "wasi 0.13.3+wasi-0.2.2", + "windows-targets 0.52.6", ] [[package]] name = "gio" -version = "0.22.8" +version = "0.18.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b3e1f669909c326b9413bde5a742097b8c90a7d78f45326db13668984769ded" +checksum = "d4fc8f532f87b79cbc51a79748f16a6828fb784be93145a322fa14d06d354c73" dependencies = [ "futures-channel", "futures-core", @@ -1006,21 +981,23 @@ dependencies = [ "gio-sys", "glib", "libc", + "once_cell", "pin-project-lite", "smallvec", + "thiserror 1.0.69", ] [[package]] name = "gio-sys" -version = "0.22.8" +version = "0.18.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "353fdc7da7cd16da916104b1e0e4e7de380ec9c8aaa20d4d742d66310ab4b0d5" +checksum = "37566df850baf5e4cb0dfb78af2e4b9898d817ed9263d1090a2df958c64737d2" dependencies = [ "glib-sys", "gobject-sys", "libc", "system-deps", - "windows-sys 0.59.0", + "winapi", ] [[package]] @@ -1036,11 +1013,11 @@ dependencies = [ [[package]] name = "glib" -version = "0.22.8" +version = "0.18.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ddbcf514bd1881fc1b960e4e52b4e82873f4da3bceddbd58d42827b508888100" +checksum = "233daaf6e83ae6a12a52055f568f9d7cf4671dabb78ff9560ab6da230ce00ee5" dependencies = [ - "bitflags 2.13.0", + "bitflags 2.8.0", "futures-channel", "futures-core", "futures-executor", @@ -1052,26 +1029,30 @@ dependencies = [ "gobject-sys", "libc", "memchr", + "once_cell", "smallvec", + "thiserror 1.0.69", ] [[package]] name = "glib-macros" -version = "0.22.6" +version = "0.18.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "506d23499707c7142898429757e8d9a3871d965239a2cb66dfa05052be6d6f19" +checksum = "0bb0228f477c0900c880fd78c8759b95c7636dbd7842707f49e132378aa2acdc" dependencies = [ - "heck", + "heck 0.4.1", + "proc-macro-crate 2.0.2", + "proc-macro-error", "proc-macro2", "quote", - "syn", + "syn 2.0.98", ] [[package]] name = "glib-sys" -version = "0.22.8" +version = "0.18.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "030967459f9f676851872c6304adea7825c6d462ec9b72554c733cf0c5952233" +checksum = "063ce2eb6a8d0ea93d2bf8ba1957e78dbab6be1c2220dd3daca57d5a9d869898" dependencies = [ "libc", "system-deps", @@ -1112,9 +1093,9 @@ dependencies = [ [[package]] name = "gobject-sys" -version = "0.22.6" +version = "0.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22a861859b887a79cf461359c192c97a57d8fb0229dd291232e57aa11f6fa72c" +checksum = "0850127b514d1c4a4654ead6dedadb18198999985908e6ffe4436f53c785ce44" dependencies = [ "glib-sys", "libc", @@ -1123,21 +1104,21 @@ dependencies = [ [[package]] name = "gpu-alloc" -version = "0.6.2" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "45cf04b2726f02df5508c6de726acdc90cdf97ac771a9a0ffd8ba10a6e696bf9" +checksum = "fbcd2dba93594b227a1f57ee09b8b9da8892c34d55aa332e034a228d0fe6a171" dependencies = [ - "bitflags 2.13.0", + "bitflags 2.8.0", "gpu-alloc-types", ] [[package]] name = "gpu-alloc-types" -version = "0.3.1" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2bbed164dd10ed526c2e4fe3e721ca4a71c61730e5aafac6844b417b3227058" +checksum = "98ff03b468aa837d70984d55f5d3f846f6ec31fe34bbb97c4f85219caeee1ca4" dependencies = [ - "bitflags 2.13.0", + "bitflags 2.8.0", ] [[package]] @@ -1154,13 +1135,13 @@ dependencies = [ [[package]] name = "gpu-descriptor" -version = "0.3.2" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b89c83349105e3732062a895becfc71a8f921bb71ecbbdd8ff99263e3b53a0ca" +checksum = "dcf29e94d6d243368b7a56caa16bc213e4f9f8ed38c4d9557069527b5d5281ca" dependencies = [ - "bitflags 2.13.0", + "bitflags 2.8.0", "gpu-descriptor-types", - "hashbrown 0.15.5", + "hashbrown", ] [[package]] @@ -1169,127 +1150,75 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fdf242682df893b86f33a73828fb09ca4b2d3bb6cc95249707fc684d27484b91" dependencies = [ - "bitflags 2.13.0", -] - -[[package]] -name = "graphene-rs" -version = "0.22.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb856b9c558971c3f13ab692358926da710b046932a4e087aedcc35b040d7dff" -dependencies = [ - "glib", - "graphene-sys", -] - -[[package]] -name = "graphene-sys" -version = "0.22.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c7ffdfde88f3570d3705e0d8a2433e036d387a1f2930bbf47eafcb5f569fd04" -dependencies = [ - "glib-sys", - "libc", - "system-deps", + "bitflags 2.8.0", ] [[package]] -name = "gsk4" -version = "0.11.4" +name = "gtk" +version = "0.18.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b867be1c5f14dcb8f552c0eff6e9a9b1da5f8b43943e8efc3a63c889d84952ff" +checksum = "fd56fb197bfc42bd5d2751f4f017d44ff59fbb58140c6b49f9b3b2bdab08506a" dependencies = [ + "atk", "cairo-rs", - "gdk4", + "field-offset", + "futures-channel", + "gdk", + "gdk-pixbuf", + "gio", "glib", - "graphene-rs", - "gsk4-sys", + "gtk-sys", + "gtk3-macros", "libc", "pango", + "pkg-config", ] [[package]] -name = "gsk4-sys" -version = "0.11.4" +name = "gtk-sys" +version = "0.18.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b7c7eb2e681ee896646cfb8872b431f24d09f53ba9283289d9b10caa6707088" +checksum = "8f29a1c21c59553eb7dd40e918be54dccd60c52b049b75119d5d96ce6b624414" dependencies = [ + "atk-sys", "cairo-sys-rs", - "gdk4-sys", + "gdk-pixbuf-sys", + "gdk-sys", + "gio-sys", "glib-sys", "gobject-sys", - "graphene-sys", "libc", "pango-sys", "system-deps", ] [[package]] -name = "gtk4" -version = "0.11.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "98a0a0466484f64b07b5b8184d43fa46be78eb0b8e04ae4e179af31d770b76d9" -dependencies = [ - "cairo-rs", - "field-offset", - "futures-channel", - "gdk-pixbuf", - "gdk4", - "gio", - "glib", - "graphene-rs", - "gsk4", - "gtk4-macros", - "gtk4-sys", - "libc", - "pango", -] - -[[package]] -name = "gtk4-macros" -version = "0.11.4" +name = "gtk3-macros" +version = "0.18.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ac7179400a36a04de039c24206bb841c5596992b907b43b23ee8d5bdc40d00e" +checksum = "52ff3c5b21f14f0736fed6dcfc0bfb4225ebf5725f3c0209edeec181e4d73e9d" dependencies = [ - "proc-macro-crate", + "proc-macro-crate 1.3.1", + "proc-macro-error", "proc-macro2", "quote", - "syn", -] - -[[package]] -name = "gtk4-sys" -version = "0.11.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82b8f954786af0b1984425c4446b77f5ff6594346181316be3f850caab1c6f01" -dependencies = [ - "cairo-sys-rs", - "gdk-pixbuf-sys", - "gdk4-sys", - "gio-sys", - "glib-sys", - "gobject-sys", - "graphene-sys", - "gsk4-sys", - "libc", - "pango-sys", - "system-deps", + "syn 2.0.98", ] [[package]] name = "hashbrown" -version = "0.15.5" +version = "0.15.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" +checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" dependencies = [ - "foldhash 0.1.5", + "foldhash 0.1.4", ] [[package]] -name = "hashbrown" -version = "0.17.1" +name = "heck" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" [[package]] name = "heck" @@ -1299,9 +1228,9 @@ checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" [[package]] name = "hermit-abi" -version = "0.5.2" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c" +checksum = "fbf6a919d6cf397374f7dfeeea91d974c7c0a7221d0d0f4f20d859d329e53fcc" [[package]] name = "hexf-parse" @@ -1321,11 +1250,12 @@ dependencies = [ [[package]] name = "http" -version = "1.4.2" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6970f50e31d6fc17d3fa27329444bfa74e196cf62e95052a3f6fee181dba6425" +checksum = "f16ca2af56261c99fba8bac40a10251ce8188205a4c448fbb745a2e4daa76fea" dependencies = [ "bytes", + "fnv", "itoa", ] @@ -1337,23 +1267,21 @@ checksum = "21dec9db110f5f872ed9699c3ecf50cf16f423502706ba5c72462e28d3157573" [[package]] name = "icu_collections" -version = "2.2.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2984d1cd16c883d7935b9e07e44071dca8d917fd52ecc02c04d5fa0b5a3f191c" +checksum = "db2fa452206ebee18c4b5c2274dbf1de17008e874b4dc4f0aea9d01ca79e4526" dependencies = [ "displaydoc", - "potential_utf", - "utf8_iter", "yoke", "zerofrom", "zerovec", ] [[package]] -name = "icu_locale_core" -version = "2.2.0" +name = "icu_locid" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92219b62b3e2b4d88ac5119f8904c10f8f61bf7e95b640d25ba3075e6cac2c29" +checksum = "13acbb8371917fc971be86fc8057c41a64b521c184808a698c02acc242dbf637" dependencies = [ "displaydoc", "litemap", @@ -1362,66 +1290,104 @@ dependencies = [ "zerovec", ] +[[package]] +name = "icu_locid_transform" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01d11ac35de8e40fdeda00d9e1e9d92525f3f9d887cdd7aa81d727596788b54e" +dependencies = [ + "displaydoc", + "icu_locid", + "icu_locid_transform_data", + "icu_provider", + "tinystr", + "zerovec", +] + +[[package]] +name = "icu_locid_transform_data" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fdc8ff3388f852bede6b579ad4e978ab004f139284d7b28715f773507b946f6e" + [[package]] name = "icu_normalizer" -version = "2.2.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c56e5ee99d6e3d33bd91c5d85458b6005a22140021cc324cea84dd0e72cff3b4" +checksum = "19ce3e0da2ec68599d193c93d088142efd7f9c5d6fc9b803774855747dc6a84f" dependencies = [ + "displaydoc", "icu_collections", "icu_normalizer_data", "icu_properties", "icu_provider", "smallvec", + "utf16_iter", + "utf8_iter", + "write16", "zerovec", ] [[package]] name = "icu_normalizer_data" -version = "2.2.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da3be0ae77ea334f4da67c12f149704f19f81d1adf7c51cf482943e84a2bad38" +checksum = "f8cafbf7aa791e9b22bec55a167906f9e1215fd475cd22adfcf660e03e989516" [[package]] name = "icu_properties" -version = "2.2.0" +version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bee3b67d0ea5c2cca5003417989af8996f8604e34fb9ddf96208a033901e70de" +checksum = "93d6020766cfc6302c15dbbc9c8778c37e62c14427cb7f6e601d849e092aeef5" dependencies = [ + "displaydoc", "icu_collections", - "icu_locale_core", + "icu_locid_transform", "icu_properties_data", "icu_provider", - "zerotrie", + "tinystr", "zerovec", ] [[package]] name = "icu_properties_data" -version = "2.2.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e2bbb201e0c04f7b4b3e14382af113e17ba4f63e2c9d2ee626b720cbce54a14" +checksum = "67a8effbc3dd3e4ba1afa8ad918d5684b8868b3b26500753effea8d2eed19569" [[package]] name = "icu_provider" -version = "2.2.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "139c4cf31c8b5f33d7e199446eff9c1e02decfc2f0eec2c8d71f65befa45b421" +checksum = "6ed421c8a8ef78d3e2dbc98a973be2f3770cb42b606e3ab18d6237c4dfde68d9" dependencies = [ "displaydoc", - "icu_locale_core", + "icu_locid", + "icu_provider_macros", + "stable_deref_trait", + "tinystr", "writeable", "yoke", "zerofrom", - "zerotrie", "zerovec", ] +[[package]] +name = "icu_provider_macros" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.98", +] + [[package]] name = "idna" -version = "1.1.0" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b0875f23caa03898994f6ddc501886a45c7d3d62d04d2d90788d47be1b1e4de" +checksum = "686f825264d630750a544639377bae737628043f20d38bbc029e8f29ea968a7e" dependencies = [ "idna_adapter", "smallvec", @@ -1430,9 +1396,9 @@ dependencies = [ [[package]] name = "idna_adapter" -version = "1.2.2" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb68373c0d6620ef8105e855e7745e18b0d00d3bdb07fb532e434244cdb9a714" +checksum = "daca1df1c957320b2cf139ac61e7bd64fed304c5040df000a745aa1de3b4ef71" dependencies = [ "icu_normalizer", "icu_properties", @@ -1440,36 +1406,36 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.14.0" +version = "2.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9" +checksum = "8c9c992b02b5b4c94ea26e32fe5bccb7aa7d9f390ab5c1221ff895bc7ea8b652" dependencies = [ "equivalent", - "hashbrown 0.17.1", + "hashbrown", ] [[package]] name = "itoa" -version = "1.0.18" +version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" +checksum = "d75a2a4b1b190afb6f5425f10f6a8f959d2ea0b9c2b1d79553551850539e4674" [[package]] -name = "javascriptcore6" -version = "0.6.0" +name = "javascriptcore-rs" +version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d8d4f64d976c6dc6068723b6ef7838acf954d56b675f376c826f7e773362ddb" +checksum = "ca5671e9ffce8ffba57afc24070e906da7fc4b1ba66f2cabebf61bf2ea257fcc" dependencies = [ + "bitflags 1.3.2", "glib", - "javascriptcore6-sys", - "libc", + "javascriptcore-rs-sys", ] [[package]] -name = "javascriptcore6-sys" -version = "0.6.0" +name = "javascriptcore-rs-sys" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2b9787581c8949a7061c9b8593c4d1faf4b0fe5e5643c6c7793df20dbe39cf6" +checksum = "af1be78d14ffa4b75b66df31840478fef72b51f8c2465d4ca7c194da9f7a5124" dependencies = [ "glib-sys", "gobject-sys", @@ -1486,89 +1452,35 @@ dependencies = [ "cesu8", "cfg-if", "combine", - "jni-sys 0.3.1", + "jni-sys", "log", "thiserror 1.0.69", "walkdir", "windows-sys 0.45.0", ] -[[package]] -name = "jni" -version = "0.22.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5efd9a482cf3a427f00d6b35f14332adc7902ce91efb778580e180ff90fa3498" -dependencies = [ - "cfg-if", - "combine", - "jni-macros", - "jni-sys 0.4.1", - "log", - "simd_cesu8", - "thiserror 2.0.18", - "walkdir", - "windows-link 0.2.1", -] - -[[package]] -name = "jni-macros" -version = "0.22.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a00109accc170f0bdb141fed3e393c565b6f5e072365c3bd58f5b062591560a3" -dependencies = [ - "proc-macro2", - "quote", - "rustc_version", - "simd_cesu8", - "syn", -] - -[[package]] -name = "jni-sys" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41a652e1f9b6e0275df1f15b32661cf0d4b78d4d87ddec5e0c3c20f097433258" -dependencies = [ - "jni-sys 0.4.1", -] - [[package]] name = "jni-sys" -version = "0.4.1" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6377a88cb3910bee9b0fa88d4f42e1d2da8e79915598f65fb0c7ee14c878af2" -dependencies = [ - "jni-sys-macros", -] - -[[package]] -name = "jni-sys-macros" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38c0b942f458fe50cdac086d2f946512305e5631e720728f2a61aabcd47a6264" -dependencies = [ - "quote", - "syn", -] +checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" [[package]] name = "jobserver" -version = "0.1.35" +version = "0.1.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c00acbd29eabad4a2392fa0e921c874934dbbf4194312ad20f04a0ed67a3cb3" +checksum = "48d1dbcbbeb6a7fec7e059840aa538bd62aaccf972c7346c4d9d2059312853d0" dependencies = [ - "getrandom 0.4.3", "libc", ] [[package]] name = "js-sys" -version = "0.3.103" +version = "0.3.77" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53b44bfcdb3f8d5837a46dae1ca9660a837176eee74a28b229bc626816589102" +checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f" dependencies = [ - "cfg-if", - "futures-util", + "once_cell", "wasm-bindgen", ] @@ -1591,9 +1503,9 @@ checksum = "e2db585e1d738fc771bf08a151420d3ed193d9d895a36df7f6f8a9456b911ddc" [[package]] name = "libc" -version = "0.2.186" +version = "0.2.169" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66" +checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a" [[package]] name = "libdbus-sys" @@ -1616,14 +1528,13 @@ dependencies = [ [[package]] name = "libredox" -version = "0.1.18" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c943259e342f1e06ff2da7a83eabdfe7f92ce10262688dbf1895ff0b3e6e4652" +checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" dependencies = [ - "bitflags 2.13.0", + "bitflags 2.8.0", "libc", - "plain", - "redox_syscall 0.9.0", + "redox_syscall 0.5.9", ] [[package]] @@ -1632,38 +1543,33 @@ version = "0.4.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" -[[package]] -name = "linux-raw-sys" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53" - [[package]] name = "litemap" -version = "0.8.2" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92daf443525c4cce67b150400bc2316076100ce0b3686209eb8cf3c31612e6f0" +checksum = "4ee93343901ab17bd981295f2cf0026d4ad018c7c31ba84549a4ddbb47a45104" [[package]] name = "litrs" -version = "1.0.0" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11d3d7f243d5c5a8b9bb5d6dd2b1602c0cb0b9db1621bafc7ed66e35ff9fe092" +checksum = "b4ce301924b7887e9d637144fdade93f9dfff9b60981d4ac161db09720d39aa5" [[package]] name = "lock_api" -version = "0.4.14" +version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965" +checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" dependencies = [ + "autocfg", "scopeguard", ] [[package]] name = "log" -version = "0.4.33" +version = "0.4.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ceec5bc11778974d1bcb055b18002eba7f4b3518b6a0081b3af5f21666da9ad" +checksum = "30bde2b3dc3671ae49d8e2e9f044c7c005836e7a023ee57cffa25ab82764bb9e" [[package]] name = "malloc_buf" @@ -1687,9 +1593,9 @@ dependencies = [ [[package]] name = "memchr" -version = "2.8.2" +version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88904434abc2901f197fe8cc55f0445e7ded921dba5911dad2e2b39b48e663c4" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" [[package]] name = "memmap2" @@ -1715,7 +1621,7 @@ version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7ecfd3296f8c56b7c1f6fbac3c71cefa9d78ce009850c45000015f206dc7fa21" dependencies = [ - "bitflags 2.13.0", + "bitflags 2.8.0", "block", "core-graphics-types 0.1.3", "foreign-types", @@ -1732,7 +1638,7 @@ checksum = "364f94bc34f61332abebe8cad6f6cd82a5b65cff22c828d05d0968911462ca4f" dependencies = [ "arrayvec", "bit-set", - "bitflags 2.13.0", + "bitflags 2.8.0", "cfg_aliases 0.1.1", "codespan-reporting", "hexf-parse", @@ -1751,8 +1657,8 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c3f42e7bbe13d351b6bead8286a43aac9534b82bd3cc43e47037f012ebfd62d4" dependencies = [ - "bitflags 2.13.0", - "jni-sys 0.3.1", + "bitflags 2.8.0", + "jni-sys", "log", "ndk-sys 0.6.0+11769913", "num_enum", @@ -1772,7 +1678,7 @@ version = "0.5.0+25.2.9519653" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8c196769dd60fd4f363e11d948139556a344e79d451aeb2fa2fd040738ef7691" dependencies = [ - "jni-sys 0.3.1", + "jni-sys", ] [[package]] @@ -1781,7 +1687,7 @@ version = "0.6.0+11769913" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ee6cda3051665f1fb8d9e08fc35c96d5a244fb1be711a03b71118828afc9a873" dependencies = [ - "jni-sys 0.3.1", + "jni-sys", ] [[package]] @@ -1792,30 +1698,29 @@ checksum = "650eef8c711430f1a879fdd01d4745a7deea475becfb90269c06775983bbf086" [[package]] name = "num-conv" -version = "0.2.2" +version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "521739c6d2bac4aa25192232afe6841231376b2b26d4d9fae5ecf8ca5772e441" +checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" [[package]] name = "num_enum" -version = "0.7.6" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d0bca838442ec211fa11de3a8b0e0e8f3a4522575b5c4c06ed722e005036f26" +checksum = "4e613fc340b2220f734a8595782c551f1250e969d87d3be1ae0579e8d4065179" dependencies = [ "num_enum_derive", - "rustversion", ] [[package]] name = "num_enum_derive" -version = "0.7.6" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "680998035259dcfcafe653688bf2aa6d3e2dc05e98be6ab46afb089dc84f1df8" +checksum = "af1844ef2428cc3e1cb900be36181049ef3d3193c63e43026cfe202983b27a56" dependencies = [ - "proc-macro-crate", + "proc-macro-crate 2.0.2", "proc-macro2", "quote", - "syn", + "syn 2.0.98", ] [[package]] @@ -1859,7 +1764,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e4e89ad9e3d7d297152b17d39ed92cd50ca8063a89a9fa569046d41568891eff" dependencies = [ - "bitflags 2.13.0", + "bitflags 2.8.0", "block2 0.5.1", "libc", "objc2 0.5.2", @@ -1875,7 +1780,7 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d49e936b501e5c5bf01fda3a9452ff86dc3ea98ad5f283e1455153142d97518c" dependencies = [ - "bitflags 2.13.0", + "bitflags 2.8.0", "objc2 0.6.4", "objc2-core-foundation", "objc2-foundation 0.3.2", @@ -1887,7 +1792,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "74dd3b56391c7a0596a295029734d3c1c5e7e510a4cb30245f8221ccea96b009" dependencies = [ - "bitflags 2.13.0", + "bitflags 2.8.0", "block2 0.5.1", "objc2 0.5.2", "objc2-core-location 0.2.2", @@ -1900,7 +1805,7 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "73ad74d880bb43877038da939b7427bba67e9dd42004a18b809ba7d87cee241c" dependencies = [ - "bitflags 2.13.0", + "bitflags 2.8.0", "objc2 0.6.4", "objc2-foundation 0.3.2", ] @@ -1922,7 +1827,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "617fbf49e071c178c0b24c080767db52958f716d9eabdf0890523aeae54773ef" dependencies = [ - "bitflags 2.13.0", + "bitflags 2.8.0", "block2 0.5.1", "objc2 0.5.2", "objc2-foundation 0.2.2", @@ -1944,7 +1849,7 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2a180dd8642fa45cdb7dd721cd4c11b1cadd4929ce112ebd8b9f5803cc79d536" dependencies = [ - "bitflags 2.13.0", + "bitflags 2.8.0", "dispatch2", "objc2 0.6.4", ] @@ -1955,7 +1860,7 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e022c9d066895efa1345f8e33e584b9f958da2fd4cd116792e15e07e4720a807" dependencies = [ - "bitflags 2.13.0", + "bitflags 2.8.0", "dispatch2", "objc2 0.6.4", "objc2-core-foundation", @@ -2012,7 +1917,7 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0cde0dfb48d25d2b4862161a4d5fcc0e3c24367869ad306b0c9ec0073bfed92d" dependencies = [ - "bitflags 2.13.0", + "bitflags 2.8.0", "objc2 0.6.4", "objc2-core-foundation", "objc2-core-graphics", @@ -2039,7 +1944,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ee638a5da3799329310ad4cfa62fbf045d5f56e3ef5ba4149e7452dcf89d5a8" dependencies = [ - "bitflags 2.13.0", + "bitflags 2.8.0", "block2 0.5.1", "dispatch", "libc", @@ -2052,7 +1957,7 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3e0adef53c21f888deb4fa59fc59f7eb17404926ee8a6f59f5df0fd7f9f3272" dependencies = [ - "bitflags 2.13.0", + "bitflags 2.8.0", "block2 0.6.2", "objc2 0.6.4", "objc2-core-foundation", @@ -2064,7 +1969,7 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "180788110936d59bab6bd83b6060ffdfffb3b922ba1396b312ae795e1de9d81d" dependencies = [ - "bitflags 2.13.0", + "bitflags 2.8.0", "objc2 0.6.4", "objc2-core-foundation", ] @@ -2087,7 +1992,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dd0cba1276f6023976a406a14ffa85e1fdd19df6b0f737b063b95f6c8c7aadd6" dependencies = [ - "bitflags 2.13.0", + "bitflags 2.8.0", "block2 0.5.1", "objc2 0.5.2", "objc2-foundation 0.2.2", @@ -2099,7 +2004,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e42bee7bff906b14b167da2bac5efe6b6a07e6f7c0a21a7308d40c960242dc7a" dependencies = [ - "bitflags 2.13.0", + "bitflags 2.8.0", "block2 0.5.1", "objc2 0.5.2", "objc2-foundation 0.2.2", @@ -2112,7 +2017,7 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "96c1358452b371bf9f104e21ec536d37a650eb10f7ee379fff67d2e08d537f1f" dependencies = [ - "bitflags 2.13.0", + "bitflags 2.8.0", "objc2 0.6.4", "objc2-core-foundation", "objc2-foundation 0.3.2", @@ -2134,7 +2039,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b8bb46798b20cd6b91cbd113524c490f1686f4c4e8f49502431415f3512e2b6f" dependencies = [ - "bitflags 2.13.0", + "bitflags 2.8.0", "block2 0.5.1", "objc2 0.5.2", "objc2-cloud-kit 0.2.2", @@ -2155,7 +2060,7 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d87d638e33c06f577498cbcc50491496a3ed4246998a7fbba7ccb98b1e7eab22" dependencies = [ - "bitflags 2.13.0", + "bitflags 2.8.0", "block2 0.6.2", "objc2 0.6.4", "objc2-cloud-kit 0.3.2", @@ -2187,7 +2092,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "76cfcbf642358e8689af64cee815d139339f3ed8ad05103ed5eaf73db8d84cb3" dependencies = [ - "bitflags 2.13.0", + "bitflags 2.8.0", "block2 0.5.1", "objc2 0.5.2", "objc2-core-location 0.2.2", @@ -2210,7 +2115,7 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b2e5aaab980c433cf470df9d7af96a7b46a9d892d521a2cbbb2f8a4c16751e7f" dependencies = [ - "bitflags 2.13.0", + "bitflags 2.8.0", "block2 0.6.2", "objc2 0.6.4", "objc2-app-kit 0.3.2", @@ -2220,9 +2125,9 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.21.4" +version = "1.20.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" +checksum = "945462a4b81e43c4e3ba96bd7b49d834c6f61198356aa858733bc4acf3cbe62e" [[package]] name = "option-ext" @@ -2232,39 +2137,40 @@ checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" [[package]] name = "orbclient" -version = "0.3.55" +version = "0.3.48" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5df339f526ea9a60e371768d50efc2f2508c7203290731565d1f7a6f71d21747" +checksum = "ba0b26cec2e24f08ed8bb31519a9333140a6599b867dac464bb150bdb796fd43" dependencies = [ - "libc", "libredox", ] [[package]] name = "owned_ttf_parser" -version = "0.25.1" +version = "0.25.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36820e9051aca1014ddc75770aab4d68bc1e9e632f0f5627c4086bc216fb583b" +checksum = "22ec719bbf3b2a81c109a4e20b1f129b5566b7dce654bc3872f6a05abf82b2c4" dependencies = [ "ttf-parser", ] [[package]] name = "pango" -version = "0.22.8" +version = "0.18.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d800d8d0de2ad5d0fb046f5344dbaba14a003cf3dd27cc21d85893d35ea316c" +checksum = "7ca27ec1eb0457ab26f3036ea52229edbdb74dee1edd29063f5b9b010e7ebee4" dependencies = [ "gio", "glib", + "libc", + "once_cell", "pango-sys", ] [[package]] name = "pango-sys" -version = "0.22.0" +version = "0.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbd111a20ca90fedf03e09c59783c679c00900f1d8491cca5399f5e33609d5d6" +checksum = "436737e391a843e5933d6d9aa102cb126d501e815b83601365a948a518555dc5" dependencies = [ "glib-sys", "gobject-sys", @@ -2272,17 +2178,11 @@ dependencies = [ "system-deps", ] -[[package]] -name = "parking" -version = "2.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f38d5652c16fde515bb1ecef450ab0f6a219d619a7274976324d5e377f7dceba" - [[package]] name = "parking_lot" -version = "0.12.5" +version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a" +checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" dependencies = [ "lock_api", "parking_lot_core", @@ -2290,15 +2190,15 @@ dependencies = [ [[package]] name = "parking_lot_core" -version = "0.9.12" +version = "0.9.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1" +checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" dependencies = [ "cfg-if", "libc", - "redox_syscall 0.5.18", + "redox_syscall 0.5.9", "smallvec", - "windows-link 0.2.1", + "windows-targets 0.52.6", ] [[package]] @@ -2309,9 +2209,9 @@ checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" [[package]] name = "percent-encoding" -version = "2.3.2" +version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" +checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" [[package]] name = "phf" @@ -2354,7 +2254,7 @@ dependencies = [ "phf_shared", "proc-macro2", "quote", - "syn", + "syn 2.0.98", ] [[package]] @@ -2368,54 +2268,55 @@ dependencies = [ [[package]] name = "pin-project" -version = "1.1.13" +version = "1.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2466b2336ed02bcdca6b294417127b90ec92038d1d5c4fbeac971a922e0e0924" +checksum = "dfe2e71e1471fe07709406bf725f710b02927c9c54b2b5b2ec0e8087d97c327d" dependencies = [ "pin-project-internal", ] [[package]] name = "pin-project-internal" -version = "1.1.13" +version = "1.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c96395f0a926bc13b1c17622aaddda1ecb55d49c8f1bf9777e4d877800a43f8b" +checksum = "f6e859e6e5bd50440ab63c47e3ebabc90f26251f7c73c3d3e837b74a1cc3fa67" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.98", ] [[package]] name = "pin-project-lite" -version = "0.2.17" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" +checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" [[package]] -name = "pkg-config" -version = "0.3.33" +name = "pin-utils" +version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19f132c84eca552bf34cab8ec81f1c1dcc229b811638f9d283dceabe58c5569e" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" [[package]] -name = "plain" -version = "0.2.3" +name = "pkg-config" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4596b6d070b27117e987119b4dac604f3c58cfb0b191112e24771b2faeac1a6" +checksum = "953ec861398dccce10c670dfeaf3ec4911ca479e9c02154b3a215178c5f566f2" [[package]] name = "polling" -version = "3.11.0" +version = "3.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d0e4f59085d47d8241c88ead0f274e8a0cb551f3625263c05eb8dd897c34218" +checksum = "a604568c3202727d1507653cb121dbd627a58684eb09a820fd746bee38b4442f" dependencies = [ "cfg-if", "concurrent-queue", "hermit-abi", "pin-project-lite", - "rustix 1.1.4", - "windows-sys 0.61.2", + "rustix", + "tracing", + "windows-sys 0.59.0", ] [[package]] @@ -2424,15 +2325,6 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bc6355899e1c9462875b6757c79f3caa011a1fdae12bbb1a2e72dd1f234f8336" -[[package]] -name = "potential_utf" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0103b1cef7ec0cf76490e969665504990193874ea05c85ff9bab8b911d0a0564" -dependencies = [ - "zerovec", -] - [[package]] name = "powerfmt" version = "0.2.0" @@ -2440,76 +2332,99 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" [[package]] -name = "precomputed-hash" -version = "0.1.1" +name = "precomputed-hash" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c" + +[[package]] +name = "presser" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8cf8e6a8aa66ce33f63993ffc4ea4271eb5b0530a9002db8455ea6050c77bfa" + +[[package]] +name = "proc-macro-crate" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919" +dependencies = [ + "once_cell", + "toml_edit 0.19.15", +] + +[[package]] +name = "proc-macro-crate" +version = "2.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c" +checksum = "b00f26d3400549137f92511a46ac1cd8ce37cb5598a96d382381458b992a5d24" +dependencies = [ + "toml_datetime", + "toml_edit 0.20.2", +] [[package]] -name = "presser" -version = "0.3.1" +name = "proc-macro-error" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8cf8e6a8aa66ce33f63993ffc4ea4271eb5b0530a9002db8455ea6050c77bfa" +checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" +dependencies = [ + "proc-macro-error-attr", + "proc-macro2", + "quote", + "syn 1.0.109", + "version_check", +] [[package]] -name = "proc-macro-crate" -version = "3.5.0" +name = "proc-macro-error-attr" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e67ba7e9b2b56446f1d419b1d807906278ffa1a658a8a5d8a39dcb1f5a78614f" +checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" dependencies = [ - "toml_edit", + "proc-macro2", + "quote", + "version_check", ] [[package]] name = "proc-macro2" -version = "1.0.106" +version = "1.0.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" +checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778" dependencies = [ "unicode-ident", ] [[package]] name = "profiling" -version = "1.0.18" +version = "1.0.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d595e54a326bc53c1c197b32d295e14b169e3cfeaa8dc82b529f947fba6bcf5" +checksum = "afbdc74edc00b6f6a218ca6a5364d6226a259d4b8ea1af4a0ea063f27e179f4d" [[package]] name = "quick-xml" -version = "0.41.0" +version = "0.37.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e660451e55124f798a69a5af3f49ccfbefbd41910eefd25caf2393e1f3473ec1" +checksum = "165859e9e55f79d67b96c5d96f4e88b6f2695a1972849c15a6a3f5c59fc2c003" dependencies = [ "memchr", ] [[package]] name = "quote" -version = "1.0.46" +version = "1.0.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dfbc457d0c7a0759a614551b11a6409e5951f6c7537be1f1b7682b9ae9230368" +checksum = "0e4dccaaaf89514f546c693ddc140f729f958c247918a13380cccc6078391acc" dependencies = [ "proc-macro2", ] -[[package]] -name = "r-efi" -version = "5.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" - -[[package]] -name = "r-efi" -version = "6.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf" - [[package]] name = "range-alloc" -version = "0.1.5" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca45419789ae5a7899559e9512e58ca889e41f04f1f2445e9f4b290ceccd1d08" +checksum = "c3d6831663a5098ea164f89cff59c6284e95f4e3c76ce9848d4529f5ccca9bde" [[package]] name = "raw-window-handle" @@ -2528,20 +2443,11 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.5.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" -dependencies = [ - "bitflags 2.13.0", -] - -[[package]] -name = "redox_syscall" -version = "0.9.0" +version = "0.5.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5102a6aaa05aa011a238e178e6bca86d2cb56fc9f586d37cb80f5bca6e07759" +checksum = "82b568323e98e49e2a0899dcee453dd679fae22d69adf9b11dd508d1549b7e2f" dependencies = [ - "bitflags 2.13.0", + "bitflags 2.8.0", ] [[package]] @@ -2550,9 +2456,9 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a4e608c6638b9c18977b00b475ac1f28d14e84b27d8d42f70e0bf1e3dec127ac" dependencies = [ - "getrandom 0.2.17", + "getrandom 0.2.15", "libredox", - "thiserror 2.0.18", + "thiserror 2.0.11", ] [[package]] @@ -2569,9 +2475,9 @@ checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" [[package]] name = "rustc-hash" -version = "2.1.3" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b1e7f9a428571be2dc5bc0505c13fb6bf936822b894ec87abf8a08a4e51742d" +checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" [[package]] name = "rustc_version" @@ -2588,31 +2494,18 @@ version = "0.38.44" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154" dependencies = [ - "bitflags 2.13.0", - "errno", - "libc", - "linux-raw-sys 0.4.15", - "windows-sys 0.59.0", -] - -[[package]] -name = "rustix" -version = "1.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190" -dependencies = [ - "bitflags 2.13.0", + "bitflags 2.8.0", "errno", "libc", - "linux-raw-sys 0.12.1", + "linux-raw-sys", "windows-sys 0.59.0", ] [[package]] name = "rustversion" -version = "1.0.23" +version = "1.0.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf54715a573b99ac80df0bc206da022bcd442c974952c7b9720069370852e21f" +checksum = "f7c45b9784283f1b2e7fb61b42047c2fd678ef0960d4f6f1eba131594cc369d4" [[package]] name = "same-file" @@ -2654,7 +2547,7 @@ version = "0.38.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8adfa1c298912827b8a28b223b3b874357397ae706e6190acd9bf28cee99114d" dependencies = [ - "bitflags 2.13.0", + "bitflags 2.8.0", "cssparser", "derive_more", "log", @@ -2662,54 +2555,44 @@ dependencies = [ "phf", "phf_codegen", "precomputed-hash", - "rustc-hash 2.1.3", + "rustc-hash 2.1.1", "servo_arc", "smallvec", ] [[package]] name = "semver" -version = "1.0.28" +version = "1.0.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd" +checksum = "f79dfe2d285b0488816f30e700a7438c5a73d816b5b7d3ac72fbc48b0d185e03" [[package]] name = "serde" -version = "1.0.228" +version = "1.0.218" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" -dependencies = [ - "serde_core", - "serde_derive", -] - -[[package]] -name = "serde_core" -version = "1.0.228" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" +checksum = "e8dfc9d19bdbf6d17e22319da49161d5d0108e4188e8b680aef6299eed22df60" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.228" +version = "1.0.218" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" +checksum = "f09503e191f4e797cb8aac08e9a4a4695c5edf6a2e70e376d961ddd5c969f82b" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.98", ] [[package]] name = "serde_spanned" -version = "1.1.1" +version = "0.6.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6662b5879511e06e8999a8a235d848113e942c9124f211511b16466ee2995f26" +checksum = "87607cb1398ed59d48732e575a4c28a7a8ebf2454b964fe3f224f2afc07909e1" dependencies = [ - "serde_core", + "serde", ] [[package]] @@ -2723,9 +2606,9 @@ dependencies = [ [[package]] name = "sha2" -version = "0.10.9" +version = "0.10.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" +checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" dependencies = [ "cfg-if", "cpufeatures", @@ -2734,52 +2617,39 @@ dependencies = [ [[package]] name = "shlex" -version = "2.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba" - -[[package]] -name = "simd_cesu8" -version = "1.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94f90157bb87cddf702797c5dadfa0be7d266cdf49e22da2fcaa32eff75b2c33" -dependencies = [ - "rustc_version", - "simdutf8", -] - -[[package]] -name = "simdutf8" -version = "0.1.5" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3a9fe34e3e7a50316060351f37187a3f546bce95496156754b601a5fa71b76e" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" [[package]] name = "siphasher" -version = "1.0.3" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ee5873ec9cce0195efcb7a4e9507a04cd49aec9c83d0389df45b1ef7ba2e649" +checksum = "56199f7ddabf13fe5074ce809e7d3f42b42ae711800501b5b16ea82ad029c39d" [[package]] name = "slab" -version = "0.4.12" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" +checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" +dependencies = [ + "autocfg", +] [[package]] name = "slotmap" -version = "1.1.1" +version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bdd58c3c93c3d278ca835519292445cb4b0d4dc59ccfdf7ceadaab3f8aeb4038" +checksum = "dbff4acf519f630b3a3ddcfaea6c06b42174d9a44bc70c620e9ed1649d58b82a" dependencies = [ "version_check", ] [[package]] name = "smallvec" -version = "1.15.2" +version = "1.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ed6a63f02c8539c91a8685a86f4099661ba3da017932f6ebbea6de3f0fa7c90" +checksum = "7fcf8323ef1faaee30a44a340193b1ac6814fd9b7b4e88e9d4519a3e4abe1cfd" [[package]] name = "smithay-client-toolkit" @@ -2787,14 +2657,14 @@ version = "0.19.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3457dea1f0eb631b4034d61d4d8c32074caa6cd1ab2d59f2327bd8461e2c0016" dependencies = [ - "bitflags 2.13.0", + "bitflags 2.8.0", "calloop", "calloop-wayland-source", "cursor-icon", "libc", "log", "memmap2", - "rustix 0.38.44", + "rustix", "thiserror 1.0.69", "wayland-backend", "wayland-client", @@ -2817,9 +2687,9 @@ dependencies = [ [[package]] name = "soup3" -version = "0.9.0" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92d38b59ff6d302538efd337e15d04d61c5b909ec223c60ae4061d74605a962a" +checksum = "471f924a40f31251afc77450e781cb26d55c0b650842efafc9c6cbd2f7cc4f9f" dependencies = [ "futures-channel", "gio", @@ -2830,9 +2700,9 @@ dependencies = [ [[package]] name = "soup3-sys" -version = "0.9.0" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79d5d25225bb06f83b78ff8cc35973b56d45fcdd21af6ed6d2bbd67f5a6f9bea" +checksum = "7ebe8950a680a12f24f15ebe1bf70db7af98ad242d9db43596ad3108aab86c27" dependencies = [ "gio-sys", "glib-sys", @@ -2847,14 +2717,14 @@ version = "0.3.0+sdk-1.3.268.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eda41003dc44290527a59b13432d4a0379379fa074b70174882adfbdfd917844" dependencies = [ - "bitflags 2.13.0", + "bitflags 2.8.0", ] [[package]] name = "stable_deref_trait" -version = "1.2.1" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" [[package]] name = "static_assertions" @@ -2894,9 +2764,19 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.118" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.98" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b9ae57f904213ebb649ce6895b8a66c66f0203b9319718f69a5612a065b1422" +checksum = "36147f1a48ae0ec2b5b3bc5b537d267457555a10dc06f3dbc8cb11ba3006d3b1" dependencies = [ "proc-macro2", "quote", @@ -2905,23 +2785,23 @@ dependencies = [ [[package]] name = "synstructure" -version = "0.13.2" +version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" +checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.98", ] [[package]] name = "system-deps" -version = "7.0.8" +version = "6.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "396a35feb67335377e0251fcbc1092fc85c484bd4e3a7a54319399da127796e7" +checksum = "a3e535eb8dded36d55ec13eddacd30dec501792ff23a0b1682c38601b8cf2349" dependencies = [ "cfg-expr", - "heck", + "heck 0.5.0", "pkg-config", "toml", "version-compare", @@ -2929,25 +2809,27 @@ dependencies = [ [[package]] name = "tao" -version = "0.35.3" +version = "0.36.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e9fa4618f999c4249db1681cba0a19b890718f274de7fa93c445d46bd3a8a999" dependencies = [ - "async-channel", - "bitflags 2.13.0", + "bitflags 2.8.0", "block2 0.6.2", - "core-foundation 0.10.1", + "core-foundation 0.10.0", "core-graphics 0.25.0", "crossbeam-channel", "dbus", "dispatch2", "dlopen2", "dpi", - "gdk4-wayland", - "gdk4-x11", - "gtk4", - "jni 0.21.1", + "gdkwayland-sys", + "gdkx11-sys", + "gtk", + "jni", "libc", "log", "ndk", + "ndk-context", "ndk-sys 0.6.0+11769913", "objc2 0.6.4", "objc2-app-kit 0.3.2", @@ -2957,48 +2839,40 @@ dependencies = [ "parking_lot", "percent-encoding", "raw-window-handle", - "tao-macros 0.1.3", + "tao-macros", "unicode-segmentation", "url", - "windows 0.61.3", - "windows-core 0.61.2", + "windows 0.61.1", + "windows-core 0.61.0", "windows-version", "x11-dl", ] [[package]] name = "tao-macros" -version = "0.1.3" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "tao-macros" -version = "0.1.3" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4e16beb8b2ac17db28eab8bca40e62dbfbb34c0fcdc6d9826b11b7b5d047dfd" +checksum = "5f7eeb6d99155545da6150a1795945f16ac9c178deb2a5f2e74d776107bd5849" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.98", ] [[package]] name = "target-lexicon" -version = "0.13.5" +version = "0.12.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adb6935a6f5c20170eeceb1a3835a49e12e19d792f6dd344ccc76a985ca5a6ca" +checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1" [[package]] name = "tendril" -version = "0.5.1" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fed54709c5b3a53d09bb1c113ea4f5ceafd1e772ddcb0030a82e1d56c087b08" +checksum = "c4790fc369d5a530f4b544b094e31388b9b3a37c0f4652ade4505945f5660d24" dependencies = [ "new_debug_unreachable", + "utf-8", ] [[package]] @@ -3021,11 +2895,11 @@ dependencies = [ [[package]] name = "thiserror" -version = "2.0.18" +version = "2.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4" +checksum = "d452f284b73e6d76dd36758a0c8684b1d5be31f92b89d07fd5822175732206fc" dependencies = [ - "thiserror-impl 2.0.18", + "thiserror-impl 2.0.11", ] [[package]] @@ -3036,45 +2910,46 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.98", ] [[package]] name = "thiserror-impl" -version = "2.0.18" +version = "2.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" +checksum = "26afc1baea8a989337eeb52b6e72a039780ce45c3edfcc9c5b9d112feeb173c2" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.98", ] [[package]] name = "time" -version = "0.3.53" +version = "0.3.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18dfaaeddcb932337b5e7866ee7d0ce9b76d2fd092997146f187ec09b4558a50" +checksum = "35e7868883861bd0e56d9ac6efcaaca0d6d5d82a2a7ec8209ff492c07cf37b21" dependencies = [ "deranged", + "itoa", "num-conv", "powerfmt", - "serde_core", + "serde", "time-core", "time-macros", ] [[package]] name = "time-core" -version = "0.1.9" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e1c906769ad99c88eaa54e728060edef082f8e358ff32030cb7c7d315e81109" +checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" [[package]] name = "time-macros" -version = "0.2.31" +version = "0.2.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c431b87111666e491a90baa837f914fb45cd5dc3c268591b0220ff5057f2085f" +checksum = "2834e6017e3e5e4b9834939793b282bc03b37a3336245fa820e35e233e2a85de" dependencies = [ "num-conv", "time-core", @@ -3107,9 +2982,9 @@ dependencies = [ [[package]] name = "tinystr" -version = "0.8.3" +version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8323304221c2a851516f22236c5722a72eaa19749016521d6dff0824447d96d" +checksum = "9117f5d4db391c1cf6927e7bea3db74b9a1c1add8f7eda9ffd5364f40f57b82f" dependencies = [ "displaydoc", "zerovec", @@ -3117,60 +2992,54 @@ dependencies = [ [[package]] name = "toml" -version = "1.1.2+spec-1.1.0" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81f3d15e84cbcd896376e6730314d59fb5a87f31e4b038454184435cd57defee" +checksum = "185d8ab0dfbb35cf1399a6344d8484209c088f75f8f68230da55d48d95d43e3d" dependencies = [ - "indexmap", - "serde_core", + "serde", "serde_spanned", "toml_datetime", - "toml_parser", - "toml_writer", - "winnow", + "toml_edit 0.20.2", ] [[package]] name = "toml_datetime" -version = "1.1.1+spec-1.1.0" +version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3165f65f62e28e0115a00b2ebdd37eb6f3b641855f9d636d3cd4103767159ad7" +checksum = "7cda73e2f1397b1262d6dfdcef8aafae14d1de7748d66822d3bfeeb6d03e5e4b" dependencies = [ - "serde_core", + "serde", ] [[package]] name = "toml_edit" -version = "0.25.12+spec-1.1.0" +version = "0.19.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2153edc6955a6c354fad8f5efd38b6a8769bdccf9fe50f8e1329f81b0baa5d7" +checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" dependencies = [ "indexmap", "toml_datetime", - "toml_parser", "winnow", ] [[package]] -name = "toml_parser" -version = "1.1.2+spec-1.1.0" +name = "toml_edit" +version = "0.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2abe9b86193656635d2411dc43050282ca48aa31c2451210f4202550afb7526" +checksum = "396e4d48bbb2b7554c944bde63101b5ae446cff6ec4a24227428f15eb72ef338" dependencies = [ + "indexmap", + "serde", + "serde_spanned", + "toml_datetime", "winnow", ] -[[package]] -name = "toml_writer" -version = "1.1.1+spec-1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "756daf9b1013ebe47a8776667b466417e2d4c5679d441c26230efd9ef78692db" - [[package]] name = "tracing" -version = "0.1.44" +version = "0.1.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100" +checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" dependencies = [ "pin-project-lite", "tracing-attributes", @@ -3179,20 +3048,20 @@ dependencies = [ [[package]] name = "tracing-attributes" -version = "0.1.31" +version = "0.1.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da" +checksum = "395ae124c09f9e6918a2310af6038fba074bcf474ac352496d5910dd59a2226d" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.98", ] [[package]] name = "tracing-core" -version = "0.1.36" +version = "0.1.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a" +checksum = "e672c95779cf947c5311f83787af4fa8fffd12fb27e4993211a84bdfd9610f9c" dependencies = [ "once_cell", ] @@ -3205,21 +3074,21 @@ checksum = "d2df906b07856748fa3f6e0ad0cbaa047052d4a7dd609e231c4f72cee8c36f31" [[package]] name = "typenum" -version = "1.20.1" +version = "1.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6f5e870be6c3b371b77fe0ee0bafb859fa4964b4404c27de1d380043c4dda20" +checksum = "1dccffe3ce07af9386bfd29e80c0ab1a8205a2fc34e4bcd40364df902cfa8f3f" [[package]] name = "unicode-ident" -version = "1.0.24" +version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" +checksum = "00e2473a93778eb0bad35909dff6a10d28e63f792f16ed15e404fca9d5eeedbe" [[package]] name = "unicode-segmentation" -version = "1.13.3" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6f5d3c3b1bf09027a88a6bc961fc00497d651009560b5463668dc81b0fa87a8" +checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" [[package]] name = "unicode-width" @@ -3235,16 +3104,27 @@ checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" [[package]] name = "url" -version = "2.5.8" +version = "2.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff67a8a4397373c3ef660812acab3268222035010ab8680ec4215f38ba3d0eed" +checksum = "32f8b686cadd1473f4bd0117a5d28d36b1ade384ea9b5069a1c40aefed7fda60" dependencies = [ "form_urlencoded", "idna", "percent-encoding", - "serde", ] +[[package]] +name = "utf-8" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" + +[[package]] +name = "utf16_iter" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8232dd3cdaed5356e0f716d285e4b40b932ac434100fe9b7e0e8e935b9e6246" + [[package]] name = "utf8_iter" version = "1.0.4" @@ -3253,9 +3133,9 @@ checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" [[package]] name = "version-compare" -version = "0.2.1" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03c2856837ef78f57382f06b2b8563a2f512f7185d732608fd9176cb3b8edf0e" +checksum = "852e951cb7832cb45cb1169900d19760cfa39b82bc0ea9c0e5a14ae88411c98b" [[package]] name = "version_check" @@ -3275,47 +3155,63 @@ dependencies = [ [[package]] name = "wasi" -version = "0.11.1+wasi-snapshot-preview1" +version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] -name = "wasip2" -version = "1.0.4+wasi-0.2.12" +name = "wasi" +version = "0.13.3+wasi-0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b67efb37e106e55ce722a510d6b5f9c17f083e5fc79afc2badeb12cc313d9487" +checksum = "26816d2e1a4a36a2940b96c5296ce403917633dff8f3440e9b236ed6f6bacad2" dependencies = [ - "wit-bindgen", + "wit-bindgen-rt", ] [[package]] name = "wasm-bindgen" -version = "0.2.126" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b067c0c11094aef6b7a801c1e34a26affafdf3d051dba08456b868789aaf9a4" +checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5" dependencies = [ "cfg-if", "once_cell", "rustversion", "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6" +dependencies = [ + "bumpalo", + "log", + "proc-macro2", + "quote", + "syn 2.0.98", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-futures" -version = "0.4.76" +version = "0.4.50" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c62df1340f32221cb9c54d6a27b030e3dba64361d4a95bed55f9aacb44da291d" +checksum = "555d470ec0bc3bb57890405e5d4322cc9ea83cebb085523ced7be4144dac1e61" dependencies = [ + "cfg-if", "js-sys", + "once_cell", "wasm-bindgen", + "web-sys", ] [[package]] name = "wasm-bindgen-macro" -version = "0.2.126" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "167ce5e579f6bcf889c4f7175a8a5a585de84e8ff93976ce393efa5f2837aab1" +checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -3323,35 +3219,35 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.126" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3997c7839262f4ef12cf90b818d6340c18e80f263f1a94bf157d0ec4420380e" +checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" dependencies = [ - "bumpalo", "proc-macro2", "quote", - "syn", + "syn 2.0.98", + "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.126" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc1b4cb0cc549fcf58d7dfc081778139b3d283a081644e833e84682ad71cea24" +checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d" dependencies = [ "unicode-ident", ] [[package]] name = "wayland-backend" -version = "0.3.15" +version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2857dd20b54e916ec7253b3d6b4d5c4d7d4ca2c33c2e11c6c76a99bd8744755d" +checksum = "b7208998eaa3870dad37ec8836979581506e0c5c64c20c9e79e9d2a10d6f47bf" dependencies = [ "cc", "downcast-rs", - "rustix 1.1.4", + "rustix", "scoped-tls", "smallvec", "wayland-sys", @@ -3359,12 +3255,12 @@ dependencies = [ [[package]] name = "wayland-client" -version = "0.31.14" +version = "0.31.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "645c7c96bb74690c3189b5c9cb4ca1627062bb23693a4fad9d8c3de958260144" +checksum = "c2120de3d33638aaef5b9f4472bff75f07c56379cf76ea320bd3a3d65ecaf73f" dependencies = [ - "bitflags 2.13.0", - "rustix 1.1.4", + "bitflags 2.8.0", + "rustix", "wayland-backend", "wayland-scanner", ] @@ -3375,29 +3271,29 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "625c5029dbd43d25e6aa9615e88b829a5cad13b2819c4ae129fdbb7c31ab4c7e" dependencies = [ - "bitflags 2.13.0", + "bitflags 2.8.0", "cursor-icon", "wayland-backend", ] [[package]] name = "wayland-cursor" -version = "0.31.14" +version = "0.31.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a52d18780be9b1314328a3de5f930b73d2200112e3849ca6cb11822793fb34d" +checksum = "a93029cbb6650748881a00e4922b076092a6a08c11e7fbdb923f064b23968c5d" dependencies = [ - "rustix 1.1.4", + "rustix", "wayland-client", "xcursor", ] [[package]] name = "wayland-protocols" -version = "0.32.13" +version = "0.32.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23d0c813de3daa2ed6520af85a3bd49b0e722a3078506899aa9686fea58dc4b6" +checksum = "0781cf46869b37e36928f7b432273c0995aa8aed9552c556fb18754420541efc" dependencies = [ - "bitflags 2.13.0", + "bitflags 2.8.0", "wayland-backend", "wayland-client", "wayland-scanner", @@ -3405,11 +3301,11 @@ dependencies = [ [[package]] name = "wayland-protocols-plasma" -version = "0.3.12" +version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b6d8cf1eb2c1c31ed1f5643c88a6e53538129d4af80030c8cabd1f9fa884d91" +checksum = "7ccaacc76703fefd6763022ac565b590fcade92202492381c95b2edfdf7d46b3" dependencies = [ - "bitflags 2.13.0", + "bitflags 2.8.0", "wayland-backend", "wayland-client", "wayland-protocols", @@ -3418,11 +3314,11 @@ dependencies = [ [[package]] name = "wayland-protocols-wlr" -version = "0.3.12" +version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb04e52f7836d7c7976c78ca0250d61e33873c34156a2a1fc9474828ec268234" +checksum = "248a02e6f595aad796561fa82d25601bd2c8c3b145b1c7453fc8f94c1a58f8b2" dependencies = [ - "bitflags 2.13.0", + "bitflags 2.8.0", "wayland-backend", "wayland-client", "wayland-protocols", @@ -3431,7 +3327,9 @@ dependencies = [ [[package]] name = "wayland-scanner" -version = "0.31.10" +version = "0.31.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "896fdafd5d28145fce7958917d69f2fd44469b1d4e861cb5961bcbeebc6d1484" dependencies = [ "proc-macro2", "quick-xml", @@ -3440,9 +3338,9 @@ dependencies = [ [[package]] name = "wayland-sys" -version = "0.31.11" +version = "0.31.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8eab23fefc9e41f8e841df4a9c707e8a8c4ed26e944ef69297184de2785e3be" +checksum = "dbcebb399c77d5aa9fa5db874806ee7b4eba4e73650948e8f93963f128896615" dependencies = [ "dlib", "log", @@ -3452,9 +3350,9 @@ dependencies = [ [[package]] name = "web-sys" -version = "0.3.103" +version = "0.3.77" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8622dcb61c0bcc9fffa6938bed81210af2da9a7e4a1a834b2e37a59b6dfb6141" +checksum = "33b6dd2ef9186f1f2072e409e99cd22a975331a6b3591b12c764e0e55c60d5d2" dependencies = [ "js-sys", "wasm-bindgen", @@ -3472,9 +3370,9 @@ dependencies = [ [[package]] name = "web_atoms" -version = "0.2.5" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "075474b12bcb3d2e3d4546580e9de478eeeead668a1761e2a8860c836b7ef297" +checksum = "57a9779e9f04d2ac1ce317aee707aa2f6b773afba7b931222bff6983843b1576" dependencies = [ "phf", "phf_codegen", @@ -3483,72 +3381,83 @@ dependencies = [ ] [[package]] -name = "webkit6" -version = "0.6.1" +name = "webkit2gtk" +version = "2.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4959dd2a92813d4b2ae134e71345a03030bcff189b4f79cd131e9218aba22b70" +checksum = "a1027150013530fb2eaf806408df88461ae4815a45c541c8975e61d6f2fc4793" dependencies = [ - "gdk4", + "bitflags 1.3.2", + "cairo-rs", + "gdk", + "gdk-sys", "gio", + "gio-sys", "glib", - "gtk4", - "javascriptcore6", + "glib-sys", + "gobject-sys", + "gtk", + "gtk-sys", + "javascriptcore-rs", "libc", + "once_cell", "soup3", - "webkit6-sys", + "webkit2gtk-sys", ] [[package]] -name = "webkit6-sys" -version = "0.6.0" +name = "webkit2gtk-sys" +version = "2.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "236078ce03ff041bf87904c8257e6a9b0e9e0f957267c15f9c1756aadcf02581" +checksum = "916a5f65c2ef0dfe12fff695960a2ec3d4565359fdbb2e9943c974e06c734ea5" dependencies = [ - "gdk4-sys", + "bitflags 1.3.2", + "cairo-sys-rs", + "gdk-sys", "gio-sys", "glib-sys", "gobject-sys", - "gtk4-sys", - "javascriptcore6-sys", + "gtk-sys", + "javascriptcore-rs-sys", "libc", + "pkg-config", "soup3-sys", "system-deps", ] [[package]] name = "webview2-com" -version = "0.38.2" +version = "0.38.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7130243a7a5b33c54a444e54842e6a9e133de08b5ad7b5861cd8ed9a6a5bc96a" +checksum = "d4ba622a989277ef3886dd5afb3e280e3dd6d974b766118950a08f8f678ad6a4" dependencies = [ "webview2-com-macros", "webview2-com-sys", - "windows 0.61.3", - "windows-core 0.61.2", - "windows-implement 0.60.2", - "windows-interface 0.59.3", + "windows 0.61.1", + "windows-core 0.61.0", + "windows-implement 0.60.0", + "windows-interface 0.59.1", ] [[package]] name = "webview2-com-macros" -version = "0.8.1" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67a921c1b6914c367b2b823cd4cde6f96beec77d30a939c8199bb377cf9b9b54" +checksum = "1d228f15bba3b9d56dde8bddbee66fa24545bd17b48d5128ccf4a8742b18e431" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.98", ] [[package]] name = "webview2-com-sys" -version = "0.38.2" +version = "0.38.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "381336cfffd772377d291702245447a5251a2ffa5bad679c99e61bc48bacbf9c" +checksum = "36695906a1b53a3bf5c4289621efedac12b73eeb0b89e7e1a89b517302d5d75c" dependencies = [ - "thiserror 2.0.18", - "windows 0.61.3", - "windows-core 0.61.2", + "thiserror 2.0.11", + "windows 0.61.1", + "windows-core 0.61.0", ] [[package]] @@ -3584,7 +3493,7 @@ checksum = "d63c3c478de8e7e01786479919c8769f62a22eec16788d8c2ac77ce2c132778a" dependencies = [ "arrayvec", "bit-vec", - "bitflags 2.13.0", + "bitflags 2.8.0", "cfg_aliases 0.1.1", "document-features", "indexmap", @@ -3611,7 +3520,7 @@ dependencies = [ "arrayvec", "ash", "bit-set", - "bitflags 2.13.0", + "bitflags 2.8.0", "block", "bytemuck", "cfg_aliases 0.1.1", @@ -3652,7 +3561,7 @@ version = "23.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "610f6ff27778148c31093f3b03abc4840f9636d58d597ca2f5977433acfe0068" dependencies = [ - "bitflags 2.13.0", + "bitflags 2.8.0", "js-sys", "web-sys", ] @@ -3675,9 +3584,9 @@ checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" [[package]] name = "winapi-util" -version = "0.1.11" +version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" +checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" dependencies = [ "windows-sys 0.59.0", ] @@ -3700,14 +3609,14 @@ dependencies = [ [[package]] name = "windows" -version = "0.61.3" +version = "0.61.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9babd3a767a4c1aef6900409f85f5d53ce2544ccdfaa86dad48c91782c6d6893" +checksum = "c5ee8f3d025738cb02bad7868bbb5f8a6327501e870bf51f1b455b0a2454a419" dependencies = [ "windows-collections", - "windows-core 0.61.2", + "windows-core 0.61.0", "windows-future", - "windows-link 0.1.3", + "windows-link 0.1.1", "windows-numerics", ] @@ -3717,7 +3626,7 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3beeceb5e5cfd9eb1d76b381630e82c4241ccd0d27f1a39ed41b2760b255c5e8" dependencies = [ - "windows-core 0.61.2", + "windows-core 0.61.0", ] [[package]] @@ -3735,26 +3644,25 @@ dependencies = [ [[package]] name = "windows-core" -version = "0.61.2" +version = "0.61.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0fdd3ddb90610c7638aa2b3a3ab2904fb9e5cdbecc643ddb3647212781c4ae3" +checksum = "4763c1de310c86d75a878046489e2e5ba02c649d185f21c67d4cf8a56d098980" dependencies = [ - "windows-implement 0.60.2", - "windows-interface 0.59.3", - "windows-link 0.1.3", - "windows-result 0.3.4", - "windows-strings 0.4.2", + "windows-implement 0.60.0", + "windows-interface 0.59.1", + "windows-link 0.1.1", + "windows-result 0.3.2", + "windows-strings 0.4.0", ] [[package]] name = "windows-future" -version = "0.2.1" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc6a41e98427b19fe4b73c550f060b59fa592d7d686537eebf9385621bfbad8e" +checksum = "7a1d6bbefcb7b60acd19828e1bc965da6fcf18a7e39490c5f8be71e54a19ba32" dependencies = [ - "windows-core 0.61.2", - "windows-link 0.1.3", - "windows-threading", + "windows-core 0.61.0", + "windows-link 0.1.1", ] [[package]] @@ -3765,18 +3673,18 @@ checksum = "2bbd5b46c938e506ecbce286b6628a02171d56153ba733b6c741fc627ec9579b" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.98", ] [[package]] name = "windows-implement" -version = "0.60.2" +version = "0.60.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" +checksum = "a47fddd13af08290e67f4acabf4b459f647552718f683a7b415d290ac744a836" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.98", ] [[package]] @@ -3787,25 +3695,25 @@ checksum = "053c4c462dc91d3b1504c6fe5a726dd15e216ba718e84a0e46a88fbe5ded3515" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.98", ] [[package]] name = "windows-interface" -version = "0.59.3" +version = "0.59.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" +checksum = "bd9211b69f8dcdfa817bfd14bf1c97c9188afa36f4750130fcdf3f400eca9fa8" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.98", ] [[package]] name = "windows-link" -version = "0.1.3" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a" +checksum = "76840935b766e1b0a05c0066835fb9ec80071d4c09a16f6bd5f7e655e3c14c38" [[package]] name = "windows-link" @@ -3819,8 +3727,8 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9150af68066c4c5c07ddc0ce30421554771e528bde427614c61038bc2c92c2b1" dependencies = [ - "windows-core 0.61.2", - "windows-link 0.1.3", + "windows-core 0.61.0", + "windows-link 0.1.1", ] [[package]] @@ -3834,11 +3742,11 @@ dependencies = [ [[package]] name = "windows-result" -version = "0.3.4" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56f42bd332cc6c8eac5af113fc0c1fd6a8fd2aa08a0119358686e5160d0586c6" +checksum = "c64fd11a4fd95df68efcfee5f44a294fe71b8bc6a91993e2791938abcc712252" dependencies = [ - "windows-link 0.1.3", + "windows-link 0.1.1", ] [[package]] @@ -3853,11 +3761,11 @@ dependencies = [ [[package]] name = "windows-strings" -version = "0.4.2" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56e6c93f3a0c3b36176cb1327a4958a0353d5d166c2a35cb268ace15e91d3b57" +checksum = "7a2ba9642430ee452d5a7aa78d72907ebe8cfda358e8cb7918a2050581322f97" dependencies = [ - "windows-link 0.1.3", + "windows-link 0.1.1", ] [[package]] @@ -3887,15 +3795,6 @@ dependencies = [ "windows-targets 0.52.6", ] -[[package]] -name = "windows-sys" -version = "0.61.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" -dependencies = [ - "windows-link 0.2.1", -] - [[package]] name = "windows-targets" version = "0.42.2" @@ -3911,6 +3810,21 @@ dependencies = [ "windows_x86_64_msvc 0.42.2", ] +[[package]] +name = "windows-targets" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +dependencies = [ + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", +] + [[package]] name = "windows-targets" version = "0.52.6" @@ -3927,22 +3841,13 @@ dependencies = [ "windows_x86_64_msvc 0.52.6", ] -[[package]] -name = "windows-threading" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b66463ad2e0ea3bbf808b7f1d371311c80e115c0b71d60efc142cafbcfb057a6" -dependencies = [ - "windows-link 0.1.3", -] - [[package]] name = "windows-version" -version = "0.1.7" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4060a1da109b9d0326b7262c8e12c84df67cc0dbc9e33cf49e01ccc2eb63631" +checksum = "e04a5c6627e310a23ad2358483286c7df260c964eb2d003d8efd6d0f4e79265c" dependencies = [ - "windows-link 0.2.1", + "windows-link 0.1.1", ] [[package]] @@ -3951,6 +3856,12 @@ version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" + [[package]] name = "windows_aarch64_gnullvm" version = "0.52.6" @@ -3963,6 +3874,12 @@ version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" + [[package]] name = "windows_aarch64_msvc" version = "0.52.6" @@ -3975,6 +3892,12 @@ version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" +[[package]] +name = "windows_i686_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" + [[package]] name = "windows_i686_gnu" version = "0.52.6" @@ -3993,6 +3916,12 @@ version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" +[[package]] +name = "windows_i686_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" + [[package]] name = "windows_i686_msvc" version = "0.52.6" @@ -4005,6 +3934,12 @@ version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" + [[package]] name = "windows_x86_64_gnu" version = "0.52.6" @@ -4017,6 +3952,12 @@ version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" + [[package]] name = "windows_x86_64_gnullvm" version = "0.52.6" @@ -4029,6 +3970,12 @@ version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" + [[package]] name = "windows_x86_64_msvc" version = "0.52.6" @@ -4037,14 +3984,14 @@ checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "winit" -version = "0.30.13" +version = "0.30.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6755fa58a9f8350bd1e472d4c3fcc25f824ec358933bba33306d0b63df5978d" +checksum = "a809eacf18c8eca8b6635091543f02a5a06ddf3dad846398795460e6e0ae3cc0" dependencies = [ "ahash", "android-activity", "atomic-waker", - "bitflags 2.13.0", + "bitflags 2.8.0", "block2 0.5.1", "bytemuck", "calloop", @@ -4067,7 +4014,7 @@ dependencies = [ "pin-project", "raw-window-handle", "redox_syscall 0.4.1", - "rustix 0.38.44", + "rustix", "sctk-adwaita", "smithay-client-toolkit", "smol_str", @@ -4089,28 +4036,37 @@ dependencies = [ [[package]] name = "winnow" -version = "1.0.3" +version = "0.5.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0592e1c9d151f854e6fd382574c3a0855250e1d9b2f99d9281c6e6391af352f1" +checksum = "f593a95398737aeed53e489c785df13f3618e41dbcd6718c6addbf1395aa6876" dependencies = [ "memchr", ] [[package]] -name = "wit-bindgen" -version = "0.57.1" +name = "wit-bindgen-rt" +version = "0.33.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ebf944e87a7c253233ad6766e082e3cd714b5d03812acc24c318f549614536e" +checksum = "3268f3d866458b787f390cf61f4bbb563b922d091359f9608842999eaee3943c" +dependencies = [ + "bitflags 2.8.0", +] + +[[package]] +name = "write16" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1890f4022759daae28ed4fe62859b1236caebfc61ede2f63ed4e695f3f6d936" [[package]] name = "writeable" -version = "0.6.3" +version = "0.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ffae5123b2d3fc086436f8834ae3ab053a283cfac8fe0a0b8eaae044768a4c4" +checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51" [[package]] name = "wry" -version = "0.55.1" +version = "0.56.0" dependencies = [ "base64", "block2 0.6.2", @@ -4120,12 +4076,14 @@ dependencies = [ "dom_query", "dpi", "dunce", - "getrandom 0.3.4", + "gdkx11", + "getrandom 0.3.1", "glow 0.16.0", - "gtk4", + "gtk", "http", "http-range", - "jni 0.21.1", + "javascriptcore-rs", + "jni", "libc", "libloading", "ndk", @@ -4142,20 +4100,31 @@ dependencies = [ "sha2", "soup3", "tao", - "tao-macros 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", - "thiserror 2.0.18", + "tao-macros", + "thiserror 2.0.11", "tracing", "url", - "webkit6", + "webkit2gtk", + "webkit2gtk-sys", "webview2-com", "wgpu", - "windows 0.61.3", - "windows-core 0.61.2", + "windows 0.61.1", + "windows-core 0.61.0", "windows-version", "winit", "x11-dl", ] +[[package]] +name = "x11" +version = "2.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "502da5464ccd04011667b11c435cb992822c2c0dbde1770c988480d312a0db2e" +dependencies = [ + "libc", + "pkg-config", +] + [[package]] name = "x11-dl" version = "2.21.0" @@ -4169,30 +4138,30 @@ dependencies = [ [[package]] name = "x11rb" -version = "0.13.2" +version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9993aa5be5a26815fe2c3eacfc1fde061fc1a1f094bf1ad2a18bf9c495dd7414" +checksum = "5d91ffca73ee7f68ce055750bf9f6eca0780b8c85eff9bc046a3b0da41755e12" dependencies = [ "as-raw-xcb-connection", "gethostname", "libc", "libloading", "once_cell", - "rustix 1.1.4", + "rustix", "x11rb-protocol", ] [[package]] name = "x11rb-protocol" -version = "0.13.2" +version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea6fc2961e4ef194dcbfe56bb845534d0dc8098940c7e5c012a258bfec6701bd" +checksum = "ec107c4503ea0b4a98ef47356329af139c0a4f7750e621cf2973cd3385ebcb3d" [[package]] name = "xcursor" -version = "0.3.10" +version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bec9e4a500ca8864c5b47b8b482a73d62e4237670e5b5f1d6b9e3cae50f28f2b" +checksum = "0ef33da6b1660b4ddbfb3aef0ade110c8b8a781a3b6382fa5f2b5b040fd55f61" [[package]] name = "xkbcommon-dl" @@ -4200,7 +4169,7 @@ version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d039de8032a9a8856a6be89cea3e5d12fdd82306ab7c94d74e6deab2460651c5" dependencies = [ - "bitflags 2.13.0", + "bitflags 2.8.0", "dlib", "log", "once_cell", @@ -4215,16 +4184,17 @@ checksum = "b9cc00251562a284751c9973bace760d86c0276c471b4be569fe6b068ee97a56" [[package]] name = "xml-rs" -version = "0.8.28" +version = "0.8.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ae8337f8a065cfc972643663ea4279e04e7256de865aa66fe25cec5fb912d3f" +checksum = "c5b940ebc25896e71dd073bad2dbaa2abfe97b0a391415e22ad1326d9c54e3c4" [[package]] name = "yoke" -version = "0.8.3" +version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "709fe23a0424b6a435d82152b1bd3fdfb0833487d5fa90d05d42762a9891fef5" +checksum = "120e6aef9aa629e3d4f52dc8cc43a015c7724194c97dfaf45180d2daf2b77f40" dependencies = [ + "serde", "stable_deref_trait", "yoke-derive", "zerofrom", @@ -4232,73 +4202,62 @@ dependencies = [ [[package]] name = "yoke-derive" -version = "0.8.2" +version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de844c262c8848816172cef550288e7dc6c7b7814b4ee56b3e1553f275f1858e" +checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.98", "synstructure", ] [[package]] name = "zerocopy" -version = "0.8.53" +version = "0.7.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75726053136156d419e285b9b7eddaaea9e3fea6ce32eed44a89901f0bd98de1" +checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.8.53" +version = "0.7.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4714fd92cf900833d49538023a9b3915155210801d1c1169eba513b2addefd71" +checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.98", ] [[package]] name = "zerofrom" -version = "0.1.8" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ec05a11813ea801ff6d75110ad09cd0824ddba17dfe17128ea0d5f68e6c5272" +checksum = "cff3ee08c995dee1859d998dea82f7374f2826091dd9cd47def953cae446cd2e" dependencies = [ "zerofrom-derive", ] [[package]] name = "zerofrom-derive" -version = "0.1.7" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11532158c46691caf0f2593ea8358fed6bbf68a0315e80aae9bd41fbade684a1" +checksum = "595eed982f7d355beb85837f651fa22e90b3c044842dc7f2c2842c086f295808" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.98", "synstructure", ] -[[package]] -name = "zerotrie" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f9152d31db0792fa83f70fb2f83148effb5c1f5b8c7686c3459e361d9bc20bf" -dependencies = [ - "displaydoc", - "yoke", - "zerofrom", -] - [[package]] name = "zerovec" -version = "0.11.6" +version = "0.10.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90f911cbc359ab6af17377d242225f4d75119aec87ea711a880987b18cd7b239" +checksum = "aa2b893d79df23bfb12d5461018d408ea19dfafe76c2c7ef6d4eba614f8ff079" dependencies = [ "yoke", "zerofrom", @@ -4307,11 +4266,11 @@ dependencies = [ [[package]] name = "zerovec-derive" -version = "0.11.3" +version = "0.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "625dc425cab0dca6dc3c3319506e6593dcb08a9f387ea3b284dbd52a92c40555" +checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.98", ] diff --git a/ports/wry/Cargo.toml b/ports/wry/Cargo.toml index 49f7fb288208..bfcffa3687d1 100644 --- a/ports/wry/Cargo.toml +++ b/ports/wry/Cargo.toml @@ -2,7 +2,7 @@ [package] name = "wry" -version = "0.55.1" +version = "0.56.0" authors = ["Tauri Programme within The Commons Conservancy"] edition = "2021" license = "Apache-2.0 OR MIT" @@ -16,7 +16,7 @@ exclude = ["/.changes", "/.github", "/audits", "/wry-logo.svg"] [package.metadata.docs.rs] no-default-features = true -features = ["protocol", "os-webview"] +features = ["os-webview"] targets = [ "x86_64-unknown-linux-gnu", "x86_64-pc-windows-msvc", @@ -24,12 +24,9 @@ targets = [ ] [features] -default = ["protocol", "os-webview", "x11"] +default = ["os-webview", "x11"] serde = ["dpi/serde"] -protocol = [] devtools = [] -transparent = [] -fullscreen = [] linux-body = ["webkit/v2_42", "os-webview"] mac-proxy = [] os-webview = ["soup3", "webkit", "dep:gtk"] @@ -191,7 +188,7 @@ sha2 = "0.10" base64 = "0.22" jni = "0.21" ndk = "0.9" -tao-macros = "0.1" +tao-macros = "0.1.4" tao = { path = "../tao" } libc = "0.2" dom_query = { version = "0.28.0", default-features = false } diff --git a/ports/wry/README.md b/ports/wry/README.md index 2e0078748e63..73e00e78a029 100644 --- a/ports/wry/README.md +++ b/ports/wry/README.md @@ -45,7 +45,7 @@ let mut app = App::default(); event_loop.run_app(&mut app).unwrap(); ``` -If you also want to support Wayland too, then we recommend you use [`WebViewBuilderExtUnix::new_gtk`] on Linux. +If you also want to support Wayland too, then we recommend you use [`WebViewBuilderExtUnix::build_gtk`] on Linux. See the following example using [`tao`]: ```rust @@ -97,7 +97,7 @@ event_loop.run_app(&mut app).unwrap(); ``` If you want to support X11 and Wayland at the same time, we recommend using -[`WebViewExtUnix::new_gtk`] or [`WebViewBuilderExtUnix::new_gtk`] with [`gtk::Fixed`]. +[`WebViewExtUnix::new_gtk`] or [`WebViewBuilderExtUnix::build_gtk`] with [`gtk::Fixed`]. ```rust let event_loop = EventLoop::new(); @@ -114,7 +114,6 @@ let builder = WebViewBuilder::new() let webview = builder.build_as_child(&window).unwrap(); #[cfg(target_os = "linux")] let webview = { - # use gtk::prelude::*; let vbox = window.default_vbox().unwrap(); // tao adds a gtk::Box by default let fixed = gtk::Fixed::new(); vbox.append(&fixed); @@ -276,17 +275,12 @@ Wry uses a set of feature flags to toggle several advanced features. - `os-webview` (default): Enables the default WebView framework on the platform. This must be enabled for the crate to work. This feature was added in preparation of other ports like cef and servo. -- `protocol` (default): Enables [`WebViewBuilder::with_custom_protocol`] to define custom URL scheme for handling tasks like - loading assets. -- `drag-drop` (default): Enables [`WebViewBuilder::with_drag_drop_handler`] to control the behavior when there are files - interacting with the window. +- `x11` (default): Enables x11 support and dependencies on Linux. +- `serde`: Enables `dpi`'s `serde` feature. - `devtools`: Enables devtools on release builds. Devtools are always enabled in debug builds. On **macOS**, enabling devtools, requires calling private APIs so you should not enable this flag in release build if your app needs to publish to App Store. -- `transparent`: Transparent background on **macOS** requires calling private functions. - Avoid this in release build if your app needs to publish to App Store. -- `fullscreen`: Fullscreen video and other media on **macOS** requires calling private functions. - Avoid this in release build if your app needs to publish to App Store. +- `mac-proxy`: Enables `WebViewBuilder::with_proxy_config` on macOS. - `linux-body`: Enables body support of custom protocol request on Linux. Requires WebKit2GTK v2.40 or above. - `tracing`: enables [`tracing`] for `evaluate_script`, `ipc_handler`, and `custom_protocols`. diff --git a/ports/wry/examples/async_custom_protocol.rs b/ports/wry/examples/async_custom_protocol.rs index 0d550000f985..68ad7e67cb81 100644 --- a/ports/wry/examples/async_custom_protocol.rs +++ b/ports/wry/examples/async_custom_protocol.rs @@ -1,139 +1,121 @@ -// Copyright 2019-2024 Tauri Programme within The Commons Conservancy -// SPDX-License-Identifier: Apache-2.0 -// SPDX-License-Identifier: MIT - // Copyright 2020-2023 Tauri Programme within The Commons Conservancy // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT -fn main() -> wry::Result<()> { - imp::main() -} - -#[cfg(not(feature = "protocol"))] -mod imp { - pub fn main() -> wry::Result<()> { - unimplemented!() - } -} +use std::path::PathBuf; -#[cfg(feature = "protocol")] -mod imp { - use std::path::PathBuf; +use tao::{ + event::{Event, WindowEvent}, + event_loop::{ControlFlow, EventLoop}, + window::WindowBuilder, +}; +use wry::{ + http::{header::CONTENT_TYPE, Request, Response, StatusCode}, + WebViewBuilder, +}; - use tao::{ - event::{Event, WindowEvent}, - event_loop::{ControlFlow, EventLoop}, - window::WindowBuilder, - }; - use wry::{ - http::{header::CONTENT_TYPE, Request, Response, StatusCode}, - WebViewBuilder, - }; +pub fn main() -> wry::Result<()> { + let event_loop = EventLoop::new(); + let window = WindowBuilder::new().build(&event_loop).unwrap(); - pub fn main() -> wry::Result<()> { - let event_loop = EventLoop::new(); - let window = WindowBuilder::new().build(&event_loop).unwrap(); + let builder = WebViewBuilder::new() + .with_asynchronous_custom_protocol("wry".into(), move |_webview_id, request, responder| { + match get_wry_response(request) { + Ok(http_response) => responder.respond(http_response), + Err(e) => responder.respond( + http::Response::builder() + .header(CONTENT_TYPE, "text/plain") + .status(500) + .body(e.to_string().as_bytes().to_vec()) + .unwrap(), + ), + } + }) + // tell the webview to load the custom protocol + .with_url("wry://localhost"); - let builder = WebViewBuilder::new() - .with_asynchronous_custom_protocol("wry".into(), move |_webview_id, request, responder| { - match get_wry_response(request) { - Ok(http_response) => responder.respond(http_response), - Err(e) => responder.respond( - http::Response::builder() - .header(CONTENT_TYPE, "text/plain") - .status(500) - .body(e.to_string().as_bytes().to_vec()) - .unwrap(), - ), - } - }) - // tell the webview to load the custom protocol - .with_url("wry://localhost"); + #[cfg(any( + target_os = "windows", + target_os = "macos", + target_os = "ios", + target_os = "android" + ))] + let _webview = builder.build(&window)?; + #[cfg(not(any( + target_os = "windows", + target_os = "macos", + target_os = "ios", + target_os = "android" + )))] + let _webview = { + use tao::platform::unix::WindowExtUnix; + use wry::WebViewBuilderExtUnix; + let vbox = window.default_vbox().unwrap(); + builder.build_gtk(vbox)? + }; - #[cfg(any( - target_os = "windows", - target_os = "macos", - target_os = "ios", - target_os = "android" - ))] - let _webview = builder.build(&window)?; - #[cfg(not(any( - target_os = "windows", - target_os = "macos", - target_os = "ios", - target_os = "android" - )))] - let _webview = { - use tao::platform::unix::WindowExtUnix; - use wry::WebViewBuilderExtUnix; - let vbox = window.default_vbox().unwrap(); - builder.build_gtk(vbox)? - }; + event_loop.run(move |event, _, control_flow| { + *control_flow = ControlFlow::Wait; - event_loop.run(move |event, _, control_flow| { - *control_flow = ControlFlow::Wait; + if let Event::WindowEvent { + event: WindowEvent::CloseRequested, + .. + } = event + { + *control_flow = ControlFlow::Exit + } + }); +} - if let Event::WindowEvent { - event: WindowEvent::CloseRequested, - .. - } = event - { - *control_flow = ControlFlow::Exit - } - }); +fn get_wry_response( + request: Request>, +) -> Result>, Box> { + let path = request.uri().path(); + // Read the file content from file path + let root = PathBuf::from("examples/custom_protocol"); + let path = if path == "/" { + "index.html" + } else { + // removing leading slash + &path[1..] + }; + let root = std::fs::canonicalize(root)?; + let file_path = std::fs::canonicalize(root.join(path))?; + if !file_path.starts_with(&root) { + return Ok( + Response::builder() + .status(StatusCode::NOT_FOUND) + .header(CONTENT_TYPE, "text/plain") + .body(b"not found".to_vec())?, + ); } + let content = std::fs::read(file_path)?; - fn get_wry_response( - request: Request>, - ) -> Result>, Box> { - // Read the file content from file path - let root = std::fs::canonicalize(PathBuf::from("examples/custom_protocol"))?; - let request_path = request.uri().path(); - let path = if request_path == "/" { - "index.html" - } else { - // removing leading slash - &request_path[1..] - }; - let file_path = match std::fs::canonicalize(root.join(path)) { - Ok(file_path) if file_path.starts_with(&root) => file_path, - _ => return text_response(StatusCode::NOT_FOUND, "not found"), - }; - - // Return asset contents and mime types based on file extensions - // If you don't want to do this manually, there are some crates for you. - // Such as `infer` and `mime_guess`. - let mimetype = if path.ends_with(".html") || request_path == "/" { - Some("text/html") - } else if path.ends_with(".js") { - Some("text/javascript") - } else if path.ends_with(".png") { - Some("image/png") - } else if path.ends_with(".wasm") { - Some("application/wasm") - } else { - None - }; - let Some(mimetype) = mimetype else { - return text_response(StatusCode::UNSUPPORTED_MEDIA_TYPE, "unsupported media type"); - }; - let content = std::fs::read(file_path)?; - - Response::builder() - .header(CONTENT_TYPE, mimetype) - .body(content) - .map_err(Into::into) - } + // Return asset contents and mime types based on file extensions + // If you don't want to do this manually, there are some crates for you. + // Such as `infer` and `mime_guess`. + let mimetype = if path.ends_with(".html") || path == "/" { + Some("text/html") + } else if path.ends_with(".js") { + Some("text/javascript") + } else if path.ends_with(".png") { + Some("image/png") + } else if path.ends_with(".wasm") { + Some("application/wasm") + } else { + None + }; + let Some(mimetype) = mimetype else { + return Ok( + Response::builder() + .status(StatusCode::UNSUPPORTED_MEDIA_TYPE) + .header(CONTENT_TYPE, "text/plain") + .body(b"unsupported media type".to_vec())?, + ); + }; - fn text_response( - status: StatusCode, - body: &str, - ) -> Result>, Box> { - Response::builder() - .header(CONTENT_TYPE, "text/plain") - .status(status) - .body(body.as_bytes().to_vec()) - .map_err(Into::into) - } + Response::builder() + .header(CONTENT_TYPE, mimetype) + .body(content) + .map_err(Into::into) } diff --git a/ports/wry/examples/custom_protocol.rs b/ports/wry/examples/custom_protocol.rs index b25116c68a03..44fd309b0550 100644 --- a/ports/wry/examples/custom_protocol.rs +++ b/ports/wry/examples/custom_protocol.rs @@ -6,116 +6,102 @@ // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT -fn main() -> wry::Result<()> { - imp::main() -} - -#[cfg(not(feature = "protocol"))] -mod imp { - pub fn main() -> wry::Result<()> { - unimplemented!() - } -} +use std::path::PathBuf; -#[cfg(feature = "protocol")] -mod imp { - use std::path::PathBuf; - - use tao::{ - event::{Event, WindowEvent}, - event_loop::{ControlFlow, EventLoop}, - window::WindowBuilder, - }; - use wry::{ - http::{header::CONTENT_TYPE, Request, Response}, - WebViewBuilder, - }; +use tao::{ + event::{Event, WindowEvent}, + event_loop::{ControlFlow, EventLoop}, + window::WindowBuilder, +}; +use wry::{ + http::{header::CONTENT_TYPE, Request, Response}, + WebViewBuilder, +}; - pub fn main() -> wry::Result<()> { - let event_loop = EventLoop::new(); - let window = WindowBuilder::new().build(&event_loop).unwrap(); +pub fn main() -> wry::Result<()> { + let event_loop = EventLoop::new(); + let window = WindowBuilder::new().build(&event_loop).unwrap(); - let builder = WebViewBuilder::new() - .with_custom_protocol( - "wry".into(), - move |_webview_id, request| match get_wry_response(request) { - Ok(r) => r.map(Into::into), - Err(e) => http::Response::builder() - .header(CONTENT_TYPE, "text/plain") - .status(500) - .body(e.to_string().as_bytes().to_vec()) - .unwrap() - .map(Into::into), - }, - ) - // tell the webview to load the custom protocol - .with_url("wry://localhost"); + let builder = WebViewBuilder::new() + .with_custom_protocol( + "wry".into(), + move |_webview_id, request| match get_wry_response(request) { + Ok(r) => r.map(Into::into), + Err(e) => http::Response::builder() + .header(CONTENT_TYPE, "text/plain") + .status(500) + .body(e.to_string().as_bytes().to_vec()) + .unwrap() + .map(Into::into), + }, + ) + // tell the webview to load the custom protocol + .with_url("wry://localhost"); - #[cfg(any( - target_os = "windows", - target_os = "macos", - target_os = "ios", - target_os = "android" - ))] - let _webview = builder.build(&window)?; - #[cfg(not(any( - target_os = "windows", - target_os = "macos", - target_os = "ios", - target_os = "android" - )))] - let _webview = { - use tao::platform::unix::WindowExtUnix; - use wry::WebViewBuilderExtUnix; - let vbox = window.default_vbox().unwrap(); - builder.build_gtk(vbox)? - }; + #[cfg(any( + target_os = "windows", + target_os = "macos", + target_os = "ios", + target_os = "android" + ))] + let _webview = builder.build(&window)?; + #[cfg(not(any( + target_os = "windows", + target_os = "macos", + target_os = "ios", + target_os = "android" + )))] + let _webview = { + use tao::platform::unix::WindowExtUnix; + use wry::WebViewBuilderExtUnix; + let vbox = window.default_vbox().unwrap(); + builder.build_gtk(vbox)? + }; - event_loop.run(move |event, _, control_flow| { - *control_flow = ControlFlow::Wait; + event_loop.run(move |event, _, control_flow| { + *control_flow = ControlFlow::Wait; - if let Event::WindowEvent { - event: WindowEvent::CloseRequested, - .. - } = event - { - *control_flow = ControlFlow::Exit - } - }); - } + if let Event::WindowEvent { + event: WindowEvent::CloseRequested, + .. + } = event + { + *control_flow = ControlFlow::Exit + } + }); +} - fn get_wry_response( - request: Request>, - ) -> Result>, Box> { - let path = request.uri().path(); - // Read the file content from file path - let root = PathBuf::from("examples/custom_protocol"); - let path = if path == "/" { - "index.html" - } else { - // removing leading slash - &path[1..] - }; - let content = std::fs::read(std::fs::canonicalize(root.join(path))?)?; +fn get_wry_response( + request: Request>, +) -> Result>, Box> { + let path = request.uri().path(); + // Read the file content from file path + let root = PathBuf::from("examples/custom_protocol"); + let path = if path == "/" { + "index.html" + } else { + // removing leading slash + &path[1..] + }; + let content = std::fs::read(std::fs::canonicalize(root.join(path))?)?; - // Return asset contents and mime types based on file extentions - // If you don't want to do this manually, there are some crates for you. - // Such as `infer` and `mime_guess`. - let mimetype = if path.ends_with(".html") || path == "/" { - "text/html" - } else if path.ends_with(".js") { - "text/javascript" - } else if path.ends_with(".png") { - "image/png" - } else if path.ends_with(".wasm") { - "application/wasm" - } else { - unimplemented!(); - }; + // Return asset contents and mime types based on file extentions + // If you don't want to do this manually, there are some crates for you. + // Such as `infer` and `mime_guess`. + let mimetype = if path.ends_with(".html") || path == "/" { + "text/html" + } else if path.ends_with(".js") { + "text/javascript" + } else if path.ends_with(".png") { + "image/png" + } else if path.ends_with(".wasm") { + "application/wasm" + } else { + unimplemented!(); + }; - Response::builder() - .header(CONTENT_TYPE, mimetype) - .body(content) - .map_err(Into::into) - } + Response::builder() + .header(CONTENT_TYPE, mimetype) + .body(content) + .map_err(Into::into) } diff --git a/ports/wry/examples/streaming.rs b/ports/wry/examples/streaming.rs index 08a26689f715..894583f9f894 100644 --- a/ports/wry/examples/streaming.rs +++ b/ports/wry/examples/streaming.rs @@ -1,296 +1,275 @@ -// Copyright 2019-2024 Tauri Programme within The Commons Conservancy -// SPDX-License-Identifier: Apache-2.0 -// SPDX-License-Identifier: MIT - // Copyright 2020-2023 Tauri Programme within The Commons Conservancy // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT -fn main() -> wry::Result<()> { - imp::main() -} +use std::{ + io::{Read, Seek, SeekFrom, Write}, + path::PathBuf, +}; + +use http::{header, StatusCode}; +use http_range::HttpRange; +use tao::{ + event::{Event, WindowEvent}, + event_loop::{ControlFlow, EventLoop}, + window::WindowBuilder, +}; +use wry::{ + http::{header::*, Request, Response}, + WebViewBuilder, +}; + +pub fn main() -> wry::Result<()> { + let event_loop = EventLoop::new(); + let window = WindowBuilder::new().build(&event_loop).unwrap(); + + let builder = WebViewBuilder::new() + .with_custom_protocol( + "wry".into(), + move |_webview_id, request| match wry_protocol(request) { + Ok(r) => r.map(Into::into), + Err(e) => http::Response::builder() + .header(CONTENT_TYPE, "text/plain") + .status(500) + .body(e.to_string().as_bytes().to_vec()) + .unwrap() + .map(Into::into), + }, + ) + .with_custom_protocol( + "stream".into(), + move |_webview_id, request| match stream_protocol(request) { + Ok(r) => r.map(Into::into), + Err(e) => http::Response::builder() + .header(CONTENT_TYPE, "text/plain") + .status(500) + .body(e.to_string().as_bytes().to_vec()) + .unwrap() + .map(Into::into), + }, + ) + // tell the webview to load the custom protocol + .with_url("wry://localhost"); + + #[cfg(any( + target_os = "windows", + target_os = "macos", + target_os = "ios", + target_os = "android" + ))] + let _webview = builder.build(&window)?; + #[cfg(not(any( + target_os = "windows", + target_os = "macos", + target_os = "ios", + target_os = "android" + )))] + let _webview = { + use tao::platform::unix::WindowExtUnix; + use wry::WebViewBuilderExtUnix; + let vbox = window.default_vbox().unwrap(); + builder.build_gtk(vbox)? + }; -#[cfg(not(feature = "protocol"))] -mod imp { - pub fn main() -> wry::Result<()> { - unimplemented!() - } -} + event_loop.run(move |event, _, control_flow| { + *control_flow = ControlFlow::Wait; -#[cfg(feature = "protocol")] -mod imp { + if let Event::WindowEvent { + event: WindowEvent::CloseRequested, + .. + } = event + { + *control_flow = ControlFlow::Exit + } + }); +} - use std::{ - io::{Read, Seek, SeekFrom, Write}, - path::PathBuf, +fn wry_protocol( + request: Request>, +) -> Result>, Box> { + let path = request.uri().path(); + // Read the file content from file path + let root = PathBuf::from("examples/streaming"); + let path = if path == "/" { + "index.html" + } else { + // removing leading slash + &path[1..] }; - - use http::{header, StatusCode}; - use http_range::HttpRange; - use tao::{ - event::{Event, WindowEvent}, - event_loop::{ControlFlow, EventLoop}, - window::WindowBuilder, + let root = std::fs::canonicalize(root)?; + let file_path = std::fs::canonicalize(root.join(path))?; + if !file_path.starts_with(&root) { + return Err("requested path escapes the example root".into()); + } + let content = std::fs::read(file_path)?; + + // Return asset contents and mime types based on file extensions + // If you don't want to do this manually, there are some crates for you. + // Such as `infer` and `mime_guess`. + let mimetype = if path.ends_with(".html") || path == "/" { + Some("text/html") + } else if path.ends_with(".js") { + Some("text/javascript") + } else { + None }; - use wry::{ - http::{header::*, Request, Response}, - WebViewBuilder, + let Some(mimetype) = mimetype else { + return Ok( + Response::builder() + .status(StatusCode::UNSUPPORTED_MEDIA_TYPE) + .header(CONTENT_TYPE, "text/plain") + .body(b"unsupported media type".to_vec())?, + ); }; - pub fn main() -> wry::Result<()> { - let event_loop = EventLoop::new(); - let window = WindowBuilder::new().build(&event_loop).unwrap(); - - let builder = WebViewBuilder::new() - .with_custom_protocol( - "wry".into(), - move |_webview_id, request| match wry_protocol(request) { - Ok(r) => r.map(Into::into), - Err(e) => http::Response::builder() - .header(CONTENT_TYPE, "text/plain") - .status(500) - .body(e.to_string().as_bytes().to_vec()) - .unwrap() - .map(Into::into), - }, - ) - .with_custom_protocol( - "stream".into(), - move |_webview_id, request| match stream_protocol(request) { - Ok(r) => r.map(Into::into), - Err(e) => http::Response::builder() - .header(CONTENT_TYPE, "text/plain") - .status(500) - .body(e.to_string().as_bytes().to_vec()) - .unwrap() - .map(Into::into), - }, - ) - // tell the webview to load the custom protocol - .with_url("wry://localhost"); - - #[cfg(any( - target_os = "windows", - target_os = "macos", - target_os = "ios", - target_os = "android" - ))] - let _webview = builder.build(&window)?; - #[cfg(not(any( - target_os = "windows", - target_os = "macos", - target_os = "ios", - target_os = "android" - )))] - let _webview = { - use tao::platform::unix::WindowExtUnix; - use wry::WebViewBuilderExtUnix; - let vbox = window.default_vbox().unwrap(); - builder.build_gtk(vbox)? - }; + Response::builder() + .header(CONTENT_TYPE, mimetype) + .body(content) + .map_err(Into::into) +} - event_loop.run(move |event, _, control_flow| { - *control_flow = ControlFlow::Wait; +fn stream_protocol( + request: http::Request>, +) -> Result>, Box> { + // skip leading `/` + let path = percent_encoding::percent_decode(&request.uri().path().as_bytes()[1..]) + .decode_utf8_lossy() + .to_string(); + + let mut file = std::fs::File::open(path)?; + + // get file length + let len = { + let old_pos = file.stream_position()?; + let len = file.seek(SeekFrom::End(0))?; + file.seek(SeekFrom::Start(old_pos))?; + len + }; - if let Event::WindowEvent { - event: WindowEvent::CloseRequested, - .. - } = event - { - *control_flow = ControlFlow::Exit - } - }); - } + let mut resp = Response::builder().header(CONTENT_TYPE, "video/mp4"); - fn wry_protocol( - request: Request>, - ) -> Result>, Box> { - let path = request.uri().path(); - // Read the file content from file path - let root = PathBuf::from("examples/streaming"); - let path = if path == "/" { - "index.html" - } else { - // removing leading slash - &path[1..] + // if the webview sent a range header, we need to send a 206 in return + // Actually only macOS and Windows are supported. Linux will ALWAYS return empty headers. + let http_response = if let Some(range_header) = request.headers().get("range") { + let not_satisfiable = || { + Response::builder() + .status(StatusCode::RANGE_NOT_SATISFIABLE) + .header(header::CONTENT_RANGE, format!("bytes */{len}")) + .body(vec![]) }; - let root = std::fs::canonicalize(root)?; - let file_path = std::fs::canonicalize(root.join(path))?; - if !file_path.starts_with(&root) { - return Err("requested path escapes streaming example root".into()); - } - let content = std::fs::read(file_path)?; - - // Return asset contents and mime types based on file extensions - // If you don't want to do this manually, there are some crates for you. - // Such as `infer` and `mime_guess`. - let mimetype = if path.ends_with(".html") || path == "/" { - Some("text/html") - } else if path.ends_with(".js") { - Some("text/javascript") + + // parse range header + let ranges = if let Ok(ranges) = HttpRange::parse(range_header.to_str()?, len) { + ranges + .iter() + // map the output back to spec range , example: 0-499 + .map(|r| (r.start, r.start + r.length - 1)) + .collect::>() } else { - None - }; - let Some(mimetype) = mimetype else { - return Ok( - Response::builder() - .status(StatusCode::UNSUPPORTED_MEDIA_TYPE) - .header(CONTENT_TYPE, "text/plain") - .body(b"unsupported media type".to_vec())?, - ); + return Ok(not_satisfiable()?); }; - Response::builder() - .header(CONTENT_TYPE, mimetype) - .body(content) - .map_err(Into::into) - } + /// The Maximum bytes we send in one range + const MAX_LEN: u64 = 1000 * 1024; - fn stream_protocol( - request: http::Request>, - ) -> Result>, Box> { - // skip leading `/` - let path = percent_encoding::percent_decode(&request.uri().path().as_bytes()[1..]) - .decode_utf8_lossy() - .to_string(); - - // This demo intentionally streams the local path typed into the page. - // Constrain this to an allow-listed directory before reusing it with untrusted content. - let mut file = std::fs::File::open(path)?; - - // get file length - let len = { - let old_pos = file.stream_position()?; - let len = file.seek(SeekFrom::End(0))?; - file.seek(SeekFrom::Start(old_pos))?; - len - }; + if ranges.len() == 1 { + let &(start, mut end) = ranges.first().unwrap(); - let mut resp = Response::builder().header(CONTENT_TYPE, "video/mp4"); - - // if the webview sent a range header, we need to send a 206 in return - // Actually only macOS and Windows are supported. Linux will ALWAYS return empty headers. - let http_response = if let Some(range_header) = request.headers().get("range") { - let not_satisfiable = || { - Response::builder() - .status(StatusCode::RANGE_NOT_SATISFIABLE) - .header(header::CONTENT_RANGE, format!("bytes */{len}")) - .body(vec![]) - }; - - // parse range header - let ranges = if let Ok(ranges) = HttpRange::parse(range_header.to_str()?, len) { - ranges - .iter() - // map the output back to spec range , example: 0-499 - .map(|r| (r.start, r.start + r.length - 1)) - .collect::>() - } else { + // check if a range is not satisfiable + // + // this should be already taken care of by HttpRange::parse + // but checking here again for extra assurance + if start >= len || end >= len || end < start { return Ok(not_satisfiable()?); - }; + } + + // adjust end byte for MAX_LEN + end = start + (end - start).min(len - start).min(MAX_LEN - 1); + + // calculate number of bytes needed to be read + let bytes_to_read = end + 1 - start; - /// The Maximum bytes we send in one range - const MAX_LEN: u64 = 1000 * 1024; + // allocate a buf with a suitable capacity + let mut buf = Vec::with_capacity(bytes_to_read as usize); + // seek the file to the starting byte + file.seek(SeekFrom::Start(start))?; + // read the needed bytes + file.take(bytes_to_read).read_to_end(&mut buf)?; + + resp = resp.header(CONTENT_RANGE, format!("bytes {start}-{end}/{len}")); + resp = resp.header(CONTENT_LENGTH, end + 1 - start); + resp = resp.status(StatusCode::PARTIAL_CONTENT); + resp.body(buf) + } else { + let mut buf = Vec::new(); + let ranges = ranges + .iter() + .filter_map(|&(start, mut end)| { + // filter out unsatisfiable ranges + // + // this should be already taken care of by HttpRange::parse + // but checking here again for extra assurance + if start >= len || end >= len || end < start { + None + } else { + // adjust end byte for MAX_LEN + end = start + (end - start).min(len - start).min(MAX_LEN - 1); + Some((start, end)) + } + }) + .collect::>(); + + let boundary = random_boundary(); + let boundary_sep = format!("\r\n--{boundary}\r\n"); + let boundary_closer = format!("\r\n--{boundary}\r\n"); + + resp = resp.header( + CONTENT_TYPE, + format!("multipart/byteranges; boundary={boundary}"), + ); - if ranges.len() == 1 { - let &(start, mut end) = ranges.first().unwrap(); + for (end, start) in ranges { + // a new range is being written, write the range boundary + buf.write_all(boundary_sep.as_bytes())?; - // check if a range is not satisfiable - // - // this should be already taken care of by HttpRange::parse - // but checking here again for extra assurance - if start >= len || end >= len || end < start { - return Ok(not_satisfiable()?); - } + // write the needed headers `Content-Type` and `Content-Range` + buf.write_all(format!("{CONTENT_TYPE}: video/mp4\r\n").as_bytes())?; + buf.write_all(format!("{CONTENT_RANGE}: bytes {start}-{end}/{len}\r\n").as_bytes())?; - // adjust end byte for MAX_LEN - end = start + (end - start).min(len - start).min(MAX_LEN - 1); + // write the separator to indicate the start of the range body + buf.write_all("\r\n".as_bytes())?; // calculate number of bytes needed to be read let bytes_to_read = end + 1 - start; - // allocate a buf with a suitable capacity - let mut buf = Vec::with_capacity(bytes_to_read as usize); - // seek the file to the starting byte + let mut local_buf = vec![0_u8; bytes_to_read as usize]; file.seek(SeekFrom::Start(start))?; - // read the needed bytes - file.take(bytes_to_read).read_to_end(&mut buf)?; - - resp = resp.header(CONTENT_RANGE, format!("bytes {start}-{end}/{len}")); - resp = resp.header(CONTENT_LENGTH, end + 1 - start); - resp = resp.status(StatusCode::PARTIAL_CONTENT); - resp.body(buf) - } else { - let mut buf = Vec::new(); - let ranges = ranges - .iter() - .filter_map(|&(start, mut end)| { - // filter out unsatisfiable ranges - // - // this should be already taken care of by HttpRange::parse - // but checking here again for extra assurance - if start >= len || end >= len || end < start { - None - } else { - // adjust end byte for MAX_LEN - end = start + (end - start).min(len - start).min(MAX_LEN - 1); - Some((start, end)) - } - }) - .collect::>(); - - let boundary = random_boundary(); - let boundary_sep = format!("\r\n--{boundary}\r\n"); - let boundary_closer = format!("\r\n--{boundary}\r\n"); - - resp = resp.header( - CONTENT_TYPE, - format!("multipart/byteranges; boundary={boundary}"), - ); - - for (start, end) in ranges { - // a new range is being written, write the range boundary - buf.write_all(boundary_sep.as_bytes())?; - - // write the needed headers `Content-Type` and `Content-Range` - buf.write_all(format!("{CONTENT_TYPE}: video/mp4\r\n").as_bytes())?; - buf.write_all(format!("{CONTENT_RANGE}: bytes {start}-{end}/{len}\r\n").as_bytes())?; - - // write the separator to indicate the start of the range body - buf.write_all("\r\n".as_bytes())?; - - // calculate number of bytes needed to be read - let bytes_to_read = end + 1 - start; - - let mut local_buf = vec![0_u8; bytes_to_read as usize]; - file.seek(SeekFrom::Start(start))?; - file.read_exact(&mut local_buf)?; - buf.extend_from_slice(&local_buf); - } - // all ranges have been written, write the closing boundary - buf.write_all(boundary_closer.as_bytes())?; - - resp.body(buf) + file.read_exact(&mut local_buf)?; + buf.extend_from_slice(&local_buf); } - } else { - resp = resp.header(CONTENT_LENGTH, len); - let mut buf = Vec::with_capacity(len as usize); - file.read_to_end(&mut buf)?; + // all ranges have been written, write the closing boundary + buf.write_all(boundary_closer.as_bytes())?; + resp.body(buf) - }; + } + } else { + resp = resp.header(CONTENT_LENGTH, len); + let mut buf = Vec::with_capacity(len as usize); + file.read_to_end(&mut buf)?; + resp.body(buf) + }; - http_response.map_err(Into::into) - } + http_response.map_err(Into::into) +} - fn random_boundary() -> String { - let mut x = [0_u8; 30]; - getrandom::fill(&mut x).expect("failed to get random bytes"); - (x[..]) - .iter() - .map(|&x| format!("{x:x}")) - .fold(String::new(), |mut a, x| { - a.push_str(x.as_str()); - a - }) - } +fn random_boundary() -> String { + let mut x = [0_u8; 30]; + getrandom::fill(&mut x).expect("failed to get random bytes"); + (x[..]) + .iter() + .map(|&x| format!("{x:x}")) + .fold(String::new(), |mut a, x| { + a.push_str(x.as_str()); + a + }) } diff --git a/ports/wry/examples/transparent.rs b/ports/wry/examples/transparent.rs index dc6a9e5e7df2..8bcd2289fd41 100644 --- a/ports/wry/examples/transparent.rs +++ b/ports/wry/examples/transparent.rs @@ -36,7 +36,6 @@ fn main() -> wry::Result<()> { let builder = WebViewBuilder::new() // The second is on webview... - // Feature `transparent` is required for transparency to work. .with_transparent(true) // And the last is in html. .with_html( diff --git a/ports/wry/src/android/binding.rs b/ports/wry/src/android/binding.rs index e681cfe73414..cea8dfa85788 100644 --- a/ports/wry/src/android/binding.rs +++ b/ports/wry/src/android/binding.rs @@ -39,7 +39,7 @@ macro_rules! android_binding { ($domain:ident, $package:ident, $wry:path) => {{ use $wry::{android_setup as _, prelude::*}; - android_fn!($domain, $package, Rust, wryCreate, []); + android_fn!($domain, $package, Rust, onFirstActivityCreateWry, []); android_fn!( $domain, $package, @@ -278,7 +278,7 @@ fn handle_request( } #[allow(non_snake_case)] -pub unsafe fn wryCreate(env: JNIEnv, _: JClass) { +pub unsafe fn onFirstActivityCreateWry(env: JNIEnv, _: JClass) { let mut main_pipe = MainPipe { env }; let looper = ThreadLooper::for_thread().unwrap(); diff --git a/ports/wry/src/android/kotlin/Rust.kt b/ports/wry/src/android/kotlin/Rust.kt index 4deeb8bf0345..0c28e837505b 100644 --- a/ports/wry/src/android/kotlin/Rust.kt +++ b/ports/wry/src/android/kotlin/Rust.kt @@ -20,20 +20,19 @@ object Rust { System.loadLibrary("{{library}}") } - @JvmStatic external fun onActivityCreate(activity: WryActivity) - @JvmStatic external fun onActivityDestroy(activity: WryActivity) - @JvmStatic external fun onActivitySaveInstanceState() - @JvmStatic external fun onActivityLowMemory() + // Tao bindings + @JvmStatic external fun onFirstActivityCreate() + @JvmStatic external fun onCreate(activity: WryActivity) + @JvmStatic external fun onStart(activity: WryActivity) + @JvmStatic external fun onResume(activity: WryActivity) + @JvmStatic external fun onPause(activity: WryActivity) + @JvmStatic external fun onStop(activity: WryActivity) + @JvmStatic external fun onDestroy(activity: WryActivity) @JvmStatic external fun onWindowFocusChanged(activity: WryActivity, focus: Boolean) + @JvmStatic external fun onLowMemory() @JvmStatic external fun onNewIntent(intent: Intent) - @JvmStatic external fun create() - @JvmStatic external fun start() - @JvmStatic external fun resume() - @JvmStatic external fun pause() - @JvmStatic external fun stop() - - @JvmStatic external fun wryCreate() + @JvmStatic external fun onFirstActivityCreateWry() @JvmStatic external fun onWebviewDestroy(activity: WryActivity, webviewId: String) @JvmStatic external fun ipc(webviewId: String, url: String, message: String) diff --git a/ports/wry/src/android/kotlin/WryActivity.kt b/ports/wry/src/android/kotlin/WryActivity.kt index 477de477a28e..a19d01f6e4d9 100644 --- a/ports/wry/src/android/kotlin/WryActivity.kt +++ b/ports/wry/src/android/kotlin/WryActivity.kt @@ -13,10 +13,8 @@ import android.content.Intent import android.os.Build import android.os.Bundle import android.webkit.WebView -import android.view.KeyEvent import androidx.activity.OnBackPressedCallback import androidx.activity.result.ActivityResult -import androidx.activity.result.ActivityResultCallback import androidx.activity.result.ActivityResultLauncher import androidx.activity.result.contract.ActivityResultContracts import androidx.appcompat.app.AppCompatActivity @@ -24,33 +22,14 @@ import androidx.lifecycle.DefaultLifecycleObserver import androidx.lifecycle.LifecycleOwner import androidx.lifecycle.ProcessLifecycleOwner -private val ACTIVITY_ID_KEY = "__wryActivityId" +private const val ACTIVITY_ID_KEY = "__wryActivityId" object WryLifecycleObserver : DefaultLifecycleObserver { + // This only runs once: https://developer.android.com/reference/androidx/lifecycle/ProcessLifecycleOwner override fun onCreate(owner: LifecycleOwner) { super.onCreate(owner) - Rust.create() - Rust.wryCreate() - } - - override fun onStart(owner: LifecycleOwner) { - super.onStart(owner) - Rust.start() - } - - override fun onResume(owner: LifecycleOwner) { - super.onResume(owner) - Rust.resume() - } - - override fun onPause(owner: LifecycleOwner) { - super.onPause(owner) - Rust.pause() - } - - override fun onStop(owner: LifecycleOwner) { - super.onStop(owner) - Rust.stop() + Rust.onFirstActivityCreate() + Rust.onFirstActivityCreateWry() } } @@ -149,8 +128,13 @@ abstract class WryActivity : AppCompatActivity() { activityListener?.invoke(result) } + Rust.onCreate(this) ProcessLifecycleOwner.get().lifecycle.addObserver(WryLifecycleObserver) - Rust.onActivityCreate(this) + } + + override fun onStart() { + super.onStart() + Rust.onStart(this) } override fun onWindowFocusChanged(hasFocus: Boolean) { @@ -161,11 +145,11 @@ abstract class WryActivity : AppCompatActivity() { override fun onSaveInstanceState(outState: Bundle) { super.onSaveInstanceState(outState) outState.putInt(ACTIVITY_ID_KEY, id) - Rust.onActivitySaveInstanceState() } override fun onPause() { super.onPause() + Rust.onPause(this) if (::mWebView.isInitialized) { mWebView.onPause() } @@ -173,20 +157,26 @@ abstract class WryActivity : AppCompatActivity() { override fun onResume() { super.onResume() + Rust.onResume(this) if (::mWebView.isInitialized) { mWebView.onResume() } } + override fun onStop() { + super.onStop() + Rust.onStop(this) + } + override fun onDestroy() { super.onDestroy() - Rust.onActivityDestroy(this) + Rust.onDestroy(this) Rust.onWebviewDestroy(this, if (::mWebView.isInitialized) { mWebView.id } else { "" }) } override fun onLowMemory() { super.onLowMemory() - Rust.onActivityLowMemory() + Rust.onLowMemory() } override fun onNewIntent(intent: Intent) { @@ -198,6 +188,7 @@ abstract class WryActivity : AppCompatActivity() { return Class.forName(name) } + // Called by tao through JNI fun startActivity(cls: Class<*>): Int { val intent = Intent(this, cls) val id = kotlin.random.Random.nextInt() diff --git a/ports/wry/src/android/main_pipe.rs b/ports/wry/src/android/main_pipe.rs index 8f83ecc9ce67..e7f4a8c1a69f 100644 --- a/ports/wry/src/android/main_pipe.rs +++ b/ports/wry/src/android/main_pipe.rs @@ -124,8 +124,18 @@ impl<'a> MainPipe<'a> { pub fn recv(&mut self) -> JniResult<()> { let package = super::PACKAGE.get().unwrap(); - // SAFETY: The `CHANNEL` is `static` that the sender never drops - let (activity_id, message) = CHANNEL.1.recv().unwrap(); + while let Ok((activity_id, message)) = CHANNEL.1.try_recv() { + self.handle_message(package, activity_id, message)?; + } + Ok(()) + } + + fn handle_message( + &mut self, + package: &str, + activity_id: ActivityId, + message: WebViewMessage, + ) -> JniResult<()> { match message { WebViewMessage::CreateWebView(attrs) => { let Some(ActivityProxy { activity, .. }) = activity_proxy(activity_id) else { diff --git a/ports/wry/src/android/mod.rs b/ports/wry/src/android/mod.rs index bd539df0db5e..5dc13ee071ab 100644 --- a/ports/wry/src/android/mod.rs +++ b/ports/wry/src/android/mod.rs @@ -109,6 +109,10 @@ pub fn destroy_webview(activity_id: ActivityId, webview_id: &WebviewId) { /// /// This function must be run on the thread where the [`JNIEnv`] is registered and the looper is local, /// hence the requirement for a [`ThreadLooper`]. +/// +/// When used with tao, this is usually passed in like +/// `tao::android_binding!($domain, $app_name, $activity, android_setup, $main)` +/// to fill in `on_activity_create` which is run at the end of `onCreate` of an activiy pub unsafe fn android_setup( package: &str, mut env: JNIEnv, diff --git a/ports/wry/src/lib.rs b/ports/wry/src/lib.rs index b0ac97c2c31c..b23e659b6324 100644 --- a/ports/wry/src/lib.rs +++ b/ports/wry/src/lib.rs @@ -55,7 +55,7 @@ //! event_loop.run_app(&mut app).unwrap(); //! ``` //! -//! If you also want to support Wayland too, then we recommend you use [`WebViewBuilderExtUnix::new_gtk`] on Linux. +//! If you also want to support Wayland too, then we recommend you use [`WebViewBuilderExtUnix::build_gtk`] on Linux. //! See the following example using [`tao`]: //! //! ```no_run @@ -115,7 +115,7 @@ //! ``` //! //! If you want to support X11 and Wayland at the same time, we recommend using -//! [`WebViewExtUnix::new_gtk`] or [`WebViewBuilderExtUnix::new_gtk`] with [`gtk::Fixed`]. +//! [`WebViewExtUnix::new_gtk`] or [`WebViewBuilderExtUnix::build_gtk`] with [`gtk::Fixed`]. //! //! ```no_run //! # use wry::{WebViewBuilder, raw_window_handle, Rect, dpi::*}; @@ -302,15 +302,12 @@ //! //! - `os-webview` (default): Enables the default WebView framework on the platform. This must be enabled //! for the crate to work. This feature was added in preparation of other ports like cef and servo. -//! - `protocol` (default): Enables [`WebViewBuilder::with_custom_protocol`] to define custom URL scheme for handling tasks like -//! loading assets. +//! - `x11` (default): Enables x11 support and dependencies on Linux. +//! - `serde`: Enables `dpi`'s `serde` feature. //! - `devtools`: Enables devtools on release builds. Devtools are always enabled in debug builds. //! On **macOS**, enabling devtools, requires calling private APIs so you should not enable this flag in release //! build if your app needs to publish to App Store. -//! - `transparent`: Transparent background on **macOS** requires calling private functions. -//! Avoid this in release build if your app needs to publish to App Store. -//! - `fullscreen`: Fullscreen video and other media on **macOS** requires calling private functions. -//! Avoid this in release build if your app needs to publish to App Store. +//! - `mac-proxy`: Enables `WebViewBuilder::with_proxy_config` on macOS. //! - `linux-body`: Enables body support of custom protocol request on Linux. Requires //! WebKit2GTK v2.40 or above. //! - `tracing`: enables [`tracing`] for `evaluate_script`, `ipc_handler`, and `custom_protocols`. @@ -781,7 +778,7 @@ struct WebViewAttributes<'a> { /// The webview bounds. Defaults to `x: 0, y: 0, width: 200, height: 200`. /// This is only effective if the webview was created by [`WebViewBuilder::new_as_child`] - /// or on Linux, if was created by [`WebViewExtUnix::new_gtk`] or [`WebViewBuilderExtUnix::new_gtk`] with [`gtk::Fixed`]. + /// or on Linux, if was created by [`WebViewExtUnix::new_gtk`] or [`WebViewBuilderExtUnix::build_gtk`] with [`gtk::Fixed`]. pub bounds: Option, /// Whether background throttling should be disabled. @@ -1079,7 +1076,6 @@ impl<'a> WebViewBuilder<'a> { /// elsewhere in Android (provided the app has appropriate access), but not from the `assets` /// folder which lives within the apk. For the cases where this can be used, it works the same as in macOS and Linux. /// - iOS: To get the path of your assets, you can call [`CFBundle::resources_path`](https://docs.rs/core-foundation/latest/core_foundation/bundle/struct.CFBundle.html#method.resources_path). So url like `wry://assets/index.html` could get the html file in assets directory. - #[cfg(feature = "protocol")] pub fn with_custom_protocol(mut self, name: String, handler: F) -> Self where F: Fn(WebViewId, Request>) -> Response> + Send + Sync + 'static, @@ -1144,7 +1140,6 @@ impl<'a> WebViewBuilder<'a> { /// }); /// }); /// ``` - #[cfg(feature = "protocol")] pub fn with_asynchronous_custom_protocol(mut self, name: String, handler: F) -> Self where F: Fn(WebViewId, Request>, RequestAsyncResponder) + Send + Sync + 'static, @@ -1482,7 +1477,7 @@ impl<'a> WebViewBuilder<'a> { } /// Specify the webview position relative to its parent if it will be created as a child - /// or if created using [`WebViewBuilderExtUnix::new_gtk`] with [`gtk::Fixed`]. + /// or if created using [`WebViewBuilderExtUnix::build_gtk`] with [`gtk::Fixed`]. /// /// Defaults to `x: 0, y: 0, width: 200, height: 200`. pub fn with_bounds(mut self, bounds: Rect) -> Self { @@ -1539,7 +1534,7 @@ impl<'a> WebViewBuilder<'a> { /// /// # Platform-specific: /// - /// - **Linux**: Only X11 is supported, if you want to support Wayland too, use [`WebViewBuilderExtUnix::new_gtk`]. + /// - **Linux**: Only X11 is supported, if you want to support Wayland too, use [`WebViewBuilderExtUnix::build_gtk`]. /// /// Although this method only needs a window handle, WebKitGTK still needs GTK to be initialized /// and the GTK main context to be iterated alongside your event loop. @@ -1583,7 +1578,7 @@ impl<'a> WebViewBuilder<'a> { /// Checkout the [Platform Considerations](https://docs.rs/wry/latest/wry/#platform-considerations) section in the crate root documentation. /// /// If you want to support child webviews on X11 and Wayland at the same time, - /// we recommend using [`WebViewBuilderExtUnix::new_gtk`] with [`gtk::Fixed`]. + /// we recommend using [`WebViewBuilderExtUnix::build_gtk`] with [`gtk::Fixed`]. /// - **Android/iOS:** Unsupported. /// /// # Panics: @@ -1994,7 +1989,6 @@ pub trait WebViewBuilderExtAndroid { /// String, similar to [`with_custom_protocol`], but also sets the WebViewAssetLoader with the /// necessary domain (which is fixed as `.assets`). This cannot be used in conjunction /// to `with_custom_protocol` for Android, as it changes the way in which requests are handled. - #[cfg(feature = "protocol")] fn with_asset_loader(self, protocol: String) -> Self; /// Determines whether the custom protocols should use `https://.localhost` instead of the default `http://.localhost`. @@ -2021,7 +2015,6 @@ impl WebViewBuilderExtAndroid for WebViewBuilder<'_> { self } - #[cfg(feature = "protocol")] fn with_asset_loader(mut self, protocol: String) -> Self { if self.attrs.custom_protocols.contains_key(&protocol) { self.error = self @@ -2304,7 +2297,7 @@ impl WebView { /// Set the webview bounds. /// /// This is only effective if the webview was created as a child - /// or created using [`WebViewBuilderExtUnix::new_gtk`] with [`gtk::Fixed`]. + /// or created using [`WebViewBuilderExtUnix::build_gtk`] with [`gtk::Fixed`]. pub fn set_bounds(&self, bounds: Rect) -> Result<()> { self.webview.set_bounds(bounds) } diff --git a/ports/wry/src/wkwebview/mod.rs b/ports/wry/src/wkwebview/mod.rs index 31915df22184..2799904b1414 100644 --- a/ports/wry/src/wkwebview/mod.rs +++ b/ports/wry/src/wkwebview/mod.rs @@ -388,7 +388,8 @@ impl InnerWebView { data_store.setValue_forKey(Some(&proxies), ns_string!("proxyConfigurations")); } - // NOTE: Private API — `allowsPictureInPictureMediaPlayback` is a private KVC key on WKPreferences. + // NOTE: Private API on macOS — `allowsPictureInPictureMediaPlayback` is a private KVC key on WKPreferences. + // On iOS it's public _preference.setValue_forKey( Some(&_yes), ns_string!("allowsPictureInPictureMediaPlayback"), @@ -412,28 +413,26 @@ impl InnerWebView { config.setMediaTypesRequiringUserActionForPlayback(WKAudiovisualMediaTypes::None); } - #[cfg(feature = "transparent")] + let version = util::operating_system_version(); + if attributes.transparent || attributes.background_color.is_some() { let no = NSNumber::numberWithBool(false); - #[cfg(target_os = "macos")] - { - let version = util::operating_system_version(); - if version.0 > 10 || (version.0 == 10 && version.1 >= 14) { - // NOTE: Private API — `drawsBackground`. - // Available: macOS 10.14+ (no public doc). - config.setValue_forKey(Some(&no), ns_string!("drawsBackground")); - } - } - #[cfg(target_os = "ios")] - { - // NOTE: Private API — `drawsBackground`. - config.setValue_forKey(Some(&no), ns_string!("drawsBackground")); - } + // TODO: Check if this property exists or is used on iOS + config.setValue_forKey(Some(&no), ns_string!("drawsBackground")); } - #[cfg(feature = "fullscreen")] - // NOTE: Private API — `fullScreenEnabled` is a private KVC key on WKPreferences. - _preference.setValue_forKey(Some(&_yes), ns_string!("fullScreenEnabled")); + if (cfg!(target_os = "macos") && (version.0 > 12 || (version.0 == 12 && version.1 >= 3))) + || (cfg!(target_os = "ios") && (version.0 > 15 || (version.0 == 15 && version.1 >= 4))) + { + // NOTE: Public API alternative for private config fullScreenEnabled (see below) + // Only available on macOS 12.3+ and iOS 15.4+ + _preference.setElementFullscreenEnabled(true); + } else { + // NOTE: Private API — `fullScreenEnabled` is a private KVC key on WKPreferences. + // This is the private API alternative to setElementFullscreenEnabled above that also works on older macOS/iOS versions. + // TODO: Remove this for Tauri v3? + _preference.setValue_forKey(Some(&_yes), ns_string!("fullScreenEnabled")); + } #[cfg(target_os = "macos")] let webview = { @@ -586,15 +585,15 @@ impl InnerWebView { if attributes.devtools { // // Available: macOS 13.3+, iOS 16.4+ + // Enables debugging via Safari's Web Inspector, NOT via in-webview devtools. let has_inspectable_property: bool = NSObject::respondsToSelector(&webview, objc2::sel!(setInspectable:)); if has_inspectable_property { webview.setInspectable(true); } // NOTE: Private API — `developerExtrasEnabled` is a private KVC key on WKPreferences. - // this cannot be on an `else` statement, it does not work on macOS :( - let dev = ns_string!("developerExtrasEnabled"); - _preference.setValue_forKey(Some(&_yes), dev); + // This enables the in-webview devtools + _preference.setValue_forKey(Some(&_yes), ns_string!("developerExtrasEnabled")); } // Message handler @@ -1029,7 +1028,7 @@ r#"Object.defineProperty(window, 'ipc', { self.webview.setBackgroundColor(Some(&color)); } - #[cfg(all(target_os = "macos", feature = "transparent"))] + #[cfg(target_os = "macos")] unsafe { let (red, green, blue, alpha) = _background_color; diff --git a/renovate.json b/renovate.json index a87fed5d72b8..71453f8663fb 100644 --- a/renovate.json +++ b/renovate.json @@ -38,7 +38,17 @@ { "description": "Group napi-rs packages", "groupName": "napi-rs packages", - "matchSourceUrls": ["https://github.com/napi-rs/napi-rs"] + "matchPackageNames": ["napi", "napi-build", "napi-derive", "@napi-rs/cli"] + }, + { + "description": "Bump CLI packages", + "groupName": "cli packages", + "matchFileNames": [ + "crates/tauri-cli/Cargo.toml", + "packages/cli/Cargo.toml", + "packages/cli/package.json" + ], + "rangeStrategy": "bump" } ], "postUpdateOptions": ["pnpmDedupe"]