diff --git a/.changes/README.md b/.changes/README.md new file mode 100644 index 000000000..318eea024 --- /dev/null +++ b/.changes/README.md @@ -0,0 +1,44 @@ +# Changes + +##### via https://github.com/jbolda/covector + +As you create PRs and make changes that require a version bump, please add a new markdown file in this folder. You do not note the version _number_, but rather the type of bump that you expect: major, minor, or patch. The filename is not important, as long as it is a `.md`, but we recommend that it represents the overall change for organizational purposes. + +When you select the version bump required, you do _not_ need to consider dependencies. Only note the package with the actual change, and any packages that depend on that package will be bumped automatically in the process. + +Use the following format: + +```md +--- +'package-a': 'patch:enhance' +'package-b': 'patch:enhance' +--- + +Change summary goes here +``` + +Summaries do not have a specific character limit, but are text only. These summaries are used within the (future implementation of) changelogs. They will give context to the change and also point back to the original PR if more details and context are needed. + +Changes will be designated as a `major`, `minor` or `patch` as further described in [semver](https://semver.org/). + +Given a version number MAJOR.MINOR.PATCH, increment the: + +- MAJOR version when you make incompatible API changes, +- MINOR version when you add functionality in a backwards compatible manner, and +- PATCH version when you make backwards compatible bug fixes. + +Additional labels for pre-release and build metadata are available as extensions to the MAJOR.MINOR.PATCH format, but will be discussed prior to usage (as extra steps will be necessary in consideration of merging and publishing). + +Additionally you could specify a tag for the change file to group it with other changes by prefixing the bump with `:`, for example: + +```md +--- +'package-a': 'patch:enhance' +--- + +Change summary goes here +``` + +which will group this change file with other changes that specify the `bug` tag. + +For list of available tags, see the `changeTags` key in [./config.json](./config.json) diff --git a/.changes/acl-default-permission-verification.md b/.changes/acl-default-permission-verification.md new file mode 100644 index 000000000..478b48a52 --- /dev/null +++ b/.changes/acl-default-permission-verification.md @@ -0,0 +1,6 @@ +--- +"tauri-build": patch:enhance +"tauri-utils": patch:enhance +--- + +Fallback to an empty permission set if the plugin did not define its `default` permissions. diff --git a/.changes/acl-platform-refactor.md b/.changes/acl-platform-refactor.md new file mode 100644 index 000000000..a465a0e6f --- /dev/null +++ b/.changes/acl-platform-refactor.md @@ -0,0 +1,8 @@ +--- +"tauri-utils": patch:enhance +"tauri": patch:enhance +"tauri-cli": patch:enhance +"@tauri-apps/cli": patch:enhance +--- + +Changed the permission and capability platforms to be optional. diff --git a/.changes/acl-scope-refactor.md b/.changes/acl-scope-refactor.md new file mode 100644 index 000000000..7781a67fc --- /dev/null +++ b/.changes/acl-scope-refactor.md @@ -0,0 +1,5 @@ +--- +"tauri": patch:breaking +--- + +Removed the lifetime parameter from `ipc::GlobalScope` and `ipc::CommandScope`. diff --git a/.changes/acl-urlpattern.md b/.changes/acl-urlpattern.md new file mode 100644 index 000000000..3a2a6b4c1 --- /dev/null +++ b/.changes/acl-urlpattern.md @@ -0,0 +1,6 @@ +--- +"tauri": patch:breaking +"tauri-utils": patch:breaking +--- + +The ACL configuration for remote URLs now uses the URLPattern standard instead of glob patterns. diff --git a/.changes/allow-recursive-asset-scope-on-file-drop-directory.md b/.changes/allow-recursive-asset-scope-on-file-drop-directory.md new file mode 100644 index 000000000..9f7ad5f40 --- /dev/null +++ b/.changes/allow-recursive-asset-scope-on-file-drop-directory.md @@ -0,0 +1,5 @@ +--- +"tauri": 'patch:enhance' +--- + +A file-drop now allows sub-directories recursively when the path is a directory. diff --git a/.changes/api-readd-window-created-event.md b/.changes/api-readd-window-created-event.md new file mode 100644 index 000000000..a0e52ad91 --- /dev/null +++ b/.changes/api-readd-window-created-event.md @@ -0,0 +1,5 @@ +--- +'@tauri-apps/api': 'patch:bug' +--- + +Re-added the `TauriEvent.WINDOW_CREATED` (`tauri://window-created`) event. diff --git a/.changes/api-tauri-event-file-drop-rename.md b/.changes/api-tauri-event-file-drop-rename.md new file mode 100644 index 000000000..f7e9d4f80 --- /dev/null +++ b/.changes/api-tauri-event-file-drop-rename.md @@ -0,0 +1,9 @@ +--- +'@tauri-apps/api': 'patch:breaking' +--- + +Renamed the following enum variants of `TauriEvent` enum: + +- `TauriEvent.WEBVIEW_FILE_DROP` -> `TauriEvent.FILE_DROP` +- `TauriEvent.WEBVIEW_FILE_DROP_HOVER` -> `TauriEvent.FILE_DROP_HOVER` +- `TauriEvent.WEBVIEW_FILE_DROP_CANCELLED` -> `TauriEvent.FILE_DROP_CANCELLED` diff --git a/.changes/api-tray-by-id.md b/.changes/api-tray-by-id.md new file mode 100644 index 000000000..f1e9b621c --- /dev/null +++ b/.changes/api-tray-by-id.md @@ -0,0 +1,5 @@ +--- +'@tauri-apps/api': 'patch:feat' +--- + +Add `TrayIcon.getById` and `TrayIcon.removeById` static methods. diff --git a/.changes/api-webview-window-new-methods.md b/.changes/api-webview-window-new-methods.md new file mode 100644 index 000000000..063c15f7a --- /dev/null +++ b/.changes/api-webview-window-new-methods.md @@ -0,0 +1,5 @@ +--- +'@tauri-apps/api': 'patch:feat' +--- + +Add a new `webviewWindow` module that exports `WebviewWindow` class and related methods such as `getCurrent` and `getAll`. diff --git a/.changes/api-webview-window.md b/.changes/api-webview-window.md new file mode 100644 index 000000000..d2fd15e5e --- /dev/null +++ b/.changes/api-webview-window.md @@ -0,0 +1,5 @@ +--- +'@tauri-apps/api': 'patch:breaking' +--- + +Move `WebviewWindow` class from `webview` module to a new `webviewWindow` module. diff --git a/.changes/api-window-on-filedrop.md b/.changes/api-window-on-filedrop.md new file mode 100644 index 000000000..12ff6b77a --- /dev/null +++ b/.changes/api-window-on-filedrop.md @@ -0,0 +1,5 @@ +--- +'@tauri-apps/api': 'patch:feat' +--- + +Add `Window.onFileDropEvent` method. diff --git a/.changes/app-manifest.md b/.changes/app-manifest.md new file mode 100644 index 000000000..d7afeab8e --- /dev/null +++ b/.changes/app-manifest.md @@ -0,0 +1,9 @@ +--- +"tauri": patch:enhance +"tauri-build": patch:breaking +"tauri-utils": patch:breaking +"tauri-plugin": patch:breaking +"tauri-codegen": patch:breaking +--- + +Allow defining permissions for the application commands via `tauri_build::Attributes::app_manifest`. diff --git a/.changes/assets-setup.md b/.changes/assets-setup.md new file mode 100644 index 000000000..5d107aae9 --- /dev/null +++ b/.changes/assets-setup.md @@ -0,0 +1,5 @@ +--- +"tauri": patch:feat +--- + +The `Assets` trait now include a `setup` method that lets you run initialization code for your custom asset provider. diff --git a/.changes/build-schema-generation.md b/.changes/build-schema-generation.md new file mode 100644 index 000000000..cbcfb2bea --- /dev/null +++ b/.changes/build-schema-generation.md @@ -0,0 +1,5 @@ +--- +"tauri-build": patch:bug +--- + +Fixed generation of capability schema for permissions field which previously disallowed mixed (strings and objects) permission definition. \ No newline at end of file diff --git a/.changes/bundler-deep-link-reg-path.md b/.changes/bundler-deep-link-reg-path.md new file mode 100644 index 000000000..bbbf9ee24 --- /dev/null +++ b/.changes/bundler-deep-link-reg-path.md @@ -0,0 +1,5 @@ +--- +'tauri-bundler': 'patch:bug' +--- + +Fixed an issue that caused the msi bundler to crash when deep link schemes were configured. diff --git a/.changes/bundler-license.md b/.changes/bundler-license.md new file mode 100644 index 000000000..d71414cd2 --- /dev/null +++ b/.changes/bundler-license.md @@ -0,0 +1,5 @@ +--- +'tauri-bundler': 'patch:bug' +--- + +Fix NSIS installer always containing a license page even though `licenseFile` option is not set in the config. diff --git a/.changes/bundler-rpm-license.md b/.changes/bundler-rpm-license.md new file mode 100644 index 000000000..f8152363b --- /dev/null +++ b/.changes/bundler-rpm-license.md @@ -0,0 +1,5 @@ +--- +"tauri-bundler": patch:bug +--- + +Don't fallback to `licenseFile` and use only `license` field when building RPM. \ No newline at end of file diff --git a/.changes/capabilities-multiwebview.md b/.changes/capabilities-multiwebview.md new file mode 100644 index 000000000..1fc9aa9e6 --- /dev/null +++ b/.changes/capabilities-multiwebview.md @@ -0,0 +1,6 @@ +--- +"tauri": patch:enhance +"tauri-utils": patch:enhance +--- + +Add `webviews` array on the capability for usage on multiwebview contexts. diff --git a/.changes/capabilities-tauri-conf.md b/.changes/capabilities-tauri-conf.md new file mode 100644 index 000000000..83dc6f786 --- /dev/null +++ b/.changes/capabilities-tauri-conf.md @@ -0,0 +1,7 @@ +--- +"tauri-build": patch:breaking +"tauri-utils": patch:enhance +"tauri-codegen": patch:enhance +--- + +Added a new configuration option `tauri.conf.json > app > security > capabilities` to reference existing capabilities and inline new ones. If it is empty, all capabilities are still included preserving the current behavior. diff --git a/.changes/capability-builder-platform.md b/.changes/capability-builder-platform.md new file mode 100644 index 000000000..c4684ce10 --- /dev/null +++ b/.changes/capability-builder-platform.md @@ -0,0 +1,5 @@ +--- +"tauri": patch:feat +--- + +Added `CapabilityBuilder::platform` to link the runtime capability with a specific platform. diff --git a/.changes/capability-context-refactor.md b/.changes/capability-context-refactor.md new file mode 100644 index 000000000..62d1c9a7e --- /dev/null +++ b/.changes/capability-context-refactor.md @@ -0,0 +1,7 @@ +--- +"tauri-utils": patch:breaking +"tauri-cli": patch:breaking +"@tauri-apps/cli": patch:breaking +--- + +Changed the capability format to allow configuring both `remote: { urls: Vec }` and `local: bool (default: true)` instead of choosing one on the `context` field. diff --git a/.changes/cli-acl-subcommands.md b/.changes/cli-acl-subcommands.md new file mode 100644 index 000000000..44895acfc --- /dev/null +++ b/.changes/cli-acl-subcommands.md @@ -0,0 +1,12 @@ +--- +'tauri-cli': 'patch:feat' +'@tauri-apps/cli': 'patch:feat' +--- + +Add new subcommands for managing permissions and cababilities: + +- `tauri permission new` +- `tauri permission add` +- `tauri permission rm` +- `tauri permission ls` +- `tauri capability new` diff --git a/.changes/cli-add-@-spec.md b/.changes/cli-add-@-spec.md new file mode 100644 index 000000000..66ecdcac5 --- /dev/null +++ b/.changes/cli-add-@-spec.md @@ -0,0 +1,6 @@ +--- +"tauri-cli": "minor:feat" +"@tauri-apps/cli": "minor:feat" +--- + +Support specifying a version for `tauri add` subcommand, for example: `tauri add window-state@2.0.0-beta.2` diff --git a/.changes/cli-build-no-bundle.md b/.changes/cli-build-no-bundle.md new file mode 100644 index 000000000..bbcdd1437 --- /dev/null +++ b/.changes/cli-build-no-bundle.md @@ -0,0 +1,6 @@ +--- +'tauri-cli': 'patch:enhance' +'@tauri-apps/cli': 'patch:enhance' +--- + +Add `--no-bundle` flag for `tauri build` command to skip bundling. Previously `none` was used to skip bundling, it will now be treated as invalid format and a warning will be emitted instead. diff --git a/.changes/cli-empty-responses.md b/.changes/cli-empty-responses.md new file mode 100644 index 000000000..d5a4ee4eb --- /dev/null +++ b/.changes/cli-empty-responses.md @@ -0,0 +1,6 @@ +--- +'tauri-cli': 'patch:enhance' +'@tauri-apps/cli': 'patch:enhance' +--- + +Allow empty responses for `devUrl`, `beforeDevCommand` and `beforeBuildCommands` questions in `tauri init`. diff --git a/.changes/cli-include-dir-cargo-manifest-dir.md b/.changes/cli-include-dir-cargo-manifest-dir.md new file mode 100644 index 000000000..7b3679496 --- /dev/null +++ b/.changes/cli-include-dir-cargo-manifest-dir.md @@ -0,0 +1,5 @@ +--- +"tauri-cli": patch:enhance +--- + +Use `$CARGO_MANIFEST_DIR` when including templates at build-time. \ No newline at end of file diff --git a/.changes/cli-mobile-init-partition.md b/.changes/cli-mobile-init-partition.md new file mode 100644 index 000000000..fd414f26e --- /dev/null +++ b/.changes/cli-mobile-init-partition.md @@ -0,0 +1,6 @@ +--- +'tauri-cli': 'patch:bug' +'@tauri-apps/cli': 'patch:bug' +--- + +Fixes Android and iOS project initialization when the Tauri CLI is on a different disk partition. diff --git a/.changes/cli-openssl-cargo-mobile2-removal.md b/.changes/cli-openssl-cargo-mobile2-removal.md new file mode 100644 index 000000000..f6ae732e4 --- /dev/null +++ b/.changes/cli-openssl-cargo-mobile2-removal.md @@ -0,0 +1,6 @@ +--- +"@tauri-apps/cli": patch:enhance +"tauri-cli": patch:enhance +--- + +`openssl` is no longer a required dependency on macOS. diff --git a/.changes/cli-plugins-migrate.md b/.changes/cli-plugins-migrate.md new file mode 100644 index 000000000..be41341fa --- /dev/null +++ b/.changes/cli-plugins-migrate.md @@ -0,0 +1,6 @@ +--- +'tauri-cli': 'patch:enhance' +'@tauri-apps/cli': 'patch:enhance' +--- + +Add plugins to `Cargo.toml` when using `tauri migrate` diff --git a/.changes/cli-update-deps-fix-log.md b/.changes/cli-update-deps-fix-log.md new file mode 100644 index 000000000..e8d158d6e --- /dev/null +++ b/.changes/cli-update-deps-fix-log.md @@ -0,0 +1,5 @@ +--- +"tauri-cli": patch:deps +--- + +Update dependencies, fix `log` compilation issue. diff --git a/.changes/cli-updater-unkown-fields.md b/.changes/cli-updater-unkown-fields.md new file mode 100644 index 000000000..077d19af0 --- /dev/null +++ b/.changes/cli-updater-unkown-fields.md @@ -0,0 +1,6 @@ +--- +"tauri-cli": patch:bug +"@tauri-apps/cli": patch:bug +--- + +Fix bundling when `plugins > updater > windows > installerArgs` are set in `tauri.conf.json` \ No newline at end of file diff --git a/.changes/cli-windows-build-tools-detect-utf8.md b/.changes/cli-windows-build-tools-detect-utf8.md new file mode 100644 index 000000000..235c70076 --- /dev/null +++ b/.changes/cli-windows-build-tools-detect-utf8.md @@ -0,0 +1,6 @@ +--- +"tauri-cli": patch:bug +"@tauri-apps/cli": patch:bug +--- + +On Windows, fixed `tauri info` fails to detect the build tool when the system language is CJK. diff --git a/.changes/codegen-capabilities-attribute.md b/.changes/codegen-capabilities-attribute.md new file mode 100644 index 000000000..c96d47141 --- /dev/null +++ b/.changes/codegen-capabilities-attribute.md @@ -0,0 +1,6 @@ +--- +"tauri-macros": patch:enhance +"tauri-codegen": patch:enhance +--- + +The `generate_context` proc macro now accepts a `capabilities` attribute where the value is an array of file paths that can be [conditionally compiled](https://doc.rust-lang.org/reference/conditional-compilation.html). These capabilities are added to the application along the capabilities defined in the Tauri configuration file. diff --git a/.changes/codegen-set-assets.md b/.changes/codegen-set-assets.md new file mode 100644 index 000000000..efa70d526 --- /dev/null +++ b/.changes/codegen-set-assets.md @@ -0,0 +1,6 @@ +--- +"tauri-macros": patch:feat +"tauri-codegen": patch:feat +--- + +The `Context` codegen now accepts a `assets` input to define a custom `tauri::Assets` implementation. diff --git a/.changes/color-context-generation.md b/.changes/color-context-generation.md new file mode 100644 index 000000000..68eb29eb1 --- /dev/null +++ b/.changes/color-context-generation.md @@ -0,0 +1,6 @@ +--- +'tauri-utils': 'patch:bug' +'tauri': 'patch:bug' +--- + +Fix compile time error in context generation when using `app.windows.windowEffects.color` diff --git a/.changes/context-assets-runtime-generic.md b/.changes/context-assets-runtime-generic.md new file mode 100644 index 000000000..c9c65519d --- /dev/null +++ b/.changes/context-assets-runtime-generic.md @@ -0,0 +1,5 @@ +--- +"tauri": patch:breaking +--- + +The `Context` struct and the `Assets` trait now takes a `R: Runtime` generic. diff --git a/.changes/context-assets-unbox.md b/.changes/context-assets-unbox.md new file mode 100644 index 000000000..489bf9b49 --- /dev/null +++ b/.changes/context-assets-unbox.md @@ -0,0 +1,5 @@ +--- +"tauri": patch:breaking +--- + +`Context::assets` now returns `&dyn Assets` instead of `&A` generic. diff --git a/.changes/context-remove-assets-generics.md b/.changes/context-remove-assets-generics.md new file mode 100644 index 000000000..3fc550477 --- /dev/null +++ b/.changes/context-remove-assets-generics.md @@ -0,0 +1,5 @@ +--- +"tauri": patch:breaking +--- + +The `Context` type no longer uses the `` generic so the assets implementation can be swapped with `Context::assets_mut`. diff --git a/.changes/context-remove-assets-mut.md b/.changes/context-remove-assets-mut.md new file mode 100644 index 000000000..84db6b346 --- /dev/null +++ b/.changes/context-remove-assets-mut.md @@ -0,0 +1,5 @@ +--- +"tauri": patch:breaking +--- + +Removed `Context::assets_mut` and added `Context::set_assets`. diff --git a/.changes/context-runtime-authority.md b/.changes/context-runtime-authority.md new file mode 100644 index 000000000..4d2a65e5c --- /dev/null +++ b/.changes/context-runtime-authority.md @@ -0,0 +1,5 @@ +--- +"tauri-utils": patch:enhance +--- + +The `Context` struct now includes the runtime authority instead of the resolved ACL. This does not impact most applications. diff --git a/.changes/core-app-tray-remove-tray-apis-removed.md b/.changes/core-app-tray-remove-tray-apis-removed.md new file mode 100644 index 000000000..b0289051a --- /dev/null +++ b/.changes/core-app-tray-remove-tray-apis-removed.md @@ -0,0 +1,5 @@ +--- +'tauri': 'patch:breaking' +--- + +Removed `App/AppHandle::tray` and `App/AppHandle::remove_tray`, use `App/AppHandle::tray_by_id` and `App/AppHandle::remove_tray_by_id` instead. If these APIs were used to access tray icon configured in `tauri.conf.json`, you can use `App/AppHandle::tray_by_id` with ID `main` or the configured value. diff --git a/.changes/core-center-window.md b/.changes/core-center-window.md new file mode 100644 index 000000000..f6993db82 --- /dev/null +++ b/.changes/core-center-window.md @@ -0,0 +1,6 @@ +--- +'tauri': 'patch:enhance' +'tauri-runtime-wry': 'patch' +--- + +Enhance centering a newly created window, it will no longer jump to center after being visible. diff --git a/.changes/core-emit-created-events.md b/.changes/core-emit-created-events.md new file mode 100644 index 000000000..3215c1dc7 --- /dev/null +++ b/.changes/core-emit-created-events.md @@ -0,0 +1,5 @@ +--- +tauri: 'patch:bug' +--- + +Fixed an issue preventing webview/window creation events to not be emitted. This also fixed the `getByLabel` and `getAll` JavaScript functions. diff --git a/.changes/core-emit-js-all-targets.md b/.changes/core-emit-js-all-targets.md new file mode 100644 index 000000000..6a62a35c5 --- /dev/null +++ b/.changes/core-emit-js-all-targets.md @@ -0,0 +1,5 @@ +--- +'tauri': 'patch:bug' +--- + +Fix `emit` and `emit_to` (when used with `EventTarget::Any`) always skipping the webview listeners. diff --git a/.changes/core-js-event-anytarget.md b/.changes/core-js-event-anytarget.md new file mode 100644 index 000000000..3d55b2c6a --- /dev/null +++ b/.changes/core-js-event-anytarget.md @@ -0,0 +1,5 @@ +--- +'tauri': 'patch:bug' +--- + +Fix JS event listeners registered using JS `listen` api with `EventTarget::Any` never fired. diff --git a/.changes/core-once-event-return-event-id.md b/.changes/core-once-event-return-event-id.md new file mode 100644 index 000000000..aa9e13b31 --- /dev/null +++ b/.changes/core-once-event-return-event-id.md @@ -0,0 +1,5 @@ +--- +'tauri': 'patch:enhance' +--- + +Return an id when using from `Manager::once_any`, `App::once`, `Window::once`, `Webview::once`, `WebviewWindow::once` and `fs::Scope::once`. diff --git a/.changes/core-path-basename-replace.md b/.changes/core-path-basename-replace.md new file mode 100644 index 000000000..4f2b51328 --- /dev/null +++ b/.changes/core-path-basename-replace.md @@ -0,0 +1,6 @@ +--- +'tauri': 'patch:bug' +'@tauri-apps/api': patch:bug +--- + +Fix `basename(path, 'ext')` JS API when removing all occurances of `ext` where it should only remove the last one. diff --git a/.changes/core-window-hasdisplayhandle.md b/.changes/core-window-hasdisplayhandle.md new file mode 100644 index 000000000..a2b1346d3 --- /dev/null +++ b/.changes/core-window-hasdisplayhandle.md @@ -0,0 +1,5 @@ +--- +tauri: 'patch:enhance' +--- + +`tauri::Window` and `tauri::WebviewWindow` now implement `raw_window_handle::HasDisplayHandle`. diff --git a/.changes/csp-header-linux.md b/.changes/csp-header-linux.md new file mode 100644 index 000000000..4ca673cc3 --- /dev/null +++ b/.changes/csp-header-linux.md @@ -0,0 +1,7 @@ +--- +"tauri": patch:enhance +"tauri-utils": patch:enhance +"tauri-codegen": patch:enhance +--- + +Do not include a CSP tag in the application HTML and rely on the custom protocol response header instead. diff --git a/.changes/deb-rpm-post-pre-scripts-bundler.md b/.changes/deb-rpm-post-pre-scripts-bundler.md new file mode 100644 index 000000000..2a7f5b942 --- /dev/null +++ b/.changes/deb-rpm-post-pre-scripts-bundler.md @@ -0,0 +1,5 @@ +--- +'tauri-bundler': 'minor:feat' +--- + +Add suport for include `preinstall`, `postinstall`, `preremove` and `postremove` scripts into Debian and RPM packages. diff --git a/.changes/deb-rpm-post-pre-scripts-config.md b/.changes/deb-rpm-post-pre-scripts-config.md new file mode 100644 index 000000000..0723d7301 --- /dev/null +++ b/.changes/deb-rpm-post-pre-scripts-config.md @@ -0,0 +1,5 @@ +--- +'tauri-utils': 'minor:feat' +--- + +Added `preInstallScript`, `postInstallScript`, `preRemoveScript` and `postRemoveScript` options for `bundler > deb` and `bundler > rpm` configs. diff --git a/.changes/deb-rpm-provides-conflicts-replaces.md b/.changes/deb-rpm-provides-conflicts-replaces.md new file mode 100644 index 000000000..8b6fd7473 --- /dev/null +++ b/.changes/deb-rpm-provides-conflicts-replaces.md @@ -0,0 +1,6 @@ +--- +'tauri-bundler': 'minor:feat' +'tauri-utils': 'minor:feat' +--- + +Added support for `provides`, `conflicts` and `replaces` (`obsoletes` for RPM) options for `bundler > deb` and `bundler > rpm` configs. diff --git a/.changes/dev-fn.md b/.changes/dev-fn.md new file mode 100644 index 000000000..7b0b12901 --- /dev/null +++ b/.changes/dev-fn.md @@ -0,0 +1,5 @@ +--- +"tauri": patch:enhance +--- + +Added `tauri::dev()` to determine whether we are running in development mode or not. diff --git a/.changes/downgrade-minisign.md b/.changes/downgrade-minisign.md new file mode 100644 index 000000000..918440a6b --- /dev/null +++ b/.changes/downgrade-minisign.md @@ -0,0 +1,6 @@ +--- +"tauri-cli": patch:bug +"@tauri-apps/cli": patch:bug +--- + +Downgrade minisign dependency fixing updater signing key bug and prevent it from happening in the future. diff --git a/.changes/enhance-event-emit.md b/.changes/enhance-event-emit.md new file mode 100644 index 000000000..492e208b7 --- /dev/null +++ b/.changes/enhance-event-emit.md @@ -0,0 +1,5 @@ +--- +"tauri": patch:enhance +--- + +Improve and optimize event emit calls. diff --git a/.changes/enhance-ipc-url-check.md b/.changes/enhance-ipc-url-check.md new file mode 100644 index 000000000..834b2c2f5 --- /dev/null +++ b/.changes/enhance-ipc-url-check.md @@ -0,0 +1,5 @@ +--- +"tauri": patch:enhance +--- + +Enhance the IPC URL check by using the Origin header on the custom protocol IPC and the new request URI field on the postMessage IPC instead of using `Webview::url()` which only returns the URL of the main frame and is not suitable for iframes (iframe URL fetch is still not supported on Android and on Linux when using the postMessage IPC). diff --git a/.changes/enhance-resource-dir-resolution.md b/.changes/enhance-resource-dir-resolution.md new file mode 100644 index 000000000..2d0edc5e3 --- /dev/null +++ b/.changes/enhance-resource-dir-resolution.md @@ -0,0 +1,5 @@ +--- +"tauri-utils": patch:enhance +--- + +Enhance resource directory resolution on development. diff --git a/.changes/expose-image-constructor.md b/.changes/expose-image-constructor.md new file mode 100644 index 000000000..07b03cbc3 --- /dev/null +++ b/.changes/expose-image-constructor.md @@ -0,0 +1,5 @@ +--- +"@tauri-apps/api": patch:enhance +--- + +The `Image` constructor is now public (for internal use only). diff --git a/.changes/expose-js-image.md b/.changes/expose-js-image.md new file mode 100644 index 000000000..19b187ef1 --- /dev/null +++ b/.changes/expose-js-image.md @@ -0,0 +1,6 @@ +--- +"tauri": patch:breaking +"tauri-codegen": patch:breaking +--- + +Expose `tauri::image` module to export the `JsImage` type and removed the `Image` root re-export. diff --git a/.changes/fix-acl-webview-check.md b/.changes/fix-acl-webview-check.md new file mode 100644 index 000000000..0aa74c0ce --- /dev/null +++ b/.changes/fix-acl-webview-check.md @@ -0,0 +1,5 @@ +--- +"tauri": patch:bug +--- + +Fixes capability webview label check. diff --git a/.changes/fix-add-child-deadlock.md b/.changes/fix-add-child-deadlock.md new file mode 100644 index 000000000..2f3c9db12 --- /dev/null +++ b/.changes/fix-add-child-deadlock.md @@ -0,0 +1,5 @@ +--- +"tauri": patch:bug +--- + +Fixes `Window::add_child` deadlock. diff --git a/.changes/fix-capability-schema-definitions.md b/.changes/fix-capability-schema-definitions.md new file mode 100644 index 000000000..2935b5039 --- /dev/null +++ b/.changes/fix-capability-schema-definitions.md @@ -0,0 +1,5 @@ +--- +"tauri-build": patch:bug +--- + +Fixes the capability schema not resolving inner definitions. diff --git a/.changes/fix-channel-ipc-response.md b/.changes/fix-channel-ipc-response.md new file mode 100644 index 000000000..1ab50f088 --- /dev/null +++ b/.changes/fix-channel-ipc-response.md @@ -0,0 +1,5 @@ +--- +"tauri": patch:bug +--- + +Fix regression on IPC response when using a channel to return objects. diff --git a/.changes/fix-clear-residual-listeners.md b/.changes/fix-clear-residual-listeners.md new file mode 100644 index 000000000..d08ed8452 --- /dev/null +++ b/.changes/fix-clear-residual-listeners.md @@ -0,0 +1,5 @@ +--- +"tauri": patch:bug +--- + +Clear JS event listeneres on page load, which fixes zombie listeners when the page reloads. diff --git a/.changes/fix-cli-migration-http-acl.md b/.changes/fix-cli-migration-http-acl.md new file mode 100644 index 000000000..8956b9c77 --- /dev/null +++ b/.changes/fix-cli-migration-http-acl.md @@ -0,0 +1,5 @@ +--- +"tauri": patch:bug +--- + +Fix `tauri migrate` for http plugin ACL. diff --git a/.changes/fix-fs-scope-check-symlink.md b/.changes/fix-fs-scope-check-symlink.md new file mode 100644 index 000000000..7804412d4 --- /dev/null +++ b/.changes/fix-fs-scope-check-symlink.md @@ -0,0 +1,5 @@ +--- +"tauri": patch:bug +--- + +Resolve symlinks on the filesystem scope check. diff --git a/.changes/fix-inner-size.md b/.changes/fix-inner-size.md new file mode 100644 index 000000000..83d2191bf --- /dev/null +++ b/.changes/fix-inner-size.md @@ -0,0 +1,5 @@ +--- +"tauri-runtime-wry": patch:bug +--- + +Fix window inner size evaluation on macOS. diff --git a/.changes/fix-ios-dev-logs.md b/.changes/fix-ios-dev-logs.md new file mode 100644 index 000000000..eaa22703a --- /dev/null +++ b/.changes/fix-ios-dev-logs.md @@ -0,0 +1,6 @@ +--- +"@tauri-apps/cli": patch:bug +"tauri-cli": patch:bug +--- + +Fixes process logs not showing on `ios dev`. diff --git a/.changes/fix-ipc-error-json.md b/.changes/fix-ipc-error-json.md new file mode 100644 index 000000000..2e5e5f756 --- /dev/null +++ b/.changes/fix-ipc-error-json.md @@ -0,0 +1,5 @@ +--- +tauri: 'patch:bug' +--- + +Fixed an issue where errors where returned as strings instead of objects from commands. diff --git a/.changes/fix-menu-remove-api.md b/.changes/fix-menu-remove-api.md new file mode 100644 index 000000000..81b738aeb --- /dev/null +++ b/.changes/fix-menu-remove-api.md @@ -0,0 +1,5 @@ +--- +"tauri": patch:bug +--- + +Fixes the menu plugin `remove` command signature. diff --git a/.changes/fix-metadata-on-close.md b/.changes/fix-metadata-on-close.md new file mode 100644 index 000000000..7208e19ef --- /dev/null +++ b/.changes/fix-metadata-on-close.md @@ -0,0 +1,5 @@ +--- +"tauri": patch:bug +--- + +Fixes an issue causing `getAll()` to list webviews that were already destroyed. diff --git a/.changes/fix-mobile-cmd-case.md b/.changes/fix-mobile-cmd-case.md new file mode 100644 index 000000000..653e2c007 --- /dev/null +++ b/.changes/fix-mobile-cmd-case.md @@ -0,0 +1,5 @@ +--- +"tauri": patch:bug +--- + +Convert the command name to camelCase when executing a mobile plugin command. diff --git a/.changes/fix-mobile-process-spawn.md b/.changes/fix-mobile-process-spawn.md new file mode 100644 index 000000000..776868528 --- /dev/null +++ b/.changes/fix-mobile-process-spawn.md @@ -0,0 +1,6 @@ +--- +"tauri-cli": patch:bug +"@tauri-apps/cli": patch:bug +--- + +Fixes android and iOS process spawning not working on Node.js. diff --git a/.changes/fix-remote-domain-url.md b/.changes/fix-remote-domain-url.md new file mode 100644 index 000000000..d21f99c67 --- /dev/null +++ b/.changes/fix-remote-domain-url.md @@ -0,0 +1,5 @@ +--- +"tauri": patch:bug +--- + +Fixes capability remote domain not allowing subpaths, query parameters and hash when those values are empty. diff --git a/.changes/fix-reparent.md b/.changes/fix-reparent.md new file mode 100644 index 000000000..04ee7756b --- /dev/null +++ b/.changes/fix-reparent.md @@ -0,0 +1,5 @@ +--- +"tauri": patch:bug +--- + +Fixes `Webview::reparent` not updating the webview parent window reference. diff --git a/.changes/fix-runtime-wry-32bit.md b/.changes/fix-runtime-wry-32bit.md new file mode 100644 index 000000000..712cf1ec7 --- /dev/null +++ b/.changes/fix-runtime-wry-32bit.md @@ -0,0 +1,5 @@ +--- +tauri-runtime-wry: patch:bug +--- + +Fixes an issue causing compilation to fail for i686 and armv7 32-bit targets. \ No newline at end of file diff --git a/.changes/fix-scope-resolution.md b/.changes/fix-scope-resolution.md new file mode 100644 index 000000000..911699347 --- /dev/null +++ b/.changes/fix-scope-resolution.md @@ -0,0 +1,6 @@ +--- +"tauri": patch:bug +"tauri-utils": patch:bug +--- + +Fixes scope resolution grouping scopes for all windows. diff --git a/.changes/fix-visibility-change.md b/.changes/fix-visibility-change.md new file mode 100644 index 000000000..024cda8e7 --- /dev/null +++ b/.changes/fix-visibility-change.md @@ -0,0 +1,5 @@ +--- +"tauri-runtime-wry": patch:bug +--- + +Fix webview's visibility doesn't change with the app window diff --git a/.changes/fix-window-center-monitor-scale.md b/.changes/fix-window-center-monitor-scale.md new file mode 100644 index 000000000..0d967f138 --- /dev/null +++ b/.changes/fix-window-center-monitor-scale.md @@ -0,0 +1,5 @@ +--- +"tauri-runtime-wry": patch:bug +--- + +Fix window centering not taking monitor scale into account diff --git a/.changes/fix-window-center-work-area.md b/.changes/fix-window-center-work-area.md new file mode 100644 index 000000000..78d1596c4 --- /dev/null +++ b/.changes/fix-window-center-work-area.md @@ -0,0 +1,5 @@ +--- +"tauri-runtime-wry": patch:bug +--- + +Fix window centering not taking taskbar into account on Windows diff --git a/.changes/fix-window-destroy-deadlock.md b/.changes/fix-window-destroy-deadlock.md new file mode 100644 index 000000000..014ca46c1 --- /dev/null +++ b/.changes/fix-window-destroy-deadlock.md @@ -0,0 +1,5 @@ +--- +"tauri": patch:bug +--- + +Fixes a deadlock when the window is destroyed. diff --git a/.changes/global-api-script-path-plugins.md b/.changes/global-api-script-path-plugins.md new file mode 100644 index 000000000..2de6c03a0 --- /dev/null +++ b/.changes/global-api-script-path-plugins.md @@ -0,0 +1,8 @@ +--- +"tauri": patch:feat +"tauri-codegen": patch:feat +"tauri-build": patch:feat +"tauri-plugin": patch:feat +--- + +Allow plugins to define (at compile time) JavaScript that are initialized when `withGlobalTauri` is true. diff --git a/.changes/http-v1.md b/.changes/http-v1.md new file mode 100644 index 000000000..646717162 --- /dev/null +++ b/.changes/http-v1.md @@ -0,0 +1,7 @@ +--- +'tauri': 'patch:deps' +'tauri-runtime': 'patch' +'tauri-runtime-wry': 'patch' +--- + +Updated `http` crate to `1.1` diff --git a/.changes/ico-featrue-flags.md b/.changes/ico-featrue-flags.md new file mode 100644 index 000000000..bf488fe43 --- /dev/null +++ b/.changes/ico-featrue-flags.md @@ -0,0 +1,5 @@ +--- +'tauri': 'major:breaking' +--- + +Renamed `icon-ico` and `icon-png` feature flags to `image-ico` and `image-png` respectively diff --git a/.changes/image-crate.md b/.changes/image-crate.md new file mode 100644 index 000000000..e4a89d724 --- /dev/null +++ b/.changes/image-crate.md @@ -0,0 +1,5 @@ +--- +"tauri": patch:breaking +--- + +Use the image crate for `tauri::image::Image` and remove the `from_png_bytes` and `from_ico_bytes` APIs. diff --git a/.changes/image-rgba-uint8array.md b/.changes/image-rgba-uint8array.md new file mode 100644 index 000000000..b57acab3d --- /dev/null +++ b/.changes/image-rgba-uint8array.md @@ -0,0 +1,5 @@ +--- +"@tauri-apps/api": patch:breaking +--- + +`Image::rgba()` now returns `Promise`. diff --git a/.changes/image-size-refactor.md b/.changes/image-size-refactor.md new file mode 100644 index 000000000..5de4791d1 --- /dev/null +++ b/.changes/image-size-refactor.md @@ -0,0 +1,6 @@ +--- +"@tauri-apps/api": patch:breaking +"tauri": patch:breaking +--- + +Removed `width` and `height` methods on the JS `Image` class, use `size` instead. diff --git a/.changes/inline-plugins.md b/.changes/inline-plugins.md new file mode 100644 index 000000000..b2d7f52cc --- /dev/null +++ b/.changes/inline-plugins.md @@ -0,0 +1,5 @@ +--- +'tauri-build': patch:enhance +--- + +Added `Attributes::plugin()` to register a plugin that is inlined in the application crate. diff --git a/.changes/ios-signing-optional.md b/.changes/ios-signing-optional.md new file mode 100644 index 000000000..420ca2882 --- /dev/null +++ b/.changes/ios-signing-optional.md @@ -0,0 +1,6 @@ +--- +"@tauri-apps/cli": patch:enhance +"tauri-cli": patch:enhance +--- + +Setting up code signing is no longer required on iOS when using the simulator. diff --git a/.changes/ipc-post-message-fallback.md b/.changes/ipc-post-message-fallback.md new file mode 100644 index 000000000..b271d945f --- /dev/null +++ b/.changes/ipc-post-message-fallback.md @@ -0,0 +1,5 @@ +--- +"tauri": patch:enhance +--- + +Fallback to the postMessage IPC interface if we cannot reach the IPC custom protocol. diff --git a/.changes/ipc-request-param-refactor.md b/.changes/ipc-request-param-refactor.md new file mode 100644 index 000000000..6562c2f88 --- /dev/null +++ b/.changes/ipc-request-param-refactor.md @@ -0,0 +1,6 @@ +--- +"tauri-runtime": patch:breaking +"tauri-runtime-wry": patch:breaking +--- + +The IPC handler closure now receives a `http::Request` instead of a String representing the request body. diff --git a/.changes/isolation-pattern-key-extractable.md b/.changes/isolation-pattern-key-extractable.md new file mode 100644 index 000000000..ef79d5adc --- /dev/null +++ b/.changes/isolation-pattern-key-extractable.md @@ -0,0 +1,5 @@ +--- +"tauri-utils": patch:enhance +--- + +Make the isolation pattern encrypt key unextractable. diff --git a/.changes/isolation-script-remove-itself.md b/.changes/isolation-script-remove-itself.md new file mode 100644 index 000000000..f7b0888df --- /dev/null +++ b/.changes/isolation-script-remove-itself.md @@ -0,0 +1,5 @@ +--- +"tauri-utils": patch:enhance +--- + +The isolation iframe script now removes itself after execution. diff --git a/.changes/mobile-watcher.md b/.changes/mobile-watcher.md new file mode 100644 index 000000000..2402e4f8f --- /dev/null +++ b/.changes/mobile-watcher.md @@ -0,0 +1,6 @@ +--- +"@tauri-apps/cli": patch:bug +"tauri-cli": patch:bug +--- + +Fixes dev watcher on mobile dev. diff --git a/.changes/multiwebview-bounds-fixes.md b/.changes/multiwebview-bounds-fixes.md new file mode 100644 index 000000000..9080a90b8 --- /dev/null +++ b/.changes/multiwebview-bounds-fixes.md @@ -0,0 +1,5 @@ +--- +"tauri-runtime-wry": patch:bug +--- + +Fixes auto resize and positioning when using the multiwebview mode. diff --git a/.changes/nsis-dpi-aware.md b/.changes/nsis-dpi-aware.md new file mode 100644 index 000000000..0fb75afb3 --- /dev/null +++ b/.changes/nsis-dpi-aware.md @@ -0,0 +1,5 @@ +--- +'tauri-build': 'patch:enhance' +--- + +Enable Hight DPI awareness for NSIS installer so it is not blurry on some systems. diff --git a/.changes/path-result-error-rexport.md b/.changes/path-result-error-rexport.md new file mode 100644 index 000000000..6783b717f --- /dev/null +++ b/.changes/path-result-error-rexport.md @@ -0,0 +1,5 @@ +--- +'tauri': 'major:breaking' +--- + +Removed `tauri::path::Result` and `tauri::path::Error` which were merely an unintentional re-export of `tauri::Result` and `tauri::Error` so use those instead. diff --git a/.changes/permission-platforms.md b/.changes/permission-platforms.md new file mode 100644 index 000000000..575e8b48a --- /dev/null +++ b/.changes/permission-platforms.md @@ -0,0 +1,6 @@ +--- +"tauri": patch:feat +"tauri-utils": patch:feat +--- + +Allow defining a permission that only applies to a set of target platforms via the `platforms` configuration option. diff --git a/.changes/permission-table.md b/.changes/permission-table.md new file mode 100644 index 000000000..d31dadd9e --- /dev/null +++ b/.changes/permission-table.md @@ -0,0 +1,5 @@ +--- +"tauri-utils": patch:enhance +--- + +Changed plugin markdown docs generation to table format. diff --git a/.changes/plugin-global-api-script.md b/.changes/plugin-global-api-script.md new file mode 100644 index 000000000..299385d6e --- /dev/null +++ b/.changes/plugin-global-api-script.md @@ -0,0 +1,5 @@ +--- +"tauri-plugin": patch:feat +--- + +Added `Builder::global_api_script_path` to define a JavaScript file containing the initialization script for the plugin API bindings when `withGlobalTauri` is used. diff --git a/.changes/pre.json b/.changes/pre.json index 8dddbffe0..fb5f0d241 100644 --- a/.changes/pre.json +++ b/.changes/pre.json @@ -1,13 +1,165 @@ { "tag": "beta", "changes": [ + ".changes/acl-default-permission-verification.md", + ".changes/acl-platform-refactor.md", + ".changes/acl-scope-refactor.md", + ".changes/acl-urlpattern.md", + ".changes/allow-recursive-asset-scope-on-file-drop-directory.md", + ".changes/api-readd-window-created-event.md", + ".changes/api-tauri-event-file-drop-rename.md", + ".changes/api-tray-by-id.md", + ".changes/api-webview-window-new-methods.md", + ".changes/api-webview-window.md", + ".changes/api-window-on-filedrop.md", + ".changes/app-manifest.md", + ".changes/assets-setup.md", ".changes/beta.md", + ".changes/build-schema-generation.md", + ".changes/bundler-deep-link-reg-path.md", + ".changes/bundler-license.md", + ".changes/bundler-rpm-license.md", + ".changes/capabilities-multiwebview.md", + ".changes/capabilities-tauri-conf.md", + ".changes/capability-builder-platform.md", + ".changes/capability-context-refactor.md", + ".changes/cli-acl-subcommands.md", + ".changes/cli-add-@-spec.md", + ".changes/cli-build-no-bundle.md", + ".changes/cli-empty-responses.md", + ".changes/cli-include-dir-cargo-manifest-dir.md", + ".changes/cli-mobile-init-partition.md", + ".changes/cli-openssl-cargo-mobile2-removal.md", + ".changes/cli-plugin-android-init.md", + ".changes/cli-plugins-migrate.md", + ".changes/cli-update-deps-fix-log.md", + ".changes/cli-updater-unkown-fields.md", + ".changes/cli-windows-build-tools-detect-utf8.md", + ".changes/codegen-capabilities-attribute.md", + ".changes/codegen-set-assets.md", + ".changes/color-context-generation.md", + ".changes/context-assets-runtime-generic.md", + ".changes/context-assets-unbox.md", + ".changes/context-remove-assets-generics.md", + ".changes/context-remove-assets-mut.md", + ".changes/context-runtime-authority.md", + ".changes/core-app-tray-remove-tray-apis-removed.md", + ".changes/core-center-window.md", + ".changes/core-emit-created-events.md", + ".changes/core-emit-js-all-targets.md", + ".changes/core-js-event-anytarget.md", + ".changes/core-once-event-return-event-id.md", + ".changes/core-path-basename-replace.md", + ".changes/core-window-hasdisplayhandle.md", + ".changes/csp-header-linux.md", + ".changes/deb-rpm-post-pre-scripts-bundler.md", + ".changes/deb-rpm-post-pre-scripts-config.md", + ".changes/dev-fn.md", + ".changes/downgrade-minisign.md", + ".changes/enhance-event-emit.md", + ".changes/enhance-ipc-url-check.md", + ".changes/enhance-resource-dir-resolution.md", + ".changes/expose-image-constructor.md", + ".changes/expose-js-image.md", + ".changes/fix-acl-webview-check.md", + ".changes/fix-add-child-deadlock.md", + ".changes/fix-capability-schema-definitions.md", + ".changes/fix-channel-ipc-response.md", + ".changes/fix-clear-residual-listeners.md", + ".changes/fix-cli-migration-http-acl.md", ".changes/fix-codegen-rerun-if-changed.md", + ".changes/fix-config-arg.md", + ".changes/fix-fs-scope-check-symlink.md", + ".changes/fix-inner-size.md", + ".changes/fix-invoke-devtools-by-hotkey.md", + ".changes/fix-ios-dev-logs.md", + ".changes/fix-ipc-error-json.md", + ".changes/fix-menu-remove-api.md", + ".changes/fix-migrate-updater.md", + ".changes/fix-mobile-cmd-case.md", + ".changes/fix-mobile-process-spawn.md", ".changes/fix-process-ipc-message-fn.md", + ".changes/fix-remote-domain-url.md", + ".changes/fix-reparent.md", ".changes/fix-rewrite-schema.md", + ".changes/fix-scope-resolution.md", + ".changes/fix-tauri-build-license-field.md", ".changes/fix-tauri-build-unix.md", + ".changes/fix-visibility-change.md", + ".changes/fix-webview-close.md", + ".changes/fix-window-center-monitor-scale.md", + ".changes/fix-window-destroy-deadlock.md", + ".changes/global-api-script-path-plugins.md", + ".changes/handle-empty-permissions.md", + ".changes/http-v1.md", + ".changes/ico-featrue-flags.md", + ".changes/image-crate.md", + ".changes/image-rgba-uint8array.md", + ".changes/image-size-refactor.md", + ".changes/inline-plugins.md", + ".changes/ios-signing-optional.md", + ".changes/ipc-post-message-fallback.md", + ".changes/ipc-request-param-refactor.md", + ".changes/isolation-pattern-key-extractable.md", + ".changes/isolation-script-remove-itself.md", + ".changes/mobile-watcher.md", + ".changes/multiwebview-bounds-fixes.md", + ".changes/nsis-dpi-aware.md", + ".changes/path-result-error-rexport.md", + ".changes/permission-platforms.md", + ".changes/permission-table.md", + ".changes/plugin-global-api-script.md", + ".changes/preserve-channel-order.md", + ".changes/progress-bar-state-refactor.md", + ".changes/re-export-progress-bar-status.md", + ".changes/rect-strcut.md", ".changes/refactor-capabilities-schema.md", + ".changes/refactor-capability-remote-option.md", + ".changes/refactor-scope-ret-value.md", + ".changes/remove-app-custom-protocol-feature.md", + ".changes/remove-from-format-image.md", + ".changes/remove-unit-uri.md", + ".changes/rename-file-drop.md", + ".changes/reparent.md", ".changes/rerun-if-permission-created.md", - ".changes/update-plugin-template.md" + ".changes/resources_table_access.md", + ".changes/runtime-add-capability.md", + ".changes/runtime-capability-dynamic.md", + ".changes/runtime-dpi-mod-moved.md", + ".changes/runtime-icon-lifetime.md", + ".changes/rwh-06.md", + ".changes/schema_str.md", + ".changes/set-auto-resize.md", + ".changes/strict-csp-isolation-frame.md", + ".changes/tauri-build-codegen-capabilities.md", + ".changes/tauri-build-dev-changes.md", + ".changes/tauri-cli-add-default-perm.md", + ".changes/tauri-close-requested-target-specific.md", + ".changes/tauri-context-icon-methods.md", + ".changes/tauri-error-sync.md", + ".changes/tauri-icon-removed.md", + ".changes/tauri-image-codegen.md", + ".changes/tauri-image.md", + ".changes/tauri-plugin-identifier-alphanumeric.md", + ".changes/tauri-runtime-webview-events.md", + ".changes/tauri-scope-object-error-sync.md", + ".changes/tauri-utils-capability-refactor.md", + ".changes/tauri-utils-plugin-module.md", + ".changes/tauri-webview-events.md", + ".changes/tray-rect.md", + ".changes/truncate-before-write-buildtask.md", + ".changes/unstable-child-webview.md", + ".changes/update-acl-paths-cli.md", + ".changes/update-app-template-capabilities-conf.md", + ".changes/update-plugin-template.md", + ".changes/utils-bundle-target-all.md", + ".changes/utils-bundle-type-all.md", + ".changes/utils-debug-eprintln.md", + ".changes/utils-named-capability-file.md", + ".changes/utils-remove-asset-trait.md", + ".changes/webview-bounds.md", + ".changes/wry-0.36.md", + ".changes/wry-0.37.md", + ".changes/wry-0.38.md" ] } diff --git a/.changes/preserve-channel-order.md b/.changes/preserve-channel-order.md new file mode 100644 index 000000000..59b4595fa --- /dev/null +++ b/.changes/preserve-channel-order.md @@ -0,0 +1,6 @@ +--- +"tauri": patch:enhance +"@tauri-apps/api": patch:enhance +--- + +Added a mechanism to preserve channel message order. diff --git a/.changes/progress-bar-state-refactor.md b/.changes/progress-bar-state-refactor.md new file mode 100644 index 000000000..3c65c686e --- /dev/null +++ b/.changes/progress-bar-state-refactor.md @@ -0,0 +1,6 @@ +--- +"tauri": patch:breaking +"tauri-utils": patch:breaking +--- + +Moved `ProgressBarState` from `tauri-utils` to the `tauri::window` module and removed the `unity_uri` field. diff --git a/.changes/re-export-progress-bar-status.md b/.changes/re-export-progress-bar-status.md new file mode 100644 index 000000000..edec293a2 --- /dev/null +++ b/.changes/re-export-progress-bar-status.md @@ -0,0 +1,5 @@ +--- +'tauri': 'patch:bug' +--- + +Export `ProgressBarStatus`, regression introduced in `2.0.0-beta.4` diff --git a/.changes/rect-strcut.md b/.changes/rect-strcut.md new file mode 100644 index 000000000..923f7777b --- /dev/null +++ b/.changes/rect-strcut.md @@ -0,0 +1,5 @@ +--- +'tauri': 'patch:feat' +--- + +Added `Rect` struct. diff --git a/.changes/refactor-capability-remote-option.md b/.changes/refactor-capability-remote-option.md new file mode 100644 index 000000000..2572cc10a --- /dev/null +++ b/.changes/refactor-capability-remote-option.md @@ -0,0 +1,6 @@ +--- +"tauri-utils": patch:breaking +"tauri": patch:breaking +--- + +Changed the capability `remote` configuration to take a list of `urls` instead of `domains` for more flexibility. diff --git a/.changes/refactor-scope-ret-value.md b/.changes/refactor-scope-ret-value.md new file mode 100644 index 000000000..63112d4e7 --- /dev/null +++ b/.changes/refactor-scope-ret-value.md @@ -0,0 +1,5 @@ +--- +"tauri": patch:breaking +--- + +The `allows` and `denies` methods from `ipc::ScopeValue`, `ipc::CommandScope` and `ipc::GlobalScope` now returns `&Vec>` instead of `&Vec`. diff --git a/.changes/remove-app-custom-protocol-feature.md b/.changes/remove-app-custom-protocol-feature.md new file mode 100644 index 000000000..ffd7353aa --- /dev/null +++ b/.changes/remove-app-custom-protocol-feature.md @@ -0,0 +1,8 @@ +--- +"@tauri-apps/cli": patch:breaking +"tauri-cli": patch:breaking +"tauri": patch:breaking +"tauri-build": patch:breaking +--- + +The `custom-protocol` Cargo feature is no longer required on your application and is now ignored. To check if running on production, use `#[cfg(not(dev))]` instead of `#[cfg(feature = "custom-protocol")]`. diff --git a/.changes/remove-from-format-image.md b/.changes/remove-from-format-image.md new file mode 100644 index 000000000..7fbde64f8 --- /dev/null +++ b/.changes/remove-from-format-image.md @@ -0,0 +1,5 @@ +--- +"@tauri-apps/api": patch:breaking +--- + +Remove the `Image.fromPngBytes` and `Image.fromIcoBytes` APIs. Use `Image.fromBytes` instead. diff --git a/.changes/remove-unit-uri.md b/.changes/remove-unit-uri.md new file mode 100644 index 000000000..7ddb6fcaf --- /dev/null +++ b/.changes/remove-unit-uri.md @@ -0,0 +1,5 @@ +--- +"@tauri-apps/api": patch:breaking +--- + +Removed the `unityUri` option from the progress bar state, no longer required. diff --git a/.changes/rename-file-drop.md b/.changes/rename-file-drop.md new file mode 100644 index 000000000..26fd3bd0c --- /dev/null +++ b/.changes/rename-file-drop.md @@ -0,0 +1,9 @@ +--- +"tauri": patch:breaking +"tauri-runtime": patch:breaking +"tauri-utils": patch:breaking +"tauri-runtime-wry": patch:breaking +"@tauri-apps/api": patch:breaking +--- + +Rename `FileDrop` to `DragDrop` on structs, enums and enum variants. Also renamed `file_drop` to `drag_drop` on fields and function names. diff --git a/.changes/reparent.md b/.changes/reparent.md new file mode 100644 index 000000000..318162aa5 --- /dev/null +++ b/.changes/reparent.md @@ -0,0 +1,8 @@ +--- +"@tauri-apps/api": patch:feat +"tauri": patch:feat +"tauri-runtime": patch:feat +"tauri-runtime-wry": patch:feat +--- + +Added the `reparent` function to the webview API. diff --git a/.changes/resources_table_access.md b/.changes/resources_table_access.md new file mode 100644 index 000000000..649badeeb --- /dev/null +++ b/.changes/resources_table_access.md @@ -0,0 +1,5 @@ +--- +'tauri': 'patch:breaking' +--- + +`Manager::resources_table` is now scoped so each `App/AppHandle/Window/Webview/WebviewWindow` has its own resource collection. diff --git a/.changes/runtime-add-capability.md b/.changes/runtime-add-capability.md new file mode 100644 index 000000000..851d11801 --- /dev/null +++ b/.changes/runtime-add-capability.md @@ -0,0 +1,5 @@ +--- +"tauri": patch:enhance +--- + +Added `Manager::add_capability` to add a capability file at runtime. diff --git a/.changes/runtime-capability-dynamic.md b/.changes/runtime-capability-dynamic.md new file mode 100644 index 000000000..404c76579 --- /dev/null +++ b/.changes/runtime-capability-dynamic.md @@ -0,0 +1,5 @@ +--- +"tauri": patch:enhance +--- + +`Manager::add_capability` now allows adding a dynamically defined capability instead of only relying on static strings. diff --git a/.changes/runtime-dpi-mod-moved.md b/.changes/runtime-dpi-mod-moved.md new file mode 100644 index 000000000..51629e345 --- /dev/null +++ b/.changes/runtime-dpi-mod-moved.md @@ -0,0 +1,5 @@ +--- +'tauri-runtime': 'major:breaking' +--- + +Moved `window::dpi` module to the root of the crate. diff --git a/.changes/runtime-icon-lifetime.md b/.changes/runtime-icon-lifetime.md new file mode 100644 index 000000000..664ff1e78 --- /dev/null +++ b/.changes/runtime-icon-lifetime.md @@ -0,0 +1,5 @@ +--- +'tauri-runtime': 'major:breaking' +--- + +Add a lifetime parameter for `Icon` type. Also changed `rgba` field to be `Cow<'a, [u8]>` diff --git a/.changes/set-auto-resize.md b/.changes/set-auto-resize.md new file mode 100644 index 000000000..454f44ecd --- /dev/null +++ b/.changes/set-auto-resize.md @@ -0,0 +1,7 @@ +--- +"tauri": patch:feat +"tauri-runtime": patch:feat +"tauri-runtime-wry": patch:feat +--- + +Added `set_auto_resize` method for the webview. diff --git a/.changes/set-zoom.md b/.changes/set-zoom.md new file mode 100644 index 000000000..d095b4918 --- /dev/null +++ b/.changes/set-zoom.md @@ -0,0 +1,8 @@ +--- +"@tauri-apps/api": minor:feat +"tauri": minor:feat +"tauri-runtime": minor:feat +"tauri-runtime-wry": minor:feat +--- + +Added the `set_zoom` function to the webview API. diff --git a/.changes/strict-csp-isolation-frame.md b/.changes/strict-csp-isolation-frame.md new file mode 100644 index 000000000..c01271311 --- /dev/null +++ b/.changes/strict-csp-isolation-frame.md @@ -0,0 +1,5 @@ +--- +"tauri": patch:enhance +--- + +Use a strict content security policy on the isolation pattern iframe. diff --git a/.changes/tauri-build-codegen-capabilities.md b/.changes/tauri-build-codegen-capabilities.md new file mode 100644 index 000000000..cc767345a --- /dev/null +++ b/.changes/tauri-build-codegen-capabilities.md @@ -0,0 +1,5 @@ +--- +"tauri-build": patch:enhance +--- + +Added `CodegenContext::capability` to include a capability file dynamically. diff --git a/.changes/tauri-build-dev-changes.md b/.changes/tauri-build-dev-changes.md new file mode 100644 index 000000000..bcf4204f6 --- /dev/null +++ b/.changes/tauri-build-dev-changes.md @@ -0,0 +1,5 @@ +--- +"tauri-build": patch:breaking +--- + +Removed `tauri_build::CodegenContext::dev()` and added `tauri_build::dev()`. diff --git a/.changes/tauri-cli-add-default-perm.md b/.changes/tauri-cli-add-default-perm.md new file mode 100644 index 000000000..814a6d26b --- /dev/null +++ b/.changes/tauri-cli-add-default-perm.md @@ -0,0 +1,6 @@ +--- +'tauri-cli': 'patch:feat' +'@tauri-apps/cli': 'patch:feat' +--- + +Add default permission for a plugin to capabilities when using `tauri add `. diff --git a/.changes/tauri-context-icon-methods.md b/.changes/tauri-context-icon-methods.md new file mode 100644 index 000000000..0f8cdbaef --- /dev/null +++ b/.changes/tauri-context-icon-methods.md @@ -0,0 +1,5 @@ +--- +'tauri': 'major:breaking' +--- + +Removed `Context::default_window_icon_mut` and `Context::tray_icon_mut`, use `Context::set_default_window_icon` and `Context::set_tray_icon` instead. Also changed `Context::set_tray_icon` to accept `Option`. diff --git a/.changes/tauri-icon-removed.md b/.changes/tauri-icon-removed.md new file mode 100644 index 000000000..5be02ecf0 --- /dev/null +++ b/.changes/tauri-icon-removed.md @@ -0,0 +1,5 @@ +--- +'tauri': 'major:breaking' +--- + +Removed `Icon` enum, use the new `Image` type instead. All APIs that previously accepted `Icon` have changed to accept `Image` instead. diff --git a/.changes/tauri-image-codegen.md b/.changes/tauri-image-codegen.md new file mode 100644 index 000000000..ce4e64afb --- /dev/null +++ b/.changes/tauri-image-codegen.md @@ -0,0 +1,5 @@ +--- +'tauri-codegen': 'major:breaking' +--- + +Change the generated context code to use the new `Image` type in tauri. diff --git a/.changes/tauri-image.md b/.changes/tauri-image.md new file mode 100644 index 000000000..0b72a4306 --- /dev/null +++ b/.changes/tauri-image.md @@ -0,0 +1,6 @@ +--- +'tauri': 'minor:feat' +'@tauri-apps/api': 'minor:feat' +--- + +Add a new `Image` type in Rust and JS. diff --git a/.changes/tauri-plugin-identifier-alphanumeric.md b/.changes/tauri-plugin-identifier-alphanumeric.md new file mode 100644 index 000000000..d6cbbc36d --- /dev/null +++ b/.changes/tauri-plugin-identifier-alphanumeric.md @@ -0,0 +1,6 @@ +--- +'tauri': 'patch:enhance' +'tauri-utils': 'patch:enhance' +--- + +Relax requirements on plugin's identifiers to be alphanumeric and `-` instead of only lower alpha and `-`. diff --git a/.changes/tauri-runtime-webview-events.md b/.changes/tauri-runtime-webview-events.md new file mode 100644 index 000000000..b021ab1d1 --- /dev/null +++ b/.changes/tauri-runtime-webview-events.md @@ -0,0 +1,6 @@ +--- +'tauri-runtime': 'patch' +'tauri-runtime-wry': 'patch' +--- + +Add `WebviewEvent`, `RunEvent::WebviewEvent` and `WebviewDispatch::on_webview_event`. diff --git a/.changes/tauri-utils-capability-refactor.md b/.changes/tauri-utils-capability-refactor.md new file mode 100644 index 000000000..c1fa7873f --- /dev/null +++ b/.changes/tauri-utils-capability-refactor.md @@ -0,0 +1,5 @@ +--- +"tauri-utils": patch:enhance +--- + +Refactored the capability types and resolution algorithm. diff --git a/.changes/tauri-utils-plugin-module.md b/.changes/tauri-utils-plugin-module.md new file mode 100644 index 000000000..033ed9f23 --- /dev/null +++ b/.changes/tauri-utils-plugin-module.md @@ -0,0 +1,5 @@ +--- +"tauri-utils": patch:feat +--- + +Added the `plugin` module. diff --git a/.changes/tauri-webview-events.md b/.changes/tauri-webview-events.md new file mode 100644 index 000000000..7a4aa10ef --- /dev/null +++ b/.changes/tauri-webview-events.md @@ -0,0 +1,9 @@ +--- +'tauri': 'patch:feat' +--- + +Add webview-specific events for multi-webview windows: + +- Add `WebviewEvent` enum +- Add `RunEvent::WebviewEvent` variant. +- Add `Builder::on_webview_event` and `Webview::on_webview_event` methods. diff --git a/.changes/tray-rect.md b/.changes/tray-rect.md new file mode 100644 index 000000000..28f8528cd --- /dev/null +++ b/.changes/tray-rect.md @@ -0,0 +1,9 @@ +--- +'tauri': 'patch:breaking' +--- + +Refactored the tray icon event struct: + +- Changed `TrayIconEvent.icon_rect` type to use the new `tauri::Rect` type. +- Removed `TrayIconEvent.x` and `TrayIconEvent.y` fields and combined them into `TrayIconEvent.position` field. +- Removed `tauri::tray::Rectangle` struct. diff --git a/.changes/truncate-before-write-buildtask.md b/.changes/truncate-before-write-buildtask.md new file mode 100644 index 000000000..0897545c0 --- /dev/null +++ b/.changes/truncate-before-write-buildtask.md @@ -0,0 +1,6 @@ +--- +"tauri-cli": patch:bug +"@tauri-apps/cli": patch:bug +--- + +Fixes truncation of existing BuildTask.kt when running `tauri android init`. diff --git a/.changes/unstable-child-webview.md b/.changes/unstable-child-webview.md new file mode 100644 index 000000000..bebc812d1 --- /dev/null +++ b/.changes/unstable-child-webview.md @@ -0,0 +1,6 @@ +--- +"tauri": patch:enhance +"tauri-runtime-wry": patch:enhance +--- + +When using the `unstable` feature flag, `WebviewWindow` will internally use the child webview interface for flexibility. diff --git a/.changes/update-acl-paths-cli.md b/.changes/update-acl-paths-cli.md new file mode 100644 index 000000000..19a4e7b55 --- /dev/null +++ b/.changes/update-acl-paths-cli.md @@ -0,0 +1,6 @@ +--- +"tauri-cli": patch:changes +"@tauri-apps/cli": patch:changes +--- + +Updates to new ACL manifest path. diff --git a/.changes/update-app-template-capabilities-conf.md b/.changes/update-app-template-capabilities-conf.md new file mode 100644 index 000000000..ebfe18dd2 --- /dev/null +++ b/.changes/update-app-template-capabilities-conf.md @@ -0,0 +1,6 @@ +--- +"@tauri-apps/cli": patch:enhance +"tauri-cli": patch:enhance +--- + +Update app template following capabilities configuration change. diff --git a/.changes/utils-bundle-target-all.md b/.changes/utils-bundle-target-all.md new file mode 100644 index 000000000..3a3e98a4e --- /dev/null +++ b/.changes/utils-bundle-target-all.md @@ -0,0 +1,5 @@ +--- +'tauri-utils': 'patch:bug' +--- + +Fix `BundleTarget::to_vec` returning an empty vec for `BundleTarget::All` variant. diff --git a/.changes/utils-bundle-type-all.md b/.changes/utils-bundle-type-all.md new file mode 100644 index 000000000..07906a628 --- /dev/null +++ b/.changes/utils-bundle-type-all.md @@ -0,0 +1,5 @@ +--- +'tauri-utils': 'patch:bug' +--- + +Add `BundleType::all` method to return all possible `BundleType` variants. diff --git a/.changes/utils-debug-eprintln.md b/.changes/utils-debug-eprintln.md new file mode 100644 index 000000000..8f1f275fe --- /dev/null +++ b/.changes/utils-debug-eprintln.md @@ -0,0 +1,5 @@ +--- +'tauri-utils': 'major:breaking' +--- + +Removed `debug_eprintln!` and `consume_unused_variable` macros. diff --git a/.changes/utils-named-capability-file.md b/.changes/utils-named-capability-file.md new file mode 100644 index 000000000..4510542a0 --- /dev/null +++ b/.changes/utils-named-capability-file.md @@ -0,0 +1,5 @@ +--- +"tauri-utils": major:breaking +--- + +Changed `CapabiltyFile::List` enum variant to be a tuple-struct and added `CapabiltyFile::NamedList`. This allows more flexibility when parsing capabilties from JSON files. \ No newline at end of file diff --git a/.changes/utils-remove-asset-trait.md b/.changes/utils-remove-asset-trait.md new file mode 100644 index 000000000..c37e15ae2 --- /dev/null +++ b/.changes/utils-remove-asset-trait.md @@ -0,0 +1,5 @@ +--- +"tauri-utils": patch:breaking +--- + +Removed the `assets::Assets` trait which is now part of the `tauri` crate. diff --git a/.changes/webview-bounds.md b/.changes/webview-bounds.md new file mode 100644 index 000000000..eb0c4db25 --- /dev/null +++ b/.changes/webview-bounds.md @@ -0,0 +1,5 @@ +--- +'tauri': 'minor:feat' +--- + +Add `Webview::bounds` and `Webview::set_bounds` APIs. diff --git a/.changes/wry-0.37.md b/.changes/wry-0.37.md new file mode 100644 index 000000000..d31f4f498 --- /dev/null +++ b/.changes/wry-0.37.md @@ -0,0 +1,5 @@ +--- +"tauri-runtime-wry": patch:deps +--- + +Upgraded to `wry@0.37.0` diff --git a/.changes/wry-0.38.md b/.changes/wry-0.38.md new file mode 100644 index 000000000..dd35e9dee --- /dev/null +++ b/.changes/wry-0.38.md @@ -0,0 +1,5 @@ +--- +"tauri-runtime-wry": patch:deps +--- + +Upgraded to `wry@0.38.0` diff --git a/.changes/zoom-hotkeys-enabled.md b/.changes/zoom-hotkeys-enabled.md new file mode 100644 index 000000000..ac7a4447f --- /dev/null +++ b/.changes/zoom-hotkeys-enabled.md @@ -0,0 +1,8 @@ +--- +"@tauri-apps/api": minor:feat +"tauri": minor:feat +"tauri-runtime": minor:feat +"tauri-runtime-wry": minor:feat +--- + +Add `zoom_hotkeys_enabled` to enable browser native zoom controls on creating webviews. diff --git a/.changes/zoom-polyfill.md b/.changes/zoom-polyfill.md new file mode 100644 index 000000000..88dad403f --- /dev/null +++ b/.changes/zoom-polyfill.md @@ -0,0 +1,6 @@ +--- +"tauri": minor:feat +"tauri-runtime": minor:feat +--- + +Provide a basic zoom hotkey polyfill for non-Windows platforms diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml index 8862ef7bd..062d6b941 100644 --- a/.github/FUNDING.yml +++ b/.github/FUNDING.yml @@ -1,4 +1,4 @@ -# Copyright 2019-2023 Tauri Programme within The Commons Conservancy +# Copyright 2019-2024 Tauri Programme within The Commons Conservancy # SPDX-License-Identifier: Apache-2.0 # SPDX-License-Identifier: MIT diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml index de42e9c69..a0cf4b952 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yml +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -1,4 +1,4 @@ -# Copyright 2019-2023 Tauri Programme within The Commons Conservancy +# Copyright 2019-2024 Tauri Programme within The Commons Conservancy # SPDX-License-Identifier: Apache-2.0 # SPDX-License-Identifier: MIT diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml index d23ca5084..dabd03289 100644 --- a/.github/ISSUE_TEMPLATE/config.yml +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -1,4 +1,4 @@ -# Copyright 2019-2023 Tauri Programme within The Commons Conservancy +# Copyright 2019-2024 Tauri Programme within The Commons Conservancy # SPDX-License-Identifier: Apache-2.0 # SPDX-License-Identifier: MIT diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml index ce1bbe9d4..c0b461914 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.yml +++ b/.github/ISSUE_TEMPLATE/feature_request.yml @@ -1,4 +1,4 @@ -# Copyright 2019-2023 Tauri Programme within The Commons Conservancy +# Copyright 2019-2024 Tauri Programme within The Commons Conservancy # SPDX-License-Identifier: Apache-2.0 # SPDX-License-Identifier: MIT diff --git a/.github/workflows/audit.yml b/.github/workflows/audit.yml index 2baec425e..d1565185c 100644 --- a/.github/workflows/audit.yml +++ b/.github/workflows/audit.yml @@ -1,4 +1,4 @@ -# Copyright 2019-2023 Tauri Programme within The Commons Conservancy +# Copyright 2019-2024 Tauri Programme within The Commons Conservancy # SPDX-License-Identifier: Apache-2.0 # SPDX-License-Identifier: MIT diff --git a/.github/workflows/bench.yml b/.github/workflows/bench.yml index 5c6b5c407..d5295a20e 100644 --- a/.github/workflows/bench.yml +++ b/.github/workflows/bench.yml @@ -1,4 +1,4 @@ -# Copyright 2019-2023 Tauri Programme within The Commons Conservancy +# Copyright 2019-2024 Tauri Programme within The Commons Conservancy # SPDX-License-Identifier: Apache-2.0 # SPDX-License-Identifier: MIT diff --git a/.github/workflows/check-change-tags.yml b/.github/workflows/check-change-tags.yml index d37360957..06e0c280e 100644 --- a/.github/workflows/check-change-tags.yml +++ b/.github/workflows/check-change-tags.yml @@ -1,4 +1,4 @@ -# Copyright 2019-2023 Tauri Programme within The Commons Conservancy +# Copyright 2019-2024 Tauri Programme within The Commons Conservancy # SPDX-License-Identifier: Apache-2.0 # SPDX-License-Identifier: MIT diff --git a/.github/workflows/check-generated-files.yml b/.github/workflows/check-generated-files.yml index e6f2b780f..9ffe3992c 100644 --- a/.github/workflows/check-generated-files.yml +++ b/.github/workflows/check-generated-files.yml @@ -1,4 +1,4 @@ -# Copyright 2019-2023 Tauri Programme within The Commons Conservancy +# Copyright 2019-2024 Tauri Programme within The Commons Conservancy # SPDX-License-Identifier: Apache-2.0 # SPDX-License-Identifier: MIT diff --git a/.github/workflows/check-license-header.yml b/.github/workflows/check-license-header.yml index 95757a4e2..f9fced552 100644 --- a/.github/workflows/check-license-header.yml +++ b/.github/workflows/check-license-header.yml @@ -1,4 +1,4 @@ -# Copyright 2019-2023 Tauri Programme within The Commons Conservancy +# Copyright 2019-2024 Tauri Programme within The Commons Conservancy # SPDX-License-Identifier: Apache-2.0 # SPDX-License-Identifier: MIT diff --git a/.github/workflows/covector-status.yml b/.github/workflows/covector-status.yml index fcb3996d6..ea3b84a2b 100644 --- a/.github/workflows/covector-status.yml +++ b/.github/workflows/covector-status.yml @@ -1,4 +1,4 @@ -# Copyright 2019-2023 Tauri Programme within The Commons Conservancy +# Copyright 2019-2024 Tauri Programme within The Commons Conservancy # SPDX-License-Identifier: Apache-2.0 # SPDX-License-Identifier: MIT diff --git a/.github/workflows/covector-version-or-publish-v1.yml b/.github/workflows/covector-version-or-publish-v1.yml new file mode 100644 index 000000000..e018b7bb1 --- /dev/null +++ b/.github/workflows/covector-version-or-publish-v1.yml @@ -0,0 +1,230 @@ +# Copyright 2019-2024 Tauri Programme within The Commons Conservancy +# SPDX-License-Identifier: Apache-2.0 +# SPDX-License-Identifier: MIT + +name: covector version or publish + +on: + push: + branches: + - 1.x + +jobs: + msrv-list: + runs-on: ${{ matrix.platform.os }} + strategy: + fail-fast: false + matrix: + platform: + - { + target: x86_64-pc-windows-msvc, + os: windows-latest, + toolchain: '1.61.0' + } + - { + target: x86_64-unknown-linux-gnu, + os: ubuntu-latest, + toolchain: '1.60.0' + } + - { + target: x86_64-apple-darwin, + os: macos-latest, + toolchain: '1.60.0' + } + steps: + - uses: actions/checkout@v4 + + - name: install rust ${{ matrix.platform.toolchain }} + uses: actions-rs/toolchain@v1 + with: + toolchain: ${{ matrix.platform.toolchain }} + target: ${{ matrix.platform.target }} + override: true + default: true + + - name: install Linux dependencies + if: contains(matrix.platform.target, 'unknown-linux') + run: | + sudo apt-get update + sudo apt-get install -y webkit2gtk-4.0 libayatana-appindicator3-dev + + - uses: Swatinem/rust-cache@v2 + + - name: delete lockfile + run: rm Cargo.lock + + - name: Downgrade crates with MSRV conflict + # The --precise flag can only be used once per invocation. + run: | + cargo update -p system-deps:6.2.0 --precise 6.1.1 + cargo update -p toml:0.7.8 --precise 0.7.3 + cargo update -p toml_edit:0.19.15 --precise 0.19.8 + cargo update -p embed-resource --precise 2.3.0 + cargo update -p toml_datetime --precise 0.6.1 + cargo update -p serde_spanned --precise 0.6.1 + cargo update -p winnow --precise 0.4.1 + cargo update -p plist --precise 1.5.1 + cargo update -p time --precise 0.3.15 + cargo update -p ignore --precise 0.4.18 + cargo update -p raw-window-handle --precise 0.5.0 + cargo update -p cargo_toml:0.15.3 --precise 0.15.2 + cargo update -p zbus --precise 3.13.0 + cargo update -p zbus_names --precise 2.5.0 + cargo update -p colored --precise 2.0.2 + cargo update -p arboard --precise 3.2.1 + cargo update -p tempfile --precise 3.6.0 + cargo update -p serde_with:3.6.1 --precise 3.0.0 + cargo update -p tokio --precise 1.29.0 + cargo update -p flate2 --precise 1.0.26 + cargo update -p h2 --precise 0.3.20 + cargo update -p reqwest --precise 0.11.18 + cargo update -p bstr --precise 1.6.2 + cargo update -p cfg-expr:0.15.7 --precise 0.15.4 + cargo update -p memchr --precise 2.6.2 + cargo update -p async-executor --precise 1.5.1 + cargo update -p proptest --precise 1.2.0 + cargo update -p regex --precise 1.9.6 + cargo update -p bstr --precise 1.6.2 + cargo update -p backtrace --precise 0.3.68 + cargo update -p blocking --precise 1.4.1 + cargo update -p ignore --precise 0.4.18 + cargo update -p regex --precise 1.9.6 + cargo update -p globset --precise 0.4.13 + cargo update -p crossbeam-channel --precise 0.5.8 + cargo update -p crossbeam-utils --precise 0.8.16 + cargo update -p image --precise 0.24.4 + cargo update -p async-process --precise 1.7.0 + cargo update -p is-terminal --precise 0.4.7 + cargo update -p tar --precise 0.4.39 + cargo update -p serde_json --precise 1.0.97 + cargo update -p petgraph --precise 0.6.3 + cargo update -p os_str_bytes --precise 6.5.1 + + - name: test build + run: cargo check --target ${{ matrix.platform.target }} --features tracing,compression,wry,linux-protocol-headers,isolation,custom-protocol,api-all,cli,updater,system-tray,windows7-compat,http-multipart,test + + run-integration-tests: + runs-on: ${{ matrix.platform }} + needs: msrv-list + + strategy: + fail-fast: false + matrix: + platform: [ubuntu-latest, macos-latest, windows-latest] + + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + - name: install stable + uses: actions-rs/toolchain@v1 + with: + toolchain: stable + - name: install Linux dependencies + if: matrix.platform == 'ubuntu-latest' + run: | + sudo apt-get update + sudo apt-get install -y webkit2gtk-4.0 libayatana-appindicator3-dev libfuse2 + + - uses: Swatinem/rust-cache@v2 + with: + workspaces: | + core -> ../target + tooling/cli + + - name: build CLI + uses: actions-rs/cargo@v1 + with: + command: build + args: --manifest-path ./tooling/cli/Cargo.toml + + - name: run integration tests + run: cargo test --test '*' -- --ignored + + - name: run CLI tests + timeout-minutes: 30 + run: | + cd ./tooling/cli/node + yarn + yarn build + yarn test + + version-or-publish: + runs-on: ubuntu-latest + timeout-minutes: 65 + outputs: + change: ${{ steps.covector.outputs.change }} + commandRan: ${{ steps.covector.outputs.commandRan }} + successfulPublish: ${{ steps.covector.outputs.successfulPublish }} + needs: + - run-integration-tests + + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + - uses: actions/setup-node@v2 + with: + node-version: 14 + registry-url: 'https://registry.npmjs.org' + cache: yarn + cache-dependency-path: tooling/*/yarn.lock + + - name: cargo login + run: cargo login ${{ secrets.ORG_CRATES_IO_TOKEN }} + - name: git config + run: | + git config --global user.name "${{ github.event.pusher.name }}" + git config --global user.email "${{ github.event.pusher.email }}" + + - name: covector version or publish (publish when no change files present) + uses: jbolda/covector/packages/action@covector-v0 + id: covector + env: + NODE_AUTH_TOKEN: ${{ secrets.ORG_NPM_TOKEN }} + CARGO_AUDIT_OPTIONS: ${{ secrets.CARGO_AUDIT_OPTIONS }} + with: + token: ${{ secrets.GITHUB_TOKEN }} + command: 'version-or-publish' + createRelease: true + + - name: Create Pull Request With Versions Bumped + if: steps.covector.outputs.commandRan == 'version' + uses: tauri-apps/create-pull-request@v3 + with: + token: ${{ secrets.GITHUB_TOKEN }} + branch: release/version-updates-v1 + title: Apply Version Updates From Current Changes (v1) + commit-message: 'apply version updates' + labels: 'version updates' + body: ${{ steps.covector.outputs.change }} + + - name: Trigger doc update + if: | + steps.covector.outputs.successfulPublish == 'true' && + steps.covector.outputs.packagesPublished != '' + uses: peter-evans/repository-dispatch@v1 + with: + token: ${{ secrets.ORG_TAURI_BOT_PAT }} + repository: tauri-apps/tauri-docs + event-type: update-docs + + - name: Trigger `@tauri-apps/cli` publishing workflow + if: | + steps.covector.outputs.successfulPublish == 'true' && + contains(steps.covector.outputs.packagesPublished, '@tauri-apps/cli') + uses: peter-evans/repository-dispatch@v1 + with: + token: ${{ secrets.ORG_TAURI_BOT_PAT }} + event-type: publish-js-cli + client-payload: >- + {"releaseId": "${{ steps.covector.outputs['-tauri-apps-cli-releaseId'] }}" } + + - name: Trigger `tauri-cli` publishing workflow + if: | + steps.covector.outputs.successfulPublish == 'true' && + contains(steps.covector.outputs.packagesPublished, 'tauri-cli') + uses: peter-evans/repository-dispatch@v1 + with: + token: ${{ secrets.ORG_TAURI_BOT_PAT }} + event-type: publish-clirs diff --git a/.github/workflows/covector-version-or-publish.yml b/.github/workflows/covector-version-or-publish.yml index b9344e9a7..1add9377b 100644 --- a/.github/workflows/covector-version-or-publish.yml +++ b/.github/workflows/covector-version-or-publish.yml @@ -1,4 +1,4 @@ -# Copyright 2019-2023 Tauri Programme within The Commons Conservancy +# Copyright 2019-2024 Tauri Programme within The Commons Conservancy # SPDX-License-Identifier: Apache-2.0 # SPDX-License-Identifier: MIT @@ -118,6 +118,7 @@ jobs: contains(steps.covector.outputs.packagesPublished, '@tauri-apps/cli') uses: peter-evans/repository-dispatch@v1 with: + token: ${{ secrets.ORG_TAURI_BOT_PAT }} event-type: publish-js-cli client-payload: >- {"releaseId": "${{ steps.covector.outputs['-tauri-apps-cli-releaseId'] }}" } @@ -128,4 +129,5 @@ jobs: contains(steps.covector.outputs.packagesPublished, 'tauri-cli') uses: peter-evans/repository-dispatch@v1 with: + token: ${{ secrets.ORG_TAURI_BOT_PAT }} event-type: publish-clirs diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index 06e4fcbfd..b2faff3a4 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -1,4 +1,4 @@ -# Copyright 2019-2023 Tauri Programme within The Commons Conservancy +# Copyright 2019-2024 Tauri Programme within The Commons Conservancy # SPDX-License-Identifier: Apache-2.0 # SPDX-License-Identifier: MIT diff --git a/.github/workflows/lint-cli.yml b/.github/workflows/lint-cli.yml index eee0d5ab4..a1163916e 100644 --- a/.github/workflows/lint-cli.yml +++ b/.github/workflows/lint-cli.yml @@ -1,4 +1,4 @@ -# Copyright 2019-2023 Tauri Programme within The Commons Conservancy +# Copyright 2019-2024 Tauri Programme within The Commons Conservancy # SPDX-License-Identifier: Apache-2.0 # SPDX-License-Identifier: MIT diff --git a/.github/workflows/lint-core.yml b/.github/workflows/lint-core.yml index f11543cfa..bbbc9dc50 100644 --- a/.github/workflows/lint-core.yml +++ b/.github/workflows/lint-core.yml @@ -1,4 +1,4 @@ -# Copyright 2019-2023 Tauri Programme within The Commons Conservancy +# Copyright 2019-2024 Tauri Programme within The Commons Conservancy # SPDX-License-Identifier: Apache-2.0 # SPDX-License-Identifier: MIT diff --git a/.github/workflows/lint-js.yml b/.github/workflows/lint-js.yml index 5fe27a62c..b6e91163a 100644 --- a/.github/workflows/lint-js.yml +++ b/.github/workflows/lint-js.yml @@ -1,4 +1,4 @@ -# Copyright 2019-2023 Tauri Programme within The Commons Conservancy +# Copyright 2019-2024 Tauri Programme within The Commons Conservancy # SPDX-License-Identifier: Apache-2.0 # SPDX-License-Identifier: MIT diff --git a/.github/workflows/publish-cli-js.yml b/.github/workflows/publish-cli-js.yml index d7be5a5ad..16c25f949 100644 --- a/.github/workflows/publish-cli-js.yml +++ b/.github/workflows/publish-cli-js.yml @@ -1,4 +1,4 @@ -# Copyright 2019-2023 Tauri Programme within The Commons Conservancy +# Copyright 2019-2024 Tauri Programme within The Commons Conservancy # SPDX-License-Identifier: Apache-2.0 # SPDX-License-Identifier: MIT diff --git a/.github/workflows/publish-cli-rs.yml b/.github/workflows/publish-cli-rs.yml index 906456ce3..e1db87a37 100644 --- a/.github/workflows/publish-cli-rs.yml +++ b/.github/workflows/publish-cli-rs.yml @@ -1,4 +1,4 @@ -# Copyright 2019-2023 Tauri Programme within The Commons Conservancy +# Copyright 2019-2024 Tauri Programme within The Commons Conservancy # SPDX-License-Identifier: Apache-2.0 # SPDX-License-Identifier: MIT diff --git a/.github/workflows/supply-chain.yml b/.github/workflows/supply-chain.yml new file mode 100644 index 000000000..2db8db33d --- /dev/null +++ b/.github/workflows/supply-chain.yml @@ -0,0 +1,44 @@ +# Copyright 2019-2024 Tauri Programme within The Commons Conservancy +# SPDX-License-Identifier: Apache-2.0 +# SPDX-License-Identifier: MIT + +name: supply chain health status +on: + workflow_dispatch: + schedule: + - cron: '0 0 * * *' + push: + branches: + - dev + - 1.x + paths: + - '.github/workflows/supply-chain.yml' + - '**/Cargo.lock' + - '**/Cargo.toml' +jobs: + cargo-vet: + name: check rust dependencies with cargo vet + runs-on: ubuntu-latest + env: + CARGO_VET_VERSION: 0.9.1 + steps: + - uses: actions/checkout@master + - name: Install Rust + run: rustup update stable && rustup default stable + + - uses: actions/cache@v2 + with: + path: ${{ runner.tool_cache }}/cargo-vet + key: cargo-vet-bin-${{ env.CARGO_VET_VERSION }} + + - name: Add the tool cache directory to the search path + run: echo "${{ runner.tool_cache }}/cargo-vet/bin" >> $GITHUB_PATH + + - name: Ensure that the tool cache is populated with the cargo-vet binary + run: cargo install --root ${{ runner.tool_cache }}/cargo-vet --version ${{ env.CARGO_VET_VERSION }} cargo-vet + + - name: Invoke cargo-vet + run: cargo vet --locked + + - name: Provide audit suggestions + run: cargo vet --locked suggestions diff --git a/.github/workflows/test-android.yml b/.github/workflows/test-android.yml index e6af3d541..7b6cef068 100644 --- a/.github/workflows/test-android.yml +++ b/.github/workflows/test-android.yml @@ -1,4 +1,4 @@ -# Copyright 2019-2023 Tauri Programme within The Commons Conservancy +# Copyright 2019-2024 Tauri Programme within The Commons Conservancy # SPDX-License-Identifier: Apache-2.0 # SPDX-License-Identifier: MIT @@ -84,6 +84,14 @@ jobs: - name: build CLI run: cargo build --manifest-path ./tooling/cli/Cargo.toml + + - name: move CLI to cargo bin dir + if: matrix.os != "windows-latest" + run: mv ./tooling/cli/target/debug/cargo-tauri $HOME/.cargo/bin + + - name: move CLI to cargo bin dir + if: matrix.os == "windows-latest" + run: mv ./tooling/cli/target/debug/cargo-tauri.exe $HOME/.cargo/bin - name: build Tauri API working-directory: ./tooling/api @@ -95,12 +103,12 @@ jobs: - name: init Android Studio project working-directory: ./examples/api - run: ../../tooling/cli/target/debug/cargo-tauri android init + run: cargo tauri android init env: NDK_HOME: ${{ steps.setup-ndk.outputs.ndk-path }} - name: build APK working-directory: ./examples/api - run: ../../tooling/cli/target/debug/cargo-tauri android build + run: cargo tauri android build env: NDK_HOME: ${{ steps.setup-ndk.outputs.ndk-path }} diff --git a/.github/workflows/test-cli-js.yml b/.github/workflows/test-cli-js.yml index dc9e299ef..cea87e2ab 100644 --- a/.github/workflows/test-cli-js.yml +++ b/.github/workflows/test-cli-js.yml @@ -1,4 +1,4 @@ -# Copyright 2019-2023 Tauri Programme within The Commons Conservancy +# Copyright 2019-2024 Tauri Programme within The Commons Conservancy # SPDX-License-Identifier: Apache-2.0 # SPDX-License-Identifier: MIT diff --git a/.github/workflows/test-cli-rs.yml b/.github/workflows/test-cli-rs.yml index 34b356851..4b79bbf30 100644 --- a/.github/workflows/test-cli-rs.yml +++ b/.github/workflows/test-cli-rs.yml @@ -1,4 +1,4 @@ -# Copyright 2019-2023 Tauri Programme within The Commons Conservancy +# Copyright 2019-2024 Tauri Programme within The Commons Conservancy # SPDX-License-Identifier: Apache-2.0 # SPDX-License-Identifier: MIT diff --git a/.github/workflows/test-core.yml b/.github/workflows/test-core.yml index fb34ec63d..30259b1c4 100644 --- a/.github/workflows/test-core.yml +++ b/.github/workflows/test-core.yml @@ -1,4 +1,4 @@ -# Copyright 2019-2023 Tauri Programme within The Commons Conservancy +# Copyright 2019-2024 Tauri Programme within The Commons Conservancy # SPDX-License-Identifier: Apache-2.0 # SPDX-License-Identifier: MIT @@ -94,7 +94,7 @@ jobs: - name: test (using cross) if: ${{ matrix.platform.cross }} run: | - cargo install cross --git https://github.com/cross-rs/cross + cargo install cross --git https://github.com/cross-rs/cross --locked cross ${{ matrix.platform.command }} --target ${{ matrix.platform.target }} ${{ matrix.features.args }} - name: test (using cargo) diff --git a/.github/workflows/test-lint-bundler.yml b/.github/workflows/test-lint-bundler.yml index 72379e120..38419168f 100644 --- a/.github/workflows/test-lint-bundler.yml +++ b/.github/workflows/test-lint-bundler.yml @@ -1,4 +1,4 @@ -# Copyright 2019-2023 Tauri Programme within The Commons Conservancy +# Copyright 2019-2024 Tauri Programme within The Commons Conservancy # SPDX-License-Identifier: Apache-2.0 # SPDX-License-Identifier: MIT diff --git a/.github/workflows/udeps.yml b/.github/workflows/udeps.yml index ebb2e6076..09242967a 100644 --- a/.github/workflows/udeps.yml +++ b/.github/workflows/udeps.yml @@ -1,4 +1,4 @@ -# Copyright 2019-2023 Tauri Programme within The Commons Conservancy +# Copyright 2019-2024 Tauri Programme within The Commons Conservancy # SPDX-License-Identifier: Apache-2.0 # SPDX-License-Identifier: MIT diff --git a/.gitignore b/.gitignore index 4fb1d3064..61ca7e97e 100644 --- a/.gitignore +++ b/.gitignore @@ -73,7 +73,7 @@ TODO.md target # lock for libs -/Cargo.lock +#/Cargo.lock Committed to prevent msrv checks from failing /tooling/bench/tests/Cargo.lock /yarn.lock diff --git a/.husky/pre-commit b/.husky/pre-commit index 7178a5417..cdebb4e89 100755 --- a/.husky/pre-commit +++ b/.husky/pre-commit @@ -1,6 +1,6 @@ #!/bin/sh -# Copyright 2019-2023 Tauri Programme within The Commons Conservancy +# Copyright 2019-2024 Tauri Programme within The Commons Conservancy # SPDX-License-Identifier: Apache-2.0 # SPDX-License-Identifier: MIT diff --git a/.scripts/cargo-check.ps1 b/.scripts/cargo-check.ps1 index bf67d2511..fd4855a8f 100755 --- a/.scripts/cargo-check.ps1 +++ b/.scripts/cargo-check.ps1 @@ -1,6 +1,6 @@ #!/usr/bin/env pwsh -# Copyright 2019-2023 Tauri Programme within The Commons Conservancy +# Copyright 2019-2024 Tauri Programme within The Commons Conservancy # SPDX-License-Identifier: Apache-2.0 # SPDX-License-Identifier: MIT diff --git a/.scripts/cargo-check.sh b/.scripts/cargo-check.sh index 9c1134247..f441ae72e 100755 --- a/.scripts/cargo-check.sh +++ b/.scripts/cargo-check.sh @@ -1,6 +1,6 @@ #!/usr/bin/env sh -# Copyright 2019-2023 Tauri Programme within The Commons Conservancy +# Copyright 2019-2024 Tauri Programme within The Commons Conservancy # SPDX-License-Identifier: Apache-2.0 # SPDX-License-Identifier: MIT diff --git a/.scripts/ci/check-change-tags.js b/.scripts/ci/check-change-tags.js index d49c7c54c..1cd7de3d9 100644 --- a/.scripts/ci/check-change-tags.js +++ b/.scripts/ci/check-change-tags.js @@ -1,6 +1,6 @@ #!/usr/bin/env node -// Copyright 2019-2023 Tauri Programme within The Commons Conservancy +// Copyright 2019-2024 Tauri Programme within The Commons Conservancy // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT @@ -69,7 +69,9 @@ function checkChangeFiles(changeFiles) { const [_bin, _script, ...files] = process.argv if (files.length > 0) { - checkChangeFiles(files.filter((f) => f.toLowerCase() !== 'readme.md')) + checkChangeFiles( + files.filter((f) => f.toLowerCase() !== '.changes/readme.md') + ) } else { const changeFiles = fs .readdirSync('.changes') diff --git a/.scripts/ci/check-license-header.js b/.scripts/ci/check-license-header.js index 40da76105..5afd062f8 100644 --- a/.scripts/ci/check-license-header.js +++ b/.scripts/ci/check-license-header.js @@ -1,6 +1,6 @@ #!/usr/bin/env node -// Copyright 2019-2023 Tauri Programme within The Commons Conservancy +// Copyright 2019-2024 Tauri Programme within The Commons Conservancy // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT @@ -8,7 +8,7 @@ const fs = require('fs') const path = require('path') const readline = require('readline') -const header = `Copyright 2019-2023 Tauri Programme within The Commons Conservancy +const header = `Copyright 2019-2024 Tauri Programme within The Commons Conservancy SPDX-License-Identifier: Apache-2.0 SPDX-License-Identifier: MIT` const bundlerLicense = diff --git a/.scripts/ci/has-diff.sh b/.scripts/ci/has-diff.sh index 30c19d4f1..1f4699030 100755 --- a/.scripts/ci/has-diff.sh +++ b/.scripts/ci/has-diff.sh @@ -1,6 +1,6 @@ #!/bin/bash -# Copyright 2019-2023 Tauri Programme within The Commons Conservancy +# Copyright 2019-2024 Tauri Programme within The Commons Conservancy # SPDX-License-Identifier: Apache-2.0 # SPDX-License-Identifier: MIT diff --git a/.scripts/ci/pack-cli.sh b/.scripts/ci/pack-cli.sh index 1cef91b9d..359194f26 100755 --- a/.scripts/ci/pack-cli.sh +++ b/.scripts/ci/pack-cli.sh @@ -1,6 +1,6 @@ #!/bin/bash -# Copyright 2019-2023 Tauri Programme within The Commons Conservancy +# Copyright 2019-2024 Tauri Programme within The Commons Conservancy # SPDX-License-Identifier: Apache-2.0 # SPDX-License-Identifier: MIT diff --git a/.scripts/covector/package-latest-version.js b/.scripts/covector/package-latest-version.js index 11f7f4119..954645348 100644 --- a/.scripts/covector/package-latest-version.js +++ b/.scripts/covector/package-latest-version.js @@ -1,6 +1,6 @@ #!/usr/bin/env node -// Copyright 2019-2023 Tauri Programme within The Commons Conservancy +// Copyright 2019-2024 Tauri Programme within The Commons Conservancy // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT diff --git a/.scripts/covector/sync-cli-metadata.js b/.scripts/covector/sync-cli-metadata.js index 26b7e41a6..a8c4a53c2 100644 --- a/.scripts/covector/sync-cli-metadata.js +++ b/.scripts/covector/sync-cli-metadata.js @@ -1,6 +1,6 @@ #!/usr/bin/env node -// Copyright 2019-2023 Tauri Programme within The Commons Conservancy +// Copyright 2019-2024 Tauri Programme within The Commons Conservancy // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT diff --git a/.scripts/docker/build.sh b/.scripts/docker/build.sh index 10f61e7f0..5aa7c5af7 100755 --- a/.scripts/docker/build.sh +++ b/.scripts/docker/build.sh @@ -1,6 +1,6 @@ #!/bin/sh -# Copyright 2019-2023 Tauri Programme within The Commons Conservancy +# Copyright 2019-2024 Tauri Programme within The Commons Conservancy # SPDX-License-Identifier: Apache-2.0 # SPDX-License-Identifier: MIT diff --git a/.scripts/setup.ps1 b/.scripts/setup.ps1 index a841c5f99..f135b1bfe 100644 --- a/.scripts/setup.ps1 +++ b/.scripts/setup.ps1 @@ -1,6 +1,6 @@ #!/usr/bin/env pwsh -# Copyright 2019-2023 Tauri Programme within The Commons Conservancy +# Copyright 2019-2024 Tauri Programme within The Commons Conservancy # SPDX-License-Identifier: Apache-2.0 # SPDX-License-Identifier: MIT diff --git a/.scripts/setup.sh b/.scripts/setup.sh index 5e2f91743..4b7eac507 100755 --- a/.scripts/setup.sh +++ b/.scripts/setup.sh @@ -1,6 +1,6 @@ #!/usr/bin/env bash -# Copyright 2019-2023 Tauri Programme within The Commons Conservancy +# Copyright 2019-2024 Tauri Programme within The Commons Conservancy # SPDX-License-Identifier: Apache-2.0 # SPDX-License-Identifier: MIT diff --git a/.scripts/update-lockfiles.sh b/.scripts/update-lockfiles.sh index 961d0fb4c..7d01b6430 100755 --- a/.scripts/update-lockfiles.sh +++ b/.scripts/update-lockfiles.sh @@ -1,6 +1,6 @@ #!/usr/bin/env sh -# Copyright 2019-2023 Tauri Programme within The Commons Conservancy +# Copyright 2019-2024 Tauri Programme within The Commons Conservancy # SPDX-License-Identifier: Apache-2.0 # SPDX-License-Identifier: MIT diff --git a/.scripts/utils/batch_to_exe.cmd b/.scripts/utils/batch_to_exe.cmd index 3d5ffa6d8..753412513 100644 --- a/.scripts/utils/batch_to_exe.cmd +++ b/.scripts/utils/batch_to_exe.cmd @@ -1,10 +1,10 @@ -: Copyright 2019-2023 Tauri Programme within The Commons Conservancy +: Copyright 2019-2024 Tauri Programme within The Commons Conservancy : SPDX-License-Identifier: Apache-2.0 : SPDX-License-Identifier: MIT @ECHO OFF -REM Copyright 2019-2023 Tauri Programme within The Commons Conservancy +REM Copyright 2019-2024 Tauri Programme within The Commons Conservancy REM SPDX-License-Identifier: Apache-2.0 REM SPDX-License-Identifier: MIT diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 000000000..20f33b24f --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,4913 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "acl-tests" +version = "0.1.0" +dependencies = [ + "insta", + "serde_json", + "tauri-utils", +] + +[[package]] +name = "addr2line" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + +[[package]] +name = "aead" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d122413f284cf2d62fb1b7db97e02edb8cda96d769b16e443a4f6195e35662b0" +dependencies = [ + "crypto-common", + "generic-array", +] + +[[package]] +name = "aes" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0" +dependencies = [ + "cfg-if", + "cipher", + "cpufeatures", +] + +[[package]] +name = "aes-gcm" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "831010a0f742e1209b3bcea8fab6a8e149051ba6099432c8cb2cc117dec3ead1" +dependencies = [ + "aead", + "aes", + "cipher", + "ctr", + "ghash", + "subtle", +] + +[[package]] +name = "aho-corasick" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +dependencies = [ + "memchr", +] + +[[package]] +name = "alloc-no-stdlib" +version = "2.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc7bb162ec39d46ab1ca8c77bf72e890535becd1751bb45f64c597edb4c8c6b3" + +[[package]] +name = "alloc-stdlib" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94fb8275041c72129eb51b7d0322c29b8387a0386127718b096429201a5d6ece" +dependencies = [ + "alloc-no-stdlib", +] + +[[package]] +name = "android-tzdata" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" + +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + +[[package]] +name = "anyhow" +version = "1.0.81" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0952808a6c2afd1aa8947271f3a60f1a6763c7b912d210184c5149b5cf147247" + +[[package]] +name = "atk" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4af014b17dd80e8af9fa689b2d4a211ddba6eb583c1622f35d0cb543f6b17e4" +dependencies = [ + "atk-sys", + "glib", + "libc", +] + +[[package]] +name = "atk-sys" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "251e0b7d90e33e0ba930891a505a9a35ece37b2dd37a14f3ffc306c13b980009" +dependencies = [ + "glib-sys", + "gobject-sys", + "libc", + "system-deps", +] + +[[package]] +name = "autocfg" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1fdabc7756949593fe60f30ec81974b613357de856987752631dea1e3394c80" + +[[package]] +name = "backtrace" +version = "0.3.71" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26b05800d2e817c8b3b4b54abd461726265fa9789ae34330622f2db9ee696f9d" +dependencies = [ + "addr2line", + "cc", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", +] + +[[package]] +name = "base64" +version = "0.21.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" + +[[package]] +name = "base64" +version = "0.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9475866fec1451be56a3c2400fd081ff546538961565ccb5b7142cbd22bc7a51" + +[[package]] +name = "bit-set" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0700ddab506f33b20a03b13996eccd309a48e5ff77d0d95926aa0210fb4e95f1" +dependencies = [ + "bit-vec", +] + +[[package]] +name = "bit-vec" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitflags" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" +dependencies = [ + "serde", +] + +[[package]] +name = "block" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a" + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "brotli" +version = "3.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d640d25bc63c50fb1f0b545ffd80207d2e10a4c965530809b40ba3386825c391" +dependencies = [ + "alloc-no-stdlib", + "alloc-stdlib", + "brotli-decompressor", +] + +[[package]] +name = "brotli-decompressor" +version = "2.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e2e4afe60d7dd600fdd3de8d0f08c2b7ec039712e3b6137ff98b7004e82de4f" +dependencies = [ + "alloc-no-stdlib", + "alloc-stdlib", +] + +[[package]] +name = "bumpalo" +version = "3.15.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ff69b9dd49fd426c69a0db9fc04dd934cdb6645ff000864d98f7e2af8830eaa" + +[[package]] +name = "bytemuck" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d6d68c57235a3a081186990eca2867354726650f42f7516ca50c28d6281fd15" + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "bytes" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "514de17de45fdb8dc022b1a7975556c53c86f9f0aa5f534b98977b171857c2c9" +dependencies = [ + "serde", +] + +[[package]] +name = "cairo-rs" +version = "0.18.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ca26ef0159422fb77631dc9d17b102f253b876fe1586b03b803e63a309b4ee2" +dependencies = [ + "bitflags 2.5.0", + "cairo-sys-rs", + "glib", + "libc", + "once_cell", + "thiserror", +] + +[[package]] +name = "cairo-sys-rs" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "685c9fa8e590b8b3d678873528d83411db17242a73fccaed827770ea0fedda51" +dependencies = [ + "glib-sys", + "libc", + "system-deps", +] + +[[package]] +name = "camino" +version = "1.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c59e92b5a388f549b863a7bea62612c09f24c8393560709a54558a9abdfb3b9c" +dependencies = [ + "serde", +] + +[[package]] +name = "cargo-platform" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "694c8807f2ae16faecc43dc17d74b3eb042482789fd0eb64b39a2e04e087053f" +dependencies = [ + "serde", +] + +[[package]] +name = "cargo_metadata" +version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d886547e41f740c616ae73108f6eb70afe6d940c7bc697cb30f13daec073037" +dependencies = [ + "camino", + "cargo-platform", + "semver", + "serde", + "serde_json", + "thiserror", +] + +[[package]] +name = "cargo_toml" +version = "0.17.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a969e13a7589e9e3e4207e153bae624ade2b5622fb4684a4923b23ec3d57719" +dependencies = [ + "serde", + "toml 0.8.2", +] + +[[package]] +name = "cc" +version = "1.0.90" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8cd6604a82acf3039f1144f54b8eb34e91ffba622051189e71b781822d5ee1f5" + +[[package]] +name = "cesu8" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c" + +[[package]] +name = "cfb" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d38f2da7a0a2c4ccf0065be06397cc26a81f4e528be095826eee9d4adbb8c60f" +dependencies = [ + "byteorder", + "fnv", + "uuid", +] + +[[package]] +name = "cfg-expr" +version = "0.15.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa50868b64a9a6fda9d593ce778849ea8715cd2a3d2cc17ffdb4a2f2f2f1961d" +dependencies = [ + "smallvec", + "target-lexicon", +] + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "cfg_aliases" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e" + +[[package]] +name = "cfg_aliases" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77e53693616d3075149f4ead59bdeecd204ac6b8192d8969757601b74bddf00f" + +[[package]] +name = "chrono" +version = "0.4.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a0d04d43504c61aa6c7531f1871dd0d418d91130162063b789da00fd7057a5e" +dependencies = [ + "android-tzdata", + "iana-time-zone", + "num-traits", + "serde", + "windows-targets 0.52.4", +] + +[[package]] +name = "cipher" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" +dependencies = [ + "crypto-common", + "inout", +] + +[[package]] +name = "cocoa" +version = "0.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6140449f97a6e97f9511815c5632d84c8aacf8ac271ad77c559218161a1373c" +dependencies = [ + "bitflags 1.3.2", + "block", + "cocoa-foundation", + "core-foundation", + "core-graphics", + "foreign-types 0.5.0", + "libc", + "objc", +] + +[[package]] +name = "cocoa-foundation" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c6234cbb2e4c785b456c0644748b1ac416dd045799740356f8363dfe00c93f7" +dependencies = [ + "bitflags 1.3.2", + "block", + "core-foundation", + "core-graphics-types", + "libc", + "objc", +] + +[[package]] +name = "color_quant" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b" + +[[package]] +name = "combine" +version = "4.6.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35ed6e9d84f0b51a7f52daf1c7d71dd136fd7a3f41a8462b8cdb8c78d920fad4" +dependencies = [ + "bytes", + "memchr", +] + +[[package]] +name = "console" +version = "0.15.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e1f83fc076bd6dd27517eacdf25fef6c4dfe5f1d7448bafaaf3a26f13b5e4eb" +dependencies = [ + "encode_unicode", + "lazy_static", + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "convert_case" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" + +[[package]] +name = "core-foundation" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" + +[[package]] +name = "core-graphics" +version = "0.23.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "970a29baf4110c26fedbc7f82107d42c23f7e88e404c4577ed73fe99ff85a212" +dependencies = [ + "bitflags 1.3.2", + "core-foundation", + "core-graphics-types", + "foreign-types 0.5.0", + "libc", +] + +[[package]] +name = "core-graphics-types" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45390e6114f68f718cc7a830514a96f903cccd70d02a8f6d9f643ac4ba45afaf" +dependencies = [ + "bitflags 1.3.2", + "core-foundation", + "libc", +] + +[[package]] +name = "cpufeatures" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53fe5e26ff1b7aef8bca9c6080520cfb8d9333c7568e1829cef191a9723e5504" +dependencies = [ + "libc", +] + +[[package]] +name = "crc32fast" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3855a8a784b474f333699ef2bbca9db2c4a1f6d9088a90a2d25b1eb53111eaa" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "crossbeam-channel" +version = "0.5.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab3db02a9c5b5121e1e42fbdb1aeb65f5e02624cc58c43f2884c6ccac0b82f95" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "248e3bacc7dc6baa3b21e405ee045c3047101a49145e7e9eca583ab4c2ca5345" + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "rand_core 0.6.4", + "typenum", +] + +[[package]] +name = "cssparser" +version = "0.27.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "754b69d351cdc2d8ee09ae203db831e005560fc6030da058f86ad60c92a9cb0a" +dependencies = [ + "cssparser-macros", + "dtoa-short", + "itoa 0.4.8", + "matches", + "phf 0.8.0", + "proc-macro2", + "quote", + "smallvec", + "syn 1.0.109", +] + +[[package]] +name = "cssparser-macros" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13b588ba4ac1a99f7f2964d24b3d896ddc6bf847ee3855dbd4366f058cfcd331" +dependencies = [ + "quote", + "syn 2.0.57", +] + +[[package]] +name = "ctor" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad291aa74992b9b7a7e88c38acbbf6ad7e107f1d90ee8775b7bc1fc3394f485c" +dependencies = [ + "quote", + "syn 2.0.57", +] + +[[package]] +name = "ctr" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0369ee1ad671834580515889b80f2ea915f23b8be8d0daa4bbaf2ac5c7590835" +dependencies = [ + "cipher", +] + +[[package]] +name = "darling" +version = "0.20.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "54e36fcd13ed84ffdfda6f5be89b31287cbb80c439841fe69e04841435464391" +dependencies = [ + "darling_core", + "darling_macro", +] + +[[package]] +name = "darling_core" +version = "0.20.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c2cf1c23a687a1feeb728783b993c4e1ad83d99f351801977dd809b48d0a70f" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn 2.0.57", +] + +[[package]] +name = "darling_macro" +version = "0.20.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a668eda54683121533a393014d8692171709ff57a7d61f187b6e782719f8933f" +dependencies = [ + "darling_core", + "quote", + "syn 2.0.57", +] + +[[package]] +name = "data-url" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c297a1c74b71ae29df00c3e22dd9534821d60eb9af5a0192823fa2acea70c2a" + +[[package]] +name = "deranged" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4" +dependencies = [ + "powerfmt", + "serde", +] + +[[package]] +name = "derive_more" +version = "0.99.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321" +dependencies = [ + "convert_case", + "proc-macro2", + "quote", + "rustc_version", + "syn 1.0.109", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "crypto-common", +] + +[[package]] +name = "dirs-next" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b98cf8ebf19c3d1b223e151f99a4f9f0690dca41414773390fc824184ac833e1" +dependencies = [ + "cfg-if", + "dirs-sys-next", +] + +[[package]] +name = "dirs-sys-next" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ebda144c4fe02d1f7ea1a7d9641b6fc6b580adcfa024ae48797ecdeb6825b4d" +dependencies = [ + "libc", + "redox_users", + "winapi", +] + +[[package]] +name = "dispatch" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd0c93bb4b0c6d9b77f4435b0ae98c24d17f1c45b2ff844c6151a07256ca923b" + +[[package]] +name = "dlib" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "330c60081dcc4c72131f8eb70510f1ac07223e5d4163db481a04a0befcffa412" +dependencies = [ + "libloading 0.8.3", +] + +[[package]] +name = "dlopen2" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e1297103d2bbaea85724fcee6294c2d50b1081f9ad47d0f6f6f61eda65315a6" +dependencies = [ + "dlopen2_derive", + "libc", + "once_cell", + "winapi", +] + +[[package]] +name = "dlopen2_derive" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2b99bf03862d7f545ebc28ddd33a665b50865f4dfd84031a393823879bd4c54" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.57", +] + +[[package]] +name = "dpi" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f25c0e292a7ca6d6498557ff1df68f32c99850012b6ea401cf8daf771f22ff53" +dependencies = [ + "serde", +] + +[[package]] +name = "dtoa" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcbb2bf8e87535c23f7a8a321e364ce21462d0ff10cb6407820e8e96dfff6653" + +[[package]] +name = "dtoa-short" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbaceec3c6e4211c79e7b1800fb9680527106beb2f9c51904a3210c03a448c74" +dependencies = [ + "dtoa", +] + +[[package]] +name = "dunce" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56ce8c6da7551ec6c462cbaf3bfbc75131ebbfa1c944aeaa9dab51ca1c5f0c3b" + +[[package]] +name = "dyn-clone" +version = "1.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d6ef0072f8a535281e4876be788938b528e9a1d43900b82c2569af7da799125" + +[[package]] +name = "embed-resource" +version = "2.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6985554d0688b687c5cb73898a34fbe3ad6c24c58c238a4d91d5e840670ee9d" +dependencies = [ + "cc", + "memchr", + "rustc_version", + "toml 0.8.2", + "vswhom", + "winreg 0.52.0", +] + +[[package]] +name = "embed_plist" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ef6b89e5b37196644d8796de5268852ff179b44e96276cf4290264843743bb7" + +[[package]] +name = "encode_unicode" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f" + +[[package]] +name = "env_logger" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a19187fea3ac7e84da7dacf48de0c45d63c6a76f9490dae389aead16c243fce3" +dependencies = [ + "log", + "regex", +] + +[[package]] +name = "equivalent" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" + +[[package]] +name = "errno" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "fastrand" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "658bd65b1cf4c852a3cc96f18a8ce7b5640f6b703f905c7d74532294c2a63984" + +[[package]] +name = "fdeflate" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f9bfee30e4dedf0ab8b422f03af778d9612b63f502710fc500a334ebe2de645" +dependencies = [ + "simd-adler32", +] + +[[package]] +name = "field-offset" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38e2275cc4e4fc009b0669731a1e5ab7ebf11f469eaede2bab9309a5b4d6057f" +dependencies = [ + "memoffset", + "rustc_version", +] + +[[package]] +name = "flate2" +version = "1.0.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46303f565772937ffe1d394a4fac6f411c6013172fadde9dcdb1e147a086940e" +dependencies = [ + "crc32fast", + "miniz_oxide", +] + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "foreign-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +dependencies = [ + "foreign-types-shared 0.1.1", +] + +[[package]] +name = "foreign-types" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d737d9aa519fb7b749cbc3b962edcf310a8dd1f4b67c91c4f83975dbdd17d965" +dependencies = [ + "foreign-types-macros", + "foreign-types-shared 0.3.1", +] + +[[package]] +name = "foreign-types-macros" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a5c6c585bc94aaf2c7b51dd4c2ba22680844aba4c687be581871a6f518c5742" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.57", +] + +[[package]] +name = "foreign-types-shared" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" + +[[package]] +name = "foreign-types-shared" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa9a19cbb55df58761df49b23516a86d432839add4af60fc256da840f66ed35b" + +[[package]] +name = "form_urlencoded" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "futf" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df420e2e84819663797d1ec6544b13c5be84629e7bb00dc960d6917db2987843" +dependencies = [ + "mac", + "new_debug_unreachable", +] + +[[package]] +name = "futures-channel" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78" +dependencies = [ + "futures-core", +] + +[[package]] +name = "futures-core" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" + +[[package]] +name = "futures-executor" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a576fc72ae164fca6b9db127eaa9a9dda0d61316034f33a0a0d4eda41f02b01d" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1" + +[[package]] +name = "futures-macro" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.57", +] + +[[package]] +name = "futures-sink" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5" + +[[package]] +name = "futures-task" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" + +[[package]] +name = "futures-util" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" +dependencies = [ + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "pin-utils", + "slab", +] + +[[package]] +name = "fxhash" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c31b6d751ae2c7f11320402d34e41349dd1016f8d5d45e48c4312bc8625af50c" +dependencies = [ + "byteorder", +] + +[[package]] +name = "gdk" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f5ba081bdef3b75ebcdbfc953699ed2d7417d6bd853347a42a37d76406a33646" +dependencies = [ + "cairo-rs", + "gdk-pixbuf", + "gdk-sys", + "gio", + "glib", + "libc", + "pango", +] + +[[package]] +name = "gdk-pixbuf" +version = "0.18.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50e1f5f1b0bfb830d6ccc8066d18db35c487b1b2b1e8589b5dfe9f07e8defaec" +dependencies = [ + "gdk-pixbuf-sys", + "gio", + "glib", + "libc", + "once_cell", +] + +[[package]] +name = "gdk-pixbuf-sys" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9839ea644ed9c97a34d129ad56d38a25e6756f99f3a88e15cd39c20629caf7" +dependencies = [ + "gio-sys", + "glib-sys", + "gobject-sys", + "libc", + "system-deps", +] + +[[package]] +name = "gdk-sys" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31ff856cb3386dae1703a920f803abafcc580e9b5f711ca62ed1620c25b51ff2" +dependencies = [ + "cairo-sys-rs", + "gdk-pixbuf-sys", + "gio-sys", + "glib-sys", + "gobject-sys", + "libc", + "pango-sys", + "pkg-config", + "system-deps", +] + +[[package]] +name = "gdkwayland-sys" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a90fbf5c033c65d93792192a49a8efb5bb1e640c419682a58bb96f5ae77f3d4a" +dependencies = [ + "gdk-sys", + "glib-sys", + "gobject-sys", + "libc", + "pkg-config", + "system-deps", +] + +[[package]] +name = "gdkx11" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db2ea8a4909d530f79921290389cbd7c34cb9d623bfe970eaae65ca5f9cd9cce" +dependencies = [ + "gdk", + "gdkx11-sys", + "gio", + "glib", + "libc", + "x11", +] + +[[package]] +name = "gdkx11-sys" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fee8f00f4ee46cad2939b8990f5c70c94ff882c3028f3cc5abf950fa4ab53043" +dependencies = [ + "gdk-sys", + "glib-sys", + "libc", + "system-deps", + "x11", +] + +[[package]] +name = "generator" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5cc16584ff22b460a382b7feec54b23d2908d858152e5739a120b949293bd74e" +dependencies = [ + "cc", + "libc", + "log", + "rustversion", + "windows 0.48.0", +] + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "getrandom" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" +dependencies = [ + "cfg-if", + "libc", + "wasi 0.9.0+wasi-snapshot-preview1", +] + +[[package]] +name = "getrandom" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "190092ea657667030ac6a35e305e62fc4dd69fd98ac98631e5d3a2b1575a12b5" +dependencies = [ + "cfg-if", + "libc", + "wasi 0.11.0+wasi-snapshot-preview1", +] + +[[package]] +name = "ghash" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0d8a4362ccb29cb0b265253fb0a2728f592895ee6854fd9bc13f2ffda266ff1" +dependencies = [ + "opaque-debug", + "polyval", +] + +[[package]] +name = "gimli" +version = "0.28.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" + +[[package]] +name = "gio" +version = "0.18.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4fc8f532f87b79cbc51a79748f16a6828fb784be93145a322fa14d06d354c73" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-util", + "gio-sys", + "glib", + "libc", + "once_cell", + "pin-project-lite", + "smallvec", + "thiserror", +] + +[[package]] +name = "gio-sys" +version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37566df850baf5e4cb0dfb78af2e4b9898d817ed9263d1090a2df958c64737d2" +dependencies = [ + "glib-sys", + "gobject-sys", + "libc", + "system-deps", + "winapi", +] + +[[package]] +name = "glib" +version = "0.18.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "233daaf6e83ae6a12a52055f568f9d7cf4671dabb78ff9560ab6da230ce00ee5" +dependencies = [ + "bitflags 2.5.0", + "futures-channel", + "futures-core", + "futures-executor", + "futures-task", + "futures-util", + "gio-sys", + "glib-macros", + "glib-sys", + "gobject-sys", + "libc", + "memchr", + "once_cell", + "smallvec", + "thiserror", +] + +[[package]] +name = "glib-macros" +version = "0.18.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bb0228f477c0900c880fd78c8759b95c7636dbd7842707f49e132378aa2acdc" +dependencies = [ + "heck 0.4.1", + "proc-macro-crate 2.0.2", + "proc-macro-error", + "proc-macro2", + "quote", + "syn 2.0.57", +] + +[[package]] +name = "glib-sys" +version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "063ce2eb6a8d0ea93d2bf8ba1957e78dbab6be1c2220dd3daca57d5a9d869898" +dependencies = [ + "libc", + "system-deps", +] + +[[package]] +name = "glob" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" + +[[package]] +name = "gobject-sys" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0850127b514d1c4a4654ead6dedadb18198999985908e6ffe4436f53c785ce44" +dependencies = [ + "glib-sys", + "libc", + "system-deps", +] + +[[package]] +name = "gtk" +version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93c4f5e0e20b60e10631a5f06da7fe3dda744b05ad0ea71fee2f47adf865890c" +dependencies = [ + "atk", + "cairo-rs", + "field-offset", + "futures-channel", + "gdk", + "gdk-pixbuf", + "gio", + "glib", + "gtk-sys", + "gtk3-macros", + "libc", + "pango", + "pkg-config", +] + +[[package]] +name = "gtk-sys" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "771437bf1de2c1c0b496c11505bdf748e26066bbe942dfc8f614c9460f6d7722" +dependencies = [ + "atk-sys", + "cairo-sys-rs", + "gdk-pixbuf-sys", + "gdk-sys", + "gio-sys", + "glib-sys", + "gobject-sys", + "libc", + "pango-sys", + "system-deps", +] + +[[package]] +name = "gtk3-macros" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6063efb63db582968fb7df72e1ae68aa6360dcfb0a75143f34fc7d616bad75e" +dependencies = [ + "proc-macro-crate 1.3.1", + "proc-macro-error", + "proc-macro2", + "quote", + "syn 2.0.57", +] + +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" + +[[package]] +name = "hashbrown" +version = "0.14.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" + +[[package]] +name = "heck" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "hermit-abi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "html5ever" +version = "0.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bea68cab48b8459f17cf1c944c67ddc572d272d9f2b274140f223ecb1da4a3b7" +dependencies = [ + "log", + "mac", + "markup5ever", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "http" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21b9ddb458710bc376481b842f5da65cdf31522de232c1ca8146abce2a358258" +dependencies = [ + "bytes", + "fnv", + "itoa 1.0.11", +] + +[[package]] +name = "http-body" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1cac85db508abc24a2e48553ba12a996e87244a0395ce011e62b37158745d643" +dependencies = [ + "bytes", + "http", +] + +[[package]] +name = "http-body-util" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0475f8b2ac86659c21b64320d5d653f9efe42acd2a4e560073ec61a155a34f1d" +dependencies = [ + "bytes", + "futures-core", + "http", + "http-body", + "pin-project-lite", +] + +[[package]] +name = "http-range" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21dec9db110f5f872ed9699c3ecf50cf16f423502706ba5c72462e28d3157573" + +[[package]] +name = "httparse" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" + +[[package]] +name = "hyper" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "186548d73ac615b32a73aafe38fb4f56c0d340e110e5a200bcadbaf2e199263a" +dependencies = [ + "bytes", + "futures-channel", + "futures-util", + "http", + "http-body", + "httparse", + "itoa 1.0.11", + "pin-project-lite", + "smallvec", + "tokio", + "want", +] + +[[package]] +name = "hyper-rustls" +version = "0.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0bea761b46ae2b24eb4aef630d8d1c398157b6fc29e6350ecf090a0b70c952c" +dependencies = [ + "futures-util", + "http", + "hyper", + "hyper-util", + "rustls", + "rustls-pki-types", + "tokio", + "tokio-rustls", + "tower-service", +] + +[[package]] +name = "hyper-tls" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0" +dependencies = [ + "bytes", + "http-body-util", + "hyper", + "hyper-util", + "native-tls", + "tokio", + "tokio-native-tls", + "tower-service", +] + +[[package]] +name = "hyper-util" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca38ef113da30126bbff9cd1705f9273e15d45498615d138b0c20279ac7a76aa" +dependencies = [ + "bytes", + "futures-channel", + "futures-util", + "http", + "http-body", + "hyper", + "pin-project-lite", + "socket2", + "tokio", + "tower", + "tower-service", + "tracing", +] + +[[package]] +name = "iana-time-zone" +version = "0.1.60" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7ffbb5a1b541ea2561f8c41c087286cc091e21e556a4f09a8f6cbf17b69b141" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "wasm-bindgen", + "windows-core 0.52.0", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + +[[package]] +name = "ico" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3804960be0bb5e4edb1e1ad67afd321a9ecfd875c3e65c099468fd2717d7cae" +dependencies = [ + "byteorder", + "png", +] + +[[package]] +name = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + +[[package]] +name = "idna" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" +dependencies = [ + "unicode-bidi", + "unicode-normalization", +] + +[[package]] +name = "image" +version = "0.24.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5690139d2f55868e080017335e4b94cb7414274c74f1669c84fb5feba2c9f69d" +dependencies = [ + "bytemuck", + "byteorder", + "color_quant", + "num-traits", + "png", +] + +[[package]] +name = "indexmap" +version = "1.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" +dependencies = [ + "autocfg", + "hashbrown 0.12.3", + "serde", +] + +[[package]] +name = "indexmap" +version = "2.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26" +dependencies = [ + "equivalent", + "hashbrown 0.14.3", + "serde", +] + +[[package]] +name = "infer" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb33622da908807a06f9513c19b3c1ad50fab3e4137d82a78107d502075aa199" +dependencies = [ + "cfb", +] + +[[package]] +name = "inout" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0c10553d664a4d0bcff9f4215d0aac67a639cc68ef660840afe309b807bc9f5" +dependencies = [ + "generic-array", +] + +[[package]] +name = "insta" +version = "1.38.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3eab73f58e59ca6526037208f0e98851159ec1633cf17b6cd2e1f2c3fd5d53cc" +dependencies = [ + "console", + "lazy_static", + "linked-hash-map", + "similar", +] + +[[package]] +name = "instant" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "ipnet" +version = "2.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3" + +[[package]] +name = "itoa" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b71991ff56294aa922b450139ee08b3bfc70982c6b2c7562771375cf73542dd4" + +[[package]] +name = "itoa" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" + +[[package]] +name = "javascriptcore-rs" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca5671e9ffce8ffba57afc24070e906da7fc4b1ba66f2cabebf61bf2ea257fcc" +dependencies = [ + "bitflags 1.3.2", + "glib", + "javascriptcore-rs-sys", +] + +[[package]] +name = "javascriptcore-rs-sys" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af1be78d14ffa4b75b66df31840478fef72b51f8c2465d4ca7c194da9f7a5124" +dependencies = [ + "glib-sys", + "gobject-sys", + "libc", + "system-deps", +] + +[[package]] +name = "jni" +version = "0.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a87aa2bb7d2af34197c04845522473242e1aa17c12f4935d5856491a7fb8c97" +dependencies = [ + "cesu8", + "cfg-if", + "combine", + "jni-sys", + "log", + "thiserror", + "walkdir", + "windows-sys 0.45.0", +] + +[[package]] +name = "jni-sys" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" + +[[package]] +name = "js-sys" +version = "0.3.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "json-patch" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55ff1e1486799e3f64129f8ccad108b38290df9cd7015cd31bed17239f0789d6" +dependencies = [ + "serde", + "serde_json", + "thiserror", + "treediff", +] + +[[package]] +name = "json5" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96b0db21af676c1ce64250b5f40f3ce2cf27e4e47cb91ed91eb6fe9350b430c1" +dependencies = [ + "pest", + "pest_derive", + "serde", +] + +[[package]] +name = "keyboard-types" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b750dcadc39a09dbadd74e118f6dd6598df77fa01df0cfcdc52c28dece74528a" +dependencies = [ + "bitflags 2.5.0", + "serde", + "unicode-segmentation", +] + +[[package]] +name = "kuchikiki" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f29e4755b7b995046f510a7520c42b2fed58b77bd94d5a87a8eb43d2fd126da8" +dependencies = [ + "cssparser", + "html5ever", + "indexmap 1.9.3", + "matches", + "selectors", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "libappindicator" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03589b9607c868cc7ae54c0b2a22c8dc03dd41692d48f2d7df73615c6a95dc0a" +dependencies = [ + "glib", + "gtk", + "gtk-sys", + "libappindicator-sys", + "log", +] + +[[package]] +name = "libappindicator-sys" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e9ec52138abedcc58dc17a7c6c0c00a2bdb4f3427c7f63fa97fd0d859155caf" +dependencies = [ + "gtk-sys", + "libloading 0.7.4", + "once_cell", +] + +[[package]] +name = "libc" +version = "0.2.153" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" + +[[package]] +name = "libloading" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b67380fd3b2fbe7527a606e18729d21c6f3951633d0500574c4dc22d2d638b9f" +dependencies = [ + "cfg-if", + "winapi", +] + +[[package]] +name = "libloading" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c2a198fb6b0eada2a8df47933734e6d35d350665a33a3593d7164fa52c75c19" +dependencies = [ + "cfg-if", + "windows-targets 0.52.4", +] + +[[package]] +name = "libm" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058" + +[[package]] +name = "libredox" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" +dependencies = [ + "bitflags 2.5.0", + "libc", +] + +[[package]] +name = "libxdo" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00333b8756a3d28e78def82067a377de7fa61b24909000aeaa2b446a948d14db" +dependencies = [ + "libxdo-sys", +] + +[[package]] +name = "libxdo-sys" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db23b9e7e2b7831bbd8aac0bbeeeb7b68cbebc162b227e7052e8e55829a09212" +dependencies = [ + "libc", + "x11", +] + +[[package]] +name = "line-wrap" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd1bc4d24ad230d21fb898d1116b1801d7adfc449d42026475862ab48b11e70e" + +[[package]] +name = "linked-hash-map" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" + +[[package]] +name = "linux-raw-sys" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c" + +[[package]] +name = "lock_api" +version = "0.4.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" + +[[package]] +name = "loom" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff50ecb28bb86013e935fb6683ab1f6d3a20016f123c76fd4c27470076ac30f5" +dependencies = [ + "cfg-if", + "generator", + "scoped-tls", + "serde", + "serde_json", + "tracing", + "tracing-subscriber", +] + +[[package]] +name = "mac" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c41e0c4fef86961ac6d6f8a82609f55f31b05e4fce149ac5710e439df7619ba4" + +[[package]] +name = "malloc_buf" +version = "0.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62bb907fe88d54d8d9ce32a3cceab4218ed2f6b7d35617cafe9adf84e43919cb" +dependencies = [ + "libc", +] + +[[package]] +name = "markup5ever" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a2629bb1404f3d34c2e921f21fd34ba00b206124c81f65c50b43b6aaefeb016" +dependencies = [ + "log", + "phf 0.10.1", + "phf_codegen 0.10.0", + "string_cache", + "string_cache_codegen", + "tendril", +] + +[[package]] +name = "matchers" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558" +dependencies = [ + "regex-automata 0.1.10", +] + +[[package]] +name = "matches" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2532096657941c2fea9c289d370a250971c689d4f143798ff67113ec042024a5" + +[[package]] +name = "memchr" +version = "2.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d" + +[[package]] +name = "memoffset" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a" +dependencies = [ + "autocfg", +] + +[[package]] +name = "mime" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" + +[[package]] +name = "miniz_oxide" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d811f3e15f28568be3407c8e7fdb6514c1cda3cb30683f15b6a1a1dc4ea14a7" +dependencies = [ + "adler", + "simd-adler32", +] + +[[package]] +name = "mio" +version = "0.8.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c" +dependencies = [ + "libc", + "wasi 0.11.0+wasi-snapshot-preview1", + "windows-sys 0.48.0", +] + +[[package]] +name = "muda" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f428b4e9db3d17e2f809dfb1ff9ddfbbf16c71790d1656d10aee320877e1392f" +dependencies = [ + "cocoa", + "crossbeam-channel", + "dpi", + "gtk", + "keyboard-types", + "libxdo", + "objc", + "once_cell", + "png", + "serde", + "thiserror", + "windows-sys 0.52.0", +] + +[[package]] +name = "native-tls" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07226173c32f2926027b63cce4bcd8076c3552846cbe7925f3aaffeac0a3b92e" +dependencies = [ + "lazy_static", + "libc", + "log", + "openssl", + "openssl-probe", + "openssl-sys", + "schannel", + "security-framework", + "security-framework-sys", + "tempfile", +] + +[[package]] +name = "ndk" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "451422b7e4718271c8b5b3aadf5adedba43dc76312454b387e98fae0fc951aa0" +dependencies = [ + "bitflags 1.3.2", + "jni-sys", + "ndk-sys", + "num_enum", + "raw-window-handle 0.5.2", + "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.4.1+23.1.7779620" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3cf2aae958bd232cac5069850591667ad422d263686d75b52a065f9badeee5a3" +dependencies = [ + "jni-sys", +] + +[[package]] +name = "new_debug_unreachable" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "650eef8c711430f1a879fdd01d4745a7deea475becfb90269c06775983bbf086" + +[[package]] +name = "nodrop" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72ef4a56884ca558e5ddb05a1d1e7e1bfd9a68d9ed024c21704cc98872dae1bb" + +[[package]] +name = "nu-ansi-term" +version = "0.46.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" +dependencies = [ + "overload", + "winapi", +] + +[[package]] +name = "num-conv" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" + +[[package]] +name = "num-traits" +version = "0.2.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da0df0e5185db44f69b44f26786fe401b6c293d1907744beaa7fa62b2e5a517a" +dependencies = [ + "autocfg", + "libm", +] + +[[package]] +name = "num_cpus" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" +dependencies = [ + "hermit-abi", + "libc", +] + +[[package]] +name = "num_enum" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f646caf906c20226733ed5b1374287eb97e3c2a5c227ce668c1f2ce20ae57c9" +dependencies = [ + "num_enum_derive", +] + +[[package]] +name = "num_enum_derive" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcbff9bc912032c62bf65ef1d5aea88983b420f4f839db1e9b0c281a25c9c799" +dependencies = [ + "proc-macro-crate 1.3.1", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "objc" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "915b1b472bc21c53464d6c8461c9d3af805ba1ef837e1cac254428f4a77177b1" +dependencies = [ + "malloc_buf", + "objc_exception", +] + +[[package]] +name = "objc_exception" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad970fb455818ad6cba4c122ad012fae53ae8b4795f86378bce65e4f6bab2ca4" +dependencies = [ + "cc", +] + +[[package]] +name = "objc_id" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c92d4ddb4bd7b50d730c215ff871754d0da6b2178849f8a2a2ab69712d0c073b" +dependencies = [ + "objc", +] + +[[package]] +name = "object" +version = "0.32.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441" +dependencies = [ + "memchr", +] + +[[package]] +name = "once_cell" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" + +[[package]] +name = "opaque-debug" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" + +[[package]] +name = "openssl" +version = "0.10.64" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95a0481286a310808298130d22dd1fef0fa571e05a8f44ec801801e84b216b1f" +dependencies = [ + "bitflags 2.5.0", + "cfg-if", + "foreign-types 0.3.2", + "libc", + "once_cell", + "openssl-macros", + "openssl-sys", +] + +[[package]] +name = "openssl-macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.57", +] + +[[package]] +name = "openssl-probe" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" + +[[package]] +name = "openssl-src" +version = "300.2.3+3.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5cff92b6f71555b61bb9315f7c64da3ca43d87531622120fea0195fc761b4843" +dependencies = [ + "cc", +] + +[[package]] +name = "openssl-sys" +version = "0.9.102" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c597637d56fbc83893a35eb0dd04b2b8e7a50c91e64e9493e398b5df4fb45fa2" +dependencies = [ + "cc", + "libc", + "openssl-src", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "overload" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" + +[[package]] +name = "pango" +version = "0.18.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ca27ec1eb0457ab26f3036ea52229edbdb74dee1edd29063f5b9b010e7ebee4" +dependencies = [ + "gio", + "glib", + "libc", + "once_cell", + "pango-sys", +] + +[[package]] +name = "pango-sys" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "436737e391a843e5933d6d9aa102cb126d501e815b83601365a948a518555dc5" +dependencies = [ + "glib-sys", + "gobject-sys", + "libc", + "system-deps", +] + +[[package]] +name = "parking_lot" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c42a9226546d68acdd9c0a280d17ce19bfe27a46bf68784e4066115788d008e" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-targets 0.48.5", +] + +[[package]] +name = "percent-encoding" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" + +[[package]] +name = "pest" +version = "2.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56f8023d0fb78c8e03784ea1c7f3fa36e68a723138990b8d5a47d916b651e7a8" +dependencies = [ + "memchr", + "thiserror", + "ucd-trie", +] + +[[package]] +name = "pest_derive" +version = "2.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0d24f72393fd16ab6ac5738bc33cdb6a9aa73f8b902e8fe29cf4e67d7dd1026" +dependencies = [ + "pest", + "pest_generator", +] + +[[package]] +name = "pest_generator" +version = "2.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fdc17e2a6c7d0a492f0158d7a4bd66cc17280308bbaff78d5bef566dca35ab80" +dependencies = [ + "pest", + "pest_meta", + "proc-macro2", + "quote", + "syn 2.0.57", +] + +[[package]] +name = "pest_meta" +version = "2.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "934cd7631c050f4674352a6e835d5f6711ffbfb9345c2fc0107155ac495ae293" +dependencies = [ + "once_cell", + "pest", + "sha2", +] + +[[package]] +name = "phf" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3dfb61232e34fcb633f43d12c58f83c1df82962dcdfa565a4e866ffc17dafe12" +dependencies = [ + "phf_macros 0.8.0", + "phf_shared 0.8.0", + "proc-macro-hack", +] + +[[package]] +name = "phf" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fabbf1ead8a5bcbc20f5f8b939ee3f5b0f6f281b6ad3468b84656b658b455259" +dependencies = [ + "phf_shared 0.10.0", +] + +[[package]] +name = "phf" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ade2d8b8f33c7333b51bcf0428d37e217e9f32192ae4772156f65063b8ce03dc" +dependencies = [ + "phf_macros 0.11.2", + "phf_shared 0.11.2", +] + +[[package]] +name = "phf_codegen" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cbffee61585b0411840d3ece935cce9cb6321f01c45477d30066498cd5e1a815" +dependencies = [ + "phf_generator 0.8.0", + "phf_shared 0.8.0", +] + +[[package]] +name = "phf_codegen" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fb1c3a8bc4dd4e5cfce29b44ffc14bedd2ee294559a294e2a4d4c9e9a6a13cd" +dependencies = [ + "phf_generator 0.10.0", + "phf_shared 0.10.0", +] + +[[package]] +name = "phf_generator" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17367f0cc86f2d25802b2c26ee58a7b23faeccf78a396094c13dced0d0182526" +dependencies = [ + "phf_shared 0.8.0", + "rand 0.7.3", +] + +[[package]] +name = "phf_generator" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d5285893bb5eb82e6aaf5d59ee909a06a16737a8970984dd7746ba9283498d6" +dependencies = [ + "phf_shared 0.10.0", + "rand 0.8.5", +] + +[[package]] +name = "phf_generator" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48e4cc64c2ad9ebe670cb8fd69dd50ae301650392e81c05f9bfcb2d5bdbc24b0" +dependencies = [ + "phf_shared 0.11.2", + "rand 0.8.5", +] + +[[package]] +name = "phf_macros" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f6fde18ff429ffc8fe78e2bf7f8b7a5a5a6e2a8b58bc5a9ac69198bbda9189c" +dependencies = [ + "phf_generator 0.8.0", + "phf_shared 0.8.0", + "proc-macro-hack", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "phf_macros" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3444646e286606587e49f3bcf1679b8cef1dc2c5ecc29ddacaffc305180d464b" +dependencies = [ + "phf_generator 0.11.2", + "phf_shared 0.11.2", + "proc-macro2", + "quote", + "syn 2.0.57", +] + +[[package]] +name = "phf_shared" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c00cf8b9eafe68dde5e9eaa2cef8ee84a9336a47d566ec55ca16589633b65af7" +dependencies = [ + "siphasher", +] + +[[package]] +name = "phf_shared" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6796ad771acdc0123d2a88dc428b5e38ef24456743ddb1744ed628f9815c096" +dependencies = [ + "siphasher", +] + +[[package]] +name = "phf_shared" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90fcb95eef784c2ac79119d1dd819e162b5da872ce6f3c3abe1e8ca1c082f72b" +dependencies = [ + "siphasher", +] + +[[package]] +name = "pin-project" +version = "1.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6bf43b791c5b9e34c3d182969b4abb522f9343702850a2e57f460d00d09b4b3" +dependencies = [ + "pin-project-internal", +] + +[[package]] +name = "pin-project-internal" +version = "1.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.57", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "pkg-config" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" + +[[package]] +name = "plist" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9d34169e64b3c7a80c8621a48adaf44e0cf62c78a9b25dd9dd35f1881a17cf9" +dependencies = [ + "base64 0.21.7", + "indexmap 2.2.6", + "line-wrap", + "quick-xml", + "serde", + "time", +] + +[[package]] +name = "png" +version = "0.17.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06e4b0d3d1312775e782c86c91a111aa1f910cbb65e1337f9975b5f9a554b5e1" +dependencies = [ + "bitflags 1.3.2", + "crc32fast", + "fdeflate", + "flate2", + "miniz_oxide", +] + +[[package]] +name = "polyval" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d1fe60d06143b2430aa532c94cfe9e29783047f06c0d7fd359a9a51b729fa25" +dependencies = [ + "cfg-if", + "cpufeatures", + "opaque-debug", + "universal-hash", +] + +[[package]] +name = "powerfmt" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" + +[[package]] +name = "ppv-lite86" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" + +[[package]] +name = "precomputed-hash" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c" + +[[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 = "b00f26d3400549137f92511a46ac1cd8ce37cb5598a96d382381458b992a5d24" +dependencies = [ + "toml_datetime", + "toml_edit 0.20.2", +] + +[[package]] +name = "proc-macro-error" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" +dependencies = [ + "proc-macro-error-attr", + "proc-macro2", + "quote", + "syn 1.0.109", + "version_check", +] + +[[package]] +name = "proc-macro-error-attr" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" +dependencies = [ + "proc-macro2", + "quote", + "version_check", +] + +[[package]] +name = "proc-macro-hack" +version = "0.5.20+deprecated" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc375e1527247fe1a97d8b7156678dfe7c1af2fc075c9a4db3690ecd2a148068" + +[[package]] +name = "proc-macro2" +version = "1.0.79" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e835ff2298f5721608eb1a980ecaee1aef2c132bf95ecc026a11b7bf3c01c02e" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "proptest" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31b476131c3c86cb68032fdc5cb6d5a1045e3e42d96b69fa599fd77701e1f5bf" +dependencies = [ + "bit-set", + "bit-vec", + "bitflags 2.5.0", + "lazy_static", + "num-traits", + "rand 0.8.5", + "rand_chacha 0.3.1", + "rand_xorshift", + "regex-syntax 0.8.3", + "rusty-fork", + "tempfile", + "unarray", +] + +[[package]] +name = "quick-error" +version = "1.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" + +[[package]] +name = "quick-xml" +version = "0.31.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1004a344b30a54e2ee58d66a71b32d2db2feb0a31f9a2d302bf0536f15de2a33" +dependencies = [ + "memchr", +] + +[[package]] +name = "quickcheck" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "588f6378e4dd99458b60ec275b4477add41ce4fa9f64dcba6f15adccb19b50d6" +dependencies = [ + "env_logger", + "log", + "rand 0.8.5", +] + +[[package]] +name = "quickcheck_macros" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b22a693222d716a9587786f37ac3f6b4faedb5b80c23914e7303ff5a1d8016e9" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "quote" +version = "1.0.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" +dependencies = [ + "getrandom 0.1.16", + "libc", + "rand_chacha 0.2.2", + "rand_core 0.5.1", + "rand_hc", + "rand_pcg", +] + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha 0.3.1", + "rand_core 0.6.4", +] + +[[package]] +name = "rand_chacha" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" +dependencies = [ + "ppv-lite86", + "rand_core 0.5.1", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core 0.6.4", +] + +[[package]] +name = "rand_core" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" +dependencies = [ + "getrandom 0.1.16", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom 0.2.12", +] + +[[package]] +name = "rand_hc" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" +dependencies = [ + "rand_core 0.5.1", +] + +[[package]] +name = "rand_pcg" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16abd0c1b639e9eb4d7c50c0b8100b0d0f849be2349829c740fe8e6eb4816429" +dependencies = [ + "rand_core 0.5.1", +] + +[[package]] +name = "rand_xorshift" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d25bf25ec5ae4a3f1b92f929810509a2f53d7dca2f50b794ff57e3face536c8f" +dependencies = [ + "rand_core 0.6.4", +] + +[[package]] +name = "raw-window-handle" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2ff9a1f06a88b01621b7ae906ef0211290d1c8a168a15542486a8f61c0833b9" + +[[package]] +name = "raw-window-handle" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42a9830a0e1b9fb145ebb365b8bc4ccd75f290f98c0247deafbbe2c75cefb544" + +[[package]] +name = "redox_syscall" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" +dependencies = [ + "bitflags 1.3.2", +] + +[[package]] +name = "redox_users" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd283d9651eeda4b2a83a43c1c91b266c40fd76ecd39a50a8c630ae69dc72891" +dependencies = [ + "getrandom 0.2.12", + "libredox", + "thiserror", +] + +[[package]] +name = "regex" +version = "1.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c117dbdfde9c8308975b6a18d71f3f385c89461f7b3fb054288ecf2a2058ba4c" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata 0.4.6", + "regex-syntax 0.8.3", +] + +[[package]] +name = "regex-automata" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" +dependencies = [ + "regex-syntax 0.6.29", +] + +[[package]] +name = "regex-automata" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86b83b8b9847f9bf95ef68afb0b8e6cdb80f498442f5179a29fad448fcc1eaea" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax 0.8.3", +] + +[[package]] +name = "regex-syntax" +version = "0.6.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" + +[[package]] +name = "regex-syntax" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56" + +[[package]] +name = "reqwest" +version = "0.12.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d66674f2b6fb864665eea7a3c1ac4e3dfacd2fda83cf6f935a612e01b0e3338" +dependencies = [ + "base64 0.21.7", + "bytes", + "futures-core", + "futures-util", + "http", + "http-body", + "http-body-util", + "hyper", + "hyper-rustls", + "hyper-tls", + "hyper-util", + "ipnet", + "js-sys", + "log", + "mime", + "native-tls", + "once_cell", + "percent-encoding", + "pin-project-lite", + "rustls", + "rustls-pemfile", + "rustls-pki-types", + "serde", + "serde_json", + "serde_urlencoded", + "sync_wrapper", + "tokio", + "tokio-native-tls", + "tokio-rustls", + "tokio-util", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "wasm-streams", + "web-sys", + "webpki-roots", + "winreg 0.50.0", +] + +[[package]] +name = "restart" +version = "0.1.0" +dependencies = [ + "tauri", + "tempfile", +] + +[[package]] +name = "ring" +version = "0.17.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c17fa4cb658e3583423e915b9f3acc01cceaee1860e33d59ebae66adc3a2dc0d" +dependencies = [ + "cc", + "cfg-if", + "getrandom 0.2.12", + "libc", + "spin", + "untrusted", + "windows-sys 0.52.0", +] + +[[package]] +name = "rustc-demangle" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" + +[[package]] +name = "rustc_version" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" +dependencies = [ + "semver", +] + +[[package]] +name = "rustix" +version = "0.38.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65e04861e65f21776e67888bfbea442b3642beaa0138fdb1dd7a84a52dffdb89" +dependencies = [ + "bitflags 2.5.0", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.52.0", +] + +[[package]] +name = "rustls" +version = "0.22.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99008d7ad0bbbea527ec27bddbc0e432c5b87d8175178cee68d2eec9c4a1813c" +dependencies = [ + "log", + "ring", + "rustls-pki-types", + "rustls-webpki", + "subtle", + "zeroize", +] + +[[package]] +name = "rustls-pemfile" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c74cae0a4cf6ccbbf5f359f08efdf8ee7e1dc532573bf0db71968cb56b1448c" +dependencies = [ + "base64 0.21.7", +] + +[[package]] +name = "rustls-pki-types" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ecd36cc4259e3e4514335c4a138c6b43171a8d61d8f5c9348f9fc7529416f247" + +[[package]] +name = "rustls-webpki" +version = "0.102.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "faaa0a62740bedb9b2ef5afa303da42764c012f743917351dc9a237ea1663610" +dependencies = [ + "ring", + "rustls-pki-types", + "untrusted", +] + +[[package]] +name = "rustversion" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ffc183a10b4478d04cbbbfc96d0873219d962dd5accaff2ffbd4ceb7df837f4" + +[[package]] +name = "rusty-fork" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb3dcc6e454c328bb824492db107ab7c0ae8fcffe4ad210136ef014458c1bc4f" +dependencies = [ + "fnv", + "quick-error", + "tempfile", + "wait-timeout", +] + +[[package]] +name = "ryu" +version = "1.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e86697c916019a8588c99b5fac3cead74ec0b4b819707a682fd4d23fa0ce1ba1" + +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "schannel" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fbc91545643bcf3a0bbb6569265615222618bdf33ce4ffbbd13c4bbd4c093534" +dependencies = [ + "windows-sys 0.52.0", +] + +[[package]] +name = "schemars" +version = "0.8.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45a28f4c49489add4ce10783f7911893516f15afe45d015608d41faca6bc4d29" +dependencies = [ + "dyn-clone", + "indexmap 1.9.3", + "schemars_derive", + "serde", + "serde_json", + "url", +] + +[[package]] +name = "schemars_derive" +version = "0.8.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c767fd6fa65d9ccf9cf026122c1b555f2ef9a4f0cea69da4d7dbc3e258d30967" +dependencies = [ + "proc-macro2", + "quote", + "serde_derive_internals", + "syn 1.0.109", +] + +[[package]] +name = "scoped-tls" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294" + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "security-framework" +version = "2.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "770452e37cad93e0a50d5abc3990d2bc351c36d0328f86cefec2f2fb206eaef6" +dependencies = [ + "bitflags 1.3.2", + "core-foundation", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41f3cc463c0ef97e11c3461a9d3787412d30e8e7eb907c79180c4a57bf7c04ef" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "selectors" +version = "0.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df320f1889ac4ba6bc0cdc9c9af7af4bd64bb927bccdf32d81140dc1f9be12fe" +dependencies = [ + "bitflags 1.3.2", + "cssparser", + "derive_more", + "fxhash", + "log", + "matches", + "phf 0.8.0", + "phf_codegen 0.8.0", + "precomputed-hash", + "servo_arc", + "smallvec", + "thin-slice", +] + +[[package]] +name = "semver" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92d43fe69e652f3df9bdc2b85b2854a0825b86e4fb76bc44d945137d053639ca" +dependencies = [ + "serde", +] + +[[package]] +name = "serde" +version = "1.0.197" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fb1c873e1b9b056a4dc4c0c198b24c3ffa059243875552b2bd0933b1aee4ce2" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.197" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7eb0b34b42edc17f6b7cac84a52a1c5f0e1bb2227e997ca9011ea3dd34e8610b" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.57", +] + +[[package]] +name = "serde_derive_internals" +version = "0.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85bf8229e7920a9f636479437026331ce11aa132b4dde37d121944a44d6e5f3c" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "serde_json" +version = "1.0.115" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12dc5c46daa8e9fdf4f5e71b6cf9a53f2487da0e86e55808e2d35539666497dd" +dependencies = [ + "itoa 1.0.11", + "ryu", + "serde", +] + +[[package]] +name = "serde_repr" +version = "0.1.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b2e6b945e9d3df726b65d6ee24060aff8e3533d431f677a9695db04eff9dfdb" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.57", +] + +[[package]] +name = "serde_spanned" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb3622f419d1296904700073ea6cc23ad690adbd66f13ea683df73298736f0c1" +dependencies = [ + "serde", +] + +[[package]] +name = "serde_urlencoded" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" +dependencies = [ + "form_urlencoded", + "itoa 1.0.11", + "ryu", + "serde", +] + +[[package]] +name = "serde_with" +version = "3.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee80b0e361bbf88fd2f6e242ccd19cfda072cb0faa6ae694ecee08199938569a" +dependencies = [ + "base64 0.21.7", + "chrono", + "hex", + "indexmap 1.9.3", + "indexmap 2.2.6", + "serde", + "serde_derive", + "serde_json", + "serde_with_macros", + "time", +] + +[[package]] +name = "serde_with_macros" +version = "3.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6561dc161a9224638a31d876ccdfefbc1df91d3f3a8342eddb35f055d48c7655" +dependencies = [ + "darling", + "proc-macro2", + "quote", + "syn 2.0.57", +] + +[[package]] +name = "serialize-to-javascript" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9823f2d3b6a81d98228151fdeaf848206a7855a7a042bbf9bf870449a66cafb" +dependencies = [ + "serde", + "serde_json", + "serialize-to-javascript-impl", +] + +[[package]] +name = "serialize-to-javascript-impl" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74064874e9f6a15f04c1f3cb627902d0e6b410abbf36668afa873c61889f1763" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "servo_arc" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d98238b800e0d1576d8b6e3de32827c2d74bee68bb97748dcf5071fb53965432" +dependencies = [ + "nodrop", + "stable_deref_trait", +] + +[[package]] +name = "sha2" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "sharded-slab" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" +dependencies = [ + "lazy_static", +] + +[[package]] +name = "signal-hook-registry" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1" +dependencies = [ + "libc", +] + +[[package]] +name = "simd-adler32" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" + +[[package]] +name = "similar" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa42c91313f1d05da9b26f267f931cf178d4aba455b4c4622dd7355eb80c6640" + +[[package]] +name = "siphasher" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d" + +[[package]] +name = "slab" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" +dependencies = [ + "autocfg", +] + +[[package]] +name = "smallvec" +version = "1.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" + +[[package]] +name = "socket2" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05ffd9c0a93b7543e062e759284fcf5f5e3b098501104bfbdde4d404db792871" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "softbuffer" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071916a85d1db274b4ed57af3a14afb66bd836ae7f82ebb6f1fd3455107830d9" +dependencies = [ + "bytemuck", + "cfg_aliases 0.2.0", + "cocoa", + "core-graphics", + "foreign-types 0.5.0", + "js-sys", + "log", + "objc", + "raw-window-handle 0.6.0", + "redox_syscall", + "wasm-bindgen", + "wayland-sys", + "web-sys", + "windows-sys 0.52.0", +] + +[[package]] +name = "soup3" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "471f924a40f31251afc77450e781cb26d55c0b650842efafc9c6cbd2f7cc4f9f" +dependencies = [ + "futures-channel", + "gio", + "glib", + "libc", + "soup3-sys", +] + +[[package]] +name = "soup3-sys" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ebe8950a680a12f24f15ebe1bf70db7af98ad242d9db43596ad3108aab86c27" +dependencies = [ + "gio-sys", + "glib-sys", + "gobject-sys", + "libc", + "system-deps", +] + +[[package]] +name = "spin" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" + +[[package]] +name = "stable_deref_trait" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" + +[[package]] +name = "state" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b8c4a4445d81357df8b1a650d0d0d6fbbbfe99d064aa5e02f3e4022061476d8" +dependencies = [ + "loom", +] + +[[package]] +name = "string_cache" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f91138e76242f575eb1d3b38b4f1362f10d3a43f47d182a5b359af488a02293b" +dependencies = [ + "new_debug_unreachable", + "once_cell", + "parking_lot", + "phf_shared 0.10.0", + "precomputed-hash", + "serde", +] + +[[package]] +name = "string_cache_codegen" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6bb30289b722be4ff74a408c3cc27edeaad656e06cb1fe8fa9231fa59c728988" +dependencies = [ + "phf_generator 0.10.0", + "phf_shared 0.10.0", + "proc-macro2", + "quote", +] + +[[package]] +name = "strsim" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" + +[[package]] +name = "subtle" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" + +[[package]] +name = "swift-rs" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bbdb58577b6301f8d17ae2561f32002a5bae056d444e0f69e611e504a276204" +dependencies = [ + "base64 0.21.7", + "serde", + "serde_json", +] + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.57" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11a6ae1e52eb25aab8f3fb9fca13be982a373b8f1157ca14b897a825ba4a2d35" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "sync_wrapper" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" + +[[package]] +name = "system-deps" +version = "6.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3e535eb8dded36d55ec13eddacd30dec501792ff23a0b1682c38601b8cf2349" +dependencies = [ + "cfg-expr", + "heck 0.5.0", + "pkg-config", + "toml 0.8.2", + "version-compare", +] + +[[package]] +name = "tao" +version = "0.27.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd5b6ec2c43abd15155f040c765001098f50f425414b679225d471a1cd782753" +dependencies = [ + "bitflags 1.3.2", + "cocoa", + "core-foundation", + "core-graphics", + "crossbeam-channel", + "dispatch", + "dlopen2", + "dpi", + "gdkwayland-sys", + "gdkx11-sys", + "gtk", + "instant", + "jni", + "lazy_static", + "libc", + "log", + "ndk", + "ndk-context", + "ndk-sys", + "objc", + "once_cell", + "parking_lot", + "raw-window-handle 0.6.0", + "scopeguard", + "tao-macros", + "unicode-segmentation", + "url", + "windows 0.54.0", + "windows-version", + "x11-dl", +] + +[[package]] +name = "tao-macros" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec114582505d158b669b136e6851f85840c109819d77c42bb7c0709f727d18c2" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "target-lexicon" +version = "0.12.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1fc403891a21bcfb7c37834ba66a547a8f402146eba7265b5a6d88059c9ff2f" + +[[package]] +name = "tauri" +version = "2.0.0-beta.14" +dependencies = [ + "anyhow", + "bytes", + "cargo_toml", + "cocoa", + "data-url", + "dirs-next", + "embed_plist", + "futures-util", + "getrandom 0.2.12", + "glob", + "gtk", + "heck 0.4.1", + "http", + "http-range", + "image", + "jni", + "libc", + "log", + "mime", + "muda", + "objc", + "percent-encoding", + "proptest", + "quickcheck", + "quickcheck_macros", + "raw-window-handle 0.6.0", + "reqwest", + "serde", + "serde_json", + "serde_repr", + "serialize-to-javascript", + "state", + "swift-rs", + "tauri", + "tauri-build", + "tauri-macros", + "tauri-runtime", + "tauri-runtime-wry", + "tauri-utils", + "thiserror", + "tokio", + "tracing", + "tray-icon", + "url", + "urlpattern", + "uuid", + "webkit2gtk", + "webview2-com", + "window-vibrancy", + "windows 0.54.0", +] + +[[package]] +name = "tauri-build" +version = "2.0.0-beta.11" +dependencies = [ + "anyhow", + "cargo_toml", + "dirs-next", + "glob", + "heck 0.4.1", + "json-patch", + "quote", + "schemars", + "semver", + "serde", + "serde_json", + "tauri-codegen", + "tauri-utils", + "tauri-winres", + "toml 0.8.2", + "walkdir", +] + +[[package]] +name = "tauri-codegen" +version = "2.0.0-beta.11" +dependencies = [ + "base64 0.22.0", + "brotli", + "ico", + "json-patch", + "plist", + "png", + "proc-macro2", + "quote", + "regex", + "semver", + "serde", + "serde_json", + "sha2", + "syn 2.0.57", + "tauri-utils", + "thiserror", + "time", + "url", + "uuid", + "walkdir", +] + +[[package]] +name = "tauri-config-schema" +version = "0.0.0" +dependencies = [ + "schemars", + "serde", + "serde_json", + "tauri-utils", + "url", +] + +[[package]] +name = "tauri-macros" +version = "2.0.0-beta.11" +dependencies = [ + "heck 0.4.1", + "proc-macro2", + "quote", + "syn 2.0.57", + "tauri-codegen", + "tauri-utils", +] + +[[package]] +name = "tauri-plugin" +version = "2.0.0-beta.11" +dependencies = [ + "anyhow", + "glob", + "plist", + "schemars", + "serde", + "serde_json", + "tauri-utils", + "toml 0.8.2", + "walkdir", +] + +[[package]] +name = "tauri-runtime" +version = "2.0.0-beta.11" +dependencies = [ + "dpi", + "gtk", + "http", + "jni", + "raw-window-handle 0.6.0", + "serde", + "serde_json", + "tauri-utils", + "thiserror", + "url", + "windows 0.54.0", +] + +[[package]] +name = "tauri-runtime-wry" +version = "2.0.0-beta.11" +dependencies = [ + "cocoa", + "gtk", + "http", + "jni", + "log", + "percent-encoding", + "raw-window-handle 0.6.0", + "softbuffer", + "tao", + "tauri-runtime", + "tauri-utils", + "tracing", + "url", + "webkit2gtk", + "webview2-com", + "windows 0.54.0", + "wry", +] + +[[package]] +name = "tauri-utils" +version = "2.0.0-beta.11" +dependencies = [ + "aes-gcm", + "brotli", + "cargo_metadata", + "ctor", + "dunce", + "getrandom 0.2.12", + "glob", + "heck 0.4.1", + "html5ever", + "infer", + "json-patch", + "json5", + "kuchikiki", + "log", + "memchr", + "phf 0.11.2", + "proc-macro2", + "quote", + "regex", + "schemars", + "semver", + "serde", + "serde_json", + "serde_with", + "serialize-to-javascript", + "swift-rs", + "thiserror", + "toml 0.8.2", + "url", + "urlpattern", + "walkdir", +] + +[[package]] +name = "tauri-winres" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5993dc129e544393574288923d1ec447c857f3f644187f4fbf7d9a875fbfc4fb" +dependencies = [ + "embed-resource", + "toml 0.7.8", +] + +[[package]] +name = "tempfile" +version = "3.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85b77fafb263dd9d05cbeac119526425676db3784113aa9295c88498cbf8bff1" +dependencies = [ + "cfg-if", + "fastrand", + "rustix", + "windows-sys 0.52.0", +] + +[[package]] +name = "tendril" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d24a120c5fc464a3458240ee02c299ebcb9d67b5249c8848b09d639dca8d7bb0" +dependencies = [ + "futf", + "mac", + "utf-8", +] + +[[package]] +name = "thin-slice" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8eaa81235c7058867fa8c0e7314f33dcce9c215f535d1913822a2b3f5e289f3c" + +[[package]] +name = "thiserror" +version = "1.0.58" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03468839009160513471e86a034bb2c5c0e4baae3b43f79ffc55c4a5427b3297" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.58" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c61f3ba182994efc43764a46c018c347bc492c79f024e705f46567b418f6d4f7" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.57", +] + +[[package]] +name = "thread_local" +version = "1.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c" +dependencies = [ + "cfg-if", + "once_cell", +] + +[[package]] +name = "time" +version = "0.3.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8248b6521bb14bc45b4067159b9b6ad792e2d6d754d6c41fb50e29fefe38749" +dependencies = [ + "deranged", + "itoa 1.0.11", + "num-conv", + "powerfmt", + "serde", + "time-core", + "time-macros", +] + +[[package]] +name = "time-core" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" + +[[package]] +name = "time-macros" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ba3a3ef41e6672a2f0f001392bb5dcd3ff0a9992d618ca761a11c3121547774" +dependencies = [ + "num-conv", + "time-core", +] + +[[package]] +name = "tinyvec" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + +[[package]] +name = "tokio" +version = "1.37.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1adbebffeca75fcfd058afa480fb6c0b81e165a0323f9c9d39c9697e37c46787" +dependencies = [ + "backtrace", + "bytes", + "libc", + "mio", + "num_cpus", + "parking_lot", + "pin-project-lite", + "signal-hook-registry", + "socket2", + "tokio-macros", + "windows-sys 0.48.0", +] + +[[package]] +name = "tokio-macros" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.57", +] + +[[package]] +name = "tokio-native-tls" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" +dependencies = [ + "native-tls", + "tokio", +] + +[[package]] +name = "tokio-rustls" +version = "0.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "775e0c0f0adb3a2f22a00c4745d728b479985fc15ee7ca6a2608388c5569860f" +dependencies = [ + "rustls", + "rustls-pki-types", + "tokio", +] + +[[package]] +name = "tokio-util" +version = "0.7.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5419f34732d9eb6ee4c3578b7989078579b7f039cbbb9ca2c4da015749371e15" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "pin-project-lite", + "tokio", + "tracing", +] + +[[package]] +name = "toml" +version = "0.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd79e69d3b627db300ff956027cc6c3798cef26d22526befdfcd12feeb6d2257" +dependencies = [ + "serde", + "serde_spanned", + "toml_datetime", + "toml_edit 0.19.15", +] + +[[package]] +name = "toml" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "185d8ab0dfbb35cf1399a6344d8484209c088f75f8f68230da55d48d95d43e3d" +dependencies = [ + "serde", + "serde_spanned", + "toml_datetime", + "toml_edit 0.20.2", +] + +[[package]] +name = "toml_datetime" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7cda73e2f1397b1262d6dfdcef8aafae14d1de7748d66822d3bfeeb6d03e5e4b" +dependencies = [ + "serde", +] + +[[package]] +name = "toml_edit" +version = "0.19.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" +dependencies = [ + "indexmap 2.2.6", + "serde", + "serde_spanned", + "toml_datetime", + "winnow", +] + +[[package]] +name = "toml_edit" +version = "0.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "396e4d48bbb2b7554c944bde63101b5ae446cff6ec4a24227428f15eb72ef338" +dependencies = [ + "indexmap 2.2.6", + "serde", + "serde_spanned", + "toml_datetime", + "winnow", +] + +[[package]] +name = "tower" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c" +dependencies = [ + "futures-core", + "futures-util", + "pin-project", + "pin-project-lite", + "tokio", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "tower-layer" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c20c8dbed6283a09604c3e69b4b7eeb54e298b8a600d4d5ecb5ad39de609f1d0" + +[[package]] +name = "tower-service" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" + +[[package]] +name = "tracing" +version = "0.1.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" +dependencies = [ + "log", + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.57", +] + +[[package]] +name = "tracing-core" +version = "0.1.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" +dependencies = [ + "once_cell", + "valuable", +] + +[[package]] +name = "tracing-log" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" +dependencies = [ + "log", + "once_cell", + "tracing-core", +] + +[[package]] +name = "tracing-subscriber" +version = "0.3.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b" +dependencies = [ + "matchers", + "nu-ansi-term", + "once_cell", + "regex", + "sharded-slab", + "smallvec", + "thread_local", + "tracing", + "tracing-core", + "tracing-log", +] + +[[package]] +name = "tray-icon" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8713f74e697917aa794800289e15bce534fc91450312ab2d3edf5b8907f7301a" +dependencies = [ + "cocoa", + "core-graphics", + "crossbeam-channel", + "dirs-next", + "libappindicator", + "muda", + "objc", + "once_cell", + "png", + "serde", + "thiserror", + "windows-sys 0.52.0", +] + +[[package]] +name = "treediff" +version = "4.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d127780145176e2b5d16611cc25a900150e86e9fd79d3bde6ff3a37359c9cb5" +dependencies = [ + "serde_json", +] + +[[package]] +name = "try-lock" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" + +[[package]] +name = "typenum" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" + +[[package]] +name = "ucd-trie" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed646292ffc8188ef8ea4d1e0e0150fb15a5c2e12ad9b8fc191ae7a8a7f3c4b9" + +[[package]] +name = "unarray" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eaea85b334db583fe3274d12b4cd1880032beab409c0d774be044d4480ab9a94" + +[[package]] +name = "unic-char-property" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8c57a407d9b6fa02b4795eb81c5b6652060a15a7903ea981f3d723e6c0be221" +dependencies = [ + "unic-char-range", +] + +[[package]] +name = "unic-char-range" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0398022d5f700414f6b899e10b8348231abf9173fa93144cbc1a43b9793c1fbc" + +[[package]] +name = "unic-common" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80d7ff825a6a654ee85a63e80f92f054f904f21e7d12da4e22f9834a4aaa35bc" + +[[package]] +name = "unic-ucd-ident" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e230a37c0381caa9219d67cf063aa3a375ffed5bf541a452db16e744bdab6987" +dependencies = [ + "unic-char-property", + "unic-char-range", + "unic-ucd-version", +] + +[[package]] +name = "unic-ucd-version" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96bd2f2237fe450fcd0a1d2f5f4e91711124f7857ba2e964247776ebeeb7b0c4" +dependencies = [ + "unic-common", +] + +[[package]] +name = "unicode-bidi" +version = "0.3.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75" + +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" + +[[package]] +name = "unicode-normalization" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a56d1686db2308d901306f92a263857ef59ea39678a5458e7cb17f01415101f5" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "unicode-segmentation" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202" + +[[package]] +name = "universal-hash" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc1de2c688dc15305988b563c3854064043356019f97a4b46276fe734c4f07ea" +dependencies = [ + "crypto-common", + "subtle", +] + +[[package]] +name = "untrusted" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" + +[[package]] +name = "url" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31e6302e3bb753d46e83516cae55ae196fc0c309407cf11ab35cc51a4c2a4633" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", + "serde", +] + +[[package]] +name = "urlpattern" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9bd5ff03aea02fa45b13a7980151fe45009af1980ba69f651ec367121a31609" +dependencies = [ + "derive_more", + "regex", + "serde", + "unic-ucd-ident", + "url", +] + +[[package]] +name = "utf-8" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" + +[[package]] +name = "uuid" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a183cf7feeba97b4dd1c0d46788634f6221d87fa961b305bed08c851829efcc0" +dependencies = [ + "getrandom 0.2.12", +] + +[[package]] +name = "valuable" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" + +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + +[[package]] +name = "version-compare" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "852e951cb7832cb45cb1169900d19760cfa39b82bc0ea9c0e5a14ae88411c98b" + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "vswhom" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be979b7f07507105799e854203b470ff7c78a1639e330a58f183b5fea574608b" +dependencies = [ + "libc", + "vswhom-sys", +] + +[[package]] +name = "vswhom-sys" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3b17ae1f6c8a2b28506cd96d412eebf83b4a0ff2cbefeeb952f2f9dfa44ba18" +dependencies = [ + "cc", + "libc", +] + +[[package]] +name = "wait-timeout" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f200f5b12eb75f8c1ed65abd4b2db8a6e1b138a20de009dacee265a2498f3f6" +dependencies = [ + "libc", +] + +[[package]] +name = "walkdir" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" +dependencies = [ + "same-file", + "winapi-util", +] + +[[package]] +name = "want" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" +dependencies = [ + "try-lock", +] + +[[package]] +name = "wasi" +version = "0.9.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "wasm-bindgen" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8" +dependencies = [ + "cfg-if", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn 2.0.57", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76bc14366121efc8dbb487ab05bcc9d346b3b5ec0eaa76e46594cabbe51762c0" +dependencies = [ + "cfg-if", + "js-sys", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.57", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96" + +[[package]] +name = "wasm-streams" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b65dc4c90b63b118468cf747d8bf3566c1913ef60be765b5730ead9e0a3ba129" +dependencies = [ + "futures-util", + "js-sys", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + +[[package]] +name = "wayland-sys" +version = "0.31.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15a0c8eaff5216d07f226cb7a549159267f3467b289d9a2e52fd3ef5aae2b7af" +dependencies = [ + "dlib", + "log", + "pkg-config", +] + +[[package]] +name = "web-sys" +version = "0.3.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77afa9a11836342370f4817622a2f0f418b134426d91a82dfb48f532d2ec13ef" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "webkit2gtk" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76b1bc1e54c581da1e9f179d0b38512ba358fb1af2d634a1affe42e37172361a" +dependencies = [ + "bitflags 1.3.2", + "cairo-rs", + "gdk", + "gdk-sys", + "gio", + "gio-sys", + "glib", + "glib-sys", + "gobject-sys", + "gtk", + "gtk-sys", + "javascriptcore-rs", + "libc", + "once_cell", + "soup3", + "webkit2gtk-sys", +] + +[[package]] +name = "webkit2gtk-sys" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62daa38afc514d1f8f12b8693d30d5993ff77ced33ce30cd04deebc267a6d57c" +dependencies = [ + "bitflags 1.3.2", + "cairo-sys-rs", + "gdk-sys", + "gio-sys", + "glib-sys", + "gobject-sys", + "gtk-sys", + "javascriptcore-rs-sys", + "libc", + "pkg-config", + "soup3-sys", + "system-deps", +] + +[[package]] +name = "webpki-roots" +version = "0.26.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3de34ae270483955a94f4b21bdaaeb83d508bb84a01435f393818edb0012009" +dependencies = [ + "rustls-pki-types", +] + +[[package]] +name = "webview2-com" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38d5949fc3f537e90240c3e4f78dda2fa0431b671d50845a2f582173ef8a1201" +dependencies = [ + "webview2-com-macros", + "webview2-com-sys", + "windows 0.54.0", + "windows-core 0.54.0", + "windows-implement", + "windows-interface", +] + +[[package]] +name = "webview2-com-macros" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac1345798ecd8122468840bcdf1b95e5dc6d2206c5e4b0eafa078d061f59c9bc" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.57", +] + +[[package]] +name = "webview2-com-sys" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd1eaa1be63d6fdcadf893c40d7d53c889a6342b3a94930d34e6964d5bb7e8db" +dependencies = [ + "thiserror", + "windows 0.54.0", + "windows-core 0.54.0", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f29e6f9198ba0d26b4c9f07dbe6f9ed633e1f3d5b8b414090084349e46a52596" +dependencies = [ + "winapi", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "window-vibrancy" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33082acd404763b315866e14a0d5193f3422c81086657583937a750cdd3ec340" +dependencies = [ + "cocoa", + "objc", + "raw-window-handle 0.6.0", + "windows-sys 0.52.0", + "windows-version", +] + +[[package]] +name = "windows" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e686886bc078bc1b0b600cac0147aadb815089b6e4da64016cbd754b6342700f" +dependencies = [ + "windows-targets 0.48.5", +] + +[[package]] +name = "windows" +version = "0.54.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9252e5725dbed82865af151df558e754e4a3c2c30818359eb17465f1346a1b49" +dependencies = [ + "windows-core 0.54.0", + "windows-implement", + "windows-interface", + "windows-targets 0.52.4", +] + +[[package]] +name = "windows-core" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" +dependencies = [ + "windows-targets 0.52.4", +] + +[[package]] +name = "windows-core" +version = "0.54.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12661b9c89351d684a50a8a643ce5f608e20243b9fb84687800163429f161d65" +dependencies = [ + "windows-result", + "windows-targets 0.52.4", +] + +[[package]] +name = "windows-implement" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "942ac266be9249c84ca862f0a164a39533dc2f6f33dc98ec89c8da99b82ea0bd" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.57", +] + +[[package]] +name = "windows-interface" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da33557140a288fae4e1d5f8873aaf9eb6613a9cf82c3e070223ff177f598b60" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.57", +] + +[[package]] +name = "windows-result" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd19df78e5168dfb0aedc343d1d1b8d422ab2db6756d2dc3fef75035402a3f64" +dependencies = [ + "windows-targets 0.52.4", +] + +[[package]] +name = "windows-sys" +version = "0.45.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" +dependencies = [ + "windows-targets 0.42.2", +] + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets 0.48.5", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets 0.52.4", +] + +[[package]] +name = "windows-targets" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" +dependencies = [ + "windows_aarch64_gnullvm 0.42.2", + "windows_aarch64_msvc 0.42.2", + "windows_i686_gnu 0.42.2", + "windows_i686_msvc 0.42.2", + "windows_x86_64_gnu 0.42.2", + "windows_x86_64_gnullvm 0.42.2", + "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.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7dd37b7e5ab9018759f893a1952c9420d060016fc19a472b4bb20d1bdd694d1b" +dependencies = [ + "windows_aarch64_gnullvm 0.52.4", + "windows_aarch64_msvc 0.52.4", + "windows_i686_gnu 0.52.4", + "windows_i686_msvc 0.52.4", + "windows_x86_64_gnu 0.52.4", + "windows_x86_64_gnullvm 0.52.4", + "windows_x86_64_msvc 0.52.4", +] + +[[package]] +name = "windows-version" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75aa004c988e080ad34aff5739c39d0312f4684699d6d71fc8a198d057b8b9b4" +dependencies = [ + "windows-targets 0.52.4", +] + +[[package]] +name = "windows_aarch64_gnullvm" +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.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bcf46cf4c365c6f2d1cc93ce535f2c8b244591df96ceee75d8e83deb70a9cac9" + +[[package]] +name = "windows_aarch64_msvc" +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.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da9f259dd3bcf6990b55bffd094c4f7235817ba4ceebde8e6d11cd0c5633b675" + +[[package]] +name = "windows_i686_gnu" +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.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b474d8268f99e0995f25b9f095bc7434632601028cf86590aea5c8a5cb7801d3" + +[[package]] +name = "windows_i686_msvc" +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.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1515e9a29e5bed743cb4415a9ecf5dfca648ce85ee42e15873c3cd8610ff8e02" + +[[package]] +name = "windows_x86_64_gnu" +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.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5eee091590e89cc02ad514ffe3ead9eb6b660aedca2183455434b93546371a03" + +[[package]] +name = "windows_x86_64_gnullvm" +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.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77ca79f2451b49fa9e2af39f0747fe999fcda4f5e241b2898624dca97a1f2177" + +[[package]] +name = "windows_x86_64_msvc" +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.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32b752e52a2da0ddfbdbcc6fceadfeede4c939ed16d13e648833a61dfb611ed8" + +[[package]] +name = "winnow" +version = "0.5.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f593a95398737aeed53e489c785df13f3618e41dbcd6718c6addbf1395aa6876" +dependencies = [ + "memchr", +] + +[[package]] +name = "winreg" +version = "0.50.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1" +dependencies = [ + "cfg-if", + "windows-sys 0.48.0", +] + +[[package]] +name = "winreg" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a277a57398d4bfa075df44f501a17cfdf8542d224f0d36095a2adc7aee4ef0a5" +dependencies = [ + "cfg-if", + "windows-sys 0.48.0", +] + +[[package]] +name = "wry" +version = "0.39.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4eca9d50437c04fc67e82c196ddd31d8e35794150713ae2d647f3a58c7f45d1a" +dependencies = [ + "base64 0.21.7", + "block", + "cfg_aliases 0.1.1", + "cocoa", + "core-graphics", + "crossbeam-channel", + "dpi", + "dunce", + "gdkx11", + "gtk", + "html5ever", + "http", + "javascriptcore-rs", + "jni", + "kuchikiki", + "libc", + "ndk", + "ndk-context", + "ndk-sys", + "objc", + "objc_id", + "once_cell", + "percent-encoding", + "raw-window-handle 0.6.0", + "sha2", + "soup3", + "tao-macros", + "thiserror", + "tracing", + "webkit2gtk", + "webkit2gtk-sys", + "webview2-com", + "windows 0.54.0", + "windows-version", + "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" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38735924fedd5314a6e548792904ed8c6de6636285cb9fec04d5b1db85c1516f" +dependencies = [ + "libc", + "once_cell", + "pkg-config", +] + +[[package]] +name = "zeroize" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "525b4ec142c6b68a2d10f01f7bbf6755599ca3f81ea53b8431b7dd348f5fdb2d" diff --git a/LICENSE.spdx b/LICENSE.spdx index 7e8f2bfad..978d1d422 100644 --- a/LICENSE.spdx +++ b/LICENSE.spdx @@ -6,7 +6,7 @@ PackageSupplier: Organization: The Tauri Programme in the Commons Conservancy PackageHomePage: https://tauri.app PackageLicenseDeclared: Apache-2.0 PackageLicenseDeclared: MIT -PackageCopyrightText: 2019-2022, The Tauri Programme in the Commons Conservancy +PackageCopyrightText: 2019-2024, The Tauri Programme in the Commons Conservancy PackageSummary: Tauri is a rust project that enables developers to make secure and small desktop applications using a web frontend. diff --git a/README.md b/README.md index 64d67e470..074c51ada 100644 --- a/README.md +++ b/README.md @@ -35,7 +35,7 @@ The list of Tauri's features includes, but is not limited to: - Built-in self updater (desktop only) - System tray icons - Native notifications -- Localhost free (:fire:) +- Localhost free (🔥) - GitHub action for streamlined CI - VS Code extension diff --git a/core/tauri-build/CHANGELOG.md b/core/tauri-build/CHANGELOG.md index 89db693b6..13cefd392 100644 --- a/core/tauri-build/CHANGELOG.md +++ b/core/tauri-build/CHANGELOG.md @@ -1,5 +1,111 @@ # Changelog +## \[2.0.0-beta.11] + +### Dependencies + +- Upgraded to `tauri-utils@2.0.0-beta.11` +- Upgraded to `tauri-codegen@2.0.0-beta.11` + +## \[2.0.0-beta.10] + +### New Features + +- [`e227fe02f`](https://www.github.com/tauri-apps/tauri/commit/e227fe02f986e145c0731a64693e1c830a9eb5b0)([#9156](https://www.github.com/tauri-apps/tauri/pull/9156)) Allow plugins to define (at compile time) JavaScript that are initialized when `withGlobalTauri` is true. + +### Enhancements + +- [`7213b9e47`](https://www.github.com/tauri-apps/tauri/commit/7213b9e47242bef814aa7257e0bf84631bf5fe7e)([#9124](https://www.github.com/tauri-apps/tauri/pull/9124)) Fallback to an empty permission set if the plugin did not define its `default` permissions. + +### Dependencies + +- Upgraded to `tauri-utils@2.0.0-beta.10` +- Upgraded to `tauri-codegen@2.0.0-beta.10` + +## \[2.0.0-beta.9] + +### Dependencies + +- Upgraded to `tauri-codegen@2.0.0-beta.9` +- Upgraded to `tauri-utils@2.0.0-beta.9` + +## \[2.0.0-beta.8] + +### Dependencies + +- Upgraded to `tauri-utils@2.0.0-beta.8` +- Upgraded to `tauri-codegen@2.0.0-beta.8` + +## \[2.0.0-beta.7] + +### Bug Fixes + +- [`bb23511ea`](https://www.github.com/tauri-apps/tauri/commit/bb23511ea80bcaffbdebf057301e463fff268c90)([#9079](https://www.github.com/tauri-apps/tauri/pull/9079)) Fixed generation of capability schema for permissions field which previously disallowed mixed (strings and objects) permission definition. + +### Dependencies + +- Upgraded to `tauri-utils@2.0.0-beta.7` +- Upgraded to `tauri-codegen@2.0.0-beta.7` + +## \[2.0.0-beta.6] + +### Dependencies + +- Upgraded to `tauri-utils@2.0.0-beta.6` +- Upgraded to `tauri-codegen@2.0.0-beta.6` + +### Breaking Changes + +- [`3657ad82`](https://www.github.com/tauri-apps/tauri/commit/3657ad82f88ce528551d032d521c52eed3f396b4)([#9008](https://www.github.com/tauri-apps/tauri/pull/9008)) Allow defining permissions for the application commands via `tauri_build::Attributes::app_manifest`. + +## \[2.0.0-beta.5] + +### Breaking Changes + +- [`b9e6a018`](https://www.github.com/tauri-apps/tauri/commit/b9e6a01879d9233040f3d3fab11c59e70563da7e)([#8937](https://www.github.com/tauri-apps/tauri/pull/8937)) The `custom-protocol` Cargo feature is no longer required on your application and is now ignored. To check if running on production, use `#[cfg(not(dev))]` instead of `#[cfg(feature = "custom-protocol")]`. +- [`b9e6a018`](https://www.github.com/tauri-apps/tauri/commit/b9e6a01879d9233040f3d3fab11c59e70563da7e)([#8937](https://www.github.com/tauri-apps/tauri/pull/8937)) Removed `tauri_build::CodegenContext::dev()` and added `tauri_build::dev()`. + +### Dependencies + +- Upgraded to `tauri-utils@2.0.0-beta.5` +- Upgraded to `tauri-codegen@2.0.0-beta.5` + +## \[2.0.0-beta.4] + +### Enhancements + +- [`b5eb6472`](https://www.github.com/tauri-apps/tauri/commit/b5eb64728aeb410d3f3068608a94762655c4690f)([#8940](https://www.github.com/tauri-apps/tauri/pull/8940)) Enable Hight DPI awareness for NSIS installer so it is not blurry on some systems. + +### Dependencies + +- Upgraded to `tauri-utils@2.0.0-beta.4` +- Upgraded to `tauri-codegen@2.0.0-beta.4` + +## \[2.0.0-beta.3] + +### Dependencies + +- Upgraded to `tauri-utils@2.0.0-beta.3` +- Upgraded to `tauri-codegen@2.0.0-beta.3` + +## \[2.0.0-beta.2] + +### Enhancements + +- [`83a68deb`](https://www.github.com/tauri-apps/tauri/commit/83a68deb5676d39cd4728d2e140f6b46d5f787ed)([#8797](https://www.github.com/tauri-apps/tauri/pull/8797)) Added a new configuration option `tauri.conf.json > app > security > capabilities` to reference existing capabilities and inline new ones. If it is empty, all capabilities are still included preserving the current behavior. +- [`edb11c13`](https://www.github.com/tauri-apps/tauri/commit/edb11c138def2e317099db432479e3ca5dbf803f)([#8781](https://www.github.com/tauri-apps/tauri/pull/8781)) Added `Attributes::plugin()` to register a plugin that is inlined in the application crate. +- [`8d16a80d`](https://www.github.com/tauri-apps/tauri/commit/8d16a80d2fb2468667e7987d0cc99dbc7e3b9d0a)([#8802](https://www.github.com/tauri-apps/tauri/pull/8802)) Added `CodegenContext::capability` to include a capability file dynamically. + +### Bug Fixes + +- [`0e8e9cd0`](https://www.github.com/tauri-apps/tauri/commit/0e8e9cd064627e734adf8f62e571dc5f4e8f4d9f)([#8906](https://www.github.com/tauri-apps/tauri/pull/8906)) Fixes the capability schema not resolving inner definitions. +- [`19fb5f0b`](https://www.github.com/tauri-apps/tauri/commit/19fb5f0b20479885bf8bc4fdd8c431052420191d)([#8782](https://www.github.com/tauri-apps/tauri/pull/8782)) Fix generating invalid schema files. + +### Dependencies + +- Upgraded to `tauri-utils@2.0.0-beta.2` +- Upgraded to `tauri-codegen@2.0.0-beta.2` + ## \[2.0.0-beta.1] ### Enhancements @@ -174,6 +280,13 @@ - First mobile alpha release! - [fa3a1098](https://www.github.com/tauri-apps/tauri/commit/fa3a10988a03aed1b66fb17d893b1a9adb90f7cd) feat(ci): prepare 2.0.0-alpha.0 ([#5786](https://www.github.com/tauri-apps/tauri/pull/5786)) on 2022-12-08 +## \[1.5.1] + +### Dependencies + +- Upgraded to `tauri-utils@1.5.2` +- Upgraded to `tauri-codegen@1.4.2` + ## \[1.5.0] ### What's Changed diff --git a/core/tauri-build/Cargo.toml b/core/tauri-build/Cargo.toml index 45adb3653..2c3ad55e0 100644 --- a/core/tauri-build/Cargo.toml +++ b/core/tauri-build/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "tauri-build" -version = "2.0.0-beta.1" +version = "2.0.0-beta.11" description = "build time code to pair with https://crates.io/crates/tauri" exclude = [ "CHANGELOG.md", "/target" ] readme = "README.md" @@ -28,8 +28,8 @@ rustdoc-args = [ "--cfg", "docsrs" ] [dependencies] anyhow = "1" quote = { version = "1", optional = true } -tauri-codegen = { version = "2.0.0-beta.1", path = "../tauri-codegen", optional = true } -tauri-utils = { version = "2.0.0-beta.1", path = "../tauri-utils", features = [ "build", "resources" ] } +tauri-codegen = { version = "2.0.0-beta.11", path = "../tauri-codegen", optional = true } +tauri-utils = { version = "2.0.0-beta.11", path = "../tauri-utils", features = [ "build", "resources" ] } cargo_toml = "0.17" serde = "1" serde_json = "1" diff --git a/core/tauri-build/src/acl.rs b/core/tauri-build/src/acl.rs index b8226c058..ea0961a73 100644 --- a/core/tauri-build/src/acl.rs +++ b/core/tauri-build/src/acl.rs @@ -1,11 +1,12 @@ -// Copyright 2019-2023 Tauri Programme within The Commons Conservancy +// Copyright 2019-2024 Tauri Programme within The Commons Conservancy // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT use std::{ - collections::{BTreeMap, BTreeSet}, + collections::{BTreeMap, BTreeSet, HashMap}, + env::current_dir, fs::{copy, create_dir_all, read_to_string, write}, - path::PathBuf, + path::{Path, PathBuf}, }; use anyhow::{Context, Result}; @@ -17,7 +18,11 @@ use schemars::{ schema_for, }; use tauri_utils::{ - acl::{build::CapabilityFile, capability::Capability, plugin::Manifest}, + acl::{ + capability::{Capability, CapabilityFile}, + manifest::Manifest, + APP_ACL_KEY, + }, platform::Target, }; @@ -25,43 +30,119 @@ const CAPABILITIES_SCHEMA_FILE_NAME: &str = "schema.json"; /// Path of the folder where schemas are saved. const CAPABILITIES_SCHEMA_FOLDER_PATH: &str = "gen/schemas"; const CAPABILITIES_FILE_NAME: &str = "capabilities.json"; -const PLUGIN_MANIFESTS_FILE_NAME: &str = "plugin-manifests.json"; +const ACL_MANIFESTS_FILE_NAME: &str = "acl-manifests.json"; -fn capabilities_schema(plugin_manifests: &BTreeMap) -> RootSchema { +/// Definition of a plugin that is part of the Tauri application instead of having its own crate. +/// +/// By default it generates a plugin manifest that parses permissions from the `permissions/$plugin-name` directory. +/// To change the glob pattern that is used to find permissions, use [`Self::permissions_path_pattern`]. +/// +/// To autogenerate permissions for each of the plugin commands, see [`Self::commands`]. +#[derive(Debug, Default)] +pub struct InlinedPlugin { + commands: &'static [&'static str], + permissions_path_pattern: Option<&'static str>, +} + +impl InlinedPlugin { + pub fn new() -> Self { + Self::default() + } + + /// Define a list of commands that gets permissions autogenerated in the format of `allow-$command` and `deny-$command` + /// where $command is the command name in snake_case. + pub fn commands(mut self, commands: &'static [&'static str]) -> Self { + self.commands = commands; + self + } + + /// Sets a glob pattern that is used to find the permissions of this inlined plugin. + /// + /// **Note:** You must emit [rerun-if-changed] instructions for the plugin permissions directory. + /// + /// By default it is `./permissions/$plugin-name/**/*` + pub fn permissions_path_pattern(mut self, pattern: &'static str) -> Self { + self.permissions_path_pattern.replace(pattern); + self + } +} + +/// Tauri application permission manifest. +/// +/// By default it generates a manifest that parses permissions from the `permissions` directory. +/// To change the glob pattern that is used to find permissions, use [`Self::permissions_path_pattern`]. +/// +/// To autogenerate permissions for each of the app commands, see [`Self::commands`]. +#[derive(Debug, Default)] +pub struct AppManifest { + commands: &'static [&'static str], + permissions_path_pattern: Option<&'static str>, +} + +impl AppManifest { + pub fn new() -> Self { + Self::default() + } + + /// Define a list of commands that gets permissions autogenerated in the format of `allow-$command` and `deny-$command` + /// where $command is the command name in snake_case. + pub fn commands(mut self, commands: &'static [&'static str]) -> Self { + self.commands = commands; + self + } + + /// Sets a glob pattern that is used to find the permissions of the app. + /// + /// **Note:** You must emit [rerun-if-changed] instructions for the permissions directory. + /// + /// By default it is `./permissions/**/*` ignoring any [`InlinedPlugin`]. + pub fn permissions_path_pattern(mut self, pattern: &'static str) -> Self { + self.permissions_path_pattern.replace(pattern); + self + } +} + +fn capabilities_schema(acl_manifests: &BTreeMap) -> RootSchema { let mut schema = schema_for!(CapabilityFile); - fn schema_from(plugin: &str, id: &str, description: Option<&str>) -> Schema { + fn schema_from(key: &str, id: &str, description: Option<&str>) -> Schema { + let command_name = if key == APP_ACL_KEY { + id.to_string() + } else { + format!("{key}:{id}") + }; Schema::Object(SchemaObject { metadata: Some(Box::new(Metadata { description: description .as_ref() - .map(|d| format!("{plugin}:{id} -> {d}")), + .map(|d| format!("{command_name} -> {d}")), ..Default::default() })), instance_type: Some(InstanceType::String.into()), - enum_values: Some(vec![serde_json::Value::String(format!("{plugin}:{id}"))]), + enum_values: Some(vec![serde_json::Value::String(command_name)]), ..Default::default() }) } let mut permission_schemas = Vec::new(); - for (plugin, manifest) in plugin_manifests { + for (key, manifest) in acl_manifests { for (set_id, set) in &manifest.permission_sets { - permission_schemas.push(schema_from(plugin, set_id, Some(&set.description))); + permission_schemas.push(schema_from(key, set_id, Some(&set.description))); } - if let Some(default) = &manifest.default_permission { - permission_schemas.push(schema_from( - plugin, - "default", - Some(default.description.as_ref()), - )); - } + permission_schemas.push(schema_from( + key, + "default", + manifest + .default_permission + .as_ref() + .map(|d| d.description.as_ref()), + )); for (permission_id, permission) in &manifest.permissions { permission_schemas.push(schema_from( - plugin, + key, permission_id, permission.description.as_deref(), )); @@ -83,27 +164,32 @@ fn capabilities_schema(plugin_manifests: &BTreeMap) -> RootSch })); } + let mut definitions = Vec::new(); + if let Some(Schema::Object(obj)) = schema.definitions.get_mut("PermissionEntry") { let permission_entry_any_of_schemas = obj.subschemas().any_of.as_mut().unwrap(); - if let Schema::Object(mut scope_extended_schema_obj) = - permission_entry_any_of_schemas.remove(permission_entry_any_of_schemas.len() - 1) + if let Schema::Object(scope_extended_schema_obj) = + permission_entry_any_of_schemas.last_mut().unwrap() { let mut global_scope_one_of = Vec::new(); - for (plugin, manifest) in plugin_manifests { + for (key, manifest) in acl_manifests { if let Some(global_scope_schema) = &manifest.global_scope_schema { - let global_scope_schema_def: Schema = serde_json::from_value(global_scope_schema.clone()) - .unwrap_or_else(|e| panic!("invalid JSON schema for plugin {plugin}: {e}")); + let global_scope_schema_def: RootSchema = + serde_json::from_value(global_scope_schema.clone()) + .unwrap_or_else(|e| panic!("invalid JSON schema for plugin {key}: {e}")); let global_scope_schema = Schema::Object(SchemaObject { array: Some(Box::new(ArrayValidation { - items: Some(global_scope_schema_def.into()), + items: Some(Schema::Object(global_scope_schema_def.schema).into()), ..Default::default() })), ..Default::default() }); + definitions.push(global_scope_schema_def.definitions); + let mut required = BTreeSet::new(); required.insert("identifier".to_string()); @@ -113,15 +199,20 @@ fn capabilities_schema(plugin_manifests: &BTreeMap) -> RootSch }; let mut permission_schemas = Vec::new(); - if let Some(default) = &manifest.default_permission { - permission_schemas.push(schema_from(plugin, "default", Some(&default.description))); - } + permission_schemas.push(schema_from( + key, + "default", + manifest + .default_permission + .as_ref() + .map(|d| d.description.as_ref()), + )); for set in manifest.permission_sets.values() { - permission_schemas.push(schema_from(plugin, &set.identifier, Some(&set.description))); + permission_schemas.push(schema_from(key, &set.identifier, Some(&set.description))); } for permission in manifest.permissions.values() { permission_schemas.push(schema_from( - plugin, + key, &permission.identifier, permission.description.as_deref(), )); @@ -161,20 +252,19 @@ fn capabilities_schema(plugin_manifests: &BTreeMap) -> RootSch one_of: Some(global_scope_one_of), ..Default::default() })); - - permission_entry_any_of_schemas.push(scope_extended_schema_obj.into()); }; } } + for definitions_map in definitions { + schema.definitions.extend(definitions_map); + } + schema } -pub fn generate_schema( - plugin_manifests: &BTreeMap, - target: Target, -) -> Result<()> { - let schema = capabilities_schema(plugin_manifests); +pub fn generate_schema(acl_manifests: &BTreeMap, target: Target) -> Result<()> { + let schema = capabilities_schema(acl_manifests); let schema_str = serde_json::to_string_pretty(&schema).unwrap(); let out_dir = PathBuf::from(CAPABILITIES_SCHEMA_FOLDER_PATH); create_dir_all(&out_dir).context("unable to create schema output directory")?; @@ -209,17 +299,17 @@ pub fn save_capabilities(capabilities: &BTreeMap) -> Result< Ok(capabilities_path) } -pub fn save_plugin_manifests(plugin_manifests: &BTreeMap) -> Result { - let plugin_manifests_path = - PathBuf::from(CAPABILITIES_SCHEMA_FOLDER_PATH).join(PLUGIN_MANIFESTS_FILE_NAME); - let plugin_manifests_json = serde_json::to_string(&plugin_manifests)?; - if plugin_manifests_json != read_to_string(&plugin_manifests_path).unwrap_or_default() { - std::fs::write(&plugin_manifests_path, plugin_manifests_json)?; +pub fn save_acl_manifests(acl_manifests: &BTreeMap) -> Result { + let acl_manifests_path = + PathBuf::from(CAPABILITIES_SCHEMA_FOLDER_PATH).join(ACL_MANIFESTS_FILE_NAME); + let acl_manifests_json = serde_json::to_string(&acl_manifests)?; + if acl_manifests_json != read_to_string(&acl_manifests_path).unwrap_or_default() { + std::fs::write(&acl_manifests_path, acl_manifests_json)?; } - Ok(plugin_manifests_path) + Ok(acl_manifests_path) } -pub fn get_plugin_manifests() -> Result> { +pub fn get_manifests_from_plugins() -> Result> { let permission_map = tauri_utils::acl::build::read_permissions().context("failed to read plugin permissions")?; let mut global_scope_map = tauri_utils::acl::build::read_global_scope_schemas() @@ -234,52 +324,190 @@ pub fn get_plugin_manifests() -> Result> { Ok(processed) } +pub fn inline_plugins( + out_dir: &Path, + inlined_plugins: HashMap<&'static str, InlinedPlugin>, +) -> Result> { + let mut acl_manifests = BTreeMap::new(); + + for (name, plugin) in inlined_plugins { + let plugin_out_dir = out_dir.join("plugins").join(name); + create_dir_all(&plugin_out_dir)?; + + let mut permission_files = if plugin.commands.is_empty() { + Vec::new() + } else { + tauri_utils::acl::build::autogenerate_command_permissions( + &plugin_out_dir, + plugin.commands, + "", + false, + ); + tauri_utils::acl::build::define_permissions( + &plugin_out_dir.join("*").to_string_lossy(), + name, + &plugin_out_dir, + |_| true, + )? + }; + + if let Some(pattern) = plugin.permissions_path_pattern { + permission_files.extend(tauri_utils::acl::build::define_permissions( + pattern, + name, + &plugin_out_dir, + |_| true, + )?); + } else { + let default_permissions_path = Path::new("permissions").join(name); + println!( + "cargo:rerun-if-changed={}", + default_permissions_path.display() + ); + permission_files.extend(tauri_utils::acl::build::define_permissions( + &default_permissions_path + .join("**") + .join("*") + .to_string_lossy(), + name, + &plugin_out_dir, + |_| true, + )?); + } + + let manifest = tauri_utils::acl::manifest::Manifest::new(permission_files, None); + acl_manifests.insert(name.into(), manifest); + } + + Ok(acl_manifests) +} + +pub fn app_manifest_permissions( + out_dir: &Path, + manifest: AppManifest, + inlined_plugins: &HashMap<&'static str, InlinedPlugin>, +) -> Result { + let app_out_dir = out_dir.join("app-manifest"); + create_dir_all(&app_out_dir)?; + let pkg_name = "__app__"; + + let mut permission_files = if manifest.commands.is_empty() { + Vec::new() + } else { + let autogenerated_path = Path::new("./permissions/autogenerated"); + tauri_utils::acl::build::autogenerate_command_permissions( + autogenerated_path, + manifest.commands, + "", + false, + ); + tauri_utils::acl::build::define_permissions( + &autogenerated_path.join("*").to_string_lossy(), + pkg_name, + &app_out_dir, + |_| true, + )? + }; + + if let Some(pattern) = manifest.permissions_path_pattern { + permission_files.extend(tauri_utils::acl::build::define_permissions( + pattern, + pkg_name, + &app_out_dir, + |_| true, + )?); + } else { + let default_permissions_path = Path::new("permissions"); + println!( + "cargo:rerun-if-changed={}", + default_permissions_path.display() + ); + + let permissions_root = current_dir()?.join("permissions"); + let inlined_plugins_permissions: Vec<_> = inlined_plugins + .keys() + .map(|name| permissions_root.join(name)) + .collect(); + + permission_files.extend(tauri_utils::acl::build::define_permissions( + &default_permissions_path + .join("**") + .join("*") + .to_string_lossy(), + pkg_name, + &app_out_dir, + // filter out directories containing inlined plugins + |p| { + !inlined_plugins_permissions + .iter() + .any(|inlined_path| p.starts_with(inlined_path)) + }, + )?); + } + + Ok(tauri_utils::acl::manifest::Manifest::new( + permission_files, + None, + )) +} + pub fn validate_capabilities( - plugin_manifests: &BTreeMap, + acl_manifests: &BTreeMap, capabilities: &BTreeMap, ) -> Result<()> { let target = tauri_utils::platform::Target::from_triple(&std::env::var("TARGET").unwrap()); for capability in capabilities.values() { - if !capability.platforms.contains(&target) { + if !capability + .platforms + .as_ref() + .map(|platforms| platforms.contains(&target)) + .unwrap_or(true) + { continue; } for permission_entry in &capability.permissions { let permission_id = permission_entry.identifier(); - if let Some((plugin_name, permission_name)) = permission_id.get().split_once(':') { - let permission_exists = plugin_manifests - .get(plugin_name) - .map(|manifest| { - if permission_name == "default" { - manifest.default_permission.is_some() - } else { - manifest.permissions.contains_key(permission_name) - || manifest.permission_sets.contains_key(permission_name) - } - }) - .unwrap_or(false); + let (key, permission_name) = permission_id + .get() + .split_once(':') + .unwrap_or_else(|| (APP_ACL_KEY, permission_id.get())); - if !permission_exists { - let mut available_permissions = Vec::new(); - for (plugin, manifest) in plugin_manifests { - if manifest.default_permission.is_some() { - available_permissions.push(format!("{plugin}:default")); - } - for p in manifest.permissions.keys() { - available_permissions.push(format!("{plugin}:{p}")); - } - for p in manifest.permission_sets.keys() { - available_permissions.push(format!("{plugin}:{p}")); - } + let permission_exists = acl_manifests + .get(key) + .map(|manifest| { + // the default permission is always treated as valid, the CLI automatically adds it on the `tauri add` command + permission_name == "default" + || manifest.permissions.contains_key(permission_name) + || manifest.permission_sets.contains_key(permission_name) + }) + .unwrap_or(false); + + if !permission_exists { + let mut available_permissions = Vec::new(); + for (key, manifest) in acl_manifests { + let prefix = if key == APP_ACL_KEY { + "".to_string() + } else { + format!("{key}:") + }; + if manifest.default_permission.is_some() { + available_permissions.push(format!("{prefix}default")); + } + for p in manifest.permissions.keys() { + available_permissions.push(format!("{prefix}{p}")); + } + for p in manifest.permission_sets.keys() { + available_permissions.push(format!("{prefix}{p}")); } - - anyhow::bail!( - "Permission {} not found, expected one of {}", - permission_id.get(), - available_permissions.join(", ") - ); } + + anyhow::bail!( + "Permission {} not found, expected one of {}", + permission_id.get(), + available_permissions.join(", ") + ); } } } diff --git a/core/tauri-build/src/codegen/context.rs b/core/tauri-build/src/codegen/context.rs index 65afc364a..0bd8dbcf5 100644 --- a/core/tauri-build/src/codegen/context.rs +++ b/core/tauri-build/src/codegen/context.rs @@ -1,4 +1,4 @@ -// Copyright 2019-2023 Tauri Programme within The Commons Conservancy +// Copyright 2019-2024 Tauri Programme within The Commons Conservancy // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT @@ -7,7 +7,7 @@ use std::{ env::var, fs::{create_dir_all, File}, io::{BufWriter, Write}, - path::PathBuf, + path::{Path, PathBuf}, }; use tauri_codegen::{context_codegen, ContextData}; use tauri_utils::config::FrontendDist; @@ -17,17 +17,17 @@ use tauri_utils::config::FrontendDist; #[cfg_attr(docsrs, doc(cfg(feature = "codegen")))] #[derive(Debug)] pub struct CodegenContext { - dev: bool, config_path: PathBuf, out_file: PathBuf, + capabilities: Option>, } impl Default for CodegenContext { fn default() -> Self { Self { - dev: false, config_path: PathBuf::from("tauri.conf.json"), out_file: PathBuf::from("tauri-build-context.rs"), + capabilities: None, } } } @@ -66,11 +66,13 @@ impl CodegenContext { self } - /// Run the codegen in a `dev` context, meaning that Tauri is using a dev server or local file for development purposes, - /// usually with the `tauri dev` CLI command. + /// Adds a capability file to the generated context. #[must_use] - pub fn dev(mut self) -> Self { - self.dev = true; + pub fn capability>(mut self, path: P) -> Self { + self + .capabilities + .get_or_insert_with(Default::default) + .push(path.as_ref().to_path_buf()); self } @@ -119,12 +121,14 @@ impl CodegenContext { ); let code = context_codegen(ContextData { - dev: self.dev, + dev: crate::dev(), config, config_parent, // it's very hard to have a build script for unit tests, so assume this is always called from // outside the tauri crate, making the ::tauri root valid. root: quote::quote!(::tauri), + capabilities: self.capabilities, + assets: None, })?; // get the full output file path diff --git a/core/tauri-build/src/codegen/mod.rs b/core/tauri-build/src/codegen/mod.rs index 61a7e5b29..788e5921e 100644 --- a/core/tauri-build/src/codegen/mod.rs +++ b/core/tauri-build/src/codegen/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2019-2023 Tauri Programme within The Commons Conservancy +// Copyright 2019-2024 Tauri Programme within The Commons Conservancy // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT diff --git a/core/tauri-build/src/lib.rs b/core/tauri-build/src/lib.rs index 7d892ecb4..2c90b5b64 100644 --- a/core/tauri-build/src/lib.rs +++ b/core/tauri-build/src/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2019-2023 Tauri Programme within The Commons Conservancy +// Copyright 2019-2024 Tauri Programme within The Commons Conservancy // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT @@ -15,15 +15,15 @@ use anyhow::Context; pub use anyhow::Result; use cargo_toml::Manifest; -use heck::AsShoutySnakeCase; use tauri_utils::{ - acl::build::parse_capabilities, + acl::{build::parse_capabilities, APP_ACL_KEY}, config::{BundleResources, Config, WebviewInstallMode}, resources::{external_binaries, ResourcePaths}, }; use std::{ + collections::HashMap, env::var_os, fs::copy, path::{Path, PathBuf}, @@ -40,7 +40,9 @@ mod static_vcruntime; #[cfg_attr(docsrs, doc(cfg(feature = "codegen")))] pub use codegen::context::CodegenContext; -const PLUGIN_MANIFESTS_FILE_NAME: &str = "plugin-manifests.json"; +pub use acl::{AppManifest, InlinedPlugin}; + +const ACL_MANIFESTS_FILE_NAME: &str = "acl-manifests.json"; const CAPABILITIES_FILE_NAME: &str = "capabilities.json"; fn copy_file(from: impl AsRef, to: impl AsRef) -> Result<()> { @@ -206,15 +208,6 @@ fn copy_frameworks(dest_dir: &Path, frameworks: &[String]) -> Result<()> { Ok(()) } -// checks if the given Cargo feature is enabled. -fn has_feature(feature: &str) -> bool { - // when a feature is enabled, Cargo sets the `CARGO_FEATURE_, #[cfg(feature = "codegen")] codegen: Option, + inlined_plugins: HashMap<&'static str, InlinedPlugin>, + app_manifest: AppManifest, } impl Attributes { @@ -365,6 +360,22 @@ impl Attributes { self } + /// Adds the given plugin to the list of inlined plugins (a plugin that is part of your application). + /// + /// See [`InlinedPlugin`] for more information. + pub fn plugin(mut self, name: &'static str, plugin: InlinedPlugin) -> Self { + self.inlined_plugins.insert(name, plugin); + self + } + + /// Sets the application manifest for the Access Control List. + /// + /// See [`AppManifest`] for more information. + pub fn app_manifest(mut self, manifest: AppManifest) -> Self { + self.app_manifest = manifest; + self + } + #[cfg(feature = "codegen")] #[cfg_attr(docsrs, doc(cfg(feature = "codegen")))] #[must_use] @@ -374,6 +385,12 @@ impl Attributes { } } +pub fn dev() -> bool { + std::env::var("DEP_TAURI_DEV") + .expect("missing `cargo:dev` instruction, please update tauri to latest") + == "true" +} + /// Run all build time helpers for your Tauri Application. /// /// The current helpers include the following: @@ -454,7 +471,7 @@ pub fn try_build(attributes: Attributes) -> Result<()> { mobile::generate_gradle_files(project_dir)?; } - cfg_alias("dev", !has_feature("custom-protocol")); + cfg_alias("dev", dev()); let ws_path = get_workspace_dir()?; let mut manifest = @@ -473,24 +490,41 @@ pub fn try_build(attributes: Attributes) -> Result<()> { let out_dir = PathBuf::from(std::env::var("OUT_DIR").unwrap()); manifest::check(&config, &mut manifest)?; - let plugin_manifests = acl::get_plugin_manifests()?; - std::fs::write( - out_dir.join(PLUGIN_MANIFESTS_FILE_NAME), - serde_json::to_string(&plugin_manifests)?, + + let mut acl_manifests = acl::get_manifests_from_plugins()?; + let app_manifest = acl::app_manifest_permissions( + &out_dir, + attributes.app_manifest, + &attributes.inlined_plugins, )?; + if app_manifest.default_permission.is_some() + || !app_manifest.permission_sets.is_empty() + || !app_manifest.permissions.is_empty() + { + acl_manifests.insert(APP_ACL_KEY.into(), app_manifest); + } + acl_manifests.extend(acl::inline_plugins(&out_dir, attributes.inlined_plugins)?); + + std::fs::write( + out_dir.join(ACL_MANIFESTS_FILE_NAME), + serde_json::to_string(&acl_manifests)?, + )?; + let capabilities = if let Some(pattern) = attributes.capabilities_path_pattern { parse_capabilities(pattern)? } else { println!("cargo:rerun-if-changed=capabilities"); parse_capabilities("./capabilities/**/*")? }; - acl::generate_schema(&plugin_manifests, target)?; - acl::validate_capabilities(&plugin_manifests, &capabilities)?; + acl::generate_schema(&acl_manifests, target)?; + acl::validate_capabilities(&acl_manifests, &capabilities)?; let capabilities_path = acl::save_capabilities(&capabilities)?; copy(capabilities_path, out_dir.join(CAPABILITIES_FILE_NAME))?; - acl::save_plugin_manifests(&plugin_manifests)?; + acl::save_acl_manifests(&acl_manifests)?; + + tauri_utils::plugin::load_global_api_scripts(&out_dir); println!("cargo:rustc-env=TAURI_ENV_TARGET_TRIPLE={target_triple}"); diff --git a/core/tauri-build/src/manifest.rs b/core/tauri-build/src/manifest.rs index fea47477a..e321a3624 100644 --- a/core/tauri-build/src/manifest.rs +++ b/core/tauri-build/src/manifest.rs @@ -1,4 +1,4 @@ -// Copyright 2019-2023 Tauri Programme within The Commons Conservancy +// Copyright 2019-2024 Tauri Programme within The Commons Conservancy // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT diff --git a/core/tauri-build/src/mobile.rs b/core/tauri-build/src/mobile.rs index e73161594..c12ac1e62 100644 --- a/core/tauri-build/src/mobile.rs +++ b/core/tauri-build/src/mobile.rs @@ -1,4 +1,4 @@ -// Copyright 2019-2023 Tauri Programme within The Commons Conservancy +// Copyright 2019-2024 Tauri Programme within The Commons Conservancy // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT diff --git a/core/tauri-build/src/static_vcruntime.rs b/core/tauri-build/src/static_vcruntime.rs index 0d95f9a6d..833c7d227 100644 --- a/core/tauri-build/src/static_vcruntime.rs +++ b/core/tauri-build/src/static_vcruntime.rs @@ -1,4 +1,4 @@ -// Copyright 2019-2023 Tauri Programme within The Commons Conservancy +// Copyright 2019-2024 Tauri Programme within The Commons Conservancy // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT diff --git a/core/tauri-codegen/CHANGELOG.md b/core/tauri-codegen/CHANGELOG.md index 0d92637ef..e5c068f5a 100644 --- a/core/tauri-codegen/CHANGELOG.md +++ b/core/tauri-codegen/CHANGELOG.md @@ -1,5 +1,94 @@ # Changelog +## \[2.0.0-beta.11] + +### Dependencies + +- Upgraded to `tauri-utils@2.0.0-beta.11` + +## \[2.0.0-beta.10] + +### New Features + +- [`e227fe02f`](https://www.github.com/tauri-apps/tauri/commit/e227fe02f986e145c0731a64693e1c830a9eb5b0)([#9156](https://www.github.com/tauri-apps/tauri/pull/9156)) Allow plugins to define (at compile time) JavaScript that are initialized when `withGlobalTauri` is true. + +### Dependencies + +- Upgraded to `tauri-utils@2.0.0-beta.10` + +## \[2.0.0-beta.9] + +### New Features + +- [`ba0206d8a`](https://www.github.com/tauri-apps/tauri/commit/ba0206d8a30a9b43ec5090dcaabd1a23baa1420c)([#9141](https://www.github.com/tauri-apps/tauri/pull/9141)) The `Context` codegen now accepts a `assets` input to define a custom `tauri::Assets` implementation. + +### Dependencies + +- Upgraded to `tauri-utils@2.0.0-beta.9` + +## \[2.0.0-beta.8] + +### Dependencies + +- Upgraded to `tauri-utils@2.0.0-beta.8` + +### Breaking Changes + +- [`ed48e2b3c`](https://www.github.com/tauri-apps/tauri/commit/ed48e2b3c7fa914e4c9af432c02b8154f872c68a)([#9122](https://www.github.com/tauri-apps/tauri/pull/9122)) Expose `tauri::image` module to export the `JsImage` type and removed the `Image` root re-export. + +## \[2.0.0-beta.7] + +### Dependencies + +- Upgraded to `tauri-utils@2.0.0-beta.7` + +### Breaking Changes + +- [`d1e77acd8`](https://www.github.com/tauri-apps/tauri/commit/d1e77acd8dfdf554b90b542513a58a2de1ef2360)([#9011](https://www.github.com/tauri-apps/tauri/pull/9011)) Change the generated context code to use the new `Image` type in tauri. + +## \[2.0.0-beta.6] + +### Dependencies + +- Upgraded to `tauri-utils@2.0.0-beta.6` + +### Breaking Changes + +- [`3657ad82`](https://www.github.com/tauri-apps/tauri/commit/3657ad82f88ce528551d032d521c52eed3f396b4)([#9008](https://www.github.com/tauri-apps/tauri/pull/9008)) Allow defining permissions for the application commands via `tauri_build::Attributes::app_manifest`. + +## \[2.0.0-beta.5] + +### Enhancements + +- [`bc5b5e67`](https://www.github.com/tauri-apps/tauri/commit/bc5b5e671a546512f823f1c157421b4c3311dfc0)([#8984](https://www.github.com/tauri-apps/tauri/pull/8984)) Do not include a CSP tag in the application HTML and rely on the custom protocol response header instead. + +### Dependencies + +- Upgraded to `tauri-utils@2.0.0-beta.5` + +## \[2.0.0-beta.4] + +### Dependencies + +- Upgraded to `tauri-utils@2.0.0-beta.4` + +## \[2.0.0-beta.3] + +### Dependencies + +- Upgraded to `tauri-utils@2.0.0-beta.3` + +## \[2.0.0-beta.2] + +### Enhancements + +- [`83a68deb`](https://www.github.com/tauri-apps/tauri/commit/83a68deb5676d39cd4728d2e140f6b46d5f787ed)([#8797](https://www.github.com/tauri-apps/tauri/pull/8797)) Added a new configuration option `tauri.conf.json > app > security > capabilities` to reference existing capabilities and inline new ones. If it is empty, all capabilities are still included preserving the current behavior. +- [`8d16a80d`](https://www.github.com/tauri-apps/tauri/commit/8d16a80d2fb2468667e7987d0cc99dbc7e3b9d0a)([#8802](https://www.github.com/tauri-apps/tauri/pull/8802)) The `generate_context` proc macro now accepts a `capabilities` attribute where the value is an array of file paths that can be [conditionally compiled](https://doc.rust-lang.org/reference/conditional-compilation.html). These capabilities are added to the application along the capabilities defined in the Tauri configuration file. + +### Dependencies + +- Upgraded to `tauri-utils@2.0.0-beta.2` + ## \[2.0.0-beta.1] ### Dependencies @@ -135,6 +224,12 @@ - First mobile alpha release! - [fa3a1098](https://www.github.com/tauri-apps/tauri/commit/fa3a10988a03aed1b66fb17d893b1a9adb90f7cd) feat(ci): prepare 2.0.0-alpha.0 ([#5786](https://www.github.com/tauri-apps/tauri/pull/5786)) on 2022-12-08 +## \[1.4.2] + +### Dependencies + +- Upgraded to `tauri-utils@1.5.2` + ## \[1.4.1] ### Dependencies diff --git a/core/tauri-codegen/Cargo.toml b/core/tauri-codegen/Cargo.toml index befddab47..ff3855f7d 100644 --- a/core/tauri-codegen/Cargo.toml +++ b/core/tauri-codegen/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "tauri-codegen" -version = "2.0.0-beta.1" +version = "2.0.0-beta.11" description = "code generation meant to be consumed inside of `tauri` through `tauri-build` or `tauri-macros`" exclude = [ "CHANGELOG.md", "/target" ] readme = "README.md" @@ -14,12 +14,13 @@ rust-version = { workspace = true } [dependencies] sha2 = "0.10" -base64 = "0.21" +base64 = "0.22" proc-macro2 = "1" quote = "1" +syn = "2" serde = { version = "1", features = [ "derive" ] } serde_json = "1" -tauri-utils = { version = "2.0.0-beta.1", path = "../tauri-utils", features = [ "build" ] } +tauri-utils = { version = "2.0.0-beta.11", path = "../tauri-utils", features = [ "build" ] } thiserror = "1" walkdir = "2" brotli = { version = "3", optional = true, default-features = false, features = [ "std" ] } diff --git a/core/tauri-codegen/src/context.rs b/core/tauri-codegen/src/context.rs index f495174d9..32c5d23ca 100644 --- a/core/tauri-codegen/src/context.rs +++ b/core/tauri-codegen/src/context.rs @@ -1,8 +1,9 @@ -// Copyright 2019-2023 Tauri Programme within The Commons Conservancy +// Copyright 2019-2024 Tauri Programme within The Commons Conservancy // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT use std::collections::BTreeMap; +use std::convert::identity; use std::path::{Path, PathBuf}; use std::{ffi::OsStr, str::FromStr}; @@ -11,19 +12,22 @@ use proc_macro2::TokenStream; use quote::quote; use sha2::{Digest, Sha256}; -use tauri_utils::acl::capability::Capability; -use tauri_utils::acl::plugin::Manifest; +use syn::Expr; +use tauri_utils::acl::capability::{Capability, CapabilityFile}; +use tauri_utils::acl::manifest::Manifest; use tauri_utils::acl::resolved::Resolved; use tauri_utils::assets::AssetKey; -use tauri_utils::config::{Config, FrontendDist, PatternKind}; +use tauri_utils::config::{CapabilityEntry, Config, FrontendDist, PatternKind}; use tauri_utils::html::{ - inject_nonce_token, parse as parse_html, serialize_node as serialize_html_node, + inject_nonce_token, parse as parse_html, serialize_node as serialize_html_node, NodeRef, }; use tauri_utils::platform::Target; +use tauri_utils::plugin::GLOBAL_API_SCRIPT_FILE_LIST_PATH; +use tauri_utils::tokens::{map_lit, str_lit}; use crate::embedded_assets::{AssetOptions, CspHashes, EmbeddedAssets, EmbeddedAssetsError}; -const PLUGIN_MANIFESTS_FILE_NAME: &str = "plugin-manifests.json"; +const ACL_MANIFESTS_FILE_NAME: &str = "acl-manifests.json"; const CAPABILITIES_FILE_NAME: &str = "capabilities.json"; /// Necessary data needed by [`context_codegen`] to generate code for a Tauri application context. @@ -32,14 +36,36 @@ pub struct ContextData { pub config: Config, pub config_parent: PathBuf, pub root: TokenStream, + /// Additional capabilities to include. + pub capabilities: Option>, + /// The custom assets implementation + pub assets: Option, +} + +fn inject_script_hashes(document: &NodeRef, key: &AssetKey, csp_hashes: &mut CspHashes) { + if let Ok(inline_script_elements) = document.select("script:not(empty)") { + let mut scripts = Vec::new(); + for inline_script_el in inline_script_elements { + let script = inline_script_el.as_node().text_contents(); + let mut hasher = Sha256::new(); + hasher.update(&script); + let hash = hasher.finalize(); + scripts.push(format!( + "'sha256-{}'", + base64::engine::general_purpose::STANDARD.encode(hash) + )); + } + csp_hashes + .inline_scripts + .entry(key.clone().into()) + .or_default() + .append(&mut scripts); + } } fn map_core_assets( options: &AssetOptions, - target: Target, ) -> impl Fn(&AssetKey, &Path, &mut Vec, &mut CspHashes) -> Result<(), EmbeddedAssetsError> { - #[cfg(feature = "isolation")] - let pattern = tauri_utils::html::PatternObject::from(&options.pattern); let csp = options.csp; let dangerous_disable_asset_csp_modification = options.dangerous_disable_asset_csp_modification.clone(); @@ -49,45 +75,10 @@ fn map_core_assets( if csp { let document = parse_html(String::from_utf8_lossy(input).into_owned()); - if target == Target::Linux { - ::tauri_utils::html::inject_csp_token(&document); - } - inject_nonce_token(&document, &dangerous_disable_asset_csp_modification); if dangerous_disable_asset_csp_modification.can_modify("script-src") { - if let Ok(inline_script_elements) = document.select("script:not(empty)") { - let mut scripts = Vec::new(); - for inline_script_el in inline_script_elements { - let script = inline_script_el.as_node().text_contents(); - let mut hasher = Sha256::new(); - hasher.update(&script); - let hash = hasher.finalize(); - scripts.push(format!( - "'sha256-{}'", - base64::engine::general_purpose::STANDARD.encode(hash) - )); - } - csp_hashes - .inline_scripts - .entry(key.clone().into()) - .or_default() - .append(&mut scripts); - } - } - - #[cfg(feature = "isolation")] - if dangerous_disable_asset_csp_modification.can_modify("style-src") { - if let tauri_utils::html::PatternObject::Isolation { .. } = &pattern { - // create the csp for the isolation iframe styling now, to make the runtime less complex - let mut hasher = Sha256::new(); - hasher.update(tauri_utils::pattern::isolation::IFRAME_STYLE); - let hash = hasher.finalize(); - csp_hashes.styles.push(format!( - "'sha256-{}'", - base64::engine::general_purpose::STANDARD.encode(hash) - )); - } + inject_script_hashes(&document, key, csp_hashes); } *input = serialize_html_node(&document); @@ -102,9 +93,18 @@ fn map_isolation( _options: &AssetOptions, dir: PathBuf, ) -> impl Fn(&AssetKey, &Path, &mut Vec, &mut CspHashes) -> Result<(), EmbeddedAssetsError> { - move |_key, path, input, _csp_hashes| { + // create the csp for the isolation iframe styling now, to make the runtime less complex + let mut hasher = Sha256::new(); + hasher.update(tauri_utils::pattern::isolation::IFRAME_STYLE); + let hash = hasher.finalize(); + let iframe_style_csp_hash = format!( + "'sha256-{}'", + base64::engine::general_purpose::STANDARD.encode(hash) + ); + + move |key, path, input, csp_hashes| { if path.extension() == Some(OsStr::new("html")) { - let isolation_html = tauri_utils::html::parse(String::from_utf8_lossy(input).into_owned()); + let isolation_html = parse_html(String::from_utf8_lossy(input).into_owned()); // this is appended, so no need to reverse order it tauri_utils::html::inject_codegen_isolation_script(&isolation_html); @@ -112,6 +112,15 @@ fn map_isolation( // temporary workaround for windows not loading assets tauri_utils::html::inline_isolation(&isolation_html, &dir); + inject_nonce_token( + &isolation_html, + &tauri_utils::config::DisabledCspModificationKind::Flag(false), + ); + + inject_script_hashes(&isolation_html, key, csp_hashes); + + csp_hashes.styles.push(iframe_style_csp_hash.clone()); + *input = isolation_html.to_string().as_bytes().to_vec() } @@ -126,6 +135,8 @@ pub fn context_codegen(data: ContextData) -> Result Result match url { FrontendDist::Url(_url) => Default::default(), FrontendDist::Directory(path) => { @@ -171,7 +185,7 @@ pub fn context_codegen(data: ContextData) -> Result EmbeddedAssets::new( files @@ -179,12 +193,13 @@ pub fn context_codegen(data: ContextData) -> Result>(), &options, - map_core_assets(&options, target), + map_core_assets(&options), )?, _ => unimplemented!(), }, None => Default::default(), - } + }; + quote!(#assets) }; let out_dir = { @@ -278,10 +293,10 @@ pub fn context_codegen(data: ContextData) -> Result Result quote!(#root::Pattern::Brownfield(std::marker::PhantomData)), + PatternKind::Brownfield => quote!(#root::Pattern::Brownfield), #[cfg(not(feature = "isolation"))] PatternKind::Isolation { dir: _ } => { - quote!(#root::Pattern::Brownfield(std::marker::PhantomData)) + quote!(#root::Pattern::Brownfield) } #[cfg(feature = "isolation")] PatternKind::Isolation { dir } => { @@ -371,7 +386,7 @@ pub fn context_codegen(data: ContextData) -> Result = if acl_file_path.exists() { let acl_file = std::fs::read_to_string(acl_file_path).expect("failed to read plugin manifest map"); @@ -381,7 +396,8 @@ pub fn context_codegen(data: ContextData) -> Result = if capabilities_file_path.exists() { + let mut capabilities_from_files: BTreeMap = if capabilities_file_path.exists() + { let capabilities_file = std::fs::read_to_string(capabilities_file_path).expect("failed to read capabilities"); serde_json::from_str(&capabilities_file).expect("failed to parse capabilities") @@ -389,7 +405,87 @@ pub fn context_codegen(data: ContextData) -> Result { + capabilities.insert(capability.identifier.clone(), capability.clone()); + } + CapabilityEntry::Reference(id) => { + let capability = capabilities_from_files + .remove(id) + .unwrap_or_else(|| panic!("capability with identifier {id} not found")); + capabilities.insert(id.clone(), capability); + } + } + } + capabilities + }; + + let acl_tokens = map_lit( + quote! { ::std::collections::BTreeMap }, + &acl, + str_lit, + identity, + ); + + if let Some(paths) = additional_capabilities { + for path in paths { + let capability = CapabilityFile::load(&path) + .unwrap_or_else(|e| panic!("failed to read capability {}: {e}", path.display())); + match capability { + CapabilityFile::Capability(c) => { + capabilities.insert(c.identifier.clone(), c); + } + CapabilityFile::List(capabilities_list) + | CapabilityFile::NamedList { + capabilities: capabilities_list, + } => { + capabilities.extend( + capabilities_list + .into_iter() + .map(|c| (c.identifier.clone(), c)), + ); + } + } + } + } + + let resolved = Resolved::resolve(&acl, capabilities, target).expect("failed to resolve ACL"); + let runtime_authority = quote!(#root::ipc::RuntimeAuthority::new(#acl_tokens, #resolved)); + + let plugin_global_api_script_file_list_path = out_dir.join(GLOBAL_API_SCRIPT_FILE_LIST_PATH); + let plugin_global_api_script = + if config.app.with_global_tauri && plugin_global_api_script_file_list_path.exists() { + let file_list_str = std::fs::read_to_string(plugin_global_api_script_file_list_path) + .expect("failed to read plugin global API script paths"); + let file_list = serde_json::from_str::>(&file_list_str) + .expect("failed to parse plugin global API script paths"); + + let mut plugins = Vec::new(); + for path in file_list { + plugins.push(std::fs::read_to_string(&path).unwrap_or_else(|e| { + panic!( + "failed to read plugin global API script {}: {e}", + path.display() + ) + })); + } + + Some(plugins) + } else { + None + }; + + let plugin_global_api_script = if let Some(scripts) = plugin_global_api_script { + let scripts = scripts.into_iter().map(|s| quote!(#s)); + quote!(::std::option::Option::Some(&[#(#scripts),*])) + } else { + quote!(::std::option::Option::None) + }; Ok(quote!({ #[allow(unused_mut, clippy::let_and_return)] @@ -401,7 +497,8 @@ pub fn context_codegen(data: ContextData) -> Result>( })?; let icon_file_name = icon_file_name.to_str().unwrap(); - let icon = quote!(#root::Icon::Rgba { - rgba: include_bytes!(concat!(std::env!("OUT_DIR"), "/", #icon_file_name)).to_vec(), - width: #width, - height: #height - }); + let icon = quote!(#root::image::Image::new(include_bytes!(concat!(std::env!("OUT_DIR"), "/", #icon_file_name)), #width, #height)); Ok(icon) } @@ -498,11 +591,7 @@ fn png_icon>( })?; let icon_file_name = icon_file_name.to_str().unwrap(); - let icon = quote!(#root::Icon::Rgba { - rgba: include_bytes!(concat!(std::env!("OUT_DIR"), "/", #icon_file_name)).to_vec(), - width: #width, - height: #height, - }); + let icon = quote!(#root::image::Image::new(include_bytes!(concat!(std::env!("OUT_DIR"), "/", #icon_file_name)), #width, #height)); Ok(icon) } diff --git a/core/tauri-codegen/src/embedded_assets.rs b/core/tauri-codegen/src/embedded_assets.rs index ce9e0fcb4..1f22b1860 100644 --- a/core/tauri-codegen/src/embedded_assets.rs +++ b/core/tauri-codegen/src/embedded_assets.rs @@ -1,4 +1,4 @@ -// Copyright 2019-2023 Tauri Programme within The Commons Conservancy +// Copyright 2019-2024 Tauri Programme within The Commons Conservancy // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT diff --git a/core/tauri-codegen/src/lib.rs b/core/tauri-codegen/src/lib.rs index eeb2d6c9f..ab8b95ffb 100644 --- a/core/tauri-codegen/src/lib.rs +++ b/core/tauri-codegen/src/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2019-2023 Tauri Programme within The Commons Conservancy +// Copyright 2019-2024 Tauri Programme within The Commons Conservancy // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT diff --git a/core/tauri-codegen/src/vendor/blake3_reference.rs b/core/tauri-codegen/src/vendor/blake3_reference.rs index c984af280..c783a81e5 100644 --- a/core/tauri-codegen/src/vendor/blake3_reference.rs +++ b/core/tauri-codegen/src/vendor/blake3_reference.rs @@ -1,4 +1,4 @@ -// Copyright 2019-2023 Tauri Programme within The Commons Conservancy +// Copyright 2019-2024 Tauri Programme within The Commons Conservancy // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT diff --git a/core/tauri-codegen/src/vendor/mod.rs b/core/tauri-codegen/src/vendor/mod.rs index 98c696d05..323fa5884 100644 --- a/core/tauri-codegen/src/vendor/mod.rs +++ b/core/tauri-codegen/src/vendor/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2019-2023 Tauri Programme within The Commons Conservancy +// Copyright 2019-2024 Tauri Programme within The Commons Conservancy // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT diff --git a/core/tauri-config-schema/build.rs b/core/tauri-config-schema/build.rs index 1c867b0ba..cfb52844c 100644 --- a/core/tauri-config-schema/build.rs +++ b/core/tauri-config-schema/build.rs @@ -1,4 +1,4 @@ -// Copyright 2019-2023 Tauri Programme within The Commons Conservancy +// Copyright 2019-2024 Tauri Programme within The Commons Conservancy // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT diff --git a/core/tauri-config-schema/schema.json b/core/tauri-config-schema/schema.json index e8c2be4a7..964c616d8 100644 --- a/core/tauri-config-schema/schema.json +++ b/core/tauri-config-schema/schema.json @@ -40,6 +40,7 @@ "enable": false, "scope": [] }, + "capabilities": [], "dangerousDisableAssetCspModification": false, "freezePrototype": false, "pattern": { @@ -158,6 +159,7 @@ "enable": false, "scope": [] }, + "capabilities": [], "dangerousDisableAssetCspModification": false, "freezePrototype": false, "pattern": { @@ -219,8 +221,8 @@ "null" ] }, - "fileDropEnabled": { - "description": "Whether the file drop is enabled or not on the webview. By default it is enabled.\n\nDisabling it is required to use drag and drop on the frontend on Windows.", + "dragDropEnabled": { + "description": "Whether the drag and drop is enabled or not on the webview. By default it is enabled.\n\nDisabling it is required to use HTML5 drag and drop on the frontend on Windows.", "default": true, "type": "boolean" }, @@ -448,6 +450,11 @@ "null" ], "format": "uri" + }, + "zoomHotkeysEnabled": { + "description": "Whether page zooming by hotkeys is enabled\n\n## Platform-specific:\n\n- **Windows**: Controls WebView2's [`IsZoomControlEnabled`](https://learn.microsoft.com/en-us/microsoft-edge/webview2/reference/winrt/microsoft_web_webview2_core/corewebview2settings?view=webview2-winrt-1.0.2420.47#iszoomcontrolenabled) setting. - **MacOS / Linux**: Injects a polyfill that zooms in and out with `ctrl/command` + `-/=`, 20% in each step, ranging from 20% to 1000%. Requires `webview:allow-set-webview-zoom` permission\n\n- **Android / iOS**: Unsupported.", + "default": false, + "type": "boolean" } }, "additionalProperties": false @@ -878,6 +885,14 @@ "$ref": "#/definitions/PatternKind" } ] + }, + "capabilities": { + "description": "List of capabilities that are enabled on the application.\n\nIf the list is empty, all capabilities are included.", + "default": [], + "type": "array", + "items": { + "$ref": "#/definitions/CapabilityEntry" + } } }, "additionalProperties": false @@ -1040,6 +1055,252 @@ } ] }, + "CapabilityEntry": { + "description": "A capability entry which can be either an inlined capability or a reference to a capability defined on its own file.", + "anyOf": [ + { + "description": "An inlined capability.", + "allOf": [ + { + "$ref": "#/definitions/Capability" + } + ] + }, + { + "description": "Reference to a capability identifier.", + "type": "string" + } + ] + }, + "Capability": { + "description": "a grouping and boundary mechanism developers can use to separate windows or plugins functionality from each other at runtime.\n\nIf a window is not matching any capability then it has no access to the IPC layer at all.\n\nThis can be done to create trust groups and reduce impact of vulnerabilities in certain plugins or windows. Windows can be added to a capability by exact name or glob patterns like *, admin-* or main-window.", + "type": "object", + "required": [ + "identifier", + "permissions" + ], + "properties": { + "identifier": { + "description": "Identifier of the capability.", + "type": "string" + }, + "description": { + "description": "Description of the capability.", + "default": "", + "type": "string" + }, + "remote": { + "description": "Configure remote URLs that can use the capability permissions.", + "anyOf": [ + { + "$ref": "#/definitions/CapabilityRemote" + }, + { + "type": "null" + } + ] + }, + "local": { + "description": "Whether this capability is enabled for local app URLs or not. Defaults to `true`.", + "default": true, + "type": "boolean" + }, + "windows": { + "description": "List of windows that uses this capability. Can be a glob pattern.\n\nOn multiwebview windows, prefer [`Self::webviews`] for a fine grained access control.", + "type": "array", + "items": { + "type": "string" + } + }, + "webviews": { + "description": "List of webviews that uses this capability. Can be a glob pattern.\n\nThis is only required when using on multiwebview contexts, by default all child webviews of a window that matches [`Self::windows`] are linked.", + "type": "array", + "items": { + "type": "string" + } + }, + "permissions": { + "description": "List of permissions attached to this capability. Must include the plugin name as prefix in the form of `${plugin-name}:${permission-name}`.", + "type": "array", + "items": { + "$ref": "#/definitions/PermissionEntry" + } + }, + "platforms": { + "description": "Target platforms this capability applies. By default all platforms are affected by this capability.", + "type": [ + "array", + "null" + ], + "items": { + "$ref": "#/definitions/Target" + } + } + } + }, + "CapabilityRemote": { + "description": "Configuration for remote URLs that are associated with the capability.", + "type": "object", + "required": [ + "urls" + ], + "properties": { + "urls": { + "description": "Remote domains this capability refers to using the [URLPattern standard](https://urlpattern.spec.whatwg.org/).\n\n# Examples\n\n- \"https://*.mydomain.dev\": allows subdomains of mydomain.dev - \"https://mydomain.dev/api/*\": allows any subpath of mydomain.dev/api", + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "PermissionEntry": { + "description": "An entry for a permission value in a [`Capability`] can be either a raw permission [`Identifier`] or an object that references a permission and extends its scope.", + "anyOf": [ + { + "description": "Reference a permission or permission set by identifier.", + "allOf": [ + { + "$ref": "#/definitions/Identifier" + } + ] + }, + { + "description": "Reference a permission or permission set by identifier and extends its scope.", + "type": "object", + "required": [ + "identifier" + ], + "properties": { + "identifier": { + "description": "Identifier of the permission or permission set.", + "allOf": [ + { + "$ref": "#/definitions/Identifier" + } + ] + }, + "allow": { + "description": "Data that defines what is allowed by the scope.", + "type": [ + "array", + "null" + ], + "items": { + "$ref": "#/definitions/Value" + } + }, + "deny": { + "description": "Data that defines what is denied by the scope.", + "type": [ + "array", + "null" + ], + "items": { + "$ref": "#/definitions/Value" + } + } + } + } + ] + }, + "Identifier": { + "type": "string" + }, + "Value": { + "description": "All supported ACL values.", + "anyOf": [ + { + "description": "Represents a null JSON value.", + "type": "null" + }, + { + "description": "Represents a [`bool`].", + "type": "boolean" + }, + { + "description": "Represents a valid ACL [`Number`].", + "allOf": [ + { + "$ref": "#/definitions/Number" + } + ] + }, + { + "description": "Represents a [`String`].", + "type": "string" + }, + { + "description": "Represents a list of other [`Value`]s.", + "type": "array", + "items": { + "$ref": "#/definitions/Value" + } + }, + { + "description": "Represents a map of [`String`] keys to [`Value`]s.", + "type": "object", + "additionalProperties": { + "$ref": "#/definitions/Value" + } + } + ] + }, + "Number": { + "description": "A valid ACL number.", + "anyOf": [ + { + "description": "Represents an [`i64`].", + "type": "integer", + "format": "int64" + }, + { + "description": "Represents a [`f64`].", + "type": "number", + "format": "double" + } + ] + }, + "Target": { + "description": "Platform target.", + "oneOf": [ + { + "description": "MacOS.", + "type": "string", + "enum": [ + "macOS" + ] + }, + { + "description": "Windows.", + "type": "string", + "enum": [ + "windows" + ] + }, + { + "description": "Linux.", + "type": "string", + "enum": [ + "linux" + ] + }, + { + "description": "Android.", + "type": "string", + "enum": [ + "android" + ] + }, + { + "description": "iOS.", + "type": "string", + "enum": [ + "iOS" + ] + } + ] + }, "TrayIconConfig": { "description": "Configuration for application tray icon.\n\nSee more: ", "type": "object", @@ -2166,6 +2427,36 @@ "type": "string" } }, + "provides": { + "description": "The list of dependencies the package provides.", + "type": [ + "array", + "null" + ], + "items": { + "type": "string" + } + }, + "conflicts": { + "description": "The list of package conflicts.", + "type": [ + "array", + "null" + ], + "items": { + "type": "string" + } + }, + "replaces": { + "description": "The list of package replaces.", + "type": [ + "array", + "null" + ], + "items": { + "type": "string" + } + }, "files": { "description": "The files to include on the package.", "default": {}, @@ -2174,12 +2465,61 @@ "type": "string" } }, + "section": { + "description": "Define the section in Debian Control file. See : https://www.debian.org/doc/debian-policy/ch-archive.html#s-subsections", + "type": [ + "string", + "null" + ] + }, + "priority": { + "description": "Change the priority of the Debian Package. By default, it is set to `optional`. Recognized Priorities as of now are : `required`, `important`, `standard`, `optional`, `extra`", + "type": [ + "string", + "null" + ] + }, + "changelog": { + "description": "Path of the uncompressed Changelog file, to be stored at /usr/share/doc/package-name/changelog.gz. See https://www.debian.org/doc/debian-policy/ch-docs.html#changelog-files-and-release-notes", + "type": [ + "string", + "null" + ] + }, "desktopTemplate": { "description": "Path to a custom desktop file Handlebars template.\n\nAvailable variables: `categories`, `comment` (optional), `exec`, `icon` and `name`.", "type": [ "string", "null" ] + }, + "preInstallScript": { + "description": "Path to script that will be executed before the package is unpacked. See https://www.debian.org/doc/debian-policy/ch-maintainerscripts.html", + "type": [ + "string", + "null" + ] + }, + "postInstallScript": { + "description": "Path to script that will be executed after the package is unpacked. See https://www.debian.org/doc/debian-policy/ch-maintainerscripts.html", + "type": [ + "string", + "null" + ] + }, + "preRemoveScript": { + "description": "Path to script that will be executed before the package is removed. See https://www.debian.org/doc/debian-policy/ch-maintainerscripts.html", + "type": [ + "string", + "null" + ] + }, + "postRemoveScript": { + "description": "Path to script that will be executed after the package is removed. See https://www.debian.org/doc/debian-policy/ch-maintainerscripts.html", + "type": [ + "string", + "null" + ] } }, "additionalProperties": false @@ -2198,6 +2538,36 @@ "type": "string" } }, + "provides": { + "description": "The list of RPM dependencies your application provides.", + "type": [ + "array", + "null" + ], + "items": { + "type": "string" + } + }, + "conflicts": { + "description": "The list of RPM dependencies your application conflicts with. They must not be present in order for the package to be installed.", + "type": [ + "array", + "null" + ], + "items": { + "type": "string" + } + }, + "obsoletes": { + "description": "The list of RPM dependencies your application supersedes - if this package is installed, packages listed as “obsoletes” will be automatically removed (if they are present).", + "type": [ + "array", + "null" + ], + "items": { + "type": "string" + } + }, "release": { "description": "The RPM release tag.", "default": "1", @@ -2224,6 +2594,34 @@ "string", "null" ] + }, + "preInstallScript": { + "description": "Path to script that will be executed before the package is unpacked. See http://ftp.rpm.org/max-rpm/s1-rpm-inside-scripts.html", + "type": [ + "string", + "null" + ] + }, + "postInstallScript": { + "description": "Path to script that will be executed after the package is unpacked. See http://ftp.rpm.org/max-rpm/s1-rpm-inside-scripts.html", + "type": [ + "string", + "null" + ] + }, + "preRemoveScript": { + "description": "Path to script that will be executed before the package is removed. See http://ftp.rpm.org/max-rpm/s1-rpm-inside-scripts.html", + "type": [ + "string", + "null" + ] + }, + "postRemoveScript": { + "description": "Path to script that will be executed after the package is removed. See http://ftp.rpm.org/max-rpm/s1-rpm-inside-scripts.html", + "type": [ + "string", + "null" + ] } }, "additionalProperties": false diff --git a/core/tauri-config-schema/src/main.rs b/core/tauri-config-schema/src/main.rs index df66520e3..53374aabb 100644 --- a/core/tauri-config-schema/src/main.rs +++ b/core/tauri-config-schema/src/main.rs @@ -1,4 +1,4 @@ -// Copyright 2019-2023 Tauri Programme within The Commons Conservancy +// Copyright 2019-2024 Tauri Programme within The Commons Conservancy // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT diff --git a/core/tauri-macros/CHANGELOG.md b/core/tauri-macros/CHANGELOG.md index d473f0d2e..52a40da48 100644 --- a/core/tauri-macros/CHANGELOG.md +++ b/core/tauri-macros/CHANGELOG.md @@ -1,5 +1,83 @@ # Changelog +## \[2.0.0-beta.11] + +### Dependencies + +- Upgraded to `tauri-utils@2.0.0-beta.11` +- Upgraded to `tauri-codegen@2.0.0-beta.11` + +## \[2.0.0-beta.10] + +### Dependencies + +- Upgraded to `tauri-utils@2.0.0-beta.10` +- Upgraded to `tauri-codegen@2.0.0-beta.10` + +## \[2.0.0-beta.9] + +### New Features + +- [`ba0206d8a`](https://www.github.com/tauri-apps/tauri/commit/ba0206d8a30a9b43ec5090dcaabd1a23baa1420c)([#9141](https://www.github.com/tauri-apps/tauri/pull/9141)) The `Context` codegen now accepts a `assets` input to define a custom `tauri::Assets` implementation. + +### Dependencies + +- Upgraded to `tauri-codegen@2.0.0-beta.9` +- Upgraded to `tauri-utils@2.0.0-beta.9` + +## \[2.0.0-beta.8] + +### Dependencies + +- Upgraded to `tauri-utils@2.0.0-beta.8` +- Upgraded to `tauri-codegen@2.0.0-beta.8` + +## \[2.0.0-beta.7] + +### Dependencies + +- Upgraded to `tauri-utils@2.0.0-beta.7` +- Upgraded to `tauri-codegen@2.0.0-beta.7` + +## \[2.0.0-beta.6] + +### Dependencies + +- Upgraded to `tauri-utils@2.0.0-beta.6` +- Upgraded to `tauri-codegen@2.0.0-beta.6` + +## \[2.0.0-beta.5] + +### Dependencies + +- Upgraded to `tauri-utils@2.0.0-beta.5` +- Upgraded to `tauri-codegen@2.0.0-beta.5` + +## \[2.0.0-beta.4] + +### Dependencies + +- Upgraded to `tauri-utils@2.0.0-beta.4` +- Upgraded to `tauri-codegen@2.0.0-beta.4` + +## \[2.0.0-beta.3] + +### Dependencies + +- Upgraded to `tauri-utils@2.0.0-beta.3` +- Upgraded to `tauri-codegen@2.0.0-beta.3` + +## \[2.0.0-beta.2] + +### Enhancements + +- [`8d16a80d`](https://www.github.com/tauri-apps/tauri/commit/8d16a80d2fb2468667e7987d0cc99dbc7e3b9d0a)([#8802](https://www.github.com/tauri-apps/tauri/pull/8802)) The `generate_context` proc macro now accepts a `capabilities` attribute where the value is an array of file paths that can be [conditionally compiled](https://doc.rust-lang.org/reference/conditional-compilation.html). These capabilities are added to the application along the capabilities defined in the Tauri configuration file. + +### Dependencies + +- Upgraded to `tauri-utils@2.0.0-beta.2` +- Upgraded to `tauri-codegen@2.0.0-beta.2` + ## \[2.0.0-beta.1] ### Dependencies @@ -141,6 +219,13 @@ - First mobile alpha release! - [fa3a1098](https://www.github.com/tauri-apps/tauri/commit/fa3a10988a03aed1b66fb17d893b1a9adb90f7cd) feat(ci): prepare 2.0.0-alpha.0 ([#5786](https://www.github.com/tauri-apps/tauri/pull/5786)) on 2022-12-08 +## \[1.4.3] + +### Dependencies + +- Upgraded to `tauri-utils@1.5.2` +- Upgraded to `tauri-codegen@1.4.2` + ## \[1.4.2] ### Enhancements diff --git a/core/tauri-macros/Cargo.toml b/core/tauri-macros/Cargo.toml index d3d64c5f6..eb554aa16 100644 --- a/core/tauri-macros/Cargo.toml +++ b/core/tauri-macros/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "tauri-macros" -version = "2.0.0-beta.1" +version = "2.0.0-beta.11" description = "Macros for the tauri crate." exclude = [ "CHANGELOG.md", "/target" ] readme = "README.md" @@ -20,8 +20,8 @@ proc-macro2 = { version = "1", features = [ "span-locations" ] } quote = "1" syn = { version = "2", features = [ "full" ] } heck = "0.4" -tauri-codegen = { version = "2.0.0-beta.1", default-features = false, path = "../tauri-codegen" } -tauri-utils = { version = "2.0.0-beta.1", path = "../tauri-utils" } +tauri-codegen = { version = "2.0.0-beta.11", default-features = false, path = "../tauri-codegen" } +tauri-utils = { version = "2.0.0-beta.11", path = "../tauri-utils" } [features] custom-protocol = [ ] diff --git a/core/tauri-macros/src/command/handler.rs b/core/tauri-macros/src/command/handler.rs index 62bf999a6..f422e8bac 100644 --- a/core/tauri-macros/src/command/handler.rs +++ b/core/tauri-macros/src/command/handler.rs @@ -1,4 +1,4 @@ -// Copyright 2019-2023 Tauri Programme within The Commons Conservancy +// Copyright 2019-2024 Tauri Programme within The Commons Conservancy // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT diff --git a/core/tauri-macros/src/command/mod.rs b/core/tauri-macros/src/command/mod.rs index 35c4661c3..fc3bbb681 100644 --- a/core/tauri-macros/src/command/mod.rs +++ b/core/tauri-macros/src/command/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2019-2023 Tauri Programme within The Commons Conservancy +// Copyright 2019-2024 Tauri Programme within The Commons Conservancy // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT diff --git a/core/tauri-macros/src/command/wrapper.rs b/core/tauri-macros/src/command/wrapper.rs index a4a2c4a60..306d964e8 100644 --- a/core/tauri-macros/src/command/wrapper.rs +++ b/core/tauri-macros/src/command/wrapper.rs @@ -1,4 +1,4 @@ -// Copyright 2019-2023 Tauri Programme within The Commons Conservancy +// Copyright 2019-2024 Tauri Programme within The Commons Conservancy // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT diff --git a/core/tauri-macros/src/context.rs b/core/tauri-macros/src/context.rs index 2bf5eef1f..9b166919b 100644 --- a/core/tauri-macros/src/context.rs +++ b/core/tauri-macros/src/context.rs @@ -1,14 +1,14 @@ -// Copyright 2019-2023 Tauri Programme within The Commons Conservancy +// Copyright 2019-2024 Tauri Programme within The Commons Conservancy // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT use proc_macro2::{Ident, Span, TokenStream}; use quote::{quote, ToTokens}; -use std::{env::VarError, path::PathBuf}; +use std::path::PathBuf; use syn::{ parse::{Parse, ParseBuffer}, punctuated::Punctuated, - LitStr, PathArguments, PathSegment, Token, + Expr, ExprLit, Lit, LitStr, Meta, PathArguments, PathSegment, Token, }; use tauri_codegen::{context_codegen, get_config, ContextData}; use tauri_utils::{config::parse::does_supported_file_name_exist, platform::Target}; @@ -16,6 +16,8 @@ use tauri_utils::{config::parse::does_supported_file_name_exist, platform::Targe pub(crate) struct ContextItems { config_file: PathBuf, root: syn::Path, + capabilities: Option>, + assets: Option, } impl Parse for ContextItems { @@ -26,51 +28,106 @@ impl Parse for ContextItems { .map(Target::from_triple) .unwrap_or_else(|_| Target::current()); - let config_file = if input.is_empty() { - std::env::var("CARGO_MANIFEST_DIR").map(|m| PathBuf::from(m).join("tauri.conf.json")) - } else { - let raw: LitStr = input.parse()?; + let mut root = None; + let mut capabilities = None; + let mut assets = None; + let config_file = input.parse::().ok().map(|raw| { + let _ = input.parse::(); let path = PathBuf::from(raw.value()); if path.is_relative() { - std::env::var("CARGO_MANIFEST_DIR").map(|m| PathBuf::from(m).join(path)) + std::env::var("CARGO_MANIFEST_DIR") + .map(|m| PathBuf::from(m).join(path)) + .map_err(|e| e.to_string()) } else { Ok(path) } + .and_then(|path| { + if does_supported_file_name_exist(target, &path) { + Ok(path) + } else { + Err(format!( + "no file at path {} exists, expected tauri config file", + path.display() + )) + } + }) + }); + + while let Ok(meta) = input.parse::() { + match meta { + Meta::Path(p) => { + root.replace(p); + } + Meta::NameValue(v) => { + let ident = v.path.require_ident()?; + match ident.to_string().as_str() { + "capabilities" => { + if let Expr::Array(array) = v.value { + capabilities.replace( + array + .elems + .into_iter() + .map(|e| { + if let Expr::Lit(ExprLit { + attrs: _, + lit: Lit::Str(s), + }) = e + { + Ok(s.value().into()) + } else { + Err(syn::Error::new( + input.span(), + "unexpected expression for capability", + )) + } + }) + .collect::, syn::Error>>()?, + ); + } else { + return Err(syn::Error::new( + input.span(), + "unexpected value for capabilities", + )); + } + } + "assets" => { + assets.replace(v.value); + } + name => { + return Err(syn::Error::new( + input.span(), + format!("unknown attribute {name}"), + )); + } + } + } + Meta::List(_) => { + return Err(syn::Error::new(input.span(), "unexpected list input")); + } + } } - .map_err(|error| match error { - VarError::NotPresent => "no CARGO_MANIFEST_DIR env var, this should be set by cargo".into(), - VarError::NotUnicode(_) => "CARGO_MANIFEST_DIR env var contained invalid utf8".into(), - }) - .and_then(|path| { - if does_supported_file_name_exist(target, &path) { - Ok(path) - } else { - Err(format!( - "no file at path {} exists, expected tauri config file", - path.display() - )) - } - }) - .map_err(|e| input.error(e))?; - - let context_path = if input.is_empty() { - let mut segments = Punctuated::new(); - segments.push(PathSegment { - ident: Ident::new("tauri", Span::call_site()), - arguments: PathArguments::None, - }); - syn::Path { - leading_colon: Some(Token![::](Span::call_site())), - segments, - } - } else { - let _: Token![,] = input.parse()?; - input.call(syn::Path::parse_mod_style)? - }; Ok(Self { - config_file, - root: context_path, + config_file: config_file + .unwrap_or_else(|| { + std::env::var("CARGO_MANIFEST_DIR") + .map(|m| PathBuf::from(m).join("tauri.conf.json")) + .map_err(|e| e.to_string()) + }) + .map_err(|e| input.error(e))?, + root: root.unwrap_or_else(|| { + let mut segments = Punctuated::new(); + segments.push(PathSegment { + ident: Ident::new("tauri", Span::call_site()), + arguments: PathArguments::None, + }); + syn::Path { + leading_colon: Some(Token![::](Span::call_site())), + segments, + } + }), + capabilities, + assets, }) } } @@ -83,6 +140,8 @@ pub(crate) fn generate_context(context: ContextItems) -> TokenStream { config, config_parent, root: context.root.to_token_stream(), + capabilities: context.capabilities, + assets: context.assets, }) .and_then(|data| context_codegen(data).map_err(|e| e.to_string())); diff --git a/core/tauri-macros/src/lib.rs b/core/tauri-macros/src/lib.rs index c45f3724d..a5d55748d 100644 --- a/core/tauri-macros/src/lib.rs +++ b/core/tauri-macros/src/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2019-2023 Tauri Programme within The Commons Conservancy +// Copyright 2019-2024 Tauri Programme within The Commons Conservancy // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT @@ -94,19 +94,19 @@ pub fn default_runtime(attributes: TokenStream, input: TokenStream) -> TokenStre /// Accepts a closure-like syntax to call arbitrary code on a menu item /// after matching against `kind` and retrieving it from `resources_table` using `rid`. /// -/// You can optionally pass a third parameter to select which item kinds +/// You can optionally pass a 5th parameter to select which item kinds /// to match against, by providing a `|` separated list of item kinds /// ```ignore -/// do_menu_item!(|i| i.set_text(text), Check | Submenu); +/// do_menu_item!(resources_table, rid, kind, |i| i.set_text(text), Check | Submenu); /// ``` /// You could also provide a negated list /// ```ignore -/// do_menu_item!(|i| i.set_text(text), !Check); -/// do_menu_item!(|i| i.set_text(text), !Check | !Submenu); +/// do_menu_item!(resources_table, rid, kind, |i| i.set_text(text), !Check); +/// do_menu_item!(resources_table, rid, kind, |i| i.set_text(text), !Check | !Submenu); /// ``` /// but you can't have mixed negations and positive kinds. /// ```ignore -/// do_menu_item!(|i| i.set_text(text), !Check | Submeun); +/// do_menu_item!(resources_table, rid, kind, |i| i.set_text(text), !Check | Submeun); /// ``` /// /// #### Example @@ -115,7 +115,7 @@ pub fn default_runtime(attributes: TokenStream, input: TokenStream) -> TokenStre /// let rid = 23; /// let kind = ItemKind::Check; /// let resources_table = app.resources_table(); -/// do_menu_item!(|i| i.set_text(text)) +/// do_menu_item!(resources_table, rid, kind, |i| i.set_text(text)) /// ``` /// which will expand into: /// ```ignore diff --git a/core/tauri-macros/src/menu.rs b/core/tauri-macros/src/menu.rs index 2e6dc9bd8..1e230d152 100644 --- a/core/tauri-macros/src/menu.rs +++ b/core/tauri-macros/src/menu.rs @@ -1,4 +1,4 @@ -// Copyright 2019-2023 Tauri Programme within The Commons Conservancy +// Copyright 2019-2024 Tauri Programme within The Commons Conservancy // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT @@ -20,13 +20,32 @@ pub struct DoMenuItemInput { } #[derive(Clone)] -struct NegatedIdent(bool, Ident); +struct NegatedIdent { + negated: bool, + ident: Ident, +} + +impl NegatedIdent { + fn new(ident: &str) -> Self { + Self { + negated: false, + ident: Ident::new(ident, Span::call_site()), + } + } + + fn is_negated(&self) -> bool { + self.negated + } +} impl Parse for NegatedIdent { fn parse(input: ParseStream) -> syn::Result { - let t = input.parse::(); - let i: Ident = input.parse()?; - Ok(NegatedIdent(t.is_ok(), i)) + let negated_token = input.parse::(); + let ident: Ident = input.parse()?; + Ok(NegatedIdent { + negated: negated_token.is_ok(), + ident, + }) } } @@ -67,32 +86,31 @@ pub fn do_menu_item(input: DoMenuItemInput) -> TokenStream { } = input; let defaults = vec![ - NegatedIdent(false, Ident::new("Submenu", Span::call_site())), - NegatedIdent(false, Ident::new("MenuItem", Span::call_site())), - NegatedIdent(false, Ident::new("Predefined", Span::call_site())), - NegatedIdent(false, Ident::new("Check", Span::call_site())), - NegatedIdent(false, Ident::new("Icon", Span::call_site())), + NegatedIdent::new("Submenu"), + NegatedIdent::new("MenuItem"), + NegatedIdent::new("Predefined"), + NegatedIdent::new("Check"), + NegatedIdent::new("Icon"), ]; if kinds.is_empty() { kinds.extend(defaults.clone()); } - let has_negated = kinds.iter().any(|n| n.0); - + let has_negated = kinds.iter().any(|n| n.is_negated()); if has_negated { kinds.extend(defaults); - kinds.sort_by(|a, b| a.1.cmp(&b.1)); - kinds.dedup_by(|a, b| a.1 == b.1); + kinds.sort_by(|a, b| a.ident.cmp(&b.ident)); + kinds.dedup_by(|a, b| a.ident == b.ident); } let (kinds, types): (Vec, Vec) = kinds .into_iter() .filter_map(|nident| { - if nident.0 { + if nident.is_negated() { None } else { - match nident.1 { + match nident.ident { i if i == "MenuItem" => Some((i, Ident::new("MenuItem", Span::call_site()))), i if i == "Submenu" => Some((i, Ident::new("Submenu", Span::call_site()))), i if i == "Predefined" => Some((i, Ident::new("PredefinedMenuItem", Span::call_site()))), diff --git a/core/tauri-macros/src/mobile.rs b/core/tauri-macros/src/mobile.rs index eae55fa68..d18af1eb4 100644 --- a/core/tauri-macros/src/mobile.rs +++ b/core/tauri-macros/src/mobile.rs @@ -1,4 +1,4 @@ -// Copyright 2019-2023 Tauri Programme within The Commons Conservancy +// Copyright 2019-2024 Tauri Programme within The Commons Conservancy // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT diff --git a/core/tauri-macros/src/runtime.rs b/core/tauri-macros/src/runtime.rs index 94bcf89ef..d7ad940cc 100644 --- a/core/tauri-macros/src/runtime.rs +++ b/core/tauri-macros/src/runtime.rs @@ -1,4 +1,4 @@ -// Copyright 2019-2023 Tauri Programme within The Commons Conservancy +// Copyright 2019-2024 Tauri Programme within The Commons Conservancy // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT diff --git a/core/tauri-plugin/CHANGELOG.md b/core/tauri-plugin/CHANGELOG.md index 2a15448d0..4a7ace686 100644 --- a/core/tauri-plugin/CHANGELOG.md +++ b/core/tauri-plugin/CHANGELOG.md @@ -1,5 +1,78 @@ # Changelog +## \[2.0.0-beta.11] + +### Dependencies + +- Upgraded to `tauri-utils@2.0.0-beta.11` + +## \[2.0.0-beta.10] + +### New Features + +- [`e227fe02f`](https://www.github.com/tauri-apps/tauri/commit/e227fe02f986e145c0731a64693e1c830a9eb5b0)([#9156](https://www.github.com/tauri-apps/tauri/pull/9156)) Allow plugins to define (at compile time) JavaScript that are initialized when `withGlobalTauri` is true. +- [`e227fe02f`](https://www.github.com/tauri-apps/tauri/commit/e227fe02f986e145c0731a64693e1c830a9eb5b0)([#9156](https://www.github.com/tauri-apps/tauri/pull/9156)) Added `Builder::global_api_script_path` to define a JavaScript file containing the initialization script for the plugin API bindings when `withGlobalTauri` is used. + +### Dependencies + +- Upgraded to `tauri-utils@2.0.0-beta.10` + +## \[2.0.0-beta.9] + +### Dependencies + +- Upgraded to `tauri-utils@2.0.0-beta.9` + +## \[2.0.0-beta.8] + +### Dependencies + +- Upgraded to `tauri-utils@2.0.0-beta.8` + +## \[2.0.0-beta.7] + +### Dependencies + +- Upgraded to `tauri-utils@2.0.0-beta.7` + +## \[2.0.0-beta.6] + +### Dependencies + +- Upgraded to `tauri-utils@2.0.0-beta.6` + +### Breaking Changes + +- [`3657ad82`](https://www.github.com/tauri-apps/tauri/commit/3657ad82f88ce528551d032d521c52eed3f396b4)([#9008](https://www.github.com/tauri-apps/tauri/pull/9008)) Allow defining permissions for the application commands via `tauri_build::Attributes::app_manifest`. + +## \[2.0.0-beta.5] + +### Dependencies + +- Upgraded to `tauri-utils@2.0.0-beta.5` + +## \[2.0.0-beta.4] + +### Dependencies + +- Upgraded to `tauri-utils@2.0.0-beta.4` + +## \[2.0.0-beta.3] + +### Dependencies + +- Upgraded to `tauri-utils@2.0.0-beta.3` + +## \[2.0.0-beta.2] + +### Enhancements + +- [`dd7571a7`](https://www.github.com/tauri-apps/tauri/commit/dd7571a7808676c8063a4983b9c6687dfaf03a09)([#8815](https://www.github.com/tauri-apps/tauri/pull/8815)) Do not generate JSON schema and markdown reference file if the plugin does not define any permissions and delete those files if they exist. + +### Dependencies + +- Upgraded to `tauri-utils@2.0.0-beta.2` + ## \[2.0.0-beta.1] ### Bug Fixes diff --git a/core/tauri-plugin/Cargo.toml b/core/tauri-plugin/Cargo.toml index a4cb4b00b..86e798fd1 100644 --- a/core/tauri-plugin/Cargo.toml +++ b/core/tauri-plugin/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "tauri-plugin" -version = "2.0.0-beta.1" +version = "2.0.0-beta.11" description = "Build script and runtime Tauri plugin definitions" authors = { workspace = true } homepage = { workspace = true } @@ -30,12 +30,12 @@ runtime = [ ] [dependencies] anyhow = { version = "1", optional = true } serde = { version = "1", optional = true } -tauri-utils = { version = "2.0.0-beta.1", default-features = false, path = "../tauri-utils" } +tauri-utils = { version = "2.0.0-beta.11", default-features = false, features = [ "build" ], path = "../tauri-utils" } serde_json = { version = "1", optional = true } glob = { version = "0.3", optional = true } toml = { version = "0.8", optional = true } schemars = { version = "0.8", features = [ "preserve_order" ] } -walkdir = { version = "1", optional = true } +walkdir = { version = "2", optional = true } [target."cfg(target_os = \"macos\")".dependencies] plist = { version = "1", optional = true } diff --git a/core/tauri-plugin/src/build/mobile.rs b/core/tauri-plugin/src/build/mobile.rs index 747c60cab..af6535f31 100644 --- a/core/tauri-plugin/src/build/mobile.rs +++ b/core/tauri-plugin/src/build/mobile.rs @@ -1,4 +1,4 @@ -// Copyright 2019-2023 Tauri Programme within The Commons Conservancy +// Copyright 2019-2024 Tauri Programme within The Commons Conservancy // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT @@ -183,12 +183,12 @@ fn insert_into_xml(xml: &str, block_identifier: &str, parent_tag: &str, contents } if let Some(index) = line.find(&parent_closing_tag) { - let identation = " ".repeat(index + 4); - rewritten.push(format!("{}{}", identation, block_comment)); + let indentation = " ".repeat(index + 4); + rewritten.push(format!("{}{}", indentation, block_comment)); for l in contents.split('\n') { - rewritten.push(format!("{}{}", identation, l)); + rewritten.push(format!("{}{}", indentation, l)); } - rewritten.push(format!("{}{}", identation, block_comment)); + rewritten.push(format!("{}{}", indentation, block_comment)); } rewritten.push(line.to_string()); diff --git a/core/tauri-plugin/src/build/mod.rs b/core/tauri-plugin/src/build/mod.rs index a3f0dd297..559fd78b4 100644 --- a/core/tauri-plugin/src/build/mod.rs +++ b/core/tauri-plugin/src/build/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2019-2023 Tauri Programme within The Commons Conservancy +// Copyright 2019-2024 Tauri Programme within The Commons Conservancy // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT @@ -31,6 +31,7 @@ pub fn plugin_config(name: &str) -> Option { pub struct Builder<'a> { commands: &'a [&'static str], global_scope_schema: Option, + global_api_script_path: Option, android_path: Option, ios_path: Option, } @@ -40,6 +41,7 @@ impl<'a> Builder<'a> { Self { commands, global_scope_schema: None, + global_api_script_path: None, android_path: None, ios_path: None, } @@ -51,6 +53,14 @@ impl<'a> Builder<'a> { self } + /// Sets the path to the script that is injected in the webview when the `withGlobalTauri` configuration is set to true. + /// + /// This is usually an IIFE that injects the plugin API JavaScript bindings to `window.__TAURI__`. + pub fn global_api_script_path>(mut self, path: P) -> Self { + self.global_api_script_path.replace(path.into()); + self + } + /// Sets the Android project path. pub fn android_path>(mut self, android_path: P) -> Self { self.android_path.replace(android_path.into()); @@ -95,17 +105,18 @@ impl<'a> Builder<'a> { std::fs::create_dir_all(&autogenerated).expect("unable to create permissions dir"); if !self.commands.is_empty() { - acl::build::autogenerate_command_permissions(&commands_dir, self.commands, ""); + acl::build::autogenerate_command_permissions(&commands_dir, self.commands, "", true); } println!("cargo:rerun-if-changed=permissions"); - let permissions = acl::build::define_permissions("./permissions/**/*.*", &name, &out_dir)?; + let permissions = + acl::build::define_permissions("./permissions/**/*.*", &name, &out_dir, |_| true)?; if permissions.is_empty() { let _ = std::fs::remove_file(format!( "./permissions/{}/{}", acl::build::PERMISSION_SCHEMAS_FOLDER_NAME, - acl::build::PERMISSION_SCHEMA_FILE_NAME + acl::PERMISSION_SCHEMA_FILE_NAME )); let _ = std::fs::remove_file(autogenerated.join(acl::build::PERMISSION_DOCS_FILE_NAME)); } else { @@ -117,6 +128,10 @@ impl<'a> Builder<'a> { acl::build::define_global_scope_schema(global_scope_schema, &name, &out_dir)?; } + if let Some(path) = self.global_api_script_path { + tauri_utils::plugin::define_global_api_script_path(path); + } + mobile::setup(self.android_path, self.ios_path)?; Ok(()) diff --git a/core/tauri-plugin/src/lib.rs b/core/tauri-plugin/src/lib.rs index c32c3e683..131a1a91f 100644 --- a/core/tauri-plugin/src/lib.rs +++ b/core/tauri-plugin/src/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2019-2023 Tauri Programme within The Commons Conservancy +// Copyright 2019-2024 Tauri Programme within The Commons Conservancy // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT @@ -22,4 +22,5 @@ mod runtime; pub use build::*; #[cfg(feature = "runtime")] #[cfg_attr(docsrs, doc(feature = "runtime"))] +#[allow(unused)] pub use runtime::*; diff --git a/core/tauri-plugin/src/runtime.rs b/core/tauri-plugin/src/runtime.rs index a05603857..3d77814f5 100644 --- a/core/tauri-plugin/src/runtime.rs +++ b/core/tauri-plugin/src/runtime.rs @@ -1,3 +1,3 @@ -// Copyright 2019-2023 Tauri Programme within The Commons Conservancy +// Copyright 2019-2024 Tauri Programme within The Commons Conservancy // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT diff --git a/core/tauri-runtime-wry/CHANGELOG.md b/core/tauri-runtime-wry/CHANGELOG.md index 3cf229649..d6b26d395 100644 --- a/core/tauri-runtime-wry/CHANGELOG.md +++ b/core/tauri-runtime-wry/CHANGELOG.md @@ -1,5 +1,121 @@ # Changelog +## \[2.0.0-beta.11] + +### Bug Fixes + +- [`4c0c780e0`](https://www.github.com/tauri-apps/tauri/commit/4c0c780e00d8851be38cb1c22f636d9e4ed34a23)([#2690](https://www.github.com/tauri-apps/tauri/pull/2690)) Fix window inner size evaluation on macOS. +- [`5bd47b446`](https://www.github.com/tauri-apps/tauri/commit/5bd47b44673f74b1b4e8d704b7a95539915ede76)([#9246](https://www.github.com/tauri-apps/tauri/pull/9246)) Fix webview's visibility doesn't change with the app window + +### What's Changed + +- [`06833f4fa`](https://www.github.com/tauri-apps/tauri/commit/06833f4fa8e63ecc55fe3fc874a9e397e77a5709)([#9100](https://www.github.com/tauri-apps/tauri/pull/9100)) Updated `http` crate to `1.1` + +### Dependencies + +- Upgraded to `tauri-utils@2.0.0-beta.11` +- Upgraded to `tauri-runtime@2.0.0-beta.11` +- [`06833f4fa`](https://www.github.com/tauri-apps/tauri/commit/06833f4fa8e63ecc55fe3fc874a9e397e77a5709)([#9100](https://www.github.com/tauri-apps/tauri/pull/9100)) Upgraded to `wry@0.38.0` + +### Breaking Changes + +- [`06833f4fa`](https://www.github.com/tauri-apps/tauri/commit/06833f4fa8e63ecc55fe3fc874a9e397e77a5709)([#9100](https://www.github.com/tauri-apps/tauri/pull/9100)) The IPC handler closure now receives a `http::Request` instead of a String representing the request body. +- [`06833f4fa`](https://www.github.com/tauri-apps/tauri/commit/06833f4fa8e63ecc55fe3fc874a9e397e77a5709)([#9100](https://www.github.com/tauri-apps/tauri/pull/9100)) Rename `FileDrop` to `DragDrop` on structs, enums and enum variants. Also renamed `file_drop` to `drag_drop` on fields and function names. + +## \[2.0.0-beta.10] + +### Dependencies + +- Upgraded to `tauri-utils@2.0.0-beta.10` +- Upgraded to `tauri-runtime@2.0.0-beta.10` + +## \[2.0.0-beta.9] + +### Dependencies + +- Upgraded to `tauri-utils@2.0.0-beta.9` +- Upgraded to `tauri-runtime@2.0.0-beta.9` + +## \[2.0.0-beta.8] + +### Dependencies + +- Upgraded to `tauri-utils@2.0.0-beta.8` +- Upgraded to `tauri-runtime@2.0.0-beta.8` + +## \[2.0.0-beta.7] + +### New Features + +- [`46de49aaa`](https://www.github.com/tauri-apps/tauri/commit/46de49aaad4a148fafc31d591be0e2ed12256507)([#9059](https://www.github.com/tauri-apps/tauri/pull/9059)) Added `set_auto_resize` method for the webview. + +### Enhancements + +- [`46de49aaa`](https://www.github.com/tauri-apps/tauri/commit/46de49aaad4a148fafc31d591be0e2ed12256507)([#9059](https://www.github.com/tauri-apps/tauri/pull/9059)) When using the `unstable` feature flag, `WebviewWindow` will internally use the child webview interface for flexibility. + +### Dependencies + +- Upgraded to `tauri-utils@2.0.0-beta.7` +- Upgraded to `tauri-runtime@2.0.0-beta.7` + +## \[2.0.0-beta.6] + +### Bug Fixes + +- [`222a96b7`](https://www.github.com/tauri-apps/tauri/commit/222a96b74b145fb48d3f0c109897962d56fae57a)([#8999](https://www.github.com/tauri-apps/tauri/pull/8999)) Fixes auto resize and positioning when using the multiwebview mode. + +### Dependencies + +- Upgraded to `tauri-utils@2.0.0-beta.6` +- Upgraded to `tauri-runtime@2.0.0-beta.6` + +## \[2.0.0-beta.5] + +### Dependencies + +- Upgraded to `tauri-utils@2.0.0-beta.5` +- Upgraded to `tauri-runtime@2.0.0-beta.5` + +## \[2.0.0-beta.4] + +### New Features + +- [`fdcaf935`](https://www.github.com/tauri-apps/tauri/commit/fdcaf935fa75ecfa2806939c4faad4fe9e880386)([#8939](https://www.github.com/tauri-apps/tauri/pull/8939)) Added the `reparent` function to the webview API. + +### Bug Fixes + +- [`6e3bd4b9`](https://www.github.com/tauri-apps/tauri/commit/6e3bd4b9f815ddde8b5eaf9f69991d4de80bb584)([#8942](https://www.github.com/tauri-apps/tauri/pull/8942)) Fix window centering not taking monitor scale into account + +### Dependencies + +- Upgraded to `tauri-utils@2.0.0-beta.4` +- Upgraded to `tauri-runtime@2.0.0-beta.4` +- [`d75713ac`](https://www.github.com/tauri-apps/tauri/commit/d75713ac6c6115534e520303f5c38aa78704de69)([#8936](https://www.github.com/tauri-apps/tauri/pull/8936)) Upgraded to `wry@0.37.0` + +## \[2.0.0-beta.3] + +### Dependencies + +- Upgraded to `tauri-utils@2.0.0-beta.3` +- Upgraded to `tauri-runtime@2.0.0-beta.3` + +## \[2.0.0-beta.2] + +### What's Changed + +- [`76ce9f61`](https://www.github.com/tauri-apps/tauri/commit/76ce9f61dd3c5bdd589c7557543894e1f770dd16)([#3002](https://www.github.com/tauri-apps/tauri/pull/3002)) Enhance centering a newly created window, it will no longer jump to center after being visible. +- [`16e550ec`](https://www.github.com/tauri-apps/tauri/commit/16e550ec1503765158cdc3bb2a20e70ec710e981)([#8844](https://www.github.com/tauri-apps/tauri/pull/8844)) Add `WebviewEvent`, `RunEvent::WebviewEvent` and `WebviewDispatch::on_webview_event`. + +### Dependencies + +- Upgraded to `tauri-utils@2.0.0-beta.2` +- Upgraded to `tauri-runtime@2.0.0-beta.2` +- [`2f55bfec`](https://www.github.com/tauri-apps/tauri/commit/2f55bfecbf0244f3b5aa1ad7622182fca3fcdcbb)([#8795](https://www.github.com/tauri-apps/tauri/pull/8795)) Update `wry` to 0.36. + +### Breaking Changes + +- [`2f55bfec`](https://www.github.com/tauri-apps/tauri/commit/2f55bfecbf0244f3b5aa1ad7622182fca3fcdcbb)([#8795](https://www.github.com/tauri-apps/tauri/pull/8795)) Update raw-window-handle to 0.6. + ## \[2.0.0-beta.1] ### Dependencies @@ -47,10 +163,6 @@ - [`d621d343`](https://www.github.com/tauri-apps/tauri/commit/d621d3437ce3947175eecf345b2c6d1c4c7ce020)([#8607](https://www.github.com/tauri-apps/tauri/pull/8607)) Added tracing for window startup, plugins, `Window::eval`, events, IPC, updater and custom protocol request handlers behind the `tracing` feature flag. -### Bug Fixes - -- [`0d0501cb`](https://www.github.com/tauri-apps/tauri/commit/0d0501cb7b5e767c51a3697a148acfe84211a7ad)([#8394](https://www.github.com/tauri-apps/tauri/pull/8394)) Use `arboard` instead of `tao` clipboard implementation to prevent a crash. - ### What's Changed - [`cb640c8e`](https://www.github.com/tauri-apps/tauri/commit/cb640c8e949a3d78d78162e2e61b51bf8afae983)([#8393](https://www.github.com/tauri-apps/tauri/pull/8393)) Fix `RunEvent::WindowEvent(event: WindowEvent::FileDrop(FileDropEvent))` never triggered and always prevent default OS behavior when `disable_file_drop_handler` is not used. @@ -220,6 +332,25 @@ - Support `with_webview` for Android platform alowing execution of JNI code in context. - [8ea87e9c](https://www.github.com/tauri-apps/tauri/commit/8ea87e9c9ca8ba4c7017c8281f78aacd08f45785) feat(android): with_webview access for jni execution ([#5148](https://www.github.com/tauri-apps/tauri/pull/5148)) on 2022-09-08 +## \[0.14.4] + +### Bug Fixes + +- [`24210735`](https://www.github.com/tauri-apps/tauri/commit/2421073576a6d45783176be57b0188668558aff7)([#8117](https://www.github.com/tauri-apps/tauri/pull/8117)) Fixes a crash on macOS when accessing the windows map. +- [`510b6226`](https://www.github.com/tauri-apps/tauri/commit/510b62261c70331ce3f5bfd24137dac1bc4a0bbe)([#8822](https://www.github.com/tauri-apps/tauri/pull/8822)) Add missing `arboard` feature flag to prevent panics in wayland session. + +## \[0.14.3] + +### Bug Fixes + +- [`0d0501cb`](https://www.github.com/tauri-apps/tauri/commit/0d0501cb7b5e767c51a3697a148acfe84211a7ad)([#8394](https://www.github.com/tauri-apps/tauri/pull/8394)) Use `arboard` instead of `tao` clipboard implementation to prevent a crash. +- [`b2f83f03`](https://www.github.com/tauri-apps/tauri/commit/b2f83f03a872baa91e2b6bbb22a3e7a5cd975dc0)([#8402](https://www.github.com/tauri-apps/tauri/pull/8402)) Use `Arc` instead of `Rc` to prevent crashes on macOS. + +### Dependencies + +- Upgraded to `tauri-utils@1.5.2` +- Upgraded to `tauri-runtime@0.14.2` + ## \[0.14.2] ### Enhancements diff --git a/core/tauri-runtime-wry/Cargo.toml b/core/tauri-runtime-wry/Cargo.toml index 5cdd25638..d5904d2cb 100644 --- a/core/tauri-runtime-wry/Cargo.toml +++ b/core/tauri-runtime-wry/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "tauri-runtime-wry" -version = "2.0.0-beta.1" +version = "2.0.0-beta.11" description = "Wry bindings to the Tauri runtime" exclude = [ "CHANGELOG.md", "/target" ] readme = "README.md" @@ -13,21 +13,23 @@ edition = { workspace = true } rust-version = { workspace = true } [dependencies] -wry = { version = "0.36", default-features = false, features = [ "file-drop", "protocol", "os-webview" ] } -tao = { version = "0.25", default-features = false, features = [ "rwh_06" ] } -tauri-runtime = { version = "2.0.0-beta.1", path = "../tauri-runtime" } -tauri-utils = { version = "2.0.0-beta.1", path = "../tauri-utils" } +wry = { version = "0.39", default-features = false, features = [ "drag-drop", "protocol", "os-webview" ] } +tao = { version = "0.27", default-features = false, features = [ "rwh_06" ] } +tauri-runtime = { version = "2.0.0-beta.11", path = "../tauri-runtime" } +tauri-utils = { version = "2.0.0-beta.11", path = "../tauri-utils" } raw-window-handle = "0.6" -http = "0.2" +http = "1.1" +url = "2" tracing = { version = "0.1", optional = true } +log = "0.4" [target."cfg(windows)".dependencies] -webview2-com = "0.28" -softbuffer = "0.4" +webview2-com = "0.29" +softbuffer = { version = "0.4", default-features = false } [target."cfg(windows)".dependencies.windows] - version = "0.52" - features = [ "Win32_Foundation" ] + version = "0.54" + features = [ "Win32_Foundation", "Win32_Graphics_Dwm" ] [target."cfg(any(target_os = \"linux\", target_os = \"dragonfly\", target_os = \"freebsd\", target_os = \"openbsd\", target_os = \"netbsd\"))".dependencies] gtk = { version = "0.18", features = [ "v3_24" ] } @@ -51,3 +53,4 @@ objc-exception = [ "wry/objc-exception" ] linux-protocol-body = [ "wry/linux-body", "webkit2gtk/v2_40" ] tracing = [ "dep:tracing", "wry/tracing" ] macos-proxy = [ "wry/mac-proxy" ] +unstable = [ ] diff --git a/core/tauri-runtime-wry/build.rs b/core/tauri-runtime-wry/build.rs index 2ccb32962..57f522ab6 100644 --- a/core/tauri-runtime-wry/build.rs +++ b/core/tauri-runtime-wry/build.rs @@ -1,4 +1,4 @@ -// Copyright 2019-2023 Tauri Programme within The Commons Conservancy +// Copyright 2019-2024 Tauri Programme within The Commons Conservancy // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT diff --git a/core/tauri-runtime-wry/src/lib.rs b/core/tauri-runtime-wry/src/lib.rs index de7ded2ad..6d09edd4a 100644 --- a/core/tauri-runtime-wry/src/lib.rs +++ b/core/tauri-runtime-wry/src/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2019-2023 Tauri Programme within The Commons Conservancy +// Copyright 2019-2024 Tauri Programme within The Commons Conservancy // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT @@ -11,18 +11,20 @@ html_favicon_url = "https://github.com/tauri-apps/tauri/raw/dev/app-icon.png" )] +use http::Request; use raw_window_handle::{DisplayHandle, HasDisplayHandle, HasWindowHandle}; + use tauri_runtime::{ + dpi::{LogicalPosition, LogicalSize, PhysicalPosition, PhysicalSize, Position, Size}, monitor::Monitor, webview::{DetachedWebview, DownloadEvent, PendingWebview, WebviewIpcHandler}, window::{ - dpi::{LogicalPosition, LogicalSize, PhysicalPosition, PhysicalSize, Position, Size}, - CursorIcon, DetachedWindow, FileDropEvent, PendingWindow, RawWindow, WindowBuilder, - WindowBuilderBase, WindowEvent, WindowId, + CursorIcon, DetachedWindow, DragDropEvent, PendingWindow, RawWindow, WebviewEvent, + WindowBuilder, WindowBuilderBase, WindowEvent, WindowId, }, - DeviceEventFilter, Error, EventLoopProxy, ExitRequestedEventAction, Icon, Result, RunEvent, - Runtime, RuntimeHandle, RuntimeInitArgs, UserAttentionType, UserEvent, WebviewDispatch, - WindowDispatch, WindowEventId, + DeviceEventFilter, Error, EventLoopProxy, ExitRequestedEventAction, Icon, ProgressBarState, + ProgressBarStatus, Result, RunEvent, Runtime, RuntimeHandle, RuntimeInitArgs, UserAttentionType, + UserEvent, WebviewDispatch, WebviewEventId, WindowDispatch, WindowEventId, }; #[cfg(target_os = "macos")] @@ -58,11 +60,10 @@ use tao::{ }; #[cfg(target_os = "macos")] use tauri_utils::TitleBarStyle; -use tauri_utils::{ - config::WindowConfig, debug_eprintln, ProgressBarState, ProgressBarStatus, Theme, -}; +use tauri_utils::{config::WindowConfig, Theme}; +use url::Url; use wry::{ - FileDropEvent as WryFileDropEvent, ProxyConfig, ProxyEndpoint, Url, WebContext, WebView, + DragDropEvent as WryDragDropEvent, ProxyConfig, ProxyEndpoint, WebContext, WebView, WebViewBuilder, }; @@ -97,7 +98,7 @@ use std::{ cell::RefCell, collections::{ hash_map::Entry::{Occupied, Vacant}, - HashMap, + BTreeMap, HashMap, }, fmt, ops::Deref, @@ -112,7 +113,17 @@ use std::{ }; pub type WebviewId = u32; -type IpcHandler = dyn Fn(String) + 'static; +type IpcHandler = dyn Fn(Request) + 'static; + +#[cfg(any( + windows, + target_os = "linux", + target_os = "dragonfly", + target_os = "freebsd", + target_os = "netbsd", + target_os = "openbsd" +))] +mod undecorated_resizing; mod webview; pub use webview::Webview; @@ -121,6 +132,8 @@ pub type WebContextStore = Arc, WebContext>>>; // window pub type WindowEventHandler = Box; pub type WindowEventListeners = Arc>>; +pub type WebviewEventHandler = Box; +pub type WebviewEventListeners = Arc>>; #[derive(Debug, Clone, Default)] pub struct WindowIdStore(Arc>>); @@ -158,7 +171,11 @@ macro_rules! webview_getter { getter!( $self, rx, - Message::Webview($self.window_id, $self.webview_id, $message(tx)) + Message::Webview( + *$self.window_id.lock().unwrap(), + $self.webview_id, + $message(tx) + ) ) }}; } @@ -172,7 +189,7 @@ pub(crate) fn send_user_message( &context.main_thread.window_target, message, UserMessageContext { - webview_id_map: context.webview_id_map.clone(), + window_id_map: context.window_id_map.clone(), windows: context.main_thread.windows.clone(), }, ); @@ -187,7 +204,7 @@ pub(crate) fn send_user_message( #[derive(Clone)] pub struct Context { - pub webview_id_map: WindowIdStore, + pub window_id_map: WindowIdStore, main_thread_id: ThreadId, pub proxy: TaoEventLoopProxy>, main_thread: DispatcherMainThreadContext, @@ -195,6 +212,7 @@ pub struct Context { next_window_id: Arc, next_webview_id: Arc, next_window_event_id: Arc, + next_webview_event_id: Arc, next_webcontext_id: Arc, } @@ -222,6 +240,10 @@ impl Context { self.next_window_event_id.fetch_add(1, Ordering::Relaxed) } + fn next_webview_event_id(&self) -> u32 { + self.next_webview_event_id.fetch_add(1, Ordering::Relaxed) + } + fn next_webcontext_id(&self) -> u32 { self.next_webcontext_id.fetch_add(1, Ordering::Relaxed) } @@ -263,7 +285,7 @@ impl Context { let detached_webview = webview_id.map(|id| DetachedWebview { label: label.clone(), dispatcher: WryWebviewDispatcher { - window_id, + window_id: Arc::new(Mutex::new(window_id)), webview_id: id, context: self.clone(), }, @@ -287,6 +309,9 @@ impl Context { let webview_id = self.next_webview_id(); + let window_id_wrapper = Arc::new(Mutex::new(window_id)); + let window_id_wrapper_ = window_id_wrapper.clone(); + send_user_message( self, Message::CreateWebview( @@ -295,7 +320,7 @@ impl Context { create_webview( WebviewKind::WindowChild, window, - window_id, + window_id_wrapper_, webview_id, &context, pending, @@ -305,7 +330,7 @@ impl Context { )?; let dispatcher = WryWebviewDispatcher { - window_id, + window_id: window_id_wrapper, webview_id, context: self.clone(), }; @@ -340,11 +365,23 @@ pub enum ActiveTracingSpan { }, } +#[derive(Debug)] +pub struct WindowsStore(RefCell>); + +// SAFETY: we ensure this type is only used on the main thread. +#[allow(clippy::non_send_fields_in_send_ty)] +unsafe impl Send for WindowsStore {} + +// SAFETY: we ensure this type is only used on the main thread. +#[allow(clippy::non_send_fields_in_send_ty)] +unsafe impl Sync for WindowsStore {} + #[derive(Debug, Clone)] pub struct DispatcherMainThreadContext { pub window_target: EventLoopWindowTarget>, pub web_context: WebContextStore, - pub windows: Rc>>, + // changing this to an Rc will cause frequent app crashes. + pub windows: Arc, #[cfg(feature = "tracing")] pub active_tracing_spans: ActiveTraceSpanStore, } @@ -379,19 +416,25 @@ impl From for DeviceEventFilterWrapper { } } +pub struct RectWrapper(pub wry::Rect); +impl From for RectWrapper { + fn from(value: tauri_runtime::Rect) -> Self { + RectWrapper(wry::Rect { + position: value.position, + size: value.size, + }) + } +} + /// Wrapper around a [`tao::window::Icon`] that can be created from an [`Icon`]. pub struct TaoIcon(pub TaoWindowIcon); -fn icon_err(e: E) -> Error { - Error::InvalidIcon(Box::new(e)) -} - -impl TryFrom for TaoIcon { +impl TryFrom> for TaoIcon { type Error = Error; - fn try_from(icon: Icon) -> std::result::Result { - TaoWindowIcon::from_rgba(icon.rgba, icon.width, icon.height) + fn try_from(icon: Icon<'_>) -> std::result::Result { + TaoWindowIcon::from_rgba(icon.rgba.to_vec(), icon.width, icon.height) .map(Self) - .map_err(icon_err) + .map_err(|e| Error::InvalidIcon(Box::new(e))) } } @@ -404,14 +447,12 @@ impl WindowEventWrapper { // because wry replaces the NSView TaoWindowEvent::Resized(_) => { if let Some(w) = &window.inner { - Self(Some(WindowEvent::Resized( - PhysicalSizeWrapper(inner_size( - w, - &window.webviews, - window.has_children.load(Ordering::Relaxed), - )) - .into(), - ))) + let size = inner_size( + w, + &window.webviews, + window.has_children.load(Ordering::Relaxed), + ); + Self(Some(WindowEvent::Resized(PhysicalSizeWrapper(size).into()))) } else { Self(None) } @@ -463,16 +504,6 @@ impl<'a> From<&TaoWindowEvent<'a>> for WindowEventWrapper { } } -impl From for WindowEventWrapper { - fn from(event: WebviewEvent) -> Self { - let event = match event { - WebviewEvent::Focused(focused) => WindowEvent::Focused(focused), - WebviewEvent::FileDrop(event) => WindowEvent::FileDrop(event), - }; - Self(Some(event)) - } -} - pub struct MonitorHandleWrapper(pub MonitorHandle); impl From for Monitor { @@ -655,7 +686,7 @@ impl From for ProgressBarStateWrapper { state: progress_state .status .map(|state| ProgressStateWrapper::from(state).0), - unity_uri: progress_state.unity_uri, + desktop_filename: progress_state.desktop_filename, }) } } @@ -994,53 +1025,6 @@ impl WindowBuilder for WindowBuilderWrapper { } } -pub struct FileDropEventWrapper(WryFileDropEvent); - -// on Linux, the paths are percent-encoded -#[cfg(any( - target_os = "linux", - target_os = "dragonfly", - target_os = "freebsd", - target_os = "netbsd", - target_os = "openbsd" -))] -fn decode_path(path: PathBuf) -> PathBuf { - percent_encoding::percent_decode(path.display().to_string().as_bytes()) - .decode_utf8_lossy() - .into_owned() - .into() -} - -// on Windows and macOS, we do not need to decode the path -#[cfg(not(any( - target_os = "linux", - target_os = "dragonfly", - target_os = "freebsd", - target_os = "netbsd", - target_os = "openbsd" -)))] -fn decode_path(path: PathBuf) -> PathBuf { - path -} - -impl From for FileDropEvent { - fn from(event: FileDropEventWrapper) -> Self { - match event.0 { - WryFileDropEvent::Hovered { paths, position } => FileDropEvent::Hovered { - paths: paths.into_iter().map(decode_path).collect(), - position: PhysicalPosition::new(position.0 as f64, position.1 as f64), - }, - WryFileDropEvent::Dropped { paths, position } => FileDropEvent::Dropped { - paths: paths.into_iter().map(decode_path).collect(), - position: PhysicalPosition::new(position.0 as f64, position.1 as f64), - }, - // default to cancelled - // FIXME(maybe): Add `FileDropEvent::Unknown` event? - _ => FileDropEvent::Cancelled, - } - } -} - #[cfg(any( target_os = "linux", target_os = "dragonfly", @@ -1168,23 +1152,45 @@ pub enum WindowMessage { RequestRedraw, } +#[derive(Debug, Clone)] +pub enum SynthesizedWindowEvent { + Focused(bool), + DragDrop(DragDropEvent), +} + +impl From for WindowEventWrapper { + fn from(event: SynthesizedWindowEvent) -> Self { + let event = match event { + SynthesizedWindowEvent::Focused(focused) => WindowEvent::Focused(focused), + SynthesizedWindowEvent::DragDrop(event) => WindowEvent::DragDrop(event), + }; + Self(Some(event)) + } +} + pub enum WebviewMessage { + AddEventListener(WebviewEventId, Box), #[cfg(not(all(feature = "tracing", not(target_os = "android"))))] EvaluateScript(String), #[cfg(all(feature = "tracing", not(target_os = "android")))] EvaluateScript(String, Sender<()>, tracing::Span), - #[allow(dead_code)] WebviewEvent(WebviewEvent), + SynthesizedWindowEvent(SynthesizedWindowEvent), Navigate(Url), Print, Close, SetPosition(Position), SetSize(Size), + SetBounds(tauri_runtime::Rect), SetFocus, + Reparent(WindowId, Sender>), + SetAutoResize(bool), + SetZoom(f64), // Getters - Url(Sender), - Position(Sender>), - Size(Sender>), + Url(Sender>), + Bounds(Sender>), + Position(Sender>>), + Size(Sender>>), WithWebview(Box), // Devtools #[cfg(any(debug_assertions, feature = "devtools"))] @@ -1195,13 +1201,6 @@ pub enum WebviewMessage { IsDevToolsOpen(Sender), } -#[allow(dead_code)] -#[derive(Debug, Clone)] -pub enum WebviewEvent { - FileDrop(FileDropEvent), - Focused(bool), -} - pub type CreateWindowClosure = Box>) -> Result + Send>; @@ -1238,7 +1237,7 @@ impl Clone for Message { /// The Tauri [`WebviewDispatch`] for [`Wry`]. #[derive(Debug, Clone)] pub struct WryWebviewDispatcher { - window_id: WindowId, + window_id: Arc>, webview_id: WebviewId, context: Context, } @@ -1250,11 +1249,21 @@ impl WebviewDispatch for WryWebviewDispatcher { send_user_message(&self.context, Message::Task(Box::new(f))) } + fn on_webview_event(&self, f: F) -> WindowEventId { + let id = self.context.next_webview_event_id(); + let _ = self.context.proxy.send_event(Message::Webview( + *self.window_id.lock().unwrap(), + self.webview_id, + WebviewMessage::AddEventListener(id, Box::new(f)), + )); + id + } + fn with_webview) + Send + 'static>(&self, f: F) -> Result<()> { send_user_message( &self.context, Message::Webview( - self.window_id, + *self.window_id.lock().unwrap(), self.webview_id, WebviewMessage::WithWebview(Box::new(move |webview| f(Box::new(webview)))), ), @@ -1266,7 +1275,7 @@ impl WebviewDispatch for WryWebviewDispatcher { let _ = send_user_message( &self.context, Message::Webview( - self.window_id, + *self.window_id.lock().unwrap(), self.webview_id, WebviewMessage::OpenDevTools, ), @@ -1278,7 +1287,7 @@ impl WebviewDispatch for WryWebviewDispatcher { let _ = send_user_message( &self.context, Message::Webview( - self.window_id, + *self.window_id.lock().unwrap(), self.webview_id, WebviewMessage::CloseDevTools, ), @@ -1294,15 +1303,19 @@ impl WebviewDispatch for WryWebviewDispatcher { // Getters fn url(&self) -> Result { - webview_getter!(self, WebviewMessage::Url) + webview_getter!(self, WebviewMessage::Url)? + } + + fn bounds(&self) -> Result { + webview_getter!(self, WebviewMessage::Bounds)? } fn position(&self) -> Result> { - webview_getter!(self, WebviewMessage::Position) + webview_getter!(self, WebviewMessage::Position)? } fn size(&self) -> Result> { - webview_getter!(self, WebviewMessage::Size) + webview_getter!(self, WebviewMessage::Size)? } // Setters @@ -1311,7 +1324,7 @@ impl WebviewDispatch for WryWebviewDispatcher { send_user_message( &self.context, Message::Webview( - self.window_id, + *self.window_id.lock().unwrap(), self.webview_id, WebviewMessage::Navigate(url), ), @@ -1321,14 +1334,33 @@ impl WebviewDispatch for WryWebviewDispatcher { fn print(&self) -> Result<()> { send_user_message( &self.context, - Message::Webview(self.window_id, self.webview_id, WebviewMessage::Print), + Message::Webview( + *self.window_id.lock().unwrap(), + self.webview_id, + WebviewMessage::Print, + ), ) } fn close(&self) -> Result<()> { send_user_message( &self.context, - Message::Webview(self.window_id, self.webview_id, WebviewMessage::Close), + Message::Webview( + *self.window_id.lock().unwrap(), + self.webview_id, + WebviewMessage::Close, + ), + ) + } + + fn set_bounds(&self, bounds: tauri_runtime::Rect) -> Result<()> { + send_user_message( + &self.context, + Message::Webview( + *self.window_id.lock().unwrap(), + self.webview_id, + WebviewMessage::SetBounds(bounds), + ), ) } @@ -1336,7 +1368,7 @@ impl WebviewDispatch for WryWebviewDispatcher { send_user_message( &self.context, Message::Webview( - self.window_id, + *self.window_id.lock().unwrap(), self.webview_id, WebviewMessage::SetSize(size), ), @@ -1347,7 +1379,7 @@ impl WebviewDispatch for WryWebviewDispatcher { send_user_message( &self.context, Message::Webview( - self.window_id, + *self.window_id.lock().unwrap(), self.webview_id, WebviewMessage::SetPosition(position), ), @@ -1357,7 +1389,40 @@ impl WebviewDispatch for WryWebviewDispatcher { fn set_focus(&self) -> Result<()> { send_user_message( &self.context, - Message::Webview(self.window_id, self.webview_id, WebviewMessage::SetFocus), + Message::Webview( + *self.window_id.lock().unwrap(), + self.webview_id, + WebviewMessage::SetFocus, + ), + ) + } + + fn reparent(&self, window_id: WindowId) -> Result<()> { + let mut current_window_id = self.window_id.lock().unwrap(); + let (tx, rx) = channel(); + send_user_message( + &self.context, + Message::Webview( + *current_window_id, + self.webview_id, + WebviewMessage::Reparent(window_id, tx), + ), + )?; + + rx.recv().unwrap()?; + + *current_window_id = window_id; + Ok(()) + } + + fn set_auto_resize(&self, auto_resize: bool) -> Result<()> { + send_user_message( + &self.context, + Message::Webview( + *self.window_id.lock().unwrap(), + self.webview_id, + WebviewMessage::SetAutoResize(auto_resize), + ), ) } @@ -1369,7 +1434,7 @@ impl WebviewDispatch for WryWebviewDispatcher { self, rx, Message::Webview( - self.window_id, + *self.window_id.lock().unwrap(), self.webview_id, WebviewMessage::EvaluateScript(script.into(), tx, tracing::Span::current()), ) @@ -1381,12 +1446,23 @@ impl WebviewDispatch for WryWebviewDispatcher { send_user_message( &self.context, Message::Webview( - self.window_id, + *self.window_id.lock().unwrap(), self.webview_id, WebviewMessage::EvaluateScript(script.into()), ), ) } + + fn set_zoom(&self, scale_factor: f64) -> Result<()> { + send_user_message( + &self.context, + Message::Webview( + *self.window_id.lock().unwrap(), + self.webview_id, + WebviewMessage::SetZoom(scale_factor), + ), + ) + } } /// The Tauri [`WindowDispatch`] for [`Wry`]. @@ -1461,12 +1537,12 @@ impl WindowDispatch for WryWindowDispatcher { window_getter!(self, WindowMessage::IsFocused) } - /// Gets the window’s current decoration state. + /// Gets the window's current decoration state. fn is_decorated(&self) -> Result { window_getter!(self, WindowMessage::IsDecorated) } - /// Gets the window’s current resizable state. + /// Gets the window's current resizable state. fn is_resizable(&self) -> Result { window_getter!(self, WindowMessage::IsResizable) } @@ -1853,12 +1929,14 @@ impl WindowDispatch for WryWindowDispatcher { #[derive(Clone)] pub struct WebviewWrapper { + label: String, id: WebviewId, inner: Rc, context_store: WebContextStore, + webview_event_listeners: WebviewEventListeners, // the key of the WebContext if it's not shared context_key: Option, - bounds: Option>>, + bounds: Arc>>, } impl Deref for WebviewWrapper { @@ -1886,6 +1964,9 @@ pub struct WindowWrapper { has_children: AtomicBool, webviews: Vec, window_event_listeners: WindowEventListeners, + #[cfg(windows)] + is_window_fullscreen: bool, + #[cfg(windows)] is_window_transparent: bool, #[cfg(windows)] surface: Option, Arc>>, @@ -1896,7 +1977,6 @@ impl fmt::Debug for WindowWrapper { f.debug_struct("WindowWrapper") .field("label", &self.label) .field("inner", &self.inner) - .field("is_window_transparent", &self.is_window_transparent) .finish() } } @@ -1977,7 +2057,7 @@ impl WryHandle { pub fn window_id(&self, window_id: TaoWindowId) -> WindowId { *self .context - .webview_id_map + .window_id_map .0 .lock() .unwrap() @@ -2131,11 +2211,11 @@ impl Wry { let main_thread_id = current_thread().id(); let web_context = WebContextStore::default(); - let windows = Rc::new(RefCell::new(HashMap::default())); - let webview_id_map = WindowIdStore::default(); + let windows = Arc::new(WindowsStore(RefCell::new(BTreeMap::default()))); + let window_id_map = WindowIdStore::default(); let context = Context { - webview_id_map, + window_id_map, main_thread_id, proxy: event_loop.create_proxy(), main_thread: DispatcherMainThreadContext { @@ -2149,6 +2229,7 @@ impl Wry { next_window_id: Default::default(), next_webview_id: Default::default(), next_window_event_id: Default::default(), + next_webview_event_id: Default::default(), next_webcontext_id: Default::default(), }; @@ -2231,13 +2312,14 @@ impl Runtime for Wry { .context .main_thread .windows + .0 .borrow_mut() .insert(window_id, window); let detached_webview = webview_id.map(|id| DetachedWebview { label: label.clone(), dispatcher: WryWebviewDispatcher { - window_id, + window_id: Arc::new(Mutex::new(window_id)), webview_id: id, context: self.context.clone(), }, @@ -2262,16 +2344,19 @@ impl Runtime for Wry { .context .main_thread .windows + .0 .borrow() .get(&window_id) .and_then(|w| w.inner.clone()); if let Some(window) = window { + let window_id_wrapper = Arc::new(Mutex::new(window_id)); + let webview_id = self.context.next_webview_id(); let webview = create_webview( WebviewKind::WindowChild, &window, - window_id, + window_id_wrapper.clone(), webview_id, &self.context, pending, @@ -2281,6 +2366,7 @@ impl Runtime for Wry { .context .main_thread .windows + .0 .borrow_mut() .get_mut(&window_id) .map(|w| { @@ -2290,7 +2376,7 @@ impl Runtime for Wry { }); let dispatcher = WryWebviewDispatcher { - window_id, + window_id: window_id_wrapper, webview_id, context: self.context.clone(), }; @@ -2344,10 +2430,10 @@ impl Runtime for Wry { } #[cfg(desktop)] - fn run_iteration)>(&mut self, mut callback: F) { + fn run_iteration) + 'static>(&mut self, mut callback: F) { use tao::platform::run_return::EventLoopExtRunReturn; let windows = self.context.main_thread.windows.clone(); - let webview_id_map = self.context.webview_id_map.clone(); + let window_id_map = self.context.window_id_map.clone(); let web_context = &self.context.main_thread.web_context; let plugins = self.context.plugins.clone(); @@ -2372,7 +2458,7 @@ impl Runtime for Wry { control_flow, EventLoopIterationContext { callback: &mut callback, - webview_id_map: webview_id_map.clone(), + window_id_map: window_id_map.clone(), windows: windows.clone(), #[cfg(feature = "tracing")] active_tracing_spans: active_tracing_spans.clone(), @@ -2391,7 +2477,7 @@ impl Runtime for Wry { EventLoopIterationContext { callback: &mut callback, windows: windows.clone(), - webview_id_map: webview_id_map.clone(), + window_id_map: window_id_map.clone(), #[cfg(feature = "tracing")] active_tracing_spans: active_tracing_spans.clone(), }, @@ -2401,7 +2487,7 @@ impl Runtime for Wry { fn run) + 'static>(self, mut callback: F) { let windows = self.context.main_thread.windows.clone(); - let webview_id_map = self.context.webview_id_map.clone(); + let window_id_map = self.context.window_id_map.clone(); let web_context = self.context.main_thread.web_context; let plugins = self.context.plugins.clone(); @@ -2418,7 +2504,7 @@ impl Runtime for Wry { control_flow, EventLoopIterationContext { callback: &mut callback, - webview_id_map: webview_id_map.clone(), + window_id_map: window_id_map.clone(), windows: windows.clone(), #[cfg(feature = "tracing")] active_tracing_spans: active_tracing_spans.clone(), @@ -2435,7 +2521,7 @@ impl Runtime for Wry { control_flow, EventLoopIterationContext { callback: &mut callback, - webview_id_map: webview_id_map.clone(), + window_id_map: window_id_map.clone(), windows: windows.clone(), #[cfg(feature = "tracing")] active_tracing_spans: active_tracing_spans.clone(), @@ -2446,16 +2532,16 @@ impl Runtime for Wry { } pub struct EventLoopIterationContext<'a, T: UserEvent> { - pub callback: &'a mut (dyn FnMut(RunEvent)), - pub webview_id_map: WindowIdStore, - pub windows: Rc>>, + pub callback: &'a mut (dyn FnMut(RunEvent) + 'static), + pub window_id_map: WindowIdStore, + pub windows: Arc, #[cfg(feature = "tracing")] pub active_tracing_spans: ActiveTraceSpanStore, } struct UserMessageContext { - windows: Rc>>, - webview_id_map: WindowIdStore, + windows: Arc, + window_id_map: WindowIdStore, } fn handle_user_message( @@ -2464,7 +2550,7 @@ fn handle_user_message( context: UserMessageContext, ) { let UserMessageContext { - webview_id_map, + window_id_map, windows, } = context; match message { @@ -2484,7 +2570,7 @@ fn handle_user_message( } }, Message::Window(id, window_message) => { - let w = windows.borrow().get(&id).map(|w| { + let w = windows.0.borrow().get(&id).map(|w| { ( w.inner.clone(), w.webviews.clone(), @@ -2568,7 +2654,38 @@ fn handle_user_message( } // Setters WindowMessage::Center => { - let _ = center_window(&window, inner_size(&window, &webviews, has_children)); + #[cfg(not(target_os = "macos"))] + if let Some(monitor) = window.current_monitor() { + #[allow(unused_mut)] + let mut window_size = window.outer_size(); + #[cfg(windows)] + if window.is_decorated() { + use windows::Win32::Foundation::RECT; + use windows::Win32::Graphics::Dwm::{ + DwmGetWindowAttribute, DWMWA_EXTENDED_FRAME_BOUNDS, + }; + let mut rect = RECT::default(); + let result = unsafe { + DwmGetWindowAttribute( + HWND(window.hwnd()), + DWMWA_EXTENDED_FRAME_BOUNDS, + &mut rect as *mut _ as *mut _, + std::mem::size_of::() as u32, + ) + }; + if result.is_ok() { + window_size.height = (rect.bottom - rect.top) as u32; + } + } + window.set_outer_position(calculate_window_center_position(window_size, monitor)); + } + + #[cfg(target_os = "macos")] + { + use cocoa::{appkit::NSWindow, base::id}; + let ns_window: id = window.ns_window() as _; + unsafe { ns_window.center() }; + } } WindowMessage::RequestUserAttention(request_type) => { window.request_user_attention(request_type.map(|r| r.0)); @@ -2582,8 +2699,16 @@ fn handle_user_message( WindowMessage::Unmaximize => window.set_maximized(false), WindowMessage::Minimize => window.set_minimized(true), WindowMessage::Unminimize => window.set_minimized(false), - WindowMessage::Show => window.set_visible(true), - WindowMessage::Hide => window.set_visible(false), + WindowMessage::Show => { + window.set_visible(true); + #[cfg(windows)] + let _ = set_webview_visibility(&webviews, !window.is_minimized()); + } + WindowMessage::Hide => { + window.set_visible(false); + #[cfg(windows)] + let _ = set_webview_visibility(&webviews, false); + } WindowMessage::Close => { panic!("cannot handle `WindowMessage::Close` on the main thread") } @@ -2623,6 +2748,10 @@ fn handle_user_message( } else { window.set_fullscreen(None) } + #[cfg(windows)] + if let Some(w) = windows.0.borrow_mut().get_mut(&id) { + w.is_window_fullscreen = fullscreen; + } } WindowMessage::SetFocus => { window.set_focus(); @@ -2674,9 +2803,73 @@ fn handle_user_message( } } } - Message::Webview(window_id, webview_id, webview_message) => { - let webview_handle = windows.borrow().get(&window_id).map(|w| { + #[cfg(any( + target_os = "macos", + windows, + target_os = "linux", + target_os = "dragonfly", + target_os = "freebsd", + target_os = "netbsd", + target_os = "openbsd" + ))] + if let WebviewMessage::Reparent(new_parent_window_id, tx) = webview_message { + let webview_handle = windows.0.borrow_mut().get_mut(&window_id).and_then(|w| { + w.webviews + .iter() + .position(|w| w.id == webview_id) + .map(|webview_index| w.webviews.remove(webview_index)) + }); + + if let Some(webview) = webview_handle { + if let Some((Some(new_parent_window), new_parent_window_webviews)) = windows + .0 + .borrow_mut() + .get_mut(&new_parent_window_id) + .map(|w| (w.inner.clone(), &mut w.webviews)) + { + #[cfg(target_os = "macos")] + let reparent_result = { + use wry::WebViewExtMacOS; + webview.inner.reparent(new_parent_window.ns_window() as _) + }; + #[cfg(windows)] + let reparent_result = { webview.inner.reparent(new_parent_window.hwnd()) }; + + #[cfg(any( + target_os = "linux", + target_os = "dragonfly", + target_os = "freebsd", + target_os = "netbsd", + target_os = "openbsd" + ))] + let reparent_result = { + if let Some(container) = new_parent_window.default_vbox() { + webview.inner.reparent(container) + } else { + Err(wry::Error::MessageSender) + } + }; + + match reparent_result { + Ok(_) => { + new_parent_window_webviews.push(webview); + tx.send(Ok(())).unwrap(); + } + Err(e) => { + log::error!("failed to reparent webview: {e}"); + tx.send(Err(Error::FailedToSendMessage)).unwrap(); + } + } + } + } else { + tx.send(Err(Error::FailedToSendMessage)).unwrap(); + } + + return; + } + + let webview_handle = windows.0.borrow().get(&window_id).map(|w| { ( w.inner.clone(), w.webviews.iter().find(|w| w.id == webview_id).cloned(), @@ -2684,66 +2877,179 @@ fn handle_user_message( }); if let Some((Some(window), Some(webview))) = webview_handle { match webview_message { + WebviewMessage::WebviewEvent(_) => { /* already handled */ } + WebviewMessage::SynthesizedWindowEvent(_) => { /* already handled */ } + WebviewMessage::Reparent(_window_id, _tx) => { /* already handled */ } + WebviewMessage::AddEventListener(id, listener) => { + webview + .webview_event_listeners + .lock() + .unwrap() + .insert(id, listener); + } + #[cfg(all(feature = "tracing", not(target_os = "android")))] WebviewMessage::EvaluateScript(script, tx, span) => { let _span = span.entered(); if let Err(e) = webview.evaluate_script(&script) { - debug_eprintln!("{}", e); + log::error!("{}", e); } tx.send(()).unwrap(); } #[cfg(not(all(feature = "tracing", not(target_os = "android"))))] WebviewMessage::EvaluateScript(script) => { if let Err(e) = webview.evaluate_script(&script) { - debug_eprintln!("{}", e); + log::error!("{}", e); + } + } + WebviewMessage::Navigate(url) => { + if let Err(e) = webview.load_url(url.as_str()) { + log::error!("failed to navigate to url {}: {}", url, e); } } - WebviewMessage::Navigate(url) => webview.load_url(url.as_str()), WebviewMessage::Print => { let _ = webview.print(); } WebviewMessage::Close => { - windows.borrow_mut().get_mut(&window_id).map(|window| { + windows.0.borrow_mut().get_mut(&window_id).map(|window| { if let Some(i) = window.webviews.iter().position(|w| w.id == webview.id) { window.webviews.remove(i); } window }); } - WebviewMessage::SetSize(size) => { - let mut bounds = webview.bounds(); - let size = size.to_logical(window.scale_factor()); - bounds.width = size.width; - bounds.height = size.height; + WebviewMessage::SetBounds(bounds) => { + let bounds: RectWrapper = bounds.into(); + let bounds = bounds.0; - if let Some(b) = &webview.bounds { - let window_size = window.inner_size(); - let mut bounds = b.lock().unwrap(); - bounds.width_rate = size.width as f32 / window_size.width as f32; - bounds.height_rate = size.height as f32 / window_size.height as f32; + if let Some(b) = &mut *webview.bounds.lock().unwrap() { + let scale_factor = window.scale_factor(); + let size = bounds.size.to_logical::(scale_factor); + let position = bounds.position.to_logical::(scale_factor); + let window_size = window.inner_size().to_logical::(scale_factor); + b.width_rate = size.width / window_size.width; + b.height_rate = size.height / window_size.height; + b.x_rate = position.x / window_size.width; + b.y_rate = position.y / window_size.height; } - webview.set_bounds(bounds); + if let Err(e) = webview.set_bounds(bounds) { + log::error!("failed to set webview size: {e}"); + } } - WebviewMessage::SetPosition(position) => { - let mut bounds = webview.bounds(); - let position = position.to_logical(window.scale_factor()); - bounds.x = position.x; - bounds.y = position.y; + WebviewMessage::SetSize(size) => match webview.bounds() { + Ok(mut bounds) => { + bounds.size = size; - if let Some(b) = &webview.bounds { - let window_size = window.inner_size(); - let mut bounds = b.lock().unwrap(); - bounds.width_rate = position.x as f32 / window_size.width as f32; - bounds.height_rate = position.y as f32 / window_size.height as f32; + let scale_factor = window.scale_factor(); + let size = size.to_logical::(scale_factor); + + if let Some(b) = &mut *webview.bounds.lock().unwrap() { + let window_size = window.inner_size().to_logical::(scale_factor); + b.width_rate = size.width / window_size.width; + b.height_rate = size.height / window_size.height; + } + + if let Err(e) = webview.set_bounds(bounds) { + log::error!("failed to set webview size: {e}"); + } } + Err(e) => { + log::error!("failed to get webview bounds: {e}"); + } + }, + WebviewMessage::SetPosition(position) => match webview.bounds() { + Ok(mut bounds) => { + bounds.position = position; - webview.set_bounds(bounds); + let scale_factor = window.scale_factor(); + let position = position.to_logical::(scale_factor); + + if let Some(b) = &mut *webview.bounds.lock().unwrap() { + let window_size = window.inner_size().to_logical::(scale_factor); + b.x_rate = position.x / window_size.width; + b.y_rate = position.y / window_size.height; + } + + if let Err(e) = webview.set_bounds(bounds) { + log::error!("failed to set webview position: {e}"); + } + } + Err(e) => { + log::error!("failed to get webview bounds: {e}"); + } + }, + WebviewMessage::SetZoom(scale_factor) => { + if let Err(e) = webview.zoom(scale_factor) { + log::error!("failed to set webview zoom: {e}"); + } + } + // Getters + WebviewMessage::Url(tx) => { + tx.send( + webview + .url() + .map(|u| u.parse().expect("invalid webview URL")) + .map_err(|_| Error::FailedToSendMessage), + ) + .unwrap(); + } + WebviewMessage::Bounds(tx) => { + tx.send( + webview + .bounds() + .map(|bounds| tauri_runtime::Rect { + size: bounds.size, + position: bounds.position, + }) + .map_err(|_| Error::FailedToSendMessage), + ) + .unwrap(); + } + WebviewMessage::Position(tx) => { + tx.send( + webview + .bounds() + .map(|bounds| bounds.position.to_physical(window.scale_factor())) + .map_err(|_| Error::FailedToSendMessage), + ) + .unwrap(); + } + WebviewMessage::Size(tx) => { + tx.send( + webview + .bounds() + .map(|bounds| bounds.size.to_physical(window.scale_factor())) + .map_err(|_| Error::FailedToSendMessage), + ) + .unwrap(); } WebviewMessage::SetFocus => { - webview.focus(); + if let Err(e) = webview.focus() { + log::error!("failed to focus webview: {e}"); + } } - WebviewMessage::WebviewEvent(_event) => { /* already handled */ } + WebviewMessage::SetAutoResize(auto_resize) => match webview.bounds() { + Ok(bounds) => { + let scale_factor = window.scale_factor(); + let window_size = window.inner_size().to_logical::(scale_factor); + *webview.bounds.lock().unwrap() = if auto_resize { + let size = bounds.size.to_logical::(scale_factor); + let position = bounds.position.to_logical::(scale_factor); + Some(WebviewBounds { + x_rate: position.x / window_size.width, + y_rate: position.y / window_size.height, + width_rate: size.width / window_size.width, + height_rate: size.height / window_size.height, + }) + } else { + None + }; + } + Err(e) => { + log::error!("failed to get webview bounds: {e}"); + } + }, WebviewMessage::WithWebview(f) => { #[cfg(any( target_os = "linux", @@ -2786,7 +3092,6 @@ fn handle_user_message( f(webview.handle()) } } - #[cfg(any(debug_assertions, feature = "devtools"))] WebviewMessage::OpenDevTools => { webview.open_devtools(); @@ -2799,58 +3104,48 @@ fn handle_user_message( WebviewMessage::IsDevToolsOpen(tx) => { tx.send(webview.is_devtools_open()).unwrap(); } - - // Getters - WebviewMessage::Url(tx) => { - tx.send(webview.url()).unwrap(); - } - WebviewMessage::Position(tx) => { - let bounds = webview.bounds(); - let position = - LogicalPosition::new(bounds.x, bounds.y).to_physical(window.scale_factor()); - tx.send(position).unwrap(); - } - WebviewMessage::Size(tx) => { - let bounds = webview.bounds(); - let size = - LogicalSize::new(bounds.width, bounds.height).to_physical(window.scale_factor()); - tx.send(size).unwrap(); - } } } } Message::CreateWebview(window_id, handler) => { let window = windows + .0 .borrow() .get(&window_id) .and_then(|w| w.inner.clone()); if let Some(window) = window { match handler(&window) { Ok(webview) => { - windows.borrow_mut().get_mut(&window_id).map(|w| { + windows.0.borrow_mut().get_mut(&window_id).map(|w| { w.webviews.push(webview); + w.has_children.store(true, Ordering::Relaxed); w }); } Err(e) => { - debug_eprintln!("{}", e); + log::error!("{}", e); } } } } Message::CreateWindow(window_id, handler) => match handler(event_loop) { Ok(webview) => { - windows.borrow_mut().insert(window_id, webview); + windows.0.borrow_mut().insert(window_id, webview); } Err(e) => { - debug_eprintln!("{}", e); + log::error!("{}", e); } }, Message::CreateRawWindow(window_id, handler, sender) => { let (label, builder) = handler(); + + #[cfg(windows)] + let is_window_fullscreen = builder.window.fullscreen.is_some(); + #[cfg(windows)] let is_window_transparent = builder.window.transparent; + if let Ok(window) = builder.build(event_loop) { - webview_id_map.insert(window.id(), window_id); + window_id_map.insert(window.id(), window_id); let window = Arc::new(window); @@ -2870,7 +3165,7 @@ fn handle_user_message( None }; - windows.borrow_mut().insert( + windows.0.borrow_mut().insert( window_id, WindowWrapper { label, @@ -2878,6 +3173,9 @@ fn handle_user_message( inner: Some(window.clone()), window_event_listeners: Default::default(), webviews: Vec::new(), + #[cfg(windows)] + is_window_fullscreen, + #[cfg(windows)] is_window_transparent, #[cfg(windows)] surface, @@ -2901,7 +3199,7 @@ fn handle_event_loop( ) { let EventLoopIterationContext { callback, - webview_id_map, + window_id_map, windows, #[cfg(feature = "tracing")] active_tracing_spans, @@ -2930,8 +3228,8 @@ fn handle_event_loop( #[cfg(any(feature = "tracing", windows))] Event::RedrawRequested(id) => { #[cfg(windows)] - if let Some(window_id) = webview_id_map.get(&id) { - let mut windows_ref = windows.borrow_mut(); + if let Some(window_id) = window_id_map.get(&id) { + let mut windows_ref = windows.0.borrow_mut(); if let Some(window) = windows_ref.get_mut(&window_id) { if window.is_window_transparent { if let Some(surface) = &mut window.surface { @@ -2949,19 +3247,50 @@ fn handle_event_loop( Event::UserEvent(Message::Webview( window_id, - _webview_id, + webview_id, WebviewMessage::WebviewEvent(event), + )) => { + let windows_ref = windows.0.borrow(); + if let Some(window) = windows_ref.get(&window_id) { + if let Some(webview) = window.webviews.iter().find(|w| w.id == webview_id) { + let label = webview.label.clone(); + let webview_event_listeners = webview.webview_event_listeners.clone(); + + drop(windows_ref); + + callback(RunEvent::WebviewEvent { + label, + event: event.clone(), + }); + let listeners = webview_event_listeners.lock().unwrap(); + let handlers = listeners.values(); + for handler in handlers { + handler(&event); + } + } + } + } + + Event::UserEvent(Message::Webview( + window_id, + _webview_id, + WebviewMessage::SynthesizedWindowEvent(event), )) => { if let Some(event) = WindowEventWrapper::from(event).0 { - let windows = windows.borrow(); - let window = windows.get(&window_id); + let windows_ref = windows.0.borrow(); + let window = windows_ref.get(&window_id); if let Some(window) = window { + let label = window.label.clone(); + let window_event_listeners = window.window_event_listeners.clone(); + + drop(windows_ref); + callback(RunEvent::WindowEvent { - label: window.label.clone(), + label, event: event.clone(), }); - let listeners = window.window_event_listeners.lock().unwrap(); + let listeners = window_event_listeners.lock().unwrap(); let handlers = listeners.values(); for handler in handlers { handler(&event); @@ -2973,9 +3302,9 @@ fn handle_event_loop( Event::WindowEvent { event, window_id, .. } => { - if let Some(window_id) = webview_id_map.get(&window_id) { + if let Some(window_id) = window_id_map.get(&window_id) { { - let windows_ref = windows.borrow(); + let windows_ref = windows.0.borrow(); if let Some(window) = windows_ref.get(&window_id) { if let Some(event) = WindowEventWrapper::parse(window, &event).0 { let label = window.label.clone(); @@ -2999,14 +3328,16 @@ fn handle_event_loop( match event { #[cfg(windows)] TaoWindowEvent::ThemeChanged(theme) => { - if let Some(window) = windows.borrow().get(&window_id) { + if let Some(window) = windows.0.borrow().get(&window_id) { for webview in &window.webviews { let theme = match theme { TaoTheme::Dark => wry::Theme::Dark, TaoTheme::Light => wry::Theme::Light, _ => wry::Theme::Light, }; - webview.set_theme(theme); + if let Err(e) = webview.set_theme(theme) { + log::error!("failed to set theme: {e}"); + } } } } @@ -3014,9 +3345,9 @@ fn handle_event_loop( on_close_requested(callback, window_id, windows.clone()); } TaoWindowEvent::Destroyed => { - let removed = windows.borrow_mut().remove(&window_id).is_some(); + let removed = windows.0.borrow_mut().remove(&window_id).is_some(); if removed { - let is_empty = windows.borrow().is_empty(); + let is_empty = windows.0.borrow().is_empty(); if is_empty { let (tx, rx) = channel(); callback(RunEvent::ExitRequested { code: None, tx }); @@ -3031,18 +3362,28 @@ fn handle_event_loop( } } TaoWindowEvent::Resized(size) => { - if let Some(webviews) = windows.borrow().get(&window_id).map(|w| w.webviews.clone()) { - for webview in webviews { - if let Some(bounds) = &webview.bounds { - let b = bounds.lock().unwrap(); - webview.set_bounds(wry::Rect { - x: (size.width as f32 * b.x_rate) as i32, - y: (size.height as f32 * b.y_rate) as i32, - width: (size.width as f32 * b.width_rate) as u32, - height: (size.height as f32 * b.height_rate) as u32, - }); + if let Some((Some(window), webviews)) = windows + .0 + .borrow() + .get(&window_id) + .map(|w| (w.inner.clone(), w.webviews.clone())) + { + let size = size.to_logical::(window.scale_factor()); + for webview in &webviews { + if let Some(b) = &*webview.bounds.lock().unwrap() { + if let Err(e) = webview.set_bounds(wry::Rect { + position: LogicalPosition::new(size.width * b.x_rate, size.height * b.y_rate) + .into(), + size: LogicalSize::new(size.width * b.width_rate, size.height * b.height_rate) + .into(), + }) { + log::error!("failed to autoresize webview: {e}"); + } } } + #[cfg(windows)] + let _ = + set_webview_visibility(&webviews, window.is_visible() && !window.is_minimized()); } } _ => {} @@ -3076,7 +3417,7 @@ fn handle_event_loop( event_loop, message, UserMessageContext { - webview_id_map, + window_id_map, windows, }, ); @@ -3090,13 +3431,13 @@ fn handle_event_loop( } } -fn on_close_requested( - callback: &mut (dyn FnMut(RunEvent)), +fn on_close_requested<'a, T: UserEvent>( + callback: &'a mut (dyn FnMut(RunEvent) + 'static), window_id: WindowId, - windows: Rc>>, + windows: Arc, ) { let (tx, rx) = channel(); - let windows_ref = windows.borrow(); + let windows_ref = windows.0.borrow(); if let Some(w) = windows_ref.get(&window_id) { let label = w.label.clone(); let window_event_listeners = w.window_event_listeners.clone(); @@ -3121,30 +3462,14 @@ fn on_close_requested( } } -fn on_window_close(window_id: WindowId, windows: Rc>>) { - if let Some(window_wrapper) = windows.borrow_mut().get_mut(&window_id) { +fn on_window_close(window_id: WindowId, windows: Arc) { + if let Some(window_wrapper) = windows.0.borrow_mut().get_mut(&window_id) { window_wrapper.inner = None; #[cfg(windows)] window_wrapper.surface.take(); } } -pub fn center_window(window: &Window, window_size: TaoPhysicalSize) -> Result<()> { - if let Some(monitor) = window.current_monitor() { - let screen_size = monitor.size(); - let monitor_pos = monitor.position(); - let x = (screen_size.width as i32 - window_size.width as i32) / 2; - let y = (screen_size.height as i32 - window_size.height as i32) / 2; - window.set_outer_position(TaoPhysicalPosition::new( - monitor_pos.x + x, - monitor_pos.y + y, - )); - Ok(()) - } else { - Err(Error::FailedToGetMonitor) - } -} - fn parse_proxy_url(url: &Url) -> Result { let host = url.host().map(|h| h.to_string()).unwrap_or_default(); let port = url.port().map(|p| p.to_string()).unwrap_or_default(); @@ -3187,7 +3512,10 @@ fn create_window( let window_event_listeners = WindowEventListeners::default(); + #[cfg(windows)] let is_window_transparent = window_builder.inner.window.transparent; + #[cfg(windows)] + let is_window_fullscreen = window_builder.inner.window.fullscreen.is_some(); #[cfg(target_os = "macos")] { @@ -3199,6 +3527,58 @@ fn create_window( } } + #[cfg(desktop)] + if window_builder.center { + let monitor = if let Some(window_position) = &window_builder.inner.window.position { + event_loop.available_monitors().find(|m| { + let monitor_pos = m.position(); + let monitor_size = m.size(); + + // type annotations required for 32bit targets. + let window_position: LogicalPosition = window_position.to_logical(m.scale_factor()); + + monitor_pos.x <= window_position.x + && window_position.x <= monitor_pos.x + monitor_size.width as i32 + && monitor_pos.y <= window_position.y + && window_position.y <= monitor_pos.y + monitor_size.height as i32 + }) + } else { + event_loop.primary_monitor() + }; + + if let Some(monitor) = monitor { + let desired_size = window_builder + .inner + .window + .inner_size + .unwrap_or_else(|| TaoPhysicalSize::new(800, 600).into()); + let scale_factor = monitor.scale_factor(); + #[allow(unused_mut)] + let mut window_size = window_builder + .inner + .window + .inner_size_constraints + .clamp(desired_size, scale_factor) + .to_physical::(scale_factor); + #[cfg(windows)] + { + if window_builder.inner.window.decorations { + use windows::Win32::UI::WindowsAndMessaging::{AdjustWindowRect, WS_OVERLAPPEDWINDOW}; + let mut rect = windows::Win32::Foundation::RECT::default(); + let result = unsafe { AdjustWindowRect(&mut rect, WS_OVERLAPPEDWINDOW, false) }; + if result.is_ok() { + window_size.width += (rect.right - rect.left) as u32; + // rect.bottom is made out of shadow, and we don't care about it + window_size.height += -rect.top as u32; + } + } + } + let position = calculate_window_center_position(window_size, monitor); + let logical_position = position.to_logical::(scale_factor); + window_builder = window_builder.position(logical_position.x, logical_position.y); + } + } + let window = window_builder.inner.build(event_loop).unwrap(); #[cfg(feature = "tracing")] @@ -3216,11 +3596,7 @@ fn create_window( }); } - context.webview_id_map.insert(window.id(), window_id); - - if window_builder.center { - let _ = center_window(&window, window.inner_size()); - } + context.window_id_map.insert(window.id(), window_id); if let Some(handler) = after_window_creation { let raw = RawWindow { @@ -3251,9 +3627,12 @@ fn create_window( if let Some(webview) = webview { webviews.push(create_webview( + #[cfg(feature = "unstable")] + WebviewKind::WindowChild, + #[cfg(not(feature = "unstable"))] WebviewKind::WindowContent, &window, - window_id, + Arc::new(Mutex::new(window_id)), webview_id, context, webview, @@ -3284,13 +3663,17 @@ fn create_window( inner: Some(window), webviews, window_event_listeners, + #[cfg(windows)] + is_window_fullscreen, + #[cfg(windows)] is_window_transparent, #[cfg(windows)] surface, }) } -// the kind of the webview +/// the kind of the webview +#[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Copy)] enum WebviewKind { // webview is the entire window content WindowContent, @@ -3298,7 +3681,7 @@ enum WebviewKind { WindowChild, } -#[derive(Clone)] +#[derive(Debug, Clone)] struct WebviewBounds { x_rate: f32, y_rate: f32, @@ -3309,7 +3692,7 @@ struct WebviewBounds { fn create_webview( kind: WebviewKind, window: &Window, - window_id: WindowId, + window_id: Arc>, id: WebviewId, context: &Context, pending: PendingWebview>, @@ -3368,54 +3751,98 @@ fn create_webview( let mut webview_builder = builder .with_focused(window.is_focused()) .with_url(&url) - .unwrap() // safe to unwrap because we validate the URL beforehand .with_transparent(webview_attributes.transparent) - .with_accept_first_mouse(webview_attributes.accept_first_mouse); + .with_accept_first_mouse(webview_attributes.accept_first_mouse) + .with_hotkeys_zoom(webview_attributes.zoom_hotkeys_enabled); - if webview_attributes.file_drop_handler_enabled { + #[cfg(windows)] + if kind == WebviewKind::WindowContent { + webview_builder = webview_builder.with_initialization_script(undecorated_resizing::SCRIPT); + } + + if webview_attributes.drag_drop_handler_enabled { let proxy = context.proxy.clone(); - webview_builder = webview_builder.with_file_drop_handler(move |event| { - let event: FileDropEvent = FileDropEventWrapper(event).into(); - let _ = proxy.send_event(Message::Webview( - window_id, - id, - WebviewMessage::WebviewEvent(WebviewEvent::FileDrop(event)), - )); + let window_id_ = window_id.clone(); + webview_builder = webview_builder.with_drag_drop_handler(move |event| { + let event = match event { + WryDragDropEvent::Enter { + paths, + position: (x, y), + } => DragDropEvent::Dragged { + paths, + position: PhysicalPosition::new(x as _, y as _), + }, + WryDragDropEvent::Over { position: (x, y) } => DragDropEvent::DragOver { + position: PhysicalPosition::new(x as _, y as _), + }, + WryDragDropEvent::Drop { + paths, + position: (x, y), + } => DragDropEvent::Dropped { + paths, + position: PhysicalPosition::new(x as _, y as _), + }, + WryDragDropEvent::Leave => DragDropEvent::Cancelled, + _ => unimplemented!(), + }; + + let message = if kind == WebviewKind::WindowContent { + WebviewMessage::SynthesizedWindowEvent(SynthesizedWindowEvent::DragDrop(event)) + } else { + WebviewMessage::WebviewEvent(WebviewEvent::DragDrop(event)) + }; + + let _ = proxy.send_event(Message::Webview(*window_id_.lock().unwrap(), id, message)); true }); } if let Some(navigation_handler) = pending.navigation_handler { webview_builder = webview_builder.with_navigation_handler(move |url| { - Url::parse(&url) + url + .parse() .map(|url| navigation_handler(&url)) .unwrap_or(true) }); } - let webview_bounds = if let Some((position, size)) = webview_attributes.bounds { - let size = size.to_logical(window.scale_factor()); - let position = position.to_logical(window.scale_factor()); - webview_builder = webview_builder.with_bounds(wry::Rect { - x: position.x, - y: position.y, - width: size.width, - height: size.height, - }); + let webview_bounds = if let Some(bounds) = webview_attributes.bounds { + let bounds: RectWrapper = bounds.into(); + let bounds = bounds.0; - let window_size = window.inner_size(); + let scale_factor = window.scale_factor(); + let position = bounds.position.to_logical::(scale_factor); + let size = bounds.size.to_logical::(scale_factor); + + webview_builder = webview_builder.with_bounds(bounds); + + let window_size = window.inner_size().to_logical::(scale_factor); if webview_attributes.auto_resize { Some(WebviewBounds { - x_rate: (position.x as f32) / window_size.width as f32, - y_rate: (position.y as f32) / window_size.height as f32, - width_rate: (size.width as f32) / window_size.width as f32, - height_rate: (size.height as f32) / window_size.height as f32, + x_rate: position.x / window_size.width, + y_rate: position.y / window_size.height, + width_rate: size.width / window_size.width, + height_rate: size.height / window_size.height, }) } else { None } } else { + #[cfg(feature = "unstable")] + { + webview_builder = webview_builder.with_bounds(wry::Rect { + position: LogicalPosition::new(0, 0).into(), + size: window.inner_size().into(), + }); + Some(WebviewBounds { + x_rate: 0., + y_rate: 0., + width_rate: 1., + height_rate: 1., + }) + } + #[cfg(not(feature = "unstable"))] None }; @@ -3440,7 +3867,7 @@ fn create_webview( if let Some(page_load_handler) = pending.on_page_load_handler { webview_builder = webview_builder.with_on_page_load_handler(move |event, url| { - let _ = Url::parse(&url).map(|url| { + let _ = url.parse().map(|url| { page_load_handler( url, match event { @@ -3480,15 +3907,14 @@ fn create_webview( webview_builder = webview_builder.with_https_scheme(false); } - if let Some(handler) = ipc_handler { - webview_builder = webview_builder.with_ipc_handler(create_ipc_handler( - window_id, - id, - context.clone(), - label.clone(), - handler, - )); - } + webview_builder = webview_builder.with_ipc_handler(create_ipc_handler( + kind, + window_id.clone(), + id, + context.clone(), + label.clone(), + ipc_handler, + )); for (scheme, protocol) in uri_scheme_protocols { webview_builder = @@ -3565,19 +3991,31 @@ fn create_webview( .build() .map_err(|e| Error::CreateWebview(Box::new(e)))?; + #[cfg(any( + target_os = "linux", + target_os = "dragonfly", + target_os = "freebsd", + target_os = "netbsd", + target_os = "openbsd" + ))] + if kind == WebviewKind::WindowContent { + undecorated_resizing::attach_resize_handler(&webview); + } + #[cfg(windows)] - { + if kind == WebviewKind::WindowContent { let controller = webview.controller(); let proxy = context.proxy.clone(); let proxy_ = proxy.clone(); + let window_id_ = window_id.clone(); let mut token = EventRegistrationToken::default(); unsafe { controller.add_GotFocus( &FocusChangedEventHandler::create(Box::new(move |_, _| { let _ = proxy.send_event(Message::Webview( - window_id, + *window_id_.lock().unwrap(), id, - WebviewMessage::WebviewEvent(WebviewEvent::Focused(true)), + WebviewMessage::SynthesizedWindowEvent(SynthesizedWindowEvent::Focused(true)), )); Ok(()) })), @@ -3589,9 +4027,9 @@ fn create_webview( controller.add_LostFocus( &FocusChangedEventHandler::create(Box::new(move |_, _| { let _ = proxy_.send_event(Message::Webview( - window_id, + *window_id.lock().unwrap(), id, - WebviewMessage::WebviewEvent(WebviewEvent::Focused(false)), + WebviewMessage::SynthesizedWindowEvent(SynthesizedWindowEvent::Focused(false)), )); Ok(()) })), @@ -3602,38 +4040,50 @@ fn create_webview( } Ok(WebviewWrapper { + label, id, inner: Rc::new(webview), context_store: context.main_thread.web_context.clone(), + webview_event_listeners: Default::default(), context_key: if automation_enabled { None } else { web_context_key }, - bounds: webview_bounds.map(|b| Arc::new(Mutex::new(b))), + bounds: Arc::new(Mutex::new(webview_bounds)), }) } /// Create a wry ipc handler from a tauri ipc handler. fn create_ipc_handler( - window_id: WindowId, + _kind: WebviewKind, + window_id: Arc>, webview_id: WebviewId, context: Context, label: String, - handler: WebviewIpcHandler>, + ipc_handler: Option>>, ) -> Box { Box::new(move |request| { - handler( - DetachedWebview { - label: label.clone(), - dispatcher: WryWebviewDispatcher { - window_id, - webview_id, - context: context.clone(), + #[cfg(windows)] + if _kind == WebviewKind::WindowContent + && undecorated_resizing::handle_request(context.clone(), *window_id.lock().unwrap(), &request) + { + return; + } + + if let Some(handler) = &ipc_handler { + handler( + DetachedWebview { + label: label.clone(), + dispatcher: WryWebviewDispatcher { + window_id: window_id.clone(), + webview_id, + context: context.clone(), + }, }, - }, - request, - ); + request, + ); + } }) } @@ -3643,7 +4093,7 @@ fn inner_size( webviews: &[WebviewWrapper], has_children: bool, ) -> TaoPhysicalSize { - if has_children && webviews.len() == 1 { + if !has_children { use wry::WebViewExtMacOS; let webview = webviews.first().unwrap(); let view_frame = unsafe { cocoa::appkit::NSView::frame(webview.webview()) }; @@ -3664,6 +4114,32 @@ fn inner_size( window.inner_size() } +fn calculate_window_center_position( + window_size: TaoPhysicalSize, + target_monitor: MonitorHandle, +) -> TaoPhysicalPosition { + #[cfg(windows)] + { + use tao::platform::windows::MonitorHandleExtWindows; + use windows::Win32::Graphics::Gdi::{GetMonitorInfoW, HMONITOR, MONITORINFO}; + let mut monitor_info = MONITORINFO::default(); + monitor_info.cbSize = std::mem::size_of::() as u32; + let status = unsafe { GetMonitorInfoW(HMONITOR(target_monitor.hmonitor()), &mut monitor_info) }; + if status.into() { + let available_width = monitor_info.rcWork.right - monitor_info.rcWork.left; + let available_height = monitor_info.rcWork.bottom - monitor_info.rcWork.top; + let x = (available_width - window_size.width as i32) / 2 + monitor_info.rcWork.left; + let y = (available_height - window_size.height as i32) / 2 + monitor_info.rcWork.top; + return TaoPhysicalPosition::new(x, y); + } + } + let screen_size = target_monitor.size(); + let monitor_pos = target_monitor.position(); + let x = (screen_size.width as i32 - window_size.width as i32) / 2 + monitor_pos.x; + let y = (screen_size.height as i32 - window_size.height as i32) / 2 + monitor_pos.y; + TaoPhysicalPosition::new(x, y) +} + #[cfg(windows)] fn clear_window_surface( window: &Window, @@ -3680,3 +4156,15 @@ fn clear_window_surface( let _ = buffer.present(); } } + +#[cfg(windows)] +fn set_webview_visibility( + webviews: &[WebviewWrapper], + is_visible: bool, +) -> windows::core::Result<()> { + for webview in webviews { + let controller = webview.controller(); + unsafe { controller.SetIsVisible(is_visible) }?; + } + Ok(()) +} diff --git a/core/tauri-runtime-wry/src/undecorated_resizing.rs b/core/tauri-runtime-wry/src/undecorated_resizing.rs new file mode 100644 index 000000000..5ec301d7b --- /dev/null +++ b/core/tauri-runtime-wry/src/undecorated_resizing.rs @@ -0,0 +1,328 @@ +// Copyright 2019-2024 Tauri Programme within The Commons Conservancy +// SPDX-License-Identifier: Apache-2.0 +// SPDX-License-Identifier: MIT + +#![cfg(any( + windows, + target_os = "linux", + target_os = "dragonfly", + target_os = "freebsd", + target_os = "netbsd", + target_os = "openbsd" +))] + +const CLIENT: isize = 0b0000; +const LEFT: isize = 0b0001; +const RIGHT: isize = 0b0010; +const TOP: isize = 0b0100; +const BOTTOM: isize = 0b1000; +const TOPLEFT: isize = TOP | LEFT; +const TOPRIGHT: isize = TOP | RIGHT; +const BOTTOMLEFT: isize = BOTTOM | LEFT; +const BOTTOMRIGHT: isize = BOTTOM | RIGHT; + +#[cfg(not(windows))] +pub use self::gtk::*; +#[cfg(windows)] +pub use self::windows::*; + +#[cfg(windows)] +type WindowDimensions = u32; +#[cfg(not(windows))] +type WindowDimensions = i32; +#[cfg(windows)] +type WindowPositions = i32; +#[cfg(not(windows))] +type WindowPositions = f64; + +#[derive(Debug, PartialEq, Eq, Clone, Copy)] +enum HitTestResult { + Client, + Left, + Right, + Top, + Bottom, + TopLeft, + TopRight, + BottomLeft, + BottomRight, + NoWhere, +} + +fn hit_test( + width: WindowDimensions, + height: WindowDimensions, + x: WindowPositions, + y: WindowPositions, + border_x: WindowPositions, + border_y: WindowPositions, +) -> HitTestResult { + #[cfg(windows)] + let (top, left) = (0, 0); + #[cfg(not(windows))] + let (top, left) = (0., 0.); + + let bottom = top + height as WindowPositions; + let right = left + width as WindowPositions; + + #[rustfmt::skip] + let result = (LEFT * (x < left + border_x) as isize) + | (RIGHT * (x >= right - border_x) as isize) + | (TOP * (y < top + border_y) as isize) + | (BOTTOM * (y >= bottom - border_y) as isize); + + match result { + CLIENT => HitTestResult::Client, + LEFT => HitTestResult::Left, + RIGHT => HitTestResult::Right, + TOP => HitTestResult::Top, + BOTTOM => HitTestResult::Bottom, + TOPLEFT => HitTestResult::TopLeft, + TOPRIGHT => HitTestResult::TopRight, + BOTTOMLEFT => HitTestResult::BottomLeft, + BOTTOMRIGHT => HitTestResult::BottomRight, + _ => HitTestResult::NoWhere, + } +} + +#[cfg(windows)] +mod windows { + use super::{hit_test, HitTestResult}; + + use tao::window::{CursorIcon, ResizeDirection, Window}; + use windows::Win32::UI::WindowsAndMessaging::{ + GetSystemMetrics, SM_CXFRAME, SM_CXPADDEDBORDER, SM_CYFRAME, + }; + + const MESSAGE_MOUSEMOVE: &str = "__internal_on_mousemove__|"; + const MESSAGE_MOUSEDOWN: &str = "__internal_on_mousedown__|"; + pub const SCRIPT: &str = r#" +;(function () { + document.addEventListener('mousemove', (e) => { + window.ipc.postMessage( + `__internal_on_mousemove__|${e.clientX},${e.clientY}` + ) + }) + document.addEventListener('mousedown', (e) => { + if (e.button === 0) { + window.ipc.postMessage( + `__internal_on_mousedown__|${e.clientX},${e.clientY}` + ) + } + }) +})() +"#; + + impl HitTestResult { + fn drag_resize_window(&self, window: &Window) { + self.change_cursor(window); + let edge = match self { + HitTestResult::Left => ResizeDirection::West, + HitTestResult::Right => ResizeDirection::East, + HitTestResult::Top => ResizeDirection::North, + HitTestResult::Bottom => ResizeDirection::South, + HitTestResult::TopLeft => ResizeDirection::NorthWest, + HitTestResult::TopRight => ResizeDirection::NorthEast, + HitTestResult::BottomLeft => ResizeDirection::SouthWest, + HitTestResult::BottomRight => ResizeDirection::SouthEast, + + // if not on an edge, don't start resizing + _ => return, + }; + let _ = window.drag_resize_window(edge); + } + + fn change_cursor(&self, window: &Window) { + let cursor = match self { + HitTestResult::Left => CursorIcon::WResize, + HitTestResult::Right => CursorIcon::EResize, + HitTestResult::Top => CursorIcon::NResize, + HitTestResult::Bottom => CursorIcon::SResize, + HitTestResult::TopLeft => CursorIcon::NwResize, + HitTestResult::TopRight => CursorIcon::NeResize, + HitTestResult::BottomLeft => CursorIcon::SwResize, + HitTestResult::BottomRight => CursorIcon::SeResize, + + // if not on an edge, don't change the cursor, otherwise we cause flickering + _ => return, + }; + window.set_cursor_icon(cursor); + } + } + + // Returns whether handled or not + pub fn handle_request( + context: crate::Context, + window_id: crate::WindowId, + request: &http::Request, + ) -> bool { + if let Some(args) = request.body().strip_prefix(MESSAGE_MOUSEMOVE) { + if let Some(window) = context.main_thread.windows.0.borrow().get(&window_id) { + if let Some(w) = window.inner.as_ref() { + if !w.is_decorated() + && w.is_resizable() + && !w.is_maximized() + && !window.is_window_fullscreen + { + let (x, y) = args.split_once(',').unwrap(); + let (x, y) = (x.parse().unwrap(), y.parse().unwrap()); + let size = w.inner_size(); + let padded_border = unsafe { GetSystemMetrics(SM_CXPADDEDBORDER) }; + let border_x = unsafe { GetSystemMetrics(SM_CXFRAME) + padded_border }; + let border_y = unsafe { GetSystemMetrics(SM_CYFRAME) + padded_border }; + hit_test(size.width, size.height, x, y, border_x, border_y).change_cursor(w); + } + } + } + + return true; + } + if let Some(args) = request.body().strip_prefix(MESSAGE_MOUSEDOWN) { + if let Some(window) = context.main_thread.windows.0.borrow().get(&window_id) { + if let Some(w) = window.inner.as_ref() { + if !w.is_decorated() + && w.is_resizable() + && !w.is_maximized() + && !window.is_window_fullscreen + { + let (x, y) = args.split_once(',').unwrap(); + let (x, y) = (x.parse().unwrap(), y.parse().unwrap()); + let size = w.inner_size(); + let padded_border = unsafe { GetSystemMetrics(SM_CXPADDEDBORDER) }; + let border_x = unsafe { GetSystemMetrics(SM_CXFRAME) + padded_border }; + let border_y = unsafe { GetSystemMetrics(SM_CYFRAME) + padded_border }; + hit_test(size.width, size.height, x, y, border_x, border_y).drag_resize_window(w); + } + } + } + + return true; + } + + false + } +} + +#[cfg(not(windows))] +mod gtk { + use super::{hit_test, HitTestResult}; + + const BORDERLESS_RESIZE_INSET: i32 = 5; + + impl HitTestResult { + fn to_gtk_edge(self) -> gtk::gdk::WindowEdge { + match self { + HitTestResult::Client | HitTestResult::NoWhere => gtk::gdk::WindowEdge::__Unknown(0), + HitTestResult::Left => gtk::gdk::WindowEdge::West, + HitTestResult::Right => gtk::gdk::WindowEdge::East, + HitTestResult::Top => gtk::gdk::WindowEdge::North, + HitTestResult::Bottom => gtk::gdk::WindowEdge::South, + HitTestResult::TopLeft => gtk::gdk::WindowEdge::NorthWest, + HitTestResult::TopRight => gtk::gdk::WindowEdge::NorthEast, + HitTestResult::BottomLeft => gtk::gdk::WindowEdge::SouthWest, + HitTestResult::BottomRight => gtk::gdk::WindowEdge::SouthEast, + } + } + } + + pub fn attach_resize_handler(webview: &wry::WebView) { + use gtk::{ + gdk::{prelude::*, WindowEdge}, + glib::Propagation, + prelude::*, + }; + use wry::WebViewExtUnix; + + let webview = webview.webview(); + + webview.add_events( + gtk::gdk::EventMask::BUTTON1_MOTION_MASK + | gtk::gdk::EventMask::BUTTON_PRESS_MASK + | gtk::gdk::EventMask::TOUCH_MASK, + ); + + webview.connect_button_press_event( + move |webview: &webkit2gtk::WebView, event: >k::gdk::EventButton| { + if event.button() == 1 { + // This one should be GtkBox + if let Some(window) = webview.parent().and_then(|w| w.parent()) { + // Safe to unwrap unless this is not from tao + let window: gtk::Window = window.downcast().unwrap(); + if !window.is_decorated() && window.is_resizable() && !window.is_maximized() { + if let Some(window) = window.window() { + let (root_x, root_y) = event.root(); + let (window_x, window_y) = window.position(); + let (client_x, client_y) = (root_x - window_x as f64, root_y - window_y as f64); + let border = window.scale_factor() * BORDERLESS_RESIZE_INSET; + let edge = hit_test( + window.width(), + window.height(), + client_x, + client_y, + border as _, + border as _, + ) + .to_gtk_edge(); + + // we ignore the `__Unknown` variant so the webview receives the click correctly if it is not on the edges. + match edge { + WindowEdge::__Unknown(_) => (), + _ => { + window.begin_resize_drag(edge, 1, root_x as i32, root_y as i32, event.time()) + } + } + } + } + } + } + + Propagation::Proceed + }, + ); + + webview.connect_touch_event( + move |webview: &webkit2gtk::WebView, event: >k::gdk::Event| { + // This one should be GtkBox + if let Some(window) = webview.parent().and_then(|w| w.parent()) { + // Safe to unwrap unless this is not from tao + let window: gtk::Window = window.downcast().unwrap(); + if !window.is_decorated() && window.is_resizable() && !window.is_maximized() { + if let Some(window) = window.window() { + if let Some((root_x, root_y)) = event.root_coords() { + if let Some(device) = event.device() { + let (window_x, window_y) = window.position(); + let (client_x, client_y) = (root_x - window_x as f64, root_y - window_y as f64); + let border = window.scale_factor() * BORDERLESS_RESIZE_INSET; + let edge = hit_test( + window.width(), + window.height(), + client_x, + client_y, + border as _, + border as _, + ) + .to_gtk_edge(); + + // we ignore the `__Unknown` variant so the window receives the click correctly if it is not on the edges. + match edge { + WindowEdge::__Unknown(_) => (), + _ => window.begin_resize_drag_for_device( + edge, + &device, + 0, + root_x as i32, + root_y as i32, + event.time(), + ), + } + } + } + } + } + } + + Propagation::Proceed + }, + ); + } +} diff --git a/core/tauri-runtime-wry/src/webview.rs b/core/tauri-runtime-wry/src/webview.rs index fc01cdf2a..422d5a253 100644 --- a/core/tauri-runtime-wry/src/webview.rs +++ b/core/tauri-runtime-wry/src/webview.rs @@ -1,4 +1,4 @@ -// Copyright 2019-2023 Tauri Programme within The Commons Conservancy +// Copyright 2019-2024 Tauri Programme within The Commons Conservancy // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT diff --git a/core/tauri-runtime/CHANGELOG.md b/core/tauri-runtime/CHANGELOG.md index 0c1ddd5a3..0893587fa 100644 --- a/core/tauri-runtime/CHANGELOG.md +++ b/core/tauri-runtime/CHANGELOG.md @@ -1,5 +1,95 @@ # Changelog +## \[2.0.0-beta.11] + +### What's Changed + +- [`06833f4fa`](https://www.github.com/tauri-apps/tauri/commit/06833f4fa8e63ecc55fe3fc874a9e397e77a5709)([#9100](https://www.github.com/tauri-apps/tauri/pull/9100)) Updated `http` crate to `1.1` + +### Dependencies + +- Upgraded to `tauri-utils@2.0.0-beta.11` + +### Breaking Changes + +- [`06833f4fa`](https://www.github.com/tauri-apps/tauri/commit/06833f4fa8e63ecc55fe3fc874a9e397e77a5709)([#9100](https://www.github.com/tauri-apps/tauri/pull/9100)) The IPC handler closure now receives a `http::Request` instead of a String representing the request body. +- [`06833f4fa`](https://www.github.com/tauri-apps/tauri/commit/06833f4fa8e63ecc55fe3fc874a9e397e77a5709)([#9100](https://www.github.com/tauri-apps/tauri/pull/9100)) Rename `FileDrop` to `DragDrop` on structs, enums and enum variants. Also renamed `file_drop` to `drag_drop` on fields and function names. +- [`06833f4fa`](https://www.github.com/tauri-apps/tauri/commit/06833f4fa8e63ecc55fe3fc874a9e397e77a5709)([#9100](https://www.github.com/tauri-apps/tauri/pull/9100)) Moved `window::dpi` module to the root of the crate. + +## \[2.0.0-beta.10] + +### Dependencies + +- Upgraded to `tauri-utils@2.0.0-beta.10` + +## \[2.0.0-beta.9] + +### Dependencies + +- Upgraded to `tauri-utils@2.0.0-beta.9` + +## \[2.0.0-beta.8] + +### Dependencies + +- Upgraded to `tauri-utils@2.0.0-beta.8` + +## \[2.0.0-beta.7] + +### New Features + +- [`46de49aaa`](https://www.github.com/tauri-apps/tauri/commit/46de49aaad4a148fafc31d591be0e2ed12256507)([#9059](https://www.github.com/tauri-apps/tauri/pull/9059)) Added `set_auto_resize` method for the webview. + +### Dependencies + +- Upgraded to `tauri-utils@2.0.0-beta.7` + +### Breaking Changes + +- [`d1e77acd8`](https://www.github.com/tauri-apps/tauri/commit/d1e77acd8dfdf554b90b542513a58a2de1ef2360)([#9011](https://www.github.com/tauri-apps/tauri/pull/9011)) Add a lifetime parameter for `Icon` type. Also changed `rgba` field to be `Cow<'a, [u8]>` + +## \[2.0.0-beta.6] + +### Dependencies + +- Upgraded to `tauri-utils@2.0.0-beta.6` + +## \[2.0.0-beta.5] + +### Dependencies + +- Upgraded to `tauri-utils@2.0.0-beta.5` + +## \[2.0.0-beta.4] + +### New Features + +- [`fdcaf935`](https://www.github.com/tauri-apps/tauri/commit/fdcaf935fa75ecfa2806939c4faad4fe9e880386)([#8939](https://www.github.com/tauri-apps/tauri/pull/8939)) Added the `reparent` function to the webview API. + +### Dependencies + +- Upgraded to `tauri-utils@2.0.0-beta.4` + +## \[2.0.0-beta.3] + +### Dependencies + +- Upgraded to `tauri-utils@2.0.0-beta.3` + +## \[2.0.0-beta.2] + +### What's Changed + +- [`16e550ec`](https://www.github.com/tauri-apps/tauri/commit/16e550ec1503765158cdc3bb2a20e70ec710e981)([#8844](https://www.github.com/tauri-apps/tauri/pull/8844)) Add `WebviewEvent`, `RunEvent::WebviewEvent` and `WebviewDispatch::on_webview_event`. + +### Dependencies + +- Upgraded to `tauri-utils@2.0.0-beta.2` + +### Breaking Changes + +- [`2f55bfec`](https://www.github.com/tauri-apps/tauri/commit/2f55bfecbf0244f3b5aa1ad7622182fca3fcdcbb)([#8795](https://www.github.com/tauri-apps/tauri/pull/8795)) Update raw-window-handle to 0.6. + ## \[2.0.0-beta.1] ### Dependencies @@ -186,6 +276,12 @@ - Bumped due to a bump in tauri-utils. - [fa3a1098](https://www.github.com/tauri-apps/tauri/commit/fa3a10988a03aed1b66fb17d893b1a9adb90f7cd) feat(ci): prepare 2.0.0-alpha.0 ([#5786](https://www.github.com/tauri-apps/tauri/pull/5786)) on 2022-12-08 +## \[0.14.2] + +### Dependencies + +- Upgraded to `tauri-utils@1.5.2` + ## \[0.14.1] ### Enhancements diff --git a/core/tauri-runtime/Cargo.toml b/core/tauri-runtime/Cargo.toml index e8cf98bfa..7e0b5d333 100644 --- a/core/tauri-runtime/Cargo.toml +++ b/core/tauri-runtime/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "tauri-runtime" -version = "2.0.0-beta.1" +version = "2.0.0-beta.11" description = "Runtime for Tauri applications" exclude = [ "CHANGELOG.md", "/target" ] readme = "README.md" @@ -29,13 +29,14 @@ targets = [ serde = { version = "1.0", features = [ "derive" ] } serde_json = "1.0" thiserror = "1.0" -tauri-utils = { version = "2.0.0-beta.1", path = "../tauri-utils" } -http = "0.2.4" +tauri-utils = { version = "2.0.0-beta.11", path = "../tauri-utils" } +http = "1.1" raw-window-handle = "0.6" url = { version = "2" } +dpi = { version = "0.1", features = [ "serde" ] } [target."cfg(windows)".dependencies.windows] -version = "0.52" +version = "0.54" features = [ "Win32_Foundation" ] [target."cfg(any(target_os = \"linux\", target_os = \"dragonfly\", target_os = \"freebsd\", target_os = \"openbsd\", target_os = \"netbsd\"))".dependencies] diff --git a/core/tauri-runtime/build.rs b/core/tauri-runtime/build.rs index 2ccb32962..57f522ab6 100644 --- a/core/tauri-runtime/build.rs +++ b/core/tauri-runtime/build.rs @@ -1,4 +1,4 @@ -// Copyright 2019-2023 Tauri Programme within The Commons Conservancy +// Copyright 2019-2024 Tauri Programme within The Commons Conservancy // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT diff --git a/core/tauri-runtime/src/lib.rs b/core/tauri-runtime/src/lib.rs index cad68dae9..79331693e 100644 --- a/core/tauri-runtime/src/lib.rs +++ b/core/tauri-runtime/src/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2019-2023 Tauri Programme within The Commons Conservancy +// Copyright 2019-2024 Tauri Programme within The Commons Conservancy // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT @@ -13,9 +13,9 @@ #![cfg_attr(docsrs, feature(doc_cfg))] use raw_window_handle::DisplayHandle; -use serde::Deserialize; -use std::{fmt::Debug, sync::mpsc::Sender}; -use tauri_utils::{ProgressBarState, Theme}; +use serde::{Deserialize, Serialize}; +use std::{borrow::Cow, fmt::Debug, sync::mpsc::Sender}; +use tauri_utils::Theme; use url::Url; use webview::{DetachedWebview, PendingWebview}; @@ -24,11 +24,9 @@ pub mod monitor; pub mod webview; pub mod window; +use dpi::{PhysicalPosition, PhysicalSize, Position, Size}; use monitor::Monitor; -use window::{ - dpi::{PhysicalPosition, PhysicalSize, Position, Size}, - CursorIcon, DetachedWindow, PendingWindow, RawWindow, WindowEvent, -}; +use window::{CursorIcon, DetachedWindow, PendingWindow, RawWindow, WebviewEvent, WindowEvent}; use window::{WindowBuilder, WindowId}; use http::{ @@ -37,7 +35,57 @@ use http::{ status::InvalidStatusCode, }; +/// UI scaling utilities. +pub use dpi; + pub type WindowEventId = u32; +pub type WebviewEventId = u32; + +/// A rectangular region. +#[derive(Clone, Copy, Debug, Serialize)] +pub struct Rect { + /// Rect position. + pub position: dpi::Position, + /// Rect size. + pub size: dpi::Size, +} + +impl Default for Rect { + fn default() -> Self { + Self { + position: Position::Logical((0, 0).into()), + size: Size::Logical((0, 0).into()), + } + } +} + +/// Progress bar status. +#[derive(Debug, Clone, Copy, Deserialize)] +#[serde(rename_all = "camelCase")] +pub enum ProgressBarStatus { + /// Hide progress bar. + None, + /// Normal state. + Normal, + /// Indeterminate state. **Treated as Normal on Linux and macOS** + Indeterminate, + /// Paused state. **Treated as Normal on Linux** + Paused, + /// Error state. **Treated as Normal on Linux** + Error, +} + +/// Progress Bar State +#[derive(Debug, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct ProgressBarState { + /// The progress bar status. + pub status: Option, + /// The progress bar progress. This can be a value ranging from `0` to `100` + pub progress: Option, + /// The `.desktop` filename with the Unity desktop window manager, for example `myapp.desktop` **Linux Only** + pub desktop_filename: Option, +} /// Type of user attention requested on a window. #[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize)] @@ -133,9 +181,9 @@ pub type Result = std::result::Result; /// Window icon. #[derive(Debug, Clone)] -pub struct Icon { +pub struct Icon<'a> { /// RGBA bytes of the icon. - pub rgba: Vec, + pub rgba: Cow<'a, [u8]>, /// Icon width. pub width: u32, /// Icon height. @@ -166,6 +214,13 @@ pub enum RunEvent { /// The detailed event. event: WindowEvent, }, + /// An event associated with a webview. + WebviewEvent { + /// The webview label. + label: String, + /// The detailed event. + event: WebviewEvent, + }, /// Application ready. Ready, /// Sent if the event loop is being resumed. @@ -348,7 +403,7 @@ pub trait Runtime: Debug + Sized + 'static { /// Runs an iteration of the runtime event loop and returns control flow to the caller. #[cfg(desktop)] - fn run_iteration)>(&mut self, callback: F); + fn run_iteration) + 'static>(&mut self, callback: F); /// Run the webview runtime. fn run) + 'static>(self, callback: F); @@ -362,6 +417,9 @@ pub trait WebviewDispatch: Debug + Clone + Send + Sync + Sized + ' /// Run a task on the main thread. fn run_on_main_thread(&self, f: F) -> Result<()>; + /// Registers a webview event handler. + fn on_webview_event(&self, f: F) -> WebviewEventId; + /// Runs a closure with the platform webview object as argument. fn with_webview) + Send + 'static>(&self, f: F) -> Result<()>; @@ -382,6 +440,9 @@ pub trait WebviewDispatch: Debug + Clone + Send + Sync + Sized + ' /// Returns the webview's current URL. fn url(&self) -> Result; + /// Returns the webview's bounds. + fn bounds(&self) -> Result; + /// Returns the position of the top-left hand corner of the webviews's client area relative to the top-left hand corner of the window. fn position(&self) -> Result>; @@ -390,7 +451,7 @@ pub trait WebviewDispatch: Debug + Clone + Send + Sync + Sized + ' // SETTER - /// Naviagte to the given URL. + /// Navigate to the given URL. fn navigate(&self, url: Url) -> Result<()>; /// Opens the dialog to prints the contents of the webview. @@ -399,6 +460,9 @@ pub trait WebviewDispatch: Debug + Clone + Send + Sync + Sized + ' /// Closes the webview. fn close(&self) -> Result<()>; + /// Sets the webview's bounds. + fn set_bounds(&self, bounds: Rect) -> Result<()>; + /// Resizes the webview. fn set_size(&self, size: Size) -> Result<()>; @@ -410,6 +474,15 @@ pub trait WebviewDispatch: Debug + Clone + Send + Sync + Sized + ' /// Executes javascript on the window this [`WindowDispatch`] represents. fn eval_script>(&self, script: S) -> Result<()>; + + /// Moves the webview to the given window. + fn reparent(&self, window_id: WindowId) -> Result<()>; + + /// Sets whether the webview should automatically grow and shrink its size and position when the parent window resizes. + fn set_auto_resize(&self, auto_resize: bool) -> Result<()>; + + /// Set the webview zoom level + fn set_zoom(&self, scale_factor: f64) -> Result<()>; } /// Window dispatcher. A thread-safe handle to the window APIs. @@ -472,7 +545,7 @@ pub trait WindowDispatch: Debug + Clone + Send + Sync + Sized + 's /// - **Linux / iOS / Android:** Unsupported. fn is_maximizable(&self) -> Result; - /// Gets the window's native minize button state. + /// Gets the window's native minimize button state. /// /// ## Platform-specific /// diff --git a/core/tauri-runtime/src/monitor.rs b/core/tauri-runtime/src/monitor.rs index ce0139148..0bedbbf4f 100644 --- a/core/tauri-runtime/src/monitor.rs +++ b/core/tauri-runtime/src/monitor.rs @@ -1,8 +1,8 @@ -// Copyright 2019-2023 Tauri Programme within The Commons Conservancy +// Copyright 2019-2024 Tauri Programme within The Commons Conservancy // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT -use super::window::dpi::{PhysicalPosition, PhysicalSize}; +use crate::dpi::{PhysicalPosition, PhysicalSize}; /// Monitor descriptor. #[derive(Debug, Clone)] diff --git a/core/tauri-runtime/src/webview.rs b/core/tauri-runtime/src/webview.rs index 6864a4210..f286ab237 100644 --- a/core/tauri-runtime/src/webview.rs +++ b/core/tauri-runtime/src/webview.rs @@ -1,17 +1,12 @@ -// Copyright 2019-2023 Tauri Programme within The Commons Conservancy +// Copyright 2019-2024 Tauri Programme within The Commons Conservancy // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT //! A layer between raw [`Runtime`] webviews and Tauri. //! -use crate::{ - window::{ - dpi::{Position, Size}, - is_label_valid, - }, - Runtime, UserEvent, -}; +use crate::{window::is_label_valid, Rect, Runtime, UserEvent}; +use http::Request; use tauri_utils::config::{WebviewUrl, WindowConfig, WindowEffectsConfig}; use url::Url; @@ -202,16 +197,17 @@ pub struct WebviewAttributes { pub user_agent: Option, pub initialization_scripts: Vec, pub data_directory: Option, - pub file_drop_handler_enabled: bool, + pub drag_drop_handler_enabled: bool, pub clipboard: bool, pub accept_first_mouse: bool, pub additional_browser_args: Option, pub window_effects: Option, pub incognito: bool, pub transparent: bool, - pub bounds: Option<(Position, Size)>, + pub bounds: Option, pub auto_resize: bool, pub proxy_url: Option, + pub zoom_hotkeys_enabled: bool, } impl From<&WindowConfig> for WebviewAttributes { @@ -223,8 +219,8 @@ impl From<&WindowConfig> for WebviewAttributes { builder = builder.transparent(config.transparent); } builder = builder.accept_first_mouse(config.accept_first_mouse); - if !config.file_drop_enabled { - builder = builder.disable_file_drop_handler(); + if !config.drag_drop_enabled { + builder = builder.disable_drag_drop_handler(); } if let Some(user_agent) = &config.user_agent { builder = builder.user_agent(user_agent); @@ -238,6 +234,7 @@ impl From<&WindowConfig> for WebviewAttributes { if let Some(url) = &config.proxy_url { builder = builder.proxy_url(url.to_owned()); } + builder = builder.zoom_hotkeys_enabled(config.zoom_hotkeys_enabled); builder } } @@ -250,7 +247,7 @@ impl WebviewAttributes { user_agent: None, initialization_scripts: Vec::new(), data_directory: None, - file_drop_handler_enabled: true, + drag_drop_handler_enabled: true, clipboard: false, accept_first_mouse: false, additional_browser_args: None, @@ -260,6 +257,7 @@ impl WebviewAttributes { bounds: None, auto_resize: false, proxy_url: None, + zoom_hotkeys_enabled: false, } } @@ -284,10 +282,10 @@ impl WebviewAttributes { self } - /// Disables the file drop handler. This is required to use drag and drop APIs on the front end on Windows. + /// Disables the drag and drop handler. This is required to use HTML5 drag and drop APIs on the frontend on Windows. #[must_use] - pub fn disable_file_drop_handler(mut self) -> Self { - self.file_drop_handler_enabled = false; + pub fn disable_drag_drop_handler(mut self) -> Self { + self.drag_drop_handler_enabled = false; self } @@ -350,7 +348,22 @@ impl WebviewAttributes { self.proxy_url = Some(url); self } + + /// Whether page zooming by hotkeys is enabled + /// + /// ## Platform-specific: + /// + /// - **Windows**: Controls WebView2's [`IsZoomControlEnabled`](https://learn.microsoft.com/en-us/microsoft-edge/webview2/reference/winrt/microsoft_web_webview2_core/corewebview2settings?view=webview2-winrt-1.0.2420.47#iszoomcontrolenabled) setting. + /// - **MacOS / Linux**: Injects a polyfill that zooms in and out with `ctrl/command` + `-/=`, + /// 20% in each step, ranging from 20% to 1000%. Requires `webview:allow-set-webview-zoom` permission + /// + /// - **Android / iOS**: Unsupported. + #[must_use] + pub fn zoom_hotkeys_enabled(mut self, enabled: bool) -> Self { + self.zoom_hotkeys_enabled = enabled; + self + } } /// IPC handler. -pub type WebviewIpcHandler = Box, String) + Send>; +pub type WebviewIpcHandler = Box, Request) + Send>; diff --git a/core/tauri-runtime/src/window.rs b/core/tauri-runtime/src/window.rs index 10b3e34a5..a52088c31 100644 --- a/core/tauri-runtime/src/window.rs +++ b/core/tauri-runtime/src/window.rs @@ -1,4 +1,4 @@ -// Copyright 2019-2023 Tauri Programme within The Commons Conservancy +// Copyright 2019-2024 Tauri Programme within The Commons Conservancy // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT @@ -21,11 +21,6 @@ use std::{ sync::mpsc::Sender, }; -use self::dpi::PhysicalPosition; - -/// UI scaling utilities. -pub mod dpi; - /// An event from a window. #[derive(Debug, Clone)] pub enum WindowEvent { @@ -57,31 +52,45 @@ pub enum WindowEvent { /// The window inner size. new_inner_size: dpi::PhysicalSize, }, - /// An event associated with the file drop action. - FileDrop(FileDropEvent), + /// An event associated with the drag and drop action. + DragDrop(DragDropEvent), /// The system window theme has changed. /// /// Applications might wish to react to this to change the theme of the content of the window when the system changes the window theme. ThemeChanged(Theme), } -/// The file drop event payload. +/// An event from a window. +#[derive(Debug, Clone)] +pub enum WebviewEvent { + /// An event associated with the drag and drop action. + DragDrop(DragDropEvent), +} + +/// The drag drop event payload. #[derive(Debug, Clone)] #[non_exhaustive] -pub enum FileDropEvent { - /// The file(s) have been dragged onto the window, but have not been dropped yet. - Hovered { +pub enum DragDropEvent { + /// A drag operation started. + Dragged { + /// Paths of the files that are being dragged. paths: Vec, /// The position of the mouse cursor. - position: PhysicalPosition, + position: dpi::PhysicalPosition, }, - /// The file(s) have been dropped onto the window. + /// The files have been dragged onto the window, but have not been dropped yet. + DragOver { + /// The position of the mouse cursor. + position: dpi::PhysicalPosition, + }, + /// The user dropped the operation. Dropped { + /// Path of the files that were dropped. paths: Vec, /// The position of the mouse cursor. - position: PhysicalPosition, + position: dpi::PhysicalPosition, }, - /// The file drop was aborted. + /// The drag operation was cancelled. Cancelled, } @@ -453,7 +462,7 @@ impl> PendingWindow { } /// Identifier of a window. -#[derive(Debug, Clone, Copy, Hash, Eq, PartialEq)] +#[derive(Debug, Clone, Copy, Hash, Eq, PartialEq, Ord, PartialOrd)] pub struct WindowId(u32); impl From for WindowId { diff --git a/core/tauri-runtime/src/window/dpi.rs b/core/tauri-runtime/src/window/dpi.rs deleted file mode 100644 index 0710db98d..000000000 --- a/core/tauri-runtime/src/window/dpi.rs +++ /dev/null @@ -1,401 +0,0 @@ -// Copyright 2019-2023 Tauri Programme within The Commons Conservancy -// SPDX-License-Identifier: Apache-2.0 -// SPDX-License-Identifier: MIT - -use serde::{Deserialize, Serialize}; - -pub trait Pixel: Copy + Into { - fn from_f64(f: f64) -> Self; - fn cast(self) -> P { - P::from_f64(self.into()) - } -} - -impl Pixel for u8 { - fn from_f64(f: f64) -> Self { - f.round() as u8 - } -} -impl Pixel for u16 { - fn from_f64(f: f64) -> Self { - f.round() as u16 - } -} -impl Pixel for u32 { - fn from_f64(f: f64) -> Self { - f.round() as u32 - } -} -impl Pixel for i8 { - fn from_f64(f: f64) -> Self { - f.round() as i8 - } -} -impl Pixel for i16 { - fn from_f64(f: f64) -> Self { - f.round() as i16 - } -} -impl Pixel for i32 { - fn from_f64(f: f64) -> Self { - f.round() as i32 - } -} -impl Pixel for f32 { - fn from_f64(f: f64) -> Self { - f as f32 - } -} -impl Pixel for f64 { - fn from_f64(f: f64) -> Self { - f - } -} - -/// Checks that the scale factor is a normal positive `f64`. -/// -/// All functions that take a scale factor assert that this will return `true`. If you're sourcing scale factors from -/// anywhere other than tao, it's recommended to validate them using this function before passing them to tao; -/// otherwise, you risk panics. -#[inline] -pub fn validate_scale_factor(scale_factor: f64) -> bool { - scale_factor.is_sign_positive() && scale_factor.is_normal() -} - -/// A position represented in logical pixels. -/// -/// The position is stored as floats, so please be careful. Casting floats to integers truncates the -/// fractional part, which can cause noticeable issues. To help with that, an `Into<(i32, i32)>` -/// implementation is provided which does the rounding for you. -#[derive(Debug, Copy, Clone, Eq, PartialEq, Default, Hash, Serialize, Deserialize)] -pub struct LogicalPosition

{ - pub x: P, - pub y: P, -} - -impl

LogicalPosition

{ - #[inline] - pub const fn new(x: P, y: P) -> Self { - LogicalPosition { x, y } - } -} - -impl LogicalPosition

{ - #[inline] - pub fn from_physical>, X: Pixel>( - physical: T, - scale_factor: f64, - ) -> Self { - physical.into().to_logical(scale_factor) - } - - #[inline] - pub fn to_physical(&self, scale_factor: f64) -> PhysicalPosition { - assert!(validate_scale_factor(scale_factor)); - let x = self.x.into() * scale_factor; - let y = self.y.into() * scale_factor; - PhysicalPosition::new(x, y).cast() - } - - #[inline] - pub fn cast(&self) -> LogicalPosition { - LogicalPosition { - x: self.x.cast(), - y: self.y.cast(), - } - } -} - -impl From<(X, X)> for LogicalPosition

{ - fn from((x, y): (X, X)) -> LogicalPosition

{ - LogicalPosition::new(x.cast(), y.cast()) - } -} - -impl From> for (X, X) { - fn from(pos: LogicalPosition

) -> Self { - (pos.x.cast(), pos.y.cast()) - } -} - -impl From<[X; 2]> for LogicalPosition

{ - fn from([x, y]: [X; 2]) -> LogicalPosition

{ - LogicalPosition::new(x.cast(), y.cast()) - } -} - -impl From> for [X; 2] { - fn from(pos: LogicalPosition

) -> Self { - [pos.x.cast(), pos.y.cast()] - } -} - -/// A position represented in physical pixels. -#[derive(Debug, Copy, Clone, Eq, PartialEq, Default, Hash, Serialize, Deserialize)] -pub struct PhysicalPosition

{ - pub x: P, - pub y: P, -} - -impl

PhysicalPosition

{ - #[inline] - pub const fn new(x: P, y: P) -> Self { - PhysicalPosition { x, y } - } -} - -impl PhysicalPosition

{ - #[inline] - pub fn from_logical>, X: Pixel>( - logical: T, - scale_factor: f64, - ) -> Self { - logical.into().to_physical(scale_factor) - } - - #[inline] - pub fn to_logical(&self, scale_factor: f64) -> LogicalPosition { - assert!(validate_scale_factor(scale_factor)); - let x = self.x.into() / scale_factor; - let y = self.y.into() / scale_factor; - LogicalPosition::new(x, y).cast() - } - - #[inline] - pub fn cast(&self) -> PhysicalPosition { - PhysicalPosition { - x: self.x.cast(), - y: self.y.cast(), - } - } -} - -impl From<(X, X)> for PhysicalPosition

{ - fn from((x, y): (X, X)) -> PhysicalPosition

{ - PhysicalPosition::new(x.cast(), y.cast()) - } -} - -impl From> for (X, X) { - fn from(pos: PhysicalPosition

) -> Self { - (pos.x.cast(), pos.y.cast()) - } -} - -impl From<[X; 2]> for PhysicalPosition

{ - fn from([x, y]: [X; 2]) -> PhysicalPosition

{ - PhysicalPosition::new(x.cast(), y.cast()) - } -} - -impl From> for [X; 2] { - fn from(pos: PhysicalPosition

) -> Self { - [pos.x.cast(), pos.y.cast()] - } -} - -/// A size represented in logical pixels. -#[derive(Debug, Copy, Clone, Eq, PartialEq, Default, Hash, Serialize, Deserialize)] -pub struct LogicalSize

{ - pub width: P, - pub height: P, -} - -impl

LogicalSize

{ - #[inline] - pub const fn new(width: P, height: P) -> Self { - LogicalSize { width, height } - } -} - -impl LogicalSize

{ - #[inline] - pub fn from_physical>, X: Pixel>(physical: T, scale_factor: f64) -> Self { - physical.into().to_logical(scale_factor) - } - - #[inline] - pub fn to_physical(&self, scale_factor: f64) -> PhysicalSize { - assert!(validate_scale_factor(scale_factor)); - let width = self.width.into() * scale_factor; - let height = self.height.into() * scale_factor; - PhysicalSize::new(width, height).cast() - } - - #[inline] - pub fn cast(&self) -> LogicalSize { - LogicalSize { - width: self.width.cast(), - height: self.height.cast(), - } - } -} - -impl From<(X, X)> for LogicalSize

{ - fn from((x, y): (X, X)) -> LogicalSize

{ - LogicalSize::new(x.cast(), y.cast()) - } -} - -impl From> for (X, X) { - fn from(size: LogicalSize

) -> Self { - (size.width.cast(), size.height.cast()) - } -} - -impl From<[X; 2]> for LogicalSize

{ - fn from([x, y]: [X; 2]) -> LogicalSize

{ - LogicalSize::new(x.cast(), y.cast()) - } -} - -impl From> for [X; 2] { - fn from(size: LogicalSize

) -> Self { - [size.width.cast(), size.height.cast()] - } -} - -/// A size represented in physical pixels. -#[derive(Debug, Copy, Clone, Eq, PartialEq, Default, Hash, Serialize, Deserialize)] -pub struct PhysicalSize

{ - pub width: P, - pub height: P, -} - -impl

PhysicalSize

{ - #[inline] - pub const fn new(width: P, height: P) -> Self { - PhysicalSize { width, height } - } -} - -impl PhysicalSize

{ - #[inline] - pub fn from_logical>, X: Pixel>(logical: T, scale_factor: f64) -> Self { - logical.into().to_physical(scale_factor) - } - - #[inline] - pub fn to_logical(&self, scale_factor: f64) -> LogicalSize { - assert!(validate_scale_factor(scale_factor)); - let width = self.width.into() / scale_factor; - let height = self.height.into() / scale_factor; - LogicalSize::new(width, height).cast() - } - - #[inline] - pub fn cast(&self) -> PhysicalSize { - PhysicalSize { - width: self.width.cast(), - height: self.height.cast(), - } - } -} - -impl From<(X, X)> for PhysicalSize

{ - fn from((x, y): (X, X)) -> PhysicalSize

{ - PhysicalSize::new(x.cast(), y.cast()) - } -} - -impl From> for (X, X) { - fn from(size: PhysicalSize

) -> Self { - (size.width.cast(), size.height.cast()) - } -} - -impl From<[X; 2]> for PhysicalSize

{ - fn from([x, y]: [X; 2]) -> PhysicalSize

{ - PhysicalSize::new(x.cast(), y.cast()) - } -} - -impl From> for [X; 2] { - fn from(size: PhysicalSize

) -> Self { - [size.width.cast(), size.height.cast()] - } -} - -/// A size that's either physical or logical. -#[derive(Debug, Copy, Clone, PartialEq, Serialize, Deserialize)] -#[serde(tag = "type", content = "data")] -pub enum Size { - Physical(PhysicalSize), - Logical(LogicalSize), -} - -impl Size { - pub fn new>(size: S) -> Size { - size.into() - } - - pub fn to_logical(&self, scale_factor: f64) -> LogicalSize

{ - match *self { - Size::Physical(size) => size.to_logical(scale_factor), - Size::Logical(size) => size.cast(), - } - } - - pub fn to_physical(&self, scale_factor: f64) -> PhysicalSize

{ - match *self { - Size::Physical(size) => size.cast(), - Size::Logical(size) => size.to_physical(scale_factor), - } - } -} - -impl From> for Size { - #[inline] - fn from(size: PhysicalSize

) -> Size { - Size::Physical(size.cast()) - } -} - -impl From> for Size { - #[inline] - fn from(size: LogicalSize

) -> Size { - Size::Logical(size.cast()) - } -} - -/// A position that's either physical or logical. -#[derive(Debug, Copy, Clone, PartialEq, Serialize, Deserialize)] -#[serde(tag = "type", content = "data")] -pub enum Position { - Physical(PhysicalPosition), - Logical(LogicalPosition), -} - -impl Position { - pub fn new>(position: S) -> Position { - position.into() - } - - pub fn to_logical(&self, scale_factor: f64) -> LogicalPosition

{ - match *self { - Position::Physical(position) => position.to_logical(scale_factor), - Position::Logical(position) => position.cast(), - } - } - - pub fn to_physical(&self, scale_factor: f64) -> PhysicalPosition

{ - match *self { - Position::Physical(position) => position.cast(), - Position::Logical(position) => position.to_physical(scale_factor), - } - } -} - -impl From> for Position { - #[inline] - fn from(position: PhysicalPosition

) -> Position { - Position::Physical(position.cast()) - } -} - -impl From> for Position { - #[inline] - fn from(position: LogicalPosition

) -> Position { - Position::Logical(position.cast()) - } -} diff --git a/core/tauri-utils/CHANGELOG.md b/core/tauri-utils/CHANGELOG.md index 54d498c96..3fbbd6bfd 100644 --- a/core/tauri-utils/CHANGELOG.md +++ b/core/tauri-utils/CHANGELOG.md @@ -1,5 +1,112 @@ # Changelog +## \[2.0.0-beta.11] + +### New Features + +- [`259d84529`](https://www.github.com/tauri-apps/tauri/commit/259d845290dde40639537258b2810567910f47f3)([#9209](https://www.github.com/tauri-apps/tauri/pull/9209)) Added `preInstallScript`, `postInstallScript`, `preRemoveScript` and `postRemoveScript` options for `bundler > deb` and `bundler > rpm` configs. + +### Enhancements + +- [`7c334cb18`](https://www.github.com/tauri-apps/tauri/commit/7c334cb1851ab034a3cfb472dd99dfc61ad3ca7f)([#9327](https://www.github.com/tauri-apps/tauri/pull/9327)) Make the isolation pattern encrypt key unextractable. +- [`a804a70a7`](https://www.github.com/tauri-apps/tauri/commit/a804a70a7aa1dc40fa9043206ad2265c6a5a437b)([#9328](https://www.github.com/tauri-apps/tauri/pull/9328)) The isolation iframe script now removes itself after execution. + +### Breaking Changes + +- [`06833f4fa`](https://www.github.com/tauri-apps/tauri/commit/06833f4fa8e63ecc55fe3fc874a9e397e77a5709)([#9100](https://www.github.com/tauri-apps/tauri/pull/9100)) Rename `FileDrop` to `DragDrop` on structs, enums and enum variants. Also renamed `file_drop` to `drag_drop` on fields and function names. + +## \[2.0.0-beta.10] + +### New Features + +- [`e227fe02f`](https://www.github.com/tauri-apps/tauri/commit/e227fe02f986e145c0731a64693e1c830a9eb5b0)([#9156](https://www.github.com/tauri-apps/tauri/pull/9156)) Added the `plugin` module. + +### Enhancements + +- [`7213b9e47`](https://www.github.com/tauri-apps/tauri/commit/7213b9e47242bef814aa7257e0bf84631bf5fe7e)([#9124](https://www.github.com/tauri-apps/tauri/pull/9124)) Fallback to an empty permission set if the plugin did not define its `default` permissions. + +## \[2.0.0-beta.9] + +### Breaking Changes + +- [`490a6b424`](https://www.github.com/tauri-apps/tauri/commit/490a6b424e81714524150aef96fbf6cf7004b940)([#9147](https://www.github.com/tauri-apps/tauri/pull/9147)) Removed the `assets::Assets` trait which is now part of the `tauri` crate. + +## \[2.0.0-beta.8] + +### Enhancements + +- [`3e472d0af`](https://www.github.com/tauri-apps/tauri/commit/3e472d0afcd67545dd6d9f18d304580a3b2759a8)([#9115](https://www.github.com/tauri-apps/tauri/pull/9115)) Changed the permission and capability platforms to be optional. + +### Breaking Changes + +- [`4ef17d083`](https://www.github.com/tauri-apps/tauri/commit/4ef17d08336a2e0df4a7ef9adea746d7419710b6)([#9116](https://www.github.com/tauri-apps/tauri/pull/9116)) The ACL configuration for remote URLs now uses the URLPattern standard instead of glob patterns. + +## \[2.0.0-beta.7] + +### Bug Fixes + +- [`86fa339de`](https://www.github.com/tauri-apps/tauri/commit/86fa339de7b176efafa9b3e89f94dcad5fcd03da)([#9071](https://www.github.com/tauri-apps/tauri/pull/9071)) Fix compile time error in context generation when using `app.windows.windowEffects.color` +- [`6c0683224`](https://www.github.com/tauri-apps/tauri/commit/6c068322460300e9d56a4fac5b018d4c437daa9e)([#9068](https://www.github.com/tauri-apps/tauri/pull/9068)) Fixes scope resolution grouping scopes for all windows. +- [`c68218b36`](https://www.github.com/tauri-apps/tauri/commit/c68218b362c417b62e56c7a2b5b32c13fe035a83)([#8990](https://www.github.com/tauri-apps/tauri/pull/8990)) Fix `BundleTarget::to_vec` returning an empty vec for `BundleTarget::All` variant. +- [`c68218b36`](https://www.github.com/tauri-apps/tauri/commit/c68218b362c417b62e56c7a2b5b32c13fe035a83)([#8990](https://www.github.com/tauri-apps/tauri/pull/8990)) Add `BundleType::all` method to return all possible `BundleType` variants. + +### Breaking Changes + +- [`9aa0d6e95`](https://www.github.com/tauri-apps/tauri/commit/9aa0d6e959269a9d99ff474e7f12bd397ea75fcd)([#9069](https://www.github.com/tauri-apps/tauri/pull/9069)) Removed `debug_eprintln!` and `consume_unused_variable` macros. +- [`bb23511ea`](https://www.github.com/tauri-apps/tauri/commit/bb23511ea80bcaffbdebf057301e463fff268c90)([#9079](https://www.github.com/tauri-apps/tauri/pull/9079)) Changed `CapabiltyFile::List` enum variant to be a tuple-struct and added `CapabiltyFile::NamedList`. This allows more flexibility when parsing capabilties from JSON files. + +## \[2.0.0-beta.6] + +### New Features + +- [`d7f56fef`](https://www.github.com/tauri-apps/tauri/commit/d7f56fef85cac3af4e2dbac1eac40e5567b1f160)([#9014](https://www.github.com/tauri-apps/tauri/pull/9014)) Allow defining a permission that only applies to a set of target platforms via the `platforms` configuration option. + +### Enhancements + +- [`04440edc`](https://www.github.com/tauri-apps/tauri/commit/04440edce870f9d06055616034941d79443d5a87)([#9019](https://www.github.com/tauri-apps/tauri/pull/9019)) Changed plugin markdown docs generation to table format. + +### Breaking Changes + +- [`3657ad82`](https://www.github.com/tauri-apps/tauri/commit/3657ad82f88ce528551d032d521c52eed3f396b4)([#9008](https://www.github.com/tauri-apps/tauri/pull/9008)) Allow defining permissions for the application commands via `tauri_build::Attributes::app_manifest`. + +## \[2.0.0-beta.5] + +### Enhancements + +- [`bc5b5e67`](https://www.github.com/tauri-apps/tauri/commit/bc5b5e671a546512f823f1c157421b4c3311dfc0)([#8984](https://www.github.com/tauri-apps/tauri/pull/8984)) Do not include a CSP tag in the application HTML and rely on the custom protocol response header instead. + +## \[2.0.0-beta.4] + +### Breaking Changes + +- [`a76fb118`](https://www.github.com/tauri-apps/tauri/commit/a76fb118ce2de22e1bdb4216bf0ac01dfc3e5799)([#8950](https://www.github.com/tauri-apps/tauri/pull/8950)) Changed the capability format to allow configuring both `remote: { urls: Vec }` and `local: bool (default: true)` instead of choosing one on the `context` field. + +## \[2.0.0-beta.3] + +### Breaking Changes + +- [`361ec37f`](https://www.github.com/tauri-apps/tauri/commit/361ec37fd4a5caa5b6630b9563ef079f53c6c336)([#8932](https://www.github.com/tauri-apps/tauri/pull/8932)) Moved `ProgressBarState` from `tauri-utils` to the `tauri::window` module and removed the `unity_uri` field. + +## \[2.0.0-beta.2] + +### Enhancements + +- [`0cb0a15c`](https://www.github.com/tauri-apps/tauri/commit/0cb0a15ce22af3d649cf219ac04188c14c5f4905)([#8789](https://www.github.com/tauri-apps/tauri/pull/8789)) Add `webviews` array on the capability for usage on multiwebview contexts. +- [`83a68deb`](https://www.github.com/tauri-apps/tauri/commit/83a68deb5676d39cd4728d2e140f6b46d5f787ed)([#8797](https://www.github.com/tauri-apps/tauri/pull/8797)) Added a new configuration option `tauri.conf.json > app > security > capabilities` to reference existing capabilities and inline new ones. If it is empty, all capabilities are still included preserving the current behavior. +- [`8d16a80d`](https://www.github.com/tauri-apps/tauri/commit/8d16a80d2fb2468667e7987d0cc99dbc7e3b9d0a)([#8802](https://www.github.com/tauri-apps/tauri/pull/8802)) The `Context` struct now includes the runtime authority instead of the resolved ACL. This does not impact most applications. +- [`28fb036c`](https://www.github.com/tauri-apps/tauri/commit/28fb036ce476c6f22815c35385f923135212c6f3)([#8852](https://www.github.com/tauri-apps/tauri/pull/8852)) Enhance resource directory resolution on development. +- [`dd7571a7`](https://www.github.com/tauri-apps/tauri/commit/dd7571a7808676c8063a4983b9c6687dfaf03a09)([#8815](https://www.github.com/tauri-apps/tauri/pull/8815)) Do not generate JSON schema and markdown reference file if the plugin does not define any permissions and delete those files if they exist. +- [`5618f6d2`](https://www.github.com/tauri-apps/tauri/commit/5618f6d2ffc9ebf40710145538b06bebfa55f878)([#8856](https://www.github.com/tauri-apps/tauri/pull/8856)) Relax requirements on plugin's identifiers to be alphanumeric and `-` instead of only lower alpha and `-`. +- [`8d16a80d`](https://www.github.com/tauri-apps/tauri/commit/8d16a80d2fb2468667e7987d0cc99dbc7e3b9d0a)([#8802](https://www.github.com/tauri-apps/tauri/pull/8802)) Refactored the capability types and resolution algorithm. + +### Bug Fixes + +- [`ae0fe47c`](https://www.github.com/tauri-apps/tauri/commit/ae0fe47c4c35fa87c77acf42af32ef3f0615cb08)([#8774](https://www.github.com/tauri-apps/tauri/pull/8774)) Fix compile error when `tauri.conf.json` had `bundle > license` set. + +### Breaking Changes + +- [`f284f9c5`](https://www.github.com/tauri-apps/tauri/commit/f284f9c545deeb77d15b6e8b1d0d05f49c40634c)([#8898](https://www.github.com/tauri-apps/tauri/pull/8898)) Changed the capability `remote` configuration to take a list of `urls` instead of `domains` for more flexibility. + ## \[2.0.0-beta.1] ### Enhancements @@ -164,6 +271,18 @@ ## \[1.5.3] +### New features + +- [`7aa30dec`](https://www.github.com/tauri-apps/tauri/commit/7aa30dec85a17c3d3faaf3841b93e10991b991b0)([#8620](https://www.github.com/tauri-apps/tauri/pull/8620)) Add `priority`, `section` and `changelog` options in Debian config. + +## \[1.5.2] + +### Bug Fixes + +- [`9b230de7`](https://www.github.com/tauri-apps/tauri/commit/9b230de7bc6690c2733f5324d50b999af1f7a6ef)([#8407](https://www.github.com/tauri-apps/tauri/pull/8407)) Fix compile error when parsing config that includes float values. + +## \[1.5.3] + ### New Features - [`b3e53e72`](https://www.github.com/tauri-apps/tauri/commit/b3e53e7243311a2659b7569dddc20c56ac9f9d8e)([#8288](https://www.github.com/tauri-apps/tauri/pull/8288)) Added `Assets::iter` to iterate on all embedded assets. diff --git a/core/tauri-utils/Cargo.toml b/core/tauri-utils/Cargo.toml index e9ddf58fa..5f48e1c77 100644 --- a/core/tauri-utils/Cargo.toml +++ b/core/tauri-utils/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "tauri-utils" -version = "2.0.0-beta.1" +version = "2.0.0-beta.11" description = "Utilities for Tauri" exclude = [ "CHANGELOG.md", "/target" ] readme = "README.md" @@ -33,12 +33,14 @@ json5 = { version = "0.4", optional = true } toml = { version = "0.8", features = [ "parse" ] } json-patch = "1.2" glob = "0.3" +urlpattern = "0.2" +regex = "1" walkdir = { version = "2", optional = true } memchr = "2" semver = "1" infer = "0.15" dunce = "1" -log = "0.4.20" +log = "0.4.21" cargo_metadata = { version = "0.18", optional = true } [target."cfg(target_os = \"linux\")".dependencies] diff --git a/core/tauri-utils/src/acl/build.rs b/core/tauri-utils/src/acl/build.rs index 37297c5d9..bc4a60cf9 100644 --- a/core/tauri-utils/src/acl/build.rs +++ b/core/tauri-utils/src/acl/build.rs @@ -1,4 +1,4 @@ -// Copyright 2019-2023 Tauri Programme within The Commons Conservancy +// Copyright 2019-2024 Tauri Programme within The Commons Conservancy // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT @@ -16,9 +16,12 @@ use schemars::{ schema::{InstanceType, Metadata, RootSchema, Schema, SchemaObject, SubschemaValidation}, schema_for, }; -use serde::Deserialize; -use super::{capability::Capability, plugin::PermissionFile}; +use super::{ + capability::{Capability, CapabilityFile}, + manifest::PermissionFile, + PERMISSION_SCHEMA_FILE_NAME, +}; /// Known name of the folder containing autogenerated permissions. pub const AUTOGENERATED_FOLDER_NAME: &str = "autogenerated"; @@ -35,9 +38,6 @@ pub const PERMISSION_FILE_EXTENSIONS: &[&str] = &["json", "toml"]; /// Known foldername of the permission schema files pub const PERMISSION_SCHEMAS_FOLDER_NAME: &str = "schemas"; -/// Known filename of the permission schema JSON file -pub const PERMISSION_SCHEMA_FILE_NAME: &str = "schema.json"; - /// Known filename of the permission documentation file pub const PERMISSION_DOCS_FILE_NAME: &str = "reference.md"; @@ -49,24 +49,12 @@ const CAPABILITIES_SCHEMA_FOLDER_NAME: &str = "schemas"; const CORE_PLUGIN_PERMISSIONS_TOKEN: &str = "__CORE_PLUGIN__"; -/// Capability formats accepted in a capability file. -#[derive(Deserialize, schemars::JsonSchema)] -#[serde(untagged)] -pub enum CapabilityFile { - /// A single capability. - Capability(Capability), - /// A list of capabilities. - List { - /// The list of capabilities. - capabilities: Vec, - }, -} - /// Write the permissions to a temporary directory and pass it to the immediate consuming crate. -pub fn define_permissions( +pub fn define_permissions bool>( pattern: &str, pkg_name: &str, out_dir: &Path, + filter_fn: F, ) -> Result, Error> { let permission_files = glob::glob(pattern)? .flatten() @@ -78,6 +66,7 @@ pub fn define_permissions( .map(|e| PERMISSION_FILE_EXTENSIONS.contains(&e)) .unwrap_or_default() }) + .filter(|p| filter_fn(p)) // filter schemas .filter(|p| p.parent().unwrap().file_name().unwrap() != PERMISSION_SCHEMAS_FOLDER_NAME) .collect::>(); @@ -143,19 +132,11 @@ pub fn parse_capabilities( // TODO: remove this before stable .filter(|p| p.parent().unwrap().file_name().unwrap() != CAPABILITIES_SCHEMA_FOLDER_NAME) { - let capability_file = std::fs::read_to_string(&path).map_err(Error::ReadFile)?; - let ext = path.extension().unwrap().to_string_lossy().to_string(); - let capability: CapabilityFile = match ext.as_str() { - "toml" => toml::from_str(&capability_file)?, - "json" => serde_json::from_str(&capability_file)?, - _ => return Err(Error::UnknownCapabilityFormat(ext)), - }; - - match capability { + match CapabilityFile::load(&path)? { CapabilityFile::Capability(capability) => { capabilities_map.insert(capability.identifier.clone(), capability); } - CapabilityFile::List { capabilities } => { + CapabilityFile::List(capabilities) | CapabilityFile::NamedList { capabilities } => { for capability in capabilities { capabilities_map.insert(capability.identifier.clone(), capability); } @@ -254,12 +235,12 @@ pub fn generate_schema>( /// Generate a markdown documentation page containing the list of permissions of the plugin. pub fn generate_docs(permissions: &[PermissionFile], out_dir: &Path) -> Result<(), Error> { - let mut docs = "# Permissions\n\n".to_string(); + let mut docs = "| Permission | Description |\n|------|-----|\n".to_string(); fn docs_from(id: &str, description: Option<&str>) -> String { - let mut docs = format!("## {id}"); + let mut docs = format!("|`{id}`"); if let Some(d) = description { - docs.push_str(&format!("\n\n{d}")); + docs.push_str(&format!("|{d}|")); } docs } @@ -267,12 +248,12 @@ pub fn generate_docs(permissions: &[PermissionFile], out_dir: &Path) -> Result<( for permission in permissions { for set in &permission.set { docs.push_str(&docs_from(&set.identifier, Some(&set.description))); - docs.push_str("\n\n"); + docs.push('\n'); } if let Some(default) = &permission.default { docs.push_str(&docs_from("default", default.description.as_deref())); - docs.push_str("\n\n"); + docs.push('\n'); } for permission in &permission.permission { @@ -280,7 +261,7 @@ pub fn generate_docs(permissions: &[PermissionFile], out_dir: &Path) -> Result<( &permission.identifier, permission.description.as_deref(), )); - docs.push_str("\n\n"); + docs.push('\n'); } } @@ -377,26 +358,40 @@ fn parse_permissions(paths: Vec) -> Result, Error> } /// Autogenerate permission files for a list of commands. -pub fn autogenerate_command_permissions(path: &Path, commands: &[&str], license_header: &str) { +pub fn autogenerate_command_permissions( + path: &Path, + commands: &[&str], + license_header: &str, + schema_ref: bool, +) { if !path.exists() { create_dir_all(path).expect("unable to create autogenerated commands dir"); } - let cwd = current_dir().unwrap(); - let components_len = path.strip_prefix(&cwd).unwrap_or(path).components().count(); - let schema_path = (1..components_len) - .map(|_| "..") - .collect::() - .join(PERMISSION_SCHEMAS_FOLDER_NAME) - .join(PERMISSION_SCHEMA_FILE_NAME); + let schema_entry = if schema_ref { + let cwd = current_dir().unwrap(); + let components_len = path.strip_prefix(&cwd).unwrap_or(path).components().count(); + let schema_path = (1..components_len) + .map(|_| "..") + .collect::() + .join(PERMISSION_SCHEMAS_FOLDER_NAME) + .join(PERMISSION_SCHEMA_FILE_NAME); + format!( + "\n\"$schema\" = \"{}\"\n", + dunce::simplified(&schema_path) + .display() + .to_string() + .replace('\\', "/") + ) + } else { + "".to_string() + }; for command in commands { let slugified_command = command.replace('_', "-"); let toml = format!( r###"{license_header}# Automatically generated - DO NOT EDIT! - -"$schema" = "{schema_path}" - +{schema_entry} [[permission]] identifier = "allow-{slugified_command}" description = "Enables the {command} command without any pre-configured scope." @@ -409,10 +404,6 @@ commands.deny = ["{command}"] "###, command = command, slugified_command = slugified_command, - schema_path = dunce::simplified(&schema_path) - .display() - .to_string() - .replace('\\', "/") ); let out_path = path.join(format!("{command}.toml")); diff --git a/core/tauri-utils/src/acl/capability.rs b/core/tauri-utils/src/acl/capability.rs index 4f35ae782..25ed11163 100644 --- a/core/tauri-utils/src/acl/capability.rs +++ b/core/tauri-utils/src/acl/capability.rs @@ -1,9 +1,11 @@ -// Copyright 2019-2023 Tauri Programme within The Commons Conservancy +// Copyright 2019-2024 Tauri Programme within The Commons Conservancy // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT //! End-user abstraction for selecting permissions a window has access to. +use std::{path::Path, str::FromStr}; + use crate::{acl::Identifier, platform::Target}; use serde::{Deserialize, Serialize}; @@ -11,7 +13,7 @@ use super::Scopes; /// An entry for a permission value in a [`Capability`] can be either a raw permission [`Identifier`] /// or an object that references a permission and extends its scope. -#[derive(Debug, Clone, Serialize, Deserialize)] +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] #[serde(untagged)] #[cfg_attr(feature = "schema", derive(schemars::JsonSchema))] pub enum PermissionEntry { @@ -46,7 +48,7 @@ impl PermissionEntry { /// /// This can be done to create trust groups and reduce impact of vulnerabilities in certain plugins or windows. /// Windows can be added to a capability by exact name or glob patterns like *, admin-* or main-window. -#[derive(Debug, Clone, Serialize, Deserialize)] +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] #[cfg_attr(feature = "schema", derive(schemars::JsonSchema))] pub struct Capability { /// Identifier of the capability. @@ -54,41 +56,149 @@ pub struct Capability { /// Description of the capability. #[serde(default)] pub description: String, - /// Execution context of the capability. - /// - /// At runtime, Tauri filters the IPC command together with the context to determine whether it is allowed or not and its scope. - #[serde(default)] - pub context: CapabilityContext, + /// Configure remote URLs that can use the capability permissions. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub remote: Option, + /// Whether this capability is enabled for local app URLs or not. Defaults to `true`. + #[serde(default = "default_capability_local")] + pub local: bool, /// List of windows that uses this capability. Can be a glob pattern. + /// + /// On multiwebview windows, prefer [`Self::webviews`] for a fine grained access control. + #[serde(default, skip_serializing_if = "Vec::is_empty")] pub windows: Vec, + /// List of webviews that uses this capability. Can be a glob pattern. + /// + /// This is only required when using on multiwebview contexts, by default + /// all child webviews of a window that matches [`Self::windows`] are linked. + #[serde(default, skip_serializing_if = "Vec::is_empty")] + pub webviews: Vec, /// List of permissions attached to this capability. Must include the plugin name as prefix in the form of `${plugin-name}:${permission-name}`. pub permissions: Vec, - /// Target platforms this capability applies. By default all platforms applies. - #[serde(default = "default_platforms")] - pub platforms: Vec, + /// Target platforms this capability applies. By default all platforms are affected by this capability. + #[serde(skip_serializing_if = "Option::is_none")] + pub platforms: Option>, } -fn default_platforms() -> Vec { - vec![ - Target::Linux, - Target::MacOS, - Target::Windows, - Target::Android, - Target::Ios, - ] +fn default_capability_local() -> bool { + true } -/// Context of the capability. +/// Configuration for remote URLs that are associated with the capability. #[derive(Debug, Default, Clone, Serialize, Deserialize, Eq, PartialEq, PartialOrd, Ord, Hash)] #[cfg_attr(feature = "schema", derive(schemars::JsonSchema))] #[serde(rename_all = "camelCase")] -pub enum CapabilityContext { - /// Capability refers to local URL usage. - #[default] - Local, - /// Capability refers to remote usage. - Remote { - /// Remote domains this capability refers to. Can use glob patterns. - domains: Vec, +pub struct CapabilityRemote { + /// Remote domains this capability refers to using the [URLPattern standard](https://urlpattern.spec.whatwg.org/). + /// + /// # Examples + /// + /// - "https://*.mydomain.dev": allows subdomains of mydomain.dev + /// - "https://mydomain.dev/api/*": allows any subpath of mydomain.dev/api + pub urls: Vec, +} + +/// Capability formats accepted in a capability file. +#[derive(Deserialize)] +#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))] +#[serde(untagged)] +pub enum CapabilityFile { + /// A single capability. + Capability(Capability), + /// A list of capabilities. + List(Vec), + /// A list of capabilities. + NamedList { + /// The list of capabilities. + capabilities: Vec, }, } + +impl CapabilityFile { + /// Load the given capability file. + pub fn load>(path: P) -> Result { + let path = path.as_ref(); + let capability_file = std::fs::read_to_string(path).map_err(super::Error::ReadFile)?; + let ext = path.extension().unwrap().to_string_lossy().to_string(); + let file: Self = match ext.as_str() { + "toml" => toml::from_str(&capability_file)?, + "json" => serde_json::from_str(&capability_file)?, + _ => return Err(super::Error::UnknownCapabilityFormat(ext)), + }; + Ok(file) + } +} + +impl FromStr for CapabilityFile { + type Err = super::Error; + + fn from_str(s: &str) -> Result { + serde_json::from_str(s) + .or_else(|_| toml::from_str(s)) + .map_err(Into::into) + } +} + +#[cfg(feature = "build")] +mod build { + use std::convert::identity; + + use proc_macro2::TokenStream; + use quote::{quote, ToTokens, TokenStreamExt}; + + use super::*; + use crate::{literal_struct, tokens::*}; + + impl ToTokens for CapabilityRemote { + fn to_tokens(&self, tokens: &mut TokenStream) { + let urls = vec_lit(&self.urls, str_lit); + literal_struct!( + tokens, + ::tauri::utils::acl::capability::CapabilityRemote, + urls + ); + } + } + + impl ToTokens for PermissionEntry { + fn to_tokens(&self, tokens: &mut TokenStream) { + let prefix = quote! { ::tauri::utils::acl::capability::PermissionEntry }; + + tokens.append_all(match self { + Self::PermissionRef(id) => { + quote! { #prefix::PermissionRef(#id) } + } + Self::ExtendedPermission { identifier, scope } => { + quote! { #prefix::ExtendedPermission { + identifier: #identifier, + scope: #scope + } } + } + }); + } + } + + impl ToTokens for Capability { + fn to_tokens(&self, tokens: &mut TokenStream) { + let identifier = str_lit(&self.identifier); + let description = str_lit(&self.description); + let remote = &self.remote; + let local = self.local; + let windows = vec_lit(&self.windows, str_lit); + let permissions = vec_lit(&self.permissions, identity); + let platforms = opt_vec_lit(self.platforms.as_ref(), identity); + + literal_struct!( + tokens, + ::tauri::utils::acl::capability::Capability, + identifier, + description, + remote, + local, + windows, + permissions, + platforms + ); + } + } +} diff --git a/core/tauri-utils/src/acl/identifier.rs b/core/tauri-utils/src/acl/identifier.rs index 75c1d937a..92d210aa8 100644 --- a/core/tauri-utils/src/acl/identifier.rs +++ b/core/tauri-utils/src/acl/identifier.rs @@ -1,4 +1,4 @@ -// Copyright 2019-2023 Tauri Programme within The Commons Conservancy +// Copyright 2019-2024 Tauri Programme within The Commons Conservancy // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT @@ -17,13 +17,28 @@ const MAX_LEN_BASE: usize = 64; const MAX_LEN_IDENTIFIER: usize = MAX_LEN_PREFIX + 1 + MAX_LEN_BASE; /// Plugin identifier. -#[derive(Debug, Clone)] -#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))] +#[derive(Debug, Clone, PartialEq, Eq)] pub struct Identifier { inner: String, separator: Option, } +#[cfg(feature = "schema")] +impl schemars::JsonSchema for Identifier { + fn schema_name() -> String { + "Identifier".to_string() + } + + fn schema_id() -> std::borrow::Cow<'static, str> { + // Include the module, in case a type with the same name is in another module/crate + std::borrow::Cow::Borrowed(concat!(module_path!(), "::Identifier")) + } + + fn json_schema(gen: &mut schemars::gen::SchemaGenerator) -> schemars::schema::Schema { + String::json_schema(gen) + } +} + impl AsRef for Identifier { #[inline(always)] fn as_ref(&self) -> &str { @@ -73,12 +88,12 @@ enum ValidByte { } impl ValidByte { - fn lower_alpha(byte: u8) -> Option { - byte.is_ascii_lowercase().then_some(Self::Byte(byte)) + fn alpha_numeric(byte: u8) -> Option { + byte.is_ascii_alphanumeric().then_some(Self::Byte(byte)) } - fn lower_alpha_hyphen(byte: u8) -> Option { - matches!(byte, b'a'..=b'z' | b'-').then_some(Self::Byte(byte)) + fn alpha_numeric_hyphen(byte: u8) -> Option { + (byte.is_ascii_alphanumeric() || byte == b'-').then_some(Self::Byte(byte)) } fn next(&self, next: u8) -> Option { @@ -87,9 +102,9 @@ impl ValidByte { (ValidByte::Separator, b'-') => None, (_, IDENTIFIER_SEPARATOR) => Some(ValidByte::Separator), - (ValidByte::Separator, next) => ValidByte::lower_alpha(next), - (ValidByte::Byte(b'-'), next) => ValidByte::lower_alpha(next), - (ValidByte::Byte(_), next) => ValidByte::lower_alpha_hyphen(next), + (ValidByte::Separator, next) => ValidByte::alpha_numeric(next), + (ValidByte::Byte(b'-'), next) => ValidByte::alpha_numeric(next), + (ValidByte::Byte(_), next) => ValidByte::alpha_numeric_hyphen(next), } } } @@ -107,7 +122,7 @@ pub enum ParseIdentifierError { /// Identifier is too long. #[error("identifiers cannot be longer than {}, found {0}", MAX_LEN_IDENTIFIER)] - Humungous(usize), + Humongous(usize), /// Identifier is not in a valid format. #[error("identifiers can only include lowercase ASCII, hyphens which are not leading or trailing, and a single colon if using a prefix")] @@ -143,26 +158,26 @@ impl TryFrom for Identifier { let mut bytes = value.bytes(); if bytes.len() > MAX_LEN_IDENTIFIER { - return Err(Self::Error::Humungous(bytes.len())); + return Err(Self::Error::Humongous(bytes.len())); } // grab the first byte only before parsing the rest let mut prev = bytes .next() - .and_then(ValidByte::lower_alpha) + .and_then(ValidByte::alpha_numeric) .ok_or(Self::Error::InvalidFormat)?; let mut idx = 0; - let mut seperator = None; + let mut separator = None; for byte in bytes { idx += 1; // we already consumed first item match prev.next(byte) { None => return Err(Self::Error::InvalidFormat), Some(next @ ValidByte::Byte(_)) => prev = next, Some(ValidByte::Separator) => { - if seperator.is_none() { + if separator.is_none() { // safe to unwrap because idx starts at 1 and cannot go over MAX_IDENTIFIER_LEN - seperator = Some(idx.try_into().unwrap()); + separator = Some(idx.try_into().unwrap()); prev = ValidByte::Separator } else { return Err(Self::Error::MultipleSeparators); @@ -183,7 +198,7 @@ impl TryFrom for Identifier { Ok(Self { inner: value, - separator: seperator, + separator, }) } } @@ -222,6 +237,8 @@ mod tests { #[test] fn format() { assert!(ident("prefix:base").is_ok()); + assert!(ident("prefix3:base").is_ok()); + assert!(ident("preFix:base").is_ok()); // bad assert!(ident("tauri-plugin-prefix:base").is_err()); @@ -260,3 +277,19 @@ mod tests { assert_eq!(ident("base").unwrap().get_prefix(), None); } } + +#[cfg(feature = "build")] +mod build { + use proc_macro2::TokenStream; + use quote::{quote, ToTokens, TokenStreamExt}; + + use super::*; + + impl ToTokens for Identifier { + fn to_tokens(&self, tokens: &mut TokenStream) { + let s = self.get(); + tokens + .append_all(quote! { ::tauri::utils::acl::Identifier::try_from(#s.to_string()).unwrap() }) + } + } +} diff --git a/core/tauri-utils/src/acl/plugin.rs b/core/tauri-utils/src/acl/manifest.rs similarity index 94% rename from core/tauri-utils/src/acl/plugin.rs rename to core/tauri-utils/src/acl/manifest.rs index 9f427651b..117967531 100644 --- a/core/tauri-utils/src/acl/plugin.rs +++ b/core/tauri-utils/src/acl/manifest.rs @@ -1,4 +1,4 @@ -// Copyright 2019-2023 Tauri Programme within The Commons Conservancy +// Copyright 2019-2024 Tauri Programme within The Commons Conservancy // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT @@ -12,7 +12,7 @@ use serde::{Deserialize, Serialize}; /// The default permission set of the plugin. /// /// Works similarly to a permission with the "default" identifier. -#[derive(Debug, Deserialize)] +#[derive(Debug, Deserialize, Serialize)] #[cfg_attr(feature = "schema", derive(schemars::JsonSchema))] pub struct DefaultPermission { /// The version of the permission. @@ -26,14 +26,14 @@ pub struct DefaultPermission { } /// Permission file that can define a default permission, a set of permissions or a list of inlined permissions. -#[derive(Debug, Deserialize)] +#[derive(Debug, Deserialize, Serialize)] #[cfg_attr(feature = "schema", derive(schemars::JsonSchema))] pub struct PermissionFile { /// The default permission set for the plugin pub default: Option, /// A list of permissions sets defined - #[serde(default)] + #[serde(default, skip_serializing_if = "Vec::is_empty")] pub set: Vec, /// A list of inlined permissions @@ -158,7 +158,7 @@ mod build { literal_struct!( tokens, - ::tauri::utils::acl::plugin::Manifest, + ::tauri::utils::acl::manifest::Manifest, default_permission, permissions, permission_sets, diff --git a/core/tauri-utils/src/acl/mod.rs b/core/tauri-utils/src/acl/mod.rs index aecfceea7..82405649f 100644 --- a/core/tauri-utils/src/acl/mod.rs +++ b/core/tauri-utils/src/acl/mod.rs @@ -1,21 +1,28 @@ -// Copyright 2019-2023 Tauri Programme within The Commons Conservancy +// Copyright 2019-2024 Tauri Programme within The Commons Conservancy // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT //! Access Control List types. -use glob::Pattern; use serde::{Deserialize, Serialize}; -use std::num::NonZeroU64; +use std::{num::NonZeroU64, str::FromStr, sync::Arc}; use thiserror::Error; +use url::Url; + +use crate::platform::Target; pub use self::{identifier::*, value::*}; +/// Known filename of the permission schema JSON file +pub const PERMISSION_SCHEMA_FILE_NAME: &str = "schema.json"; +/// Known ACL key for the app permissions. +pub const APP_ACL_KEY: &str = "__app-acl__"; + #[cfg(feature = "build")] pub mod build; pub mod capability; pub mod identifier; -pub mod plugin; +pub mod manifest; pub mod resolved; pub mod value; @@ -84,27 +91,20 @@ pub enum Error { set: String, }, - /// Plugin has no default permission. - #[error("plugin {plugin} has no default permission")] - MissingDefaultPermission { - /// Plugin name. - plugin: String, - }, - - /// Unknown plugin. - #[error("unknown plugin {plugin}, expected one of {available}")] - UnknownPlugin { - /// Plugin name. - plugin: String, - /// Available plugins. + /// Unknown ACL manifest. + #[error("unknown ACL for {key}, expected one of {available}")] + UnknownManifest { + /// Manifest key. + key: String, + /// Available manifest keys. available: String, }, /// Unknown permission. - #[error("unknown permission {permission} for plugin {plugin}")] + #[error("unknown permission {permission} for {key}")] UnknownPermission { - /// Plugin name. - plugin: String, + /// Manifest key. + key: String, /// Permission identifier. permission: String, @@ -131,7 +131,7 @@ pub struct Commands { /// It can be of any serde serializable type and is used for allowing or preventing certain actions inside a Tauri command. /// /// The scope is passed to the command and handled/enforced by the command itself. -#[derive(Debug, Default, Clone, Serialize, Deserialize)] +#[derive(Debug, Default, PartialEq, Clone, Serialize, Deserialize)] #[cfg_attr(feature = "schema", derive(schemars::JsonSchema))] pub struct Scopes { /// Data that defines what is allowed by the scope. @@ -142,6 +142,12 @@ pub struct Scopes { pub deny: Option>, } +impl Scopes { + fn is_empty(&self) -> bool { + self.allow.is_none() && self.deny.is_none() + } +} + /// Descriptions of explicit privileges of commands. /// /// It can enable commands to be accessible in the frontend of the application. @@ -151,12 +157,14 @@ pub struct Scopes { #[cfg_attr(feature = "schema", derive(schemars::JsonSchema))] pub struct Permission { /// The version of the permission. + #[serde(skip_serializing_if = "Option::is_none")] pub version: Option, /// A unique identifier for the permission. pub identifier: String, /// Human-readable description of what the permission does. + #[serde(skip_serializing_if = "Option::is_none")] pub description: Option, /// Allowed or denied commands when using this permission. @@ -164,8 +172,12 @@ pub struct Permission { pub commands: Commands, /// Allowed or denied scoped when using this permission. - #[serde(default)] + #[serde(default, skip_serializing_if = "Scopes::is_empty")] pub scope: Scopes, + + /// Target platforms this permission applies. By default all platforms are affected by this permission. + #[serde(skip_serializing_if = "Option::is_none")] + pub platforms: Option>, } /// A set of direct permissions grouped together under a new name. @@ -182,18 +194,117 @@ pub struct PermissionSet { pub permissions: Vec, } +/// UrlPattern for [`ExecutionContext::Remote`]. +#[derive(Debug, Clone)] +pub struct RemoteUrlPattern(Arc, String); + +impl FromStr for RemoteUrlPattern { + type Err = urlpattern::quirks::Error; + + fn from_str(s: &str) -> std::result::Result { + let mut init = urlpattern::UrlPatternInit::parse_constructor_string::(s, None)?; + if init.search.as_ref().map(|p| p.is_empty()).unwrap_or(true) { + init.search.replace("*".to_string()); + } + if init.hash.as_ref().map(|p| p.is_empty()).unwrap_or(true) { + init.hash.replace("*".to_string()); + } + if init + .pathname + .as_ref() + .map(|p| p.is_empty() || p == "/") + .unwrap_or(true) + { + init.pathname.replace("*".to_string()); + } + let pattern = urlpattern::UrlPattern::parse(init)?; + Ok(Self(Arc::new(pattern), s.to_string())) + } +} + +impl RemoteUrlPattern { + #[doc(hidden)] + pub fn as_str(&self) -> &str { + &self.1 + } + + /// Test if a given URL matches the pattern. + pub fn test(&self, url: &Url) -> bool { + self + .0 + .test(urlpattern::UrlPatternMatchInput::Url(url.clone())) + .unwrap_or_default() + } +} + +impl PartialEq for RemoteUrlPattern { + fn eq(&self, other: &Self) -> bool { + self.0.protocol() == other.0.protocol() + && self.0.username() == other.0.username() + && self.0.password() == other.0.password() + && self.0.hostname() == other.0.hostname() + && self.0.port() == other.0.port() + && self.0.pathname() == other.0.pathname() + && self.0.search() == other.0.search() + && self.0.hash() == other.0.hash() + } +} + +impl Eq for RemoteUrlPattern {} + /// Execution context of an IPC call. -#[derive(Debug, Clone, Eq, PartialEq, PartialOrd, Ord, Hash)] +#[derive(Debug, Default, Clone, Eq, PartialEq)] pub enum ExecutionContext { /// A local URL is used (the Tauri app URL). + #[default] Local, - /// Remote URL is tring to use the IPC. + /// Remote URL is trying to use the IPC. Remote { - /// The domain trying to access the IPC (glob pattern). - domain: Pattern, + /// The URL trying to access the IPC (URL pattern). + url: RemoteUrlPattern, }, } +#[cfg(test)] +mod tests { + use crate::acl::RemoteUrlPattern; + + #[test] + fn url_pattern_domain_wildcard() { + let pattern: RemoteUrlPattern = "http://*".parse().unwrap(); + + assert!(pattern.test(&"http://tauri.app/path".parse().unwrap())); + assert!(pattern.test(&"http://tauri.app/path?q=1".parse().unwrap())); + + assert!(pattern.test(&"http://localhost/path".parse().unwrap())); + assert!(pattern.test(&"http://localhost/path?q=1".parse().unwrap())); + + let pattern: RemoteUrlPattern = "http://*.tauri.app".parse().unwrap(); + + assert!(!pattern.test(&"http://tauri.app/path".parse().unwrap())); + assert!(!pattern.test(&"http://tauri.app/path?q=1".parse().unwrap())); + assert!(pattern.test(&"http://api.tauri.app/path".parse().unwrap())); + assert!(pattern.test(&"http://api.tauri.app/path?q=1".parse().unwrap())); + assert!(!pattern.test(&"http://localhost/path".parse().unwrap())); + assert!(!pattern.test(&"http://localhost/path?q=1".parse().unwrap())); + } + + #[test] + fn url_pattern_path_wildcard() { + let pattern: RemoteUrlPattern = "http://localhost/*".parse().unwrap(); + assert!(pattern.test(&"http://localhost/path".parse().unwrap())); + assert!(pattern.test(&"http://localhost/path?q=1".parse().unwrap())); + } + + #[test] + fn url_pattern_scheme_wildcard() { + let pattern: RemoteUrlPattern = "*://localhost".parse().unwrap(); + assert!(pattern.test(&"http://localhost/path".parse().unwrap())); + assert!(pattern.test(&"https://localhost/path?q=1".parse().unwrap())); + assert!(pattern.test(&"custom://localhost/path".parse().unwrap())); + } +} + #[cfg(feature = "build")] mod build_ { use std::convert::identity; @@ -212,9 +323,9 @@ mod build_ { Self::Local => { quote! { #prefix::Local } } - Self::Remote { domain } => { - let domain = domain.as_str(); - quote! { #prefix::Remote { domain: #domain.parse().unwrap() } } + Self::Remote { url } => { + let url = url.as_str(); + quote! { #prefix::Remote { url: #url.parse().unwrap() } } } }); } @@ -246,6 +357,8 @@ mod build_ { let description = opt_str_lit(self.description.as_ref()); let commands = &self.commands; let scope = &self.scope; + let platforms = opt_vec_lit(self.platforms.as_ref(), identity); + literal_struct!( tokens, ::tauri::utils::acl::Permission, @@ -253,7 +366,8 @@ mod build_ { identifier, description, commands, - scope + scope, + platforms ) } } diff --git a/core/tauri-utils/src/acl/resolved.rs b/core/tauri-utils/src/acl/resolved.rs index 9a138fce8..57c71e991 100644 --- a/core/tauri-utils/src/acl/resolved.rs +++ b/core/tauri-utils/src/acl/resolved.rs @@ -1,23 +1,17 @@ -// Copyright 2019-2023 Tauri Programme within The Commons Conservancy +// Copyright 2019-2024 Tauri Programme within The Commons Conservancy // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT //! Resolved ACL for runtime usage. -use std::{ - collections::{hash_map::DefaultHasher, BTreeMap, HashSet}, - fmt, - hash::{Hash, Hasher}, -}; - -use glob::Pattern; +use std::{collections::BTreeMap, fmt}; use crate::platform::Target; use super::{ - capability::{Capability, CapabilityContext, PermissionEntry}, - plugin::Manifest, - Commands, Error, ExecutionContext, Permission, PermissionSet, Scopes, Value, + capability::{Capability, PermissionEntry}, + manifest::Manifest, + Commands, Error, ExecutionContext, Permission, PermissionSet, Scopes, Value, APP_ACL_KEY, }; /// A key for a scope, used to link a [`ResolvedCommand#structfield.scope`] to the store [`Resolved#structfield.scopes`]. @@ -25,7 +19,7 @@ pub type ScopeKey = u64; /// Metadata for what referenced a [`ResolvedCommand`]. #[cfg(debug_assertions)] -#[derive(Debug, Clone, Hash, PartialEq, Eq)] +#[derive(Default, Clone, PartialEq, Eq)] pub struct ResolvedCommandReference { /// Identifier of the capability. pub capability: String, @@ -36,26 +30,32 @@ pub struct ResolvedCommandReference { /// A resolved command permission. #[derive(Default, Clone, PartialEq, Eq)] pub struct ResolvedCommand { - /// The list of capability/permission that referenced this command. + /// The execution context of this command. + pub context: ExecutionContext, + /// The capability/permission that referenced this command. #[cfg(debug_assertions)] - pub referenced_by: Vec, + pub referenced_by: ResolvedCommandReference, /// The list of window label patterns that was resolved for this command. pub windows: Vec, - /// The reference of the scope that is associated with this command. See [`Resolved#structfield.scopes`]. - pub scope: Option, + /// The list of webview label patterns that was resolved for this command. + pub webviews: Vec, + /// The reference of the scope that is associated with this command. See [`Resolved#structfield.command_scopes`]. + pub scope_id: Option, } impl fmt::Debug for ResolvedCommand { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("ResolvedCommand") + .field("context", &self.context) .field("windows", &self.windows) - .field("scope", &self.scope) + .field("webviews", &self.webviews) + .field("scope_id", &self.scope_id) .finish() } } /// A resolved scope. Merges all scopes defined for a single command. -#[derive(Debug, Default)] +#[derive(Debug, Default, Clone)] pub struct ResolvedScope { /// Allows something on the command. pub allow: Vec, @@ -63,47 +63,23 @@ pub struct ResolvedScope { pub deny: Vec, } -/// A command key for the map of allowed and denied commands. -/// Takes into consideration the command name and the execution context. -#[derive(Debug, Clone, Eq, PartialEq, PartialOrd, Ord, Hash)] -pub struct CommandKey { - /// The full command name. - pub name: String, - /// The context of the command. - pub context: ExecutionContext, -} - /// Resolved access control list. -#[derive(Default)] +#[derive(Debug, Default)] pub struct Resolved { - /// ACL plugin manifests. - #[cfg(debug_assertions)] - pub acl: BTreeMap, /// The commands that are allowed. Map each command with its context to a [`ResolvedCommand`]. - pub allowed_commands: BTreeMap, + pub allowed_commands: BTreeMap>, /// The commands that are denied. Map each command with its context to a [`ResolvedCommand`]. - pub denied_commands: BTreeMap, + pub denied_commands: BTreeMap>, /// The store of scopes referenced by a [`ResolvedCommand`]. pub command_scope: BTreeMap, /// The global scope. pub global_scope: BTreeMap, } -impl fmt::Debug for Resolved { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("Resolved") - .field("allowed_commands", &self.allowed_commands) - .field("denied_commands", &self.denied_commands) - .field("command_scope", &self.command_scope) - .field("global_scope", &self.global_scope) - .finish() - } -} - impl Resolved { /// Resolves the ACL for the given plugin permissions and app capabilities. pub fn resolve( - acl: BTreeMap, + acl: &BTreeMap, capabilities: BTreeMap, target: Target, ) -> Result { @@ -111,76 +87,43 @@ impl Resolved { let mut denied_commands = BTreeMap::new(); let mut current_scope_id = 0; - let mut command_scopes = BTreeMap::new(); + let mut command_scope = BTreeMap::new(); let mut global_scope: BTreeMap> = BTreeMap::new(); // resolve commands for capability in capabilities.values() { - if !capability.platforms.contains(&target) { + if !capability + .platforms + .as_ref() + .map(|platforms| platforms.contains(&target)) + .unwrap_or(true) + { continue; } - for permission_entry in &capability.permissions { - let permission_id = permission_entry.identifier(); - let permission_name = permission_id.get_base(); - - if let Some(plugin_name) = permission_id.get_prefix() { - let permissions = get_permissions(plugin_name, permission_name, &acl)?; - - let mut resolved_scope = Scopes::default(); - let mut commands = Commands::default(); - - if let PermissionEntry::ExtendedPermission { - identifier: _, - scope, - } = permission_entry - { - if let Some(allow) = scope.allow.clone() { - resolved_scope - .allow - .get_or_insert_with(Default::default) - .extend(allow); - } - if let Some(deny) = scope.deny.clone() { - resolved_scope - .deny - .get_or_insert_with(Default::default) - .extend(deny); - } - } - - for permission in permissions { - if let Some(allow) = permission.scope.allow.clone() { - resolved_scope - .allow - .get_or_insert_with(Default::default) - .extend(allow); - } - if let Some(deny) = permission.scope.deny.clone() { - resolved_scope - .deny - .get_or_insert_with(Default::default) - .extend(deny); - } - - commands.allow.extend(permission.commands.allow.clone()); - commands.deny.extend(permission.commands.deny.clone()); - } - + with_resolved_permissions( + capability, + acl, + target, + |ResolvedPermission { + key, + permission_name, + commands, + scope, + }| { if commands.allow.is_empty() && commands.deny.is_empty() { // global scope - global_scope - .entry(plugin_name.to_string()) - .or_default() - .push(resolved_scope); + global_scope.entry(key.to_string()).or_default().push(scope); } else { - let has_scope = resolved_scope.allow.is_some() || resolved_scope.deny.is_some(); - if has_scope { + let scope_id = if scope.allow.is_some() || scope.deny.is_some() { current_scope_id += 1; - command_scopes.insert(current_scope_id, resolved_scope); - } - - let scope_id = if has_scope { + command_scope.insert( + current_scope_id, + ResolvedScope { + allow: scope.allow.unwrap_or_default(), + deny: scope.deny.unwrap_or_default(), + }, + ); Some(current_scope_id) } else { None @@ -189,65 +132,46 @@ impl Resolved { for allowed_command in &commands.allow { resolve_command( &mut allowed_commands, - format!("plugin:{plugin_name}|{allowed_command}"), + if key == APP_ACL_KEY { + allowed_command.to_string() + } else { + format!("plugin:{key}|{allowed_command}") + }, capability, scope_id, #[cfg(debug_assertions)] permission_name.to_string(), - ); + )?; } for denied_command in &commands.deny { resolve_command( &mut denied_commands, - format!("plugin:{plugin_name}|{denied_command}"), + if key == APP_ACL_KEY { + denied_command.to_string() + } else { + format!("plugin:{key}|{denied_command}") + }, capability, scope_id, #[cfg(debug_assertions)] permission_name.to_string(), - ); + )?; } } - } - } - } - // resolve scopes - let mut resolved_scopes = BTreeMap::new(); - - for allowed in allowed_commands.values_mut() { - if !allowed.scope.is_empty() { - allowed.scope.sort(); - - let mut hasher = DefaultHasher::new(); - allowed.scope.hash(&mut hasher); - let hash = hasher.finish(); - - allowed.resolved_scope_key.replace(hash); - - let resolved_scope = ResolvedScope { - allow: allowed - .scope - .iter() - .flat_map(|s| command_scopes.get(s).unwrap().allow.clone()) - .flatten() - .collect(), - deny: allowed - .scope - .iter() - .flat_map(|s| command_scopes.get(s).unwrap().deny.clone()) - .flatten() - .collect(), - }; - - resolved_scopes.insert(hash, resolved_scope); - } + Ok(()) + }, + )?; } let global_scope = global_scope .into_iter() - .map(|(plugin_name, scopes)| { - let mut resolved_scope = ResolvedScope::default(); + .map(|(key, scopes)| { + let mut resolved_scope = ResolvedScope { + allow: Vec::new(), + deny: Vec::new(), + }; for scope in scopes { if let Some(allow) = scope.allow { resolved_scope.allow.extend(allow); @@ -256,42 +180,14 @@ impl Resolved { resolved_scope.deny.extend(deny); } } - (plugin_name, resolved_scope) + (key, resolved_scope) }) .collect(); let resolved = Self { - #[cfg(debug_assertions)] - acl, - allowed_commands: allowed_commands - .into_iter() - .map(|(key, cmd)| { - Ok(( - key, - ResolvedCommand { - #[cfg(debug_assertions)] - referenced_by: cmd.referenced_by, - windows: parse_window_patterns(cmd.windows)?, - scope: cmd.resolved_scope_key, - }, - )) - }) - .collect::>()?, - denied_commands: denied_commands - .into_iter() - .map(|(key, cmd)| { - Ok(( - key, - ResolvedCommand { - #[cfg(debug_assertions)] - referenced_by: cmd.referenced_by, - windows: parse_window_patterns(cmd.windows)?, - scope: cmd.resolved_scope_key, - }, - )) - }) - .collect::>()?, - command_scope: resolved_scopes, + allowed_commands, + denied_commands, + command_scope, global_scope, }; @@ -299,62 +195,135 @@ impl Resolved { } } -fn parse_window_patterns(windows: HashSet) -> Result, Error> { +fn parse_glob_patterns(mut raw: Vec) -> Result, Error> { + raw.sort(); + let mut patterns = Vec::new(); - for window in windows { - patterns.push(glob::Pattern::new(&window)?); + for pattern in raw { + patterns.push(glob::Pattern::new(&pattern)?); } + Ok(patterns) } -#[derive(Debug, Default)] -struct ResolvedCommandTemp { - #[cfg(debug_assertions)] - pub referenced_by: Vec, - pub windows: HashSet, - pub scope: Vec, - pub resolved_scope_key: Option, +struct ResolvedPermission<'a> { + key: &'a str, + permission_name: &'a str, + commands: Commands, + scope: Scopes, +} + +fn with_resolved_permissions) -> Result<(), Error>>( + capability: &Capability, + acl: &BTreeMap, + target: Target, + mut f: F, +) -> Result<(), Error> { + for permission_entry in &capability.permissions { + let permission_id = permission_entry.identifier(); + let permission_name = permission_id.get_base(); + + let key = permission_id.get_prefix().unwrap_or(APP_ACL_KEY); + + let permissions = get_permissions(key, permission_name, acl)? + .into_iter() + .filter(|p| { + p.platforms + .as_ref() + .map(|platforms| platforms.contains(&target)) + .unwrap_or(true) + }) + .collect::>(); + + let mut resolved_scope = Scopes::default(); + let mut commands = Commands::default(); + + if let PermissionEntry::ExtendedPermission { + identifier: _, + scope, + } = permission_entry + { + if let Some(allow) = scope.allow.clone() { + resolved_scope + .allow + .get_or_insert_with(Default::default) + .extend(allow); + } + if let Some(deny) = scope.deny.clone() { + resolved_scope + .deny + .get_or_insert_with(Default::default) + .extend(deny); + } + } + + for permission in permissions { + if let Some(allow) = permission.scope.allow.clone() { + resolved_scope + .allow + .get_or_insert_with(Default::default) + .extend(allow); + } + if let Some(deny) = permission.scope.deny.clone() { + resolved_scope + .deny + .get_or_insert_with(Default::default) + .extend(deny); + } + + commands.allow.extend(permission.commands.allow.clone()); + commands.deny.extend(permission.commands.deny.clone()); + } + + f(ResolvedPermission { + key, + permission_name, + commands, + scope: resolved_scope, + })?; + } + + Ok(()) } fn resolve_command( - commands: &mut BTreeMap, + commands: &mut BTreeMap>, command: String, capability: &Capability, scope_id: Option, #[cfg(debug_assertions)] referenced_by_permission_identifier: String, -) { - let contexts = match &capability.context { - CapabilityContext::Local => { - vec![ExecutionContext::Local] - } - CapabilityContext::Remote { domains } => domains - .iter() - .map(|domain| ExecutionContext::Remote { - domain: Pattern::new(domain) - .unwrap_or_else(|e| panic!("invalid glob pattern for remote domain {domain}: {e}")), - }) - .collect(), - }; +) -> Result<(), Error> { + let mut contexts = Vec::new(); + if capability.local { + contexts.push(ExecutionContext::Local); + } + if let Some(remote) = &capability.remote { + contexts.extend(remote.urls.iter().map(|url| { + ExecutionContext::Remote { + url: url + .parse() + .unwrap_or_else(|e| panic!("invalid URL pattern for remote URL {url}: {e}")), + } + })); + } for context in contexts { - let resolved = commands - .entry(CommandKey { - name: command.clone(), - context, - }) - .or_default(); + let resolved_list = commands.entry(command.clone()).or_default(); - #[cfg(debug_assertions)] - resolved.referenced_by.push(ResolvedCommandReference { - capability: capability.identifier.clone(), - permission: referenced_by_permission_identifier.clone(), + resolved_list.push(ResolvedCommand { + context, + #[cfg(debug_assertions)] + referenced_by: ResolvedCommandReference { + capability: capability.identifier.clone(), + permission: referenced_by_permission_identifier.clone(), + }, + windows: parse_glob_patterns(capability.windows.clone())?, + webviews: parse_glob_patterns(capability.webviews.clone())?, + scope_id, }); - - resolved.windows.extend(capability.windows.clone()); - if let Some(id) = scope_id { - resolved.scope.push(id); - } } + + Ok(()) } // get the permissions from a permission set @@ -381,12 +350,16 @@ fn get_permission_set_permissions<'a>( } fn get_permissions<'a>( - plugin_name: &'a str, + key: &'a str, permission_name: &'a str, acl: &'a BTreeMap, ) -> Result, Error> { - let manifest = acl.get(plugin_name).ok_or_else(|| Error::UnknownPlugin { - plugin: plugin_name.to_string(), + let manifest = acl.get(key).ok_or_else(|| Error::UnknownManifest { + key: if key == APP_ACL_KEY { + "app manifest".to_string() + } else { + key.to_string() + }, available: acl.keys().cloned().collect::>().join(", "), })?; @@ -394,18 +367,19 @@ fn get_permissions<'a>( manifest .default_permission .as_ref() - .ok_or_else(|| Error::UnknownPermission { - plugin: plugin_name.to_string(), - permission: permission_name.to_string(), - }) - .and_then(|default| get_permission_set_permissions(manifest, default)) + .map(|default| get_permission_set_permissions(manifest, default)) + .unwrap_or_else(|| Ok(Vec::new())) } else if let Some(set) = manifest.permission_sets.get(permission_name) { get_permission_set_permissions(manifest, set) } else if let Some(permission) = manifest.permissions.get(permission_name) { Ok(vec![permission]) } else { Err(Error::UnknownPermission { - plugin: plugin_name.to_string(), + key: if key == APP_ACL_KEY { + "app manifest".to_string() + } else { + key.to_string() + }, permission: permission_name.to_string(), }) } @@ -420,19 +394,6 @@ mod build { use super::*; use crate::{literal_struct, tokens::*}; - impl ToTokens for CommandKey { - fn to_tokens(&self, tokens: &mut TokenStream) { - let name = str_lit(&self.name); - let context = &self.context; - literal_struct!( - tokens, - ::tauri::utils::acl::resolved::CommandKey, - name, - context - ) - } - } - #[cfg(debug_assertions)] impl ToTokens for ResolvedCommandReference { fn to_tokens(&self, tokens: &mut TokenStream) { @@ -450,30 +411,40 @@ mod build { impl ToTokens for ResolvedCommand { fn to_tokens(&self, tokens: &mut TokenStream) { #[cfg(debug_assertions)] - let referenced_by = vec_lit(&self.referenced_by, identity); + let referenced_by = &self.referenced_by; + + let context = &self.context; let windows = vec_lit(&self.windows, |window| { let w = window.as_str(); quote!(#w.parse().unwrap()) }); - let scope = opt_lit(self.scope.as_ref()); + let webviews = vec_lit(&self.webviews, |window| { + let w = window.as_str(); + quote!(#w.parse().unwrap()) + }); + let scope_id = opt_lit(self.scope_id.as_ref()); #[cfg(debug_assertions)] { literal_struct!( tokens, ::tauri::utils::acl::resolved::ResolvedCommand, + context, referenced_by, windows, - scope + webviews, + scope_id ) } #[cfg(not(debug_assertions))] literal_struct!( tokens, ::tauri::utils::acl::resolved::ResolvedCommand, + context, windows, - scope + webviews, + scope_id ) } } @@ -493,26 +464,18 @@ mod build { impl ToTokens for Resolved { fn to_tokens(&self, tokens: &mut TokenStream) { - #[cfg(debug_assertions)] - let acl = map_lit( - quote! { ::std::collections::BTreeMap }, - &self.acl, - str_lit, - identity, - ); - let allowed_commands = map_lit( quote! { ::std::collections::BTreeMap }, &self.allowed_commands, - identity, - identity, + str_lit, + |v| vec_lit(v, identity), ); let denied_commands = map_lit( quote! { ::std::collections::BTreeMap }, &self.denied_commands, - identity, - identity, + str_lit, + |v| vec_lit(v, identity), ); let command_scope = map_lit( @@ -529,19 +492,6 @@ mod build { identity, ); - #[cfg(debug_assertions)] - { - literal_struct!( - tokens, - ::tauri::utils::acl::resolved::Resolved, - acl, - allowed_commands, - denied_commands, - command_scope, - global_scope - ) - } - #[cfg(not(debug_assertions))] literal_struct!( tokens, ::tauri::utils::acl::resolved::Resolved, diff --git a/core/tauri-utils/src/acl/value.rs b/core/tauri-utils/src/acl/value.rs index 25fcad52b..34c7efc48 100644 --- a/core/tauri-utils/src/acl/value.rs +++ b/core/tauri-utils/src/acl/value.rs @@ -1,4 +1,4 @@ -// Copyright 2019-2023 Tauri Programme within The Commons Conservancy +// Copyright 2019-2024 Tauri Programme within The Commons Conservancy // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT @@ -11,7 +11,7 @@ use std::fmt::Debug; use serde::{Deserialize, Serialize}; /// A valid ACL number. -#[derive(Debug, Serialize, Deserialize, Copy, Clone, PartialOrd, PartialEq)] +#[derive(Debug, PartialEq, Serialize, Deserialize, Copy, Clone, PartialOrd)] #[cfg_attr(feature = "schema", derive(schemars::JsonSchema))] #[serde(untagged)] pub enum Number { @@ -37,7 +37,7 @@ impl From for Number { } /// All supported ACL values. -#[derive(Debug, Serialize, Deserialize, Clone, PartialOrd, PartialEq)] +#[derive(Debug, PartialEq, Serialize, Deserialize, Clone, PartialOrd)] #[cfg_attr(feature = "schema", derive(schemars::JsonSchema))] #[serde(untagged)] pub enum Value { diff --git a/core/tauri-utils/src/assets.rs b/core/tauri-utils/src/assets.rs index ca7f3de69..4e85bf56c 100644 --- a/core/tauri-utils/src/assets.rs +++ b/core/tauri-utils/src/assets.rs @@ -1,4 +1,4 @@ -// Copyright 2019-2023 Tauri Programme within The Commons Conservancy +// Copyright 2019-2024 Tauri Programme within The Commons Conservancy // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT @@ -104,18 +104,6 @@ impl CspHash<'_> { } } -/// Represents a container of file assets that are retrievable during runtime. -pub trait Assets: Send + Sync + 'static { - /// Get the content of the passed [`AssetKey`]. - fn get(&self, key: &AssetKey) -> Option>; - - /// Iterator for the assets. - fn iter(&self) -> Box + '_>; - - /// Gets the hashes for the CSP tag of the HTML on the given path. - fn csp_hashes(&self, html_path: &AssetKey) -> Box> + '_>; -} - /// [`Assets`] implementation that only contains compile-time compressed and embedded assets. #[derive(Debug)] pub struct EmbeddedAssets { @@ -139,11 +127,10 @@ impl EmbeddedAssets { html_hashes, } } -} -impl Assets for EmbeddedAssets { + /// Get an asset by key. #[cfg(feature = "compression")] - fn get(&self, key: &AssetKey) -> Option> { + pub fn get(&self, key: &AssetKey) -> Option> { self .assets .get(key.as_ref()) @@ -157,8 +144,9 @@ impl Assets for EmbeddedAssets { .map(Cow::Owned) } + /// Get an asset by key. #[cfg(not(feature = "compression"))] - fn get(&self, key: &AssetKey) -> Option> { + pub fn get(&self, key: &AssetKey) -> Option> { self .assets .get(key.as_ref()) @@ -166,11 +154,13 @@ impl Assets for EmbeddedAssets { .map(|a| Cow::Owned(a.to_vec())) } - fn iter(&self) -> Box + '_> { - Box::new(self.assets.into_iter()) + /// Iterate on the assets. + pub fn iter(&self) -> Box + '_> { + Box::new(self.assets.into_iter().map(|(k, b)| (*k, *b))) } - fn csp_hashes(&self, html_path: &AssetKey) -> Box> + '_> { + /// CSP hashes for the given asset. + pub fn csp_hashes(&self, html_path: &AssetKey) -> Box> + '_> { Box::new( self .global_hashes diff --git a/core/tauri-utils/src/build.rs b/core/tauri-utils/src/build.rs index a21be51bc..510d47762 100644 --- a/core/tauri-utils/src/build.rs +++ b/core/tauri-utils/src/build.rs @@ -1,4 +1,4 @@ -// Copyright 2019-2023 Tauri Programme within The Commons Conservancy +// Copyright 2019-2024 Tauri Programme within The Commons Conservancy // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT diff --git a/core/tauri-utils/src/config.rs b/core/tauri-utils/src/config.rs index 452b54943..5951b30f3 100644 --- a/core/tauri-utils/src/config.rs +++ b/core/tauri-utils/src/config.rs @@ -1,4 +1,4 @@ -// Copyright 2019-2023 Tauri Programme within The Commons Conservancy +// Copyright 2019-2024 Tauri Programme within The Commons Conservancy // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT @@ -34,7 +34,7 @@ use std::{ /// Items to help with parsing content into a [`Config`]. pub mod parse; -use crate::{TitleBarStyle, WindowEffect, WindowEffectState}; +use crate::{acl::capability::Capability, TitleBarStyle, WindowEffect, WindowEffectState}; pub use self::parse::parse; @@ -121,6 +121,22 @@ pub enum BundleType { Updater, } +impl BundleType { + /// All bundle types. + fn all() -> &'static [Self] { + &[ + BundleType::Deb, + BundleType::Rpm, + BundleType::AppImage, + BundleType::Msi, + BundleType::Nsis, + BundleType::App, + BundleType::Dmg, + BundleType::Updater, + ] + } +} + impl Display for BundleType { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!( @@ -274,7 +290,7 @@ impl BundleTarget { #[allow(dead_code)] pub fn to_vec(&self) -> Vec { match self { - Self::All => vec![], + Self::All => BundleType::all().to_vec(), Self::List(list) => list.clone(), Self::One(i) => vec![i.clone()], } @@ -307,13 +323,44 @@ pub struct AppImageConfig { pub struct DebConfig { /// The list of deb dependencies your application relies on. pub depends: Option>, + /// The list of dependencies the package provides. + pub provides: Option>, + /// The list of package conflicts. + pub conflicts: Option>, + /// The list of package replaces. + pub replaces: Option>, /// The files to include on the package. #[serde(default)] pub files: HashMap, + /// Define the section in Debian Control file. See : https://www.debian.org/doc/debian-policy/ch-archive.html#s-subsections + pub section: Option, + /// Change the priority of the Debian Package. By default, it is set to `optional`. + /// Recognized Priorities as of now are : `required`, `important`, `standard`, `optional`, `extra` + pub priority: Option, + /// Path of the uncompressed Changelog file, to be stored at /usr/share/doc/package-name/changelog.gz. See + /// https://www.debian.org/doc/debian-policy/ch-docs.html#changelog-files-and-release-notes + pub changelog: Option, /// Path to a custom desktop file Handlebars template. /// /// Available variables: `categories`, `comment` (optional), `exec`, `icon` and `name`. + #[serde(alias = "desktop-template")] pub desktop_template: Option, + /// Path to script that will be executed before the package is unpacked. See + /// https://www.debian.org/doc/debian-policy/ch-maintainerscripts.html + #[serde(alias = "pre-install-script")] + pub pre_install_script: Option, + /// Path to script that will be executed after the package is unpacked. See + /// https://www.debian.org/doc/debian-policy/ch-maintainerscripts.html + #[serde(alias = "post-install-script")] + pub post_install_script: Option, + /// Path to script that will be executed before the package is removed. See + /// https://www.debian.org/doc/debian-policy/ch-maintainerscripts.html + #[serde(alias = "pre-remove-script")] + pub pre_remove_script: Option, + /// Path to script that will be executed after the package is removed. See + /// https://www.debian.org/doc/debian-policy/ch-maintainerscripts.html + #[serde(alias = "post-remove-script")] + pub post_remove_script: Option, } /// Configuration for Linux bundles. @@ -343,6 +390,14 @@ pub struct LinuxConfig { pub struct RpmConfig { /// The list of RPM dependencies your application relies on. pub depends: Option>, + /// The list of RPM dependencies your application provides. + pub provides: Option>, + /// The list of RPM dependencies your application conflicts with. They must not be present + /// in order for the package to be installed. + pub conflicts: Option>, + /// The list of RPM dependencies your application supersedes - if this package is installed, + /// packages listed as “obsoletes” will be automatically removed (if they are present). + pub obsoletes: Option>, /// The RPM release tag. #[serde(default = "default_release")] pub release: String, @@ -355,17 +410,41 @@ pub struct RpmConfig { /// Path to a custom desktop file Handlebars template. /// /// Available variables: `categories`, `comment` (optional), `exec`, `icon` and `name`. + #[serde(alias = "desktop-template")] pub desktop_template: Option, + /// Path to script that will be executed before the package is unpacked. See + /// http://ftp.rpm.org/max-rpm/s1-rpm-inside-scripts.html + #[serde(alias = "pre-install-script")] + pub pre_install_script: Option, + /// Path to script that will be executed after the package is unpacked. See + /// http://ftp.rpm.org/max-rpm/s1-rpm-inside-scripts.html + #[serde(alias = "post-install-script")] + pub post_install_script: Option, + /// Path to script that will be executed before the package is removed. See + /// http://ftp.rpm.org/max-rpm/s1-rpm-inside-scripts.html + #[serde(alias = "pre-remove-script")] + pub pre_remove_script: Option, + /// Path to script that will be executed after the package is removed. See + /// http://ftp.rpm.org/max-rpm/s1-rpm-inside-scripts.html + #[serde(alias = "post-remove-script")] + pub post_remove_script: Option, } impl Default for RpmConfig { fn default() -> Self { Self { depends: None, + provides: None, + conflicts: None, + obsoletes: None, release: default_release(), epoch: 0, files: Default::default(), desktop_template: None, + pre_install_script: None, + post_install_script: None, + pre_remove_script: None, + post_remove_script: None, } } } @@ -1042,11 +1121,11 @@ pub struct WindowConfig { /// The user agent for the webview #[serde(alias = "user-agent")] pub user_agent: Option, - /// Whether the file drop is enabled or not on the webview. By default it is enabled. + /// Whether the drag and drop is enabled or not on the webview. By default it is enabled. /// - /// Disabling it is required to use drag and drop on the frontend on Windows. - #[serde(default = "default_true", alias = "file-drop-enabled")] - pub file_drop_enabled: bool, + /// Disabling it is required to use HTML5 drag and drop on the frontend on Windows. + #[serde(default = "default_true", alias = "drag-drop-enabled")] + pub drag_drop_enabled: bool, /// Whether or not the window starts centered or not. #[serde(default)] pub center: bool, @@ -1214,6 +1293,17 @@ pub struct WindowConfig { /// /// - **macOS**: Requires the `macos-proxy` feature flag and only compiles for macOS 14+. pub proxy_url: Option, + /// Whether page zooming by hotkeys is enabled + /// + /// ## Platform-specific: + /// + /// - **Windows**: Controls WebView2's [`IsZoomControlEnabled`](https://learn.microsoft.com/en-us/microsoft-edge/webview2/reference/winrt/microsoft_web_webview2_core/corewebview2settings?view=webview2-winrt-1.0.2420.47#iszoomcontrolenabled) setting. + /// - **MacOS / Linux**: Injects a polyfill that zooms in and out with `ctrl/command` + `-/=`, + /// 20% in each step, ranging from 20% to 1000%. Requires `webview:allow-set-webview-zoom` permission + /// + /// - **Android / iOS**: Unsupported. + #[serde(default)] + pub zoom_hotkeys_enabled: bool, } impl Default for WindowConfig { @@ -1222,7 +1312,7 @@ impl Default for WindowConfig { label: default_window_label(), url: WebviewUrl::default(), user_agent: None, - file_drop_enabled: true, + drag_drop_enabled: true, center: false, x: None, y: None, @@ -1259,6 +1349,7 @@ impl Default for WindowConfig { incognito: false, parent: None, proxy_url: None, + zoom_hotkeys_enabled: false, } } } @@ -1422,23 +1513,6 @@ impl Default for DisabledCspModificationKind { } } -/// External command access definition. -#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)] -#[cfg_attr(feature = "schema", derive(JsonSchema))] -#[serde(rename_all = "camelCase", deny_unknown_fields)] -pub struct RemoteDomainAccessScope { - /// The URL scheme to allow. By default, all schemas are allowed. - pub scheme: Option, - /// The domain to allow. - pub domain: String, - /// The list of window labels this scope applies to. - pub windows: Vec, - /// The list of plugins that are allowed in this scope. - /// The names should be without the `tauri-plugin-` prefix, for example `"store"` for `tauri-plugin-store`. - #[serde(default)] - pub plugins: Vec, -} - /// Protocol scope definition. /// It is a list of glob patterns that restrict the API access from the webview. /// @@ -1519,7 +1593,7 @@ pub struct AssetProtocolConfig { /// /// See more: #[skip_serializing_none] -#[derive(Debug, Default, PartialEq, Eq, Clone, Deserialize, Serialize)] +#[derive(Debug, Default, PartialEq, Clone, Deserialize, Serialize)] #[cfg_attr(feature = "schema", derive(JsonSchema))] #[serde(rename_all = "camelCase", deny_unknown_fields)] pub struct SecurityConfig { @@ -1558,6 +1632,22 @@ pub struct SecurityConfig { /// The pattern to use. #[serde(default)] pub pattern: PatternKind, + /// List of capabilities that are enabled on the application. + /// + /// If the list is empty, all capabilities are included. + #[serde(default)] + pub capabilities: Vec, +} + +/// A capability entry which can be either an inlined capability or a reference to a capability defined on its own file. +#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)] +#[cfg_attr(feature = "schema", derive(JsonSchema))] +#[serde(rename_all = "camelCase", untagged)] +pub enum CapabilityEntry { + /// An inlined capability. + Inlined(Capability), + /// Reference to a capability identifier. + Reference(String), } /// The application pattern. @@ -2056,7 +2146,7 @@ mod build { impl ToTokens for Color { fn to_tokens(&self, tokens: &mut TokenStream) { let Color(r, g, b, a) = self; - tokens.append_all(quote! {::tauri::utils::Color(#r,#g,#b,#a)}); + tokens.append_all(quote! {::tauri::utils::config::Color(#r,#g,#b,#a)}); } } impl ToTokens for WindowEffectsConfig { @@ -2144,7 +2234,7 @@ mod build { let label = str_lit(&self.label); let url = &self.url; let user_agent = opt_str_lit(self.user_agent.as_ref()); - let file_drop_enabled = self.file_drop_enabled; + let drag_drop_enabled = self.drag_drop_enabled; let center = self.center; let x = opt_lit(self.x.as_ref()); let y = opt_lit(self.y.as_ref()); @@ -2181,6 +2271,7 @@ mod build { let window_effects = opt_lit(self.window_effects.as_ref()); let incognito = self.incognito; let parent = opt_str_lit(self.parent.as_ref()); + let zoom_hotkeys_enabled = self.zoom_hotkeys_enabled; literal_struct!( tokens, @@ -2188,7 +2279,7 @@ mod build { label, url, user_agent, - file_drop_enabled, + drag_drop_enabled, center, x, y, @@ -2224,7 +2315,8 @@ mod build { shadow, window_effects, incognito, - parent + parent, + zoom_hotkeys_enabled ); } } @@ -2432,21 +2524,19 @@ mod build { } } - impl ToTokens for RemoteDomainAccessScope { + impl ToTokens for CapabilityEntry { fn to_tokens(&self, tokens: &mut TokenStream) { - let scheme = opt_str_lit(self.scheme.as_ref()); - let domain = str_lit(&self.domain); - let windows = vec_lit(&self.windows, str_lit); - let plugins = vec_lit(&self.plugins, str_lit); + let prefix = quote! { ::tauri::utils::config::CapabilityEntry }; - literal_struct!( - tokens, - ::tauri::utils::config::RemoteDomainAccessScope, - scheme, - domain, - windows, - plugins - ); + tokens.append_all(match self { + Self::Inlined(capability) => { + quote! { #prefix::Inlined(#capability) } + } + Self::Reference(id) => { + let id = str_lit(id); + quote! { #prefix::Reference(#id) } + } + }); } } @@ -2458,6 +2548,7 @@ mod build { let dangerous_disable_asset_csp_modification = &self.dangerous_disable_asset_csp_modification; let asset_protocol = &self.asset_protocol; let pattern = &self.pattern; + let capabilities = vec_lit(&self.capabilities, identity); literal_struct!( tokens, @@ -2467,7 +2558,8 @@ mod build { freeze_prototype, dangerous_disable_asset_csp_modification, asset_protocol, - pattern + pattern, + capabilities ); } } @@ -2606,6 +2698,7 @@ mod test { dangerous_disable_asset_csp_modification: DisabledCspModificationKind::Flag(false), asset_protocol: AssetProtocolConfig::default(), pattern: Default::default(), + capabilities: Vec::new(), }, tray_icon: None, macos_private_api: false, diff --git a/core/tauri-utils/src/config/parse.rs b/core/tauri-utils/src/config/parse.rs index 016c57896..ccf6ad536 100644 --- a/core/tauri-utils/src/config/parse.rs +++ b/core/tauri-utils/src/config/parse.rs @@ -1,4 +1,4 @@ -// Copyright 2019-2023 Tauri Programme within The Commons Conservancy +// Copyright 2019-2024 Tauri Programme within The Commons Conservancy // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT diff --git a/core/tauri-utils/src/html.rs b/core/tauri-utils/src/html.rs index 082a5daad..97c83e2a3 100644 --- a/core/tauri-utils/src/html.rs +++ b/core/tauri-utils/src/html.rs @@ -1,4 +1,4 @@ -// Copyright 2019-2023 Tauri Programme within The Commons Conservancy +// Copyright 2019-2024 Tauri Programme within The Commons Conservancy // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT @@ -23,8 +23,6 @@ use crate::config::{DisabledCspModificationKind, PatternKind}; #[cfg(feature = "isolation")] use crate::pattern::isolation::IsolationJavascriptCodegen; -/// The token used on the CSP tag content. -pub const CSP_TOKEN: &str = "__TAURI_CSP__"; /// The token used for script nonces. pub const SCRIPT_NONCE_TOKEN: &str = "__TAURI_SCRIPT_NONCE__"; /// The token used for style nonces. @@ -133,8 +131,8 @@ fn with_head(document: &NodeRef, f: F) { } fn inject_nonce(document: &NodeRef, selector: &str, token: &str) { - if let Ok(scripts) = document.select(selector) { - for target in scripts { + if let Ok(elements) = document.select(selector) { + for target in elements { let node = target.as_node(); let element = node.as_element().unwrap(); @@ -168,11 +166,6 @@ pub fn inject_csp(document: &NodeRef, csp: &str) { }); } -/// Injects a content security policy token to the HTML. -pub fn inject_csp_token(document: &NodeRef) { - inject_csp(document, CSP_TOKEN) -} - fn create_csp_meta_tag(csp: &str) -> NodeRef { NodeRef::new_element( QualName::new(None, ns!(html), LocalName::from("meta")), @@ -241,7 +234,16 @@ impl Default for IsolationSide { #[cfg(feature = "isolation")] pub fn inject_codegen_isolation_script(document: &NodeRef) { with_head(document, |head| { - let script = NodeRef::new_element(QualName::new(None, ns!(html), "script".into()), None); + let script = NodeRef::new_element( + QualName::new(None, ns!(html), "script".into()), + vec![( + ExpandedName::new(ns!(), LocalName::from("nonce")), + Attribute { + prefix: None, + value: SCRIPT_NONCE_TOKEN.into(), + }, + )], + ); script.append(NodeRef::new_text( IsolationJavascriptCodegen {} .render_default(&Default::default()) @@ -298,12 +300,12 @@ mod tests { ]; for html in htmls { let document = kuchiki::parse_html().one(html); - super::inject_csp_token(&document); + let csp = "csp-string"; + super::inject_csp(&document, csp); assert_eq!( document.to_string(), format!( - r#""#, - super::CSP_TOKEN + r#""#, ) ); } diff --git a/core/tauri-utils/src/io.rs b/core/tauri-utils/src/io.rs index 9dc699fa6..be4fd0d07 100644 --- a/core/tauri-utils/src/io.rs +++ b/core/tauri-utils/src/io.rs @@ -1,4 +1,4 @@ -// Copyright 2019-2023 Tauri Programme within The Commons Conservancy +// Copyright 2019-2024 Tauri Programme within The Commons Conservancy // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT diff --git a/core/tauri-utils/src/lib.rs b/core/tauri-utils/src/lib.rs index a8df7a46d..0cf8c6287 100644 --- a/core/tauri-utils/src/lib.rs +++ b/core/tauri-utils/src/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2019-2023 Tauri Programme within The Commons Conservancy +// Copyright 2019-2024 Tauri Programme within The Commons Conservancy // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT @@ -22,8 +22,6 @@ use std::{ use semver::Version; use serde::{Deserialize, Deserializer, Serialize, Serializer}; -use log::warn; - pub mod acl; pub mod assets; pub mod config; @@ -31,6 +29,7 @@ pub mod html; pub mod io; pub mod mime_type; pub mod platform; +pub mod plugin; /// Prepare application resources and sidecars. #[cfg(feature = "resources")] pub mod resources; @@ -320,7 +319,7 @@ impl Default for Env { .unwrap_or(true); if !is_temp { - warn!("`APPDIR` or `APPIMAGE` environment variable found but this application was not detected as an AppImage; this might be a security issue."); + log::warn!("`APPDIR` or `APPIMAGE` environment variable found but this application was not detected as an AppImage; this might be a security issue."); } } env @@ -388,65 +387,9 @@ pub enum Error { NotAllowedToWalkDir(std::path::PathBuf), } -/// Suppresses the unused-variable warnings of the given inputs. -/// -/// This does not move any values. Instead, it just suppresses the warning by taking a -/// reference to the value. -#[macro_export] -macro_rules! consume_unused_variable { - ($($arg:expr),*) => { - $( - let _ = &$arg; - )* - () - }; -} - -/// Prints to the standard error, with a newline. -/// -/// Equivalent to the [`eprintln!`] macro, except that it's only effective for debug builds. -#[macro_export] -macro_rules! debug_eprintln { - () => ($crate::debug_eprintln!("")); - ($($arg:tt)*) => { - #[cfg(debug_assertions)] - eprintln!($($arg)*); - #[cfg(not(debug_assertions))] - $crate::consume_unused_variable!($($arg)*); - }; -} - /// Reconstructs a path from its components using the platform separator then converts it to String and removes UNC prefixes on Windows if it exists. pub fn display_path>(p: P) -> String { dunce::simplified(&p.as_ref().components().collect::()) .display() .to_string() } - -/// Progress bar status. -#[derive(Debug, Clone, Copy, Deserialize)] -#[serde(rename_all = "camelCase")] -pub enum ProgressBarStatus { - /// Hide progress bar. - None, - /// Normal state. - Normal, - /// Indeterminate state. **Treated as Normal on Linux and macOS** - Indeterminate, - /// Paused state. **Treated as Normal on Linux** - Paused, - /// Error state. **Treated as Normal on Linux** - Error, -} - -/// Progress Bar State -#[derive(Debug, Deserialize)] -#[serde(rename_all = "camelCase")] -pub struct ProgressBarState { - /// The progress bar status. - pub status: Option, - /// The progress bar progress. This can be a value ranging from `0` to `100` - pub progress: Option, - /// The identifier for your app to communicate with the Unity desktop window manager **Linux Only** - pub unity_uri: Option, -} diff --git a/core/tauri-utils/src/mime_type.rs b/core/tauri-utils/src/mime_type.rs index 6b51335e6..e78cdd90d 100644 --- a/core/tauri-utils/src/mime_type.rs +++ b/core/tauri-utils/src/mime_type.rs @@ -1,4 +1,4 @@ -// Copyright 2019-2023 Tauri Programme within The Commons Conservancy +// Copyright 2019-2024 Tauri Programme within The Commons Conservancy // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT diff --git a/core/tauri-utils/src/pattern/isolation.js b/core/tauri-utils/src/pattern/isolation.js index 10406235b..f1908bffc 100644 --- a/core/tauri-utils/src/pattern/isolation.js +++ b/core/tauri-utils/src/pattern/isolation.js @@ -1,4 +1,4 @@ -// Copyright 2019-2023 Tauri Programme within The Commons Conservancy +// Copyright 2019-2024 Tauri Programme within The Commons Conservancy // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT @@ -29,7 +29,7 @@ 'raw', aesGcmKeyRaw, 'AES-GCM', - true, + false, ['encrypt'] ) @@ -135,4 +135,6 @@ } setTimeout(waitUntilReady, readyIntervalMs) + + document.currentScript.remove() })() diff --git a/core/tauri-utils/src/pattern/isolation.rs b/core/tauri-utils/src/pattern/isolation.rs index 3b2dfc084..afffdf487 100644 --- a/core/tauri-utils/src/pattern/isolation.rs +++ b/core/tauri-utils/src/pattern/isolation.rs @@ -1,4 +1,4 @@ -// Copyright 2019-2023 Tauri Programme within The Commons Conservancy +// Copyright 2019-2024 Tauri Programme within The Commons Conservancy // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT diff --git a/core/tauri-utils/src/pattern/mod.rs b/core/tauri-utils/src/pattern/mod.rs index 8861e1b43..97ca49547 100644 --- a/core/tauri-utils/src/pattern/mod.rs +++ b/core/tauri-utils/src/pattern/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2019-2023 Tauri Programme within The Commons Conservancy +// Copyright 2019-2024 Tauri Programme within The Commons Conservancy // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT diff --git a/core/tauri-utils/src/platform.rs b/core/tauri-utils/src/platform.rs index 1ff29964b..5c3bc8e02 100644 --- a/core/tauri-utils/src/platform.rs +++ b/core/tauri-utils/src/platform.rs @@ -1,4 +1,4 @@ -// Copyright 2019-2023 Tauri Programme within The Commons Conservancy +// Copyright 2019-2024 Tauri Programme within The Commons Conservancy // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT @@ -6,7 +6,7 @@ use std::{ fmt::Display, - path::{PathBuf, MAIN_SEPARATOR}, + path::{Path, PathBuf, MAIN_SEPARATOR}, }; use serde::{Deserialize, Serialize}; @@ -221,6 +221,28 @@ pub fn target_triple() -> crate::Result { Ok(format!("{arch}-{os}")) } +#[cfg(not(test))] +fn is_cargo_output_directory(path: &Path) -> bool { + path.join(".cargo-lock").exists() +} + +#[cfg(test)] +const CARGO_OUTPUT_DIRECTORIES: &[&str] = &["debug", "release", "custom-profile"]; + +#[cfg(test)] +fn is_cargo_output_directory(path: &Path) -> bool { + let last_component = path + .components() + .last() + .unwrap() + .as_os_str() + .to_str() + .unwrap(); + CARGO_OUTPUT_DIRECTORIES + .iter() + .any(|dirname| &last_component == dirname) +} + /// Computes the resource directory of the current environment. /// /// On Windows, it's the path to the executable. @@ -233,17 +255,32 @@ pub fn target_triple() -> crate::Result { /// `${exe_dir}/../lib/${exe_name}`. /// /// On MacOS, it's `${exe_dir}../Resources` (inside .app). -#[allow(unused_variables)] pub fn resource_dir(package_info: &PackageInfo, env: &Env) -> crate::Result { let exe = current_exe()?; - let exe_dir = exe.parent().expect("failed to get exe directory"); + resource_dir_from(exe, package_info, env) +} + +#[allow(unused_variables)] +fn resource_dir_from>( + exe: P, + package_info: &PackageInfo, + env: &Env, +) -> crate::Result { + let exe_dir = exe.as_ref().parent().expect("failed to get exe directory"); let curr_dir = exe_dir.display().to_string(); - if curr_dir.ends_with(format!("{MAIN_SEPARATOR}target{MAIN_SEPARATOR}debug").as_str()) - || curr_dir.ends_with(format!("{MAIN_SEPARATOR}target{MAIN_SEPARATOR}release").as_str()) - || cfg!(target_os = "windows") + let parts: Vec<&str> = curr_dir.split(MAIN_SEPARATOR).collect(); + let len = parts.len(); + + // Check if running from the Cargo output directory, which means it's an executable in a development machine + // We check if the binary is inside a `target` folder which can be either `target/$profile` or `target/$triple/$profile` + // and see if there's a .cargo-lock file along the executable + // This ensures the check is safer so it doesn't affect apps in production + // Windows also includes the resources in the executable folder so we check that too + if cfg!(target_os = "windows") + || ((len >= 2 && parts[len - 2] == "target") || (len >= 3 && parts[len - 3] == "target")) + && is_cargo_output_directory(exe_dir) { - // running from the out dir or windows return Ok(exe_dir.to_path_buf()); } @@ -284,3 +321,65 @@ pub fn resource_dir(package_info: &PackageInfo, env: &Env) -> crate::Result quote! { #prefix::MacOS }, + Self::Linux => quote! { #prefix::Linux }, + Self::Windows => quote! { #prefix::Windows }, + Self::Android => quote! { #prefix::Android }, + Self::Ios => quote! { #prefix::Ios }, + }); + } + } +} + +#[cfg(test)] +mod tests { + use std::path::PathBuf; + + use crate::{Env, PackageInfo}; + + #[test] + fn resolve_resource_dir() { + let package_info = PackageInfo { + name: "MyApp".into(), + version: "1.0.0".parse().unwrap(), + authors: "", + description: "", + crate_name: "", + }; + let env = Env::default(); + + let path = PathBuf::from("/path/to/target/aarch64-apple-darwin/debug/app"); + let resource_dir = super::resource_dir_from(&path, &package_info, &env).unwrap(); + assert_eq!(resource_dir, path.parent().unwrap()); + + let path = PathBuf::from("/path/to/target/custom-profile/app"); + let resource_dir = super::resource_dir_from(&path, &package_info, &env).unwrap(); + assert_eq!(resource_dir, path.parent().unwrap()); + + let path = PathBuf::from("/path/to/target/release/app"); + let resource_dir = super::resource_dir_from(&path, &package_info, &env).unwrap(); + assert_eq!(resource_dir, path.parent().unwrap()); + + let path = PathBuf::from("/path/to/target/unknown-profile/app"); + let resource_dir = super::resource_dir_from(&path, &package_info, &env); + #[cfg(target_os = "macos")] + assert!(resource_dir.is_err()); + #[cfg(target_os = "linux")] + assert_eq!(resource_dir.unwrap(), PathBuf::from("/usr/lib/my-app")); + #[cfg(windows)] + assert_eq!(resource_dir.unwrap(), path.parent().unwrap()); + } +} diff --git a/core/tauri-utils/src/platform/starting_binary.rs b/core/tauri-utils/src/platform/starting_binary.rs index 213e76d10..226d2f8d0 100644 --- a/core/tauri-utils/src/platform/starting_binary.rs +++ b/core/tauri-utils/src/platform/starting_binary.rs @@ -1,4 +1,4 @@ -// Copyright 2019-2023 Tauri Programme within The Commons Conservancy +// Copyright 2019-2024 Tauri Programme within The Commons Conservancy // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT @@ -41,6 +41,8 @@ impl StartingBinary { /// /// Because [`Error`] is not clone-able, it is recreated instead. pub(super) fn cloned(&self) -> Result { + // false positive + #[allow(clippy::useless_asref)] self .0 .as_ref() diff --git a/core/tauri-utils/src/plugin.rs b/core/tauri-utils/src/plugin.rs new file mode 100644 index 000000000..4255d0aa2 --- /dev/null +++ b/core/tauri-utils/src/plugin.rs @@ -0,0 +1,51 @@ +// Copyright 2019-2024 Tauri Programme within The Commons Conservancy +// SPDX-License-Identifier: Apache-2.0 +// SPDX-License-Identifier: MIT + +//! Compile-time and runtime types for Tauri plugins. +#[cfg(feature = "build")] +pub use build::*; + +#[cfg(feature = "build")] +mod build { + use std::{ + env::vars_os, + path::{Path, PathBuf}, + }; + + const GLOBAL_API_SCRIPT_PATH_KEY: &str = "GLOBAL_API_SCRIPT_PATH"; + /// Known file name of the file that contains an array with the path of all API scripts defined with [`define_global_api_script_path`]. + pub const GLOBAL_API_SCRIPT_FILE_LIST_PATH: &str = "__global-api-script.js"; + + /// Defines the path to the global API script using Cargo instructions. + pub fn define_global_api_script_path(path: PathBuf) { + println!( + "cargo:{GLOBAL_API_SCRIPT_PATH_KEY}={}", + path + .canonicalize() + .expect("failed to canonicalize global API script path") + .display() + ) + } + + /// Collects the path of all the global API scripts defined with [`define_global_api_script_path`] + /// and saves them to the out dir with filename [`GLOBAL_API_SCRIPT_FILE_LIST_PATH`]. + pub fn load_global_api_scripts(out_dir: &Path) { + let mut scripts = Vec::new(); + + for (key, value) in vars_os() { + let key = key.to_string_lossy(); + + if key.starts_with("DEP_") && key.ends_with(GLOBAL_API_SCRIPT_PATH_KEY) { + let script_path = PathBuf::from(value); + scripts.push(script_path); + } + } + + std::fs::write( + out_dir.join(GLOBAL_API_SCRIPT_FILE_LIST_PATH), + serde_json::to_string(&scripts).expect("failed to serialize global API script paths"), + ) + .expect("failed to write global API script"); + } +} diff --git a/core/tauri-utils/src/resources.rs b/core/tauri-utils/src/resources.rs index fb62a64dc..ccc786826 100644 --- a/core/tauri-utils/src/resources.rs +++ b/core/tauri-utils/src/resources.rs @@ -1,4 +1,4 @@ -// Copyright 2019-2023 Tauri Programme within The Commons Conservancy +// Copyright 2019-2024 Tauri Programme within The Commons Conservancy // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT diff --git a/core/tauri-utils/src/tokens.rs b/core/tauri-utils/src/tokens.rs index 7c0dcaba1..238d8df2f 100644 --- a/core/tauri-utils/src/tokens.rs +++ b/core/tauri-utils/src/tokens.rs @@ -1,4 +1,4 @@ -// Copyright 2019-2023 Tauri Programme within The Commons Conservancy +// Copyright 2019-2024 Tauri Programme within The Commons Conservancy // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT diff --git a/core/tauri/.scripts/loop_qc.sh b/core/tauri/.scripts/loop_qc.sh index 2f17b91e6..13a7c36ff 100644 --- a/core/tauri/.scripts/loop_qc.sh +++ b/core/tauri/.scripts/loop_qc.sh @@ -1,5 +1,5 @@ #!/bin/bash -# Copyright 2019-2023 Tauri Programme within The Commons Conservancy +# Copyright 2019-2024 Tauri Programme within The Commons Conservancy # SPDX-License-Identifier: Apache-2.0 # SPDX-License-Identifier: MIT diff --git a/core/tauri/CHANGELOG.md b/core/tauri/CHANGELOG.md index a1c397e52..c175ba1f9 100644 --- a/core/tauri/CHANGELOG.md +++ b/core/tauri/CHANGELOG.md @@ -1,5 +1,304 @@ # Changelog +## \[2.0.0-beta.14] + +### New Features + +- [`06833f4fa`](https://www.github.com/tauri-apps/tauri/commit/06833f4fa8e63ecc55fe3fc874a9e397e77a5709)([#9100](https://www.github.com/tauri-apps/tauri/pull/9100)) Added `Rect` struct. +- [`06833f4fa`](https://www.github.com/tauri-apps/tauri/commit/06833f4fa8e63ecc55fe3fc874a9e397e77a5709)([#9100](https://www.github.com/tauri-apps/tauri/pull/9100)) Add `Webview::bounds` and `Webview::set_bounds` APIs. + +### Enhancements + +- [`06833f4fa`](https://www.github.com/tauri-apps/tauri/commit/06833f4fa8e63ecc55fe3fc874a9e397e77a5709)([#9100](https://www.github.com/tauri-apps/tauri/pull/9100)) Enhance the IPC URL check by using the Origin header on the custom protocol IPC and the new request URI field on the postMessage IPC instead of using `Webview::url()` which only returns the URL of the main frame and is not suitable for iframes (iframe URL fetch is still not supported on Android and on Linux when using the postMessage IPC). + +### Bug Fixes + +- [`c33f6e6cf`](https://www.github.com/tauri-apps/tauri/commit/c33f6e6cf35a0d34b5598875a2e5b642a01c8b38)([#9211](https://www.github.com/tauri-apps/tauri/pull/9211)) Fixed an issue preventing webview/window creation events to not be emitted. This also fixed the `getByLabel` and `getAll` JavaScript functions. + +### What's Changed + +- [`06833f4fa`](https://www.github.com/tauri-apps/tauri/commit/06833f4fa8e63ecc55fe3fc874a9e397e77a5709)([#9100](https://www.github.com/tauri-apps/tauri/pull/9100)) Updated `http` crate to `1.1` + +### Dependencies + +- Upgraded to `tauri-utils@2.0.0-beta.11` +- Upgraded to `tauri-runtime-wry@2.0.0-beta.11` +- Upgraded to `tauri-runtime@2.0.0-beta.11` +- Upgraded to `tauri-macros@2.0.0-beta.11` +- Upgraded to `tauri-build@2.0.0-beta.11` + +### Breaking Changes + +- [`06833f4fa`](https://www.github.com/tauri-apps/tauri/commit/06833f4fa8e63ecc55fe3fc874a9e397e77a5709)([#9100](https://www.github.com/tauri-apps/tauri/pull/9100)) Rename `FileDrop` to `DragDrop` on structs, enums and enum variants. Also renamed `file_drop` to `drag_drop` on fields and function names. +- [`284eca9ef`](https://www.github.com/tauri-apps/tauri/commit/284eca9ef2396b76ce3df6f32fb3b2d2c40044ad)([#9272](https://www.github.com/tauri-apps/tauri/pull/9272)) `Manager::resources_table` is now scoped so each `App/AppHandle/Window/Webview/WebviewWindow` has its own resource collection. +- [`06833f4fa`](https://www.github.com/tauri-apps/tauri/commit/06833f4fa8e63ecc55fe3fc874a9e397e77a5709)([#9100](https://www.github.com/tauri-apps/tauri/pull/9100)) Refactored the tray icon event struct: + + - Changed `TrayIconEvent.icon_rect` type to use the new `tauri::Rect` type. + - Removed `TrayIconEvent.x` and `TrayIconEvent.y` fields and combined them into `TrayIconEvent.position` field. + - Removed `tauri::tray::Rectangle` struct. + +## \[2.0.0-beta.13] + +### Enhancements + +- [`75f5cb401`](https://www.github.com/tauri-apps/tauri/commit/75f5cb4015f72745161110ad0076cf4945411a6d)([#9214](https://www.github.com/tauri-apps/tauri/pull/9214)) `tauri::Window` and `tauri::WebviewWindow` now implement `raw_window_handle::HasDisplayHandle`. + +### Bug Fixes + +- [`81b853bc8`](https://www.github.com/tauri-apps/tauri/commit/81b853bc875ce2da4e300614ca234f10d54966a6)([#9213](https://www.github.com/tauri-apps/tauri/pull/9213)) Fixed an issue where errors where returned as strings instead of objects from commands. +- [`43230cb6b`](https://www.github.com/tauri-apps/tauri/commit/43230cb6b7a4b14a23ea8f05636ae06f03c718e9)([#9219](https://www.github.com/tauri-apps/tauri/pull/9219)) Fixes the menu plugin `remove` command signature. + +## \[2.0.0-beta.12] + +### New Features + +- [`e227fe02f`](https://www.github.com/tauri-apps/tauri/commit/e227fe02f986e145c0731a64693e1c830a9eb5b0)([#9156](https://www.github.com/tauri-apps/tauri/pull/9156)) Allow plugins to define (at compile time) JavaScript that are initialized when `withGlobalTauri` is true. + +### Enhancements + +- [`79b8a3514`](https://www.github.com/tauri-apps/tauri/commit/79b8a3514baedcd9c35e777d2b6d89a7a086ddec)([#9151](https://www.github.com/tauri-apps/tauri/pull/9151)) Improve and optimize event emit calls. + +### Bug Fixes + +- [`379cc2b35`](https://www.github.com/tauri-apps/tauri/commit/379cc2b3547395474d4b66b4222679cf4538428d)([#9165](https://www.github.com/tauri-apps/tauri/pull/9165)) Fix `basename(path, 'ext')` JS API when removing all occurances of `ext` where it should only remove the last one. + +### Dependencies + +- Upgraded to `tauri-build@2.0.0-beta.10` +- Upgraded to `tauri-utils@2.0.0-beta.10` +- Upgraded to `tauri-runtime@2.0.0-beta.10` +- Upgraded to `tauri-runtime-wry@2.0.0-beta.10` +- Upgraded to `tauri-macros@2.0.0-beta.10` + +### Breaking Changes + +- [`acdd76833`](https://www.github.com/tauri-apps/tauri/commit/acdd76833db6d81f4012418133d0042220de100b)([#9155](https://www.github.com/tauri-apps/tauri/pull/9155)) Removed `App/AppHandle::tray` and `App/AppHandle::remove_tray`, use `App/AppHandle::tray_by_id` and `App/AppHandle::remove_tray_by_id` instead. If these APIs were used to access tray icon configured in `tauri.conf.json`, you can use `App/AppHandle::tray_by_id` with ID `main` or the configured value. +- [`ea0242db4`](https://www.github.com/tauri-apps/tauri/commit/ea0242db4aa6c127d2bb4a2e275000ba47c9e68c)([#9179](https://www.github.com/tauri-apps/tauri/pull/9179)) Removed `width` and `height` methods on the JS `Image` class, use `size` instead. + +## \[2.0.0-beta.11] + +### New Features + +- [`490a6b424`](https://www.github.com/tauri-apps/tauri/commit/490a6b424e81714524150aef96fbf6cf7004b940)([#9147](https://www.github.com/tauri-apps/tauri/pull/9147)) The `Assets` trait now include a `setup` method that lets you run initialization code for your custom asset provider. + +### Bug Fixes + +- [`85de230f3`](https://www.github.com/tauri-apps/tauri/commit/85de230f313da81cbbd061e66e8de64e5b33104c)([#9144](https://www.github.com/tauri-apps/tauri/pull/9144)) Fix old JS listeners being dropped on page load after it was possible to create new listeners. +- [`e673854c8`](https://www.github.com/tauri-apps/tauri/commit/e673854c8333cb8a8d298471737293f17ec5a3ee)([#9133](https://www.github.com/tauri-apps/tauri/pull/9133)) Fixes capability remote domain not allowing subpaths, query parameters and hash when those values are empty. + +### Dependencies + +- Upgraded to `tauri-macros@2.0.0-beta.9` +- Upgraded to `tauri-utils@2.0.0-beta.9` +- Upgraded to `tauri-build@2.0.0-beta.9` +- Upgraded to `tauri-runtime@2.0.0-beta.9` +- Upgraded to `tauri-runtime-wry@2.0.0-beta.9` + +### Breaking Changes + +- [`490a6b424`](https://www.github.com/tauri-apps/tauri/commit/490a6b424e81714524150aef96fbf6cf7004b940)([#9147](https://www.github.com/tauri-apps/tauri/pull/9147)) The `Context` struct and the `Assets` trait now takes a `R: Runtime` generic. +- [`ba0206d8a`](https://www.github.com/tauri-apps/tauri/commit/ba0206d8a30a9b43ec5090dcaabd1a23baa1420c)([#9141](https://www.github.com/tauri-apps/tauri/pull/9141)) `Context::assets` now returns `&dyn Assets` instead of `&A` generic. +- [`ba0206d8a`](https://www.github.com/tauri-apps/tauri/commit/ba0206d8a30a9b43ec5090dcaabd1a23baa1420c)([#9141](https://www.github.com/tauri-apps/tauri/pull/9141)) The `Context` type no longer uses the `` generic so the assets implementation can be swapped with `Context::assets_mut`. +- [`490a6b424`](https://www.github.com/tauri-apps/tauri/commit/490a6b424e81714524150aef96fbf6cf7004b940)([#9147](https://www.github.com/tauri-apps/tauri/pull/9147)) Removed `Context::assets_mut` and added `Context::set_assets`. +- [`db0a24a97`](https://www.github.com/tauri-apps/tauri/commit/db0a24a973191752aeecfbd556faa254b0f17e79)([#9132](https://www.github.com/tauri-apps/tauri/pull/9132)) Use the image crate for `tauri::image::Image` and remove the `from_png_bytes` and `from_ico_bytes` APIs. + +## \[2.0.0-beta.10] + +### New Features + +- [`3e472d0af`](https://www.github.com/tauri-apps/tauri/commit/3e472d0afcd67545dd6d9f18d304580a3b2759a8)([#9115](https://www.github.com/tauri-apps/tauri/pull/9115)) Added `CapabilityBuilder::platform` to link the runtime capability with a specific platform. + +### Enhancements + +- [`3e472d0af`](https://www.github.com/tauri-apps/tauri/commit/3e472d0afcd67545dd6d9f18d304580a3b2759a8)([#9115](https://www.github.com/tauri-apps/tauri/pull/9115)) Changed the permission and capability platforms to be optional. +- [`9dc9ca6e3`](https://www.github.com/tauri-apps/tauri/commit/9dc9ca6e38be62ef2746c7a4c2b77b2d67c0d998)([#9113](https://www.github.com/tauri-apps/tauri/pull/9113)) Added `tauri::dev()` to determine whether we are running in development mode or not. + +### Bug Fixes + +- [`5541aafef`](https://www.github.com/tauri-apps/tauri/commit/5541aafef33113bc292558ba125e685135aabab4)([#9107](https://www.github.com/tauri-apps/tauri/pull/9107)) Fix `emit` and `emit_to` (when used with `EventTarget::Any`) always skipping the webview listeners. +- [`80c12ead4`](https://www.github.com/tauri-apps/tauri/commit/80c12ead4655af91f08046f19c2d478a4cbf94cd)([#9121](https://www.github.com/tauri-apps/tauri/pull/9121)) Fix regression on IPC response when using a channel to return objects. + +### Dependencies + +- Upgraded to `tauri-utils@2.0.0-beta.8` +- Upgraded to `tauri-runtime@2.0.0-beta.8` +- Upgraded to `tauri-runtime-wry@2.0.0-beta.8` +- Upgraded to `tauri-macros@2.0.0-beta.8` +- Upgraded to `tauri-build@2.0.0-beta.8` + +### Breaking Changes + +- [`4ef17d083`](https://www.github.com/tauri-apps/tauri/commit/4ef17d08336a2e0df4a7ef9adea746d7419710b6)([#9116](https://www.github.com/tauri-apps/tauri/pull/9116)) The ACL configuration for remote URLs now uses the URLPattern standard instead of glob patterns. +- [`ed48e2b3c`](https://www.github.com/tauri-apps/tauri/commit/ed48e2b3c7fa914e4c9af432c02b8154f872c68a)([#9122](https://www.github.com/tauri-apps/tauri/pull/9122)) Expose `tauri::image` module to export the `JsImage` type and removed the `Image` root re-export. + +## \[2.0.0-beta.9] + +### New Features + +- [`46de49aaa`](https://www.github.com/tauri-apps/tauri/commit/46de49aaad4a148fafc31d591be0e2ed12256507)([#9059](https://www.github.com/tauri-apps/tauri/pull/9059)) Added `set_auto_resize` method for the webview. +- [`d1e77acd8`](https://www.github.com/tauri-apps/tauri/commit/d1e77acd8dfdf554b90b542513a58a2de1ef2360)([#9011](https://www.github.com/tauri-apps/tauri/pull/9011)) Add a new `Image` type in Rust and JS. + +### Enhancements + +- [`a77be9747`](https://www.github.com/tauri-apps/tauri/commit/a77be9747443ffc29c34160b55893483bb5f0d74)([#9038](https://www.github.com/tauri-apps/tauri/pull/9038)) Fallback to the postMessage IPC interface if we cannot reach the IPC custom protocol. +- [`e62ca4ee9`](https://www.github.com/tauri-apps/tauri/commit/e62ca4ee95f4308a6ad128d0f100c85634e28223)([#9070](https://www.github.com/tauri-apps/tauri/pull/9070)) Added a mechanism to preserve channel message order. +- [`03098b531`](https://www.github.com/tauri-apps/tauri/commit/03098b531562e4d58ab12ad9da2acb1eb3480497)([#9036](https://www.github.com/tauri-apps/tauri/pull/9036)) `Manager::add_capability` now allows adding a dynamically defined capability instead of only relying on static strings. +- [`b5c743276`](https://www.github.com/tauri-apps/tauri/commit/b5c7432769b84ffe22db721dcfc6af218026f5d4)([#9086](https://www.github.com/tauri-apps/tauri/pull/9086)) Use a strict content security policy on the isolation pattern iframe. +- [`46de49aaa`](https://www.github.com/tauri-apps/tauri/commit/46de49aaad4a148fafc31d591be0e2ed12256507)([#9059](https://www.github.com/tauri-apps/tauri/pull/9059)) When using the `unstable` feature flag, `WebviewWindow` will internally use the child webview interface for flexibility. + +### Bug Fixes + +- [`86fa339de`](https://www.github.com/tauri-apps/tauri/commit/86fa339de7b176efafa9b3e89f94dcad5fcd03da)([#9071](https://www.github.com/tauri-apps/tauri/pull/9071)) Fix compile time error in context generation when using `app.windows.windowEffects.color` +- [`947a50b8e`](https://www.github.com/tauri-apps/tauri/commit/947a50b8e28379c452c32eddc3e0101870e50055)([#9049](https://www.github.com/tauri-apps/tauri/pull/9049)) Fix `tauri migrate` for http plugin ACL. +- [`fe18012d3`](https://www.github.com/tauri-apps/tauri/commit/fe18012d30d1d8b3ffa10c8e321710eba644ef94)([#9072](https://www.github.com/tauri-apps/tauri/pull/9072)) Resolve symlinks on the filesystem scope check. +- [`6c0683224`](https://www.github.com/tauri-apps/tauri/commit/6c068322460300e9d56a4fac5b018d4c437daa9e)([#9068](https://www.github.com/tauri-apps/tauri/pull/9068)) Fixes scope resolution grouping scopes for all windows. + +### Dependencies + +- Upgraded to `tauri-build@2.0.0-beta.7` +- Upgraded to `tauri-utils@2.0.0-beta.7` +- Upgraded to `tauri-runtime@2.0.0-beta.7` +- Upgraded to `tauri-runtime-wry@2.0.0-beta.7` +- Upgraded to `tauri-macros@2.0.0-beta.7` + +### Breaking Changes + +- [`d1e77acd8`](https://www.github.com/tauri-apps/tauri/commit/d1e77acd8dfdf554b90b542513a58a2de1ef2360)([#9011](https://www.github.com/tauri-apps/tauri/pull/9011)) Renamed `icon-ico` and `icon-png` feature flags to `image-ico` and `image-png` respectively +- [`720357fd5`](https://www.github.com/tauri-apps/tauri/commit/720357fd5cd1fefef8485077dfb116ee39ef4ab4)([#9104](https://www.github.com/tauri-apps/tauri/pull/9104)) Removed `tauri::path::Result` and `tauri::path::Error` which were merely an unintentional re-export of `tauri::Result` and `tauri::Error` so use those instead. +- [`6c0683224`](https://www.github.com/tauri-apps/tauri/commit/6c068322460300e9d56a4fac5b018d4c437daa9e)([#9068](https://www.github.com/tauri-apps/tauri/pull/9068)) The `allows` and `denies` methods from `ipc::ScopeValue`, `ipc::CommandScope` and `ipc::GlobalScope` now returns `&Vec>` instead of `&Vec`. +- [`d1e77acd8`](https://www.github.com/tauri-apps/tauri/commit/d1e77acd8dfdf554b90b542513a58a2de1ef2360)([#9011](https://www.github.com/tauri-apps/tauri/pull/9011)) Removed `Context::default_window_icon_mut` and `Context::tray_icon_mut`, use `Context::set_default_window_icon` and `Context::set_tray_icon` instead. Also changed `Context::set_tray_icon` to accept `Option`. +- [`d1e77acd8`](https://www.github.com/tauri-apps/tauri/commit/d1e77acd8dfdf554b90b542513a58a2de1ef2360)([#9011](https://www.github.com/tauri-apps/tauri/pull/9011)) Removed `Icon` enum, use the new `Image` type instead. All APIs that previously accepted `Icon` have changed to accept `Image` instead. + +## \[2.0.0-beta.8] + +### New Features + +- [`d7f56fef`](https://www.github.com/tauri-apps/tauri/commit/d7f56fef85cac3af4e2dbac1eac40e5567b1f160)([#9014](https://www.github.com/tauri-apps/tauri/pull/9014)) Allow defining a permission that only applies to a set of target platforms via the `platforms` configuration option. + +### Bug Fixes + +- [`e1d5b790`](https://www.github.com/tauri-apps/tauri/commit/e1d5b7906369a40df19e8ee86c56f90a27d6357c)([#8995](https://www.github.com/tauri-apps/tauri/pull/8995)) Fixes capability webview label check. +- [`222a96b7`](https://www.github.com/tauri-apps/tauri/commit/222a96b74b145fb48d3f0c109897962d56fae57a)([#8999](https://www.github.com/tauri-apps/tauri/pull/8999)) Fixes `Window::add_child` deadlock. +- [`e4463f08`](https://www.github.com/tauri-apps/tauri/commit/e4463f08145c044bd37dc1c6f5f39e6a572ace3e)([#8930](https://www.github.com/tauri-apps/tauri/pull/8930)) Clear JS event listeneres on page load, which fixes zombie listeners when the page reloads. +- [`222a96b7`](https://www.github.com/tauri-apps/tauri/commit/222a96b74b145fb48d3f0c109897962d56fae57a)([#8999](https://www.github.com/tauri-apps/tauri/pull/8999)) Fixes `Webview::reparent` not updating the webview parent window reference. + +### Dependencies + +- Upgraded to `tauri-build@2.0.0-beta.6` +- Upgraded to `tauri-utils@2.0.0-beta.6` +- Upgraded to `tauri-runtime-wry@2.0.0-beta.6` +- Upgraded to `tauri-runtime@2.0.0-beta.6` +- Upgraded to `tauri-macros@2.0.0-beta.6` + +### Breaking Changes + +- [`3657ad82`](https://www.github.com/tauri-apps/tauri/commit/3657ad82f88ce528551d032d521c52eed3f396b4)([#9008](https://www.github.com/tauri-apps/tauri/pull/9008)) Allow defining permissions for the application commands via `tauri_build::Attributes::app_manifest`. + +### Breaking Changes + +- [`b9e6a018`](https://www.github.com/tauri-apps/tauri/commit/b9e6a01879d9233040f3d3fab11c59e70563da7e)([#8937](https://www.github.com/tauri-apps/tauri/pull/8937)) The `custom-protocol` Cargo feature is no longer required on your application and is now ignored. To check if running on production, use `#[cfg(not(dev))]` instead of `#[cfg(feature = "custom-protocol")]`. + +## \[2.0.0-beta.7] + +### Enhancements + +- [`bc5b5e67`](https://www.github.com/tauri-apps/tauri/commit/bc5b5e671a546512f823f1c157421b4c3311dfc0)([#8984](https://www.github.com/tauri-apps/tauri/pull/8984)) Do not include a CSP tag in the application HTML and rely on the custom protocol response header instead. + +### Bug Fixes + +- [`6cb601d4`](https://www.github.com/tauri-apps/tauri/commit/6cb601d42e2af75aa818371b8b8f7d5b2e77dc90)([#8983](https://www.github.com/tauri-apps/tauri/pull/8983)) Convert the command name to camelCase when executing a mobile plugin command. +- [`60bf11ab`](https://www.github.com/tauri-apps/tauri/commit/60bf11abcbec8d0362aa256e2293985bfd62620f)([#8986](https://www.github.com/tauri-apps/tauri/pull/8986)) Export `ProgressBarStatus`, regression introduced in `2.0.0-beta.4` + +### Dependencies + +- Upgraded to `tauri-utils@2.0.0-beta.5` +- Upgraded to `tauri-runtime@2.0.0-beta.5` +- Upgraded to `tauri-runtime-wry@2.0.0-beta.5` +- Upgraded to `tauri-macros@2.0.0-beta.5` +- Upgraded to `tauri-build@2.0.0-beta.5` + +## \[2.0.0-beta.6] + +### Bug Fixes + +- [`6edc563c`](https://www.github.com/tauri-apps/tauri/commit/6edc563cf9ca26b4622c3135d92e493a5d5bd6e8)([#8953](https://www.github.com/tauri-apps/tauri/pull/8953)) Fixes a deadlock when the window is destroyed. + +## \[2.0.0-beta.5] + +### New Features + +- [`fdcaf935`](https://www.github.com/tauri-apps/tauri/commit/fdcaf935fa75ecfa2806939c4faad4fe9e880386)([#8939](https://www.github.com/tauri-apps/tauri/pull/8939)) Added the `reparent` function to the webview API. + +### Dependencies + +- Upgraded to `tauri-utils@2.0.0-beta.4` +- Upgraded to `tauri-runtime-wry@2.0.0-beta.4` +- Upgraded to `tauri-build@2.0.0-beta.4` +- Upgraded to `tauri-runtime@2.0.0-beta.4` +- Upgraded to `tauri-macros@2.0.0-beta.4` + +## \[2.0.0-beta.4] + +### Enhancements + +- [`3fb414b6`](https://www.github.com/tauri-apps/tauri/commit/3fb414b61ad7cfce67751230826fddfb39effec5)([#8914](https://www.github.com/tauri-apps/tauri/pull/8914)) Return an id when using from `Manager::once_any`, `App::once`, `Window::once`, `Webview::once`, `WebviewWindow::once` and `fs::Scope::once`. + +### Dependencies + +- Upgraded to `tauri-utils@2.0.0-beta.3` +- Upgraded to `tauri-runtime@2.0.0-beta.3` +- Upgraded to `tauri-runtime-wry@2.0.0-beta.3` +- Upgraded to `tauri-macros@2.0.0-beta.3` +- Upgraded to `tauri-build@2.0.0-beta.3` + +### Breaking Changes + +- [`361ec37f`](https://www.github.com/tauri-apps/tauri/commit/361ec37fd4a5caa5b6630b9563ef079f53c6c336)([#8932](https://www.github.com/tauri-apps/tauri/pull/8932)) Moved `ProgressBarState` from `tauri-utils` to the `tauri::window` module and removed the `unity_uri` field. + +## \[2.0.0-beta.3] + +### New Features + +- [`16e550ec`](https://www.github.com/tauri-apps/tauri/commit/16e550ec1503765158cdc3bb2a20e70ec710e981)([#8844](https://www.github.com/tauri-apps/tauri/pull/8844)) Add webview-specific events for multi-webview windows: + + - Add `WebviewEvent` enum + - Add `RunEvent::WebviewEvent` variant. + - Add `Builder::on_webview_event` and `Webview::on_webview_event` methods. + +### Enhancements + +- [`11a5816b`](https://www.github.com/tauri-apps/tauri/commit/11a5816bdffcbaa20df936dee43751de2cf67530)([#8864](https://www.github.com/tauri-apps/tauri/pull/8864)) A file-drop now allows sub-directories recursively when the path is a directory. +- [`0cb0a15c`](https://www.github.com/tauri-apps/tauri/commit/0cb0a15ce22af3d649cf219ac04188c14c5f4905)([#8789](https://www.github.com/tauri-apps/tauri/pull/8789)) Add `webviews` array on the capability for usage on multiwebview contexts. +- [`258494bd`](https://www.github.com/tauri-apps/tauri/commit/258494bd247b6d36485bf16bf7184b93fd299da9)([#8806](https://www.github.com/tauri-apps/tauri/pull/8806)) Added `Manager::add_capability` to add a capability file at runtime. +- [`5618f6d2`](https://www.github.com/tauri-apps/tauri/commit/5618f6d2ffc9ebf40710145538b06bebfa55f878)([#8856](https://www.github.com/tauri-apps/tauri/pull/8856)) Relax requirements on plugin's identifiers to be alphanumeric and `-` instead of only lower alpha and `-`. + +### Bug Fixes + +- [`16e550ec`](https://www.github.com/tauri-apps/tauri/commit/16e550ec1503765158cdc3bb2a20e70ec710e981)([#8844](https://www.github.com/tauri-apps/tauri/pull/8844)) Fix JS event listeners registered using JS `listen` api with `EventTarget::Any` never fired. +- [`8751c329`](https://www.github.com/tauri-apps/tauri/commit/8751c3299f2b7229c6108aa37dedf1dc5edb3e5c)([#8793](https://www.github.com/tauri-apps/tauri/pull/8793)) Fix invoking toggle devtools by hotkey. +- [`bd73ab0a`](https://www.github.com/tauri-apps/tauri/commit/bd73ab0a1adcf648e38d579b92515dababf34993)([#8766](https://www.github.com/tauri-apps/tauri/pull/8766)) When using the multiwebview mode, properly remove the webview from memory on `Webview::close`. +- [`46b6598a`](https://www.github.com/tauri-apps/tauri/commit/46b6598a94cd0c6fa4a1654ac67432d94ea20ebf)([#8826](https://www.github.com/tauri-apps/tauri/pull/8826)) Fix JS `onCloseRequested` catching close event from other windows. +- [`2e6db908`](https://www.github.com/tauri-apps/tauri/commit/2e6db908d7b3a2c928c46b0ad9ccf9ec55a29480)([#8777](https://www.github.com/tauri-apps/tauri/pull/8777)) Fix regression in `tauri::Error` not being `Sync`. + +### What's Changed + +- [`76ce9f61`](https://www.github.com/tauri-apps/tauri/commit/76ce9f61dd3c5bdd589c7557543894e1f770dd16)([#3002](https://www.github.com/tauri-apps/tauri/pull/3002)) Enhance centering a newly created window, it will no longer jump to center after being visible. + +### Dependencies + +- Upgraded to `tauri-utils@2.0.0-beta.2` +- Upgraded to `tauri-build@2.0.0-beta.2` +- Upgraded to `tauri-macros@2.0.0-beta.2` +- Upgraded to `tauri-runtime-wry@2.0.0-beta.2` +- Upgraded to `tauri-runtime@2.0.0-beta.2` + +### Breaking Changes + +- [`258494bd`](https://www.github.com/tauri-apps/tauri/commit/258494bd247b6d36485bf16bf7184b93fd299da9)([#8806](https://www.github.com/tauri-apps/tauri/pull/8806)) Removed the lifetime parameter from `ipc::GlobalScope` and `ipc::CommandScope`. +- [`f284f9c5`](https://www.github.com/tauri-apps/tauri/commit/f284f9c545deeb77d15b6e8b1d0d05f49c40634c)([#8898](https://www.github.com/tauri-apps/tauri/pull/8898)) Changed the capability `remote` configuration to take a list of `urls` instead of `domains` for more flexibility. +- [`2f55bfec`](https://www.github.com/tauri-apps/tauri/commit/2f55bfecbf0244f3b5aa1ad7622182fca3fcdcbb)([#8795](https://www.github.com/tauri-apps/tauri/pull/8795)) Update raw-window-handle to 0.6. +- [`2e6db908`](https://www.github.com/tauri-apps/tauri/commit/2e6db908d7b3a2c928c46b0ad9ccf9ec55a29480)([#8777](https://www.github.com/tauri-apps/tauri/pull/8777)) Require `ScopeObject::Error` to be `Sync` as well. + ## \[2.0.0-beta.2] ### Bug Fixes @@ -104,10 +403,6 @@ - [`d621d343`](https://www.github.com/tauri-apps/tauri/commit/d621d3437ce3947175eecf345b2c6d1c4c7ce020)([#8607](https://www.github.com/tauri-apps/tauri/pull/8607)) Added tracing for window startup, plugins, `Window::eval`, events, IPC, updater and custom protocol request handlers behind the `tracing` feature flag. -### Bug Fixes - -- [`50a3d170`](https://www.github.com/tauri-apps/tauri/commit/50a3d170f242178d41fe7e8a3adf964541f6fe9c)([#8408](https://www.github.com/tauri-apps/tauri/pull/8408)) On Windows, fix `open` dialog `defaultPath`, when invoked from JS, not working if the path uses forward slash (`/`) - ### What's Changed - [`cb640c8e`](https://www.github.com/tauri-apps/tauri/commit/cb640c8e949a3d78d78162e2e61b51bf8afae983)([#8393](https://www.github.com/tauri-apps/tauri/pull/8393)) Fix `RunEvent::WindowEvent(event: WindowEvent::FileDrop(FileDropEvent))` never triggered and always prevent default OS behavior when `disable_file_drop_handler` is not used. @@ -543,6 +838,44 @@ - Export types required by the `mobile_entry_point` macro. - [98904863](https://www.github.com/tauri-apps/tauri/commit/9890486321c9c79ccfb7c547fafee85b5c3ffa71) feat(core): add `mobile_entry_point` macro ([#4983](https://www.github.com/tauri-apps/tauri/pull/4983)) on 2022-08-21 +## \[1.6.0] + +### New Features + +- [`6e488378`](https://www.github.com/tauri-apps/tauri/commit/6e48837860203582d2ef8e59d4524f98511a14c0)([#8474](https://www.github.com/tauri-apps/tauri/pull/8474)) Re-export `Url` type. + +### Enhancements + +- [`8ce51cec`](https://www.github.com/tauri-apps/tauri/commit/8ce51cec3baf4ed88d80c59bf3bbe96fd369c7a0)([#7718](https://www.github.com/tauri-apps/tauri/pull/7718)) On Windows, retain command line args when relaunching the app after an update. Supports NSIS and WiX (without elevated update task). + +### Bug Fixes + +- [`cc3d8e77`](https://www.github.com/tauri-apps/tauri/commit/cc3d8e77313672f25520e278bbe8fae1b275a735)([#8539](https://www.github.com/tauri-apps/tauri/pull/8539)) Fixes a deadlock when reading a stdout or stderr line returns an error. +- [`b546b42d`](https://www.github.com/tauri-apps/tauri/commit/b546b42db7e75a59232367dd6212fe3b75bb4c6d)([#8577](https://www.github.com/tauri-apps/tauri/pull/8577)) Preserve the order of JS object/map keys in IPC calls. This also fixes issues with the JS `http` module when calling to servers that required a specific order of `FormBody` contents. +- [`8f8729d9`](https://www.github.com/tauri-apps/tauri/commit/8f8729d91843acd2bd2a24731db865d690dd9ab1)([#8312](https://www.github.com/tauri-apps/tauri/pull/8312)) On macOS, allow cancelling maximization when doubleclick happens on `data-tauri-drag-region` by simply keeping the left moust button pressed and then moving the mouse away of the starting position of the click, which is consistent with the native behavior of macOS. + +### Dependencies + +- Upgraded to `tauri-runtime-wry@0.14.4` + +## \[1.5.4] + +### Enhancements + +- [`3c371aa8`](https://www.github.com/tauri-apps/tauri/commit/3c371aa8ee4032998f859b570702e81e26e77c6c)([#8228](https://www.github.com/tauri-apps/tauri/pull/8228)) Added `test::get_ipc_response`. + +### Bug Fixes + +- [`50a3d170`](https://www.github.com/tauri-apps/tauri/commit/50a3d170f242178d41fe7e8a3adf964541f6fe9c)([#8408](https://www.github.com/tauri-apps/tauri/pull/8408)) On Windows, fix `open` dialog `defaultPath`, when invoked from JS, not working if the path uses forward slash (`/`) +- [`645e1dcc`](https://www.github.com/tauri-apps/tauri/commit/645e1dcc6e113564e2ddaacf9cb8338aed1a0bd0)([#8404](https://www.github.com/tauri-apps/tauri/pull/8404)) Fix NSIS updater failing to launch when using `basicUi` mode. + +### Dependencies + +- Upgraded to `tauri-runtime-wry@0.14.3` +- Upgraded to `tauri-utils@1.5.2` +- Upgraded to `tauri-runtime@0.14.2` +- Upgraded to `tauri-macros@1.4.3` + ## \[1.5.3] ### Enhancements diff --git a/core/tauri/Cargo.toml b/core/tauri/Cargo.toml index 9c395aa2a..52cb69bbc 100644 --- a/core/tauri/Cargo.toml +++ b/core/tauri/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "tauri" -version = "2.0.0-beta.2" +version = "2.0.0-beta.14" description = "Make tiny, secure apps for all desktop platforms with Tauri" exclude = [ "/test", "/.scripts", "CHANGELOG.md", "/target" ] readme = "README.md" @@ -21,7 +21,7 @@ features = [ "custom-protocol", "tray-icon", "devtools", - "icon-png", + "image-png", "protocol-asset", "test" ] @@ -47,36 +47,36 @@ serde = { version = "1.0", features = [ "derive", "rc" ] } tokio = { version = "1", features = [ "rt", "rt-multi-thread", "sync", "fs", "io-util" ] } futures-util = "0.3" uuid = { version = "1", features = [ "v4" ], optional = true } -url = { version = "2.4" } +url = "2" anyhow = "1.0" thiserror = "1.0" -tauri-runtime = { version = "2.0.0-beta.1", path = "../tauri-runtime" } -tauri-macros = { version = "2.0.0-beta.1", path = "../tauri-macros" } -tauri-utils = { version = "2.0.0-beta.1", features = [ "resources" ], path = "../tauri-utils" } -tauri-runtime-wry = { version = "2.0.0-beta.1", path = "../tauri-runtime-wry", optional = true } +tauri-runtime = { version = "2.0.0-beta.11", path = "../tauri-runtime" } +tauri-macros = { version = "2.0.0-beta.11", path = "../tauri-macros" } +tauri-utils = { version = "2.0.0-beta.11", features = [ "resources" ], path = "../tauri-utils" } +tauri-runtime-wry = { version = "2.0.0-beta.11", path = "../tauri-runtime-wry", optional = true } getrandom = "0.2" serde_repr = "0.1" state = "0.6" -http = "0.2" +http = "1.1" dirs-next = "2.0" percent-encoding = "2.3" -reqwest = { version = "0.11", default-features = false, features = [ "json", "stream" ] } +reqwest = { version = "0.12", default-features = false, features = [ "json", "stream" ] } bytes = { version = "1", features = [ "serde" ] } raw-window-handle = "0.6" glob = "0.3" +urlpattern = "0.2" mime = "0.3" data-url = { version = "0.3", optional = true } serialize-to-javascript = "=0.1.1" -infer = { version = "0.15", optional = true } -png = { version = "0.17", optional = true } -ico = { version = "0.3.0", optional = true } +image = { version = "0.24", default-features = false, optional = true } http-range = { version = "0.1.5", optional = true } tracing = { version = "0.1", optional = true } -static_assertions = "1" +heck = "0.4" +log = "0.4" [target."cfg(any(target_os = \"linux\", target_os = \"dragonfly\", target_os = \"freebsd\", target_os = \"openbsd\", target_os = \"netbsd\", target_os = \"windows\", target_os = \"macos\"))".dependencies] -muda = { version = "0.11", default-features = false, features = [ "serde" ] } -tray-icon = { version = "0.11", default-features = false, features = [ "serde" ], optional = true } +muda = { version = "0.13", default-features = false, features = [ "serde" ] } +tray-icon = { version = "0.13", default-features = false, features = [ "serde" ], optional = true } [target."cfg(any(target_os = \"linux\", target_os = \"dragonfly\", target_os = \"freebsd\", target_os = \"openbsd\", target_os = \"netbsd\"))".dependencies] gtk = { version = "0.18", features = [ "v3_24" ] } @@ -89,17 +89,13 @@ objc = "0.2" window-vibrancy = "0.5" [target."cfg(windows)".dependencies] -webview2-com = "0.28" +webview2-com = "0.29" window-vibrancy = "0.5" [target."cfg(windows)".dependencies.windows] - version = "0.52" + version = "0.54" features = [ "Win32_Foundation" ] -[target."cfg(any(target_os = \"android\", target_os = \"ios\"))".dependencies] -log = "0.4" -heck = "0.4" - [target."cfg(target_os = \"android\")".dependencies] jni = "0.21" @@ -111,8 +107,8 @@ swift-rs = "1.0.6" [build-dependencies] heck = "0.4" -tauri-build = { path = "../tauri-build/", default-features = false, version = "2.0.0-beta.1" } -tauri-utils = { path = "../tauri-utils/", version = "2.0.0-beta.1", features = [ "build" ] } +tauri-build = { path = "../tauri-build/", default-features = false, version = "2.0.0-beta.11" } +tauri-utils = { path = "../tauri-utils/", version = "2.0.0-beta.11", features = [ "build" ] } [dev-dependencies] proptest = "1.4.0" @@ -127,7 +123,7 @@ http-range = "0.1.5" [features] default = [ "wry", "compression", "objc-exception", "common-controls-v6" ] -unstable = [ ] +unstable = [ "tauri-runtime-wry/unstable" ] common-controls-v6 = [ "tray-icon?/common-controls-v6", "muda/common-controls-v6" ] tray-icon = [ "dep:tray-icon" ] tracing = [ @@ -156,8 +152,8 @@ webview-data-url = [ "data-url" ] protocol-asset = [ "http-range" ] config-json5 = [ "tauri-macros/config-json5" ] config-toml = [ "tauri-macros/config-toml" ] -icon-ico = [ "infer", "ico" ] -icon-png = [ "infer", "png" ] +image-ico = [ "image/ico" ] +image-png = [ "image/png" ] macos-proxy = [ "tauri-runtime-wry/macos-proxy" ] [[example]] diff --git a/core/tauri/build.rs b/core/tauri/build.rs index ea85b4148..f741f09b0 100644 --- a/core/tauri/build.rs +++ b/core/tauri/build.rs @@ -1,4 +1,4 @@ -// Copyright 2019-2023 Tauri Programme within The Commons Conservancy +// Copyright 2019-2024 Tauri Programme within The Commons Conservancy // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT @@ -106,8 +106,6 @@ const PLUGINS: &[(&str, &[(&str, bool)])] = &[ ("toggle_maximize", false), // internal ("internal_toggle_maximize", true), - ("internal_on_mousemove", true), - ("internal_on_mousedown", true), ], ), ( @@ -123,7 +121,9 @@ const PLUGINS: &[(&str, &[(&str, bool)])] = &[ ("set_webview_size", false), ("set_webview_position", false), ("set_webview_focus", false), + ("set_webview_zoom", false), ("print", false), + ("reparent", false), // internal ("internal_toggle_devtools", true), ], @@ -138,6 +138,16 @@ const PLUGINS: &[(&str, &[(&str, bool)])] = &[ ("app_hide", false), ], ), + ( + "image", + &[ + ("new", true), + ("from_bytes", true), + ("from_path", true), + ("rgba", true), + ("size", true), + ], + ), ("resources", &[("close", true)]), ( "menu", @@ -170,6 +180,8 @@ const PLUGINS: &[(&str, &[(&str, bool)])] = &[ "tray", &[ ("new", false), + ("get_by_id", false), + ("remove_by_id", false), ("set_icon", false), ("set_menu", false), ("set_tooltip", false), @@ -206,19 +218,18 @@ fn alias(alias: &str, has_feature: bool) { } fn main() { - alias("custom_protocol", has_feature("custom-protocol")); - alias("dev", !has_feature("custom-protocol")); + let custom_protocol = has_feature("custom-protocol"); + let dev = !custom_protocol; + alias("custom_protocol", custom_protocol); + alias("dev", dev); + + println!("cargo:dev={}", dev); let target_os = std::env::var("CARGO_CFG_TARGET_OS").unwrap(); let mobile = target_os == "ios" || target_os == "android"; alias("desktop", !mobile); alias("mobile", mobile); - alias( - "ipc_custom_protocol", - target_os != "android" && (target_os != "linux" || has_feature("linux-ipc-protocol")), - ); - let out_dir = PathBuf::from(var("OUT_DIR").unwrap()); let checked_features_out_path = out_dir.join("checked_features"); @@ -307,7 +318,7 @@ fn main() { } fn define_permissions(out_dir: &Path) { - let license_header = r#"# Copyright 2019-2023 Tauri Programme within The Commons Conservancy + let license_header = r#"# Copyright 2019-2024 Tauri Programme within The Commons Conservancy # SPDX-License-Identifier: Apache-2.0 # SPDX-License-Identifier: MIT "#; @@ -322,6 +333,7 @@ fn define_permissions(out_dir: &Path) { &commands_dir, &commands.iter().map(|(cmd, _)| *cmd).collect::>(), license_header, + false, ); let default_permissions = commands .iter() @@ -355,6 +367,7 @@ permissions = [{default_permissions}] .to_string_lossy(), &format!("tauri:{plugin}"), out_dir, + |_| true, ) .unwrap_or_else(|e| panic!("failed to define permissions for {plugin}: {e}")); diff --git a/core/tauri/mobile/android-codegen/TauriActivity.kt b/core/tauri/mobile/android-codegen/TauriActivity.kt index 1e93240b3..1b3fb7b0f 100644 --- a/core/tauri/mobile/android-codegen/TauriActivity.kt +++ b/core/tauri/mobile/android-codegen/TauriActivity.kt @@ -1,4 +1,4 @@ -// Copyright 2019-2023 Tauri Programme within The Commons Conservancy +// Copyright 2019-2024 Tauri Programme within The Commons Conservancy // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT diff --git a/core/tauri/mobile/android/src/androidTest/java/app/tauri/ExampleInstrumentedTest.kt b/core/tauri/mobile/android/src/androidTest/java/app/tauri/ExampleInstrumentedTest.kt index 791991038..77ce904cf 100644 --- a/core/tauri/mobile/android/src/androidTest/java/app/tauri/ExampleInstrumentedTest.kt +++ b/core/tauri/mobile/android/src/androidTest/java/app/tauri/ExampleInstrumentedTest.kt @@ -1,4 +1,4 @@ -// Copyright 2019-2023 Tauri Programme within The Commons Conservancy +// Copyright 2019-2024 Tauri Programme within The Commons Conservancy // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT diff --git a/core/tauri/mobile/android/src/main/java/app/tauri/FsUtils.kt b/core/tauri/mobile/android/src/main/java/app/tauri/FsUtils.kt index 43d09a252..235775cb1 100644 --- a/core/tauri/mobile/android/src/main/java/app/tauri/FsUtils.kt +++ b/core/tauri/mobile/android/src/main/java/app/tauri/FsUtils.kt @@ -1,4 +1,4 @@ -// Copyright 2019-2023 Tauri Programme within The Commons Conservancy +// Copyright 2019-2024 Tauri Programme within The Commons Conservancy // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT diff --git a/core/tauri/mobile/android/src/main/java/app/tauri/JniMethod.kt b/core/tauri/mobile/android/src/main/java/app/tauri/JniMethod.kt index d12778b5f..41301d407 100644 --- a/core/tauri/mobile/android/src/main/java/app/tauri/JniMethod.kt +++ b/core/tauri/mobile/android/src/main/java/app/tauri/JniMethod.kt @@ -1,4 +1,4 @@ -// Copyright 2019-2023 Tauri Programme within The Commons Conservancy +// Copyright 2019-2024 Tauri Programme within The Commons Conservancy // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT diff --git a/core/tauri/mobile/android/src/main/java/app/tauri/Logger.kt b/core/tauri/mobile/android/src/main/java/app/tauri/Logger.kt index a4789dd50..c0473788b 100644 --- a/core/tauri/mobile/android/src/main/java/app/tauri/Logger.kt +++ b/core/tauri/mobile/android/src/main/java/app/tauri/Logger.kt @@ -1,4 +1,4 @@ -// Copyright 2019-2023 Tauri Programme within The Commons Conservancy +// Copyright 2019-2024 Tauri Programme within The Commons Conservancy // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT diff --git a/core/tauri/mobile/android/src/main/java/app/tauri/PathPlugin.kt b/core/tauri/mobile/android/src/main/java/app/tauri/PathPlugin.kt index 65147db5a..e33a9597e 100644 --- a/core/tauri/mobile/android/src/main/java/app/tauri/PathPlugin.kt +++ b/core/tauri/mobile/android/src/main/java/app/tauri/PathPlugin.kt @@ -1,4 +1,4 @@ -// Copyright 2019-2023 Tauri Programme within The Commons Conservancy +// Copyright 2019-2024 Tauri Programme within The Commons Conservancy // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT diff --git a/core/tauri/mobile/android/src/main/java/app/tauri/PermissionHelper.kt b/core/tauri/mobile/android/src/main/java/app/tauri/PermissionHelper.kt index 7d560fbac..37468d20d 100644 --- a/core/tauri/mobile/android/src/main/java/app/tauri/PermissionHelper.kt +++ b/core/tauri/mobile/android/src/main/java/app/tauri/PermissionHelper.kt @@ -1,4 +1,4 @@ -// Copyright 2019-2023 Tauri Programme within The Commons Conservancy +// Copyright 2019-2024 Tauri Programme within The Commons Conservancy // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT diff --git a/core/tauri/mobile/android/src/main/java/app/tauri/PermissionState.kt b/core/tauri/mobile/android/src/main/java/app/tauri/PermissionState.kt index 4655c0dc0..33092ed55 100644 --- a/core/tauri/mobile/android/src/main/java/app/tauri/PermissionState.kt +++ b/core/tauri/mobile/android/src/main/java/app/tauri/PermissionState.kt @@ -1,4 +1,4 @@ -// Copyright 2019-2023 Tauri Programme within The Commons Conservancy +// Copyright 2019-2024 Tauri Programme within The Commons Conservancy // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT diff --git a/core/tauri/mobile/android/src/main/java/app/tauri/annotation/ActivityCallback.kt b/core/tauri/mobile/android/src/main/java/app/tauri/annotation/ActivityCallback.kt index 7da7c5fb8..d68093994 100644 --- a/core/tauri/mobile/android/src/main/java/app/tauri/annotation/ActivityCallback.kt +++ b/core/tauri/mobile/android/src/main/java/app/tauri/annotation/ActivityCallback.kt @@ -1,4 +1,4 @@ -// Copyright 2019-2023 Tauri Programme within The Commons Conservancy +// Copyright 2019-2024 Tauri Programme within The Commons Conservancy // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT diff --git a/core/tauri/mobile/android/src/main/java/app/tauri/annotation/InvokeArg.kt b/core/tauri/mobile/android/src/main/java/app/tauri/annotation/InvokeArg.kt index ffef326d0..08a2f7524 100644 --- a/core/tauri/mobile/android/src/main/java/app/tauri/annotation/InvokeArg.kt +++ b/core/tauri/mobile/android/src/main/java/app/tauri/annotation/InvokeArg.kt @@ -1,4 +1,4 @@ -// Copyright 2019-2023 Tauri Programme within The Commons Conservancy +// Copyright 2019-2024 Tauri Programme within The Commons Conservancy // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT diff --git a/core/tauri/mobile/android/src/main/java/app/tauri/annotation/Permission.kt b/core/tauri/mobile/android/src/main/java/app/tauri/annotation/Permission.kt index c5eb5ac73..bd5ab5ddf 100644 --- a/core/tauri/mobile/android/src/main/java/app/tauri/annotation/Permission.kt +++ b/core/tauri/mobile/android/src/main/java/app/tauri/annotation/Permission.kt @@ -1,4 +1,4 @@ -// Copyright 2019-2023 Tauri Programme within The Commons Conservancy +// Copyright 2019-2024 Tauri Programme within The Commons Conservancy // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT diff --git a/core/tauri/mobile/android/src/main/java/app/tauri/annotation/PermissionCallback.kt b/core/tauri/mobile/android/src/main/java/app/tauri/annotation/PermissionCallback.kt index 960d9f3a1..5fdeddf9d 100644 --- a/core/tauri/mobile/android/src/main/java/app/tauri/annotation/PermissionCallback.kt +++ b/core/tauri/mobile/android/src/main/java/app/tauri/annotation/PermissionCallback.kt @@ -1,4 +1,4 @@ -// Copyright 2019-2023 Tauri Programme within The Commons Conservancy +// Copyright 2019-2024 Tauri Programme within The Commons Conservancy // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT diff --git a/core/tauri/mobile/android/src/main/java/app/tauri/annotation/PluginMethod.kt b/core/tauri/mobile/android/src/main/java/app/tauri/annotation/PluginMethod.kt index e102782ae..7a73045e4 100644 --- a/core/tauri/mobile/android/src/main/java/app/tauri/annotation/PluginMethod.kt +++ b/core/tauri/mobile/android/src/main/java/app/tauri/annotation/PluginMethod.kt @@ -1,4 +1,4 @@ -// Copyright 2019-2023 Tauri Programme within The Commons Conservancy +// Copyright 2019-2024 Tauri Programme within The Commons Conservancy // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT diff --git a/core/tauri/mobile/android/src/main/java/app/tauri/annotation/TauriPlugin.kt b/core/tauri/mobile/android/src/main/java/app/tauri/annotation/TauriPlugin.kt index 41beb5017..4e8bc786f 100644 --- a/core/tauri/mobile/android/src/main/java/app/tauri/annotation/TauriPlugin.kt +++ b/core/tauri/mobile/android/src/main/java/app/tauri/annotation/TauriPlugin.kt @@ -1,4 +1,4 @@ -// Copyright 2019-2023 Tauri Programme within The Commons Conservancy +// Copyright 2019-2024 Tauri Programme within The Commons Conservancy // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT diff --git a/core/tauri/mobile/android/src/main/java/app/tauri/plugin/Channel.kt b/core/tauri/mobile/android/src/main/java/app/tauri/plugin/Channel.kt index 34eba4ca5..581b074a4 100644 --- a/core/tauri/mobile/android/src/main/java/app/tauri/plugin/Channel.kt +++ b/core/tauri/mobile/android/src/main/java/app/tauri/plugin/Channel.kt @@ -1,4 +1,4 @@ -// Copyright 2019-2023 Tauri Programme within The Commons Conservancy +// Copyright 2019-2024 Tauri Programme within The Commons Conservancy // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT diff --git a/core/tauri/mobile/android/src/main/java/app/tauri/plugin/InvalidPluginMethodException.kt b/core/tauri/mobile/android/src/main/java/app/tauri/plugin/InvalidPluginMethodException.kt index 371b15388..4325313fc 100644 --- a/core/tauri/mobile/android/src/main/java/app/tauri/plugin/InvalidPluginMethodException.kt +++ b/core/tauri/mobile/android/src/main/java/app/tauri/plugin/InvalidPluginMethodException.kt @@ -1,4 +1,4 @@ -// Copyright 2019-2023 Tauri Programme within The Commons Conservancy +// Copyright 2019-2024 Tauri Programme within The Commons Conservancy // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT diff --git a/core/tauri/mobile/android/src/main/java/app/tauri/plugin/Invoke.kt b/core/tauri/mobile/android/src/main/java/app/tauri/plugin/Invoke.kt index 4ff17d535..d951ca55b 100644 --- a/core/tauri/mobile/android/src/main/java/app/tauri/plugin/Invoke.kt +++ b/core/tauri/mobile/android/src/main/java/app/tauri/plugin/Invoke.kt @@ -1,4 +1,4 @@ -// Copyright 2019-2023 Tauri Programme within The Commons Conservancy +// Copyright 2019-2024 Tauri Programme within The Commons Conservancy // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT diff --git a/core/tauri/mobile/android/src/main/java/app/tauri/plugin/JSArray.kt b/core/tauri/mobile/android/src/main/java/app/tauri/plugin/JSArray.kt index 6db42b86c..84bdf4017 100644 --- a/core/tauri/mobile/android/src/main/java/app/tauri/plugin/JSArray.kt +++ b/core/tauri/mobile/android/src/main/java/app/tauri/plugin/JSArray.kt @@ -1,4 +1,4 @@ -// Copyright 2019-2023 Tauri Programme within The Commons Conservancy +// Copyright 2019-2024 Tauri Programme within The Commons Conservancy // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT diff --git a/core/tauri/mobile/android/src/main/java/app/tauri/plugin/JSObject.kt b/core/tauri/mobile/android/src/main/java/app/tauri/plugin/JSObject.kt index 3affc81aa..2d4056108 100644 --- a/core/tauri/mobile/android/src/main/java/app/tauri/plugin/JSObject.kt +++ b/core/tauri/mobile/android/src/main/java/app/tauri/plugin/JSObject.kt @@ -1,4 +1,4 @@ -// Copyright 2019-2023 Tauri Programme within The Commons Conservancy +// Copyright 2019-2024 Tauri Programme within The Commons Conservancy // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT diff --git a/core/tauri/mobile/android/src/main/java/app/tauri/plugin/Plugin.kt b/core/tauri/mobile/android/src/main/java/app/tauri/plugin/Plugin.kt index 4bc27e453..456ee271f 100644 --- a/core/tauri/mobile/android/src/main/java/app/tauri/plugin/Plugin.kt +++ b/core/tauri/mobile/android/src/main/java/app/tauri/plugin/Plugin.kt @@ -1,4 +1,4 @@ -// Copyright 2019-2023 Tauri Programme within The Commons Conservancy +// Copyright 2019-2024 Tauri Programme within The Commons Conservancy // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT diff --git a/core/tauri/mobile/android/src/main/java/app/tauri/plugin/PluginHandle.kt b/core/tauri/mobile/android/src/main/java/app/tauri/plugin/PluginHandle.kt index 30d1d77c9..5e10f17bd 100644 --- a/core/tauri/mobile/android/src/main/java/app/tauri/plugin/PluginHandle.kt +++ b/core/tauri/mobile/android/src/main/java/app/tauri/plugin/PluginHandle.kt @@ -1,4 +1,4 @@ -// Copyright 2019-2023 Tauri Programme within The Commons Conservancy +// Copyright 2019-2024 Tauri Programme within The Commons Conservancy // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT diff --git a/core/tauri/mobile/android/src/main/java/app/tauri/plugin/PluginManager.kt b/core/tauri/mobile/android/src/main/java/app/tauri/plugin/PluginManager.kt index 14274cd2f..1b4a3b8ab 100644 --- a/core/tauri/mobile/android/src/main/java/app/tauri/plugin/PluginManager.kt +++ b/core/tauri/mobile/android/src/main/java/app/tauri/plugin/PluginManager.kt @@ -1,4 +1,4 @@ -// Copyright 2019-2023 Tauri Programme within The Commons Conservancy +// Copyright 2019-2024 Tauri Programme within The Commons Conservancy // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT diff --git a/core/tauri/mobile/android/src/main/java/app/tauri/plugin/PluginMethodData.kt b/core/tauri/mobile/android/src/main/java/app/tauri/plugin/PluginMethodData.kt index 87b254312..acb09477e 100644 --- a/core/tauri/mobile/android/src/main/java/app/tauri/plugin/PluginMethodData.kt +++ b/core/tauri/mobile/android/src/main/java/app/tauri/plugin/PluginMethodData.kt @@ -1,4 +1,4 @@ -// Copyright 2019-2023 Tauri Programme within The Commons Conservancy +// Copyright 2019-2024 Tauri Programme within The Commons Conservancy // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT diff --git a/core/tauri/mobile/android/src/main/java/app/tauri/plugin/PluginResult.kt b/core/tauri/mobile/android/src/main/java/app/tauri/plugin/PluginResult.kt index 142954ba9..601cd48ec 100644 --- a/core/tauri/mobile/android/src/main/java/app/tauri/plugin/PluginResult.kt +++ b/core/tauri/mobile/android/src/main/java/app/tauri/plugin/PluginResult.kt @@ -1,4 +1,4 @@ -// Copyright 2019-2023 Tauri Programme within The Commons Conservancy +// Copyright 2019-2024 Tauri Programme within The Commons Conservancy // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT diff --git a/core/tauri/mobile/android/src/test/java/app/tauri/ExampleUnitTest.kt b/core/tauri/mobile/android/src/test/java/app/tauri/ExampleUnitTest.kt index 7db9cdf69..16022f41a 100644 --- a/core/tauri/mobile/android/src/test/java/app/tauri/ExampleUnitTest.kt +++ b/core/tauri/mobile/android/src/test/java/app/tauri/ExampleUnitTest.kt @@ -1,4 +1,4 @@ -// Copyright 2019-2023 Tauri Programme within The Commons Conservancy +// Copyright 2019-2024 Tauri Programme within The Commons Conservancy // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT diff --git a/core/tauri/mobile/ios-api/Package.swift b/core/tauri/mobile/ios-api/Package.swift index 50bafbd80..5e72c9339 100644 --- a/core/tauri/mobile/ios-api/Package.swift +++ b/core/tauri/mobile/ios-api/Package.swift @@ -1,5 +1,5 @@ // swift-tools-version:5.3 -// Copyright 2019-2023 Tauri Programme within The Commons Conservancy +// Copyright 2019-2024 Tauri Programme within The Commons Conservancy // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT diff --git a/core/tauri/mobile/ios-api/Sources/Tauri/Channel.swift b/core/tauri/mobile/ios-api/Sources/Tauri/Channel.swift index bb448bf9b..add065c74 100644 --- a/core/tauri/mobile/ios-api/Sources/Tauri/Channel.swift +++ b/core/tauri/mobile/ios-api/Sources/Tauri/Channel.swift @@ -1,4 +1,4 @@ -// Copyright 2019-2023 Tauri Programme within The Commons Conservancy +// Copyright 2019-2024 Tauri Programme within The Commons Conservancy // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT diff --git a/core/tauri/mobile/ios-api/Sources/Tauri/Invoke.swift b/core/tauri/mobile/ios-api/Sources/Tauri/Invoke.swift index 72f124d48..b1b5dbfe5 100644 --- a/core/tauri/mobile/ios-api/Sources/Tauri/Invoke.swift +++ b/core/tauri/mobile/ios-api/Sources/Tauri/Invoke.swift @@ -1,4 +1,4 @@ -// Copyright 2019-2023 Tauri Programme within The Commons Conservancy +// Copyright 2019-2024 Tauri Programme within The Commons Conservancy // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT diff --git a/core/tauri/mobile/ios-api/Sources/Tauri/JSTypes.swift b/core/tauri/mobile/ios-api/Sources/Tauri/JSTypes.swift index 505342dba..8fd5d2f29 100644 --- a/core/tauri/mobile/ios-api/Sources/Tauri/JSTypes.swift +++ b/core/tauri/mobile/ios-api/Sources/Tauri/JSTypes.swift @@ -1,4 +1,4 @@ -// Copyright 2019-2023 Tauri Programme within The Commons Conservancy +// Copyright 2019-2024 Tauri Programme within The Commons Conservancy // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT diff --git a/core/tauri/mobile/ios-api/Sources/Tauri/JsonValue.swift b/core/tauri/mobile/ios-api/Sources/Tauri/JsonValue.swift index 7c863ded5..c3043511b 100644 --- a/core/tauri/mobile/ios-api/Sources/Tauri/JsonValue.swift +++ b/core/tauri/mobile/ios-api/Sources/Tauri/JsonValue.swift @@ -1,4 +1,4 @@ -// Copyright 2019-2023 Tauri Programme within The Commons Conservancy +// Copyright 2019-2024 Tauri Programme within The Commons Conservancy // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT diff --git a/core/tauri/mobile/ios-api/Sources/Tauri/Logger.swift b/core/tauri/mobile/ios-api/Sources/Tauri/Logger.swift index 115359f8f..9fa6e3fe7 100644 --- a/core/tauri/mobile/ios-api/Sources/Tauri/Logger.swift +++ b/core/tauri/mobile/ios-api/Sources/Tauri/Logger.swift @@ -1,4 +1,4 @@ -// Copyright 2019-2023 Tauri Programme within The Commons Conservancy +// Copyright 2019-2024 Tauri Programme within The Commons Conservancy // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT @@ -37,11 +37,19 @@ public class Logger { } public static func debug(_ items: Any..., category: String = "app") { + #if DEBUG + Logger.log(items, category: category, type: OSLogType.default) + #else Logger.log(items, category: category, type: OSLogType.debug) + #endif } public static func info(_ items: Any..., category: String = "app") { + #if DEBUG + Logger.log(items, category: category, type: OSLogType.default) + #else Logger.log(items, category: category, type: OSLogType.info) + #endif } public static func error(_ items: Any..., category: String = "app") { diff --git a/core/tauri/mobile/ios-api/Sources/Tauri/Plugin/Plugin.swift b/core/tauri/mobile/ios-api/Sources/Tauri/Plugin/Plugin.swift index c90715b58..7ce67f9b2 100644 --- a/core/tauri/mobile/ios-api/Sources/Tauri/Plugin/Plugin.swift +++ b/core/tauri/mobile/ios-api/Sources/Tauri/Plugin/Plugin.swift @@ -1,4 +1,4 @@ -// Copyright 2019-2023 Tauri Programme within The Commons Conservancy +// Copyright 2019-2024 Tauri Programme within The Commons Conservancy // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT diff --git a/core/tauri/mobile/ios-api/Sources/Tauri/Tauri.swift b/core/tauri/mobile/ios-api/Sources/Tauri/Tauri.swift index d130015df..9a9c40e3d 100644 --- a/core/tauri/mobile/ios-api/Sources/Tauri/Tauri.swift +++ b/core/tauri/mobile/ios-api/Sources/Tauri/Tauri.swift @@ -1,4 +1,4 @@ -// Copyright 2019-2023 Tauri Programme within The Commons Conservancy +// Copyright 2019-2024 Tauri Programme within The Commons Conservancy // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT diff --git a/core/tauri/mobile/ios-api/Sources/Tauri/UiUtils.swift b/core/tauri/mobile/ios-api/Sources/Tauri/UiUtils.swift index 6ca74b960..4097596e2 100644 --- a/core/tauri/mobile/ios-api/Sources/Tauri/UiUtils.swift +++ b/core/tauri/mobile/ios-api/Sources/Tauri/UiUtils.swift @@ -1,4 +1,4 @@ -// Copyright 2019-2023 Tauri Programme within The Commons Conservancy +// Copyright 2019-2024 Tauri Programme within The Commons Conservancy // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT diff --git a/core/tauri/mobile/ios-api/Tests/TauriTests/TauriTests.swift b/core/tauri/mobile/ios-api/Tests/TauriTests/TauriTests.swift index 0681f06fd..49f6ee223 100644 --- a/core/tauri/mobile/ios-api/Tests/TauriTests/TauriTests.swift +++ b/core/tauri/mobile/ios-api/Tests/TauriTests/TauriTests.swift @@ -1,4 +1,4 @@ -// Copyright 2019-2023 Tauri Programme within The Commons Conservancy +// Copyright 2019-2024 Tauri Programme within The Commons Conservancy // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT diff --git a/core/tauri/permissions/app/autogenerated/reference.md b/core/tauri/permissions/app/autogenerated/reference.md index 4090c5819..3791761b0 100644 --- a/core/tauri/permissions/app/autogenerated/reference.md +++ b/core/tauri/permissions/app/autogenerated/reference.md @@ -1,46 +1,13 @@ -# Permissions - -## allow-app-hide - -Enables the app_hide command without any pre-configured scope. - -## deny-app-hide - -Denies the app_hide command without any pre-configured scope. - -## allow-app-show - -Enables the app_show command without any pre-configured scope. - -## deny-app-show - -Denies the app_show command without any pre-configured scope. - -## allow-name - -Enables the name command without any pre-configured scope. - -## deny-name - -Denies the name command without any pre-configured scope. - -## allow-tauri-version - -Enables the tauri_version command without any pre-configured scope. - -## deny-tauri-version - -Denies the tauri_version command without any pre-configured scope. - -## allow-version - -Enables the version command without any pre-configured scope. - -## deny-version - -Denies the version command without any pre-configured scope. - -## default - -Default permissions for the plugin. - +| Permission | Description | +|------|-----| +|`allow-app-hide`|Enables the app_hide command without any pre-configured scope.| +|`deny-app-hide`|Denies the app_hide command without any pre-configured scope.| +|`allow-app-show`|Enables the app_show command without any pre-configured scope.| +|`deny-app-show`|Denies the app_show command without any pre-configured scope.| +|`allow-name`|Enables the name command without any pre-configured scope.| +|`deny-name`|Denies the name command without any pre-configured scope.| +|`allow-tauri-version`|Enables the tauri_version command without any pre-configured scope.| +|`deny-tauri-version`|Denies the tauri_version command without any pre-configured scope.| +|`allow-version`|Enables the version command without any pre-configured scope.| +|`deny-version`|Denies the version command without any pre-configured scope.| +|`default`|Default permissions for the plugin.| diff --git a/core/tauri/permissions/event/autogenerated/reference.md b/core/tauri/permissions/event/autogenerated/reference.md index bbbda5264..dd683ebed 100644 --- a/core/tauri/permissions/event/autogenerated/reference.md +++ b/core/tauri/permissions/event/autogenerated/reference.md @@ -1,38 +1,11 @@ -# Permissions - -## allow-emit - -Enables the emit command without any pre-configured scope. - -## deny-emit - -Denies the emit command without any pre-configured scope. - -## allow-emit-to - -Enables the emit_to command without any pre-configured scope. - -## deny-emit-to - -Denies the emit_to command without any pre-configured scope. - -## allow-listen - -Enables the listen command without any pre-configured scope. - -## deny-listen - -Denies the listen command without any pre-configured scope. - -## allow-unlisten - -Enables the unlisten command without any pre-configured scope. - -## deny-unlisten - -Denies the unlisten command without any pre-configured scope. - -## default - -Default permissions for the plugin. - +| Permission | Description | +|------|-----| +|`allow-emit`|Enables the emit command without any pre-configured scope.| +|`deny-emit`|Denies the emit command without any pre-configured scope.| +|`allow-emit-to`|Enables the emit_to command without any pre-configured scope.| +|`deny-emit-to`|Denies the emit_to command without any pre-configured scope.| +|`allow-listen`|Enables the listen command without any pre-configured scope.| +|`deny-listen`|Denies the listen command without any pre-configured scope.| +|`allow-unlisten`|Enables the unlisten command without any pre-configured scope.| +|`deny-unlisten`|Denies the unlisten command without any pre-configured scope.| +|`default`|Default permissions for the plugin.| diff --git a/core/tauri/permissions/image/autogenerated/reference.md b/core/tauri/permissions/image/autogenerated/reference.md new file mode 100644 index 000000000..75a366089 --- /dev/null +++ b/core/tauri/permissions/image/autogenerated/reference.md @@ -0,0 +1,13 @@ +| Permission | Description | +|------|-----| +|`allow-from-bytes`|Enables the from_bytes command without any pre-configured scope.| +|`deny-from-bytes`|Denies the from_bytes command without any pre-configured scope.| +|`allow-from-path`|Enables the from_path command without any pre-configured scope.| +|`deny-from-path`|Denies the from_path command without any pre-configured scope.| +|`allow-new`|Enables the new command without any pre-configured scope.| +|`deny-new`|Denies the new command without any pre-configured scope.| +|`allow-rgba`|Enables the rgba command without any pre-configured scope.| +|`deny-rgba`|Denies the rgba command without any pre-configured scope.| +|`allow-size`|Enables the size command without any pre-configured scope.| +|`deny-size`|Denies the size command without any pre-configured scope.| +|`default`|Default permissions for the plugin.| diff --git a/core/tauri/permissions/menu/autogenerated/reference.md b/core/tauri/permissions/menu/autogenerated/reference.md index c5e6a4854..5d46fc95b 100644 --- a/core/tauri/permissions/menu/autogenerated/reference.md +++ b/core/tauri/permissions/menu/autogenerated/reference.md @@ -1,182 +1,47 @@ -# Permissions - -## allow-append - -Enables the append command without any pre-configured scope. - -## deny-append - -Denies the append command without any pre-configured scope. - -## allow-create-default - -Enables the create_default command without any pre-configured scope. - -## deny-create-default - -Denies the create_default command without any pre-configured scope. - -## allow-get - -Enables the get command without any pre-configured scope. - -## deny-get - -Denies the get command without any pre-configured scope. - -## allow-insert - -Enables the insert command without any pre-configured scope. - -## deny-insert - -Denies the insert command without any pre-configured scope. - -## allow-is-checked - -Enables the is_checked command without any pre-configured scope. - -## deny-is-checked - -Denies the is_checked command without any pre-configured scope. - -## allow-is-enabled - -Enables the is_enabled command without any pre-configured scope. - -## deny-is-enabled - -Denies the is_enabled command without any pre-configured scope. - -## allow-items - -Enables the items command without any pre-configured scope. - -## deny-items - -Denies the items command without any pre-configured scope. - -## allow-new - -Enables the new command without any pre-configured scope. - -## deny-new - -Denies the new command without any pre-configured scope. - -## allow-popup - -Enables the popup command without any pre-configured scope. - -## deny-popup - -Denies the popup command without any pre-configured scope. - -## allow-prepend - -Enables the prepend command without any pre-configured scope. - -## deny-prepend - -Denies the prepend command without any pre-configured scope. - -## allow-remove - -Enables the remove command without any pre-configured scope. - -## deny-remove - -Denies the remove command without any pre-configured scope. - -## allow-remove-at - -Enables the remove_at command without any pre-configured scope. - -## deny-remove-at - -Denies the remove_at command without any pre-configured scope. - -## allow-set-accelerator - -Enables the set_accelerator command without any pre-configured scope. - -## deny-set-accelerator - -Denies the set_accelerator command without any pre-configured scope. - -## allow-set-as-app-menu - -Enables the set_as_app_menu command without any pre-configured scope. - -## deny-set-as-app-menu - -Denies the set_as_app_menu command without any pre-configured scope. - -## allow-set-as-help-menu-for-nsapp - -Enables the set_as_help_menu_for_nsapp command without any pre-configured scope. - -## deny-set-as-help-menu-for-nsapp - -Denies the set_as_help_menu_for_nsapp command without any pre-configured scope. - -## allow-set-as-window-menu - -Enables the set_as_window_menu command without any pre-configured scope. - -## deny-set-as-window-menu - -Denies the set_as_window_menu command without any pre-configured scope. - -## allow-set-as-windows-menu-for-nsapp - -Enables the set_as_windows_menu_for_nsapp command without any pre-configured scope. - -## deny-set-as-windows-menu-for-nsapp - -Denies the set_as_windows_menu_for_nsapp command without any pre-configured scope. - -## allow-set-checked - -Enables the set_checked command without any pre-configured scope. - -## deny-set-checked - -Denies the set_checked command without any pre-configured scope. - -## allow-set-enabled - -Enables the set_enabled command without any pre-configured scope. - -## deny-set-enabled - -Denies the set_enabled command without any pre-configured scope. - -## allow-set-icon - -Enables the set_icon command without any pre-configured scope. - -## deny-set-icon - -Denies the set_icon command without any pre-configured scope. - -## allow-set-text - -Enables the set_text command without any pre-configured scope. - -## deny-set-text - -Denies the set_text command without any pre-configured scope. - -## allow-text - -Enables the text command without any pre-configured scope. - -## deny-text - -Denies the text command without any pre-configured scope. - -## default - -Default permissions for the plugin. - +| Permission | Description | +|------|-----| +|`allow-append`|Enables the append command without any pre-configured scope.| +|`deny-append`|Denies the append command without any pre-configured scope.| +|`allow-create-default`|Enables the create_default command without any pre-configured scope.| +|`deny-create-default`|Denies the create_default command without any pre-configured scope.| +|`allow-get`|Enables the get command without any pre-configured scope.| +|`deny-get`|Denies the get command without any pre-configured scope.| +|`allow-insert`|Enables the insert command without any pre-configured scope.| +|`deny-insert`|Denies the insert command without any pre-configured scope.| +|`allow-is-checked`|Enables the is_checked command without any pre-configured scope.| +|`deny-is-checked`|Denies the is_checked command without any pre-configured scope.| +|`allow-is-enabled`|Enables the is_enabled command without any pre-configured scope.| +|`deny-is-enabled`|Denies the is_enabled command without any pre-configured scope.| +|`allow-items`|Enables the items command without any pre-configured scope.| +|`deny-items`|Denies the items command without any pre-configured scope.| +|`allow-new`|Enables the new command without any pre-configured scope.| +|`deny-new`|Denies the new command without any pre-configured scope.| +|`allow-popup`|Enables the popup command without any pre-configured scope.| +|`deny-popup`|Denies the popup command without any pre-configured scope.| +|`allow-prepend`|Enables the prepend command without any pre-configured scope.| +|`deny-prepend`|Denies the prepend command without any pre-configured scope.| +|`allow-remove`|Enables the remove command without any pre-configured scope.| +|`deny-remove`|Denies the remove command without any pre-configured scope.| +|`allow-remove-at`|Enables the remove_at command without any pre-configured scope.| +|`deny-remove-at`|Denies the remove_at command without any pre-configured scope.| +|`allow-set-accelerator`|Enables the set_accelerator command without any pre-configured scope.| +|`deny-set-accelerator`|Denies the set_accelerator command without any pre-configured scope.| +|`allow-set-as-app-menu`|Enables the set_as_app_menu command without any pre-configured scope.| +|`deny-set-as-app-menu`|Denies the set_as_app_menu command without any pre-configured scope.| +|`allow-set-as-help-menu-for-nsapp`|Enables the set_as_help_menu_for_nsapp command without any pre-configured scope.| +|`deny-set-as-help-menu-for-nsapp`|Denies the set_as_help_menu_for_nsapp command without any pre-configured scope.| +|`allow-set-as-window-menu`|Enables the set_as_window_menu command without any pre-configured scope.| +|`deny-set-as-window-menu`|Denies the set_as_window_menu command without any pre-configured scope.| +|`allow-set-as-windows-menu-for-nsapp`|Enables the set_as_windows_menu_for_nsapp command without any pre-configured scope.| +|`deny-set-as-windows-menu-for-nsapp`|Denies the set_as_windows_menu_for_nsapp command without any pre-configured scope.| +|`allow-set-checked`|Enables the set_checked command without any pre-configured scope.| +|`deny-set-checked`|Denies the set_checked command without any pre-configured scope.| +|`allow-set-enabled`|Enables the set_enabled command without any pre-configured scope.| +|`deny-set-enabled`|Denies the set_enabled command without any pre-configured scope.| +|`allow-set-icon`|Enables the set_icon command without any pre-configured scope.| +|`deny-set-icon`|Denies the set_icon command without any pre-configured scope.| +|`allow-set-text`|Enables the set_text command without any pre-configured scope.| +|`deny-set-text`|Denies the set_text command without any pre-configured scope.| +|`allow-text`|Enables the text command without any pre-configured scope.| +|`deny-text`|Denies the text command without any pre-configured scope.| +|`default`|Default permissions for the plugin.| diff --git a/core/tauri/permissions/path/autogenerated/reference.md b/core/tauri/permissions/path/autogenerated/reference.md index b03009e85..0793c4091 100644 --- a/core/tauri/permissions/path/autogenerated/reference.md +++ b/core/tauri/permissions/path/autogenerated/reference.md @@ -1,70 +1,19 @@ -# Permissions - -## allow-basename - -Enables the basename command without any pre-configured scope. - -## deny-basename - -Denies the basename command without any pre-configured scope. - -## allow-dirname - -Enables the dirname command without any pre-configured scope. - -## deny-dirname - -Denies the dirname command without any pre-configured scope. - -## allow-extname - -Enables the extname command without any pre-configured scope. - -## deny-extname - -Denies the extname command without any pre-configured scope. - -## allow-is-absolute - -Enables the is_absolute command without any pre-configured scope. - -## deny-is-absolute - -Denies the is_absolute command without any pre-configured scope. - -## allow-join - -Enables the join command without any pre-configured scope. - -## deny-join - -Denies the join command without any pre-configured scope. - -## allow-normalize - -Enables the normalize command without any pre-configured scope. - -## deny-normalize - -Denies the normalize command without any pre-configured scope. - -## allow-resolve - -Enables the resolve command without any pre-configured scope. - -## deny-resolve - -Denies the resolve command without any pre-configured scope. - -## allow-resolve-directory - -Enables the resolve_directory command without any pre-configured scope. - -## deny-resolve-directory - -Denies the resolve_directory command without any pre-configured scope. - -## default - -Default permissions for the plugin. - +| Permission | Description | +|------|-----| +|`allow-basename`|Enables the basename command without any pre-configured scope.| +|`deny-basename`|Denies the basename command without any pre-configured scope.| +|`allow-dirname`|Enables the dirname command without any pre-configured scope.| +|`deny-dirname`|Denies the dirname command without any pre-configured scope.| +|`allow-extname`|Enables the extname command without any pre-configured scope.| +|`deny-extname`|Denies the extname command without any pre-configured scope.| +|`allow-is-absolute`|Enables the is_absolute command without any pre-configured scope.| +|`deny-is-absolute`|Denies the is_absolute command without any pre-configured scope.| +|`allow-join`|Enables the join command without any pre-configured scope.| +|`deny-join`|Denies the join command without any pre-configured scope.| +|`allow-normalize`|Enables the normalize command without any pre-configured scope.| +|`deny-normalize`|Denies the normalize command without any pre-configured scope.| +|`allow-resolve`|Enables the resolve command without any pre-configured scope.| +|`deny-resolve`|Denies the resolve command without any pre-configured scope.| +|`allow-resolve-directory`|Enables the resolve_directory command without any pre-configured scope.| +|`deny-resolve-directory`|Denies the resolve_directory command without any pre-configured scope.| +|`default`|Default permissions for the plugin.| diff --git a/core/tauri/permissions/resources/autogenerated/reference.md b/core/tauri/permissions/resources/autogenerated/reference.md index 198236d0d..5be000e92 100644 --- a/core/tauri/permissions/resources/autogenerated/reference.md +++ b/core/tauri/permissions/resources/autogenerated/reference.md @@ -1,14 +1,5 @@ -# Permissions - -## allow-close - -Enables the close command without any pre-configured scope. - -## deny-close - -Denies the close command without any pre-configured scope. - -## default - -Default permissions for the plugin. - +| Permission | Description | +|------|-----| +|`allow-close`|Enables the close command without any pre-configured scope.| +|`deny-close`|Denies the close command without any pre-configured scope.| +|`default`|Default permissions for the plugin.| diff --git a/core/tauri/permissions/tray/autogenerated/reference.md b/core/tauri/permissions/tray/autogenerated/reference.md index 5137a1a86..7c621e6e0 100644 --- a/core/tauri/permissions/tray/autogenerated/reference.md +++ b/core/tauri/permissions/tray/autogenerated/reference.md @@ -1,78 +1,25 @@ -# Permissions - -## allow-new - -Enables the new command without any pre-configured scope. - -## deny-new - -Denies the new command without any pre-configured scope. - -## allow-set-icon - -Enables the set_icon command without any pre-configured scope. - -## deny-set-icon - -Denies the set_icon command without any pre-configured scope. - -## allow-set-icon-as-template - -Enables the set_icon_as_template command without any pre-configured scope. - -## deny-set-icon-as-template - -Denies the set_icon_as_template command without any pre-configured scope. - -## allow-set-menu - -Enables the set_menu command without any pre-configured scope. - -## deny-set-menu - -Denies the set_menu command without any pre-configured scope. - -## allow-set-show-menu-on-left-click - -Enables the set_show_menu_on_left_click command without any pre-configured scope. - -## deny-set-show-menu-on-left-click - -Denies the set_show_menu_on_left_click command without any pre-configured scope. - -## allow-set-temp-dir-path - -Enables the set_temp_dir_path command without any pre-configured scope. - -## deny-set-temp-dir-path - -Denies the set_temp_dir_path command without any pre-configured scope. - -## allow-set-title - -Enables the set_title command without any pre-configured scope. - -## deny-set-title - -Denies the set_title command without any pre-configured scope. - -## allow-set-tooltip - -Enables the set_tooltip command without any pre-configured scope. - -## deny-set-tooltip - -Denies the set_tooltip command without any pre-configured scope. - -## allow-set-visible - -Enables the set_visible command without any pre-configured scope. - -## deny-set-visible - -Denies the set_visible command without any pre-configured scope. - -## default - -Default permissions for the plugin. - +| Permission | Description | +|------|-----| +|`allow-get-by-id`|Enables the get_by_id command without any pre-configured scope.| +|`deny-get-by-id`|Denies the get_by_id command without any pre-configured scope.| +|`allow-new`|Enables the new command without any pre-configured scope.| +|`deny-new`|Denies the new command without any pre-configured scope.| +|`allow-remove-by-id`|Enables the remove_by_id command without any pre-configured scope.| +|`deny-remove-by-id`|Denies the remove_by_id command without any pre-configured scope.| +|`allow-set-icon`|Enables the set_icon command without any pre-configured scope.| +|`deny-set-icon`|Denies the set_icon command without any pre-configured scope.| +|`allow-set-icon-as-template`|Enables the set_icon_as_template command without any pre-configured scope.| +|`deny-set-icon-as-template`|Denies the set_icon_as_template command without any pre-configured scope.| +|`allow-set-menu`|Enables the set_menu command without any pre-configured scope.| +|`deny-set-menu`|Denies the set_menu command without any pre-configured scope.| +|`allow-set-show-menu-on-left-click`|Enables the set_show_menu_on_left_click command without any pre-configured scope.| +|`deny-set-show-menu-on-left-click`|Denies the set_show_menu_on_left_click command without any pre-configured scope.| +|`allow-set-temp-dir-path`|Enables the set_temp_dir_path command without any pre-configured scope.| +|`deny-set-temp-dir-path`|Denies the set_temp_dir_path command without any pre-configured scope.| +|`allow-set-title`|Enables the set_title command without any pre-configured scope.| +|`deny-set-title`|Denies the set_title command without any pre-configured scope.| +|`allow-set-tooltip`|Enables the set_tooltip command without any pre-configured scope.| +|`deny-set-tooltip`|Denies the set_tooltip command without any pre-configured scope.| +|`allow-set-visible`|Enables the set_visible command without any pre-configured scope.| +|`deny-set-visible`|Denies the set_visible command without any pre-configured scope.| +|`default`|Default permissions for the plugin.| diff --git a/core/tauri/permissions/webview/autogenerated/reference.md b/core/tauri/permissions/webview/autogenerated/reference.md index c845e562c..7a5e7969c 100644 --- a/core/tauri/permissions/webview/autogenerated/reference.md +++ b/core/tauri/permissions/webview/autogenerated/reference.md @@ -1,86 +1,27 @@ -# Permissions - -## allow-create-webview - -Enables the create_webview command without any pre-configured scope. - -## deny-create-webview - -Denies the create_webview command without any pre-configured scope. - -## allow-create-webview-window - -Enables the create_webview_window command without any pre-configured scope. - -## deny-create-webview-window - -Denies the create_webview_window command without any pre-configured scope. - -## allow-internal-toggle-devtools - -Enables the internal_toggle_devtools command without any pre-configured scope. - -## deny-internal-toggle-devtools - -Denies the internal_toggle_devtools command without any pre-configured scope. - -## allow-print - -Enables the print command without any pre-configured scope. - -## deny-print - -Denies the print command without any pre-configured scope. - -## allow-set-webview-focus - -Enables the set_webview_focus command without any pre-configured scope. - -## deny-set-webview-focus - -Denies the set_webview_focus command without any pre-configured scope. - -## allow-set-webview-position - -Enables the set_webview_position command without any pre-configured scope. - -## deny-set-webview-position - -Denies the set_webview_position command without any pre-configured scope. - -## allow-set-webview-size - -Enables the set_webview_size command without any pre-configured scope. - -## deny-set-webview-size - -Denies the set_webview_size command without any pre-configured scope. - -## allow-webview-close - -Enables the webview_close command without any pre-configured scope. - -## deny-webview-close - -Denies the webview_close command without any pre-configured scope. - -## allow-webview-position - -Enables the webview_position command without any pre-configured scope. - -## deny-webview-position - -Denies the webview_position command without any pre-configured scope. - -## allow-webview-size - -Enables the webview_size command without any pre-configured scope. - -## deny-webview-size - -Denies the webview_size command without any pre-configured scope. - -## default - -Default permissions for the plugin. - +| Permission | Description | +|------|-----| +|`allow-create-webview`|Enables the create_webview command without any pre-configured scope.| +|`deny-create-webview`|Denies the create_webview command without any pre-configured scope.| +|`allow-create-webview-window`|Enables the create_webview_window command without any pre-configured scope.| +|`deny-create-webview-window`|Denies the create_webview_window command without any pre-configured scope.| +|`allow-internal-toggle-devtools`|Enables the internal_toggle_devtools command without any pre-configured scope.| +|`deny-internal-toggle-devtools`|Denies the internal_toggle_devtools command without any pre-configured scope.| +|`allow-print`|Enables the print command without any pre-configured scope.| +|`deny-print`|Denies the print command without any pre-configured scope.| +|`allow-reparent`|Enables the reparent command without any pre-configured scope.| +|`deny-reparent`|Denies the reparent command without any pre-configured scope.| +|`allow-set-webview-focus`|Enables the set_webview_focus command without any pre-configured scope.| +|`deny-set-webview-focus`|Denies the set_webview_focus command without any pre-configured scope.| +|`allow-set-webview-position`|Enables the set_webview_position command without any pre-configured scope.| +|`deny-set-webview-position`|Denies the set_webview_position command without any pre-configured scope.| +|`allow-set-webview-size`|Enables the set_webview_size command without any pre-configured scope.| +|`deny-set-webview-size`|Denies the set_webview_size command without any pre-configured scope.| +|`allow-set-webview-zoom`|Enables the set_webview_zoom command without any pre-configured scope.| +|`deny-set-webview-zoom`|Denies the set_webview_zoom command without any pre-configured scope.| +|`allow-webview-close`|Enables the webview_close command without any pre-configured scope.| +|`deny-webview-close`|Denies the webview_close command without any pre-configured scope.| +|`allow-webview-position`|Enables the webview_position command without any pre-configured scope.| +|`deny-webview-position`|Denies the webview_position command without any pre-configured scope.| +|`allow-webview-size`|Enables the webview_size command without any pre-configured scope.| +|`deny-webview-size`|Denies the webview_size command without any pre-configured scope.| +|`default`|Default permissions for the plugin.| diff --git a/core/tauri/permissions/window/autogenerated/reference.md b/core/tauri/permissions/window/autogenerated/reference.md index b4a1341c4..868a10c91 100644 --- a/core/tauri/permissions/window/autogenerated/reference.md +++ b/core/tauri/permissions/window/autogenerated/reference.md @@ -1,502 +1,123 @@ -# Permissions - -## allow-available-monitors - -Enables the available_monitors command without any pre-configured scope. - -## deny-available-monitors - -Denies the available_monitors command without any pre-configured scope. - -## allow-center - -Enables the center command without any pre-configured scope. - -## deny-center - -Denies the center command without any pre-configured scope. - -## allow-close - -Enables the close command without any pre-configured scope. - -## deny-close - -Denies the close command without any pre-configured scope. - -## allow-create - -Enables the create command without any pre-configured scope. - -## deny-create - -Denies the create command without any pre-configured scope. - -## allow-current-monitor - -Enables the current_monitor command without any pre-configured scope. - -## deny-current-monitor - -Denies the current_monitor command without any pre-configured scope. - -## allow-destroy - -Enables the destroy command without any pre-configured scope. - -## deny-destroy - -Denies the destroy command without any pre-configured scope. - -## allow-hide - -Enables the hide command without any pre-configured scope. - -## deny-hide - -Denies the hide command without any pre-configured scope. - -## allow-inner-position - -Enables the inner_position command without any pre-configured scope. - -## deny-inner-position - -Denies the inner_position command without any pre-configured scope. - -## allow-inner-size - -Enables the inner_size command without any pre-configured scope. - -## deny-inner-size - -Denies the inner_size command without any pre-configured scope. - -## allow-internal-on-mousedown - -Enables the internal_on_mousedown command without any pre-configured scope. - -## deny-internal-on-mousedown - -Denies the internal_on_mousedown command without any pre-configured scope. - -## allow-internal-on-mousemove - -Enables the internal_on_mousemove command without any pre-configured scope. - -## deny-internal-on-mousemove - -Denies the internal_on_mousemove command without any pre-configured scope. - -## allow-internal-toggle-maximize - -Enables the internal_toggle_maximize command without any pre-configured scope. - -## deny-internal-toggle-maximize - -Denies the internal_toggle_maximize command without any pre-configured scope. - -## allow-is-closable - -Enables the is_closable command without any pre-configured scope. - -## deny-is-closable - -Denies the is_closable command without any pre-configured scope. - -## allow-is-decorated - -Enables the is_decorated command without any pre-configured scope. - -## deny-is-decorated - -Denies the is_decorated command without any pre-configured scope. - -## allow-is-focused - -Enables the is_focused command without any pre-configured scope. - -## deny-is-focused - -Denies the is_focused command without any pre-configured scope. - -## allow-is-fullscreen - -Enables the is_fullscreen command without any pre-configured scope. - -## deny-is-fullscreen - -Denies the is_fullscreen command without any pre-configured scope. - -## allow-is-maximizable - -Enables the is_maximizable command without any pre-configured scope. - -## deny-is-maximizable - -Denies the is_maximizable command without any pre-configured scope. - -## allow-is-maximized - -Enables the is_maximized command without any pre-configured scope. - -## deny-is-maximized - -Denies the is_maximized command without any pre-configured scope. - -## allow-is-minimizable - -Enables the is_minimizable command without any pre-configured scope. - -## deny-is-minimizable - -Denies the is_minimizable command without any pre-configured scope. - -## allow-is-minimized - -Enables the is_minimized command without any pre-configured scope. - -## deny-is-minimized - -Denies the is_minimized command without any pre-configured scope. - -## allow-is-resizable - -Enables the is_resizable command without any pre-configured scope. - -## deny-is-resizable - -Denies the is_resizable command without any pre-configured scope. - -## allow-is-visible - -Enables the is_visible command without any pre-configured scope. - -## deny-is-visible - -Denies the is_visible command without any pre-configured scope. - -## allow-maximize - -Enables the maximize command without any pre-configured scope. - -## deny-maximize - -Denies the maximize command without any pre-configured scope. - -## allow-minimize - -Enables the minimize command without any pre-configured scope. - -## deny-minimize - -Denies the minimize command without any pre-configured scope. - -## allow-outer-position - -Enables the outer_position command without any pre-configured scope. - -## deny-outer-position - -Denies the outer_position command without any pre-configured scope. - -## allow-outer-size - -Enables the outer_size command without any pre-configured scope. - -## deny-outer-size - -Denies the outer_size command without any pre-configured scope. - -## allow-primary-monitor - -Enables the primary_monitor command without any pre-configured scope. - -## deny-primary-monitor - -Denies the primary_monitor command without any pre-configured scope. - -## allow-request-user-attention - -Enables the request_user_attention command without any pre-configured scope. - -## deny-request-user-attention - -Denies the request_user_attention command without any pre-configured scope. - -## allow-scale-factor - -Enables the scale_factor command without any pre-configured scope. - -## deny-scale-factor - -Denies the scale_factor command without any pre-configured scope. - -## allow-set-always-on-bottom - -Enables the set_always_on_bottom command without any pre-configured scope. - -## deny-set-always-on-bottom - -Denies the set_always_on_bottom command without any pre-configured scope. - -## allow-set-always-on-top - -Enables the set_always_on_top command without any pre-configured scope. - -## deny-set-always-on-top - -Denies the set_always_on_top command without any pre-configured scope. - -## allow-set-closable - -Enables the set_closable command without any pre-configured scope. - -## deny-set-closable - -Denies the set_closable command without any pre-configured scope. - -## allow-set-content-protected - -Enables the set_content_protected command without any pre-configured scope. - -## deny-set-content-protected - -Denies the set_content_protected command without any pre-configured scope. - -## allow-set-cursor-grab - -Enables the set_cursor_grab command without any pre-configured scope. - -## deny-set-cursor-grab - -Denies the set_cursor_grab command without any pre-configured scope. - -## allow-set-cursor-icon - -Enables the set_cursor_icon command without any pre-configured scope. - -## deny-set-cursor-icon - -Denies the set_cursor_icon command without any pre-configured scope. - -## allow-set-cursor-position - -Enables the set_cursor_position command without any pre-configured scope. - -## deny-set-cursor-position - -Denies the set_cursor_position command without any pre-configured scope. - -## allow-set-cursor-visible - -Enables the set_cursor_visible command without any pre-configured scope. - -## deny-set-cursor-visible - -Denies the set_cursor_visible command without any pre-configured scope. - -## allow-set-decorations - -Enables the set_decorations command without any pre-configured scope. - -## deny-set-decorations - -Denies the set_decorations command without any pre-configured scope. - -## allow-set-effects - -Enables the set_effects command without any pre-configured scope. - -## deny-set-effects - -Denies the set_effects command without any pre-configured scope. - -## allow-set-focus - -Enables the set_focus command without any pre-configured scope. - -## deny-set-focus - -Denies the set_focus command without any pre-configured scope. - -## allow-set-fullscreen - -Enables the set_fullscreen command without any pre-configured scope. - -## deny-set-fullscreen - -Denies the set_fullscreen command without any pre-configured scope. - -## allow-set-icon - -Enables the set_icon command without any pre-configured scope. - -## deny-set-icon - -Denies the set_icon command without any pre-configured scope. - -## allow-set-ignore-cursor-events - -Enables the set_ignore_cursor_events command without any pre-configured scope. - -## deny-set-ignore-cursor-events - -Denies the set_ignore_cursor_events command without any pre-configured scope. - -## allow-set-max-size - -Enables the set_max_size command without any pre-configured scope. - -## deny-set-max-size - -Denies the set_max_size command without any pre-configured scope. - -## allow-set-maximizable - -Enables the set_maximizable command without any pre-configured scope. - -## deny-set-maximizable - -Denies the set_maximizable command without any pre-configured scope. - -## allow-set-min-size - -Enables the set_min_size command without any pre-configured scope. - -## deny-set-min-size - -Denies the set_min_size command without any pre-configured scope. - -## allow-set-minimizable - -Enables the set_minimizable command without any pre-configured scope. - -## deny-set-minimizable - -Denies the set_minimizable command without any pre-configured scope. - -## allow-set-position - -Enables the set_position command without any pre-configured scope. - -## deny-set-position - -Denies the set_position command without any pre-configured scope. - -## allow-set-progress-bar - -Enables the set_progress_bar command without any pre-configured scope. - -## deny-set-progress-bar - -Denies the set_progress_bar command without any pre-configured scope. - -## allow-set-resizable - -Enables the set_resizable command without any pre-configured scope. - -## deny-set-resizable - -Denies the set_resizable command without any pre-configured scope. - -## allow-set-shadow - -Enables the set_shadow command without any pre-configured scope. - -## deny-set-shadow - -Denies the set_shadow command without any pre-configured scope. - -## allow-set-size - -Enables the set_size command without any pre-configured scope. - -## deny-set-size - -Denies the set_size command without any pre-configured scope. - -## allow-set-skip-taskbar - -Enables the set_skip_taskbar command without any pre-configured scope. - -## deny-set-skip-taskbar - -Denies the set_skip_taskbar command without any pre-configured scope. - -## allow-set-title - -Enables the set_title command without any pre-configured scope. - -## deny-set-title - -Denies the set_title command without any pre-configured scope. - -## allow-set-visible-on-all-workspaces - -Enables the set_visible_on_all_workspaces command without any pre-configured scope. - -## deny-set-visible-on-all-workspaces - -Denies the set_visible_on_all_workspaces command without any pre-configured scope. - -## allow-show - -Enables the show command without any pre-configured scope. - -## deny-show - -Denies the show command without any pre-configured scope. - -## allow-start-dragging - -Enables the start_dragging command without any pre-configured scope. - -## deny-start-dragging - -Denies the start_dragging command without any pre-configured scope. - -## allow-theme - -Enables the theme command without any pre-configured scope. - -## deny-theme - -Denies the theme command without any pre-configured scope. - -## allow-title - -Enables the title command without any pre-configured scope. - -## deny-title - -Denies the title command without any pre-configured scope. - -## allow-toggle-maximize - -Enables the toggle_maximize command without any pre-configured scope. - -## deny-toggle-maximize - -Denies the toggle_maximize command without any pre-configured scope. - -## allow-unmaximize - -Enables the unmaximize command without any pre-configured scope. - -## deny-unmaximize - -Denies the unmaximize command without any pre-configured scope. - -## allow-unminimize - -Enables the unminimize command without any pre-configured scope. - -## deny-unminimize - -Denies the unminimize command without any pre-configured scope. - -## default - -Default permissions for the plugin. - +| Permission | Description | +|------|-----| +|`allow-available-monitors`|Enables the available_monitors command without any pre-configured scope.| +|`deny-available-monitors`|Denies the available_monitors command without any pre-configured scope.| +|`allow-center`|Enables the center command without any pre-configured scope.| +|`deny-center`|Denies the center command without any pre-configured scope.| +|`allow-close`|Enables the close command without any pre-configured scope.| +|`deny-close`|Denies the close command without any pre-configured scope.| +|`allow-create`|Enables the create command without any pre-configured scope.| +|`deny-create`|Denies the create command without any pre-configured scope.| +|`allow-current-monitor`|Enables the current_monitor command without any pre-configured scope.| +|`deny-current-monitor`|Denies the current_monitor command without any pre-configured scope.| +|`allow-destroy`|Enables the destroy command without any pre-configured scope.| +|`deny-destroy`|Denies the destroy command without any pre-configured scope.| +|`allow-hide`|Enables the hide command without any pre-configured scope.| +|`deny-hide`|Denies the hide command without any pre-configured scope.| +|`allow-inner-position`|Enables the inner_position command without any pre-configured scope.| +|`deny-inner-position`|Denies the inner_position command without any pre-configured scope.| +|`allow-inner-size`|Enables the inner_size command without any pre-configured scope.| +|`deny-inner-size`|Denies the inner_size command without any pre-configured scope.| +|`allow-internal-toggle-maximize`|Enables the internal_toggle_maximize command without any pre-configured scope.| +|`deny-internal-toggle-maximize`|Denies the internal_toggle_maximize command without any pre-configured scope.| +|`allow-is-closable`|Enables the is_closable command without any pre-configured scope.| +|`deny-is-closable`|Denies the is_closable command without any pre-configured scope.| +|`allow-is-decorated`|Enables the is_decorated command without any pre-configured scope.| +|`deny-is-decorated`|Denies the is_decorated command without any pre-configured scope.| +|`allow-is-focused`|Enables the is_focused command without any pre-configured scope.| +|`deny-is-focused`|Denies the is_focused command without any pre-configured scope.| +|`allow-is-fullscreen`|Enables the is_fullscreen command without any pre-configured scope.| +|`deny-is-fullscreen`|Denies the is_fullscreen command without any pre-configured scope.| +|`allow-is-maximizable`|Enables the is_maximizable command without any pre-configured scope.| +|`deny-is-maximizable`|Denies the is_maximizable command without any pre-configured scope.| +|`allow-is-maximized`|Enables the is_maximized command without any pre-configured scope.| +|`deny-is-maximized`|Denies the is_maximized command without any pre-configured scope.| +|`allow-is-minimizable`|Enables the is_minimizable command without any pre-configured scope.| +|`deny-is-minimizable`|Denies the is_minimizable command without any pre-configured scope.| +|`allow-is-minimized`|Enables the is_minimized command without any pre-configured scope.| +|`deny-is-minimized`|Denies the is_minimized command without any pre-configured scope.| +|`allow-is-resizable`|Enables the is_resizable command without any pre-configured scope.| +|`deny-is-resizable`|Denies the is_resizable command without any pre-configured scope.| +|`allow-is-visible`|Enables the is_visible command without any pre-configured scope.| +|`deny-is-visible`|Denies the is_visible command without any pre-configured scope.| +|`allow-maximize`|Enables the maximize command without any pre-configured scope.| +|`deny-maximize`|Denies the maximize command without any pre-configured scope.| +|`allow-minimize`|Enables the minimize command without any pre-configured scope.| +|`deny-minimize`|Denies the minimize command without any pre-configured scope.| +|`allow-outer-position`|Enables the outer_position command without any pre-configured scope.| +|`deny-outer-position`|Denies the outer_position command without any pre-configured scope.| +|`allow-outer-size`|Enables the outer_size command without any pre-configured scope.| +|`deny-outer-size`|Denies the outer_size command without any pre-configured scope.| +|`allow-primary-monitor`|Enables the primary_monitor command without any pre-configured scope.| +|`deny-primary-monitor`|Denies the primary_monitor command without any pre-configured scope.| +|`allow-request-user-attention`|Enables the request_user_attention command without any pre-configured scope.| +|`deny-request-user-attention`|Denies the request_user_attention command without any pre-configured scope.| +|`allow-scale-factor`|Enables the scale_factor command without any pre-configured scope.| +|`deny-scale-factor`|Denies the scale_factor command without any pre-configured scope.| +|`allow-set-always-on-bottom`|Enables the set_always_on_bottom command without any pre-configured scope.| +|`deny-set-always-on-bottom`|Denies the set_always_on_bottom command without any pre-configured scope.| +|`allow-set-always-on-top`|Enables the set_always_on_top command without any pre-configured scope.| +|`deny-set-always-on-top`|Denies the set_always_on_top command without any pre-configured scope.| +|`allow-set-closable`|Enables the set_closable command without any pre-configured scope.| +|`deny-set-closable`|Denies the set_closable command without any pre-configured scope.| +|`allow-set-content-protected`|Enables the set_content_protected command without any pre-configured scope.| +|`deny-set-content-protected`|Denies the set_content_protected command without any pre-configured scope.| +|`allow-set-cursor-grab`|Enables the set_cursor_grab command without any pre-configured scope.| +|`deny-set-cursor-grab`|Denies the set_cursor_grab command without any pre-configured scope.| +|`allow-set-cursor-icon`|Enables the set_cursor_icon command without any pre-configured scope.| +|`deny-set-cursor-icon`|Denies the set_cursor_icon command without any pre-configured scope.| +|`allow-set-cursor-position`|Enables the set_cursor_position command without any pre-configured scope.| +|`deny-set-cursor-position`|Denies the set_cursor_position command without any pre-configured scope.| +|`allow-set-cursor-visible`|Enables the set_cursor_visible command without any pre-configured scope.| +|`deny-set-cursor-visible`|Denies the set_cursor_visible command without any pre-configured scope.| +|`allow-set-decorations`|Enables the set_decorations command without any pre-configured scope.| +|`deny-set-decorations`|Denies the set_decorations command without any pre-configured scope.| +|`allow-set-effects`|Enables the set_effects command without any pre-configured scope.| +|`deny-set-effects`|Denies the set_effects command without any pre-configured scope.| +|`allow-set-focus`|Enables the set_focus command without any pre-configured scope.| +|`deny-set-focus`|Denies the set_focus command without any pre-configured scope.| +|`allow-set-fullscreen`|Enables the set_fullscreen command without any pre-configured scope.| +|`deny-set-fullscreen`|Denies the set_fullscreen command without any pre-configured scope.| +|`allow-set-icon`|Enables the set_icon command without any pre-configured scope.| +|`deny-set-icon`|Denies the set_icon command without any pre-configured scope.| +|`allow-set-ignore-cursor-events`|Enables the set_ignore_cursor_events command without any pre-configured scope.| +|`deny-set-ignore-cursor-events`|Denies the set_ignore_cursor_events command without any pre-configured scope.| +|`allow-set-max-size`|Enables the set_max_size command without any pre-configured scope.| +|`deny-set-max-size`|Denies the set_max_size command without any pre-configured scope.| +|`allow-set-maximizable`|Enables the set_maximizable command without any pre-configured scope.| +|`deny-set-maximizable`|Denies the set_maximizable command without any pre-configured scope.| +|`allow-set-min-size`|Enables the set_min_size command without any pre-configured scope.| +|`deny-set-min-size`|Denies the set_min_size command without any pre-configured scope.| +|`allow-set-minimizable`|Enables the set_minimizable command without any pre-configured scope.| +|`deny-set-minimizable`|Denies the set_minimizable command without any pre-configured scope.| +|`allow-set-position`|Enables the set_position command without any pre-configured scope.| +|`deny-set-position`|Denies the set_position command without any pre-configured scope.| +|`allow-set-progress-bar`|Enables the set_progress_bar command without any pre-configured scope.| +|`deny-set-progress-bar`|Denies the set_progress_bar command without any pre-configured scope.| +|`allow-set-resizable`|Enables the set_resizable command without any pre-configured scope.| +|`deny-set-resizable`|Denies the set_resizable command without any pre-configured scope.| +|`allow-set-shadow`|Enables the set_shadow command without any pre-configured scope.| +|`deny-set-shadow`|Denies the set_shadow command without any pre-configured scope.| +|`allow-set-size`|Enables the set_size command without any pre-configured scope.| +|`deny-set-size`|Denies the set_size command without any pre-configured scope.| +|`allow-set-skip-taskbar`|Enables the set_skip_taskbar command without any pre-configured scope.| +|`deny-set-skip-taskbar`|Denies the set_skip_taskbar command without any pre-configured scope.| +|`allow-set-title`|Enables the set_title command without any pre-configured scope.| +|`deny-set-title`|Denies the set_title command without any pre-configured scope.| +|`allow-set-visible-on-all-workspaces`|Enables the set_visible_on_all_workspaces command without any pre-configured scope.| +|`deny-set-visible-on-all-workspaces`|Denies the set_visible_on_all_workspaces command without any pre-configured scope.| +|`allow-show`|Enables the show command without any pre-configured scope.| +|`deny-show`|Denies the show command without any pre-configured scope.| +|`allow-start-dragging`|Enables the start_dragging command without any pre-configured scope.| +|`deny-start-dragging`|Denies the start_dragging command without any pre-configured scope.| +|`allow-theme`|Enables the theme command without any pre-configured scope.| +|`deny-theme`|Denies the theme command without any pre-configured scope.| +|`allow-title`|Enables the title command without any pre-configured scope.| +|`deny-title`|Denies the title command without any pre-configured scope.| +|`allow-toggle-maximize`|Enables the toggle_maximize command without any pre-configured scope.| +|`deny-toggle-maximize`|Denies the toggle_maximize command without any pre-configured scope.| +|`allow-unmaximize`|Enables the unmaximize command without any pre-configured scope.| +|`deny-unmaximize`|Denies the unmaximize command without any pre-configured scope.| +|`allow-unminimize`|Enables the unminimize command without any pre-configured scope.| +|`deny-unminimize`|Denies the unminimize command without any pre-configured scope.| +|`default`|Default permissions for the plugin.| diff --git a/core/tauri/scripts/bundle.global.js b/core/tauri/scripts/bundle.global.js index 3531a725c..64810f78b 100644 --- a/core/tauri/scripts/bundle.global.js +++ b/core/tauri/scripts/bundle.global.js @@ -1 +1 @@ -var __TAURI_IIFE__=function(e){"use strict";function t(e,t,n,i){if("a"===n&&!i)throw new TypeError("Private accessor was defined without a getter");if("function"==typeof t?e!==t||!i:!t.has(e))throw new TypeError("Cannot read private member from an object whose class did not declare it");return"m"===n?i:"a"===n?i.call(e):i?i.value:t.get(e)}function n(e,t,n,i,r){if("m"===i)throw new TypeError("Private method is not writable");if("a"===i&&!r)throw new TypeError("Private accessor was defined without a setter");if("function"==typeof t?e!==t||!r:!t.has(e))throw new TypeError("Cannot write private member to an object whose class did not declare it");return"a"===i?r.call(e,n):r?r.value=n:t.set(e,n),n}var i,r;function a(e,t=!1){return window.__TAURI_INTERNALS__.transformCallback(e,t)}"function"==typeof SuppressedError&&SuppressedError;class s{constructor(){this.__TAURI_CHANNEL_MARKER__=!0,i.set(this,(()=>{})),this.id=a((e=>{t(this,i,"f").call(this,e)}))}set onmessage(e){n(this,i,e,"f")}get onmessage(){return t(this,i,"f")}toJSON(){return`__CHANNEL__:${this.id}`}}i=new WeakMap;class l{constructor(e,t,n){this.plugin=e,this.event=t,this.channelId=n}async unregister(){return o(`plugin:${this.plugin}|remove_listener`,{event:this.event,channelId:this.channelId})}}async function o(e,t={},n){return window.__TAURI_INTERNALS__.invoke(e,t,n)}class u{get rid(){return t(this,r,"f")}constructor(e){r.set(this,void 0),n(this,r,e,"f")}async close(){return o("plugin:resources|close",{rid:this.rid})}}r=new WeakMap;var c=Object.freeze({__proto__:null,Channel:s,PluginListener:l,Resource:u,addPluginListener:async function(e,t,n){const i=new s;return i.onmessage=n,o(`plugin:${e}|register_listener`,{event:t,handler:i}).then((()=>new l(e,t,i.id)))},convertFileSrc:function(e,t="asset"){return window.__TAURI_INTERNALS__.convertFileSrc(e,t)},invoke:o,transformCallback:a});var d,p=Object.freeze({__proto__:null,getName:async function(){return o("plugin:app|name")},getTauriVersion:async function(){return o("plugin:app|tauri_version")},getVersion:async function(){return o("plugin:app|version")},hide:async function(){return o("plugin:app|app_hide")},show:async function(){return o("plugin:app|app_show")}});async function h(e,t){await o("plugin:event|unlisten",{event:e,eventId:t})}async function y(e,t,n){const i="string"==typeof n?.target?{kind:"AnyLabel",label:n.target}:n?.target??{kind:"Any"};return o("plugin:event|listen",{event:e,target:i,handler:a(t)}).then((t=>async()=>h(e,t)))}async function w(e,t,n){return y(e,(n=>{t(n),h(e,n.id).catch((()=>{}))}),n)}async function g(e,t){await o("plugin:event|emit",{event:e,payload:t})}async function b(e,t,n){const i="string"==typeof e?{kind:"AnyLabel",label:e}:e;await o("plugin:event|emit_to",{target:i,event:t,payload:n})}!function(e){e.WINDOW_RESIZED="tauri://resize",e.WINDOW_MOVED="tauri://move",e.WINDOW_CLOSE_REQUESTED="tauri://close-requested",e.WINDOW_DESTROYED="tauri://destroyed",e.WINDOW_FOCUS="tauri://focus",e.WINDOW_BLUR="tauri://blur",e.WINDOW_SCALE_FACTOR_CHANGED="tauri://scale-change",e.WINDOW_THEME_CHANGED="tauri://theme-changed",e.WEBVIEW_CREATED="tauri://webview-created",e.WEBVIEW_FILE_DROP="tauri://file-drop",e.WEBVIEW_FILE_DROP_HOVER="tauri://file-drop-hover",e.WEBVIEW_FILE_DROP_CANCELLED="tauri://file-drop-cancelled"}(d||(d={}));var _=Object.freeze({__proto__:null,get TauriEvent(){return d},emit:g,emitTo:b,listen:y,once:w});class m{constructor(e,t){this.type="Logical",this.width=e,this.height=t}}class f{constructor(e,t){this.type="Physical",this.width=e,this.height=t}toLogical(e){return new m(this.width/e,this.height/e)}}class v{constructor(e,t){this.type="Logical",this.x=e,this.y=t}}class k{constructor(e,t){this.type="Physical",this.x=e,this.y=t}toLogical(e){return new v(this.x/e,this.y/e)}}var E,A,D=Object.freeze({__proto__:null,LogicalPosition:v,LogicalSize:m,PhysicalPosition:k,PhysicalSize:f});!function(e){e[e.Critical=1]="Critical",e[e.Informational=2]="Informational"}(E||(E={}));class P{constructor(e){this._preventDefault=!1,this.event=e.event,this.id=e.id}preventDefault(){this._preventDefault=!0}isPreventDefault(){return this._preventDefault}}function L(){return new T(window.__TAURI_INTERNALS__.metadata.currentWindow.label,{skip:!0})}function I(){return window.__TAURI_INTERNALS__.metadata.windows.map((e=>new T(e.label,{skip:!0})))}!function(e){e.None="none",e.Normal="normal",e.Indeterminate="indeterminate",e.Paused="paused",e.Error="error"}(A||(A={}));const S=["tauri://created","tauri://error"];class T{constructor(e,t={}){this.label=e,this.listeners=Object.create(null),t?.skip||o("plugin:window|create",{options:{...t,parent:"string"==typeof t.parent?t.parent:t.parent?.label,label:e}}).then((async()=>this.emit("tauri://created"))).catch((async e=>this.emit("tauri://error",e)))}static getByLabel(e){return I().find((t=>t.label===e))??null}static getCurrent(){return L()}static getAll(){return I()}static async getFocusedWindow(){for(const e of I())if(await e.isFocused())return e;return null}async listen(e,t){return this._handleTauriEvent(e,t)?Promise.resolve((()=>{const n=this.listeners[e];n.splice(n.indexOf(t),1)})):y(e,t,{target:{kind:"Window",label:this.label}})}async once(e,t){return this._handleTauriEvent(e,t)?Promise.resolve((()=>{const n=this.listeners[e];n.splice(n.indexOf(t),1)})):w(e,t,{target:{kind:"Window",label:this.label}})}async emit(e,t){if(S.includes(e)){for(const n of this.listeners[e]||[])n({event:e,id:-1,payload:t});return Promise.resolve()}return g(e,t)}async emitTo(e,t,n){if(S.includes(t)){for(const e of this.listeners[t]||[])e({event:t,id:-1,payload:n});return Promise.resolve()}return b(e,t,n)}_handleTauriEvent(e,t){return!!S.includes(e)&&(e in this.listeners?this.listeners[e].push(t):this.listeners[e]=[t],!0)}async scaleFactor(){return o("plugin:window|scale_factor",{label:this.label})}async innerPosition(){return o("plugin:window|inner_position",{label:this.label}).then((({x:e,y:t})=>new k(e,t)))}async outerPosition(){return o("plugin:window|outer_position",{label:this.label}).then((({x:e,y:t})=>new k(e,t)))}async innerSize(){return o("plugin:window|inner_size",{label:this.label}).then((({width:e,height:t})=>new f(e,t)))}async outerSize(){return o("plugin:window|outer_size",{label:this.label}).then((({width:e,height:t})=>new f(e,t)))}async isFullscreen(){return o("plugin:window|is_fullscreen",{label:this.label})}async isMinimized(){return o("plugin:window|is_minimized",{label:this.label})}async isMaximized(){return o("plugin:window|is_maximized",{label:this.label})}async isFocused(){return o("plugin:window|is_focused",{label:this.label})}async isDecorated(){return o("plugin:window|is_decorated",{label:this.label})}async isResizable(){return o("plugin:window|is_resizable",{label:this.label})}async isMaximizable(){return o("plugin:window|is_maximizable",{label:this.label})}async isMinimizable(){return o("plugin:window|is_minimizable",{label:this.label})}async isClosable(){return o("plugin:window|is_closable",{label:this.label})}async isVisible(){return o("plugin:window|is_visible",{label:this.label})}async title(){return o("plugin:window|title",{label:this.label})}async theme(){return o("plugin:window|theme",{label:this.label})}async center(){return o("plugin:window|center",{label:this.label})}async requestUserAttention(e){let t=null;return e&&(t=e===E.Critical?{type:"Critical"}:{type:"Informational"}),o("plugin:window|request_user_attention",{label:this.label,value:t})}async setResizable(e){return o("plugin:window|set_resizable",{label:this.label,value:e})}async setMaximizable(e){return o("plugin:window|set_maximizable",{label:this.label,value:e})}async setMinimizable(e){return o("plugin:window|set_minimizable",{label:this.label,value:e})}async setClosable(e){return o("plugin:window|set_closable",{label:this.label,value:e})}async setTitle(e){return o("plugin:window|set_title",{label:this.label,value:e})}async maximize(){return o("plugin:window|maximize",{label:this.label})}async unmaximize(){return o("plugin:window|unmaximize",{label:this.label})}async toggleMaximize(){return o("plugin:window|toggle_maximize",{label:this.label})}async minimize(){return o("plugin:window|minimize",{label:this.label})}async unminimize(){return o("plugin:window|unminimize",{label:this.label})}async show(){return o("plugin:window|show",{label:this.label})}async hide(){return o("plugin:window|hide",{label:this.label})}async close(){return o("plugin:window|close",{label:this.label})}async destroy(){return o("plugin:window|destroy",{label:this.label})}async setDecorations(e){return o("plugin:window|set_decorations",{label:this.label,value:e})}async setShadow(e){return o("plugin:window|set_shadow",{label:this.label,value:e})}async setEffects(e){return o("plugin:window|set_effects",{label:this.label,value:e})}async clearEffects(){return o("plugin:window|set_effects",{label:this.label,value:null})}async setAlwaysOnTop(e){return o("plugin:window|set_always_on_top",{label:this.label,value:e})}async setAlwaysOnBottom(e){return o("plugin:window|set_always_on_bottom",{label:this.label,value:e})}async setContentProtected(e){return o("plugin:window|set_content_protected",{label:this.label,value:e})}async setSize(e){if(!e||"Logical"!==e.type&&"Physical"!==e.type)throw new Error("the `size` argument must be either a LogicalSize or a PhysicalSize instance");return o("plugin:window|set_size",{label:this.label,value:{type:e.type,data:{width:e.width,height:e.height}}})}async setMinSize(e){if(e&&"Logical"!==e.type&&"Physical"!==e.type)throw new Error("the `size` argument must be either a LogicalSize or a PhysicalSize instance");return o("plugin:window|set_min_size",{label:this.label,value:e?{type:e.type,data:{width:e.width,height:e.height}}:null})}async setMaxSize(e){if(e&&"Logical"!==e.type&&"Physical"!==e.type)throw new Error("the `size` argument must be either a LogicalSize or a PhysicalSize instance");return o("plugin:window|set_max_size",{label:this.label,value:e?{type:e.type,data:{width:e.width,height:e.height}}:null})}async setPosition(e){if(!e||"Logical"!==e.type&&"Physical"!==e.type)throw new Error("the `position` argument must be either a LogicalPosition or a PhysicalPosition instance");return o("plugin:window|set_position",{label:this.label,value:{type:e.type,data:{x:e.x,y:e.y}}})}async setFullscreen(e){return o("plugin:window|set_fullscreen",{label:this.label,value:e})}async setFocus(){return o("plugin:window|set_focus",{label:this.label})}async setIcon(e){return o("plugin:window|set_icon",{label:this.label,value:"string"==typeof e?e:Array.from(e)})}async setSkipTaskbar(e){return o("plugin:window|set_skip_taskbar",{label:this.label,value:e})}async setCursorGrab(e){return o("plugin:window|set_cursor_grab",{label:this.label,value:e})}async setCursorVisible(e){return o("plugin:window|set_cursor_visible",{label:this.label,value:e})}async setCursorIcon(e){return o("plugin:window|set_cursor_icon",{label:this.label,value:e})}async setCursorPosition(e){if(!e||"Logical"!==e.type&&"Physical"!==e.type)throw new Error("the `position` argument must be either a LogicalPosition or a PhysicalPosition instance");return o("plugin:window|set_cursor_position",{label:this.label,value:{type:e.type,data:{x:e.x,y:e.y}}})}async setIgnoreCursorEvents(e){return o("plugin:window|set_ignore_cursor_events",{label:this.label,value:e})}async startDragging(){return o("plugin:window|start_dragging",{label:this.label})}async startResizeDragging(e){return o("plugin:window|start_resize_dragging",{label:this.label,value:e})}async setProgressBar(e){return o("plugin:window|set_progress_bar",{label:this.label,value:e})}async setVisibleOnAllWorkspaces(e){return o("plugin:window|set_visible_on_all_workspaces",{label:this.label,value:e})}async onResized(e){return this.listen(d.WINDOW_RESIZED,(t=>{t.payload=R(t.payload),e(t)}))}async onMoved(e){return this.listen(d.WINDOW_MOVED,(t=>{t.payload=W(t.payload),e(t)}))}async onCloseRequested(e){return this.listen(d.WINDOW_CLOSE_REQUESTED,(t=>{const n=new P(t);Promise.resolve(e(n)).then((()=>{if(!n.isPreventDefault())return this.destroy()}))}))}async onFocusChanged(e){const t=await this.listen(d.WINDOW_FOCUS,(t=>{e({...t,payload:!0})})),n=await this.listen(d.WINDOW_BLUR,(t=>{e({...t,payload:!1})}));return()=>{t(),n()}}async onScaleChanged(e){return this.listen(d.WINDOW_SCALE_FACTOR_CHANGED,e)}async onThemeChanged(e){return this.listen(d.WINDOW_THEME_CHANGED,e)}}var C,x;function z(e){return null===e?null:{name:e.name,scaleFactor:e.scaleFactor,position:W(e.position),size:R(e.size)}}function W(e){return new k(e.x,e.y)}function R(e){return new f(e.width,e.height)}!function(e){e.AppearanceBased="appearanceBased",e.Light="light",e.Dark="dark",e.MediumLight="mediumLight",e.UltraDark="ultraDark",e.Titlebar="titlebar",e.Selection="selection",e.Menu="menu",e.Popover="popover",e.Sidebar="sidebar",e.HeaderView="headerView",e.Sheet="sheet",e.WindowBackground="windowBackground",e.HudWindow="hudWindow",e.FullScreenUI="fullScreenUI",e.Tooltip="tooltip",e.ContentBackground="contentBackground",e.UnderWindowBackground="underWindowBackground",e.UnderPageBackground="underPageBackground",e.Mica="mica",e.Blur="blur",e.Acrylic="acrylic",e.Tabbed="tabbed",e.TabbedDark="tabbedDark",e.TabbedLight="tabbedLight"}(C||(C={})),function(e){e.FollowsWindowActiveState="followsWindowActiveState",e.Active="active",e.Inactive="inactive"}(x||(x={}));var F=Object.freeze({__proto__:null,CloseRequestedEvent:P,get Effect(){return C},get EffectState(){return x},LogicalPosition:v,LogicalSize:m,PhysicalPosition:k,PhysicalSize:f,get ProgressBarStatus(){return A},get UserAttentionType(){return E},Window:T,availableMonitors:async function(){return o("plugin:window|available_monitors").then((e=>e.map(z)))},currentMonitor:async function(){return o("plugin:window|current_monitor").then(z)},getAll:I,getCurrent:L,primaryMonitor:async function(){return o("plugin:window|primary_monitor").then(z)}});function O(){return new U(L(),window.__TAURI_INTERNALS__.metadata.currentWebview.label,{skip:!0})}function N(){return window.__TAURI_INTERNALS__.metadata.webviews.map((e=>new U(T.getByLabel(e.windowLabel),e.label,{skip:!0})))}const M=["tauri://created","tauri://error"];class U{constructor(e,t,n){this.window=e,this.label=t,this.listeners=Object.create(null),n?.skip||o("plugin:webview|create_webview",{windowLabel:e.label,label:t,options:n}).then((async()=>this.emit("tauri://created"))).catch((async e=>this.emit("tauri://error",e)))}static getByLabel(e){return N().find((t=>t.label===e))??null}static getCurrent(){return O()}static getAll(){return N()}async listen(e,t){return this._handleTauriEvent(e,t)?Promise.resolve((()=>{const n=this.listeners[e];n.splice(n.indexOf(t),1)})):y(e,t,{target:{kind:"Webview",label:this.label}})}async once(e,t){return this._handleTauriEvent(e,t)?Promise.resolve((()=>{const n=this.listeners[e];n.splice(n.indexOf(t),1)})):w(e,t,{target:{kind:"Webview",label:this.label}})}async emit(e,t){if(M.includes(e)){for(const n of this.listeners[e]||[])n({event:e,id:-1,payload:t});return Promise.resolve()}return g(e,t)}async emitTo(e,t,n){if(M.includes(t)){for(const e of this.listeners[t]||[])e({event:t,id:-1,payload:n});return Promise.resolve()}return b(e,t,n)}_handleTauriEvent(e,t){return!!M.includes(e)&&(e in this.listeners?this.listeners[e].push(t):this.listeners[e]=[t],!0)}async position(){return o("plugin:webview|webview_position",{label:this.label}).then((({x:e,y:t})=>new k(e,t)))}async size(){return o("plugin:webview|webview_size",{label:this.label}).then((({width:e,height:t})=>new f(e,t)))}async close(){return o("plugin:webview|close",{label:this.label})}async setSize(e){if(!e||"Logical"!==e.type&&"Physical"!==e.type)throw new Error("the `size` argument must be either a LogicalSize or a PhysicalSize instance");return o("plugin:webview|set_webview_size",{label:this.label,value:{type:e.type,data:{width:e.width,height:e.height}}})}async setPosition(e){if(!e||"Logical"!==e.type&&"Physical"!==e.type)throw new Error("the `position` argument must be either a LogicalPosition or a PhysicalPosition instance");return o("plugin:webview|set_webview_position",{label:this.label,value:{type:e.type,data:{x:e.x,y:e.y}}})}async setFocus(){return o("plugin:webview|set_webview_focus",{label:this.label})}async onFileDropEvent(e){const t=await this.listen(d.WEBVIEW_FILE_DROP,(t=>{e({...t,payload:{type:"drop",paths:t.payload.paths,position:B(t.payload.position)}})})),n=await this.listen(d.WEBVIEW_FILE_DROP_HOVER,(t=>{e({...t,payload:{type:"hover",paths:t.payload.paths,position:B(t.payload.position)}})})),i=await this.listen(d.WEBVIEW_FILE_DROP_CANCELLED,(t=>{e({...t,payload:{type:"cancel"}})}));return()=>{t(),n(),i()}}}function B(e){return new k(e.x,e.y)}class V{constructor(e,t={}){this.label=e,this.listeners=Object.create(null),t?.skip||o("plugin:webview|create_webview_window",{options:{...t,parent:"string"==typeof t.parent?t.parent:t.parent?.label,label:e}}).then((async()=>this.emit("tauri://created"))).catch((async e=>this.emit("tauri://error",e)))}static getByLabel(e){const t=N().find((t=>t.label===e))??null;return t?new V(t.label,{skip:!0}):null}static getCurrent(){const e=O();return new V(e.label,{skip:!0})}static getAll(){return N().map((e=>new V(e.label,{skip:!0})))}async listen(e,t){return this._handleTauriEvent(e,t)?Promise.resolve((()=>{const n=this.listeners[e];n.splice(n.indexOf(t),1)})):y(e,t,{target:{kind:"WebviewWindow",label:this.label}})}async once(e,t){return this._handleTauriEvent(e,t)?Promise.resolve((()=>{const n=this.listeners[e];n.splice(n.indexOf(t),1)})):w(e,t,{target:{kind:"WebviewWindow",label:this.label}})}}var j,H;j=V,H=[T,U],(Array.isArray(H)?H:[H]).forEach((e=>{Object.getOwnPropertyNames(e.prototype).forEach((t=>{"object"==typeof j.prototype&&j.prototype&&t in j.prototype||Object.defineProperty(j.prototype,t,Object.getOwnPropertyDescriptor(e.prototype,t)??Object.create(null))}))}));var G,q=Object.freeze({__proto__:null,Webview:U,WebviewWindow:V,getAll:N,getCurrent:O});!function(e){e[e.Audio=1]="Audio",e[e.Cache=2]="Cache",e[e.Config=3]="Config",e[e.Data=4]="Data",e[e.LocalData=5]="LocalData",e[e.Document=6]="Document",e[e.Download=7]="Download",e[e.Picture=8]="Picture",e[e.Public=9]="Public",e[e.Video=10]="Video",e[e.Resource=11]="Resource",e[e.Temp=12]="Temp",e[e.AppConfig=13]="AppConfig",e[e.AppData=14]="AppData",e[e.AppLocalData=15]="AppLocalData",e[e.AppCache=16]="AppCache",e[e.AppLog=17]="AppLog",e[e.Desktop=18]="Desktop",e[e.Executable=19]="Executable",e[e.Font=20]="Font",e[e.Home=21]="Home",e[e.Runtime=22]="Runtime",e[e.Template=23]="Template"}(G||(G={}));var Q=Object.freeze({__proto__:null,get BaseDirectory(){return G},appCacheDir:async function(){return o("plugin:path|resolve_directory",{directory:G.AppCache})},appConfigDir:async function(){return o("plugin:path|resolve_directory",{directory:G.AppConfig})},appDataDir:async function(){return o("plugin:path|resolve_directory",{directory:G.AppData})},appLocalDataDir:async function(){return o("plugin:path|resolve_directory",{directory:G.AppLocalData})},appLogDir:async function(){return o("plugin:path|resolve_directory",{directory:G.AppLog})},audioDir:async function(){return o("plugin:path|resolve_directory",{directory:G.Audio})},basename:async function(e,t){return o("plugin:path|basename",{path:e,ext:t})},cacheDir:async function(){return o("plugin:path|resolve_directory",{directory:G.Cache})},configDir:async function(){return o("plugin:path|resolve_directory",{directory:G.Config})},dataDir:async function(){return o("plugin:path|resolve_directory",{directory:G.Data})},delimiter:function(){return window.__TAURI_INTERNALS__.plugins.path.delimiter},desktopDir:async function(){return o("plugin:path|resolve_directory",{directory:G.Desktop})},dirname:async function(e){return o("plugin:path|dirname",{path:e})},documentDir:async function(){return o("plugin:path|resolve_directory",{directory:G.Document})},downloadDir:async function(){return o("plugin:path|resolve_directory",{directory:G.Download})},executableDir:async function(){return o("plugin:path|resolve_directory",{directory:G.Executable})},extname:async function(e){return o("plugin:path|extname",{path:e})},fontDir:async function(){return o("plugin:path|resolve_directory",{directory:G.Font})},homeDir:async function(){return o("plugin:path|resolve_directory",{directory:G.Home})},isAbsolute:async function(e){return o("plugin:path|isAbsolute",{path:e})},join:async function(...e){return o("plugin:path|join",{paths:e})},localDataDir:async function(){return o("plugin:path|resolve_directory",{directory:G.LocalData})},normalize:async function(e){return o("plugin:path|normalize",{path:e})},pictureDir:async function(){return o("plugin:path|resolve_directory",{directory:G.Picture})},publicDir:async function(){return o("plugin:path|resolve_directory",{directory:G.Public})},resolve:async function(...e){return o("plugin:path|resolve",{paths:e})},resolveResource:async function(e){return o("plugin:path|resolve_directory",{directory:G.Resource,path:e})},resourceDir:async function(){return o("plugin:path|resolve_directory",{directory:G.Resource})},runtimeDir:async function(){return o("plugin:path|resolve_directory",{directory:G.Runtime})},sep:function(){return window.__TAURI_INTERNALS__.plugins.path.sep},tempDir:async function(){return o("plugin:path|resolve_directory",{directory:G.Temp})},templateDir:async function(){return o("plugin:path|resolve_directory",{directory:G.Template})},videoDir:async function(){return o("plugin:path|resolve_directory",{directory:G.Video})}});class $ extends u{constructor(e,t){super(e),this.id=t}static async new(e){e?.menu&&(e.menu=[e.menu.rid,e.menu.kind]),e?.icon&&(e.icon="string"==typeof e.icon?e.icon:Array.from(e.icon));const t=new s;return e?.action&&(t.onmessage=e.action,delete e.action),o("plugin:tray|new",{options:e??{},handler:t}).then((([e,t])=>new $(e,t)))}async setIcon(e){let t=null;return e&&(t="string"==typeof e?e:Array.from(e)),o("plugin:tray|set_icon",{rid:this.rid,icon:t})}async setMenu(e){return e&&(e=[e.rid,e.kind]),o("plugin:tray|set_menu",{rid:this.rid,menu:e})}async setTooltip(e){return o("plugin:tray|set_tooltip",{rid:this.rid,tooltip:e})}async setTitle(e){return o("plugin:tray|set_title",{rid:this.rid,title:e})}async setVisible(e){return o("plugin:tray|set_visible",{rid:this.rid,visible:e})}async setTempDirPath(e){return o("plugin:tray|set_temp_dir_path",{rid:this.rid,path:e})}async setIconAsTemplate(e){return o("plugin:tray|set_icon_as_template",{rid:this.rid,asTemplate:e})}async setMenuOnLeftClick(e){return o("plugin:tray|set_show_menu_on_left_click",{rid:this.rid,onLeft:e})}}var Z,J,K,Y=Object.freeze({__proto__:null,TrayIcon:$});function X(e){if("items"in e)e.items=e.items?.map((e=>"rid"in e?e:X(e)));else if("action"in e&&e.action){const t=new s;return t.onmessage=e.action,delete e.action,{...e,handler:t}}return e}async function ee(e,t){const n=new s;let i=null;return t&&"object"==typeof t&&("action"in t&&t.action&&(n.onmessage=t.action,delete t.action),"items"in t&&t.items&&(i=t.items.map((e=>"rid"in e?[e.rid,e.kind]:X(e))))),o("plugin:menu|new",{kind:e,options:t?{...t,items:i}:void 0,handler:n})}class te extends u{get id(){return t(this,Z,"f")}get kind(){return t(this,J,"f")}constructor(e,t,i){super(e),Z.set(this,void 0),J.set(this,void 0),n(this,Z,t,"f"),n(this,J,i,"f")}}Z=new WeakMap,J=new WeakMap;class ne extends te{constructor(e,t){super(e,t,"MenuItem")}static async new(e){return ee("MenuItem",e).then((([e,t])=>new ne(e,t)))}async text(){return o("plugin:menu|text",{rid:this.rid,kind:this.kind})}async setText(e){return o("plugin:menu|set_text",{rid:this.rid,kind:this.kind,text:e})}async isEnabled(){return o("plugin:menu|is_enabled",{rid:this.rid,kind:this.kind})}async setEnabled(e){return o("plugin:menu|set_enabled",{rid:this.rid,kind:this.kind,enabled:e})}async setAccelerator(e){return o("plugin:menu|set_accelerator",{rid:this.rid,kind:this.kind,accelerator:e})}}class ie extends te{constructor(e,t){super(e,t,"Check")}static async new(e){return ee("Check",e).then((([e,t])=>new ie(e,t)))}async text(){return o("plugin:menu|text",{rid:this.rid,kind:this.kind})}async setText(e){return o("plugin:menu|set_text",{rid:this.rid,kind:this.kind,text:e})}async isEnabled(){return o("plugin:menu|is_enabled",{rid:this.rid,kind:this.kind})}async setEnabled(e){return o("plugin:menu|set_enabled",{rid:this.rid,kind:this.kind,enabled:e})}async setAccelerator(e){return o("plugin:menu|set_accelerator",{rid:this.rid,kind:this.kind,accelerator:e})}async isChecked(){return o("plugin:menu|is_checked",{rid:this.rid})}async setChecked(e){return o("plugin:menu|set_checked",{rid:this.rid,checked:e})}}!function(e){e.Add="Add",e.Advanced="Advanced",e.Bluetooth="Bluetooth",e.Bookmarks="Bookmarks",e.Caution="Caution",e.ColorPanel="ColorPanel",e.ColumnView="ColumnView",e.Computer="Computer",e.EnterFullScreen="EnterFullScreen",e.Everyone="Everyone",e.ExitFullScreen="ExitFullScreen",e.FlowView="FlowView",e.Folder="Folder",e.FolderBurnable="FolderBurnable",e.FolderSmart="FolderSmart",e.FollowLinkFreestanding="FollowLinkFreestanding",e.FontPanel="FontPanel",e.GoLeft="GoLeft",e.GoRight="GoRight",e.Home="Home",e.IChatTheater="IChatTheater",e.IconView="IconView",e.Info="Info",e.InvalidDataFreestanding="InvalidDataFreestanding",e.LeftFacingTriangle="LeftFacingTriangle",e.ListView="ListView",e.LockLocked="LockLocked",e.LockUnlocked="LockUnlocked",e.MenuMixedState="MenuMixedState",e.MenuOnState="MenuOnState",e.MobileMe="MobileMe",e.MultipleDocuments="MultipleDocuments",e.Network="Network",e.Path="Path",e.PreferencesGeneral="PreferencesGeneral",e.QuickLook="QuickLook",e.RefreshFreestanding="RefreshFreestanding",e.Refresh="Refresh",e.Remove="Remove",e.RevealFreestanding="RevealFreestanding",e.RightFacingTriangle="RightFacingTriangle",e.Share="Share",e.Slideshow="Slideshow",e.SmartBadge="SmartBadge",e.StatusAvailable="StatusAvailable",e.StatusNone="StatusNone",e.StatusPartiallyAvailable="StatusPartiallyAvailable",e.StatusUnavailable="StatusUnavailable",e.StopProgressFreestanding="StopProgressFreestanding",e.StopProgress="StopProgress",e.TrashEmpty="TrashEmpty",e.TrashFull="TrashFull",e.User="User",e.UserAccounts="UserAccounts",e.UserGroup="UserGroup",e.UserGuest="UserGuest"}(K||(K={}));class re extends te{constructor(e,t){super(e,t,"Icon")}static async new(e){return ee("Icon",e).then((([e,t])=>new re(e,t)))}async text(){return o("plugin:menu|text",{rid:this.rid,kind:this.kind})}async setText(e){return o("plugin:menu|set_text",{rid:this.rid,kind:this.kind,text:e})}async isEnabled(){return o("plugin:menu|is_enabled",{rid:this.rid,kind:this.kind})}async setEnabled(e){return o("plugin:menu|set_enabled",{rid:this.rid,kind:this.kind,enabled:e})}async setAccelerator(e){return o("plugin:menu|set_accelerator",{rid:this.rid,kind:this.kind,accelerator:e})}async setIcon(e){return o("plugin:menu|set_icon",{rid:this.rid,icon:e})}}class ae extends te{constructor(e,t){super(e,t,"Predefined")}static async new(e){return ee("Predefined",e).then((([e,t])=>new ae(e,t)))}async text(){return o("plugin:menu|text",{rid:this.rid,kind:this.kind})}async setText(e){return o("plugin:menu|set_text",{rid:this.rid,kind:this.kind,text:e})}}function se([e,t,n]){switch(n){case"Submenu":return new le(e,t);case"Predefined":return new ae(e,t);case"Check":return new ie(e,t);case"Icon":return new re(e,t);default:return new ne(e,t)}}class le extends te{constructor(e,t){super(e,t,"Submenu")}static async new(e){return ee("Submenu",e).then((([e,t])=>new le(e,t)))}async text(){return o("plugin:menu|text",{rid:this.rid,kind:this.kind})}async setText(e){return o("plugin:menu|set_text",{rid:this.rid,kind:this.kind,text:e})}async isEnabled(){return o("plugin:menu|is_enabled",{rid:this.rid,kind:this.kind})}async setEnabled(e){return o("plugin:menu|set_enabled",{rid:this.rid,kind:this.kind,enabled:e})}async append(e){return o("plugin:menu|append",{rid:this.rid,kind:this.kind,items:(Array.isArray(e)?e:[e]).map((e=>"rid"in e?[e.rid,e.kind]:e))})}async prepend(e){return o("plugin:menu|prepend",{rid:this.rid,kind:this.kind,items:(Array.isArray(e)?e:[e]).map((e=>"rid"in e?[e.rid,e.kind]:e))})}async insert(e,t){return o("plugin:menu|insert",{rid:this.rid,kind:this.kind,items:(Array.isArray(e)?e:[e]).map((e=>"rid"in e?[e.rid,e.kind]:e)),position:t})}async remove(e){return o("plugin:menu|remove",{rid:this.rid,kind:this.kind,item:[e.rid,e.kind]})}async removeAt(e){return o("plugin:menu|remove_at",{rid:this.rid,kind:this.kind,position:e}).then(se)}async items(){return o("plugin:menu|items",{rid:this.rid,kind:this.kind}).then((e=>e.map(se)))}async get(e){return o("plugin:menu|get",{rid:this.rid,kind:this.kind,id:e}).then((e=>e?se(e):null))}async popup(e,t){let n=null;return e&&(n={type:e instanceof k?"Physical":"Logical",data:e}),o("plugin:menu|popup",{rid:this.rid,kind:this.kind,window:t?.label??null,at:n})}async setAsWindowsMenuForNSApp(){return o("plugin:menu|set_as_windows_menu_for_nsapp",{rid:this.rid})}async setAsHelpMenuForNSApp(){return o("plugin:menu|set_as_help_menu_for_nsapp",{rid:this.rid})}}function oe([e,t,n]){switch(n){case"Submenu":return new le(e,t);case"Predefined":return new ae(e,t);case"Check":return new ie(e,t);case"Icon":return new re(e,t);default:return new ne(e,t)}}class ue extends te{constructor(e,t){super(e,t,"Menu")}static async new(e){return ee("Menu",e).then((([e,t])=>new ue(e,t)))}static async default(){return o("plugin:menu|create_default").then((([e,t])=>new ue(e,t)))}async append(e){return o("plugin:menu|append",{rid:this.rid,kind:this.kind,items:(Array.isArray(e)?e:[e]).map((e=>"rid"in e?[e.rid,e.kind]:e))})}async prepend(e){return o("plugin:menu|prepend",{rid:this.rid,kind:this.kind,items:(Array.isArray(e)?e:[e]).map((e=>"rid"in e?[e.rid,e.kind]:e))})}async insert(e,t){return o("plugin:menu|insert",{rid:this.rid,kind:this.kind,items:(Array.isArray(e)?e:[e]).map((e=>"rid"in e?[e.rid,e.kind]:e)),position:t})}async remove(e){return o("plugin:menu|remove",{rid:this.rid,kind:this.kind,item:[e.rid,e.kind]})}async removeAt(e){return o("plugin:menu|remove_at",{rid:this.rid,kind:this.kind,position:e}).then(oe)}async items(){return o("plugin:menu|items",{rid:this.rid,kind:this.kind}).then((e=>e.map(oe)))}async get(e){return o("plugin:menu|get",{rid:this.rid,kind:this.kind,id:e}).then((e=>e?oe(e):null))}async popup(e,t){let n=null;return e&&(n={type:e instanceof k?"Physical":"Logical",data:e}),o("plugin:menu|popup",{rid:this.rid,kind:this.kind,window:t?.label??null,at:n})}async setAsAppMenu(){return o("plugin:menu|set_as_app_menu",{rid:this.rid}).then((e=>e?new ue(e[0],e[1]):null))}async setAsWindowMenu(e){return o("plugin:menu|set_as_window_menu",{rid:this.rid,window:e?.label??null}).then((e=>e?new ue(e[0],e[1]):null))}}var ce=Object.freeze({__proto__:null,CheckMenuItem:ie,IconMenuItem:re,Menu:ue,MenuItem:ne,get NativeIcon(){return K},PredefinedMenuItem:ae,Submenu:le});return e.app=p,e.core=c,e.dpi=D,e.event=_,e.menu=ce,e.path=Q,e.tray=Y,e.webview=q,e.window=F,e}({});window.__TAURI__=__TAURI_IIFE__; +var __TAURI_IIFE__=function(e){"use strict";function t(e,t,n,i){if("a"===n&&!i)throw new TypeError("Private accessor was defined without a getter");if("function"==typeof t?e!==t||!i:!t.has(e))throw new TypeError("Cannot read private member from an object whose class did not declare it");return"m"===n?i:"a"===n?i.call(e):i?i.value:t.get(e)}function n(e,t,n,i,r){if("m"===i)throw new TypeError("Private method is not writable");if("a"===i&&!r)throw new TypeError("Private accessor was defined without a setter");if("function"==typeof t?e!==t||!r:!t.has(e))throw new TypeError("Cannot write private member to an object whose class did not declare it");return"a"===i?r.call(e,n):r?r.value=n:t.set(e,n),n}var i,r,a,s;function l(e,t=!1){return window.__TAURI_INTERNALS__.transformCallback(e,t)}"function"==typeof SuppressedError&&SuppressedError;class o{constructor(){this.__TAURI_CHANNEL_MARKER__=!0,i.set(this,(()=>{})),r.set(this,0),a.set(this,{}),this.id=l((({message:e,id:s})=>{if(s===t(this,r,"f")){n(this,r,s+1,"f"),t(this,i,"f").call(this,e);const l=Object.keys(t(this,a,"f"));if(l.length>0){let e=s+1;for(const n of l.sort()){if(parseInt(n)!==e)break;{const r=t(this,a,"f")[n];delete t(this,a,"f")[n],t(this,i,"f").call(this,r),e+=1}}}}else t(this,a,"f")[s.toString()]=e}))}set onmessage(e){n(this,i,e,"f")}get onmessage(){return t(this,i,"f")}toJSON(){return`__CHANNEL__:${this.id}`}}i=new WeakMap,r=new WeakMap,a=new WeakMap;class u{constructor(e,t,n){this.plugin=e,this.event=t,this.channelId=n}async unregister(){return c(`plugin:${this.plugin}|remove_listener`,{event:this.event,channelId:this.channelId})}}async function c(e,t={},n){return window.__TAURI_INTERNALS__.invoke(e,t,n)}class d{get rid(){return t(this,s,"f")}constructor(e){s.set(this,void 0),n(this,s,e,"f")}async close(){return c("plugin:resources|close",{rid:this.rid})}}s=new WeakMap;var p=Object.freeze({__proto__:null,Channel:o,PluginListener:u,Resource:d,addPluginListener:async function(e,t,n){const i=new o;return i.onmessage=n,c(`plugin:${e}|register_listener`,{event:t,handler:i}).then((()=>new u(e,t,i.id)))},convertFileSrc:function(e,t="asset"){return window.__TAURI_INTERNALS__.convertFileSrc(e,t)},invoke:c,transformCallback:l});var h,y=Object.freeze({__proto__:null,getName:async function(){return c("plugin:app|name")},getTauriVersion:async function(){return c("plugin:app|tauri_version")},getVersion:async function(){return c("plugin:app|version")},hide:async function(){return c("plugin:app|app_hide")},show:async function(){return c("plugin:app|app_show")}});async function w(e,t){await c("plugin:event|unlisten",{event:e,eventId:t})}async function g(e,t,n){const i="string"==typeof n?.target?{kind:"AnyLabel",label:n.target}:n?.target??{kind:"Any"};return c("plugin:event|listen",{event:e,target:i,handler:l(t)}).then((t=>async()=>w(e,t)))}async function b(e,t,n){return g(e,(n=>{t(n),w(e,n.id).catch((()=>{}))}),n)}async function _(e,t){await c("plugin:event|emit",{event:e,payload:t})}async function m(e,t,n){const i="string"==typeof e?{kind:"AnyLabel",label:e}:e;await c("plugin:event|emit_to",{target:i,event:t,payload:n})}!function(e){e.WINDOW_RESIZED="tauri://resize",e.WINDOW_MOVED="tauri://move",e.WINDOW_CLOSE_REQUESTED="tauri://close-requested",e.WINDOW_DESTROYED="tauri://destroyed",e.WINDOW_FOCUS="tauri://focus",e.WINDOW_BLUR="tauri://blur",e.WINDOW_SCALE_FACTOR_CHANGED="tauri://scale-change",e.WINDOW_THEME_CHANGED="tauri://theme-changed",e.WINDOW_CREATED="tauri://window-created",e.WEBVIEW_CREATED="tauri://webview-created",e.DRAG="tauri://drag",e.DROP="tauri://drop",e.DROP_OVER="tauri://drop-over",e.DROP_CANCELLED="tauri://drag-cancelled"}(h||(h={}));var f=Object.freeze({__proto__:null,get TauriEvent(){return h},emit:_,emitTo:m,listen:g,once:b});class v{constructor(e,t){this.type="Logical",this.width=e,this.height=t}}class k{constructor(e,t){this.type="Physical",this.width=e,this.height=t}toLogical(e){return new v(this.width/e,this.height/e)}}class A{constructor(e,t){this.type="Logical",this.x=e,this.y=t}}class D{constructor(e,t){this.type="Physical",this.x=e,this.y=t}toLogical(e){return new A(this.x/e,this.y/e)}}var E=Object.freeze({__proto__:null,LogicalPosition:A,LogicalSize:v,PhysicalPosition:D,PhysicalSize:k});class P extends d{constructor(e){super(e)}static async new(e,t,n){return c("plugin:image|new",{rgba:L(e),width:t,height:n}).then((e=>new P(e)))}static async fromBytes(e){return c("plugin:image|from_bytes",{bytes:L(e)}).then((e=>new P(e)))}static async fromPath(e){return c("plugin:image|from_path",{path:e}).then((e=>new P(e)))}async rgba(){return c("plugin:image|rgba",{rid:this.rid}).then((e=>new Uint8Array(e)))}async size(){return c("plugin:image|size",{rid:this.rid})}}function L(e){return null==e?null:"string"==typeof e?e:e instanceof Uint8Array?Array.from(e):e instanceof ArrayBuffer?Array.from(new Uint8Array(e)):e instanceof P?e.rid:e}var S,I,T=Object.freeze({__proto__:null,Image:P,transformImage:L});!function(e){e[e.Critical=1]="Critical",e[e.Informational=2]="Informational"}(S||(S={}));class C{constructor(e){this._preventDefault=!1,this.event=e.event,this.id=e.id}preventDefault(){this._preventDefault=!0}isPreventDefault(){return this._preventDefault}}function x(){return new O(window.__TAURI_INTERNALS__.metadata.currentWindow.label,{skip:!0})}function z(){return window.__TAURI_INTERNALS__.metadata.windows.map((e=>new O(e.label,{skip:!0})))}!function(e){e.None="none",e.Normal="normal",e.Indeterminate="indeterminate",e.Paused="paused",e.Error="error"}(I||(I={}));const R=["tauri://created","tauri://error"];class O{constructor(e,t={}){this.label=e,this.listeners=Object.create(null),t?.skip||c("plugin:window|create",{options:{...t,parent:"string"==typeof t.parent?t.parent:t.parent?.label,label:e}}).then((async()=>this.emit("tauri://created"))).catch((async e=>this.emit("tauri://error",e)))}static getByLabel(e){return z().find((t=>t.label===e))??null}static getCurrent(){return x()}static getAll(){return z()}static async getFocusedWindow(){for(const e of z())if(await e.isFocused())return e;return null}async listen(e,t){return this._handleTauriEvent(e,t)?Promise.resolve((()=>{const n=this.listeners[e];n.splice(n.indexOf(t),1)})):g(e,t,{target:{kind:"Window",label:this.label}})}async once(e,t){return this._handleTauriEvent(e,t)?Promise.resolve((()=>{const n=this.listeners[e];n.splice(n.indexOf(t),1)})):b(e,t,{target:{kind:"Window",label:this.label}})}async emit(e,t){if(R.includes(e)){for(const n of this.listeners[e]||[])n({event:e,id:-1,payload:t});return Promise.resolve()}return _(e,t)}async emitTo(e,t,n){if(R.includes(t)){for(const e of this.listeners[t]||[])e({event:t,id:-1,payload:n});return Promise.resolve()}return m(e,t,n)}_handleTauriEvent(e,t){return!!R.includes(e)&&(e in this.listeners?this.listeners[e].push(t):this.listeners[e]=[t],!0)}async scaleFactor(){return c("plugin:window|scale_factor",{label:this.label})}async innerPosition(){return c("plugin:window|inner_position",{label:this.label}).then((({x:e,y:t})=>new D(e,t)))}async outerPosition(){return c("plugin:window|outer_position",{label:this.label}).then((({x:e,y:t})=>new D(e,t)))}async innerSize(){return c("plugin:window|inner_size",{label:this.label}).then((({width:e,height:t})=>new k(e,t)))}async outerSize(){return c("plugin:window|outer_size",{label:this.label}).then((({width:e,height:t})=>new k(e,t)))}async isFullscreen(){return c("plugin:window|is_fullscreen",{label:this.label})}async isMinimized(){return c("plugin:window|is_minimized",{label:this.label})}async isMaximized(){return c("plugin:window|is_maximized",{label:this.label})}async isFocused(){return c("plugin:window|is_focused",{label:this.label})}async isDecorated(){return c("plugin:window|is_decorated",{label:this.label})}async isResizable(){return c("plugin:window|is_resizable",{label:this.label})}async isMaximizable(){return c("plugin:window|is_maximizable",{label:this.label})}async isMinimizable(){return c("plugin:window|is_minimizable",{label:this.label})}async isClosable(){return c("plugin:window|is_closable",{label:this.label})}async isVisible(){return c("plugin:window|is_visible",{label:this.label})}async title(){return c("plugin:window|title",{label:this.label})}async theme(){return c("plugin:window|theme",{label:this.label})}async center(){return c("plugin:window|center",{label:this.label})}async requestUserAttention(e){let t=null;return e&&(t=e===S.Critical?{type:"Critical"}:{type:"Informational"}),c("plugin:window|request_user_attention",{label:this.label,value:t})}async setResizable(e){return c("plugin:window|set_resizable",{label:this.label,value:e})}async setMaximizable(e){return c("plugin:window|set_maximizable",{label:this.label,value:e})}async setMinimizable(e){return c("plugin:window|set_minimizable",{label:this.label,value:e})}async setClosable(e){return c("plugin:window|set_closable",{label:this.label,value:e})}async setTitle(e){return c("plugin:window|set_title",{label:this.label,value:e})}async maximize(){return c("plugin:window|maximize",{label:this.label})}async unmaximize(){return c("plugin:window|unmaximize",{label:this.label})}async toggleMaximize(){return c("plugin:window|toggle_maximize",{label:this.label})}async minimize(){return c("plugin:window|minimize",{label:this.label})}async unminimize(){return c("plugin:window|unminimize",{label:this.label})}async show(){return c("plugin:window|show",{label:this.label})}async hide(){return c("plugin:window|hide",{label:this.label})}async close(){return c("plugin:window|close",{label:this.label})}async destroy(){return c("plugin:window|destroy",{label:this.label})}async setDecorations(e){return c("plugin:window|set_decorations",{label:this.label,value:e})}async setShadow(e){return c("plugin:window|set_shadow",{label:this.label,value:e})}async setEffects(e){return c("plugin:window|set_effects",{label:this.label,value:e})}async clearEffects(){return c("plugin:window|set_effects",{label:this.label,value:null})}async setAlwaysOnTop(e){return c("plugin:window|set_always_on_top",{label:this.label,value:e})}async setAlwaysOnBottom(e){return c("plugin:window|set_always_on_bottom",{label:this.label,value:e})}async setContentProtected(e){return c("plugin:window|set_content_protected",{label:this.label,value:e})}async setSize(e){if(!e||"Logical"!==e.type&&"Physical"!==e.type)throw new Error("the `size` argument must be either a LogicalSize or a PhysicalSize instance");return c("plugin:window|set_size",{label:this.label,value:{type:e.type,data:{width:e.width,height:e.height}}})}async setMinSize(e){if(e&&"Logical"!==e.type&&"Physical"!==e.type)throw new Error("the `size` argument must be either a LogicalSize or a PhysicalSize instance");return c("plugin:window|set_min_size",{label:this.label,value:e?{type:e.type,data:{width:e.width,height:e.height}}:null})}async setMaxSize(e){if(e&&"Logical"!==e.type&&"Physical"!==e.type)throw new Error("the `size` argument must be either a LogicalSize or a PhysicalSize instance");return c("plugin:window|set_max_size",{label:this.label,value:e?{type:e.type,data:{width:e.width,height:e.height}}:null})}async setPosition(e){if(!e||"Logical"!==e.type&&"Physical"!==e.type)throw new Error("the `position` argument must be either a LogicalPosition or a PhysicalPosition instance");return c("plugin:window|set_position",{label:this.label,value:{type:e.type,data:{x:e.x,y:e.y}}})}async setFullscreen(e){return c("plugin:window|set_fullscreen",{label:this.label,value:e})}async setFocus(){return c("plugin:window|set_focus",{label:this.label})}async setIcon(e){return c("plugin:window|set_icon",{label:this.label,value:L(e)})}async setSkipTaskbar(e){return c("plugin:window|set_skip_taskbar",{label:this.label,value:e})}async setCursorGrab(e){return c("plugin:window|set_cursor_grab",{label:this.label,value:e})}async setCursorVisible(e){return c("plugin:window|set_cursor_visible",{label:this.label,value:e})}async setCursorIcon(e){return c("plugin:window|set_cursor_icon",{label:this.label,value:e})}async setCursorPosition(e){if(!e||"Logical"!==e.type&&"Physical"!==e.type)throw new Error("the `position` argument must be either a LogicalPosition or a PhysicalPosition instance");return c("plugin:window|set_cursor_position",{label:this.label,value:{type:e.type,data:{x:e.x,y:e.y}}})}async setIgnoreCursorEvents(e){return c("plugin:window|set_ignore_cursor_events",{label:this.label,value:e})}async startDragging(){return c("plugin:window|start_dragging",{label:this.label})}async startResizeDragging(e){return c("plugin:window|start_resize_dragging",{label:this.label,value:e})}async setProgressBar(e){return c("plugin:window|set_progress_bar",{label:this.label,value:e})}async setVisibleOnAllWorkspaces(e){return c("plugin:window|set_visible_on_all_workspaces",{label:this.label,value:e})}async onResized(e){return this.listen(h.WINDOW_RESIZED,(t=>{t.payload=U(t.payload),e(t)}))}async onMoved(e){return this.listen(h.WINDOW_MOVED,(t=>{t.payload=M(t.payload),e(t)}))}async onCloseRequested(e){return this.listen(h.WINDOW_CLOSE_REQUESTED,(t=>{const n=new C(t);Promise.resolve(e(n)).then((()=>{if(!n.isPreventDefault())return this.destroy()}))}))}async onDragDropEvent(e){const t=await this.listen(h.DRAG,(t=>{e({...t,payload:{type:"dragged",paths:t.payload.paths,position:M(t.payload.position)}})})),n=await this.listen(h.DROP,(t=>{e({...t,payload:{type:"dropped",paths:t.payload.paths,position:M(t.payload.position)}})})),i=await this.listen(h.DROP_OVER,(t=>{e({...t,payload:{type:"dragOver",position:M(t.payload.position)}})})),r=await this.listen(h.DROP_CANCELLED,(t=>{e({...t,payload:{type:"cancelled"}})}));return()=>{t(),n(),i(),r()}}async onFocusChanged(e){const t=await this.listen(h.WINDOW_FOCUS,(t=>{e({...t,payload:!0})})),n=await this.listen(h.WINDOW_BLUR,(t=>{e({...t,payload:!1})}));return()=>{t(),n()}}async onScaleChanged(e){return this.listen(h.WINDOW_SCALE_FACTOR_CHANGED,e)}async onThemeChanged(e){return this.listen(h.WINDOW_THEME_CHANGED,e)}}var W,N;function F(e){return null===e?null:{name:e.name,scaleFactor:e.scaleFactor,position:M(e.position),size:U(e.size)}}function M(e){return new D(e.x,e.y)}function U(e){return new k(e.width,e.height)}!function(e){e.AppearanceBased="appearanceBased",e.Light="light",e.Dark="dark",e.MediumLight="mediumLight",e.UltraDark="ultraDark",e.Titlebar="titlebar",e.Selection="selection",e.Menu="menu",e.Popover="popover",e.Sidebar="sidebar",e.HeaderView="headerView",e.Sheet="sheet",e.WindowBackground="windowBackground",e.HudWindow="hudWindow",e.FullScreenUI="fullScreenUI",e.Tooltip="tooltip",e.ContentBackground="contentBackground",e.UnderWindowBackground="underWindowBackground",e.UnderPageBackground="underPageBackground",e.Mica="mica",e.Blur="blur",e.Acrylic="acrylic",e.Tabbed="tabbed",e.TabbedDark="tabbedDark",e.TabbedLight="tabbedLight"}(W||(W={})),function(e){e.FollowsWindowActiveState="followsWindowActiveState",e.Active="active",e.Inactive="inactive"}(N||(N={}));var B=Object.freeze({__proto__:null,CloseRequestedEvent:C,get Effect(){return W},get EffectState(){return N},LogicalPosition:A,LogicalSize:v,PhysicalPosition:D,PhysicalSize:k,get ProgressBarStatus(){return I},get UserAttentionType(){return S},Window:O,availableMonitors:async function(){return c("plugin:window|available_monitors").then((e=>e.map(F)))},currentMonitor:async function(){return c("plugin:window|current_monitor").then(F)},getAll:z,getCurrent:x,primaryMonitor:async function(){return c("plugin:window|primary_monitor").then(F)}});function j(){return new H(x(),window.__TAURI_INTERNALS__.metadata.currentWebview.label,{skip:!0})}function V(){return window.__TAURI_INTERNALS__.metadata.webviews.map((e=>new H(O.getByLabel(e.windowLabel),e.label,{skip:!0})))}const G=["tauri://created","tauri://error"];class H{constructor(e,t,n){this.window=e,this.label=t,this.listeners=Object.create(null),n?.skip||c("plugin:webview|create_webview",{windowLabel:e.label,label:t,options:n}).then((async()=>this.emit("tauri://created"))).catch((async e=>this.emit("tauri://error",e)))}static getByLabel(e){return V().find((t=>t.label===e))??null}static getCurrent(){return j()}static getAll(){return V()}async listen(e,t){return this._handleTauriEvent(e,t)?Promise.resolve((()=>{const n=this.listeners[e];n.splice(n.indexOf(t),1)})):g(e,t,{target:{kind:"Webview",label:this.label}})}async once(e,t){return this._handleTauriEvent(e,t)?Promise.resolve((()=>{const n=this.listeners[e];n.splice(n.indexOf(t),1)})):b(e,t,{target:{kind:"Webview",label:this.label}})}async emit(e,t){if(G.includes(e)){for(const n of this.listeners[e]||[])n({event:e,id:-1,payload:t});return Promise.resolve()}return _(e,t)}async emitTo(e,t,n){if(G.includes(t)){for(const e of this.listeners[t]||[])e({event:t,id:-1,payload:n});return Promise.resolve()}return m(e,t,n)}_handleTauriEvent(e,t){return!!G.includes(e)&&(e in this.listeners?this.listeners[e].push(t):this.listeners[e]=[t],!0)}async position(){return c("plugin:webview|webview_position",{label:this.label}).then((({x:e,y:t})=>new D(e,t)))}async size(){return c("plugin:webview|webview_size",{label:this.label}).then((({width:e,height:t})=>new k(e,t)))}async close(){return c("plugin:webview|close",{label:this.label})}async setSize(e){if(!e||"Logical"!==e.type&&"Physical"!==e.type)throw new Error("the `size` argument must be either a LogicalSize or a PhysicalSize instance");return c("plugin:webview|set_webview_size",{label:this.label,value:{type:e.type,data:{width:e.width,height:e.height}}})}async setPosition(e){if(!e||"Logical"!==e.type&&"Physical"!==e.type)throw new Error("the `position` argument must be either a LogicalPosition or a PhysicalPosition instance");return c("plugin:webview|set_webview_position",{label:this.label,value:{type:e.type,data:{x:e.x,y:e.y}}})}async setFocus(){return c("plugin:webview|set_webview_focus",{label:this.label})}async setZoom(e){return c("plugin:webview|set_webview_zoom",{label:this.label,value:e})}async reparent(e){return c("plugin:webview|set_webview_focus",{label:this.label,window:"string"==typeof e?e:e.label})}async onDragDropEvent(e){const t=await this.listen(h.DRAG,(t=>{e({...t,payload:{type:"dragged",paths:t.payload.paths,position:q(t.payload.position)}})})),n=await this.listen(h.DROP,(t=>{e({...t,payload:{type:"dropped",paths:t.payload.paths,position:q(t.payload.position)}})})),i=await this.listen(h.DROP_CANCELLED,(t=>{e({...t,payload:{type:"dragOver",position:q(t.payload.position)}})})),r=await this.listen(h.DROP_CANCELLED,(t=>{e({...t,payload:{type:"cancelled"}})}));return()=>{t(),n(),i(),r()}}}function q(e){return new D(e.x,e.y)}var Q,Z,$=Object.freeze({__proto__:null,Webview:H,getAll:V,getCurrent:j});function J(){const e=j();return new Y(e.label,{skip:!0})}function K(){return window.__TAURI_INTERNALS__.metadata.webviews.map((e=>new Y(e.label,{skip:!0})))}class Y{constructor(e,t={}){this.label=e,this.listeners=Object.create(null),t?.skip||c("plugin:webview|create_webview_window",{options:{...t,parent:"string"==typeof t.parent?t.parent:t.parent?.label,label:e}}).then((async()=>this.emit("tauri://created"))).catch((async e=>this.emit("tauri://error",e)))}static getByLabel(e){const t=K().find((t=>t.label===e))??null;return t?new Y(t.label,{skip:!0}):null}static getCurrent(){return J()}static getAll(){return K().map((e=>new Y(e.label,{skip:!0})))}async listen(e,t){return this._handleTauriEvent(e,t)?Promise.resolve((()=>{const n=this.listeners[e];n.splice(n.indexOf(t),1)})):g(e,t,{target:{kind:"WebviewWindow",label:this.label}})}async once(e,t){return this._handleTauriEvent(e,t)?Promise.resolve((()=>{const n=this.listeners[e];n.splice(n.indexOf(t),1)})):b(e,t,{target:{kind:"WebviewWindow",label:this.label}})}}Q=Y,Z=[O,H],(Array.isArray(Z)?Z:[Z]).forEach((e=>{Object.getOwnPropertyNames(e.prototype).forEach((t=>{"object"==typeof Q.prototype&&Q.prototype&&t in Q.prototype||Object.defineProperty(Q.prototype,t,Object.getOwnPropertyDescriptor(e.prototype,t)??Object.create(null))}))}));var X,ee=Object.freeze({__proto__:null,WebviewWindow:Y,getAll:K,getCurrent:J});!function(e){e[e.Audio=1]="Audio",e[e.Cache=2]="Cache",e[e.Config=3]="Config",e[e.Data=4]="Data",e[e.LocalData=5]="LocalData",e[e.Document=6]="Document",e[e.Download=7]="Download",e[e.Picture=8]="Picture",e[e.Public=9]="Public",e[e.Video=10]="Video",e[e.Resource=11]="Resource",e[e.Temp=12]="Temp",e[e.AppConfig=13]="AppConfig",e[e.AppData=14]="AppData",e[e.AppLocalData=15]="AppLocalData",e[e.AppCache=16]="AppCache",e[e.AppLog=17]="AppLog",e[e.Desktop=18]="Desktop",e[e.Executable=19]="Executable",e[e.Font=20]="Font",e[e.Home=21]="Home",e[e.Runtime=22]="Runtime",e[e.Template=23]="Template"}(X||(X={}));var te=Object.freeze({__proto__:null,get BaseDirectory(){return X},appCacheDir:async function(){return c("plugin:path|resolve_directory",{directory:X.AppCache})},appConfigDir:async function(){return c("plugin:path|resolve_directory",{directory:X.AppConfig})},appDataDir:async function(){return c("plugin:path|resolve_directory",{directory:X.AppData})},appLocalDataDir:async function(){return c("plugin:path|resolve_directory",{directory:X.AppLocalData})},appLogDir:async function(){return c("plugin:path|resolve_directory",{directory:X.AppLog})},audioDir:async function(){return c("plugin:path|resolve_directory",{directory:X.Audio})},basename:async function(e,t){return c("plugin:path|basename",{path:e,ext:t})},cacheDir:async function(){return c("plugin:path|resolve_directory",{directory:X.Cache})},configDir:async function(){return c("plugin:path|resolve_directory",{directory:X.Config})},dataDir:async function(){return c("plugin:path|resolve_directory",{directory:X.Data})},delimiter:function(){return window.__TAURI_INTERNALS__.plugins.path.delimiter},desktopDir:async function(){return c("plugin:path|resolve_directory",{directory:X.Desktop})},dirname:async function(e){return c("plugin:path|dirname",{path:e})},documentDir:async function(){return c("plugin:path|resolve_directory",{directory:X.Document})},downloadDir:async function(){return c("plugin:path|resolve_directory",{directory:X.Download})},executableDir:async function(){return c("plugin:path|resolve_directory",{directory:X.Executable})},extname:async function(e){return c("plugin:path|extname",{path:e})},fontDir:async function(){return c("plugin:path|resolve_directory",{directory:X.Font})},homeDir:async function(){return c("plugin:path|resolve_directory",{directory:X.Home})},isAbsolute:async function(e){return c("plugin:path|isAbsolute",{path:e})},join:async function(...e){return c("plugin:path|join",{paths:e})},localDataDir:async function(){return c("plugin:path|resolve_directory",{directory:X.LocalData})},normalize:async function(e){return c("plugin:path|normalize",{path:e})},pictureDir:async function(){return c("plugin:path|resolve_directory",{directory:X.Picture})},publicDir:async function(){return c("plugin:path|resolve_directory",{directory:X.Public})},resolve:async function(...e){return c("plugin:path|resolve",{paths:e})},resolveResource:async function(e){return c("plugin:path|resolve_directory",{directory:X.Resource,path:e})},resourceDir:async function(){return c("plugin:path|resolve_directory",{directory:X.Resource})},runtimeDir:async function(){return c("plugin:path|resolve_directory",{directory:X.Runtime})},sep:function(){return window.__TAURI_INTERNALS__.plugins.path.sep},tempDir:async function(){return c("plugin:path|resolve_directory",{directory:X.Temp})},templateDir:async function(){return c("plugin:path|resolve_directory",{directory:X.Template})},videoDir:async function(){return c("plugin:path|resolve_directory",{directory:X.Video})}});class ne extends d{constructor(e,t){super(e),this.id=t}static async getById(e){return c("plugin:tray|get_by_id",{id:e}).then((t=>t?new ne(t,e):null))}static async removeById(e){return c("plugin:tray|remove_by_id",{id:e})}static async new(e){e?.menu&&(e.menu=[e.menu.rid,e.menu.kind]),e?.icon&&(e.icon=L(e.icon));const t=new o;return e?.action&&(t.onmessage=e.action,delete e.action),c("plugin:tray|new",{options:e??{},handler:t}).then((([e,t])=>new ne(e,t)))}async setIcon(e){let t=null;return e&&(t=L(e)),c("plugin:tray|set_icon",{rid:this.rid,icon:t})}async setMenu(e){return e&&(e=[e.rid,e.kind]),c("plugin:tray|set_menu",{rid:this.rid,menu:e})}async setTooltip(e){return c("plugin:tray|set_tooltip",{rid:this.rid,tooltip:e})}async setTitle(e){return c("plugin:tray|set_title",{rid:this.rid,title:e})}async setVisible(e){return c("plugin:tray|set_visible",{rid:this.rid,visible:e})}async setTempDirPath(e){return c("plugin:tray|set_temp_dir_path",{rid:this.rid,path:e})}async setIconAsTemplate(e){return c("plugin:tray|set_icon_as_template",{rid:this.rid,asTemplate:e})}async setMenuOnLeftClick(e){return c("plugin:tray|set_show_menu_on_left_click",{rid:this.rid,onLeft:e})}}var ie,re,ae,se=Object.freeze({__proto__:null,TrayIcon:ne});function le(e){if("items"in e)e.items=e.items?.map((e=>"rid"in e?e:le(e)));else if("action"in e&&e.action){const t=new o;return t.onmessage=e.action,delete e.action,{...e,handler:t}}return e}async function oe(e,t){const n=new o;let i=null;return t&&"object"==typeof t&&("action"in t&&t.action&&(n.onmessage=t.action,delete t.action),"items"in t&&t.items&&(i=t.items.map((e=>"rid"in e?[e.rid,e.kind]:("item"in e&&"object"==typeof e.item&&e.item.About?.icon&&(e.item.About.icon=L(e.item.About.icon)),"icon"in e&&e.icon&&(e.icon=L(e.icon)),le(e)))))),c("plugin:menu|new",{kind:e,options:t?{...t,items:i}:void 0,handler:n})}class ue extends d{get id(){return t(this,ie,"f")}get kind(){return t(this,re,"f")}constructor(e,t,i){super(e),ie.set(this,void 0),re.set(this,void 0),n(this,ie,t,"f"),n(this,re,i,"f")}}ie=new WeakMap,re=new WeakMap;class ce extends ue{constructor(e,t){super(e,t,"MenuItem")}static async new(e){return oe("MenuItem",e).then((([e,t])=>new ce(e,t)))}async text(){return c("plugin:menu|text",{rid:this.rid,kind:this.kind})}async setText(e){return c("plugin:menu|set_text",{rid:this.rid,kind:this.kind,text:e})}async isEnabled(){return c("plugin:menu|is_enabled",{rid:this.rid,kind:this.kind})}async setEnabled(e){return c("plugin:menu|set_enabled",{rid:this.rid,kind:this.kind,enabled:e})}async setAccelerator(e){return c("plugin:menu|set_accelerator",{rid:this.rid,kind:this.kind,accelerator:e})}}class de extends ue{constructor(e,t){super(e,t,"Check")}static async new(e){return oe("Check",e).then((([e,t])=>new de(e,t)))}async text(){return c("plugin:menu|text",{rid:this.rid,kind:this.kind})}async setText(e){return c("plugin:menu|set_text",{rid:this.rid,kind:this.kind,text:e})}async isEnabled(){return c("plugin:menu|is_enabled",{rid:this.rid,kind:this.kind})}async setEnabled(e){return c("plugin:menu|set_enabled",{rid:this.rid,kind:this.kind,enabled:e})}async setAccelerator(e){return c("plugin:menu|set_accelerator",{rid:this.rid,kind:this.kind,accelerator:e})}async isChecked(){return c("plugin:menu|is_checked",{rid:this.rid})}async setChecked(e){return c("plugin:menu|set_checked",{rid:this.rid,checked:e})}}!function(e){e.Add="Add",e.Advanced="Advanced",e.Bluetooth="Bluetooth",e.Bookmarks="Bookmarks",e.Caution="Caution",e.ColorPanel="ColorPanel",e.ColumnView="ColumnView",e.Computer="Computer",e.EnterFullScreen="EnterFullScreen",e.Everyone="Everyone",e.ExitFullScreen="ExitFullScreen",e.FlowView="FlowView",e.Folder="Folder",e.FolderBurnable="FolderBurnable",e.FolderSmart="FolderSmart",e.FollowLinkFreestanding="FollowLinkFreestanding",e.FontPanel="FontPanel",e.GoLeft="GoLeft",e.GoRight="GoRight",e.Home="Home",e.IChatTheater="IChatTheater",e.IconView="IconView",e.Info="Info",e.InvalidDataFreestanding="InvalidDataFreestanding",e.LeftFacingTriangle="LeftFacingTriangle",e.ListView="ListView",e.LockLocked="LockLocked",e.LockUnlocked="LockUnlocked",e.MenuMixedState="MenuMixedState",e.MenuOnState="MenuOnState",e.MobileMe="MobileMe",e.MultipleDocuments="MultipleDocuments",e.Network="Network",e.Path="Path",e.PreferencesGeneral="PreferencesGeneral",e.QuickLook="QuickLook",e.RefreshFreestanding="RefreshFreestanding",e.Refresh="Refresh",e.Remove="Remove",e.RevealFreestanding="RevealFreestanding",e.RightFacingTriangle="RightFacingTriangle",e.Share="Share",e.Slideshow="Slideshow",e.SmartBadge="SmartBadge",e.StatusAvailable="StatusAvailable",e.StatusNone="StatusNone",e.StatusPartiallyAvailable="StatusPartiallyAvailable",e.StatusUnavailable="StatusUnavailable",e.StopProgressFreestanding="StopProgressFreestanding",e.StopProgress="StopProgress",e.TrashEmpty="TrashEmpty",e.TrashFull="TrashFull",e.User="User",e.UserAccounts="UserAccounts",e.UserGroup="UserGroup",e.UserGuest="UserGuest"}(ae||(ae={}));class pe extends ue{constructor(e,t){super(e,t,"Icon")}static async new(e){return oe("Icon",e).then((([e,t])=>new pe(e,t)))}async text(){return c("plugin:menu|text",{rid:this.rid,kind:this.kind})}async setText(e){return c("plugin:menu|set_text",{rid:this.rid,kind:this.kind,text:e})}async isEnabled(){return c("plugin:menu|is_enabled",{rid:this.rid,kind:this.kind})}async setEnabled(e){return c("plugin:menu|set_enabled",{rid:this.rid,kind:this.kind,enabled:e})}async setAccelerator(e){return c("plugin:menu|set_accelerator",{rid:this.rid,kind:this.kind,accelerator:e})}async setIcon(e){return c("plugin:menu|set_icon",{rid:this.rid,icon:L(e)})}}class he extends ue{constructor(e,t){super(e,t,"Predefined")}static async new(e){return oe("Predefined",e).then((([e,t])=>new he(e,t)))}async text(){return c("plugin:menu|text",{rid:this.rid,kind:this.kind})}async setText(e){return c("plugin:menu|set_text",{rid:this.rid,kind:this.kind,text:e})}}function ye([e,t,n]){switch(n){case"Submenu":return new we(e,t);case"Predefined":return new he(e,t);case"Check":return new de(e,t);case"Icon":return new pe(e,t);default:return new ce(e,t)}}class we extends ue{constructor(e,t){super(e,t,"Submenu")}static async new(e){return oe("Submenu",e).then((([e,t])=>new we(e,t)))}async text(){return c("plugin:menu|text",{rid:this.rid,kind:this.kind})}async setText(e){return c("plugin:menu|set_text",{rid:this.rid,kind:this.kind,text:e})}async isEnabled(){return c("plugin:menu|is_enabled",{rid:this.rid,kind:this.kind})}async setEnabled(e){return c("plugin:menu|set_enabled",{rid:this.rid,kind:this.kind,enabled:e})}async append(e){return c("plugin:menu|append",{rid:this.rid,kind:this.kind,items:(Array.isArray(e)?e:[e]).map((e=>"rid"in e?[e.rid,e.kind]:e))})}async prepend(e){return c("plugin:menu|prepend",{rid:this.rid,kind:this.kind,items:(Array.isArray(e)?e:[e]).map((e=>"rid"in e?[e.rid,e.kind]:e))})}async insert(e,t){return c("plugin:menu|insert",{rid:this.rid,kind:this.kind,items:(Array.isArray(e)?e:[e]).map((e=>"rid"in e?[e.rid,e.kind]:e)),position:t})}async remove(e){return c("plugin:menu|remove",{rid:this.rid,kind:this.kind,item:[e.rid,e.kind]})}async removeAt(e){return c("plugin:menu|remove_at",{rid:this.rid,kind:this.kind,position:e}).then(ye)}async items(){return c("plugin:menu|items",{rid:this.rid,kind:this.kind}).then((e=>e.map(ye)))}async get(e){return c("plugin:menu|get",{rid:this.rid,kind:this.kind,id:e}).then((e=>e?ye(e):null))}async popup(e,t){let n=null;return e&&(n={type:e instanceof D?"Physical":"Logical",data:e}),c("plugin:menu|popup",{rid:this.rid,kind:this.kind,window:t?.label??null,at:n})}async setAsWindowsMenuForNSApp(){return c("plugin:menu|set_as_windows_menu_for_nsapp",{rid:this.rid})}async setAsHelpMenuForNSApp(){return c("plugin:menu|set_as_help_menu_for_nsapp",{rid:this.rid})}}function ge([e,t,n]){switch(n){case"Submenu":return new we(e,t);case"Predefined":return new he(e,t);case"Check":return new de(e,t);case"Icon":return new pe(e,t);default:return new ce(e,t)}}class be extends ue{constructor(e,t){super(e,t,"Menu")}static async new(e){return oe("Menu",e).then((([e,t])=>new be(e,t)))}static async default(){return c("plugin:menu|create_default").then((([e,t])=>new be(e,t)))}async append(e){return c("plugin:menu|append",{rid:this.rid,kind:this.kind,items:(Array.isArray(e)?e:[e]).map((e=>"rid"in e?[e.rid,e.kind]:e))})}async prepend(e){return c("plugin:menu|prepend",{rid:this.rid,kind:this.kind,items:(Array.isArray(e)?e:[e]).map((e=>"rid"in e?[e.rid,e.kind]:e))})}async insert(e,t){return c("plugin:menu|insert",{rid:this.rid,kind:this.kind,items:(Array.isArray(e)?e:[e]).map((e=>"rid"in e?[e.rid,e.kind]:e)),position:t})}async remove(e){return c("plugin:menu|remove",{rid:this.rid,kind:this.kind,item:[e.rid,e.kind]})}async removeAt(e){return c("plugin:menu|remove_at",{rid:this.rid,kind:this.kind,position:e}).then(ge)}async items(){return c("plugin:menu|items",{rid:this.rid,kind:this.kind}).then((e=>e.map(ge)))}async get(e){return c("plugin:menu|get",{rid:this.rid,kind:this.kind,id:e}).then((e=>e?ge(e):null))}async popup(e,t){let n=null;return e&&(n={type:e instanceof D?"Physical":"Logical",data:e}),c("plugin:menu|popup",{rid:this.rid,kind:this.kind,window:t?.label??null,at:n})}async setAsAppMenu(){return c("plugin:menu|set_as_app_menu",{rid:this.rid}).then((e=>e?new be(e[0],e[1]):null))}async setAsWindowMenu(e){return c("plugin:menu|set_as_window_menu",{rid:this.rid,window:e?.label??null}).then((e=>e?new be(e[0],e[1]):null))}}var _e=Object.freeze({__proto__:null,CheckMenuItem:de,IconMenuItem:pe,Menu:be,MenuItem:ce,get NativeIcon(){return ae},PredefinedMenuItem:he,Submenu:we});return e.app=y,e.core=p,e.dpi=E,e.event=f,e.image=T,e.menu=_e,e.path=te,e.tray=se,e.webview=$,e.webviewWindow=ee,e.window=B,e}({});window.__TAURI__=__TAURI_IIFE__; diff --git a/core/tauri/scripts/core.js b/core/tauri/scripts/core.js index 3d09e3738..0898ec109 100644 --- a/core/tauri/scripts/core.js +++ b/core/tauri/scripts/core.js @@ -1,4 +1,4 @@ -// Copyright 2019-2023 Tauri Programme within The Commons Conservancy +// Copyright 2019-2024 Tauri Programme within The Commons Conservancy // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT @@ -71,7 +71,7 @@ true) const action = () => { - window.window.__TAURI_INTERNALS__.ipc({ + window.__TAURI_INTERNALS__.ipc({ cmd, callback, error, diff --git a/core/tauri/scripts/freeze_prototype.js b/core/tauri/scripts/freeze_prototype.js index 85b0598b5..113ca8ce1 100644 --- a/core/tauri/scripts/freeze_prototype.js +++ b/core/tauri/scripts/freeze_prototype.js @@ -1,4 +1,4 @@ -// Copyright 2019-2023 Tauri Programme within The Commons Conservancy +// Copyright 2019-2024 Tauri Programme within The Commons Conservancy // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT diff --git a/core/tauri/scripts/init.js b/core/tauri/scripts/init.js index 2636e666b..3f766afad 100644 --- a/core/tauri/scripts/init.js +++ b/core/tauri/scripts/init.js @@ -1,4 +1,4 @@ -// Copyright 2019-2023 Tauri Programme within The Commons Conservancy +// Copyright 2019-2024 Tauri Programme within The Commons Conservancy // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT diff --git a/core/tauri/scripts/ipc-protocol.js b/core/tauri/scripts/ipc-protocol.js index 1f8e0018e..a3ea522e0 100644 --- a/core/tauri/scripts/ipc-protocol.js +++ b/core/tauri/scripts/ipc-protocol.js @@ -1,4 +1,4 @@ -// Copyright 2019-2023 Tauri Programme within The Commons Conservancy +// Copyright 2019-2024 Tauri Programme within The Commons Conservancy // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT @@ -6,72 +6,73 @@ const processIpcMessage = __RAW_process_ipc_message_fn__ const osName = __TEMPLATE_os_name__ const fetchChannelDataCommand = __TEMPLATE_fetch_channel_data_command__ - const useCustomProtocol = __TEMPLATE_use_custom_protocol__ + const linuxIpcProtocolEnabled = __TEMPLATE_linux_ipc_protocol_enabled__ + let customProtocolIpcFailed = false - Object.defineProperty(window.__TAURI_INTERNALS__, 'postMessage', { - value: (message) => { - const { cmd, callback, error, payload, options } = message + // on Linux we only use the custom-protocol-based IPC if the linux-ipc-protocol Cargo feature is enabled + // on Android we never use it because Android does not have support to reading the request body + const canUseCustomProtocol = + osName === 'linux' ? linuxIpcProtocolEnabled : osName !== 'android' - // use custom protocol for IPC if: - // - the flag is set to true or - // - the command is the fetch data command or - // - when not on Linux/Android - // AND - // - when not on macOS with an https URL - if ( - (useCustomProtocol || - cmd === fetchChannelDataCommand || - !(osName === 'linux' || osName === 'android')) && - !( - (osName === 'macos' || osName === 'ios') && - location.protocol === 'https:' - ) - ) { - const { contentType, data } = processIpcMessage(payload) - fetch(window.__TAURI_INTERNALS__.convertFileSrc(cmd, 'ipc'), { - method: 'POST', - body: data, - headers: { - 'Content-Type': contentType, - 'Tauri-Callback': callback, - 'Tauri-Error': error, - ...options?.headers + function sendIpcMessage(message) { + const { cmd, callback, error, payload, options } = message + + if ( + !customProtocolIpcFailed && + (canUseCustomProtocol || cmd === fetchChannelDataCommand) + ) { + const { contentType, data } = processIpcMessage(payload) + fetch(window.__TAURI_INTERNALS__.convertFileSrc(cmd, 'ipc'), { + method: 'POST', + body: data, + headers: { + 'Content-Type': contentType, + 'Tauri-Callback': callback, + 'Tauri-Error': error, + ...options?.headers + } + }) + .then((response) => { + const cb = response.ok ? callback : error + // we need to split here because on Android the content-type gets duplicated + switch ((response.headers.get('content-type') || '').split(',')[0]) { + case 'application/json': + return response.json().then((r) => [cb, r]) + case 'text/plain': + return response.text().then((r) => [cb, r]) + default: + return response.arrayBuffer().then((r) => [cb, r]) } }) - .then((response) => { - const cb = response.ok ? callback : error - // we need to split here because on Android the content-type gets duplicated - switch ( - (response.headers.get('content-type') || '').split(',')[0] - ) { - case 'application/json': - return response.json().then((r) => [cb, r]) - case 'text/plain': - return response.text().then((r) => [cb, r]) - default: - return response.arrayBuffer().then((r) => [cb, r]) - } - }) - .then(([cb, data]) => { - if (window[`_${cb}`]) { - window[`_${cb}`](data) - } else { - console.warn( - `[TAURI] Couldn't find callback id {cb} in window. This might happen when the app is reloaded while Rust is running an asynchronous operation.` - ) - } - }) - } else { - // otherwise use the postMessage interface - const { data } = processIpcMessage({ - cmd, - callback, - error, - options, - payload + .then(([cb, data]) => { + if (window[`_${cb}`]) { + window[`_${cb}`](data) + } else { + console.warn( + `[TAURI] Couldn't find callback id {cb} in window. This might happen when the app is reloaded while Rust is running an asynchronous operation.` + ) + } }) - window.ipc.postMessage(data) - } + .catch(() => { + // failed to use the custom protocol IPC (either the webview blocked a custom protocol or it was a CSP error) + // so we need to fallback to the postMessage interface + customProtocolIpcFailed = true + sendIpcMessage(message) + }) + } else { + // otherwise use the postMessage interface + const { data } = processIpcMessage({ + cmd, + callback, + error, + options, + payload + }) + window.ipc.postMessage(data) } + } + + Object.defineProperty(window.__TAURI_INTERNALS__, 'postMessage', { + value: sendIpcMessage }) })() diff --git a/core/tauri/scripts/ipc.js b/core/tauri/scripts/ipc.js index b944139c6..77b21f5d3 100644 --- a/core/tauri/scripts/ipc.js +++ b/core/tauri/scripts/ipc.js @@ -1,4 +1,4 @@ -// Copyright 2019-2023 Tauri Programme within The Commons Conservancy +// Copyright 2019-2024 Tauri Programme within The Commons Conservancy // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT diff --git a/core/tauri/scripts/isolation.js b/core/tauri/scripts/isolation.js index c8e0ed7fb..19a34c9ba 100644 --- a/core/tauri/scripts/isolation.js +++ b/core/tauri/scripts/isolation.js @@ -1,15 +1,17 @@ -// Copyright 2019-2023 Tauri Programme within The Commons Conservancy +// Copyright 2019-2024 Tauri Programme within The Commons Conservancy // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT -window.addEventListener('DOMContentLoaded', () => { - let style = document.createElement('style') - style.textContent = __TEMPLATE_style__ - document.head.append(style) +if (location.href !== __TEMPLATE_isolation_src__) { + window.addEventListener('DOMContentLoaded', () => { + let style = document.createElement('style') + style.textContent = __TEMPLATE_style__ + document.head.append(style) - let iframe = document.createElement('iframe') - iframe.id = '__tauri_isolation__' - iframe.sandbox.add('allow-scripts') - iframe.src = __TEMPLATE_isolation_src__ - document.body.append(iframe) -}) + let iframe = document.createElement('iframe') + iframe.id = '__tauri_isolation__' + iframe.sandbox.add('allow-scripts') + iframe.src = __TEMPLATE_isolation_src__ + document.body.append(iframe) + }) +} diff --git a/core/tauri/scripts/pattern.js b/core/tauri/scripts/pattern.js index c07a1c492..dce39b4ea 100644 --- a/core/tauri/scripts/pattern.js +++ b/core/tauri/scripts/pattern.js @@ -1,4 +1,4 @@ -// Copyright 2019-2023 Tauri Programme within The Commons Conservancy +// Copyright 2019-2024 Tauri Programme within The Commons Conservancy // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT diff --git a/core/tauri/scripts/process-ipc-message-fn.js b/core/tauri/scripts/process-ipc-message-fn.js index 36c041b8e..ba621be39 100644 --- a/core/tauri/scripts/process-ipc-message-fn.js +++ b/core/tauri/scripts/process-ipc-message-fn.js @@ -1,4 +1,4 @@ -// Copyright 2019-2023 Tauri Programme within The Commons Conservancy +// Copyright 2019-2024 Tauri Programme within The Commons Conservancy // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT diff --git a/core/tauri/src/app.rs b/core/tauri/src/app.rs index 05101cff3..8765c3a0e 100644 --- a/core/tauri/src/app.rs +++ b/core/tauri/src/app.rs @@ -1,8 +1,9 @@ -// Copyright 2019-2023 Tauri Programme within The Commons Conservancy +// Copyright 2019-2024 Tauri Programme within The Commons Conservancy // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT use crate::{ + image::Image, ipc::{ channel::ChannelDataIpcQueue, CallbackFn, CommandArg, CommandItem, Invoke, InvokeError, InvokeHandler, InvokeResponder, InvokeResponse, @@ -12,16 +13,17 @@ use crate::{ AppManager, Asset, }, plugin::{Plugin, PluginStore}, + resources::ResourceTable, runtime::{ - window::WindowEvent as RuntimeWindowEvent, ExitRequestedEventAction, - RunEvent as RuntimeRunEvent, + window::{WebviewEvent as RuntimeWebviewEvent, WindowEvent as RuntimeWindowEvent}, + ExitRequestedEventAction, RunEvent as RuntimeRunEvent, }, sealed::{ManagerBase, RuntimeOrDispatch}, utils::config::Config, - utils::{assets::Assets, Env}, + utils::Env, webview::PageLoadPayload, - Context, DeviceEventFilter, EventLoopMessage, Icon, Manager, Monitor, Runtime, Scopes, - StateManager, Theme, Webview, WebviewWindowBuilder, Window, + Context, DeviceEventFilter, EventLoopMessage, Manager, Monitor, Runtime, Scopes, StateManager, + Theme, Webview, WebviewWindowBuilder, Window, }; #[cfg(desktop)] @@ -34,19 +36,17 @@ use tauri_macros::default_runtime; #[cfg(desktop)] use tauri_runtime::EventLoopProxy; use tauri_runtime::{ - window::{ - dpi::{PhysicalPosition, PhysicalSize}, - FileDropEvent, - }, + dpi::{PhysicalPosition, PhysicalSize}, + window::DragDropEvent, RuntimeInitArgs, }; -use tauri_utils::{debug_eprintln, PackageInfo}; +use tauri_utils::PackageInfo; use std::{ borrow::Cow, collections::HashMap, fmt, - sync::{mpsc::Sender, Arc}, + sync::{mpsc::Sender, Arc, MutexGuard}, }; use crate::{event::EventId, runtime::RuntimeHandle, Event, EventTarget}; @@ -62,6 +62,8 @@ pub(crate) type GlobalMenuEventListener = Box = Box; pub(crate) type GlobalWindowEventListener = Box, &WindowEvent) + Send + Sync>; +pub(crate) type GlobalWebviewEventListener = + Box, &WebviewEvent) + Send + Sync>; /// A closure that is run when the Tauri application is setting up. pub type SetupHook = Box) -> Result<(), Box> + Send>; @@ -129,8 +131,8 @@ pub enum WindowEvent { /// The window inner size. new_inner_size: PhysicalSize, }, - /// An event associated with the file drop action. - FileDrop(FileDropEvent), + /// An event associated with the drag and drop action. + DragDrop(DragDropEvent), /// The system window theme has changed. Only delivered if the window [`theme`](`crate::window::WindowBuilder#method.theme`) is `None`. /// /// Applications might wish to react to this to change the theme of the content of the window when the system changes the window theme. @@ -158,12 +160,28 @@ impl From for WindowEvent { scale_factor, new_inner_size, }, - RuntimeWindowEvent::FileDrop(event) => Self::FileDrop(event), + RuntimeWindowEvent::DragDrop(event) => Self::DragDrop(event), RuntimeWindowEvent::ThemeChanged(theme) => Self::ThemeChanged(theme), } } } +/// An event from a window. +#[derive(Debug, Clone)] +#[non_exhaustive] +pub enum WebviewEvent { + /// An event associated with the drag and drop action. + DragDrop(DragDropEvent), +} + +impl From for WebviewEvent { + fn from(event: RuntimeWebviewEvent) -> Self { + match event { + RuntimeWebviewEvent::DragDrop(e) => Self::DragDrop(e), + } + } +} + /// An application event, triggered from the event loop. /// /// See [`App::run`](crate::App#method.run) for usage examples. @@ -177,7 +195,7 @@ pub enum RunEvent { ExitRequested { /// Exit code. /// [`Option::None`] when the exit is requested by user interaction, - /// [`Option::Some`] when requested programatically via [`AppHandle#method.exit`] and [`AppHandle#method.restart`]. + /// [`Option::Some`] when requested programmatically via [`AppHandle#method.exit`] and [`AppHandle#method.restart`]. code: Option, /// Event API api: ExitRequestApi, @@ -190,6 +208,14 @@ pub enum RunEvent { /// The detailed event. event: WindowEvent, }, + /// An event associated with a webview. + #[non_exhaustive] + WebviewEvent { + /// The window label. + label: String, + /// The detailed event. + event: WebviewEvent, + }, /// Application ready. Ready, /// Sent if the event loop is being resumed. @@ -239,7 +265,7 @@ impl AssetResolver { } /// Iterate on all assets. - pub fn iter(&self) -> Box + '_> { + pub fn iter(&self) -> Box + '_> { self.manager.assets.iter() } } @@ -376,7 +402,7 @@ impl AppHandle { /// Exits the app by triggering [`RunEvent::ExitRequested`] and [`RunEvent::Exit`]. pub fn exit(&self, exit_code: i32) { if let Err(e) = self.runtime_handle.request_exit(exit_code) { - debug_eprintln!("failed to exit: {}", e); + log::error!("failed to exit: {}", e); self.cleanup_before_exit(); std::process::exit(exit_code); } @@ -391,7 +417,12 @@ impl AppHandle { } } -impl Manager for AppHandle {} +impl Manager for AppHandle { + fn resources_table(&self) -> MutexGuard<'_, ResourceTable> { + self.manager.resources_table() + } +} + impl ManagerBase for AppHandle { fn manager(&self) -> &AppManager { &self.manager @@ -432,7 +463,12 @@ impl fmt::Debug for App { } } -impl Manager for App {} +impl Manager for App { + fn resources_table(&self) -> MutexGuard<'_, ResourceTable> { + self.manager.resources_table() + } +} + impl ManagerBase for App { fn manager(&self) -> &AppManager { &self.manager @@ -482,13 +518,7 @@ macro_rules! shared_app_impl { &self, handler: F, ) { - self - .manager - .menu - .global_event_listeners - .lock() - .unwrap() - .push(Box::new(handler)); + self.manager.menu.on_menu_event(handler) } /// Registers a global tray icon menu event listener. @@ -498,35 +528,7 @@ macro_rules! shared_app_impl { &self, handler: F, ) { - self - .manager - .tray - .global_event_listeners - .lock() - .unwrap() - .push(Box::new(handler)); - } - - /// Gets the first tray icon registered, - /// usually the one configured in the Tauri configuration file. - #[cfg(all(desktop, feature = "tray-icon"))] - #[cfg_attr(docsrs, doc(cfg(all(desktop, feature = "tray-icon"))))] - pub fn tray(&self) -> Option> { - self.manager.tray.icons.lock().unwrap().first().cloned() - } - - /// Removes the first tray icon registerd, usually the one configured in - /// tauri config file, from tauri's internal state and returns it. - /// - /// Note that dropping the returned icon, will cause the tray icon to disappear. - #[cfg(all(desktop, feature = "tray-icon"))] - #[cfg_attr(docsrs, doc(cfg(all(desktop, feature = "tray-icon"))))] - pub fn remove_tray(&self) -> Option> { - let mut icons = self.manager.tray.icons.lock().unwrap(); - if !icons.is_empty() { - return Some(icons.swap_remove(0)); - } - None + self.manager.tray.on_tray_icon_event(handler) } /// Gets a tray icon using the provided id. @@ -537,20 +539,13 @@ macro_rules! shared_app_impl { I: ?Sized, TrayIconId: PartialEq<&'a I>, { - self - .manager - .tray - .icons - .lock() - .unwrap() - .iter() - .find(|t| t.id() == &id) - .cloned() + self.manager.tray.tray_by_id(id) } /// Removes a tray icon using the provided id from tauri's internal state and returns it. /// - /// Note that dropping the returned icon, will cause the tray icon to disappear. + /// Note that dropping the returned icon, may cause the tray icon to disappear + /// if it wasn't cloned somewhere else or referenced by JS. #[cfg(all(desktop, feature = "tray-icon"))] #[cfg_attr(docsrs, doc(cfg(all(desktop, feature = "tray-icon"))))] pub fn remove_tray_by_id<'a, I>(&self, id: &'a I) -> Option> @@ -558,12 +553,7 @@ macro_rules! shared_app_impl { I: ?Sized, TrayIconId: PartialEq<&'a I>, { - let mut icons = self.manager.tray.icons.lock().unwrap(); - let idx = icons.iter().position(|t| t.id() == &id); - if let Some(idx) = idx { - return Some(icons.swap_remove(idx)); - } - None + self.manager.tray.remove_tray_by_id(id) } /// Gets the app's configuration, defined on the `tauri.conf.json` file. @@ -607,7 +597,7 @@ macro_rules! shared_app_impl { }) } /// Returns the default window icon. - pub fn default_window_icon(&self) -> Option<&Icon> { + pub fn default_window_icon(&self) -> Option<&Image<'_>> { self.manager.window.default_icon.as_ref() } @@ -764,7 +754,6 @@ macro_rules! shared_app_impl { pub fn cleanup_before_exit(&self) { #[cfg(all(desktop, feature = "tray-icon"))] self.manager.tray.icons.lock().unwrap().clear(); - self.resources_table().clear(); } } @@ -819,7 +808,7 @@ macro_rules! shared_app_impl { /// Listen to an event on this app only once. /// /// See [`Self::listen`] for more information. - pub fn once(&self, event: impl Into, handler: F) + pub fn once(&self, event: impl Into, handler: F) -> EventId where F: FnOnce(Event) + Send + 'static, { @@ -844,6 +833,7 @@ impl App { self.handle.plugin(crate::webview::plugin::init())?; self.handle.plugin(crate::app::plugin::init())?; self.handle.plugin(crate::resources::plugin::init())?; + self.handle.plugin(crate::image::plugin::init())?; #[cfg(desktop)] self.handle.plugin(crate::menu::plugin::init())?; #[cfg(all(desktop, feature = "tray-icon"))] @@ -976,7 +966,7 @@ impl App { /// } /// ``` #[cfg(desktop)] - pub fn run_iteration, RunEvent)>(&mut self, mut callback: F) { + pub fn run_iteration, RunEvent) + 'static>(&mut self, mut callback: F) { let manager = self.manager.clone(); let app_handle = self.handle().clone(); @@ -1043,6 +1033,9 @@ pub struct Builder { /// Window event handlers that listens to all windows. window_event_listeners: Vec>, + /// Webview event handlers that listens to all webviews. + webview_event_listeners: Vec>, + /// The device event filter. device_event_filter: DeviceEventFilter, } @@ -1055,7 +1048,7 @@ struct InvokeInitializationScript<'a> { process_ipc_message_fn: &'a str, os_name: &'a str, fetch_channel_data_command: &'a str, - use_custom_protocol: bool, + linux_ipc_protocol_enabled: bool, } /// Make `Wry` the default `Runtime` for `Builder` @@ -1088,7 +1081,7 @@ impl Builder { process_ipc_message_fn: crate::manager::webview::PROCESS_IPC_MESSAGE_FN, os_name: std::env::consts::OS, fetch_channel_data_command: crate::ipc::channel::FETCH_CHANNEL_DATA_COMMAND, - use_custom_protocol: cfg!(ipc_custom_protocol), + linux_ipc_protocol_enabled: cfg!(feature = "linux-ipc-protocol"), } .render_default(&Default::default()) .unwrap() @@ -1101,6 +1094,7 @@ impl Builder { menu: None, enable_macos_default_menu: true, window_event_listeners: Vec::new(), + webview_event_listeners: Vec::new(), device_event_filter: Default::default(), } } @@ -1400,6 +1394,27 @@ tauri::Builder::default() self } + /// Registers a webview event handler for all webviews. + /// + /// # Examples + /// ``` + /// tauri::Builder::default() + /// .on_webview_event(|window, event| match event { + /// tauri::WebviewEvent::DragDrop(event) => { + /// println!("{:?}", event); + /// } + /// _ => {} + /// }); + /// ``` + #[must_use] + pub fn on_webview_event, &WebviewEvent) + Send + Sync + 'static>( + mut self, + handler: F, + ) -> Self { + self.webview_event_listeners.push(Box::new(handler)); + self + } + /// Registers a URI scheme protocol available to all webviews. /// Leverages [setURLSchemeHandler](https://developer.apple.com/documentation/webkit/wkwebviewconfiguration/2875766-seturlschemehandler) on macOS, /// [AddWebResourceRequestedFilter](https://docs.microsoft.com/en-us/dotnet/api/microsoft.web.webview2.core.corewebview2.addwebresourcerequestedfilter?view=webview2-dotnet-1.0.774.44) on Windows @@ -1528,7 +1543,7 @@ tauri::Builder::default() feature = "tracing", tracing::instrument(name = "app::build", skip_all) )] - pub fn build(mut self, context: Context) -> crate::Result> { + pub fn build(mut self, context: Context) -> crate::Result> { #[cfg(target_os = "macos")] if self.menu.is_none() && self.enable_macos_default_menu { self.menu = Some(Box::new(|app_handle| { @@ -1544,6 +1559,7 @@ tauri::Builder::default() self.uri_scheme_protocols, self.state, self.window_event_listeners, + self.webview_event_listeners, #[cfg(desktop)] HashMap::new(), (self.invoke_responder, self.invoke_initialization_script), @@ -1695,7 +1711,7 @@ tauri::Builder::default() } /// Runs the configured Tauri application. - pub fn run(self, context: Context) -> crate::Result<()> { + pub fn run(self, context: Context) -> crate::Result<()> { self.build(context)?.run(|_, _| {}); Ok(()) } @@ -1770,6 +1786,8 @@ fn setup(app: &mut App) -> crate::Result<()> { .build_internal(&window_labels, &webview_labels)?; } + app.manager.assets.setup(app); + if let Some(setup) = app.setup.take() { (setup)(app).map_err(|e| crate::Error::Setup(e.into()))?; } @@ -1800,6 +1818,10 @@ fn on_event_loop_event( label, event: event.into(), }, + RuntimeRunEvent::WebviewEvent { label, event } => RunEvent::WebviewEvent { + label, + event: event.into(), + }, RuntimeRunEvent::Ready => { // set the app icon in development #[cfg(all(dev, target_os = "macos"))] diff --git a/core/tauri/src/app/plugin.rs b/core/tauri/src/app/plugin.rs index ce2b35322..791dda39d 100644 --- a/core/tauri/src/app/plugin.rs +++ b/core/tauri/src/app/plugin.rs @@ -1,4 +1,4 @@ -// Copyright 2019-2023 Tauri Programme within The Commons Conservancy +// Copyright 2019-2024 Tauri Programme within The Commons Conservancy // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT diff --git a/core/tauri/src/async_runtime.rs b/core/tauri/src/async_runtime.rs index ebc65954b..f3ef1d140 100644 --- a/core/tauri/src/async_runtime.rs +++ b/core/tauri/src/async_runtime.rs @@ -1,4 +1,4 @@ -// Copyright 2019-2023 Tauri Programme within The Commons Conservancy +// Copyright 2019-2024 Tauri Programme within The Commons Conservancy // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT diff --git a/core/tauri/src/error.rs b/core/tauri/src/error.rs index 6096890ff..3779076f0 100644 --- a/core/tauri/src/error.rs +++ b/core/tauri/src/error.rs @@ -1,4 +1,4 @@ -// Copyright 2019-2023 Tauri Programme within The Commons Conservancy +// Copyright 2019-2024 Tauri Programme within The Commons Conservancy // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT @@ -40,6 +40,9 @@ pub enum Error { /// Webview label must be unique. #[error("a webview with label `{0}` already exists")] WebviewLabelAlreadyExists(String), + /// Cannot use the webview reparent function on webview windows. + #[error("cannot reparent when using a WebviewWindow")] + CannotReparentWebviewWindow, /// Embedded asset not found. #[error("asset not found: {0}")] AssetNotFound(String), @@ -78,10 +81,10 @@ pub enum Error { /// Invalid glob pattern. #[error("invalid glob pattern: {0}")] GlobPattern(#[from] glob::PatternError), - /// Error decoding PNG image. - #[cfg(feature = "icon-png")] - #[error("failed to decode PNG: {0}")] - PngDecode(#[from] png::DecodingError), + /// Image error. + #[cfg(any(feature = "image-png", feature = "image-ico"))] + #[error("failed to process image: {0}")] + Image(#[from] image::error::ImageError), /// The Window's raw handle is invalid for the platform. #[error("Unexpected `raw_window_handle` for the current platform")] InvalidWindowHandle, diff --git a/core/tauri/src/event/listener.rs b/core/tauri/src/event/listener.rs index 640d5f4f7..26e10d312 100644 --- a/core/tauri/src/event/listener.rs +++ b/core/tauri/src/event/listener.rs @@ -1,4 +1,4 @@ -// Copyright 2019-2023 Tauri Programme within The Commons Conservancy +// Copyright 2019-2024 Tauri Programme within The Commons Conservancy // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT @@ -16,12 +16,6 @@ use std::{ }, }; -#[derive(Debug, Clone, PartialEq, Eq, Hash)] -struct JsHandler { - target: EventTarget, - id: EventId, -} - /// What to do with the pending handler when resolving it? enum Pending { Unlisten(EventId), @@ -48,6 +42,18 @@ impl Handler { } } +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +struct JsHandler { + target: EventTarget, + id: EventId, +} + +impl JsHandler { + fn new(target: EventTarget, id: EventId) -> Self { + Self { target, id } + } +} + type WebviewLabel = String; type EventName = String; @@ -159,7 +165,7 @@ impl Listeners { event: String, target: EventTarget, handler: F, - ) { + ) -> EventId { let self_ = self.clone(); let handler = Cell::new(Some(handler)); @@ -170,7 +176,7 @@ impl Listeners { .expect("attempted to call handler more than once"); handler(event); self_.unlisten(id); - }); + }) } /// Removes an event listener. @@ -189,23 +195,16 @@ impl Listeners { F: Fn(&EventTarget) -> bool, { let mut maybe_pending = false; + match self.inner.handlers.try_lock() { Err(_) => self.insert_pending(Pending::Emit(emit_args.clone())), Ok(lock) => { if let Some(handlers) = lock.get(&emit_args.event_name) { - let handlers: Vec<_> = match filter { - Some(filter) => handlers - .iter() - .filter(|(_, Handler { target, .. })| *target == EventTarget::Any || filter(target)) - .collect(), - None => handlers.iter().collect(), - }; - - if !handlers.is_empty() { + let handlers = handlers.iter(); + let handlers = handlers.filter(|(_, h)| match_any_or_filter(&h.target, &filter)); + for (&id, Handler { callback, .. }) in handlers { maybe_pending = true; - for (&id, Handler { callback, .. }) in handlers { - (callback)(Event::new(id, emit_args.payload.clone())) - } + (callback)(Event::new(id, emit_args.payload.clone())) } } } @@ -236,37 +235,19 @@ impl Listeners { .or_default() .entry(event.to_string()) .or_default() - .insert(JsHandler { id, target }); + .insert(JsHandler::new(target, id)); } - pub(crate) fn unlisten_js(&self, id: EventId) { - let mut listeners = self.inner.js_event_listeners.lock().unwrap(); - - let mut empty = None; - let listeners = listeners.values_mut(); - 'outer: for listeners in listeners { - for (key, handlers) in listeners.iter_mut() { - let mut found = false; - - handlers.retain(|h| { - let keep = h.id != id; - if !found { - found = !keep - } - keep - }); + pub(crate) fn unlisten_js(&self, event: &str, id: EventId) { + let mut js_listeners = self.inner.js_event_listeners.lock().unwrap(); + let js_listeners = js_listeners.values_mut(); + for js_listeners in js_listeners { + if let Some(handlers) = js_listeners.get_mut(event) { + handlers.retain(|h| h.id != id); if handlers.is_empty() { - empty.replace(key.clone()); + js_listeners.remove(event); } - - if found { - break 'outer; - } - } - - if let Some(key) = &empty { - listeners.remove(key); } } } @@ -276,37 +257,67 @@ impl Listeners { event: &str, filter: F, ) -> bool { - let listeners = self.inner.js_event_listeners.lock().unwrap(); - listeners.values().any(|events| { + let js_listeners = self.inner.js_event_listeners.lock().unwrap(); + js_listeners.values().any(|events| { events .get(event) - .map(|handlers| handlers.iter().any(|h| filter(&h.target))) + .map(|handlers| handlers.iter().any(|handler| filter(&handler.target))) .unwrap_or(false) }) } - pub(crate) fn try_for_each_js<'a, R, I, F>( + pub(crate) fn emit_js_filter<'a, R, I, F>( &self, - event: &str, mut webviews: I, - callback: F, + event: &str, + emit_args: &EmitArgs, + filter: Option, ) -> crate::Result<()> where R: Runtime, I: Iterator>, - F: Fn(&Webview, &EventTarget) -> crate::Result<()>, + F: Fn(&EventTarget) -> bool, { - let listeners = self.inner.js_event_listeners.lock().unwrap(); + let js_listeners = self.inner.js_event_listeners.lock().unwrap(); webviews.try_for_each(|webview| { - if let Some(handlers) = listeners.get(webview.label()).and_then(|s| s.get(event)) { - for JsHandler { target, .. } in handlers { - callback(webview, target)?; - } + if let Some(handlers) = js_listeners.get(webview.label()).and_then(|s| s.get(event)) { + let ids = handlers + .iter() + .filter(|handler| match_any_or_filter(&handler.target, &filter)) + .map(|handler| handler.id) + .collect::>(); + webview.emit_js(emit_args, &ids)?; } Ok(()) }) } + + pub(crate) fn emit_js<'a, R, I>( + &self, + webviews: I, + event: &str, + emit_args: &EmitArgs, + ) -> crate::Result<()> + where + R: Runtime, + I: Iterator>, + { + self.emit_js_filter( + webviews, + event, + emit_args, + None::<&dyn Fn(&EventTarget) -> bool>, + ) + } +} + +#[inline(always)] +fn match_any_or_filter bool>( + target: &EventTarget, + filter: &Option, +) -> bool { + *target == EventTarget::Any || filter.as_ref().map(|f| f(target)).unwrap_or(true) } #[cfg(test)] diff --git a/core/tauri/src/event/mod.rs b/core/tauri/src/event/mod.rs index d165581c4..3e2fd207c 100644 --- a/core/tauri/src/event/mod.rs +++ b/core/tauri/src/event/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2019-2023 Tauri Programme within The Commons Conservancy +// Copyright 2019-2024 Tauri Programme within The Commons Conservancy // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT @@ -174,21 +174,19 @@ pub fn listen_js_script( handler: &str, ) -> String { format!( - " - (function () {{ + "(function () {{ if (window['{listeners}'] === void 0) {{ Object.defineProperty(window, '{listeners}', {{ value: Object.create(null) }}); }} if (window['{listeners}']['{event}'] === void 0) {{ - Object.defineProperty(window['{listeners}'], '{event}', {{ value: [] }}); + Object.defineProperty(window['{listeners}'], '{event}', {{ value: Object.create(null) }}); }} const eventListeners = window['{listeners}']['{event}'] const listener = {{ - id: {event_id}, target: {target}, handler: {handler} }}; - eventListeners.push(listener); + Object.defineProperty(eventListeners, '{event_id}', {{ value: listener, configurable: true }}); }})() ", listeners = listeners_object_name, @@ -199,14 +197,14 @@ pub fn listen_js_script( pub fn emit_js_script( event_emit_function_name: &str, emit_args: &EmitArgs, - serialized_target: &str, + serialized_ids: &str, ) -> crate::Result { Ok(format!( - "(function () {{ const fn = window['{}']; fn && fn({{event: {}, payload: {}}}, {target}) }})()", + "(function () {{ const fn = window['{}']; fn && fn({{event: {}, payload: {}}}, {ids}) }})()", event_emit_function_name, emit_args.event, emit_args.payload, - target = serialized_target, + ids = serialized_ids, )) } @@ -216,14 +214,10 @@ pub fn unlisten_js_script( event_id: EventId, ) -> String { format!( - " - (function () {{ + "(function () {{ const listeners = (window['{listeners_object_name}'] || {{}})['{event_name}'] if (listeners) {{ - const index = window['{listeners_object_name}']['{event_name}'].findIndex(e => e.id === {event_id}) - if (index > -1) {{ - window['{listeners_object_name}']['{event_name}'].splice(index, 1) - }} + delete window['{listeners_object_name}']['{event_name}'][{event_id}]; }} }})() ", @@ -232,14 +226,13 @@ pub fn unlisten_js_script( pub fn event_initialization_script(function: &str, listeners: &str) -> String { format!( - " - Object.defineProperty(window, '{function}', {{ - value: function (eventData, target) {{ + "Object.defineProperty(window, '{function}', {{ + value: function (eventData, ids) {{ const listeners = (window['{listeners}'] && window['{listeners}'][eventData.event]) || [] - for (let i = listeners.length - 1; i >= 0; i--) {{ - const listener = listeners[i] - if ((listener.target.kind === 'Global' && target.kind === 'Global') || (listener.target.kind === target.kind && listener.target.label === target.label)) {{ - eventData.id = listener.id + for (const id of ids) {{ + const listener = listeners[id] + if (listener) {{ + eventData.id = id listener.handler(eventData) }} }} diff --git a/core/tauri/src/event/plugin.rs b/core/tauri/src/event/plugin.rs index 9eebca3bc..34b404a4d 100644 --- a/core/tauri/src/event/plugin.rs +++ b/core/tauri/src/event/plugin.rs @@ -1,4 +1,4 @@ -// Copyright 2019-2023 Tauri Programme within The Commons Conservancy +// Copyright 2019-2024 Tauri Programme within The Commons Conservancy // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT diff --git a/core/tauri/src/image/mod.rs b/core/tauri/src/image/mod.rs new file mode 100644 index 000000000..e7a3c9c85 --- /dev/null +++ b/core/tauri/src/image/mod.rs @@ -0,0 +1,200 @@ +// Copyright 2019-2024 Tauri Programme within The Commons Conservancy +// SPDX-License-Identifier: Apache-2.0 +// SPDX-License-Identifier: MIT + +//! Image types used by this crate and also referenced by the JavaScript API layer. + +pub(crate) mod plugin; + +use std::borrow::Cow; +use std::sync::Arc; + +use crate::{Manager, Resource, ResourceId, Runtime}; + +/// An RGBA Image in row-major order from top to bottom. +#[derive(Debug, Clone)] +pub struct Image<'a> { + rgba: Cow<'a, [u8]>, + width: u32, + height: u32, +} + +impl Resource for Image<'static> {} + +impl Image<'static> { + /// Creates a new Image using RGBA data, in row-major order from top to bottom, and with specified width and height. + /// + /// Similar to [`Self::new`] but avoids cloning the rgba data to get an owned Image. + pub const fn new_owned(rgba: Vec, width: u32, height: u32) -> Self { + Self { + rgba: Cow::Owned(rgba), + width, + height, + } + } +} + +impl<'a> Image<'a> { + /// Creates a new Image using RGBA data, in row-major order from top to bottom, and with specified width and height. + pub const fn new(rgba: &'a [u8], width: u32, height: u32) -> Self { + Self { + rgba: Cow::Borrowed(rgba), + width, + height, + } + } + + /// Creates a new image using the provided bytes. + /// + /// Only `ico` and `png` are supported (based on activated feature flag). + #[cfg(any(feature = "image-ico", feature = "image-png"))] + #[cfg_attr(docsrs, doc(cfg(any(feature = "image-ico", feature = "image-png"))))] + pub fn from_bytes(bytes: &[u8]) -> crate::Result { + use image::GenericImageView; + + let img = image::load_from_memory(bytes)?; + let pixels = img + .pixels() + .flat_map(|(_, _, pixel)| pixel.0) + .collect::>(); + Ok(Self { + rgba: Cow::Owned(pixels), + width: img.width(), + height: img.height(), + }) + } + + /// Creates a new image using the provided path. + /// + /// Only `ico` and `png` are supported (based on activated feature flag). + #[cfg(any(feature = "image-ico", feature = "image-png"))] + #[cfg_attr(docsrs, doc(cfg(any(feature = "image-ico", feature = "image-png"))))] + pub fn from_path>(path: P) -> crate::Result { + let bytes = std::fs::read(path)?; + Self::from_bytes(&bytes) + } + + /// Returns the RGBA data for this image, in row-major order from top to bottom. + pub fn rgba(&'a self) -> &'a [u8] { + &self.rgba + } + + /// Returns the width of this image. + pub fn width(&self) -> u32 { + self.width + } + + /// Returns the height of this image. + pub fn height(&self) -> u32 { + self.height + } + + /// Convert into a 'static owned [`Image`]. + /// This will allocate. + pub fn to_owned(self) -> Image<'static> { + Image { + rgba: match self.rgba { + Cow::Owned(v) => Cow::Owned(v), + Cow::Borrowed(v) => Cow::Owned(v.to_vec()), + }, + height: self.height, + width: self.width, + } + } +} + +impl<'a> From> for crate::runtime::Icon<'a> { + fn from(img: Image<'a>) -> Self { + Self { + rgba: img.rgba, + width: img.width, + height: img.height, + } + } +} + +#[cfg(desktop)] +impl TryFrom> for muda::Icon { + type Error = crate::Error; + + fn try_from(img: Image<'_>) -> Result { + muda::Icon::from_rgba(img.rgba.to_vec(), img.width, img.height).map_err(Into::into) + } +} + +#[cfg(all(desktop, feature = "tray-icon"))] +impl TryFrom> for tray_icon::Icon { + type Error = crate::Error; + + fn try_from(img: Image<'_>) -> Result { + tray_icon::Icon::from_rgba(img.rgba.to_vec(), img.width, img.height).map_err(Into::into) + } +} + +/// An image type that accepts file paths, raw bytes, previously loaded images and image objects. +/// This type is meant to be used along the [transformImage](https://beta.tauri.app/references/v2/js/image/namespaceimage/#transformimage) API. +/// +/// # Stability +/// +/// The stability of the variants are not guaranteed, and matching against them is not recommended. +/// Use [`JsImage::into_img`] instead. +#[derive(serde::Deserialize)] +#[serde(untagged)] +#[non_exhaustive] +pub enum JsImage { + /// A reference to a image in the filesystem. + #[non_exhaustive] + Path(std::path::PathBuf), + /// Image from raw bytes. + #[non_exhaustive] + Bytes(Vec), + /// An image that was previously loaded with the API and is stored in the resource table. + #[non_exhaustive] + Resource(ResourceId), + /// Raw RGBA definition of an image. + #[non_exhaustive] + Rgba { + /// Image bytes. + rgba: Vec, + /// Image width. + width: u32, + /// Image height. + height: u32, + }, +} + +impl JsImage { + /// Converts this intermediate image format into an actual [`Image`]. + pub fn into_img>(self, manager: &M) -> crate::Result>> { + match self { + Self::Resource(rid) => manager.resources_table().get::>(rid), + #[cfg(any(feature = "image-ico", feature = "image-png"))] + Self::Path(path) => Image::from_path(path).map(Arc::new).map_err(Into::into), + + #[cfg(any(feature = "image-ico", feature = "image-png"))] + Self::Bytes(bytes) => Image::from_bytes(&bytes).map(Arc::new).map_err(Into::into), + + Self::Rgba { + rgba, + width, + height, + } => Ok(Arc::new(Image::new_owned(rgba, width, height))), + + #[cfg(not(any(feature = "image-ico", feature = "image-png")))] + _ => Err( + std::io::Error::new( + std::io::ErrorKind::InvalidInput, + format!( + "expected RGBA image data, found {}", + match self { + JsImage::Path(_) => "a file path", + JsImage::Bytes(_) => "raw bytes", + _ => unreachable!(), + } + ), + ) + .into(), + ), + } + } +} diff --git a/core/tauri/src/image/plugin.rs b/core/tauri/src/image/plugin.rs new file mode 100644 index 000000000..d4a9ead37 --- /dev/null +++ b/core/tauri/src/image/plugin.rs @@ -0,0 +1,87 @@ +// Copyright 2019-2024 Tauri Programme within The Commons Conservancy +// SPDX-License-Identifier: Apache-2.0 +// SPDX-License-Identifier: MIT + +use serde::Serialize; + +use crate::plugin::{Builder, TauriPlugin}; +use crate::Manager; +use crate::{command, image::Image, ResourceId, Runtime, Webview}; + +#[command(root = "crate")] +fn new( + webview: Webview, + rgba: Vec, + width: u32, + height: u32, +) -> crate::Result { + let image = Image::new_owned(rgba, width, height); + let mut resources_table = webview.resources_table(); + let rid = resources_table.add(image); + Ok(rid) +} + +#[cfg(any(feature = "image-ico", feature = "image-png"))] +#[command(root = "crate")] +fn from_bytes(webview: Webview, bytes: Vec) -> crate::Result { + let image = Image::from_bytes(&bytes)?.to_owned(); + let mut resources_table = webview.resources_table(); + let rid = resources_table.add(image); + Ok(rid) +} + +#[cfg(not(any(feature = "image-ico", feature = "image-png")))] +#[command(root = "crate")] +fn from_bytes() -> std::result::Result<(), &'static str> { + Err("from_bytes is only supported if the `image-ico` or `image-png` Cargo features are enabled") +} + +#[cfg(any(feature = "image-ico", feature = "image-png"))] +#[command(root = "crate")] +fn from_path( + webview: Webview, + path: std::path::PathBuf, +) -> crate::Result { + let image = Image::from_path(path)?.to_owned(); + let mut resources_table = webview.resources_table(); + let rid = resources_table.add(image); + Ok(rid) +} + +#[cfg(not(any(feature = "image-ico", feature = "image-png")))] +#[command(root = "crate")] +fn from_path() -> std::result::Result<(), &'static str> { + Err("from_path is only supported if the `image-ico` or `image-png` Cargo features are enabled") +} + +#[command(root = "crate")] +fn rgba(webview: Webview, rid: ResourceId) -> crate::Result> { + let resources_table = webview.resources_table(); + let image = resources_table.get::>(rid)?; + Ok(image.rgba().to_vec()) +} + +#[derive(Serialize)] +struct Size { + width: u32, + height: u32, +} + +#[command(root = "crate")] +fn size(webview: Webview, rid: ResourceId) -> crate::Result { + let resources_table = webview.resources_table(); + let image = resources_table.get::>(rid)?; + Ok(Size { + width: image.width(), + height: image.height(), + }) +} + +/// Initializes the plugin. +pub fn init() -> TauriPlugin { + Builder::new("image") + .invoke_handler(crate::generate_handler![ + new, from_bytes, from_path, rgba, size + ]) + .build() +} diff --git a/core/tauri/src/ios.rs b/core/tauri/src/ios.rs index a46bf1741..75aaf7569 100644 --- a/core/tauri/src/ios.rs +++ b/core/tauri/src/ios.rs @@ -1,4 +1,4 @@ -// Copyright 2019-2023 Tauri Programme within The Commons Conservancy +// Copyright 2019-2024 Tauri Programme within The Commons Conservancy // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT diff --git a/core/tauri/src/ipc/authority.rs b/core/tauri/src/ipc/authority.rs index 1ff3f4a91..662fa48a1 100644 --- a/core/tauri/src/ipc/authority.rs +++ b/core/tauri/src/ipc/authority.rs @@ -1,18 +1,27 @@ -// Copyright 2019-2023 Tauri Programme within The Commons Conservancy +// Copyright 2019-2024 Tauri Programme within The Commons Conservancy // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT +use std::collections::BTreeMap; use std::fmt::{Debug, Display}; -use std::{collections::BTreeMap, ops::Deref}; +use std::sync::Arc; use serde::de::DeserializeOwned; +use serde::Serialize; use state::TypeMap; -use tauri_utils::acl::Value; use tauri_utils::acl::{ - resolved::{CommandKey, Resolved, ResolvedCommand, ResolvedScope, ScopeKey}, - ExecutionContext, + capability::{Capability, CapabilityFile, PermissionEntry}, + manifest::Manifest, + Value, APP_ACL_KEY, }; +use tauri_utils::acl::{ + resolved::{Resolved, ResolvedCommand, ResolvedScope, ScopeKey}, + ExecutionContext, Scopes, +}; +use tauri_utils::platform::Target; + +use url::Url; use crate::{ipc::InvokeError, sealed::ManagerBase, Runtime}; use crate::{AppHandle, Manager}; @@ -21,10 +30,9 @@ use super::{CommandArg, CommandItem}; /// The runtime authority used to authorize IPC execution based on the Access Control List. pub struct RuntimeAuthority { - #[cfg(debug_assertions)] - acl: BTreeMap, - allowed_commands: BTreeMap, - denied_commands: BTreeMap, + acl: BTreeMap, + allowed_commands: BTreeMap>, + denied_commands: BTreeMap>, pub(crate) scope_manager: ScopeManager, } @@ -34,8 +42,8 @@ pub enum Origin { Local, /// Remote origin. Remote { - /// Remote origin domain. - domain: String, + /// Remote URL. + url: Url, }, } @@ -43,7 +51,7 @@ impl Display for Origin { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { Self::Local => write!(f, "local"), - Self::Remote { domain } => write!(f, "remote: {domain}"), + Self::Remote { url } => write!(f, "remote: {url}"), } } } @@ -52,27 +60,182 @@ impl Origin { fn matches(&self, context: &ExecutionContext) -> bool { match (self, context) { (Self::Local, ExecutionContext::Local) => true, - ( - Self::Remote { domain }, - ExecutionContext::Remote { - domain: domain_pattern, - }, - ) => domain_pattern.matches(domain), + (Self::Remote { url }, ExecutionContext::Remote { url: url_pattern }) => { + url_pattern.test(url) + } _ => false, } } } +/// A capability that can be added at runtime. +pub trait RuntimeCapability { + /// Creates the capability file. + fn build(self) -> CapabilityFile; +} + +impl> RuntimeCapability for T { + fn build(self) -> CapabilityFile { + self.as_ref().parse().expect("invalid capability") + } +} + +/// A builder for a [`Capability`]. +pub struct CapabilityBuilder(Capability); + +impl CapabilityBuilder { + /// Creates a new capability builder with a unique identifier. + pub fn new(identifier: impl Into) -> Self { + Self(Capability { + identifier: identifier.into(), + description: "".into(), + remote: None, + local: true, + windows: Vec::new(), + webviews: Vec::new(), + permissions: Vec::new(), + platforms: None, + }) + } + + /// Allows this capability to be used by a remote URL. + pub fn remote(mut self, url: String) -> Self { + self + .0 + .remote + .get_or_insert_with(Default::default) + .urls + .push(url); + self + } + + /// Whether this capability is applied on local app URLs or not. Defaults to `true`. + pub fn local(mut self, local: bool) -> Self { + self.0.local = local; + self + } + + /// Link this capability to the given window label. + pub fn window(mut self, window: impl Into) -> Self { + self.0.windows.push(window.into()); + self + } + + /// Link this capability to the a list of window labels. + pub fn windows(mut self, windows: impl IntoIterator>) -> Self { + self.0.windows.extend(windows.into_iter().map(|w| w.into())); + self + } + + /// Link this capability to the given webview label. + pub fn webview(mut self, webview: impl Into) -> Self { + self.0.webviews.push(webview.into()); + self + } + + /// Link this capability to the a list of window labels. + pub fn webviews(mut self, webviews: impl IntoIterator>) -> Self { + self + .0 + .webviews + .extend(webviews.into_iter().map(|w| w.into())); + self + } + + /// Add a new permission to this capability. + pub fn permission(mut self, permission: impl Into) -> Self { + let permission = permission.into(); + self.0.permissions.push(PermissionEntry::PermissionRef( + permission + .clone() + .try_into() + .unwrap_or_else(|_| panic!("invalid permission identifier '{permission}'")), + )); + self + } + + /// Add a new scoped permission to this capability. + pub fn permission_scoped( + mut self, + permission: impl Into, + allowed: Vec, + denied: Vec, + ) -> Self { + let permission = permission.into(); + let identifier = permission + .clone() + .try_into() + .unwrap_or_else(|_| panic!("invalid permission identifier '{permission}'")); + + let allowed_scope = allowed + .into_iter() + .map(|a| { + serde_json::to_value(a) + .expect("failed to serialize scope") + .into() + }) + .collect(); + let denied_scope = denied + .into_iter() + .map(|a| { + serde_json::to_value(a) + .expect("failed to serialize scope") + .into() + }) + .collect(); + let scope = Scopes { + allow: Some(allowed_scope), + deny: Some(denied_scope), + }; + + self + .0 + .permissions + .push(PermissionEntry::ExtendedPermission { identifier, scope }); + self + } + + /// Adds a target platform for this capability. + /// + /// By default all platforms are applied. + pub fn platform(mut self, platform: Target) -> Self { + self + .0 + .platforms + .get_or_insert_with(Default::default) + .push(platform); + self + } + + /// Adds target platforms for this capability. + /// + /// By default all platforms are applied. + pub fn platforms(mut self, platforms: impl IntoIterator) -> Self { + self + .0 + .platforms + .get_or_insert_with(Default::default) + .extend(platforms); + self + } +} + +impl RuntimeCapability for CapabilityBuilder { + fn build(self) -> CapabilityFile { + CapabilityFile::Capability(self.0) + } +} + impl RuntimeAuthority { - pub(crate) fn new(resolved_acl: Resolved) -> Self { + #[doc(hidden)] + pub fn new(acl: BTreeMap, resolved_acl: Resolved) -> Self { let command_cache = resolved_acl .command_scope .keys() .map(|key| (*key, ::new())) .collect(); Self { - #[cfg(debug_assertions)] - acl: resolved_acl.acl, + acl, allowed_commands: resolved_acl.allowed_commands, denied_commands: resolved_acl.denied_commands, scope_manager: ScopeManager { @@ -84,25 +247,117 @@ impl RuntimeAuthority { } } + pub(crate) fn has_app_manifest(&self) -> bool { + self.acl.contains_key(APP_ACL_KEY) + } + + #[doc(hidden)] + pub fn __allow_command(&mut self, command: String, context: ExecutionContext) { + self.allowed_commands.insert( + command, + vec![ResolvedCommand { + context, + windows: vec!["*".parse().unwrap()], + ..Default::default() + }], + ); + } + + /// Adds the given capability to the runtime authority. + pub fn add_capability(&mut self, capability: impl RuntimeCapability) -> crate::Result<()> { + let mut capabilities = BTreeMap::new(); + match capability.build() { + CapabilityFile::Capability(c) => { + capabilities.insert(c.identifier.clone(), c); + } + + CapabilityFile::List(capabilities_list) + | CapabilityFile::NamedList { + capabilities: capabilities_list, + } => { + capabilities.extend( + capabilities_list + .into_iter() + .map(|c| (c.identifier.clone(), c)), + ); + } + } + + let resolved = Resolved::resolve( + &self.acl, + capabilities, + tauri_utils::platform::Target::current(), + ) + .unwrap(); + + // fill global scope + for (plugin, global_scope) in resolved.global_scope { + let global_scope_entry = self.scope_manager.global_scope.entry(plugin).or_default(); + + global_scope_entry.allow.extend(global_scope.allow); + global_scope_entry.deny.extend(global_scope.deny); + + self.scope_manager.global_scope_cache = Default::default(); + } + + // denied commands + for (cmd_key, resolved_cmds) in resolved.denied_commands { + let entry = self.denied_commands.entry(cmd_key).or_default(); + entry.extend(resolved_cmds); + } + + // allowed commands + for (cmd_key, resolved_cmds) in resolved.allowed_commands { + // fill command scope + for resolved_cmd in &resolved_cmds { + if let Some(scope_id) = resolved_cmd.scope_id { + let command_scope = resolved.command_scope.get(&scope_id).unwrap(); + + let command_scope_entry = self + .scope_manager + .command_scope + .entry(scope_id) + .or_default(); + command_scope_entry + .allow + .extend(command_scope.allow.clone()); + command_scope_entry.deny.extend(command_scope.deny.clone()); + + self.scope_manager.command_cache.remove(&scope_id); + } + } + + let entry = self.allowed_commands.entry(cmd_key).or_default(); + entry.extend(resolved_cmds); + } + + Ok(()) + } + #[cfg(debug_assertions)] pub(crate) fn resolve_access_message( &self, - plugin: &str, + key: &str, command_name: &str, window: &str, + webview: &str, origin: &Origin, ) -> String { - fn print_references(resolved: &ResolvedCommand) -> String { + fn print_references(resolved: Vec<&ResolvedCommand>) -> String { resolved - .referenced_by .iter() - .map(|r| format!("capability: {}, permission: {}", r.capability, r.permission)) + .map(|r| { + format!( + "capability: {}, permission: {}", + r.referenced_by.capability, r.referenced_by.permission + ) + }) .collect::>() .join(" || ") } fn has_permissions_allowing_command( - manifest: &crate::utils::acl::plugin::Manifest, + manifest: &crate::utils::acl::manifest::Manifest, set: &crate::utils::acl::PermissionSet, command: &str, ) -> bool { @@ -126,34 +381,52 @@ impl RuntimeAuthority { false } - let command = format!("plugin:{plugin}|{command_name}"); - if let Some((_cmd, resolved)) = self - .denied_commands - .iter() - .find(|(cmd, _)| cmd.name == command && origin.matches(&cmd.context)) - { + let command = if key == APP_ACL_KEY { + command_name.to_string() + } else { + format!("plugin:{key}|{command_name}") + }; + + let command_pretty_name = if key == APP_ACL_KEY { + command_name.to_string() + } else { + format!("{key}.{command_name}") + }; + + if let Some(resolved) = self.denied_commands.get(&command).map(|r| { + r.iter() + .filter(|cmd| origin.matches(&cmd.context)) + .collect() + }) { format!( - "{plugin}.{command_name} denied on origin {origin}, referenced by: {}", + "{command_pretty_name} denied on origin {origin}, referenced by: {}", print_references(resolved) ) } else { - let command_matches = self - .allowed_commands - .iter() - .filter(|(cmd, _)| cmd.name == command) - .collect::>(); + let command_matches = self.allowed_commands.get(&command); - if let Some((_cmd, resolved)) = command_matches - .iter() - .find(|(cmd, _)| origin.matches(&cmd.context)) - { - if resolved.windows.iter().any(|w| w.matches(window)) { + if let Some(resolved) = self.allowed_commands.get(&command).map(|r| { + r.iter() + .filter(|cmd| origin.matches(&cmd.context)) + .collect::>() + }) { + if resolved + .iter() + .any(|cmd| cmd.webviews.iter().any(|w| w.matches(webview))) + || resolved + .iter() + .any(|cmd| cmd.windows.iter().any(|w| w.matches(window))) + { "allowed".to_string() } else { - format!("{plugin}.{command_name} not allowed on window {window}, expected one of {}, referenced by {}", resolved.windows.iter().map(|w| w.as_str()).collect::>().join(", "), print_references(resolved)) + format!("{command_pretty_name} not allowed on window {window}, webview {webview}, allowed windows: {}, allowed webviews: {}, referenced by {}", + resolved.iter().flat_map(|cmd| cmd.windows.iter().map(|w| w.as_str())).collect::>().join(", "), + resolved.iter().flat_map(|cmd| cmd.webviews.iter().map(|w| w.as_str())).collect::>().join(", "), + print_references(resolved) + ) } } else { - let permission_error_detail = if let Some(manifest) = self.acl.get(plugin) { + let permission_error_detail = if let Some(manifest) = self.acl.get(key) { let mut permissions_referencing_command = Vec::new(); if let Some(default) = &manifest.default_permission { @@ -178,7 +451,11 @@ impl RuntimeAuthority { "Permissions associated with this command: {}", permissions_referencing_command .iter() - .map(|p| format!("{plugin}:{p}")) + .map(|p| if key == APP_ACL_KEY { + p.to_string() + } else { + format!("{key}:{p}") + }) .collect::>() .join(", ") ) @@ -186,27 +463,28 @@ impl RuntimeAuthority { "Plugin did not define its manifest".to_string() }; - if command_matches.is_empty() { - format!("{plugin}.{command_name} not allowed. {permission_error_detail}") - } else { + if let Some(resolved_cmds) = command_matches { format!( - "{plugin}.{command_name} not allowed on origin [{}]. Please create a capability that has this origin on the context field.\n\nFound matches for: {}\n\n{permission_error_detail}", + "{command_pretty_name} not allowed on origin [{}]. Please create a capability that has this origin on the context field.\n\nFound matches for: {}\n\n{permission_error_detail}", origin, - command_matches + resolved_cmds .iter() - .map(|(cmd, resolved)| { - let context = match &cmd.context { + .map(|resolved| { + let context = match &resolved.context { ExecutionContext::Local => "[local]".to_string(), - ExecutionContext::Remote { domain } => format!("[remote: {}]", domain.as_str()), + ExecutionContext::Remote { url } => format!("[remote: {}]", url.as_str()), }; format!( - "- context: {context}, referenced by: {}", - print_references(resolved) + "- context: {context}, referenced by: capability: {}, permission: {}", + resolved.referenced_by.capability, + resolved.referenced_by.permission ) }) .collect::>() .join("\n") ) + } else { + format!("{command_pretty_name} not allowed. {permission_error_detail}") } } } @@ -217,134 +495,156 @@ impl RuntimeAuthority { &self, command: &str, window: &str, + webview: &str, origin: &Origin, - ) -> Option<&ResolvedCommand> { + ) -> Option> { if self .denied_commands - .keys() - .any(|cmd| cmd.name == command && origin.matches(&cmd.context)) + .get(command) + .map(|resolved| resolved.iter().any(|cmd| origin.matches(&cmd.context))) + .is_some() { None } else { - self - .allowed_commands - .iter() - .find(|(cmd, _)| cmd.name == command && origin.matches(&cmd.context)) - .map(|(_cmd, resolved)| resolved) - .filter(|resolved| resolved.windows.iter().any(|w| w.matches(window))) + self.allowed_commands.get(command).and_then(|resolved| { + let resolved_cmds = resolved + .iter() + .filter(|cmd| { + origin.matches(&cmd.context) + && (cmd.webviews.iter().any(|w| w.matches(webview)) + || cmd.windows.iter().any(|w| w.matches(window))) + }) + .cloned() + .collect::>(); + if resolved_cmds.is_empty() { + None + } else { + Some(resolved_cmds) + } + }) } } } -/// List of allowed and denied objects that match either the command-specific or plugin global scope criterias. +/// List of allowed and denied objects that match either the command-specific or plugin global scope criteria. #[derive(Debug)] pub struct ScopeValue { - allow: Vec, - deny: Vec, + allow: Arc>>, + deny: Arc>>, } impl ScopeValue { + fn clone(&self) -> Self { + Self { + allow: self.allow.clone(), + deny: self.deny.clone(), + } + } + /// What this access scope allows. - pub fn allows(&self) -> &Vec { + pub fn allows(&self) -> &Vec> { &self.allow } /// What this access scope denies. - pub fn denies(&self) -> &Vec { + pub fn denies(&self) -> &Vec> { &self.deny } } -#[derive(Debug)] -enum OwnedOrRef<'a, T: Debug> { - Owned(T), - Ref(&'a T), -} - -impl<'a, T: Debug> Deref for OwnedOrRef<'a, T> { - type Target = T; - fn deref(&self) -> &Self::Target { - match self { - Self::Owned(t) => t, - Self::Ref(r) => r, - } - } -} - /// Access scope for a command that can be retrieved directly in the command function. #[derive(Debug)] -pub struct CommandScope<'a, T: ScopeObject>(OwnedOrRef<'a, ScopeValue>); +pub struct CommandScope { + allow: Vec>, + deny: Vec>, +} -impl<'a, T: ScopeObject> CommandScope<'a, T> { +impl CommandScope { /// What this access scope allows. - pub fn allows(&self) -> &Vec { - &self.0.allow + pub fn allows(&self) -> &Vec> { + &self.allow } /// What this access scope denies. - pub fn denies(&self) -> &Vec { - &self.0.deny + pub fn denies(&self) -> &Vec> { + &self.deny } } -impl<'a, R: Runtime, T: ScopeObject> CommandArg<'a, R> for CommandScope<'a, T> { +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 { - if let Some(scope_id) = command.acl.as_ref().and_then(|resolved| resolved.scope) { - Ok(CommandScope(OwnedOrRef::Ref( - command + let scope_ids = command.acl.as_ref().map(|resolved| { + resolved + .iter() + .filter_map(|cmd| cmd.scope_id) + .collect::>() + }); + if let Some(scope_ids) = scope_ids { + let mut allow = Vec::new(); + let mut deny = Vec::new(); + + for scope_id in scope_ids { + let scope = command .message .webview .manager() .runtime_authority + .lock() + .unwrap() .scope_manager - .get_command_scope_typed(command.message.webview.app_handle(), &scope_id)?, - ))) + .get_command_scope_typed::(command.message.webview.app_handle(), &scope_id)?; + + for s in scope.allows() { + allow.push(s.clone()); + } + for s in scope.denies() { + deny.push(s.clone()); + } + } + + Ok(CommandScope { allow, deny }) } else { - Ok(CommandScope(OwnedOrRef::Owned(ScopeValue { - allow: Vec::new(), - deny: Vec::new(), - }))) + Ok(CommandScope { + allow: Default::default(), + deny: Default::default(), + }) } } } /// Global access scope that can be retrieved directly in the command function. #[derive(Debug)] -pub struct GlobalScope<'a, T: ScopeObject>(&'a ScopeValue); +pub struct GlobalScope(ScopeValue); -impl<'a, T: ScopeObject> GlobalScope<'a, T> { +impl GlobalScope { /// What this access scope allows. - pub fn allows(&self) -> &Vec { + pub fn allows(&self) -> &Vec> { &self.0.allow } /// What this access scope denies. - pub fn denies(&self) -> &Vec { + pub fn denies(&self) -> &Vec> { &self.0.deny } } -impl<'a, R: Runtime, T: ScopeObject> CommandArg<'a, R> for GlobalScope<'a, T> { +impl<'a, R: Runtime, T: ScopeObject> CommandArg<'a, R> for GlobalScope { /// Grabs the [`ResolvedScope`] from the [`CommandItem`] and returns the associated [`GlobalScope`]. fn from_command(command: CommandItem<'a, R>) -> Result { command - .plugin - .ok_or_else(|| { - InvokeError::from_anyhow(anyhow::anyhow!( - "global scope not available for app commands" - )) - }) - .and_then(|plugin| { - command - .message - .webview - .manager() - .runtime_authority - .scope_manager - .get_global_scope_typed(command.message.webview.app_handle(), plugin) - .map_err(InvokeError::from_error) - }) + .message + .webview + .manager() + .runtime_authority + .lock() + .unwrap() + .scope_manager + .get_global_scope_typed( + command.message.webview.app_handle(), + command.plugin.unwrap_or(APP_ACL_KEY), + ) + .map_err(InvokeError::from_error) .map(GlobalScope) } } @@ -379,32 +679,35 @@ impl ScopeManager { pub(crate) fn get_global_scope_typed( &self, app: &AppHandle, - plugin: &str, - ) -> crate::Result<&ScopeValue> { - match self.global_scope_cache.try_get() { - Some(cached) => Ok(cached), + key: &str, + ) -> crate::Result> { + match self.global_scope_cache.try_get::>() { + Some(cached) => Ok(cached.clone()), None => { - let mut allow: Vec = Vec::new(); - let mut deny: Vec = Vec::new(); + let mut allow = Vec::new(); + let mut deny = Vec::new(); - if let Some(global_scope) = self.global_scope.get(plugin) { + if let Some(global_scope) = self.global_scope.get(key) { for allowed in &global_scope.allow { - allow.push( - T::deserialize(app, allowed.clone()) - .map_err(|e| crate::Error::CannotDeserializeScope(Box::new(e)))?, - ); + allow + .push(Arc::new(T::deserialize(app, allowed.clone()).map_err( + |e| crate::Error::CannotDeserializeScope(Box::new(e)), + )?)); } for denied in &global_scope.deny { - deny.push( - T::deserialize(app, denied.clone()) - .map_err(|e| crate::Error::CannotDeserializeScope(Box::new(e)))?, - ); + deny + .push(Arc::new(T::deserialize(app, denied.clone()).map_err( + |e| crate::Error::CannotDeserializeScope(Box::new(e)), + )?)); } } - let scope = ScopeValue { allow, deny }; - let _ = self.global_scope_cache.set(scope); - Ok(self.global_scope_cache.get()) + let scope = ScopeValue { + allow: Arc::new(allow), + deny: Arc::new(deny), + }; + self.global_scope_cache.set(scope.clone()); + Ok(scope) } } } @@ -413,36 +716,39 @@ impl ScopeManager { &self, app: &AppHandle, key: &ScopeKey, - ) -> crate::Result<&ScopeValue> { + ) -> crate::Result> { let cache = self.command_cache.get(key).unwrap(); - match cache.try_get() { - Some(cached) => Ok(cached), + match cache.try_get::>() { + Some(cached) => Ok(cached.clone()), None => { let resolved_scope = self .command_scope .get(key) .unwrap_or_else(|| panic!("missing command scope for key {key}")); - let mut allow: Vec = Vec::new(); - let mut deny: Vec = Vec::new(); + let mut allow = Vec::new(); + let mut deny = Vec::new(); for allowed in &resolved_scope.allow { - allow.push( - T::deserialize(app, allowed.clone()) - .map_err(|e| crate::Error::CannotDeserializeScope(Box::new(e)))?, - ); + allow + .push(Arc::new(T::deserialize(app, allowed.clone()).map_err( + |e| crate::Error::CannotDeserializeScope(Box::new(e)), + )?)); } for denied in &resolved_scope.deny { - deny.push( - T::deserialize(app, denied.clone()) - .map_err(|e| crate::Error::CannotDeserializeScope(Box::new(e)))?, - ); + deny + .push(Arc::new(T::deserialize(app, denied.clone()).map_err( + |e| crate::Error::CannotDeserializeScope(Box::new(e)), + )?)); } - let value = ScopeValue { allow, deny }; + let value = ScopeValue { + allow: Arc::new(allow), + deny: Arc::new(deny), + }; - let _ = cache.set(value); - Ok(cache.get()) + let _ = cache.set(value.clone()); + Ok(value) } } } @@ -452,7 +758,7 @@ impl ScopeManager { mod tests { use glob::Pattern; use tauri_utils::acl::{ - resolved::{CommandKey, Resolved, ResolvedCommand}, + resolved::{Resolved, ResolvedCommand}, ExecutionContext, }; @@ -462,137 +768,176 @@ mod tests { #[test] fn window_glob_pattern_matches() { - let command = CommandKey { - name: "my-command".into(), - context: ExecutionContext::Local, - }; + let command = "my-command"; let window = "main-*"; + let webview = "other-*"; - let resolved_cmd = ResolvedCommand { + let resolved_cmd = vec![ResolvedCommand { windows: vec![Pattern::new(window).unwrap()], ..Default::default() - }; - let allowed_commands = [(command.clone(), resolved_cmd.clone())] + }]; + let allowed_commands = [(command.to_string(), resolved_cmd.clone())] .into_iter() .collect(); - let authority = RuntimeAuthority::new(Resolved { - allowed_commands, - ..Default::default() - }); + let authority = RuntimeAuthority::new( + Default::default(), + Resolved { + allowed_commands, + ..Default::default() + }, + ); assert_eq!( authority.resolve_access( - &command.name, + command, &window.replace('*', "something"), + webview, &Origin::Local ), - Some(&resolved_cmd) + Some(resolved_cmd) + ); + } + + #[test] + fn webview_glob_pattern_matches() { + let command = "my-command"; + let window = "other-*"; + let webview = "main-*"; + + let resolved_cmd = vec![ResolvedCommand { + windows: vec![Pattern::new(window).unwrap()], + webviews: vec![Pattern::new(webview).unwrap()], + ..Default::default() + }]; + let allowed_commands = [(command.to_string(), resolved_cmd.clone())] + .into_iter() + .collect(); + + let authority = RuntimeAuthority::new( + Default::default(), + Resolved { + allowed_commands, + ..Default::default() + }, + ); + + assert_eq!( + authority.resolve_access( + command, + window, + &webview.replace('*', "something"), + &Origin::Local + ), + Some(resolved_cmd) ); } #[test] fn remote_domain_matches() { - let domain = "tauri.app"; - let command = CommandKey { - name: "my-command".into(), - context: ExecutionContext::Remote { - domain: Pattern::new(domain).unwrap(), - }, - }; + let url = "https://tauri.app"; + let command = "my-command"; let window = "main"; + let webview = "main"; - let resolved_cmd = ResolvedCommand { + let resolved_cmd = vec![ResolvedCommand { windows: vec![Pattern::new(window).unwrap()], - scope: None, + context: ExecutionContext::Remote { + url: url.parse().unwrap(), + }, ..Default::default() - }; - let allowed_commands = [(command.clone(), resolved_cmd.clone())] + }]; + let allowed_commands = [(command.to_string(), resolved_cmd.clone())] .into_iter() .collect(); - let authority = RuntimeAuthority::new(Resolved { - allowed_commands, - ..Default::default() - }); + let authority = RuntimeAuthority::new( + Default::default(), + Resolved { + allowed_commands, + ..Default::default() + }, + ); assert_eq!( authority.resolve_access( - &command.name, + command, window, + webview, &Origin::Remote { - domain: domain.into() + url: url.parse().unwrap() } ), - Some(&resolved_cmd) + Some(resolved_cmd) ); } #[test] fn remote_domain_glob_pattern_matches() { - let domain = "tauri.*"; - let command = CommandKey { - name: "my-command".into(), - context: ExecutionContext::Remote { - domain: Pattern::new(domain).unwrap(), - }, - }; + let url = "http://tauri.*"; + let command = "my-command"; let window = "main"; + let webview = "main"; - let resolved_cmd = ResolvedCommand { + let resolved_cmd = vec![ResolvedCommand { windows: vec![Pattern::new(window).unwrap()], - scope: None, + context: ExecutionContext::Remote { + url: url.parse().unwrap(), + }, ..Default::default() - }; - let allowed_commands = [(command.clone(), resolved_cmd.clone())] + }]; + let allowed_commands = [(command.to_string(), resolved_cmd.clone())] .into_iter() .collect(); - let authority = RuntimeAuthority::new(Resolved { - allowed_commands, - ..Default::default() - }); + let authority = RuntimeAuthority::new( + Default::default(), + Resolved { + allowed_commands, + ..Default::default() + }, + ); assert_eq!( authority.resolve_access( - &command.name, + command, window, + webview, &Origin::Remote { - domain: domain.replace('*', "studio") + url: url.replace('*', "studio").parse().unwrap() } ), - Some(&resolved_cmd) + Some(resolved_cmd) ); } #[test] fn remote_context_denied() { - let command = CommandKey { - name: "my-command".into(), - context: ExecutionContext::Local, - }; + let command = "my-command"; let window = "main"; + let webview = "main"; - let resolved_cmd = ResolvedCommand { + let resolved_cmd = vec![ResolvedCommand { windows: vec![Pattern::new(window).unwrap()], - scope: None, ..Default::default() - }; - let allowed_commands = [(command.clone(), resolved_cmd.clone())] - .into_iter() - .collect(); + }]; + let allowed_commands = [(command.to_string(), resolved_cmd)].into_iter().collect(); - let authority = RuntimeAuthority::new(Resolved { - allowed_commands, - ..Default::default() - }); + let authority = RuntimeAuthority::new( + Default::default(), + Resolved { + allowed_commands, + ..Default::default() + }, + ); assert!(authority .resolve_access( - &command.name, + command, window, + webview, &Origin::Remote { - domain: "tauri.app".into() + url: "https://tauri.app".parse().unwrap() } ) .is_none()); @@ -600,39 +945,40 @@ mod tests { #[test] fn denied_command_takes_precendence() { - let command = CommandKey { - name: "my-command".into(), - context: ExecutionContext::Local, - }; + let command = "my-command"; let window = "main"; + let webview = "main"; let windows = vec![Pattern::new(window).unwrap()]; let allowed_commands = [( - command.clone(), - ResolvedCommand { + command.to_string(), + vec![ResolvedCommand { windows: windows.clone(), ..Default::default() - }, + }], )] .into_iter() .collect(); let denied_commands = [( - command.clone(), - ResolvedCommand { + command.to_string(), + vec![ResolvedCommand { windows: windows.clone(), ..Default::default() - }, + }], )] .into_iter() .collect(); - let authority = RuntimeAuthority::new(Resolved { - allowed_commands, - denied_commands, - ..Default::default() - }); + let authority = RuntimeAuthority::new( + Default::default(), + Resolved { + allowed_commands, + denied_commands, + ..Default::default() + }, + ); assert!(authority - .resolve_access(&command.name, window, &Origin::Local) + .resolve_access(command, window, webview, &Origin::Local) .is_none()); } } diff --git a/core/tauri/src/ipc/channel.rs b/core/tauri/src/ipc/channel.rs index 715433a04..609039eb5 100644 --- a/core/tauri/src/ipc/channel.rs +++ b/core/tauri/src/ipc/channel.rs @@ -1,4 +1,4 @@ -// Copyright 2019-2023 Tauri Programme within The Commons Conservancy +// Copyright 2019-2024 Tauri Programme within The Commons Conservancy // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT @@ -6,7 +6,7 @@ use std::{ collections::HashMap, str::FromStr, sync::{ - atomic::{AtomicU32, Ordering}, + atomic::{AtomicU32, AtomicUsize, Ordering}, Arc, Mutex, }, }; @@ -89,7 +89,28 @@ impl FromStr for JavaScriptChannelId { impl JavaScriptChannelId { /// Gets a [`Channel`] for this channel ID on the given [`Webview`]. pub fn channel_on(&self, webview: Webview) -> Channel { - Channel::from_callback_fn(webview, self.0) + let callback_id = self.0; + let counter = AtomicUsize::new(0); + + Channel::new_with_id(callback_id.0, move |body| { + let data_id = CHANNEL_DATA_COUNTER.fetch_add(1, Ordering::Relaxed); + + webview + .state::() + .0 + .lock() + .unwrap() + .insert(data_id, body); + + let i = counter.fetch_add(1, Ordering::Relaxed); + + webview.eval(&format!( + "window.__TAURI_INTERNALS__.invoke('{FETCH_CHANNEL_DATA_COMMAND}', null, {{ headers: {{ '{CHANNEL_ID_HEADER_NAME}': '{data_id}' }} }}).then((response) => window['_' + {}]({{ message: response, id: {i} }})).catch(console.error)", + callback_id.0 + ))?; + + Ok(()) + }) } } @@ -134,16 +155,20 @@ impl Channel { pub(crate) fn from_callback_fn(webview: Webview, callback: CallbackFn) -> Self { Channel::new_with_id(callback.0, move |body| { let data_id = CHANNEL_DATA_COUNTER.fetch_add(1, Ordering::Relaxed); + webview .state::() .0 .lock() .unwrap() .insert(data_id, body); + webview.eval(&format!( - "window.__TAURI_INTERNALS__.invoke('{FETCH_CHANNEL_DATA_COMMAND}', null, {{ headers: {{ '{CHANNEL_ID_HEADER_NAME}': '{data_id}' }} }}).then(window['_' + {}]).catch(console.error)", + "window.__TAURI_INTERNALS__.invoke('{FETCH_CHANNEL_DATA_COMMAND}', null, {{ headers: {{ '{CHANNEL_ID_HEADER_NAME}': '{data_id}' }} }}).then((response) => window['_' + {}](response)).catch(console.error)", callback.0 - )) + ))?; + + Ok(()) }) } diff --git a/core/tauri/src/ipc/command.rs b/core/tauri/src/ipc/command.rs index 4de295e20..dc99065cd 100644 --- a/core/tauri/src/ipc/command.rs +++ b/core/tauri/src/ipc/command.rs @@ -1,4 +1,4 @@ -// Copyright 2019-2023 Tauri Programme within The Commons Conservancy +// Copyright 2019-2024 Tauri Programme within The Commons Conservancy // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT @@ -33,7 +33,7 @@ pub struct CommandItem<'a, R: Runtime> { pub message: &'a InvokeMessage, /// The resolved ACL for this command. - pub acl: &'a Option, + pub acl: &'a Option>, } /// Trait implemented by command arguments to derive a value from a [`CommandItem`]. diff --git a/core/tauri/src/ipc/format_callback.rs b/core/tauri/src/ipc/format_callback.rs index ea118c74b..4f6038465 100644 --- a/core/tauri/src/ipc/format_callback.rs +++ b/core/tauri/src/ipc/format_callback.rs @@ -1,4 +1,4 @@ -// Copyright 2019-2023 Tauri Programme within The Commons Conservancy +// Copyright 2019-2024 Tauri Programme within The Commons Conservancy // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT diff --git a/core/tauri/src/ipc/mod.rs b/core/tauri/src/ipc/mod.rs index efe9c0406..856bec722 100644 --- a/core/tauri/src/ipc/mod.rs +++ b/core/tauri/src/ipc/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2019-2023 Tauri Programme within The Commons Conservancy +// Copyright 2019-2024 Tauri Programme within The Commons Conservancy // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT @@ -21,11 +21,13 @@ use crate::{webview::Webview, Runtime, StateManager}; mod authority; pub(crate) mod channel; mod command; -#[cfg(any(target_os = "macos", target_os = "ios", not(ipc_custom_protocol)))] pub(crate) mod format_callback; pub(crate) mod protocol; -pub use authority::{CommandScope, GlobalScope, Origin, RuntimeAuthority, ScopeObject, ScopeValue}; +pub use authority::{ + CapabilityBuilder, CommandScope, GlobalScope, Origin, RuntimeAuthority, RuntimeCapability, + ScopeObject, ScopeValue, +}; pub use channel::{Channel, JavaScriptChannelId}; pub use command::{private, CommandArg, CommandItem}; @@ -163,7 +165,7 @@ pub struct Invoke { pub resolver: InvokeResolver, /// Resolved ACL for this IPC invoke. - pub acl: Option, + pub acl: Option>, } /// Error response from an [`InvokeMessage`]. diff --git a/core/tauri/src/ipc/protocol.rs b/core/tauri/src/ipc/protocol.rs index e9acaf8a0..152c0b64e 100644 --- a/core/tauri/src/ipc/protocol.rs +++ b/core/tauri/src/ipc/protocol.rs @@ -1,4 +1,4 @@ -// Copyright 2019-2023 Tauri Programme within The Commons Conservancy +// Copyright 2019-2024 Tauri Programme within The Commons Conservancy // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT @@ -11,15 +11,15 @@ use crate::{ }; use http::{ header::{ACCESS_CONTROL_ALLOW_HEADERS, ACCESS_CONTROL_ALLOW_ORIGIN, CONTENT_TYPE}, - HeaderValue, Method, StatusCode, + HeaderValue, Method, Request, StatusCode, }; +use url::Url; use super::{CallbackFn, InvokeBody, InvokeResponse}; const TAURI_CALLBACK_HEADER_NAME: &str = "Tauri-Callback"; const TAURI_ERROR_HEADER_NAME: &str = "Tauri-Error"; -#[cfg(any(target_os = "macos", target_os = "ios", not(ipc_custom_protocol)))] pub fn message_handler( manager: Arc>, ) -> crate::runtime::webview::WebviewIpcHandler { @@ -93,7 +93,7 @@ pub fn get(manager: Arc>, label: String) -> UriSchemeP let mut response = http::Response::new(serde_json::to_vec(&e.0).unwrap().into()); *response.status_mut() = StatusCode::BAD_REQUEST; - (response, mime::TEXT_PLAIN) + (response, mime::APPLICATION_JSON) } }; @@ -162,12 +162,16 @@ pub fn get(manager: Arc>, label: String) -> UriSchemeP }) } -#[cfg(any(target_os = "macos", target_os = "ios", not(ipc_custom_protocol)))] -fn handle_ipc_message(message: String, manager: &AppManager, label: &str) { +fn handle_ipc_message(request: Request, manager: &AppManager, label: &str) { if let Some(webview) = manager.get_webview(label) { #[cfg(feature = "tracing")] - let _span = - tracing::trace_span!("ipc::request", kind = "post-message", request = message).entered(); + let _span = tracing::trace_span!( + "ipc::request", + kind = "post-message", + uri = request.uri().to_string(), + body = request.body() + ) + .entered(); use serde::{Deserialize, Deserializer}; @@ -229,7 +233,7 @@ fn handle_ipc_message(message: String, manager: &AppManager, labe let _span = tracing::trace_span!("ipc::request::decrypt_isolation_payload").entered(); invoke_message.replace( - serde_json::from_str::>(&message) + serde_json::from_str::>(request.body()) .map_err(Into::into) .and_then(|message| { Ok(Message { @@ -244,16 +248,19 @@ fn handle_ipc_message(message: String, manager: &AppManager, labe } } - match invoke_message.unwrap_or_else(|| { + let message = invoke_message.unwrap_or_else(|| { #[cfg(feature = "tracing")] let _span = tracing::trace_span!("ipc::request::deserialize").entered(); - serde_json::from_str::(&message).map_err(Into::into) - }) { + serde_json::from_str::(request.body()).map_err(Into::into) + }); + + match message { Ok(message) => { let request = InvokeRequest { cmd: message.cmd, callback: message.callback, error: message.error, + url: Url::parse(&request.uri().to_string()).expect("invalid IPC request URL"), body: message.payload.into(), headers: message.options.map(|o| o.headers.0).unwrap_or_default(), }; @@ -305,7 +312,7 @@ fn handle_ipc_message(message: String, manager: &AppManager, labe mime_type = match &response { InvokeResponse::Ok(InvokeBody::Json(_)) => mime::APPLICATION_JSON, InvokeResponse::Ok(InvokeBody::Raw(_)) => mime::APPLICATION_OCTET_STREAM, - InvokeResponse::Err(_) => mime::TEXT_PLAIN, + InvokeResponse::Err(_) => mime::APPLICATION_JSON, } .essence_str() ) @@ -372,17 +379,33 @@ fn parse_invoke_request( .decode_utf8_lossy() .to_string(); - // the body is not set if ipc_custom_protocol is not enabled so we'll just ignore it - #[cfg(all(feature = "isolation", ipc_custom_protocol))] - if let crate::Pattern::Isolation { crypto_keys, .. } = &*manager.pattern { - #[cfg(feature = "tracing")] - let _span = tracing::trace_span!("ipc::request::decrypt_isolation_payload").entered(); + // on Android and on Linux (without the linux-ipc-protocol Cargo feature) we cannot read the request body + // so we must ignore it because some commands use the IPC for faster response + let has_payload = !body.is_empty(); - body = crate::utils::pattern::isolation::RawIsolationPayload::try_from(&body) - .and_then(|raw| crypto_keys.decrypt(raw)) - .map_err(|e| e.to_string())?; + #[cfg(feature = "isolation")] + if let crate::Pattern::Isolation { crypto_keys, .. } = &*manager.pattern { + // if the platform does not support request body, we ignore it + if has_payload { + #[cfg(feature = "tracing")] + let _span = tracing::trace_span!("ipc::request::decrypt_isolation_payload").entered(); + + body = crate::utils::pattern::isolation::RawIsolationPayload::try_from(&body) + .and_then(|raw| crypto_keys.decrypt(raw)) + .map_err(|e| e.to_string())?; + } } + let url = Url::parse( + parts + .headers + .get("Origin") + .ok_or("missing Origin header")? + .to_str() + .map_err(|_| "Origin header value must be a string")?, + ) + .map_err(|_| "Origin header is not a valid URL")?; + let callback = CallbackFn( parts .headers @@ -406,7 +429,7 @@ fn parse_invoke_request( let content_type = parts .headers - .get(reqwest::header::CONTENT_TYPE) + .get(http::header::CONTENT_TYPE) .and_then(|h| h.to_str().ok()) .map(|mime| mime.parse()) .unwrap_or(Ok(mime::APPLICATION_OCTET_STREAM)) @@ -418,12 +441,12 @@ fn parse_invoke_request( let body = if content_type == mime::APPLICATION_OCTET_STREAM { body.into() } else if content_type == mime::APPLICATION_JSON { - if cfg!(ipc_custom_protocol) { + // if the platform does not support request body, we ignore it + if has_payload { serde_json::from_slice::(&body) .map_err(|e| e.to_string())? .into() } else { - // the body is not set if ipc_custom_protocol is not enabled so we'll just ignore it serde_json::Value::Object(Default::default()).into() } } else { @@ -437,6 +460,7 @@ fn parse_invoke_request( cmd, callback, error, + url, body, headers: parts.headers, }; diff --git a/core/tauri/src/lib.rs b/core/tauri/src/lib.rs index 0f915f280..bdf398b5a 100644 --- a/core/tauri/src/lib.rs +++ b/core/tauri/src/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2019-2023 Tauri Programme within The Commons Conservancy +// Copyright 2019-2024 Tauri Programme within The Commons Conservancy // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT @@ -34,8 +34,8 @@ //! - **compression** *(enabled by default): Enables asset compression. You should only disable this if you want faster compile times in release builds - it produces larger binaries. //! - **config-json5**: Adds support to JSON5 format for `tauri.conf.json`. //! - **config-toml**: Adds support to TOML format for the configuration `Tauri.toml`. -//! - **icon-ico**: Adds support to set `.ico` window icons. Enables [`Icon::File`] and [`Icon::Raw`] variants. -//! - **icon-png**: Adds support to set `.png` window icons. Enables [`Icon::File`] and [`Icon::Raw`] variants. +//! - **image-ico**: Adds support to parse `.ico` image, see [`Image`]. +//! - **image-png**: Adds support to parse `.png` image, see [`Image`]. //! - **macos-proxy**: Adds support for [`WebviewBuilder::proxy_url`] on macOS. Requires macOS 14+. //! //! ## Cargo allowlist features @@ -69,6 +69,7 @@ pub use cocoa; #[doc(hidden)] pub use embed_plist; pub use error::{Error, Result}; +use ipc::{RuntimeAuthority, RuntimeCapability}; pub use resources::{Resource, ResourceId, ResourceTable}; #[cfg(target_os = "ios")] #[doc(hidden)] @@ -77,6 +78,8 @@ pub use swift_rs; pub use tauri_macros::mobile_entry_point; pub use tauri_macros::{command, generate_handler}; +pub use url::Url; + pub(crate) mod app; pub mod async_runtime; mod error; @@ -91,9 +94,11 @@ mod vibrancy; pub mod webview; pub mod window; use tauri_runtime as runtime; +pub mod image; #[cfg(target_os = "ios")] mod ios; #[cfg(desktop)] +#[cfg_attr(docsrs, doc(cfg(desktop)))] pub mod menu; /// Path APIs. pub mod path; @@ -185,13 +190,14 @@ pub use tauri_runtime_wry::{tao, wry}; /// A task to run on the main thread. pub type SyncTask = Box; -use serde::{Deserialize, Serialize}; +use serde::Serialize; use std::{ + borrow::Cow, collections::HashMap, fmt::{self, Debug}, sync::MutexGuard, }; -use utils::acl::resolved::Resolved; +use utils::assets::{AssetKey, CspHash, EmbeddedAssets}; #[cfg(feature = "wry")] #[cfg_attr(docsrs, doc(cfg(feature = "wry")))] @@ -206,19 +212,18 @@ pub use self::utils::TitleBarStyle; pub use self::event::{Event, EventId, EventTarget}; pub use { - self::app::{App, AppHandle, AssetResolver, Builder, CloseRequestApi, RunEvent, WindowEvent}, + self::app::{ + App, AppHandle, AssetResolver, Builder, CloseRequestApi, RunEvent, WebviewEvent, WindowEvent, + }, self::manager::Asset, self::runtime::{ + dpi::{LogicalPosition, LogicalSize, PhysicalPosition, PhysicalSize, Pixel, Position, Size}, webview::WebviewAttributes, - window::{ - dpi::{LogicalPosition, LogicalSize, PhysicalPosition, PhysicalSize, Pixel, Position, Size}, - CursorIcon, FileDropEvent, - }, - DeviceEventFilter, UserAttentionType, + window::{CursorIcon, DragDropEvent}, + DeviceEventFilter, Rect, UserAttentionType, }, self::state::{State, StateManager}, self::utils::{ - assets::Assets, config::{Config, WebviewUrl}, Env, PackageInfo, Theme, }, @@ -327,89 +332,39 @@ macro_rules! tauri_build_context { pub use pattern::Pattern; -/// A icon definition. -#[derive(Debug, Clone)] -#[non_exhaustive] -pub enum Icon { - /// Icon from file path. - #[cfg(any(feature = "icon-ico", feature = "icon-png"))] - #[cfg_attr(docsrs, doc(cfg(any(feature = "icon-ico", feature = "icon-png"))))] - File(std::path::PathBuf), - /// Icon from raw RGBA bytes. Width and height is parsed at runtime. - #[cfg(any(feature = "icon-ico", feature = "icon-png"))] - #[cfg_attr(docsrs, doc(cfg(any(feature = "icon-ico", feature = "icon-png"))))] - Raw(Vec), - /// Icon from raw RGBA bytes. - Rgba { - /// RGBA bytes of the icon image. - rgba: Vec, - /// Icon width. - width: u32, - /// Icon height. - height: u32, - }, +/// Whether we are running in development mode or not. +pub fn dev() -> bool { + !cfg!(feature = "custom-protocol") } -impl TryFrom for runtime::Icon { - type Error = Error; +/// Represents a container of file assets that are retrievable during runtime. +pub trait Assets: Send + Sync + 'static { + /// Initialize the asset provider. + fn setup(&self, app: &App) { + let _ = app; + } - fn try_from(icon: Icon) -> Result { - #[allow(irrefutable_let_patterns)] - if let Icon::Rgba { - rgba, - width, - height, - } = icon - { - Ok(Self { - rgba, - width, - height, - }) - } else { - #[cfg(not(any(feature = "icon-ico", feature = "icon-png")))] - panic!("unexpected Icon variant"); - #[cfg(any(feature = "icon-ico", feature = "icon-png"))] - { - let bytes = match icon { - Icon::File(p) => std::fs::read(p)?, - Icon::Raw(r) => r, - Icon::Rgba { .. } => unreachable!(), - }; - let extension = infer::get(&bytes) - .expect("could not determine icon extension") - .extension(); - match extension { - #[cfg(feature = "icon-ico")] - "ico" => { - let icon_dir = ico::IconDir::read(std::io::Cursor::new(bytes))?; - let entry = &icon_dir.entries()[0]; - Ok(Self { - rgba: entry.decode()?.rgba_data().to_vec(), - width: entry.width(), - height: entry.height(), - }) - } - #[cfg(feature = "icon-png")] - "png" => { - let decoder = png::Decoder::new(std::io::Cursor::new(bytes)); - let mut reader = decoder.read_info()?; - let mut buffer = Vec::new(); - while let Ok(Some(row)) = reader.next_row() { - buffer.extend(row.data()); - } - Ok(Self { - rgba: buffer, - width: reader.info().width, - height: reader.info().height, - }) - } - _ => panic!( - "image `{extension}` extension not supported; please file a Tauri feature request. `png` or `ico` icons are supported with the `icon-png` and `icon-ico` feature flags" - ), - } - } - } + /// Get the content of the passed [`AssetKey`]. + fn get(&self, key: &AssetKey) -> Option>; + + /// Iterator for the assets. + fn iter(&self) -> Box + '_>; + + /// Gets the hashes for the CSP tag of the HTML on the given path. + fn csp_hashes(&self, html_path: &AssetKey) -> Box> + '_>; +} + +impl Assets for EmbeddedAssets { + fn get(&self, key: &AssetKey) -> Option> { + EmbeddedAssets::get(self, key) + } + + fn iter(&self) -> Box + '_> { + EmbeddedAssets::iter(self) + } + + fn csp_hashes(&self, html_path: &AssetKey) -> Box> + '_> { + EmbeddedAssets::csp_hashes(self, html_path) } } @@ -418,27 +373,31 @@ impl TryFrom for runtime::Icon { /// # Stability /// This is the output of the [`generate_context`] macro, and is not considered part of the stable API. /// Unless you know what you are doing and are prepared for this type to have breaking changes, do not create it yourself. -pub struct Context { +#[tauri_macros::default_runtime(Wry, wry)] +pub struct Context { pub(crate) config: Config, - pub(crate) assets: Box, - pub(crate) default_window_icon: Option, + /// Asset provider. + pub assets: Box>, + pub(crate) default_window_icon: Option>, pub(crate) app_icon: Option>, #[cfg(all(desktop, feature = "tray-icon"))] - pub(crate) tray_icon: Option, + pub(crate) tray_icon: Option>, pub(crate) package_info: PackageInfo, pub(crate) _info_plist: (), pub(crate) pattern: Pattern, - pub(crate) resolved_acl: Resolved, + pub(crate) runtime_authority: RuntimeAuthority, + pub(crate) plugin_global_api_scripts: Option<&'static [&'static str]>, } -impl fmt::Debug for Context { +impl fmt::Debug for Context { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let mut d = f.debug_struct("Context"); d.field("config", &self.config) .field("default_window_icon", &self.default_window_icon) .field("app_icon", &self.app_icon) .field("package_info", &self.package_info) - .field("pattern", &self.pattern); + .field("pattern", &self.pattern) + .field("plugin_global_api_scripts", &self.plugin_global_api_scripts); #[cfg(all(desktop, feature = "tray-icon"))] d.field("tray_icon", &self.tray_icon); @@ -447,7 +406,7 @@ impl fmt::Debug for Context { } } -impl Context { +impl Context { /// The config the application was prepared with. #[inline(always)] pub fn config(&self) -> &Config { @@ -462,42 +421,42 @@ impl Context { /// The assets to be served directly by Tauri. #[inline(always)] - pub fn assets(&self) -> &A { - &self.assets + pub fn assets(&self) -> &dyn Assets { + self.assets.as_ref() } - /// A mutable reference to the assets to be served directly by Tauri. + /// Replace the [`Assets`] implementation and returns the previous value so you can use it as a fallback if desired. #[inline(always)] - pub fn assets_mut(&mut self) -> &mut A { - &mut self.assets + pub fn set_assets(&mut self, assets: Box>) -> Box> { + std::mem::replace(&mut self.assets, assets) } /// The default window icon Tauri should use when creating windows. #[inline(always)] - pub fn default_window_icon(&self) -> Option<&Icon> { + pub fn default_window_icon(&self) -> Option<&image::Image<'_>> { self.default_window_icon.as_ref() } - /// A mutable reference to the default window icon Tauri should use when creating windows. + /// Set the default window icon Tauri should use when creating windows. #[inline(always)] - pub fn default_window_icon_mut(&mut self) -> &mut Option { - &mut self.default_window_icon + pub fn set_default_window_icon(&mut self, icon: Option>) { + self.default_window_icon = icon; } - /// The icon to use on the system tray UI. + /// The icon to use on the tray icon. #[cfg(all(desktop, feature = "tray-icon"))] #[cfg_attr(docsrs, doc(cfg(all(desktop, feature = "tray-icon"))))] #[inline(always)] - pub fn tray_icon(&self) -> Option<&Icon> { + pub fn tray_icon(&self) -> Option<&image::Image<'_>> { self.tray_icon.as_ref() } - /// A mutable reference to the icon to use on the tray icon. + /// Set the icon to use on the tray icon. #[cfg(all(desktop, feature = "tray-icon"))] #[cfg_attr(docsrs, doc(cfg(all(desktop, feature = "tray-icon"))))] #[inline(always)] - pub fn tray_icon_mut(&mut self) -> &mut Option { - &mut self.tray_icon + pub fn set_tray_icon(&mut self, icon: Option>) { + self.tray_icon = icon; } /// Package information. @@ -525,8 +484,8 @@ impl Context { /// This API is unstable. #[doc(hidden)] #[inline(always)] - pub fn resolved_acl(&mut self) -> &mut Resolved { - &mut self.resolved_acl + pub fn runtime_authority_mut(&mut self) -> &mut RuntimeAuthority { + &mut self.runtime_authority } /// Create a new [`Context`] from the minimal required items. @@ -534,13 +493,14 @@ impl Context { #[allow(clippy::too_many_arguments)] pub fn new( config: Config, - assets: Box, - default_window_icon: Option, + assets: Box>, + default_window_icon: Option>, app_icon: Option>, package_info: PackageInfo, info_plist: (), pattern: Pattern, - resolved_acl: Resolved, + runtime_authority: RuntimeAuthority, + plugin_global_api_scripts: Option<&'static [&'static str]>, ) -> Self { Self { config, @@ -552,18 +512,11 @@ impl Context { package_info, _info_plist: info_plist, pattern, - resolved_acl, + runtime_authority, + plugin_global_api_scripts, } } - /// Sets the app tray icon. - #[cfg(all(desktop, feature = "tray-icon"))] - #[cfg_attr(docsrs, doc(cfg(all(desktop, feature = "tray-icon"))))] - #[inline(always)] - pub fn set_tray_icon(&mut self, icon: Icon) { - self.tray_icon.replace(icon); - } - /// Sets the app shell scope. #[cfg(shell_scope)] #[inline(always)] @@ -651,7 +604,7 @@ pub trait Manager: sealed::ManagerBase { /// Listens once to an emitted event to any [target](EventTarget) . /// /// See [`Self::listen_any`] for more information. - fn once_any(&self, event: impl Into, handler: F) + fn once_any(&self, event: impl Into, handler: F) -> EventId where F: FnOnce(Event) + Send + 'static, { @@ -800,7 +753,7 @@ pub trait Manager: sealed::ManagerBase { /// Fetch a single webview window from the manager. fn get_webview_window(&self, label: &str) -> Option> { self.manager().get_webview(label).and_then(|webview| { - if webview.window().webview_window { + if webview.window().is_webview_window() { Some(WebviewWindow { webview }) } else { None @@ -815,7 +768,7 @@ pub trait Manager: sealed::ManagerBase { .webviews() .into_iter() .filter_map(|(label, webview)| { - if webview.window().webview_window { + if webview.window().is_webview_window() { Some((label, WebviewWindow { webview })) } else { None @@ -942,10 +895,8 @@ pub trait Manager: sealed::ManagerBase { self.manager().state.try_get() } - /// Get a reference to the resources table. - fn resources_table(&self) -> MutexGuard<'_, ResourceTable> { - self.manager().resources_table() - } + /// Get a reference to the resources table of this manager. + fn resources_table(&self) -> MutexGuard<'_, ResourceTable>; /// Gets the managed [`Env`]. fn env(&self) -> Env { @@ -962,6 +913,28 @@ pub trait Manager: sealed::ManagerBase { fn path(&self) -> &crate::path::PathResolver { self.state::>().inner() } + + /// Adds a capability to the app. + /// + /// # Examples + /// ``` + /// use tauri::Manager; + /// + /// tauri::Builder::default() + /// .setup(|app| { + /// #[cfg(feature = "beta")] + /// app.add_capability(include_str!("../capabilities/beta.json")); + /// Ok(()) + /// }); + /// ``` + fn add_capability(&self, capability: impl RuntimeCapability) -> Result<()> { + self + .manager() + .runtime_authority + .lock() + .unwrap() + .add_capability(capability) + } } /// Prevent implementation details from leaking out of the [`Manager`] trait. @@ -991,40 +964,6 @@ pub(crate) mod sealed { } } -#[derive(Deserialize)] -#[serde(untagged)] -pub(crate) enum IconDto { - #[cfg(any(feature = "icon-png", feature = "icon-ico"))] - File(std::path::PathBuf), - #[cfg(any(feature = "icon-png", feature = "icon-ico"))] - Raw(Vec), - Rgba { - rgba: Vec, - width: u32, - height: u32, - }, -} - -impl From for Icon { - fn from(icon: IconDto) -> Self { - match icon { - #[cfg(any(feature = "icon-png", feature = "icon-ico"))] - IconDto::File(path) => Self::File(path), - #[cfg(any(feature = "icon-png", feature = "icon-ico"))] - IconDto::Raw(raw) => Self::Raw(raw), - IconDto::Rgba { - rgba, - width, - height, - } => Self::Rgba { - rgba, - width, - height, - }, - } - } -} - #[allow(unused)] macro_rules! run_main_thread { ($handle:ident, $ex:expr) => {{ diff --git a/core/tauri/src/manager/menu.rs b/core/tauri/src/manager/menu.rs index be3733b35..3536bb48c 100644 --- a/core/tauri/src/manager/menu.rs +++ b/core/tauri/src/manager/menu.rs @@ -1,4 +1,4 @@ -// Copyright 2019-2023 Tauri Programme within The Commons Conservancy +// Copyright 2019-2024 Tauri Programme within The Commons Conservancy // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT @@ -8,7 +8,7 @@ use std::{ }; use crate::{ - menu::{Menu, MenuId}, + menu::{Menu, MenuEvent, MenuId}, AppHandle, Runtime, Window, }; @@ -16,7 +16,7 @@ pub struct MenuManager { /// A set containing a reference to the active menus, including /// the app-wide menu and the window-specific menus /// - /// This should be mainly used to acceess [`Menu::haccel`] + /// This should be mainly used to access [`Menu::haccel`] /// to setup the accelerator handling in the event loop pub menus: Arc>>>, /// The menu set to all windows. @@ -87,4 +87,12 @@ impl MenuManager { None } } + + pub fn on_menu_event, MenuEvent) + Send + Sync + 'static>(&self, handler: F) { + self + .global_event_listeners + .lock() + .unwrap() + .push(Box::new(handler)); + } } diff --git a/core/tauri/src/manager/mod.rs b/core/tauri/src/manager/mod.rs index b2da64f96..283656826 100644 --- a/core/tauri/src/manager/mod.rs +++ b/core/tauri/src/manager/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2019-2023 Tauri Programme within The Commons Conservancy +// Copyright 2019-2024 Tauri Programme within The Commons Conservancy // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT @@ -13,7 +13,6 @@ use serde::Serialize; use url::Url; use tauri_macros::default_runtime; -use tauri_utils::debug_eprintln; use tauri_utils::{ assets::{AssetKey, CspHash}, config::{Csp, CspDirectiveSources}, @@ -21,12 +20,12 @@ use tauri_utils::{ }; use crate::{ - app::{AppHandle, GlobalWindowEventListener, OnPageLoad}, + app::{AppHandle, GlobalWebviewEventListener, GlobalWindowEventListener, OnPageLoad}, event::{assert_event_name_is_valid, Event, EventId, EventTarget, Listeners}, ipc::{Invoke, InvokeHandler, InvokeResponder, RuntimeAuthority}, plugin::PluginStore, - utils::{assets::Assets, config::Config, PackageInfo}, - Context, Pattern, Runtime, StateManager, Window, + utils::{config::Config, PackageInfo}, + Assets, Context, Pattern, Runtime, StateManager, Window, }; use crate::{event::EmitArgs, resources::ResourceTable, Webview}; @@ -47,16 +46,17 @@ struct CspHashStrings { /// Sets the CSP value to the asset HTML if needed (on Linux). /// Returns the CSP string for access on the response header (on Windows and macOS). #[allow(clippy::borrowed_box)] -fn set_csp( +pub(crate) fn set_csp( asset: &mut String, - assets: &Box, + assets: &impl std::borrow::Borrow>, asset_path: &AssetKey, manager: &AppManager, csp: Csp, -) -> String { +) -> HashMap { let mut csp = csp.into(); let hash_strings = assets + .borrow() .csp_hashes(asset_path) .fold(CspHashStrings::default(), |mut acc, hash| { match hash { @@ -67,7 +67,7 @@ fn set_csp( acc.style.push(hash.into()); } _csp_hash => { - debug_eprintln!("Unknown CspHash variant encountered: {:?}", _csp_hash); + log::debug!("Unknown CspHash variant encountered: {:?}", _csp_hash); } } @@ -99,15 +99,7 @@ fn set_csp( ); } - #[cfg(feature = "isolation")] - if let Pattern::Isolation { schema, .. } = &*manager.pattern { - let default_src = csp - .entry("default-src".into()) - .or_insert_with(Default::default); - default_src.push(crate::pattern::format_real_schema(schema)); - } - - Csp::DirectiveMap(csp).to_string() + csp } // inspired by https://github.com/rust-lang/rust/blob/1be5c8f90912c446ecbdc405cbc4a89f9acd20fd/library/alloc/src/str.rs#L260-L297 @@ -175,7 +167,7 @@ pub struct Asset { #[default_runtime(crate::Wry, wry)] pub struct AppManager { - pub runtime_authority: RuntimeAuthority, + pub runtime_authority: Mutex, pub window: window::WindowManager, pub webview: webview::WebviewManager, #[cfg(all(desktop, feature = "tray-icon"))] @@ -187,7 +179,7 @@ pub struct AppManager { pub listeners: Listeners, pub state: Arc, pub config: Config, - pub assets: Box, + pub assets: Box>, pub app_icon: Option>, @@ -196,6 +188,9 @@ pub struct AppManager { /// Application pattern. pub pattern: Arc, + /// Global API scripts collected from plugins. + pub plugin_global_api_scripts: Arc>, + /// Application Resources Table pub(crate) resources_table: Arc>, } @@ -224,13 +219,14 @@ impl fmt::Debug for AppManager { impl AppManager { #[allow(clippy::too_many_arguments, clippy::type_complexity)] pub(crate) fn with_handlers( - #[allow(unused_mut)] mut context: Context, + #[allow(unused_mut)] mut context: Context, plugins: PluginStore, invoke_handler: Box>, on_page_load: Option>>, uri_scheme_protocols: HashMap>>, state: StateManager, window_event_listeners: Vec>, + webiew_event_listeners: Vec>, #[cfg(desktop)] window_menu_event_listeners: HashMap< String, crate::app::GlobalMenuEventListener>, @@ -244,7 +240,7 @@ impl AppManager { } Self { - runtime_authority: RuntimeAuthority::new(context.resolved_acl), + runtime_authority: Mutex::new(context.runtime_authority), window: window::WindowManager { windows: Mutex::default(), default_icon: context.default_window_icon, @@ -255,6 +251,7 @@ impl AppManager { invoke_handler, on_page_load, uri_scheme_protocols: Mutex::new(uri_scheme_protocols), + event_listeners: Arc::new(webiew_event_listeners), invoke_responder, invoke_initialization_script, }, @@ -280,6 +277,7 @@ impl AppManager { app_icon: context.app_icon, package_info: context.package_info, pattern: Arc::new(context.pattern), + plugin_global_api_scripts: Arc::new(context.plugin_global_api_scripts), resources_table: Arc::default(), } } @@ -326,7 +324,7 @@ impl AppManager { } fn csp(&self) -> Option { - if cfg!(feature = "custom-protocol") { + if !crate::dev() { self.config.app.security.csp.clone() } else { self @@ -360,14 +358,14 @@ impl AppManager { let asset_response = assets .get(&path.as_str().into()) .or_else(|| { - debug_eprintln!("Asset `{path}` not found; fallback to {path}.html"); + log::debug!("Asset `{path}` not found; fallback to {path}.html"); let fallback = format!("{}.html", path.as_str()).into(); let asset = assets.get(&fallback); asset_path = fallback; asset }) .or_else(|| { - debug_eprintln!( + log::debug!( "Asset `{}` not found; fallback to {}/index.html", path, path @@ -378,7 +376,7 @@ impl AppManager { asset }) .or_else(|| { - debug_eprintln!("Asset `{}` not found; fallback to index.html", path); + log::debug!("Asset `{}` not found; fallback to index.html", path); let fallback = AssetKey::from("index.html"); let asset = assets.get(&fallback); asset_path = fallback; @@ -395,7 +393,17 @@ impl AppManager { let final_data = if is_html { let mut asset = String::from_utf8_lossy(&asset).into_owned(); if let Some(csp) = self.csp() { - csp_header.replace(set_csp(&mut asset, &self.assets, &asset_path, self, csp)); + #[allow(unused_mut)] + let mut csp_map = set_csp(&mut asset, &self.assets, &asset_path, self, csp); + #[cfg(feature = "isolation")] + if let Pattern::Isolation { schema, .. } = &*self.pattern { + let default_src = csp_map + .entry("default-src".into()) + .or_insert_with(Default::default); + default_src.push(crate::pattern::format_real_schema(schema)); + } + + csp_header.replace(Csp::DirectiveMap(csp_map).to_string()); } asset.as_bytes().to_vec() @@ -410,7 +418,7 @@ impl AppManager { }) } Err(e) => { - debug_eprintln!("{:?}", e); // TODO log::error! + log::error!("{:?}", e); Err(Box::new(e)) } } @@ -467,7 +475,7 @@ impl AppManager { event: String, target: EventTarget, handler: F, - ) { + ) -> EventId { assert_event_name_is_valid(&event); self.listeners().once(event, target, handler) } @@ -485,16 +493,11 @@ impl AppManager { let listeners = self.listeners(); - listeners.try_for_each_js( - event, + listeners.emit_js_filter( self.webview.webviews_lock().values(), - |webview, target| { - if filter(target) { - webview.emit_js(&emit_args, target) - } else { - Ok(()) - } - }, + event, + &emit_args, + Some(&filter), )?; listeners.emit_filter(emit_args, Some(filter))?; @@ -511,12 +514,7 @@ impl AppManager { let listeners = self.listeners(); - listeners.try_for_each_js( - event, - self.webview.webviews_lock().values(), - |webview, target| webview.emit_js(&emit_args, target), - )?; - + listeners.emit_js(self.webview.webviews_lock().values(), event, &emit_args)?; listeners.emit(emit_args)?; Ok(()) @@ -536,7 +534,8 @@ impl AppManager { } pub(crate) fn on_window_close(&self, label: &str) { - if let Some(window) = self.window.windows_lock().remove(label) { + let window = self.window.windows_lock().remove(label); + if let Some(window) = window { for webview in window.webviews() { self.webview.webviews_lock().remove(webview.label()); } @@ -545,6 +544,12 @@ impl AppManager { pub(crate) fn on_webview_close(&self, label: &str) { self.webview.webviews_lock().remove(label); + + if let Ok(webview_labels_array) = serde_json::to_string(&self.webview.labels()) { + let _ = self.webview.eval_script_all(format!( + r#"(function () {{ const metadata = window.__TAURI_INTERNALS__.metadata; if (metadata != null) {{ metadata.webviews = {webview_labels_array}.map(function (label) {{ return {{ label: label }} }}) }} }})()"#, + )); + } } pub fn windows(&self) -> HashMap> { @@ -559,7 +564,6 @@ impl AppManager { self.webview.webviews_lock().clone() } - /// Resources table managed by the application. pub(crate) fn resources_table(&self) -> MutexGuard<'_, ResourceTable> { self .resources_table @@ -647,6 +651,7 @@ mod test { StateManager::new(), Default::default(), Default::default(), + Default::default(), (None, "".into()), ); diff --git a/core/tauri/src/manager/tray.rs b/core/tauri/src/manager/tray.rs index 34c7f00c8..4522438cd 100644 --- a/core/tauri/src/manager/tray.rs +++ b/core/tauri/src/manager/tray.rs @@ -1,4 +1,4 @@ -// Copyright 2019-2023 Tauri Programme within The Commons Conservancy +// Copyright 2019-2024 Tauri Programme within The Commons Conservancy // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT @@ -6,12 +6,13 @@ use std::{collections::HashMap, fmt, sync::Mutex}; use crate::{ app::GlobalTrayIconEventListener, - tray::{TrayIcon, TrayIconId}, - AppHandle, Icon, Runtime, + image::Image, + tray::{TrayIcon, TrayIconEvent, TrayIconId}, + AppHandle, Runtime, }; pub struct TrayManager { - pub(crate) icon: Option, + pub(crate) icon: Option>, /// Tray icons pub(crate) icons: Mutex>>, /// Global Tray icon event listeners. @@ -27,3 +28,43 @@ impl fmt::Debug for TrayManager { .finish() } } + +impl TrayManager { + pub fn on_tray_icon_event, TrayIconEvent) + Send + Sync + 'static>( + &self, + handler: F, + ) { + self + .global_event_listeners + .lock() + .unwrap() + .push(Box::new(handler)); + } + + pub fn tray_by_id<'a, I>(&self, id: &'a I) -> Option> + where + I: ?Sized, + TrayIconId: PartialEq<&'a I>, + { + self + .icons + .lock() + .unwrap() + .iter() + .find(|t| t.id() == &id) + .cloned() + } + + pub fn remove_tray_by_id<'a, I>(&self, id: &'a I) -> Option> + where + I: ?Sized, + TrayIconId: PartialEq<&'a I>, + { + let mut icons = self.icons.lock().unwrap(); + let idx = icons.iter().position(|t| t.id() == &id); + if let Some(idx) = idx { + return Some(icons.swap_remove(idx)); + } + None + } +} diff --git a/core/tauri/src/manager/webview.rs b/core/tauri/src/manager/webview.rs index 92e3e0a22..e45b4ac33 100644 --- a/core/tauri/src/manager/webview.rs +++ b/core/tauri/src/manager/webview.rs @@ -1,4 +1,4 @@ -// Copyright 2019-2023 Tauri Programme within The Commons Conservancy +// Copyright 2019-2024 Tauri Programme within The Commons Conservancy // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT @@ -12,20 +12,29 @@ use std::{ use serde::Serialize; use serialize_to_javascript::{default_template, DefaultTemplate, Template}; -use tauri_runtime::webview::{DetachedWebview, PendingWebview}; +use tauri_runtime::{ + webview::{DetachedWebview, PendingWebview}, + window::DragDropEvent, +}; use tauri_utils::config::WebviewUrl; use url::Url; use crate::{ - app::{OnPageLoad, UriSchemeResponder}, + app::{GlobalWebviewEventListener, OnPageLoad, UriSchemeResponder, WebviewEvent}, ipc::{InvokeHandler, InvokeResponder}, pattern::PatternJavascript, sealed::ManagerBase, webview::PageLoadPayload, - AppHandle, EventLoopMessage, Manager, Runtime, Webview, Window, + AppHandle, EventLoopMessage, EventTarget, Manager, Runtime, Scopes, Webview, Window, }; -use super::AppManager; +use super::{ + window::{ + DragDropPayload, DragOverPayload, DRAG_EVENT, DROP_CANCELLED_EVENT, DROP_EVENT, + DROP_HOVER_EVENT, + }, + AppManager, +}; // we need to proxy the dev server on mobile because we can't use `localhost`, so we use the local IP address // and we do not get a secure context without the custom protocol that proxies to the dev server @@ -73,6 +82,8 @@ pub struct WebviewManager { pub on_page_load: Option>>, /// The webview protocols available to all webviews. pub uri_scheme_protocols: Mutex>>>, + /// Webview event listeners to all webviews. + pub event_listeners: Arc>>, /// Responder for invoke calls. pub invoke_responder: Option>>, @@ -200,6 +211,12 @@ impl WebviewManager { ); } + if let Some(plugin_global_api_scripts) = &*app_manager.plugin_global_api_scripts { + for script in plugin_global_api_scripts.iter() { + webview_attributes = webview_attributes.initialization_script(script); + } + } + pending.webview_attributes = webview_attributes; let mut registered_scheme_protocols = Vec::new(); @@ -305,7 +322,12 @@ impl WebviewManager { crypto_keys, } = &*app_manager.pattern { - let protocol = crate::protocol::isolation::get(assets.clone(), *crypto_keys.aes_gcm().raw()); + let protocol = crate::protocol::isolation::get( + manager.manager_owned(), + schema, + assets.clone(), + *crypto_keys.aes_gcm().raw(), + ); pending.register_uri_scheme_protocol(schema, move |request, responder| { protocol(request, UriSchemeResponder(responder)) }); @@ -488,12 +510,9 @@ impl WebviewManager { manager, )?; - #[cfg(any(target_os = "macos", target_os = "ios", not(ipc_custom_protocol)))] - { - pending.ipc_handler = Some(crate::ipc::protocol::message_handler( - manager.manager_owned(), - )); - } + pending.ipc_handler = Some(crate::ipc::protocol::message_handler( + manager.manager_owned(), + )); // in `windows`, we need to force a data_directory // but we do respect user-specification @@ -515,6 +534,23 @@ impl WebviewManager { } } + #[cfg(all(desktop, not(target_os = "windows")))] + if pending.webview_attributes.zoom_hotkeys_enabled { + #[derive(Template)] + #[default_template("../webview/scripts/zoom-hotkey.js")] + struct HotkeyZoom<'a> { + os_name: &'a str, + } + + pending.webview_attributes.initialization_scripts.push( + HotkeyZoom { + os_name: std::env::consts::OS, + } + .render_default(&Default::default())? + .into_string(), + ) + } + #[cfg(feature = "isolation")] let pattern = app_manager.pattern.clone(); let navigation_handler = pending.navigation_handler.take(); @@ -557,6 +593,15 @@ impl WebviewManager { ) -> Webview { let webview = Webview::new(window, webview); + let webview_event_listeners = self.event_listeners.clone(); + let webview_ = webview.clone(); + webview.on_webview_event(move |event| { + let _ = on_webview_event(&webview_, event); + for handler in webview_event_listeners.iter() { + handler(&webview_, event); + } + }); + // insert the webview into our manager { self @@ -585,6 +630,19 @@ impl WebviewManager { .expect("failed to run on_webview_created hook"); } + if let Ok(webview_labels_array) = serde_json::to_string(&webview.manager.webview.labels()) { + let _ = webview.manager.webview.eval_script_all(format!( + "window.__TAURI_INTERNALS__.metadata.webviews = {webview_labels_array}.map(function (label) {{ return {{ label: label }} }})", + )); + } + + let _ = webview.manager.emit( + "tauri://webview-created", + Some(crate::webview::CreatedEvent { + label: webview.label().into(), + }), + ); + webview } @@ -600,3 +658,47 @@ impl WebviewManager { self.webviews_lock().keys().cloned().collect() } } + +impl Webview { + /// Emits event to [`EventTarget::Window`] and [`EventTarget::WebviewWindow`] + fn emit_to_webview(&self, event: &str, payload: S) -> crate::Result<()> { + let window_label = self.label(); + self.emit_filter(event, payload, |target| match target { + EventTarget::Webview { label } | EventTarget::WebviewWindow { label } => { + label == window_label + } + _ => false, + }) + } +} + +fn on_webview_event(webview: &Webview, event: &WebviewEvent) -> crate::Result<()> { + match event { + WebviewEvent::DragDrop(event) => match event { + DragDropEvent::Dragged { paths, position } => { + let payload = DragDropPayload { paths, position }; + webview.emit_to_webview(DRAG_EVENT, payload)? + } + DragDropEvent::DragOver { position } => { + let payload = DragOverPayload { position }; + webview.emit_to_webview(DROP_HOVER_EVENT, payload)? + } + DragDropEvent::Dropped { paths, position } => { + let scopes = webview.state::(); + for path in paths { + if path.is_file() { + let _ = scopes.allow_file(path); + } else { + let _ = scopes.allow_directory(path, false); + } + } + let payload = DragDropPayload { paths, position }; + webview.emit_to_webview(DROP_EVENT, payload)? + } + DragDropEvent::Cancelled => webview.emit_to_webview(DROP_CANCELLED_EVENT, ())?, + _ => unimplemented!(), + }, + } + + Ok(()) +} diff --git a/core/tauri/src/manager/window.rs b/core/tauri/src/manager/window.rs index a1cb55961..17c8e3ea7 100644 --- a/core/tauri/src/manager/window.rs +++ b/core/tauri/src/manager/window.rs @@ -1,4 +1,4 @@ -// Copyright 2019-2023 Tauri Programme within The Commons Conservancy +// Copyright 2019-2024 Tauri Programme within The Commons Conservancy // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT @@ -11,20 +11,16 @@ use std::{ use serde::Serialize; use tauri_runtime::{ + dpi::{PhysicalPosition, PhysicalSize}, window::WindowBuilder, - window::{ - dpi::{PhysicalPosition, PhysicalSize}, - DetachedWindow, FileDropEvent, PendingWindow, - }, + window::{DetachedWindow, DragDropEvent, PendingWindow}, }; use crate::{ - app::GlobalWindowEventListener, sealed::ManagerBase, AppHandle, EventLoopMessage, EventTarget, - Icon, Manager, Runtime, Scopes, Window, WindowEvent, + app::GlobalWindowEventListener, image::Image, sealed::ManagerBase, AppHandle, EventLoopMessage, + EventTarget, Manager, Runtime, Scopes, Window, WindowEvent, }; -use super::AppManager; - const WINDOW_RESIZED_EVENT: &str = "tauri://resize"; const WINDOW_MOVED_EVENT: &str = "tauri://move"; const WINDOW_CLOSE_REQUESTED_EVENT: &str = "tauri://close-requested"; @@ -33,13 +29,14 @@ const WINDOW_FOCUS_EVENT: &str = "tauri://focus"; const WINDOW_BLUR_EVENT: &str = "tauri://blur"; const WINDOW_SCALE_FACTOR_CHANGED_EVENT: &str = "tauri://scale-change"; const WINDOW_THEME_CHANGED: &str = "tauri://theme-changed"; -const WINDOW_FILE_DROP_EVENT: &str = "tauri://file-drop"; -const WINDOW_FILE_DROP_HOVER_EVENT: &str = "tauri://file-drop-hover"; -const WINDOW_FILE_DROP_CANCELLED_EVENT: &str = "tauri://file-drop-cancelled"; +pub const DRAG_EVENT: &str = "tauri://drag"; +pub const DROP_EVENT: &str = "tauri://drop"; +pub const DROP_HOVER_EVENT: &str = "tauri://drop-over"; +pub const DROP_CANCELLED_EVENT: &str = "tauri://drag-cancelled"; pub struct WindowManager { pub windows: Mutex>>, - pub default_icon: Option, + pub default_icon: Option>, /// Window event listeners to all windows. pub event_listeners: Arc>>, } @@ -68,9 +65,7 @@ impl WindowManager { if !pending.window_builder.has_icon() { if let Some(default_window_icon) = self.default_icon.clone() { - pending.window_builder = pending - .window_builder - .icon(default_window_icon.try_into()?)?; + pending.window_builder = pending.window_builder.icon(default_window_icon.into())?; } } @@ -81,7 +76,6 @@ impl WindowManager { &self, app_handle: AppHandle, window: DetachedWindow, - multiwebview: bool, #[cfg(desktop)] menu: Option>, ) -> Window { let window = Window::new( @@ -90,14 +84,12 @@ impl WindowManager { app_handle, #[cfg(desktop)] menu, - multiwebview, ); let window_ = window.clone(); let window_event_listeners = self.event_listeners.clone(); - let manager = window.manager.clone(); window.on_window_event(move |event| { - let _ = on_window_event(&window_, &manager, event); + let _ = on_window_event(&window_, event); for handler in window_event_listeners.iter() { handler(&window_, event); } @@ -152,16 +144,17 @@ impl Window { } #[derive(Serialize, Clone)] -struct FileDropPayload<'a> { - paths: &'a Vec, - position: &'a PhysicalPosition, +pub struct DragDropPayload<'a> { + pub paths: &'a Vec, + pub position: &'a PhysicalPosition, } -fn on_window_event( - window: &Window, - manager: &AppManager, - event: &WindowEvent, -) -> crate::Result<()> { +#[derive(Serialize, Clone)] +pub struct DragOverPayload<'a> { + pub position: &'a PhysicalPosition, +} + +fn on_window_event(window: &Window, event: &WindowEvent) -> crate::Result<()> { match event { WindowEvent::Resized(size) => window.emit_to_window(WINDOW_RESIZED_EVENT, size)?, WindowEvent::Moved(position) => window.emit_to_window(WINDOW_MOVED_EVENT, position)?, @@ -174,12 +167,11 @@ fn on_window_event( WindowEvent::Destroyed => { window.emit_to_window(WINDOW_DESTROYED_EVENT, ())?; let label = window.label(); - let webviews_map = manager.webview.webviews_lock(); - let webviews = webviews_map.values(); - for webview in webviews { - webview.eval(&format!( - r#"(function () {{ const metadata = window.__TAURI_INTERNALS__.metadata; if (metadata != null) {{ metadata.windows = window.__TAURI_INTERNALS__.metadata.windows.filter(w => w.label !== "{label}"); }} }})()"#, - ))?; + + if let Ok(webview_labels_array) = serde_json::to_string(&window.manager().webview.labels()) { + let _ = window.manager().webview.eval_script_all(format!( + r#"(function () {{ const metadata = window.__TAURI_INTERNALS__.metadata; if (metadata != null) {{ metadata.windows = window.__TAURI_INTERNALS__.metadata.windows.filter(w => w.label !== "{label}"); metadata.webviews = {webview_labels_array}.map(function (label) {{ return {{ label: label }} }}) }} }})()"#, + )); } } WindowEvent::Focused(focused) => window.emit_to_window( @@ -201,24 +193,56 @@ fn on_window_event( size: *new_inner_size, }, )?, - WindowEvent::FileDrop(event) => match event { - FileDropEvent::Hovered { paths, position } => { - let payload = FileDropPayload { paths, position }; - window.emit_to_window(WINDOW_FILE_DROP_HOVER_EVENT, payload)? + WindowEvent::DragDrop(event) => match event { + DragDropEvent::Dragged { paths, position } => { + let payload = DragDropPayload { paths, position }; + + if window.is_webview_window() { + window.emit_to(EventTarget::labeled(window.label()), DRAG_EVENT, payload)? + } else { + window.emit_to_window(DRAG_EVENT, payload)? + } } - FileDropEvent::Dropped { paths, position } => { + DragDropEvent::DragOver { position } => { + let payload = DragOverPayload { position }; + if window.is_webview_window() { + window.emit_to( + EventTarget::labeled(window.label()), + DROP_HOVER_EVENT, + payload, + )? + } else { + window.emit_to_window(DROP_HOVER_EVENT, payload)? + } + } + DragDropEvent::Dropped { paths, position } => { let scopes = window.state::(); for path in paths { if path.is_file() { let _ = scopes.allow_file(path); } else { - let _ = scopes.allow_directory(path, false); + let _ = scopes.allow_directory(path, true); } } - let payload = FileDropPayload { paths, position }; - window.emit_to_window(WINDOW_FILE_DROP_EVENT, payload)? + let payload = DragDropPayload { paths, position }; + + if window.is_webview_window() { + window.emit_to(EventTarget::labeled(window.label()), DROP_EVENT, payload)? + } else { + window.emit_to_window(DROP_EVENT, payload)? + } + } + DragDropEvent::Cancelled => { + if window.is_webview_window() { + window.emit_to( + EventTarget::labeled(window.label()), + DROP_CANCELLED_EVENT, + (), + )? + } else { + window.emit_to_window(DROP_CANCELLED_EVENT, ())? + } } - FileDropEvent::Cancelled => window.emit_to_window(WINDOW_FILE_DROP_CANCELLED_EVENT, ())?, _ => unimplemented!(), }, WindowEvent::ThemeChanged(theme) => { diff --git a/core/tauri/src/menu/builders/check.rs b/core/tauri/src/menu/builders/check.rs index 2b7a49729..4120a1a1a 100644 --- a/core/tauri/src/menu/builders/check.rs +++ b/core/tauri/src/menu/builders/check.rs @@ -1,4 +1,4 @@ -// Copyright 2019-2023 Tauri Programme within The Commons Conservancy +// Copyright 2019-2024 Tauri Programme within The Commons Conservancy // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT diff --git a/core/tauri/src/menu/builders/icon.rs b/core/tauri/src/menu/builders/icon.rs index bfbf15e82..fea7e00cd 100644 --- a/core/tauri/src/menu/builders/icon.rs +++ b/core/tauri/src/menu/builders/icon.rs @@ -1,23 +1,24 @@ -// Copyright 2019-2023 Tauri Programme within The Commons Conservancy +// Copyright 2019-2024 Tauri Programme within The Commons Conservancy // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT use crate::{ + image::Image, menu::{IconMenuItem, MenuId, NativeIcon}, - Icon, Manager, Runtime, + Manager, Runtime, }; /// A builder type for [`IconMenuItem`] -pub struct IconMenuItemBuilder { +pub struct IconMenuItemBuilder<'a> { id: Option, text: String, enabled: bool, - icon: Option, + icon: Option>, native_icon: Option, accelerator: Option, } -impl IconMenuItemBuilder { +impl<'a> IconMenuItemBuilder<'a> { /// Create a new menu item builder. /// /// - `text` could optionally contain an `&` before a character to assign this character as the mnemonic @@ -70,7 +71,7 @@ impl IconMenuItemBuilder { /// /// **Note:** This method conflicts with [`Self::native_icon`] /// so calling one of them, will reset the other. - pub fn icon(mut self, icon: Icon) -> Self { + pub fn icon(mut self, icon: Image<'a>) -> Self { self.icon.replace(icon); self.native_icon = None; self diff --git a/core/tauri/src/menu/builders/menu.rs b/core/tauri/src/menu/builders/menu.rs index 8f4aab334..5d968f9c6 100644 --- a/core/tauri/src/menu/builders/menu.rs +++ b/core/tauri/src/menu/builders/menu.rs @@ -1,8 +1,8 @@ -// Copyright 2019-2023 Tauri Programme within The Commons Conservancy +// Copyright 2019-2024 Tauri Programme within The Commons Conservancy // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT -use crate::{menu::*, Icon, Manager, Runtime}; +use crate::{image::Image, menu::*, Manager, Runtime}; /// A builder type for [`Menu`] /// @@ -13,11 +13,7 @@ use crate::{menu::*, Icon, Manager, Runtime}; /// tauri::Builder::default() /// .setup(move |app| { /// let handle = app.handle(); -/// # let icon1 = tauri::Icon::Rgba { -/// # rgba: Vec::new(), -/// # width: 0, -/// # height: 0, -/// # }; +/// # let icon1 = tauri::image::Image::new(&[], 0, 0); /// let menu = MenuBuilder::new(handle) /// .item(&MenuItem::new(handle, "MenuItem 1", true, None::<&str>)?) /// .items(&[ @@ -99,7 +95,7 @@ impl<'m, R: Runtime, M: Manager> MenuBuilder<'m, R, M> { } /// Add an [IconMenuItem] to the menu. - pub fn icon, S: AsRef>(mut self, id: I, text: S, icon: Icon) -> Self { + pub fn icon, S: AsRef>(mut self, id: I, text: S, icon: Image<'_>) -> Self { self.items.push( IconMenuItem::with_id(self.manager, id, text, true, Some(icon), None::<&str>) .map(|i| i.kind()), @@ -285,7 +281,7 @@ impl<'m, R: Runtime, M: Manager> MenuBuilder<'m, R, M> { } /// Add About app menu item to the menu. - pub fn about(mut self, metadata: Option) -> Self { + pub fn about(mut self, metadata: Option>) -> Self { self .items .push(PredefinedMenuItem::about(self.manager, None, metadata).map(|i| i.kind())); diff --git a/core/tauri/src/menu/builders/mod.rs b/core/tauri/src/menu/builders/mod.rs index 172efc8c0..ce898c74a 100644 --- a/core/tauri/src/menu/builders/mod.rs +++ b/core/tauri/src/menu/builders/mod.rs @@ -1,10 +1,10 @@ -// Copyright 2019-2023 Tauri Programme within The Commons Conservancy +// Copyright 2019-2024 Tauri Programme within The Commons Conservancy // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT #![cfg(desktop)] -//! A module containting menu builder types +//! A module containing menu builder types mod menu; pub use menu::MenuBuilder; diff --git a/core/tauri/src/menu/builders/normal.rs b/core/tauri/src/menu/builders/normal.rs index 523f4f537..7297e5a2d 100644 --- a/core/tauri/src/menu/builders/normal.rs +++ b/core/tauri/src/menu/builders/normal.rs @@ -1,4 +1,4 @@ -// Copyright 2019-2023 Tauri Programme within The Commons Conservancy +// Copyright 2019-2024 Tauri Programme within The Commons Conservancy // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT diff --git a/core/tauri/src/menu/builders/submenu.rs b/core/tauri/src/menu/builders/submenu.rs index 3d9dc31cd..f86258d2c 100644 --- a/core/tauri/src/menu/builders/submenu.rs +++ b/core/tauri/src/menu/builders/submenu.rs @@ -1,8 +1,8 @@ -// Copyright 2019-2023 Tauri Programme within The Commons Conservancy +// Copyright 2019-2024 Tauri Programme within The Commons Conservancy // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT -use crate::{menu::*, Icon, Manager, Runtime}; +use crate::{image::Image, menu::*, Manager, Runtime}; /// A builder type for [`Submenu`] /// @@ -13,11 +13,7 @@ use crate::{menu::*, Icon, Manager, Runtime}; /// tauri::Builder::default() /// .setup(move |app| { /// let handle = app.handle(); -/// # let icon1 = tauri::Icon::Rgba { -/// # rgba: Vec::new(), -/// # width: 0, -/// # height: 0, -/// # }; +/// # let icon1 = tauri::image::Image::new(&[], 0, 0); /// # let icon2 = icon1.clone(); /// let menu = Menu::new(handle)?; /// let submenu = SubmenuBuilder::new(handle, "File") @@ -120,7 +116,7 @@ impl<'m, R: Runtime, M: Manager> SubmenuBuilder<'m, R, M> { } /// Add an [IconMenuItem] to the submenu. - pub fn icon, S: AsRef>(mut self, id: I, text: S, icon: Icon) -> Self { + pub fn icon, S: AsRef>(mut self, id: I, text: S, icon: Image<'_>) -> Self { self.items.push( IconMenuItem::with_id(self.manager, id, text, true, Some(icon), None::<&str>) .map(|i| i.kind()), @@ -306,7 +302,7 @@ impl<'m, R: Runtime, M: Manager> SubmenuBuilder<'m, R, M> { } /// Add About app menu item to the submenu. - pub fn about(mut self, metadata: Option) -> Self { + pub fn about(mut self, metadata: Option>) -> Self { self .items .push(PredefinedMenuItem::about(self.manager, None, metadata).map(|i| i.kind())); diff --git a/core/tauri/src/menu/check.rs b/core/tauri/src/menu/check.rs index d879b537d..e19d2d0af 100644 --- a/core/tauri/src/menu/check.rs +++ b/core/tauri/src/menu/check.rs @@ -1,4 +1,4 @@ -// Copyright 2019-2023 Tauri Programme within The Commons Conservancy +// Copyright 2019-2024 Tauri Programme within The Commons Conservancy // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT diff --git a/core/tauri/src/menu/icon.rs b/core/tauri/src/menu/icon.rs index 3f8586e75..1ae9176b1 100644 --- a/core/tauri/src/menu/icon.rs +++ b/core/tauri/src/menu/icon.rs @@ -1,4 +1,4 @@ -// Copyright 2019-2023 Tauri Programme within The Commons Conservancy +// Copyright 2019-2024 Tauri Programme within The Commons Conservancy // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT @@ -8,7 +8,7 @@ use super::run_item_main_thread; use super::{IconMenuItem, NativeIcon}; use crate::menu::IconMenuItemInner; use crate::run_main_thread; -use crate::{menu::MenuId, AppHandle, Icon, Manager, Runtime}; +use crate::{image::Image, menu::MenuId, AppHandle, Manager, Runtime}; impl IconMenuItem { /// Create a new menu item. @@ -19,7 +19,7 @@ impl IconMenuItem { manager: &M, text: T, enabled: bool, - icon: Option, + icon: Option>, accelerator: Option, ) -> crate::Result where @@ -31,8 +31,11 @@ impl IconMenuItem { let app_handle = handle.clone(); let text = text.as_ref().to_owned(); - let icon = icon.and_then(|i| i.try_into().ok()); let accelerator = accelerator.and_then(|s| s.as_ref().parse().ok()); + let icon = match icon { + Some(i) => Some(i.try_into()?), + None => None, + }; let item = run_main_thread!(handle, || { let item = muda::IconMenuItem::new(text, enabled, icon, accelerator); @@ -55,7 +58,7 @@ impl IconMenuItem { id: I, text: T, enabled: bool, - icon: Option, + icon: Option>, accelerator: Option, ) -> crate::Result where @@ -69,8 +72,11 @@ impl IconMenuItem { let id = id.into(); let text = text.as_ref().to_owned(); - let icon = icon.and_then(|i| i.try_into().ok()); let accelerator = accelerator.and_then(|s| s.as_ref().parse().ok()); + let icon = match icon { + Some(i) => Some(i.try_into()?), + None => None, + }; let item = run_main_thread!(handle, || { let item = muda::IconMenuItem::with_id(id.clone(), text, enabled, icon, accelerator); @@ -207,10 +213,12 @@ impl IconMenuItem { } /// Change this menu item icon or remove it. - pub fn set_icon(&self, icon: Option) -> crate::Result<()> { - run_item_main_thread!(self, |self_: Self| (*self_.0) - .as_ref() - .set_icon(icon.and_then(|i| i.try_into().ok()))) + pub fn set_icon(&self, icon: Option>) -> crate::Result<()> { + let icon = match icon { + Some(i) => Some(i.try_into()?), + None => None, + }; + run_item_main_thread!(self, |self_: Self| (*self_.0).as_ref().set_icon(icon)) } /// Change this menu item icon to a native image or remove it. diff --git a/core/tauri/src/menu/menu.rs b/core/tauri/src/menu/menu.rs index c6fdd4a21..27d232e8c 100644 --- a/core/tauri/src/menu/menu.rs +++ b/core/tauri/src/menu/menu.rs @@ -1,4 +1,4 @@ -// Copyright 2019-2023 Tauri Programme within The Commons Conservancy +// Copyright 2019-2024 Tauri Programme within The Commons Conservancy // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT @@ -40,7 +40,7 @@ impl ContextMenuBase for Menu { window: crate::Window, position: Option

, ) -> crate::Result<()> { - let position = position.map(Into::into).map(super::into_position); + let position = position.map(Into::into); run_item_main_thread!(self, move |self_: Self| { #[cfg(target_os = "macos")] if let Ok(view) = window.ns_view() { @@ -253,7 +253,7 @@ impl Menu { /// Add a menu item to the end of this menu. /// - /// ## Platform-spcific: + /// ## Platform-specific: /// /// - **macOS:** Only [`Submenu`] can be added to the menu. /// @@ -268,7 +268,7 @@ impl Menu { /// Add menu items to the end of this menu. It calls [`Menu::append`] in a loop internally. /// - /// ## Platform-spcific: + /// ## Platform-specific: /// /// - **macOS:** Only [`Submenu`] can be added to the menu /// @@ -283,7 +283,7 @@ impl Menu { /// Add a menu item to the beginning of this menu. /// - /// ## Platform-spcific: + /// ## Platform-specific: /// /// - **macOS:** Only [`Submenu`] can be added to the menu /// @@ -298,7 +298,7 @@ impl Menu { /// Add menu items to the beginning of this menu. It calls [`Menu::insert_items`] with position of `0` internally. /// - /// ## Platform-spcific: + /// ## Platform-specific: /// /// - **macOS:** Only [`Submenu`] can be added to the menu /// @@ -307,9 +307,9 @@ impl Menu { self.insert_items(items, 0) } - /// Insert a menu item at the specified `postion` in the menu. + /// Insert a menu item at the specified `position` in the menu. /// - /// ## Platform-spcific: + /// ## Platform-specific: /// /// - **macOS:** Only [`Submenu`] can be added to the menu /// @@ -322,9 +322,9 @@ impl Menu { .map_err(Into::into) } - /// Insert menu items at the specified `postion` in the menu. + /// Insert menu items at the specified `position` in the menu. /// - /// ## Platform-spcific: + /// ## Platform-specific: /// /// - **macOS:** Only [`Submenu`] can be added to the menu /// diff --git a/core/tauri/src/menu/mod.rs b/core/tauri/src/menu/mod.rs index aaa5367d4..de1bf7870 100644 --- a/core/tauri/src/menu/mod.rs +++ b/core/tauri/src/menu/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2019-2023 Tauri Programme within The Commons Conservancy +// Copyright 2019-2024 Tauri Programme within The Commons Conservancy // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT @@ -21,7 +21,7 @@ pub use builders::*; pub use menu::{HELP_SUBMENU_ID, WINDOW_SUBMENU_ID}; use serde::{Deserialize, Serialize}; -use crate::{AppHandle, Icon, Runtime}; +use crate::{image::Image, AppHandle, Runtime}; pub use muda::MenuId; macro_rules! run_item_main_thread { @@ -153,7 +153,7 @@ gen_wrappers!( MenuItem(MenuItemInner, MenuItem), /// A type that is a submenu inside a [`Menu`] or [`Submenu`] Submenu(SubmenuInner, Submenu), - /// A predefined (native) menu item which has a predfined behavior by the OS or by this crate. + /// A predefined (native) menu item which has a predefined behavior by the OS or by this crate. PredefinedMenuItem(PredefinedMenuItemInner, Predefined), /// A menu item inside a [`Menu`] or [`Submenu`] /// and usually contains a text and a check mark or a similar toggle @@ -166,7 +166,7 @@ gen_wrappers!( /// Application metadata for the [`PredefinedMenuItem::about`]. #[derive(Debug, Clone, Default)] -pub struct AboutMetadata { +pub struct AboutMetadata<'a> { /// Sets the application name. pub name: Option, /// The application version. @@ -220,15 +220,15 @@ pub struct AboutMetadata { /// ## Platform-specific /// /// - **Windows:** Unsupported. - pub icon: Option, + pub icon: Option>, } /// A builder type for [`AboutMetadata`]. #[derive(Clone, Debug, Default)] -pub struct AboutMetadataBuilder(AboutMetadata); +pub struct AboutMetadataBuilder<'a>(AboutMetadata<'a>); -impl AboutMetadataBuilder { - /// Create a new about metdata builder. +impl<'a> AboutMetadataBuilder<'a> { + /// Create a new about metadata builder. pub fn new() -> Self { Default::default() } @@ -316,20 +316,27 @@ impl AboutMetadataBuilder { /// ## Platform-specific /// /// - **Windows:** Unsupported. - pub fn icon(mut self, icon: Option) -> Self { + pub fn icon(mut self, icon: Option>) -> Self { self.0.icon = icon; self } /// Construct the final [`AboutMetadata`] - pub fn build(self) -> AboutMetadata { + pub fn build(self) -> AboutMetadata<'a> { self.0 } } -impl From for muda::AboutMetadata { - fn from(value: AboutMetadata) -> Self { - Self { +impl TryFrom> for muda::AboutMetadata { + type Error = crate::Error; + + fn try_from(value: AboutMetadata<'_>) -> Result { + let icon = match value.icon { + Some(i) => Some(i.try_into()?), + None => None, + }; + + Ok(Self { authors: value.authors, name: value.name, version: value.version, @@ -340,8 +347,8 @@ impl From for muda::AboutMetadata { website: value.website, website_label: value.website_label, credits: value.credits, - icon: value.icon.and_then(|i| i.try_into().ok()), - } + icon, + }) } } @@ -755,31 +762,3 @@ pub(crate) mod sealed { ) -> crate::Result<()>; } } - -impl TryFrom for muda::Icon { - type Error = crate::Error; - - fn try_from(value: crate::Icon) -> Result { - let value: crate::runtime::Icon = value.try_into()?; - muda::Icon::from_rgba(value.rgba, value.width, value.height).map_err(Into::into) - } -} - -pub(crate) fn into_logical_position( - p: crate::LogicalPosition

, -) -> muda::LogicalPosition

{ - muda::LogicalPosition { x: p.x, y: p.y } -} - -pub(crate) fn into_physical_position( - p: crate::PhysicalPosition

, -) -> muda::PhysicalPosition

{ - muda::PhysicalPosition { x: p.x, y: p.y } -} - -pub(crate) fn into_position(p: crate::Position) -> muda::Position { - match p { - crate::Position::Physical(p) => muda::Position::Physical(into_physical_position(p)), - crate::Position::Logical(p) => muda::Position::Logical(into_logical_position(p)), - } -} diff --git a/core/tauri/src/menu/normal.rs b/core/tauri/src/menu/normal.rs index f0f4f0987..be85bf73e 100644 --- a/core/tauri/src/menu/normal.rs +++ b/core/tauri/src/menu/normal.rs @@ -1,4 +1,4 @@ -// Copyright 2019-2023 Tauri Programme within The Commons Conservancy +// Copyright 2019-2024 Tauri Programme within The Commons Conservancy // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT diff --git a/core/tauri/src/menu/plugin.rs b/core/tauri/src/menu/plugin.rs index cb99e99b4..2d0a69ed2 100644 --- a/core/tauri/src/menu/plugin.rs +++ b/core/tauri/src/menu/plugin.rs @@ -1,23 +1,21 @@ -// Copyright 2019-2023 Tauri Programme within The Commons Conservancy +// Copyright 2019-2024 Tauri Programme within The Commons Conservancy // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT -use std::{ - collections::HashMap, - sync::{Mutex, MutexGuard}, -}; +use std::{collections::HashMap, sync::Mutex}; use serde::{Deserialize, Serialize}; -use tauri_runtime::window::dpi::Position; +use tauri_runtime::dpi::Position; use super::{sealed::ContextMenuBase, *}; use crate::{ command, + image::JsImage, ipc::{channel::JavaScriptChannelId, Channel}, plugin::{Builder, TauriPlugin}, - resources::{ResourceId, ResourceTable}, + resources::ResourceId, sealed::ManagerBase, - AppHandle, IconDto, Manager, RunEvent, Runtime, State, Webview, Window, + Manager, RunEvent, Runtime, State, Webview, Window, }; use tauri_macros::do_menu_item; @@ -44,24 +42,32 @@ pub(crate) struct AboutMetadata { pub website: Option, pub website_label: Option, pub credits: Option, - pub icon: Option, + pub icon: Option, } -impl From for super::AboutMetadata { - fn from(value: AboutMetadata) -> Self { - Self { - name: value.name, - version: value.version, - short_version: value.short_version, - authors: value.authors, - comments: value.comments, - copyright: value.copyright, - license: value.license, - website: value.website, - website_label: value.website_label, - credits: value.credits, - icon: value.icon.map(Into::into), - } +impl AboutMetadata { + pub fn into_metadata>( + self, + manager: &M, + ) -> crate::Result> { + let icon = match self.icon { + Some(i) => Some(i.into_img(manager)?.as_ref().clone()), + None => None, + }; + + Ok(super::AboutMetadata { + name: self.name, + version: self.version, + short_version: self.short_version, + authors: self.authors, + comments: self.comments, + copyright: self.copyright, + license: self.license, + website: self.website, + website_label: self.website_label, + credits: self.credits, + icon, + }) } } @@ -96,11 +102,7 @@ struct SubmenuPayload { } impl SubmenuPayload { - pub fn create_item( - self, - webview: &Webview, - resources_table: &MutexGuard<'_, ResourceTable>, - ) -> crate::Result> { + pub fn create_item(self, webview: &Webview) -> crate::Result> { let mut builder = if let Some(id) = self.id { SubmenuBuilder::with_id(webview, id, self.text) } else { @@ -110,7 +112,7 @@ impl SubmenuPayload { builder = builder.enabled(enabled); } for item in self.items { - builder = item.with_item(webview, resources_table, |i| Ok(builder.item(i)))?; + builder = item.with_item(webview, |i| Ok(builder.item(i)))?; } builder.build() @@ -161,7 +163,7 @@ impl CheckMenuItemPayload { #[serde(untagged)] enum Icon { Native(NativeIcon), - Icon(IconDto), + Icon(JsImage), } #[derive(Deserialize)] @@ -190,7 +192,7 @@ impl IconMenuItemPayload { } builder = match self.icon { Icon::Native(native_icon) => builder.native_icon(native_icon), - Icon::Icon(icon) => builder.icon(icon.into()), + Icon::Icon(icon) => builder.icon(icon.into_img(webview)?.as_ref().clone()), }; let item = builder.build(webview)?; @@ -276,7 +278,11 @@ impl PredefinedMenuItemPayload { Predefined::CloseWindow => PredefinedMenuItem::close_window(webview, self.text.as_deref()), Predefined::Quit => PredefinedMenuItem::quit(webview, self.text.as_deref()), Predefined::About(metadata) => { - PredefinedMenuItem::about(webview, self.text.as_deref(), metadata.map(Into::into)) + let metadata = match metadata { + Some(m) => Some(m.into_metadata(webview)?), + None => None, + }; + PredefinedMenuItem::about(webview, self.text.as_deref(), metadata) } Predefined::Services => PredefinedMenuItem::services(webview, self.text.as_deref()), } @@ -298,14 +304,14 @@ impl MenuItemPayloadKind { pub fn with_item) -> crate::Result>( self, webview: &Webview, - resources_table: &MutexGuard<'_, ResourceTable>, f: F, ) -> crate::Result { match self { Self::ExistingItem((rid, kind)) => { + let resources_table = webview.resources_table(); do_menu_item!(resources_table, rid, kind, |i| f(&*i)) } - Self::Submenu(i) => f(&i.create_item(webview, resources_table)?), + Self::Submenu(i) => f(&i.create_item(webview)?), Self::Predefined(i) => f(&i.create_item(webview)?), Self::Check(i) => f(&i.create_item(webview)?), Self::Icon(i) => f(&i.create_item(webview)?), @@ -330,7 +336,7 @@ struct NewOptions { #[command(root = "crate")] fn new( - app: AppHandle, + app: Webview, webview: Webview, kind: ItemKind, options: Option, @@ -348,7 +354,7 @@ fn new( } if let Some(items) = options.items { for item in items { - builder = item.with_item(&webview, &resources_table, |i| Ok(builder.item(i)))?; + builder = item.with_item(&webview, |i| Ok(builder.item(i)))?; } } let menu = builder.build()?; @@ -365,7 +371,7 @@ fn new( enabled: options.enabled, items: options.items.unwrap_or_default(), } - .create_item(&webview, &resources_table)?; + .create_item(&webview)?; let id = submenu.id().clone(); let rid = resources_table.add(submenu); @@ -448,13 +454,13 @@ fn append( ItemKind::Menu => { let menu = resources_table.get::>(rid)?; for item in items { - item.with_item(&webview, &resources_table, |i| menu.append(i))?; + item.with_item(&webview, |i| menu.append(i))?; } } ItemKind::Submenu => { let submenu = resources_table.get::>(rid)?; for item in items { - item.with_item(&webview, &resources_table, |i| submenu.append(i))?; + item.with_item(&webview, |i| submenu.append(i))?; } } _ => return Err(anyhow::anyhow!("unexpected menu item kind").into()), @@ -475,13 +481,13 @@ fn prepend( ItemKind::Menu => { let menu = resources_table.get::>(rid)?; for item in items { - item.with_item(&webview, &resources_table, |i| menu.prepend(i))?; + item.with_item(&webview, |i| menu.prepend(i))?; } } ItemKind::Submenu => { let submenu = resources_table.get::>(rid)?; for item in items { - item.with_item(&webview, &resources_table, |i| submenu.prepend(i))?; + item.with_item(&webview, |i| submenu.prepend(i))?; } } _ => return Err(anyhow::anyhow!("unexpected menu item kind").into()), @@ -503,14 +509,14 @@ fn insert( ItemKind::Menu => { let menu = resources_table.get::>(rid)?; for item in items { - item.with_item(&webview, &resources_table, |i| menu.insert(i, position))?; + item.with_item(&webview, |i| menu.insert(i, position))?; position += 1 } } ItemKind::Submenu => { let submenu = resources_table.get::>(rid)?; for item in items { - item.with_item(&webview, &resources_table, |i| submenu.insert(i, position))?; + item.with_item(&webview, |i| submenu.insert(i, position))?; position += 1 } } @@ -522,21 +528,22 @@ fn insert( #[command(root = "crate")] fn remove( - app: AppHandle, - menu_rid: ResourceId, - menu_kind: ItemKind, + webview: Webview, + rid: ResourceId, + kind: ItemKind, item: (ResourceId, ItemKind), ) -> crate::Result<()> { - let resources_table = app.resources_table(); - let (rid, kind) = item; - match menu_kind { + let resources_table = webview.resources_table(); + let (item_rid, item_kind) = item; + match kind { ItemKind::Menu => { - let menu = resources_table.get::>(menu_rid)?; - do_menu_item!(resources_table, rid, kind, |i| menu.remove(&*i))?; + let menu = resources_table.get::>(rid)?; + do_menu_item!(resources_table, item_rid, item_kind, |i| menu.remove(&*i))?; } ItemKind::Submenu => { - let submenu = resources_table.get::>(menu_rid)?; - do_menu_item!(resources_table, rid, kind, |i| submenu.remove(&*i))?; + let submenu = resources_table.get::>(rid)?; + do_menu_item!(resources_table, item_rid, item_kind, |i| submenu + .remove(&*i))?; } _ => return Err(anyhow::anyhow!("unexpected menu item kind").into()), }; @@ -560,12 +567,12 @@ macro_rules! make_item_resource { #[command(root = "crate")] fn remove_at( - app: AppHandle, + webview: Webview, rid: ResourceId, kind: ItemKind, position: usize, ) -> crate::Result> { - let mut resources_table = app.resources_table(); + let mut resources_table = webview.resources_table(); match kind { ItemKind::Menu => { let menu = resources_table.get::>(rid)?; @@ -587,11 +594,11 @@ fn remove_at( #[command(root = "crate")] fn items( - app: AppHandle, + webview: Webview, rid: ResourceId, kind: ItemKind, ) -> crate::Result> { - let mut resources_table = app.resources_table(); + let mut resources_table = webview.resources_table(); let items = match kind { ItemKind::Menu => resources_table.get::>(rid)?.items()?, ItemKind::Submenu => resources_table.get::>(rid)?.items()?, @@ -608,12 +615,12 @@ fn items( #[command(root = "crate")] fn get( - app: AppHandle, + webview: Webview, rid: ResourceId, kind: ItemKind, id: MenuId, ) -> crate::Result> { - let mut resources_table = app.resources_table(); + let mut resources_table = webview.resources_table(); match kind { ItemKind::Menu => { let menu = resources_table.get::>(rid)?; @@ -635,7 +642,7 @@ fn get( #[command(root = "crate")] async fn popup( - app: AppHandle, + webview: Webview, current_window: Window, rid: ResourceId, kind: ItemKind, @@ -643,11 +650,11 @@ async fn popup( at: Option, ) -> crate::Result<()> { let window = window - .map(|w| app.manager().get_window(&w)) + .map(|w| webview.manager().get_window(&w)) .unwrap_or(Some(current_window)); if let Some(window) = window { - let resources_table = app.resources_table(); + let resources_table = webview.resources_table(); match kind { ItemKind::Menu => { let menu = resources_table.get::>(rid)?; @@ -665,8 +672,11 @@ async fn popup( } #[command(root = "crate")] -fn create_default(app: AppHandle) -> crate::Result<(ResourceId, MenuId)> { - let mut resources_table = app.resources_table(); +fn create_default( + app: AppHandle, + webview: Webview, +) -> crate::Result<(ResourceId, MenuId)> { + let mut resources_table = webview.resources_table(); let menu = Menu::default(&app)?; let id = menu.id().clone(); let rid = resources_table.add(menu); @@ -675,10 +685,10 @@ fn create_default(app: AppHandle) -> crate::Result<(ResourceId, M #[command(root = "crate")] async fn set_as_app_menu( - app: AppHandle, + webview: Webview, rid: ResourceId, ) -> crate::Result> { - let mut resources_table = app.resources_table(); + let mut resources_table = webview.resources_table(); let menu = resources_table.get::>(rid)?; if let Some(menu) = menu.set_as_app_menu()? { let id = menu.id().clone(); @@ -690,17 +700,17 @@ async fn set_as_app_menu( #[command(root = "crate")] async fn set_as_window_menu( - app: AppHandle, + webview: Webview, current_window: Window, rid: ResourceId, window: Option, ) -> crate::Result> { let window = window - .map(|w| app.manager().get_window(&w)) + .map(|w| webview.manager().get_window(&w)) .unwrap_or(Some(current_window)); if let Some(window) = window { - let mut resources_table = app.resources_table(); + let mut resources_table = webview.resources_table(); let menu = resources_table.get::>(rid)?; if let Some(menu) = menu.set_as_window_menu(&window)? { let id = menu.id().clone(); @@ -712,40 +722,40 @@ async fn set_as_window_menu( } #[command(root = "crate")] -fn text(app: AppHandle, rid: ResourceId, kind: ItemKind) -> crate::Result { - let resources_table = app.resources_table(); +fn text(webview: Webview, rid: ResourceId, kind: ItemKind) -> crate::Result { + let resources_table = webview.resources_table(); do_menu_item!(resources_table, rid, kind, |i| i.text()) } #[command(root = "crate")] fn set_text( - app: AppHandle, + webview: Webview, rid: ResourceId, kind: ItemKind, text: String, ) -> crate::Result<()> { - let resources_table = app.resources_table(); + let resources_table = webview.resources_table(); do_menu_item!(resources_table, rid, kind, |i| i.set_text(text)) } #[command(root = "crate")] fn is_enabled( - app: AppHandle, + webview: Webview, rid: ResourceId, kind: ItemKind, ) -> crate::Result { - let resources_table = app.resources_table(); + let resources_table = webview.resources_table(); do_menu_item!(resources_table, rid, kind, |i| i.is_enabled(), !Predefined) } #[command(root = "crate")] fn set_enabled( - app: AppHandle, + webview: Webview, rid: ResourceId, kind: ItemKind, enabled: bool, ) -> crate::Result<()> { - let resources_table = app.resources_table(); + let resources_table = webview.resources_table(); do_menu_item!( resources_table, rid, @@ -757,12 +767,12 @@ fn set_enabled( #[command(root = "crate")] fn set_accelerator( - app: AppHandle, + webview: Webview, rid: ResourceId, kind: ItemKind, accelerator: Option, ) -> crate::Result<()> { - let resources_table = app.resources_table(); + let resources_table = webview.resources_table(); do_menu_item!( resources_table, rid, @@ -774,61 +784,69 @@ fn set_accelerator( #[command(root = "crate")] fn set_as_windows_menu_for_nsapp( - app: AppHandle, + webview: Webview, rid: ResourceId, ) -> crate::Result<()> { #[cfg(target_os = "macos")] { - let resources_table = app.resources_table(); + let resources_table = webview.resources_table(); let submenu = resources_table.get::>(rid)?; submenu.set_as_help_menu_for_nsapp()?; } let _ = rid; - let _ = app; + let _ = webview; Ok(()) } #[command(root = "crate")] -fn set_as_help_menu_for_nsapp(app: AppHandle, rid: ResourceId) -> crate::Result<()> { +fn set_as_help_menu_for_nsapp( + webview: Webview, + rid: ResourceId, +) -> crate::Result<()> { #[cfg(target_os = "macos")] { - let resources_table = app.resources_table(); + let resources_table = webview.resources_table(); let submenu = resources_table.get::>(rid)?; submenu.set_as_help_menu_for_nsapp()?; } let _ = rid; - let _ = app; + let _ = webview; Ok(()) } #[command(root = "crate")] -fn is_checked(app: AppHandle, rid: ResourceId) -> crate::Result { - let resources_table = app.resources_table(); +fn is_checked(webview: Webview, rid: ResourceId) -> crate::Result { + let resources_table = webview.resources_table(); let check_item = resources_table.get::>(rid)?; check_item.is_checked() } #[command(root = "crate")] -fn set_checked(app: AppHandle, rid: ResourceId, checked: bool) -> crate::Result<()> { - let resources_table = app.resources_table(); +fn set_checked( + webview: Webview, + rid: ResourceId, + checked: bool, +) -> crate::Result<()> { + let resources_table = webview.resources_table(); let check_item = resources_table.get::>(rid)?; check_item.set_checked(checked) } #[command(root = "crate")] fn set_icon( - app: AppHandle, + webview: Webview, rid: ResourceId, icon: Option, ) -> crate::Result<()> { - let resources_table = app.resources_table(); + let resources_table = webview.resources_table(); let icon_item = resources_table.get::>(rid)?; + match icon { Some(Icon::Native(icon)) => icon_item.set_native_icon(Some(icon)), - Some(Icon::Icon(icon)) => icon_item.set_icon(Some(icon.into())), + Some(Icon::Icon(icon)) => icon_item.set_icon(Some(icon.into_img(&webview)?.as_ref().clone())), None => { icon_item.set_icon(None)?; icon_item.set_native_icon(None)?; diff --git a/core/tauri/src/menu/predefined.rs b/core/tauri/src/menu/predefined.rs index c8b1e1c1a..b25713615 100644 --- a/core/tauri/src/menu/predefined.rs +++ b/core/tauri/src/menu/predefined.rs @@ -1,4 +1,4 @@ -// Copyright 2019-2023 Tauri Programme within The Commons Conservancy +// Copyright 2019-2024 Tauri Programme within The Commons Conservancy // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT @@ -337,15 +337,20 @@ impl PredefinedMenuItem { pub fn about>( manager: &M, text: Option<&str>, - metadata: Option, + metadata: Option>, ) -> crate::Result { let handle = manager.app_handle(); let app_handle = handle.clone(); let text = text.map(|t| t.to_owned()); + let metadata = match metadata { + Some(m) => Some(m.try_into()?), + None => None, + }; + let item = run_main_thread!(handle, || { - let item = muda::PredefinedMenuItem::about(text.as_deref(), metadata.map(Into::into)); + let item = muda::PredefinedMenuItem::about(text.as_deref(), metadata); PredefinedMenuItemInner { id: item.id().clone(), inner: Some(item), diff --git a/core/tauri/src/menu/submenu.rs b/core/tauri/src/menu/submenu.rs index 0b74fd724..785d8f09b 100644 --- a/core/tauri/src/menu/submenu.rs +++ b/core/tauri/src/menu/submenu.rs @@ -1,4 +1,4 @@ -// Copyright 2019-2023 Tauri Programme within The Commons Conservancy +// Copyright 2019-2024 Tauri Programme within The Commons Conservancy // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT @@ -32,7 +32,7 @@ impl ContextMenuBase for Submenu { window: crate::Window, position: Option

, ) -> crate::Result<()> { - let position = position.map(Into::into).map(super::into_position); + let position = position.map(Into::into); run_item_main_thread!(self, move |self_: Self| { #[cfg(target_os = "macos")] if let Ok(view) = window.ns_view() { @@ -191,7 +191,7 @@ impl Submenu { self.insert_items(items, 0) } - /// Insert a menu item at the specified `postion` in this submenu. + /// Insert a menu item at the specified `position` in this submenu. pub fn insert(&self, item: &dyn IsMenuItem, position: usize) -> crate::Result<()> { let kind = item.kind(); run_item_main_thread!(self, |self_: Self| { @@ -202,7 +202,7 @@ impl Submenu { .map_err(Into::into) } - /// Insert menu items at the specified `postion` in this submenu. + /// Insert menu items at the specified `position` in this submenu. pub fn insert_items(&self, items: &[&dyn IsMenuItem], position: usize) -> crate::Result<()> { for (i, item) in items.iter().enumerate() { self.insert(*item, position + i)? @@ -260,7 +260,7 @@ impl Submenu { /// Set the text for this submenu. `text` could optionally contain /// an `&` before a character to assign this character as the mnemonic - /// for this submenu. To display a `&` without assigning a mnemenonic, use `&&`. + /// for this submenu. To display a `&` without assigning a mnemonic, use `&&`. pub fn set_text>(&self, text: S) -> crate::Result<()> { let text = text.as_ref().to_string(); run_item_main_thread!(self, |self_: Self| (*self_.0).as_ref().set_text(text)) diff --git a/core/tauri/src/path/android.rs b/core/tauri/src/path/android.rs index 730e9f10b..c2f6be2f8 100644 --- a/core/tauri/src/path/android.rs +++ b/core/tauri/src/path/android.rs @@ -1,4 +1,4 @@ -// Copyright 2019-2023 Tauri Programme within The Commons Conservancy +// Copyright 2019-2024 Tauri Programme within The Commons Conservancy // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT diff --git a/core/tauri/src/path/desktop.rs b/core/tauri/src/path/desktop.rs index 3a81b358f..bba815886 100644 --- a/core/tauri/src/path/desktop.rs +++ b/core/tauri/src/path/desktop.rs @@ -1,4 +1,4 @@ -// Copyright 2019-2023 Tauri Programme within The Commons Conservancy +// Copyright 2019-2024 Tauri Programme within The Commons Conservancy // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT @@ -6,7 +6,7 @@ use super::{Error, Result}; use crate::{AppHandle, Manager, Runtime}; use std::path::PathBuf; -/// A helper class to access the mobile camera APIs. +/// The path resolver is a helper class for general and application-specific path APIs. pub struct PathResolver(pub(crate) AppHandle); impl PathResolver { diff --git a/core/tauri/src/path/init.js b/core/tauri/src/path/init.js index b417ef6f0..a402f61e9 100644 --- a/core/tauri/src/path/init.js +++ b/core/tauri/src/path/init.js @@ -1,4 +1,4 @@ -// Copyright 2019-2023 Tauri Programme within The Commons Conservancy +// Copyright 2019-2024 Tauri Programme within The Commons Conservancy // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT diff --git a/core/tauri/src/path/mod.rs b/core/tauri/src/path/mod.rs index f51f7ac2b..038ca2f17 100644 --- a/core/tauri/src/path/mod.rs +++ b/core/tauri/src/path/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2019-2023 Tauri Programme within The Commons Conservancy +// Copyright 2019-2024 Tauri Programme within The Commons Conservancy // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT @@ -11,7 +11,7 @@ use serde_repr::{Deserialize_repr, Serialize_repr}; pub(crate) mod plugin; -pub use crate::error::*; +use crate::error::*; #[cfg(target_os = "android")] mod android; diff --git a/core/tauri/src/path/plugin.rs b/core/tauri/src/path/plugin.rs index ff4326d9e..cacd46389 100644 --- a/core/tauri/src/path/plugin.rs +++ b/core/tauri/src/path/plugin.rs @@ -1,4 +1,4 @@ -// Copyright 2019-2023 Tauri Programme within The Commons Conservancy +// Copyright 2019-2024 Tauri Programme within The Commons Conservancy // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT @@ -179,16 +179,17 @@ pub fn extname(path: String) -> Result { } #[command(root = "crate")] -pub fn basename(path: String, ext: Option) -> Result { - match Path::new(&path) - .file_name() - .and_then(std::ffi::OsStr::to_str) - { - Some(p) => Ok(if let Some(ext) = ext { - p.replace(ext.as_str(), "") - } else { - p.to_string() - }), +pub fn basename(path: &str, ext: Option<&str>) -> Result { + let file_name = Path::new(path).file_name().map(|f| f.to_string_lossy()); + match file_name { + Some(p) => { + let maybe_stripped = if let Some(ext) = ext { + p.strip_suffix(ext).unwrap_or(&p).to_string() + } else { + p.to_string() + }; + Ok(maybe_stripped) + } None => Err(Error::NoBasename), } } @@ -245,3 +246,40 @@ pub(crate) fn init() -> TauriPlugin { }) .build() } + +#[cfg(test)] +mod tests { + + #[test] + fn basename() { + let path = "/path/to/some-json-file.json"; + assert_eq!( + super::basename(path, Some(".json")).unwrap(), + "some-json-file" + ); + + let path = "/path/to/some-json-file.json"; + assert_eq!( + super::basename(path, Some("json")).unwrap(), + "some-json-file." + ); + + let path = "/path/to/some-json-file.html.json"; + assert_eq!( + super::basename(path, Some(".json")).unwrap(), + "some-json-file.html" + ); + + let path = "/path/to/some-json-file.json.json"; + assert_eq!( + super::basename(path, Some(".json")).unwrap(), + "some-json-file.json" + ); + + let path = "/path/to/some-json-file.json.html"; + assert_eq!( + super::basename(path, Some(".json")).unwrap(), + "some-json-file.json.html" + ); + } +} diff --git a/core/tauri/src/pattern.rs b/core/tauri/src/pattern.rs index c86d54627..221fce9fb 100644 --- a/core/tauri/src/pattern.rs +++ b/core/tauri/src/pattern.rs @@ -1,29 +1,26 @@ -// Copyright 2019-2023 Tauri Programme within The Commons Conservancy +// Copyright 2019-2024 Tauri Programme within The Commons Conservancy // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT -use std::marker::PhantomData; #[cfg(feature = "isolation")] use std::sync::Arc; use serde::Serialize; use serialize_to_javascript::{default_template, Template}; -use tauri_utils::assets::{Assets, EmbeddedAssets}; - /// The domain of the isolation iframe source. pub const ISOLATION_IFRAME_SRC_DOMAIN: &str = "localhost"; /// An application pattern. #[derive(Debug)] -pub enum Pattern { +pub enum Pattern { /// The brownfield pattern. - Brownfield(PhantomData), + Brownfield, /// Isolation pattern. Recommended for security purposes. #[cfg(feature = "isolation")] Isolation { /// The HTML served on `isolation://index.html`. - assets: Arc, + assets: Arc, /// The schema used for the isolation frames. schema: String, @@ -55,7 +52,7 @@ pub(crate) enum PatternObject { impl From<&Pattern> for PatternObject { fn from(pattern: &Pattern) -> Self { match pattern { - Pattern::Brownfield(_) => Self::Brownfield, + Pattern::Brownfield => Self::Brownfield, #[cfg(feature = "isolation")] Pattern::Isolation { .. } => Self::Isolation { side: IsolationSide::default(), diff --git a/core/tauri/src/plugin.rs b/core/tauri/src/plugin.rs index 149d7f606..a57178d2a 100644 --- a/core/tauri/src/plugin.rs +++ b/core/tauri/src/plugin.rs @@ -1,4 +1,4 @@ -// Copyright 2019-2023 Tauri Programme within The Commons Conservancy +// Copyright 2019-2024 Tauri Programme within The Commons Conservancy // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT @@ -139,11 +139,13 @@ impl PluginApi { } /// Gets the global scope defined on the permissions that are part of the app ACL. - pub fn scope(&self) -> crate::Result<&ScopeValue> { + pub fn scope(&self) -> crate::Result> { self .handle .manager .runtime_authority + .lock() + .unwrap() .scope_manager .get_global_scope_typed(&self.handle, self.name) } @@ -516,7 +518,7 @@ impl Builder { /// /// # Known limitations /// - /// URI scheme protocols are registered when the webview is created. Due to this limitation, if the plugin is registed after a webview has been created, this protocol won't be available. + /// URI scheme protocols are registered when the webview is created. Due to this limitation, if the plugin is registered after a webview has been created, this protocol won't be available. /// /// # Arguments /// diff --git a/core/tauri/src/plugin/mobile.rs b/core/tauri/src/plugin/mobile.rs index 381b27420..e5ecd4951 100644 --- a/core/tauri/src/plugin/mobile.rs +++ b/core/tauri/src/plugin/mobile.rs @@ -1,4 +1,4 @@ -// Copyright 2019-2023 Tauri Programme within The Commons Conservancy +// Copyright 2019-2024 Tauri Programme within The Commons Conservancy // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT diff --git a/core/tauri/src/process.rs b/core/tauri/src/process.rs index de205a56f..9a23d653b 100644 --- a/core/tauri/src/process.rs +++ b/core/tauri/src/process.rs @@ -1,4 +1,4 @@ -// Copyright 2019-2023 Tauri Programme within The Commons Conservancy +// Copyright 2019-2024 Tauri Programme within The Commons Conservancy // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT diff --git a/core/tauri/src/protocol/asset.rs b/core/tauri/src/protocol/asset.rs index bddd1ffa5..941a7cc57 100644 --- a/core/tauri/src/protocol/asset.rs +++ b/core/tauri/src/protocol/asset.rs @@ -1,4 +1,4 @@ -// Copyright 2019-2023 Tauri Programme within The Commons Conservancy +// Copyright 2019-2024 Tauri Programme within The Commons Conservancy // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT @@ -6,7 +6,6 @@ use crate::{path::SafePathBuf, scope, webview::UriSchemeProtocolHandler}; use http::{header::*, status::StatusCode, Request, Response}; use http_range::HttpRange; use std::{borrow::Cow, io::SeekFrom}; -use tauri_utils::debug_eprintln; use tauri_utils::mime_type::MimeType; use tokio::fs::File; use tokio::io::{AsyncReadExt, AsyncSeekExt, AsyncWriteExt}; @@ -40,12 +39,12 @@ fn get_response( let mut resp = Response::builder().header("Access-Control-Allow-Origin", window_origin); if let Err(e) = SafePathBuf::new(path.clone().into()) { - debug_eprintln!("asset protocol path \"{}\" is not valid: {}", path, e); + log::error!("asset protocol path \"{}\" is not valid: {}", path, e); return resp.status(403).body(Vec::new().into()).map_err(Into::into); } if !scope.is_allowed(&path) { - debug_eprintln!("asset protocol not configured to allow the path: {}", path); + log::error!("asset protocol not configured to allow the path: {}", path); return resp.status(403).body(Vec::new().into()).map_err(Into::into); } diff --git a/core/tauri/src/protocol/isolation.rs b/core/tauri/src/protocol/isolation.rs index 37a031a68..62206a87e 100644 --- a/core/tauri/src/protocol/isolation.rs +++ b/core/tauri/src/protocol/isolation.rs @@ -1,21 +1,48 @@ -// Copyright 2019-2023 Tauri Programme within The Commons Conservancy +// Copyright 2019-2024 Tauri Programme within The Commons Conservancy // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT +use crate::Assets; use http::header::CONTENT_TYPE; use serialize_to_javascript::Template; -use tauri_utils::assets::{Assets, EmbeddedAssets}; +use tauri_utils::{assets::EmbeddedAssets, config::Csp}; use std::sync::Arc; -use crate::{manager::webview::PROCESS_IPC_MESSAGE_FN, webview::UriSchemeProtocolHandler}; +use crate::{ + manager::{set_csp, webview::PROCESS_IPC_MESSAGE_FN, AppManager}, + webview::UriSchemeProtocolHandler, + Runtime, +}; + +pub fn get( + manager: Arc>, + schema: &str, + assets: Arc, + aes_gcm_key: [u8; 32], +) -> UriSchemeProtocolHandler { + let frame_src = if cfg!(any(windows, target_os = "android")) { + format!("http://{schema}.localhost") + } else { + format!("{schema}:") + }; + + let assets = assets as Arc>; -pub fn get(assets: Arc, aes_gcm_key: [u8; 32]) -> UriSchemeProtocolHandler { Box::new(move |request, responder| { let response = match request_to_path(&request).as_str() { "index.html" => match assets.get(&"index.html".into()) { Some(asset) => { - let asset = String::from_utf8_lossy(asset.as_ref()); + let mut asset = String::from_utf8_lossy(asset.as_ref()).into_owned(); + let csp_map = set_csp( + &mut asset, + &assets, + &"index.html".into(), + &manager, + Csp::Policy(format!("default-src 'none'; frame-src {}", frame_src)), + ); + let csp = Csp::DirectiveMap(csp_map).to_string(); + let template = tauri_utils::pattern::isolation::IsolationJavascriptRuntime { runtime_aes_gcm_key: &aes_gcm_key, process_ipc_message_fn: PROCESS_IPC_MESSAGE_FN, @@ -23,6 +50,7 @@ pub fn get(assets: Arc, aes_gcm_key: [u8; 32]) -> UriSchemeProto match template.render(asset.as_ref(), &Default::default()) { Ok(asset) => http::Response::builder() .header(CONTENT_TYPE, mime::TEXT_HTML.as_ref()) + .header("Content-Security-Policy", csp) .body(asset.into_string().as_bytes().to_vec()), Err(_) => http::Response::builder() .status(http::StatusCode::INTERNAL_SERVER_ERROR) diff --git a/core/tauri/src/protocol/mod.rs b/core/tauri/src/protocol/mod.rs index 3f75e4de4..2298bd51f 100644 --- a/core/tauri/src/protocol/mod.rs +++ b/core/tauri/src/protocol/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2019-2023 Tauri Programme within The Commons Conservancy +// Copyright 2019-2024 Tauri Programme within The Commons Conservancy // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT diff --git a/core/tauri/src/protocol/tauri.rs b/core/tauri/src/protocol/tauri.rs index d7629d7c4..3b713e0b8 100644 --- a/core/tauri/src/protocol/tauri.rs +++ b/core/tauri/src/protocol/tauri.rs @@ -1,4 +1,4 @@ -// Copyright 2019-2023 Tauri Programme within The Commons Conservancy +// Copyright 2019-2024 Tauri Programme within The Commons Conservancy // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT @@ -146,7 +146,7 @@ fn get_response( .body(response.body.to_vec().into())? } Err(e) => { - tauri_utils::debug_eprintln!("Failed to request {}: {}", url.as_str(), e); + log::error!("Failed to request {}: {}", url.as_str(), e); return Err(Box::new(e)); } } @@ -164,14 +164,6 @@ fn get_response( if let Some(handler) = &web_resource_request_handler { handler(request, &mut response); } - // if it's an HTML file, we need to set the CSP meta tag on Linux - #[cfg(target_os = "linux")] - if let Some(response_csp) = response.headers().get("Content-Security-Policy") { - let response_csp = String::from_utf8_lossy(response_csp.as_bytes()); - let html = String::from_utf8_lossy(response.body()); - let body = html.replacen(tauri_utils::html::CSP_TOKEN, &response_csp, 1); - *response.body_mut() = body.as_bytes().to_vec().into(); - } Ok(response) } diff --git a/core/tauri/src/resources/mod.rs b/core/tauri/src/resources/mod.rs index 0829374f5..fcf8982e3 100644 --- a/core/tauri/src/resources/mod.rs +++ b/core/tauri/src/resources/mod.rs @@ -1,5 +1,5 @@ // Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. -// Copyright 2019-2023 Tauri Programme within The Commons Conservancy +// Copyright 2019-2024 Tauri Programme within The Commons Conservancy // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT @@ -7,7 +7,6 @@ pub(crate) mod plugin; -use crate::error::Error; use std::{ any::{type_name, Any, TypeId}, borrow::Cow, @@ -75,10 +74,15 @@ pub type ResourceId = u32; #[derive(Default)] pub struct ResourceTable { index: BTreeMap>, - next_rid: ResourceId, } impl ResourceTable { + fn new_random_rid() -> u32 { + let mut bytes = [0_u8; 4]; + getrandom::getrandom(&mut bytes).expect("failed to get random bytes"); + u32::from_ne_bytes(bytes) + } + /// Inserts resource into the resource table, which takes ownership of it. /// /// The resource type is erased at runtime and must be statically known @@ -107,10 +111,9 @@ impl ResourceTable { /// /// Returns a unique resource ID, which acts as a key for this resource. pub fn add_arc_dyn(&mut self, resource: Arc) -> ResourceId { - let rid = self.next_rid; + let rid = Self::new_random_rid(); let removed_resource = self.index.insert(rid, resource); assert!(removed_resource.is_none()); - self.next_rid += 1; rid } @@ -121,24 +124,24 @@ impl ResourceTable { /// Returns a reference counted pointer to the resource of type `T` with the /// given `rid`. If `rid` is not present or has a type different than `T`, - /// this function returns [`Error::BadResourceId`]. - pub fn get(&self, rid: ResourceId) -> Result, Error> { + /// this function returns [`Error::BadResourceId`](crate::Error::BadResourceId). + pub fn get(&self, rid: ResourceId) -> crate::Result> { self .index .get(&rid) .and_then(|rc| rc.downcast_arc::()) - .map(Clone::clone) - .ok_or_else(|| Error::BadResourceId(rid)) + .cloned() + .ok_or_else(|| crate::Error::BadResourceId(rid)) } /// Returns a reference counted pointer to the resource of the given `rid`. /// If `rid` is not present, this function returns [`Error::BadResourceId`]. - pub fn get_any(&self, rid: ResourceId) -> Result, Error> { + pub fn get_any(&self, rid: ResourceId) -> crate::Result> { self .index .get(&rid) - .map(Clone::clone) - .ok_or_else(|| Error::BadResourceId(rid)) + .ok_or_else(|| crate::Error::BadResourceId(rid)) + .cloned() } /// Replaces a resource with a new resource. @@ -161,7 +164,7 @@ impl ResourceTable { /// assume that `Arc::strong_count(&returned_arc)` is always equal to 1 on success. /// In particular, be really careful when you want to extract the inner value of /// type `T` from `Arc`. - pub fn take(&mut self, rid: ResourceId) -> Result, Error> { + pub fn take(&mut self, rid: ResourceId) -> crate::Result> { let resource = self.get::(rid)?; self.index.remove(&rid); Ok(resource) @@ -175,11 +178,11 @@ impl ResourceTable { /// we cannot assume that `Arc::strong_count(&returned_arc)` is always equal to 1 /// on success. In particular, be really careful when you want to extract the /// inner value of type `T` from `Arc`. - pub fn take_any(&mut self, rid: ResourceId) -> Result, Error> { + pub fn take_any(&mut self, rid: ResourceId) -> crate::Result> { self .index .remove(&rid) - .ok_or_else(|| Error::BadResourceId(rid)) + .ok_or_else(|| crate::Error::BadResourceId(rid)) } /// Returns an iterator that yields a `(id, name)` pair for every resource @@ -199,17 +202,11 @@ impl ResourceTable { /// counted, therefore pending ops are not automatically cancelled. A resource /// may implement the `close()` method to perform clean-ups such as canceling /// ops. - pub fn close(&mut self, rid: ResourceId) -> Result<(), Error> { + pub fn close(&mut self, rid: ResourceId) -> crate::Result<()> { self .index .remove(&rid) - .ok_or_else(|| Error::BadResourceId(rid)) + .ok_or_else(|| crate::Error::BadResourceId(rid)) .map(|resource| resource.close()) } - - /// Removes and frees all resources stored. Note that the - /// resource's `close()` method is *not* called. - pub(crate) fn clear(&mut self) { - self.index.clear() - } } diff --git a/core/tauri/src/resources/plugin.rs b/core/tauri/src/resources/plugin.rs index 863352fb6..7786fe74e 100644 --- a/core/tauri/src/resources/plugin.rs +++ b/core/tauri/src/resources/plugin.rs @@ -1,18 +1,18 @@ -// Copyright 2019-2023 Tauri Programme within The Commons Conservancy +// Copyright 2019-2024 Tauri Programme within The Commons Conservancy // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT use crate::{ command, plugin::{Builder, TauriPlugin}, - AppHandle, Manager, Runtime, + Manager, Runtime, Webview, }; use super::ResourceId; #[command(root = "crate")] -fn close(app: AppHandle, rid: ResourceId) -> crate::Result<()> { - app.resources_table().close(rid) +fn close(webview: Webview, rid: ResourceId) -> crate::Result<()> { + webview.resources_table().close(rid) } pub(crate) fn init() -> TauriPlugin { diff --git a/core/tauri/src/scope/fs.rs b/core/tauri/src/scope/fs.rs index 934f0a584..11af889c2 100644 --- a/core/tauri/src/scope/fs.rs +++ b/core/tauri/src/scope/fs.rs @@ -1,4 +1,4 @@ -// Copyright 2019-2023 Tauri Programme within The Commons Conservancy +// Copyright 2019-2024 Tauri Programme within The Commons Conservancy // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT @@ -201,7 +201,7 @@ impl Scope { } /// Listen to an event on this scope and immediately unlisten. - pub fn once(&self, f: F) { + pub fn once(&self, f: F) -> ScopeEventId { let listerners = self.event_listeners.clone(); let handler = std::cell::Cell::new(Some(f)); let id = self.next_event_id(); @@ -212,6 +212,7 @@ impl Scope { .expect("attempted to call handler more than once"); handler(event) }); + id } /// Removes an event listener on this scope. @@ -297,6 +298,14 @@ impl Scope { /// Determines if the given path is allowed on this scope. pub fn is_allowed>(&self, path: P) -> bool { let path = path.as_ref(); + let path = if path.is_symlink() { + match std::fs::read_link(path) { + Ok(p) => p, + Err(_) => return false, + } + } else { + path.to_path_buf() + }; let path = if !path.exists() { crate::Result::Ok(path.to_path_buf()) } else { diff --git a/core/tauri/src/scope/mod.rs b/core/tauri/src/scope/mod.rs index d2c7b7c8e..638778173 100644 --- a/core/tauri/src/scope/mod.rs +++ b/core/tauri/src/scope/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2019-2023 Tauri Programme within The Commons Conservancy +// Copyright 2019-2024 Tauri Programme within The Commons Conservancy // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT diff --git a/core/tauri/src/state.rs b/core/tauri/src/state.rs index 24a827077..843e55329 100644 --- a/core/tauri/src/state.rs +++ b/core/tauri/src/state.rs @@ -1,4 +1,4 @@ -// Copyright 2019-2023 Tauri Programme within The Commons Conservancy +// Copyright 2019-2024 Tauri Programme within The Commons Conservancy // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT diff --git a/core/tauri/src/test/mock_runtime.rs b/core/tauri/src/test/mock_runtime.rs index 891e6d341..113f6798c 100644 --- a/core/tauri/src/test/mock_runtime.rs +++ b/core/tauri/src/test/mock_runtime.rs @@ -1,4 +1,4 @@ -// Copyright 2019-2023 Tauri Programme within The Commons Conservancy +// Copyright 2019-2024 Tauri Programme within The Commons Conservancy // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT @@ -6,21 +6,19 @@ #![allow(missing_docs)] use tauri_runtime::{ + dpi::{PhysicalPosition, PhysicalSize, Position, Size}, monitor::Monitor, webview::{DetachedWebview, PendingWebview}, - window::{ - dpi::{PhysicalPosition, PhysicalSize, Position, Size}, - CursorIcon, DetachedWindow, PendingWindow, RawWindow, WindowEvent, WindowId, - }, + window::{CursorIcon, DetachedWindow, PendingWindow, RawWindow, WindowEvent, WindowId}, window::{WindowBuilder, WindowBuilderBase}, - DeviceEventFilter, Error, EventLoopProxy, ExitRequestedEventAction, Icon, Result, RunEvent, - Runtime, RuntimeHandle, RuntimeInitArgs, UserAttentionType, UserEvent, WebviewDispatch, - WindowDispatch, WindowEventId, + DeviceEventFilter, Error, EventLoopProxy, ExitRequestedEventAction, Icon, ProgressBarState, + Result, RunEvent, Runtime, RuntimeHandle, RuntimeInitArgs, UserAttentionType, UserEvent, + WebviewDispatch, WindowDispatch, WindowEventId, }; #[cfg(target_os = "macos")] use tauri_utils::TitleBarStyle; -use tauri_utils::{config::WindowConfig, ProgressBarState, Theme}; +use tauri_utils::{config::WindowConfig, Theme}; use url::Url; #[cfg(windows)] @@ -61,6 +59,7 @@ pub struct RuntimeContext { next_window_id: Arc, next_webview_id: Arc, next_window_event_id: Arc, + next_webview_event_id: Arc, } // SAFETY: we ensure this type is only used on the main thread. @@ -100,6 +99,10 @@ impl RuntimeContext { fn next_window_event_id(&self) -> WindowEventId { self.next_window_event_id.fetch_add(1, Ordering::Relaxed) } + + fn next_webview_event_id(&self) -> WindowEventId { + self.next_webview_event_id.fetch_add(1, Ordering::Relaxed) + } } impl fmt::Debug for RuntimeContext { @@ -386,7 +389,7 @@ impl WindowBuilder for MockWindowBuilder { self } - fn icon(self, icon: Icon) -> Result { + fn icon(self, icon: Icon<'_>) -> Result { Ok(self) } @@ -460,6 +463,13 @@ impl WebviewDispatch for MockWebviewDispatcher { self.context.send_message(Message::Task(Box::new(f))) } + fn on_webview_event( + &self, + f: F, + ) -> tauri_runtime::WebviewEventId { + self.context.next_window_event_id() + } + fn with_webview) + Send + 'static>(&self, f: F) -> Result<()> { Ok(()) } @@ -475,6 +485,10 @@ impl WebviewDispatch for MockWebviewDispatcher { Ok(false) } + fn set_zoom(&self, scale_factor: f64) -> Result<()> { + Ok(()) + } + fn eval_script>(&self, script: S) -> Result<()> { self .last_evaluated_script @@ -493,6 +507,10 @@ impl WebviewDispatch for MockWebviewDispatcher { .map_err(|_| Error::FailedToReceiveMessage) } + fn bounds(&self) -> Result { + Ok(tauri_runtime::Rect::default()) + } + fn position(&self) -> Result> { Ok(PhysicalPosition { x: 0, y: 0 }) } @@ -517,6 +535,10 @@ impl WebviewDispatch for MockWebviewDispatcher { Ok(()) } + fn set_bounds(&self, bounds: tauri_runtime::Rect) -> Result<()> { + Ok(()) + } + fn set_size(&self, _size: Size) -> Result<()> { Ok(()) } @@ -528,6 +550,14 @@ impl WebviewDispatch for MockWebviewDispatcher { fn set_focus(&self) -> Result<()> { Ok(()) } + + fn reparent(&self, window_id: WindowId) -> Result<()> { + Ok(()) + } + + fn set_auto_resize(&self, auto_resize: bool) -> Result<()> { + Ok(()) + } } impl WindowDispatch for MockWindowDispatcher { @@ -853,7 +883,7 @@ impl WindowDispatch for MockWindowDispatcher { Ok(()) } - fn set_icon(&self, icon: Icon) -> Result<()> { + fn set_icon(&self, icon: Icon<'_>) -> Result<()> { Ok(()) } @@ -922,6 +952,7 @@ impl MockRuntime { next_window_id: Default::default(), next_webview_id: Default::default(), next_window_event_id: Default::default(), + next_webview_event_id: Default::default(), }; Self { is_running, diff --git a/core/tauri/src/test/mod.rs b/core/tauri/src/test/mod.rs index 15a33d8c4..37f35ef56 100644 --- a/core/tauri/src/test/mod.rs +++ b/core/tauri/src/test/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2019-2023 Tauri Programme within The Commons Conservancy +// Copyright 2019-2024 Tauri Programme within The Commons Conservancy // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT @@ -39,6 +39,7 @@ //! cmd: "ping".into(), //! callback: tauri::ipc::CallbackFn(0), //! error: tauri::ipc::CallbackFn(1), +//! url: "http://tauri.localhost".parse().unwrap(), //! body: tauri::ipc::InvokeBody::default(), //! headers: Default::default(), //! }, @@ -55,29 +56,29 @@ use serde::Serialize; use std::{borrow::Cow, collections::HashMap, fmt::Debug}; use crate::{ - ipc::{InvokeBody, InvokeError, InvokeResponse}, + ipc::{InvokeBody, InvokeError, InvokeResponse, RuntimeAuthority}, webview::InvokeRequest, - App, Builder, Context, Pattern, Webview, + App, Assets, Builder, Context, Pattern, Runtime, Webview, }; use tauri_utils::{ acl::resolved::Resolved, - assets::{AssetKey, Assets, CspHash}, + assets::{AssetKey, CspHash}, config::{AppConfig, Config}, }; /// An empty [`Assets`] implementation. pub struct NoopAsset { - assets: HashMap<&'static str, &'static [u8]>, + assets: HashMap>, csp_hashes: Vec>, } -impl Assets for NoopAsset { +impl Assets for NoopAsset { fn get(&self, key: &AssetKey) -> Option> { None } - fn iter(&self) -> Box + '_> { - Box::new(self.assets.iter()) + fn iter(&self) -> Box + '_> { + Box::new(self.assets.iter().map(|(k, b)| (k.as_str(), b.as_slice()))) } fn csp_hashes(&self, html_path: &AssetKey) -> Box> + '_> { @@ -94,7 +95,7 @@ pub fn noop_assets() -> NoopAsset { } /// Creates a new [`crate::Context`] for testing. -pub fn mock_context(assets: A) -> crate::Context { +pub fn mock_context>(assets: A) -> crate::Context { Context { config: Config { schema: None, @@ -125,15 +126,9 @@ pub fn mock_context(assets: A) -> crate::Context { crate_name: "test", }, _info_plist: (), - pattern: Pattern::Brownfield(std::marker::PhantomData), - resolved_acl: Resolved { - #[cfg(debug_assertions)] - acl: Default::default(), - allowed_commands: Default::default(), - denied_commands: Default::default(), - command_scope: Default::default(), - global_scope: Default::default(), - }, + pattern: Pattern::Brownfield, + runtime_authority: RuntimeAuthority::new(Default::default(), Resolved::default()), + plugin_global_api_scripts: None, } } @@ -192,6 +187,7 @@ pub fn mock_app() -> App { /// cmd: "ping".into(), /// callback: tauri::ipc::CallbackFn(0), /// error: tauri::ipc::CallbackFn(1), +/// url: "http://tauri.localhost".parse().unwrap(), /// body: tauri::ipc::InvokeBody::default(), /// headers: Default::default(), /// }, @@ -248,6 +244,7 @@ pub fn assert_ipc_response< /// cmd: "ping".into(), /// callback: tauri::ipc::CallbackFn(0), /// error: tauri::ipc::CallbackFn(1), +/// url: "http://tauri.localhost".parse().unwrap(), /// body: tauri::ipc::InvokeBody::default(), /// headers: Default::default(), /// }, diff --git a/core/tauri/src/tray/mod.rs b/core/tauri/src/tray/mod.rs index 81cd7a92d..99f008002 100644 --- a/core/tauri/src/tray/mod.rs +++ b/core/tauri/src/tray/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2019-2023 Tauri Programme within The Commons Conservancy +// Copyright 2019-2024 Tauri Programme within The Commons Conservancy // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT @@ -12,24 +12,13 @@ use crate::app::{GlobalMenuEventListener, GlobalTrayIconEventListener}; use crate::menu::ContextMenu; use crate::menu::MenuEvent; use crate::resources::Resource; -use crate::{menu::run_item_main_thread, AppHandle, Icon, Manager, Runtime}; +use crate::{ + image::Image, menu::run_item_main_thread, AppHandle, Manager, PhysicalPosition, Rect, Runtime, +}; use serde::Serialize; use std::path::Path; pub use tray_icon::TrayIconId; -/// Describes a rectangle including position (x - y axis) and size. -#[derive(Debug, PartialEq, Clone, Copy, Default, Serialize)] -pub struct Rectangle { - /// The x-coordinate of the upper-left corner of the rectangle. - pub left: f64, - /// The y-coordinate of the upper-left corner of the rectangle. - pub top: f64, - /// The x-coordinate of the lower-right corner of the rectangle. - pub right: f64, - /// The y-coordinate of the lower-right corner of the rectangle. - pub bottom: f64, -} - /// Describes the click type that triggered this tray icon event. #[derive(Clone, Copy, PartialEq, Eq, Debug, Serialize)] pub enum ClickType { @@ -51,19 +40,17 @@ impl Default for ClickType { /// /// ## Platform-specific: /// -/// - **Linux**: Unsupported. The event is not emmited even though the icon is shown, +/// - **Linux**: Unsupported. The event is not emitted even though the icon is shown, /// the icon will still show a context menu on right click. #[derive(Debug, Clone, Default, Serialize)] #[serde(rename_all = "camelCase")] pub struct TrayIconEvent { /// Id of the tray icon which triggered this event. pub id: TrayIconId, - /// Physical X Position of the click the triggered this event. - pub x: f64, - /// Physical Y Position of the click the triggered this event. - pub y: f64, + /// Physical Position of the click the triggered this event. + pub position: PhysicalPosition, /// Position and size of the tray icon - pub icon_rect: Rectangle, + pub icon_rect: Rect, /// The click type that triggered this event. pub click_type: ClickType, } @@ -75,17 +62,6 @@ impl TrayIconEvent { } } -impl From for Rectangle { - fn from(value: tray_icon::Rectangle) -> Self { - Self { - bottom: value.bottom, - left: value.left, - top: value.top, - right: value.right, - } - } -} - impl From for ClickType { fn from(value: tray_icon::ClickType) -> Self { match value { @@ -100,9 +76,11 @@ impl From for TrayIconEvent { fn from(value: tray_icon::TrayIconEvent) -> Self { Self { id: value.id, - x: value.x, - y: value.y, - icon_rect: value.icon_rect.into(), + position: value.position, + icon_rect: Rect { + position: value.icon_rect.position.into(), + size: value.icon_rect.size.into(), + }, click_type: value.click_type.into(), } } @@ -159,7 +137,7 @@ impl TrayIconBuilder { /// /// - **Linux:** Sometimes the icon won't be visible unless a menu is set. /// Setting an empty [`Menu`](crate::menu::Menu) is enough. - pub fn icon(mut self, icon: Icon) -> Self { + pub fn icon(mut self, icon: Image<'_>) -> Self { let icon = icon.try_into().ok(); if let Some(icon) = icon { self.inner = self.inner.with_icon(icon); @@ -364,8 +342,11 @@ impl TrayIcon { } /// Sets a new tray icon. If `None` is provided, it will remove the icon. - pub fn set_icon(&self, icon: Option) -> crate::Result<()> { - let icon = icon.and_then(|i| i.try_into().ok()); + pub fn set_icon(&self, icon: Option>) -> crate::Result<()> { + let icon = match icon { + Some(i) => Some(i.try_into()?), + None => None, + }; run_item_main_thread!(self, |self_: Self| self_.inner.set_icon(icon))?.map_err(Into::into) } @@ -441,15 +422,6 @@ impl TrayIcon { } } -impl TryFrom for tray_icon::Icon { - type Error = crate::Error; - - fn try_from(value: Icon) -> Result { - let value: crate::runtime::Icon = value.try_into()?; - tray_icon::Icon::from_rgba(value.rgba, value.width, value.height).map_err(Into::into) - } -} - impl Resource for TrayIcon { fn close(self: std::sync::Arc) { self.app_handle.remove_tray_by_id(&self.id); diff --git a/core/tauri/src/tray/plugin.rs b/core/tauri/src/tray/plugin.rs index 5ca61a10c..8b6c09cae 100644 --- a/core/tauri/src/tray/plugin.rs +++ b/core/tauri/src/tray/plugin.rs @@ -1,4 +1,4 @@ -// Copyright 2019-2023 Tauri Programme within The Commons Conservancy +// Copyright 2019-2024 Tauri Programme within The Commons Conservancy // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT @@ -8,12 +8,13 @@ use serde::Deserialize; use crate::{ command, + image::JsImage, ipc::Channel, menu::{plugin::ItemKind, Menu, Submenu}, plugin::{Builder, TauriPlugin}, resources::ResourceId, tray::TrayIconBuilder, - AppHandle, IconDto, Manager, Runtime, + AppHandle, Manager, Runtime, Webview, }; use super::TrayIcon; @@ -23,7 +24,7 @@ use super::TrayIcon; struct TrayIconOptions { id: Option, menu: Option<(ResourceId, ItemKind)>, - icon: Option, + icon: Option, tooltip: Option, title: Option, temp_dir_path: Option, @@ -33,7 +34,7 @@ struct TrayIconOptions { #[command(root = "crate")] fn new( - app: AppHandle, + webview: Webview, options: TrayIconOptions, handler: Channel, ) -> crate::Result<(ResourceId, String)> { @@ -47,7 +48,7 @@ fn new( let _ = handler.send(e); }); - let mut resources_table = app.resources_table(); + let mut resources_table = webview.resources_table(); if let Some((rid, kind)) = options.menu { match kind { @@ -63,7 +64,7 @@ fn new( }; } if let Some(icon) = options.icon { - builder = builder.icon(icon.into()); + builder = builder.icon(icon.into_img(&webview)?.as_ref().clone()); } if let Some(tooltip) = options.tooltip { builder = builder.tooltip(tooltip); @@ -81,7 +82,7 @@ fn new( builder = builder.menu_on_left_click(menu_on_left_click); } - let tray = builder.build(&app)?; + let tray = builder.build(&webview)?; let id = tray.id().as_ref().to_string(); let rid = resources_table.add(tray); @@ -89,23 +90,50 @@ fn new( } #[command(root = "crate")] -fn set_icon( +fn get_by_id( app: AppHandle, + webview: Webview, + id: &str, +) -> crate::Result> { + let tray = app.tray_by_id(id); + let maybe_rid = tray.map(|tray| { + let mut resources_table = webview.resources_table(); + resources_table.add(tray) + }); + Ok(maybe_rid) +} + +#[command(root = "crate")] +fn remove_by_id(app: AppHandle, id: &str) -> crate::Result<()> { + app + .remove_tray_by_id(id) + .ok_or_else(|| anyhow::anyhow!("Can't find a tray associated with this id: {id}")) + .map(|_| ()) + .map_err(Into::into) +} + +#[command(root = "crate")] +fn set_icon( + webview: Webview, rid: ResourceId, - icon: Option, + icon: Option, ) -> crate::Result<()> { - let resources_table = app.resources_table(); + let resources_table = webview.resources_table(); let tray = resources_table.get::>(rid)?; - tray.set_icon(icon.map(Into::into)) + let icon = match icon { + Some(i) => Some(i.into_img(&webview)?.as_ref().clone()), + None => None, + }; + tray.set_icon(icon) } #[command(root = "crate")] fn set_menu( - app: AppHandle, + webview: Webview, rid: ResourceId, menu: Option<(ResourceId, ItemKind)>, ) -> crate::Result<()> { - let resources_table = app.resources_table(); + let resources_table = webview.resources_table(); let tray = resources_table.get::>(rid)?; if let Some((rid, kind)) = menu { match kind { @@ -127,62 +155,66 @@ fn set_menu( #[command(root = "crate")] fn set_tooltip( - app: AppHandle, + webview: Webview, rid: ResourceId, tooltip: Option, ) -> crate::Result<()> { - let resources_table = app.resources_table(); + let resources_table = webview.resources_table(); let tray = resources_table.get::>(rid)?; tray.set_tooltip(tooltip) } #[command(root = "crate")] fn set_title( - app: AppHandle, + webview: Webview, rid: ResourceId, title: Option, ) -> crate::Result<()> { - let resources_table = app.resources_table(); + let resources_table = webview.resources_table(); let tray = resources_table.get::>(rid)?; tray.set_title(title) } #[command(root = "crate")] -fn set_visible(app: AppHandle, rid: ResourceId, visible: bool) -> crate::Result<()> { - let resources_table = app.resources_table(); +fn set_visible( + webview: Webview, + rid: ResourceId, + visible: bool, +) -> crate::Result<()> { + let resources_table = webview.resources_table(); let tray = resources_table.get::>(rid)?; tray.set_visible(visible) } #[command(root = "crate")] fn set_temp_dir_path( - app: AppHandle, + webview: Webview, rid: ResourceId, path: Option, ) -> crate::Result<()> { - let resources_table = app.resources_table(); + let resources_table = webview.resources_table(); let tray = resources_table.get::>(rid)?; tray.set_temp_dir_path(path) } #[command(root = "crate")] fn set_icon_as_template( - app: AppHandle, + webview: Webview, rid: ResourceId, as_template: bool, ) -> crate::Result<()> { - let resources_table = app.resources_table(); + let resources_table = webview.resources_table(); let tray = resources_table.get::>(rid)?; tray.set_icon_as_template(as_template) } #[command(root = "crate")] fn set_show_menu_on_left_click( - app: AppHandle, + webview: Webview, rid: ResourceId, on_left: bool, ) -> crate::Result<()> { - let resources_table = app.resources_table(); + let resources_table = webview.resources_table(); let tray = resources_table.get::>(rid)?; tray.set_show_menu_on_left_click(on_left) } @@ -191,6 +223,8 @@ pub(crate) fn init() -> TauriPlugin { Builder::new("tray") .invoke_handler(crate::generate_handler![ new, + get_by_id, + remove_by_id, set_icon, set_menu, set_tooltip, diff --git a/core/tauri/src/vibrancy/macos.rs b/core/tauri/src/vibrancy/macos.rs index 3d262e4ab..d1305718c 100644 --- a/core/tauri/src/vibrancy/macos.rs +++ b/core/tauri/src/vibrancy/macos.rs @@ -1,4 +1,4 @@ -// Copyright 2019-2023 Tauri Programme within The Commons Conservancy +// Copyright 2019-2024 Tauri Programme within The Commons Conservancy // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT diff --git a/core/tauri/src/vibrancy/mod.rs b/core/tauri/src/vibrancy/mod.rs index 86bfa1610..38d8caf65 100644 --- a/core/tauri/src/vibrancy/mod.rs +++ b/core/tauri/src/vibrancy/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2019-2023 Tauri Programme within The Commons Conservancy +// Copyright 2019-2024 Tauri Programme within The Commons Conservancy // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT diff --git a/core/tauri/src/vibrancy/windows.rs b/core/tauri/src/vibrancy/windows.rs index 990698cb3..610b465cc 100644 --- a/core/tauri/src/vibrancy/windows.rs +++ b/core/tauri/src/vibrancy/windows.rs @@ -1,4 +1,4 @@ -// Copyright 2019-2023 Tauri Programme within The Commons Conservancy +// Copyright 2019-2024 Tauri Programme within The Commons Conservancy // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT diff --git a/core/tauri/src/webview/mod.rs b/core/tauri/src/webview/mod.rs index a62546ffd..43f9bbe69 100644 --- a/core/tauri/src/webview/mod.rs +++ b/core/tauri/src/webview/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2019-2023 Tauri Programme within The Commons Conservancy +// Copyright 2019-2024 Tauri Programme within The Commons Conservancy // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT @@ -13,20 +13,20 @@ use http::HeaderMap; use serde::Serialize; use tauri_macros::default_runtime; pub use tauri_runtime::webview::PageLoadEvent; -use tauri_runtime::{ - webview::{DetachedWebview, PendingWebview, WebviewAttributes}, - WebviewDispatch, -}; #[cfg(desktop)] use tauri_runtime::{ - window::dpi::{PhysicalPosition, PhysicalSize, Position, Size}, + dpi::{PhysicalPosition, PhysicalSize, Position, Size}, WindowDispatch, }; +use tauri_runtime::{ + webview::{DetachedWebview, PendingWebview, WebviewAttributes}, + Rect, WebviewDispatch, +}; use tauri_utils::config::{WebviewUrl, WindowConfig}; pub use url::Url; use crate::{ - app::UriSchemeResponder, + app::{UriSchemeResponder, WebviewEvent}, event::{EmitArgs, EventTarget}, ipc::{ CallbackFn, CommandArg, CommandItem, Invoke, InvokeBody, InvokeError, InvokeMessage, @@ -34,14 +34,14 @@ use crate::{ }, manager::{webview::WebviewLabelDef, AppManager}, sealed::{ManagerBase, RuntimeOrDispatch}, - AppHandle, Event, EventId, EventLoopMessage, Manager, Runtime, Window, + AppHandle, Event, EventId, EventLoopMessage, Manager, ResourceTable, Runtime, Window, }; use std::{ borrow::Cow, hash::{Hash, Hasher}, path::PathBuf, - sync::{Arc, Mutex}, + sync::{Arc, Mutex, MutexGuard}, }; pub(crate) type WebResourceRequestHandler = @@ -54,8 +54,8 @@ pub(crate) type OnPageLoad = dyn Fn(Webview, PageLoadPayload<'_>) + Send + pub(crate) type DownloadHandler = dyn Fn(Webview, DownloadEvent<'_>) -> bool + Send + Sync; #[derive(Clone, Serialize)] -struct CreatedEvent { - label: String, +pub(crate) struct CreatedEvent { + pub(crate) label: String, } /// Download event for the [`WebviewBuilder#method.on_download`] hook. @@ -118,6 +118,8 @@ pub struct InvokeRequest { pub callback: CallbackFn, /// The error callback. pub error: CallbackFn, + /// URL of the frame that requested this command. + pub url: Url, /// The body of the request. pub body: InvokeBody, /// The request headers. @@ -565,17 +567,17 @@ tauri::Builder::default() })); } - if let Some(on_page_load_handler) = self.on_page_load_handler.take() { - let label = pending.label.clone(); - let manager = manager.manager_owned(); - pending - .on_page_load_handler - .replace(Box::new(move |url, event| { - if let Some(w) = manager.get_webview(&label) { - on_page_load_handler(w, PageLoadPayload { url: &url, event }); + let label_ = pending.label.clone(); + let manager_ = manager.manager_owned(); + pending + .on_page_load_handler + .replace(Box::new(move |url, event| { + if let Some(w) = manager_.get_webview(&label_) { + if let Some(handler) = self.on_page_load_handler.as_ref() { + handler(w, PageLoadPayload { url: &url, event }); } - })); - } + } + })); manager.manager().webview.prepare_webview( manager, @@ -606,7 +608,7 @@ tauri::Builder::default() .webviews_lock() .values() .map(|w| WebviewLabelDef { - window_label: w.window.label().to_string(), + window_label: w.window().label().to_string(), label: w.label().to_string(), }) .collect::>(); @@ -616,7 +618,7 @@ tauri::Builder::default() let mut pending = self.into_pending_webview(&window, window.label(), &window_labels, &webview_labels)?; - pending.webview_attributes.bounds = Some((position, size)); + pending.webview_attributes.bounds = Some(Rect { size, position }); let webview = match &mut window.runtime() { RuntimeOrDispatch::Dispatch(dispatcher) => dispatcher.create_webview(pending), @@ -624,22 +626,6 @@ tauri::Builder::default() } .map(|webview| app_manager.webview.attach_webview(window.clone(), webview))?; - app_manager.webview.eval_script_all(format!( - "window.__TAURI_INTERNALS__.metadata.windows = {window_labels_array}.map(function (label) {{ return {{ label: label }} }})", - window_labels_array = serde_json::to_string(&app_manager.webview.labels())?, - ))?; - - app_manager.emit_filter( - "tauri://webview-created", - Some(CreatedEvent { - label: webview.label().into(), - }), - |s| match s { - EventTarget::Webview { label } => label == webview.label(), - _ => false, - }, - )?; - Ok(webview) } } @@ -730,10 +716,10 @@ fn main() { self } - /// Disables the file drop handler. This is required to use drag and drop APIs on the front end on Windows. + /// Disables the drag and drop handler. This is required to use HTML5 drag and drop APIs on the frontend on Windows. #[must_use] - pub fn disable_file_drop_handler(mut self) -> Self { - self.webview_attributes.file_drop_handler_enabled = false; + pub fn disable_drag_drop_handler(mut self) -> Self { + self.webview_attributes.drag_drop_handler_enabled = false; self } @@ -789,20 +775,39 @@ fn main() { self.webview_attributes.auto_resize = true; self } + + /// Whether page zooming by hotkeys is enabled + /// + /// ## Platform-specific: + /// + /// - **Windows**: Controls WebView2's [`IsZoomControlEnabled`](https://learn.microsoft.com/en-us/microsoft-edge/webview2/reference/winrt/microsoft_web_webview2_core/corewebview2settings?view=webview2-winrt-1.0.2420.47#iszoomcontrolenabled) setting. + /// - **MacOS / Linux**: Injects a polyfill that zooms in and out with `ctrl/command` + `-/=`, + /// 20% in each step, ranging from 20% to 1000%. Requires `webview:allow-set-webview-zoom` permission + /// + /// - **Android / iOS**: Unsupported. + #[must_use] + pub fn zoom_hotkeys_enabled(mut self, enabled: bool) -> Self { + self.webview_attributes.zoom_hotkeys_enabled = enabled; + self + } } /// Webview. #[default_runtime(crate::Wry, wry)] pub struct Webview { - pub(crate) window: Window, + window_label: Arc>, + /// The manager to associate this webview with. + pub(crate) manager: Arc>, + pub(crate) app_handle: AppHandle, /// The webview created by the runtime. pub(crate) webview: DetachedWebview, + pub(crate) resources_table: Arc>, } impl std::fmt::Debug for Webview { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_struct("Window") - .field("window", &self.window) + .field("window_label", &self.window_label) .field("webview", &self.webview) .finish() } @@ -811,8 +816,11 @@ impl std::fmt::Debug for Webview { impl Clone for Webview { fn clone(&self) -> Self { Self { - window: self.window.clone(), + window_label: self.window_label.clone(), + manager: self.manager.clone(), + app_handle: self.app_handle.clone(), webview: self.webview.clone(), + resources_table: self.resources_table.clone(), } } } @@ -836,7 +844,13 @@ impl PartialEq for Webview { impl Webview { /// Create a new webview that is attached to the window. pub(crate) fn new(window: Window, webview: DetachedWebview) -> Self { - Self { window, webview } + Self { + window_label: Arc::new(Mutex::new(window.label().into())), + manager: window.manager.clone(), + app_handle: window.app_handle.clone(), + webview, + resources_table: Default::default(), + } } /// Initializes a webview builder with the given window label and URL to load on the webview. @@ -861,6 +875,14 @@ impl Webview { pub fn label(&self) -> &str { &self.webview.label } + + /// Registers a window event listener. + pub fn on_webview_event(&self, f: F) { + self + .webview + .dispatcher + .on_webview_event(move |event| f(&event.clone().into())); + } } /// Desktop webview setters and actions. @@ -875,39 +897,36 @@ impl Webview { /// Closes this webview. pub fn close(&self) -> crate::Result<()> { - if self.window.webview_window { - self.window.close() - } else { - self.webview.dispatcher.close()?; - self.manager().on_webview_close(self.label()); - Ok(()) - } + self.webview.dispatcher.close()?; + self.manager().on_webview_close(self.label()); + Ok(()) + } + + /// Resizes this webview. + pub fn set_bounds(&self, bounds: Rect) -> crate::Result<()> { + self + .webview + .dispatcher + .set_bounds(bounds) + .map_err(Into::into) } /// Resizes this webview. pub fn set_size>(&self, size: S) -> crate::Result<()> { - if self.window.webview_window { - self.window.set_size(size.into()) - } else { - self - .webview - .dispatcher - .set_size(size.into()) - .map_err(Into::into) - } + self + .webview + .dispatcher + .set_size(size.into()) + .map_err(Into::into) } /// Sets this webviews's position. pub fn set_position>(&self, position: Pos) -> crate::Result<()> { - if self.window.webview_window { - self.window.set_position(position.into()) - } else { - self - .webview - .dispatcher - .set_position(position.into()) - .map_err(Into::into) - } + self + .webview + .dispatcher + .set_position(position.into()) + .map_err(Into::into) } /// Focus the webview. @@ -915,33 +934,61 @@ impl Webview { self.webview.dispatcher.set_focus().map_err(Into::into) } + /// Move the webview to the given window. + pub fn reparent(&self, window: &Window) -> crate::Result<()> { + #[cfg(not(feature = "unstable"))] + { + let current_window = self.window(); + if current_window.is_webview_window() || window.is_webview_window() { + return Err(crate::Error::CannotReparentWebviewWindow); + } + } + + self.webview.dispatcher.reparent(window.window.id)?; + *self.window_label.lock().unwrap() = window.label().to_string(); + Ok(()) + } + + /// Sets whether the webview should automatically grow and shrink its size and position when the parent window resizes. + pub fn set_auto_resize(&self, auto_resize: bool) -> crate::Result<()> { + self + .webview + .dispatcher + .set_auto_resize(auto_resize) + .map_err(Into::into) + } + + /// Returns the bounds of the webviews's client area. + pub fn bounds(&self) -> crate::Result { + self.webview.dispatcher.bounds().map_err(Into::into) + } + /// Returns the webview position. /// /// - For child webviews, returns the position of the top-left hand corner of the webviews's client area relative to the top-left hand corner of the parent window. /// - For webview window, returns the inner position of the window. pub fn position(&self) -> crate::Result> { - if self.window.webview_window { - self.window.inner_position() - } else { - self.webview.dispatcher.position().map_err(Into::into) - } + self.webview.dispatcher.position().map_err(Into::into) } /// Returns the physical size of the webviews's client area. pub fn size(&self) -> crate::Result> { - if self.window.webview_window { - self.window.inner_size() - } else { - self.webview.dispatcher.size().map_err(Into::into) - } + self.webview.dispatcher.size().map_err(Into::into) } } /// Webview APIs. impl Webview { /// The window that is hosting this webview. - pub fn window(&self) -> &Window { - &self.window + pub fn window(&self) -> Window { + self + .manager + .get_window(&self.window_label.lock().unwrap()) + .expect("could not locate webview parent window") + } + + pub(crate) fn window_label(&self) -> String { + self.window_label.lock().unwrap().clone() } /// Executes a closure, providing it with the webview handle that is specific to the current platform. @@ -1068,8 +1115,7 @@ fn main() { /// Handles this window receiving an [`InvokeRequest`]. pub fn on_message(self, request: InvokeRequest, responder: Box>) { let manager = self.manager_owned(); - let current_url = self.url(); - let is_local = self.is_local_url(¤t_url); + let is_local = self.is_local_url(&request.url); let custom_responder = self.manager().webview.invoke_responder.clone(); @@ -1091,7 +1137,7 @@ fn main() { ); #[cfg(mobile)] - let app_handle = self.window.app_handle.clone(); + let app_handle = self.app_handle.clone(); let message = InvokeMessage::new( self, @@ -1105,16 +1151,19 @@ fn main() { Origin::Local } else { Origin::Remote { - domain: current_url - .domain() - .map(|d| d.to_string()) - .unwrap_or_default(), + url: request.url.clone(), } }; - let resolved_acl = manager - .runtime_authority - .resolve_access(&request.cmd, &message.webview.webview.label, &acl_origin) - .cloned(); + let (resolved_acl, has_app_acl_manifest) = { + let runtime_authority = manager.runtime_authority.lock().unwrap(); + let acl = runtime_authority.resolve_access( + &request.cmd, + message.webview.window().label(), + message.webview.label(), + &acl_origin, + ); + (acl, runtime_authority.has_app_manifest()) + }; let mut invoke = Invoke { message, @@ -1122,32 +1171,46 @@ fn main() { acl: resolved_acl, }; - if let Some((plugin, command_name)) = request.cmd.strip_prefix("plugin:").map(|raw_command| { + let plugin_command = request.cmd.strip_prefix("plugin:").map(|raw_command| { let mut tokens = raw_command.split('|'); // safe to unwrap: split always has a least one item let plugin = tokens.next().unwrap(); let command = tokens.next().map(|c| c.to_string()).unwrap_or_default(); (plugin, command) - }) { - if request.cmd != crate::ipc::channel::FETCH_CHANNEL_DATA_COMMAND && invoke.acl.is_none() { - #[cfg(debug_assertions)] - { - invoke - .resolver - .reject(manager.runtime_authority.resolve_access_message( - plugin, - &command_name, - &invoke.message.webview.webview.label, - &acl_origin, - )); - } - #[cfg(not(debug_assertions))] - invoke - .resolver - .reject(format!("Command {} not allowed by ACL", request.cmd)); - return; - } + }); + // we only check ACL on plugin commands or if the app defined its ACL manifest + if (plugin_command.is_some() || has_app_acl_manifest) + && request.cmd != crate::ipc::channel::FETCH_CHANNEL_DATA_COMMAND + && invoke.acl.is_none() + { + #[cfg(debug_assertions)] + { + let (key, command_name) = plugin_command + .clone() + .unwrap_or_else(|| (tauri_utils::acl::APP_ACL_KEY, request.cmd.clone())); + invoke.resolver.reject( + manager + .runtime_authority + .lock() + .unwrap() + .resolve_access_message( + key, + &command_name, + invoke.message.webview.window().label(), + invoke.message.webview.label(), + &acl_origin, + ), + ); + } + #[cfg(not(debug_assertions))] + invoke + .resolver + .reject(format!("Command {} not allowed by ACL", request.cmd)); + return; + } + + if let Some((plugin, command_name)) = plugin_command { invoke.message.command = command_name; let command = invoke.message.command.clone(); @@ -1184,7 +1247,7 @@ fn main() { if let Err(e) = crate::plugin::mobile::run_command( plugin, &app_handle, - message.command, + heck::AsLowerCamelCase(message.command).to_string(), payload, move |response| match response { Ok(r) => resolver_.resolve(r), @@ -1248,16 +1311,16 @@ fn main() { id, ))?; - listeners.unlisten_js(id); + listeners.unlisten_js(event, id); Ok(()) } - pub(crate) fn emit_js(&self, emit_args: &EmitArgs, target: &EventTarget) -> crate::Result<()> { + pub(crate) fn emit_js(&self, emit_args: &EmitArgs, ids: &[u32]) -> crate::Result<()> { self.eval(&crate::event::emit_js_script( self.manager().listeners().function_name(), emit_args, - &serde_json::to_string(target)?, + &serde_json::to_string(ids)?, )?)?; Ok(()) } @@ -1369,6 +1432,21 @@ tauri::Builder::default() .is_devtools_open() .unwrap_or_default() } + + /// Set the webview zoom level + /// + /// ## Platform-specific: + /// + /// - **Android**: Not supported. + /// - **macOS**: available on macOS 11+ only. + /// - **iOS**: available on iOS 14+ only. + pub fn set_zoom(&self, scale_factor: f64) -> crate::Result<()> { + self + .webview + .dispatcher + .set_zoom(scale_factor) + .map_err(Into::into) + } } /// Event system APIs. @@ -1398,7 +1476,7 @@ tauri::Builder::default() where F: Fn(Event) + Send + 'static, { - self.window.manager.listen( + self.manager.listen( event.into(), EventTarget::Webview { label: self.label().to_string(), @@ -1437,17 +1515,17 @@ tauri::Builder::default() "#### )] pub fn unlisten(&self, id: EventId) { - self.window.manager.unlisten(id) + self.manager.unlisten(id) } /// Listen to an event on this webview only once. /// /// See [`Self::listen`] for more information. - pub fn once(&self, event: impl Into, handler: F) + pub fn once(&self, event: impl Into, handler: F) -> EventId where F: FnOnce(Event) + Send + 'static, { - self.window.manager.once( + self.manager.once( event.into(), EventTarget::Webview { label: self.label().to_string(), @@ -1457,23 +1535,30 @@ tauri::Builder::default() } } -impl Manager for Webview {} +impl Manager for Webview { + fn resources_table(&self) -> MutexGuard<'_, ResourceTable> { + self + .resources_table + .lock() + .expect("poisoned window resources table") + } +} impl ManagerBase for Webview { fn manager(&self) -> &AppManager { - &self.window.manager + &self.manager } fn manager_owned(&self) -> Arc> { - self.window.manager.clone() + self.manager.clone() } fn runtime(&self) -> RuntimeOrDispatch<'_, R> { - self.window.app_handle.runtime() + self.app_handle.runtime() } fn managed_app_handle(&self) -> &AppHandle { - &self.window.app_handle + &self.app_handle } } diff --git a/core/tauri/src/webview/plugin.rs b/core/tauri/src/webview/plugin.rs index 9e0ed5334..09d2aca59 100644 --- a/core/tauri/src/webview/plugin.rs +++ b/core/tauri/src/webview/plugin.rs @@ -1,4 +1,4 @@ -// Copyright 2019-2023 Tauri Programme within The Commons Conservancy +// Copyright 2019-2024 Tauri Programme within The Commons Conservancy // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT @@ -13,7 +13,7 @@ use crate::{ mod desktop_commands { use serde::Deserialize; - use tauri_runtime::window::dpi::{Position, Size}; + use tauri_runtime::dpi::{Position, Size}; use tauri_utils::config::{WebviewUrl, WindowConfig}; use super::*; @@ -28,7 +28,7 @@ mod desktop_commands { #[serde(default)] url: WebviewUrl, user_agent: Option, - file_drop_enabled: Option, + drag_drop_enabled: Option, x: f64, y: f64, width: f64, @@ -40,6 +40,8 @@ mod desktop_commands { window_effects: Option, #[serde(default)] incognito: bool, + #[serde(default)] + zoom_hotkeys_enabled: bool, } #[command(root = "crate")] @@ -71,17 +73,18 @@ mod desktop_commands { let mut builder = crate::webview::WebviewBuilder::new(label, options.url); builder.webview_attributes.user_agent = options.user_agent; - builder.webview_attributes.file_drop_handler_enabled = - options.file_drop_enabled.unwrap_or(true); + builder.webview_attributes.drag_drop_handler_enabled = + options.drag_drop_enabled.unwrap_or(true); builder.webview_attributes.transparent = options.transparent; builder.webview_attributes.accept_first_mouse = options.accept_first_mouse; builder.webview_attributes.window_effects = options.window_effects; builder.webview_attributes.incognito = options.incognito; + builder.webview_attributes.zoom_hotkeys_enabled = options.zoom_hotkeys_enabled; window.add_child( builder, - tauri_runtime::window::dpi::LogicalPosition::new(options.x, options.y), - tauri_runtime::window::dpi::LogicalSize::new(options.width, options.height), + tauri_runtime::dpi::LogicalPosition::new(options.x, options.y), + tauri_runtime::dpi::LogicalSize::new(options.width, options.height), )?; Ok(()) @@ -144,13 +147,9 @@ mod desktop_commands { getter!( webview_position, position, - tauri_runtime::window::dpi::PhysicalPosition - ); - getter!( - webview_size, - size, - tauri_runtime::window::dpi::PhysicalSize + tauri_runtime::dpi::PhysicalPosition ); + getter!(webview_size, size, tauri_runtime::dpi::PhysicalSize); //getter!(is_focused, bool); setter!(print); @@ -158,6 +157,20 @@ mod desktop_commands { setter!(set_webview_size, set_size, Size); setter!(set_webview_position, set_position, Position); setter!(set_webview_focus, set_focus); + setter!(set_webview_zoom, set_zoom, f64); + + #[command(root = "crate")] + pub async fn reparent( + webview: crate::Webview, + label: Option, + window: String, + ) -> crate::Result<()> { + let webview = get_webview(webview, label)?; + if let Some(window) = webview.manager.get_window(&window) { + webview.reparent(&window)?; + } + Ok(()) + } #[cfg(any(debug_assertions, feature = "devtools"))] #[command(root = "crate")] @@ -226,7 +239,9 @@ pub fn init() -> TauriPlugin { desktop_commands::set_webview_size, desktop_commands::set_webview_position, desktop_commands::set_webview_focus, + desktop_commands::set_webview_zoom, desktop_commands::print, + desktop_commands::reparent, #[cfg(any(debug_assertions, feature = "devtools"))] desktop_commands::internal_toggle_devtools, ]); diff --git a/core/tauri/src/webview/scripts/print.js b/core/tauri/src/webview/scripts/print.js index 55ffdb388..df5b92a1b 100644 --- a/core/tauri/src/webview/scripts/print.js +++ b/core/tauri/src/webview/scripts/print.js @@ -1,4 +1,4 @@ -// Copyright 2019-2023 Tauri Programme within The Commons Conservancy +// Copyright 2019-2024 Tauri Programme within The Commons Conservancy // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT diff --git a/core/tauri/src/webview/scripts/toggle-devtools.js b/core/tauri/src/webview/scripts/toggle-devtools.js index 2f949c785..5ef423cae 100644 --- a/core/tauri/src/webview/scripts/toggle-devtools.js +++ b/core/tauri/src/webview/scripts/toggle-devtools.js @@ -1,4 +1,4 @@ -// Copyright 2019-2023 Tauri Programme within The Commons Conservancy +// Copyright 2019-2024 Tauri Programme within The Commons Conservancy // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT diff --git a/core/tauri/src/webview/scripts/zoom-hotkey.js b/core/tauri/src/webview/scripts/zoom-hotkey.js new file mode 100644 index 000000000..f3ff01cdd --- /dev/null +++ b/core/tauri/src/webview/scripts/zoom-hotkey.js @@ -0,0 +1,28 @@ +// Copyright 2019-2024 Tauri Programme within The Commons Conservancy +// SPDX-License-Identifier: Apache-2.0 +// SPDX-License-Identifier: MIT + +const OS_NAME = __TEMPLATE_os_name__ + +let zoomLevel = 1 + +const MAX_ZOOM_LEVEL = 10 +const MIN_ZOOM_LEVEL = 0.2 + +window.addEventListener('keydown', (event) => { + if (OS_NAME === 'macos' ? event.metaKey : event.ctrlKey) { + if (event.key === '-') { + zoomLevel -= 0.2 + } else if (event.key === '=') { + zoomLevel += 0.2 + } else if (event.key === '0') { + zoomLevel = 1 + } else { + return + } + zoomLevel = Math.min(Math.max(zoomLevel, MIN_ZOOM_LEVEL), MAX_ZOOM_LEVEL) + window.__TAURI_INTERNALS__.invoke('plugin:webview|set_webview_zoom', { + value: zoomLevel + }) + } +}) diff --git a/core/tauri/src/webview/webview_window.rs b/core/tauri/src/webview/webview_window.rs index 69f66eba6..62b2bf0ae 100644 --- a/core/tauri/src/webview/webview_window.rs +++ b/core/tauri/src/webview/webview_window.rs @@ -1,27 +1,30 @@ -// Copyright 2019-2023 Tauri Programme within The Commons Conservancy +// Copyright 2019-2024 Tauri Programme within The Commons Conservancy // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT //! [`Window`] that hosts a single [`Webview`]. -use std::{borrow::Cow, path::PathBuf, sync::Arc}; +use std::{ + borrow::Cow, + path::PathBuf, + sync::{Arc, MutexGuard}, +}; use crate::{ event::EventTarget, - runtime::window::dpi::{PhysicalPosition, PhysicalSize}, + runtime::dpi::{PhysicalPosition, PhysicalSize}, window::Monitor, + ResourceTable, }; #[cfg(desktop)] use crate::{ + image::Image, menu::{ContextMenu, Menu}, runtime::{ - window::{ - dpi::{Position, Size}, - CursorIcon, - }, + dpi::{Position, Size}, + window::CursorIcon, UserAttentionType, }, - Icon, }; use tauri_utils::config::{WebviewUrl, WindowConfig}; use url::Url; @@ -529,7 +532,7 @@ impl<'a, R: Runtime, M: Manager> WebviewWindowBuilder<'a, R, M> { } /// Sets the window icon. - pub fn icon(mut self, icon: crate::Icon) -> crate::Result { + pub fn icon(mut self, icon: Image<'a>) -> crate::Result { self.window_builder = self.window_builder.icon(icon)?; Ok(self) } @@ -572,7 +575,7 @@ impl<'a, R: Runtime, M: Manager> WebviewWindowBuilder<'a, R, M> { /// - **Linux**: This makes the new window transient for parent, see /// - **macOS**: This adds the window as a child of parent, see pub fn parent(mut self, parent: &WebviewWindow) -> crate::Result { - self.window_builder = self.window_builder.parent(&parent.webview.window)?; + self.window_builder = self.window_builder.parent(&parent.webview.window())?; Ok(self) } @@ -586,7 +589,7 @@ impl<'a, R: Runtime, M: Manager> WebviewWindowBuilder<'a, R, M> { /// For more information, see #[cfg(windows)] pub fn owner(mut self, owner: &WebviewWindow) -> crate::Result { - self.window_builder = self.window_builder.owner(&owner.webview.window)?; + self.window_builder = self.window_builder.owner(&owner.webview.window())?; Ok(self) } @@ -638,7 +641,9 @@ impl<'a, R: Runtime, M: Manager> WebviewWindowBuilder<'a, R, M> { target_os = "openbsd" ))] pub fn transient_for(mut self, parent: &WebviewWindow) -> crate::Result { - self.window_builder = self.window_builder.transient_for(&parent.webview.window)?; + self.window_builder = self + .window_builder + .transient_for(&parent.webview.window())?; Ok(self) } @@ -790,10 +795,10 @@ fn main() { self } - /// Disables the file drop handler. This is required to use drag and drop APIs on the front end on Windows. + /// Disables the drag and drop handler. This is required to use HTML5 drag and drop APIs on the frontend on Windows. #[must_use] - pub fn disable_file_drop_handler(mut self) -> Self { - self.webview_builder = self.webview_builder.disable_file_drop_handler(); + pub fn disable_drag_drop_handler(mut self) -> Self { + self.webview_builder = self.webview_builder.disable_drag_drop_handler(); self } @@ -833,6 +838,21 @@ fn main() { self.webview_builder = self.webview_builder.proxy_url(url); self } + + /// Whether page zooming by hotkeys is enabled + /// + /// ## Platform-specific: + /// + /// - **Windows**: Controls WebView2's [`IsZoomControlEnabled`](https://learn.microsoft.com/en-us/microsoft-edge/webview2/reference/winrt/microsoft_web_webview2_core/corewebview2settings?view=webview2-winrt-1.0.2420.47#iszoomcontrolenabled) setting. + /// - **MacOS / Linux**: Injects a polyfill that zooms in and out with `ctrl/command` + `-/=`, + /// 20% in each step, ranging from 20% to 1000%. Requires `webview:allow-set-webview-zoom` permission + /// + /// - **Android / iOS**: Unsupported. + #[must_use] + pub fn zoom_hotkeys_enabled(mut self, enabled: bool) -> Self { + self.webview_builder = self.webview_builder.zoom_hotkeys_enabled(enabled); + self + } } /// A type that wraps a [`Window`] together with a [`Webview`]. @@ -868,7 +888,17 @@ impl raw_window_handle::HasWindowHandle for WebviewWindow { fn window_handle( &self, ) -> std::result::Result, raw_window_handle::HandleError> { - self.webview.window().window_handle() + Ok(unsafe { + raw_window_handle::WindowHandle::borrow_raw(self.webview.window().window_handle()?.as_raw()) + }) + } +} + +impl raw_window_handle::HasDisplayHandle for WebviewWindow { + fn display_handle( + &self, + ) -> std::result::Result, raw_window_handle::HandleError> { + self.webview.app_handle.display_handle() } } @@ -876,7 +906,7 @@ impl<'de, R: Runtime> CommandArg<'de, R> for WebviewWindow { /// Grabs the [`Window`] from the [`CommandItem`]. This will never fail. fn from_command(command: CommandItem<'de, R>) -> Result { let webview = command.message.webview(); - if webview.window().webview_window { + if webview.window().is_webview_window() { Ok(Self { webview }) } else { Err(InvokeError::from_anyhow(anyhow::anyhow!( @@ -1239,7 +1269,7 @@ impl WebviewWindow { self.webview.window().set_maximizable(maximizable) } - /// Determines if this window's native minize button should be enabled. + /// Determines if this window's native minimize button should be enabled. /// /// ## Platform-specific /// @@ -1416,7 +1446,7 @@ impl WebviewWindow { } /// Sets this window' icon. - pub fn set_icon(&self, icon: Icon) -> crate::Result<()> { + pub fn set_icon(&self, icon: Image<'_>) -> crate::Result<()> { self.webview.window().set_icon(icon) } @@ -1484,7 +1514,7 @@ impl WebviewWindow { /// - **iOS / Android:** Unsupported. pub fn set_progress_bar( &self, - progress_state: crate::utils::ProgressBarState, + progress_state: crate::window::ProgressBarState, ) -> crate::Result<()> { self.webview.window().set_progress_bar(progress_state) } @@ -1691,6 +1721,17 @@ tauri::Builder::default() pub fn is_devtools_open(&self) -> bool { self.webview.is_devtools_open() } + + /// Set the webview zoom level + /// + /// ## Platform-specific: + /// + /// - **Android**: Not supported. + /// - **macOS**: available on macOS 11+ only. + /// - **iOS**: available on iOS 14+ only. + pub fn set_zoom(&self, scale_factor: f64) -> crate::Result<()> { + self.webview.set_zoom(scale_factor) + } } /// Event system APIs. @@ -1766,7 +1807,7 @@ tauri::Builder::default() /// Listen to an event on this window webview only once. /// /// See [`Self::listen`] for more information. - pub fn once(&self, event: impl Into, handler: F) + pub fn once(&self, event: impl Into, handler: F) -> EventId where F: FnOnce(Event) + Send + 'static, { @@ -1780,7 +1821,15 @@ tauri::Builder::default() } } -impl Manager for WebviewWindow {} +impl Manager for WebviewWindow { + fn resources_table(&self) -> MutexGuard<'_, ResourceTable> { + self + .webview + .resources_table + .lock() + .expect("poisoned window resources table") + } +} impl ManagerBase for WebviewWindow { fn manager(&self) -> &AppManager { diff --git a/core/tauri/src/window/mod.rs b/core/tauri/src/window/mod.rs index fcfe93001..9df2fc711 100644 --- a/core/tauri/src/window/mod.rs +++ b/core/tauri/src/window/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2019-2023 Tauri Programme within The Commons Conservancy +// Copyright 2019-2024 Tauri Programme within The Commons Conservancy // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT @@ -7,11 +7,14 @@ pub(crate) mod plugin; use tauri_runtime::{ + dpi::{PhysicalPosition, PhysicalSize}, webview::PendingWebview, - window::dpi::{PhysicalPosition, PhysicalSize}, }; pub use tauri_utils::{config::Color, WindowEffect as Effect, WindowEffectState as EffectState}; +#[cfg(desktop)] +pub use crate::runtime::ProgressBarStatus; + use crate::{ app::AppHandle, event::{Event, EventId, EventTarget}, @@ -22,20 +25,20 @@ use crate::{ window::{DetachedWindow, PendingWindow, WindowBuilder as _}, RuntimeHandle, WindowDispatch, }, - sealed::ManagerBase, - sealed::RuntimeOrDispatch, + sealed::{ManagerBase, RuntimeOrDispatch}, utils::config::{WindowConfig, WindowEffectsConfig}, webview::WebviewBuilder, - EventLoopMessage, Manager, Runtime, Theme, Webview, WindowEvent, + EventLoopMessage, Manager, ResourceTable, Runtime, Theme, Webview, WindowEvent, }; #[cfg(desktop)] use crate::{ + image::Image, menu::{ContextMenu, Menu, MenuId}, runtime::{ - window::dpi::{Position, Size}, + dpi::{Position, Size}, UserAttentionType, }, - CursorIcon, Icon, + CursorIcon, }; use serde::Serialize; @@ -47,7 +50,7 @@ use tauri_macros::default_runtime; use std::{ fmt, hash::{Hash, Hasher}, - sync::Arc, + sync::{Arc, Mutex, MutexGuard}, }; /// Monitor descriptor. @@ -333,7 +336,7 @@ tauri::Builder::default() .webviews_lock() .values() .map(|w| WebviewLabelDef { - window_label: w.window.label().to_string(), + window_label: w.window().label().to_string(), label: w.label().to_string(), }) .collect::>(); @@ -401,7 +404,6 @@ tauri::Builder::default() let window = app_manager.window.attach_window( self.manager.app_handle().clone(), detached_window.clone(), - detached_window.webview.is_some(), #[cfg(desktop)] window_menu, ); @@ -422,6 +424,18 @@ tauri::Builder::default() crate::vibrancy::set_window_effects(&window, Some(effects))?; } + app_manager.webview.eval_script_all(format!( + "window.__TAURI_INTERNALS__.metadata.windows = {window_labels_array}.map(function (label) {{ return {{ label: label }} }})", + window_labels_array = serde_json::to_string(&app_manager.window.labels())?, + ))?; + + app_manager.emit( + "tauri://window-created", + Some(crate::webview::CreatedEvent { + label: window.label().into(), + }), + )?; + Ok(window) } } @@ -629,8 +643,8 @@ impl<'a, R: Runtime, M: Manager> WindowBuilder<'a, R, M> { } /// Sets the window icon. - pub fn icon(mut self, icon: Icon) -> crate::Result { - self.window_builder = self.window_builder.icon(icon.try_into()?)?; + pub fn icon(mut self, icon: Image<'a>) -> crate::Result { + self.window_builder = self.window_builder.icon(icon.into())?; Ok(self) } @@ -651,7 +665,7 @@ impl<'a, R: Runtime, M: Manager> WindowBuilder<'a, R, M> { /// /// - **Windows:** /// - `false` has no effect on decorated window, shadows are always ON. - /// - `true` will make ndecorated window have a 1px white border, + /// - `true` will make undecorated window have a 1px white border, /// and on Windows 11, it will have a rounded corners. /// - **Linux:** Unsupported. #[must_use] @@ -861,9 +875,8 @@ pub struct Window { pub(crate) app_handle: AppHandle, // The menu set for this window #[cfg(desktop)] - pub(crate) menu: Arc>>>, - /// Whether this window is a Webview window (hosts only a single webview) or a container for multiple webviews - pub(crate) webview_window: bool, + pub(crate) menu: Arc>>>, + pub(crate) resources_table: Arc>, } impl std::fmt::Debug for Window { @@ -872,7 +885,6 @@ impl std::fmt::Debug for Window { .field("window", &self.window) .field("manager", &self.manager) .field("app_handle", &self.app_handle) - .field("webview_window", &self.webview_window) .finish() } } @@ -885,6 +897,14 @@ impl raw_window_handle::HasWindowHandle for Window { } } +impl raw_window_handle::HasDisplayHandle for Window { + fn display_handle( + &self, + ) -> std::result::Result, raw_window_handle::HandleError> { + self.app_handle.display_handle() + } +} + impl Clone for Window { fn clone(&self) -> Self { Self { @@ -893,7 +913,7 @@ impl Clone for Window { app_handle: self.app_handle.clone(), #[cfg(desktop)] menu: self.menu.clone(), - webview_window: self.webview_window, + resources_table: self.resources_table.clone(), } } } @@ -913,7 +933,14 @@ impl PartialEq for Window { } } -impl Manager for Window {} +impl Manager for Window { + fn resources_table(&self) -> MutexGuard<'_, ResourceTable> { + self + .resources_table + .lock() + .expect("poisoned window resources table") + } +} impl ManagerBase for Window { fn manager(&self) -> &AppManager { @@ -948,7 +975,6 @@ impl Window { window: DetachedWindow, app_handle: AppHandle, #[cfg(desktop)] menu: Option>, - webview_window: bool, ) -> Self { Self { window, @@ -956,7 +982,7 @@ impl Window { app_handle, #[cfg(desktop)] menu: Arc::new(std::sync::Mutex::new(menu)), - webview_window, + resources_table: Default::default(), } } @@ -978,7 +1004,17 @@ impl Window { position: P, size: S, ) -> crate::Result> { - webview_builder.build(self.clone(), position.into(), size.into()) + use std::sync::mpsc::channel; + + let (tx, rx) = channel(); + let position = position.into(); + let size = size.into(); + let window_ = self.clone(); + self.run_on_main_thread(move || { + let res = webview_builder.build(window_, position, size); + tx.send(res.map_err(Into::into)).unwrap(); + })?; + rx.recv().unwrap() } /// List of webviews associated with this window. @@ -988,11 +1024,15 @@ impl Window { .webview .webviews_lock() .values() - .filter(|w| w.window() == self) + .filter(|w| w.window_label() == self.label()) .cloned() .collect() } + pub(crate) fn is_webview_window(&self) -> bool { + self.webviews().iter().all(|w| w.label() == self.label()) + } + /// Runs the given closure on the main thread. pub fn run_on_main_thread(&self, f: F) -> crate::Result<()> { self @@ -1563,7 +1603,7 @@ impl Window { .map_err(Into::into) } - /// Determines if this window's native minize button should be enabled. + /// Determines if this window's native minimize button should be enabled. /// /// ## Platform-specific /// @@ -1657,7 +1697,7 @@ impl Window { /// /// - **Windows:** /// - `false` has no effect on decorated window, shadow are always ON. - /// - `true` will make ndecorated window have a 1px white border, + /// - `true` will make undecorated window have a 1px white border, /// and on Windows 11, it will have a rounded corners. /// - **Linux:** Unsupported. pub fn set_shadow(&self, enable: bool) -> crate::Result<()> { @@ -1802,11 +1842,11 @@ tauri::Builder::default() } /// Sets this window' icon. - pub fn set_icon(&self, icon: Icon) -> crate::Result<()> { + pub fn set_icon(&self, icon: Image<'_>) -> crate::Result<()> { self .window .dispatcher - .set_icon(icon.try_into()?) + .set_icon(icon.into()) .map_err(Into::into) } @@ -1908,18 +1948,42 @@ tauri::Builder::default() /// - **Linux / macOS**: Progress bar is app-wide and not specific to this window. /// - **Linux**: Only supported desktop environments with `libunity` (e.g. GNOME). /// - **iOS / Android:** Unsupported. - pub fn set_progress_bar( - &self, - progress_state: crate::utils::ProgressBarState, - ) -> crate::Result<()> { + pub fn set_progress_bar(&self, progress_state: ProgressBarState) -> crate::Result<()> { self .window .dispatcher - .set_progress_bar(progress_state) + .set_progress_bar(crate::runtime::ProgressBarState { + status: progress_state.status, + progress: progress_state.progress, + desktop_filename: Some(format!( + "{}.desktop", + heck::AsKebabCase( + self + .config() + .product_name + .as_deref() + .unwrap_or_else(|| self.package_info().crate_name) + ) + )), + }) .map_err(Into::into) } } +/// Progress bar state. +#[cfg(desktop)] +#[cfg_attr( + docsrs, + doc(cfg(any(target_os = "macos", target_os = "linux", windows))) +)] +#[derive(serde::Deserialize)] +pub struct ProgressBarState { + /// The progress bar status. + pub status: Option, + /// The progress bar progress. This can be a value ranging from `0` to `100` + pub progress: Option, +} + /// Event system APIs. impl Window { /// Listen to an event on this window. @@ -1992,7 +2056,7 @@ tauri::Builder::default() /// Listen to an event on this window only once. /// /// See [`Self::listen`] for more information. - pub fn once(&self, event: impl Into, handler: F) + pub fn once(&self, event: impl Into, handler: F) -> EventId where F: FnOnce(Event) + Send + 'static, { diff --git a/core/tauri/src/window/plugin.rs b/core/tauri/src/window/plugin.rs index 33baf1a4e..639dee9a2 100644 --- a/core/tauri/src/window/plugin.rs +++ b/core/tauri/src/window/plugin.rs @@ -1,4 +1,4 @@ -// Copyright 2019-2023 Tauri Programme within The Commons Conservancy +// Copyright 2019-2024 Tauri Programme within The Commons Conservancy // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT @@ -11,54 +11,18 @@ use crate::{ #[cfg(desktop)] mod desktop_commands { - use serde::Deserialize; use tauri_runtime::ResizeDirection; - use tauri_utils::ProgressBarState; use super::*; use crate::{ command, sealed::ManagerBase, utils::config::{WindowConfig, WindowEffectsConfig}, - window::WindowBuilder, - AppHandle, CursorIcon, Icon, Monitor, PhysicalPosition, PhysicalSize, Position, Size, Theme, - UserAttentionType, Window, + window::{ProgressBarState, WindowBuilder}, + AppHandle, CursorIcon, Monitor, PhysicalPosition, PhysicalSize, Position, Size, Theme, + UserAttentionType, Webview, Window, }; - #[derive(Deserialize)] - #[serde(untagged)] - pub enum IconDto { - #[cfg(any(feature = "icon-png", feature = "icon-ico"))] - File(std::path::PathBuf), - #[cfg(any(feature = "icon-png", feature = "icon-ico"))] - Raw(Vec), - Rgba { - rgba: Vec, - width: u32, - height: u32, - }, - } - - impl From for Icon { - fn from(icon: IconDto) -> Self { - match icon { - #[cfg(any(feature = "icon-png", feature = "icon-ico"))] - IconDto::File(path) => Self::File(path), - #[cfg(any(feature = "icon-png", feature = "icon-ico"))] - IconDto::Raw(raw) => Self::Raw(raw), - IconDto::Rgba { - rgba, - width, - height, - } => Self::Rgba { - rgba, - width, - height, - }, - } - } - } - #[command(root = "crate")] pub async fn create(app: AppHandle, options: WindowConfig) -> crate::Result<()> { WindowBuilder::from_config(&app, &options)?.build()?; @@ -168,12 +132,14 @@ mod desktop_commands { #[command(root = "crate")] pub async fn set_icon( + webview: Webview, window: Window, label: Option, - value: IconDto, + value: crate::image::JsImage, ) -> crate::Result<()> { - get_window(window, label)? - .set_icon(value.into()) + let window = get_window(window, label)?; + window + .set_icon(value.into_img(&webview)?.as_ref().clone()) .map_err(Into::into) } @@ -204,115 +170,6 @@ mod desktop_commands { } Ok(()) } - - #[derive(Debug)] - enum HitTestResult { - Client, - Left, - Right, - Top, - Bottom, - TopLeft, - TopRight, - BottomLeft, - BottomRight, - NoWhere, - } - - impl HitTestResult { - fn drag_resize_window(&self, window: &Window) { - let _ = window.start_resize_dragging(match self { - HitTestResult::Left => ResizeDirection::West, - HitTestResult::Right => ResizeDirection::East, - HitTestResult::Top => ResizeDirection::North, - HitTestResult::Bottom => ResizeDirection::South, - HitTestResult::TopLeft => ResizeDirection::NorthWest, - HitTestResult::TopRight => ResizeDirection::NorthEast, - HitTestResult::BottomLeft => ResizeDirection::SouthWest, - HitTestResult::BottomRight => ResizeDirection::SouthEast, - _ => unreachable!(), - }); - } - - fn change_cursor(&self, window: &Window) { - let _ = window.set_cursor_icon(match self { - HitTestResult::Left => CursorIcon::WResize, - HitTestResult::Right => CursorIcon::EResize, - HitTestResult::Top => CursorIcon::NResize, - HitTestResult::Bottom => CursorIcon::SResize, - HitTestResult::TopLeft => CursorIcon::NwResize, - HitTestResult::TopRight => CursorIcon::NeResize, - HitTestResult::BottomLeft => CursorIcon::SwResize, - HitTestResult::BottomRight => CursorIcon::SeResize, - _ => CursorIcon::Default, - }); - } - } - - fn hit_test(window_size: PhysicalSize, x: i32, y: i32, scale: f64) -> HitTestResult { - const BORDERLESS_RESIZE_INSET: f64 = 5.0; - - const CLIENT: isize = 0b0000; - const LEFT: isize = 0b0001; - const RIGHT: isize = 0b0010; - const TOP: isize = 0b0100; - const BOTTOM: isize = 0b1000; - const TOPLEFT: isize = TOP | LEFT; - const TOPRIGHT: isize = TOP | RIGHT; - const BOTTOMLEFT: isize = BOTTOM | LEFT; - const BOTTOMRIGHT: isize = BOTTOM | RIGHT; - - let top = 0; - let left = 0; - let bottom = top + window_size.height as i32; - let right = left + window_size.width as i32; - - let inset = (BORDERLESS_RESIZE_INSET * scale) as i32; - - #[rustfmt::skip] - let result = - (LEFT * (if x < (left + inset) { 1 } else { 0 })) - | (RIGHT * (if x >= (right - inset) { 1 } else { 0 })) - | (TOP * (if y < (top + inset) { 1 } else { 0 })) - | (BOTTOM * (if y >= (bottom - inset) { 1 } else { 0 })); - - match result { - CLIENT => HitTestResult::Client, - LEFT => HitTestResult::Left, - RIGHT => HitTestResult::Right, - TOP => HitTestResult::Top, - BOTTOM => HitTestResult::Bottom, - TOPLEFT => HitTestResult::TopLeft, - TOPRIGHT => HitTestResult::TopRight, - BOTTOMLEFT => HitTestResult::BottomLeft, - BOTTOMRIGHT => HitTestResult::BottomRight, - _ => HitTestResult::NoWhere, - } - } - - #[command(root = "crate")] - pub async fn internal_on_mousemove( - window: Window, - x: i32, - y: i32, - ) -> crate::Result<()> { - hit_test(window.inner_size()?, x, y, window.scale_factor()?).change_cursor(&window); - Ok(()) - } - - #[command(root = "crate")] - pub async fn internal_on_mousedown( - window: Window, - x: i32, - y: i32, - ) -> crate::Result<()> { - let res = hit_test(window.inner_size()?, x, y, window.scale_factor()?); - match res { - HitTestResult::Client | HitTestResult::NoWhere => {} - _ => res.drag_resize_window(&window), - }; - Ok(()) - } } /// Initializes the plugin. @@ -336,21 +193,6 @@ pub fn init() -> TauriPlugin { .into_string(), ); - #[derive(Template)] - #[default_template("./scripts/undecorated-resizing.js")] - struct UndecoratedResizingJavascript<'a> { - os_name: &'a str, - } - - init_script.push_str( - &UndecoratedResizingJavascript { - os_name: std::env::consts::OS, - } - .render_default(&Default::default()) - .unwrap() - .into_string(), - ); - Builder::new("window") .js_init_script(init_script) .invoke_handler(|invoke| { @@ -421,8 +263,6 @@ pub fn init() -> TauriPlugin { desktop_commands::set_visible_on_all_workspaces, desktop_commands::toggle_maximize, desktop_commands::internal_toggle_maximize, - desktop_commands::internal_on_mousemove, - desktop_commands::internal_on_mousedown, ]); handler(invoke) } diff --git a/core/tauri/src/window/scripts/drag.js b/core/tauri/src/window/scripts/drag.js index bbaa3711f..a247a41bb 100644 --- a/core/tauri/src/window/scripts/drag.js +++ b/core/tauri/src/window/scripts/drag.js @@ -1,4 +1,4 @@ -// Copyright 2019-2023 Tauri Programme within The Commons Conservancy +// Copyright 2019-2024 Tauri Programme within The Commons Conservancy // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT diff --git a/core/tauri/src/window/scripts/undecorated-resizing.js b/core/tauri/src/window/scripts/undecorated-resizing.js deleted file mode 100644 index f56f1ade3..000000000 --- a/core/tauri/src/window/scripts/undecorated-resizing.js +++ /dev/null @@ -1,21 +0,0 @@ -// Copyright 2019-2023 Tauri Programme within The Commons Conservancy -// SPDX-License-Identifier: Apache-2.0 -// SPDX-License-Identifier: MIT - -;(function () { - const osName = __TEMPLATE_os_name__ - if (osName !== 'macos' && osName !== 'ios' && osName !== 'android') { - document.addEventListener('mousemove', (e) => { - window.__TAURI_INTERNALS__.invoke('plugin:window|internal_on_mousemove', { - x: e.clientX, - y: e.clientY - }) - }) - document.addEventListener('mousedown', (e) => { - window.__TAURI_INTERNALS__.invoke('plugin:window|internal_on_mousedown', { - x: e.clientX, - y: e.clientY - }) - }) - } -})() diff --git a/core/tests/acl/fixtures/capabilities/file-explorer-remote/cap.toml b/core/tests/acl/fixtures/capabilities/file-explorer-remote/cap.toml index ebc8353d3..242461ce8 100644 --- a/core/tests/acl/fixtures/capabilities/file-explorer-remote/cap.toml +++ b/core/tests/acl/fixtures/capabilities/file-explorer-remote/cap.toml @@ -2,5 +2,6 @@ identifier = "run-app" description = "app capability" windows = ["main"] permissions = ["fs:read", "fs:allow-app"] -[context.remote] -domains = ["tauri.app"] +local = false +[remote] +urls = ["https://tauri.app"] diff --git a/core/tests/acl/fixtures/capabilities/multiwebview/cap.toml b/core/tests/acl/fixtures/capabilities/multiwebview/cap.toml new file mode 100644 index 000000000..eada13bee --- /dev/null +++ b/core/tests/acl/fixtures/capabilities/multiwebview/cap.toml @@ -0,0 +1,5 @@ +identifier = "run-app" +description = "app capability" +windows = ["main"] +webviews = ["child1", "child2"] +permissions = ["ping:allow-ping"] diff --git a/core/tests/acl/fixtures/capabilities/multiwebview/required-plugins.json b/core/tests/acl/fixtures/capabilities/multiwebview/required-plugins.json new file mode 100644 index 000000000..4239d5ee3 --- /dev/null +++ b/core/tests/acl/fixtures/capabilities/multiwebview/required-plugins.json @@ -0,0 +1 @@ +["ping"] diff --git a/core/tests/acl/fixtures/capabilities/multiwindow/cap-external.toml b/core/tests/acl/fixtures/capabilities/multiwindow/cap-external.toml new file mode 100644 index 000000000..212fa3abb --- /dev/null +++ b/core/tests/acl/fixtures/capabilities/multiwindow/cap-external.toml @@ -0,0 +1,7 @@ +identifier = "run-app-external-url" +description = "external window capability" +windows = ["external"] +permissions = [ + "fs:read", + "fs:deny-home" +] diff --git a/core/tests/acl/fixtures/capabilities/multiwindow/cap-main.json b/core/tests/acl/fixtures/capabilities/multiwindow/cap-main.json new file mode 100644 index 000000000..f3fbf1570 --- /dev/null +++ b/core/tests/acl/fixtures/capabilities/multiwindow/cap-main.json @@ -0,0 +1,20 @@ +{ + "identifier": "run-app", + "description": "ap capability", + "windows": ["main"], + "permissions": [ + { + "identifier": "fs:read", + "allow": [ + { + "path": "$CONFIG/*" + } + ] + }, + "fs:allow-app", + "fs:deny-home", + "fs:allow-read-resources", + "fs:allow-move-temp", + "fs:read-download-dir" + ] +} \ No newline at end of file diff --git a/core/tests/acl/fixtures/capabilities/multiwindow/required-plugins.json b/core/tests/acl/fixtures/capabilities/multiwindow/required-plugins.json new file mode 100644 index 000000000..9ab323181 --- /dev/null +++ b/core/tests/acl/fixtures/capabilities/multiwindow/required-plugins.json @@ -0,0 +1 @@ +["fs"] diff --git a/core/tests/acl/fixtures/capabilities/platform-specific-permissions/cap.toml b/core/tests/acl/fixtures/capabilities/platform-specific-permissions/cap.toml new file mode 100644 index 000000000..f39ea410e --- /dev/null +++ b/core/tests/acl/fixtures/capabilities/platform-specific-permissions/cap.toml @@ -0,0 +1,9 @@ +identifier = "run-app" +description = "app capability" +windows = ["main"] +permissions = [ + "os:allow-apt-linux", + "os:allow-library-folder-macos", + "os:deny-webview-folder-windows", + "os:open-browser", +] diff --git a/core/tests/acl/fixtures/capabilities/platform-specific-permissions/required-plugins.json b/core/tests/acl/fixtures/capabilities/platform-specific-permissions/required-plugins.json new file mode 100644 index 000000000..349a761c0 --- /dev/null +++ b/core/tests/acl/fixtures/capabilities/platform-specific-permissions/required-plugins.json @@ -0,0 +1 @@ +["os"] diff --git a/core/tests/acl/fixtures/plugins/os/linux.toml b/core/tests/acl/fixtures/plugins/os/linux.toml new file mode 100644 index 000000000..89787e121 --- /dev/null +++ b/core/tests/acl/fixtures/plugins/os/linux.toml @@ -0,0 +1,7 @@ +[[permission]] +identifier = "allow-apt-linux" +platforms = ["linux"] +description = "Allows spawning the apt command on Linux" +commands.allow = ["spawn"] +[[permission.scope.allow]] +command = "apt" diff --git a/core/tests/acl/fixtures/plugins/os/macos.toml b/core/tests/acl/fixtures/plugins/os/macos.toml new file mode 100644 index 000000000..1b59b0b1b --- /dev/null +++ b/core/tests/acl/fixtures/plugins/os/macos.toml @@ -0,0 +1,7 @@ + +[[permission]] +identifier = "allow-library-folder-macos" +platforms = ["macOS"] +description = "Allows access to the $HOME/Library folder on maOS" +[[permission.scope.allow]] +path = "$HOME/Library/**" diff --git a/core/tests/acl/fixtures/plugins/os/open-browser.toml b/core/tests/acl/fixtures/plugins/os/open-browser.toml new file mode 100644 index 000000000..b72436924 --- /dev/null +++ b/core/tests/acl/fixtures/plugins/os/open-browser.toml @@ -0,0 +1,28 @@ +[[permission]] +identifier = "allow-servo-linux" +platforms = ["linux"] +description = "Allows starting servo on Linux" +commands.allow = ["spawn"] +[[permission.scope.allow]] +command = "servo" + +[[permission]] +identifier = "allow-edge-windows" +platforms = ["windows"] +description = "Allows starting edge on Windows" +commands.allow = ["spawn"] +[[permission.scope.allow]] +command = "edge" + +[[permission]] +identifier = "allow-safari-macos" +platforms = ["macOS"] +description = "Allows starting safari on macOS" +commands.allow = ["spawn"] +[[permission.scope.allow]] +command = "safari" + +[[set]] +identifier = "open-browser" +description = "allows opening a URL on the platform browser" +permissions = ["allow-servo-linux", "allow-edge-windows", "allow-safari-macos"] diff --git a/core/tests/acl/fixtures/plugins/os/windows.toml b/core/tests/acl/fixtures/plugins/os/windows.toml new file mode 100644 index 000000000..577b0c967 --- /dev/null +++ b/core/tests/acl/fixtures/plugins/os/windows.toml @@ -0,0 +1,6 @@ +[[permission]] +identifier = "deny-webview-folder-windows" +platforms = ["windows"] +description = "Denies access to the webview folder on Windows" +[[permission.scope.deny]] +path = "$APP/EBWebView/**" diff --git a/core/tests/acl/fixtures/snapshots/acl_tests__tests__basic-ping.snap b/core/tests/acl/fixtures/snapshots/acl_tests__tests__basic-ping.snap index afb562c98..bdedb5063 100644 --- a/core/tests/acl/fixtures/snapshots/acl_tests__tests__basic-ping.snap +++ b/core/tests/acl/fixtures/snapshots/acl_tests__tests__basic-ping.snap @@ -1,36 +1,36 @@ --- source: core/tests/acl/src/lib.rs -assertion_line: 59 expression: resolved --- Resolved { allowed_commands: { - CommandKey { - name: "plugin:ping|ping", - context: Local, - }: ResolvedCommand { - windows: [ - Pattern { - original: "main", - tokens: [ - Char( - 'm', - ), - Char( - 'a', - ), - Char( - 'i', - ), - Char( - 'n', - ), - ], - is_recursive: false, - }, - ], - scope: None, - }, + "plugin:ping|ping": [ + ResolvedCommand { + context: Local, + windows: [ + Pattern { + original: "main", + tokens: [ + Char( + 'm', + ), + Char( + 'a', + ), + Char( + 'i', + ), + Char( + 'n', + ), + ], + is_recursive: false, + }, + ], + webviews: [], + scope_id: None, + }, + ], }, denied_commands: {}, command_scope: {}, diff --git a/core/tests/acl/fixtures/snapshots/acl_tests__tests__file-explorer-remote.snap b/core/tests/acl/fixtures/snapshots/acl_tests__tests__file-explorer-remote.snap index 2e1664d70..cf1f7ea44 100644 --- a/core/tests/acl/fixtures/snapshots/acl_tests__tests__file-explorer-remote.snap +++ b/core/tests/acl/fixtures/snapshots/acl_tests__tests__file-explorer-remote.snap @@ -1,130 +1,349 @@ --- source: core/tests/acl/src/lib.rs -assertion_line: 59 expression: resolved --- Resolved { allowed_commands: { - CommandKey { - name: "plugin:fs|read_dir", - context: Remote { - domain: Pattern { - original: "tauri.app", - tokens: [ - Char( - 't', - ), - Char( - 'a', - ), - Char( - 'u', - ), - Char( - 'r', - ), - Char( - 'i', - ), - Char( - '.', - ), - Char( - 'a', - ), - Char( - 'p', - ), - Char( - 'p', - ), - ], - is_recursive: false, + "plugin:fs|read_dir": [ + ResolvedCommand { + context: Remote { + url: RemoteUrlPattern( + UrlPattern { + protocol: Component { + pattern_string: "https", + regexp: Ok( + Regex( + "^https$", + ), + ), + group_name_list: [], + matcher: Matcher { + prefix: "", + suffix: "", + inner: Literal { + literal: "https", + }, + }, + }, + username: Component { + pattern_string: "", + regexp: Ok( + Regex( + "^$", + ), + ), + group_name_list: [], + matcher: Matcher { + prefix: "", + suffix: "", + inner: Literal { + literal: "", + }, + }, + }, + password: Component { + pattern_string: "", + regexp: Ok( + Regex( + "^$", + ), + ), + group_name_list: [], + matcher: Matcher { + prefix: "", + suffix: "", + inner: Literal { + literal: "", + }, + }, + }, + hostname: Component { + pattern_string: "tauri.app", + regexp: Ok( + Regex( + "^tauri\\.app$", + ), + ), + group_name_list: [], + matcher: Matcher { + prefix: "", + suffix: "", + inner: Literal { + literal: "tauri.app", + }, + }, + }, + port: Component { + pattern_string: "", + regexp: Ok( + Regex( + "^$", + ), + ), + group_name_list: [], + matcher: Matcher { + prefix: "", + suffix: "", + inner: Literal { + literal: "", + }, + }, + }, + pathname: Component { + pattern_string: "*", + regexp: Ok( + Regex( + "^(.*)$", + ), + ), + group_name_list: [ + "0", + ], + matcher: Matcher { + prefix: "", + suffix: "", + inner: SingleCapture { + filter: None, + allow_empty: true, + }, + }, + }, + search: Component { + pattern_string: "*", + regexp: Ok( + Regex( + "^(.*)$", + ), + ), + group_name_list: [ + "0", + ], + matcher: Matcher { + prefix: "", + suffix: "", + inner: SingleCapture { + filter: None, + allow_empty: true, + }, + }, + }, + hash: Component { + pattern_string: "*", + regexp: Ok( + Regex( + "^(.*)$", + ), + ), + group_name_list: [ + "0", + ], + matcher: Matcher { + prefix: "", + suffix: "", + inner: SingleCapture { + filter: None, + allow_empty: true, + }, + }, + }, + }, + "https://tauri.app", + ), }, + windows: [ + Pattern { + original: "main", + tokens: [ + Char( + 'm', + ), + Char( + 'a', + ), + Char( + 'i', + ), + Char( + 'n', + ), + ], + is_recursive: false, + }, + ], + webviews: [], + scope_id: None, }, - }: ResolvedCommand { - windows: [ - Pattern { - original: "main", - tokens: [ - Char( - 'm', - ), - Char( - 'a', - ), - Char( - 'i', - ), - Char( - 'n', - ), - ], - is_recursive: false, - }, - ], - scope: None, - }, - CommandKey { - name: "plugin:fs|read_file", - context: Remote { - domain: Pattern { - original: "tauri.app", - tokens: [ - Char( - 't', - ), - Char( - 'a', - ), - Char( - 'u', - ), - Char( - 'r', - ), - Char( - 'i', - ), - Char( - '.', - ), - Char( - 'a', - ), - Char( - 'p', - ), - Char( - 'p', - ), - ], - is_recursive: false, + ], + "plugin:fs|read_file": [ + ResolvedCommand { + context: Remote { + url: RemoteUrlPattern( + UrlPattern { + protocol: Component { + pattern_string: "https", + regexp: Ok( + Regex( + "^https$", + ), + ), + group_name_list: [], + matcher: Matcher { + prefix: "", + suffix: "", + inner: Literal { + literal: "https", + }, + }, + }, + username: Component { + pattern_string: "", + regexp: Ok( + Regex( + "^$", + ), + ), + group_name_list: [], + matcher: Matcher { + prefix: "", + suffix: "", + inner: Literal { + literal: "", + }, + }, + }, + password: Component { + pattern_string: "", + regexp: Ok( + Regex( + "^$", + ), + ), + group_name_list: [], + matcher: Matcher { + prefix: "", + suffix: "", + inner: Literal { + literal: "", + }, + }, + }, + hostname: Component { + pattern_string: "tauri.app", + regexp: Ok( + Regex( + "^tauri\\.app$", + ), + ), + group_name_list: [], + matcher: Matcher { + prefix: "", + suffix: "", + inner: Literal { + literal: "tauri.app", + }, + }, + }, + port: Component { + pattern_string: "", + regexp: Ok( + Regex( + "^$", + ), + ), + group_name_list: [], + matcher: Matcher { + prefix: "", + suffix: "", + inner: Literal { + literal: "", + }, + }, + }, + pathname: Component { + pattern_string: "*", + regexp: Ok( + Regex( + "^(.*)$", + ), + ), + group_name_list: [ + "0", + ], + matcher: Matcher { + prefix: "", + suffix: "", + inner: SingleCapture { + filter: None, + allow_empty: true, + }, + }, + }, + search: Component { + pattern_string: "*", + regexp: Ok( + Regex( + "^(.*)$", + ), + ), + group_name_list: [ + "0", + ], + matcher: Matcher { + prefix: "", + suffix: "", + inner: SingleCapture { + filter: None, + allow_empty: true, + }, + }, + }, + hash: Component { + pattern_string: "*", + regexp: Ok( + Regex( + "^(.*)$", + ), + ), + group_name_list: [ + "0", + ], + matcher: Matcher { + prefix: "", + suffix: "", + inner: SingleCapture { + filter: None, + allow_empty: true, + }, + }, + }, + }, + "https://tauri.app", + ), }, + windows: [ + Pattern { + original: "main", + tokens: [ + Char( + 'm', + ), + Char( + 'a', + ), + Char( + 'i', + ), + Char( + 'n', + ), + ], + is_recursive: false, + }, + ], + webviews: [], + scope_id: None, }, - }: ResolvedCommand { - windows: [ - Pattern { - original: "main", - tokens: [ - Char( - 'm', - ), - Char( - 'a', - ), - Char( - 'i', - ), - Char( - 'n', - ), - ], - is_recursive: false, - }, - ], - scope: None, - }, + ], }, denied_commands: {}, command_scope: {}, diff --git a/core/tests/acl/fixtures/snapshots/acl_tests__tests__file-explorer.snap b/core/tests/acl/fixtures/snapshots/acl_tests__tests__file-explorer.snap index 0571cf4ca..1529f484d 100644 --- a/core/tests/acl/fixtures/snapshots/acl_tests__tests__file-explorer.snap +++ b/core/tests/acl/fixtures/snapshots/acl_tests__tests__file-explorer.snap @@ -1,62 +1,63 @@ --- source: core/tests/acl/src/lib.rs -assertion_line: 59 expression: resolved --- Resolved { allowed_commands: { - CommandKey { - name: "plugin:fs|read_dir", - context: Local, - }: ResolvedCommand { - windows: [ - Pattern { - original: "main", - tokens: [ - Char( - 'm', - ), - Char( - 'a', - ), - Char( - 'i', - ), - Char( - 'n', - ), - ], - is_recursive: false, - }, - ], - scope: None, - }, - CommandKey { - name: "plugin:fs|read_file", - context: Local, - }: ResolvedCommand { - windows: [ - Pattern { - original: "main", - tokens: [ - Char( - 'm', - ), - Char( - 'a', - ), - Char( - 'i', - ), - Char( - 'n', - ), - ], - is_recursive: false, - }, - ], - scope: None, - }, + "plugin:fs|read_dir": [ + ResolvedCommand { + context: Local, + windows: [ + Pattern { + original: "main", + tokens: [ + Char( + 'm', + ), + Char( + 'a', + ), + Char( + 'i', + ), + Char( + 'n', + ), + ], + is_recursive: false, + }, + ], + webviews: [], + scope_id: None, + }, + ], + "plugin:fs|read_file": [ + ResolvedCommand { + context: Local, + windows: [ + Pattern { + original: "main", + tokens: [ + Char( + 'm', + ), + Char( + 'a', + ), + Char( + 'i', + ), + Char( + 'n', + ), + ], + is_recursive: false, + }, + ], + webviews: [], + scope_id: None, + }, + ], }, denied_commands: {}, command_scope: {}, diff --git a/core/tests/acl/fixtures/snapshots/acl_tests__tests__multiwebview.snap b/core/tests/acl/fixtures/snapshots/acl_tests__tests__multiwebview.snap new file mode 100644 index 000000000..3714c4c9a --- /dev/null +++ b/core/tests/acl/fixtures/snapshots/acl_tests__tests__multiwebview.snap @@ -0,0 +1,87 @@ +--- +source: core/tests/acl/src/lib.rs +expression: resolved +--- +Resolved { + allowed_commands: { + "plugin:ping|ping": [ + ResolvedCommand { + context: Local, + windows: [ + Pattern { + original: "main", + tokens: [ + Char( + 'm', + ), + Char( + 'a', + ), + Char( + 'i', + ), + Char( + 'n', + ), + ], + is_recursive: false, + }, + ], + webviews: [ + Pattern { + original: "child1", + tokens: [ + Char( + 'c', + ), + Char( + 'h', + ), + Char( + 'i', + ), + Char( + 'l', + ), + Char( + 'd', + ), + Char( + '1', + ), + ], + is_recursive: false, + }, + Pattern { + original: "child2", + tokens: [ + Char( + 'c', + ), + Char( + 'h', + ), + Char( + 'i', + ), + Char( + 'l', + ), + Char( + 'd', + ), + Char( + '2', + ), + ], + is_recursive: false, + }, + ], + scope_id: None, + }, + ], + }, + denied_commands: {}, + command_scope: {}, + global_scope: {}, +} diff --git a/core/tests/acl/fixtures/snapshots/acl_tests__tests__multiwindow.snap b/core/tests/acl/fixtures/snapshots/acl_tests__tests__multiwindow.snap new file mode 100644 index 000000000..d778e504c --- /dev/null +++ b/core/tests/acl/fixtures/snapshots/acl_tests__tests__multiwindow.snap @@ -0,0 +1,344 @@ +--- +source: core/tests/acl/src/lib.rs +expression: resolved +--- +Resolved { + allowed_commands: { + "plugin:fs|move": [ + ResolvedCommand { + context: Local, + windows: [ + Pattern { + original: "main", + tokens: [ + Char( + 'm', + ), + Char( + 'a', + ), + Char( + 'i', + ), + Char( + 'n', + ), + ], + is_recursive: false, + }, + ], + webviews: [], + scope_id: Some( + 3, + ), + }, + ], + "plugin:fs|read_dir": [ + ResolvedCommand { + context: Local, + windows: [ + Pattern { + original: "main", + tokens: [ + Char( + 'm', + ), + Char( + 'a', + ), + Char( + 'i', + ), + Char( + 'n', + ), + ], + is_recursive: false, + }, + ], + webviews: [], + scope_id: Some( + 1, + ), + }, + ResolvedCommand { + context: Local, + windows: [ + Pattern { + original: "main", + tokens: [ + Char( + 'm', + ), + Char( + 'a', + ), + Char( + 'i', + ), + Char( + 'n', + ), + ], + is_recursive: false, + }, + ], + webviews: [], + scope_id: Some( + 2, + ), + }, + ResolvedCommand { + context: Local, + windows: [ + Pattern { + original: "main", + tokens: [ + Char( + 'm', + ), + Char( + 'a', + ), + Char( + 'i', + ), + Char( + 'n', + ), + ], + is_recursive: false, + }, + ], + webviews: [], + scope_id: Some( + 4, + ), + }, + ResolvedCommand { + context: Local, + windows: [ + Pattern { + original: "external", + tokens: [ + Char( + 'e', + ), + Char( + 'x', + ), + Char( + 't', + ), + Char( + 'e', + ), + Char( + 'r', + ), + Char( + 'n', + ), + Char( + 'a', + ), + Char( + 'l', + ), + ], + is_recursive: false, + }, + ], + webviews: [], + scope_id: None, + }, + ], + "plugin:fs|read_file": [ + ResolvedCommand { + context: Local, + windows: [ + Pattern { + original: "main", + tokens: [ + Char( + 'm', + ), + Char( + 'a', + ), + Char( + 'i', + ), + Char( + 'n', + ), + ], + is_recursive: false, + }, + ], + webviews: [], + scope_id: Some( + 1, + ), + }, + ResolvedCommand { + context: Local, + windows: [ + Pattern { + original: "main", + tokens: [ + Char( + 'm', + ), + Char( + 'a', + ), + Char( + 'i', + ), + Char( + 'n', + ), + ], + is_recursive: false, + }, + ], + webviews: [], + scope_id: Some( + 2, + ), + }, + ResolvedCommand { + context: Local, + windows: [ + Pattern { + original: "external", + tokens: [ + Char( + 'e', + ), + Char( + 'x', + ), + Char( + 't', + ), + Char( + 'e', + ), + Char( + 'r', + ), + Char( + 'n', + ), + Char( + 'a', + ), + Char( + 'l', + ), + ], + is_recursive: false, + }, + ], + webviews: [], + scope_id: None, + }, + ], + }, + denied_commands: {}, + command_scope: { + 1: ResolvedScope { + allow: [ + Map( + { + "path": String( + "$CONFIG/*", + ), + }, + ), + ], + deny: [], + }, + 2: ResolvedScope { + allow: [ + Map( + { + "path": String( + "$RESOURCE/**", + ), + }, + ), + Map( + { + "path": String( + "$RESOURCE", + ), + }, + ), + ], + deny: [], + }, + 3: ResolvedScope { + allow: [ + Map( + { + "path": String( + "$TEMP/*", + ), + }, + ), + ], + deny: [], + }, + 4: ResolvedScope { + allow: [ + Map( + { + "path": String( + "$DOWNLOAD", + ), + }, + ), + Map( + { + "path": String( + "$DOWNLOAD/**", + ), + }, + ), + ], + deny: [], + }, + }, + global_scope: { + "fs": ResolvedScope { + allow: [ + Map( + { + "path": String( + "$APP", + ), + }, + ), + ], + deny: [ + Map( + { + "path": String( + "$HOME", + ), + }, + ), + Map( + { + "path": String( + "$HOME", + ), + }, + ), + ], + }, + }, +} diff --git a/core/tests/acl/fixtures/snapshots/acl_tests__tests__scope-extended.snap b/core/tests/acl/fixtures/snapshots/acl_tests__tests__scope-extended.snap index fee8df2a6..0c88fad30 100644 --- a/core/tests/acl/fixtures/snapshots/acl_tests__tests__scope-extended.snap +++ b/core/tests/acl/fixtures/snapshots/acl_tests__tests__scope-extended.snap @@ -4,94 +4,178 @@ expression: resolved --- Resolved { allowed_commands: { - CommandKey { - name: "plugin:fs|move", - context: Local, - }: ResolvedCommand { - windows: [ - Pattern { - original: "main", - tokens: [ - Char( - 'm', - ), - Char( - 'a', - ), - Char( - 'i', - ), - Char( - 'n', - ), - ], - is_recursive: false, - }, - ], - scope: Some( - 9188997750422900590, - ), - }, - CommandKey { - name: "plugin:fs|read_dir", - context: Local, - }: ResolvedCommand { - windows: [ - Pattern { - original: "main", - tokens: [ - Char( - 'm', - ), - Char( - 'a', - ), - Char( - 'i', - ), - Char( - 'n', - ), - ], - is_recursive: false, - }, - ], - scope: Some( - 1349364295896631601, - ), - }, - CommandKey { - name: "plugin:fs|read_file", - context: Local, - }: ResolvedCommand { - windows: [ - Pattern { - original: "main", - tokens: [ - Char( - 'm', - ), - Char( - 'a', - ), - Char( - 'i', - ), - Char( - 'n', - ), - ], - is_recursive: false, - }, - ], - scope: Some( - 8031926490300119127, - ), - }, + "plugin:fs|move": [ + ResolvedCommand { + context: Local, + windows: [ + Pattern { + original: "main", + tokens: [ + Char( + 'm', + ), + Char( + 'a', + ), + Char( + 'i', + ), + Char( + 'n', + ), + ], + is_recursive: false, + }, + ], + webviews: [], + scope_id: Some( + 3, + ), + }, + ], + "plugin:fs|read_dir": [ + ResolvedCommand { + context: Local, + windows: [ + Pattern { + original: "main", + tokens: [ + Char( + 'm', + ), + Char( + 'a', + ), + Char( + 'i', + ), + Char( + 'n', + ), + ], + is_recursive: false, + }, + ], + webviews: [], + scope_id: Some( + 1, + ), + }, + ResolvedCommand { + context: Local, + windows: [ + Pattern { + original: "main", + tokens: [ + Char( + 'm', + ), + Char( + 'a', + ), + Char( + 'i', + ), + Char( + 'n', + ), + ], + is_recursive: false, + }, + ], + webviews: [], + scope_id: Some( + 2, + ), + }, + ResolvedCommand { + context: Local, + windows: [ + Pattern { + original: "main", + tokens: [ + Char( + 'm', + ), + Char( + 'a', + ), + Char( + 'i', + ), + Char( + 'n', + ), + ], + is_recursive: false, + }, + ], + webviews: [], + scope_id: Some( + 4, + ), + }, + ], + "plugin:fs|read_file": [ + ResolvedCommand { + context: Local, + windows: [ + Pattern { + original: "main", + tokens: [ + Char( + 'm', + ), + Char( + 'a', + ), + Char( + 'i', + ), + Char( + 'n', + ), + ], + is_recursive: false, + }, + ], + webviews: [], + scope_id: Some( + 1, + ), + }, + ResolvedCommand { + context: Local, + windows: [ + Pattern { + original: "main", + tokens: [ + Char( + 'm', + ), + Char( + 'a', + ), + Char( + 'i', + ), + Char( + 'n', + ), + ], + is_recursive: false, + }, + ], + webviews: [], + scope_id: Some( + 2, + ), + }, + ], }, denied_commands: {}, command_scope: { - 1349364295896631601: ResolvedScope { + 1: ResolvedScope { allow: [ Map( { @@ -100,6 +184,11 @@ Resolved { ), }, ), + ], + deny: [], + }, + 2: ResolvedScope { + allow: [ Map( { "path": String( @@ -114,6 +203,31 @@ Resolved { ), }, ), + ], + deny: [ + Map( + { + "path": String( + "$RESOURCE/**/*.key", + ), + }, + ), + ], + }, + 3: ResolvedScope { + allow: [ + Map( + { + "path": String( + "$TEMP/*", + ), + }, + ), + ], + deny: [], + }, + 4: ResolvedScope { + allow: [ Map( { "path": String( @@ -129,60 +243,6 @@ Resolved { }, ), ], - deny: [ - Map( - { - "path": String( - "$RESOURCE/**/*.key", - ), - }, - ), - ], - }, - 8031926490300119127: ResolvedScope { - allow: [ - Map( - { - "path": String( - "$HOME/.config/**", - ), - }, - ), - Map( - { - "path": String( - "$RESOURCE/**", - ), - }, - ), - Map( - { - "path": String( - "$RESOURCE", - ), - }, - ), - ], - deny: [ - Map( - { - "path": String( - "$RESOURCE/**/*.key", - ), - }, - ), - ], - }, - 9188997750422900590: ResolvedScope { - allow: [ - Map( - { - "path": String( - "$TEMP/*", - ), - }, - ), - ], deny: [], }, }, diff --git a/core/tests/acl/fixtures/snapshots/acl_tests__tests__scope.snap b/core/tests/acl/fixtures/snapshots/acl_tests__tests__scope.snap index 5805c5833..a61589596 100644 --- a/core/tests/acl/fixtures/snapshots/acl_tests__tests__scope.snap +++ b/core/tests/acl/fixtures/snapshots/acl_tests__tests__scope.snap @@ -4,94 +4,174 @@ expression: resolved --- Resolved { allowed_commands: { - CommandKey { - name: "plugin:fs|move", - context: Local, - }: ResolvedCommand { - windows: [ - Pattern { - original: "main", - tokens: [ - Char( - 'm', - ), - Char( - 'a', - ), - Char( - 'i', - ), - Char( - 'n', - ), - ], - is_recursive: false, - }, - ], - scope: Some( - 18088007599891946824, - ), - }, - CommandKey { - name: "plugin:fs|read_dir", - context: Local, - }: ResolvedCommand { - windows: [ - Pattern { - original: "main", - tokens: [ - Char( - 'm', - ), - Char( - 'a', - ), - Char( - 'i', - ), - Char( - 'n', - ), - ], - is_recursive: false, - }, - ], - scope: Some( - 5856262838373339618, - ), - }, - CommandKey { - name: "plugin:fs|read_file", - context: Local, - }: ResolvedCommand { - windows: [ - Pattern { - original: "main", - tokens: [ - Char( - 'm', - ), - Char( - 'a', - ), - Char( - 'i', - ), - Char( - 'n', - ), - ], - is_recursive: false, - }, - ], - scope: Some( - 7912899488978770657, - ), - }, + "plugin:fs|move": [ + ResolvedCommand { + context: Local, + windows: [ + Pattern { + original: "main", + tokens: [ + Char( + 'm', + ), + Char( + 'a', + ), + Char( + 'i', + ), + Char( + 'n', + ), + ], + is_recursive: false, + }, + ], + webviews: [], + scope_id: Some( + 2, + ), + }, + ], + "plugin:fs|read_dir": [ + ResolvedCommand { + context: Local, + windows: [ + Pattern { + original: "main", + tokens: [ + Char( + 'm', + ), + Char( + 'a', + ), + Char( + 'i', + ), + Char( + 'n', + ), + ], + is_recursive: false, + }, + ], + webviews: [], + scope_id: None, + }, + ResolvedCommand { + context: Local, + windows: [ + Pattern { + original: "main", + tokens: [ + Char( + 'm', + ), + Char( + 'a', + ), + Char( + 'i', + ), + Char( + 'n', + ), + ], + is_recursive: false, + }, + ], + webviews: [], + scope_id: Some( + 1, + ), + }, + ResolvedCommand { + context: Local, + windows: [ + Pattern { + original: "main", + tokens: [ + Char( + 'm', + ), + Char( + 'a', + ), + Char( + 'i', + ), + Char( + 'n', + ), + ], + is_recursive: false, + }, + ], + webviews: [], + scope_id: Some( + 3, + ), + }, + ], + "plugin:fs|read_file": [ + ResolvedCommand { + context: Local, + windows: [ + Pattern { + original: "main", + tokens: [ + Char( + 'm', + ), + Char( + 'a', + ), + Char( + 'i', + ), + Char( + 'n', + ), + ], + is_recursive: false, + }, + ], + webviews: [], + scope_id: None, + }, + ResolvedCommand { + context: Local, + windows: [ + Pattern { + original: "main", + tokens: [ + Char( + 'm', + ), + Char( + 'a', + ), + Char( + 'i', + ), + Char( + 'n', + ), + ], + is_recursive: false, + }, + ], + webviews: [], + scope_id: Some( + 1, + ), + }, + ], }, denied_commands: {}, command_scope: { - 5856262838373339618: ResolvedScope { + 1: ResolvedScope { allow: [ Map( { @@ -107,6 +187,23 @@ Resolved { ), }, ), + ], + deny: [], + }, + 2: ResolvedScope { + allow: [ + Map( + { + "path": String( + "$TEMP/*", + ), + }, + ), + ], + deny: [], + }, + 3: ResolvedScope { + allow: [ Map( { "path": String( @@ -124,37 +221,6 @@ Resolved { ], deny: [], }, - 7912899488978770657: ResolvedScope { - allow: [ - Map( - { - "path": String( - "$RESOURCE/**", - ), - }, - ), - Map( - { - "path": String( - "$RESOURCE", - ), - }, - ), - ], - deny: [], - }, - 18088007599891946824: ResolvedScope { - allow: [ - Map( - { - "path": String( - "$TEMP/*", - ), - }, - ), - ], - deny: [], - }, }, global_scope: { "fs": ResolvedScope { diff --git a/core/tests/acl/fixtures/snapshots/linux/acl_tests__tests__platform-specific-permissions.snap b/core/tests/acl/fixtures/snapshots/linux/acl_tests__tests__platform-specific-permissions.snap new file mode 100644 index 000000000..5c6219253 --- /dev/null +++ b/core/tests/acl/fixtures/snapshots/linux/acl_tests__tests__platform-specific-permissions.snap @@ -0,0 +1,97 @@ +--- +source: core/tests/acl/src/lib.rs +expression: resolved +--- +Resolved { + allowed_commands: { + "plugin:os|spawn": [ + ResolvedCommand { + context: Local, + windows: [ + Pattern { + original: "main", + tokens: [ + Char( + 'm', + ), + Char( + 'a', + ), + Char( + 'i', + ), + Char( + 'n', + ), + ], + is_recursive: false, + }, + ], + webviews: [], + scope_id: Some( + 1, + ), + }, + ResolvedCommand { + context: Local, + windows: [ + Pattern { + original: "main", + tokens: [ + Char( + 'm', + ), + Char( + 'a', + ), + Char( + 'i', + ), + Char( + 'n', + ), + ], + is_recursive: false, + }, + ], + webviews: [], + scope_id: Some( + 2, + ), + }, + ], + }, + denied_commands: {}, + command_scope: { + 1: ResolvedScope { + allow: [ + Map( + { + "command": String( + "apt", + ), + }, + ), + ], + deny: [], + }, + 2: ResolvedScope { + allow: [ + Map( + { + "command": String( + "servo", + ), + }, + ), + ], + deny: [], + }, + }, + global_scope: { + "os": ResolvedScope { + allow: [], + deny: [], + }, + }, +} diff --git a/core/tests/acl/fixtures/snapshots/macOS/acl_tests__tests__platform-specific-permissions.snap b/core/tests/acl/fixtures/snapshots/macOS/acl_tests__tests__platform-specific-permissions.snap new file mode 100644 index 000000000..b76b0fc9f --- /dev/null +++ b/core/tests/acl/fixtures/snapshots/macOS/acl_tests__tests__platform-specific-permissions.snap @@ -0,0 +1,66 @@ +--- +source: core/tests/acl/src/lib.rs +expression: resolved +--- +Resolved { + allowed_commands: { + "plugin:os|spawn": [ + ResolvedCommand { + context: Local, + windows: [ + Pattern { + original: "main", + tokens: [ + Char( + 'm', + ), + Char( + 'a', + ), + Char( + 'i', + ), + Char( + 'n', + ), + ], + is_recursive: false, + }, + ], + webviews: [], + scope_id: Some( + 1, + ), + }, + ], + }, + denied_commands: {}, + command_scope: { + 1: ResolvedScope { + allow: [ + Map( + { + "command": String( + "safari", + ), + }, + ), + ], + deny: [], + }, + }, + global_scope: { + "os": ResolvedScope { + allow: [ + Map( + { + "path": String( + "$HOME/Library/**", + ), + }, + ), + ], + deny: [], + }, + }, +} diff --git a/core/tests/acl/fixtures/snapshots/windows/acl_tests__tests__platform-specific-permissions.snap b/core/tests/acl/fixtures/snapshots/windows/acl_tests__tests__platform-specific-permissions.snap new file mode 100644 index 000000000..abc6e8c87 --- /dev/null +++ b/core/tests/acl/fixtures/snapshots/windows/acl_tests__tests__platform-specific-permissions.snap @@ -0,0 +1,66 @@ +--- +source: core/tests/acl/src/lib.rs +expression: resolved +--- +Resolved { + allowed_commands: { + "plugin:os|spawn": [ + ResolvedCommand { + context: Local, + windows: [ + Pattern { + original: "main", + tokens: [ + Char( + 'm', + ), + Char( + 'a', + ), + Char( + 'i', + ), + Char( + 'n', + ), + ], + is_recursive: false, + }, + ], + webviews: [], + scope_id: Some( + 1, + ), + }, + ], + }, + denied_commands: {}, + command_scope: { + 1: ResolvedScope { + allow: [ + Map( + { + "command": String( + "edge", + ), + }, + ), + ], + deny: [], + }, + }, + global_scope: { + "os": ResolvedScope { + allow: [], + deny: [ + Map( + { + "path": String( + "$APP/EBWebView/**", + ), + }, + ), + ], + }, + }, +} diff --git a/core/tests/acl/src/lib.rs b/core/tests/acl/src/lib.rs index f2d2aaed4..7fc51addf 100644 --- a/core/tests/acl/src/lib.rs +++ b/core/tests/acl/src/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2019-2023 Tauri Programme within The Commons Conservancy +// Copyright 2019-2024 Tauri Programme within The Commons Conservancy // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT @@ -12,7 +12,7 @@ mod tests { }; use tauri_utils::{ - acl::{build::parse_capabilities, plugin::Manifest, resolved::Resolved}, + acl::{build::parse_capabilities, manifest::Manifest, resolved::Resolved}, platform::Target, }; @@ -29,6 +29,7 @@ mod tests { &format!("{}/*.toml", plugin_path.display()), plugin, &out_dir, + |_| true, ) .expect("failed to define permissions"); let manifest = Manifest::new(permission_files, None); @@ -40,14 +41,21 @@ mod tests { #[test] fn resolve_acl() { - let mut settings = insta::Settings::clone_current(); - settings.set_snapshot_path("../fixtures/snapshots"); - let _guard = settings.bind_to_scope(); - let manifest_dir = Path::new(env!("CARGO_MANIFEST_DIR")); let fixtures_path = manifest_dir.join("fixtures").join("capabilities"); for fixture_path in read_dir(fixtures_path).expect("failed to read fixtures") { let fixture_entry = fixture_path.expect("failed to read fixture entry"); + + let mut settings = insta::Settings::clone_current(); + settings.set_snapshot_path( + if fixture_entry.path().file_name().unwrap() == "platform-specific-permissions" { + Path::new("../fixtures/snapshots").join(Target::current().to_string()) + } else { + Path::new("../fixtures/snapshots").to_path_buf() + }, + ); + let _guard = settings.bind_to_scope(); + let fixture_plugins_str = read_to_string(fixture_entry.path().join("required-plugins.json")) .expect("failed to read fixture required-plugins.json file"); let fixture_plugins: Vec = serde_json::from_str(&fixture_plugins_str) @@ -57,7 +65,7 @@ mod tests { let capabilities = parse_capabilities(&format!("{}/cap*", fixture_entry.path().display())) .expect("failed to parse capabilities"); - let resolved = Resolved::resolve(manifests, capabilities, Target::current()) + let resolved = Resolved::resolve(&manifests, capabilities, Target::current()) .expect("failed to resolve ACL"); insta::assert_debug_snapshot!( diff --git a/core/tests/restart/build.rs b/core/tests/restart/build.rs index e6c5244c7..35b4e027d 100644 --- a/core/tests/restart/build.rs +++ b/core/tests/restart/build.rs @@ -1,4 +1,4 @@ -// Copyright 2019-2023 Tauri Programme within The Commons Conservancy +// Copyright 2019-2024 Tauri Programme within The Commons Conservancy // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT diff --git a/core/tests/restart/src/main.rs b/core/tests/restart/src/main.rs index a37a66643..85b9bd46d 100644 --- a/core/tests/restart/src/main.rs +++ b/core/tests/restart/src/main.rs @@ -1,4 +1,4 @@ -// Copyright 2019-2023 Tauri Programme within The Commons Conservancy +// Copyright 2019-2024 Tauri Programme within The Commons Conservancy // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT diff --git a/core/tests/restart/tests/restart.rs b/core/tests/restart/tests/restart.rs index a174f537a..8b380cd23 100644 --- a/core/tests/restart/tests/restart.rs +++ b/core/tests/restart/tests/restart.rs @@ -1,4 +1,4 @@ -// Copyright 2019-2023 Tauri Programme within The Commons Conservancy +// Copyright 2019-2024 Tauri Programme within The Commons Conservancy // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT diff --git a/dependabot.yml b/dependabot.yml index fb2177220..8fa51318c 100644 --- a/dependabot.yml +++ b/dependabot.yml @@ -1,4 +1,4 @@ -# Copyright 2019-2023 Tauri Programme within The Commons Conservancy +# Copyright 2019-2024 Tauri Programme within The Commons Conservancy # SPDX-License-Identifier: Apache-2.0 # SPDX-License-Identifier: MIT diff --git a/examples/api/isolation-dist/index.js b/examples/api/isolation-dist/index.js index 84fc82e56..1324ffc31 100644 --- a/examples/api/isolation-dist/index.js +++ b/examples/api/isolation-dist/index.js @@ -1,4 +1,4 @@ -// Copyright 2019-2023 Tauri Programme within The Commons Conservancy +// Copyright 2019-2024 Tauri Programme within The Commons Conservancy // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT diff --git a/examples/api/package.json b/examples/api/package.json index 95f23ec5e..eaaa9d2a8 100644 --- a/examples/api/package.json +++ b/examples/api/package.json @@ -20,6 +20,6 @@ "unocss": "^0.39.3", "@sveltejs/vite-plugin-svelte": "^2.4.6", "svelte": "^4.2.1", - "vite": "^4.5.2" + "vite": "^4.5.3" } } diff --git a/examples/api/src-tauri/Cargo.lock b/examples/api/src-tauri/Cargo.lock index 770d1243f..ee9d8069d 100644 --- a/examples/api/src-tauri/Cargo.lock +++ b/examples/api/src-tauri/Cargo.lock @@ -29,9 +29,9 @@ dependencies = [ [[package]] name = "aes" -version = "0.8.3" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac1f845298e95f983ff1944b728ae08b8cebab80d684f0a832ed0fc74dfa27e2" +checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0" dependencies = [ "cfg-if", "cipher", @@ -91,59 +91,11 @@ dependencies = [ "libc", ] -[[package]] -name = "anstream" -version = "0.6.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e2e1ebcb11de5c03c67de28a7df593d32191b44939c482e97702baaaa6ab6a5" -dependencies = [ - "anstyle", - "anstyle-parse", - "anstyle-query", - "anstyle-wincon", - "colorchoice", - "utf8parse", -] - -[[package]] -name = "anstyle" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7079075b41f533b8c61d2a4d073c4676e1f8b249ff94a393b0595db304e0dd87" - -[[package]] -name = "anstyle-parse" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c75ac65da39e5fe5ab759307499ddad880d724eed2f6ce5b5e8a26f4f387928c" -dependencies = [ - "utf8parse", -] - -[[package]] -name = "anstyle-query" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e28923312444cdd728e4738b3f9c9cac739500909bb3d3c94b43551b16517648" -dependencies = [ - "windows-sys 0.52.0", -] - -[[package]] -name = "anstyle-wincon" -version = "3.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1cd54b81ec8d6180e24654d0b371ad22fc3dd083b6ff8ba325b72e00c87660a7" -dependencies = [ - "anstyle", - "windows-sys 0.52.0", -] - [[package]] name = "anyhow" -version = "1.0.79" +version = "1.0.80" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "080e9890a082662b09c1ad45f567faeeb47f22b5fb23895fbe1e651e718e25ca" +checksum = "5ad32ce52e4161730f7098c077cd2ed6229b5804ccf99e5366be1ab72a98b4e1" [[package]] name = "api" @@ -154,194 +106,16 @@ dependencies = [ "serde_json", "tauri", "tauri-build", - "tauri-plugin-cli", "tauri-plugin-sample", "tiny_http", ] -[[package]] -name = "as-raw-xcb-connection" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "175571dd1d178ced59193a6fc02dde1b972eb0bc56c892cde9beeceac5bf0f6b" - [[package]] name = "ascii" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d92bec98840b8f03a5ff5413de5293bfcd8bf96467cf5452609f939ec6f5de16" -[[package]] -name = "async-broadcast" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c48ccdbf6ca6b121e0f586cbc0e73ae440e56c67c30fa0873b4e110d9c26d2b" -dependencies = [ - "event-listener 2.5.3", - "futures-core", -] - -[[package]] -name = "async-channel" -version = "2.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ca33f4bc4ed1babef42cad36cc1f51fa88be00420404e5b1e80ab1b18f7678c" -dependencies = [ - "concurrent-queue", - "event-listener 4.0.3", - "event-listener-strategy", - "futures-core", - "pin-project-lite", -] - -[[package]] -name = "async-executor" -version = "1.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17ae5ebefcc48e7452b4987947920dac9450be1110cadf34d1b8c116bdbaf97c" -dependencies = [ - "async-lock 3.3.0", - "async-task", - "concurrent-queue", - "fastrand 2.0.1", - "futures-lite 2.2.0", - "slab", -] - -[[package]] -name = "async-fs" -version = "1.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "279cf904654eeebfa37ac9bb1598880884924aab82e290aa65c9e77a0e142e06" -dependencies = [ - "async-lock 2.8.0", - "autocfg", - "blocking", - "futures-lite 1.13.0", -] - -[[package]] -name = "async-io" -version = "1.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fc5b45d93ef0529756f812ca52e44c221b35341892d3dcc34132ac02f3dd2af" -dependencies = [ - "async-lock 2.8.0", - "autocfg", - "cfg-if", - "concurrent-queue", - "futures-lite 1.13.0", - "log", - "parking", - "polling 2.8.0", - "rustix 0.37.27", - "slab", - "socket2 0.4.10", - "waker-fn", -] - -[[package]] -name = "async-io" -version = "2.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb41eb19024a91746eba0773aa5e16036045bbf45733766661099e182ea6a744" -dependencies = [ - "async-lock 3.3.0", - "cfg-if", - "concurrent-queue", - "futures-io", - "futures-lite 2.2.0", - "parking", - "polling 3.3.2", - "rustix 0.38.30", - "slab", - "tracing", - "windows-sys 0.52.0", -] - -[[package]] -name = "async-lock" -version = "2.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "287272293e9d8c41773cec55e365490fe034813a2f172f502d6ddcf75b2f582b" -dependencies = [ - "event-listener 2.5.3", -] - -[[package]] -name = "async-lock" -version = "3.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d034b430882f8381900d3fe6f0aaa3ad94f2cb4ac519b429692a1bc2dda4ae7b" -dependencies = [ - "event-listener 4.0.3", - "event-listener-strategy", - "pin-project-lite", -] - -[[package]] -name = "async-process" -version = "1.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea6438ba0a08d81529c69b36700fa2f95837bfe3e776ab39cde9c14d9149da88" -dependencies = [ - "async-io 1.13.0", - "async-lock 2.8.0", - "async-signal", - "blocking", - "cfg-if", - "event-listener 3.1.0", - "futures-lite 1.13.0", - "rustix 0.38.30", - "windows-sys 0.48.0", -] - -[[package]] -name = "async-recursion" -version = "1.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fd55a5ba1179988837d24ab4c7cc8ed6efdeff578ede0416b4225a5fca35bd0" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.48", -] - -[[package]] -name = "async-signal" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e47d90f65a225c4527103a8d747001fc56e375203592b25ad103e1ca13124c5" -dependencies = [ - "async-io 2.3.0", - "async-lock 2.8.0", - "atomic-waker", - "cfg-if", - "futures-core", - "futures-io", - "rustix 0.38.30", - "signal-hook-registry", - "slab", - "windows-sys 0.48.0", -] - -[[package]] -name = "async-task" -version = "4.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fbb36e985947064623dbd357f727af08ffd077f93d696782f3c56365fa2e2799" - -[[package]] -name = "async-trait" -version = "0.1.77" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c980ee35e870bd1a4d2c8294d4c04d0499e67bca1e4b5cefcc693c2fa00caea9" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.48", -] - [[package]] name = "atk" version = "0.18.0" @@ -365,12 +139,6 @@ dependencies = [ "system-deps", ] -[[package]] -name = "atomic-waker" -version = "1.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" - [[package]] name = "autocfg" version = "1.1.0" @@ -398,6 +166,12 @@ version = "0.21.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" +[[package]] +name = "base64" +version = "0.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9475866fec1451be56a3c2400fd081ff546538961565ccb5b7142cbd22bc7a51" + [[package]] name = "bitflags" version = "1.3.2" @@ -428,22 +202,6 @@ dependencies = [ "generic-array", ] -[[package]] -name = "blocking" -version = "1.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a37913e8dc4ddcc604f0c6d3bf2887c995153af3611de9e23c352b44c1b9118" -dependencies = [ - "async-channel", - "async-lock 3.3.0", - "async-task", - "fastrand 2.0.1", - "futures-io", - "futures-lite 2.2.0", - "piper", - "tracing", -] - [[package]] name = "brotli" version = "3.4.0" @@ -467,29 +225,15 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.14.0" +version = "3.15.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec" +checksum = "8ea184aa71bb362a1157c896979544cc23974e08fd265f29ea96b59f0b4a555b" [[package]] name = "bytemuck" -version = "1.14.0" +version = "1.14.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "374d28ec25809ee0e23827c2ab573d729e293f281dfe393500e7ad618baa61c6" -dependencies = [ - "bytemuck_derive", -] - -[[package]] -name = "bytemuck_derive" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "965ab7eb5f8f97d2a083c799f3a1b994fc397b2fe2da5d1da1626ce15a39f2b1" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.48", -] +checksum = "a2ef034f05691a48569bd920a96c81b9d91bbad1ab5ac7c4616c1f6ef36cb79f" [[package]] name = "byteorder" @@ -542,9 +286,9 @@ dependencies = [ [[package]] name = "cargo-platform" -version = "0.1.6" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ceed8ef69d8518a5dda55c07425450b58a4e1946f4951eab6d7191ee86c2443d" +checksum = "694c8807f2ae16faecc43dc17d74b3eb042482789fd0eb64b39a2e04e087053f" dependencies = [ "serde", ] @@ -575,12 +319,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.0.83" +version = "1.0.88" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" -dependencies = [ - "libc", -] +checksum = "02f341c093d19155a6e41631ce5971aac4e9a868262212153124c15fa22d1cdc" [[package]] name = "cesu8" @@ -601,9 +342,9 @@ dependencies = [ [[package]] name = "cfg-expr" -version = "0.15.6" +version = "0.15.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6100bc57b6209840798d95cb2775684849d332f7bd788db2a8c8caf7ef82a41a" +checksum = "fa50868b64a9a6fda9d593ce778849ea8715cd2a3d2cc17ffdb4a2f2f2f1961d" dependencies = [ "smallvec", "target-lexicon", @@ -629,15 +370,15 @@ checksum = "77e53693616d3075149f4ead59bdeecd204ac6b8192d8969757601b74bddf00f" [[package]] name = "chrono" -version = "0.4.32" +version = "0.4.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41daef31d7a747c5c847246f36de49ced6f7403b4cdabc807a97b5cc184cda7a" +checksum = "5bc015644b92d5890fab7489e49d21f879d5c990186827d42ec511919404f38b" dependencies = [ "android-tzdata", "iana-time-zone", "num-traits", "serde", - "windows-targets 0.52.0", + "windows-targets 0.52.4", ] [[package]] @@ -656,33 +397,6 @@ dependencies = [ "inout", ] -[[package]] -name = "clap" -version = "4.4.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e578d6ec4194633722ccf9544794b71b1385c3c027efe0c55db226fc880865c" -dependencies = [ - "clap_builder", -] - -[[package]] -name = "clap_builder" -version = "4.4.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4df4df40ec50c46000231c914968278b1eb05098cf8f1b3a518a95030e71d1c7" -dependencies = [ - "anstream", - "anstyle", - "clap_lex", - "strsim", -] - -[[package]] -name = "clap_lex" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "702fc72eb24e5a1e48ce58027a675bc24edd52096d5397d4aea7c6dd9eca0bd1" - [[package]] name = "cocoa" version = "0.25.0" @@ -719,12 +433,6 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b" -[[package]] -name = "colorchoice" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" - [[package]] name = "combine" version = "4.6.6" @@ -735,15 +443,6 @@ dependencies = [ "memchr", ] -[[package]] -name = "concurrent-queue" -version = "2.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d16048cd947b08fa32c24458a22f5dc5e835264f689f4f5653210c69fd107363" -dependencies = [ - "crossbeam-utils", -] - [[package]] name = "convert_case" version = "0.4.0" @@ -801,18 +500,18 @@ dependencies = [ [[package]] name = "crc32fast" -version = "1.3.2" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" +checksum = "b3855a8a784b474f333699ef2bbca9db2c4a1f6d9088a90a2d25b1eb53111eaa" dependencies = [ "cfg-if", ] [[package]] name = "crossbeam-channel" -version = "0.5.11" +version = "0.5.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "176dc175b78f56c0f321911d9c8eb2b77a78a4860b9c19db83835fea1a46649b" +checksum = "ab3db02a9c5b5121e1e42fbdb1aeb65f5e02624cc58c43f2884c6ccac0b82f95" dependencies = [ "crossbeam-utils", ] @@ -858,17 +557,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "13b588ba4ac1a99f7f2964d24b3d896ddc6bf847ee3855dbd4366f058cfcd331" dependencies = [ "quote", - "syn 2.0.48", + "syn 2.0.52", ] [[package]] name = "ctor" -version = "0.2.6" +version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30d2b3721e861707777e3195b0158f950ae6dc4a27e4d02ff9f67e3eb3de199e" +checksum = "ad291aa74992b9b7a7e88c38acbbf6ad7e107f1d90ee8775b7bc1fc3394f485c" dependencies = [ "quote", - "syn 2.0.48", + "syn 2.0.52", ] [[package]] @@ -882,9 +581,9 @@ dependencies = [ [[package]] name = "darling" -version = "0.20.3" +version = "0.20.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0209d94da627ab5605dcccf08bb18afa5009cfbef48d8a8b7d7bdbc79be25c5e" +checksum = "54e36fcd13ed84ffdfda6f5be89b31287cbb80c439841fe69e04841435464391" dependencies = [ "darling_core", "darling_macro", @@ -892,27 +591,27 @@ dependencies = [ [[package]] name = "darling_core" -version = "0.20.3" +version = "0.20.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "177e3443818124b357d8e76f53be906d60937f0d3a90773a664fa63fa253e621" +checksum = "9c2cf1c23a687a1feeb728783b993c4e1ad83d99f351801977dd809b48d0a70f" dependencies = [ "fnv", "ident_case", "proc-macro2", "quote", "strsim", - "syn 2.0.48", + "syn 2.0.52", ] [[package]] name = "darling_macro" -version = "0.20.3" +version = "0.20.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "836a9bbc7ad63342d6d6e7b815ccab164bc77a2d95d84bc3117a8c0d5c98e2d5" +checksum = "a668eda54683121533a393014d8692171709ff57a7d61f187b6e782719f8933f" dependencies = [ "darling_core", "quote", - "syn 2.0.48", + "syn 2.0.52", ] [[package]] @@ -925,17 +624,6 @@ dependencies = [ "serde", ] -[[package]] -name = "derivative" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", -] - [[package]] name = "derive_more" version = "0.99.17" @@ -977,7 +665,7 @@ checksum = "4ebda144c4fe02d1f7ea1a7d9641b6fc6b580adcfa024ae48797ecdeb6825b4d" dependencies = [ "libc", "redox_users", - "winapi 0.3.9", + "winapi", ] [[package]] @@ -996,48 +684,35 @@ dependencies = [ ] [[package]] -name = "downcast-rs" -version = "1.2.0" +name = "dlopen2" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ea835d29036a4087793836fa931b08837ad5e957da9e23886b29586fb9b6650" - -[[package]] -name = "drm" -version = "0.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0f8a69e60d75ae7dab4ef26a59ca99f2a89d4c142089b537775ae0c198bdcde" -dependencies = [ - "bitflags 2.4.2", - "bytemuck", - "drm-ffi", - "drm-fourcc", - "rustix 0.38.30", -] - -[[package]] -name = "drm-ffi" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41334f8405792483e32ad05fbb9c5680ff4e84491883d2947a4757dc54cb2ac6" -dependencies = [ - "drm-sys", - "rustix 0.38.30", -] - -[[package]] -name = "drm-fourcc" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0aafbcdb8afc29c1a7ee5fbe53b5d62f4565b35a042a662ca9fecd0b54dae6f4" - -[[package]] -name = "drm-sys" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d09ff881f92f118b11105ba5e34ff8f4adf27b30dae8f12e28c193af1c83176" +checksum = "9e1297103d2bbaea85724fcee6294c2d50b1081f9ad47d0f6f6f61eda65315a6" dependencies = [ + "dlopen2_derive", "libc", - "linux-raw-sys 0.6.4", + "once_cell", + "winapi", +] + +[[package]] +name = "dlopen2_derive" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2b99bf03862d7f545ebc28ddd33a665b50865f4dfd84031a393823879bd4c54" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.52", +] + +[[package]] +name = "dpi" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f25c0e292a7ca6d6498557ff1df68f32c99850012b6ea401cf8daf771f22ff53" +dependencies = [ + "serde", ] [[package]] @@ -1063,9 +738,9 @@ checksum = "56ce8c6da7551ec6c462cbaf3bfbc75131ebbfa1c944aeaa9dab51ca1c5f0c3b" [[package]] name = "dyn-clone" -version = "1.0.16" +version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "545b22097d44f8a9581187cdf93de7a71e4722bf51200cfaba810865b49a495d" +checksum = "0d6ef0072f8a535281e4876be788938b528e9a1d43900b82c2569af7da799125" [[package]] name = "embed-resource" @@ -1087,105 +762,12 @@ version = "1.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4ef6b89e5b37196644d8796de5268852ff179b44e96276cf4290264843743bb7" -[[package]] -name = "encoding_rs" -version = "0.8.33" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7268b386296a025e474d5140678f75d6de9493ae55a5d709eeb9dd08149945e1" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "enumflags2" -version = "0.7.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5998b4f30320c9d93aed72f63af821bfdac50465b75428fce77b48ec482c3939" -dependencies = [ - "enumflags2_derive", - "serde", -] - -[[package]] -name = "enumflags2_derive" -version = "0.7.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f95e2801cd355d4a1a3e3953ce6ee5ae9603a5c833455343a8bfe3f44d418246" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.48", -] - [[package]] name = "equivalent" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" -[[package]] -name = "errno" -version = "0.3.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245" -dependencies = [ - "libc", - "windows-sys 0.52.0", -] - -[[package]] -name = "event-listener" -version = "2.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" - -[[package]] -name = "event-listener" -version = "3.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d93877bcde0eb80ca09131a08d23f0a5c18a620b01db137dba666d18cd9b30c2" -dependencies = [ - "concurrent-queue", - "parking", - "pin-project-lite", -] - -[[package]] -name = "event-listener" -version = "4.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67b215c49b2b248c855fb73579eb1f4f26c38ffdc12973e20e07b91d78d5646e" -dependencies = [ - "concurrent-queue", - "parking", - "pin-project-lite", -] - -[[package]] -name = "event-listener-strategy" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "958e4d70b6d5e81971bebec42271ec641e7ff4e170a6fa605f2b8a8b65cb97d3" -dependencies = [ - "event-listener 4.0.3", - "pin-project-lite", -] - -[[package]] -name = "fastrand" -version = "1.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e51093e27b0797c359783294ca4f0a911c270184cb10f85783b118614a1501be" -dependencies = [ - "instant", -] - -[[package]] -name = "fastrand" -version = "2.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5" - [[package]] name = "fdeflate" version = "0.3.4" @@ -1201,7 +783,7 @@ version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "38e2275cc4e4fc009b0669731a1e5ab7ebf11f469eaede2bab9309a5b4d6057f" dependencies = [ - "memoffset 0.9.0", + "memoffset", "rustc_version", ] @@ -1239,7 +821,7 @@ checksum = "1a5c6c585bc94aaf2c7b51dd4c2ba22680844aba4c687be581871a6f518c5742" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.52", ] [[package]] @@ -1299,34 +881,6 @@ version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1" -[[package]] -name = "futures-lite" -version = "1.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49a9d51ce47660b1e808d3c990b4709f2f415d928835a17dfd16991515c46bce" -dependencies = [ - "fastrand 1.9.0", - "futures-core", - "futures-io", - "memchr", - "parking", - "pin-project-lite", - "waker-fn", -] - -[[package]] -name = "futures-lite" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "445ba825b27408685aaecefd65178908c36c6e96aaf6d8599419d46e624192ba" -dependencies = [ - "fastrand 2.0.1", - "futures-core", - "futures-io", - "parking", - "pin-project-lite", -] - [[package]] name = "futures-macro" version = "0.3.30" @@ -1335,7 +889,7 @@ checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.52", ] [[package]] @@ -1477,15 +1031,16 @@ dependencies = [ [[package]] name = "generator" -version = "0.7.5" +version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5cc16584ff22b460a382b7feec54b23d2908d858152e5739a120b949293bd74e" +checksum = "b5b25e5b3e733153bcab35ee4671b46604b42516163cae442d1601cb716f2ac5" dependencies = [ "cc", + "cfg-if", "libc", "log", "rustversion", - "windows 0.48.0", + "windows 0.53.0", ] [[package]] @@ -1498,16 +1053,6 @@ dependencies = [ "version_check", ] -[[package]] -name = "gethostname" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0176e0459c2e4a1fe232f984bca6890e681076abb9934f6cea7c326f3fc47818" -dependencies = [ - "libc", - "windows-targets 0.48.5", -] - [[package]] name = "getrandom" version = "0.1.16" @@ -1575,7 +1120,7 @@ dependencies = [ "gobject-sys", "libc", "system-deps", - "winapi 0.3.9", + "winapi", ] [[package]] @@ -1608,11 +1153,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0bb0228f477c0900c880fd78c8759b95c7636dbd7842707f49e132378aa2acdc" dependencies = [ "heck", - "proc-macro-crate 2.0.1", + "proc-macro-crate 2.0.2", "proc-macro-error", "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.52", ] [[package]] @@ -1691,26 +1236,7 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn 2.0.48", -] - -[[package]] -name = "h2" -version = "0.3.24" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb2c4422095b67ee78da96fbb51a4cc413b3b25883c7717ff7ca1ab31022c9c9" -dependencies = [ - "bytes", - "fnv", - "futures-core", - "futures-sink", - "futures-util", - "http", - "indexmap 2.1.0", - "slab", - "tokio", - "tokio-util", - "tracing", + "syn 2.0.52", ] [[package]] @@ -1733,9 +1259,9 @@ checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" [[package]] name = "hermit-abi" -version = "0.3.4" +version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d3d0e0f38255e7fa3cf31335b3a56f05febd18025f4db5ef7a0cfb4f8da651f" +checksum = "379dada1584ad501b383485dd706b8afb7a70fcbc7f4da7d780638a5a6124a60" [[package]] name = "hex" @@ -1759,9 +1285,9 @@ dependencies = [ [[package]] name = "http" -version = "0.2.11" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8947b1a6fad4393052c7ba1f4cd97bed3e953a95c79c92ad9b051a04611d9fbb" +checksum = "21b9ddb458710bc376481b842f5da65cdf31522de232c1ca8146abce2a358258" dependencies = [ "bytes", "fnv", @@ -1770,12 +1296,24 @@ dependencies = [ [[package]] name = "http-body" -version = "0.4.6" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2" +checksum = "1cac85db508abc24a2e48553ba12a996e87244a0395ce011e62b37158745d643" dependencies = [ "bytes", "http", +] + +[[package]] +name = "http-body-util" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0475f8b2ac86659c21b64320d5d653f9efe42acd2a4e560073ec61a155a34f1d" +dependencies = [ + "bytes", + "futures-core", + "http", + "http-body", "pin-project-lite", ] @@ -1791,48 +1329,57 @@ version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" -[[package]] -name = "httpdate" -version = "1.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" - [[package]] name = "hyper" -version = "0.14.28" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf96e135eb83a2a8ddf766e426a841d8ddd7449d5f00d34ea02b41d2f19eef80" +checksum = "186548d73ac615b32a73aafe38fb4f56c0d340e110e5a200bcadbaf2e199263a" dependencies = [ "bytes", "futures-channel", - "futures-core", "futures-util", - "h2", "http", "http-body", "httparse", - "httpdate", "itoa 1.0.10", "pin-project-lite", - "socket2 0.5.5", + "smallvec", "tokio", - "tower-service", - "tracing", "want", ] [[package]] -name = "iana-time-zone" -version = "0.1.59" +name = "hyper-util" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6a67363e2aa4443928ce15e57ebae94fd8949958fd1223c4cfc0cd473ad7539" +checksum = "ca38ef113da30126bbff9cd1705f9273e15d45498615d138b0c20279ac7a76aa" +dependencies = [ + "bytes", + "futures-channel", + "futures-util", + "http", + "http-body", + "hyper", + "pin-project-lite", + "socket2", + "tokio", + "tower", + "tower-service", + "tracing", +] + +[[package]] +name = "iana-time-zone" +version = "0.1.60" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7ffbb5a1b541ea2561f8c41c087286cc091e21e556a4f09a8f6cbf17b69b141" dependencies = [ "android_system_properties", "core-foundation-sys", "iana-time-zone-haiku", "js-sys", "wasm-bindgen", - "windows-core", + "windows-core 0.52.0", ] [[package]] @@ -1872,14 +1419,15 @@ dependencies = [ [[package]] name = "image" -version = "0.24.8" +version = "0.24.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "034bbe799d1909622a74d1193aa50147769440040ff36cb2baa947609b0a4e23" +checksum = "5690139d2f55868e080017335e4b94cb7414274c74f1669c84fb5feba2c9f69d" dependencies = [ "bytemuck", "byteorder", "color_quant", "num-traits", + "png", ] [[package]] @@ -1895,9 +1443,9 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.1.0" +version = "2.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d530e1a18b1cb4c484e6e34556a0d948706958449fca0cab753d649f2bce3d1f" +checksum = "233cf39063f058ea2caae4091bf4a3ef70a653afbc026f5c4a4135d114e3c177" dependencies = [ "equivalent", "hashbrown 0.14.3", @@ -1931,17 +1479,6 @@ dependencies = [ "cfg-if", ] -[[package]] -name = "io-lifetimes" -version = "1.0.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2" -dependencies = [ - "hermit-abi", - "libc", - "windows-sys 0.48.0", -] - [[package]] name = "ipnet" version = "2.9.0" @@ -1995,7 +1532,7 @@ dependencies = [ "jni-sys", "log", "thiserror", - "walkdir 2.4.0", + "walkdir", "windows-sys 0.45.0", ] @@ -2007,9 +1544,9 @@ checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" [[package]] name = "js-sys" -version = "0.3.67" +version = "0.3.68" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a1d36f1235bc969acba30b7f5990b864423a6068a10f7c90ae8f0112e3a59d1" +checksum = "406cda4b368d531c842222cf9d2600a9a4acce8d29423695379c6868a143a9ee" dependencies = [ "wasm-bindgen", ] @@ -2026,16 +1563,6 @@ dependencies = [ "treediff", ] -[[package]] -name = "kernel32-sys" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d" -dependencies = [ - "winapi 0.2.8", - "winapi-build", -] - [[package]] name = "keyboard-types" version = "0.7.0" @@ -2092,9 +1619,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.152" +version = "0.2.153" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13e3bf6590cbc649f4d1a3eefc9d5d6eb746f5200ffb04e5e142700b8faa56e7" +checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" [[package]] name = "libloading" @@ -2103,7 +1630,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b67380fd3b2fbe7527a606e18729d21c6f3951633d0500574c4dc22d2d638b9f" dependencies = [ "cfg-if", - "winapi 0.3.9", + "winapi", ] [[package]] @@ -2136,24 +1663,6 @@ dependencies = [ "safemem", ] -[[package]] -name = "linux-raw-sys" -version = "0.3.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519" - -[[package]] -name = "linux-raw-sys" -version = "0.4.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c" - -[[package]] -name = "linux-raw-sys" -version = "0.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0b5399f6804fbab912acbd8878ed3532d506b7c951b8f9f164ef90fef39e3f4" - [[package]] name = "lock_api" version = "0.4.11" @@ -2166,9 +1675,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.20" +version = "0.4.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" +checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" [[package]] name = "loom" @@ -2235,24 +1744,6 @@ version = "2.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149" -[[package]] -name = "memmap2" -version = "0.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "45fd3a57831bf88bc63f8cebc0cf956116276e97fef3966103e96416209f7c92" -dependencies = [ - "libc", -] - -[[package]] -name = "memoffset" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5de893c32cde5f383baa4c04c5d6dbdd735cfd4a794b0debdb2bb1b421da5ff4" -dependencies = [ - "autocfg", -] - [[package]] name = "memoffset" version = "0.9.0" @@ -2270,9 +1761,9 @@ checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" [[package]] name = "miniz_oxide" -version = "0.7.1" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" +checksum = "9d811f3e15f28568be3407c8e7fdb6514c1cda3cb30683f15b6a1a1dc4ea14a7" dependencies = [ "adler", "simd-adler32", @@ -2291,12 +1782,13 @@ dependencies = [ [[package]] name = "muda" -version = "0.11.4" +version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e406691fa7749604bbc7964bde28a300572d52621bb84540f6907c0f8fe08737" +checksum = "f428b4e9db3d17e2f809dfb1ff9ddfbbf16c71790d1656d10aee320877e1392f" dependencies = [ "cocoa", "crossbeam-channel", + "dpi", "gtk", "keyboard-types", "objc", @@ -2342,18 +1834,6 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e4a24736216ec316047a1fc4252e27dabb04218aa4a3f37c6e7ddbf1f9782b54" -[[package]] -name = "nix" -version = "0.26.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "598beaf3cc6fdd9a5dfb1630c2800c7acd31df7aaf0f565796fba2b53ca1af1b" -dependencies = [ - "bitflags 1.3.2", - "cfg-if", - "libc", - "memoffset 0.7.1", -] - [[package]] name = "nodrop" version = "0.1.14" @@ -2367,14 +1847,20 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" dependencies = [ "overload", - "winapi 0.3.9", + "winapi", ] [[package]] -name = "num-traits" -version = "0.2.17" +name = "num-conv" +version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c" +checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" + +[[package]] +name = "num-traits" +version = "0.2.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da0df0e5185db44f69b44f26786fe401b6c293d1907744beaa7fa62b2e5a517a" dependencies = [ "autocfg", ] @@ -2459,16 +1945,6 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" -[[package]] -name = "ordered-stream" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9aa2b01e1d916879f73a53d01d1d6cee68adbb31d6d9177a8cfce093cced1d50" -dependencies = [ - "futures-core", - "pin-project-lite", -] - [[package]] name = "overload" version = "0.1.1" @@ -2500,12 +1976,6 @@ dependencies = [ "system-deps", ] -[[package]] -name = "parking" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb813b8af86854136c6922af0598d719255ecb2179515e6e7730d468f05c9cae" - [[package]] name = "parking_lot" version = "0.12.1" @@ -2639,7 +2109,7 @@ dependencies = [ "phf_shared 0.11.2", "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.52", ] [[package]] @@ -2669,6 +2139,26 @@ dependencies = [ "siphasher", ] +[[package]] +name = "pin-project" +version = "1.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6bf43b791c5b9e34c3d182969b4abb522f9343702850a2e57f460d00d09b4b3" +dependencies = [ + "pin-project-internal", +] + +[[package]] +name = "pin-project-internal" +version = "1.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.52", +] + [[package]] name = "pin-project-lite" version = "0.2.13" @@ -2681,22 +2171,11 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" -[[package]] -name = "piper" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "668d31b1c4eba19242f2088b2bf3316b82ca31082a8335764db4e083db7485d4" -dependencies = [ - "atomic-waker", - "fastrand 2.0.1", - "futures-io", -] - [[package]] name = "pkg-config" -version = "0.3.29" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2900ede94e305130c13ddd391e0ab7cbaeb783945ae07a279c268cb05109c6cb" +checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" [[package]] name = "plist" @@ -2704,19 +2183,19 @@ version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e5699cc8a63d1aa2b1ee8e12b9ad70ac790d65788cd36101fa37f87ea46c4cef" dependencies = [ - "base64", - "indexmap 2.1.0", + "base64 0.21.7", + "indexmap 2.2.3", "line-wrap", - "quick-xml 0.31.0", + "quick-xml", "serde", "time", ] [[package]] name = "png" -version = "0.17.11" +version = "0.17.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f6c3c3e617595665b8ea2ff95a86066be38fb121ff920a9c0eb282abcd1da5a" +checksum = "06e4b0d3d1312775e782c86c91a111aa1f910cbb65e1337f9975b5f9a554b5e1" dependencies = [ "bitflags 1.3.2", "crc32fast", @@ -2725,36 +2204,6 @@ dependencies = [ "miniz_oxide", ] -[[package]] -name = "polling" -version = "2.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b2d323e8ca7996b3e23126511a523f7e62924d93ecd5ae73b333815b0eb3dce" -dependencies = [ - "autocfg", - "bitflags 1.3.2", - "cfg-if", - "concurrent-queue", - "libc", - "log", - "pin-project-lite", - "windows-sys 0.48.0", -] - -[[package]] -name = "polling" -version = "3.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "545c980a3880efd47b2e262f6a4bb6daad6555cf3367aa9c4e52895f69537a41" -dependencies = [ - "cfg-if", - "concurrent-queue", - "pin-project-lite", - "rustix 0.38.30", - "tracing", - "windows-sys 0.52.0", -] - [[package]] name = "polyval" version = "0.6.1" @@ -2797,9 +2246,9 @@ dependencies = [ [[package]] name = "proc-macro-crate" -version = "2.0.1" +version = "2.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97dc5fea232fc28d2f597b37c4876b348a40e33f3b02cc975c8d006d78d94b1a" +checksum = "b00f26d3400549137f92511a46ac1cd8ce37cb5598a96d382381458b992a5d24" dependencies = [ "toml_datetime", "toml_edit 0.20.2", @@ -2844,15 +2293,6 @@ dependencies = [ "unicode-ident", ] -[[package]] -name = "quick-xml" -version = "0.30.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eff6510e86862b57b210fd8cbe8ed3f0d7d600b9c2863cd4549a2e033c66e956" -dependencies = [ - "memchr", -] - [[package]] name = "quick-xml" version = "0.31.0" @@ -2992,7 +2432,7 @@ checksum = "b62dbe01f0b06f9d8dc7d49e05a0785f153b00b2c227856282f671e0318c9b15" dependencies = [ "aho-corasick", "memchr", - "regex-automata 0.4.4", + "regex-automata 0.4.5", "regex-syntax 0.8.2", ] @@ -3007,9 +2447,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.4" +version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b7fa1134405e2ec9353fd416b17f8dacd46c473d7d3fd1cf202706a14eb792a" +checksum = "5bb987efffd3c6d0d8f5f89510bb458559eab11e4f869acb20bf845e016259cd" dependencies = [ "aho-corasick", "memchr", @@ -3030,19 +2470,19 @@ checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" [[package]] name = "reqwest" -version = "0.11.23" +version = "0.12.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37b1ae8d9ac08420c66222fb9096fc5de435c3c48542bc5336c51892cffafb41" +checksum = "2d66674f2b6fb864665eea7a3c1ac4e3dfacd2fda83cf6f935a612e01b0e3338" dependencies = [ - "base64", + "base64 0.21.7", "bytes", - "encoding_rs", "futures-core", "futures-util", - "h2", "http", "http-body", + "http-body-util", "hyper", + "hyper-util", "ipnet", "js-sys", "log", @@ -3053,7 +2493,7 @@ dependencies = [ "serde", "serde_json", "serde_urlencoded", - "system-configuration", + "sync_wrapper", "tokio", "tokio-util", "tower-service", @@ -3080,33 +2520,6 @@ dependencies = [ "semver", ] -[[package]] -name = "rustix" -version = "0.37.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fea8ca367a3a01fe35e6943c400addf443c0f57670e6ec51196f71a4b8762dd2" -dependencies = [ - "bitflags 1.3.2", - "errno", - "io-lifetimes", - "libc", - "linux-raw-sys 0.3.8", - "windows-sys 0.48.0", -] - -[[package]] -name = "rustix" -version = "0.38.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "322394588aaf33c24007e8bb3238ee3e4c5c09c084ab32bc73890b99ff326bca" -dependencies = [ - "bitflags 2.4.2", - "errno", - "libc", - "linux-raw-sys 0.4.13", - "windows-sys 0.52.0", -] - [[package]] name = "rustversion" version = "1.0.14" @@ -3115,9 +2528,9 @@ checksum = "7ffc183a10b4478d04cbbbfc96d0873219d962dd5accaff2ffbd4ceb7df837f4" [[package]] name = "ryu" -version = "1.0.16" +version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f98d2aa92eebf49b69786be48e4477826b256916e84a57ff2a4f21923b48eb4c" +checksum = "e86697c916019a8588c99b5fac3cead74ec0b4b819707a682fd4d23fa0ce1ba1" [[package]] name = "safemem" @@ -3125,16 +2538,6 @@ version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ef703b7cb59335eae2eb93ceb664c0eb7ea6bf567079d843e09420219668e072" -[[package]] -name = "same-file" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d931a44fdaa43b8637009e7632a02adc4f2b2e0733c08caa4cf00e8da4a117a7" -dependencies = [ - "kernel32-sys", - "winapi 0.2.8", -] - [[package]] name = "same-file" version = "1.0.6" @@ -3204,31 +2607,31 @@ dependencies = [ [[package]] name = "semver" -version = "1.0.21" +version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b97ed7a9823b74f99c7742f5336af7be5ecd3eeafcb1507d1fa93347b1d589b0" +checksum = "92d43fe69e652f3df9bdc2b85b2854a0825b86e4fb76bc44d945137d053639ca" dependencies = [ "serde", ] [[package]] name = "serde" -version = "1.0.195" +version = "1.0.197" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "63261df402c67811e9ac6def069e4786148c4563f4b50fd4bf30aa370d626b02" +checksum = "3fb1c873e1b9b056a4dc4c0c198b24c3ffa059243875552b2bd0933b1aee4ce2" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.195" +version = "1.0.197" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46fe8f8603d81ba86327b23a2e9cdf49e1255fb94a4c5f297f6ee0547178ea2c" +checksum = "7eb0b34b42edc17f6b7cac84a52a1c5f0e1bb2227e997ca9011ea3dd34e8610b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.52", ] [[package]] @@ -3244,9 +2647,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.111" +version = "1.0.114" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "176e46fa42316f18edd598015a5166857fc835ec732f5215eac6b7bdbf0a84f4" +checksum = "c5f09b1bd632ef549eaa9f60a1f8de742bdbc698e6cee2095fc84dde5f549ae0" dependencies = [ "itoa 1.0.10", "ryu", @@ -3261,7 +2664,7 @@ checksum = "0b2e6b945e9d3df726b65d6ee24060aff8e3533d431f677a9695db04eff9dfdb" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.52", ] [[package]] @@ -3287,16 +2690,17 @@ dependencies = [ [[package]] name = "serde_with" -version = "3.5.1" +version = "3.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f5c9fdb6b00a489875b22efd4b78fe2b363b72265cc5f6eb2e2b9ee270e6140c" +checksum = "15d167997bd841ec232f5b2b8e0e26606df2e7caa4c31b95ea9ca52b200bd270" dependencies = [ - "base64", + "base64 0.21.7", "chrono", "hex", "indexmap 1.9.3", - "indexmap 2.1.0", + "indexmap 2.2.3", "serde", + "serde_derive", "serde_json", "serde_with_macros", "time", @@ -3304,14 +2708,14 @@ dependencies = [ [[package]] name = "serde_with_macros" -version = "3.5.1" +version = "3.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dbff351eb4b33600a2e138dfa0b10b65a238ea8ff8fb2387c422c5022a3e8298" +checksum = "865f9743393e638991566a8b7a479043c2c8da94a33e0a31f18214c9cae0a64d" dependencies = [ "darling", "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.52", ] [[package]] @@ -3346,17 +2750,6 @@ dependencies = [ "stable_deref_trait", ] -[[package]] -name = "sha1" -version = "0.10.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" -dependencies = [ - "cfg-if", - "cpufeatures", - "digest", -] - [[package]] name = "sha2" version = "0.10.8" @@ -3377,15 +2770,6 @@ dependencies = [ "lazy_static", ] -[[package]] -name = "signal-hook-registry" -version = "1.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1" -dependencies = [ - "libc", -] - [[package]] name = "simd-adler32" version = "0.3.7" @@ -3415,22 +2799,12 @@ checksum = "e6ecd384b10a64542d77071bd64bd7b231f4ed5940fba55e98c3de13824cf3d7" [[package]] name = "socket2" -version = "0.4.10" +version = "0.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f7916fc008ca5542385b89a3d3ce689953c143e9304a9bf8beec1de48994c0d" +checksum = "05ffd9c0a93b7543e062e759284fcf5f5e3b098501104bfbdde4d404db792871" dependencies = [ "libc", - "winapi 0.3.9", -] - -[[package]] -name = "socket2" -version = "0.5.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b5fac59a5cb5dd637972e5fca70daf0523c9067fcdc4842f053dae04a18f8e9" -dependencies = [ - "libc", - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] @@ -3439,29 +2813,20 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "071916a85d1db274b4ed57af3a14afb66bd836ae7f82ebb6f1fd3455107830d9" dependencies = [ - "as-raw-xcb-connection", "bytemuck", "cfg_aliases 0.2.0", "cocoa", "core-graphics", - "drm", - "fastrand 2.0.1", "foreign-types", "js-sys", "log", - "memmap2", "objc", "raw-window-handle 0.6.0", "redox_syscall", - "rustix 0.38.30", - "tiny-xlib", "wasm-bindgen", - "wayland-backend", - "wayland-client", "wayland-sys", "web-sys", "windows-sys 0.52.0", - "x11rb", ] [[package]] @@ -3505,12 +2870,6 @@ dependencies = [ "loom", ] -[[package]] -name = "static_assertions" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" - [[package]] name = "string_cache" version = "0.8.7" @@ -3555,7 +2914,7 @@ version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1bbdb58577b6301f8d17ae2561f32002a5bae056d444e0f69e611e504a276204" dependencies = [ - "base64", + "base64 0.21.7", "serde", "serde_json", ] @@ -3573,9 +2932,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.48" +version = "2.0.52" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f3531638e407dfc0814761abb7c00a5b54992b849452a0646b7f65c9f770f3f" +checksum = "b699d15b36d1f02c3e7c69f8ffef53de37aefae075d8488d4ba1a7788d574a07" dependencies = [ "proc-macro2", "quote", @@ -3583,25 +2942,10 @@ dependencies = [ ] [[package]] -name = "system-configuration" -version = "0.5.1" +name = "sync_wrapper" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7" -dependencies = [ - "bitflags 1.3.2", - "core-foundation", - "system-configuration-sys", -] - -[[package]] -name = "system-configuration-sys" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a75fb188eb626b924683e3b95e3a48e63551fcfb51949de2f06a9d91dbee93c9" -dependencies = [ - "core-foundation-sys", - "libc", -] +checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" [[package]] name = "system-deps" @@ -3618,21 +2962,21 @@ dependencies = [ [[package]] name = "tao" -version = "0.25.0" +version = "0.27.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fa7ba6ee5b8908ba3a62e6a4f3683490ed732fca614cdd3f4c989bba548f9a9" +checksum = "bd5b6ec2c43abd15155f040c765001098f50f425414b679225d471a1cd782753" dependencies = [ "bitflags 1.3.2", - "cc", "cocoa", "core-foundation", "core-graphics", "crossbeam-channel", "dispatch", + "dlopen2", + "dpi", "gdkwayland-sys", "gdkx11-sys", "gtk", - "image", "instant", "jni", "lazy_static", @@ -3644,18 +2988,14 @@ dependencies = [ "objc", "once_cell", "parking_lot", - "png", - "raw-window-handle 0.5.2", "raw-window-handle 0.6.0", "scopeguard", "tao-macros", "unicode-segmentation", "url", - "windows 0.52.0", - "windows-implement", + "windows 0.54.0", "windows-version", "x11-dl", - "zbus", ] [[package]] @@ -3671,13 +3011,13 @@ dependencies = [ [[package]] name = "target-lexicon" -version = "0.12.13" +version = "0.12.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69758bda2e78f098e4ccb393021a0963bb3442eac05f135c30f61b7370bbafae" +checksum = "e1fc403891a21bcfb7c37834ba66a547a8f402146eba7265b5a6d88059c9ff2f" [[package]] name = "tauri" -version = "2.0.0-beta.2" +version = "2.0.0-beta.13" dependencies = [ "anyhow", "bytes", @@ -3691,8 +3031,7 @@ dependencies = [ "heck", "http", "http-range", - "ico", - "infer", + "image", "jni", "libc", "log", @@ -3700,15 +3039,13 @@ dependencies = [ "muda", "objc", "percent-encoding", - "png", - "raw-window-handle 0.5.2", + "raw-window-handle 0.6.0", "reqwest", "serde", "serde_json", "serde_repr", "serialize-to-javascript", "state", - "static_assertions", "swift-rs", "tauri-build", "tauri-macros", @@ -3719,16 +3056,17 @@ dependencies = [ "tokio", "tray-icon", "url", + "urlpattern", "uuid", "webkit2gtk", "webview2-com", "window-vibrancy", - "windows 0.52.0", + "windows 0.54.0", ] [[package]] name = "tauri-build" -version = "2.0.0-beta.1" +version = "2.0.0-beta.10" dependencies = [ "anyhow", "cargo_toml", @@ -3745,14 +3083,14 @@ dependencies = [ "tauri-utils", "tauri-winres", "toml 0.8.2", - "walkdir 2.4.0", + "walkdir", ] [[package]] name = "tauri-codegen" -version = "2.0.0-beta.1" +version = "2.0.0-beta.10" dependencies = [ - "base64", + "base64 0.22.0", "brotli", "ico", "json-patch", @@ -3764,29 +3102,30 @@ dependencies = [ "serde", "serde_json", "sha2", + "syn 2.0.52", "tauri-utils", "thiserror", "time", "url", "uuid", - "walkdir 2.4.0", + "walkdir", ] [[package]] name = "tauri-macros" -version = "2.0.0-beta.1" +version = "2.0.0-beta.10" dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.52", "tauri-codegen", "tauri-utils", ] [[package]] name = "tauri-plugin" -version = "2.0.0-beta.1" +version = "2.0.0-beta.10" dependencies = [ "anyhow", "glob", @@ -3796,20 +3135,7 @@ dependencies = [ "serde_json", "tauri-utils", "toml 0.8.2", - "walkdir 1.0.7", -] - -[[package]] -name = "tauri-plugin-cli" -version = "2.0.0-alpha.6" -source = "git+https://github.com/tauri-apps/plugins-workspace?branch=v2#61edbbec0acda4213ed8684f75a973e8be123a52" -dependencies = [ - "clap", - "log", - "serde", - "serde_json", - "tauri", - "thiserror", + "walkdir", ] [[package]] @@ -3825,43 +3151,46 @@ dependencies = [ [[package]] name = "tauri-runtime" -version = "2.0.0-beta.1" +version = "2.0.0-beta.10" dependencies = [ + "dpi", "gtk", "http", "jni", - "raw-window-handle 0.5.2", + "raw-window-handle 0.6.0", "serde", "serde_json", "tauri-utils", "thiserror", "url", - "windows 0.52.0", + "windows 0.54.0", ] [[package]] name = "tauri-runtime-wry" -version = "2.0.0-beta.1" +version = "2.0.0-beta.10" dependencies = [ "cocoa", "gtk", "http", "jni", + "log", "percent-encoding", - "raw-window-handle 0.5.2", + "raw-window-handle 0.6.0", "softbuffer", "tao", "tauri-runtime", "tauri-utils", + "url", "webkit2gtk", "webview2-com", - "windows 0.52.0", + "windows 0.54.0", "wry", ] [[package]] name = "tauri-utils" -version = "2.0.0-beta.1" +version = "2.0.0-beta.10" dependencies = [ "aes-gcm", "brotli", @@ -3880,6 +3209,7 @@ dependencies = [ "phf 0.11.2", "proc-macro2", "quote", + "regex", "schemars", "semver", "serde", @@ -3890,7 +3220,8 @@ dependencies = [ "thiserror", "toml 0.8.2", "url", - "walkdir 2.4.0", + "urlpattern", + "walkdir", ] [[package]] @@ -3903,19 +3234,6 @@ dependencies = [ "toml 0.7.8", ] -[[package]] -name = "tempfile" -version = "3.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01ce4141aa927a6d1bd34a041795abd0db1cccba5d5f24b009f694bdf3a1f3fa" -dependencies = [ - "cfg-if", - "fastrand 2.0.1", - "redox_syscall", - "rustix 0.38.30", - "windows-sys 0.52.0", -] - [[package]] name = "tendril" version = "0.4.3" @@ -3935,29 +3253,29 @@ checksum = "8eaa81235c7058867fa8c0e7314f33dcce9c215f535d1913822a2b3f5e289f3c" [[package]] name = "thiserror" -version = "1.0.56" +version = "1.0.57" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d54378c645627613241d077a3a79db965db602882668f9136ac42af9ecb730ad" +checksum = "1e45bcbe8ed29775f228095caf2cd67af7a4ccf756ebff23a306bf3e8b47b24b" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.56" +version = "1.0.57" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa0faa943b50f3db30a20aa7e265dbc66076993efed8463e8de414e5d06d3471" +checksum = "a953cb265bef375dae3de6663da4d3804eee9682ea80d8e2542529b73c531c81" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.52", ] [[package]] name = "thread_local" -version = "1.1.7" +version = "1.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fdd6f064ccff2d6567adcb3873ca630700f00b5ad3f060c25b5dcfd9a4ce152" +checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c" dependencies = [ "cfg-if", "once_cell", @@ -3965,12 +3283,13 @@ dependencies = [ [[package]] name = "time" -version = "0.3.31" +version = "0.3.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f657ba42c3f86e7680e53c8cd3af8abbe56b5491790b46e22e19c0d57463583e" +checksum = "c8248b6521bb14bc45b4067159b9b6ad792e2d6d754d6c41fb50e29fefe38749" dependencies = [ "deranged", "itoa 1.0.10", + "num-conv", "powerfmt", "serde", "time-core", @@ -3985,25 +3304,14 @@ checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" [[package]] name = "time-macros" -version = "0.2.16" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26197e33420244aeb70c3e8c78376ca46571bc4e701e4791c2cd9f57dcb3a43f" +checksum = "7ba3a3ef41e6672a2f0f001392bb5dcd3ff0a9992d618ca761a11c3121547774" dependencies = [ + "num-conv", "time-core", ] -[[package]] -name = "tiny-xlib" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4098d49269baa034a8d1eae9bd63e9fa532148d772121dace3bcd6a6c98eb6d" -dependencies = [ - "as-raw-xcb-connection", - "ctor", - "libloading 0.8.1", - "tracing", -] - [[package]] name = "tiny_http" version = "0.11.0" @@ -4034,9 +3342,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.35.1" +version = "1.36.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c89b4efa943be685f629b149f53829423f8f5531ea21249408e8e2f8671ec104" +checksum = "61285f6515fa018fb2d1e46eb21223fff441ee8db5d0f1435e8ab4f5cdb80931" dependencies = [ "backtrace", "bytes", @@ -4044,7 +3352,7 @@ dependencies = [ "mio", "num_cpus", "pin-project-lite", - "socket2 0.5.5", + "socket2", "windows-sys 0.48.0", ] @@ -4101,7 +3409,7 @@ version = "0.19.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" dependencies = [ - "indexmap 2.1.0", + "indexmap 2.2.3", "serde", "serde_spanned", "toml_datetime", @@ -4114,13 +3422,35 @@ version = "0.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "396e4d48bbb2b7554c944bde63101b5ae446cff6ec4a24227428f15eb72ef338" dependencies = [ - "indexmap 2.1.0", + "indexmap 2.2.3", "serde", "serde_spanned", "toml_datetime", "winnow", ] +[[package]] +name = "tower" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c" +dependencies = [ + "futures-core", + "futures-util", + "pin-project", + "pin-project-lite", + "tokio", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "tower-layer" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c20c8dbed6283a09604c3e69b4b7eeb54e298b8a600d4d5ecb5ad39de609f1d0" + [[package]] name = "tower-service" version = "0.3.2" @@ -4133,6 +3463,7 @@ version = "0.1.40" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" dependencies = [ + "log", "pin-project-lite", "tracing-attributes", "tracing-core", @@ -4146,7 +3477,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.52", ] [[package]] @@ -4190,9 +3521,9 @@ dependencies = [ [[package]] name = "tray-icon" -version = "0.11.1" +version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fad962d06d2bfd9b2ab4f665fc73b175523b834b1466a294520201c5845145f8" +checksum = "8713f74e697917aa794800289e15bce534fc91450312ab2d3edf5b8907f7301a" dependencies = [ "cocoa", "core-graphics", @@ -4210,9 +3541,9 @@ dependencies = [ [[package]] name = "treediff" -version = "4.0.2" +version = "4.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52984d277bdf2a751072b5df30ec0377febdb02f7696d64c2d7d54630bac4303" +checksum = "4d127780145176e2b5d16611cc25a900150e86e9fd79d3bde6ff3a37359c9cb5" dependencies = [ "serde_json", ] @@ -4230,14 +3561,44 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" [[package]] -name = "uds_windows" -version = "1.1.0" +name = "unic-char-property" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89daebc3e6fd160ac4aa9fc8b3bf71e1f74fbf92367ae71fb83a037e8bf164b9" +checksum = "a8c57a407d9b6fa02b4795eb81c5b6652060a15a7903ea981f3d723e6c0be221" dependencies = [ - "memoffset 0.9.0", - "tempfile", - "winapi 0.3.9", + "unic-char-range", +] + +[[package]] +name = "unic-char-range" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0398022d5f700414f6b899e10b8348231abf9173fa93144cbc1a43b9793c1fbc" + +[[package]] +name = "unic-common" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80d7ff825a6a654ee85a63e80f92f054f904f21e7d12da4e22f9834a4aaa35bc" + +[[package]] +name = "unic-ucd-ident" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e230a37c0381caa9219d67cf063aa3a375ffed5bf541a452db16e744bdab6987" +dependencies = [ + "unic-char-property", + "unic-char-range", + "unic-ucd-version", +] + +[[package]] +name = "unic-ucd-version" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96bd2f2237fe450fcd0a1d2f5f4e91711124f7857ba2e964247776ebeeb7b0c4" +dependencies = [ + "unic-common", ] [[package]] @@ -4254,18 +3615,18 @@ checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" [[package]] name = "unicode-normalization" -version = "0.1.22" +version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" +checksum = "a56d1686db2308d901306f92a263857ef59ea39678a5458e7cb17f01415101f5" dependencies = [ "tinyvec", ] [[package]] name = "unicode-segmentation" -version = "1.10.1" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36" +checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202" [[package]] name = "universal-hash" @@ -4289,18 +3650,25 @@ dependencies = [ "serde", ] +[[package]] +name = "urlpattern" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9bd5ff03aea02fa45b13a7980151fe45009af1980ba69f651ec367121a31609" +dependencies = [ + "derive_more", + "regex", + "serde", + "unic-ucd-ident", + "url", +] + [[package]] name = "utf-8" version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" -[[package]] -name = "utf8parse" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" - [[package]] name = "uuid" version = "1.7.0" @@ -4348,30 +3716,13 @@ dependencies = [ "libc", ] -[[package]] -name = "waker-fn" -version = "1.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3c4517f54858c779bbcbf228f4fca63d121bf85fbecb2dc578cdf4a39395690" - -[[package]] -name = "walkdir" -version = "1.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb08f9e670fab86099470b97cd2b252d6527f0b3cc1401acdb595ffc9dd288ff" -dependencies = [ - "kernel32-sys", - "same-file 0.1.3", - "winapi 0.2.8", -] - [[package]] name = "walkdir" version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d71d857dc86794ca4c280d616f7da00d2dbfd8cd788846559a6813e6aa4b54ee" dependencies = [ - "same-file 1.0.6", + "same-file", "winapi-util", ] @@ -4398,9 +3749,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.90" +version = "0.2.91" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1223296a201415c7fad14792dbefaace9bd52b62d33453ade1c5b5f07555406" +checksum = "c1e124130aee3fb58c5bdd6b639a0509486b0338acaaae0c84a5124b0f588b7f" dependencies = [ "cfg-if", "wasm-bindgen-macro", @@ -4408,24 +3759,24 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.90" +version = "0.2.91" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fcdc935b63408d58a32f8cc9738a0bffd8f05cc7c002086c6ef20b7312ad9dcd" +checksum = "c9e7e1900c352b609c8488ad12639a311045f40a35491fb69ba8c12f758af70b" dependencies = [ "bumpalo", "log", "once_cell", "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.52", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-futures" -version = "0.4.40" +version = "0.4.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bde2032aeb86bdfaecc8b261eef3cba735cc426c1f3a3416d1e0791be95fc461" +checksum = "877b9c3f61ceea0e56331985743b13f3d25c406a7098d45180fb5f09bc19ed97" dependencies = [ "cfg-if", "js-sys", @@ -4435,9 +3786,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.90" +version = "0.2.91" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e4c238561b2d428924c49815533a8b9121c664599558a5d9ec51f8a1740a999" +checksum = "b30af9e2d358182b5c7449424f017eba305ed32a7010509ede96cdc4696c46ed" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -4445,28 +3796,28 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.90" +version = "0.2.91" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bae1abb6806dc1ad9e560ed242107c0f6c84335f1749dd4e8ddb012ebd5e25a7" +checksum = "642f325be6301eb8107a83d12a8ac6c1e1c54345a7ef1a9261962dfefda09e66" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.52", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.90" +version = "0.2.91" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d91413b1c31d7539ba5ef2451af3f0b833a005eb27a631cec32bc0635a8602b" +checksum = "4f186bd2dcf04330886ce82d6f33dd75a7bfcf69ecf5763b89fcde53b6ac9838" [[package]] name = "wasm-streams" -version = "0.3.0" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4609d447824375f43e1ffbc051b50ad8f4b3ae8219680c94452ea05eb240ac7" +checksum = "b65dc4c90b63b118468cf747d8bf3566c1913ef60be765b5730ead9e0a3ba129" dependencies = [ "futures-util", "js-sys", @@ -4475,43 +3826,6 @@ dependencies = [ "web-sys", ] -[[package]] -name = "wayland-backend" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19152ddd73f45f024ed4534d9ca2594e0ef252c1847695255dae47f34df9fbe4" -dependencies = [ - "cc", - "downcast-rs", - "nix", - "scoped-tls", - "smallvec", - "wayland-sys", -] - -[[package]] -name = "wayland-client" -version = "0.31.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ca7d52347346f5473bf2f56705f360e8440873052e575e55890c4fa57843ed3" -dependencies = [ - "bitflags 2.4.2", - "nix", - "wayland-backend", - "wayland-scanner", -] - -[[package]] -name = "wayland-scanner" -version = "0.31.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb8e28403665c9f9513202b7e1ed71ec56fde5c107816843fb14057910b2c09c" -dependencies = [ - "proc-macro2", - "quick-xml 0.30.0", - "quote", -] - [[package]] name = "wayland-sys" version = "0.31.1" @@ -4520,15 +3834,14 @@ checksum = "15a0c8eaff5216d07f226cb7a549159267f3467b289d9a2e52fd3ef5aae2b7af" dependencies = [ "dlib", "log", - "once_cell", "pkg-config", ] [[package]] name = "web-sys" -version = "0.3.67" +version = "0.3.68" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58cd2333b6e0be7a39605f0e255892fd7418a682d8da8fe042fe25128794d2ed" +checksum = "96565907687f7aceb35bc5fc03770a8a0471d82e479f25832f54a0e3f4b28446" dependencies = [ "js-sys", "wasm-bindgen", @@ -4580,14 +3893,14 @@ dependencies = [ [[package]] name = "webview2-com" -version = "0.28.0" +version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0ae9c7e420783826cf769d2c06ac9ba462f450eca5893bb8c6c6529a4e5dd33" +checksum = "38d5949fc3f537e90240c3e4f78dda2fa0431b671d50845a2f582173ef8a1201" dependencies = [ "webview2-com-macros", "webview2-com-sys", - "windows 0.52.0", - "windows-core", + "windows 0.54.0", + "windows-core 0.54.0", "windows-implement", "windows-interface", ] @@ -4600,26 +3913,20 @@ checksum = "ac1345798ecd8122468840bcdf1b95e5dc6d2206c5e4b0eafa078d061f59c9bc" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.52", ] [[package]] name = "webview2-com-sys" -version = "0.28.0" +version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6ad85fceee6c42fa3d61239eba5a11401bf38407a849ed5ea1b407df08cca72" +checksum = "cd1eaa1be63d6fdcadf893c40d7d53c889a6342b3a94930d34e6964d5bb7e8db" dependencies = [ "thiserror", - "windows 0.52.0", - "windows-core", + "windows 0.54.0", + "windows-core 0.54.0", ] -[[package]] -name = "winapi" -version = "0.2.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a" - [[package]] name = "winapi" version = "0.3.9" @@ -4630,12 +3937,6 @@ dependencies = [ "winapi-x86_64-pc-windows-gnu", ] -[[package]] -name = "winapi-build" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc" - [[package]] name = "winapi-i686-pc-windows-gnu" version = "0.4.0" @@ -4648,7 +3949,7 @@ version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f29e6f9198ba0d26b4c9f07dbe6f9ed633e1f3d5b8b414090084349e46a52596" dependencies = [ - "winapi 0.3.9", + "winapi", ] [[package]] @@ -4659,36 +3960,37 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] name = "window-vibrancy" -version = "0.4.3" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af6abc2b9c56bd95887825a1ce56cde49a2a97c07e28db465d541f5098a2656c" +checksum = "33082acd404763b315866e14a0d5193f3422c81086657583937a750cdd3ec340" dependencies = [ "cocoa", "objc", - "raw-window-handle 0.5.2", + "raw-window-handle 0.6.0", "windows-sys 0.52.0", "windows-version", ] [[package]] name = "windows" -version = "0.48.0" +version = "0.53.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e686886bc078bc1b0b600cac0147aadb815089b6e4da64016cbd754b6342700f" +checksum = "efc5cf48f83140dcaab716eeaea345f9e93d0018fb81162753a3f76c3397b538" dependencies = [ - "windows-targets 0.48.5", + "windows-core 0.53.0", + "windows-targets 0.52.4", ] [[package]] name = "windows" -version = "0.52.0" +version = "0.54.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e48a53791691ab099e5e2ad123536d0fff50652600abaf43bbf952894110d0be" +checksum = "9252e5725dbed82865af151df558e754e4a3c2c30818359eb17465f1346a1b49" dependencies = [ - "windows-core", + "windows-core 0.54.0", "windows-implement", "windows-interface", - "windows-targets 0.52.0", + "windows-targets 0.52.4", ] [[package]] @@ -4697,29 +3999,58 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" dependencies = [ - "windows-targets 0.52.0", + "windows-targets 0.52.4", +] + +[[package]] +name = "windows-core" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9dcc5b895a6377f1ab9fa55acedab1fd5ac0db66ad1e6c7f47e28a22e446a5dd" +dependencies = [ + "windows-result", + "windows-targets 0.52.4", +] + +[[package]] +name = "windows-core" +version = "0.54.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12661b9c89351d684a50a8a643ce5f608e20243b9fb84687800163429f161d65" +dependencies = [ + "windows-result", + "windows-targets 0.52.4", ] [[package]] name = "windows-implement" -version = "0.52.0" +version = "0.53.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12168c33176773b86799be25e2a2ba07c7aab9968b37541f1094dbd7a60c8946" +checksum = "942ac266be9249c84ca862f0a164a39533dc2f6f33dc98ec89c8da99b82ea0bd" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.52", ] [[package]] name = "windows-interface" -version = "0.52.0" +version = "0.53.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d8dc32e0095a7eeccebd0e3f09e9509365ecb3fc6ac4d6f5f14a3f6392942d1" +checksum = "da33557140a288fae4e1d5f8873aaf9eb6613a9cf82c3e070223ff177f598b60" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.52", +] + +[[package]] +name = "windows-result" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd19df78e5168dfb0aedc343d1d1b8d422ab2db6756d2dc3fef75035402a3f64" +dependencies = [ + "windows-targets 0.52.4", ] [[package]] @@ -4746,7 +4077,7 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ - "windows-targets 0.52.0", + "windows-targets 0.52.4", ] [[package]] @@ -4781,17 +4112,17 @@ dependencies = [ [[package]] name = "windows-targets" -version = "0.52.0" +version = "0.52.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd" +checksum = "7dd37b7e5ab9018759f893a1952c9420d060016fc19a472b4bb20d1bdd694d1b" dependencies = [ - "windows_aarch64_gnullvm 0.52.0", - "windows_aarch64_msvc 0.52.0", - "windows_i686_gnu 0.52.0", - "windows_i686_msvc 0.52.0", - "windows_x86_64_gnu 0.52.0", - "windows_x86_64_gnullvm 0.52.0", - "windows_x86_64_msvc 0.52.0", + "windows_aarch64_gnullvm 0.52.4", + "windows_aarch64_msvc 0.52.4", + "windows_i686_gnu 0.52.4", + "windows_i686_msvc 0.52.4", + "windows_x86_64_gnu 0.52.4", + "windows_x86_64_gnullvm 0.52.4", + "windows_x86_64_msvc 0.52.4", ] [[package]] @@ -4800,7 +4131,7 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "75aa004c988e080ad34aff5739c39d0312f4684699d6d71fc8a198d057b8b9b4" dependencies = [ - "windows-targets 0.52.0", + "windows-targets 0.52.4", ] [[package]] @@ -4817,9 +4148,9 @@ checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" [[package]] name = "windows_aarch64_gnullvm" -version = "0.52.0" +version = "0.52.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea" +checksum = "bcf46cf4c365c6f2d1cc93ce535f2c8b244591df96ceee75d8e83deb70a9cac9" [[package]] name = "windows_aarch64_msvc" @@ -4835,9 +4166,9 @@ checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" [[package]] name = "windows_aarch64_msvc" -version = "0.52.0" +version = "0.52.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef" +checksum = "da9f259dd3bcf6990b55bffd094c4f7235817ba4ceebde8e6d11cd0c5633b675" [[package]] name = "windows_i686_gnu" @@ -4853,9 +4184,9 @@ checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" [[package]] name = "windows_i686_gnu" -version = "0.52.0" +version = "0.52.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313" +checksum = "b474d8268f99e0995f25b9f095bc7434632601028cf86590aea5c8a5cb7801d3" [[package]] name = "windows_i686_msvc" @@ -4871,9 +4202,9 @@ checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" [[package]] name = "windows_i686_msvc" -version = "0.52.0" +version = "0.52.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a" +checksum = "1515e9a29e5bed743cb4415a9ecf5dfca648ce85ee42e15873c3cd8610ff8e02" [[package]] name = "windows_x86_64_gnu" @@ -4889,9 +4220,9 @@ checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" [[package]] name = "windows_x86_64_gnu" -version = "0.52.0" +version = "0.52.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd" +checksum = "5eee091590e89cc02ad514ffe3ead9eb6b660aedca2183455434b93546371a03" [[package]] name = "windows_x86_64_gnullvm" @@ -4907,9 +4238,9 @@ checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" [[package]] name = "windows_x86_64_gnullvm" -version = "0.52.0" +version = "0.52.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e" +checksum = "77ca79f2451b49fa9e2af39f0747fe999fcda4f5e241b2898624dca97a1f2177" [[package]] name = "windows_x86_64_msvc" @@ -4925,15 +4256,15 @@ checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" [[package]] name = "windows_x86_64_msvc" -version = "0.52.0" +version = "0.52.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" +checksum = "32b752e52a2da0ddfbdbcc6fceadfeede4c939ed16d13e648833a61dfb611ed8" [[package]] name = "winnow" -version = "0.5.34" +version = "0.5.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7cf47b659b318dccbd69cc4797a39ae128f533dce7902a1096044d1967b9c16" +checksum = "f593a95398737aeed53e489c785df13f3618e41dbcd6718c6addbf1395aa6876" dependencies = [ "memchr", ] @@ -4960,16 +4291,17 @@ dependencies = [ [[package]] name = "wry" -version = "0.36.0" +version = "0.39.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a9e7b81968555303086ef882a0c213896a76099de4ed0b86a798775c2d54304" +checksum = "4eca9d50437c04fc67e82c196ddd31d8e35794150713ae2d647f3a58c7f45d1a" dependencies = [ - "base64", + "base64 0.21.7", "block", "cfg_aliases 0.1.1", "cocoa", "core-graphics", "crossbeam-channel", + "dpi", "dunce", "gdkx11", "gtk", @@ -4979,26 +4311,22 @@ dependencies = [ "jni", "kuchikiki", "libc", - "log", "ndk", "ndk-context", "ndk-sys", "objc", "objc_id", "once_cell", + "percent-encoding", "raw-window-handle 0.6.0", - "serde", - "serde_json", "sha2", "soup3", "tao-macros", "thiserror", - "url", "webkit2gtk", "webkit2gtk-sys", "webview2-com", - "windows 0.52.0", - "windows-implement", + "windows 0.54.0", "windows-version", "x11-dl", ] @@ -5023,138 +4351,3 @@ dependencies = [ "once_cell", "pkg-config", ] - -[[package]] -name = "x11rb" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8f25ead8c7e4cba123243a6367da5d3990e0d3affa708ea19dce96356bd9f1a" -dependencies = [ - "as-raw-xcb-connection", - "gethostname", - "libc", - "libloading 0.8.1", - "once_cell", - "rustix 0.38.30", - "x11rb-protocol", -] - -[[package]] -name = "x11rb-protocol" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e63e71c4b8bd9ffec2c963173a4dc4cbde9ee96961d4fcb4429db9929b606c34" - -[[package]] -name = "xdg-home" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2769203cd13a0c6015d515be729c526d041e9cf2c0cc478d57faee85f40c6dcd" -dependencies = [ - "nix", - "winapi 0.3.9", -] - -[[package]] -name = "zbus" -version = "3.14.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31de390a2d872e4cd04edd71b425e29853f786dc99317ed72d73d6fcf5ebb948" -dependencies = [ - "async-broadcast", - "async-executor", - "async-fs", - "async-io 1.13.0", - "async-lock 2.8.0", - "async-process", - "async-recursion", - "async-task", - "async-trait", - "blocking", - "byteorder", - "derivative", - "enumflags2", - "event-listener 2.5.3", - "futures-core", - "futures-sink", - "futures-util", - "hex", - "nix", - "once_cell", - "ordered-stream", - "rand 0.8.5", - "serde", - "serde_repr", - "sha1", - "static_assertions", - "tracing", - "uds_windows", - "winapi 0.3.9", - "xdg-home", - "zbus_macros", - "zbus_names", - "zvariant", -] - -[[package]] -name = "zbus_macros" -version = "3.14.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41d1794a946878c0e807f55a397187c11fc7a038ba5d868e7db4f3bd7760bc9d" -dependencies = [ - "proc-macro-crate 1.3.1", - "proc-macro2", - "quote", - "regex", - "syn 1.0.109", - "zvariant_utils", -] - -[[package]] -name = "zbus_names" -version = "2.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb80bb776dbda6e23d705cf0123c3b95df99c4ebeaec6c2599d4a5419902b4a9" -dependencies = [ - "serde", - "static_assertions", - "zvariant", -] - -[[package]] -name = "zvariant" -version = "3.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44b291bee0d960c53170780af148dca5fa260a63cdd24f1962fa82e03e53338c" -dependencies = [ - "byteorder", - "enumflags2", - "libc", - "serde", - "static_assertions", - "zvariant_derive", -] - -[[package]] -name = "zvariant_derive" -version = "3.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "934d7a7dfc310d6ee06c87ffe88ef4eca7d3e37bb251dece2ef93da8f17d8ecd" -dependencies = [ - "proc-macro-crate 1.3.1", - "proc-macro2", - "quote", - "syn 1.0.109", - "zvariant_utils", -] - -[[package]] -name = "zvariant_utils" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7234f0d811589db492d16893e3f21e8e2fd282e6d01b0cddee310322062cc200" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", -] diff --git a/examples/api/src-tauri/Cargo.toml b/examples/api/src-tauri/Cargo.toml index 2a097b1aa..3bb338938 100644 --- a/examples/api/src-tauri/Cargo.toml +++ b/examples/api/src-tauri/Cargo.toml @@ -20,19 +20,12 @@ tiny_http = "0.11" log = "0.4" tauri-plugin-sample = { path = "./tauri-plugin-sample/" } -[target."cfg(any(target_os = \"macos\", windows, target_os = \"linux\", target_os = \"dragonfly\", target_os = \"freebsd\", target_os = \"openbsd\", target_os = \"netbsd\"))".dependencies] -tauri-plugin-cli = { git = "https://github.com/tauri-apps/plugins-workspace", branch = "v2" } - -[patch.crates-io] -tauri = { path = "../../../core/tauri" } -tauri-build = { path = "../../../core/tauri-build" } - [dependencies.tauri] path = "../../../core/tauri" features = [ "protocol-asset", - "icon-ico", - "icon-png", + "image-ico", + "image-png", "isolation", "macos-private-api", "tray-icon" @@ -43,7 +36,7 @@ path = "../../../core/tauri" features = ["test"] [features] -custom-protocol = [ "tauri/custom-protocol" ] +prod = [ "tauri/custom-protocol" ] # default to small, optimized release binaries [profile.release] diff --git a/examples/api/src-tauri/build.rs b/examples/api/src-tauri/build.rs index e7b35e0b3..40216f916 100644 --- a/examples/api/src-tauri/build.rs +++ b/examples/api/src-tauri/build.rs @@ -1,13 +1,18 @@ -// Copyright 2019-2023 Tauri Programme within The Commons Conservancy +// Copyright 2019-2024 Tauri Programme within The Commons Conservancy // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT fn main() { - let mut codegen = tauri_build::CodegenContext::new(); - if !cfg!(feature = "custom-protocol") { - codegen = codegen.dev(); - } - - tauri_build::try_build(tauri_build::Attributes::new().codegen(codegen)) - .expect("failed to run tauri-build"); + tauri_build::try_build( + tauri_build::Attributes::new() + .codegen(tauri_build::CodegenContext::new()) + .plugin( + "app-menu", + tauri_build::InlinedPlugin::new().commands(&["toggle", "popup"]), + ) + .app_manifest( + tauri_build::AppManifest::new().commands(&["log_operation", "perform_request"]), + ), + ) + .expect("failed to run tauri-build"); } diff --git a/examples/api/src-tauri/capabilities/main.json b/examples/api/src-tauri/capabilities/main.json new file mode 100644 index 000000000..eecad73d1 --- /dev/null +++ b/examples/api/src-tauri/capabilities/main.json @@ -0,0 +1,18 @@ +{ + "$schema": "../gen/schemas/desktop-schema.json", + "identifier": "secondary-window", + "description": "capability for secondary window", + "windows": [ + "main-*" + ], + "permissions": [ + { + "identifier": "sample:allow-ping", + "deny": [ + { + "path": "tauri.app" + } + ] + } + ] +} diff --git a/examples/api/src-tauri/capabilities/run-app.json b/examples/api/src-tauri/capabilities/run-app.json index a95e15871..ea0a2952a 100644 --- a/examples/api/src-tauri/capabilities/run-app.json +++ b/examples/api/src-tauri/capabilities/run-app.json @@ -2,8 +2,21 @@ "$schema": "../gen/schemas/desktop-schema.json", "identifier": "run-app", "description": "permissions to run the app", - "windows": ["main", "main-*"], + "windows": [ + "main", + "main-*" + ], "permissions": [ + { + "identifier": "allow-log-operation", + "allow": [ + { + "event": "tauri-click" + } + ] + }, + "allow-perform-request", + "app-menu:default", "sample:allow-ping-scoped", "sample:global-scope", "path:default", @@ -11,6 +24,7 @@ "window:default", "app:default", "resources:default", + "image:default", "menu:default", "tray:default", "app:allow-app-hide", @@ -85,4 +99,4 @@ "tray:allow-set-icon-as-template", "tray:allow-set-show-menu-on-left-click" ] -} +} \ No newline at end of file diff --git a/examples/api/src-tauri/permissions/app-menu/default.toml b/examples/api/src-tauri/permissions/app-menu/default.toml new file mode 100644 index 000000000..b17fbd19a --- /dev/null +++ b/examples/api/src-tauri/permissions/app-menu/default.toml @@ -0,0 +1,3 @@ +[default] +description = "Default permissions for the plugin" +permissions = ["allow-toggle", "allow-popup"] diff --git a/examples/api/src-tauri/permissions/autogenerated/log_operation.toml b/examples/api/src-tauri/permissions/autogenerated/log_operation.toml new file mode 100644 index 000000000..a1e88b595 --- /dev/null +++ b/examples/api/src-tauri/permissions/autogenerated/log_operation.toml @@ -0,0 +1,11 @@ +# Automatically generated - DO NOT EDIT! + +[[permission]] +identifier = "allow-log-operation" +description = "Enables the log_operation command without any pre-configured scope." +commands.allow = ["log_operation"] + +[[permission]] +identifier = "deny-log-operation" +description = "Denies the log_operation command without any pre-configured scope." +commands.deny = ["log_operation"] diff --git a/examples/api/src-tauri/permissions/autogenerated/perform_request.toml b/examples/api/src-tauri/permissions/autogenerated/perform_request.toml new file mode 100644 index 000000000..0d12b9d1c --- /dev/null +++ b/examples/api/src-tauri/permissions/autogenerated/perform_request.toml @@ -0,0 +1,11 @@ +# Automatically generated - DO NOT EDIT! + +[[permission]] +identifier = "allow-perform-request" +description = "Enables the perform_request command without any pre-configured scope." +commands.allow = ["perform_request"] + +[[permission]] +identifier = "deny-perform-request" +description = "Denies the perform_request command without any pre-configured scope." +commands.deny = ["perform_request"] diff --git a/examples/api/src-tauri/src/cmd.rs b/examples/api/src-tauri/src/cmd.rs index 2c92decf7..feeba329a 100644 --- a/examples/api/src-tauri/src/cmd.rs +++ b/examples/api/src-tauri/src/cmd.rs @@ -1,9 +1,9 @@ -// Copyright 2019-2023 Tauri Programme within The Commons Conservancy +// Copyright 2019-2024 Tauri Programme within The Commons Conservancy // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT use serde::{Deserialize, Serialize}; -use tauri::command; +use tauri::{command, ipc::CommandScope}; #[derive(Debug, Deserialize)] #[allow(unused)] @@ -12,9 +12,25 @@ pub struct RequestBody { name: String, } +#[derive(Debug, Deserialize)] +pub struct LogScope { + event: String, +} + #[command] -pub fn log_operation(event: String, payload: Option) { - log::info!("{} {:?}", event, payload); +pub fn log_operation( + event: String, + payload: Option, + command_scope: CommandScope, +) -> Result<(), &'static str> { + if command_scope.denies().iter().any(|s| s.event == event) { + Err("denied") + } else if !command_scope.allows().iter().any(|s| s.event == event) { + Err("not allowed") + } else { + log::info!("{} {:?}", event, payload); + Ok(()) + } } #[derive(Serialize)] @@ -29,37 +45,3 @@ pub fn perform_request(endpoint: String, body: RequestBody) -> ApiResponse { message: "message response".into(), } } - -#[cfg(all(desktop, not(target_os = "macos")))] -#[command] -pub fn toggle_menu(window: tauri::Window) { - if window.is_menu_visible().unwrap_or_default() { - let _ = window.hide_menu(); - } else { - let _ = window.show_menu(); - } -} - -#[cfg(target_os = "macos")] -#[command] -pub fn toggle_menu( - app: tauri::AppHandle, - app_menu: tauri::State<'_, crate::AppMenu>, -) { - if let Some(menu) = app.remove_menu().unwrap() { - app_menu.0.lock().unwrap().replace(menu); - } else { - app - .set_menu(app_menu.0.lock().unwrap().clone().expect("no app menu")) - .unwrap(); - } -} - -#[cfg(desktop)] -#[command] -pub fn popup_context_menu( - window: tauri::Window, - popup_menu: tauri::State<'_, crate::PopupMenu>, -) { - window.popup_menu(&popup_menu.0).unwrap(); -} diff --git a/examples/api/src-tauri/src/lib.rs b/examples/api/src-tauri/src/lib.rs index ba7960dfb..9b558a2b4 100644 --- a/examples/api/src-tauri/src/lib.rs +++ b/examples/api/src-tauri/src/lib.rs @@ -1,9 +1,11 @@ -// Copyright 2019-2023 Tauri Programme within The Commons Conservancy +// Copyright 2019-2024 Tauri Programme within The Commons Conservancy // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT mod cmd; #[cfg(desktop)] +mod menu_plugin; +#[cfg(desktop)] mod tray; use serde::Serialize; @@ -45,7 +47,7 @@ pub fn run_app) + Send + 'static>( { let handle = app.handle(); tray::create_tray(handle)?; - handle.plugin(tauri_plugin_cli::init())?; + handle.plugin(menu_plugin::init())?; } #[cfg(target_os = "macos")] @@ -140,10 +142,6 @@ pub fn run_app) + Send + 'static>( .invoke_handler(tauri::generate_handler![ cmd::log_operation, cmd::perform_request, - #[cfg(desktop)] - cmd::toggle_menu, - #[cfg(desktop)] - cmd::popup_context_menu ]) .build(tauri::tauri_build_context!()) .expect("error while building tauri application"); diff --git a/examples/api/src-tauri/src/main.rs b/examples/api/src-tauri/src/main.rs index 33ab0aa16..12e1627af 100644 --- a/examples/api/src-tauri/src/main.rs +++ b/examples/api/src-tauri/src/main.rs @@ -1,4 +1,4 @@ -// Copyright 2019-2023 Tauri Programme within The Commons Conservancy +// Copyright 2019-2024 Tauri Programme within The Commons Conservancy // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT diff --git a/examples/api/src-tauri/src/menu_plugin.rs b/examples/api/src-tauri/src/menu_plugin.rs new file mode 100644 index 000000000..987320f9a --- /dev/null +++ b/examples/api/src-tauri/src/menu_plugin.rs @@ -0,0 +1,48 @@ +// Copyright 2019-2024 Tauri Programme within The Commons Conservancy +// SPDX-License-Identifier: Apache-2.0 +// SPDX-License-Identifier: MIT + +use tauri::{ + command, + plugin::{Builder, TauriPlugin}, + Runtime, +}; + +#[cfg(not(target_os = "macos"))] +#[command] +pub fn toggle(window: tauri::Window) { + if window.is_menu_visible().unwrap_or_default() { + let _ = window.hide_menu(); + } else { + let _ = window.show_menu(); + } +} + +#[cfg(target_os = "macos")] +#[command] +pub fn toggle( + app: tauri::AppHandle, + app_menu: tauri::State<'_, crate::AppMenu>, +) { + if let Some(menu) = app.remove_menu().unwrap() { + app_menu.0.lock().unwrap().replace(menu); + } else { + app + .set_menu(app_menu.0.lock().unwrap().clone().expect("no app menu")) + .unwrap(); + } +} + +#[command] +pub fn popup( + window: tauri::Window, + popup_menu: tauri::State<'_, crate::PopupMenu>, +) { + window.popup_menu(&popup_menu.0).unwrap(); +} + +pub fn init() -> TauriPlugin { + Builder::new("app-menu") + .invoke_handler(tauri::generate_handler![popup, toggle]) + .build() +} diff --git a/examples/api/src-tauri/src/tray.rs b/examples/api/src-tauri/src/tray.rs index 38bb2939d..c98804074 100644 --- a/examples/api/src-tauri/src/tray.rs +++ b/examples/api/src-tauri/src/tray.rs @@ -1,4 +1,4 @@ -// Copyright 2019-2023 Tauri Programme within The Commons Conservancy +// Copyright 2019-2024 Tauri Programme within The Commons Conservancy // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT @@ -81,11 +81,14 @@ pub fn create_tray(app: &tauri::AppHandle) -> tauri::Result<()> { } i @ "icon-1" | i @ "icon-2" => { if let Some(tray) = app.tray_by_id("tray-1") { - let _ = tray.set_icon(Some(tauri::Icon::Raw(if i == "icon-1" { - include_bytes!("../../../.icons/icon.ico").to_vec() + let icon = if i == "icon-1" { + tauri::image::Image::from_bytes(include_bytes!("../../../.icons/icon.ico")) } else { - include_bytes!("../../../.icons/tray_icon_with_transparency.png").to_vec() - }))); + tauri::image::Image::from_bytes(include_bytes!( + "../../../.icons/tray_icon_with_transparency.png" + )) + }; + let _ = tray.set_icon(Some(icon.unwrap())); } } "switch-menu" => { diff --git a/examples/api/src-tauri/tauri-plugin-sample/android/src/androidTest/java/com/plugin/sample/ExampleInstrumentedTest.kt b/examples/api/src-tauri/tauri-plugin-sample/android/src/androidTest/java/com/plugin/sample/ExampleInstrumentedTest.kt index 5e343b5db..795867b2a 100644 --- a/examples/api/src-tauri/tauri-plugin-sample/android/src/androidTest/java/com/plugin/sample/ExampleInstrumentedTest.kt +++ b/examples/api/src-tauri/tauri-plugin-sample/android/src/androidTest/java/com/plugin/sample/ExampleInstrumentedTest.kt @@ -1,4 +1,4 @@ -// Copyright 2019-2023 Tauri Programme within The Commons Conservancy +// Copyright 2019-2024 Tauri Programme within The Commons Conservancy // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT diff --git a/examples/api/src-tauri/tauri-plugin-sample/android/src/main/java/com/plugin/sample/Example.kt b/examples/api/src-tauri/tauri-plugin-sample/android/src/main/java/com/plugin/sample/Example.kt index a82408652..63d3581da 100644 --- a/examples/api/src-tauri/tauri-plugin-sample/android/src/main/java/com/plugin/sample/Example.kt +++ b/examples/api/src-tauri/tauri-plugin-sample/android/src/main/java/com/plugin/sample/Example.kt @@ -1,4 +1,4 @@ -// Copyright 2019-2023 Tauri Programme within The Commons Conservancy +// Copyright 2019-2024 Tauri Programme within The Commons Conservancy // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT diff --git a/examples/api/src-tauri/tauri-plugin-sample/android/src/main/java/com/plugin/sample/ExamplePlugin.kt b/examples/api/src-tauri/tauri-plugin-sample/android/src/main/java/com/plugin/sample/ExamplePlugin.kt index e61e08209..793dc37e8 100644 --- a/examples/api/src-tauri/tauri-plugin-sample/android/src/main/java/com/plugin/sample/ExamplePlugin.kt +++ b/examples/api/src-tauri/tauri-plugin-sample/android/src/main/java/com/plugin/sample/ExamplePlugin.kt @@ -1,4 +1,4 @@ -// Copyright 2019-2023 Tauri Programme within The Commons Conservancy +// Copyright 2019-2024 Tauri Programme within The Commons Conservancy // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT diff --git a/examples/api/src-tauri/tauri-plugin-sample/android/src/test/java/com/plugin/sample/ExampleUnitTest.kt b/examples/api/src-tauri/tauri-plugin-sample/android/src/test/java/com/plugin/sample/ExampleUnitTest.kt index 15ca36349..db6837ef4 100644 --- a/examples/api/src-tauri/tauri-plugin-sample/android/src/test/java/com/plugin/sample/ExampleUnitTest.kt +++ b/examples/api/src-tauri/tauri-plugin-sample/android/src/test/java/com/plugin/sample/ExampleUnitTest.kt @@ -1,4 +1,4 @@ -// Copyright 2019-2023 Tauri Programme within The Commons Conservancy +// Copyright 2019-2024 Tauri Programme within The Commons Conservancy // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT diff --git a/examples/api/src-tauri/tauri-plugin-sample/api-iife.js b/examples/api/src-tauri/tauri-plugin-sample/api-iife.js new file mode 100644 index 000000000..cb6dfc79d --- /dev/null +++ b/examples/api/src-tauri/tauri-plugin-sample/api-iife.js @@ -0,0 +1,7 @@ +// Copyright 2019-2024 Tauri Programme within The Commons Conservancy +// SPDX-License-Identifier: Apache-2.0 +// SPDX-License-Identifier: MIT + +if ('__TAURI__' in window) { + window.__TAURI__.sample = {} +} diff --git a/examples/api/src-tauri/tauri-plugin-sample/build.rs b/examples/api/src-tauri/tauri-plugin-sample/build.rs index 49cca3d6a..9ab61beb3 100644 --- a/examples/api/src-tauri/tauri-plugin-sample/build.rs +++ b/examples/api/src-tauri/tauri-plugin-sample/build.rs @@ -1,4 +1,4 @@ -// Copyright 2019-2023 Tauri Programme within The Commons Conservancy +// Copyright 2019-2024 Tauri Programme within The Commons Conservancy // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT @@ -8,5 +8,6 @@ fn main() { tauri_plugin::Builder::new(COMMANDS) .android_path("android") .ios_path("ios") + .global_api_script_path("./api-iife.js") .build(); } diff --git a/examples/api/src-tauri/tauri-plugin-sample/ios/Package.swift b/examples/api/src-tauri/tauri-plugin-sample/ios/Package.swift index 87bf32703..d0aa2fbfa 100644 --- a/examples/api/src-tauri/tauri-plugin-sample/ios/Package.swift +++ b/examples/api/src-tauri/tauri-plugin-sample/ios/Package.swift @@ -1,5 +1,5 @@ // swift-tools-version:5.3 -// Copyright 2019-2023 Tauri Programme within The Commons Conservancy +// Copyright 2019-2024 Tauri Programme within The Commons Conservancy // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT diff --git a/examples/api/src-tauri/tauri-plugin-sample/ios/Sources/ExamplePlugin.swift b/examples/api/src-tauri/tauri-plugin-sample/ios/Sources/ExamplePlugin.swift index 390aa7f7d..86083f398 100644 --- a/examples/api/src-tauri/tauri-plugin-sample/ios/Sources/ExamplePlugin.swift +++ b/examples/api/src-tauri/tauri-plugin-sample/ios/Sources/ExamplePlugin.swift @@ -1,4 +1,4 @@ -// Copyright 2019-2023 Tauri Programme within The Commons Conservancy +// Copyright 2019-2024 Tauri Programme within The Commons Conservancy // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT diff --git a/examples/api/src-tauri/tauri-plugin-sample/ios/Tests/PluginTests/PluginTests.swift b/examples/api/src-tauri/tauri-plugin-sample/ios/Tests/PluginTests/PluginTests.swift index 99992ce4c..114c4bec5 100644 --- a/examples/api/src-tauri/tauri-plugin-sample/ios/Tests/PluginTests/PluginTests.swift +++ b/examples/api/src-tauri/tauri-plugin-sample/ios/Tests/PluginTests/PluginTests.swift @@ -1,4 +1,4 @@ -// Copyright 2019-2023 Tauri Programme within The Commons Conservancy +// Copyright 2019-2024 Tauri Programme within The Commons Conservancy // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT diff --git a/examples/api/src-tauri/tauri-plugin-sample/permissions/autogenerated/reference.md b/examples/api/src-tauri/tauri-plugin-sample/permissions/autogenerated/reference.md index d44f15c8b..29df055a4 100644 --- a/examples/api/src-tauri/tauri-plugin-sample/permissions/autogenerated/reference.md +++ b/examples/api/src-tauri/tauri-plugin-sample/permissions/autogenerated/reference.md @@ -1,18 +1,6 @@ -# Permissions - -## allow-ping - -Enables the ping command without any pre-configured scope. - -## deny-ping - -Denies the ping command without any pre-configured scope. - -## global-scope - -Sets a global scope. - -## allow-ping-scoped - -Enables the ping command with a test scope. - +| Permission | Description | +|------|-----| +|`allow-ping`|Enables the ping command without any pre-configured scope.| +|`deny-ping`|Denies the ping command without any pre-configured scope.| +|`global-scope`|Sets a global scope.| +|`allow-ping-scoped`|Enables the ping command with a test scope.| diff --git a/examples/api/src-tauri/tauri-plugin-sample/permissions/schemas/schema.json b/examples/api/src-tauri/tauri-plugin-sample/permissions/schemas/schema.json index 74919da73..0bc62fcd2 100644 --- a/examples/api/src-tauri/tauri-plugin-sample/permissions/schemas/schema.json +++ b/examples/api/src-tauri/tauri-plugin-sample/permissions/schemas/schema.json @@ -17,7 +17,6 @@ }, "set": { "description": "A list of permissions sets defined", - "default": [], "type": "array", "items": { "$ref": "#/definitions/PermissionSet" @@ -132,12 +131,21 @@ }, "scope": { "description": "Allowed or denied scoped when using this permission.", - "default": {}, "allOf": [ { "$ref": "#/definitions/Scopes" } ] + }, + "platforms": { + "description": "Target platforms this permission applies. By default all platforms are affected by this permission.", + "type": [ + "array", + "null" + ], + "items": { + "$ref": "#/definitions/Target" + } } } }, @@ -243,6 +251,46 @@ } ] }, + "Target": { + "description": "Platform target.", + "oneOf": [ + { + "description": "MacOS.", + "type": "string", + "enum": [ + "macOS" + ] + }, + { + "description": "Windows.", + "type": "string", + "enum": [ + "windows" + ] + }, + { + "description": "Linux.", + "type": "string", + "enum": [ + "linux" + ] + }, + { + "description": "Android.", + "type": "string", + "enum": [ + "android" + ] + }, + { + "description": "iOS.", + "type": "string", + "enum": [ + "iOS" + ] + } + ] + }, "PermissionKind": { "type": "string", "oneOf": [ diff --git a/examples/api/src-tauri/tauri-plugin-sample/src/desktop.rs b/examples/api/src-tauri/tauri-plugin-sample/src/desktop.rs index b96655d6c..1526a1f5f 100644 --- a/examples/api/src-tauri/tauri-plugin-sample/src/desktop.rs +++ b/examples/api/src-tauri/tauri-plugin-sample/src/desktop.rs @@ -1,4 +1,4 @@ -// Copyright 2019-2023 Tauri Programme within The Commons Conservancy +// Copyright 2019-2024 Tauri Programme within The Commons Conservancy // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT diff --git a/examples/api/src-tauri/tauri-plugin-sample/src/error.rs b/examples/api/src-tauri/tauri-plugin-sample/src/error.rs index bd7381ed8..ca39ae0e0 100644 --- a/examples/api/src-tauri/tauri-plugin-sample/src/error.rs +++ b/examples/api/src-tauri/tauri-plugin-sample/src/error.rs @@ -1,4 +1,4 @@ -// Copyright 2019-2023 Tauri Programme within The Commons Conservancy +// Copyright 2019-2024 Tauri Programme within The Commons Conservancy // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT diff --git a/examples/api/src-tauri/tauri-plugin-sample/src/lib.rs b/examples/api/src-tauri/tauri-plugin-sample/src/lib.rs index 05936f576..db923fa12 100644 --- a/examples/api/src-tauri/tauri-plugin-sample/src/lib.rs +++ b/examples/api/src-tauri/tauri-plugin-sample/src/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2019-2023 Tauri Programme within The Commons Conservancy +// Copyright 2019-2024 Tauri Programme within The Commons Conservancy // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT @@ -70,7 +70,6 @@ fn ping( pub fn init() -> TauriPlugin { Builder::new("sample") .setup(|app, api| { - println!("global scope: {:?}", api.scope::()); #[cfg(mobile)] let sample = mobile::init(app, api)?; #[cfg(desktop)] diff --git a/examples/api/src-tauri/tauri-plugin-sample/src/mobile.rs b/examples/api/src-tauri/tauri-plugin-sample/src/mobile.rs index 74f1114f9..ed5f86782 100644 --- a/examples/api/src-tauri/tauri-plugin-sample/src/mobile.rs +++ b/examples/api/src-tauri/tauri-plugin-sample/src/mobile.rs @@ -1,4 +1,4 @@ -// Copyright 2019-2023 Tauri Programme within The Commons Conservancy +// Copyright 2019-2024 Tauri Programme within The Commons Conservancy // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT diff --git a/examples/api/src-tauri/tauri-plugin-sample/src/models.rs b/examples/api/src-tauri/tauri-plugin-sample/src/models.rs index 02db95608..99ac80ba9 100644 --- a/examples/api/src-tauri/tauri-plugin-sample/src/models.rs +++ b/examples/api/src-tauri/tauri-plugin-sample/src/models.rs @@ -1,4 +1,4 @@ -// Copyright 2019-2023 Tauri Programme within The Commons Conservancy +// Copyright 2019-2024 Tauri Programme within The Commons Conservancy // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT diff --git a/examples/api/src/App.svelte b/examples/api/src/App.svelte index b0288f2b0..3b077748b 100644 --- a/examples/api/src/App.svelte +++ b/examples/api/src/App.svelte @@ -1,5 +1,5 @@ diff --git a/examples/api/src/views/Welcome.svelte b/examples/api/src/views/Welcome.svelte index 084c66521..589a5c861 100644 --- a/examples/api/src/views/Welcome.svelte +++ b/examples/api/src/views/Welcome.svelte @@ -17,7 +17,7 @@ }) function contextMenu() { - invoke('popup_context_menu') + invoke('plugin:app-menu|popup') } diff --git a/examples/api/src/views/Window.svelte b/examples/api/src/views/Window.svelte index 891417599..28ed5c33a 100644 --- a/examples/api/src/views/Window.svelte +++ b/examples/api/src/views/Window.svelte @@ -8,7 +8,7 @@ EffectState, ProgressBarStatus } from '@tauri-apps/api/window' - import { WebviewWindow } from '@tauri-apps/api/webview' + import { WebviewWindow } from '@tauri-apps/api/webviewWindow' const webview = WebviewWindow.getCurrent() diff --git a/examples/api/src/vite-env.d.ts b/examples/api/src/vite-env.d.ts index c4b24aee8..b8c0f0f89 100644 --- a/examples/api/src/vite-env.d.ts +++ b/examples/api/src/vite-env.d.ts @@ -1,4 +1,4 @@ -// Copyright 2019-2023 Tauri Programme within The Commons Conservancy +// Copyright 2019-2024 Tauri Programme within The Commons Conservancy // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT diff --git a/examples/api/svelte.config.js b/examples/api/svelte.config.js index 15df6c215..3d8cb97f1 100644 --- a/examples/api/svelte.config.js +++ b/examples/api/svelte.config.js @@ -1,4 +1,4 @@ -// Copyright 2019-2023 Tauri Programme within The Commons Conservancy +// Copyright 2019-2024 Tauri Programme within The Commons Conservancy // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT diff --git a/examples/api/unocss.config.js b/examples/api/unocss.config.js index 61ba04aa3..d4eddef31 100644 --- a/examples/api/unocss.config.js +++ b/examples/api/unocss.config.js @@ -1,4 +1,4 @@ -// Copyright 2019-2023 Tauri Programme within The Commons Conservancy +// Copyright 2019-2024 Tauri Programme within The Commons Conservancy // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT diff --git a/examples/api/vite.config.js b/examples/api/vite.config.js index 895513414..d5d5459c8 100644 --- a/examples/api/vite.config.js +++ b/examples/api/vite.config.js @@ -1,4 +1,4 @@ -// Copyright 2019-2023 Tauri Programme within The Commons Conservancy +// Copyright 2019-2024 Tauri Programme within The Commons Conservancy // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT @@ -22,7 +22,7 @@ export default defineConfig({ } }, - // Vite optons tailored for Tauri development and only applied in `tauri dev` or `tauri build` + // Vite options tailored for Tauri development and only applied in `tauri dev` or `tauri build` // prevent vite from obscuring rust errors clearScreen: false, // tauri expects a fixed port, fail if that port is not available diff --git a/examples/api/yarn.lock b/examples/api/yarn.lock index 9eebb7e64..e93c7c937 100644 --- a/examples/api/yarn.lock +++ b/examples/api/yarn.lock @@ -1105,9 +1105,9 @@ unconfig@^0.3.4: mlly "^1.4.0" undici@^5.12.0: - version "5.26.5" - resolved "https://registry.yarnpkg.com/undici/-/undici-5.26.5.tgz#f6dc8c565e3cad8c4475b187f51a13e505092838" - integrity sha512-cSb4bPFd5qgR7qr2jYAi0hlX9n5YKK2ONKkLFkxl+v/9BvC0sOpZjBHDBSXc5lWAf5ty9oZdRXytBIHzgUcerw== + version "5.28.3" + resolved "https://registry.yarnpkg.com/undici/-/undici-5.28.3.tgz#a731e0eff2c3fcfd41c1169a869062be222d1e5b" + integrity sha512-3ItfzbrhDlINjaP0duwnNsKpDQk3acHI3gVJ1z4fmwMK31k5G9OVIAMLSIaP6w4FaGkaAkN6zaQO9LUvZ1t7VA== dependencies: "@fastify/busboy" "^2.0.0" @@ -1132,10 +1132,10 @@ unocss@^0.39.3: "@unocss/transformer-variant-group" "0.39.3" "@unocss/vite" "0.39.3" -vite@^4.5.2: - version "4.5.2" - resolved "https://registry.yarnpkg.com/vite/-/vite-4.5.2.tgz#d6ea8610e099851dad8c7371599969e0f8b97e82" - integrity sha512-tBCZBNSBbHQkaGyhGCDUGqeo2ph8Fstyp6FMSvTtsXeZSPpSMGlviAOav2hxVTqFcx8Hj/twtWKsMJXNY0xI8w== +vite@^4.5.3: + version "4.5.3" + resolved "https://registry.yarnpkg.com/vite/-/vite-4.5.3.tgz#d88a4529ea58bae97294c7e2e6f0eab39a50fb1a" + integrity sha512-kQL23kMeX92v3ph7IauVkXkikdDRsYMGTVl5KY2E9OY4ONLvkHf04MDTbnfo6NKxZiDLWzVpP5oTa8hQD8U3dg== dependencies: esbuild "^0.18.10" postcss "^8.4.27" diff --git a/examples/commands/commands.rs b/examples/commands/commands.rs index c656d8e99..93691c59d 100644 --- a/examples/commands/commands.rs +++ b/examples/commands/commands.rs @@ -1,4 +1,4 @@ -// Copyright 2019-2023 Tauri Programme within The Commons Conservancy +// Copyright 2019-2024 Tauri Programme within The Commons Conservancy // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT diff --git a/examples/commands/main.rs b/examples/commands/main.rs index f9c1504b9..b12d86429 100644 --- a/examples/commands/main.rs +++ b/examples/commands/main.rs @@ -1,4 +1,4 @@ -// Copyright 2019-2023 Tauri Programme within The Commons Conservancy +// Copyright 2019-2024 Tauri Programme within The Commons Conservancy // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT diff --git a/examples/file-associations/src-tauri/Cargo.toml b/examples/file-associations/src-tauri/Cargo.toml index 79f85632c..b12592efb 100644 --- a/examples/file-associations/src-tauri/Cargo.toml +++ b/examples/file-associations/src-tauri/Cargo.toml @@ -15,5 +15,4 @@ tauri = { path = "../../../core/tauri", features = [] } url = "2" [features] -default = [ "custom-protocol" ] -custom-protocol = [ "tauri/custom-protocol" ] +default = [ "tauri/custom-protocol" ] diff --git a/examples/file-associations/src-tauri/build.rs b/examples/file-associations/src-tauri/build.rs index b055ec37c..e43a276ec 100644 --- a/examples/file-associations/src-tauri/build.rs +++ b/examples/file-associations/src-tauri/build.rs @@ -1,4 +1,4 @@ -// Copyright 2019-2023 Tauri Programme within The Commons Conservancy +// Copyright 2019-2024 Tauri Programme within The Commons Conservancy // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT diff --git a/examples/file-associations/src-tauri/src/main.rs b/examples/file-associations/src-tauri/src/main.rs index a6cb3a4d7..df68074fb 100644 --- a/examples/file-associations/src-tauri/src/main.rs +++ b/examples/file-associations/src-tauri/src/main.rs @@ -1,4 +1,4 @@ -// Copyright 2019-2023 Tauri Programme within The Commons Conservancy +// Copyright 2019-2024 Tauri Programme within The Commons Conservancy // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT diff --git a/examples/helloworld/main.rs b/examples/helloworld/main.rs index 571df0f7b..cb68a2964 100644 --- a/examples/helloworld/main.rs +++ b/examples/helloworld/main.rs @@ -1,4 +1,4 @@ -// Copyright 2019-2023 Tauri Programme within The Commons Conservancy +// Copyright 2019-2024 Tauri Programme within The Commons Conservancy // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT diff --git a/examples/isolation/isolation-dist/index.js b/examples/isolation/isolation-dist/index.js index 680eae2b3..6510a210e 100644 --- a/examples/isolation/isolation-dist/index.js +++ b/examples/isolation/isolation-dist/index.js @@ -1,4 +1,4 @@ -// Copyright 2019-2023 Tauri Programme within The Commons Conservancy +// Copyright 2019-2024 Tauri Programme within The Commons Conservancy // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT diff --git a/examples/isolation/main.rs b/examples/isolation/main.rs index 1ff235466..5eb6c2975 100644 --- a/examples/isolation/main.rs +++ b/examples/isolation/main.rs @@ -1,4 +1,4 @@ -// Copyright 2019-2023 Tauri Programme within The Commons Conservancy +// Copyright 2019-2024 Tauri Programme within The Commons Conservancy // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT diff --git a/examples/multiwebview/main.rs b/examples/multiwebview/main.rs index 5f325b67a..61fef73ce 100644 --- a/examples/multiwebview/main.rs +++ b/examples/multiwebview/main.rs @@ -1,4 +1,4 @@ -// Copyright 2019-2023 Tauri Programme within The Commons Conservancy +// Copyright 2019-2024 Tauri Programme within The Commons Conservancy // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT diff --git a/examples/multiwindow/index.html b/examples/multiwindow/index.html index 5c7bc69e6..d51c0b7e5 100644 --- a/examples/multiwindow/index.html +++ b/examples/multiwindow/index.html @@ -14,7 +14,7 @@